diff --git a/.ansible-lint b/.ansible-lint new file mode 100644 index 0000000..24ee7ff --- /dev/null +++ b/.ansible-lint @@ -0,0 +1,11 @@ +--- +offline: false + +exclude_paths: + - .github/ + - docs + - mkdocs.yml + +warn_list: + - name[template] + - run-once[play] diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..cf9ada5 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,2 @@ +# https://github.com/orgs/redhat-cop/teams/rhis-code-admins +* @redhat-cop/rhis-code-admins \ No newline at end of file diff --git a/.github/CODE_OF_CONDUCT.md b/.github/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..94c230e --- /dev/null +++ b/.github/CODE_OF_CONDUCT.md @@ -0,0 +1,5 @@ +# Community Code of Conduct + +The Ansible Community Code of Conduct emphasizes respect, patience, and inclusivity in all interactions. It encourages community members to be considerate, kind, and helpful, avoiding harassment or offensive behavior. The Code applies to all Ansible-related events, forums, and communications. Violations, including harassment, can be reported to event organizers or admins, with potential consequences like warnings or expulsion from events. The policy is designed to ensure a safe, welcoming environment for all contributors, regardless of background or skill level. + +Please see the [Ansible Community Code of Conduct](https://docs.ansible.com/ansible/latest/community/code_of_conduct.html). diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md new file mode 100644 index 0000000..6fe9fb2 --- /dev/null +++ b/.github/CONTRIBUTING.md @@ -0,0 +1,72 @@ +# Contributor's Guidelines + +Refer to the [Contributing guidelines](https://docs.ansible.com/ansible/devel/community/index.html) for basic guideline. + +## General Guidance +When making a contribution to the repositories of the Red Hat Communities of Practice, try to utilize the following set of guidelines outlined in this section. Committers should adhere to these practices as they attempt to address many (but not all) of the primary concerns associated with open source repositories and project management: + +**Do:** + +- Squash commits whenever possible +- Engage with the community and with contributors +- Write tests when applicable +- Discuss with other committers whenever you are unsure of something +- Create documentation, especially for new features and functionality +- Review existing content in order to avoid overwriting existing content +- Keep it simple +- Sign your commits + +**Donโ€™t:** + +- Commit directly to the master branch +- Merge pull requests you have authored +- Break existing functionality +- Ignore requests for assistance +- Go against established repository and CoP norms + +Please checkout [General Guidelines](../docs/general_guidelines.md) for more information. + +Please follow [Ansible Good Practices](../docs/ansible_practices.md) as the project consists of full automation with Ansible. + +And finally, you can benefit from the [Git Cheat Sheet](../docs/git_cheat_sheet.md) for the basics of using Git within the project. + +## How to Contribute + +Contributions from the community play an important role in the assets associated with the Red Hat Communities of Practice (CoP). We welcome contributions from the community. Here are a few ways you can help us improve. + +### Open an Issue + +If you see something you'd like changed, but aren't sure how to change it, submit an issue describing what you'd like to see. + +### Submit a Pull Request + +If you feel like getting your hands dirty, feel free to make the change yourself. Here's how: + +1. Fork the repo on Github, and then clone it locally. +2. Create a branch named appropriately for the change you are going to make. Follow the [branch standards](../docs/branch_standards.md) to create a branch. +3. Make your code change. +4. If you are creating a new role, please add a tests for it for use in a github action if possible. +5. Push your code change up to your forked repo. +6. Open a Pull Request to merge your changes to this repo. The comment box will be filled in automatically via a template. +7. All Pull Requests will be subject to Ansible and Yaml Linting checks. Please make sure that your code complies and fix any warnings that arise. These are Checks that appear at the bottom of your Pull Request. +8. PR must be reviewed and approved by two persons who own commit permissions. + + +See [Using Pull Requests](https://help.github.com/articles/using-pull-requests/) got more information on how to use GitHub PRs. + +For an in depth guide on how to contribute see [this article](https://opensource.com/article/19/7/create-pull-request-github) + +Note that we follow the [Automation Good Practices](https://redhat-cop.github.io/automation-good-practices) and so are you expected to do. + +Use Github [discussions](https://github.com/showroom-project/showroom-code/discussions) forum or for a live chat experience try +Matrix room [#aap_config_as_code:ansible.com](https://matrix.to/#/#aap_config_as_code:ansible.com). + +## Code of Conduct + +As with all Ansible projects, we have a [Code of Conduct](./CODE_OF_CONDUCT.md). + +- [ansible communication](https://docs.ansible.com/ansible/latest/community/communication.html) +- [code of conduct](https://docs.ansible.com/ansible/latest/community/code_of_conduct.html) +- [creating your fork on github](https://guides.github.com/activities/forking/) +- [releases and maintenances](https://docs.ansible.com/ansible-core/devel/reference_appendices/release_and_maintenance.) +- [supported ansible versions](https://docs.ansible.com/ansible-core/devel/reference_appendices/release_and_maintenance.html#ansible-core-release-cycle) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..ce1b956 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,92 @@ +--- +name: ๐Ÿ› Bug Report +about: Create a bug report. Please test against the latest release before submitting + it. For anything else, please use the Forums link below. +title: 'BUG:' +labels: bug, new +assignees: '' + +--- + + + + +## Summary + + + +*Provide a concise description of the bug.* + +--- + +## Ansible, Collection details + + + +```console (paste below) +ansible --version + +ansible-galaxy collection list + +``` + +- ansible installation method: one of source, pip, OS package, EE + +--- + +## OS / ENVIRONMENT + + + +--- + +## Desired Behavior + + + +*Describe what actually expected.* + +--- + +## Actual Behavior + + + +Please give some details of what is actually happening. +Include a [minimum complete verifiable example] with: + +- playbook / task +- configuration file / list +- error + + + +```console (error) + +``` +--- + +## STEPS TO REPRODUCE + + + + + +```yaml (playbook/task) + +``` + +```yaml (config/list/array/variables) + +``` + + + + + +--- + +## Code of Conduct +*Read the Ansible Code of Conduct first.* + +- [ ] I agree to follow the [Ansible Community Code of Conduct](https://docs.ansible.com/ansible/latest/community/code_of_conduct.html) diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..576c4f7 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,10 @@ +--- +# Ref: https://help.github.com/en/github/building-a-strong-community/configuring-issue-templates-for-your-repository#configuring-the-template-chooser +blank_issues_enabled: false # default is true +contact_links: + - name: ๐Ÿ“ Ansible Community Code of Conduct + url: https://docs.ansible.com/ansible/latest/community/code_of_conduct.html + about: Be nice to other members of the community. Behave. + - name: ๐Ÿ’ฌ Talk to Steering Committee + url: https://redhat.enterprise.slack.com/archives/C06VCEWRQE4 + about: Reach out to the Red Hat Community diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..ab68a73 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,56 @@ +--- +name: โœจ Feature request +about: Suggest an idea for this project +title: 'RFE:' +labels: enhancement, new +assignees: '' + +--- + +## Issue Summary + +*Provide a concise description of the issue.* + +--- + +## Steps to Reproduce + +1. **Step 1**: +2. **Step 2**: +3. **Step 3**: + +--- + +## Expected Behavior + +**Describe the solution you'd like** +*A clear and concise description of what you want to happen.* + +**Describe alternatives you've considered** +*A clear and concise description of any alternative solutions or features you've considered.* + +--- + +## Actual Behavior + +*Describe what actually happened.* + +--- + +## Environment Details + +- **OS**: +- **Ansible version** (if applicable): +- **Other relevant information**: + +--- + +## Additional Context +*Add any other context or screenshots about the feature request here.* + +--- + +## Code of Conduct +*Read the Ansible Code of Conduct first.* + +- [ ] I agree to follow the [Ansible Community Code of Conduct](https://docs.ansible.com/ansible/latest/community/code_of_conduct.html) diff --git a/.github/ISSUE_TEMPLATE/security_report.md b/.github/ISSUE_TEMPLATE/security_report.md new file mode 100644 index 0000000..e1b052d --- /dev/null +++ b/.github/ISSUE_TEMPLATE/security_report.md @@ -0,0 +1,67 @@ +--- +name: ๐Ÿ” Security Bug Report +about: Report security bug here +title: 'SEC:' +labels: security, new +assignees: '' + +--- +## Security Bug Report + +### โš ๏ธ **Security Disclosure Process** + +If youโ€™ve found a security issue, **DO NOT** disclose it publicly. Instead, follow the responsible disclosure policy or contact the maintainers directly. Security issues must be handled with care to avoid unnecessary exposure. + +--- + +## Description + +**Provide a clear and detailed description of the security issue.** + +- What is the vulnerability or security concern? +- How can this affect the project or its users? + +--- + +## Steps to Reproduce + +**If applicable, provide steps to reproduce the security issue:** + +1. Step 1 +2. Step 2 +3. Step 3 + +--- + +## Impact + +**Describe the potential impact of this security issue.** + +- Data breach? Unauthorized access? Denial of service? Privilege escalation? etc. + +--- + +## Environment + +**Provide details about the environment where the issue occurs:** + +- OS: (e.g. Windows, macOS, Linux) +- Project version: (e.g. v1.0.0) +- Dependencies: (e.g. third-party libraries) +- Other relevant configurations: + +--- + +## Mitigation Recommendations + +**Suggest any potential fixes or mitigation steps, if known.** + +--- + +## Additional Information + +**Include any additional information or context that might help address the security issue (e.g., logs, screenshots, etc.).** + +--- + +**Reminder:** Please do not disclose this security issue publicly until it has been properly addressed. diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..57d6582 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,20 @@ +## Summary + + + +## Connected Jira issues + +* https://issues.redhat.com/browse/SHWRM-xxx + +## Connected merge requests + + + +* showroom-inventory!xxx + +## Functional test + + diff --git a/.github/PULL_REQUEST_TEMPLATE/docs_pull_request_template.md b/.github/PULL_REQUEST_TEMPLATE/docs_pull_request_template.md new file mode 100644 index 0000000..bec6886 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE/docs_pull_request_template.md @@ -0,0 +1,7 @@ +## Summary + + + +## Connected Jira issues + +* https://issues.redhat.com/browse/SHWRM-xxx diff --git a/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md b/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md new file mode 100644 index 0000000..57d6582 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md @@ -0,0 +1,20 @@ +## Summary + + + +## Connected Jira issues + +* https://issues.redhat.com/browse/SHWRM-xxx + +## Connected merge requests + + + +* showroom-inventory!xxx + +## Functional test + + diff --git a/.github/SECURITY.md b/.github/SECURITY.md new file mode 100644 index 0000000..f3e7f8a --- /dev/null +++ b/.github/SECURITY.md @@ -0,0 +1,9 @@ +# Security Policy + +## Supported Versions + +Only the latest version is supported. + +## Reporting a Vulnerability + +For any issues or concerns, please contact: [@rhis-code-admins](https://github.com/orgs/redhat-cop/teams/rhis-code-admins) diff --git a/.github/ansible-code-bot.yml b/.github/ansible-code-bot.yml new file mode 100644 index 0000000..e17583f --- /dev/null +++ b/.github/ansible-code-bot.yml @@ -0,0 +1,3 @@ +--- +schedule: + interval: "weekly" diff --git a/.github/workflows/ansible-lint.yml b/.github/workflows/ansible-lint.yml new file mode 100644 index 0000000..726555b --- /dev/null +++ b/.github/workflows/ansible-lint.yml @@ -0,0 +1,65 @@ +--- +name: Ansible Lint +run-name: ansible-lint validation on PR-${{ github.event.pull_request.number }} +on: + pull_request: + branches: ["main", "devel"] +jobs: + prepare-container: + name: Prepare for container + runs-on: [self-hosted] + steps: + - name: prepare environment for container + run: | + export XDG_RUNTIME_DIR=/run/user/$(id -u) + sudo rm -rf /opt/actions-runner/_work/_temp/_github_home/.cache/ + + ansible-lint: + name: Ansible Lint + runs-on: [self-hosted] + needs: prepare-container + container: + image: ubuntu:22.04 + options: --memory-swap "4g" --memory "3g" --security-opt label:disable + env: + DOCKER_HOST: unix:$XDG_RUNTIME_DIR/podman/podman.sock + steps: + - name: Prepare Container + run: | + apt-get -y update + apt-get -y install --no-install-recommends wget + apt-get -y install git lsb-release + env: + DEBIAN_FRONTEND: noninteractive + + - name: Git checkout + uses: actions/checkout@v4 + + - name: Populate ansdible config + run: | + cat < ansible.cfg + [defaults] + collections_path = ./collections:./imported/collections + roles_path = ./roles:./imported:./imported/roles + + [galaxy] + server_list = automation_hub_published, automation_hub_validated, galaxy + + [galaxy_server.automation_hub_published] + url=https://console.redhat.com/api/automation-hub/content/published/ + auth_url=https://sso.redhat.com/auth/realms/redhat-external/protocol/openid-connect/token + token=${{ secrets.RH_AUTOMATION_HUB_TOKEN }} + + [galaxy_server.automation_hub_validated] + url=https://console.redhat.com/api/automation-hub/content/validated/ + auth_url=https://sso.redhat.com/auth/realms/redhat-external/protocol/openid-connect/token + token=${{ secrets.RH_AUTOMATION_HUB_TOKEN }} + + [galaxy_server.galaxy] + url=https://galaxy.ansible.com/ + EOF + + - name: Run ansible-lint + uses: ansible/ansible-lint@main + with: + working_directory: /__w/showroom-code/showroom-code diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml new file mode 100644 index 0000000..45c30ba --- /dev/null +++ b/.github/workflows/gh-pages.yml @@ -0,0 +1,38 @@ +--- +name: Deploy to GitHub Pages +on: + push: + branches: + - devel + +permissions: + contents: read + pages: write + id-token: write + +jobs: + pages-deploy: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + # Not available for private repo + # - name: Markdown lint + # uses: DavidAnson/markdownlint-cli2-action@v9 + # with: + # globs: '**/*.md' + - run: pip install mkdocs-material + - run: mkdocs build + - name: Setup Pages + uses: actions/configure-pages@v5 + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + # Upload entire repository + path: './site' + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bfc6528 --- /dev/null +++ b/.gitignore @@ -0,0 +1,25 @@ +*.retry +*.log +*[_.]local[_.]* +.idea +*.kate-swp +.*.swp +*~ +tests/junit_xml/*.xml + +# macbook auto generated file +*[_.]DS_Store + +# auto generated python files after compilation +*[_.]pyc +*[_.]pyo +*[_/]__pycache__/* +.vscode/settings.json +.vs +.venv + +#rhis-code related files +.vault-password +imported/ +collections/ansible_collections/ +ansible.cfg diff --git a/.yamllint b/.yamllint new file mode 100644 index 0000000..3199669 --- /dev/null +++ b/.yamllint @@ -0,0 +1,11 @@ +--- +yaml-files: + - '*.yaml' + - '*.yml' + - '.yamllint' + +rules: + truthy: + allowed-values: + - 'true' + - 'false' diff --git a/LICENSE b/LICENSE index 261eeb9..228f9b3 100644 --- a/LICENSE +++ b/LICENSE @@ -1,201 +1,674 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/README.adoc b/README.adoc new file mode 100644 index 0000000..cff9b8e --- /dev/null +++ b/README.adoc @@ -0,0 +1,64 @@ +//// +author: Showroom Project Team +company: Red Hat, Inc. +license: GPL-3.0-only +//// + +:subject: Showroom Code +:description: This document explains Showroom Project +:doctype: book +:chapter-label: +:imagesdir: ./images +:encoding: UTF-8 +:lang: en +:autofit-option: +:url-org: https://github.com/showroom-project +:url-repo: {url-org}/showroom-code +:url-inventory-repo: {url-org}/showroom-inventory + += {subject} + +image:https://github.com/showroom-project/showroom-code/actions/workflows/ansible-lint.yml/badge.svg[Ansible Code Lint Status (GitHub Actions),link={url-repo}/actions/workflows/ansible-lint.yml] image:https://img.shields.io/badge/slack-channel-tech?logo=slack[Slack Channel,link=https://redhat.enterprise.slack.com/archives/C06UNKD7813] image:https://img.shields.io/badge/miro-workspace-board?logo=miro[Miro Workspace,link=https://miro.com/app/board/uXjVKUIlyW8=/] image:https://img.shields.io/badge/JIRA-Board-Workspace?logo=slack[JIRA Board,link=https://issues.redhat.com/secure/RapidBoard.jspa] + +This repository is intended to contain ansible automation code. All documents are stored on https://github.com/showroom-project/showroom-code[Showroom Code] repository. + +The goal of this project is to build a Red Hat based environment suitable for a showroom project by an *enterprise* ready approach an opinionated Red Hat infrastructure that implements several Standard Operating Environments for Red Hat Enterprise Linux using Red Hat Management tools (Red Hat Infrastructure Standard Adoption Model). + +The Red Hat Infrastructure Standard (*RH-IS*)footnote:disclaimer[https://github.com/redhat-cop/rhis-builder[RHIS Builder Repository]] is a framework for using integrated Red Hat technologies to build, manage, automate, optimize, and maintain Red Hat Enterprise Linux at scale in a hybrid multi-cloud. +One of the fundamental principles of the *RH-ISAM* is to strive towards โ€œ*_everything as code_*โ€. The goal is repeatability, consistency, and stability and being modular. + +== Advantages of implementing the Red Hat Infrastructure Standard + +The Red Hat Infrastructure Standard is an opinionated framework for the deployment, configuration, and use of: + +* Red Hat Enterprise Linux +* Red Hat Identity Management +* Red Hat Satellite +* Red Hat Ansible Automation Platform +* Red Hat Build of Keycloak + +It leverages the Red Hat Hybrid Cloud Console to deploy, patch, maintain, secure, automate and ensure compliance of the Red Hat Enterprise Linux systems across a Hybrid Multi-cloud environment. + +GitOps practices are used in the process to define the desired states and conduct tests on management infrastructure and managed systems through an infrastructure-as-code pipeline. + +There are also external tools used to to simulate integration points such as + +* Self hosted GitHub Runner +* RootCA based on OpenSSL + +== where to start +Current https://github.com/showroom-project/showroom-code[showroom-code] consumes https://github.com/showroom-project/showroom-inventory[showroom-inventory] repository for the credentials and configurable variables. Duplicate `showroom-inventory` repository as your own inventory to use if you plan to deploy environment on your own and update definitions based on your own. + +First you can initialize your local environment by running link:playbooks/landscape_init.yml[`landscape_init.yml`]. This playbook will create an SSH Key pair and configuration for the generic SSH that enables access using Bastion. SSH public key will encrypted and pushed to the inventory repository. + +```bash +ansible-playbook -i showroom-inventory/inventory.yml showroom-code/playbooks/landscape_init.yml --ask-vault-pass -e init_environment_set=true +``` + +link:playbooks/landscape_site.yml[`landscape_site.yml`] is the main Ansible playbook to trigger everything from scratch for a Big Bang. + +```bash +ansible-playbook -i showroom-inventory/inventory.yml showroom-code/playbooks/landscape_site.yml --ask-vault-pass +``` + +Refer to link:docs/where_to_start.md[where to start documentation] for more information. diff --git a/README.md b/README.md deleted file mode 100644 index 5c91c01..0000000 --- a/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# showroom-code -A Red Hat Technology based environment suitable for demonstrating a Red Hat opinionated architecture involving automated Standard Operating Environments for Red Hat Enterprise Linux. diff --git a/collections/requirements.yml b/collections/requirements.yml new file mode 100644 index 0000000..6af99f4 --- /dev/null +++ b/collections/requirements.yml @@ -0,0 +1,44 @@ +--- +collections: + - name: ansible.posix + version: '1.5.4' + - name: ansible.utils + version: '5.0.0' + - name: ansible.eda + version: '1.4.7' + - name: ansible.netcommon + version: '7.0.0' + - name: ansible.controller + version: '4.5.7' + - name: community.crypto + version: '2.20.0' + - name: community.general + version: '8.3.0' + - name: community.dns + version: '3.0.1' + - name: azure.azcollection + version: '2.4.0' + - name: redhat.rhel_idm + version: '1.12.1' + - name: redhat.rhel_system_roles + version: '1.23.0' + - name: redhat.satellite_operations + version: '3.0.0' + - name: redhat.satellite + version: '4.0.0' + - name: infra.aap_utilities + version: '2.5.1' + - name: infra.controller_configuration + version: '2.7.1' + - name: infra.ee_utilities + version: '3.2.0' + - name: infra.ah_configuration + version: '2.0.6' + - name: infra.leapp + version: '1.3.1' + - name: containers.podman + version: '1.13.0' + +roles: + - name: RedHatOfficial.rhel9_cis + - name: monolithprojects.github_actions_runner diff --git a/docs/CNAME b/docs/CNAME new file mode 100644 index 0000000..2343963 --- /dev/null +++ b/docs/CNAME @@ -0,0 +1 @@ +docs.showroom.run \ No newline at end of file diff --git a/docs/aap_architecture.md b/docs/aap_architecture.md new file mode 100644 index 0000000..83e1f18 --- /dev/null +++ b/docs/aap_architecture.md @@ -0,0 +1,352 @@ +# Ansible Automation Platform Architecture + +This document describes the high-level architecture for Ansible +Automation Platform. It will be the cental automation platform for all +components that will be running in cloud provider. + +This document provides a comprehensive overview for deploying the Red +Hat Ansible Automation Platform, tailored to the specified requirements. + +Even though there are managed Ansible Automation Platform available in +major cloud providers we will aim for a self-managed platform in favour +of flexibility, management, security. + +The platform will also connect to upstream for getting Ansible +collection contents from Automation Hub, Ansible Galaxy & RedHat +Certified Ansible collection etc. + +# High level design + +## Ansible Automation platform components + +Components and their specifications in Ansible Automation platform + + +++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameOSCPUโ€™sRAMStorage

1x Ansible Controller

RHEL9.x

2

16GiB

32GB default with minimum with at least +20GB available under /var/lib/awx as additional +disk

1x Private Automation Hub

RHEL9.x

2

8GiB

32GB default disk with additional disk +with /var mount

1x PostgresDB (Controller & Hub +DB)

RHEL9.x Postgres13

4

16GiB

32GB default with additional 100GB disk +with /var/lib/pgsql mount

1x Execution Node in Infra +workload

RHEL9.x

2

16 GiB

32GB

1x Hop node in DMZ Workload

RHEL9.x

2

8GiB

32GB

1x Execution node in DMZ +Workload

RHEL9.x

2

8GiB

32GB

+ +Minimum requirements + +## Architectural Diagram + +![AAP High Level Architecture](images/aap_architecture.png){ align=centre } + +## Network Requirements + + +![Ansible Automation Platform Network ports](images/aap_network.png){ align=centre } + + +## Certificates + +Red Hat Ansible Automation Platform components will use an SSL +certificate signed by an external certificate authority (CA) which is +Red Hat Identity Management acting as a subordinate Certificate +Authority. Refer to the certificate requirements from the official +[documentation](https://docs.redhat.com/en/documentation/red_hat_ansible_automation_platform/2.4/html/red_hat_ansible_automation_platform_operations_guide/changing-ssl-certs-keys#changing-ssl-certs-keys). + +## Locations + +Primarily there are two locations, Intranet and DMZ. We aim to decouple +the execution plane of Ansible Automation Platform in each locations. +Hence the execution of automation is localized in each site. + +We will also have a hop node in DMZ to mimic the air-gapped environment +which will connect Ansible Automation Platform Controller with Execution +node. + + ++++ + + + + + + + + + + + + + + + + +
NameDescription

Intranet

for internal business workloads and +infrastructure management services

DMZ

for the business workloads which have +public access enabled

+ +## Organizations + +Ansible Automation Controller will have only one organization. + + ++++ + + + + + + + + + + + + +
NameDescription

Showroom

Showroom Organization

+ +## High Availability + +There is no HA built-in for the Controllers and the deployment will use +single Controller making it single point for failure. Even though the +Controller will be in Hybrid (default) mode and it will be able to +launch jobs, we still have the Execution nodes which take the load off +from Controller. + +However that is still a SPOF in case the Controller goes down. + +## Backup + +It is planned to use built-in backup functionality to use in Ansible +Automation Platform to facilitate recovery in the event of disaster. + +There should be an Ansible playbook which can restore from backup to +minimize RTO, backups are also taken daily to minimize the RPO. + +# Source of truth + +Ansible Automation Platform will use scm/git as single source of truth +to manage/maintain managed systems. We plan to use approach similar to +GitOps. + +Refer to \#GitOps with Ansible for more details on GitOps + +# Inventory + +An Inventory is a collection of hosts against which jobs may be +launched. Inventories can be constructed from Cloud Provider, Red Hat +Satellite and / or sourced from a GIT repository. + +## Smart Host Filter + +Smart Host filter will be used to differentiate different lifecylces for +the hosts like PROD, DEV. QA + +# Projects + +A Project is a logical collection of Ansible playbooks which is sourced +from the SCM like GitHub. Source Control Type will be `Git` and source +control branch / tag must be set to `devel` pointing to this repository. + +For a Continuous Integration (Github Workflow) to spawn a job, it should +make a curl request to a job template. The credentials to the job +template should not require prompting for any particular passwords or +variable prompt. + +# LDAP Authentication + +LDAP is used as a source for account authentication information for +Ansible Automation Platform users. User authentication is provided, but +not the synchronization of user permissions and credentials. +Organization membership (as well as the organization admin) and team +memberships can be synchronized. Users are placed into organizations and +assigned to the teams automatically based on LDAP attributes by +implementing organization and team mapping control. + +# Users and Teams + +Users and Teams are constructed automatically by LDAP configuration +based on the user and group information that is stored on RH IdM. + +The following groups will be configured on AAP Controller. + + ++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Team NameDescription

aapgroup-administrator

Ansible Automation Platform +Administrators

aapgroup-auditor

Ansible Automation Platform +Auditors

aapgroup-developer

Ansible Automation Platform Programmer +- an AAP power user

aapgroup-operator

Ansible Automation Platform Server +Operators

aapgroup-user

Ansible Automation Platform User - Can +run basic Templates and Workflows

aapgroup-proj_manager

Ansible Automation Platform Project +Manager

aapgroup-template_manager

Ansible Automation Platform Template +Manager

+ +# Credentials + +Credentials are utilized for authentication when launching Jobs against +machines, synchronizing with inventory sources, and importing project +content from a version control system. By default all credentials are +stored by type of encrypted string under inventory variables repository +on GitHub. Additional credential stores such as RH IdM and Microsoft +Azure Key Vault can be also used. + + ++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Credential TypeName

Machine

Machine Credential

Source Control

GitHub Source Credential

Vault

Vault Credential

GitHub Personal Access Token

GitHub Access Token

Insights

Insights Credential

+ +# Roles + +TODO: + +# Notifications + +Notification templates are configured based on Job Templates, Project +and Inventory Updates level. For notifications a default Slack +notification channel will be consumed. + +# Insights + +Automation controller supports integration with Red Hat Insights. Once a +host is registered with Insights, it will be continually scanned for +vulnerabilities and known configuration conflicts. Each of the found +problems may have an associated fix in the form of an Ansible playbook. + +# Labels + +Labels can be used to group and filter job templates and completed jobs +in the Ansible Automation Controller display and needs to be configured +for each job or workflow template. diff --git a/docs/ansible_practices.md b/docs/ansible_practices.md new file mode 100644 index 0000000..83376c9 --- /dev/null +++ b/docs/ansible_practices.md @@ -0,0 +1,361 @@ +# Abstract + +This document simply refers to the [good practices +document](https://redhat-cop.github.io/automation-good-practices/) from +the field of Ansible practitioners at Red Hat, consultants, developers, +and others. and also [tips and +tricks](https://docs.ansible.com/ansible/latest/tips_tricks/ansible_tips_tricks.html) +from Ansible community. Please visit the related pages to get more deep +understanding if needed. + +The following rules are a good recommendation to start with. **Optimize +for Readability** should be the main indicator and if done properly, it +can be the documentation of your workflow automation. + +A clean separation of code and data (aka inventory) needs to be fostered +from the beginning. In general one basis version control system +repository for all the project code and documentation, including all +roles in a sub-directory and another version control system repository +called for the inventory and variables. The root directory in each +repository contains a `README.adoc` file which explains the repository +and the sub-directories structure and/or points to more detailed +documentation. + +The Showroom Project contains following repositories: + +1. Code repository for roles, collections, playbooks is named + [`showroom-code`](https://github.com/showroom-project/showroom-code) + +2. Inventory and variable repository is placed named + [`showroom-inventory`](https://github.com/showroom-project/showroom-inventory) + +# High level hierarchy for playbooks + +The following automation structure model is used in the project. + +![Structure of Automation](./images/ansible_structures.svg) + +## Landscape + +Top level playbooks are separated by role. A landscape is anything we +want to deploy at once which includes many import of other playbooks and +can be visualized like a workflow approach in Ansible Automation +Controller. The named should be include a prefix named `landscape_`. + +## Use Cases (Types) + +Use cases are high level collections of tasks, grouped around a specific +type of usage - base operating system configuration, a particular server +deployment etc. Use cases are mapped as epics in the Jira. These epics +contain user stories, tasks and bugs for specific features. These kind +of playbooks should start with a prefix named `type_` which can be +easily used on Ansible Automation Controller. Each type is then made of +multiple functions, represented by roles, so that the same function used +by the same type can be re-used, written only once. + +## Features (Functions) + +Depending on the complexity and distinct requirements, a use case might +contain one or more features. Features are generally mapped to user +stories in a particular epic in Jira. It is preferred to create a simple +playbook named with a prefix named `function_` for a particular role, +mostly to be used as a ad-hoc script. + +## Helper playbooks (Toolboxes) + +Non use case playbooks and scripts, generally the required for cleanup, +testing etc., are allowed as long as they are explicitly named starting +with `toolbox_` prefix. They are self contained and do not introduce +additional dependencies or blockers for the use case features. + +# Roles + +1. All new roles should be placed under the standard folder `roles`. + +2. New roles should be initiated in line, with the skeleton directory, + which has standard boilerplate code for a Galaxy-compatible Ansible + role and some enforcement around these standards. + +3. All defaults and all arguments to a role should have a name that + begins with the role name to help avoid collision with other names. + +4. Design roles focused on the functionality provided, not the software + implementation. Break down your playbooks into reusable roles. Each + role should have a clear and specific purpose. + +5. All roles have a `meta/main.yml` file with the fields **values** + documented in alignment with + [guidelines\_automation\_header.yml](guidelines_automation_header.yml). + In normal cases, only the `description` should need specific values, + and potentially the `min_ansible_version`. + +6. No empty directories, or directories with useless files. + +7. All roles have a `README.md` file with at least an explanation of + the purpose of the role; variables donโ€™t need to be repeated. The + documentation is essential for the success of the content. Place the + `README.md` file in the root directory of the role. + +8. `defaults/main.yml` contains *all* variables able to influence the + role, together with comments on usage. The variables can be + commented out if their default is to be undefined but they have to + be listed. + +9. All variables defined within a role have a prefix corresponding to + the role name to create a kind of namespace (see also variable names + in link:Inventory and Variables\[Inventory and Variables\]). + +10. Use handlers for tasks that should only run when notified. Handlers + should have clear names and be defined in the handlers section of a + role. + +11. Follow a consistent naming convention for roles. Use lowercase + letters and separate words with underscores. + +12. Documentation: Include a README.md file in each role to document its + purpose, variables, and usage. Document playbook dependencies and + any prerequisites. + +13. Internal variables (those that are not expected to be set by users) + are to be prefixed by two underscores like \`\_\_foo\_variable\`. + +14. Prefix all tags within a role with the role name or, alternatively, + a "unique enough" but descriptive prefix. + +15. Do not use dashes in role names. This will cause issues with + collections. + +16. The role should work in check mode, meaning that first of all, they + should not fail check mode, and they should also not report changes + when there are no changes to be done. + +17. Reporting changes properly is related to the other requirement named + `idempotency`. Roles should not perform changes when applied a + second time to the same system with the same parameters, and it + should not report that changes have been done if they have not been + done. + +18. Add `{{ ansible_managed | comment }}` at the top of the template + file file to indicate that the file is managed by Ansible roles, + while making sure that multi-line values are properly commented. + +19. It is a common practice to have tasks/main.yml file including other + tasks files, which weโ€™ll call sub-tasks files. Make sure that the + tasks' names in these sub-tasks files are prefixed with a shortcut + reminding of the sub-tasks fileโ€™s name. + +# Playbooks + +1. YAML documents (including Ansible playbooks) MUST begin a document + with `---` followed by one empty line. + +2. Files SHOULD use long-form YAML, not short-form YAML. + + person: {name: ansible test, address: {street: somewhere, city: Anycity, state: HE, zip: 12345}} + + use long-format YAML + + + + person: + name: ansible test + address: + street: somewhere + city: Anycity + state: HE + zip: 12345 + +1. Every task, play or block MUST have a name. + +2. Every task, play or block MUST have 1 or more appropriate tags, + annotated as an array instead of a single string. Also document tags + and their purpose(s). + +3. Strings which are *not* builtin parameter triggers like `present` + are enclosed in quotes to make them stand out to the reader. + +4. Use Ansible modules in tasks instead of commands if available. + +5. Use fully qualified collection names (FQCN) to avoid ambiguity in + which collection to search for the correct module or plugin for each + task. + +6. For builtin modules and plugins, use the `ansible.builtin` + collection name as a prefix, for example `ansible.builtin.copy`. + +7. Tasks using the `command` or `shell` module (or, more generally, any + module that doesnโ€™t report if it changed anything) have a + `changed_when` field to indicate if they changed something. If a + tasks merely collects information append `changed_when: false` to + the task. + +8. Use only the `true/false` boolean style, Do *not* use `yes/no` or + `True/False` + +9. No privilege escalation unless absolutely necessary. Using `become` + at the playbook context is strongly discouraged. All roles should + include `become` for tasks that need privileged execution. To keep + the tasks design simple, `block` or `include_tasks` can also be used + depending on the design. + +10. A playbook can contain pre\_tasks, roles, tasks and post\_tasks + sections. Avoid using both roles and tasks sections, the latter + possibly containing import\_role or include\_role tasks. + +11. It is a good practice to enable debug messages only if a higher + level of verbosity is specified while launching the playbook. + + - name: this message will only appear when verbosity is 2 or more + debug: msg: "Some more debug information if needed" verbosity: 2 + +12. Do not use special characters other than underscore in variable + names, even if YAML/JSON allow them. + +13. Use a single space separating the template markers from the variable + name inside all Jinja2 template points. For instance, always write + it as {{ variable\_name\_here }} + +14. Use double quotes for YAML strings with the exception of Jinja2 + strings which will use single quotes. + +15. Always mention the state. For many modules, the `state` parameter is + optional. Different modules have different default settings for + state, and some modules support several state settings. Explicitly + setting `state: present` or `state: absent` makes playbooks and + roles clearer. + +16. Use the serial keyword to control how many machines you update at + once in the batch. + +17. Name your playbooks descriptively, reflecting their purpose or the + infrastructure component they manage. + +18. Define variables in a separate vars section or in separate variable + files. Use meaningful variable names and provide comments for + complex or non-obvious cases. Please refer to the section + link:Inventory and Variables\[Inventory and Variables\]. + +19. Implement proper error handling in your playbooks. Avoid to use the + `ignore_errors` and `failed_when` attributes judiciously. + +# Collections + +1. All required collections should be defined in the `collections` + folder. The best practice is to list all the required collections in + the `requirements.yml` file, and let AAP controller install them as + needed. + +# Inventory and Variables + +1. Define your inventory as structured directory instead of single + file. + +2. Directory Structure include `group_vars` and `host_vars` + directories. + +3. `group_vars` folder is the main location for variable and parameter + files for a given group. The parameters defined here are valid for + all hosts that belong to the relevant group. + +4. `host_vars` folder is the main location for variable and parameter + files for a given host. + +5. All hosts are defined by their FQDNs. + +6. Use dynamic inventory with cloud approach and build smart + inventories if needed. + +7. Variables should written in the separate files that is simplifying + to use by grouping. + +8. Keep vaulted variables safely visible. Encrypt sensitive or secret + variables with Ansible Vault. However, encrypting the variable names + as well as the variable values makes it hard to find the source of + the values. To circumvent this, you can encrypt the variables + individually using `ansible-vault encrypt_string`. + +9. Do not populate the same variable in more than one hierarchy. + +10. Donโ€™t use `extra vars` to define your desired state. Make sure your + inventory completely describes how your environment is supposed to + look like. Use extra vars only for troubleshooting, debugging or + validation purposes. + +11. Avoid playbook and play variables, as well as include\_vars. Opt for + inventory variables instead. + +12. Use parent/child group relationships. It is always good to use + parent/child relationships among groups. Parent groups are also + known as nested groups or groups of groups. + +13. Variables should be prefixed with the related application, Ansible + Role, etc, to bring context and avoid with the name of the role. + +# YAML / Jinja2 Syntax + +1. Indentation should be 2 spaces. Further, arrays (i.e., lines which + begin with -) should be indented 2 spaces from its parent as well. + +2. use two spaces indentation width in YAML files; Do *not* use tabs. + +3. Lines MUST NOT have trailing whitespace. Remove all trailing + whitespaces before you commit a file. + +4. A file MUST end with a single empty line feed. + +5. Files MUST use only UTF-8 without BOM as its character encoding. + +6. Split long expressions into multiple lines. + +7. Code that is no longer needed is removed and not commented out. + +8. Avoid comments in playbooks when possible. Instead, ensure that the + task name value is descriptive enough to tell what a task does. + Variables are commented in the defaults and vars directories and, + therefore, do not need explanation in the playbooks themselves. If + needed, comments MUST have a single space after `\#` for better + readability. + +9. There SHOULD always be one line of whitespace above the start of a + comment block. + +10. When naming files, use the `.yml` extension and not `.yaml` for YAML + files, `.j2` as the file extension for Jinja files. + +11. Use lowercase a-z for naming the files. Use underscore \_ as a + separator, do NOT use dashes. + +12. Do NOT use whitespace as a separator. + +# General Ansible Guidelines + +1. Ensure that all tasks are idempotent. + +2. If there is ever a discrepancy between English-speaking dialects, + use U.S. English as a baseline for both spelling and word choice. + +3. Use the `| bool` filter when using bare variables (expressions + consisting of just one variable reference without any operator) in + when. + +4. Use bracket notation instead of dot notation for value retrieval + (e.g. `item['key']` vs. `item.key`) + +5. Do not use `meta: end_play` as it aborts the whole play instead of a + given host. + +6. Avoid the use of `lineinfile` wherever that might be feasible. + +7. Enable Callback plugins. Callback plugins enable adding new + behaviors to Ansible when responding to events. By default, callback + plugins control most of the output you see when running the command + line programs, but can also be used to add additional output, + integrate with other tools and marshal the events to a storage + backend. + +8. Use tools like `ansible-lint` to check your code for common issues + and adherence to best practices. Maintain consistent code + formatting. + +9. Implement testing for your Ansible code, either manually or through + automated testing frameworks. diff --git a/docs/azure_ansible_integration.md b/docs/azure_ansible_integration.md new file mode 100644 index 0000000..9dc67a2 --- /dev/null +++ b/docs/azure_ansible_integration.md @@ -0,0 +1,27 @@ +# Azure Ansible Integration + +An Azure service principal is a security identity used by user-created +apps, services, and automation tools to access specific Azure resources. +Think of it as a *user identity* (login and password or certificate) +with a specific role, and tightly controlled permissions to access your +resources. It only needs to be able to do specific things, unlike a +general user identity. It improves security if you only grant it the +minimum permissions level needed to perform its management tasks. In our +case we need to create an custom app and grant OWNER access on +subscription level to perform ansible tasks. (Owner role is required to +lock the VM on Azure) These information are stored encrypted in +showroom-inventory repo + +## Create Custom App + +![gitflow](./images/azure_application_register.png) + +## Assign Contributor Role to App + +![gitflow](./images/azure_application_role_assign1.png) + +![gitflow](./images/azure_application_role_assign2.png) + +## Create Secret + +![gitflow](./images/azure_client_secret.png) diff --git a/docs/azure_rhib_integration.md b/docs/azure_rhib_integration.md new file mode 100644 index 0000000..520b9b8 --- /dev/null +++ b/docs/azure_rhib_integration.md @@ -0,0 +1,178 @@ +# Red Hat Image Builder + +Insights image builder bundled with Insights enables you to create +customized images and upload the image to the target cloud environments. +Insights image builder is a tool bundled with Red Hat Insights within +Red Hat Hybrid Cloud Console. + +During the bootstrap part of the infrastructure we aim to provision base +images using Insights Image builder and upload these golden images to +the public cloud provider Azure. Automation will cover building +customized system images, that can have a subscription activation key +already embedded and uploading these images to the Azure. Custom RHEL +images are created and used to build VMs for building core Red Hat +Management Infrastructure before having Red Hat Satellite provisioning +feature. To be able to upload these images to the Azure Red Hat Insights +Image Builder requires authorization from the Azure, and the document +covers how to grant this authorization. + +# Red Hat Management Core Infrastructure + +We can provision VMs on Azure (for any public provider or on-prem +baremetal systems) by using Red Hat Insights Image Builder tool to build +the core Red Hat Management Infrastructure. + +We aim to provision the services below with this approach; + +- Jumpstart/ Bastion (RHEL 9) + +- PKI for root Certificate Authority (RHEL 9) + +- RHIdM VMs (RHEL 9) + +- Red Hat Satellite Server (RHEL 8) + +# Build Integration between Red Hat Hybrid Cloud Console and Azure + +1. Ensure you have `Azure Tenant GUID` and `Subscription ID` to use + +2. Access to the + [Integrations](https://console.redhat.com/settings/integrations) + +3. Click `Add Integration` + +4. Select `Microsoft Azure` and click `Next` + +
+ azure_rhib5.png +
+ +5. Type a name for the integration like `azure_integration` and click + `Next` + +
+ azure_rhib6.png +
+ +6. Select `Launch images` from the list and click `Next` + +
+ azure_rhib7.png +
+ +7. Click `Take me to Lighthouse` to be able to configure Azure for + allowing this integration. + +
+ azure_rhib8.png +
+ +8. Review and confirm `Subscription`, `Region`, + `` Msp Offer Name`and `Msp Offer Description `` on the screen. If + they seem correct click `Review + Create` + +
+ azure_rhib10.png +
+ +9. Review and confirm Azure Marketplace Terms and click `Create` + +
+ azure_rhib11.png +
+ +10. Return to the previous window and type Azure `Subscription ID` on + the screen and Click `Next` + +
+ azure_rhib9.png +
+ +11. Review and confirm the information and click `Add` + +Red Hat Hybrid Cloud Console is integrated with Azure as a source. + +# Authorize Image Builder + +1. Ensure that Red Hat Insights subscription exist your account. Red + Hat Insights is included with your Red Hat Enterprise Linux + subscription. + +2. Access to the [Insights image + builder](https://console.redhat.com/insights/image-builder/) + +3. Log in with your Red Hat credentials. + +4. From the images select `Create Blueprint` + +5. Select `Azure` as a public cloud provider and click `Next` + +
+ azure_rhib1.png +
+ +6. Select `Use an account configured from Sources.` option, and select + the source name which was configured on the previous step. + +
+ azure_rhib2.png +
+ +7. Click `Authorize image builder` to authorize Insights image builder + to push images to the Microsoft Azure which will redirect you to the + Azure portal where you can grant permissions by clicking `Accept`. + You need to have `Owner` or `User Access Administrator` role and + subscription must include `Microsoft.Storage` and + `Microsoft.Compute` as a resource provider. + +8. Confirm that Insights Image Builder is authorized for your tenant. + + 1. Search for `Azure Entra ID` and choose **Enterprise + applications**, from the left menu. + + 2. Search for `Red Hat Image Builder` and confirm it is authorized. + +
+ azure_rhib3.png +
+ +9. Add the **Enterprise application** as a contributor to your + `Subscriptions`. + + 1. In the search bar, type `Subscriptions` and select the + subscription for the project. This redirects you to the + `Subscriptions` dashboard. + + 2. Select your `Subscription`. + + 3. On the left menu, click `Access control (IAM)` to add a + permission so the `Insights image builder` application can + access to all your resource groups. + +
+ azure_rhib4.png +
+ + 4. From the menu, click the tab `Role assignments`. + + 5. Click `Add`. + + 6. From the dropdown menu, choose `Add role assignment`. A menu + appears on the left side. + + 7. Insert the following details: + + 1. Role: Assign the `Contributor role` + + 2. Assign access to: User, group, service principal. Add + members: Click `Select members` and type `Red Hat` in the + search bar. Press enter. + + 3. Select: `Red Hat Image Builder` application + +The Insights Image Builder application is now authorized to push images +to Microsoft Azure cloud. + +Detailed information provided by the official documentation in [Red Hat +Image +Builder](https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/8/html-single/creating_customized_images_by_using_insights_image_builder/index#creating-and-uploading-customized-rhel-system-image-to-azure-using-image-builder) diff --git a/docs/azure_start_stop_function.md b/docs/azure_start_stop_function.md new file mode 100644 index 0000000..4ba484e --- /dev/null +++ b/docs/azure_start_stop_function.md @@ -0,0 +1,200 @@ +# Start stop VM's + +In this project, weโ€™re going to automate the code for building Red Hat Management Tools to manage and build a Red Hat based environment. Since +the environment isnโ€™t going to be used for productive workloads, weโ€™ve decided to use a built-in functionality from the Microsoft Azure +Marketplace called [start/stop VMs v2](https://learn.microsoft.com/en-us/azure/azure-functions/start-stop-vms/overview#overview). +This will help decrease cloud costs for non-development time slots in the project, such as evenings and weekends. + +The `Start/Stop VMs v2` feature starts or stops Azure Virtual Machines instances across multiple subscriptions. + +Weโ€™ve decided to take a different approach to the automation code development for this project. While this isnโ€™t the main requirement, weโ€™ve agreed to exclude this part and cover it with manual steps described in this document. + +We have planned schedules for weekdays only that will start VMs automatically at 6:00 AM and shutdown at 7:00 PM (Europe/Berlin time zone). + +# Deploy Start/Stop VMs v2 + +We followed mainly the official documentation for the [start/stop VMs v2](https://learn.microsoft.com/en-us/azure/azure-functions/start-stop-vms/overview#overview) function with great enthusiasm! + +It is recommended to use separate resource group for deploying this application as it will create additional resources required for this function to work. + +1. Deploy function from the marketplace. + +![Azure Marketplace for Start / Stop Function](images/azure_start_stop1.png){ align=centre } + +2. Provide resource group, function, storage account and application insights names. + +![Configure Start/Stop Function](images/azure_start_stop2.png){ align=centre } + +3. After Deployment configure schedule for the sequenced stop. + +![Sequenced Schedule for Stop Function](images/azure_start_stop3.png){ align=centre } + +4. Configure schedule for the sequenced start. + +![Sequenced Schedule for Start Function](images/azure_start_stop4.png) + +5. Correct subscription information in the `Logic app code view` for the logic applications named `ststv2_vms_Sequenced_stop` and `ststv2_vms_Sequenced_start` + +![Update subscription information](images/azure_start_stop5.png){ align=centre } + +6. Enable Azure Function for the logic applications named `ststv2_vms_Sequenced_stop` and `ststv2_vms_Sequenced_start` + +# How to Configure VMs + +Sequenced - Start and stop actions are based on a schedule targeting VMs +with pre-defined sequencing `tags`. Only two named tags are supported - +`sequencestart` and `sequencestop`. ststv2\_vms\_Sequenced\_start and +ststv2\_vms\_Sequenced\_stop configure the sequenced start and stop. + +The proper way to use the sequence functionality is to create a tag +named `sequencestart` on each VM you wish to be started in a sequence. +The tag value needs to be an integer ranging from 1 to N for each VM in +the respective scope. The tag is optional and if not present, the VM +simply wonโ€™t participate in the sequencing. The same criteria applies to +stopping VMs with only the tag name being different and use +`sequencestop` in this case. You have to configure both the tags in each +VM to get start and stop action. If two or more VMs share the same tag +value, those VMs would be started or stopped at the same time. + +For example, the following table shows that both start and stop actions +are processed in ascending order by the value of the tag. + + +++++ + + + + + + + + + + + + + + + + + + + +
VM NameTagsAction Order

VM 1

sequence start: 1 / sequence stop: +2

Start: VM1, VM2

VM 2

sequence start: 2 / sequence stop: +1

Stop: VM2, VM1

+ +## VM Tags + +Tags must be set on the VMs to include or exclude specific VMs from +start and stop actions. Add a tag named `ssv2excludevm` in the +configuration for the VM. To exclude this VM from the start or stop +action, set the value of this new tag to `true`. To include the VM in +the action, set the value to false or donโ€™t use any exclusion tag. + +# VMs sequence Order + + +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
VM NameStart SequenceStop Sequence

RHIdM (Primary)

1

5

RHIdM (Replica)

NBDE (Primary)

NBDE (Replica)

RH Satellite

2

4

RH Keycloak

RH Bastion

3

3

AAP PGSQL

AAP Controller

4

2

AAP PAH

AAP EE

AAP HOP

AAP EE on DMZ

Capsule on DMZ

Workload VMs

5

1

Workload VMs on DMZ

RootCA

ssv2excludevm

diff --git a/docs/branch_standards.md b/docs/branch_standards.md new file mode 100644 index 0000000..15e118e --- /dev/null +++ b/docs/branch_standards.md @@ -0,0 +1,106 @@ +# Branch Standards + +GitHub helps us manage our codebase efficiently by employing a +structured branch management strategy that aligns with our diverse use +cases. These branches ensure smooth coordination among development teams +and contributors. + +In this project branching strategy tries to emulate Github flow. Project +contains two repositories; + +- Code repository for roles, collections, playbooks + +- Inventory repository for managed infrastructure and clients; + parameters for infrastructure and client groups, as well as global + parameters + +## Branch Naming Convention + +- Feature Branches: feature-<Jira ticket number> + +- Bugfix Branches: bugfix-<Jira ticket number> + +- Hotfix Branches: hotfix-<Jira ticket number> + +Contributors create their feature branches with Jira ticket number. As +most features are expected to introduce new parameters in the inventory, +the common approach is to create a similar named branch in both +repositories (i.e. code and inventory). + +In both repositories: + +- production branch (`main`) is locked to be updated manually. It can + be updated only thru Pull Request(PR) from development branch. The + main branch should always reflect the production-ready state of the + codebase. It should only contain code that has been thoroughly + reviewed, tested, and approved for deployment. + +- development branch(`devel`) serves as the main integration branch + where features are merged before being deployed to production. + Developers should base their feature branches off the development + branch. + +- feature branches should be self-contained. They need to be tested on + their own before merging to development. feature branches can be + merged to development thru Pull Request(s) following a review for + the request. + +- bugfix branches should address issues found during testing and + development. These branches should be tested and merged into + development branch. + +- hotfix branch can be created directly from the master branch in the + event of a critical bug or issue in production. Hotfixes should be + kept minimal to reduce the risk of introducing new problems. Once + the hotfix is completed, it should be merged into both the master + and development branches. + +![gitflow](images/branch_standard.jpg) + +## Pull Requests + +When a developer completes work on a feature branch, they create a pull +request (PR) to merge their changes into the development branch. Usually +two merge requests are required, one for the code and one for the +inventory repository. The PR should include a detailed description of +the changes made, any relevant screenshots or documentation updates, and +any associated issue numbers. Other team members review the code, +provide feedback, and discuss any necessary changes before the PR is +merged. All conversations must be resolution before merging. + +## Code Reviews + +Code reviews are essential for maintaining code quality and ensuring +consistency across the codebase. Reviewers should focus on readability, +maintainability, efficiency, and adherence to coding standards. +Constructive feedback should be given with the goal of improving the +code. It is expected that the branch has been re-based against the +target branch as to avoid conflicts resolution for the reviewer(s). 2 +reviewers are required to approve pull requests. Approvals will be +discarded for the PR if a new commit push exists after review and +approval. + +## Continuous Integration (CI) + +CI systems automatically build and test code changes whenever a new +commit is made or a PR is opened. Automated tests should be run to +ensure that new code does not introduce regressions or break existing +functionality. CI checks should pass before a PR can be merged. + +## Pull Request Merge + +Pull requests are merged automatically in development branch(`devel`) +when; + +* approval of 2 reviewers exist +* all conversations are resolved +* all checks have passed + +## Branch Rule Sets + +Branch rule sets are configured to align with our branching standards on +GitHub and detailed configuration is accessible by the repository +ruleset links below: + +* [showroom-code repository rulesets](https://github.com/showroom-project/showroom-code/rules) +* [showroom-inventory repository rulesets](https://github.com/showroom-project/showroom-inventory/rules) diff --git a/docs/configure_workstation_for_bastion.md b/docs/configure_workstation_for_bastion.md new file mode 100644 index 0000000..4e0c6f6 --- /dev/null +++ b/docs/configure_workstation_for_bastion.md @@ -0,0 +1,60 @@ +# Workstation configuration + +This document aims to provide information how to setup your workstation +to access to the resources in the project. You already should have SSH +public and private keys in place created by using the command +`ssh-keygen` on your workstation and also one on the bastion workstation +VM. If not done yet, please follow the [Red Hat +documentation](https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/9/html/configuring_basic_system_settings/assembly_using-secure-communications-between-two-systems-with-openssh_configuring-basic-system-settings#generating-ssh-key-pairs_assembly_using-secure-communications-between-two-systems-with-openssh) +to generate one with elliptic curves like ECDSA. + +## Configure SSH Access + +Please add following configuration on you SSH Config file which is +usually `~/.ssh/config`. + +``` bash title="File ~/.ssh/config" +# Bastion Config +Host + HostName + IdentityFile ~/.ssh/id_ecdsa + port 22 + ServerAliveInterval 300 + ServerAliveCountMax 2 + User + UserKnownHostsFile /dev/null + +# SSH Clients +Host *. + ProxyJump + User + IdentityFile ~/.ssh/id_ecdsa + ServerAliveInterval 300 + ServerAliveCountMax 2 + UserKnownHostsFile /dev/null +``` + +This configuration will allow you to access all the resources in a +protected environment over SSH using Bastion Jumphost VM like in your +local network. + +## Configure WebUI Access + +The following configuration will help you to configure your workstation +access to the project resources with WebUI using a proxy on the Bastion +Jumphost VM. When configured you can access to the resources using their +local IP addresses or fully qualified domain name. + +It is recommended to use a browser extension for automatically switching +between the proxies or direct access based on the rules for not +affecting your existing access definitions. The web resources in the +project could not exposed on Internet which you can not directly access +without using the configuration steps below. + +1. Deploy browser extension named [`Switchy Omega Extension`](https://github.com/FelisCatus/SwitchyOmega). +2. Open the extension Options to configure a new profile with a name `showroom-bastion` and `Proxy Profile` option. +3. Navigate to the newly created profile and add `__` to the servers list with a proxy port number `3128` for HTTP access in default scheme. +4. Navigate to the ห™auto switchห™ profile and add add switch rules to use with `Condition Type` equal `Host Wildcard`, `Condition Details` equal `__` and `profile` equal to `showroom-bastion`. +5. Ensure from the browser extension that `auto switch` profile set as default. + +This setup will allow you to access WebUI's of the any application on the showroom project. diff --git a/docs/cost_estimation.md b/docs/cost_estimation.md new file mode 100644 index 0000000..e022bf8 --- /dev/null +++ b/docs/cost_estimation.md @@ -0,0 +1,367 @@ +# Cost estimation +This document covers draft high level estimation of the expected monthly +costs for the project resources on Azure. + +## Microsoft Azure Estimate + +!!! quote + All prices shown are in Euro Zone โ€“ Euro (โ‚ฌ) EUR. This estimate was created at 4/26/2024 1:18:50 PM UTC. + + 1. Estimation done for a monthly of 300 hours to use. + 2. The system disks use mainly Standard SSD. + 3. One Region used only for lowering bandwith requirements. + + + +++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Service categoryService typeRegionDescriptionEstimated monthly costResource Name

Compute

Virtual Machines

Germany West Central

1 E4as v5 (4 vCPUs, 32 GB RAM) x 300 +Hours (Pay as you go), Linux, (Pay as you go); 1 managed disk โ€“ E6; +Inter Region transfer type, 5 GB outbound data transfer from Germany +West Central to East Asia

โ‚ฌ 80.34

Red Hat Satellite Server

Compute

Virtual Machines

Germany West Central

1 E2as v5 (2 vCPUs, 16 GB RAM) x 300 +Hours (Pay as you go), Linux, (Pay as you go); 1 managed disk โ€“ E15; +Inter Region transfer type, 5 GB outbound data transfer from Germany +West Central to East Asia

โ‚ฌ 55.69

Red Hat Satellite Capsule +Server

Compute

Virtual Machines

Germany West Central

2 D2as v5 (2 vCPUs, 8 GB RAM) x 300 +Hours (Pay as you go), Linux, (Pay as you go); 2 managed disks โ€“ E4; +Inter Region transfer type, 5 GB outbound data transfer from Germany +West Central to East Asia

โ‚ฌ 62.06

Red Hat IdM Server

Compute

Virtual Machines

Germany West Central

3 B2pls v2 (2 vCPUs, 4 GB RAM) x 300 +Hours (Pay as you go), Linux, (Pay as you go); 3 managed disks โ€“ E4; +Inter Region transfer type, 5 GB outbound data transfer from Germany +West Central to East Asia

โ‚ฌ 38.56

Red Hat Test VMs

Compute

Virtual Machines

Germany West Central

1 B2als v2 (2 vCPUs, 4 GB RAM) x 10 +Hours (Pay as you go), Linux, (Pay as you go); 1 managed disk โ€“ E4; +Inter Region transfer type, 5 GB outbound data transfer from Germany +West Central to East Asia

โ‚ฌ 2.62

rootCA

Compute

Virtual Machines

Germany West Central

1 DC2s v3 (2 Cores, 16 GB RAM) x 300 +Hours (Pay as you go), Linux, (Pay as you go); 1 managed disk โ€“ P6; +Inter Region transfer type, 5 GB outbound data transfer from Germany +West Central to East Asia

โ‚ฌ 42.23

Postgresql DB Server

Compute

Virtual Machines

Germany West Central

1 E4-2s v3 (2 vCPUs, 32 GB RAM) x 300 +Hours (Pay as you go), Linux, (Pay as you go); 1 managed disk โ€“ E6; +Inter Region transfer type, 5 GB outbound data transfer from Germany +West Central to East Asia

โ‚ฌ 88.65

AAP Controller

Compute

Virtual Machines

Germany West Central

1 B2ps v2 (2 vCPUs, 8 GB RAM) x 300 +Hours (Pay as you go), Linux, (Pay as you go); 1 managed disk โ€“ E6; +Inter Region transfer type, 5 GB outbound data transfer from Germany +West Central to East Asia

โ‚ฌ 25.71

AAP Private Hub

Compute

Virtual Machines

Germany West Central

1 B2ps v2 (2 vCPUs, 8 GB RAM) x 300 +Hours (Pay as you go), Linux, (Pay as you go); 1 managed disk โ€“ E4; +Inter Region transfer type, 5 GB outbound data transfer from Germany +West Central to East Asia

โ‚ฌ 23.49

AAP EE

Compute

Virtual Machines

Germany West Central

1 B2ps v2 (2 vCPUs, 8 GB RAM) x 300 +Hours (Pay as you go), Linux, (Pay as you go); 1 managed disk โ€“ E4; +Inter Region transfer type, 5 GB outbound data transfer from Germany +West Central to East Asia

โ‚ฌ 23.49

RH Build of Keycloak Server

Compute

Virtual Machines

Germany West Central

1 B2als v2 (2 vCPUs, 4 GB RAM) x 300 +Hours (Pay as you go), Linux, (Pay as you go); 1 managed disk โ€“ E4; +Inter Region transfer type, 5 GB outbound data transfer from Germany +West Central to East Asia

โ‚ฌ 14.18

Tang Server

Compute

Virtual Machines

Germany West Central

1 B4ps v2 (4 vCPUs, 16 GB RAM) x 300 +Hours (Pay as you go), Linux, (Pay as you go); 1 managed disk โ€“ P6; +Inter Region transfer type, 5 GB outbound data transfer from Germany +West Central to East Asia

โ‚ฌ 53.03

Bastion / Jumphost

Storage

Storage Accounts

Germany West Central

Managed Disks, Standard SSD, LRS +Redundancy, E4 Disk Type 3 Disks, 100 Storage transactions

โ‚ฌ 6.83

Storage

Storage Accounts

Germany West Central

Block Blob Storage, General Purpose V2, +Hierarchical Namespace, LRS Redundancy, Hot Access Tier, 1,000 GB +Capacity - Pay as you go, 100 x 10,000 Write operations, 100 x 10,000 +Read operations, 10 x 10,000 Iterative Read operations, 10 x 100 +Iterative Write operations, 1,000 GB Data Retrieval, 1,000 GB Data +Write, SFTP disabled, 1,000 GB Index, 1 x 10,000 Other +operations

โ‚ฌ 51.66

Networking

Azure Virtual Network Manager

1 Managed suscriptions x 730 +Hours

โ‚ฌ 67.41

Networking

Virtual Network

Germany West Central (Virtual Network +1): 1 TB Outbound Data Transfer; Germany West Central (Virtual Network +2): 1 TB Outbound Data Transfer

โ‚ฌ 37.83

Networking

IP Addresses

Germany West Central

Basic (Classic), 0 Dynamic IP Addresses +X 730 Hours, 15 Static IP Addresses X 730 Hours

โ‚ฌ 36.40

Networking

Azure DNS

Zone 1, DNS, Public; 1 hosted DNS zone, +1 DNS query

โ‚ฌ 0.83

Networking

Azure Firewall

Germany West Central

Standard tier, 0 Logical firewall units +x 730 Hours, 0 GB Data processed

โ‚ฌ 0.00

Networking

Load Balancer

Germany West Central

Standard Tier: 0 Rules, 0 GB Data +Processed

โ‚ฌ 0.00

Security

Key Vault

Germany West Central

Vault: 50 operations, 50 advanced +operations, 3 renewals, 5 protected keys, 0 advanced protected keys; +Managed HSM Pools: 0 Standard B1 HSM Pool(s) x 730 Hours

โ‚ฌ 13.10

Compute

Azure Functions

Germany West Central

Consumption tier, Pay as you go, 128 MB +memory, 100 milliseconds execution time, 0 executions/mo

โ‚ฌ 0.00

Management and governance

Microsoft Cost Management

No charge for managed Azure spend. 0 +Managed AWS spend per month

โ‚ฌ 0.00

Management and governance

Azure Backup

Germany West Central

Azure VMs, Standard Backup policy, 1 +Instance(s) x 0 GB, LRS Redundancy, Low Average Daily Churn, 0 GB +Average monthly backup data in Standard Tier, 0 GB Average monthly +backup data in Archive Tier

โ‚ฌ 0.00

Networking

Azure DNS

Zone 1, DNS, Private; 0 hosted DNS +zones, 0 DNS queries

โ‚ฌ 0.00

Support

Free level

Support

โ‚ฌ 0.00

Licensing Program

Billing Account

Billing Profile

Total

โ‚ฌ 724.13

diff --git a/docs/general_guidelines.md b/docs/general_guidelines.md new file mode 100644 index 0000000..f19748a --- /dev/null +++ b/docs/general_guidelines.md @@ -0,0 +1,201 @@ +# Azure cost estimation + +This document explains general guidelines for the project to work +together. We all have agreed to work with Agile methodology and also +using Open Practices in the project. Social contract is the important +part for building open framework between people in the project. + +![Social Contract](./images/social_contract.jpg){ align=centre } + +# User Stories + +We use internal JIRA to follow-up user stories, tasks, epics in the +project with agile approach. See +[JIRA](https://issues.redhat.com/secure/RapidBoard.jspa?rapidView=19625&projectKey=SHWRM&view=planning&issueLimit=100) +for the user stories and planning. + +## Definition of Ready + +### Independent + +Whatever backlog item youโ€™re working on must not depend on any other +task. It must be self-contained. Your team will avoid any unnecessary +work this way. + +### Negotiable + +A task shouldnโ€™t be rigid. You must be flexible enough to consider other +options the team might bring. + +### Valuable + +There must be a purpose to your work. More importantly, it must add +value to the product, the customer, and the business. + +### Estimable + +The task must be feasible, achievable, and measurable. Your team needs +to know how much time and effort you will require of them. If the sprint +requires multiple tasks, the same goes for each. + +### Small + +The work must be manageable. If a task is complex, you should be able to +break it down into smaller ones. Doing so prevents fire drills and +working in overdrive to meet unreasonable deadlines. And your team wonโ€™t +burn out. + +### Testable + +Specify the success and completion criteria based on business and user +needs. These allow your team to evaluate whether the task is complete. + +## Definition of Done + +1. Keep it Simple! + +2. MRs reviewed at least by 2 persons - 4 eyes principle. + +3. All issues solved within Merge request Review. + +4. Merge requests points jira tickets. + +5. All pipelines passed successfully. + +6. All Ansible code written idempotent allows same results when + executed more than once. + +7. Test steps clearly described and proof attached. + +8. Documented clearly. + +## Backlog Guidelines + +1. All "STORIES" and "TASKS" are associated with an "EPIC" type issue + that logically represents all that needs to be down under a specific + context. + +2. "STORY" type issues shall be used to represent direct value to the + customer. As such the persona of such issue is always the customer. + +3. "TASK" type issues shall be used to represent either admin-like + tasks or technical tasks that do not represent direct value to the + customer. + +4. "SUB-TASK" type issues shall be used break down stories or tasks. Do + keep in mind they do not appear directly in the backlog. Use them as + a way to organize the issues you own. + +5. The pre-fix \[P\] present on the name of an issue in JIRA, means + that the issue is "Prepared" for refinement and can thus be reviewed + by the team. + +6. The pre-fix \[R\] present on the name of an issue in JIRA, means + that the issue is "Refined/Ready" to be taken into a sprint. + +7. Any member of the Core Implementation team can and is encouraged to + add any issues that they feel relevant to the backlog for future + refinement. + +8. Backlog Items will be estimated by complexity with the usage of the + Fibonacci sequence. + +9. Prioritization of the backlog is of the sole responsibility of the + acting PO (Josรฉ Costa) and the backlog should be prioritized as part + of the refinement and planing events. + +# Git Cheat Sheet + +A quick introduction into the proposed git workflow can be found [Git +Cheat Sheet for Developers](git_cheat_sheet.md). For the structuring +of contents inside of your branch, please have a look at the automation +link below. + +# Branch Standards and Review Process + +See [Branch Standards](branch_standards.md), to learn more about how +to work with git branches. + +# IDE + +You can you any IDE you like in the project but we strongly recommend to +use `VSCode` to benefit already developed extensions that will make your +life easier. It is good to use VSCode with a remote SSH support using +Bastion to access to the project infrastructure resources. Using +ansible-lint on your local or remote Bastion VM would be beneficial +before you push your commits to GitHub. + +Following are the recommended Extensions on VSCode to use. + +- `Ansible` +- `AsciiDoc` +- `Git History` +- `Git Graph` +- `GitHub Actions` +- `GitHub Pull Requests` +- `GitHub Repositories` +- `Markdown Theme Kit` +- `Red Hat Authentication` +- `Red Hat Commons` +- `Remote - SSH` +- `Remote - SSH Editing Configuration Files` +- `Remote Development` +- `Remote Explorer` +- `Trailing Spaces` +- `TODO Highlight` +- `YAML` + +Ansible Lightspeed can be used for the code assistance with VSCode as well, refer to the [Ansible VS Code extension](https://docs.ai.ansible.redhat.com/vscode_guide/installing_vs). + +# Documentation + +Write your documentation in a way that keeps it readable in the editor +*and* in its rendered form. + +We generally use [Asciidoc](https://asciidoctor.org/) or [Markdown](https://www.markdownguide.org/) for documentation and we place it under revision control in our Git as near as possible to the code it documents. Readme-files in roles and inventories, however, are written in markdown (see note below). Create a `README.adoc` in each applicable directory to describe the content / structure of the repository, because GitHub will render it when opening the directory. + +Within inventories we use markdown `README.md` files. Ansible doesnโ€™t ignore Asciidoc files when parsing inventories and will produce a warning. + +Independent of the choice of markup language, the following rules apply: + +1. Use (inline) code highlighting for + 1. file names + 2. variable names and other Ansible objects such as tags, role names, module names, etc. + 3. command line examples + 4. host names +2. Code block delimiters specify the language. For example, a YAML block is started with `[source,yaml]` in Asciidoc (\`\`\`\`yaml\` in + Markdown). Use `bash` highlighting for example Ansible cli commands +3. Do not use spaces in front of list item indicators such as `\*` and `-` +4. Code blocks are marked explicitly (four or more dashes in Asciidoc,three back-ticks in MD). Do *not* use indentation to mark them as code blocks. +5. Internal links to files in the code repository are linked directly and not through version control system address. Use `+link:file.adoc[descriptive name]+` instead of the full static URL including server name etc. +6. Do NOT link *directly* to the `README.adoc` files but to the directory containing it, it makes it easier to navigate in GitHub. +7. Add `TODO:` in the document if there is any sections needs to done later to not forget. + +# Ansible Code Bot + +The Ansible code bot scans existing content collections, roles, and +playbooks hosted in GitHub repositories, and proactively creates pull +requests whenever best practices or quality improvement recommendations +are available. + +Ansible code bot scans our repositories to recommend code quality +improvements. It promotes Ansible best practices while avoiding common +errors that can lead to bugs or make code harder to maintain. + +We agreed to enable Ansible code bot on our GitHub repository to benefit +from the AI to improve our code during the development stages. Scheduled +scans are configured on a weekly basis +[Ansible Code bot](../.github/ansible-code-bot.yml) + +Installing and Configuring Ansible Code Bot explained in the [official +Red Hat +documentation](https://access.redhat.com/documentation/en-us/red_hat_ansible_lightspeed_with_ibm_watsonx_code_assistant/2.x_latest/html/red_hat_ansible_lightspeed_with_ibm_watsonx_code_assistant_user_guide/using-code-bot-for-suggestions_lightspeed-user-guide#doc-wrapper). + +# GitOps with Ansible Approach + +Details for GitOps with Ansible and Infrastructure as a Code approach +are explained in [GitOps with Ansible](gitops_ansible.md) document. + +# Configure Local Workstation to use Bastion + +To configure your local workstation to be able to work remotely see [Configure Local Workstation to use Bastion](configure_workstation_for_bastion.md) diff --git a/docs/git_cheat_sheet.md b/docs/git_cheat_sheet.md new file mode 100644 index 0000000..6eaaf2f --- /dev/null +++ b/docs/git_cheat_sheet.md @@ -0,0 +1,245 @@ +# Git cheat sheet +This document helps you to get started on the version control system +workflow of the Showroom project. In here you will find helpful commands +and an explanation of every step from cloning the repository and finally +adding your contents to the project. + +For a detailed description on how to use branches, how to work with +merge requests, and what tags are used how in this project, please refer +to [Branch standards](branch_standards.md). + +## Getting started + +First of all you have to clone the Showroom project to be able to start +the development process. If you want to use a graphical tool for coding, +you should have already installed one that you like. Cloning the +Showroom project with your favorite IDE isnโ€™t a part of this guide but +you better setup and use IDE. The cheat sheet will help you to get +things going on the command line only. + +## Git Knowledge + +If you are not familiar with using Git, you can learn it more in detail +using from [Git - SCM Guide](https://git-scm.com/book/en/v2/Getting-Started-What-is-Git%3F) and also from [Git Guide](https://rogerdudler.github.io/git-guide) + +### Prerequisites + +You need to have access on the dedicated Showroom Git instance. Create +an issue on if you donโ€™t have +an account or permission on there. + +To be able to clone a git repository, the git binary is a core +requirement. Please make sure you have it available on your local +operating system. + +A smooth workflow with the remote git instance requires you to add one +of your public keys for authentication with the server. If you donโ€™t +have a key already, please follow the [Git +guidelines](https://docs.github.com/en/authentication/connecting-to-github-with-ssh). + +### Clone the project + +Open your preferred command line tool to get started. + +Open the link of the Git instance. You will get a list of projects where +you are currently assigned at least as a project member. Click on the +`showroom-code` project. This project represents the base of the +Showroom project development. There is another repository contains +Ansible inventories which will fed the base Showroom project with +variables based on the surrounding data center location. + +Click the `Clone` button at the top right corner to retrieve the +appropriate link. Copy the link which is labeled as `Clone with SSH`. Go +to your command line and type in the following: + + git clone "link that you have just copied" + +Press enter and the repository will now be cloned to your computer. + +## Explore the environment + +### Show ongoing work + +After you have cloned the repository, you can give yourself a quick +overview about work that is currently ongoing within the repository. Per +default, only the `main` branch will be cloned to your computer. If you +want to access the work of your colleagues, enter and execute: + + git fetch --all + +Now you can execute + + git branch -r + +and get a list of all of the branches that are currently in progress and +not merged into the master yet. + +## Make changes + +### Create a branch + +If you want to contribute changes and additions to the repository, you +have to open up your own branch based on the current `devel` branch. To +ensure you have the latest changes on the `devel` branch present on your +local copy, type in the following commands: + + git checkout devel + git pull + +Based on the latest changes, you can create your own branch with the +following command. Be sure to follow the branch naming conventions. +Otherwise the your changes wonโ€™t be accepted later on. + + git checkout -b "name of your branch" + +The command fulfills two steps in one. At first git is going to create +the branch you have requested and this branch will also be checked out +directly so that you donโ€™t have to switch to the branch manually. + +You can now modify the contents of the Showroom project within your +repository. + +### Track changes + +The tracking of your modifications is essential. Otherwise you canโ€™t +publish them into the `devel` branch. You can get a list of changed or +untracked files with + + git status + +Adding of new or modified files requires you to execute + + git add . + +Keep in mind that this command adds all of your changes to the current +set of changes. If you want to exclude some of the files, you have to +add them manually via + + git add "file name" + +### Commit changes + +To add your changes into the git tree, you have to bundle them with a +commit message. This step can be done by executing + + git commit -m "your commit message" + +Be sure to follow the guidelines on how to write proper commit messages. +Your changes might be rejected by one of the reviewers if you donโ€™t +follow these constraints. You are able to change the message of your +commits later on via the `git rebase` command. Since this operation is +very error prone, always try to follow the guidelines in the first shot. + +1. Prefix relevant parts of commit messages with one `Feature:` or + `Hotfix:` so that we can filter for it and generate + half-automatically release notes. + + - Feature: for any new feature, tell what needs to be done to + profit from it. + + - Bugfix: if itโ€™s a bug fix. + +### Publish changes + +You can now push your changes to the remote repository. This operation +will publish your work to be accessible by all of the other colleagues. +Per default the remote is called `origin`. Feel free to modify it if you +have renamed the repository or added multiple ones: + + git push origin "name of your branch" + +## Squash commits + +Squashing commits means you take multiple commits and squash them into +one. This is typically best done before creating a merge request such +that the branch is clean and has just one commit, even if you created a +number of commits before creating the MR. It is also useful in case you +want to change history of your branch, for example if you pushed a large +file and then deleted it. If you donโ€™t squash your commits, the file +will stay in the git history, even if it isnโ€™t on your branch. If you +squash your commits, the file will never be pushed to git, because the +changes cancel each other out (commit 1 added a big file, commit 2 +removed the big file โ†’ squashing the two commits means thereโ€™s no file +in the branch). + +### Before squashing + +It is good to create a local backup branch: +`git checkout -b local-bkp; git checkout -` + +The command will create a new branch and return you to your branch. + +### Squashing + +1. Find how many commits are there on your branch, for example using + `git log --oneline`. + +2. Issue interactive rebase: `git rebase -i HEAD{tilda}$num_of_commits` + where `$num_of_commits` is a number of commits you want to squash. + If you want to squash 3 commits, it would be + `git rebase -i HEAD(tilda)3`. + +3. In the next screen, keep the first line as is and change the first + word of the next lines from `pick` to `squash`: + + # original state + pick bd52ec1 Ignore Vim swap files + pick f185eb8 managed_os: Add new feature to the role to set hostname on server + pick f4dcf78 managed_os: minor changes based on MR feedback + + # Change all commits to squash except for the first one + pick bd52ec1 Ignore Vim swap files + squash f185eb8 managed_os: Add new feature to the role to set hostname on server + squash f4dcf78 managed_os: minor changes based on MR feedback + + # After the above is done, save and exit your editor, e.g. :wq or :x in VIM + +4. In the next screen, change your commit messages. Delete all + non-commented files and input a new commit message, which will be + the new commit message for your squashed commits. + +### After squashing + +At this point, you have changed your history. Check the git diffs to see +whether you have a commit with changes that you wanted. For example, use +`qgit` on Fedora to see visual representation of your diffs. + +If you want to start again, you can restore the branch to your original +state: `git reset --hard local-bkp`. + +If the state is as desired, you can now push your changes into your +branch. + +To push, execute: `git push origin +$branch_name`. This will issue a +force push. For example, if your branch is `feature-xyz`, issue +`git push origin +feature-xyz`. + +If you created a backup branch, you can now safely delete it: +`git branch -D local-bkp`. + +### Merge changes + +Merge requests (pull requests) are the only way of adding your work into +the `devel` branch. Therefore you have to create a merge request on the +version control system. This request will be reviewed and approved by at +least another person to ensure a good level of code quality. + +Open the start page of the version control system, select the Showroom +project and click on `Pull Requests` on the left side of the page. The +newly loaded page allows you to open a `New Pull Request` at the top +right corner. There you have to select the source branch and target +branch. The source branch represents the changes that you have pushed +previously into the remote repository. As target branch the `devel` +branch should already be selected. Fill out the required fields in the +next window and give your changes a proper description and title. If you +are finished submit the merge request and wait on approval. + +### Cleanup + +When you can merge the request after you got approval, you can safely +delete the source branch on the remote repository. This can be done by +opting in the responsible checkbox during the merge process. Cleanup can +also be done on your local copy. You can delete a completed branch +locally with + + git branch -d "branch name" diff --git a/docs/github_user.md b/docs/github_user.md new file mode 100644 index 0000000..006e11b --- /dev/null +++ b/docs/github_user.md @@ -0,0 +1,31 @@ +# GitHub User + +The automation code and the Ansible Automation Platform require a connection to GitHub to push changes or sync the project from the automation controller. While using a GitHub application is considered best practice, it involves many complex prerequisites. Therefore, we have decided to create a GitHub account to be used as a service account by our systems. After creating the account, several steps must be taken to make it fully functional. + +## Create GitHub User + +First, visit GitHub's website to sign up for a new account, choosing a username that clearly indicates its purpose as a service account. Add this user the project's organization. + +## Create SSH Key + +Generate an SSH key pair on the system that will connect to GitHub; this is crucial for AAP GitHub communication. Add the public key to the service account's SSH keys on GitHub for secure access. + +## Create GPG Keys + +Creating GPG keys is essential for signing commits and tags in GitHub, as it verifies the authenticity of the contributions and ensures that they have not been tampered with. Add the public key to the service account's GPG keys on GitHub for verification. + +## Create Personal Access Token + +A personal access token (PAT) is an alternative to using passwords for authentication to GitHub when using the GitHub API or the command line. Navigate to GitHub, log in to your account, generate a new token and follow the next step for the appropriate permissions. + +## Assign Appropriate Roles + +Assign the appropriate permissions to the service account for the necessary repositories, ensuring it has the correct level of access to push changes and sync projects. +- Read access to code repository +- Read/Write access to inventory repository any branch +Finally, verify that the system can connect to GitHub using the new service account and test pushing changes and syncing the project to ensure everything is working correctly. + + + + + diff --git a/docs/gitops_ansible.md b/docs/gitops_ansible.md new file mode 100644 index 0000000..3015941 --- /dev/null +++ b/docs/gitops_ansible.md @@ -0,0 +1,108 @@ +# GitOps with Ansible +This document explains the basic approach in the project to automate +everything as a code and also manage changes with Ansible based on +GitOps. + +## What is IaC? + +IaC stands for Infrastructure as Code. Itโ€™s a practice in software +engineering where infrastructure is managed using code and software +development techniques. Instead of manually setting up and configuring +servers, networks, and other infrastructure components, IaC allows +developers and system administrators to define and manage infrastructure +configuration through code. This code is typically version-controlled, +enabling automation, consistency, and reproducibility in deploying and +managing infrastructure. + +!!! quote + + GitOps works by using Git as a single source of truth for declarative infrastructure and applications โ€” Weaveworks 'Guide To GitOps' + +## What is GitOps? + +GitOps represents a workflow rooted in Martin Fowlerโ€™s seminal +[Continuous +Integration](https://martinfowler.com/articles/continuousIntegration.html) +overview from 2006, evolving from Site Reliability Engineering (SRE), +DevOps culture, and Infrastructure as Code (IaC) principles. +Essentially, it embodies the concept of "all operations are in git," +meaning all operations are codified and stored in version control +systems like Gitlab or Github. Once codified, any alterations to the +code propagate to the end systems or applications through predefined +pipelines as per requirements. + +GitOps emerged as a cloud-native initiative aimed at applying lessons +learned from Infrastructure as Code (IaC) environments to the management +of Kubernetes clusters and multi-cluster setups. The integration of +GitOps with IaC streamlines environment setup through automation, +facilitating quicker deployments and releases such as canary +deployments. This approach also grants greater flexibility for all +stakeholders in managing environments and applications. + +Employing the Red Hat Ansible Automation Platform to enact GitOps +pipelines offers distinct advantages. Leveraging the Automation Webhook +capabilities within Ansible Platform Controller enables the +implementation of agentless GitOps workflows that extend beyond +cloud-native systems to encompass existing IT infrastructure like cloud +services and networking equipment. Utilizing Ansible provides access to +its extensive ecosystem and affords the flexibility to select the most +suitable tools for individual workflows. + +## What is a GitOps workflow? + +GitOps represents an evolution in Infrastructure as Code (IaC), +utilizing Git as the version control system for infrastructure +configurations. Similar to IaC, GitOps adopts a declarative approach to +infrastructure management, defining the desired system state and +tracking its actual state. + +In a GitOps workflow, changes are made through pull requests, altering +the state in the Git repository. Upon initiating a new release via a +pull request, the GitOps operator, situated between the GitOps pipeline +and the orchestration system, retrieves the new state declaration from +Git. Once changes are approved and merged, they are automatically +applied to the live infrastructure, allowing developers to continue +using their standard CI/CD practices. + +## Ansible GitOps Framework + +### Declarative + +While Ansible isnโ€™t inherently declarative, itโ€™s both feasible and +advisable to script Ansible in a declarative manner. In an Ansible +GitOps approach, the emphasis is on crafting code that articulates the +desired state over procedural methods to achieve it. Although procedural +code may sometimes be necessary, prioritizing declarative code yields +superior GitOps outcomes. + +### Versioned and Immutable + +For code to be versioned and immutable, it must be stored in a version +control repository, typically Git, without specifying a particular Git +service. Immutability entails deploying the same version consistently, +regardless of how many times itโ€™s deployedโ€”a concept distinct from +idempotence. In the context of GitOps, immutability means deploying any +specific revision and obtaining identical results each time. + +### Automatic Pull + +In an Ansible GitOps setup, the Ansible codebase should be configured to +autonomously update the project repository, where the configuration code +resides. At minimum, the project should refresh periodically, ensuring +that new commits become accessible on the Ansible Automation Platform +(AAP) server. An alternative approach involves integrating an +appropriate webhook into the repository, triggering repository project +updates in AAP whenever the repository refreshes. + +### Continuously Reconciled + +This criterion necessitates periodically applying the versioned +configuration to the managed environment. Typically, this involves +"software agents" like ArgoCD for OpenShift patterns or AAP for Ansible +Patterns, recognizing new commits and subsequently applying the +configuration. In the case of Ansible, it entails executing units of +work such as playbooks and roles whenever the authoritative Git +repository undergoes changes. + +Please refer to [Ansible GitOps Pattern +Theory](https://github.com/mhjacks/ansible-pattern-theory). diff --git a/docs/guidelines_automation_header.yml b/docs/guidelines_automation_header.yml new file mode 100644 index 0000000..89e7fed --- /dev/null +++ b/docs/guidelines_automation_header.yml @@ -0,0 +1,7 @@ +######################################################################## +# author: Showroom Project Team +# description: TBD describe in one line purpose of this role/playbook +# company: Red Hat, Inc. +# license: GPL-3.0-only +# min_ansible_version: 2.14 +######################################################################## diff --git a/docs/idm_architecture.md b/docs/idm_architecture.md new file mode 100644 index 0000000..8421d1e --- /dev/null +++ b/docs/idm_architecture.md @@ -0,0 +1,336 @@ +# Identity Management Architecture + +Red Hat Identity Management (RHIdM) is an integrated solution for +centrally managing the authentication, authorization, and identity +information of users, hosts, and services within an organization. This +document outlines the design and architecture of a RHIdM deployment +consisting of one primary server, one replica server, and one hidden +replica server. IdM is responsible for providing identity services for +all RHEL systems. IdM will centralize management of users, groups, +hosts, access control rules, and certificates. + +IdM Terminology is available on the official +[documentation](https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/9/html/planning_identity_management/overview-of-planning-for-identity-management-and-access-control-planning-identity-management#IdM_terminology_overview-of-planning-idm-and-access-control). + +## Architecture diagram + +![IdM architecture diagram](images/idm_arch_hld.png){ align=centre } + +### System Configuration + +1. x86\_64 architecture +2. The latest version of Red Hat Enterprise Linux 9 +3. 2-core CPU at a minimum +4. 8GB of RAM +5. fully qualified hostname +6. Azure virtual instance `D2s_v4` +7. high-bandwidth, low-latency storage, with standard or premium SSD type disk with 50 GB space +8. Will be deployed in `Infra management network` in Azure + +### IdM clients + +IdM clients are Red Hat Enterprise Linux systems enrolled with the +servers and configured to use the IdM services on these servers. + +Clients interact with the IdM servers to access services provided by +them. For example, clients use the Kerberos protocol to perform +authentication and acquire tickets for enterprise single sign-on (SSO), +use LDAP to get identity and policy information, use DNS to detect where +the servers and services are located and how to connect to them. + +### IdM servers + +IdM servers are Red Hat Enterprise Linux systems that respond to +identity, authentication, and authorization requests within an IdM +domain. IdM servers will also host any of the following services used by +domain members: + +- Certificate authority (CA) +- Key Recovery Authority (KRA) +- DNS +- Active Directory (AD) trust controller +- Active Directory (AD) trust agent + +These services are all integrated and managed in a cohesive way through +the web management interface and CLI. Through these services, it is +possible to centrally define and enforce a variety of policies and +roles, such as: + +1. Directory service +2. Host-based access control (which users/groups can access which services on which systems) +3. Role-based access control (sudo rules) +4. SELinux user maps +5. NFS server auto maps (home directory and other automounts) + +### Replicas + +Continuous functionality and high availability of Identity Management +(IdM) services is vital for users who access resources. One of the +built-in solutions for accomplishing continuous functionality and high +availability of the IdM infrastructure through load balancing is the +replication of the central directory by creating replica servers of the +first server. + +IdM uses multiple read/write replica replication. In this configuration, +all replicas joined in a replication agreement receive and provide +updates, and are therefore considered suppliers and consumers. +Replication agreements are always bilateral. + +![Basic Replication Topology](images/idm_arch_replication.png) + +The following two types of replication agreements will be setup: + +- **Domain replication agreements**: These agreements replicate the identity information. +- **Certificate replication agreements**: These agreements replicate the certificate information + +### Hidden Replicas + +By default, when you set up a replica, the installation program +automatically creates service (SRV) resource records in DNS. These +records enable clients to auto-discover the replica and its services. A +hidden replica is an IdM server that has all services running and +available. However, it has no SRV records in DNS, and LDAP server roles +are not enabled. Therefore, clients cannot use service discovery to +detect these hidden replicas. + +All server roles used in a cluster, especially the Certificate Authority +role if the integrated CA is used, must be installed on the hidden +replica for the backup to be able to restore those services. + +## Security Management + +### SElinux + +SELinux enabled as a system default on Red Hat IdM servers. + +### FIPS Mode + +FIPS mode must be enabled on the system before Red Hat IdM installation. Please refer to the [official +documentation](https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/9/html/installing_identity_management/preparing-the-system-for-ipa-server-installation_installing-identity-management#fips_compliance). + +### Certificates + +Certificate authorities operate in a hierarchy of trust. Red Hat IdM +Server will use an integrated certificate signed by an external root +certificate authority (CA). IdM will also act as a certificate provider +for other services hence it will add a certificate to the chain/bundle. + +The role of the CA includes the following purposes: + +- It issues digital certificates. +- By signing a certificate, it certifies that the subject named in the certificate owns a public key. The subject can be a user, host or service. +- It can revoke certificates, and provides revocation status via Certificate Revocation Lists (CRLs) and Online Certificate Status Protocol(OCSP). + +### Random serial numbers in IdM + +With Random Serial Numbers version 3 (RSNv3) enabled, IdM generates +fully random serial numbers for certificates and requests in PKI without +range management. A variable named `ipaserver_random_serial_numbers` +must be set on the deployment role with ansible. it is required to use +RSNv3 on all public-key infrastructure (PKI) services in the deployment, +including the CA and Key Recovery Authority (KRA). A check is performed +when KRA is installed to automatically enable RSNv3 if it is enabled on +the underlying CA. + +For more information please refer to [Installing an IdM server: With +integrated DNS, with an external CA as the root +CA](https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/9/html/installing_identity_management/installing-an-ipa-server-with-external-ca_installing-identity-management). + +## DNS + +In the environment we plan to use already existing private DNS server, +which we plan to delegate only the IdM primary domain to the DNS +integrated with IdM. It is not necessary to migrate DNS zones to the IdM +DNS. + +Ensure you have valid delegation in the public DNS tree for the DNS +domain. Do not use a domain name that is not delegated to you, not even +on a private network. + + ++++ + + + + + + + + + + + + + + + + +
DNS Nameinternal.showroom.run

IdM Domain

internal.showroom.run

Kerberos Realm Name

INTERNAL.SHOWROOM.RUN

+ +DNS delegation from the parent domain to the IdM DNS domain must be +granted. Add a name server (NS) record to the `showroom.run` parent +domain for the `internal.showroom.run` + +Each IdM will manage the DNS records for the sub-domain +`internal.showroom.run`. This domain will be delegated in the domain +name-servers. + +### DNS Forwarders + +DNS forward policy must be set to Azure DNS IP in IdM to allow internal +clients to be able to resolve internet addresses. + +**Forward first** (default): The IdM BIND service forwards DNS queries +to the configured forwarder. If a query fails because of a server error +or timeout, BIND falls back to the recursive resolution using servers on +the Internet. + + ++++ + + + + + + + + + + + + + + + + +
Forward PolicyForward IP

Forward first

168.63.129.16

DNS Forward Zone

showroom.run

+ +## Time Servers + +It is possible use `chronyd` to keep IdM hosts in sync with a central +time source. Kerberos, the underlying authentication mechanism in IdM, +uses time stamps as part of its protocol. Kerberos authentication fails +if the system time of an IdM client differs by more than five minutes +from the system time of the Key Distribution Center (KDC). All VMs use +chrony configuration and able synchronize time from the public time +servers. The default config used for RHEL systems for defining the pool +of `pool 2.rhel.pool.ntp.org`. + +## Components and communication + +Red Hat IdM consists of several underlying communication from clients to +the server, including + +1. LDAP directory service +2. Kerberos KDC service +3. DNS service +4. Certificate authority service +5. Key Recovery Authority + +![IdM network ports](images/idm_ports.png) + +## High Availability and Backup + +A good disaster recovery strategy combines the following tools to +recover from a disaster as soon as possible with minimal data loss: + +### Replication + +Replication copies database contents between IdM servers. If an IdM +server fails, you can replace the lost server by creating a new replica +based on one of the remaining servers. + +As we plan to use CA, KRA and DNS functionalities on RH IdM, we must +ensure that replica server also has sane features enabloed for the high +availability. + +### IdM backups + +The ipa-backup utility allows you to take a backup of an IdM serverโ€™s +configuration files and its data. You can later use a backup to restore +an IdM server to a previous state. + +IdM provides the `ipa-backup` utility to backup IdM data, and the +`ipa-restore` utility to restore servers and data from those backups. + +There are two backup types: + +1. Full server backup + 1. Contains all server config data along with LDAP data + 2. IdM services must be `offline` +2. Data only backup + 1. Contains only LDAP data + 2. IdM services can be `offline` or `online` + +It is preferred to have `Full server backup` once every week in case we +need to build it from scratch. We consider performing backups on hidden +replicas. IdM services can be shut down on hidden replicas without +affecting IdM clients. + +Refer to the [RH official +documentation](https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/9/html/preparing_for_disaster_recovery_with_identity_management/preparing-for-data-loss-with-idm-backups_preparing-for-disaster-recovery) +for preparing for data loss with IdM backups. + +## Features and Functionality + +### Auto Membership + +Using automatic group membership allows you to assign users and hosts to +groups automatically based on their attributes. Automember rules apply +automatically to user and host entries created after the rules were +added. They are not applied retroactively to entries that existed before +the rules were added. + +### Role-Based Access Control (RBAC) and Host-Based Access Control (HBAC) + +Host-based access control (HBAC) rules define which users or user groups +can access specified hosts or host groups by using which services or +services in a service group. By default, IdM is configured with a +default HBAC rule named `allow_all`, which allows universal access to +every host for every user via every relevant service in the entire IdM +domain. `allow_all` rule must be disabled / removed after creating rule +with our own set of HBAC rules. + +Role-based access control (RBAC) in IdM grants a very different kind of +authority to users compared to self-service and delegation access +controls. + +Role-based access control is composed of three parts: + +- Permissions grant the right to perform a specific task such as + adding or deleting users, modifying a group, and enabling + read-access. +- Privileges combine permissions, for example all the permissions + needed to add a new user. +- Roles grant a set of privileges to users, user groups, hosts or host + groups. + +### IdM location + +IdM DNS locations to allow clients to locate authentication servers +within the closest network infrastructure. + +### Self-service Rules + +Self-service access control rules define which operations an Identity +Management (IdM) entity can perform on its IdM Directory Server entry. +After the deployment it is planned to setup self-service rules which +will help the new Users that they can manage their own name details rule +grants users the ability to change their own `givenname`, `displayname`, +`title`, `jpegphoto`, `description` and `initials` attributes. + +### Two-Factor Authentication (2FA) + +Two-factor authentication using One-Time Passwords (OTP) will be +configured to enhance security. + +### Kerberos Single Sign-On (SSO) + +Kerberos SSO will be implemented to allow users to authenticate once and +access multiple services without re-entering credentials. diff --git a/docs/images/aap_architecture.png b/docs/images/aap_architecture.png new file mode 100644 index 0000000..27aa3a9 Binary files /dev/null and b/docs/images/aap_architecture.png differ diff --git a/docs/images/aap_network.png b/docs/images/aap_network.png new file mode 100644 index 0000000..7e9159b Binary files /dev/null and b/docs/images/aap_network.png differ diff --git a/docs/images/ansible_structures.svg b/docs/images/ansible_structures.svg new file mode 100644 index 0000000..45e8737 --- /dev/null +++ b/docs/images/ansible_structures.svg @@ -0,0 +1 @@ +LandscapeWorkflowPlaybook of playbooksTypePlaybook_FunctionRole_ComponentTask fileRole \ No newline at end of file diff --git a/docs/images/azure_application_register.png b/docs/images/azure_application_register.png new file mode 100644 index 0000000..250d521 Binary files /dev/null and b/docs/images/azure_application_register.png differ diff --git a/docs/images/azure_application_role_assign1.png b/docs/images/azure_application_role_assign1.png new file mode 100644 index 0000000..16beb20 Binary files /dev/null and b/docs/images/azure_application_role_assign1.png differ diff --git a/docs/images/azure_application_role_assign2.png b/docs/images/azure_application_role_assign2.png new file mode 100644 index 0000000..80e4a0d Binary files /dev/null and b/docs/images/azure_application_role_assign2.png differ diff --git a/docs/images/azure_client_secret.png b/docs/images/azure_client_secret.png new file mode 100644 index 0000000..d215a6c Binary files /dev/null and b/docs/images/azure_client_secret.png differ diff --git a/docs/images/azure_network_diagram.png b/docs/images/azure_network_diagram.png new file mode 100644 index 0000000..3b66713 Binary files /dev/null and b/docs/images/azure_network_diagram.png differ diff --git a/docs/images/azure_rhib1.png b/docs/images/azure_rhib1.png new file mode 100644 index 0000000..99451cf Binary files /dev/null and b/docs/images/azure_rhib1.png differ diff --git a/docs/images/azure_rhib10.png b/docs/images/azure_rhib10.png new file mode 100644 index 0000000..bec997c Binary files /dev/null and b/docs/images/azure_rhib10.png differ diff --git a/docs/images/azure_rhib11.png b/docs/images/azure_rhib11.png new file mode 100644 index 0000000..97222e1 Binary files /dev/null and b/docs/images/azure_rhib11.png differ diff --git a/docs/images/azure_rhib2.png b/docs/images/azure_rhib2.png new file mode 100644 index 0000000..69bbb53 Binary files /dev/null and b/docs/images/azure_rhib2.png differ diff --git a/docs/images/azure_rhib3.png b/docs/images/azure_rhib3.png new file mode 100644 index 0000000..1ca110a Binary files /dev/null and b/docs/images/azure_rhib3.png differ diff --git a/docs/images/azure_rhib4.png b/docs/images/azure_rhib4.png new file mode 100644 index 0000000..58dbac5 Binary files /dev/null and b/docs/images/azure_rhib4.png differ diff --git a/docs/images/azure_rhib5.png b/docs/images/azure_rhib5.png new file mode 100644 index 0000000..79aceb6 Binary files /dev/null and b/docs/images/azure_rhib5.png differ diff --git a/docs/images/azure_rhib6.png b/docs/images/azure_rhib6.png new file mode 100644 index 0000000..4a54ba2 Binary files /dev/null and b/docs/images/azure_rhib6.png differ diff --git a/docs/images/azure_rhib7.png b/docs/images/azure_rhib7.png new file mode 100644 index 0000000..6fabe91 Binary files /dev/null and b/docs/images/azure_rhib7.png differ diff --git a/docs/images/azure_rhib8.png b/docs/images/azure_rhib8.png new file mode 100644 index 0000000..0d33e5f Binary files /dev/null and b/docs/images/azure_rhib8.png differ diff --git a/docs/images/azure_rhib9.png b/docs/images/azure_rhib9.png new file mode 100644 index 0000000..3c4ef4d Binary files /dev/null and b/docs/images/azure_rhib9.png differ diff --git a/docs/images/azure_start_stop1.png b/docs/images/azure_start_stop1.png new file mode 100644 index 0000000..69b5ab1 Binary files /dev/null and b/docs/images/azure_start_stop1.png differ diff --git a/docs/images/azure_start_stop2.png b/docs/images/azure_start_stop2.png new file mode 100644 index 0000000..96c205a Binary files /dev/null and b/docs/images/azure_start_stop2.png differ diff --git a/docs/images/azure_start_stop3.png b/docs/images/azure_start_stop3.png new file mode 100644 index 0000000..68daa03 Binary files /dev/null and b/docs/images/azure_start_stop3.png differ diff --git a/docs/images/azure_start_stop4.png b/docs/images/azure_start_stop4.png new file mode 100644 index 0000000..7212124 Binary files /dev/null and b/docs/images/azure_start_stop4.png differ diff --git a/docs/images/azure_start_stop5.png b/docs/images/azure_start_stop5.png new file mode 100644 index 0000000..3bcf86b Binary files /dev/null and b/docs/images/azure_start_stop5.png differ diff --git a/docs/images/branch_standard.jpg b/docs/images/branch_standard.jpg new file mode 100644 index 0000000..36156b8 Binary files /dev/null and b/docs/images/branch_standard.jpg differ diff --git a/docs/images/idm_arch_hld.png b/docs/images/idm_arch_hld.png new file mode 100644 index 0000000..9433098 Binary files /dev/null and b/docs/images/idm_arch_hld.png differ diff --git a/docs/images/idm_arch_replication.png b/docs/images/idm_arch_replication.png new file mode 100644 index 0000000..e698441 Binary files /dev/null and b/docs/images/idm_arch_replication.png differ diff --git a/docs/images/idm_ports.png b/docs/images/idm_ports.png new file mode 100644 index 0000000..4426b4c Binary files /dev/null and b/docs/images/idm_ports.png differ diff --git a/docs/images/jira_slack_integration.png b/docs/images/jira_slack_integration.png new file mode 100644 index 0000000..3aa6092 Binary files /dev/null and b/docs/images/jira_slack_integration.png differ diff --git a/docs/images/satellite_arch1.png b/docs/images/satellite_arch1.png new file mode 100644 index 0000000..46be8c5 Binary files /dev/null and b/docs/images/satellite_arch1.png differ diff --git a/docs/images/satellite_arch_hld.png b/docs/images/satellite_arch_hld.png new file mode 100644 index 0000000..613f6db Binary files /dev/null and b/docs/images/satellite_arch_hld.png differ diff --git a/docs/images/satellite_arch_topology_direct_satellite.png b/docs/images/satellite_arch_topology_direct_satellite.png new file mode 100644 index 0000000..2926fde Binary files /dev/null and b/docs/images/satellite_arch_topology_direct_satellite.png differ diff --git a/docs/images/satellite_arch_topology_isolated_satellite.png b/docs/images/satellite_arch_topology_isolated_satellite.png new file mode 100644 index 0000000..0086826 Binary files /dev/null and b/docs/images/satellite_arch_topology_isolated_satellite.png differ diff --git a/docs/images/showroom.png b/docs/images/showroom.png new file mode 100644 index 0000000..e0b2e88 Binary files /dev/null and b/docs/images/showroom.png differ diff --git a/docs/images/social_contract.jpg b/docs/images/social_contract.jpg new file mode 100644 index 0000000..bd2403a Binary files /dev/null and b/docs/images/social_contract.jpg differ diff --git a/docs/images/vault_change_pass.png b/docs/images/vault_change_pass.png new file mode 100644 index 0000000..4a6dc2e Binary files /dev/null and b/docs/images/vault_change_pass.png differ diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..ed049a6 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,34 @@ +--- +hide: + - navigation + - toc +--- + +# Showroom Code + +This repository is intended to contain ansible automation code. All documents are stored on [Showroom Code](https://github.com/showroom-project/showroom-code) repository. + +!!! abstract + The goal of this project is to build a Red Hat based environment suitable for a showroom project by an *enterprise* ready approach an opinionated Red Hat infrastructure that implements several Standard Operating Environments for Red Hat Enterprise Linux using Red Hat Management tools (Red Hat Infrastructure Standard Adoption Model). + +!!! note "Red Hat Infrastructure Standard (*RH-IS*)" + + The Red Hat Infrastructure Standard (*RH-IS*) is a framework for using integrated Red Hat technologies to build, manage, automate, optimize, and maintain Red Hat Enterprise Linux at scale in a hybrid multi-cloud. + One of the fundamental principles of the *RH-ISAM* is to strive towards โ€œ*_everything as code_*โ€. The goal is repeatability, consistency, and stability. + +## Advantages of implementing the Red Hat Infrastructure Standard + +The Red Hat Infrastructure Standard is an opinionated framework for the deployment, configuration, and use of: + +* Red Hat Enterprise Linux +* Red Hat Identity Management +* Red Hat Satellite +* Red Hat Ansible Automation Platform +* Red Hat Build of Keycloak + +It leverages the Red Hat Hybrid Cloud Console to deploy, patch, maintain, secure, automate and ensure compliance of the Red Hat Enterprise Linux systems across a Hybrid Multi-cloud environment. + +GitOps practices are used in the process to define the desired states and conduct tests on management infrastructure and managed systems through an _infrastructure-as-code_ pipeline. + +!!! info "URLs" + 1. [RH-IS Builder Repository](https://github.com/redhat-cop/rhis-builder) diff --git a/docs/live_kernel.md b/docs/live_kernel.md new file mode 100644 index 0000000..b20587e --- /dev/null +++ b/docs/live_kernel.md @@ -0,0 +1,34 @@ +# Kernel Live Patching +The Red Hat Enterprise Linux kernel live patching solution can be used to patch a running kernel without rebooting or restarting any processes. + +In the future, live patching will essentially create its own patch stream, enabling customers to receive live patches for critical and important CVEs for any supported kernel for a full year after the kernel is updated. This allows reboots to be scheduled at one's convenience. The tradeoff is that live patches will no longer be provided for every errata kernel, but only for selected kernels (approximately quarterly) within the various errata streams. + +Red Hat still recommends updating your on-disk kernel as frequently as practical for the following reasons: + +1. Live patches do not address every available CVE and bug fix, making it prudent to periodically update the underlying kernel. +2. Customers likely need to reboot periodically for other reasons, presenting a good opportunity to perform a kernel update. + +Live kernel patching is already enabled in Red Hat Enterprise Linux (RHEL) versions starting with version 8.1. + +You can check by ensuring that kpatch is installed: +``` bash +$ sudo dnf install kpatch +``` + +You can install live patching like below: +``` bash +$ sudo dnf install "kpatch-patch = $(uname -r)" +``` + +`kpatch-dnf` enables automatic subscription for any kernel the system currently uses, and also for kernels to-be-installed in the future. + +Update to a new cumulative version for the current kernel + +``` bash +$ sudo dnf update "kpatch-patch = $(uname -r)" +``` + +Please checkout [RHEL 9 documentation](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/managing_monitoring_and_updating_the_kernel/applying-patches-with-kernel-live-patching_managing-monitoring-and-updating-the-kernel#components-of-kernel-live-patching_applying-patches-with-kernel-live-patching) for more details. + +!!! warning +Currently, Red Hat does not support reverting live patches without rebooting your system. In case of any issues, contact our support team. diff --git a/docs/manage_credentials.md b/docs/manage_credentials.md new file mode 100644 index 0000000..84d64a8 --- /dev/null +++ b/docs/manage_credentials.md @@ -0,0 +1,89 @@ +# Manage credentials in Ansible + +`Credentials` are kept in the repository in a vaulted file. There +was some disadvantage to this design. One of it, we couldnโ€™t search for +the variable name because they were kept in encrypted files. + +## How are credentials managed now + +The new structure has encrypted variables in the file named with the `vaulted` prefix. All existing encrypted variables are kept in the `inventory` repository under `group\_vars` and `host\_vars`. This way, variables do not show as clear text when AAP inventory syncs. All variables are shown as encrypted variables. A vault password is required to decrypt these encrypted variables. + +As an example, the AAP group\_vars structure is shown below. A variable file to keep variables in clear text that is named +`aap\_data.yml`. Also, another file to keep encrypted variables is named `vault\_aap\_data.yml`. + + # [host] ~/git/inventory $ tree group_vars/aap/ + group_vars/aap/ + |-- aap_credential_data.yml + |-- aap_data.yml + |-- aap_inventory_data.yml + |-- aap_project_data.yml + |-- saml_settings.json + |-- vault_aap_data.yml + + # [host] ~/git/inventory $ cat group_vars/aap/vault_aap_data.yml | head -7 + aap_registry_username: !vault | + $ANSIBLE_VAULT;1.1;AES256 + 30373732326232383162323862613535666366616161623536366338636538363831386431643639 + 3861333964346661396631303836626538613766326331640a373033383236666637613830303135 + 33303736313936313234643665653164666637373962653837363932323736333639623639653764 + 6135366161313939390a326439393434333561393738623238386262663461303262623330366438 + 6264 + + # [host] ~/git/inventory $ cat group_vars/aap/aap_data.yml | head -3 + rhs_repository_base_url: "http://{{ rhs_satellite_server }}/pulp/isos/{{ rhs_org_name }}/Library/ccv_aap/custom/prod_aap_setup/" + rhs_repository_short_name: "repo_aap_22_setup_file_rhel8" + aap_release_version: "2.2.0-6" + +## How to create encrypted variable + +!!! warning + + Do not use `credentials` repository anymore to keep credentials. + +Run the below command for the credential you want to encrypt. + +* Enter the vault password. (the vault password has to be the same as others as we use for other variables in the inventory.) +* Enter variable/password/credential that needs to be encrypted. (`test_password` is the input as an example below) When finished, `CTRL-D` same time to end the input. T +* Copy all the sections starting after your input until the `Encryption successful` line. (in the example below, after `test_password`) +* Insert the copied lines to the vault file that was created previously + +``` bash title="ansible-vault encrypt_string --stdin-name 'aap_admin_password'" +New Vault password: +Confirm New Vault password: +Reading plaintext input from stdin. (ctrl-d to end input) +test_passwordaap_admin_password: !vault | + $ANSIBLE_VAULT;1.1;AES256 + 30626132326231383238356635353339313136353137333864626365636537303930303464633035 + 3536646163396235623035396262663662643762333061340a313435633034373439653638396264 + 36613135326136643866363039363966333164333862633335303661373033333733623361666630 + 6335386436363731640a306635666261356131393031383266333361623633303064303063323835 + 3433 +Encryption successful +``` + +## How to change/rekey of vault password in all encrypted variables? + +A new role was developed with the name aap_vault_password_change to change/rekey all vault passwords in the inventory repository. More detailed information on how to use the role is available in the role documentation. + +!!! note + + You need to do below steps to change vault password and re-encrypt all variables: + +* Create your branch. +* Change value of vault_password in `group_vars/aap/vault_aap_vault_data.yml` with new password. +* Change values related version and date in `group_vars/aap/aap_vault_data.yml`. +* Create Merge Requests. + +**Next Steps in Automation** + +* When Merge Request is approved and merged, it will automatically trigger pipeline in Git to change new_vault_password on AAP and re-encrypt all variables with new password. This pipeline will create Merge Request automatically. +* You need to follow the MR to approved and merged. +* As a second step in automation when MR is merged, new pipeline will be triggered to change vault password on AAP. + +!!! note + + The vault password update is completed at the end of this process. Please do not forget to **update** the vault password in **Vault** as the last step. + +A whole process workflow can be seen as from the diagram below + +![Encyrption Key Change with Automatiuon](images/vault_change_pass.png){ align=center } diff --git a/docs/naming_scheme.md b/docs/naming_scheme.md new file mode 100644 index 0000000..0b7f0aa --- /dev/null +++ b/docs/naming_scheme.md @@ -0,0 +1,339 @@ +# Naming Scheme + +Use lowercase letters and numbers only. Because of some restrictions +(resource name length), all the abbreviations and codes should be as +short as possible to leave more room for using meaningful names. In +general we do not use any padding scheme in our naming conventions such +as three-digit padding scheme (###). + +## Environment + +Environment is the name that describes the deployment lifecycle of the +applications or services, such as Dev, QA, or Prod. + + ++++ + + + + + + + + + + + + + + + + + + + + +
EnvironmentAbbreviation

Production (live)

prd

Development

dev

QA / Testing

qat

+ +## Network Zone + +Perimeter-based networks operate on the assumption that all systems +within a network can be trusted. But todayโ€™s employees access their +organizationโ€™s resources from anywhere on various devices and apps, +which makes perimeter security controls irrelevant. Access control +policies that focus only on who can access a resource arenโ€™t enough. To +master the balance between security and productivity, security admins +also need to factor in how a resource is being accessed. + +Best practice: Grant temporary permissions to perform privileged tasks, +which prevents malicious or unauthorized users from gaining access after +the permissions have expired. Access is granted only when users need it. + +A perimeter network (also known as a DMZ) is a physical or logical +network segment that provides an extra layer of security between your +assets and the internet. Specialized network access control devices on +the edge of a perimeter network allow only desired traffic into your +virtual network. + +A perimeter network is where you typically enable distributed denial of +service (DDoS) protection, intrusion detection/intrusion prevention +systems (IDS/IPS), firewall rules and policies, web filtering, network +anti-malware, and more. The network security devices sit between the +internet and your Azure virtual network and have an interface on both +networks. + +- Security Isolation: DMZ provides a segregated environment, isolating + public-facing services from internal networks to contain and + mitigate security threats effectively. +- Internal Resource Protection: By placing public services in the DMZ, + internal resources are shielded from direct internet exposure, + reducing the attack surface. +- Compliance and Regulations: Many compliance frameworks mandate the + implementation of a DMZ to meet security and regulatory + requirements. + + ++++ + + + + + + + + + + + + + + + + +
Network SegmentAbbreviation

Intranet

intra

DMZ

dmz

+ +## Infrastructure + +All resources are separated on a subscription level on Azure which would +enable us to set quota / limits for the resources. + + +++++ + + + + + + + + + + + + + + + + + +

Infrastructure

Abbreviation

Description

Management

mgmt

All IT management application and +systems

Workload

work

All customer application and +systems

+ +## Hostname + +Hostname from [Greek Gods](https://namingschemes.com/Greek_Gods) can be +chosen. + +## Product Specific + +### Azure + +The choice of a name for any resource in Microsoft Azure is important +because: + +- It is difficult to change a name at a later time. +- Names must meet the requirements of their specific resource type. + +Likewise, consistent naming conventions make resources easier to locate. +They also assist in understanding the role of a resource in a solution. +Naming conventions should be applied as follows on Azure resource types. +We recommend that you keep the length of naming components short to +prevent exceeding resource name length limits. Please refer to [Naming +rules and restrictions for Azure +resources](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/resource-name-rules) +for up-to-date Azure resource name length limits. + + ++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Resource TypePrefix

Subscriptions

sub-

Resource Group

rg-

Virtual Network

vnet-

Virtual Network Gateway

vnet-gw-

Virtual Network Link

vnet-link-

Subnet

subnet-

Network Security Group

nsg-

Virtual Machines

vm-

VM storage account

stvm-

Storage account

st

NIC

nic-

Public IP Address

pip-

Load Balancer

lb-

Azure Functions

func-

Workspace Name

wrkspc-

Application Insight Name

app-insight-

+ +Following structure can be applied for naming the Azure resources when +using variables + + --- + +Some resource type names can include `` rather than +`` if the resource type is not generic to any network zone +or controls all other resources with Azure Functions like the +marketplace application named `Start/Stop VMs` + +It would be always good to have a tag with a name `description` to the +resource type and `value` describing the resource type in detail. + +#### Examples + + Resource group for all the resources to control on Production --> rg-mgmt-showroom-prd + Intranet Infrastructure Management Resource group on Production --> rg-mgmt-intra-prd + DMZ Workload Resource group on Development --> rg-work-dmz-dev + Intranet Infrastructure Management Virtual Network on Production --> vnet-mgmt-intra-prd + DMZ Workload Virtual Network on Development --> vnet-work-dmz-dev + Subscription name for the Project in Management Group --> sub-mgmt-showroom-dev + +### RH Satellite + + ++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Resource TypePrefix

Activation Key

ak_

Credential

gpg_

Custom Product

Custom Repository

repo_

Content View

cv_

Composite Content View

ccv_

Host Group

hg_

Life Cycles

Partition Table

pt_

Sync Plan

sync_

Partition Table

pt_

+ +Following structure can be applied for naming the Satellite resources +when using variables + + _ + +#### Examples + + RHEL 8 Activation Key --> ak_rhel8 + RHEL 8 Content View --> cv_rhel8 + AAP Composite Content View --> ccv_aap + Support Tools Content View --> cv_support_tools + AAP Host Group --> hg_aap + VM Hostgroup --> hg_vm + +## RHIdM + +TODO: + +## AAP + +TODO: diff --git a/docs/network_design.md b/docs/network_design.md new file mode 100644 index 0000000..a6dcf74 --- /dev/null +++ b/docs/network_design.md @@ -0,0 +1,17 @@ +# Network overview + +Since the aim is public clouds for this project we must try to make the implementations as cloud agnostic as possible so the approach can be inherited easily. + +Since our current cloud provider is Azure this approach is following Azure naming standards. + +![image](images/azure_network_diagram.png){ align=center } + +* Separate Virtual Networks for public, infra & workloads +* Virtual Network Peering will take place from public to all other networks for management. +* A peering between Infra to Workload VNETs to consume Infra services. +* Every Network Security Group must have explicit rules for allowing traffic from source to destination. +* Resource Groups will provide broader control on assets belongs to each of them. + +!!! info + + Optionally, Peering can be eliminated to workload VNETs from Public VNET for additional security. diff --git a/docs/onboarding.md b/docs/onboarding.md new file mode 100644 index 0000000..388de4a --- /dev/null +++ b/docs/onboarding.md @@ -0,0 +1,123 @@ +# Onboarding + +This project onboarding document is designed to provide new team members +with a comprehensive introduction to the project, ensuring a smooth and +efficient integration into the team. It outlines the projectโ€™s +objectives, scope, and deliverables, offering a clear understanding of +our goals and expectations. The document includes detailed information +on the project timeline, key milestones, and critical deadlines. +Additionally, it introduces the project team, clarifying roles and +responsibilities to facilitate effective collaboration. Essential +resources, tools, and communication channels are highlighted to equip +new members with the necessary support for their tasks. By following +this onboarding guide, new team members will be well-prepared to +contribute to the projectโ€™s success from the outset. + +## Project Management + +A key component of our project management and follow-up process is the +use of an [RH internal JIRA](https://issues.redhat.com/projects/SHWRM) +system. This section of the document explains how JIRA is utilized to +track project progress, manage tasks, and ensure timely completion of +deliverables. Instructions on accessing and navigating the JIRA system, +along with best practices for updating tasks and communicating within +the platform, are provided to help new members quickly become proficient +in its use. By following this onboarding guide and effectively +leveraging JIRA, new team members will be well-prepared to contribute to +the projectโ€™s success from the outset. + +It is recommended to request a user story to be onboarded from other +team members through JIRA. Please ensure that all the necessary tools +are accessible from your side when JIRA user story completed. + +## Collaboration + +Slack is our main collaboration tool for real-time communication and +team interaction. This section details how Slack is used for daily +communications, sharing updates, and quick problem-solving. Guidelines +on joining relevant channels, best practices for messaging, and tips for +effective use of Slack are included to help new members integrate +seamlessly into the teamโ€™s communication flow. By following this +onboarding guide and effectively leveraging JIRA and Slack, new team +members will be well-prepared to contribute to the projectโ€™s success +from the outset. + +Following channels are used in Slack: + +1. [proj-showroom-tech](https://redhat.enterprise.slack.com/archives/C06UNKD7813) : Technical Project Channel +2. [proj-showroom](https://redhat.enterprise.slack.com/archives/C06MJEWA7UN) : Main Project Channel +3. [proj-showroom-notifications](https://redhat.enterprise.slack.com/archives/C06VCEWRQE4) : Notifications Channel only + +## Cloud Resources + +In this project, we leverage Azure cloud resources as the foundational +infrastructure to ensure scalability, reliability, and security. Azureโ€™s +robust suite of services enables seamless deployment, management, and +scaling of applications. By utilizing Azureโ€™s +infrastructure-as-a-service (IaaS) and platform-as-a-service (PaaS) +offerings, we can efficiently handle computational workloads, data +storage, and networking requirements. This integration facilitates +optimized performance and cost-effective resource management, laying a +strong foundation for the projectโ€™s success. + +The Showroom Project is the organizational name, and required to the +switched to the directory when an access is required. + +We utilize the Showroom Project organization to access Red Hat (RH) +resources through the Red Hat Hybrid Cloud Console. This integration +allows us to manage and deploy Red Hat services efficiently within our +project infrastructure. By leveraging the Hybrid Cloud Console, we +streamline resource management, ensuring seamless coordination and +optimized performance across our cloud and on-premise environments. This +approach enhances our ability to deliver robust and scalable solutions. + +Access to the resources: + +1. Azure Cloud Access: Red Hat account used to access with two-factor authentication +2. Red Hat Hybrid Cloud Console: A new Red Hat account used related with the new organization. + +## GitHub Repositories + +We use the [Showroom Project](https://github.com/showroom-project) +organization on public GitHub. + +1. [Code repository](https://github.com/showroom-project/showroom-code) +2. [Inventory and variables repository](https://github.com/showroom-project/showroom-inventory) + +Team information, including details about individual members, is available in the [Showroom Team](https://github.com/orgs/showroom-project/teams/showroom-team) section on GitHub. + +## Visual Workspace + +Miro used as a visual workspace to facilitate brainstorming, planning, +and collaborative design. [The project +workspace](https://miro.com/app/board/uXjVKUIlyW8=/) is used at the +beginning to plan and prepare the project based on the Open Practice +Libraries. + +Team information and availability is also tracked in this workspace. + +## Credential Management + +For secure credential management, we use a [Bitwarden +Vault](https://vault.bitwarden.com/) account associated with Red Hat, +Inc. This account stores all necessary credentials required for the +project, ensuring that sensitive information is protected and easily +accessible to authorized team members. By utilizing Bitwarden, we +maintain a high level of security and organization for our credentials, +supporting the overall integrity and efficiency of our project +operations. + +## Agile Ceremonies + +An Agile ceremony is an event in the Agile process where the team +gathers to discuss their next steps. Essentially, itโ€™s a regular meeting +within the Agile framework. The main goal of an Agile ceremony is to +enhance communication among team members, ensuring alignment and shared +understanding. These ceremonies are typically led by product owners. + +There are four key Agile ceremonies: + +1. [the daily stand-up meeting](https://meet.google.com/aqa-fhvw-syx) : **Every weekdays** at 11:45 (GMT +1) +2. [the sprint planning meeting](https://meet.google.com/gri-awvj-jay) : Every 2 weeks on **Friday** at 11:30 (GMT +1) +3. [the sprint review meeting](https://meet.google.com/owq-gfhg-whr) : Every 2 weeks on **Thursday** at 16:00 (GMT +1) +4. [the sprint retrospective meeting](https://meet.google.com/qap-rsqv-xgg) : Every 2 weeks on **Friday** at 10 (GMT +1) diff --git a/docs/satellite_architecture.md b/docs/satellite_architecture.md new file mode 100644 index 0000000..fefe4d6 --- /dev/null +++ b/docs/satellite_architecture.md @@ -0,0 +1,560 @@ +# Red Hat Satellite + +This document outlines the high-level design for the installation of Red +Hat Satellite. Red Hat Satellite is directly responsible to provide +provisioning, software and patch management for the virtual machines +present on the cloud provider. + +This document provides a comprehensive overview for deploying the Red +Hat Satellite solution, tailored to the specified requirements. For +further details and step-by-step implementation guidance, refer to the +official Red Hat Satellite [overview, concepts and deployment +guideline](https://access.redhat.com/documentation/en-us/red_hat_satellite/6.15/html/overview_concepts_and_deployment_considerations/index) +documentation and installation guides. + +Deployment type will be connected which means that Red Hat Satellite +connects directly to the Red Hat Content Delivery Network (CDN) through +the internet. Proxy configuration will not be used for internet access. + +## High Level Design + +### System Configuration + +1. x86\_64 architecture + +2. The latest version of Red Hat Enterprise Linux 8 + +3. 4-core 2.0 GHz CPU at a minimum + +4. 32GB of RAM + +5. fully qualified hostname + +6. Azure virtual instance `E4as` + +7. high-bandwidth, low-latency storage, with standard or premium SSD + type disk with 500 GB space + +#### File System Configuration + + ++++ + + + + + + + + + + + + +
/var/lib/pgsql20 GB

/var/lib/pulp

400 GB

+ +### Certificates + +Red Hat Satellite Server will use an SSL certificate signed by an +external certificate authority (CA) which is Red Hat Identity Management +acting as a subordinate Certificate Authority. Refer to the certificate +requirements from the official +[documentation](https://access.redhat.com/documentation/en-us/red_hat_satellite/6.15/html/installing_satellite_server_in_a_connected_network_environment/performing-additional-configuration-on-server_satellite#Configuring_Server_with_a_Custom_SSL_Certificate_satellite). + +### Operating Systems + +Operating systems will be configured as below; + + ++++ + + + + + + + + + + + + + + + + + + + + + + + + + +
NameDescription

RedHat 8.9

Red Hat Enterprise Linux 8.9

RHEL 8.10

Red Hat Enterprise Linux 8.10

RedHat 9.3

Red Hat Enterprise Linux 9.3

RedHat 9.4

Red Hat Enterprise Linux 9.4

+ +### Architectural Diagram + +![Satellite High Level Architecture](images/satellite_arch_hld.png) + + +## Network Requirements + +Network ports requirements for Intranet hosts + +![Hosts connecting directly](images/satellite_arch_topology_direct_satellite.png) + +Network port requirements for DMZ hosts + +![Hosts connecting with capsule](images/satellite_arch_topology_isolated_satellite.png) + +Firewall configurations could be found on the [official +documentation](https://access.redhat.com/documentation/en-us/red_hat_satellite/6.15/html-single/installing_satellite_server_in_a_connected_network_environment/index#Port_and_firewall_requirements_satellite) +for the connected Red Hat Satellite. + +## Provisioning + +In Satellite, you can integrate with RHEL web console to perform actions +and monitor your hosts. Satellite can interact with Microsoft Azure +Resource Manager, including creating new virtual machines and +controlling their power management states. Only image-based provisioning +is supported for creating Azure hosts. + +DNS, DHCP and TFTP services will not be managed by Satellite, and Red +Hat Identity Management (RHDIdM) integration will be configured for user +and host management. Cloud Providers option will be used as a +provisioning method in Red Hat Satellite. + +## Organizations + +Red Hat Satellite will have only one organization. + + ++++ + + + + + + + + + + + + +
NameDescription

Showroom

Showroom Organization

+ +## Locations + +Mainly there are two locations, + + ++++ + + + + + + + + + + + + + + + + +
NameDescription

Intranet

for internal business workloads and +infrastructure management services

DMZ

for the business workloads which have +public access enabled

+ +## Capsule + +Additional Red Hat Satellite Capsule deployed in DMZ location, which +will act as a content proxy. A connection between Red Hat Satellite and +Red Hat Satellite Capsule will have secure, encrypted connection between +virtual networks. + +## Content Lifecycle + +Satellite provides features for precise management of the content +lifecycle. A lifecycle environment represents a stage in the content +lifecycle, a Content View is a filtered set of content, and can be +considered as a defined subset of content. + +**A single lifecycle environment path** โ€“ both operating system and +applications content is promoted through the same path. The path can +consist of several stages (for example Development, QA, Production), +which enables thorough testing but requires additional effort. The +following lifecycle structure will be used: + +![Life Cycles](./images/satellite_arch1.png) + + ++++ + + + + + + + + + + + + + + + + + + + + + + + + +
NameDescription

Library

Default content

Dev

Development content

QA

QA UAT content

Prod

Production content

+ +## Host Groups + +Red Hat Satellite provides several logical units for grouping hosts. +Hosts that are members of those groups inherit the group configuration. +Host groups can be nested to inherit parameters from each other allows +for designing host group hierarchies that fit particular workflows. We +consider to use `Lifecycle environment based structure`. This hierarchy +is based on a lifecycle environment. + +`Dev` environment is aimed in the project to be deployed first of all. +Host groups can be extended based on the daily requirements. + + +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameParentDescription

hg_mgmt_dev

-

Management Servers Host Group for Development

hg_work_dev

-

Workload Servers Host Group for Development

hg_work_qa

-

Workload Servers Host Group for QA

hg_work_prod

-

Workload Servers Host Group for Production

hg_intranet

hg_mgmt_dev

Intranet Management Servers Host Group for Development

hg_intranet

hg_work_dev

Intranet Workload Servers Host Group for Development

hg_intranet

hg_work_qa

Intranet Workload Servers Host Group for QAT

hg_intranet

hg_work_prod

Intranet Workload Servers Host Group for Production

hg_rhel8.9

hg_mgmt_dev/hg_intranet

Host Group for RHEL8.9 systems

hg_rhel8.10

hg_mgmt_dev/hg_intranet

Host Group for RHEL8.10 systems

hg_capsule

hg_mgmt_dev/hg_intranet/hg_rhel8.10

Host Group for Red Hat Satellite Capsule

hg_epel

hg_mgmt_dev/hg_intranet/hg_rhel8.10

Host Group EPEL enabled systems

hg_rhel9.3

hg_mgmt_dev/hg_intranet

Host Group for RHEL 9.3 systems

hg_rhel9.4

hg_mgmt_dev/hg_intranet

Host Group for RHEL 9.4 systems

hg_aap

hg_mgmt_dev/hg_intranet/hg_rhel9.4

Host Group for Red Hat Ansible Automation Platform

hg_rhel8.9

hg_work_dev/hg_intranet

Host Group for RHEL8.9 systems

hg_rhel8.10

hg_work_dev/hg_intranet

Host Group for RHEL8.10 systems

hg_rhel9.3

hg_work_dev/hg_intranet

Host Group for RHEL 9.3 systems

hg_rhel9.4

hg_work_dev/hg_intranet

Host Group for RHEL 9.4 systems

hg_rhel8.9

hg_work_qa/hg_intranet

Host Group for RHEL8.9 systems

hg_rhel8.10

hg_work_qa/hg_intranet

Host Group for RHEL8.10 systems

hg_rhel9.3

hg_work_qa/hg_intranet

Host Group for RHEL 9.3 systems

hg_rhel9.4

hg_work_qa/hg_intranet

Host Group for RHEL 9.4 systems

hg_rhel8.9

hg_work_qa/hg_intranet

Host Group for RHEL8.9 systems

hg_rhel8.10

hg_work_prod/hg_intranet

Host Group for RHEL8.10 systems

hg_rhel9.3

hg_work_prod/hg_intranet

Host Group for RHEL 9.3 systems

hg_rhel9.4

hg_work_prod/hg_intranet

Host Group for RHEL 9.4 systems

+ +## Security Management + +Satellite supports security management in various ways, including update +and errata management, OpenSCAP integration for system verification, +update and security compliance reporting, and fine grained role based +authentication. + +### SElinux + +SELinux enabled as a system default on Red Hat Satellite Server. + +### FIPS Mode + +FIPS mode must be enabled on the system before Red Hat Satellite +installation. Satellite also supports provisioning hosts that comply +with FIPS. Please refer to the +[documentation](https://access.redhat.com/documentation/en-us/red_hat_satellite/6.15/html-single/provisioning_hosts/index#Provisioning_FIPS_Compliant_Hosts_provisioning). + +## Entitlement-based Subscription Management + +Simple Content Access (SCA) will be used as a subscription management +mode. There is no requirement of attaching the Red Hat Satellite +Infrastructure Subscription to the Satellite Server using +subscription-manager. + +## Realms + +Red Hat Satellite has a realm feature that will automatically manage the +life cycle of any system registered to a realm or domain provider which +will be enabled. + +### Auto Membership + +Identity Management (IDM) supports the ability to set up automatic +membership rules based on a systemโ€™s attributes. Red Hat Satelliteโ€™s +realm feature provides administrators with the ability to map the Red +Hat Satellite host groups to the IDM parameter "userclass" which allow +administrators to configure automembership. This feature will be enabled +which allows when a system is added to the Satellite Serverโ€™s +hostgroup\_name host group, it will now automatically be added to the +Identity Management serverโ€™s "hostgroup\_name" host group as well. IDM +host groups allow for Host-Based Access Controls (HBAC), sudo policies +and other IDM functions. + +## Domains + + ++++ + + + + + + + + + + + + +
Intranetinternal.showroom.run

DMZ

showroom.run

+ +## Subnets + +All existing subnets to use in the inventory will be configured on Red +Hat Satellite. + + ++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Subnet NameSubnet

Public

10.1.0.0/29

Intra Management

10.1.1.0/24

Intra Workload

10.1.2.0/24

DMZ Management

10.1.3.0/29

DMZ Workload

10.1.3.128/25

+ +## Users, Roles and Role Based Access Controls + +Red Hat Satellite support to use Red Hat Identity Management as external +Authentication source. Red Hat IdM will be configured as an +Authentication provider. Users and groups from Red Hat Identity +Management will be configured as follows; + + ++++ + + + + + + + + + + + + + + + + +
User GroupRole

satellite-admins

Administrator

satellite-users

Viewer

+ +It is expected to use GitOps Framework to configure Red Hat Satellite, +and therefore there is no need to define any access to Red Hat Satellite +WebUI. + +## Insights + +Red Hat Insights will be installed on Red Hat Satellite and registered +with Red Hat Insights to maintain the Satellite Server, and improve +ability to monitor and diagnose problems. + +## Backup + +It is planned to use built-in backup functionality to use in Red Hat +Satellite Server to facilitate recovery in the event of disaster. +`Online` backup mode will be used in the project by scheduling a job +template on Ansible Automation Controller. + +## Tuning + +No tuning option will be applied after deployment as the deployment will +not include more than 5000 hosts and `default` tuning profile will be +applied. diff --git a/docs/sign_commits.md b/docs/sign_commits.md new file mode 100644 index 0000000..bedb1cb --- /dev/null +++ b/docs/sign_commits.md @@ -0,0 +1,119 @@ +# Sign git commit + +This document explains how to sign commits in Git and VSCode. Signing, +or code signing specifically, is the process of using cryptography to +digitally add a signature to data. The receiver of the data can verify +that the signature is authentic, and therefore mustโ€™ve come from the +signatory. + +It is mainly required to configure a few settings, namely `user.email` +and `user.name`. + + # git config --global user.email "name@domain.net" + # git config --global user.name "Name Surname" + +When you authenticate with the version control system, Git would not +care much about the commits. When you authenticate to push to remote +repositories, youโ€™re authenticating to do just thatโ€“ push changes. The +commits donโ€™t require authentication regardless of who authored or +committed them. In that case, anyone can use this settings and there is +Commit Signing exist to avoid this situation. + +GNU Privacy Guard (GnuPG or GPG) allows you to create cryptographic +asymmetric key pairs that can be used for the encryption and signing of +data. They consist of a public and private key. + +## How to Sign Commits in Git + +### Set up GPG + +First you have to install GPG, if you donโ€™t already have it. You can +verify your installation + + # gpg --version + +### How to Generate GPG Keys + +If you already have a GPG key, you can skip this step. Itโ€™s perfectly +fine to reuse GPG keys. + +You can get a list of your GPG keys with: + + # gpg --list-keys + +If you already have gpg keys, import them to use. + + # gpg --import public.key + # gpg --import private.key + +The following command will start an interactive script that will ask +questions. + + gpg --full-gen-key + +1. For what kind of key you want, input 1 which is "RSA and RSA". + +2. For key size, input 4096. This is the minimum size for GitHub and + GitLab, and the maximum size GPG will let us generate. + +3. For how long the key should last, use whatever suits you. The + default is 0, which means to never expire. + +4. Verify the information is correct by inputting y. + +5. GPG will ask for personal information which is stored in your key. + + 1. Your name, this can be anything at least 5 characters in length. + + 2. Your email address, use an email you plan to commit with. You + mustโ€™ve verified this email on the remote account youโ€™ll push + with. + + 3. A comment, you can type whatever, or press enter to leave it + blank. + +6. Verify the information is correct by inputting o. + +GPG will ask for a passphrase to protect the key and then will start +generating keys. + +### How to Export GPG Keys + +You need to export the public-key so you can upload it to GitHub. We use +the --armor argument to indicate that we want to export it in an ASCII +armored format instead of binary. This writes the public-key to a file +named gpg-key.pub. + + # gpg --export --armor A335545C5A15E29D991737510D1FCF36E72F438E > ./gpg-key.pub + +!!! tip + + Donโ€™t forget to backup your passphrase and gpg keys! + +## Enable GPG Signing + +Add your signing key to your commits. + + # git config user.signingkey A335545C5A15E29D991737510D1FBF36E72F438E + +Then to enable signing all commits, set the `commit.gpgsign` setting +using git config. + + # git config --global commit.gpgsign true + +Finally you have to tell VSCode to append the `-s` flag to the git +commit command, to use signed committing now. Open the settings, search +for โ€œgpgโ€ and check the box โ€œEnables commit signing with GPGโ€. + +## Set up GitHub + +Now you have to give GitHub (or whatever version control system youโ€™re +using) your public key. Weโ€™ll need the exported public-key for the +following steps, so open the gpg-key.pub file in any editor like VSCode, +and copy the contents to your clipboard. + +On GitHub, you can go to your `settings`, under `SSH and GPG keys`, then +click `New GPG key`. Paste the contents of gpg-key.pub into the Key +field on GitHub, and click `Add GPG key`. + +Now youโ€™re able to make signed commits to your repositories! diff --git a/docs/slack_integration.md b/docs/slack_integration.md new file mode 100644 index 0000000..78d3aa3 --- /dev/null +++ b/docs/slack_integration.md @@ -0,0 +1,43 @@ +## Slack Integration +In this project, Slack is used as the centralized notification service. +We established an efficient communication channel where team members are +not allowed to send any messages, but only the integrated applications +can send notifications. This setup integrates seamlessly with our +development workflow, allowing us to stay informed about important +events in real-time. + +!!! info + + We use following channel for notifications on Slack. [proj-showroom-notifications](https://redhat.enterprise.slack.com/archives/C06VCEWRQE4) + +## GitHub Integration + +We utilize GitHub and Slack integration to monitor new pull requests and +automated pipeline runs, ensuring that every team member is promptly +notified of any code contributions and/or workflow results.More +comprehensive customizations can be found in [this +repo](https://github.com/integrations/slack). To be able to use this +integration, GitHub app should be installed on Slack from +[here](https://redhat-external.slack.com/apps/A01BP7R4KNY-github?tab=more_info). + + # invite GitHub application and authenticate + /invite @github + + # receive notifications only on pull request creation and workflow run + /github subscribe showroom-project/showroom-code pulls workflows:{event:"pull_request" branch:"main"} + /github unsubscribe showroom-project/showroom-code issues commits releases deployments + + /github subscribe showroom-project/showroom-inventory pulls workflows:{event:"pull_request" branch:"main"} + /github unsubscribe showroom-project/showroom-inventory issues commits releases deployments + +## Jira Integration + +In-house developed Slack integration called [Red Hat Jira +Connector](https://source.redhat.com/personal_blogs/get_a_dm_in_slack_when_youre_mentioned_on_a_jira_issue) +allows Jira to send notifications on certain actions. You can customize +these notifications by navigating to Project Settings and then Slack +Integartion tab. We have limited these actions with only the followings: +Issue Created, Issue Resolved, Issue Closed, Issue Reopened, Issue +Moved, In Progress, Closed New Refinement. + +![Jira-Slack Integration](./images/jira_slack_integration.png) diff --git a/docs/subscriptions.md b/docs/subscriptions.md new file mode 100644 index 0000000..4561ced --- /dev/null +++ b/docs/subscriptions.md @@ -0,0 +1,154 @@ +# Subscriptions + +This document describes the pre-requisite configurations and +subscriptions for the project. These configurations must be in place +prior to automation and can be prepared manually. + +## Azure + +### Subscriptions + +A subscription is an agreement with Microsoft to use one or more +Microsoft cloud platforms or services, for which charges accrue based on +either a per-user license fee or on cloud-based resource consumption. + +It is planned to have two subscriptions on the Azure cloud provider. One +subscription must be granted with unlimited access to resources and the +second subscription will only grant a budgeted amount of cloud resources +to be used for the customerโ€™s workloads. + +- `sub_management`: Unlimited subscription for the Infrastructure Management resources. +- `sub_workload`: Limited subscription for the customer workload resources. + +Azure PaaS services and virtual machine-based workloads hosted in Azure +IaaS can have tenancy in any Azure datacenter across the world. You +specify the Azure datacenter, known as the location, when you create the +Azure PaaS app or service or element of an IaaS workload. + +### Users + +A Microsoft Entra tenant is a specific instance of Microsoft Entra ID +containing accounts and groups. + +Red Hat directory granted on Azure cloud provider to allow our Red Hat +usernames to be able to log in. It uses Red Hat organizations login page +as default, which allows to use single sign-on with Red Hat account. OTP +access also enabled for the Showroom project, which would ask as a token +on the next step of login. This token should be integrated with your +account on your mobile device using one of your favorite token +generators. + +By default Red Hat Project Team consumes `Contributor` and `Owner` roles +on Azure Identity Management platform. + +### Credentials + +All the credentials are stored as encrypted strings in the +[repository](https://github.com/showroom-project/showroom-inventory). + +- `azure_management_subscription_id`: Azure Subscription id is + required to access subscriptions + +- `azure_management_tenant_id`: Azure Tenant id is required to access + Azure resources. + +- `techuser_ansible_client_id`: Technical user in Azure Entra for + Ansible playbooks to run with. + +## Red Hat Console and Image Builder + +It is possible to create customized RHEL system images by using Insights +image builder, and upload those images to the Microsoft Azure cloud +target environment. Then, we can create a Virtual Machine (VM) from the +image we shared with the Microsoft Azure Cloud account. + +Insights Image Builder Tool used for the initial deployment of the +systems to bootstrap. We need to access to the API of the Insights Image +Builder Tool API to generate and customize images. + +To authorize Insights image builder to push images to the Microsoft +Azure cloud, you must: + +- Configure Insights image builder as an authorized application for + your tenant GUID + +- Give it the role of Contributor to at least one resource group. + +Technical details and steps can be found on the RHEL documentation for +[composing a customized RHEL system +image](https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/8/html/creating_customized_images_by_using_insights_image_builder/creating-and-uploading-customized-rhel-system-image-to-azure-using-image-builder) + +We also need to create a service account on the Hybrid Cloud Console to +allow a user to automate through the API using the credentials. The +steps are clearly described in the [service account creation +documentation](https://access.redhat.com/documentation/en-us/red_hat_customer_portal/1/html/creating_and_managing_service_accounts/con-ciam-svc-acct-intro-creating-service-acct). + +## Users + +We created a new organization called `Red Hat - Showroom` with account +number `11826835`. For the Red Hat project team, we defined users Red +Hat Login name in this new organization, adding a suffix of the new +organization identifier with a `-` sign, and also using mail address +definition with `+` sign, which normally enables mail notifications to +the existing Red Hat mail address. + +### Example account information: + +Red Hat Login: `-showroom@redhat.com` + +Email address: `+showroom@redhat.com` + +All members of the existing project team have +`Organization Administrator` role in the organization. + +## Subscriptions + +The following subscriptions are granted from the account team to the new +organization. + + ++++++ + + + + + + + + + + + + + + + + + + + + + + +
NameSKUQuantityService Level

Red Hat Partner Subscription (500 +Nodes)

MW02049

1

Self-Support

Red Hat Satellite Infrastructure +Subscription

MCT3718

50

Premium

+ +Details for the Partner subscriptions can be found on the [Partner +Subscriptions](https://connect.redhat.com/en/partner-with-us/partner-benefits/partner-subscriptions) +page and also on +[datasheet](https://connect.redhat.com/sites/default/files/2023-05/RH-PartnerSubscription-Datasheet512.pdf). + +## Credentials + +All the credentials are stored as encrypted strings in the +[repository](https://github.com/showroom-project/showroom-inventory). + +- `techuser-rhib_client_id`: Service account client identifier + +- `techuser-rhib_client_secret`: Service account client secret diff --git a/docs/where_to_start.md b/docs/where_to_start.md new file mode 100644 index 0000000..4a55dbf --- /dev/null +++ b/docs/where_to_start.md @@ -0,0 +1,53 @@ +# where to start? +`playbooks/landscape_site.yml` is the main Ansible playbook to trigger everything from scratch for a Big Bang. Current `showroom-code` consumes `showroom-inventory` repository for the credentials and configurable variables. Duplicate `showroom-inventory` repository as your own inventory to use if you plan to deploy environment on your own and update definitions based on your own. + +1. Please read and understand the [High Level Architecture](https://docs.showroom.run/network_design/) for the Showroom Project. + +2. Register a new domain or use your existing domain, define it in the https://github.com/showroom-project/showroom-inventory/blob/devel/group_vars/all/azure_infra.yml file. Setup Azure Nameservers for your public DNS records if you are not using Azure to host your public domain name. + +3. Change the subnet definitions in https://github.com/showroom-project/showroom-inventory/blob/devel/group_vars/all/azure_network.yml if you consider to use a different subnets. + +4. Define git username, and email address in [GitHub User](https://github.com/showroom-project/showroom-inventory/blob/devel/group_vars/all/git_repo_commit.yml). Read [documentation](https://docs.showroom.run/github_user/) for more details. This user needs to have `commit`to the relevant branch. + +5. Define IdM LDAP bind name in the file https://github.com/showroom-project/showroom-inventory/blob/devel/group_vars/all/idm_config.yml based your preferred domain name. + +6. Create Red Hat activation key manually on RH Cloud Console and define this activation key in https://github.com/showroom-project/showroom-inventory/blob/devel/group_vars/all/imagebuilder.yml to enable image based VMs to be able to register to the RH Console. + +7. Define certificate configuration in https://github.com/showroom-project/showroom-inventory/blob/devel/group_vars/all/pki_idm_generate_certs.yml + +8. Ensure that you have all Red Hat subscriptions in place to use. These are mainly `Red Hat Satellite Infrastructure Subscription`, `Red Hat Ansible Automation Platform`, and any subscription that covers access to `Red Hat Enterprise Linux Server` repositories. + +9. Create a Satellite manifest to add subscriptions to be used by Satellite. Simple Content Access(SCA) has to be enabled. + +10. Ensure that Ansible Automation Platform(AAP) subscription added to the Satellite manifest. There is a name limitation during AAP subscription process, for this reason a subscription has to be visible as `Red Hat Ansible Automation Platform`. + +11. Create a custom application on Azure and grant it on a Subscription Level with a `owner` role. + +12. Create a user on Red Hat RHSM portal and use this credentials in https://github.com/showroom-project/showroom-inventory/blob/devel/group_vars/all/vault_rh.yml + +13. Create two subscriptions on Azure to differentiate Management and business workloads. Add subscription information to the https://github.com/showroom-project/showroom-inventory/blob/devel/group_vars/all/vault_azure.yml + +14. Update AAP project definitions based on your own inventory repository in https://github.com/showroom-project/showroom-inventory/blob/devel/group_vars/aap/aap_config_controller_project.yml + +15. Define IdM configuration, users, etc in RHIdM config files https://github.com/showroom-project/showroom-inventory/tree/devel/group_vars/ipahidden/ + +16. Define RootCA passphrase in https://github.com/showroom-project/showroom-inventory/blob/devel/group_vars/rootca/vault_rootca_key_passphrase.yml + +17. Create Azure RH Image Integration based on the [Azure RH Image Integration](https://docs.showroom.run/azure_rhib_integration/) document. + +18. Create private and public SSH key pair(Azure compatible) for local user to be able deploy the environment from scratch. This information should be encrypted and stored as `vm_user`, `vm_user_public_key` and `vm_user_private_key` This can be configure automatically after you define `vm_user` in the inventory and running link:playbooks/landscape_init.yml[`landscape_init.yml`] playbook to initialize your local environment in the beginning. + +19. All credentials are stored within encrypted string format, so you need to encrypt your own credentials in the inventory repository. + +* Default ansible encryption password +* Red Hat IdM credentials like Directory Manager password, administrative user password +* Red Hat Satellite username and password for administrative access +* Ansible Automation Platform credentials like Controller admin username, Controller admin password, Private Automation Hub admin username, Private Automation Hub admin password, AAP Database password, username and password for registry.redhat.io +* Azure custom application credentials that allows ansible to access. Azure Client ID, Secret ID and value of the custom application. +* Azure subscription credentials such as Tenant ID and Subscription ID. +* GitHub user personal access token, gpg secret and public key +* Common passphrase for each host's SSL private key +* Red Hat RHSM username, password and offline token with organization number +* Proxy username and password that will be used on a Jumphost(Bastion) which will help you access to the internal resources by webUI. +* Default ansible username and password on RHIdM which will be used to access to the VMs with SSH. +* Azure Workload subscription ID when used in https://github.com/showroom-project/showroom-inventory/blob/devel/group_vars/work/vault_azure.yml diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 0000000..b00e75c --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1,60 @@ +--- +site_name: Showroom docs +repo_url: https://github.com/showroom-project/showroom-code +theme: + name: material + logo: images/showroom.png + favicon: images/showroom.png + icon: + repo: fontawesome/brands/git-alt + features: + - navigation.footer + # - content.action.edit + # - content.action.view + - content.code.copy + - navigation.indexes + - toc.follow + - navigation.top + - navigation.sections + - navigation.tabs + - navigation.tabs.sticky +markdown_extensions: + - admonition + - pymdownx.details + - pymdownx.superfences + - attr_list + - md_in_html + - pymdownx.emoji: + emoji_index: !!python/name:material.extensions.emoji.twemoji + emoji_generator: !!python/name:material.extensions.emoji.to_svg +nav: + - Home: + - index.md + - where_to_start.md + - Infrastructure: + - Identity Management: idm_architecture.md + - Ansible Automation Platform: aap_architecture.md + - Red Hat Satellite: satellite_architecture.md + - RHEL: + - live_kernel.md + - Ansible: + - ansible_practices.md + - manage_credentials.md + - gitops_ansible.md + - azure_ansible_integration.md + - Azure: + - network_design.md + - cost_estimation.md + - azure_rhib_integration.md + - azure_start_stop_function.md + - General: + - general_guidelines.md + - onboarding.md + - subscriptions.md + - git_cheat_sheet.md + - github_user.md + - sign_commits.md + - branch_standards.md + - naming_scheme.md + - configure_workstation_for_bastion.md + - slack_integration.md \ No newline at end of file diff --git a/playbooks/function_aap_configure.yml b/playbooks/function_aap_configure.yml new file mode 100644 index 0000000..32e3f8c --- /dev/null +++ b/playbooks/function_aap_configure.yml @@ -0,0 +1,19 @@ +--- +- name: Configure AAP + hosts: "{{ host | default('localhost') }}" + gather_facts: false + + pre_tasks: + - name: Wait for Controller to come up + ansible.builtin.uri: + url: "https://{{ groups.aap_controller | first }}/api/v2/ping" + status_code: 200 + register: result + until: result.status == 200 + retries: 20 + delay: 30 + + roles: + - role: aap_config + tags: aap_config + when: aap_config | bool() diff --git a/playbooks/function_aap_install.yml b/playbooks/function_aap_install.yml new file mode 100644 index 0000000..0473601 --- /dev/null +++ b/playbooks/function_aap_install.yml @@ -0,0 +1,9 @@ +--- +- name: AAP - Prepare + hosts: "{{ host | default('localhost') }}" + gather_facts: false + + roles: + - role: aap_install + when: aap_install | bool() + tags: aap_install diff --git a/playbooks/function_azure_lock_vm.yml b/playbooks/function_azure_lock_vm.yml new file mode 100644 index 0000000..98c025e --- /dev/null +++ b/playbooks/function_azure_lock_vm.yml @@ -0,0 +1,11 @@ +--- +- name: Shutdown and Lock VM + hosts: "{{ host | default('localhost') }}" + gather_facts: false + + roles: + - role: azure_vm_lock + when: + - azure_vm_lock | bool() + tags: azure_vm_lock + delegate_to: localhost diff --git a/playbooks/function_azure_managed_disk_create.yml b/playbooks/function_azure_managed_disk_create.yml new file mode 100644 index 0000000..c421140 --- /dev/null +++ b/playbooks/function_azure_managed_disk_create.yml @@ -0,0 +1,11 @@ +--- +- name: Create Managed Disk + hosts: "{{ host | default('localhost') }}" + gather_facts: false + + roles: + - role: azure_managed_disk + when: + - azure_managed_disk | bool() + tags: azure_managed_disk + delegate_to: localhost diff --git a/playbooks/function_azure_network_create.yml b/playbooks/function_azure_network_create.yml new file mode 100644 index 0000000..da3e8b1 --- /dev/null +++ b/playbooks/function_azure_network_create.yml @@ -0,0 +1,43 @@ +--- +- name: Create Azure Network + hosts: "{{ host | default('localhost') }}" + gather_facts: false + + pre_tasks: + - name: Get IdM DNS IPs + run_once: true # noqa run-once[task] + block: + - name: Get IP - "{{ groups.ipaserver | first }}" + ansible.builtin.setup: + delegate_to: "{{ groups.ipaserver | first }}" + register: __ipaserver_facts + + - name: Set ipaclient_dns_servers + ansible.builtin.set_fact: + ipaclient_dns_servers: "{{ [__ipaserver_facts.ansible_facts.ansible_default_ipv4.address] }}" + + - name: Get IP - "{{ groups.ipareplicas | first }}" + ansible.builtin.setup: + delegate_to: "{{ groups.ipareplicas | first }}" + register: __ipareplica_facts + + - name: Set ipaclient_dns_servers + ansible.builtin.set_fact: + ipaclient_dns_servers: "{{ ipaclient_dns_servers + [__ipareplica_facts.ansible_facts.ansible_default_ipv4.address] }}" + + - name: Set ipaclient_dns_servers to azure_vnet_dns_servers + ansible.builtin.set_fact: + azure_vnet_dns_servers: "{{ ipaclient_dns_servers }}" + + roles: + - role: azure_resource_group + when: + - azure_resource_group | bool() + tags: azure_resource_group + delegate_to: localhost + + - role: azure_network + when: + - azure_network | bool() + tags: azure_network + delegate_to: localhost diff --git a/playbooks/function_azure_private_dns_clean.yml b/playbooks/function_azure_private_dns_clean.yml new file mode 100644 index 0000000..96892c9 --- /dev/null +++ b/playbooks/function_azure_private_dns_clean.yml @@ -0,0 +1,55 @@ +--- +- name: Config vnet + hosts: "{{ host | default('localhost') }}" + gather_facts: false + pre_tasks: + - name: Get IdM DNS IPs + run_once: true # noqa run-once[task] + block: + - name: Get IP - "{{ groups.ipaserver | first }}" + ansible.builtin.setup: + delegate_to: "{{ groups.ipaserver | first }}" + register: __ipaserver_facts + + - name: Set ipaclient_dns_servers + ansible.builtin.set_fact: + ipaclient_dns_servers: "{{ [__ipaserver_facts.ansible_facts.ansible_default_ipv4.address] }}" + + - name: Get IP - "{{ groups.ipareplicas | first }}" + ansible.builtin.setup: + delegate_to: "{{ groups.ipareplicas | first }}" + register: __ipareplica_facts + + - name: Set ipaclient_dns_servers + ansible.builtin.set_fact: + ipaclient_dns_servers: "{{ ipaclient_dns_servers + [__ipareplica_facts.ansible_facts.ansible_default_ipv4.address] }}" + + - name: Set ipaclient_dns_servers to azure_vnet_dns_servers + ansible.builtin.set_fact: + azure_vnet_dns_servers: "{{ ipaclient_dns_servers }}" + + tasks: + - name: Remove Private DNS zone links + ansible.builtin.import_role: + name: azure_dns + tasks_from: private_dns_zone_link.yml + delegate_to: localhost + vars: + azure_dns_private_dns_zone_state: absent + + - name: Remove Private DNS zone + ansible.builtin.import_role: + name: azure_dns + tasks_from: private_dns_zone.yml + delegate_to: localhost + vars: + azure_dns_private_dns_zone_state: absent + + - name: Remove Private DNS zone + ansible.builtin.import_role: + name: azure_network + delegate_to: localhost + vars: + vnet_peering: false # noqa var-naming[no-role-prefix] + manage_nsg: false # noqa var-naming[no-role-prefix] + manage_subnet: false # noqa var-naming[no-role-prefix] diff --git a/playbooks/function_azure_snapshot_manage.yml b/playbooks/function_azure_snapshot_manage.yml new file mode 100644 index 0000000..3300315 --- /dev/null +++ b/playbooks/function_azure_snapshot_manage.yml @@ -0,0 +1,11 @@ +--- +- name: Manage Azure Snapshot + hosts: "{{ host | default('localhost') }}" + gather_facts: false + + roles: + - role: azure_snapshot + when: + - azure_snapshot | bool() + tags: azure_snapshot + delegate_to: localhost diff --git a/playbooks/function_azure_vm_create.yml b/playbooks/function_azure_vm_create.yml new file mode 100644 index 0000000..3e9c34f --- /dev/null +++ b/playbooks/function_azure_vm_create.yml @@ -0,0 +1,34 @@ +--- +- name: Create VM + hosts: "{{ host | default('localhost') }}" + gather_facts: false + + roles: + - role: azure_resource_group + when: + - azure_resource_group | bool() + tags: azure_resource_group + delegate_to: localhost + + - role: imagebuilder + when: imagebuilder | bool() + tags: imagebuilder + delegate_to: localhost + + - role: azure_network + when: + - azure_network | bool() + tags: azure_network + delegate_to: localhost + + - role: azure_vm_deploy + when: + - azure_vm_deploy | bool() + tags: azure_vm_deploy + delegate_to: localhost + + - role: azure_dns + when: + - azure_dns | bool() + tags: azure_dns + delegate_to: localhost diff --git a/playbooks/function_ca_create.yml b/playbooks/function_ca_create.yml new file mode 100644 index 0000000..ccfbd6b --- /dev/null +++ b/playbooks/function_ca_create.yml @@ -0,0 +1,23 @@ +--- +- name: Create CA + hosts: "{{ host | default('localhost') }}" + gather_facts: false + + roles: + - role: pki_rootca + when: pki_rootca | bool() + tags: pki_rootca + become: true + + - role: vault_string + when: vault_string | bool() + tags: vault_string + vars: + vault_string_filepath: "{{ __rootca_cert_path }}" + + - role: git_repo_commit + when: git_repo_commit | bool() + tags: git_repo_commit + vars: + git_repo_commit_repository: "{{ github_inventory_repo_path }}" + git_repo_commit_file: { src: "{{ __path_file_encrypted }}", dest: "group_vars/all/" } diff --git a/playbooks/function_cis_enforce.yml b/playbooks/function_cis_enforce.yml new file mode 100644 index 0000000..53dddd3 --- /dev/null +++ b/playbooks/function_cis_enforce.yml @@ -0,0 +1,10 @@ +--- +- name: Enforce CIS + hosts: "{{ host | default('localhost') }}" + gather_facts: true + + roles: + - role: RedHatOfficial.rhel9_cis + when: enforce_cis | bool() + tags: enforce_cis + become: true diff --git a/playbooks/function_clean_cv_version.yml b/playbooks/function_clean_cv_version.yml new file mode 100644 index 0000000..036a9a5 --- /dev/null +++ b/playbooks/function_clean_cv_version.yml @@ -0,0 +1,7 @@ +--- +- name: Clean out unused (C)CVs + hosts: "{{ host | default('localhost') }}" + gather_facts: false + + roles: + - role: redhat.satellite.content_view_version_cleanup diff --git a/playbooks/function_enable_live_kernel.yml b/playbooks/function_enable_live_kernel.yml new file mode 100644 index 0000000..7f86e78 --- /dev/null +++ b/playbooks/function_enable_live_kernel.yml @@ -0,0 +1,36 @@ +--- +- name: Enable Live Kernel + hosts: "{{ host | default('localhost') }}" + gather_facts: true + + tasks: + - name: Block for Live Kernel Patching + become: true + block: + - name: Ensure kpatch package is installed + ansible.builtin.package: + name: kpatch + state: present + + - name: Deploy / update related Kernel Patch for Live Kernel Patching + ansible.builtin.package: + name: "kpatch-patch = {{ ansible_kernel }}" + state: "{{ package_state | default(omit) }}" + register: __package_install + + - name: Get status of kpatch-dnf + ansible.builtin.command: + cmd: "dnf kpatch status" + register: __status_dnf_kpatch + changed_when: false + + - name: Enable DNF plugin for auto install live patches + ansible.builtin.command: + cmd: "dnf -y kpatch auto" + changed_when: true + when: "'Kpatch update setting: manual' in __status_dnf_kpatch.stdout" + + rescue: + - name: Rescue message + ansible.builtin.debug: + msg: "No kernel live patch update package exist or system is not subscribed to the repository" diff --git a/playbooks/function_github_runner_create.yml b/playbooks/function_github_runner_create.yml new file mode 100644 index 0000000..391684d --- /dev/null +++ b/playbooks/function_github_runner_create.yml @@ -0,0 +1,86 @@ +--- +- name: Create Github Runner + hosts: "{{ host | default('localhost') }}" + gather_facts: true + + roles: + - role: monolithprojects.github_actions_runner + when: create_github_runner | bool() + tags: create_github_runner + become: true + + post_tasks: + - name: Install container-tools + become: true + ansible.builtin.dnf: + name: container-tools + state: present + + - name: Enable and start podman service + become: true + ansible.builtin.systemd: + name: podman + enabled: true + state: started + + - name: Create .bashrc.d directory if it doesn't exist + ansible.builtin.file: + path: "{{ ansible_env.HOME }}/.bashrc.d" + state: directory + mode: "0755" + + - name: Set XDG_RUNTIME_DIR in systemd file + ansible.builtin.copy: + dest: "{{ ansible_env.HOME }}/.bashrc.d/systemd" + mode: "0640" + content: | + export XDG_RUNTIME_DIR=/run/user/$(id -u) + export DOCKER_HOST="unix:$XDG_RUNTIME_DIR/podman/podman.sock" + + - name: Source the new environment variable file + ansible.builtin.shell: source ~/.bashrc.d/systemd + args: + executable: /bin/bash + changed_when: false + + - name: Enable and start podman.socket for user + ansible.builtin.systemd: + name: podman.socket + enabled: true + state: started + scope: user + + - name: Remove /var/run/docker.sock if it exists + become: true + ansible.builtin.file: + path: /var/run/docker.sock + state: absent + + - name: Create symlink for podman socket + become: true + ansible.builtin.file: + src: "{{ ansible_env.XDG_RUNTIME_DIR }}/podman/podman.sock" + dest: /var/run/docker.sock + state: link + mode: "0640" + + - name: Create script to create Docker socket link + ansible.builtin.copy: + dest: "{{ ansible_env.HOME }}/create-docker-sock-link.sh" + content: | + #!/bin/bash + + # Remove the existing symbolic link if it exists + if [ -L /var/run/docker.sock ]; then + sudo rm /var/run/docker.sock + fi + + # Create a new symbolic link to the correct Podman socket + sudo ln -s $XDG_RUNTIME_DIR/podman/podman.sock /var/run/docker.sock + mode: "0750" + + - name: Create cron job to ensure Docker socket link at boot + ansible.builtin.cron: + name: "Ensure Docker socket link at boot" + special_time: reboot + job: "{{ ansible_env.HOME }}/create-docker-sock-link.sh" diff --git a/playbooks/function_idm_configuration.yml b/playbooks/function_idm_configuration.yml new file mode 100644 index 0000000..d1b926f --- /dev/null +++ b/playbooks/function_idm_configuration.yml @@ -0,0 +1,9 @@ +--- +- name: Configure IDM server + hosts: "{{ host | default('localhost') }}" + gather_facts: false + + roles: + - role: idm_config + when: idm_config | bool() + tags: idm_config diff --git a/playbooks/function_idm_csr_create.yml b/playbooks/function_idm_csr_create.yml new file mode 100644 index 0000000..0cc811f --- /dev/null +++ b/playbooks/function_idm_csr_create.yml @@ -0,0 +1,22 @@ +--- +- name: Create IDM CSR + hosts: "{{ host | default('localhost') }}" + gather_facts: true + + pre_tasks: + # as of 30.08.2024 there is a bug in the RHIdM installation when using + # the latest version of bind-dyndb-ldap package. + # https://issues.redhat.com/browse/FREEIPA-11485 + - name: Install bind-dyndb-ldap.x86_64 version 11.9 + become: true + ansible.builtin.dnf: + name: bind-dyndb-ldap-11.9-9.el9_4.x86_64 + state: present + + roles: + - role: redhat.rhel_idm.ipaserver + state: present # noqa var-naming[no-role-prefix] + when: create_ipaserver| bool() + become: true + vars: + ipaserver_external_ca: true diff --git a/playbooks/function_idm_generate_certs.yml b/playbooks/function_idm_generate_certs.yml new file mode 100644 index 0000000..6b4b5b3 --- /dev/null +++ b/playbooks/function_idm_generate_certs.yml @@ -0,0 +1,9 @@ +--- +- name: Create IDM CSR + hosts: "{{ host | default('localhost') }}" + gather_facts: false + + roles: + - role: pki_idm_generate_certs + when: pki_idm_generate_certs | bool() + become: true diff --git a/playbooks/function_idm_ipareplicas_install.yml b/playbooks/function_idm_ipareplicas_install.yml new file mode 100644 index 0000000..70dd54d --- /dev/null +++ b/playbooks/function_idm_ipareplicas_install.yml @@ -0,0 +1,65 @@ +--- +- name: Create IDM Replicas + hosts: "{{ host | default('localhost') }}" + gather_facts: true + + pre_tasks: + - name: Get IP - "{{ groups.ipaserver | first }}" + ansible.builtin.setup: + delegate_to: "{{ groups.ipaserver | first }}" + register: __ipaserver_facts + + - name: Set ipaclient_dns_servers + ansible.builtin.set_fact: + ipaclient_dns_servers: "{{ __ipaserver_facts.ansible_facts.ansible_default_ipv4.address }}" + + - name: Set reverse zone + ansible.builtin.set_fact: + ipareplica_reverse_zones: "{{ (ipaclient_dns_servers | ansible.utils.ipaddr('revdns') | split('.') | map('trim'))[1:] | join('.') }}" + + # as of 30.08.2024 there is a bug in the RHIdM installation when using + # the latest version of bind-dyndb-ldap package. + # https://issues.redhat.com/browse/FREEIPA-11485 + - name: Install bind-dyndb-ldap.x86_64 version 11.9-9 + become: true + ansible.builtin.dnf: + name: bind-dyndb-ldap-11.9-9.el9_4.x86_64 + state: present + + roles: + - role: redhat.rhel_idm.ipareplica + state: present # noqa var-naming[no-role-prefix] + when: create_ipareplicas | bool() + become: true + + post_tasks: + - name: Replace the content of /etc/named/ipa-options-ext.conf + become: true + ansible.builtin.copy: + dest: /etc/named/ipa-options-ext.conf + mode: "0640" + content: | + allow-recursion { trusted_network; }; + allow-query-cache { trusted_network; }; + listen-on-v6 { any; }; + dnssec-validation yes; + + - name: Replace the content of /etc/named/ipa-ext.conf + become: true + ansible.builtin.copy: + dest: /etc/named/ipa-ext.conf + mode: "0640" + content: | + acl "trusted_network" { + localnets; + localhost; + {% for network in azure_networks %} + {{ network.subnet }}; + {% endfor %} + }; + + - name: Restart ipa service + become: true + ansible.builtin.systemd: + name: ipa + state: restarted diff --git a/playbooks/function_idm_ipaserver_install.yml b/playbooks/function_idm_ipaserver_install.yml new file mode 100644 index 0000000..483b9ec --- /dev/null +++ b/playbooks/function_idm_ipaserver_install.yml @@ -0,0 +1,44 @@ +--- +- name: Create IDM server + hosts: "{{ host | default('localhost') }}" + gather_facts: true + + roles: + - role: redhat.rhel_idm.ipaserver + state: present # noqa var-naming[no-role-prefix] + when: create_ipaserver| bool() + become: true + vars: + ipaserver_external_cert_files: "{{ ipaserver_chain_cert_path }}" + + post_tasks: + - name: Replace the content of /etc/named/ipa-options-ext.conf + become: true + ansible.builtin.copy: + dest: /etc/named/ipa-options-ext.conf + mode: "0640" + content: | + allow-recursion { trusted_network; }; + allow-query-cache { trusted_network; }; + listen-on-v6 { any; }; + dnssec-validation yes; + + - name: Replace the content of /etc/named/ipa-ext.conf + become: true + ansible.builtin.copy: + dest: /etc/named/ipa-ext.conf + mode: "0640" + content: | + acl "trusted_network" { + localnets; + localhost; + {% for network in azure_networks %} + {{ network.subnet }}; + {% endfor %} + }; + + - name: Restart ipa service + become: true + ansible.builtin.systemd: + name: ipa + state: restarted diff --git a/playbooks/function_idm_register.yml b/playbooks/function_idm_register.yml new file mode 100644 index 0000000..19e8449 --- /dev/null +++ b/playbooks/function_idm_register.yml @@ -0,0 +1,32 @@ +--- +- name: IDM Register Client + hosts: "{{ host | default('localhost') }}" + gather_facts: false + + pre_tasks: + - name: Get IdM DNS IPs + when: ipaclient_configure_dns_resolver | bool() and idm_register_client | bool() + block: + - name: Get IP - "{{ groups.ipaserver | first }}" + ansible.builtin.setup: + delegate_to: "{{ groups.ipaserver | first }}" + register: __ipaserver_facts + + - name: Set ipaclient_dns_servers + ansible.builtin.set_fact: + ipaclient_dns_servers: "{{ [__ipaserver_facts.ansible_facts.ansible_default_ipv4.address] }}" + + - name: Get IP - "{{ groups.ipareplicas | first }}" + ansible.builtin.setup: + delegate_to: "{{ groups.ipareplicas | first }}" + register: __ipareplica_facts + + - name: Set ipaclient_dns_servers + ansible.builtin.set_fact: + ipaclient_dns_servers: "{{ ipaclient_dns_servers + [__ipareplica_facts.ansible_facts.ansible_default_ipv4.address] }}" + + roles: + - role: redhat.rhel_idm.ipaclient + state: present # noqa var-naming[no-role-prefix] + when: idm_register_client| bool() + become: true diff --git a/playbooks/function_idm_sign_csr.yml b/playbooks/function_idm_sign_csr.yml new file mode 100644 index 0000000..38387cb --- /dev/null +++ b/playbooks/function_idm_sign_csr.yml @@ -0,0 +1,9 @@ +--- +- name: Sign IDM CSR approve + hosts: "{{ host | default('localhost') }}" + gather_facts: false + + roles: + - role: pki_idm_csr_approve + when: pki_idm_csr_approve | bool() + become: true diff --git a/playbooks/function_imagebuilder_create.yml b/playbooks/function_imagebuilder_create.yml new file mode 100644 index 0000000..1232573 --- /dev/null +++ b/playbooks/function_imagebuilder_create.yml @@ -0,0 +1,16 @@ +--- +- name: Create VM Images + hosts: "{{ host | default('localhost') }}" + gather_facts: false + + roles: + - role: azure_resource_group + when: + - azure_resource_group | bool() + tags: azure_resource_group + delegate_to: localhost + + - role: imagebuilder + when: imagebuilder | bool() + tags: imagebuilder + delegate_to: localhost diff --git a/playbooks/function_init_environment.yml b/playbooks/function_init_environment.yml new file mode 100644 index 0000000..c0964eb --- /dev/null +++ b/playbooks/function_init_environment.yml @@ -0,0 +1,9 @@ +--- +- name: Initialize Environment + hosts: localhost + gather_facts: true + + roles: + - role: roles/init_environment # noqa role-name[path] + tags: init_environment + when: init_environment | bool() diff --git a/playbooks/function_leapp_analysis.yml b/playbooks/function_leapp_analysis.yml new file mode 100644 index 0000000..810c3da --- /dev/null +++ b/playbooks/function_leapp_analysis.yml @@ -0,0 +1,21 @@ +--- +- name: Leapp Analysis + hosts: "{{ host | default('localhost') }}" + become: true + strategy: free # noqa run-once[play] + force_handlers: true + gather_facts: true + + tasks: + - name: Perform OS Analysis + ansible.builtin.import_role: + name: infra.leapp.analysis + + - name: Fail if inhibitors found + ansible.builtin.fail: + msg: | + Inhibitor found and upgrade terminated. Review the tasks or the result file at /var/log/leapp/leapp-report.txt + Please use 'Leapp 8.10 to 9.4 - Remediate' job template to remediate inhibitors or investigate and remediate manually. + when: + - results_inhibitors is defined + - results_inhibitors.stdout_lines | length > 0 diff --git a/playbooks/function_leapp_remediate.yml b/playbooks/function_leapp_remediate.yml new file mode 100644 index 0000000..5701911 --- /dev/null +++ b/playbooks/function_leapp_remediate.yml @@ -0,0 +1,9 @@ +--- +- name: Leapp Remidiate + hosts: "{{ host | default('localhost') }}" + become: true + strategy: free # noqa run-once[play] + force_handlers: true + + roles: + - role: infra.leapp.remediate diff --git a/playbooks/function_leapp_test.yml b/playbooks/function_leapp_test.yml new file mode 100644 index 0000000..5079716 --- /dev/null +++ b/playbooks/function_leapp_test.yml @@ -0,0 +1,10 @@ +--- +- name: Test after Leapp upgrade + hosts: "{{ host | default('localhost') }}" + gather_facts: false + + tasks: + - name: Check that you can connect (GET) to a page and it returns a status 200 + ansible.builtin.uri: + url: "http://{{ host }}" + delegate_to: "{{ groups.aap_controller[0] }}" diff --git a/playbooks/function_leapp_upgrade.yml b/playbooks/function_leapp_upgrade.yml new file mode 100644 index 0000000..d9e72ee --- /dev/null +++ b/playbooks/function_leapp_upgrade.yml @@ -0,0 +1,9 @@ +--- +- name: Leapp Upgrade + hosts: "{{ host | default('localhost') }}" + become: true + strategy: free # noqa run-once[play] + force_handlers: true + + roles: + - role: infra.leapp.upgrade diff --git a/playbooks/function_public_certificate_create.yml b/playbooks/function_public_certificate_create.yml new file mode 100644 index 0000000..076d975 --- /dev/null +++ b/playbooks/function_public_certificate_create.yml @@ -0,0 +1,10 @@ +--- +- name: Create Public Trusted SSL Certificate Create + hosts: "{{ host | default('localhost') }}" + gather_facts: false + + roles: + - role: public_certificate + become: true + when: public_certificate | bool() + tags: public_certificate diff --git a/playbooks/function_reverse_proxy_configure.yml b/playbooks/function_reverse_proxy_configure.yml new file mode 100644 index 0000000..5c77585 --- /dev/null +++ b/playbooks/function_reverse_proxy_configure.yml @@ -0,0 +1,10 @@ +--- +- name: Configure Nginx + hosts: "{{ host | default('localhost') }}" + gather_facts: false + + roles: + - role: manage_reverse_proxy + become: true + when: manage_reverse_proxy | bool() + tags: manage_reverse_proxy diff --git a/playbooks/function_rhel_managed_disk_configure.yml b/playbooks/function_rhel_managed_disk_configure.yml new file mode 100644 index 0000000..42fc8c2 --- /dev/null +++ b/playbooks/function_rhel_managed_disk_configure.yml @@ -0,0 +1,10 @@ +--- +- name: Run Storage Role + hosts: "{{ host | default('localhost') }}" + gather_facts: true + + roles: + - role: redhat.rhel_system_roles.storage + when: run_rhel_storage | bool() + tags: run_rhel_storage + become: true diff --git a/playbooks/function_satellite_configure.yml b/playbooks/function_satellite_configure.yml new file mode 100644 index 0000000..1662a4f --- /dev/null +++ b/playbooks/function_satellite_configure.yml @@ -0,0 +1,9 @@ +--- +- name: Configure Satellite + hosts: "{{ host | default('localhost') }}" + gather_facts: false + + roles: + - role: satellite_config + tags: satellite_config + when: satellite_config | bool() diff --git a/playbooks/function_satellite_install.yml b/playbooks/function_satellite_install.yml new file mode 100644 index 0000000..ece1891 --- /dev/null +++ b/playbooks/function_satellite_install.yml @@ -0,0 +1,12 @@ +--- +- name: Satellite - Installation + hosts: "{{ host | default('localhost') }}" + gather_facts: false + roles: + - role: satellite_prepare + when: satellite_prepare | bool() + become: true + + - role: redhat.satellite_operations.installer + when: satellite_operations | bool() + become: true diff --git a/playbooks/function_satellite_promote_ccv.yml b/playbooks/function_satellite_promote_ccv.yml new file mode 100644 index 0000000..5720893 --- /dev/null +++ b/playbooks/function_satellite_promote_ccv.yml @@ -0,0 +1,10 @@ +--- +- name: RH Satellite - Promote CCV to Lifecycle + hosts: "satellite" + gather_facts: false + + tasks: + - name: Include Task - Satellite Promote CCVs + ansible.builtin.include_role: + name: satellite_config + tasks_from: satellite_promote_ccv.yml diff --git a/playbooks/function_satellite_publish_cvs.yml b/playbooks/function_satellite_publish_cvs.yml new file mode 100644 index 0000000..def36ff --- /dev/null +++ b/playbooks/function_satellite_publish_cvs.yml @@ -0,0 +1,10 @@ +--- +- name: RH Satellite - Publish CVs + hosts: "satellite" + gather_facts: false + + tasks: + - name: Include Task - Satellite Publish CVs + ansible.builtin.include_role: + name: satellite_config + tasks_from: satellite_publish_cvs.yml diff --git a/playbooks/function_satellite_register.yml b/playbooks/function_satellite_register.yml new file mode 100644 index 0000000..4d05250 --- /dev/null +++ b/playbooks/function_satellite_register.yml @@ -0,0 +1,9 @@ +--- +- name: Satellite - Register Host + hosts: "{{ host | default('localhost') }}" + gather_facts: false + + roles: + - role: satellite_register + when: satellite_register | bool() + become: true diff --git a/playbooks/function_satellite_vm_deploy.yml b/playbooks/function_satellite_vm_deploy.yml new file mode 100644 index 0000000..d6d43ce --- /dev/null +++ b/playbooks/function_satellite_vm_deploy.yml @@ -0,0 +1,10 @@ +--- +- name: Create VM from Satellite + hosts: "{{ host | default('localhost') }}" + gather_facts: false + + roles: + - role: satellite_host_deploy + when: satellite_host_deploy | bool() + tags: satellite_host_deploy + delegate_to: "{{ groups['bastion'][0] }}" diff --git a/playbooks/function_squid_configure.yml b/playbooks/function_squid_configure.yml new file mode 100644 index 0000000..bb32006 --- /dev/null +++ b/playbooks/function_squid_configure.yml @@ -0,0 +1,10 @@ +--- +- name: Configure Squid + hosts: "{{ host | default('localhost') }}" + gather_facts: false + + roles: + - role: manage_squid + become: true + when: manage_squid | bool() + tags: manage_squid diff --git a/playbooks/function_vm_post_configure.yml b/playbooks/function_vm_post_configure.yml new file mode 100644 index 0000000..5b5865a --- /dev/null +++ b/playbooks/function_vm_post_configure.yml @@ -0,0 +1,9 @@ +--- +- name: Post configure VM + hosts: "{{ host | default('localhost') }}" + gather_facts: false + + roles: + - role: post_config + when: post_config | bool() + tags: post_config diff --git a/playbooks/landscape_init.yml b/playbooks/landscape_init.yml new file mode 100644 index 0000000..1fb93d0 --- /dev/null +++ b/playbooks/landscape_init.yml @@ -0,0 +1,3 @@ +--- +- name: "Phase 0 - Initialize Local Environment" + ansible.builtin.import_playbook: function_init_environment.yml diff --git a/playbooks/landscape_site.yml b/playbooks/landscape_site.yml new file mode 100644 index 0000000..3c08c5e --- /dev/null +++ b/playbooks/landscape_site.yml @@ -0,0 +1,50 @@ +--- +- name: "Phase 1 - Create Bastion VM" + ansible.builtin.import_playbook: type_bastion_create.yml + +- name: "Phase 2 - Create RootCA VM" + ansible.builtin.import_playbook: type_rootca_create.yml + +- name: "Phase 3 - Create Github Runner VM" + ansible.builtin.import_playbook: type_github_runner_create.yml + +- name: "Phase 4.1 - Create IdM ipaserver" + ansible.builtin.import_playbook: type_ipaserver_create.yml + +- name: "Phase 4.2 - Create IdM ipareplicas" + ansible.builtin.import_playbook: type_ipareplicas_create.yml + +- name: "Phase 4.3 - Configure IdM" + ansible.builtin.import_playbook: type_ipa_config.yml + +- name: "Phase 5.1 - Install Satellite" + ansible.builtin.import_playbook: type_satellite_install.yml + +- name: "Phase Intermediary - Deploy Azure Resources for Workload Intra" + ansible.builtin.import_playbook: type_workload_intra_resources_create.yml + +# Showroom project currently not deploying workload resources in DMZ. +# However, it has to capability to do that if intended to do +# - name: "Phase Intermediary - Deploy Azure Resources for Workload Intra" +# ansible.builtin.import_playbook: type_workload_dmz_resources_create.yml + +- name: "Phase 5.2 - Configure Satellite" + ansible.builtin.import_playbook: type_satellite_configure.yml + +- name: "Phase 6.1 - Create AAP VMs" + ansible.builtin.import_playbook: type_aap_create_infrastructure.yml + +- name: "Phase Intermediary - Register Management VMs to IdM and Satellite" + ansible.builtin.import_playbook: type_idm_satellite_register.yml + +- name: "Phase 6.2 - Install AAP" + ansible.builtin.import_playbook: type_aap_install.yml + +- name: "Phase 6.2 - Configure AAP" + ansible.builtin.import_playbook: type_aap_configure.yml + +- name: "Phase 7 - Create Reverse Proxy" + ansible.builtin.import_playbook: type_reverse_proxy_create.yml + +- name: "Phase X - Enforce CIS on all hosts" + ansible.builtin.import_playbook: type_cis_enforce.yml diff --git a/playbooks/toolbox_add_temp_host_to_group.yml b/playbooks/toolbox_add_temp_host_to_group.yml new file mode 100644 index 0000000..3aed2d5 --- /dev/null +++ b/playbooks/toolbox_add_temp_host_to_group.yml @@ -0,0 +1,9 @@ +--- +- name: Add temp host + hosts: "{{ host | default('localhost') }}" + gather_facts: false + tasks: + - name: Add a host to group + ansible.builtin.add_host: + name: "{{ host_to_add }}" + groups: "{{ group_to_add }}" diff --git a/playbooks/toolbox_get_dns_ips.yml b/playbooks/toolbox_get_dns_ips.yml new file mode 100644 index 0000000..4d6d343 --- /dev/null +++ b/playbooks/toolbox_get_dns_ips.yml @@ -0,0 +1,23 @@ +--- +- name: Get IdM DNS IPs + hosts: localhost + gather_facts: false + + tasks: + - name: Get IP - "{{ groups.ipaserver | first }}" + ansible.builtin.setup: + delegate_to: "{{ groups.ipaserver | first }}" + register: __ipaserver_facts + + - name: Set ipaclient_dns_servers + ansible.builtin.set_fact: + ipaclient_dns_servers: "{{ [__ipaserver_facts.ansible_facts.ansible_default_ipv4.address] }}" + + - name: Get IP - "{{ groups.ipareplicas | first }}" + ansible.builtin.setup: + delegate_to: "{{ groups.ipareplicas | first }}" + register: __ipareplica_facts + + - name: Set ipaclient_dns_servers + ansible.builtin.set_fact: + ipaclient_dns_servers: "{{ ipaclient_dns_servers + [__ipareplica_facts.ansible_facts.ansible_default_ipv4.address] }}" diff --git a/playbooks/toolbox_satellite_hostgroup_change.yml b/playbooks/toolbox_satellite_hostgroup_change.yml new file mode 100644 index 0000000..7569e06 --- /dev/null +++ b/playbooks/toolbox_satellite_hostgroup_change.yml @@ -0,0 +1,31 @@ +--- +- name: RH Satellite - VM Host Group Change + hosts: "{{ host | default('localhost') }}" + gather_facts: true + tasks: + + - name: Get host's satellite information + redhat.satellite.host_info: + username: "{{ hostvars[groups['satellite'][0]]['satellite_username'] }}" + password: "{{ hostvars[groups['satellite'][0]]['satellite_password'] }}" + server_url: "{{ hostvars[groups['satellite'][0]]['satellite_server_url'] }}" + name: "{{ host }}" + register: __out + + - name: Set new host group if it's a parent host group + ansible.builtin.set_fact: + satellite_hostgroup_post: "hg_rhel{{ ansible_distribution_version }}" + when: ((__out.host.hostgroup_title.split('/') | map('trim'))[:-1] | join('/')) == "" + + - name: Set new host group if it's not a parent host group + ansible.builtin.set_fact: + satellite_hostgroup_post: "{{ (__out.host.hostgroup_title.split('/') | map('trim'))[:-1] | join('/') }}/hg_rhel{{ ansible_distribution_version }}" + when: ((__out.host.hostgroup_title.split('/') | map('trim'))[:-1] | join('/')) != "" + + - name: Update satellite hostgroup + redhat.satellite.host: + username: "{{ hostvars[groups['satellite'][0]]['satellite_username'] }}" + password: "{{ hostvars[groups['satellite'][0]]['satellite_password'] }}" + server_url: "{{ hostvars[groups['satellite'][0]]['satellite_server_url'] }}" + name: "{{ host }}" + hostgroup: "{{ satellite_hostgroup_post }}" diff --git a/playbooks/toolbox_vm_update.yml b/playbooks/toolbox_vm_update.yml new file mode 100644 index 0000000..201b875 --- /dev/null +++ b/playbooks/toolbox_vm_update.yml @@ -0,0 +1,31 @@ +--- +- name: Update VM packages + hosts: "{{ host | default('localhost') }}" + become: true + gather_facts: true + + tasks: + - name: Update all VM packages # noqa package-latest + ansible.builtin.dnf: + name: "*" + state: latest + register: __update_result + + - name: Check if we need to reboot + ansible.builtin.command: dnf needs-restarting -r + ignore_errors: true + register: __reboot_hint + changed_when: false + failed_when: __reboot_hint.rc not in [0, 1] + + - name: Reboot the machine if required + ansible.builtin.reboot: + reboot_timeout: 1200 + when: __reboot_hint.rc | int == 1 + + - name: Wait for the server to come back online + ansible.builtin.wait_for_connection: + delay: 10 + timeout: 600 + sleep: 5 + when: __reboot_hint.rc | int == 1 diff --git a/playbooks/toolbox_vm_wait_response.yml b/playbooks/toolbox_vm_wait_response.yml new file mode 100644 index 0000000..5b9cecc --- /dev/null +++ b/playbooks/toolbox_vm_wait_response.yml @@ -0,0 +1,11 @@ +--- +- name: Check VM is reachable + hosts: "{{ host | default('localhost') }}" + gather_facts: false + + tasks: + - name: Wait until the host is reachable + become: false + ansible.builtin.wait_for_connection: + timeout: 600 + sleep: 5 diff --git a/playbooks/type_aap_configure.yml b/playbooks/type_aap_configure.yml new file mode 100644 index 0000000..961a656 --- /dev/null +++ b/playbooks/type_aap_configure.yml @@ -0,0 +1,7 @@ +--- +- name: "AAP Configuration" + ansible.builtin.import_playbook: function_aap_configure.yml + vars: + host: aap_controller + tags: + - aap_config diff --git a/playbooks/type_aap_create_infrastructure.yml b/playbooks/type_aap_create_infrastructure.yml new file mode 100644 index 0000000..fa77e85 --- /dev/null +++ b/playbooks/type_aap_create_infrastructure.yml @@ -0,0 +1,41 @@ +--- +- name: "AAP VMs - Create" + ansible.builtin.import_playbook: function_azure_vm_create.yml + when: + - bootstrap_target is defined + - bootstrap_target == "azure" + vars: + host: aap_intra + tags: + - aap_vm_create + +- name: "AAP VM - Check if Reachable" + ansible.builtin.import_playbook: toolbox_vm_wait_response.yml + vars: + host: aap_intra + tags: + - aap_vm_create + +- name: "AAP VM - Post Configure" + ansible.builtin.import_playbook: function_vm_post_configure.yml + vars: + host: aap_intra + tags: + - aap_vm_post_conf + +- name: "AAP VM - Create Managed Disk" + ansible.builtin.import_playbook: function_azure_managed_disk_create.yml + when: + - bootstrap_target is defined + - bootstrap_target == "azure" + vars: + host: aap_intra + tags: + - aap_vm_post_conf + +- name: "AAP VM - Run RHEL Storage" + ansible.builtin.import_playbook: function_rhel_managed_disk_configure.yml + vars: + host: aap_intra + tags: + - aap_vm_post_conf diff --git a/playbooks/type_aap_install.yml b/playbooks/type_aap_install.yml new file mode 100644 index 0000000..eeae096 --- /dev/null +++ b/playbooks/type_aap_install.yml @@ -0,0 +1,14 @@ +--- +- name: "AAP - Generate Host Certificates" + ansible.builtin.import_playbook: function_idm_generate_certs.yml + vars: + host: aap_intra + tags: + - aap_cert_generate + +- name: "AAP - Install" + ansible.builtin.import_playbook: function_aap_install.yml + vars: + host: bastion + tags: + - aap_install diff --git a/playbooks/type_bastion_create.yml b/playbooks/type_bastion_create.yml new file mode 100644 index 0000000..dc506b2 --- /dev/null +++ b/playbooks/type_bastion_create.yml @@ -0,0 +1,36 @@ +--- +- name: "Bastion VM - Create" + ansible.builtin.import_playbook: function_azure_vm_create.yml + when: + - bootstrap_target is defined + - bootstrap_target == "azure" + vars: + host: bastion + tags: + - bastion_vm_create + - bastion_vm_post_conf + - bastion_vm_conf_squid + +- name: "Bastion VM - Check if Reachable" + ansible.builtin.import_playbook: toolbox_vm_wait_response.yml + vars: + host: bastion + tags: + - bastion_vm_create + - bastion_vm_post_conf + - bastion_vm_conf_squid + +- name: "Bastion VM - Post Configure" + ansible.builtin.import_playbook: function_vm_post_configure.yml + vars: + host: bastion + tags: + - bastion_vm_post_conf + - bastion_vm_conf_squid + +- name: "Bastion VM - Configure Squid" + ansible.builtin.import_playbook: function_squid_configure.yml + vars: + host: bastion + tags: + - bastion_vm_conf_squid diff --git a/playbooks/type_cis_enforce.yml b/playbooks/type_cis_enforce.yml new file mode 100644 index 0000000..a1c3df1 --- /dev/null +++ b/playbooks/type_cis_enforce.yml @@ -0,0 +1,8 @@ +--- +- name: Enforce CIS on all hosts + ansible.builtin.import_playbook: function_cis_enforce.yml + when: enforce_cis | bool() + vars: + host: all + tags: + - enforce_cis diff --git a/playbooks/type_github_runner_create.yml b/playbooks/type_github_runner_create.yml new file mode 100644 index 0000000..c4fda9f --- /dev/null +++ b/playbooks/type_github_runner_create.yml @@ -0,0 +1,31 @@ +--- +- name: "Github Runner VM - Create" + ansible.builtin.import_playbook: function_azure_vm_create.yml + when: + - bootstrap_target is defined + - bootstrap_target == "azure" + vars: + host: github_runner + tags: + - github_runner_vm_create + +- name: "Github Runner VM - Check if Reachable" + ansible.builtin.import_playbook: toolbox_vm_wait_response.yml + vars: + host: github_runner + tags: + - github_runner_vm_create + +- name: "Github Runner VM - Post Configure" + ansible.builtin.import_playbook: function_vm_post_configure.yml + vars: + host: github_runner + tags: + - github_runner_vm_post_conf + +- name: "Github Runner VM - Create Github Runner" + ansible.builtin.import_playbook: function_github_runner_create.yml + vars: + host: github_runner + tags: + - github_runner_vm_create_github_runner diff --git a/playbooks/type_idm_satellite_register.yml b/playbooks/type_idm_satellite_register.yml new file mode 100644 index 0000000..b381146 --- /dev/null +++ b/playbooks/type_idm_satellite_register.yml @@ -0,0 +1,38 @@ +--- +- name: "Management Tools - Register to IdM" + ansible.builtin.import_playbook: function_idm_register.yml + vars: + host: misc:aap_intra + tags: + - mgmt_tools_register + +- name: "Management Tools - Register to Satellite" + ansible.builtin.import_playbook: function_satellite_register.yml + vars: + host: misc:aap_intra:idm + tags: + - mgmt_tools_register + +- name: "Bastion - Register to IdM" + ansible.builtin.import_playbook: function_idm_register.yml + vars: + host: bastion + tags: + - bastion_register + +- name: "Bastion - Register to Satellite" + ansible.builtin.import_playbook: function_satellite_register.yml + vars: + host: bastion + tags: + - bastion_register + +- name: "Azure private DNS clean up" + ansible.builtin.import_playbook: function_azure_private_dns_clean.yml + vars: + host: all:!rootca + when: + - bootstrap_target is defined + - bootstrap_target == "azure" + tags: + - bastion_register diff --git a/playbooks/type_ipa_config.yml b/playbooks/type_ipa_config.yml new file mode 100644 index 0000000..c74e1b7 --- /dev/null +++ b/playbooks/type_ipa_config.yml @@ -0,0 +1,7 @@ +--- +- name: "IDM Configuration" + ansible.builtin.import_playbook: function_idm_configuration.yml + vars: + host: ipahidden + tags: + - idm_configure diff --git a/playbooks/type_ipareplicas_create.yml b/playbooks/type_ipareplicas_create.yml new file mode 100644 index 0000000..4d78260 --- /dev/null +++ b/playbooks/type_ipareplicas_create.yml @@ -0,0 +1,31 @@ +--- +- name: "IDM replicas VM - Create" + ansible.builtin.import_playbook: function_azure_vm_create.yml + when: + - bootstrap_target is defined + - bootstrap_target == "azure" + vars: + host: ipareplicas + tags: + - idm_vm_create + +- name: "IDM VM - Check if Reachable" + ansible.builtin.import_playbook: toolbox_vm_wait_response.yml + vars: + host: ipareplicas + tags: + - idm_vm_create + +- name: "IDM VM - Post Configure" + ansible.builtin.import_playbook: function_vm_post_configure.yml + vars: + host: ipareplicas + tags: + - idm_vm_post_conf + +- name: "IDM VM - IDM Deploy Replica" + ansible.builtin.import_playbook: function_idm_ipareplicas_install.yml + vars: + host: ipareplicas + tags: + - idm_install_replica diff --git a/playbooks/type_ipaserver_create.yml b/playbooks/type_ipaserver_create.yml new file mode 100644 index 0000000..7fc8298 --- /dev/null +++ b/playbooks/type_ipaserver_create.yml @@ -0,0 +1,59 @@ +--- +- name: "IDM ipaserver VM - Create" + ansible.builtin.import_playbook: function_azure_vm_create.yml + when: + - bootstrap_target is defined + - bootstrap_target == "azure" + vars: + host: ipaserver + tags: + - idm_vm_create + +- name: "IDM VM - Check if Reachable" + ansible.builtin.import_playbook: toolbox_vm_wait_response.yml + vars: + host: ipaserver + tags: + - idm_vm_create + +- name: "IDM VM - Post Configure" + ansible.builtin.import_playbook: function_vm_post_configure.yml + vars: + host: ipaserver + tags: + - idm_vm_post_conf + +- name: "IDM VM - IDM Create Certificate Sign Request" + ansible.builtin.import_playbook: function_idm_csr_create.yml + vars: + host: ipaserver + tags: + - idm_install + - idm_create_csr + +- name: "RootCA VM - Sign CSR" + ansible.builtin.import_playbook: function_idm_sign_csr.yml + vars: + host: "{{ groups.rootca | first }}" + tags: + - idm_install + - idm_sign_csr + +- name: "IDM VM - Install IDM with Signed Certificate" + ansible.builtin.import_playbook: function_idm_ipaserver_install.yml + vars: + host: ipaserver + tags: + - idm_install + - idm_install_server + +- name: "Root VM - Shutdown and Lock VM" + ansible.builtin.import_playbook: function_azure_lock_vm.yml + when: + - bootstrap_target is defined + - bootstrap_target == "azure" + vars: + host: "{{ groups.rootca | first }}" + tags: + - idm_install + - shutdown_rootca diff --git a/playbooks/type_reverse_proxy_create.yml b/playbooks/type_reverse_proxy_create.yml new file mode 100644 index 0000000..8378b75 --- /dev/null +++ b/playbooks/type_reverse_proxy_create.yml @@ -0,0 +1,15 @@ +--- +- name: "Bastion VM - Create Public Trusted SSL Certificate" + ansible.builtin.import_playbook: function_public_certificate_create.yml + vars: + host: bastion + tags: + - bastion_vm_public_certificate + - bastion_vm_conf_nginx + +- name: "Bastion VM - Configure Nginx" + ansible.builtin.import_playbook: function_reverse_proxy_configure.yml + vars: + host: bastion + tags: + - bastion_vm_conf_nginx diff --git a/playbooks/type_rootca_create.yml b/playbooks/type_rootca_create.yml new file mode 100644 index 0000000..85e259d --- /dev/null +++ b/playbooks/type_rootca_create.yml @@ -0,0 +1,36 @@ +--- +- name: "RootCA VM - Create" + ansible.builtin.import_playbook: function_azure_vm_create.yml + when: + - bootstrap_target is defined + - bootstrap_target == "azure" + vars: + host: rootca + tags: + - rootca_vm_create + - rootca_vm_post_conf + - rootca_vm_create_ca + +- name: "RootCA VM - Check if Reachable" + ansible.builtin.import_playbook: toolbox_vm_wait_response.yml + vars: + host: rootca + tags: + - rootca_vm_create + - rootca_vm_post_conf + - rootca_vm_conf_squid + +- name: "RootCA VM - Post Configure" + ansible.builtin.import_playbook: function_vm_post_configure.yml + vars: + host: rootca + tags: + - rootca_vm_post_conf + - rootca_vm_create_ca + +- name: "RootCA VM - Create CA" + ansible.builtin.import_playbook: function_ca_create.yml + vars: + host: rootca + tags: + - rootca_vm_create_ca diff --git a/playbooks/type_satellite_configure.yml b/playbooks/type_satellite_configure.yml new file mode 100644 index 0000000..94121cb --- /dev/null +++ b/playbooks/type_satellite_configure.yml @@ -0,0 +1,7 @@ +--- +- name: "Satellite VM - Configure Satellite" + ansible.builtin.import_playbook: function_satellite_configure.yml + vars: + host: satellite + tags: + - satellite_install_configure diff --git a/playbooks/type_satellite_install.yml b/playbooks/type_satellite_install.yml new file mode 100644 index 0000000..ea241c0 --- /dev/null +++ b/playbooks/type_satellite_install.yml @@ -0,0 +1,65 @@ +--- +- name: "Satellite VM - Create" + ansible.builtin.import_playbook: function_azure_vm_create.yml + when: + - bootstrap_target is defined + - bootstrap_target == "azure" + vars: + host: satellite + tags: + - satellite_vm_create + +- name: "Satellite VM - Check if Reachable" + ansible.builtin.import_playbook: toolbox_vm_wait_response.yml + vars: + host: satellite + tags: + - satellite_vm_create + +- name: "Satellite VM - Post Configure" + ansible.builtin.import_playbook: function_vm_post_configure.yml + vars: + host: satellite + tags: + - satellite_vm_post_conf + +- name: "Satellite VM - Create Managed Disk" + ansible.builtin.import_playbook: function_azure_managed_disk_create.yml + when: + - bootstrap_target is defined + - bootstrap_target == "azure" + vars: + host: satellite + tags: + - satellite_vm_managed_disk + +- name: "Satellite VM - Run RHEL Storage" + ansible.builtin.import_playbook: function_rhel_managed_disk_configure.yml + vars: + host: satellite + tags: + - satellite_vm_managed_disk + +- name: "Satellite VM - Register IdM" + ansible.builtin.import_playbook: function_idm_register.yml + vars: + host: satellite + tags: + - satellite_vm_register_idm + +- name: "Satellite VM - Generate Host Certificates" + ansible.builtin.import_playbook: function_idm_generate_certs.yml + vars: + host: satellite + tags: + - satellite_cert_generate + - satellite_install + - satellite_install_server + +- name: "Satellite VM - Install Satellite" + ansible.builtin.import_playbook: function_satellite_install.yml + vars: + host: satellite + tags: + - satellite_install + - satellite_install_server diff --git a/playbooks/type_workload_dmz_resources_create.yml b/playbooks/type_workload_dmz_resources_create.yml new file mode 100644 index 0000000..bba0df3 --- /dev/null +++ b/playbooks/type_workload_dmz_resources_create.yml @@ -0,0 +1,52 @@ +--- +- name: "Workload DMZ - Create Temp Host for Management" + ansible.builtin.import_playbook: toolbox_add_temp_host_to_group.yml + vars: + host: bastion + host_to_add: temp_host_mgmt_tools_dmz + group_to_add: mgmt_tools_dmz + tags: + - workload_dmz_temp_host_mgmt + +- name: "Workload DMZ - Create Network for Management" + ansible.builtin.import_playbook: function_azure_network_create.yml + when: + - bootstrap_target is defined + - bootstrap_target == "azure" + vars: + host: temp_host_mgmt_tools_dmz + tags: + - workload_dmz_network_create_mgmt + +- name: "Workload DMZ - Create Images for Management" + ansible.builtin.import_playbook: function_imagebuilder_create.yml + vars: + host: temp_host_mgmt_tools_dmz + tags: + - workload_dmz_images_create_mgmt + +- name: "Workload DMZ - Create Temp Host for Workloads" + ansible.builtin.import_playbook: toolbox_add_temp_host_to_group.yml + vars: + host: bastion + host_to_add: temp_host_workload_servers_dmz + group_to_add: workload_servers_dmz + tags: + - workload_dmz_temp_host_workloads + +- name: "Workload DMZ - Create Network for Workloads" + ansible.builtin.import_playbook: function_azure_network_create.yml + when: + - bootstrap_target is defined + - bootstrap_target == "azure" + vars: + host: temp_host_workload_servers_dmz + tags: + - workload_dmz_network_create_workloads + +- name: "Workload DMZ - Create Images for Workloads" + ansible.builtin.import_playbook: function_imagebuilder_create.yml + vars: + host: temp_host_workload_servers_dmz + tags: + - workload_dmz_images_create_workloads diff --git a/playbooks/type_workload_intra_resources_create.yml b/playbooks/type_workload_intra_resources_create.yml new file mode 100644 index 0000000..8cfd9cc --- /dev/null +++ b/playbooks/type_workload_intra_resources_create.yml @@ -0,0 +1,26 @@ +--- +- name: "Workload Intra - Create Temp Host" + ansible.builtin.import_playbook: toolbox_add_temp_host_to_group.yml + vars: + host: bastion + host_to_add: temp_host_workload_servers_intra + group_to_add: workload_servers_intra + tags: + - workload_intra_temp_host + +- name: "Workload Intra - Create Network" + ansible.builtin.import_playbook: function_azure_network_create.yml + when: + - bootstrap_target is defined + - bootstrap_target == "azure" + vars: + host: temp_host_workload_servers_intra + tags: + - workload_intra_network_create + +- name: "Workload Intra - Create Images" + ansible.builtin.import_playbook: function_imagebuilder_create.yml + vars: + host: temp_host_workload_servers_intra + tags: + - workload_intra_images_create diff --git a/playbooks/type_workload_vm_create.yml b/playbooks/type_workload_vm_create.yml new file mode 100644 index 0000000..768d473 --- /dev/null +++ b/playbooks/type_workload_vm_create.yml @@ -0,0 +1,9 @@ +--- +- name: "Workload Intra VM - Create" + ansible.builtin.import_playbook: function_satellite_vm_deploy.yml + +- name: "Workload Intra VM - Check if Reachable" + ansible.builtin.import_playbook: toolbox_vm_wait_response.yml + +- name: "Workload Intra VM - Run Post Configuration" + ansible.builtin.import_playbook: function_vm_post_configure.yml diff --git a/roles/aap_config/README.md b/roles/aap_config/README.md new file mode 100644 index 0000000..e7e7783 --- /dev/null +++ b/roles/aap_config/README.md @@ -0,0 +1,74 @@ +aap_config +========= + +This role configures Controller and Private Automation Hub. + +Requirements +------------ + +- Ansible Automation Platform deployed and running. +- Installed Ansible Automation Platform credentials should have been placed under inventory. +- Configuration variables are defined under inventory. + +Role Variables +-------------- + +```yaml +# variables +aap_config: true +aap_config_pah_repo_sync: true +aap_config_organization: "" + +aap_config_inventory_repo_name: "" +aap_config_default_project: "" +aap_config_default_inventory: "" + +aap_config_default_credentials: + - "Machine Credential" + - "Vault Credential" +aap_config_default_notification_template: "Slack Notifications" +aap_config_default_execution_environment: "Automation Hub Default Remote execution environment" +aap_config_default_job_run: "run" + +aap_config_default_scm_branch: "" + +controller_configuration_projects_async_retries: "90" + +controller_hostname: "" +controller_username: "" +controller_password: "{" + +ah_hostname: "" +ah_username: "" +ah_password: "" +``` + +Dependencies +------------ + +- ansible.controller +- infra.controller_configuration +- infra.ee_utilities +- infra.ah_configuration + +Example Playbook +---------------- + +```yaml +- hosts: bastion + roles: + - role: pki_idm_generate_certs + become: true + - role: aap_install + - role: aap_config +``` + +License +------- + +GPL-3.0-only + +Author Information +------------------ + +Showroom Project Team diff --git a/roles/aap_config/defaults/main.yml b/roles/aap_config/defaults/main.yml new file mode 100644 index 0000000..c3490e8 --- /dev/null +++ b/roles/aap_config/defaults/main.yml @@ -0,0 +1,2 @@ +--- +aap_config_pah_repo_sync: true diff --git a/roles/aap_config/meta/main.yml b/roles/aap_config/meta/main.yml new file mode 100644 index 0000000..35456b4 --- /dev/null +++ b/roles/aap_config/meta/main.yml @@ -0,0 +1,19 @@ +--- +galaxy_info: + author: Showroom Project Team + description: This role configures Red Hat Ansible Automation Platform. + company: Red Hat, Inc. + license: GPL-3.0-only + min_ansible_version: "2.14" + platforms: + - name: EL + versions: + - "8" + - "9" + galaxy_tags: [] +dependencies: [] +collections: + - ansible.controller + - infra.controller_configuration + - infra.ee_utilities + - infra.ah_configuration diff --git a/roles/aap_config/tasks/create_token.yml b/roles/aap_config/tasks/create_token.yml new file mode 100644 index 0000000..b806e1b --- /dev/null +++ b/roles/aap_config/tasks/create_token.yml @@ -0,0 +1,31 @@ +--- +- name: Check if admin token exists + ansible.builtin.uri: + url: "https://{{ controller_hostname | regex_replace('(/+)$') }}/api/v2/tokens/?search={{ __controller_tokens.application.split()[0] }}" + method: GET + validate_certs: true + user: "{{ controller_username }}" + password: "{{ controller_password }}" + force_basic_auth: true + status_code: 200 + register: __aap_token + +- name: Create a new token using username/password + ansible.controller.token: + application: "{{ __controller_tokens.application }}" + description: "{{ __controller_tokens.description | default(omit, true) }}" + scope: "{{ __controller_tokens.scope | default(omit, true) }}" + state: "{{ __controller_tokens.state | default(controller_state | default('present')) }}" + controller_host: "{{ controller_hostname }}" + controller_username: "{{ controller_username }}" + controller_password: "{{ controller_password }}" + register: aap_token_output + when: __aap_token.json.count < 1 + +- name: Copy Token to a file for a backup + become: true + ansible.builtin.copy: + content: "{{ aap_token_output.ansible_facts.controller_token.token }}" + dest: "/root//{{ __controller_tokens.application.split()[0] + '.token' }}" + mode: "0600" + when: __aap_token.json.count < 1 diff --git a/roles/aap_config/tasks/custom_ee.yml b/roles/aap_config/tasks/custom_ee.yml new file mode 100644 index 0000000..57c689d --- /dev/null +++ b/roles/aap_config/tasks/custom_ee.yml @@ -0,0 +1,49 @@ +--- +- name: Create Custom EE + become: true + block: + - name: Enable AAP repository + community.general.rhsm_repository: + name: "ansible-automation-platform-2.4-for-rhel-9-x86_64-rpms" + state: enabled + + - name: Ensure ansible-builder and podman installed + ansible.builtin.dnf: + name: + - podman + - ansible-builder + state: present + +- name: Configure podman + when: ee_podman_config is defined + block: + - name: Update /etc/subuid + become: true + ansible.builtin.lineinfile: + line: "{{ ee_podman_config }}" + dest: /etc/subuid + + - name: Update /etc/subgid + become: true + ansible.builtin.lineinfile: + line: "{{ ee_podman_config }}" + dest: /etc/subgid + + - name: Gather facts + ansible.builtin.gather_facts: + when: ansible_user_uid is not defined + + - name: Run loginctl enable-linger + ansible.builtin.command: "loginctl enable-linger {{ ansible_user_uid }}" + register: __cmd_out + changed_when: __cmd_out.changed + +- name: Login to registry.redhat.io + containers.podman.podman_login: + username: "{{ ee_base_registry_username }}" + password: "{{ ee_base_registry_password }}" + registry: lachesis.internal.showroom.run + +- name: Create Custom EE + ansible.builtin.import_role: + name: infra.ee_utilities.ee_builder diff --git a/roles/aap_config/tasks/main.yml b/roles/aap_config/tasks/main.yml new file mode 100644 index 0000000..98e8e93 --- /dev/null +++ b/roles/aap_config/tasks/main.yml @@ -0,0 +1,169 @@ +--- +- name: Configure Controller License + ansible.builtin.import_role: + name: infra.controller_configuration.license + when: controller_license is defined + tags: aap_config_license + +- name: Configure Private Hub Remote Repository + ansible.builtin.import_role: + name: infra.ah_configuration.collection_remote + when: ah_collection_remotes is defined + tags: aap_config_collection_remote + +- name: Configure Private Hub Repository + ansible.builtin.import_role: + name: infra.ah_configuration.collection_repository + when: ah_collection_repositories is defined + tags: aap_config_collection_repository + +- name: Configure Private Hub Repository Sync + ansible.builtin.import_role: + name: infra.ah_configuration.collection_repository_sync + when: (ah_collection_remotes is defined) and aap_config_pah_repo_sync | bool() + tags: aap_config_collection_repository_sync + +- name: Configure Private Hub Execution Environment Registry + ansible.builtin.import_role: + name: infra.ah_configuration.ee_registry + when: ah_ee_registries is defined + tags: aap_config_ah_ee_registry + +- name: Configure Private Hub Execution Environment Registry Sync + ansible.builtin.import_role: + name: infra.ah_configuration.ee_registry_sync + when: ah_ee_registries is defined + tags: aap_config_ah_ee_registry_sync + +- name: Configure Private Hub Execution Environment Repository + ansible.builtin.import_role: + name: infra.ah_configuration.ee_repository + when: ah_ee_repositories is defined + tags: aap_config_ah_ee_repository + +- name: Configure Private Hub Execution Environment Repository Sync + ansible.builtin.import_role: + name: infra.ah_configuration.ee_repository_sync + when: ah_ee_repositories is defined + tags: aap_config_ah_ee_repository_sync + +- name: Create Custom EE + ansible.builtin.include_tasks: custom_ee.yml + when: ee_list is defined + tags: aap_config_custom_ee + +- name: Configure Controller Settings + ansible.builtin.import_role: + name: infra.controller_configuration.settings + when: controller_settings is defined + tags: aap_config_settings + +- name: Configure Controller Instance Groups + ansible.builtin.import_role: + name: infra.controller_configuration.instance_groups + when: controller_instance_groups is defined + tags: aap_config_instance_groups + +- name: Configure Controller Organizations + ansible.builtin.import_role: + name: infra.controller_configuration.organizations + when: controller_organizations is defined + tags: aap_config_organizations + +- name: Configure Controller Users + ansible.builtin.import_role: + name: infra.controller_configuration.users + when: controller_user_accounts is defined + tags: aap_config_users + +- name: Configure Controller Teams + ansible.builtin.import_role: + name: infra.controller_configuration.teams + when: controller_teams is defined + tags: aap_config_teams + +- name: Configure Controller Roles + ansible.builtin.import_role: + name: infra.controller_configuration.roles + when: controller_roles is defined + tags: aap_config_roles + +- name: Configure Controller Credential Types + ansible.builtin.import_role: + name: infra.controller_configuration.credential_types + when: controller_credential_types is defined + tags: aap_config_credential_types + +- name: Configure Controller Credentials + ansible.builtin.import_role: + name: infra.controller_configuration.credentials + when: controller_credentials is defined + tags: aap_config_credentials + +- name: Configure Controller Inventories + ansible.builtin.import_role: + name: infra.controller_configuration.inventories + when: controller_inventories is defined + tags: aap_config_inventories + +- name: Configure Controller Execution Environments + ansible.builtin.import_role: + name: infra.controller_configuration.execution_environments + when: controller_execution_environments is defined + tags: aap_config_execution_environments + +- name: Configure Controller Notification Templates + ansible.builtin.import_role: + name: infra.controller_configuration.notification_templates + when: controller_notifications is defined + tags: aap_config_notification_templates + +- name: Configure Controller Projects + ansible.builtin.import_role: + name: infra.controller_configuration.projects + when: controller_projects is defined + tags: aap_config_projects + +- name: Configure Controller Inventory Sources + ansible.builtin.import_role: + name: infra.controller_configuration.inventory_sources + when: controller_inventory_sources is defined + tags: aap_config_inventory_sources + +- name: Configure Controller Labels + ansible.builtin.import_role: + name: infra.controller_configuration.labels + when: controller_labels is defined + tags: aap_config_labels + +- name: Configure Controller Job Templates + ansible.builtin.import_role: + name: infra.controller_configuration.job_templates + when: controller_templates is defined + tags: aap_config_job_templates + +- name: Configure Controller Workflow Templates + ansible.builtin.import_role: + name: infra.controller_configuration.workflow_job_templates + when: controller_workflows is defined + tags: aap_config_workflow_templates + +- name: Configure Controller Schedules + ansible.builtin.import_role: + name: infra.controller_configuration.schedules + when: controller_schedules is defined + tags: aap_config_schedules + +- name: Configure Controller Applications + ansible.builtin.import_role: + name: infra.controller_configuration.applications + when: controller_applications is defined + tags: aap_config_applications + +- name: Create Token on AAP to use for GitHub Workflows + ansible.builtin.include_tasks: create_token.yml + loop: "{{ controller_tokens }}" + loop_control: + loop_var: __controller_tokens + when: controller_tokens is defined + tags: tokens diff --git a/roles/aap_install/README.md b/roles/aap_install/README.md new file mode 100644 index 0000000..0a9843f --- /dev/null +++ b/roles/aap_install/README.md @@ -0,0 +1,50 @@ +azure_dns +========= + +This role installs AAP in an automated way. + +Requirements +------------ + +- A Red Hat Satellite server should be provisioned and AAP contents downloaded. +- Installed Satellite credentials should have been placed under inventory. +- Host certificates should have been created on the hosts by IDM. + +Role Variables +-------------- + +```yaml +# default variables +aap_install_repo_name: "Red Hat Ansible Automation Platform 2.4 for RHEL 9 x86_64 Files" +aap_install_product: "Red Hat Ansible Automation Platform" +aap_install_search_param: "name ~ platform-setup" +aap_install_destination_dir: "/var/aap-install" +aap_install_certs_dir: "{{ aap_install_destination_dir }}/certs" +aap_install_aap_bundle_file: "ansible-automation-platform-setup-2.4-7.tar.gz" +``` + +Dependencies +------------ + +- redhat.satellite + +Example Playbook +---------------- + +```yaml +- hosts: bastion + roles: + - role: pki_idm_generate_certs + become: true + - role: aap_install +``` + +License +------- + +GPL-3.0-only + +Author Information +------------------ + +Showroom Project Team diff --git a/roles/aap_install/defaults/main.yml b/roles/aap_install/defaults/main.yml new file mode 100644 index 0000000..36a2409 --- /dev/null +++ b/roles/aap_install/defaults/main.yml @@ -0,0 +1,7 @@ +--- +aap_install_repo_name: "Red Hat Ansible Automation Platform 2.4 for RHEL 9 x86_64 Files" +aap_install_product: "Red Hat Ansible Automation Platform" +aap_install_search_param: "name ~ platform-setup" +aap_install_destination_dir: "/var/aap-install" +aap_install_certs_dir: "{{ aap_install_destination_dir }}/certs" +aap_install_aap_bundle_file: "ansible-automation-platform-setup-2.4-7.tar.gz" diff --git a/roles/aap_install/meta/main.yml b/roles/aap_install/meta/main.yml new file mode 100644 index 0000000..c96223a --- /dev/null +++ b/roles/aap_install/meta/main.yml @@ -0,0 +1,16 @@ +--- +galaxy_info: + author: Showroom Project Team + description: This role installs AAP in an automated way. + company: Red Hat, Inc. + license: GPL-3.0-only + min_ansible_version: "2.14" + platforms: + - name: EL + versions: + - "8" + - "9" + galaxy_tags: [] +dependencies: [] +collections: + - redhat.satellite diff --git a/roles/aap_install/tasks/get_aap_installer_bundle.yml b/roles/aap_install/tasks/get_aap_installer_bundle.yml new file mode 100644 index 0000000..abb4cf7 --- /dev/null +++ b/roles/aap_install/tasks/get_aap_installer_bundle.yml @@ -0,0 +1,68 @@ +--- +- name: "Get the file repository info" + redhat.satellite.repository_info: + username: "{{ hostvars[groups.satellite | first]['satellite_username'] }}" + password: "{{ hostvars[groups.satellite | first]['satellite_password'] }}" + server_url: "https://{{ groups.satellite | first }}" + organization: "{{ hostvars[groups.satellite | first]['satellite_initial_organization'] }}" + name: "{{ aap_install_repo_name }}" + product: "{{ aap_install_product }}" + register: __result + +- name: "Determine the base url" + ansible.builtin.set_fact: + __software_baseurl: "{{ __result.repository.full_path }}" + +- name: "Get the name of the bundle file" + redhat.satellite.resource_info: + username: "{{ hostvars[groups.satellite | first]['satellite_username'] }}" + password: "{{ hostvars[groups.satellite | first]['satellite_password'] }}" + server_url: "https://{{ groups.satellite | first }}" + organization: "{{ hostvars[groups.satellite | first]['satellite_initial_organization'] }}" + resource: file_units + search: "{{ aap_install_search_param }}" + register: __files + +- name: "Set the path for the download" + ansible.builtin.set_fact: + __download_path: "{{ __software_baseurl }}/{{ __files.resources[0].name }}" + +- name: "Ensure the destination directory exists" + become: true + ansible.builtin.file: + path: "{{ aap_install_destination_dir }}" + state: directory + owner: "{{ vm_user }}" + group: "{{ vm_user }}" + mode: "0750" + +- name: "Clean up any failed downloads" + ansible.builtin.file: + path: "{{ aap_install_destination_dir }}/{{ aap_install_aap_bundle_file }}" + state: absent + +- name: Downloading the latest installer of type + ansible.builtin.get_url: + url: "{{ __download_path }}" + dest: "{{ aap_install_destination_dir }}/{{ aap_install_aap_bundle_file }}" + owner: "{{ vm_user }}" + group: "{{ vm_user }}" + mode: "0644" + register: __aap_setup_down_downloads + +- name: Extract the name of the downloaded installer to aap_setup_down_installer_file + ansible.builtin.set_fact: + __aap_setup_down_installer_file: "{{ __aap_setup_down_downloads.dest }}" + +- name: Extract the given installer tarball + ansible.builtin.unarchive: + src: "{{ __aap_setup_down_installer_file }}" + dest: "{{ aap_install_destination_dir }}" + list_files: true + remote_src: true + exclude: inventory + register: __aap_setup_prep_extract + +- name: Set aap_setup_prep_setup_dir + ansible.builtin.set_fact: + __aap_setup_prep_setup_dir: "{{ aap_install_destination_dir }}/{{ __aap_setup_prep_extract.files[0] }}" diff --git a/roles/aap_install/tasks/get_host_ssl_files.yml b/roles/aap_install/tasks/get_host_ssl_files.yml new file mode 100644 index 0000000..18f4403 --- /dev/null +++ b/roles/aap_install/tasks/get_host_ssl_files.yml @@ -0,0 +1,39 @@ +--- +- name: Handle SSL certificates + become: true + block: + - name: "Ensure the destination directory exists" + ansible.builtin.file: + path: "{{ aap_install_certs_dir }}" + state: directory + owner: "{{ vm_user }}" + group: "{{ vm_user }}" + mode: "0750" + + - name: Read host SSL cert + ansible.builtin.slurp: + src: "/etc/ipa/private/{{ delegated_host }}.crt" + delegate_to: "{{ delegated_host }}" + register: __host_ssl_cert + + - name: Write host SSL cert + ansible.builtin.copy: + content: "{{ __host_ssl_cert.content | b64decode }}" + dest: "{{ aap_install_certs_dir }}/{{ delegated_host }}.crt" + owner: "{{ vm_user }}" + group: "{{ vm_user }}" + mode: "0640" + + - name: Read host SSL key + ansible.builtin.slurp: + src: "/etc/ipa/private/{{ delegated_host }}.key" + delegate_to: "{{ delegated_host }}" + register: __host_ssl_key + + - name: Write host SSL key + ansible.builtin.copy: + content: "{{ __host_ssl_key.content | b64decode }}" + dest: "{{ aap_install_certs_dir }}/{{ delegated_host }}.key" + owner: "{{ vm_user }}" + group: "{{ vm_user }}" + mode: "0640" diff --git a/roles/aap_install/tasks/launch_setup.yml b/roles/aap_install/tasks/launch_setup.yml new file mode 100644 index 0000000..bd2f6b7 --- /dev/null +++ b/roles/aap_install/tasks/launch_setup.yml @@ -0,0 +1,12 @@ +--- +- name: Run Setup Ansible Automation Platform + ansible.builtin.shell: "./setup.sh -e @ldapextras.yml" # noqa: command-instead-of-shell + environment: + ANSIBLE_BECOME_METHOD: "sudo" + ANSIBLE_BECOME: "true" + ANSIBLE_HOST_KEY_CHECKING: "false" + args: + chdir: "{{ __aap_setup_prep_setup_dir }}" + register: __aap_install_result + changed_when: __aap_install_result.changed + failed_when: __aap_install_result.failed diff --git a/roles/aap_install/tasks/main.yml b/roles/aap_install/tasks/main.yml new file mode 100644 index 0000000..01544e2 --- /dev/null +++ b/roles/aap_install/tasks/main.yml @@ -0,0 +1,15 @@ +--- +- name: Get AAP installer bundle + ansible.builtin.include_tasks: get_aap_installer_bundle.yml + +- name: Get host SSL files + ansible.builtin.include_tasks: get_host_ssl_files.yml + loop: "{{ groups.aap_intra }}" + loop_control: + loop_var: delegated_host + +- name: Populate inventory + ansible.builtin.include_tasks: populate_installer_inventory.yml + +- name: Run AAP installation + ansible.builtin.include_tasks: launch_setup.yml diff --git a/roles/aap_install/tasks/populate_installer_inventory.yml b/roles/aap_install/tasks/populate_installer_inventory.yml new file mode 100644 index 0000000..8c9d9e9 --- /dev/null +++ b/roles/aap_install/tasks/populate_installer_inventory.yml @@ -0,0 +1,12 @@ +--- +- name: Populate inventory file from template + ansible.builtin.template: + src: inventory.j2 + dest: "{{ __aap_setup_prep_setup_dir }}/inventory" + mode: "0640" + +- name: Populate extra ldap file from template + ansible.builtin.template: + src: ldapextras.yml.j2 + dest: "{{ __aap_setup_prep_setup_dir }}/ldapextras.yml" + mode: "0640" diff --git a/roles/aap_install/templates/inventory.j2 b/roles/aap_install/templates/inventory.j2 new file mode 100644 index 0000000..f7cd280 --- /dev/null +++ b/roles/aap_install/templates/inventory.j2 @@ -0,0 +1,282 @@ +# Automation Controller Nodes +# There are two valid node_types that can be assigned for this group. +# A node_type=control implies that the node will only be able to run +# project and inventory updates, but not regular jobs. +# A node_type=hybrid will have the ability to run everything. +# If you do not define the node_type, it defaults to hybrid. +# +# control.example node_type=control +# hybrid.example node_type=hybrid +# hybrid2.example <- this will default to hybrid +[automationcontroller] +{{ groups['aap_controller'][0] }} node_type=control + +[automationcontroller:vars] +peers=execution_nodes + +# Execution Nodes +# There are two valid node_types that can be assigned for this group. +# A node_type=hop implies that the node will forward jobs to an execution node. +# A node_type=execution implies that the node will be able to run jobs. +# If you do not define the node_type, it defaults to execution. +# +# hop.example node_type=hop +# execution.example node_type=execution +# execution2.example <- this will default to execution +[execution_nodes] +{% for item in groups['aap_execution_node_intra'] %}{{ item + '\n'}}{% endfor %} + +[automationhub] +{% for item in groups['aap_pah'] %}{{ item + '\n'}}{% endfor %} + +# [automationedacontroller] +{# +# {% for item in groups['Ansible_Automation_Eda_Controller'] %}{{ item + '\n'}}{% endfor %} +#} + +[database] +{% for item in groups['aap_db'] %}{{ item + '\n'}}{% endfor %} + +# Single Sign-On +# If sso_redirect_host is set, that will be used for application to connect to +# SSO for authentication. This must be reachable from client machines. +# +# ssohost.example sso_redirect_host= +# [sso] +{# +# {% for item in groups['Ansible_Automation_SSO'] %}{{ item + '\n'}}{% endfor %} +#} + +[all:vars] +admin_password='{{ aap_admin_password }}' + +pg_host='{{ groups['aap_db'][0] }}' +pg_port='5432' + +pg_database='awx' +pg_username='awx' +pg_password='{{ aap_pg_password }}' +pg_sslmode='prefer' # set to 'verify-full' for client-side enforced SSL + +# Managed Postgres Options + +# If you wish to install AAP with a single managed postgres server and would +# like to modify the port for postgres, set the following variable. Note that +# the *pg_port variables for the components should be changed to match the +# value set by this variable if you wish to connect that component to this +# managed postgres database server. + +# install_pg_port=5432 + +# Execution Environment Configuration +# + +# Credentials for container registry to pull execution environment images from, +# registry_username and registry_password are required for registry.redhat.io +# +# When deployed with Automation Hub: +# - The installer will push execution environment images to Automation Hub and +# configure Automation Controller to pull images from the Hub registry. +# - To make Hub to be the only registry to pull execution environment images from, +# set 'ee_from_hub_only' to True. This is set to True by default when bundle +# installer is used. + +# +registry_url='registry.redhat.io' +registry_username='{{ registry_username }}' +registry_password='{{ registry_token }}' + +ee_from_hub_only=True + +# If you wish to add Ansible Engine 2.9 execution environment, set the following variable to true. +# However this is only available for x86_64 architecture. + +# ee_29_enabled=false + +# Receptor Configuration +# +receptor_listener_port=27199 + +# Automation Hub Configuration +# +automationhub_admin_username="{{ automationhub_admin_username }}" +automationhub_admin_password="{{ automationhub_admin_password }}" + +automationhub_pg_host='{{ groups['aap_db'][0] }}' +automationhub_pg_port='5432' + +automationhub_pg_database='automationhub' +automationhub_pg_username='automationhub' +automationhub_pg_password='{{ automationhub_pg_password }}' +automationhub_pg_sslmode='prefer' + +# Set to True to overwrite existing admin password. +# +# automationhub_force_change_admin_password = False + +# The main automation hub URL that clients will connect to (e.g. https://). +# If not specified, the first node in the [automationhub] group will be used when needed. +# +# automationhub_main_url = '' + +# By default if the automation hub package and its dependencies +# are installed they won't get upgraded when running the installer +# even if newer packages are available. One needs to run the ./setup.sh +# script with the following set to True. +# +automationhub_upgrade = True + +# By default when one uploads collections to Automation Hub +# an admin needs to approve it before it is made available +# to the users. If one wants to disable the content approval +# flow, the following setting should be set to False. +# +automationhub_require_content_approval = True + +# At import time collections can go through a series of checks. +# Behaviour is driven by galaxy-importer.cfg configuration. +# Example are ansible-doc, ansible-lint, flake8, ... +# +# The following parameter allow one to drive this configuration. +# This variable is expected to be a dictionary. +# +# automationhub_importer_settings = None + +# The default install will deploy a TLS enabled Automation Hub. +# If for some reason this is not the behavior wanted one can +# disable TLS enabled deployment. +# +# automationhub_disable_https = False + +# The default install will deploy a TLS enabled Automation Hub. +# Unless specified otherwise the HSTS web-security policy mechanism +# will be enabled. This setting allows one to disable it if need be. +# +# automationhub_disable_hsts = False + +# The default install will not create a signing service. If set to true +# a signing service will be created. + +# automationhub_create_default_collection_signing_service = False +# automationhub_create_default_container_signing_service = False + +# If a signing service is enabled, one must provide a signing script and a key. +# Note: these MUST be absolute paths. + +# automationhub_collection_signing_service_key = /absolute/path/to/key/to/sign +# automationhub_collection_signing_service_script = /absolute/path/to/script/that/signs + +# automationhub_container_signing_service_key = /absolute/path/to/key/to/sign +# automationhub_container_signing_service_script = /absolute/path/to/script/that/signs + +# If a collection signing service is enabled, collections won't be signed automatically by default. +# The following parameter will have them signed by default. +# +# automationhub_auto_sign_collections = False + +# If upgrading from Ansible Automation Platform 2.0 or earlier, you must either: +# - provide an existing Automation Hub token as 'automationhub_api_token' or +# - set 'generate_automationhub_token' to True to generate a new token +# Generating a new token will invalidate the existing token. +# +# automationhub_api_token='' +# generate_automationhub_token= + +# Automation Hub LDAP configuration +# +# For Automation Hub to connect to LDAP directly the following variables +# need to be configured. The list of all possible configuration can be found here: +# https://django-auth-ldap.readthedocs.io/en/latest/reference.html#settings +# Extra parameters will need to be passed through an ansible ldap_extra_settings dictionary. +# +automationhub_authentication_backend = "ldap" + +automationhub_ldap_server_uri = "ldap://{{ groups.ipaserver | first }}:389" +automationhub_ldap_bind_dn = "uid={{ ldap_bind_principal }},cn=users,cn=accounts,{{ ldap_domain_map }}" +automationhub_ldap_bind_password = "{{ ldap_bind_password }}" +automationhub_ldap_user_search_base_dn = "cn=users,cn=accounts,{{ ldap_domain_map }}" +automationhub_ldap_group_search_base_dn = "cn=groups,cn=accounts,{{ ldap_domain_map }}" + +# By default, bundle installer seeds certified and validated collections into +# Automation Hub. Set to False to disable the seeding. +# +# automationhub_seed_collections = True + +# Automation EDA Controller Configuration +# + +{# +# automationedacontroller_admin_password='{{ automationedacontroller_password }}' + + +# automationedacontroller_pg_host='{{ groups['Ansible_Automation_Postgresql_Database'][0] }}' +# automationedacontroller_pg_port=5432 + +# automationedacontroller_pg_database='automationedacontroller' +# automationedacontroller_pg_username='automationedacontroller' +# automationedacontroller_pg_password='{{ automationedacontroller_pg_password }}' +# automationedacontroller_pg_sslmode='prefer' + +# Certificate and key to install in Automation EDa Controller +# automationedacontroller_ssl_cert='{{ aap_setup_base_dir }}/{{ groups['Ansible_Automation_Eda_Controller'][0] }}.cert.pem' +# automationedacontroller_ssl_key='{{ aap_setup_base_dir }}/{{ groups['Ansible_Automation_Eda_Controller'][0] }}.key' +#} +# The full routeable URL used by EDA to connect to a controller host. +# This URL is required if there is no Automation Controller configured +# in inventory. +# +# Format example: automation_controller_main_url='https://' +# +# automation_controller_main_url = '' + +# Boolean flag used to verify Automation Controller's +# web certificates when making calls from Automation EDA Controller. +# +# automationedacontroller_controller_verify_ssl = True + +# The default install will generate self-signed certificates for the Automation +# Hub service. If you are providing valid certificate via automationhub_ssl_cert +# and automationhub_ssl_key, one should toggle that value to True. +# +automationhub_ssl_validate_certs = True + +# SSL-related variables + +{# +# If set, this will install a custom CA certificate to the system trust store. +# custom_ca_cert='{{ ca_directory }}{{ root_ca_file_name }}' +#} + +# Certificate and key to install in nginx for the web UI and API +web_server_ssl_cert='{{ aap_install_certs_dir }}/{{ groups['aap_controller'][0] }}.crt' +web_server_ssl_key='{{ aap_install_certs_dir }}/{{ groups['aap_controller'][0] }}.key' + +# Certificate and key to install in Automation Hub node +automationhub_ssl_cert='{{ aap_install_certs_dir }}/{{ groups['aap_pah'][0] }}.crt' +automationhub_ssl_key='{{ aap_install_certs_dir }}/{{ groups['aap_pah'][0] }}.key' + +# Server-side SSL settings for PostgreSQL (when we are installing it). +postgres_use_ssl=True +postgres_ssl_cert='{{ aap_install_certs_dir }}/{{ groups['aap_db'][0] }}.crt' +postgres_ssl_key='{{ aap_install_certs_dir }}/{{ groups['aap_db'][0] }}.key' + +# Keystore file to install in SSO node +{# +# sso_custom_keystore_file='{{ aap_setup_base_dir }}/ansible-automation-platform.jks' + +# The default install will deploy SSO with sso_use_https=True +# Keystore password is required for https enabled SSO +# sso_keystore_password='{{ sso_keystore_password }}' + +# Single-Sign-On configuration +# sso_console_admin_password='{{ sso_console_admin_password }}' + +# {% if network_proxy_hostname is defined %} +# https_proxy='{{ network_proxy_url }}' +# http_proxy='{{ network_proxy_url }}' +# no_proxy='{{ network_no_proxy }}' +# {% endif %} +#} +# The default install will register node to the Red Hat Insights Service +# if the node is registered with Subscription Manager. Set to False to disable. +enable_insights_collection = true \ No newline at end of file diff --git a/roles/aap_install/templates/ldapextras.yml.j2 b/roles/aap_install/templates/ldapextras.yml.j2 new file mode 100644 index 0000000..73c09c1 --- /dev/null +++ b/roles/aap_install/templates/ldapextras.yml.j2 @@ -0,0 +1,7 @@ +--- +ldap_extra_settings: + AUTH_LDAP_USER_FLAGS_BY_GROUP: '{"is_superuser": "cn=aapgroup-administrator,cn=groups,cn=accounts,{{ ldap_domain_map }}",}' + AUTH_LDAP_REQUIRE_GROUP: 'cn=aapgroup-user,cn=groups,cn=accounts,{{ ldap_domain_map }}' + AUTH_LDAP_USER_ATTR_MAP: '{"first_name": "givenName", "last_name": "sn", "email": "mail",}' + GALAXY_LDAP_LOGGING: True + AUTH_LDAP_MIRROR_GROUPS: True diff --git a/roles/azure_dns/README.md b/roles/azure_dns/README.md new file mode 100644 index 0000000..d2ef66e --- /dev/null +++ b/roles/azure_dns/README.md @@ -0,0 +1,54 @@ +azure_dns +========= + +This roles creates DNS zones and DNS records in Microsoft Azure. + +Requirements +------------ + +An Azure custom app must be created and granted contributor access on subscription level to perform the ansible tasks. + +Role Variables +-------------- + +```yaml +# default variables +azure_dns_zone_state: present +azure_dns_private_dns_zone_state: present + +# azure authentication variables +azure_subscription_id: +azure_tenant_id: +techuser_ansible_client_id: +techuser_ansible_secret_value: + +# azure resource group configuration variables +azure_rg: +azure_dns_zone: +azure_dns_registration_enabled: +``` + +Dependencies +------------ + +- azure_resource_group + +Example Playbook +---------------- + +```yaml +- hosts: bastion + roles: + - azure_resource_group + - azure_dns +``` + +License +------- + +GPL-3.0-only + +Author Information +------------------ + +Showroom Project Team diff --git a/roles/azure_dns/defaults/main.yml b/roles/azure_dns/defaults/main.yml new file mode 100644 index 0000000..e83237d --- /dev/null +++ b/roles/azure_dns/defaults/main.yml @@ -0,0 +1,3 @@ +--- +azure_dns_zone_state: "present" +azure_dns_registration_enabled: true diff --git a/roles/azure_dns/meta/main.yml b/roles/azure_dns/meta/main.yml new file mode 100644 index 0000000..525dbe0 --- /dev/null +++ b/roles/azure_dns/meta/main.yml @@ -0,0 +1,16 @@ +--- +galaxy_info: + author: Showroom Project Team + description: This roles creates DNS zones and records in Microsoft Azure. + company: Red Hat, Inc. + license: GPL-3.0-only + min_ansible_version: "2.14" + platforms: + - name: EL + versions: + - "8" + - "9" + galaxy_tags: [] +dependencies: [] +collections: + - azure.azcollection diff --git a/roles/azure_dns/tasks/main.yml b/roles/azure_dns/tasks/main.yml new file mode 100644 index 0000000..a1c814c --- /dev/null +++ b/roles/azure_dns/tasks/main.yml @@ -0,0 +1,23 @@ +--- +- name: Create a Public DNS zone + azure.azcollection.azure_rm_dnszone: + subscription_id: "{{ azure_subscription_id | default(omit) }}" + tenant: "{{ azure_tenant_id | default(omit) }}" + client_id: "{{ techuser_ansible_client_id | default(omit) }}" + secret: "{{ techuser_ansible_secret_value | default(omit) }}" + state: "{{ azure_dns_zone_state }}" + resource_group: "{{ azure_rg | default(omit) }}" + name: "{{ azure_dns_zone | default(omit) }}" + when: azure_dns_zone is defined + +- name: Create Private DNS Zone + ansible.builtin.include_tasks: private_dns_zone.yml + when: azure_dns_private_dns_zone is defined + +- name: Create Private DNS record + ansible.builtin.include_tasks: private_dns_zone_link.yml + when: azure_dns_private_dns_zone is defined + +- name: Create Public DNS record + ansible.builtin.include_tasks: public_dnsrecord.yml + when: __public_ip is defined diff --git a/roles/azure_dns/tasks/private_dns_zone.yml b/roles/azure_dns/tasks/private_dns_zone.yml new file mode 100644 index 0000000..5875bfc --- /dev/null +++ b/roles/azure_dns/tasks/private_dns_zone.yml @@ -0,0 +1,12 @@ +--- +- name: Create a Private DNS zone + azure.azcollection.azure_rm_privatednszone: + subscription_id: "{{ azure_subscription_id | default(omit) }}" + tenant: "{{ azure_tenant_id | default(omit) }}" + client_id: "{{ techuser_ansible_client_id | default(omit) }}" + secret: "{{ techuser_ansible_secret_value | default(omit) }}" + state: "{{ azure_dns_private_dns_zone_state | default(omit) }}" + resource_group: "{{ azure_rg | default(omit) }}" + name: "{{ azure_dns_private_dns_zone | default(omit) }}" + register: __output + failed_when: "__output.msg is defined and 'Another operation is pending for requested object.' not in __output.msg" diff --git a/roles/azure_dns/tasks/private_dns_zone_link.yml b/roles/azure_dns/tasks/private_dns_zone_link.yml new file mode 100644 index 0000000..c6c8b7e --- /dev/null +++ b/roles/azure_dns/tasks/private_dns_zone_link.yml @@ -0,0 +1,15 @@ +--- +- name: Configure private DNS zone link + azure.azcollection.azure_rm_privatednszonelink: + subscription_id: "{{ azure_subscription_id | default(omit) }}" + tenant: "{{ azure_tenant_id | default(omit) }}" + client_id: "{{ techuser_ansible_client_id | default(omit) }}" + secret: "{{ techuser_ansible_secret_value | default(omit) }}" + state: "{{ azure_dns_private_dns_zone_state | default(omit) }}" + resource_group: "{{ azure_rg | default(omit) }}" + name: "{{ azure_vnet_link }}" + zone_name: "{{ azure_dns_private_dns_zone | default(omit) }}" + virtual_network: "{{ azure_vnet }}" + registration_enabled: "{{ azure_dns_registration_enabled }}" + register: __output + failed_when: "__output.msg is defined and 'Another operation is pending for requested object.' not in __output.msg" diff --git a/roles/azure_dns/tasks/public_dnsrecord.yml b/roles/azure_dns/tasks/public_dnsrecord.yml new file mode 100644 index 0000000..696c3aa --- /dev/null +++ b/roles/azure_dns/tasks/public_dnsrecord.yml @@ -0,0 +1,13 @@ +--- +- name: Ensure a public "A" record set for VM + azure.azcollection.azure_rm_dnsrecordset: + subscription_id: "{{ azure_subscription_id | default(omit) }}" + tenant: "{{ azure_tenant_id | default(omit) }}" + client_id: "{{ techuser_ansible_client_id | default(omit) }}" + secret: "{{ techuser_ansible_secret_value | default(omit) }}" + resource_group: "{{ azure_rg }}" + relative_name: "{{ inventory_hostname | split('.') | first | lower }}" + zone_name: "{{ azure_dns_zone }}" + record_type: A + records: + - entry: "{{ __public_ip.state.ip_address }}" diff --git a/roles/azure_managed_disk/README.md b/roles/azure_managed_disk/README.md new file mode 100644 index 0000000..517d7dd --- /dev/null +++ b/roles/azure_managed_disk/README.md @@ -0,0 +1,62 @@ +azure_managed_disk +========= + +This roles creates and mounts Azure managed disk to VM. + +Requirements +------------ + +- An Azure custom app must be created and granted contributor access on subscription level to perform the ansible tasks. +- An Azure VM should have been provisioned. + +Role Variables +-------------- + +```yaml +# default variables +azure_managed_disk_state: present + +# azure authentication variables +azure_subscription_id: +azure_tenant_id: +techuser_ansible_client_id: +techuser_ansible_secret_value: + +# azure managed disk configuration variables +azure_rg: +azure_managed_disk_name: +azure_managed_disk_size: +azure_managed_disk_storage_account_type: +``` + +Dependencies +------------ + +- azure_resource_group +- imagebuilder (when custom image is needed.) +- azure_network +- azure_dns (when public IP and public DNS record needed.) + +Example Playbook +---------------- + +```yaml +- hosts: bastion + roles: + - azure_resource_group + - imagebuilder + - azure_network + - azure_vm_deploy + - azure_dns + - azure_managed_disk +``` + +License +------- + +GPL-3.0-only + +Author Information +------------------ + +Showroom Project Team \ No newline at end of file diff --git a/roles/azure_managed_disk/defaults/main.yml b/roles/azure_managed_disk/defaults/main.yml new file mode 100644 index 0000000..08f7831 --- /dev/null +++ b/roles/azure_managed_disk/defaults/main.yml @@ -0,0 +1,2 @@ +--- +azure_managed_disk_state: "present" diff --git a/roles/azure_managed_disk/meta/main.yml b/roles/azure_managed_disk/meta/main.yml new file mode 100644 index 0000000..8dc1af1 --- /dev/null +++ b/roles/azure_managed_disk/meta/main.yml @@ -0,0 +1,16 @@ +--- +galaxy_info: + author: Showroom Project Team + description: This roles creates and mounts Azure managed disk to VM. + company: Red Hat, Inc. + license: GPL-3.0-only + min_ansible_version: "2.14" + platforms: + - name: EL + versions: + - "8" + - "9" + galaxy_tags: [] +dependencies: [] +collections: + - azure.azcollection diff --git a/roles/azure_managed_disk/tasks/main.yml b/roles/azure_managed_disk/tasks/main.yml new file mode 100644 index 0000000..ba4b449 --- /dev/null +++ b/roles/azure_managed_disk/tasks/main.yml @@ -0,0 +1,23 @@ +--- +- name: Create managed disk + azure.azcollection.azure_rm_manageddisk: + subscription_id: "{{ azure_subscription_id | default(omit) }}" + tenant: "{{ azure_tenant_id | default(omit) }}" + client_id: "{{ techuser_ansible_client_id | default(omit) }}" + secret: "{{ techuser_ansible_secret_value | default(omit) }}" + state: "{{ azure_managed_disk_state }}" + resource_group: "{{ azure_rg | default(omit) }}" + name: "{{ azure_managed_disk_name | default(omit) }}" + disk_size_gb: "{{ azure_managed_disk_size | default(omit) }}" + storage_account_type: "{{ azure_managed_disk_storage_account_type | default(omit) }}" + +- name: Mount the managed disk to VM + azure.azcollection.azure_rm_manageddisk: + subscription_id: "{{ azure_subscription_id | default(omit) }}" + tenant: "{{ azure_tenant_id | default(omit) }}" + client_id: "{{ techuser_ansible_client_id | default(omit) }}" + secret: "{{ techuser_ansible_secret_value | default(omit) }}" + resource_group: "{{ azure_rg | default(omit) }}" + name: "{{ azure_managed_disk_name | default(omit) }}" + disk_size_gb: "{{ azure_managed_disk_size | default(omit) }}" + managed_by: "{{ inventory_hostname }}" diff --git a/roles/azure_network/README.md b/roles/azure_network/README.md new file mode 100644 index 0000000..8fb62e6 --- /dev/null +++ b/roles/azure_network/README.md @@ -0,0 +1,53 @@ +azure_network +========= + +This roles creates Virtual Network in Microsoft Azure. + +Requirements +------------ + +An Azure custom app must be created and granted contributor access on subscription level to perform the ansible tasks. + +Role Variables +-------------- + +```yaml +# default variables +azure_vnet_state: present + +# azure authentication variables +azure_subscription_id: +azure_tenant_id: +techuser_ansible_client_id: +techuser_ansible_secret_value: + +# azure resource group configuration variables +azure_rg: +azure_vnet: +azure_vnet_address_prefix: +``` + +Dependencies +------------ + +- azure_resource_group + +Example Playbook +---------------- + +```yaml +- hosts: bastion + roles: + - azure_resource_group + - azure_dns +``` + +License +------- + +GPL-3.0-only + +Author Information +------------------ + +Showroom Project Team \ No newline at end of file diff --git a/roles/azure_network/defaults/main.yml b/roles/azure_network/defaults/main.yml new file mode 100644 index 0000000..f78bec8 --- /dev/null +++ b/roles/azure_network/defaults/main.yml @@ -0,0 +1,9 @@ +--- +azure_network_vnet_state: "present" +azure_network_vnet_peering_state: "present" +azure_network_subnet_state: "present" + +azure_network_allow_virtual_network_access: true +azure_network_allow_forwarded_traffic: false + +azure_network_purge_rules: true diff --git a/roles/azure_network/meta/main.yml b/roles/azure_network/meta/main.yml new file mode 100644 index 0000000..79bc9b5 --- /dev/null +++ b/roles/azure_network/meta/main.yml @@ -0,0 +1,16 @@ +--- +galaxy_info: + author: Showroom Project Team + description: This roles creates Virtual Network in Microsoft Azure. + company: Red Hat, Inc. + license: GPL-3.0-only + min_ansible_version: "2.14" + platforms: + - name: EL + versions: + - "8" + - "9" + galaxy_tags: [] +dependencies: [] +collections: + - azure.azcollection diff --git a/roles/azure_network/tasks/main.yml b/roles/azure_network/tasks/main.yml new file mode 100644 index 0000000..2b2b19a --- /dev/null +++ b/roles/azure_network/tasks/main.yml @@ -0,0 +1,23 @@ +--- +- name: Include manage_vnet.yml + ansible.builtin.include_tasks: manage_vnet.yml + when: manage_vnet | bool() + tags: manage_vnet + +- name: Include manage_vnet_peering.yml + ansible.builtin.include_tasks: manage_vnet_peering.yml + when: vnet_peering | bool() + loop: "{{ vnet_peers }}" + loop_control: + loop_var: vnet_peer_var + tags: vnet_peering + +- name: Include manage_nsg.yml + ansible.builtin.include_tasks: manage_nsg.yml + when: manage_nsg | bool() + tags: manage_nsg + +- name: Include manage_subnet.yml + ansible.builtin.include_tasks: manage_subnet.yml + when: manage_subnet | bool() + tags: manage_subnet diff --git a/roles/azure_network/tasks/manage_nsg.yml b/roles/azure_network/tasks/manage_nsg.yml new file mode 100644 index 0000000..dc2fbc5 --- /dev/null +++ b/roles/azure_network/tasks/manage_nsg.yml @@ -0,0 +1,12 @@ +--- +- name: Manage Network Security Group for Subnet + azure.azcollection.azure_rm_securitygroup: + subscription_id: "{{ azure_subscription_id | default(omit) }}" + tenant: "{{ azure_tenant_id | default(omit) }}" + client_id: "{{ techuser_ansible_client_id | default(omit) }}" + secret: "{{ techuser_ansible_secret_value | default(omit) }}" + state: "{{ azure_network_subnet_state }}" + resource_group: "{{ azure_rg | default(omit) }}" + name: "{{ azure_subnet_nsg | default(omit) }}" + rules: "{{ azure_subnet_nsg_rules | default(omit) }}" + purge_rules: "{{ azure_network_purge_rules }}" diff --git a/roles/azure_network/tasks/manage_subnet.yml b/roles/azure_network/tasks/manage_subnet.yml new file mode 100644 index 0000000..3877647 --- /dev/null +++ b/roles/azure_network/tasks/manage_subnet.yml @@ -0,0 +1,13 @@ +--- +- name: Create a subnet + azure.azcollection.azure_rm_subnet: + subscription_id: "{{ azure_subscription_id | default(omit) }}" + tenant: "{{ azure_tenant_id | default(omit) }}" + client_id: "{{ techuser_ansible_client_id | default(omit) }}" + secret: "{{ techuser_ansible_secret_value | default(omit) }}" + state: "{{ azure_network_subnet_state }}" + resource_group: "{{ azure_rg | default(omit) }}" + virtual_network_name: "{{ azure_vnet | default(omit) }}" + name: "{{ azure_subnet | default(omit) }}" + address_prefix: "{{ azure_subnet_address_prefix | default(omit) }}" + security_group: "{{ azure_subnet_nsg | default(omit) }}" diff --git a/roles/azure_network/tasks/manage_vnet.yml b/roles/azure_network/tasks/manage_vnet.yml new file mode 100644 index 0000000..31028fe --- /dev/null +++ b/roles/azure_network/tasks/manage_vnet.yml @@ -0,0 +1,12 @@ +--- +- name: Manage Virtual Network + azure.azcollection.azure_rm_virtualnetwork: + subscription_id: "{{ azure_subscription_id | default(omit) }}" + tenant: "{{ azure_tenant_id | default(omit) }}" + client_id: "{{ techuser_ansible_client_id | default(omit) }}" + secret: "{{ techuser_ansible_secret_value | default(omit) }}" + state: "{{ azure_network_vnet_state }}" + resource_group: "{{ azure_rg | default(omit) }}" + name: "{{ azure_vnet | default(omit) }}" + dns_servers: "{{ azure_vnet_dns_servers | default(omit) }}" + address_prefixes: "{{ azure_vnet_address_prefix | default(omit) }}" diff --git a/roles/azure_network/tasks/manage_vnet_peering.yml b/roles/azure_network/tasks/manage_vnet_peering.yml new file mode 100644 index 0000000..4b2aac3 --- /dev/null +++ b/roles/azure_network/tasks/manage_vnet_peering.yml @@ -0,0 +1,29 @@ +--- +- name: Manage VNet Peering + azure.azcollection.azure_rm_virtualnetworkpeering: + subscription_id: "{{ vnet_peer_var.azure_subs | default(omit) }}" + tenant: "{{ azure_tenant_id | default(omit) }}" + client_id: "{{ techuser_ansible_client_id | default(omit) }}" + secret: "{{ techuser_ansible_secret_value | default(omit) }}" + state: "{{ azure_network_vnet_peering_state }}" + resource_group: "{{ vnet_peer_var.azure_rg | default(omit) }}" + virtual_network: "{{ vnet_peer_var.azure_vnet | default(omit) }}" + name: "{{ vnet_peer_var.azure_vnet_peer | default(omit) }}" + remote_virtual_network: "/subscriptions/{{ azure_subscription_id }}/resourceGroups/\ + {{ azure_rg }}/providers/Microsoft.Network/virtualNetworks/{{ azure_vnet }}" + allow_virtual_network_access: "{{ azure_network_allow_virtual_network_access }}" + allow_forwarded_traffic: "{{ azure_network_allow_forwarded_traffic }}" + +- name: Manage VNet Peering + azure.azcollection.azure_rm_virtualnetworkpeering: + subscription_id: "{{ azure_subscription_id | default(omit) }}" + tenant: "{{ azure_tenant_id | default(omit) }}" + client_id: "{{ techuser_ansible_client_id | default(omit) }}" + secret: "{{ techuser_ansible_secret_value | default(omit) }}" + state: "{{ azure_network_vnet_peering_state }}" + resource_group: "{{ azure_rg | default(omit) }}" + virtual_network: "{{ azure_vnet | default(omit) }}" + name: "{{ vnet_peer_var.azure_vnet_peer | default(omit) }}" + remote_virtual_network: "{{ vnet_peer_var.azure_vnet_id | default(omit) }}" + allow_virtual_network_access: "{{ azure_network_allow_virtual_network_access }}" + allow_forwarded_traffic: "{{ azure_network_allow_forwarded_traffic }}" diff --git a/roles/azure_resource_group/README.md b/roles/azure_resource_group/README.md new file mode 100644 index 0000000..58647ac --- /dev/null +++ b/roles/azure_resource_group/README.md @@ -0,0 +1,47 @@ +azure_resource_group +========= + +This roles creates Resource Group in Microsoft Azure. + +Requirements +------------ + +An Azure custom app must be created and granted contributer access on subscription level to perform the ansible tasks. + +Role Variables +-------------- + +```yaml +# default variables +azure_resource_group_state: present +azure_resource_group_teardown: false + +# azure authentication variables +azure_subscription_id: +azure_tenant_id: +techuser_ansible_client_id: +techuser_ansible_secret_value: + +# azure resource group configuration variables +azure_rg: +azure_region: +``` + +Example Playbook +---------------- + +```yaml +- hosts: bastion + roles: + - azure_resource_group +``` + +License +------- + +GPL-3.0-only + +Author Information +------------------ + +Showroom Project Team diff --git a/roles/azure_resource_group/defaults/main.yml b/roles/azure_resource_group/defaults/main.yml new file mode 100644 index 0000000..343f770 --- /dev/null +++ b/roles/azure_resource_group/defaults/main.yml @@ -0,0 +1,3 @@ +--- +azure_resource_group_state: "present" +azure_resource_group_teardown: false diff --git a/roles/azure_resource_group/meta/main.yml b/roles/azure_resource_group/meta/main.yml new file mode 100644 index 0000000..525dbe0 --- /dev/null +++ b/roles/azure_resource_group/meta/main.yml @@ -0,0 +1,16 @@ +--- +galaxy_info: + author: Showroom Project Team + description: This roles creates DNS zones and records in Microsoft Azure. + company: Red Hat, Inc. + license: GPL-3.0-only + min_ansible_version: "2.14" + platforms: + - name: EL + versions: + - "8" + - "9" + galaxy_tags: [] +dependencies: [] +collections: + - azure.azcollection diff --git a/roles/azure_resource_group/tasks/main.yml b/roles/azure_resource_group/tasks/main.yml new file mode 100644 index 0000000..afbaef8 --- /dev/null +++ b/roles/azure_resource_group/tasks/main.yml @@ -0,0 +1,11 @@ +--- +- name: Create Resource Group + azure.azcollection.azure_rm_resourcegroup: + subscription_id: "{{ azure_subscription_id | default(omit) }}" + tenant: "{{ azure_tenant_id | default(omit) }}" + client_id: "{{ techuser_ansible_client_id | default(omit) }}" + secret: "{{ techuser_ansible_secret_value | default(omit) }}" + state: "{{ azure_resource_group_state }}" + force_delete_nonempty: "{{ azure_resource_group_teardown }}" + name: "{{ azure_rg | default(omit) }}" + location: "{{ azure_region | default(omit) }}" diff --git a/roles/azure_snapshot/README.md b/roles/azure_snapshot/README.md new file mode 100644 index 0000000..dd35afc --- /dev/null +++ b/roles/azure_snapshot/README.md @@ -0,0 +1,49 @@ +azure_snapshot +========= + +This role creates and deletes snapshots for all disk of a VM. + +Role Variables +-------------- + +```yaml +# azure authentication variables +azure_subscription_id: +azure_tenant_id: +techuser_ansible_client_id: +techuser_ansible_secret_value: + +# Custom variables +azure_snapshot_state: +``` + +Dependencies +------------ + + +Example Playbook +---------------- + +```yaml +--- +- name: Manage Azure Snapshot + hosts: "{{ host | default('localhost') }}" + gather_facts: false + + roles: + - role: azure_snapshot + when: + - azure_snapshot | bool() + tags: azure_snapshot + delegate_to: localhost +``` + +License +------- + +GPL-3.0-only + +Author Information +------------------ + +Showroom Project Team \ No newline at end of file diff --git a/roles/azure_snapshot/defaults/main.yml b/roles/azure_snapshot/defaults/main.yml new file mode 100644 index 0000000..6ca19d0 --- /dev/null +++ b/roles/azure_snapshot/defaults/main.yml @@ -0,0 +1,3 @@ +--- +# defaults file for azure_snapshot +# azure_snapshot_state: [present|absent] diff --git a/roles/azure_snapshot/meta/main.yml b/roles/azure_snapshot/meta/main.yml new file mode 100644 index 0000000..ee212d3 --- /dev/null +++ b/roles/azure_snapshot/meta/main.yml @@ -0,0 +1,16 @@ +--- +galaxy_info: + author: Showroom Project Team + description: This role deploy/deletes snapshots. + company: Red Hat, Inc. + license: GPL-3.0-only + min_ansible_version: "2.14" + platforms: + - name: EL + versions: + - "8" + - "9" + galaxy_tags: [] +dependencies: [] +collections: + - azure.azcollection diff --git a/roles/azure_snapshot/tasks/main.yml b/roles/azure_snapshot/tasks/main.yml new file mode 100644 index 0000000..fd9062a --- /dev/null +++ b/roles/azure_snapshot/tasks/main.yml @@ -0,0 +1,73 @@ +--- +# tasks file for azure_snapshot +- name: Get facts by name + azure.azcollection.azure_rm_virtualmachine_info: + subscription_id: "{{ azure_subscription_id | default(omit) }}" + tenant: "{{ azure_tenant_id | default(omit) }}" + client_id: "{{ techuser_ansible_client_id | default(omit) }}" + secret: "{{ techuser_ansible_secret_value | default(omit) }}" + resource_group: "{{ azure_rg | default(omit) }}" + name: "{{ inventory_hostname_short }}" + register: __output + +- name: Create snapshot by copying an existing managed disk. + when: (azure_snapshot_state is not defined) or (azure_snapshot_state is defined and azure_snapshot_state == 'present') + block: + - name: Create for Data disks + azure.azcollection.azure_rm_snapshot: + tenant: "{{ azure_tenant_id | default(omit) }}" + subscription_id: "{{ azure_subscription_id | default(omit) }}" + client_id: "{{ techuser_ansible_client_id | default(omit) }}" + secret: "{{ techuser_ansible_secret_value | default(omit) }}" + resource_group: "{{ azure_rg | default(omit) }}" + name: "{{ data_disk.name | lower }}-snapshot" + location: "{{ azure_region | default(omit) | lower }}" + state: "{{ azure_snapshot_state | default(omit) | lower }}" + incremental: true + creation_data: + create_option: Copy + source_id: "{{ data_disk.managed_disk_id | lower }}" + loop: "{{ __output.vms[0].data_disks }}" + loop_control: + loop_var: data_disk + + - name: Create for OS disk + azure.azcollection.azure_rm_snapshot: + tenant: "{{ azure_tenant_id | default(omit) }}" + subscription_id: "{{ azure_subscription_id | default(omit) }}" + client_id: "{{ techuser_ansible_client_id | default(omit) }}" + secret: "{{ techuser_ansible_secret_value | default(omit) }}" + resource_group: "{{ azure_rg | default(omit) }}" + name: "{{ inventory_hostname_short }}-osdisk-snapshot" + location: "{{ azure_region | default(omit) }}" + state: "{{ azure_snapshot_state | default(omit) }}" + incremental: true + creation_data: + create_option: Copy + source_id: /subscriptions/{{ azure_subscription_id }}/resourceGroups/{{ azure_rg }}/providers/Microsoft.Compute/disks/{{ inventory_hostname_short }}-osdisk # noqa yaml[line-length] + +- name: Delete the snapshots + when: azure_snapshot_state is defined and azure_snapshot_state == 'absent' + block: + - name: Delete OS disk snapshot + azure.azcollection.azure_rm_snapshot: + subscription_id: "{{ azure_subscription_id | default(omit) }}" + tenant: "{{ azure_tenant_id | default(omit) }}" + client_id: "{{ techuser_ansible_client_id | default(omit) }}" + secret: "{{ techuser_ansible_secret_value | default(omit) }}" + resource_group: "{{ azure_rg | default(omit) }}" + name: "{{ inventory_hostname_short }}-osdisk-snapshot" + state: "{{ azure_snapshot_state }}" + + - name: Delete Data disk snapshot + azure.azcollection.azure_rm_snapshot: + subscription_id: "{{ azure_subscription_id | default(omit) }}" + tenant: "{{ azure_tenant_id | default(omit) }}" + client_id: "{{ techuser_ansible_client_id | default(omit) }}" + secret: "{{ techuser_ansible_secret_value | default(omit) }}" + resource_group: "{{ azure_rg | default(omit) }}" + name: "{{ data_disk.name }}-snapshot" + state: "{{ azure_snapshot_state }}" + loop: "{{ __output.vms[0].data_disks }}" + loop_control: + loop_var: data_disk diff --git a/roles/azure_vm_deploy/README.md b/roles/azure_vm_deploy/README.md new file mode 100644 index 0000000..1b14916 --- /dev/null +++ b/roles/azure_vm_deploy/README.md @@ -0,0 +1,65 @@ +azure_vm_deploy +========= + +This role deploys VM on Azure and creates public IP and network interface(NIC) if defined. + +Requirements +------------ + +- An Azure custom app must be created and granted contributor access on subscription level to perform the ansible tasks. + +Role Variables +-------------- + +```yaml +# default variables +azure_vm_deploy_public_ip_allocation_method: "static" +azure_vm_deploy_vm_size: "Standard_B4ms" +azure_vm_image_name: "image-rhel-94" +azure_vm_deploy_user: "{{ vm_user }}" +azure_vm_deploy_user_public_key: "{{ vm_user_public_key }}" +azure_vm_deploy_tags: "{{ vm_tags }}" + +# azure authentication variables +azure_subscription_id: +azure_tenant_id: +techuser_ansible_client_id: +techuser_ansible_secret_value: + +# Custom variables +azure_public_ip: true +vm_tags: + sequencestart: "3" + sequencestop: "3" +``` + +Dependencies +------------ + +- azure_resource_group +- imagebuilder (when custom image is needed.) +- azure_network +- azure_dns (when public IP and public DNS record needed.) + +Example Playbook +---------------- + +```yaml +- hosts: bastion + roles: + - azure_resource_group + - imagebuilder + - azure_network + - azure_vm_deploy + - azure_dns +``` + +License +------- + +GPL-3.0-only + +Author Information +------------------ + +Showroom Project Team \ No newline at end of file diff --git a/roles/azure_vm_deploy/defaults/main.yml b/roles/azure_vm_deploy/defaults/main.yml new file mode 100644 index 0000000..8a5ff60 --- /dev/null +++ b/roles/azure_vm_deploy/defaults/main.yml @@ -0,0 +1,7 @@ +--- +azure_vm_deploy_public_ip_allocation_method: "static" +azure_vm_deploy_vm_size: "Standard_B4ms" +azure_vm_deploy_image_name: "image-rhel-94" +azure_vm_deploy_user: "{{ vm_user }}" +azure_vm_deploy_user_public_key: "{{ vm_user_public_key }}" +azure_vm_deploy_tags: "{{ vm_tags }}" diff --git a/roles/azure_vm_deploy/meta/main.yml b/roles/azure_vm_deploy/meta/main.yml new file mode 100644 index 0000000..348d3c3 --- /dev/null +++ b/roles/azure_vm_deploy/meta/main.yml @@ -0,0 +1,16 @@ +--- +galaxy_info: + author: Showroom Project Team + description: This roles creates VM also public ip and nic if defined, and create a dns record. + company: Red Hat, Inc. + license: GPL-3.0-only + min_ansible_version: "2.14" + platforms: + - name: EL + versions: + - "8" + - "9" + galaxy_tags: [] +dependencies: [] +collections: + - azure.azcollection diff --git a/roles/azure_vm_deploy/tasks/main.yml b/roles/azure_vm_deploy/tasks/main.yml new file mode 100644 index 0000000..b3ef73b --- /dev/null +++ b/roles/azure_vm_deploy/tasks/main.yml @@ -0,0 +1,59 @@ +--- +- name: Create Public IP for the VM + when: manage_public_ip | bool() + block: + - name: Create Public IP if needed + ansible.builtin.include_tasks: manage_public_ip.yml + + - name: Create a Virtual Network Interface Card + azure.azcollection.azure_rm_networkinterface: + subscription_id: "{{ azure_subscription_id | default(omit) }}" + tenant: "{{ azure_tenant_id | default(omit) }}" + client_id: "{{ techuser_ansible_client_id | default(omit) }}" + secret: "{{ techuser_ansible_secret_value | default(omit) }}" + resource_group: "{{ azure_rg | default(omit) }}" + name: "{{ azure_nic_name | default(omit) }}" + virtual_network: "{{ azure_vnet | default(omit) }}" + subnet_name: "{{ azure_subnet | default(omit) }}" + ip_configurations: + - name: "{{ azure_public_ip.config_name | default(omit) }}" + public_ip_address_name: "{{ azure_public_ip.name | default(omit) }}" + security_group: "{{ azure_subnet_nsg | default(omit) }}" + +- name: Create a Virtual Network Interface Card + azure.azcollection.azure_rm_networkinterface: + subscription_id: "{{ azure_subscription_id | default(omit) }}" + tenant: "{{ azure_tenant_id | default(omit) }}" + client_id: "{{ techuser_ansible_client_id | default(omit) }}" + secret: "{{ techuser_ansible_secret_value | default(omit) }}" + resource_group: "{{ azure_rg | default(omit) }}" + name: "{{ azure_nic_name | default(omit) }}" + virtual_network: "{{ azure_vnet | default(omit) }}" + subnet_name: "{{ azure_subnet | default(omit) }}" + security_group: "{{ azure_subnet_nsg | default(omit) }}" + when: not manage_public_ip | bool() + +- name: Create the VM + azure.azcollection.azure_rm_virtualmachine: + subscription_id: "{{ azure_subscription_id | default(omit) }}" + tenant: "{{ azure_tenant_id | default(omit) }}" + client_id: "{{ techuser_ansible_client_id | default(omit) }}" + secret: "{{ techuser_ansible_secret_value | default(omit) }}" + resource_group: "{{ azure_rg | default(omit) }}" + name: "{{ inventory_hostname }}" + vm_size: "{{ azure_vm_deploy_vm_size }}" + admin_username: "{{ azure_vm_deploy_user }}" + image: + name: "{{ azure_vm_deploy_image_name }}" + resource_group: "{{ azure_rg }}" + os_disk_caching: ReadWrite + os_disk_name: "{{ inventory_hostname }}-os-disk" + network_interface_names: + - "{{ azure_nic_name | default(omit) }}" + availability_set: null + ssh_public_keys: + - path: /home/{{ azure_vm_deploy_user }}/.ssh/authorized_keys + key_data: "{{ azure_vm_deploy_user_public_key }}" + ssh_password_enabled: false + tags: "{{ azure_vm_deploy_tags }}" + register: __vm_info diff --git a/roles/azure_vm_deploy/tasks/manage_public_ip.yml b/roles/azure_vm_deploy/tasks/manage_public_ip.yml new file mode 100644 index 0000000..25f7a0d --- /dev/null +++ b/roles/azure_vm_deploy/tasks/manage_public_ip.yml @@ -0,0 +1,11 @@ +--- +- name: Create a public IP address + azure.azcollection.azure_rm_publicipaddress: + subscription_id: "{{ azure_subscription_id | default(omit) }}" + tenant: "{{ azure_tenant_id | default(omit) }}" + client_id: "{{ techuser_ansible_client_id | default(omit) }}" + secret: "{{ techuser_ansible_secret_value | default(omit) }}" + resource_group: "{{ azure_rg | default(omit) }}" + allocation_method: "{{ azure_vm_deploy_public_ip_allocation_method | default(omit) }}" + name: "{{ azure_public_ip.name | default(omit) }}" + register: __public_ip diff --git a/roles/azure_vm_lock/README.md b/roles/azure_vm_lock/README.md new file mode 100644 index 0000000..83f8f75 --- /dev/null +++ b/roles/azure_vm_lock/README.md @@ -0,0 +1,57 @@ +azure_vm_lock +========= + +This roles power-offs and locks the VM. + +Requirements +------------ + +- An Azure custom app must be created and granted Owner access on subscription level to perform Microsoft.Authorization/* or Microsoft.Authorization/locks/* actions. + +Role Variables +-------------- + +```yaml +# default variables +azure_vm_lock_state: present + +# azure authentication variables +azure_subscription_id: +azure_tenant_id: +techuser_ansible_client_id: +techuser_ansible_secret_value: + +``` + +Dependencies +------------ + +- azure_resource_group +- imagebuilder (when custom image is needed.) +- azure_network +- azure_dns (when public IP and public DNS record needed.) +- azure_vm_deploy + +Example Playbook +---------------- + +```yaml +- hosts: bastion + roles: + - azure_resource_group + - imagebuilder + - azure_network + - azure_vm_deploy + - azure_dns + - azure_vm_lock +``` + +License +------- + +GPL-3.0-only + +Author Information +------------------ + +Showroom Project Team \ No newline at end of file diff --git a/roles/azure_vm_lock/defaults/main.yml b/roles/azure_vm_lock/defaults/main.yml new file mode 100644 index 0000000..f32ad03 --- /dev/null +++ b/roles/azure_vm_lock/defaults/main.yml @@ -0,0 +1,2 @@ +--- +azure_vm_lock_state: "present" diff --git a/roles/azure_vm_lock/meta/main.yml b/roles/azure_vm_lock/meta/main.yml new file mode 100644 index 0000000..e70fdcf --- /dev/null +++ b/roles/azure_vm_lock/meta/main.yml @@ -0,0 +1,16 @@ +--- +galaxy_info: + author: Showroom Project Team + description: This roles power-offs and locks the VM. + company: Red Hat, Inc. + license: GPL-3.0-only + min_ansible_version: "2.14" + platforms: + - name: EL + versions: + - "8" + - "9" + galaxy_tags: [] +dependencies: [] +collections: + - azure.azcollection diff --git a/roles/azure_vm_lock/tasks/main.yml b/roles/azure_vm_lock/tasks/main.yml new file mode 100644 index 0000000..564662a --- /dev/null +++ b/roles/azure_vm_lock/tasks/main.yml @@ -0,0 +1,22 @@ +--- +- name: Power off VM + azure.azcollection.azure_rm_virtualmachine: + subscription_id: "{{ azure_subscription_id | default(omit) }}" + tenant: "{{ azure_tenant_id | default(omit) }}" + client_id: "{{ techuser_ansible_client_id | default(omit) }}" + secret: "{{ techuser_ansible_secret_value | default(omit) }}" + resource_group: "{{ azure_rg | default(omit) }}" + name: "{{ inventory_hostname }}" + started: false + register: __vm_info + +- name: Create a lock for the VM + azure.azcollection.azure_rm_lock: + subscription_id: "{{ azure_subscription_id | default(omit) }}" + tenant: "{{ azure_tenant_id | default(omit) }}" + client_id: "{{ techuser_ansible_client_id | default(omit) }}" + secret: "{{ techuser_ansible_secret_value | default(omit) }}" + state: "{{ azure_vm_lock_state }}" + managed_resource_id: "{{ __vm_info.ansible_facts.azure_vm.id }}" + name: "{{ azure_vm_lock_name }}" + level: read_only diff --git a/roles/git_repo_commit/README.md b/roles/git_repo_commit/README.md new file mode 100644 index 0000000..b6893ac --- /dev/null +++ b/roles/git_repo_commit/README.md @@ -0,0 +1,59 @@ +git_repo_commit +========= + +This repo first installs and configures git on the given host and then clone the given repository, add files, commit, and push changes to remote repository. + +Requirements +------------ + +- Private access token should have provided with the write access to the remote repository. +- Provided user should have rights to push `devel` branch. +- GPG signing key should be created and public key should be placed under GitHub Account. +- Configuration variables should be provided with a variable file. + +Role Variables +-------------- + +```yaml +# default variables +git_repo_commit_repo_path: /tmp +git_repo_commit_branch: devel + +# configuration variables +git_repo_commit_git_username: "{{ git_username }}" +git_repo_commit_git_email: "{{ git_email }}" +git_repo_commit_signing_key: "{{ gpg_signingkey_id }}" +git_repo_commit_commit_message: "Git repo commit by bot: {{ git_repo_commit_git_username }}" +git_repo_commit_github_pat: "{{ github_pat }}" + +# execution variables: +git_repo_commit_repository: +git_repo_commit_file: +``` + +Dependencies +------------ + +- N/A + +Example Playbook +---------------- + +```yaml +- hosts: rootCA + roles: + - role: git_repo_commit + vars: + git_repo_commit_repository: "{{ github_inventory_repo_path }}" + git_repo_commit_file: "{{ path_file_encrypted }}" +``` + +License +------- + +GPL-3.0-only + +Author Information +------------------ + +Showroom Project Team diff --git a/roles/git_repo_commit/defaults/main.yml b/roles/git_repo_commit/defaults/main.yml new file mode 100644 index 0000000..960c3d7 --- /dev/null +++ b/roles/git_repo_commit/defaults/main.yml @@ -0,0 +1,8 @@ +--- +git_repo_commit_repo_path: /tmp +git_repo_commit_git_username: "{{ git_username }}" +git_repo_commit_git_email: "{{ git_email }}" +git_repo_commit_signing_key: "{{ gpg_signingkey_id }}" +git_repo_commit_commit_message: "Git repo commit by bot: {{ git_repo_commit_git_username }}" +git_repo_commit_branch: devel +git_repo_commit_github_pat: "{{ github_pat }}" diff --git a/roles/git_repo_commit/meta/main.yml b/roles/git_repo_commit/meta/main.yml new file mode 100644 index 0000000..c0d98f5 --- /dev/null +++ b/roles/git_repo_commit/meta/main.yml @@ -0,0 +1,17 @@ +--- +galaxy_info: + author: Showroom Project Team + description: This repo first and configures git to the host and then clone the given repository, add files, commit, and push changes to remote repository. + company: Red Hat, Inc. + license: GPL-3.0-only + min_ansible_version: "2.14" + platforms: + - name: EL + versions: + - "8" + - "9" + galaxy_tags: [] +dependencies: [] +collections: + - ansible.builtin + - community.general diff --git a/roles/git_repo_commit/tasks/git_config.yml b/roles/git_repo_commit/tasks/git_config.yml new file mode 100644 index 0000000..e98e066 --- /dev/null +++ b/roles/git_repo_commit/tasks/git_config.yml @@ -0,0 +1,40 @@ +--- +- name: Copy GPG signing key to VM + ansible.builtin.copy: + content: "{{ gpg_secret_key }}" + dest: ~/.gpg_secret.key + mode: "0600" + +- name: Import GPG key # noqa: command-instead-of-module + ansible.builtin.command: gpg --import ~/.gpg_secret.key + register: __return + failed_when: false + changed_when: __return.changed + +- name: Add user.name setting to local gitconfig + community.general.git_config: + name: user.name + scope: local + repo: "{{ __repo_absolute_path }}" + value: "{{ git_repo_commit_git_username }}" + +- name: Add user.email setting to local gitconfig + community.general.git_config: + name: user.email + scope: local + repo: "{{ __repo_absolute_path }}" + value: "{{ git_repo_commit_git_email }}" + +- name: Add a user.signingkey setting to local gitconfig + community.general.git_config: + name: user.signingkey + scope: local + repo: "{{ __repo_absolute_path }}" + value: "{{ git_repo_commit_signing_key }}" + +- name: Add a commit.gpgsign setting to local gitconfig + community.general.git_config: + name: commit.gpgsign + scope: local + repo: "{{ __repo_absolute_path }}" + value: true diff --git a/roles/git_repo_commit/tasks/main.yml b/roles/git_repo_commit/tasks/main.yml new file mode 100644 index 0000000..5900e25 --- /dev/null +++ b/roles/git_repo_commit/tasks/main.yml @@ -0,0 +1,66 @@ +--- +- name: "Set Repo absolute path" + ansible.builtin.set_fact: + __repo_absolute_path: "{{ git_repo_commit_repo_path }}/{{ git_repo_commit_repository }}" + +- name: Ensure required packages are installed + become: true + ansible.builtin.dnf: + name: + - git + - pinentry + state: "present" + when: ansible_distribution == "RedHat" + +- name: Ensure the repository is cloned + ansible.builtin.git: + repo: "https://{{ git_repo_commit_github_pat }}@github.com/{{ git_repo_commit_repository }}" + dest: "{{ __repo_absolute_path }}" + version: "{{ git_repo_commit_branch }}" + update: true + force: true + +- name: Include git_config.yml + ansible.builtin.include_tasks: git_config.yml + +- name: Copy file to repository + ansible.builtin.copy: + src: "{{ git_repo_commit_file.src }}" + dest: "{{ __repo_absolute_path }}/{{ git_repo_commit_file.dest }}" + mode: "0644" + remote_src: true + +- name: Stage files # noqa: command-instead-of-module + ansible.builtin.command: "git add -A" + args: + chdir: "{{ __repo_absolute_path }}" + register: __return + failed_when: __return.failed + changed_when: __return.changed + +- name: Commit changes # noqa: command-instead-of-module + ansible.builtin.command: "git commit -S -m '{{ git_repo_commit_commit_message }}'" + args: + chdir: "{{ __repo_absolute_path }}" + register: __return + failed_when: __return.failed + changed_when: __return.changed + +- name: Push changes # noqa: command-instead-of-module + ansible.builtin.command: "git push" + args: + chdir: "{{ __repo_absolute_path }}" + register: __return + failed_when: __return.failed + changed_when: __return.changed + +- name: Remove pushed files from local + become: true + ansible.builtin.file: + path: "{{ __var_path }}" + state: absent + loop: + - "{{ git_repo_commit_file.src }}" + - "{{ __repo_absolute_path }}" + loop_control: + loop_var: __var_path diff --git a/roles/idm_config/README.md b/roles/idm_config/README.md new file mode 100644 index 0000000..281ca37 --- /dev/null +++ b/roles/idm_config/README.md @@ -0,0 +1,109 @@ +idm_config +========= + +The role configures RH IdM Server for basic start. + +Requirements +------------ + +- RH IdM VMs should have been provisioned and deployed. +- RH IdM is running properly. +- Configuration variables should be provided with a variable file. + +Role Variables +-------------- + +```yaml +# default anf general variables +idm_config_allow_sync_ptr: true +idm_config_dynamic_update: true +ipaadmin_principal: "{{ ipaadmin_principal | default(omit) }}" +ipaadmin_password: "{{ ipaadmin_password }}" + +# DNS variables +zone_name: "{{ idm_default_dns_zone }}" +idm_config_reverse_dns_networks: "" + +# user variables +idm_users: + - name: "test-user" + first: "test first name" + last: "test surname" + email: "test@test-domain.com" + state: present + +# group +idm_user_groups: + - name: "test-group" + desc: "test group description" + +# sudo +idm_sudo_rules: + - name: "test-admin" + description: "Control sudo access on all servers for admins" + state: present + cmdcategory: all + hostcategory: all + group: + - admins + +# sudo commands +idm_sudo_commands: + - name: "/opt/test" + description: "Command to configure test command" + state: present + +# password policy +idm_password_policy: + - name: "global_policy" + state: "present" + history: "3" + maxlife: "90" + minlife: "1" + minclasses: "3" + minlength: "12" + maxfail: "5" + failinterval: "60" + lockouttime: "1200" + maxrepeat: "3" + maxsequence: "3" + dictcheck: "true" + usercheck: "true" + gracelimit: "2" + priority: "" + +# HBAC +idm_hbac_rules: + - name: test_allow_all + description: "Realm Test Admin accesses all hosts" + state: present + hostcategory: all + servicecategory: all + group: + - test + - test-other +``` + +Dependencies +------------ + +- N/A + +Example Playbook +---------------- + +```yaml +- hosts: ipahidden + roles: + - idm_config +``` + +License +------- + +GPL-3.0-only + +Author Information +------------------ + +Showroom Project Team diff --git a/roles/idm_config/defaults/main.yml b/roles/idm_config/defaults/main.yml new file mode 100644 index 0000000..b0d645b --- /dev/null +++ b/roles/idm_config/defaults/main.yml @@ -0,0 +1,5 @@ +--- +# defaults file for idm_config +idm_config_reverse_dns_networks: "" +idm_config_allow_sync_ptr: true +idm_config_dynamic_update: true diff --git a/roles/idm_config/meta/main.yml b/roles/idm_config/meta/main.yml new file mode 100644 index 0000000..213678d --- /dev/null +++ b/roles/idm_config/meta/main.yml @@ -0,0 +1,19 @@ +--- +galaxy_info: + author: Showroom Project Team + description: This roles configures RH IdM. + company: Red Hat, Inc. + license: GPL-3.0-only + min_ansible_version: "2.14" + platforms: + - name: EL + versions: + - "8" + - "9" + galaxy_tags: [] +dependencies: [] +collections: + - azure.azcollection + - ansible.utils + - community.dns + - redhat.rhel_idm diff --git a/roles/idm_config/tasks/idm_ensure_records.yml b/roles/idm_config/tasks/idm_ensure_records.yml new file mode 100644 index 0000000..d9d801c --- /dev/null +++ b/roles/idm_config/tasks/idm_ensure_records.yml @@ -0,0 +1,17 @@ +--- +- name: Find out IP address for A record + ansible.builtin.command: "dig {{ __a_records_var.nameserver }} +short" + register: __ip_a_records_var + changed_when: __ip_a_records_var.failed + tags: idm_dns_records + +- name: "Ensure PTR records for the existing A Records" + redhat.rhel_idm.ipadnsrecord: + ipaadmin_principal: "{{ ipaadmin_principal | default(omit) }}" + ipaadmin_password: "{{ ipaadmin_password }}" + zone_name: "{{ idm_default_dns_zone }}" + record_name: "{{ __a_records_var.nameserver | split('.') | first }}" + record_value: "{{ __ip_a_records_var.stdout }}" + record_type: "A" + create_reverse: true + tags: idm_dns_records diff --git a/roles/idm_config/tasks/idm_manage_dns.yml b/roles/idm_config/tasks/idm_manage_dns.yml new file mode 100644 index 0000000..3645cea --- /dev/null +++ b/roles/idm_config/tasks/idm_manage_dns.yml @@ -0,0 +1,14 @@ +--- +- name: "Create reverse DNS zones" + redhat.rhel_idm.ipadnszone: + ipaadmin_principal: "{{ ipaadmin_principal | default(omit) }}" + ipaadmin_password: "{{ ipaadmin_password }}" + allow_sync_ptr: "{{ idm_config_allow_sync_ptr }}" + dynamic_update: "{{ idm_config_dynamic_update }}" + name_from_ip: "{{ subnets_var.subnet }}" + state: present + update_policy: "{{ idm_config_update_policy | default(omit) }}" + loop: "{{ idm_config_reverse_dns_networks }}" + loop_control: + loop_var: subnets_var + when: idm_config_reverse_dns_networks | length() diff --git a/roles/idm_config/tasks/idm_manage_hbac_rules.yml b/roles/idm_config/tasks/idm_manage_hbac_rules.yml new file mode 100644 index 0000000..acac7f8 --- /dev/null +++ b/roles/idm_config/tasks/idm_manage_hbac_rules.yml @@ -0,0 +1,18 @@ +--- +- name: "Create defined HBAC rules in IdM" + redhat.rhel_idm.ipahbacrule: + ipaadmin_principal: "{{ ipaadmin_principal | default(omit) }}" + ipaadmin_password: "{{ ipaadmin_password }}" + name: "{{ idmhbac_var.name }}" + description: "{{ idmhbac_var.description | default(omit) }}" + state: "{{ idmhbac_var.state }}" + hostgroup: "{{ idmhbac_var.hostgroup | default(omit) }}" + hostcategory: "{{ idmhbac_var.hostcategory | default(omit) }}" + servicecategory: "{{ idmhbac_var.servicecategory | default(omit) }}" + group: "{{ idmhbac_var.group | default(omit) }}" + user: "{{ idmhbac_var.user | default(omit) }}" + hbacsvc: "{{ idmhbac_var.hbacsvc | default(omit) }}" + loop: "{{ idm_hbac_rules }}" + loop_control: + loop_var: idmhbac_var + when: idm_hbac_rules is defined diff --git a/roles/idm_config/tasks/idm_manage_host_groups.yml b/roles/idm_config/tasks/idm_manage_host_groups.yml new file mode 100644 index 0000000..0531c63 --- /dev/null +++ b/roles/idm_config/tasks/idm_manage_host_groups.yml @@ -0,0 +1,11 @@ +--- +- name: "Create defined host groups on IdM" + redhat.rhel_idm.ipahostgroup: + ipaadmin_principal: "{{ ipaadmin_principal | default(omit) }}" + ipaadmin_password: "{{ ipaadmin_password }}" + name: "{{ idmhostgroups_var.name }}" + description: "{{ idmhostgroups_var.desc }}" + loop: "{{ idm_host_groups }}" + loop_control: + loop_var: idmhostgroups_var + when: idm_host_groups is defined diff --git a/roles/idm_config/tasks/idm_manage_pwpolicy.yml b/roles/idm_config/tasks/idm_manage_pwpolicy.yml new file mode 100644 index 0000000..d2b2352 --- /dev/null +++ b/roles/idm_config/tasks/idm_manage_pwpolicy.yml @@ -0,0 +1,25 @@ +--- +- name: "Create Password Policies" + redhat.rhel_idm.ipapwpolicy: + ipaadmin_principal: "{{ ipaadmin_principal | default(omit) }}" + ipaadmin_password: "{{ ipaadmin_password }}" + name: "{{ idmpwpolicy_var.name | default(omit) }}" + state: "{{ idmpwpolicy_var.state | default(omit) }}" + history: "{{ idmpwpolicy_var.history | default(omit) }}" + maxlife: "{{ idmpwpolicy_var.maxlife | default(omit) }}" + minlife: "{{ idmpwpolicy_var.minlife | default(omit) }}" + minclasses: "{{ idmpwpolicy_var.minclasses | default(omit) }}" + minlength: "{{ idmpwpolicy_var.minlength | default(omit) }}" + maxfail: "{{ idmpwpolicy_var.maxfail | default(omit) }}" + failinterval: "{{ idmpwpolicy_var.failinterval | default(omit) }}" + lockouttime: "{{ idmpwpolicy_var.lockouttime | default(omit) }}" + maxrepeat: "{{ idmpwpolicy_var.maxrepeat | default(omit) }}" + maxsequence: "{{ idmpwpolicy_var.maxsequence | default(omit) }}" + dictcheck: "{{ idmpwpolicy_var.dictcheck | default(omit) }}" + usercheck: "{{ idmpwpolicy_var.usercheck | default(omit) }}" + gracelimit: "{{ idmpwpolicy_var.gracelimit | default(omit) }}" + priority: "{{ idmpwpolicy_var.priority | default(omit) }}" + loop: "{{ idm_password_policy }}" + loop_control: + loop_var: idmpwpolicy_var + when: idm_password_policy is defined diff --git a/roles/idm_config/tasks/idm_manage_record.yml b/roles/idm_config/tasks/idm_manage_record.yml new file mode 100644 index 0000000..b883a1d --- /dev/null +++ b/roles/idm_config/tasks/idm_manage_record.yml @@ -0,0 +1,13 @@ +--- +- name: Retrieve all values from all nameservers for two DNS names + community.dns.nameserver_record_info: + name: + - "{{ idm_default_dns_zone }}" + type: A + register: __a_records + +- name: Configure PTR records + ansible.builtin.include_tasks: idm_ensure_records.yml + loop: "{{ __a_records.results[0].result }}" + loop_control: + loop_var: __a_records_var diff --git a/roles/idm_config/tasks/idm_manage_sudo_command.yml b/roles/idm_config/tasks/idm_manage_sudo_command.yml new file mode 100644 index 0000000..7de45c9 --- /dev/null +++ b/roles/idm_config/tasks/idm_manage_sudo_command.yml @@ -0,0 +1,12 @@ +--- +- name: "Create Sudo Commands" + redhat.rhel_idm.ipasudocmd: + ipaadmin_principal: "{{ ipaadmin_principal | default(omit) }}" + ipaadmin_password: "{{ ipaadmin_password }}" + name: "{{ idmsudocommand_var.name }}" + description: "{{ idmsudocommand_var.description | default(omit) }}" + state: "{{ idmsudocommand_var.state }}" + loop: "{{ idm_sudo_commands }}" + loop_control: + loop_var: idmsudocommand_var + when: idm_sudo_commands is defined diff --git a/roles/idm_config/tasks/idm_manage_sudo_rules.yml b/roles/idm_config/tasks/idm_manage_sudo_rules.yml new file mode 100644 index 0000000..88a1784 --- /dev/null +++ b/roles/idm_config/tasks/idm_manage_sudo_rules.yml @@ -0,0 +1,18 @@ +--- +- name: "Create defined sudo rules in IdM" + redhat.rhel_idm.ipasudorule: + ipaadmin_principal: "{{ ipaadmin_principal | default(omit) }}" + ipaadmin_password: "{{ ipaadmin_password }}" + name: "{{ idmsudo_var.name }}" + description: "{{ idmsudo_var.description | default(omit) }}" + state: "{{ idmsudo_var.state }}" + cmdcategory: "{{ idmsudo_var.cmdcategory | default(omit) }}" + hostcategory: "{{ idmsudo_var.hostcategory | default(omit) }}" + group: "{{ idmsudo_var.group | default(omit) }}" + user: "{{ idmsudo_var.user | default(omit) }}" + allow_sudocmd: "{{ idmsudo_var.allow_sudocmd | default(omit) }}" + sudooption: "{{ idmsudo_var.sudooption | default(omit) }}" + loop: "{{ idm_sudo_rules }}" + loop_control: + loop_var: idmsudo_var + when: idm_sudo_rules is defined diff --git a/roles/idm_config/tasks/idm_manage_user_groups.yml b/roles/idm_config/tasks/idm_manage_user_groups.yml new file mode 100644 index 0000000..f90c405 --- /dev/null +++ b/roles/idm_config/tasks/idm_manage_user_groups.yml @@ -0,0 +1,35 @@ +--- +- name: "Create defined user groups on IdM" + redhat.rhel_idm.ipagroup: + ipaadmin_principal: "{{ ipaadmin_principal | default(omit) }}" + ipaadmin_password: "{{ ipaadmin_password }}" + description: "{{ idmusergroups_var.desc }}" + name: "{{ idmusergroups_var.name }}" + loop: "{{ idm_user_groups }}" + loop_control: + loop_var: idmusergroups_var + when: idm_user_groups is defined + +- name: "Add defined user to group on IdM" + redhat.rhel_idm.ipagroup: + ipaadmin_principal: "{{ ipaadmin_principal | default(omit) }}" + ipaadmin_password: "{{ ipaadmin_password }}" + name: "{{ idmgroupusers_var.group }}" + user: "{{ idmgroupusers_var.user }}" + action: member + loop: "{{ idm_group_users + showroom_group_users }}" + loop_control: + loop_var: idmgroupusers_var + when: idm_group_users is defined or showroom_group_users is defined + +- name: "Add defined groups to group on IdM" + redhat.rhel_idm.ipagroup: + ipaadmin_principal: "{{ ipaadmin_principal | default(omit) }}" + ipaadmin_password: "{{ ipaadmin_password }}" + name: "{{ idmgroupgroups_var.name }}" + group: "{{ idmgroupgroups_var.group }}" + state: "{{ idmgroupgroups_var.state }}" + loop: "{{ idm_group_groups }}" + loop_control: + loop_var: idmgroupgroups_var + when: idm_group_groups is defined diff --git a/roles/idm_config/tasks/idm_manage_users.yml b/roles/idm_config/tasks/idm_manage_users.yml new file mode 100644 index 0000000..94ec76c --- /dev/null +++ b/roles/idm_config/tasks/idm_manage_users.yml @@ -0,0 +1,27 @@ +--- +- name: "Add defined users on IdM" + redhat.rhel_idm.ipauser: + ipaadmin_principal: "{{ ipaadmin_principal | default(omit) }}" + ipaadmin_password: "{{ ipaadmin_password }}" + name: "{{ idmuser_var.name }}" + first: "{{ idmuser_var.first }}" + last: "{{ idmuser_var.last }}" + city: "{{ idmuser_var.city | default(omit) }}" + displayname: "{{ idmuser_var.first }} {{ idmuser_var.last }}" + departmentnumber: "{{ idmuser_var.departmentnumber | default(omit) }}" + email: "{{ idmuser_var.email | default(omit) }}" + employeenumber: "{{ idmuser_var.employeenumber | default(omit) }}" + employeetype: "{{ idmuser_var.employeetype | default(omit) }}" + title: "{{ idmuser_var.title | default(omit) }}" + userstate: "{{ idmuser_var.userstate | default(omit) }}" + password: "{{ idmuser_var.password | default(omit) }}" + state: "{{ idmuser_var.state }}" + shell: "{{ idmuser_var.shell | default('/bin/bash') }}" + homedir: "{{ idmuser_var.homedir | default(omit) }}" + sshpubkey: "{{ idmuser_var.sshpubkey | default(omit) }}" + update_password: "{{ idmuser_var.update_password | default(omit) }}" + passwordexpiration: "{{ idmuser_var.passwordexpiration | default(omit) }}" + loop: "{{ idm_users + showroom_users }}" + loop_control: + loop_var: idmuser_var + no_log: true diff --git a/roles/idm_config/tasks/main.yml b/roles/idm_config/tasks/main.yml new file mode 100644 index 0000000..0c2014b --- /dev/null +++ b/roles/idm_config/tasks/main.yml @@ -0,0 +1,44 @@ +--- +- name: Configure IdM Users + ansible.builtin.include_tasks: + file: idm_manage_users.yml + when: idm_users is defined or showroom_users is defined + +- name: Configure IdM User Groups + ansible.builtin.include_tasks: + file: idm_manage_user_groups.yml + when: idm_user_groups is defined or idm_group_users is defined or showroom_group_users is defined or idm_group_groups is defined + +- name: Configure IdM Host Groups + ansible.builtin.include_tasks: + file: idm_manage_host_groups.yml + when: idm_host_groups is defined + +- name: Configure IdM HBAC rules + ansible.builtin.include_tasks: + file: idm_manage_hbac_rules.yml + when: idm_hbac_rules is defined + +- name: Configure IdM sudo commands + ansible.builtin.include_tasks: + file: idm_manage_sudo_command.yml + when: idm_sudo_commands is defined + +- name: Configure IdM sudo rules + ansible.builtin.include_tasks: + file: idm_manage_sudo_rules.yml + when: idm_sudo_rules is defined + +- name: Configure IdM reverse DNS zones + ansible.builtin.include_tasks: + file: idm_manage_dns.yml + when: idm_config_reverse_dns_networks | length() + +- name: Configure IdM password policies + ansible.builtin.include_tasks: + file: idm_manage_pwpolicy.yml + when: idm_password_policy | length() + +- name: Configure IdM Reverse Records for the existing A Records + ansible.builtin.include_tasks: + file: idm_manage_record.yml diff --git a/roles/imagebuilder/README.md b/roles/imagebuilder/README.md new file mode 100644 index 0000000..8ded558 --- /dev/null +++ b/roles/imagebuilder/README.md @@ -0,0 +1,80 @@ +imagebuilder +========= + +This roles creates custom images using RH Insights Image builder API if it's not already in Azure resource group. + +Requirements +------------ +An Azure custom app must be created and granted contributor access on subscription level to perform the ansible tasks. +Red Hat subscriptions and Azure subscriptions should be integrated and Red Hat Image Builder enterprise app in Azure Entra ID should have contributor role at least at resource group level. + +Role Variables +-------------- + +```yaml +--- +# variable to run role +imagebuilder: true + +imagebuilder_auth_url: "https://sso.redhat.com/auth/realms/redhat-external/protocol/openid-connect/token" +imagebuilder_url: "https://console.redhat.com/api/image-builder/v1/compose" +imagebuilder_status_url: "https://console.redhat.com/api/image-builder/v1/composes/" + +imagebuilder_bootstrap_target: "azure" +imagebuilder_image_extension: "vhd" + + +# you can use the API url to pull a current list of available distributions +imagebuilder_distribution: "rhel-94" +# default image name +imagebuilder_image_name: "image-rhel-94" + +# what we will call the image and where we put it +imagebuilder_file_directory: "/tmp" +imagebuilder_image_definition_file: "request-base-image.json" +imagebuilder_image_file_name: "{{ builder_image_name }}.{{ image_extension }}" +imagebuilder_image_file_path: "{{ imagebuilder_file_directory }}/{{ imagebuilder_image_file_name }}" + +# Custom packages to include in image. Example as follow; +imagebuilder_packages: + - firewalld + - gdisk + - git-all + +# Image definition volume sizes +imagebuilder_filesystem: + slash_size: 10737418240 + home_size: 1073741824 + var_size: 10737418240 + tmp_size: 1073741824 + var_tmp_size: 1073741824 + var_log_size: 2147483648 + var_log_audit_size: 2147483648 + +imagebuilder_timezone: "Europe/Berlin" +``` + +Dependencies +------------ + +- azure_resource_group + +Example Playbook +---------------- + +```yaml +- hosts: bastion + roles: + - azure_resource_group + - imagebuilder +``` + +License +------- + +GPL-3.0-only + +Author Information +------------------ + +Showroom Project Team \ No newline at end of file diff --git a/roles/imagebuilder/defaults/main.yml b/roles/imagebuilder/defaults/main.yml new file mode 100644 index 0000000..5decca6 --- /dev/null +++ b/roles/imagebuilder/defaults/main.yml @@ -0,0 +1,51 @@ +--- +# defaults file for imagebuilder +# variable to run role +# imagebuilder: true + +imagebuilder_auth_url: "https://sso.redhat.com/auth/realms/redhat-external/protocol/openid-connect/token" +imagebuilder_url: "https://console.redhat.com/api/image-builder/v1/compose" +imagebuilder_status_url: "https://console.redhat.com/api/image-builder/v1/composes/" + +imagebuilder_bootstrap_target: "{{ bootstrap_target }}" +imagebuilder_image_extension: "{% if bootstrap_target == 'azure' %}vhd{% elif bootstrap_target == 'aws' %}ami{% elif bootstrap_target == 'vsphere' %}vmdk{% elif bootstrap_target == 'gcp' %}img{% elif bootstrap_target == 'edge-installer' %}iso{% endif %}" # noqa yaml[line-length] + +# type extension +# vsphere vmdk +# aws ami +# image-installer iso +# guest-image qcow2 +# azure vhd +# gcp img +# edge-commit +# edge-installer iso + +# you can use the API url to pull a current list of available distributions +imagebuilder_images: + - distribution: "rhel-94" + image_name: "image-rhel-94" +imagebuilder_activation_key: "{{ rh_activation_key }}" + +# what we will call the image and where we put it +imagebuilder_file_directory: "/tmp" +imagebuilder_image_definition_file: "request-base-image.json" +imagebuilder_image_file_name: "{{ builder_image_name }}.{{ image_extension }}" +imagebuilder_image_file_path: "{{ imagebuilder_file_directory }}/{{ imagebuilder_image_file_name }}" + +# Custom packages to include in image. Example as follow; +# imagebuilder_packages: +# - firewalld +# - gdisk +# - git-all + +# Image definition volume sizes in bytes +imagebuilder_filesystem: + slash_size: 10737418240 + home_size: 10737418240 + var_size: 10737418240 + tmp_size: 2147483648 + var_tmp_size: 2147483648 + var_log_size: 4294967296 + var_log_audit_size: 2147483648 + +imagebuilder_timezone: "Europe/Berlin" diff --git a/roles/imagebuilder/meta/main.yml b/roles/imagebuilder/meta/main.yml new file mode 100644 index 0000000..c0d6079 --- /dev/null +++ b/roles/imagebuilder/meta/main.yml @@ -0,0 +1,16 @@ +--- +galaxy_info: + author: Showroom Project Team + description: This roles creates custom images using RH image builder API if it's not already in Azure resource group. + company: Red Hat, Inc. + license: GPL-3.0-only + min_ansible_version: "2.13" + platforms: + - name: EL + versions: + - "8" + - "9" + galaxy_tags: [] +dependencies: [] +collections: + - azure.azcollection diff --git a/roles/imagebuilder/tasks/compose_wait.yml b/roles/imagebuilder/tasks/compose_wait.yml new file mode 100644 index 0000000..01eb2a0 --- /dev/null +++ b/roles/imagebuilder/tasks/compose_wait.yml @@ -0,0 +1,43 @@ +--- +- name: "Retrieve a new access token" + ansible.builtin.include_tasks: retrieve_composer_access_token.yml + +- name: "Wait for console build to complete" + block: + - name: "Set or increment retry count" + ansible.builtin.set_fact: + __compose_retry_count: "{{ 0 if __compose_retry_count is undefined else __compose_retry_count | int + 1 }}" + + - name: "Watch for finished image compose" + ansible.builtin.uri: + url: "{{ imagebuilder_status_url }}{{ __compose_result.json.id }}" + method: "GET" + headers: + Authorization: "Bearer {{ __access_token }}" + Content-Type: "application/json" + validate_certs: true + return_content: true + register: __compose_status + until: + - __compose_status.json is defined + - __compose_status.json.image_status.status != "building" + - __compose_status.json.image_status.status != "pending" + retries: 60 + delay: 10 + + - name: "Finished build" + ansible.builtin.set_fact: + __composer_done: true + + rescue: + - name: "Retry count exceeded, time to fail" + ansible.builtin.fail: + msg: "Maximum retry count exceeded, have tried {{ __compose_retry_count }} times" + when: __compose_retry_count|int == 10 + + - name: "Still waiting..." + ansible.builtin.debug: + msg: "Still waiting, retry count {{ __compose_retry_count }} of 10, retrying..." + + - name: "Retry" + ansible.builtin.include_tasks: compose_wait.yml diff --git a/roles/imagebuilder/tasks/create_image.yml b/roles/imagebuilder/tasks/create_image.yml new file mode 100644 index 0000000..ed82e5a --- /dev/null +++ b/roles/imagebuilder/tasks/create_image.yml @@ -0,0 +1,20 @@ +--- +- name: Check if image already exist + block: + - name: List images by resource group + azure.azcollection.azure_rm_image_info: + subscription_id: "{{ azure_subscription_id | default(omit) }}" + tenant: "{{ azure_tenant_id | default(omit) }}" + client_id: "{{ techuser_ansible_client_id | default(omit) }}" + secret: "{{ techuser_ansible_secret_value | default(omit) }}" + name: "{{ image.image_name }}" + resource_group: "{{ azure_rg }}" + + rescue: + - name: Show fail message + ansible.builtin.debug: + msg: "Image {{ image.image_name }} doesn't exist in {{ azure_rg }}, creating new image..." + + - name: "Create image" + ansible.builtin.include_tasks: + file: deploy_image.yml diff --git a/roles/imagebuilder/tasks/deploy_image.yml b/roles/imagebuilder/tasks/deploy_image.yml new file mode 100644 index 0000000..fedebb8 --- /dev/null +++ b/roles/imagebuilder/tasks/deploy_image.yml @@ -0,0 +1,57 @@ +--- +# tasks file for imagebuilder +- name: "Retrieve an access token" + ansible.builtin.include_tasks: retrieve_composer_access_token.yml + +- name: "Setting __imagebuilder_image_type to azure bootstrap target" + ansible.builtin.set_fact: + __imagebuilder_image_type: "azure" + when: imagebuilder_bootstrap_target == "azure" + +- name: "Create the build configuration from template" + ansible.builtin.template: + src: "template.request-base-image.json.j2" + dest: "{{ imagebuilder_file_directory }}/{{ imagebuilder_image_definition_file }}" + mode: "0644" + +- name: "Create the image from the configuration" + ansible.builtin.uri: + url: "{{ imagebuilder_url }}" + method: "POST" + headers: + Authorization: "Bearer {{ __access_token }}" + Content-Type: "application/json" + body: "{{ lookup('ansible.builtin.file', '{{ imagebuilder_file_directory }}/{{ imagebuilder_image_definition_file }}') }}" + body_format: json + status_code: 201 + validate_certs: true + return_content: true + register: __compose_result + +- name: "Assert success" + ansible.builtin.assert: + that: + - __compose_result.failed == false + - __compose_result.json.id is defined + +- name: "Watch for finished image compose" + ansible.builtin.include_tasks: + file: compose_wait.yml + +- name: "Assert that the build was successful" + ansible.builtin.assert: + that: + - __compose_status.json.image_status.status == "success" + +- name: "Azure image-specific handling block" + when: __imagebuilder_image_type == "azure" + block: + - name: "Assert that the build uploaded to Azure" + ansible.builtin.assert: + that: + - __compose_status.json.image_status.upload_status.options.image_name is defined + - __compose_status.json.image_status.upload_status.status == "success" + + - name: "Retrieve VHD name from Azure imagebuild" + ansible.builtin.set_fact: + imagebuilder_vhd: "{{ __compose_status.json.image_status.upload_status.options.image_name }}" diff --git a/roles/imagebuilder/tasks/main.yml b/roles/imagebuilder/tasks/main.yml new file mode 100644 index 0000000..e03a60e --- /dev/null +++ b/roles/imagebuilder/tasks/main.yml @@ -0,0 +1,7 @@ +--- +- name: "Create image" + ansible.builtin.include_tasks: + file: create_image.yml + loop: "{{ imagebuilder_images }}" + loop_control: + loop_var: image diff --git a/roles/imagebuilder/tasks/retrieve_composer_access_token.yml b/roles/imagebuilder/tasks/retrieve_composer_access_token.yml new file mode 100644 index 0000000..7346546 --- /dev/null +++ b/roles/imagebuilder/tasks/retrieve_composer_access_token.yml @@ -0,0 +1,34 @@ +--- +- name: "Retrieve an access token" + ansible.builtin.uri: + url: "{{ imagebuilder_auth_url }}" + method: "POST" + body: + grant_type: "refresh_token" + client_id: "rhsm-api" + refresh_token: "{{ satellite_rhsm_offline_token }}" + body_format: "form-urlencoded" + validate_certs: true + return_content: true + register: __result + +- name: "Set access token" + ansible.builtin.set_fact: + __access_token: "{{ __result.json.access_token }}" + +- name: "Test connection - get API version" + ansible.builtin.uri: + url: "https://console.redhat.com/api/image-builder/v1/version" + method: "GET" + headers: + Authorization: "Bearer {{ __access_token }}" + Content-Type: "application/json" + body_format: "form-urlencoded" + validate_certs: true + return_content: true + register: __result + +- name: "Assert success" + ansible.builtin.assert: + that: + - __result.json.version == "1.0" diff --git a/roles/imagebuilder/templates/imagebuilder_azure_upload_request.j2 b/roles/imagebuilder/templates/imagebuilder_azure_upload_request.j2 new file mode 100644 index 0000000..3c29c76 --- /dev/null +++ b/roles/imagebuilder/templates/imagebuilder_azure_upload_request.j2 @@ -0,0 +1 @@ +"type": "azure", "options": { "image_name": "{{ image.image_name }}", "tenant_id": "{{ azure_tenant_id }}", "subscription_id": "{{ azure_subscription_id }}", "resource_group": "{{ azure_rg }}" } \ No newline at end of file diff --git a/roles/imagebuilder/templates/imagebuilder_filesystem.j2 b/roles/imagebuilder/templates/imagebuilder_filesystem.j2 new file mode 100644 index 0000000..5548bca --- /dev/null +++ b/roles/imagebuilder/templates/imagebuilder_filesystem.j2 @@ -0,0 +1,30 @@ + "filesystem": [ + { + "min_size": {{ imagebuilder_filesystem.slash_size }}, + "mountpoint": "/" + }, + { + "min_size": {{ imagebuilder_filesystem.home_size }}, + "mountpoint": "/home" + }, + { + "min_size": {{ imagebuilder_filesystem.tmp_size }}, + "mountpoint": "/tmp" + }, + { + "min_size": {{ imagebuilder_filesystem.var_size }}, + "mountpoint": "/var" + }, + { + "min_size": {{ imagebuilder_filesystem.var_tmp_size }}, + "mountpoint": "/var/tmp" + }, + { + "min_size": {{ imagebuilder_filesystem.var_log_size }}, + "mountpoint": "/var/log" + }, + { + "min_size": {{ imagebuilder_filesystem.var_log_audit_size }}, + "mountpoint": "/var/log/audit" + } + ], \ No newline at end of file diff --git a/roles/imagebuilder/templates/imagebuilder_packages.j2 b/roles/imagebuilder/templates/imagebuilder_packages.j2 new file mode 100644 index 0000000..1c4fd31 --- /dev/null +++ b/roles/imagebuilder/templates/imagebuilder_packages.j2 @@ -0,0 +1,3 @@ + "packages": [ + "{{ imagebuilder_packages |join('",\n "') }}" + ], \ No newline at end of file diff --git a/roles/imagebuilder/templates/imagebuilder_rh_subscriptions.j2 b/roles/imagebuilder/templates/imagebuilder_rh_subscriptions.j2 new file mode 100644 index 0000000..8d8ff1e --- /dev/null +++ b/roles/imagebuilder/templates/imagebuilder_rh_subscriptions.j2 @@ -0,0 +1,7 @@ + "subscription": { + "base-url": "https://cdn.redhat.com/", + "insights": false, + "server-url": "subscription.rhsm.redhat.com", + "organization": {{ rh_organization_number }}, + "activation-key": "{{ imagebuilder_activation_key }}" + }, \ No newline at end of file diff --git a/roles/imagebuilder/templates/imagebuilder_timezone.j2 b/roles/imagebuilder/templates/imagebuilder_timezone.j2 new file mode 100644 index 0000000..81b55bf --- /dev/null +++ b/roles/imagebuilder/templates/imagebuilder_timezone.j2 @@ -0,0 +1,3 @@ + "timezone": { + "timezone": "Europe/Berlin" + }, \ No newline at end of file diff --git a/roles/imagebuilder/templates/template.request-base-image.json.j2 b/roles/imagebuilder/templates/template.request-base-image.json.j2 new file mode 100644 index 0000000..46a85c8 --- /dev/null +++ b/roles/imagebuilder/templates/template.request-base-image.json.j2 @@ -0,0 +1,20 @@ +{ + "image_name": "{{ image.image_name }}", + "distribution": "{{ image.distribution }}", + "customizations": { + {% if imagebuilder_packages is defined -%}{%- include "imagebuilder_packages.j2" -%}{%- endif %} + {% if imagebuilder_filesystem is defined -%}{%- include "imagebuilder_filesystem.j2" -%}{%- endif %} + {% if rh_organization_number is defined and rh_activation_key is defined -%}{%- include "imagebuilder_rh_subscriptions.j2" -%}{%- endif %} + {% if imagebuilder_timezone is defined -%}{%- include "imagebuilder_timezone.j2" -%}{%- endif %} + "fips": { + "enabled": true + } + }, + "image_requests": [ + { + "image_type": "{{ __imagebuilder_image_type }}", + "architecture": "x86_64", + "upload_request": { {% if __imagebuilder_image_type == 'azure' -%}{%- include "imagebuilder_azure_upload_request.j2" -%}{%- endif %} } + } + ] +} \ No newline at end of file diff --git a/roles/init_environment/README.md b/roles/init_environment/README.md new file mode 100644 index 0000000..6ed82ac --- /dev/null +++ b/roles/init_environment/README.md @@ -0,0 +1,45 @@ +init_environment +========= + +This roles initializes configuration on local host to be able to run ansible code that is required for the project. + +* creates, encrypts and commit SSH RSA keypair if it does not exist in the inventory repository +* configures SSH config file to be able to access to the internal resources through Bastion host + +Requirements +------------ +The role is disabled by default unless you need to configure it. + +Role Variables +-------------- + +```yaml +--- +# variable to run role +init_environment_set: false + +``` + +Dependencies +------------ + + +Example Playbook +---------------- + +```yaml +- hosts: localhost + gather_facts: true + roles: + - init_environment +``` + +License +------- + +GPL-3.0-only + +Author Information +------------------ + +Showroom Project Team diff --git a/roles/init_environment/defaults/main.yml b/roles/init_environment/defaults/main.yml new file mode 100644 index 0000000..411d165 --- /dev/null +++ b/roles/init_environment/defaults/main.yml @@ -0,0 +1,13 @@ +--- +init_environment_set: false + +init_environment_collection_install_force: false + +init_environment_ansible_cfg_path_collection_dir: imported/collections +init_environment_ansible_cfg_path_roles_dir: imported/roles +init_environment_ansible_cfg_path_requirements_dir: collections +init_environment_ansible_cfg_vault_password_file: .vault-password + +init_environment_hub_auth_url: https://sso.redhat.com/auth/realms/redhat-external/protocol/openid-connect/token +init_environment_hub_published_url: https://console.redhat.com/api/automation-hub/content/published/ +init_environment_hub_validated_url: https://console.redhat.com/api/automation-hub/content/validated/ diff --git a/roles/init_environment/meta/main.yml b/roles/init_environment/meta/main.yml new file mode 100644 index 0000000..a48fec2 --- /dev/null +++ b/roles/init_environment/meta/main.yml @@ -0,0 +1,15 @@ +--- +galaxy_info: + author: Showroom Project Team + description: This roles configures local SSH config. + company: Red Hat, Inc. + license: GPL-3.0-only + min_ansible_version: "2.14" + platforms: + - name: EL + versions: + - "8" + - "9" + galaxy_tags: [] +dependencies: [] +collections: [] diff --git a/roles/init_environment/tasks/ansible_config.yml b/roles/init_environment/tasks/ansible_config.yml new file mode 100644 index 0000000..5b805c3 --- /dev/null +++ b/roles/init_environment/tasks/ansible_config.yml @@ -0,0 +1,12 @@ +--- +- name: "Write ansible.cfg" + ansible.builtin.template: + src: ansible.cfg.j2 + dest: "../ansible.cfg" + mode: "0600" + +- name: Create Ansible Vault Password file + ansible.builtin.copy: + dest: "../{{ init_environment_ansible_cfg_vault_password_file }}" + content: "{{ vault_password }}" + mode: "0600" diff --git a/roles/init_environment/tasks/collection_install.yml b/roles/init_environment/tasks/collection_install.yml new file mode 100644 index 0000000..96de5c0 --- /dev/null +++ b/roles/init_environment/tasks/collection_install.yml @@ -0,0 +1,20 @@ +--- +- name: "Install requirements file based collections {{ 'forcefully' if init_environment_collection_install_force }}" + ansible.builtin.command: + cmd: "ansible-galaxy collection install {{ '--force' if init_environment_collection_install_force }} -r {{ init_environment_ansible_cfg_path_requirements_dir }}/requirements.yml" # noqa yaml[line-length] + chdir: "../" + environment: + ANSIBLE_CONFIG: "./ansible.cfg" + register: __collection_install + changed_when: "'Nothing to do' not in __collection_install.stdout" + failed_when: __collection_install.rc not in [0] + +- name: "Install requirements file based roles {{ 'forcefully' if init_environment_collection_install_force }}" + ansible.builtin.command: + cmd: "ansible-galaxy role install {{ '--force' if init_environment_collection_install_force }} -p {{ init_environment_ansible_cfg_path_roles_dir }} -r {{ init_environment_ansible_cfg_path_requirements_dir }}/requirements.yml" # noqa yaml[line-length] + chdir: "../" + environment: + ANSIBLE_CONFIG: "./ansible.cfg" + register: __role_install + changed_when: "'already installed' not in __role_install.stderr" + failed_when: __role_install.rc not in [0] diff --git a/roles/init_environment/tasks/main.yml b/roles/init_environment/tasks/main.yml new file mode 100644 index 0000000..55b01e3 --- /dev/null +++ b/roles/init_environment/tasks/main.yml @@ -0,0 +1,13 @@ +--- +- name: Configure Local SSH + ansible.builtin.include_tasks: + file: manage_ssh.yml + when: vm_user_private_key is not defined + +- name: Configure Local Ansible Config + ansible.builtin.include_tasks: + file: ansible_config.yml + +- name: Install Ansible Collections and Roles to Local + ansible.builtin.include_tasks: + file: collection_install.yml diff --git a/roles/init_environment/tasks/manage_ssh.yml b/roles/init_environment/tasks/manage_ssh.yml new file mode 100644 index 0000000..e13d936 --- /dev/null +++ b/roles/init_environment/tasks/manage_ssh.yml @@ -0,0 +1,48 @@ +--- +- name: Generate an OpenSSH keypair with the default values (4096 bits, rsa) + community.crypto.openssh_keypair: + path: ~/.ssh/id_rsa_{{ vm_user }} + +- name: Add include showroom-config + ansible.builtin.lineinfile: + path: ~/.ssh/config + line: "Include showroom-config" + regexp: "^Include showroom-config" + mode: "0644" + insertbefore: BOF + state: present + create: true + +- name: Populate and configure local SSH config Bastion + ansible.builtin.template: + src: config.j2 + dest: ~/.ssh/showroom-config + mode: "0644" + +- name: Import vault_string + ansible.builtin.import_role: + name: vault_string + vars: + vault_string_filepath: "~/.ssh/id_rsa_{{ vm_user }}" + vault_string_variable_name: "vm_user_private_key" + +- name: Import git_repo_commit + ansible.builtin.import_role: + name: git_repo_commit + vars: + git_repo_commit_repository: "{{ github_inventory_repo_path }}" + git_repo_commit_file: { src: "{{ __path_file_encrypted }}", dest: "group_vars/all/" } + +- name: Import vault_string + ansible.builtin.import_role: + name: vault_string + vars: + vault_string_filepath: "~/.ssh/id_rsa_{{ vm_user }}.pub" + vault_string_variable_name: "vm_user_public_key" + +- name: Import git_repo_commit + ansible.builtin.import_role: + name: git_repo_commit + vars: + git_repo_commit_repository: "{{ github_inventory_repo_path }}" + git_repo_commit_file: { src: "{{ __path_file_encrypted }}", dest: "group_vars/all/" } diff --git a/roles/init_environment/templates/ansible.cfg.j2 b/roles/init_environment/templates/ansible.cfg.j2 new file mode 100644 index 0000000..6aaceca --- /dev/null +++ b/roles/init_environment/templates/ansible.cfg.j2 @@ -0,0 +1,45 @@ +[defaults] + +ansible_managed = Please do not change this file directly since it is managed by Ansible and will be overwritten. +collections_path = {{ init_environment_ansible_cfg_path_collection_dir }} +roles_path = ./roles:{{ init_environment_ansible_cfg_path_collection_dir }}/roles +log_path = $HOME/ansible.log + +forks = 20 +host_key_checking = false +gathering = smart +fact_caching = jsonfile +fact_caching_connection = $HOME/ansible/facts +fact_caching_timeout = 7200 +nocows = true +callbacks_enabled = timer, profile_tasks +display_skipped_hosts = false +remote_user = {{ vm_user }} +ansible_ssh_private_key_file = ~/.ssh/id_rsa + +vault_password_file = {{ init_environment_ansible_cfg_vault_password_file }} + +[ssh_connection] +control_path = %(directory)s/%%h-%%r-%%p +ssh_args = -o ControlMaster=auto -o ControlPersist=600s +pipelining = true + +[inventory] +ignore_patterns = id_rsa*, SSH_keys +unparsed_is_failed = true + +[galaxy] +server_list = automation_hub_published, automation_hub_validated, galaxy + +[galaxy_server.automation_hub_published] +url={{ init_environment_hub_published_url }} +auth_url={{ init_environment_hub_auth_url }} +token={{ rh_automation_hub_offline_token }} + +[galaxy_server.automation_hub_validated] +url= {{ init_environment_hub_validated_url }} +auth_url={{ init_environment_hub_auth_url }} +token={{ rh_automation_hub_offline_token }} + +[galaxy_server.galaxy] +url=https://galaxy.ansible.com/ diff --git a/roles/init_environment/templates/config.j2 b/roles/init_environment/templates/config.j2 new file mode 100644 index 0000000..30b9770 --- /dev/null +++ b/roles/init_environment/templates/config.j2 @@ -0,0 +1,20 @@ +# Showroom Bastion SSH Jumphost +Host {{ hostvars[groups.bastion | first]['ansible_host'] }} + User {{ vm_user }} + IdentityFile ~/.ssh/id_rsa_{{ vm_user }} + HostName {{ hostvars[groups.bastion | first]['ansible_host'] }} + Port 22 + UserKnownHostsFile /dev/null + StrictHostKeyChecking no + ServerAliveInterval 300 + ServerAliveCountMax 2 + +# Showroom SSH Clients +Host *.{{ azure_dns_private_dns_zone }} + ProxyJump {{ hostvars[groups.bastion | first]['ansible_host'] }} + User {{ vm_user }} + IdentityFile ~/.ssh/id_rsa_{{ vm_user }} + UserKnownHostsFile /dev/null + StrictHostKeyChecking no + ServerAliveInterval 300 + ServerAliveCountMax 2 diff --git a/roles/manage_reverse_proxy/README.md b/roles/manage_reverse_proxy/README.md new file mode 100644 index 0000000..037e781 --- /dev/null +++ b/roles/manage_reverse_proxy/README.md @@ -0,0 +1,48 @@ +manage_reverse_proxy +========= + +This roles installs and configures Nginx on the given host. It also configures Azure DNS and NSGs. + +Requirements +------------ + + +Role Variables +-------------- + +```yaml +# defaults file for manage_reverse_proxy +manage_reverse_proxy_letsencrypt_dir: "/etc/letsencrypt" +manage_reverse_proxy_ssl_key_path: "{{ manage_reverse_proxy_letsencrypt_dir }}/{{ inventory_hostname }}.key" +manage_reverse_proxy_crt_path: "{{ manage_reverse_proxy_letsencrypt_dir }}/{{ inventory_hostname }}.crt" + +# azure authentication variables +azure_subscription_id: +azure_tenant_id: +techuser_ansible_client_id: +techuser_ansible_secret_value: +``` + +Dependencies +------------ + +- public_certificate + +Example Playbook +---------------- + +```yaml +- hosts: bastion + roles: + - manage_reverse_proxy +``` + +License +------- + +GPL-3.0-only + +Author Information +------------------ + +Showroom Project Team diff --git a/roles/manage_reverse_proxy/defaults/main.yml b/roles/manage_reverse_proxy/defaults/main.yml new file mode 100644 index 0000000..427593d --- /dev/null +++ b/roles/manage_reverse_proxy/defaults/main.yml @@ -0,0 +1,4 @@ +--- +manage_reverse_proxy_letsencrypt_dir: "/etc/letsencrypt" +manage_reverse_proxy_ssl_key_path: "{{ manage_reverse_proxy_letsencrypt_dir }}/{{ inventory_hostname }}.key" +manage_reverse_proxy_crt_path: "{{ manage_reverse_proxy_letsencrypt_dir }}/{{ inventory_hostname }}.crt" diff --git a/roles/manage_reverse_proxy/handlers/main.yml b/roles/manage_reverse_proxy/handlers/main.yml new file mode 100644 index 0000000..0836a67 --- /dev/null +++ b/roles/manage_reverse_proxy/handlers/main.yml @@ -0,0 +1,5 @@ +--- +- name: Manage_reverse_proxy_restart_nginx + ansible.builtin.service: + name: nginx + state: restarted diff --git a/roles/manage_reverse_proxy/meta/main.yml b/roles/manage_reverse_proxy/meta/main.yml new file mode 100644 index 0000000..41dce38 --- /dev/null +++ b/roles/manage_reverse_proxy/meta/main.yml @@ -0,0 +1,15 @@ +--- +galaxy_info: + author: Showroom Project Team + description: This roles installs and configures Nginx based reverse proxy. + company: Red Hat, Inc. + license: GPL-3.0-only + min_ansible_version: "2.14" + platforms: + - name: EL + versions: + - "8" + - "9" + galaxy_tags: [] +dependencies: [] +collections: [] diff --git a/roles/manage_reverse_proxy/tasks/configure_azure.yml b/roles/manage_reverse_proxy/tasks/configure_azure.yml new file mode 100644 index 0000000..d96bec5 --- /dev/null +++ b/roles/manage_reverse_proxy/tasks/configure_azure.yml @@ -0,0 +1,57 @@ +--- +- name: Get public IP addresses + azure.azcollection.azure_rm_publicipaddress_info: + subscription_id: "{{ azure_subscription_id | default(omit) }}" + tenant: "{{ azure_tenant_id | default(omit) }}" + client_id: "{{ techuser_ansible_client_id | default(omit) }}" + secret: "{{ techuser_ansible_secret_value | default(omit) }}" + resource_group: "{{ azure_rg }}" + name: "{{ azure_public_ip.name }}" + register: __public_ip_addresses + delegate_to: localhost + become: false + +- name: Ensure a public wildcard DNS record exists for reverse proxy + azure.azcollection.azure_rm_dnsrecordset: + subscription_id: "{{ azure_subscription_id | default(omit) }}" + tenant: "{{ azure_tenant_id | default(omit) }}" + client_id: "{{ techuser_ansible_client_id | default(omit) }}" + secret: "{{ techuser_ansible_secret_value | default(omit) }}" + resource_group: "{{ azure_rg }}" + relative_name: "{{ reverse_proxy_dns_record }}" + zone_name: "{{ azure_dns_zone }}" + record_type: A + records: + - entry: "{{ __public_ip_addresses.publicipaddresses[0].ip_address }}" + delegate_to: localhost + become: false + +- name: Manage Network Security Group for Subnet + azure.azcollection.azure_rm_securitygroup: + subscription_id: "{{ azure_subscription_id | default(omit) }}" + tenant: "{{ azure_tenant_id | default(omit) }}" + client_id: "{{ techuser_ansible_client_id | default(omit) }}" + secret: "{{ techuser_ansible_secret_value | default(omit) }}" + resource_group: "{{ azure_rg | default(omit) }}" + name: "{{ azure_subnet_nsg | default(omit) }}" + rules: + - name: "AllowHTTPInBound" + protocol: Tcp + direction: Inbound + priority: 400 + access: Allow + source_address_prefix: "Internet" + source_port_range: "*" + destination_address_prefix: "{{ azure_vnet_address_prefix }}" + destination_port_range: "80" + - name: "AllowHTTPSInbound" + protocol: Tcp + direction: Inbound + priority: 401 + access: Allow + source_address_prefix: "Internet" + source_port_range: "*" + destination_address_prefix: "{{ azure_vnet_address_prefix }}" + destination_port_range: "443" + delegate_to: localhost + become: false diff --git a/roles/manage_reverse_proxy/tasks/configure_nginx.yml b/roles/manage_reverse_proxy/tasks/configure_nginx.yml new file mode 100644 index 0000000..959c16d --- /dev/null +++ b/roles/manage_reverse_proxy/tasks/configure_nginx.yml @@ -0,0 +1,45 @@ +--- +- name: Ensure nginx package is installed + ansible.builtin.dnf: + name: + - nginx + state: "present" + +- name: Get IP - "{{ groups.ipaserver | first }}" + ansible.builtin.setup: + delegate_to: "{{ groups.ipaserver | first }}" + register: __ipaserver_facts + +- name: Set resolver_ips + ansible.builtin.set_fact: + resolver_ips: "{{ [__ipaserver_facts.ansible_facts.ansible_default_ipv4.address] }}" + +- name: Get IP - "{{ groups.ipareplicas | first }}" + ansible.builtin.setup: + delegate_to: "{{ groups.ipareplicas | first }}" + register: __ipareplica_facts + +- name: Set resolver_ips + ansible.builtin.set_fact: + resolver_ips: "{{ resolver_ips + [__ipareplica_facts.ansible_facts.ansible_default_ipv4.address] }}" + +- name: Populate and configure Nginx config + ansible.builtin.template: + src: reverse_proxy.j2 + dest: /etc/nginx/conf.d/reverse-proxy.conf + mode: "0644" + notify: Manage_reverse_proxy_restart_nginx + +- name: Add HTTP service to firewalld + ansible.posix.firewalld: + service: http + permanent: true + state: enabled + immediate: true + +- name: Add HTTPS service to firewalld + ansible.posix.firewalld: + service: https + permanent: true + state: enabled + immediate: true diff --git a/roles/manage_reverse_proxy/tasks/main.yml b/roles/manage_reverse_proxy/tasks/main.yml new file mode 100644 index 0000000..0047980 --- /dev/null +++ b/roles/manage_reverse_proxy/tasks/main.yml @@ -0,0 +1,11 @@ +--- +- name: Include configure_nginx.yml + ansible.builtin.include_tasks: configure_nginx.yml + tags: configure_nginx + +- name: Include configure_azure.yml + ansible.builtin.include_tasks: configure_azure.yml + when: + - bootstrap_target is defined + - bootstrap_target == "azure" + tags: configure_azure diff --git a/roles/manage_reverse_proxy/templates/reverse_proxy.j2 b/roles/manage_reverse_proxy/templates/reverse_proxy.j2 new file mode 100644 index 0000000..030d7c1 --- /dev/null +++ b/roles/manage_reverse_proxy/templates/reverse_proxy.j2 @@ -0,0 +1,38 @@ +server { + listen 80 default_server; + server_name _; + return 301 https://$host$request_uri; +} + +server { + listen 443 ssl; + ssl_certificate {{ manage_reverse_proxy_crt_path }}; + ssl_certificate_key {{ manage_reverse_proxy_ssl_key_path }}; + server_name _; + + location / { + proxy_buffering off; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Scheme $scheme; + proxy_set_header X-Auth-Request-Redirect $request_uri; + proxy_set_header X-Forwarded-Server $host; + proxy_redirect http:// https://; + proxy_connect_timeout 150; + proxy_send_timeout 100; + proxy_read_timeout 100; + client_max_body_size 200M; + client_body_buffer_size 512k; + keepalive_timeout 5; + add_header Strict-Transport-Security max-age=63072000; + add_header X-Frame-Options DENY; + add_header X-Content-Type-Options nosniff; + proxy_ssl_server_name on; + resolver {{ resolver_ips | join(' ') }} valid=30s; + proxy_pass https://$host; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-Port $server_port; + proxy_set_header X-Forwarded-Host $host; + } +} \ No newline at end of file diff --git a/roles/manage_squid/README.md b/roles/manage_squid/README.md new file mode 100644 index 0000000..97da3d0 --- /dev/null +++ b/roles/manage_squid/README.md @@ -0,0 +1,44 @@ +manage_squid +========= + +This roles installs squid package and configures it to allow internal network access. + +Requirements +------------ + +This role needs ansible.posix collection to be installed, user and password to be defined in the inventory. + +Role Variables +-------------- + +```yaml +--- +# defaults file for manage_squid +manage_squid_allowed_network: "10.0.0.0/8" +manage_squid_http_port: 3128 +manage_squid_admin_username: squid_user +manage_squid_admin_password: squid_password +``` + +Dependencies +------------ + + +Example Playbook +---------------- + +```yaml +- hosts: bastion + roles: + - manage_Squid +``` + +License +------- + +GPL-3.0-only + +Author Information +------------------ + +Showroom Project Team diff --git a/roles/manage_squid/defaults/main.yml b/roles/manage_squid/defaults/main.yml new file mode 100644 index 0000000..85097a5 --- /dev/null +++ b/roles/manage_squid/defaults/main.yml @@ -0,0 +1,5 @@ +--- +manage_squid_allowed_network: "10.0.0.0/8" +manage_squid_http_port: 3128 +manage_squid_admin_username: "{{ squid_admin_username }}" +manage_squid_admin_password: "{{ squid_admin_password }}" diff --git a/roles/manage_squid/handlers/main.yml b/roles/manage_squid/handlers/main.yml new file mode 100644 index 0000000..ac7a2fc --- /dev/null +++ b/roles/manage_squid/handlers/main.yml @@ -0,0 +1,10 @@ +--- +- name: Manage_squid_reload_firewall + ansible.builtin.service: + name: firewalld + state: reloaded + +- name: Manage_squid_reload_squid + ansible.builtin.service: + name: squid + state: reloaded diff --git a/roles/manage_squid/meta/main.yml b/roles/manage_squid/meta/main.yml new file mode 100644 index 0000000..8e12a15 --- /dev/null +++ b/roles/manage_squid/meta/main.yml @@ -0,0 +1,17 @@ +--- +galaxy_info: + author: Showroom Project Team + description: This roles installs squid package and configures it to allow internal network access. + company: Red Hat, Inc. + license: GPL-3.0-only + min_ansible_version: "2.14" + platforms: + - name: EL + versions: + - "8" + - "9" + galaxy_tags: [] +dependencies: [] +collections: + - ansible.posix + - community.general diff --git a/roles/manage_squid/tasks/main.yml b/roles/manage_squid/tasks/main.yml new file mode 100644 index 0000000..eb978f5 --- /dev/null +++ b/roles/manage_squid/tasks/main.yml @@ -0,0 +1,45 @@ +--- +- name: Ensure squid package is installed + ansible.builtin.dnf: + name: + - squid + - httpd-tools + state: "present" + +- name: Add squid admin to a password file and ensure permissions are set + ansible.builtin.command: "htpasswd -c -B -b /etc/squid/passwd {{ manage_squid_admin_username }} {{ manage_squid_admin_password }}" + register: __return + failed_when: __return.failed + changed_when: __return.changed + no_log: true + +- name: Add squid user to a password file and ensure permissions are set + ansible.builtin.command: "htpasswd -B -b /etc/squid/passwd {{ user.name }} {{ user.password }}" + loop: "{{ showroom_users }}" + loop_control: + loop_var: user + when: showroom_users is defined + register: __return + failed_when: __return.failed + changed_when: __return.changed + no_log: true + +- name: Configure squid.conf file + ansible.builtin.template: + src: squid.conf.j2 + dest: /etc/squid/squid.conf + mode: "0640" + notify: Manage_squid_reload_squid + +- name: Start and enable squid service + ansible.builtin.service: + name: squid + state: "started" + enabled: true + +- name: Ensure squid is allowed on firewall + ansible.posix.firewalld: + service: squid + permanent: true + state: enabled + notify: Manage_squid_reload_firewall diff --git a/roles/manage_squid/templates/squid.conf.j2 b/roles/manage_squid/templates/squid.conf.j2 new file mode 100644 index 0000000..23e9275 --- /dev/null +++ b/roles/manage_squid/templates/squid.conf.j2 @@ -0,0 +1,85 @@ +{{ ansible_managed | comment }} +# Example rule allowing access from your local networks. +# Adapt to list your (internal) IP networks from where browsing +# should be allowed + +acl localnet src 0.0.0.1-0.255.255.255 # RFC 1122 "this" network (LAN) +acl localnet src 10.0.0.0/8 # RFC 1918 local private network (LAN) +acl localnet src 100.64.0.0/10 # RFC 6598 shared address space (CGN) +acl localnet src 169.254.0.0/16 # RFC 3927 link-local (directly plugged) machines +acl localnet src 172.16.0.0/12 # RFC 1918 local private network (LAN) +acl localnet src 192.168.0.0/16 # RFC 1918 local private network (LAN) +acl localnet src fc00::/7 # RFC 4193 local private network range +acl localnet src fe80::/10 # RFC 4291 link-local (directly plugged) machines + +acl SSL_ports port 443 +acl Safe_ports port 80 # http +acl Safe_ports port 21 # ftp +acl Safe_ports port 443 # https +acl Safe_ports port 70 # gopher +acl Safe_ports port 210 # wais +acl Safe_ports port 1025-65535 # unregistered ports +acl Safe_ports port 280 # http-mgmt +acl Safe_ports port 488 # gss-http +acl Safe_ports port 591 # filemaker +acl Safe_ports port 777 # multiling http + +# +# Recommended minimum Access Permission configuration: +# +# Deny requests to certain unsafe ports +http_access deny !Safe_ports + +# Deny CONNECT to other than secure SSL ports +http_access deny CONNECT !SSL_ports + +# Only allow cachemgr access from localhost +http_access allow localhost manager +http_access deny manager + +# We strongly recommend the following be uncommented to protect innocent +# web applications running on the proxy server who think the only +# one who can access services on "localhost" is a local user +#http_access deny to_localhost + +# +# INSERT YOUR OWN RULE(S) HERE TO ALLOW ACCESS FROM YOUR CLIENTS +# + +auth_param basic program /usr/lib64/squid/basic_ncsa_auth /etc/squid/passwd +auth_param basic realm Squid Basic Authentication +auth_param basic credentialsttl 1 minute +acl auth_users proxy_auth REQUIRED +acl auth_users_dst dst {{ manage_squid_allowed_network }} +http_access allow auth_users auth_users_dst + +# Example rule allowing access from your local networks. +# Adapt localnet in the ACL section to list your (internal) IP networks +# from where browsing should be allowed +http_access allow localnet +http_access allow localhost + +# http_access allow clientdest + +# And finally deny all other access to this proxy +http_access deny all + +# Squid normally listens to port 3128 +http_port {{ manage_squid_http_port }} + +# Uncomment and adjust the following to add a disk cache directory. +#cache_dir ufs /var/spool/squid 100 16 256 + +dns_v4_first on +forwarded_for delete +via off + +# Leave coredumps in the first cache dir +coredump_dir /var/spool/squid + +# +# Add any of your own refresh_pattern entries above these. +# +refresh_pattern ^ftp: 1440 20% 10080 +refresh_pattern -i (/cgi-bin/|\?) 0 0% 0 +refresh_pattern . 0 20% 4320 \ No newline at end of file diff --git a/roles/pki_idm_csr_approve/README.md b/roles/pki_idm_csr_approve/README.md new file mode 100644 index 0000000..6c9f84a --- /dev/null +++ b/roles/pki_idm_csr_approve/README.md @@ -0,0 +1,52 @@ +pki_idm_csr_approve +========= + +This role signs Red Hat Identity Management (IdM) CSR with rootCA certificate. + +Requirements +------------ + +A signed rootCA certificate should be existed on rootCA VM. + +Role Variables +-------------- + +```yaml +# default variables +pki_idm_csr_approve_csr_path_idm: /root/ipa.csr +pki_idm_csr_approve_crt_path_idm: /root/chain.crt + +pki_idm_csr_approve_csr_path_rootca: /tmp/ipa.csr +pki_idm_csr_approve_crt_path_rootca: /tmp/ipa.crt +pki_idm_csr_approve_chain_path_rootca: /tmp/chain.crt + +pki_idm_csr_approve_root_path: /etc/ssl/ca +pki_idm_csr_approve_key: rootca_certificate.key +pki_idm_csr_approve_cert: rootca_certificate.crt +pki_idm_csr_approve_passphrase_secret: "{{ ca_root_passphrase_secret }}" +``` + +Dependencies +------------ + +- pki_rootca + +Example Playbook +---------------- + +```yaml +- hosts: rootCA + roles: + - pki_rootca + - pki_idm_cert +``` + +License +------- + +GPL-3.0-only + +Author Information +------------------ + +Showroom Project Team diff --git a/roles/pki_idm_csr_approve/defaults/main.yml b/roles/pki_idm_csr_approve/defaults/main.yml new file mode 100644 index 0000000..64d4941 --- /dev/null +++ b/roles/pki_idm_csr_approve/defaults/main.yml @@ -0,0 +1,12 @@ +--- +pki_idm_csr_approve_csr_path_idm: /root/ipa.csr +pki_idm_csr_approve_crt_path_idm: /root/chain.crt + +pki_idm_csr_approve_csr_path_rootca: /tmp/ipa.csr +pki_idm_csr_approve_crt_path_rootca: /tmp/ipa.crt +pki_idm_csr_approve_chain_path_rootca: /tmp/chain.crt + +pki_idm_csr_approve_root_path: /etc/ssl/ca +pki_idm_csr_approve_key: rootca_certificate.key +pki_idm_csr_approve_cert: rootca_certificate.crt +pki_idm_csr_approve_passphrase_secret: "{{ ca_root_passphrase_secret }}" diff --git a/roles/pki_idm_csr_approve/meta/main.yml b/roles/pki_idm_csr_approve/meta/main.yml new file mode 100644 index 0000000..ffbb075 --- /dev/null +++ b/roles/pki_idm_csr_approve/meta/main.yml @@ -0,0 +1,17 @@ +--- +galaxy_info: + author: Showroom Project Team + description: This role signs Red Hat Identity Management (IdM) CSR with rootCA certificate. + company: Red Hat, Inc. + license: GPL-3.0-only + min_ansible_version: "2.14" + platforms: + - name: EL + versions: + - "8" + - "9" + galaxy_tags: [] +dependencies: [] +collections: + - community.builtin + - community.crypto diff --git a/roles/pki_idm_csr_approve/tasks/main.yml b/roles/pki_idm_csr_approve/tasks/main.yml new file mode 100644 index 0000000..ef6c225 --- /dev/null +++ b/roles/pki_idm_csr_approve/tasks/main.yml @@ -0,0 +1,39 @@ +--- +- name: Read CSR + ansible.builtin.slurp: + src: "{{ pki_idm_csr_approve_csr_path_idm }}" + register: __ipa_csr + delegate_to: "{{ groups.ipaserver | first }}" + +- name: Copy CSR to rootCA VM + ansible.builtin.copy: + content: "{{ __ipa_csr.content | b64decode }}" + dest: "{{ pki_idm_csr_approve_csr_path_rootca }}" + mode: "0644" + +- name: Generate an OpenSSL certificate signed with your own CA certificate + community.crypto.x509_certificate: + path: "{{ pki_idm_csr_approve_crt_path_rootca }}" + csr_path: "{{ pki_idm_csr_approve_csr_path_rootca }}" + ownca_path: "{{ pki_idm_csr_approve_root_path }}/{{ pki_idm_csr_approve_cert }}" + ownca_privatekey_path: "{{ pki_idm_csr_approve_root_path }}/{{ pki_idm_csr_approve_key }}" + ownca_privatekey_passphrase: "{{ ca_root_passphrase_secret }}" + provider: ownca + +- name: Get RootCA certificate + ansible.builtin.slurp: + src: "{{ pki_idm_csr_approve_root_path }}/{{ pki_idm_csr_approve_cert }}" + register: __rootca_content + +- name: Get Intermediate certificate + ansible.builtin.slurp: + src: "{{ pki_idm_csr_approve_crt_path_rootca }}" + register: __signed_sub_ca_content + +- name: Chain certificates + ansible.builtin.copy: + content: "{{ __signed_sub_ca_content.content | b64decode }}{{ __rootca_content.content | b64decode }}" + dest: "{{ pki_idm_csr_approve_crt_path_idm }}" + remote_src: true + mode: "0644" + delegate_to: "{{ groups.ipaserver | first }}" diff --git a/roles/pki_idm_generate_certs/README.md b/roles/pki_idm_generate_certs/README.md new file mode 100644 index 0000000..cc76ab0 --- /dev/null +++ b/roles/pki_idm_generate_certs/README.md @@ -0,0 +1,71 @@ +azure_dns +========= + +This role generates signed certificate for the host using IDM. + +Requirements +------------ + +- A Red Hat Satellite server should be provisioned and AAP contents downloaded. +- Installed Satellite credentials should have been placed under inventory. + +Role Variables +-------------- + +```yaml +# idm authentication variables +ipaadmin_principal: "{{ ipaadmin_principal }}" +ipaadmin_password: "{{ ipaadmin_password }}" + +# default variables +pki_idm_generate_certs_ssl_certs_dir: "/etc/ipa/private/" +pki_idm_generate_certs_ssl_crt_path: "{{ pki_idm_generate_certs_ssl_certs_dir }}{{ inventory_hostname }}.crt" +pki_idm_generate_certs_ssl_key_path: "{{ pki_idm_generate_certs_ssl_certs_dir }}{{ inventory_hostname }}.key" +pki_idm_generate_certs_ssl_csr_path: "{{ pki_idm_generate_certs_ssl_certs_dir }}{{ inventory_hostname }}.csr" +pki_idm_generate_certs_ssl_rsa_key_pass: "" + +pki_idm_generate_certs_crt_service_type: "HTTP" +pki_idm_generate_certs_crt_force_regen: true +pki_idm_generate_certs_ssl_private_key_cipher: "auto" +pki_idm_generate_certs_ssl_private_key_size: 4096 +pki_idm_generate_certs_ssl_private_key_format: "pkcs8" +pki_idm_generate_certs_csr_digest: "" +pki_idm_generate_certs_csr_common_name: "" +pki_idm_generate_certs_csr_organization_name: "" +pki_idm_generate_certs_csr_organization_unit_name: "" +pki_idm_generate_certs_csr_locality_name: "" +pki_idm_generate_certs_csr_state_or_province_name: "" +pki_idm_generate_certs_csr_country_name: "" +pki_idm_generate_certs_csr_email_address: "" +pki_idm_generate_certs_csr_subject_alt_name: "" + +# configuration variables +pki_idm_generate_certs_ipa_server_ca_path: +pki_idm_generate_certs_ipa_client_trust_path: +idm_default_dns_zone: +``` + +Dependencies +------------ + +- redhat.rhel_idm + +Example Playbook +---------------- + +```yaml +- hosts: bastion + roles: + - pki_dim_generate_certs + become: true +``` + +License +------- + +GPL-3.0-only + +Author Information +------------------ + +Showroom Project Team diff --git a/roles/pki_idm_generate_certs/defaults/main.yml b/roles/pki_idm_generate_certs/defaults/main.yml new file mode 100644 index 0000000..c6badf4 --- /dev/null +++ b/roles/pki_idm_generate_certs/defaults/main.yml @@ -0,0 +1,26 @@ +--- +pki_idm_generate_certs_ssl_certs_dir: "/etc/ipa/private/" +pki_idm_generate_certs_ssl_crt_path: "{{ pki_idm_generate_certs_ssl_certs_dir }}{{ inventory_hostname }}.crt" +pki_idm_generate_certs_ssl_key_path: "{{ pki_idm_generate_certs_ssl_certs_dir }}{{ inventory_hostname }}.key" +pki_idm_generate_certs_ssl_csr_path: "{{ pki_idm_generate_certs_ssl_certs_dir }}{{ inventory_hostname }}.csr" +pki_idm_generate_certs_ssl_rsa_key_pass: "" + +pki_idm_generate_certs_ipa_server_ca_path: "{{ ipa_server_ca_path }}" +pki_idm_generate_certs_ipa_client_trust_path: "{{ ipa_client_trust_path }}" + +pki_idm_generate_certs_crt_service_type: "HTTP" +pki_idm_generate_certs_crt_force_regen: true + +pki_idm_generate_certs_ssl_private_key_cipher: "auto" +pki_idm_generate_certs_ssl_private_key_size: 4096 +pki_idm_generate_certs_ssl_private_key_format: "pkcs8" + +pki_idm_generate_certs_csr_digest: "" +pki_idm_generate_certs_csr_common_name: "" +pki_idm_generate_certs_csr_organization_name: "" +pki_idm_generate_certs_csr_organization_unit_name: "" +pki_idm_generate_certs_csr_locality_name: "" +pki_idm_generate_certs_csr_state_or_province_name: "" +pki_idm_generate_certs_csr_country_name: "" +pki_idm_generate_certs_csr_email_address: "" +pki_idm_generate_certs_csr_subject_alt_name: "" diff --git a/roles/pki_idm_generate_certs/meta/main.yml b/roles/pki_idm_generate_certs/meta/main.yml new file mode 100644 index 0000000..a18492a --- /dev/null +++ b/roles/pki_idm_generate_certs/meta/main.yml @@ -0,0 +1,16 @@ +--- +galaxy_info: + author: Showroom Project Team + description: This role generates signed certificate for the host using IDM. + company: Red Hat, Inc. + license: GPL-3.0-only + min_ansible_version: "2.14" + platforms: + - name: EL + versions: + - "8" + - "9" + galaxy_tags: [] +dependencies: [] +collections: + - redhat.rhel_idm diff --git a/roles/pki_idm_generate_certs/tasks/generate_certs.yml b/roles/pki_idm_generate_certs/tasks/generate_certs.yml new file mode 100644 index 0000000..9534c25 --- /dev/null +++ b/roles/pki_idm_generate_certs/tasks/generate_certs.yml @@ -0,0 +1,147 @@ +--- +- name: "Create a secure directory" + ansible.builtin.file: + path: "{{ pki_idm_generate_certs_ssl_certs_dir }}" + state: "directory" + owner: "root" + group: "root" + mode: "0600" + +- name: "Set target service name" + ansible.builtin.set_fact: + __target_service: "{{ pki_idm_generate_certs_crt_service_type }}/{{ inventory_hostname }}" + +- name: "Ensure target service is present" + redhat.rhel_idm.ipaservice: + name: "{{ __target_service }}" + state: present + ipaadmin_password: "{{ ipaadmin_password }}" + +- name: "Check for an existing certificate file" + ansible.builtin.stat: + path: "{{ pki_idm_generate_certs_ssl_crt_path }}" + register: __cert_exists + +- name: "Cert exists and regen requested - stop tracking and clean up" + when: __cert_exists.stat.exists and pki_idm_generate_certs_crt_force_regen + block: + - name: "Read existing cert" # noqa: command-instead-of-module + ansible.builtin.shell: "sed -n 2,$(expr $(wc -l < {{ pki_idm_generate_certs_ssl_crt_path }}) - 1)p {{ pki_idm_generate_certs_ssl_crt_path }}" + register: __cert_info + changed_when: __cert_info.changed + failed_when: __cert_info.failed + + - name: "Get cert serial number" + ansible.builtin.shell: >- + set -o pipefail && echo '{{ ipaadmin_password }}' | kinit {{ ipaadmin_principal }}; + ipa cert-find --certificate='{{ __cert_info.stdout }}' | grep 'Serial number:' | cut -c18- + register: __serial_num + changed_when: __serial_num.changed + failed_when: __serial_num.failed + + - name: "Check if certmonger is already tracking the certificate" + ansible.builtin.shell: >- + set -o pipefail && echo '{{ ipaadmin_password }}' | kinit {{ ipaadmin_principal }}; + ipa-getcert list -f {{ pki_idm_generate_certs_ssl_crt_path }} | grep 'status:' + register: __certmonger_tracking_status + failed_when: false + changed_when: false + + - name: "Stop tracking cert with certmonger" + ansible.builtin.shell: >- + set -o pipefail && echo '{{ ipaadmin_password }}' | kinit {{ ipaadmin_principal }}; + ipa-getcert stop-tracking -k {{ pki_idm_generate_certs_ssl_key_path }} + -f {{ pki_idm_generate_certs_ssl_crt_path }} -i {{ inventory_hostname }} + no_log: true + when: "'status: MONITORING' in __certmonger_tracking_status.stdout" + register: __stop_tracking + changed_when: __stop_tracking.changed + failed_when: __stop_tracking.failed + + - name: "Remove the old cert from the service" + ansible.builtin.shell: >- + set -o pipefail && echo '{{ ipaadmin_password }}' | kinit {{ ipaadmin_principal }}; + ipa service-remove-cert {{ __target_service }} --certificate='{{ __cert_info.stdout }}' + no_log: true + when: "'status: MONITORING' in __certmonger_tracking_status.stdout" + register: __remove_service + changed_when: __remove_service.changed + failed_when: __remove_service.failed + + - name: "Revoke the old cert with reason 6 - certificateHold - an admin can completely revoke it later" + ansible.builtin.shell: >- + set -o pipefail && echo '{{ ipaadmin_password }}' | kinit {{ ipaadmin_principal }}; + ipa cert-revoke {{ __serial_num.stdout_lines.1 }} --revocation-reason=6 + no_log: true + when: "'status: MONITORING' in __certmonger_tracking_status.stdout" + register: __revoke_oldcert + changed_when: __revoke_oldcert.changed + failed_when: __revoke_oldcert.failed + + - name: "Cert exists, rename old cert .revoked" + ansible.builtin.copy: + src: "{{ pki_idm_generate_certs_ssl_crt_path }}" + dest: "{{ pki_idm_generate_certs_ssl_crt_path }}.revoked" + remote_src: true + owner: root + group: root + mode: "0640" + +- name: Generating New Certificate + when: not __cert_exists.stat.exists or pki_idm_generate_certs_crt_force_regen + block: + - name: Create private key with password protection + community.crypto.openssl_privatekey: + path: "{{ pki_idm_generate_certs_ssl_key_path }}" + passphrase: "{{ pki_idm_generate_certs_ssl_rsa_key_pass }}" + cipher: "{{ pki_idm_generate_certs_ssl_private_key_cipher }}" + size: "{{ pki_idm_generate_certs_ssl_private_key_size }}" + format: "{{ pki_idm_generate_certs_ssl_private_key_format }}" + + - name: Create certificate signing request (CSR) for Satellite + community.crypto.openssl_csr_pipe: + privatekey_path: "{{ pki_idm_generate_certs_ssl_key_path }}" + privatekey_passphrase: "{{ pki_idm_generate_certs_ssl_rsa_key_pass }}" + common_name: "{{ pki_idm_generate_certs_csr_common_name | default(omit) }}" + organization_name: "{{ pki_idm_generate_certs_csr_organization_name }}" + organizational_unit_name: "{{ pki_idm_generate_certs_csr_organization_unit_name }}" + locality_name: "{{ pki_idm_generate_certs_csr_locality_name }}" + state_or_province_name: "{{ pki_idm_generate_certs_csr_state_or_province_name }}" + country_name: "{{ pki_idm_generate_certs_csr_country_name }}" + email_address: "{{ pki_idm_generate_certs_csr_email_address }}" + subject_alt_name: "{{ pki_idm_generate_certs_csr_subject_alt_name }}" + register: __satellite_csr + + - name: Request a certificate for a web server + redhat.rhel_idm.ipacert: + ipaadmin_password: "{{ ipaadmin_password }}" + state: requested + csr: "{{ __satellite_csr.csr }}" + principal: "{{ __target_service }}" + certificate_out: "{{ pki_idm_generate_certs_ssl_crt_path }}" + + - name: "Remove passphrase from the private key" + ansible.builtin.command: >- + openssl rsa -in {{ pki_idm_generate_certs_ssl_key_path }} -out {{ pki_idm_generate_certs_ssl_key_path }} + -passin pass:{{ pki_idm_generate_certs_ssl_rsa_key_pass }} + register: __openssl_passout + changed_when: __openssl_passout.changed + failed_when: __openssl_passout.failed + + - name: "Check if certmonger is already tracking the certificate" + ansible.builtin.shell: >- + set -o pipefail && echo '{{ ipaadmin_password }}' | kinit {{ ipaadmin_principal }}; + ipa-getcert list -f {{ pki_idm_generate_certs_ssl_crt_path }} | grep 'status:' + register: __certmonger_tracking_status + changed_when: __certmonger_tracking_status.changed + failed_when: __certmonger_tracking_status.rc not in [0, 1] + + - name: "Start certmonger tracking the new certificate for autorenewals" + ansible.builtin.shell: >- + set -o pipefail && echo '{{ ipaadmin_password }}' | kinit {{ ipaadmin_principal }}; + ipa-getcert start-tracking -k {{ pki_idm_generate_certs_ssl_key_path }} -f {{ pki_idm_generate_certs_ssl_crt_path }} -I {{ inventory_hostname }} + when: "'status: MONITORING' not in __certmonger_tracking_status.stdout" + register: __start_tracking + changed_when: __start_tracking.changed + failed_when: __start_tracking.rc not in [0, 1] + no_log: true diff --git a/roles/pki_idm_generate_certs/tasks/main.yml b/roles/pki_idm_generate_certs/tasks/main.yml new file mode 100644 index 0000000..84dc452 --- /dev/null +++ b/roles/pki_idm_generate_certs/tasks/main.yml @@ -0,0 +1,10 @@ +--- +- name: "Ensure certificates are generated" + ansible.builtin.import_tasks: generate_certs.yml + when: generate_certs | bool() + tags: generate_certs + +- name: "Ensure CA trust updated" + ansible.builtin.import_tasks: update_ca_trust.yml + when: update_ca_trust | bool() + tags: update_ca_trust diff --git a/roles/pki_idm_generate_certs/tasks/update_ca_trust.yml b/roles/pki_idm_generate_certs/tasks/update_ca_trust.yml new file mode 100644 index 0000000..f7b3d20 --- /dev/null +++ b/roles/pki_idm_generate_certs/tasks/update_ca_trust.yml @@ -0,0 +1,21 @@ +--- +- name: "Copy the ipa ca-certificates to the trust directory" + ansible.builtin.copy: + src: "{{ pki_idm_generate_certs_ipa_server_ca_path }}" + dest: "{{ pki_idm_generate_certs_ipa_client_trust_path }}" + remote_src: true + owner: root + group: root + mode: "0644" + +- name: "Enable the trust" + ansible.builtin.command: "update-ca-trust enable" + register: __result + changed_when: __result.changed + failed_when: __result.failed + +- name: "Update the trust" + ansible.builtin.command: "update-ca-trust" + register: __result + changed_when: __result.changed + failed_when: __result.failed diff --git a/roles/pki_rootca/README.md b/roles/pki_rootca/README.md new file mode 100644 index 0000000..317eec9 --- /dev/null +++ b/roles/pki_rootca/README.md @@ -0,0 +1,48 @@ +pki_rootca +========= + +This roles creates private key and self-signed rootCA certificate. + +Requirements +------------ + +- A server should have been provisioned to create and store the rootCA private key and self-signed certificate. +- Configuration variables should be provided with a variable file. + +Role Variables +-------------- + +```yaml +# default variables +pki_root_ca_root_path: /etc/ssl/ca/ +pki_root_ca_key: root-ca-certificate.key +pki_root_ca_cert: root-ca-certificate.crt + +# configuration variables +pki_rootca_passphrase_secret: "{{ ca_root_passphrase_secret }}" +pki_rootca_ca_common_name: "{{ ca_common_name }}" +``` + +Dependencies +------------ + +- N/A + +Example Playbook +---------------- + +```yaml +- hosts: rootCA + roles: + - pki_rootca +``` + +License +------- + +GPL-3.0-only + +Author Information +------------------ + +Showroom Project Team diff --git a/roles/pki_rootca/defaults/main.yml b/roles/pki_rootca/defaults/main.yml new file mode 100644 index 0000000..9a5a738 --- /dev/null +++ b/roles/pki_rootca/defaults/main.yml @@ -0,0 +1,7 @@ +--- +pki_rootca_root_path: /etc/ssl/ca +pki_rootca_key: rootca_certificate.key +pki_rootca_cert: rootca_certificate.crt + +pki_rootca_passphrase_secret: "{{ ca_root_passphrase_secret }}" +pki_rootca_ca_common_name: "{{ ca_common_name }}" diff --git a/roles/pki_rootca/meta/main.yml b/roles/pki_rootca/meta/main.yml new file mode 100644 index 0000000..c89125d --- /dev/null +++ b/roles/pki_rootca/meta/main.yml @@ -0,0 +1,17 @@ +--- +galaxy_info: + author: Showroom Project Team + description: This roles creates private key and self-signed rootCA certificate. + company: Red Hat, Inc. + license: GPL-3.0-only + min_ansible_version: "2.14" + platforms: + - name: EL + versions: + - "8" + - "9" + galaxy_tags: [] +dependencies: [] +collections: + - community.builtin + - community.crypto diff --git a/roles/pki_rootca/tasks/main.yml b/roles/pki_rootca/tasks/main.yml new file mode 100644 index 0000000..dc81e1e --- /dev/null +++ b/roles/pki_rootca/tasks/main.yml @@ -0,0 +1,65 @@ +--- +- name: Ensure python3-cryptography package is installed + become: true + ansible.builtin.dnf: + name: python3-cryptography + state: "present" + +- name: Create CA root path + ansible.builtin.file: + path: "{{ pki_rootca_root_path }}" + state: directory + mode: "0755" + +- name: Check whether certificate exists + ansible.builtin.stat: + path: "{{ pki_rootca_root_path }}/{{ pki_rootca_cert }}" + register: __certificate_exists + +- name: Check whether private key exists + ansible.builtin.stat: + path: "{{ pki_rootca_root_path }}/{{ pki_rootca_cert }}" + register: __key_exists + +- name: Create CA + when: not __certificate_exists.stat.exists and not __key_exists.stat.exists + block: + - name: Create private key with password protection + community.crypto.openssl_privatekey: + path: "{{ pki_rootca_root_path }}/{{ pki_rootca_key }}" + passphrase: "{{ pki_rootca_passphrase_secret }}" + cipher: auto + format: pkcs8 + + - name: Create certificate signing request (CSR) for RootCA certificate + community.crypto.openssl_csr_pipe: + privatekey_path: "{{ pki_rootca_root_path }}/{{ pki_rootca_key }}" + privatekey_passphrase: "{{ pki_rootca_passphrase_secret }}" + common_name: "{{ pki_rootca_ca_common_name | default(omit) }}" + use_common_name_for_san: false + basic_constraints: + - 'CA:TRUE' + basic_constraints_critical: true + key_usage: + - keyCertSign + key_usage_critical: true + register: __root_ca_csr + + - name: Create self-signed CA certificate from CSR + community.crypto.x509_certificate: + path: "{{ pki_rootca_root_path }}/{{ pki_rootca_cert }}" + csr_content: "{{ __root_ca_csr.csr }}" + privatekey_path: "{{ pki_rootca_root_path }}/{{ pki_rootca_key }}" + privatekey_passphrase: "{{ pki_rootca_passphrase_secret }}" + provider: selfsigned + mode: "0644" + +- name: "Set RootCA cert path" + ansible.builtin.set_fact: + vault_string: false + git_repo_commit: false + when: __certificate_exists.stat.exists and __key_exists.stat.exists + +- name: "Set RootCA cert path" + ansible.builtin.set_fact: + __rootca_cert_path: "{{ pki_rootca_root_path }}/{{ pki_rootca_cert }}" diff --git a/roles/post_config/README.md b/roles/post_config/README.md new file mode 100644 index 0000000..1f72c86 --- /dev/null +++ b/roles/post_config/README.md @@ -0,0 +1,45 @@ +post_config +========= + +This roles runs post configuration tasks on new provisioned VMs; +- Installs defined packages +- Copies private key when required. + +Requirements +------------ + + +Role Variables +-------------- + +```yaml +# defaults file for post_config +post_config_priv_key: false +# to install custom packages define this var in the inventory +# post_config_packages: +# - git +# - ansible-core +``` + +Dependencies +------------ + + +Example Playbook +---------------- + +```yaml +- hosts: bastion + roles: + - post_config +``` + +License +------- + +GPL-3.0-only + +Author Information +------------------ + +Showroom Project Team diff --git a/roles/post_config/defaults/main.yml b/roles/post_config/defaults/main.yml new file mode 100644 index 0000000..a322495 --- /dev/null +++ b/roles/post_config/defaults/main.yml @@ -0,0 +1,8 @@ +--- +# defaults file for post_config +post_config_priv_key: false +# to install custom packages define this var in the inventory +# post_config_packages: +# - git +# - ansible-core +post_config_httpd_content: This page created with ansible diff --git a/roles/post_config/meta/main.yml b/roles/post_config/meta/main.yml new file mode 100644 index 0000000..50bbcf0 --- /dev/null +++ b/roles/post_config/meta/main.yml @@ -0,0 +1,14 @@ +--- +galaxy_info: + author: Showroom Project Team + description: This roles installs defined packages and copies private key when required. + company: Red Hat, Inc. + license: GPL-3.0-only + min_ansible_version: "2.14" + platforms: + - name: EL + versions: + - "8" + - "9" + galaxy_tags: [] +dependencies: [] diff --git a/roles/post_config/tasks/httpd_deploy.yml b/roles/post_config/tasks/httpd_deploy.yml new file mode 100644 index 0000000..3ccdf9b --- /dev/null +++ b/roles/post_config/tasks/httpd_deploy.yml @@ -0,0 +1,27 @@ +--- +- name: Deploy httpd + become: true + block: + - name: Install httpd package + ansible.builtin.dnf: + name: httpd + state: present + + - name: Start and enable httpd service + ansible.builtin.service: + name: httpd + enabled: true + state: started + + - name: Allow http in the firewall + ansible.posix.firewalld: + service: http + state: enabled + permanent: true + immediate: true + + - name: Create a custom index.html file + ansible.builtin.copy: + dest: /var/www/html/index.html + content: "{{ post_config_httpd_content }}" + mode: "0644" diff --git a/roles/post_config/tasks/main.yml b/roles/post_config/tasks/main.yml new file mode 100644 index 0000000..93b3dda --- /dev/null +++ b/roles/post_config/tasks/main.yml @@ -0,0 +1,47 @@ +--- +- name: Gather facts + ansible.builtin.setup: + +- name: Deploy SSH private key to VM + become: false + ansible.builtin.copy: + content: "{{ vm_user_private_key }}" + dest: ~/.ssh/id_rsa + owner: "{{ vm_user }}" + group: "{{ vm_user }}" + mode: "0600" + when: post_config_priv_key | bool() + +- name: Update /etc/hosts with template etc_hosts.j2 + become: true + ansible.builtin.template: + dest: /etc/hosts + src: etc_hosts.j2 + owner: root + group: root + mode: "0644" + backup: true + +- name: Install required packages + become: true + ansible.builtin.dnf: + name: "{{ post_config_packages }}" + state: present + when: post_config_packages is defined + +- name: "Configure kernel parameters" # noqa var-naming[no-role-prefix] + become: true + ansible.builtin.import_role: + name: redhat.rhel_system_roles.kernel_settings + when: kernel_settings_sysctl is defined + +- name: "Configure Cron for RH Offline Token Refresh" + ansible.builtin.include_tasks: refresh_token.yml + loop: "{{ rh_offline_tokens }}" + loop_control: + loop_var: token + when: rh_offline_tokens is defined + +- name: "Configure httpd" + ansible.builtin.include_tasks: httpd_deploy.yml + when: httpd_deploy | bool() diff --git a/roles/post_config/tasks/refresh_token.yml b/roles/post_config/tasks/refresh_token.yml new file mode 100644 index 0000000..6739e40 --- /dev/null +++ b/roles/post_config/tasks/refresh_token.yml @@ -0,0 +1,19 @@ +- name: Block for create a cronjob for token refresh + become: true + block: + - name: Create a cron job for token refresh + ansible.builtin.cron: + name: RH API token refresh + weekday: "*" + minute: "0" + hour: "09" + user: root + job: >- + curl https://sso.redhat.com/auth/realms/redhat-external/protocol/openid-connect/token -d grant_type=refresh_token\ + -d client_id="cloud-services" -d refresh_token="{{ token.value }}" --fail --silent --show-error --output /dev/null + cron_file: rh-token-refresh-{{ token.name }} + + - name: Update cron file permission + ansible.builtin.file: + path: "/etc/cron.d/rh-token-refresh-{{ token.name }}" + mode: "0644" diff --git a/roles/post_config/templates/etc_hosts.j2 b/roles/post_config/templates/etc_hosts.j2 new file mode 100644 index 0000000..5dc7def --- /dev/null +++ b/roles/post_config/templates/etc_hosts.j2 @@ -0,0 +1,5 @@ +{{ ansible_managed }} +127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 +::1 localhost localhost.localdomain localhost6 localhost6.localdomain6 +# +{{ ansible_default_ipv4.address }} {{ inventory_hostname }} {{ inventory_hostname_short }} \ No newline at end of file diff --git a/roles/public_certificate/README.md b/roles/public_certificate/README.md new file mode 100644 index 0000000..59f29f9 --- /dev/null +++ b/roles/public_certificate/README.md @@ -0,0 +1,56 @@ +public_certificate +========= + +This roles creates a CSR and request Let's encrypt to sign it. + +Requirements +------------ + + +Role Variables +-------------- + +```yaml +# defaults file for public_certificate +public_certificate_acme_version: 2 +public_certificate_acme_directory: https://acme-v02.api.letsencrypt.org/directory +public_certificate_acme_challenge_type: dns-01 +public_certificate_acme_challenge_subdomain: "_acme-challenge.{{ azure_dns_private_dnz_zone_subdomain }}" +public_certificate_letsencrypt_dir: "/etc/letsencrypt" + +public_certificate_letsencrypt_account_key_path: "{{ public_certificate_letsencrypt_dir }}/account.key" +public_certificate_letsencrypt_ssl_key_path: "{{ public_certificate_letsencrypt_dir }}/{{ inventory_hostname }}.key" +public_certificate_letsencrypt_csr_path: "{{ public_certificate_letsencrypt_dir }}{{ inventory_hostname }}.csr" +public_certificate_letsencrypt_crt_path: "{{ public_certificate_letsencrypt_dir }}/{{ inventory_hostname }}.crt" +public_certificate_letsencrypt_chain_path: "{{ public_certificate_letsencrypt_dir }}/{{ inventory_hostname }}_chain.crt" +public_certificate_letsencrypt_fullchain_path: "{{ public_certificate_letsencrypt_dir }}/{{ inventory_hostname }}_fullchain.crt" + +# azure authentication variables +azure_subscription_id: +azure_tenant_id: +techuser_ansible_client_id: +techuser_ansible_secret_value: +``` + +Dependencies +------------ + + +Example Playbook +---------------- + +```yaml +- hosts: bastion + roles: + - public_certificate +``` + +License +------- + +GPL-3.0-only + +Author Information +------------------ + +Showroom Project Team diff --git a/roles/public_certificate/defaults/main.yml b/roles/public_certificate/defaults/main.yml new file mode 100644 index 0000000..e155a22 --- /dev/null +++ b/roles/public_certificate/defaults/main.yml @@ -0,0 +1,13 @@ +--- +public_certificate_acme_version: 2 +public_certificate_acme_directory: https://acme-v02.api.letsencrypt.org/directory +public_certificate_acme_challenge_type: dns-01 +public_certificate_acme_challenge_subdomain: "_acme-challenge.{{ azure_dns_private_dnz_zone_subdomain }}" +public_certificate_letsencrypt_dir: "/etc/letsencrypt" + +public_certificate_letsencrypt_account_key_path: "{{ public_certificate_letsencrypt_dir }}/account.key" +public_certificate_letsencrypt_ssl_key_path: "{{ public_certificate_letsencrypt_dir }}/{{ inventory_hostname }}.key" +public_certificate_letsencrypt_csr_path: "{{ public_certificate_letsencrypt_dir }}{{ inventory_hostname }}.csr" +public_certificate_letsencrypt_crt_path: "{{ public_certificate_letsencrypt_dir }}/{{ inventory_hostname }}.crt" +public_certificate_letsencrypt_chain_path: "{{ public_certificate_letsencrypt_dir }}/{{ inventory_hostname }}_chain.crt" +public_certificate_letsencrypt_fullchain_path: "{{ public_certificate_letsencrypt_dir }}/{{ inventory_hostname }}_fullchain.crt" diff --git a/roles/public_certificate/handlers/main.yml b/roles/public_certificate/handlers/main.yml new file mode 100644 index 0000000..49ada4f --- /dev/null +++ b/roles/public_certificate/handlers/main.yml @@ -0,0 +1,15 @@ +--- +- name: Public_certificate_dns_revert + azure.azcollection.azure_rm_dnsrecordset: + subscription_id: "{{ azure_subscription_id | default(omit) }}" + tenant: "{{ azure_tenant_id | default(omit) }}" + client_id: "{{ techuser_ansible_client_id | default(omit) }}" + secret: "{{ techuser_ansible_secret_value | default(omit) }}" + resource_group: "{{ azure_rg }}" + relative_name: "{{ public_certificate_acme_challenge_subdomain }}" + zone_name: "{{ azure_dns_zone }}" + time_to_live: 60 + record_type: TXT + state: absent + delegate_to: localhost + become: false diff --git a/roles/public_certificate/meta/main.yml b/roles/public_certificate/meta/main.yml new file mode 100644 index 0000000..b4a90f8 --- /dev/null +++ b/roles/public_certificate/meta/main.yml @@ -0,0 +1,15 @@ +--- +galaxy_info: + author: Showroom Project Team + description: This roles gets Let's Encrypt signed SSL certificate for the given host. + company: Red Hat, Inc. + license: GPL-3.0-only + min_ansible_version: "2.14" + platforms: + - name: EL + versions: + - "8" + - "9" + galaxy_tags: [] +dependencies: [] +collections: [] diff --git a/roles/public_certificate/tasks/main.yml b/roles/public_certificate/tasks/main.yml new file mode 100644 index 0000000..9024cb9 --- /dev/null +++ b/roles/public_certificate/tasks/main.yml @@ -0,0 +1,89 @@ +--- +- name: "Create let's encrypt directory" + ansible.builtin.file: + path: "{{ public_certificate_letsencrypt_dir }}" + state: directory + owner: root + group: root + mode: "0755" + +- name: Generate let's encrypt account key + community.crypto.openssl_privatekey: + path: "{{ public_certificate_letsencrypt_account_key_path }}" + format: pkcs8 + size: 4096 + cipher: auto + passphrase: "{{ letsencrypt_account_key_passphrase }}" + +- name: Generate let's encrypt private key + community.crypto.openssl_privatekey: + path: "{{ public_certificate_letsencrypt_ssl_key_path }}" + format: pkcs8 + size: 4096 + +- name: Generate an OpenSSL Certificate Signing Request + community.crypto.openssl_csr: + path: "{{ public_certificate_letsencrypt_csr_path }}" + privatekey_path: "{{ public_certificate_letsencrypt_ssl_key_path }}" + common_name: "{{ public_certificate_common_name }}" + +- name: "Begin Let's Encrypt challenges" + community.crypto.acme_certificate: + acme_directory: "{{ public_certificate_acme_directory }}" + acme_version: "{{ public_certificate_acme_version }}" + account_key_src: "{{ public_certificate_letsencrypt_account_key_path }}" + account_email: "{{ public_certificate_acme_email }}" + terms_agreed: true + challenge: "{{ public_certificate_acme_challenge_type }}" + account_key_passphrase: "{{ letsencrypt_account_key_passphrase }}" + csr: "{{ public_certificate_letsencrypt_csr_path }}" + cert: "{{ public_certificate_letsencrypt_crt_path }}" + fullchain_dest: "{{ public_certificate_letsencrypt_fullchain_path }}" + register: __acme_challenge_result + +- name: Get signed certificates + when: __acme_challenge_result.cert_days < 0 + block: + - name: Set ACME challenge variables + ansible.builtin.set_fact: + __challenge_url: "{{ __acme_challenge_result.challenge_data_dns.keys() | list | first }}" + + - name: Set ACME challenge variables + ansible.builtin.set_fact: + __challenge_token: "{{ __acme_challenge_result.challenge_data_dns[__challenge_url][0] }}" + + - name: Ensure a public "TXT" record set for challenge + azure.azcollection.azure_rm_dnsrecordset: + subscription_id: "{{ azure_subscription_id | default(omit) }}" + tenant: "{{ azure_tenant_id | default(omit) }}" + client_id: "{{ techuser_ansible_client_id | default(omit) }}" + secret: "{{ techuser_ansible_secret_value | default(omit) }}" + resource_group: "{{ azure_rg }}" + relative_name: "{{ public_certificate_acme_challenge_subdomain }}" + zone_name: "{{ azure_dns_zone }}" + time_to_live: 60 + record_type: TXT + records: + - entry: "{{ __challenge_token }}" + delegate_to: localhost + become: false + when: + - bootstrap_target is defined + - bootstrap_target == "azure" + + - name: "Complete Let's Encrypt challenges" + community.crypto.acme_certificate: + acme_directory: "{{ public_certificate_acme_directory }}" + acme_version: "{{ public_certificate_acme_version }}" + account_key_src: "{{ public_certificate_letsencrypt_account_key_path }}" + account_email: "{{ public_certificate_acme_email }}" + challenge: "{{ public_certificate_acme_challenge_type }}" + account_key_passphrase: "{{ letsencrypt_account_key_passphrase }}" + csr: "{{ public_certificate_letsencrypt_csr_path }}" + cert: "{{ public_certificate_letsencrypt_crt_path }}" + chain_dest: "{{ public_certificate_letsencrypt_chain_path }}" + fullchain_dest: "{{ public_certificate_letsencrypt_fullchain_path }}" + data: "{{ __acme_challenge_result }}" + retries: 60 + delay: 10 + notify: Public_certificate_dns_revert diff --git a/roles/satellite_config/defaults/main.yml b/roles/satellite_config/defaults/main.yml new file mode 100644 index 0000000..7e61264 --- /dev/null +++ b/roles/satellite_config/defaults/main.yml @@ -0,0 +1,3 @@ +--- +async_timeout: 14400 # noqa: var-naming[no-role-prefix] +async_delay: 15 # noqa: var-naming[no-role-prefix] diff --git a/roles/satellite_config/files/ssg-rhel8-ds-tailoring.xml b/roles/satellite_config/files/ssg-rhel8-ds-tailoring.xml new file mode 100644 index 0000000..207ede3 --- /dev/null +++ b/roles/satellite_config/files/ssg-rhel8-ds-tailoring.xml @@ -0,0 +1,1952 @@ + + + + 1 + + CIS Red Hat Enterprise Linux 8 Benchmark for Level 2 - Server [CUSTOMIZED] + This profile defines a baseline that aligns to the "Level 2 - Server" +configuration from the Center for Internet Securityยฎ Red Hat Enterprise +Linux 8 Benchmarkโ„ข, v2.0.0, released 2022-02-23. + +This profile includes Center for Internet Securityยฎ +Red Hat Enterprise Linux 8 CIS Benchmarksโ„ข content. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + time.example.ca,time2.example.ca,time.nrc.ca,time.chu.nrc.ca + ^[\s\n]+\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*(?:[\n]+|(?:\\n)+)[\s\n]+The[\s\n]+credentials[\s\n]+you[\s\n]+used[\s\n]+to[\s\n]+access[\s\n]+this[\s\n]+information[\s\n]+system[\s\n]+\(IS\)[\s\n]+have(?:[\n]+|(?:\\n)+)[\s\n]+been[\s\n]+authenticated\.[\s\n]+By[\s\n]+remaining[\s\n]+connected,[\s\n]+you[\s\n]+declare[\s\n]+that[\s\n]+you[\s\n]+are(?:[\n]+|(?:\\n)+)[\s\n]+the[\s\n]+authorized[\s\n]+user[\s\n]+of[\s\n]+these[\s\n]+credentials[\s\n]+and[\s\n]+agree[\s\n]+to[\s\n]+the[\s\n]+terms[\s\n]+of(?:[\n]+|(?:\\n)+)[\s\n]+use[\s\n]+of[\s\n]+this[\s\n]+IS\.(?:[\n]+|(?:\\n)+)(?:[\n]+|(?:\\n)+)[\s\n]+All[\s\n]+activities[\s\n]+performed[\s\n]+on[\s\n]+this[\s\n]+device[\s\n]+are[\s\n]+logged[\s\n]+and[\s\n]+monitored\.(?:[\n]+|(?:\\n)+)(?:[\n]+|(?:\\n)+)[\s\n]+Unauthorized[\s\n]+attempts[\s\n]+to[\s\n]+elevate[\s\n]+privilege[\s\n]+or[\s\n]+attempts[\s\n]+to[\s\n]+circumvent(?:[\n]+|(?:\\n)+)[\s\n]+system[\s\n]+security[\s\n]+will[\s\n]+result[\s\n]+in[\s\n]+civil[\s\n]+and/or[\s\n]+criminal[\s\n]+penalties\.(?:[\n]+|(?:\\n)+)[\s\n]+\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*(?:[\n]+|(?:\\n)+)$ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + CIS Red Hat Enterprise Linux 8 Benchmark for Level 1 - Server [CUSTOMIZED] + This profile defines a baseline that aligns to the "Level 1 - Server" +configuration from the Center for Internet Securityยฎ Red Hat Enterprise +Linux 8 Benchmarkโ„ข, v2.0.0, released 2022-02-23. + +This profile includes Center for Internet Securityยฎ +Red Hat Enterprise Linux 8 CIS Benchmarksโ„ข content. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ^[\s\n]+\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*(?:[\n]+|(?:\\n)+)[\s\n]+The[\s\n]+credentials[\s\n]+you[\s\n]+used[\s\n]+to[\s\n]+access[\s\n]+this[\s\n]+information[\s\n]+system[\s\n]+\(IS\)[\s\n]+have(?:[\n]+|(?:\\n)+)[\s\n]+been[\s\n]+authenticated\.[\s\n]+By[\s\n]+remaining[\s\n]+connected,[\s\n]+you[\s\n]+declare[\s\n]+that[\s\n]+you[\s\n]+are(?:[\n]+|(?:\\n)+)[\s\n]+the[\s\n]+authorized[\s\n]+user[\s\n]+of[\s\n]+these[\s\n]+credentials[\s\n]+and[\s\n]+agree[\s\n]+to[\s\n]+the[\s\n]+terms[\s\n]+of(?:[\n]+|(?:\\n)+)[\s\n]+use[\s\n]+of[\s\n]+this[\s\n]+IS\.(?:[\n]+|(?:\\n)+)(?:[\n]+|(?:\\n)+)[\s\n]+All[\s\n]+activities[\s\n]+performed[\s\n]+on[\s\n]+this[\s\n]+device[\s\n]+are[\s\n]+logged[\s\n]+and[\s\n]+monitored\.(?:[\n]+|(?:\\n)+)(?:[\n]+|(?:\\n)+)[\s\n]+Unauthorized[\s\n]+attempts[\s\n]+to[\s\n]+elevate[\s\n]+privilege[\s\n]+or[\s\n]+attempts[\s\n]+to[\s\n]+circumvent(?:[\n]+|(?:\\n)+)[\s\n]+system[\s\n]+security[\s\n]+will[\s\n]+result[\s\n]+in[\s\n]+civil[\s\n]+and/or[\s\n]+criminal[\s\n]+penalties\.(?:[\n]+|(?:\\n)+)[\s\n]+\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*(?:[\n]+|(?:\\n)+)$ + sha1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Criminal Justice Information Services (CJIS) Security Policy [CUSTOMIZED] + This profile is derived from FBI's CJIS v5.4 +Security Policy. A copy of this policy can be found at the CJIS Security +Policy Resource Center: + +https://www.fbi.gov/services/cjis/cjis-security-policy-resource-center + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ^[\s\n]+\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*(?:[\n]+|(?:\\n)+)[\s\n]+The[\s\n]+credentials[\s\n]+you[\s\n]+used[\s\n]+to[\s\n]+access[\s\n]+this[\s\n]+information[\s\n]+system[\s\n]+\(IS\)[\s\n]+have(?:[\n]+|(?:\\n)+)[\s\n]+been[\s\n]+authenticated\.[\s\n]+By[\s\n]+remaining[\s\n]+connected,[\s\n]+you[\s\n]+declare[\s\n]+that[\s\n]+you[\s\n]+are(?:[\n]+|(?:\\n)+)[\s\n]+the[\s\n]+authorized[\s\n]+user[\s\n]+of[\s\n]+these[\s\n]+credentials[\s\n]+and[\s\n]+agree[\s\n]+to[\s\n]+the[\s\n]+terms[\s\n]+of(?:[\n]+|(?:\\n)+)[\s\n]+use[\s\n]+of[\s\n]+this[\s\n]+IS\.(?:[\n]+|(?:\\n)+)(?:[\n]+|(?:\\n)+)[\s\n]+All[\s\n]+activities[\s\n]+performed[\s\n]+on[\s\n]+this[\s\n]+device[\s\n]+are[\s\n]+logged[\s\n]+and[\s\n]+monitored\.(?:[\n]+|(?:\\n)+)(?:[\n]+|(?:\\n)+)[\s\n]+Unauthorized[\s\n]+attempts[\s\n]+to[\s\n]+elevate[\s\n]+privilege[\s\n]+or[\s\n]+attempts[\s\n]+to[\s\n]+circumvent(?:[\n]+|(?:\\n)+)[\s\n]+system[\s\n]+security[\s\n]+will[\s\n]+result[\s\n]+in[\s\n]+civil[\s\n]+and/or[\s\n]+criminal[\s\n]+penalties\.(?:[\n]+|(?:\\n)+)[\s\n]+\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*(?:[\n]+|(?:\\n)+)$ + + + + + + + + + + + + + + + + + + + + + + DISA STIG for Red Hat Enterprise Linux 8 [CUSTOMIZED] + This profile contains configuration checks that align to the +DISA STIG for Red Hat Enterprise Linux 8 V1R7. + +In addition to being applicable to Red Hat Enterprise Linux 8, DISA recognizes this +configuration baseline as applicable to the operating system tier of +Red Hat technologies that are based on Red Hat Enterprise Linux 8, such as: + +- Red Hat Enterprise Linux Server +- Red Hat Enterprise Linux Workstation and Desktop +- Red Hat Enterprise Linux for HPC +- Red Hat Storage +- Red Hat Containers with a Red Hat Enterprise Linux 8 image + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ^[\s\n]+\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*(?:[\n]+|(?:\\n)+)[\s\n]+The[\s\n]+credentials[\s\n]+you[\s\n]+used[\s\n]+to[\s\n]+access[\s\n]+this[\s\n]+information[\s\n]+system[\s\n]+\(IS\)[\s\n]+have(?:[\n]+|(?:\\n)+)[\s\n]+been[\s\n]+authenticated\.[\s\n]+By[\s\n]+remaining[\s\n]+connected,[\s\n]+you[\s\n]+declare[\s\n]+that[\s\n]+you[\s\n]+are(?:[\n]+|(?:\\n)+)[\s\n]+the[\s\n]+authorized[\s\n]+user[\s\n]+of[\s\n]+these[\s\n]+credentials[\s\n]+and[\s\n]+agree[\s\n]+to[\s\n]+the[\s\n]+terms[\s\n]+of(?:[\n]+|(?:\\n)+)[\s\n]+use[\s\n]+of[\s\n]+this[\s\n]+IS\.(?:[\n]+|(?:\\n)+)(?:[\n]+|(?:\\n)+)[\s\n]+All[\s\n]+activities[\s\n]+performed[\s\n]+on[\s\n]+this[\s\n]+device[\s\n]+are[\s\n]+logged[\s\n]+and[\s\n]+monitored\.(?:[\n]+|(?:\\n)+)(?:[\n]+|(?:\\n)+)[\s\n]+Unauthorized[\s\n]+attempts[\s\n]+to[\s\n]+elevate[\s\n]+privilege[\s\n]+or[\s\n]+attempts[\s\n]+to[\s\n]+circumvent(?:[\n]+|(?:\\n)+)[\s\n]+system[\s\n]+security[\s\n]+will[\s\n]+result[\s\n]+in[\s\n]+civil[\s\n]+and/or[\s\n]+criminal[\s\n]+penalties\.(?:[\n]+|(?:\\n)+)[\s\n]+\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*(?:[\n]+|(?:\\n)+)$ + time.example.ca,time2.example.ca,time.chu.nrc.ca + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/roles/satellite_config/files/ssg-rhel8-ds.xml b/roles/satellite_config/files/ssg-rhel8-ds.xml new file mode 100644 index 0000000..d3ba4e6 --- /dev/null +++ b/roles/satellite_config/files/ssg-rhel8-ds.xml @@ -0,0 +1,361769 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + System architecture is AARCH64 + oval:ssg-proc_sys_kernel_osrelease_arch_aarch64:def:1 + + + Package audit is installed + oval:ssg-installed_env_has_audit_package:def:1 + + + Package chrony is installed + oval:ssg-installed_env_has_chrony_package:def:1 + + + Package gdm is installed + oval:ssg-installed_env_has_gdm_package:def:1 + + + Package grub2 is installed + oval:ssg-installed_env_has_grub2_package:def:1 + + + Package krb5 server is installed and version is lesser than 1.17-18 + oval:ssg-krb5_server_older_than_1_17_18:def:1 + + + Package krb5 workstation is installed and version is lesser than 1.17-18 + oval:ssg-krb5_workstation_older_than_1_17_18:def:1 + + + Package libuser is installed + oval:ssg-installed_env_has_libuser_package:def:1 + + + Package providing /etc/login.defs is installed + oval:ssg-installed_env_has_login_defs:def:1 + + + Bare-metal or Virtual Machine + oval:ssg-installed_env_is_a_machine:def:1 + + + Package net-snmp is installed + oval:ssg-installed_env_has_net-snmp_package:def:1 + + + oVirt is not installed (no Host nor Manager) + oval:ssg-installed_env_has_no_ovirt:def:1 + + + System boot mode is non-UEFI + oval:ssg-system_boot_mode_is_non_uefi:def:1 + + + System architecture is not S390X + oval:ssg-proc_sys_kernel_osrelease_arch_not_s390x:def:1 + + + Package nss-pam-ldapd is installed + oval:ssg-installed_env_has_nss-pam-ldapd_package:def:1 + + + Package ntp is installed + oval:ssg-installed_env_has_ntp_package:def:1 + + + Package pam is installed + oval:ssg-installed_env_has_pam_package:def:1 + + + There is a /tmp partition + oval:ssg-installed_env_mounts_tmp:def:1 + + + There is a /var/tmp partition + oval:ssg-installed_env_mounts_var_tmp:def:1 + + + Package polkit is installed + oval:ssg-installed_env_has_polkit_package:def:1 + + + Package postfix is installed + oval:ssg-installed_env_has_postfix_package:def:1 + + + System architecture is S390X + oval:ssg-proc_sys_kernel_osrelease_arch_s390x:def:1 + + + Package sssd-common is installed + oval:ssg-installed_env_has_sssd-common_package:def:1 + + + SSSD is configured to use LDAP + oval:ssg-sssd_conf_uses_ldap:def:1 + + + Package sudo is installed + oval:ssg-installed_env_has_sudo_package:def:1 + + + Package systemd is installed + oval:ssg-installed_env_has_systemd_package:def:1 + + + Package tftp-server is installed + oval:ssg-installed_env_has_tftp_server_package:def:1 + + + Package tmux is installed + oval:ssg-installed_env_has_tmux_package:def:1 + + + System boot mode is UEFI + oval:ssg-system_boot_mode_is_uefi:def:1 + + + Package usbguard is installed + oval:ssg-installed_env_has_usbguard_package:def:1 + + + WiFi interface is present + oval:ssg-installed_env_has_wifi_interface:def:1 + + + Package yum is installed + oval:ssg-installed_env_has_yum_package:def:1 + + + Red Hat Enterprise Linux 8 + oval:ssg-installed_OS_is_rhel8:def:1 + + + Red Hat Enterprise Linux 8.0 + oval:ssg-installed_OS_is_rhel8_0:def:1 + + + Red Hat Enterprise Linux 8.1 + oval:ssg-installed_OS_is_rhel8_1:def:1 + + + Red Hat Enterprise Linux 8.10 + oval:ssg-installed_OS_is_rhel8_10:def:1 + + + Red Hat Enterprise Linux 8.2 + oval:ssg-installed_OS_is_rhel8_2:def:1 + + + Red Hat Enterprise Linux 8.3 + oval:ssg-installed_OS_is_rhel8_3:def:1 + + + Red Hat Enterprise Linux 8.4 + oval:ssg-installed_OS_is_rhel8_4:def:1 + + + Red Hat Enterprise Linux 8.5 + oval:ssg-installed_OS_is_rhel8_5:def:1 + + + Red Hat Enterprise Linux 8.6 + oval:ssg-installed_OS_is_rhel8_6:def:1 + + + Red Hat Enterprise Linux 8.7 + oval:ssg-installed_OS_is_rhel8_7:def:1 + + + Red Hat Enterprise Linux 8.8 + oval:ssg-installed_OS_is_rhel8_8:def:1 + + + Red Hat Enterprise Linux 8.9 + oval:ssg-installed_OS_is_rhel8_9:def:1 + + + + + + draft + Guide to the Secure Configuration of Red Hat Enterprise Linux 8 + This guide presents a catalog of security-relevant +configuration settings for Red Hat Enterprise Linux 8. It is a rendering of +content structured in the eXtensible Configuration Checklist Description Format (XCCDF) +in order to support security automation. The SCAP content is +is available in the scap-security-guide package which is developed at + + https://www.open-scap.org/security-policies/scap-security-guide. + +Providing system administrators with such guidance informs them how to securely +configure systems under their control in a variety of network roles. Policy +makers and baseline creators can use this catalog of settings, with its +associated references to higher-level security control catalogs, in order to +assist them in security baseline creation. This guide is a catalog, not a +checklist, and satisfaction of every item is not likely to be possible or +sensible in many operational scenarios. However, the XCCDF format enables +granular selection and adjustment of settings, and their association with OVAL +and OCIL content provides an automated checking capability. Transformations of +this document, and its associated automated checking content, are capable of +providing baselines that meet a diverse set of policy objectives. Some example +XCCDF Profiles, which are selections of items that form checklists and +can be used as baselines, are available with this guide. They can be +processed, in an automated fashion, with tools that support the Security +Content Automation Protocol (SCAP). The DISA STIG, which provides required +settings for US Department of Defense systems, is one example of a baseline +created from this guidance. + + Do not attempt to implement any of the settings in +this guide without first testing them in a non-operational environment. The +creators of this guidance assume no responsibility whatsoever for its use by +other parties, and makes no guarantees, expressed or implied, about its +quality, reliability, or any other characteristic. + + The SCAP Security Guide Project + + https://www.open-scap.org/security-policies/scap-security-guide + + Red Hat and Red Hat Enterprise Linux are either registered +trademarks or trademarks of Red Hat, Inc. in the United States and other +countries. All other names are registered trademarks or trademarks of their +respective companies. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0.1.64 + + SCAP Security Guide Project + SCAP Security Guide Project + Frank J Cameron (CAM1244) <cameron@ctc.com> + 0x66656c6978 <0x66656c6978@users.noreply.github.com> + Håvard F. Aasen <havard.f.aasen@pfft.no> + Jack Adolph <jack.adolph@gmail.com> + Edgar Aguilar <edgar.aguilar@oracle.com> + Gabe Alford <redhatrises@gmail.com> + Firas AlShafei <firas.alshafei@us.abb.com> + Rodrigo Alvares <ralvares@redhat.com> + Christopher Anderson <cba@fedoraproject.org> + angystardust <angystardust@users.noreply.github.com> + anivan-suse <anastasija.ivanovic@suse.com> + anixon-rh <55244503+anixon-rh@users.noreply.github.com> + Ikko Ashimine <eltociear@gmail.com> + Chuck Atkins <chuck.atkins@kitware.com> + ayfantis <ayfantis@localhost.localdomain> + Ryan Ballanger <root@rballang-admin-2.fastenal.com> + Alex Baranowski <alex@euro-linux.com> + Eduardo Barretto <eduardo.barretto@canonical.com> + Molly Jo Bault <Molly.Jo.Bault@ballardtech.com> + Andrew Becker <A-Beck@users.noreply.github.com> + Gabriel Becker <ggasparb@redhat.com> + Alexander Bergmann <abergmann@suse.com> + Dale Bewley <dale@bewley.net> + Jose Luis BG <bgjoseluis@gmail.com> + binyanling <binyanling@uniontech.com> + Joseph Bisch <joseph.bisch@gmail.com> + Jeffrey Blank <blank@eclipse.ncsc.mil> + Olivier Bonhomme <ptitoliv@ptitoliv.net> + Lance Bragstad <lbragstad@gmail.com> + Ted Brunell <tbrunell@redhat.com> + Marcus Burghardt <maburgha@redhat.com> + Matthew Burket <mburket@redhat.com> + Blake Burkhart <blake.burkhart@us.af.mil> + Patrick Callahan <pmc@patrickcallahan.com> + George Campbell <gcampbell@palantir.com> + Nick Carboni <ncarboni@redhat.com> + Carlos <64919342+carlosmmatos@users.noreply.github.com> + James Cassell <james.cassell@ll.mit.edu> + Frank Caviggia <fcaviggi@ra.iad.redhat.com> + Eric Christensen <echriste@redhat.com> + Dan Clark <danclark@redhat.com> + Jayson Cofell <1051437+70k10@users.noreply.github.com> + Caleb Cooper <coopercd@ornl.gov> + Richard Maciel Costa <richard.maciel.costa@canonical.com> + Deric Crago <deric.crago@gmail.com> + crleekwc <crleekwc@gmail.com> + cyarbrough76 <42849651+cyarbrough76@users.noreply.github.com> + Maura Dailey <maura@eclipse.ncsc.mil> + Klaas Demter <demter@atix.de> + dhanushkar-wso2 <dhanushkar@wso2.com> + Andrew DiPrinzio <andrew.diprinzio@jhuapl.edu> + dom <dominique.blaze@devinci.fr> + Jean-Baptiste Donnette <jean-baptiste.donnette@epita.fr> + Marco De Donno <mdedonno1337@gmail.com> + dperrone <dperrone@redhat.com> + drax <applezip@gmail.com> + Sebastian Dunne <sdunne@redhat.com> + François Duthilleul <francoisduthilleul@gmail.com> + Greg Elin <gregelin@gitmachines.com> + eradot4027 <jrtonmac@gmail.com> + Alexis Facques <alexis.facques@mythalesgroup.io> + Leah Fisher <lfisher047@gmail.com> + Yavor Georgiev <strandjata@gmail.com> + Alijohn Ghassemlouei <alijohn@secureagc.com> + Swarup Ghosh <swghosh@redhat.com> + ghylock <ghylock@gmail.com> + Andrew Gilmore <agilmore2@gmail.com> + Joshua Glemza <jglemza@nasa.gov> + Nick Gompper <forestgomp@yahoo.com> + Loren Gordon <lorengordon@users.noreply.github.com> + Patrik Greco <sikevux@sikevux.se> + Steve Grubb <sgrubb@redhat.com> + guangyee <gyee@suse.com> + Marek Haicman <mhaicman@redhat.com> + Vern Hart <vern.hart@canonical.com> + Alex Haydock <alex@alexhaydock.co.uk> + Rebekah Hayes <rhayes@corp.rivierautilities.com> + Trey Henefield <thenefield@gmail.com> + Henning Henkel <henning.henkel@helvetia.ch> + hex2a <hex2a@users.noreply.github.com> + John Hooks <jhooks@starscream.pa.jhbcomputers.com> + Jakub Hrozek <jhrozek@redhat.com> + De Huo <De.Huo@windriver.com> + Robin Price II <robin@redhat.com> + Yasir Imam <yimam@redhat.com> + Jiri Jaburek <jjaburek@redhat.com> + Keith Jackson <keithkjackson@gmail.com> + Jeremiah Jahn <jeremiah@goodinassociates.com> + Jakub Jelen <jjelen@redhat.com> + Jessicahfy <Jessicahfy@users.noreply.github.com> + Stephan Joerrens <Stephan.Joerrens@fiduciagad.de> + Hunter Jones <hjones2199@gmail.com> + Jono <jono@ubuntu-18.localdomain> + justchris1 <justchris1@justchris1.email> + Kai Kang <kai.kang@windriver.com> + Charles Kernstock <charles.kernstock@ultra-ats.com> + Yuli Khodorkovskiy <ykhodorkovskiy@tresys.com> + Sherine Khoury <skhoury@redhat.com> + Nathan Kinder <nkinder@redhat.com> + Lee Kinser <lee.kinser@gmail.com> + Evgeny Kolesnikov <ekolesni@redhat.com> + Peter 'Pessoft' Kolínek <github@pessoft.com> + Luke Kordell <luke.t.kordell@lmco.com> + Malte Kraus <malte.kraus@suse.com> + Seth Kress <seth.kress@dsainc.com> + Felix Krohn <felix.krohn@helvetia.ch> + kspargur <kspargur@kspargur.csb> + Amit Kumar <amitkuma@redhat.com> + Fen Labalme <fen@civicactions.com> + Ade Lee <alee@redhat.com> + Christopher Lee <Crleekwc@gmail.com> + Ian Lee <lee1001@llnl.gov> + Jarrett Lee <jarrettl@umd.edu> + Joseph Lenox <joseph.lenox@collins.com> + Jan Lieskovsky <jlieskov@redhat.com> + Markus Linnala <Markus.Linnala@knowit.fi> + Flos Lonicerae <lonicerae@gmail.com> + Šimon Lukašík <slukasik@redhat.com> + Milan Lysonek <mlysonek@redhat.com> + Fredrik Lysén <fredrik@pipemore.se> + Caitlin Macleod <caitelatte@gmail.com> + Nick Maludy <nmaludy@gmail.com> + Lokesh Mandvekar <lsm5@fedoraproject.org> + Matus Marhefka <mmarhefk@redhat.com> + Jamie Lorwey Martin <jlmartin@redhat.com> + Carlos Matos <cmatos@redhat.com> + Robert McAllister <rmcallis@redhat.com> + Karen McCarron <kmccarro@redhat.com> + Michael McConachie <michael@redhat.com> + Marcus Meissner <meissner@suse.de> + Khary Mendez <kmendez@redhat.com> + Rodney Mercer <rmercer@harris.com> + mgjadoul <mgjadoul@laptomatic.auth-o-matic.corp> + Matt Micene <nzwulfin@gmail.com> + Brian Millett <bmillett@gmail.com> + Takuya Mishina <tmishina@jp.ibm.com> + Mixer9 <35545791+Mixer9@users.noreply.github.com> + mmosel <mmosel@kde.example.com> + Zbynek Moravec <zmoravec@redhat.com> + Kazuo Moriwaka <moriwaka@users.noreply.github.com> + Michael Moseley <michael@eclipse.ncsc.mil> + Renaud Métrich <rmetrich@redhat.com> + Joe Nall <joe@nall.com> + Neiloy <neiloy@redhat.com> + Axel Nennker <axel@nennker.de> + Michele Newman <mnewman@redhat.com> + Sean O'Keeffe <seanokeeffe797@gmail.com> + Jiri Odehnal <jodehnal@redhat.com> + Ilya Okomin <ilya.okomin@oracle.com> + Kaustubh Padegaonkar <theTuxRacer@gmail.com> + Michael Palmiotto <mpalmiotto@tresys.com> + Eryx Paredes <eryxp@lyft.com> + Max R.D. Parmer <maxp@trystero.is> + Arnaud Patard <apatard@hupstream.com> + Jan Pazdziora <jpazdziora@redhat.com> + pcactr <paul.c.arnold4.ctr@mail.mil> + Kenneth Peeples <kennethwpeeples@gmail.com> + Nathan Peters <Nathaniel.Peters@ca.com> + Frank Lin PIAT <fpiat@klabs.be> + Stefan Pietsch <mail.ipv4v6+gh@gmail.com> + piggyvenus <piggyvenus@gmail.com> + Vojtech Polasek <vpolasek@redhat.com> + Orion Poplawski <orion@nwra.com> + Nick Poyant <npoyant@redhat.com> + Martin Preisler <mpreisle@redhat.com> + Wesley Ceraso Prudencio <wcerasop@redhat.com> + Raphael Sanchez Prudencio <rsprudencio@redhat.com> + T.O. Radzy Radzykewycz <radzy@windriver.com> + Kenyon Ralph <kenyon@kenyonralph.com> + Mike Ralph <mralph@redhat.com> + Federico Ramirez <federico.r.ramirez@oracle.com> + rchikov <rumen.chikov@suse.com> + Rick Renshaw <Richard_Renshaw@xtoenergy.com> + Chris Reynolds <c.reynolds82@gmail.com> + rhayes <rhayes@rivierautilities.com> + Pat Riehecky <riehecky@fnal.gov> + rlucente-se-jboss <rlucente@redhat.com> + Juan Antonio Osorio Robles <juan.osoriorobles@eu.equinix.com> + Matt Rogers <mrogers@redhat.com> + Jesse Roland <jesse.roland@onyxpoint.com> + Joshua Roys <roysjosh@gmail.com> + rrenshaw <bofh69@yahoo.com> + Chris Ruffalo <chris.ruffalo@gmail.com> + rumch-se <77793453+rumch-se@users.noreply.github.com> + Ray Shaw (Cont ARL/CISD) rvshaw <rvshaw@esme.arl.army.mil> + Earl Sampson <ESampson@suse.com> + sampsone <esampson@suse.com> + Willy Santos <wsantos@redhat.com> + Nagarjuna Sarvepalli <snagarju@redhat.com> + Anderson Sasaki <33833274+ansasaki@users.noreply.github.com> + Gautam Satish <gautams@hpe.com> + Watson Sato <wsato@redhat.com> + Satoru SATOH <satoru.satoh@gmail.com> + Alexander Scheel <ascheel@redhat.com> + Bryan Schneiders <pschneiders@trisept.com> + shaneboulden <shane.boulden@gmail.com> + Vincent Shen <47534281+Vincent056@users.noreply.github.com> + Dhriti Shikhar <dhriti.shikhar.rokz@gmail.com> + Spencer Shimko <sshimko@tresys.com> + Mark Shoger <mshoger@redhat.com> + THOBY Simon <Simon.THOBY@viveris.fr> + Thomas Sjögren <konstruktoid@users.noreply.github.com> + Francisco Slavin <fslavin@tresys.com> + David Smith <dsmith@eclipse.ncsc.mil> + Kevin Spargur <kspargur@redhat.com> + Kenneth Stailey <kstailey.lists@gmail.com> + Leland Steinke <leland.j.steinke.ctr@mail.mil> + Justin Stephenson <jstephen@redhat.com> + Brian Stinson <brian@bstinson.com> + Jake Stookey <jakestookey@gmail.com> + Jonathan Sturges <jsturges@redhat.com> + Ian Tewksbury <itewk@redhat.com> + Philippe Thierry <phil@reseau-libre.net> + Derek Thurston <thegrit@gmail.com> + tianzhenjia <jiatianzhen@cmss.chinamobile.com> + Greg Tinsley <gtinsley@redhat.com> + Paul Tittle <ptittle@cmf.nrl.navy.mil> + tom <tom@localhost.localdomain> + tomas.hudik <tomas.hudik@embedit.cz> + Jeb Trayer <jeb.d.trayer@uscg.mil> + TrilokGeer <tgeer@redhat.com> + Viktors Trubovics <viktors.trubovics@suse.com> + Nico Truzzolino <nico.truzzolino@gmx.de> + Brian Turek <brian.turek@gmail.com> + Matěj Týč <matyc@redhat.com> + VadimDor <29509093+VadimDor@users.noreply.github.com> + Trevor Vaughan <tvaughan@onyxpoint.com> + vtrubovics <82443408+vtrubovics@users.noreply.github.com> + Samuel Warren <swarren@redhat.com> + wcushen <54533890+wcushen@users.noreply.github.com> + Shawn Wells <shawn@shawndwells.io> + Daniel E. White <linuxdan@users.noreply.github.com> + Bernhard M. Wiedemann <bwiedemann@suse.de> + Roy Williams <roywilli@roywilli.redhat.com> + Willumpie <willumpie@xs4all.nl> + Rob Wilmoth <rwilmoth@redhat.com> + Lucas Yamanishi <lucas.yamanishi@onyxpoint.com> + Xirui Yang <xirui.yang@oracle.com> + yarunachalam <yarunachalam@suse.com> + Guang Yee <guang.yee@suse.com> + Achilleas John Yfantis <ayfantis@redhat.com> + YiLin.Li <YiLin.Li@linux.alibaba.com> + YuQing <yyq0391@163.com> + Kevin Zimmerman <kevin.zimmerman@kitware.com> + Luigi Mario Zuccarelli <luzuccar@redhat.com> + Jan Černý <jcerny@redhat.com> + Michal Šrubař <msrubar@redhat.com> + https://github.com/ComplianceAsCode/content/releases/latest + + + ANSSI-BP-028 (enhanced) + This profile contains configurations that align to ANSSI-BP-028 v1.2 at the enhanced hardening level. + +ANSSI is the French National Information Security Agency, and stands for Agence nationale de la sécurité des systèmes d'information. +ANSSI-BP-028 is a configuration recommendation for GNU/Linux systems. + +A copy of the ANSSI-BP-028 can be found at the ANSSI website: +https://www.ssi.gouv.fr/administration/guide/recommandations-de-securite-relatives-a-un-systeme-gnulinux/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ANSSI-BP-028 (high) + This profile contains configurations that align to ANSSI-BP-028 v1.2 at the high hardening level. + +ANSSI is the French National Information Security Agency, and stands for Agence nationale de la sécurité des systèmes d'information. +ANSSI-BP-028 is a configuration recommendation for GNU/Linux systems. + +A copy of the ANSSI-BP-028 can be found at the ANSSI website: +https://www.ssi.gouv.fr/administration/guide/recommandations-de-securite-relatives-a-un-systeme-gnulinux/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ANSSI-BP-028 (intermediary) + This profile contains configurations that align to ANSSI-BP-028 v1.2 at the intermediary hardening level. + +ANSSI is the French National Information Security Agency, and stands for Agence nationale de la sécurité des systèmes d'information. +ANSSI-BP-028 is a configuration recommendation for GNU/Linux systems. + +A copy of the ANSSI-BP-028 can be found at the ANSSI website: +https://www.ssi.gouv.fr/administration/guide/recommandations-de-securite-relatives-a-un-systeme-gnulinux/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ANSSI-BP-028 (minimal) + This profile contains configurations that align to ANSSI-BP-028 v1.2 at the minimal hardening level. + +ANSSI is the French National Information Security Agency, and stands for Agence nationale de la sécurité des systèmes d'information. +ANSSI-BP-028 is a configuration recommendation for GNU/Linux systems. + +A copy of the ANSSI-BP-028 can be found at the ANSSI website: +https://www.ssi.gouv.fr/administration/guide/recommandations-de-securite-relatives-a-un-systeme-gnulinux/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + CIS Red Hat Enterprise Linux 8 Benchmark for Level 2 - Server + This profile defines a baseline that aligns to the "Level 2 - Server" +configuration from the Center for Internet Security® Red Hat Enterprise +Linux 8 Benchmark™, v2.0.0, released 2022-02-23. + +This profile includes Center for Internet Security® +Red Hat Enterprise Linux 8 CIS Benchmarks™ content. + https://www.cisecurity.org/benchmark/red_hat_linux/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + CIS Red Hat Enterprise Linux 8 Benchmark for Level 1 - Server + This profile defines a baseline that aligns to the "Level 1 - Server" +configuration from the Center for Internet Security® Red Hat Enterprise +Linux 8 Benchmark™, v2.0.0, released 2022-02-23. + +This profile includes Center for Internet Security® +Red Hat Enterprise Linux 8 CIS Benchmarks™ content. + https://www.cisecurity.org/benchmark/red_hat_linux/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + CIS Red Hat Enterprise Linux 8 Benchmark for Level 1 - Workstation + This profile defines a baseline that aligns to the "Level 1 - Workstation" +configuration from the Center for Internet Security® Red Hat Enterprise +Linux 8 Benchmark™, v2.0.0, released 2022-02-23. + +This profile includes Center for Internet Security® +Red Hat Enterprise Linux 8 CIS Benchmarks™ content. + https://www.cisecurity.org/benchmark/red_hat_linux/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + CIS Red Hat Enterprise Linux 8 Benchmark for Level 2 - Workstation + This profile defines a baseline that aligns to the "Level 2 - Workstation" +configuration from the Center for Internet Security® Red Hat Enterprise +Linux 8 Benchmark™, v2.0.0, released 2022-02-23. + +This profile includes Center for Internet Security® +Red Hat Enterprise Linux 8 CIS Benchmarks™ content. + https://www.cisecurity.org/benchmark/red_hat_linux/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Criminal Justice Information Services (CJIS) Security Policy + This profile is derived from FBI's CJIS v5.4 +Security Policy. A copy of this policy can be found at the CJIS Security +Policy Resource Center: + +https://www.fbi.gov/services/cjis/cjis-security-policy-resource-center + https://www.fbi.gov/services/cjis/cjis-security-policy-resource-center + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Unclassified Information in Non-federal Information Systems and Organizations (NIST 800-171) + From NIST 800-171, Section 2.2: +Security requirements for protecting the confidentiality of CUI in nonfederal +information systems and organizations have a well-defined structure that +consists of: + +(i) a basic security requirements section; +(ii) a derived security requirements section. + +The basic security requirements are obtained from FIPS Publication 200, which +provides the high-level and fundamental security requirements for federal +information and information systems. The derived security requirements, which +supplement the basic security requirements, are taken from the security controls +in NIST Special Publication 800-53. + +This profile configures Red Hat Enterprise Linux 8 to the NIST Special +Publication 800-53 controls identified for securing Controlled Unclassified +Information (CUI)." + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Australian Cyber Security Centre (ACSC) Essential Eight + This profile contains configuration checks for Red Hat Enterprise Linux 8 +that align to the Australian Cyber Security Centre (ACSC) Essential Eight. + +A copy of the Essential Eight in Linux Environments guide can be found at the +ACSC website: + +https://www.cyber.gov.au/acsc/view-all-content/publications/hardening-linux-workstations-and-servers + https://www.cyber.gov.au/acsc/view-all-content/publications/hardening-linux-workstations-and-servers + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Health Insurance Portability and Accountability Act (HIPAA) + The HIPAA Security Rule establishes U.S. national standards to protect individuals’ +electronic personal health information that is created, received, used, or +maintained by a covered entity. The Security Rule requires appropriate +administrative, physical and technical safeguards to ensure the +confidentiality, integrity, and security of electronic protected health +information. + +This profile configures Red Hat Enterprise Linux 8 to the HIPAA Security +Rule identified for securing of electronic protected health information. +Use of this profile in no way guarantees or makes claims against legal compliance against the HIPAA Security Rule(s). + https://www.hhs.gov/hipaa/for-professionals/index.html + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Australian Cyber Security Centre (ACSC) ISM Official + This profile contains configuration checks for Red Hat Enterprise Linux 8 +that align to the Australian Cyber Security Centre (ACSC) Information Security Manual (ISM) +with the applicability marking of OFFICIAL. + +The ISM uses a risk-based approach to cyber security. This profile provides a guide to aligning +Red Hat Enterprise Linux security controls with the ISM, which can be used to select controls +specific to an organisation's security posture and risk profile. + +A copy of the ISM can be found at the ACSC website: + +https://www.cyber.gov.au/ism + https://www.cyber.gov.au/ism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Protection Profile for General Purpose Operating Systems + This profile reflects rhisam configuration controls identified in the +NIAP Configuration Annex to the Protection Profile for General Purpose +Operating Systems (Protection Profile Version 4.2.1). + +This configuration profile is consistent with CNSSI-1253, which requires +U.S. National Security Systems to adhere to certain configuration +parameters. Accordingly, this configuration profile is suitable for +use in U.S. National Security Systems. + https://www.niap-ccevs.org/Profile/PP.cfm + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + PCI-DSS v3.2.1 Control Baseline for Red Hat Enterprise Linux 8 + Ensures PCI-DSS v3.2.1 security configuration settings are applied. + https://www.pcisecuritystandards.org/documents/PCI_DSS_v3-2-1.pdf + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Red Hat Corporate Profile for Certified Cloud Providers (RH CCP) + This profile contains the minimum security relevant +configuration settings recommended by Red Hat, Inc for +Red Hat Enterprise Linux 8 instances deployed by Red Hat Certified +Cloud Providers. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Standard System Security Profile for Red Hat Enterprise Linux 8 + This profile contains rules to ensure standard security baseline +of a Red Hat Enterprise Linux 8 system. Regardless of your system's workload +all of these checks should pass. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + DISA STIG for Red Hat Enterprise Linux 8 + This profile contains configuration checks that align to the +DISA STIG for Red Hat Enterprise Linux 8 V1R7. + +In addition to being applicable to Red Hat Enterprise Linux 8, DISA recognizes this +configuration baseline as applicable to the operating system tier of +Red Hat technologies that are based on Red Hat Enterprise Linux 8, such as: + +- Red Hat Enterprise Linux Server +- Red Hat Enterprise Linux Workstation and Desktop +- Red Hat Enterprise Linux for HPC +- Red Hat Storage +- Red Hat Containers with a Red Hat Enterprise Linux 8 image + https://public.cyber.mil/stigs/downloads/?_dl_facet_stigs=operating-systems%2Cunix-linux + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + DISA STIG with GUI for Red Hat Enterprise Linux 8 + This profile contains configuration checks that align to the +DISA STIG with GUI for Red Hat Enterprise Linux 8 V1R7. + +In addition to being applicable to Red Hat Enterprise Linux 8, DISA recognizes this +configuration baseline as applicable to the operating system tier of +Red Hat technologies that are based on Red Hat Enterprise Linux 8, such as: + +- Red Hat Enterprise Linux Server +- Red Hat Enterprise Linux Workstation and Desktop +- Red Hat Enterprise Linux for HPC +- Red Hat Storage +- Red Hat Containers with a Red Hat Enterprise Linux 8 image + +Warning: The installation and use of a Graphical User Interface (GUI) +increases your attack vector and decreases your overall security posture. If +your Information Systems Security Officer (ISSO) lacks a documented operational +requirement for a graphical user interface, please consider using the +standard DISA STIG for Red Hat Enterprise Linux 8 profile. + https://public.cyber.mil/stigs/downloads/?_dl_facet_stigs=operating-systems%2Cunix-linux + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + System Settings + Contains rules that check correct system settings. + + Installing and Maintaining Software + The following sections contain information on +security-relevant choices during the initial operating system +installation process and the setup of software +updates. + + Prefer to use a 64-bit Operating System when supported + Prefer installation of 64-bit operating systems when the CPU supports it. + There is no remediation besides installing a 64-bit operating system. + BP28(R10) + Use of a 64-bit operating system offers a few advantages, like a larger address space range for +Address Space Layout Randomization (ASLR) and systematic presence of No eXecute and Execute Disable (NX/XD) protection bits. + CCE-83694-0 + + + + + + + + + System and Software Integrity + System and software integrity can be gained by installing antivirus, increasing +system encryption strength with FIPS, verifying installed software, enabling SELinux, +installing an Intrusion Prevention System, etc. However, installing or enabling integrity +checking tools cannot prevent intrusions, but they can detect that an intrusion +may have occurred. Requirements for integrity checking may be highly dependent on +the environment in which the system will be used. Snapshot-based approaches such +as AIDE may induce considerable overhead in the presence of frequent software updates. + + Software Integrity Checking + Both the AIDE (Advanced Intrusion Detection Environment) +software and the RPM package management system provide +mechanisms for verifying the integrity of installed software. +AIDE uses snapshots of file metadata (such as hashes) and compares these +to current system files in order to detect changes. + +The RPM package management system can conduct integrity +checks by comparing information in its metadata database with +files installed on the system. + + Integrity Scan Notification Email Address + Specify the email address for designated personnel if baseline +configurations are changed in an unauthorized manner. + root@localhost + + + Verify Integrity with RPM + The RPM package management system includes the ability +to verify the integrity of installed packages by comparing the +installed files with information about the files taken from the +package metadata stored in the RPM database. Although an attacker +could corrupt the RPM database (analogous to attacking the AIDE +database as described above), this check can still reveal +modification of important files. To list which files on the system differ from what is expected by the RPM database: +$ rpm -qVa +See the man page for rpm to see a complete explanation of each column. + + Verify File Hashes with RPM + Without cryptographic integrity protections, system +executables and files can be altered by unauthorized users without +detection. +The RPM package management system can check the hashes of +installed software packages, including many that are important to system +security. +To verify that the cryptographic hash of system files and commands matches vendor +values, run the following command to list which files on the system +have hashes that differ from what is expected by the RPM database: +$ rpm -Va --noconfig | grep '^..5' +A "c" in the second column indicates that a file is a configuration file, which +may appropriately be expected to change. If the file was not expected to +change, investigate the cause of the change using audit logs or other means. +The package can then be reinstalled to restore the file. +Run the following command to determine which package owns the file: +$ rpm -qf FILENAME +The package can be reinstalled from a yum repository using the command: +$ sudo yum reinstall PACKAGENAME +Alternatively, the package can be reinstalled from trusted media using the command: +$ sudo rpm -Uvh PACKAGENAME + 11 + 2 + 3 + 9 + 5.10.4.1 + APO01.06 + BAI03.05 + BAI06.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS06.02 + 3.3.8 + 3.4.1 + CCI-000366 + CCI-001749 + 164.308(a)(1)(ii)(D) + 164.312(b) + 164.312(c)(1) + 164.312(c)(2) + 164.312(e)(2)(i) + 4.3.4.3.2 + 4.3.4.3.3 + 4.3.4.4.4 + SR 3.1 + SR 3.3 + SR 3.4 + SR 3.8 + SR 7.6 + A.11.2.4 + A.12.1.2 + A.12.2.1 + A.12.5.1 + A.12.6.2 + A.14.1.2 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + CM-6(d) + CM-6(c) + SI-7 + SI-7(1) + SI-7(6) + AU-9(3) + PR.DS-6 + PR.DS-8 + PR.IP-1 + Req-11.5 + SRG-OS-000480-GPOS-00227 + 6.1.1 + The hashes of important files like system executables should match the +information given by the RPM database. Executables with erroneous hashes could +be a sign of nefarious activity on the system. + CCE-80857-6 + - name: 'Set fact: Package manager reinstall command (dnf)' + set_fact: + package_manager_reinstall_cmd: dnf reinstall -y + when: ansible_distribution == "Fedora" + tags: + - CCE-80857-6 + - CJIS-5.10.4.1 + - NIST-800-171-3.3.8 + - NIST-800-171-3.4.1 + - NIST-800-53-AU-9(3) + - NIST-800-53-CM-6(c) + - NIST-800-53-CM-6(d) + - NIST-800-53-SI-7 + - NIST-800-53-SI-7(1) + - NIST-800-53-SI-7(6) + - PCI-DSS-Req-11.5 + - high_complexity + - high_severity + - medium_disruption + - no_reboot_needed + - restrict_strategy + - rpm_verify_hashes + +- name: 'Set fact: Package manager reinstall command (yum)' + set_fact: + package_manager_reinstall_cmd: yum reinstall -y + when: (ansible_distribution == "RedHat" or ansible_distribution == "OracleLinux") + tags: + - CCE-80857-6 + - CJIS-5.10.4.1 + - NIST-800-171-3.3.8 + - NIST-800-171-3.4.1 + - NIST-800-53-AU-9(3) + - NIST-800-53-CM-6(c) + - NIST-800-53-CM-6(d) + - NIST-800-53-SI-7 + - NIST-800-53-SI-7(1) + - NIST-800-53-SI-7(6) + - PCI-DSS-Req-11.5 + - high_complexity + - high_severity + - medium_disruption + - no_reboot_needed + - restrict_strategy + - rpm_verify_hashes + +- name: Read files with incorrect hash + command: rpm -Va --nodeps --nosize --nomtime --nordev --nocaps --nolinkto --nouser + --nogroup --nomode --noghost --noconfig + args: + warn: false + register: files_with_incorrect_hash + changed_when: false + failed_when: files_with_incorrect_hash.rc > 1 + check_mode: false + when: (package_manager_reinstall_cmd is defined) + tags: + - CCE-80857-6 + - CJIS-5.10.4.1 + - NIST-800-171-3.3.8 + - NIST-800-171-3.4.1 + - NIST-800-53-AU-9(3) + - NIST-800-53-CM-6(c) + - NIST-800-53-CM-6(d) + - NIST-800-53-SI-7 + - NIST-800-53-SI-7(1) + - NIST-800-53-SI-7(6) + - PCI-DSS-Req-11.5 + - high_complexity + - high_severity + - medium_disruption + - no_reboot_needed + - restrict_strategy + - rpm_verify_hashes + +- name: Create list of packages + command: rpm -qf "{{ item }}" + args: + warn: false + with_items: '{{ files_with_incorrect_hash.stdout_lines | map(''regex_findall'', + ''^[.]+[5]+.* (\/.*)'', ''\1'') | map(''join'') | select(''match'', ''(\/.*)'') + | list | unique }}' + register: list_of_packages + changed_when: false + check_mode: false + when: + - files_with_incorrect_hash.stdout_lines is defined + - (files_with_incorrect_hash.stdout_lines | length > 0) + tags: + - CCE-80857-6 + - CJIS-5.10.4.1 + - NIST-800-171-3.3.8 + - NIST-800-171-3.4.1 + - NIST-800-53-AU-9(3) + - NIST-800-53-CM-6(c) + - NIST-800-53-CM-6(d) + - NIST-800-53-SI-7 + - NIST-800-53-SI-7(1) + - NIST-800-53-SI-7(6) + - PCI-DSS-Req-11.5 + - high_complexity + - high_severity + - medium_disruption + - no_reboot_needed + - restrict_strategy + - rpm_verify_hashes + +- name: Reinstall packages of files with incorrect hash + command: '{{ package_manager_reinstall_cmd }} ''{{ item }}''' + args: + warn: false + with_items: '{{ list_of_packages.results | map(attribute=''stdout_lines'') | list + | unique }}' + when: + - files_with_incorrect_hash.stdout_lines is defined + - (package_manager_reinstall_cmd is defined and (files_with_incorrect_hash.stdout_lines + | length > 0)) + tags: + - CCE-80857-6 + - CJIS-5.10.4.1 + - NIST-800-171-3.3.8 + - NIST-800-171-3.4.1 + - NIST-800-53-AU-9(3) + - NIST-800-53-CM-6(c) + - NIST-800-53-CM-6(d) + - NIST-800-53-SI-7 + - NIST-800-53-SI-7(1) + - NIST-800-53-SI-7(6) + - PCI-DSS-Req-11.5 + - high_complexity + - high_severity + - medium_disruption + - no_reboot_needed + - restrict_strategy + - rpm_verify_hashes + + +# Find which files have incorrect hash (not in /etc, because of the system related config files) and then get files names +files_with_incorrect_hash="$(rpm -Va --noconfig | grep -E '^..5' | awk '{print $NF}' )" + +# From files names get package names and change newline to space, because rpm writes each package to new line +packages_to_reinstall="$(rpm -qf $files_with_incorrect_hash | tr '\n' ' ')" + +yum reinstall -y $packages_to_reinstall + + + + + + + + + + Verify and Correct Ownership with RPM + The RPM package management system can check file ownership +permissions of installed software packages, including many that are +important to system security. After locating a file with incorrect +permissions, which can be found with +rpm -Va | awk '{ if (substr($0,6,1)=="U" || substr($0,7,1)=="G") print $NF }' +run the following command to determine which package owns it: +$ rpm -qf FILENAME +Next, run the following command to reset its permissions to +the correct values: +$ sudo rpm --setugids PACKAGENAME + Profiles may require that specific files be owned by root while the default owner defined +by the vendor is different. +Such files will be reported as a finding and need to be evaluated according to your policy +and deployment environment. + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + 6 + 9 + 5.10.4.1 + APO01.06 + APO11.04 + BAI03.05 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.04 + DSS05.07 + DSS06.02 + MEA02.01 + 3.3.8 + 3.4.1 + CCI-001494 + CCI-001496 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.7.3 + 4.3.4.3.2 + 4.3.4.3.3 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.1 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 5.2 + SR 7.6 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.12.1.2 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.5.1 + A.12.6.2 + A.12.7.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R4.2 + CIP-003-8 R6 + CIP-007-3 R4 + CIP-007-3 R4.1 + CIP-007-3 R4.2 + CM-6(d) + CM-6(c) + SI-7 + SI-7(1) + SI-7(6) + AU-9(3) + PR.AC-4 + PR.DS-5 + PR.IP-1 + PR.PT-1 + Req-11.5 + SRG-OS-000256-GPOS-00097 + SRG-OS-000257-GPOS-00098 + SRG-OS-000278-GPOS-00108 + 1.8.1.4 + 1.8.1.5 + 1.8.1.6 + 6.1.1 + 6.1.2 + 6.1.3 + 6.1.4 + 6.1.5 + 6.1.6 + 6.1.7 + 6.1.8 + 6.1.9 + Ownership of binaries and configuration files that is incorrect +could allow an unauthorized user to gain privileges that they should +not have. The ownership set by the vendor should be maintained. Any +deviations from this baseline should be investigated. + CCE-82196-7 + - name: Read list of files with incorrect ownership + command: rpm -Va --nodeps --nosignature --nofiledigest --nosize --nomtime --nordev + --nocaps --nolinkto --nomode + args: + warn: false + register: files_with_incorrect_ownership + failed_when: files_with_incorrect_ownership.rc > 1 + changed_when: false + check_mode: false + tags: + - CCE-82196-7 + - CJIS-5.10.4.1 + - NIST-800-171-3.3.8 + - NIST-800-171-3.4.1 + - NIST-800-53-AU-9(3) + - NIST-800-53-CM-6(c) + - NIST-800-53-CM-6(d) + - NIST-800-53-SI-7 + - NIST-800-53-SI-7(1) + - NIST-800-53-SI-7(6) + - PCI-DSS-Req-11.5 + - high_complexity + - high_severity + - medium_disruption + - no_reboot_needed + - restrict_strategy + - rpm_verify_ownership + +- name: Create list of packages + command: rpm -qf "{{ item }}" + args: + warn: false + with_items: '{{ files_with_incorrect_ownership.stdout_lines | map(''regex_findall'', + ''^[.]+[U|G]+.* (\/.*)'', ''\1'') | map(''join'') | select(''match'', ''(\/.*)'') + | list | unique }}' + register: list_of_packages + changed_when: false + check_mode: false + when: (files_with_incorrect_ownership.stdout_lines | length > 0) + tags: + - CCE-82196-7 + - CJIS-5.10.4.1 + - NIST-800-171-3.3.8 + - NIST-800-171-3.4.1 + - NIST-800-53-AU-9(3) + - NIST-800-53-CM-6(c) + - NIST-800-53-CM-6(d) + - NIST-800-53-SI-7 + - NIST-800-53-SI-7(1) + - NIST-800-53-SI-7(6) + - PCI-DSS-Req-11.5 + - high_complexity + - high_severity + - medium_disruption + - no_reboot_needed + - restrict_strategy + - rpm_verify_ownership + +- name: Correct file ownership with RPM + command: rpm --quiet --setugids '{{ item }}' + args: + warn: false + with_items: '{{ list_of_packages.results | map(attribute=''stdout_lines'') | list + | unique }}' + when: (files_with_incorrect_ownership.stdout_lines | length > 0) + tags: + - CCE-82196-7 + - CJIS-5.10.4.1 + - NIST-800-171-3.3.8 + - NIST-800-171-3.4.1 + - NIST-800-53-AU-9(3) + - NIST-800-53-CM-6(c) + - NIST-800-53-CM-6(d) + - NIST-800-53-SI-7 + - NIST-800-53-SI-7(1) + - NIST-800-53-SI-7(6) + - PCI-DSS-Req-11.5 + - high_complexity + - high_severity + - medium_disruption + - no_reboot_needed + - restrict_strategy + - rpm_verify_ownership + + +# Declare array to hold set of RPM packages we need to correct permissions for +declare -A SETPERMS_RPM_DICT + +# Create a list of files on the system having permissions different from what +# is expected by the RPM database +readarray -t FILES_WITH_INCORRECT_PERMS < <(rpm -Va --nofiledigest | awk '{ if (substr($0,6,1)=="U" || substr($0,7,1)=="G") print $NF }') + +for FILE_PATH in "${FILES_WITH_INCORRECT_PERMS[@]}" +do + RPM_PACKAGE=$(rpm -qf "$FILE_PATH") + # Use an associative array to store packages as it's keys, not having to care about duplicates. + SETPERMS_RPM_DICT["$RPM_PACKAGE"]=1 +done + +# For each of the RPM packages left in the list -- reset its permissions to the +# correct values +for RPM_PACKAGE in "${!SETPERMS_RPM_DICT[@]}" +do + rpm --setugids "${RPM_PACKAGE}" +done + + + + + + + + + + Verify and Correct File Permissions with RPM + The RPM package management system can check file access permissions +of installed software packages, including many that are important +to system security. +Verify that the file permissions of system files +and commands match vendor values. Check the file permissions +with the following command: +$ sudo rpm -Va | awk '{ if (substr($0,2,1)=="M") print $NF }' +Output indicates files that do not match vendor defaults. +After locating a file with incorrect permissions, +run the following command to determine which package owns it: +$ rpm -qf FILENAME + +Next, run the following command to reset its permissions to +the correct values: +$ sudo rpm --setperms PACKAGENAME + Profiles may require that specific files have stricter file permissions than defined by the +vendor. +Such files will be reported as a finding and need to be evaluated according to your policy +and deployment environment. + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + 6 + 9 + 5.10.4.1 + APO01.06 + APO11.04 + BAI03.05 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.04 + DSS05.07 + DSS06.02 + MEA02.01 + 3.3.8 + 3.4.1 + CCI-001493 + CCI-001494 + CCI-001495 + CCI-001496 + 164.308(a)(1)(ii)(D) + 164.312(b) + 164.312(c)(1) + 164.312(c)(2) + 164.312(e)(2)(i) + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.7.3 + 4.3.4.3.2 + 4.3.4.3.3 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.1 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 5.2 + SR 7.6 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.12.1.2 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.5.1 + A.12.6.2 + A.12.7.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R4.2 + CIP-003-8 R6 + CIP-007-3 R4 + CIP-007-3 R4.1 + CIP-007-3 R4.2 + CM-6(d) + CM-6(c) + SI-7 + SI-7(1) + SI-7(6) + AU-9(3) + CM-6(a) + PR.AC-4 + PR.DS-5 + PR.IP-1 + PR.PT-1 + Req-11.5 + SRG-OS-000256-GPOS-00097 + SRG-OS-000257-GPOS-00098 + SRG-OS-000258-GPOS-00099 + SRG-OS-000278-GPOS-00108 + 1.8.1.4 + 1.8.1.5 + 1.8.1.6 + 6.1.1 + 6.1.2 + 6.1.3 + 6.1.4 + 6.1.5 + 6.1.6 + 6.1.7 + 6.1.8 + 6.1.9 + Permissions on system binaries and configuration files that are too generous +could allow an unauthorized user to gain privileges that they should not have. +The permissions set by the vendor should be maintained. Any deviations from +this baseline should be investigated. + CCE-80858-4 + - name: Read list of files with incorrect permissions + command: rpm -Va --nodeps --nosignature --nofiledigest --nosize --nomtime --nordev + --nocaps --nolinkto --nouser --nogroup + args: + warn: false + register: files_with_incorrect_permissions + failed_when: files_with_incorrect_permissions.rc > 1 + changed_when: false + check_mode: false + tags: + - CCE-80858-4 + - CJIS-5.10.4.1 + - NIST-800-171-3.3.8 + - NIST-800-171-3.4.1 + - NIST-800-53-AU-9(3) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-6(c) + - NIST-800-53-CM-6(d) + - NIST-800-53-SI-7 + - NIST-800-53-SI-7(1) + - NIST-800-53-SI-7(6) + - PCI-DSS-Req-11.5 + - high_complexity + - high_severity + - medium_disruption + - no_reboot_needed + - restrict_strategy + - rpm_verify_permissions + +- name: Create list of packages + command: rpm -qf "{{ item }}" + args: + warn: false + with_items: '{{ files_with_incorrect_permissions.stdout_lines | map(''regex_findall'', + ''^[.]+[M]+.* (\/.*)'', ''\1'') | map(''join'') | select(''match'', ''(\/.*)'') + | list | unique }}' + register: list_of_packages + changed_when: false + check_mode: false + when: (files_with_incorrect_permissions.stdout_lines | length > 0) + tags: + - CCE-80858-4 + - CJIS-5.10.4.1 + - NIST-800-171-3.3.8 + - NIST-800-171-3.4.1 + - NIST-800-53-AU-9(3) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-6(c) + - NIST-800-53-CM-6(d) + - NIST-800-53-SI-7 + - NIST-800-53-SI-7(1) + - NIST-800-53-SI-7(6) + - PCI-DSS-Req-11.5 + - high_complexity + - high_severity + - medium_disruption + - no_reboot_needed + - restrict_strategy + - rpm_verify_permissions + +- name: Correct file permissions with RPM + command: rpm --setperms '{{ item }}' + args: + warn: false + with_items: '{{ list_of_packages.results | map(attribute=''stdout_lines'') | list + | unique }}' + when: (files_with_incorrect_permissions.stdout_lines | length > 0) + tags: + - CCE-80858-4 + - CJIS-5.10.4.1 + - NIST-800-171-3.3.8 + - NIST-800-171-3.4.1 + - NIST-800-53-AU-9(3) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-6(c) + - NIST-800-53-CM-6(d) + - NIST-800-53-SI-7 + - NIST-800-53-SI-7(1) + - NIST-800-53-SI-7(6) + - PCI-DSS-Req-11.5 + - high_complexity + - high_severity + - medium_disruption + - no_reboot_needed + - restrict_strategy + - rpm_verify_permissions + + +# Declare array to hold set of RPM packages we need to correct permissions for +declare -A SETPERMS_RPM_DICT + +# Create a list of files on the system having permissions different from what +# is expected by the RPM database +readarray -t FILES_WITH_INCORRECT_PERMS < <(rpm -Va --nofiledigest | awk '{ if (substr($0,2,1)=="M") print $NF }') + +for FILE_PATH in "${FILES_WITH_INCORRECT_PERMS[@]}" +do + # NOTE: some files maybe controlled by more then one package + readarray -t RPM_PACKAGES < <(rpm -qf "${FILE_PATH}") + for RPM_PACKAGE in "${RPM_PACKAGES[@]}" + do + # Use an associative array to store packages as it's keys, not having to care about duplicates. + SETPERMS_RPM_DICT["$RPM_PACKAGE"]=1 + done +done + +# For each of the RPM packages left in the list -- reset its permissions to the +# correct values +for RPM_PACKAGE in "${!SETPERMS_RPM_DICT[@]}" +do + rpm --restore "${RPM_PACKAGE}" +done + + + + + + + + + + + Verify Integrity with AIDE + AIDE conducts integrity checks by comparing information about +files with previously-gathered information. Ideally, the AIDE database is +created immediately after initial system configuration, and then again after any +software update. AIDE is highly configurable, with further configuration +information located in /usr/share/doc/aide-VERSION. + + + Install AIDE + The aide package can be installed with the following command: + +$ sudo yum install aide + BP28(R51) + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 2 + 3 + 5 + 7 + 8 + 9 + 5.10.1.3 + APO01.06 + BAI01.06 + BAI02.01 + BAI03.05 + BAI06.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.03 + DSS03.05 + DSS04.07 + DSS05.02 + DSS05.03 + DSS05.05 + DSS05.07 + DSS06.02 + DSS06.06 + CCI-002696 + CCI-002699 + CCI-001744 + 4.3.4.3.2 + 4.3.4.3.3 + 4.3.4.4.4 + SR 3.1 + SR 3.3 + SR 3.4 + SR 3.8 + SR 4.1 + SR 6.2 + SR 7.6 + 1034 + 1288 + 1341 + 1417 + A.11.2.4 + A.12.1.2 + A.12.2.1 + A.12.4.1 + A.12.5.1 + A.12.6.2 + A.14.1.2 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.14.2.7 + A.15.2.1 + A.8.2.3 + CM-6(a) + DE.CM-1 + DE.CM-7 + PR.DS-1 + PR.DS-6 + PR.DS-8 + PR.IP-1 + PR.IP-3 + Req-11.5 + SRG-OS-000363-GPOS-00150 + SRG-OS-000445-GPOS-00199 + RHEL-08-010359 + 1.3.1 + SV-251710r809354_rule + The AIDE package must be installed if it is to be available for integrity checking. + CCE-80844-4 + +package --add=aide + + include install_aide + +class install_aide { + package { 'aide': + ensure => 'installed', + } +} + + - name: Ensure aide is installed + package: + name: aide + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80844-4 + - CJIS-5.10.1.3 + - DISA-STIG-RHEL-08-010359 + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-11.5 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_aide_installed + + +[[packages]] +name = "aide" +version = "*" + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if ! rpm -q --quiet "aide" ; then + yum install -y "aide" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Build and Test AIDE Database + Run the following command to generate a new database: + +$ sudo /usr/sbin/aide --init + +By default, the database will be written to the file + +/var/lib/aide/aide.db.new.gz. + +Storing the database, the configuration file /etc/aide.conf, and the binary +/usr/sbin/aide +(or hashes of these files), in a secure location (such as on read-only media) provides additional assurance about their integrity. +The newly-generated database can be installed as follows: + +$ sudo cp /var/lib/aide/aide.db.new.gz /var/lib/aide/aide.db.gz + +To initiate a manual check, run the following command: +$ sudo /usr/sbin/aide --check +If this check produces any unexpected output, investigate. + BP28(R51) + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 2 + 3 + 5 + 7 + 8 + 9 + 5.10.1.3 + APO01.06 + BAI01.06 + BAI02.01 + BAI03.05 + BAI06.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.03 + DSS03.05 + DSS04.07 + DSS05.02 + DSS05.03 + DSS05.05 + DSS05.07 + DSS06.02 + DSS06.06 + 4.3.4.3.2 + 4.3.4.3.3 + 4.3.4.4.4 + SR 3.1 + SR 3.3 + SR 3.4 + SR 3.8 + SR 4.1 + SR 6.2 + SR 7.6 + A.11.2.4 + A.12.1.2 + A.12.2.1 + A.12.4.1 + A.12.5.1 + A.12.6.2 + A.14.1.2 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.14.2.7 + A.15.2.1 + A.8.2.3 + CM-6(a) + DE.CM-1 + DE.CM-7 + PR.DS-1 + PR.DS-6 + PR.DS-8 + PR.IP-1 + PR.IP-3 + Req-11.5 + 1.3.1 + For AIDE to be effective, an initial database of "known-good" information about files +must be captured and it should be able to be verified against the installed files. + CCE-80675-2 + - name: Ensure AIDE is installed + package: + name: '{{ item }}' + state: present + with_items: + - aide + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80675-2 + - CJIS-5.10.1.3 + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-11.5 + - aide_build_database + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Build and Test AIDE Database + command: /usr/sbin/aide --init + changed_when: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80675-2 + - CJIS-5.10.1.3 + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-11.5 + - aide_build_database + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Check whether the stock AIDE Database exists + stat: + path: /var/lib/aide/aide.db.new.gz + register: aide_database_stat + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80675-2 + - CJIS-5.10.1.3 + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-11.5 + - aide_build_database + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Stage AIDE Database + copy: + src: /var/lib/aide/aide.db.new.gz + dest: /var/lib/aide/aide.db.gz + backup: true + remote_src: true + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - (aide_database_stat.stat.exists is defined and aide_database_stat.stat.exists) + tags: + - CCE-80675-2 + - CJIS-5.10.1.3 + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-11.5 + - aide_build_database + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if ! rpm -q --quiet "aide" ; then + yum install -y "aide" +fi + +/usr/sbin/aide --init +/bin/cp -p /var/lib/aide/aide.db.new.gz /var/lib/aide/aide.db.gz + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure AIDE to Verify the Audit Tools + The operating system file integrity tool must be configured to protect the integrity of the audit tools. + CCI-001496 + AU-9(3) + AU-9(3).1 + SRG-OS-000278-GPOS-00108 + RHEL-08-030650 + SV-230475r833333_rule + Protecting the integrity of the tools used for auditing purposes is a +critical step toward ensuring the integrity of audit information. Audit +information includes all information (e.g., audit records, audit settings, +and audit reports) needed to successfully audit information system +activity. + +Audit tools include but are not limited to vendor-provided and open-source +audit tools needed to successfully view and manipulate audit information +system activity and records. Audit tools include custom queries and report +generators. + +It is not uncommon for attackers to replace the audit tools or inject code +into the existing tools to provide the capability to hide or erase system +activity from the audit logs. + +To address this risk, audit tools must be cryptographically signed to +provide the capability to identify when the audit tools have been modified, +manipulated, or replaced. An example is a checksum hash of the file or +files. + CCE-85964-5 + - name: Ensure aide is installed + package: + name: '{{ item }}' + state: present + with_items: + - aide + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-85964-5 + - DISA-STIG-RHEL-08-030650 + - NIST-800-53-AU-9(3) + - NIST-800-53-AU-9(3).1 + - aide_check_audit_tools + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Set audit_tools fact + set_fact: + audit_tools: + - /usr/sbin/auditctl + - /usr/sbin/auditd + - /usr/sbin/augenrules + - /usr/sbin/aureport + - /usr/sbin/ausearch + - /usr/sbin/autrace + - /usr/sbin/rsyslogd + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-85964-5 + - DISA-STIG-RHEL-08-030650 + - NIST-800-53-AU-9(3) + - NIST-800-53-AU-9(3).1 + - aide_check_audit_tools + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Ensure existing AIDE configuration for audit tools are correct + lineinfile: + path: /etc/aide.conf + regexp: ^{{ item }}\s + line: '{{ item }} p+i+n+u+g+s+b+acl+xattrs+sha512 ' + with_items: '{{ audit_tools }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-85964-5 + - DISA-STIG-RHEL-08-030650 + - NIST-800-53-AU-9(3) + - NIST-800-53-AU-9(3).1 + - aide_check_audit_tools + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Configure AIDE to properly protect audit tools + lineinfile: + path: /etc/aide.conf + line: '{{ item }} p+i+n+u+g+s+b+acl+xattrs+sha512 ' + with_items: '{{ audit_tools }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-85964-5 + - DISA-STIG-RHEL-08-030650 + - NIST-800-53-AU-9(3) + - NIST-800-53-AU-9(3).1 + - aide_check_audit_tools + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if ! rpm -q --quiet "aide" ; then + yum install -y "aide" +fi + + + + + + + + + + +if grep -i '^.*/usr/sbin/auditctl.*$' /etc/aide.conf; then +sed -i "s#.*/usr/sbin/auditctl.*#/usr/sbin/auditctl p+i+n+u+g+s+b+acl+xattrs+sha512 +#" /etc/aide.conf +else +echo "/usr/sbin/auditctl p+i+n+u+g+s+b+acl+xattrs+sha512 +" >> /etc/aide.conf +fi + +if grep -i '^.*/usr/sbin/auditd.*$' /etc/aide.conf; then +sed -i "s#.*/usr/sbin/auditd.*#/usr/sbin/auditd p+i+n+u+g+s+b+acl+xattrs+sha512 +#" /etc/aide.conf +else +echo "/usr/sbin/auditd p+i+n+u+g+s+b+acl+xattrs+sha512 +" >> /etc/aide.conf +fi + +if grep -i '^.*/usr/sbin/ausearch.*$' /etc/aide.conf; then +sed -i "s#.*/usr/sbin/ausearch.*#/usr/sbin/ausearch p+i+n+u+g+s+b+acl+xattrs+sha512 +#" /etc/aide.conf +else +echo "/usr/sbin/ausearch p+i+n+u+g+s+b+acl+xattrs+sha512 +" >> /etc/aide.conf +fi + +if grep -i '^.*/usr/sbin/aureport.*$' /etc/aide.conf; then +sed -i "s#.*/usr/sbin/aureport.*#/usr/sbin/aureport p+i+n+u+g+s+b+acl+xattrs+sha512 +#" /etc/aide.conf +else +echo "/usr/sbin/aureport p+i+n+u+g+s+b+acl+xattrs+sha512 +" >> /etc/aide.conf +fi + +if grep -i '^.*/usr/sbin/autrace.*$' /etc/aide.conf; then +sed -i "s#.*/usr/sbin/autrace.*#/usr/sbin/autrace p+i+n+u+g+s+b+acl+xattrs+sha512 +#" /etc/aide.conf +else +echo "/usr/sbin/autrace p+i+n+u+g+s+b+acl+xattrs+sha512 +" >> /etc/aide.conf +fi + +if grep -i '^.*/usr/sbin/augenrules.*$' /etc/aide.conf; then +sed -i "s#.*/usr/sbin/augenrules.*#/usr/sbin/augenrules p+i+n+u+g+s+b+acl+xattrs+sha512 +#" /etc/aide.conf +else +echo "/usr/sbin/augenrules p+i+n+u+g+s+b+acl+xattrs+sha512 +" >> /etc/aide.conf +fi + +if grep -i '^.*/usr/sbin/rsyslogd.*$' /etc/aide.conf; then +sed -i "s#.*/usr/sbin/rsyslogd.*#/usr/sbin/rsyslogd p+i+n+u+g+s+b+acl+xattrs+sha512 +#" /etc/aide.conf +else +echo "/usr/sbin/rsyslogd p+i+n+u+g+s+b+acl+xattrs+sha512 +" >> /etc/aide.conf +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure Periodic Execution of AIDE + At a minimum, AIDE should be configured to run a weekly scan. +To implement a daily execution of AIDE at 4:05am using cron, add the following line to /etc/crontab: +05 4 * * * root /usr/sbin/aide --check +To implement a weekly execution of AIDE at 4:05am using cron, add the following line to /etc/crontab: +05 4 * * 0 root /usr/sbin/aide --check +AIDE can be executed periodically through other means; this is merely one example. +The usage of cron's special time codes, such as @daily and +@weekly is acceptable. + BP28(R51) + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 2 + 3 + 5 + 7 + 8 + 9 + 5.10.1.3 + APO01.06 + BAI01.06 + BAI02.01 + BAI03.05 + BAI06.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.03 + DSS03.05 + DSS04.07 + DSS05.02 + DSS05.03 + DSS05.05 + DSS05.07 + DSS06.02 + DSS06.06 + CCI-001744 + CCI-002699 + CCI-002702 + 4.3.4.3.2 + 4.3.4.3.3 + 4.3.4.4.4 + SR 3.1 + SR 3.3 + SR 3.4 + SR 3.8 + SR 4.1 + SR 6.2 + SR 7.6 + A.11.2.4 + A.12.1.2 + A.12.2.1 + A.12.4.1 + A.12.5.1 + A.12.6.2 + A.14.1.2 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.14.2.7 + A.15.2.1 + A.8.2.3 + SI-7 + SI-7(1) + CM-6(a) + DE.CM-1 + DE.CM-7 + PR.DS-1 + PR.DS-6 + PR.DS-8 + PR.IP-1 + PR.IP-3 + Req-11.5 + SRG-OS-000363-GPOS-00150 + SRG-OS-000446-GPOS-00200 + SRG-OS-000447-GPOS-00201 + 1.3.2 + By default, AIDE does not install itself for periodic execution. Periodically +running AIDE is necessary to reveal unexpected changes in installed files. + +Unauthorized changes to the baseline configuration could make the system vulnerable +to various attacks or allow unauthorized access to the operating system. Changes to +operating system configurations can have unintended side effects, some of which may +be relevant to security. + +Detecting such changes and providing an automated response can help avoid unintended, +negative consequences that could ultimately affect the security state of the operating +system. The operating system's Information Management Officer (IMO)/Information System +Security Officer (ISSO) and System Administrators (SAs) must be notified via email and/or +monitoring system trap when there is an unauthorized modification of a configuration item. + CCE-80676-0 + - name: Ensure AIDE is installed + package: + name: '{{ item }}' + state: present + with_items: + - aide + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80676-0 + - CJIS-5.10.1.3 + - NIST-800-53-CM-6(a) + - NIST-800-53-SI-7 + - NIST-800-53-SI-7(1) + - PCI-DSS-Req-11.5 + - aide_periodic_cron_checking + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Set cron package name - RedHat + set_fact: + cron_pkg_name: cronie + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ansible_os_family == "RedHat" or ansible_os_family == "Suse" + tags: + - CCE-80676-0 + - CJIS-5.10.1.3 + - NIST-800-53-CM-6(a) + - NIST-800-53-SI-7 + - NIST-800-53-SI-7(1) + - PCI-DSS-Req-11.5 + - aide_periodic_cron_checking + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Set cron package name - Debian + set_fact: + cron_pkg_name: cron + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ansible_os_family == "Debian" + tags: + - CCE-80676-0 + - CJIS-5.10.1.3 + - NIST-800-53-CM-6(a) + - NIST-800-53-SI-7 + - NIST-800-53-SI-7(1) + - PCI-DSS-Req-11.5 + - aide_periodic_cron_checking + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Install cron + package: + name: '{{ cron_pkg_name }}' + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80676-0 + - CJIS-5.10.1.3 + - NIST-800-53-CM-6(a) + - NIST-800-53-SI-7 + - NIST-800-53-SI-7(1) + - PCI-DSS-Req-11.5 + - aide_periodic_cron_checking + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Configure Periodic Execution of AIDE + cron: + name: run AIDE check + minute: 5 + hour: 4 + weekday: 0 + user: root + job: /usr/sbin/aide --check + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80676-0 + - CJIS-5.10.1.3 + - NIST-800-53-CM-6(a) + - NIST-800-53-SI-7 + - NIST-800-53-SI-7(1) + - PCI-DSS-Req-11.5 + - aide_periodic_cron_checking + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if ! rpm -q --quiet "aide" ; then + yum install -y "aide" +fi + +if ! grep -q "/usr/sbin/aide --check" /etc/crontab ; then + echo "05 4 * * * root /usr/sbin/aide --check" >> /etc/crontab +else + sed -i '\!^.* --check.*$!d' /etc/crontab + echo "05 4 * * * root /usr/sbin/aide --check" >> /etc/crontab +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure Notification of Post-AIDE Scan Details + AIDE should notify appropriate personnel of the details of a scan after the scan has been run. +If AIDE has already been configured for periodic execution in /etc/crontab, append the +following line to the existing AIDE line: + | /bin/mail -s "$(hostname) - AIDE Integrity Check" root@localhost +Otherwise, add the following line to /etc/crontab: +05 4 * * * root /usr/sbin/aide --check | /bin/mail -s "$(hostname) - AIDE Integrity Check" root@localhost +AIDE can be executed periodically through other means; this is merely one example. + BP28(R51) + 1 + 11 + 12 + 13 + 15 + 16 + 2 + 3 + 5 + 7 + 8 + 9 + BAI01.06 + BAI06.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.03 + DSS03.05 + DSS05.02 + DSS05.05 + DSS05.07 + CCI-001744 + CCI-002699 + CCI-002702 + 4.3.4.3.2 + 4.3.4.3.3 + SR 6.2 + SR 7.6 + A.12.1.2 + A.12.4.1 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.14.2.7 + A.15.2.1 + CM-6(a) + CM-3(5) + DE.CM-1 + DE.CM-7 + PR.IP-1 + PR.IP-3 + SRG-OS-000363-GPOS-00150 + SRG-OS-000446-GPOS-00200 + SRG-OS-000447-GPOS-00201 + RHEL-08-010360 + SV-230263r627750_rule + Unauthorized changes to the baseline configuration could make the system vulnerable +to various attacks or allow unauthorized access to the operating system. Changes to +operating system configurations can have unintended side effects, some of which may +be relevant to security. + +Detecting such changes and providing an automated response can help avoid unintended, +negative consequences that could ultimately affect the security state of the operating +system. The operating system's Information Management Officer (IMO)/Information System +Security Officer (ISSO) and System Administrators (SAs) must be notified via email and/or +monitoring system trap when there is an unauthorized modification of a configuration item. + CCE-82891-3 + - name: XCCDF Value var_aide_scan_notification_email # promote to variable + set_fact: + var_aide_scan_notification_email: !!str + tags: + - always + +- name: Ensure AIDE is installed + package: + name: '{{ item }}' + state: present + with_items: + - aide + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82891-3 + - DISA-STIG-RHEL-08-010360 + - NIST-800-53-CM-3(5) + - NIST-800-53-CM-6(a) + - aide_scan_notification + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Configure Notification of Post-AIDE Scan Details + cron: + name: run AIDE check + minute: 5 + hour: 4 + weekday: 0 + user: root + job: /usr/sbin/aide --check | /bin/mail -s "$(hostname) - AIDE Integrity Check" + {{ var_aide_scan_notification_email }} + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82891-3 + - DISA-STIG-RHEL-08-010360 + - NIST-800-53-CM-3(5) + - NIST-800-53-CM-6(a) + - aide_scan_notification + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if ! rpm -q --quiet "aide" ; then + yum install -y "aide" +fi +var_aide_scan_notification_email='' + + +CRONTAB=/etc/crontab +CRONDIRS='/etc/cron.d /etc/cron.daily /etc/cron.weekly /etc/cron.monthly' + +# NOTE: on some platforms, /etc/crontab may not exist +if [ -f /etc/crontab ]; then + CRONTAB_EXIST=/etc/crontab +fi + +if [ -f /var/spool/cron/root ]; then + VARSPOOL=/var/spool/cron/root +fi + +if ! grep -qR '^.*/usr/sbin/aide\s*\-\-check.*|.*\/bin\/mail\s*-s\s*".*"\s*.*@.*$' $CRONTAB_EXIST $VARSPOOL $CRONDIRS; then + echo "0 5 * * * root /usr/sbin/aide --check | /bin/mail -s \"\$(hostname) - AIDE Integrity Check\" $var_aide_scan_notification_email" >> $CRONTAB +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure AIDE to Use FIPS 140-2 for Validating Hashes + By default, the sha512 option is added to the NORMAL ruleset in AIDE. +If using a custom ruleset or the sha512 option is missing, add sha512 +to the appropriate ruleset. +For example, add sha512 to the following line in /etc/aide.conf: +NORMAL = FIPSR+sha512 +AIDE rules can be configured in multiple ways; this is merely one example that is already +configured by default. + System Crypto Modules must be provided by a vendor that undergoes +FIPS-140 certifications. +FIPS-140 is applicable to all Federal agencies that use +cryptographic-based security systems to protect sensitive information +in computer and telecommunication systems (including voice systems) as +defined in Section 5131 of the Information Technology Management Reform +Act of 1996, Public Law 104-106. This standard shall be used in +designing and implementing cryptographic modules that Federal +departments and agencies operate or are operated for them under +contract. See https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.140-2.pdf +To meet this, the system has to have cryptographic software provided by +a vendor that has undergone this certification. This means providing +documentation, test results, design information, and independent third +party review by an accredited lab. While open source software is +capable of meeting this, it does not meet FIPS-140 unless the vendor +submits to this process. + 2 + 3 + APO01.06 + BAI03.05 + BAI06.01 + DSS06.02 + 3.13.11 + CCI-000366 + 4.3.4.4.4 + SR 3.1 + SR 3.3 + SR 3.4 + SR 3.8 + A.11.2.4 + A.12.2.1 + A.12.5.1 + A.14.1.2 + A.14.1.3 + A.14.2.4 + SI-7 + SI-7(1) + CM-6(a) + PR.DS-6 + PR.DS-8 + SRG-OS-000480-GPOS-00227 + File integrity tools use cryptographic hashes for verifying file contents and directories +have not been altered. These hashes must be FIPS 140-2 approved cryptographic hashes. + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if ! rpm -q --quiet "aide" ; then + yum install -y "aide" +fi + +aide_conf="/etc/aide.conf" +forbidden_hashes=(sha1 rmd160 sha256 whirlpool tiger haval gost crc32) + +groups=$(LC_ALL=C grep "^[A-Z][A-Za-z_]*" $aide_conf | cut -f1 -d ' ' | tr -d ' ' | sort -u) + +for group in $groups +do + config=$(grep "^$group\s*=" $aide_conf | cut -f2 -d '=' | tr -d ' ') + + if ! [[ $config = *sha512* ]] + then + config=$config"+sha512" + fi + + for hash in "${forbidden_hashes[@]}" + do + config=$(echo $config | sed "s/$hash//") + done + + config=$(echo $config | sed "s/^\+*//") + config=$(echo $config | sed "s/\+\++/+/") + config=$(echo $config | sed "s/\+$//") + + sed -i "s/^$group\s*=.*/$group = $config/g" $aide_conf +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure AIDE to Verify Access Control Lists (ACLs) + By default, the acl option is added to the FIPSR ruleset in AIDE. +If using a custom ruleset or the acl option is missing, add acl +to the appropriate ruleset. +For example, add acl to the following line in /etc/aide.conf: +FIPSR = p+i+n+u+g+s+m+c+acl+selinux+xattrs+sha256 +AIDE rules can be configured in multiple ways; this is merely one example that is already +configured by default. + +The remediation provided with this rule adds acl to all rule sets available in +/etc/aide.conf + BP28(R51) + 2 + 3 + APO01.06 + BAI03.05 + BAI06.01 + DSS06.02 + CCI-000366 + 4.3.4.4.4 + SR 3.1 + SR 3.3 + SR 3.4 + SR 3.8 + A.11.2.4 + A.12.2.1 + A.12.5.1 + A.14.1.2 + A.14.1.3 + A.14.2.4 + SI-7 + SI-7(1) + CM-6(a) + PR.DS-6 + PR.DS-8 + SRG-OS-000480-GPOS-00227 + RHEL-08-040310 + SV-230552r627750_rule + ACLs can provide permissions beyond those permitted through the file mode and must be +verified by the file integrity tools. + CCE-84220-3 + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if ! rpm -q --quiet "aide" ; then + yum install -y "aide" +fi + +aide_conf="/etc/aide.conf" + +groups=$(LC_ALL=C grep "^[A-Z][A-Za-z_]*" $aide_conf | grep -v "^ALLXTRAHASHES" | cut -f1 -d '=' | tr -d ' ' | sort -u) + +for group in $groups +do + config=$(grep "^$group\s*=" $aide_conf | cut -f2 -d '=' | tr -d ' ') + + if ! [[ $config = *acl* ]] + then + if [[ -z $config ]] + then + config="acl" + else + config=$config"+acl" + fi + fi + sed -i "s/^$group\s*=.*/$group = $config/g" $aide_conf +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure AIDE to Verify Extended Attributes + By default, the xattrs option is added to the FIPSR ruleset in AIDE. +If using a custom ruleset or the xattrs option is missing, add xattrs +to the appropriate ruleset. +For example, add xattrs to the following line in /etc/aide.conf: +FIPSR = p+i+n+u+g+s+m+c+acl+selinux+xattrs+sha256 +AIDE rules can be configured in multiple ways; this is merely one example that is already +configured by default. + +The remediation provided with this rule adds xattrs to all rule sets available in +/etc/aide.conf + BP28(R51) + 2 + 3 + APO01.06 + BAI03.05 + BAI06.01 + DSS06.02 + CCI-000366 + 4.3.4.4.4 + SR 3.1 + SR 3.3 + SR 3.4 + SR 3.8 + A.11.2.4 + A.12.2.1 + A.12.5.1 + A.14.1.2 + A.14.1.3 + A.14.2.4 + SI-7 + SI-7(1) + CM-6(a) + PR.DS-6 + PR.DS-8 + SRG-OS-000480-GPOS-00227 + RHEL-08-040300 + SV-230551r627750_rule + Extended attributes in file systems are used to contain arbitrary data and file metadata +with security implications. + CCE-83733-6 + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if ! rpm -q --quiet "aide" ; then + yum install -y "aide" +fi + +aide_conf="/etc/aide.conf" + +groups=$(LC_ALL=C grep "^[A-Z][A-Za-z_]*" $aide_conf | grep -v "^ALLXTRAHASHES" | cut -f1 -d '=' | tr -d ' ' | sort -u) + +for group in $groups +do + config=$(grep "^$group\s*=" $aide_conf | cut -f2 -d '=' | tr -d ' ') + + if ! [[ $config = *xattrs* ]] + then + if [[ -z $config ]] + then + config="xattrs" + else + config=$config"+xattrs" + fi + fi + sed -i "s/^$group\s*=.*/$group = $config/g" $aide_conf +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Audit Tools Must Be Group-owned by Root + Red Hat Enterprise Linux 8 systems providing tools to interface with audit information will leverage user permissions and roles identifying the user accessing the tools, and the corresponding rights the user enjoys, to make access decisions regarding the access to audit tools. + +Audit tools include, but are not limited to, vendor-provided and open source audit tools needed to successfully view and manipulate audit information system activity and records. Audit tools include custom queries and report generators. + +Audit tools must have the correct group owner. + CCI-001493 + CCI-001494 + CCI-001495 + AU-9 + SRG-OS-000256-GPOS-00097 + SRG-OS-000257-GPOS-00098 + SRG-OS-000258-GPOS-00099 + RHEL-08-030640 + SV-230474r627750_rule + Protecting audit information also includes identifying and protecting the tools used to view and manipulate log data. +Therefore, protecting audit tools is necessary to prevent unauthorized operations on audit information. + CCE-86239-1 + - name: Test for existence /sbin/auditctl + stat: + path: /sbin/auditctl + register: file_exists + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86239-1 + - DISA-STIG-RHEL-08-030640 + - NIST-800-53-AU-9 + - configure_strategy + - file_audit_tools_group_ownership + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure group owner 0 on /sbin/auditctl + file: + path: /sbin/auditctl + group: '0' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-86239-1 + - DISA-STIG-RHEL-08-030640 + - NIST-800-53-AU-9 + - configure_strategy + - file_audit_tools_group_ownership + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Test for existence /sbin/aureport + stat: + path: /sbin/aureport + register: file_exists + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86239-1 + - DISA-STIG-RHEL-08-030640 + - NIST-800-53-AU-9 + - configure_strategy + - file_audit_tools_group_ownership + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure group owner 0 on /sbin/aureport + file: + path: /sbin/aureport + group: '0' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-86239-1 + - DISA-STIG-RHEL-08-030640 + - NIST-800-53-AU-9 + - configure_strategy + - file_audit_tools_group_ownership + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Test for existence /sbin/ausearch + stat: + path: /sbin/ausearch + register: file_exists + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86239-1 + - DISA-STIG-RHEL-08-030640 + - NIST-800-53-AU-9 + - configure_strategy + - file_audit_tools_group_ownership + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure group owner 0 on /sbin/ausearch + file: + path: /sbin/ausearch + group: '0' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-86239-1 + - DISA-STIG-RHEL-08-030640 + - NIST-800-53-AU-9 + - configure_strategy + - file_audit_tools_group_ownership + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Test for existence /sbin/autrace + stat: + path: /sbin/autrace + register: file_exists + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86239-1 + - DISA-STIG-RHEL-08-030640 + - NIST-800-53-AU-9 + - configure_strategy + - file_audit_tools_group_ownership + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure group owner 0 on /sbin/autrace + file: + path: /sbin/autrace + group: '0' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-86239-1 + - DISA-STIG-RHEL-08-030640 + - NIST-800-53-AU-9 + - configure_strategy + - file_audit_tools_group_ownership + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Test for existence /sbin/auditd + stat: + path: /sbin/auditd + register: file_exists + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86239-1 + - DISA-STIG-RHEL-08-030640 + - NIST-800-53-AU-9 + - configure_strategy + - file_audit_tools_group_ownership + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure group owner 0 on /sbin/auditd + file: + path: /sbin/auditd + group: '0' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-86239-1 + - DISA-STIG-RHEL-08-030640 + - NIST-800-53-AU-9 + - configure_strategy + - file_audit_tools_group_ownership + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Test for existence /sbin/rsyslogd + stat: + path: /sbin/rsyslogd + register: file_exists + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86239-1 + - DISA-STIG-RHEL-08-030640 + - NIST-800-53-AU-9 + - configure_strategy + - file_audit_tools_group_ownership + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure group owner 0 on /sbin/rsyslogd + file: + path: /sbin/rsyslogd + group: '0' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-86239-1 + - DISA-STIG-RHEL-08-030640 + - NIST-800-53-AU-9 + - configure_strategy + - file_audit_tools_group_ownership + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Test for existence /sbin/augenrules + stat: + path: /sbin/augenrules + register: file_exists + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86239-1 + - DISA-STIG-RHEL-08-030640 + - NIST-800-53-AU-9 + - configure_strategy + - file_audit_tools_group_ownership + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure group owner 0 on /sbin/augenrules + file: + path: /sbin/augenrules + group: '0' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-86239-1 + - DISA-STIG-RHEL-08-030640 + - NIST-800-53-AU-9 + - configure_strategy + - file_audit_tools_group_ownership + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +chgrp 0 /sbin/auditctl + +chgrp 0 /sbin/aureport + +chgrp 0 /sbin/ausearch + +chgrp 0 /sbin/autrace + +chgrp 0 /sbin/auditd + +chgrp 0 /sbin/rsyslogd + +chgrp 0 /sbin/augenrules + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Audit Tools Must Be Owned by Root + Red Hat Enterprise Linux 8 systems providing tools to interface with audit information will leverage user permissions and roles identifying the user accessing the tools, and the corresponding rights the user enjoys, to make access decisions regarding the access to audit tools. + +Audit tools include, but are not limited to, vendor-provided and open source audit tools needed to successfully view and manipulate audit information system activity and records. Audit tools include custom queries and report generators. + +Audit tools must have the correct owner. + CCI-001493 + CCI-001494 + CCI-001495 + AU-9 + SRG-OS-000256-GPOS-00097 + SRG-OS-000257-GPOS-00098 + SRG-OS-000258-GPOS-00099 + RHEL-08-030630 + SV-230473r744008_rule + Protecting audit information also includes identifying and protecting the tools used to view and manipulate log data. +Therefore, protecting audit tools is necessary to prevent unauthorized operations on audit information. + CCE-86259-9 + - name: Test for existence /sbin/auditctl + stat: + path: /sbin/auditctl + register: file_exists + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86259-9 + - DISA-STIG-RHEL-08-030630 + - NIST-800-53-AU-9 + - configure_strategy + - file_audit_tools_ownership + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure owner 0 on /sbin/auditctl + file: + path: /sbin/auditctl + owner: '0' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-86259-9 + - DISA-STIG-RHEL-08-030630 + - NIST-800-53-AU-9 + - configure_strategy + - file_audit_tools_ownership + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Test for existence /sbin/aureport + stat: + path: /sbin/aureport + register: file_exists + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86259-9 + - DISA-STIG-RHEL-08-030630 + - NIST-800-53-AU-9 + - configure_strategy + - file_audit_tools_ownership + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure owner 0 on /sbin/aureport + file: + path: /sbin/aureport + owner: '0' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-86259-9 + - DISA-STIG-RHEL-08-030630 + - NIST-800-53-AU-9 + - configure_strategy + - file_audit_tools_ownership + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Test for existence /sbin/ausearch + stat: + path: /sbin/ausearch + register: file_exists + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86259-9 + - DISA-STIG-RHEL-08-030630 + - NIST-800-53-AU-9 + - configure_strategy + - file_audit_tools_ownership + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure owner 0 on /sbin/ausearch + file: + path: /sbin/ausearch + owner: '0' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-86259-9 + - DISA-STIG-RHEL-08-030630 + - NIST-800-53-AU-9 + - configure_strategy + - file_audit_tools_ownership + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Test for existence /sbin/autrace + stat: + path: /sbin/autrace + register: file_exists + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86259-9 + - DISA-STIG-RHEL-08-030630 + - NIST-800-53-AU-9 + - configure_strategy + - file_audit_tools_ownership + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure owner 0 on /sbin/autrace + file: + path: /sbin/autrace + owner: '0' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-86259-9 + - DISA-STIG-RHEL-08-030630 + - NIST-800-53-AU-9 + - configure_strategy + - file_audit_tools_ownership + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Test for existence /sbin/auditd + stat: + path: /sbin/auditd + register: file_exists + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86259-9 + - DISA-STIG-RHEL-08-030630 + - NIST-800-53-AU-9 + - configure_strategy + - file_audit_tools_ownership + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure owner 0 on /sbin/auditd + file: + path: /sbin/auditd + owner: '0' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-86259-9 + - DISA-STIG-RHEL-08-030630 + - NIST-800-53-AU-9 + - configure_strategy + - file_audit_tools_ownership + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Test for existence /sbin/rsyslogd + stat: + path: /sbin/rsyslogd + register: file_exists + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86259-9 + - DISA-STIG-RHEL-08-030630 + - NIST-800-53-AU-9 + - configure_strategy + - file_audit_tools_ownership + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure owner 0 on /sbin/rsyslogd + file: + path: /sbin/rsyslogd + owner: '0' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-86259-9 + - DISA-STIG-RHEL-08-030630 + - NIST-800-53-AU-9 + - configure_strategy + - file_audit_tools_ownership + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Test for existence /sbin/augenrules + stat: + path: /sbin/augenrules + register: file_exists + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86259-9 + - DISA-STIG-RHEL-08-030630 + - NIST-800-53-AU-9 + - configure_strategy + - file_audit_tools_ownership + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure owner 0 on /sbin/augenrules + file: + path: /sbin/augenrules + owner: '0' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-86259-9 + - DISA-STIG-RHEL-08-030630 + - NIST-800-53-AU-9 + - configure_strategy + - file_audit_tools_ownership + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +chown 0 /sbin/auditctl + +chown 0 /sbin/aureport + +chown 0 /sbin/ausearch + +chown 0 /sbin/autrace + +chown 0 /sbin/auditd + +chown 0 /sbin/rsyslogd + +chown 0 /sbin/augenrules + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Audit Tools Must Have a Mode of 0755 or Less Permissive + Red Hat Enterprise Linux 8 systems providing tools to interface with audit information will leverage user permissions and roles identifying the user accessing the tools, and the corresponding rights the user enjoys, to make access decisions regarding the access to audit tools. + +Audit tools include, but are not limited to, vendor-provided and open source audit tools needed to successfully view and manipulate audit information system activity and records. Audit tools include custom queries and report generators. + +Audit tools must have a mode of 0755 or less permissive. + CCI-001493 + AU-9 + SRG-OS-000256-GPOS-00097 + SRG-OS-000257-GPOS-00098 + SRG-OS-000258-GPOS-00099 + RHEL-08-030620 + SV-230472r627750_rule + Protecting audit information also includes identifying and protecting the tools used to view and manipulate log data. +Therefore, protecting audit tools is necessary to prevent unauthorized operations on audit information. + CCE-86227-6 + - name: Test for existence /sbin/auditctl + stat: + path: /sbin/auditctl + register: file_exists + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86227-6 + - DISA-STIG-RHEL-08-030620 + - NIST-800-53-AU-9 + - configure_strategy + - file_audit_tools_permissions + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure permission u-s,g-ws,o-wt on /sbin/auditctl + file: + path: /sbin/auditctl + mode: u-s,g-ws,o-wt + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-86227-6 + - DISA-STIG-RHEL-08-030620 + - NIST-800-53-AU-9 + - configure_strategy + - file_audit_tools_permissions + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Test for existence /sbin/aureport + stat: + path: /sbin/aureport + register: file_exists + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86227-6 + - DISA-STIG-RHEL-08-030620 + - NIST-800-53-AU-9 + - configure_strategy + - file_audit_tools_permissions + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure permission u-s,g-ws,o-wt on /sbin/aureport + file: + path: /sbin/aureport + mode: u-s,g-ws,o-wt + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-86227-6 + - DISA-STIG-RHEL-08-030620 + - NIST-800-53-AU-9 + - configure_strategy + - file_audit_tools_permissions + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Test for existence /sbin/ausearch + stat: + path: /sbin/ausearch + register: file_exists + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86227-6 + - DISA-STIG-RHEL-08-030620 + - NIST-800-53-AU-9 + - configure_strategy + - file_audit_tools_permissions + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure permission u-s,g-ws,o-wt on /sbin/ausearch + file: + path: /sbin/ausearch + mode: u-s,g-ws,o-wt + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-86227-6 + - DISA-STIG-RHEL-08-030620 + - NIST-800-53-AU-9 + - configure_strategy + - file_audit_tools_permissions + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Test for existence /sbin/autrace + stat: + path: /sbin/autrace + register: file_exists + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86227-6 + - DISA-STIG-RHEL-08-030620 + - NIST-800-53-AU-9 + - configure_strategy + - file_audit_tools_permissions + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure permission u-s,g-ws,o-wt on /sbin/autrace + file: + path: /sbin/autrace + mode: u-s,g-ws,o-wt + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-86227-6 + - DISA-STIG-RHEL-08-030620 + - NIST-800-53-AU-9 + - configure_strategy + - file_audit_tools_permissions + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Test for existence /sbin/auditd + stat: + path: /sbin/auditd + register: file_exists + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86227-6 + - DISA-STIG-RHEL-08-030620 + - NIST-800-53-AU-9 + - configure_strategy + - file_audit_tools_permissions + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure permission u-s,g-ws,o-wt on /sbin/auditd + file: + path: /sbin/auditd + mode: u-s,g-ws,o-wt + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-86227-6 + - DISA-STIG-RHEL-08-030620 + - NIST-800-53-AU-9 + - configure_strategy + - file_audit_tools_permissions + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Test for existence /sbin/rsyslogd + stat: + path: /sbin/rsyslogd + register: file_exists + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86227-6 + - DISA-STIG-RHEL-08-030620 + - NIST-800-53-AU-9 + - configure_strategy + - file_audit_tools_permissions + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure permission u-s,g-ws,o-wt on /sbin/rsyslogd + file: + path: /sbin/rsyslogd + mode: u-s,g-ws,o-wt + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-86227-6 + - DISA-STIG-RHEL-08-030620 + - NIST-800-53-AU-9 + - configure_strategy + - file_audit_tools_permissions + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Test for existence /sbin/augenrules + stat: + path: /sbin/augenrules + register: file_exists + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86227-6 + - DISA-STIG-RHEL-08-030620 + - NIST-800-53-AU-9 + - configure_strategy + - file_audit_tools_permissions + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure permission u-s,g-ws,o-wt on /sbin/augenrules + file: + path: /sbin/augenrules + mode: u-s,g-ws,o-wt + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-86227-6 + - DISA-STIG-RHEL-08-030620 + - NIST-800-53-AU-9 + - configure_strategy + - file_audit_tools_permissions + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +chmod u-s,g-ws,o-wt /sbin/auditctl + +chmod u-s,g-ws,o-wt /sbin/aureport + +chmod u-s,g-ws,o-wt /sbin/ausearch + +chmod u-s,g-ws,o-wt /sbin/autrace + +chmod u-s,g-ws,o-wt /sbin/auditd + +chmod u-s,g-ws,o-wt /sbin/rsyslogd + +chmod u-s,g-ws,o-wt /sbin/augenrules + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + + Federal Information Processing Standard (FIPS) + The Federal Information Processing Standard (FIPS) is a computer security standard which +is developed by the U.S. Government and industry working groups to validate the quality +of cryptographic modules. The FIPS standard provides four security levels to ensure +adequate coverage of different industries, implementation of cryptographic modules, and +organizational sizes and requirements. + +FIPS 140-2 is the current standard for validating that mechanisms used to access cryptographic modules +utilize authentication that meets industry and government requirements. For government systems, this allows +Security Levels 1, 2, 3, or 4 for use on Red Hat Enterprise Linux 8. + +See http://csrc.nist.gov/publications/PubsFIPS.html for more information. + + + Enable Dracut FIPS Module + To enable FIPS mode, run the following command: +fips-mode-setup --enable +To enable FIPS, the system requires that the fips module is added in dracut configuration. +Check if /etc/dracut.conf.d/40-fips.conf contain add_dracutmodules+=" fips " + The system needs to be rebooted for these changes to take effect. + System Crypto Modules must be provided by a vendor that undergoes FIPS-140 certifications. +FIPS-140 is applicable to all Federal agencies that use cryptographic-based security +systems to protect sensitive information in computer and telecommunication systems +(including voice systems) as defined in Section 5131 of the Information Technology +Management Reform Act of 1996, Public Law 104-106. This standard shall be used in designing +and implementing cryptographic modules that Federal departments and agencies operate or are +operated for them under contract. +See https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.140-2.pdf +To meet this, the system has to have cryptographic software provided by a vendor that has +undergone this certification. This means providing documentation, test results, design +information, and independent third party review by an accredited lab. While open source +software is capable of meeting this, it does not meet FIPS-140 unless the vendor submits to +this process. + CCI-000068 + CCI-000803 + CCI-002450 + 1446 + CIP-003-8 R4.2 + CIP-007-3 R5.1 + SC-12(2) + SC-12(3) + IA-7 + SC-13 + CM-6(a) + SC-12 + FCS_RBG_EXT.1 + SRG-OS-000478-GPOS-00223 + SRG-OS-000120-VMM-000600 + SRG-OS-000478-VMM-001980 + SRG-OS-000396-VMM-001590 + RHEL-08-010020 + SV-230223r792855_rule + Use of weak or untested encryption algorithms undermines the purposes of utilizing encryption to +protect data. The operating system must implement cryptographic modules adhering to the higher +standards approved by the federal government since this provides assurance they have been tested +and validated. + CCE-82155-3 + - name: Check to see the current status of FIPS mode + command: /usr/bin/fips-mode-setup --check + register: is_fips_enabled + changed_when: false + failed_when: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82155-3 + - DISA-STIG-RHEL-08-010020 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-7 + - NIST-800-53-SC-12 + - NIST-800-53-SC-12(2) + - NIST-800-53-SC-12(3) + - NIST-800-53-SC-13 + - enable_dracut_fips_module + - high_severity + - medium_complexity + - medium_disruption + - reboot_required + - restrict_strategy + +- name: Enable FIPS mode + command: /usr/bin/fips-mode-setup --enable + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - is_fips_enabled.stdout.find('FIPS mode is enabled.') == -1 + tags: + - CCE-82155-3 + - DISA-STIG-RHEL-08-010020 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-7 + - NIST-800-53-SC-12 + - NIST-800-53-SC-12(2) + - NIST-800-53-SC-12(3) + - NIST-800-53-SC-13 + - enable_dracut_fips_module + - high_severity + - medium_complexity + - medium_disruption + - reboot_required + - restrict_strategy + +- name: Enable Dracut FIPS Module + lineinfile: + path: /etc/dracut.conf.d/40-fips.conf + line: add_dracutmodules+=" fips " + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82155-3 + - DISA-STIG-RHEL-08-010020 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-7 + - NIST-800-53-SC-12 + - NIST-800-53-SC-12(2) + - NIST-800-53-SC-12(3) + - NIST-800-53-SC-13 + - enable_dracut_fips_module + - high_severity + - medium_complexity + - medium_disruption + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +fips-mode-setup --enable +FIPS_CONF="/etc/dracut.conf.d/40-fips.conf" +if ! grep "^add_dracutmodules+=\" fips \"" $FIPS_CONF; then + echo "add_dracutmodules+=\" fips \"" >> $FIPS_CONF +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Enable FIPS Mode + To enable FIPS mode, run the following command: +fips-mode-setup --enable + +The fips-mode-setup command will configure the system in +FIPS mode by automatically configuring the following: +Setting the kernel FIPS mode flag (/proc/sys/crypto/fips_enabled) to 1Creating /etc/system-fipsSetting the system crypto policy in /etc/crypto-policies/config to Loading the Dracut fips module + The system needs to be rebooted for these changes to take effect. + This rule DOES NOT CHECK if the components of the operating system are FIPS certified. +You can find the list of FIPS certified modules at +https://csrc.nist.gov/projects/cryptographic-module-validation-program/validated-modules/search. +This rule checks if the system is running in FIPS mode. See the rule description for more information about what it means. + CCI-000068 + CCI-000803 + CCI-002450 + 1446 + CIP-003-8 R4.2 + CIP-007-3 R5.1 + CM-3(6) + SC-12(2) + SC-12(3) + IA-7 + SC-13 + CM-6(a) + SC-12 + FCS_COP.1(1) + FCS_COP.1(2) + FCS_COP.1(3) + FCS_COP.1(4) + FCS_CKM.1 + FCS_CKM.2 + FCS_TLSC_EXT.1 + FCS_RBG_EXT.1 + SRG-OS-000478-GPOS-00223 + SRG-OS-000396-GPOS-00176 + SRG-OS-000120-VMM-000600 + SRG-OS-000478-VMM-001980 + SRG-OS-000396-VMM-001590 + RHEL-08-010020 + SV-230223r792855_rule + Use of weak or untested encryption algorithms undermines the purposes of utilizing encryption to +protect data. The operating system must implement cryptographic modules adhering to the higher +standards approved by the federal government since this provides assurance they have been tested +and validated. + + CCE-80942-6 + - name: XCCDF Value var_system_crypto_policy # promote to variable + set_fact: + var_system_crypto_policy: !!str + tags: + - always + +- name: Check to see the current status of FIPS mode + command: /usr/bin/fips-mode-setup --check + register: is_fips_enabled + changed_when: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80942-6 + - DISA-STIG-RHEL-08-010020 + - NIST-800-53-CM-3(6) + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-7 + - NIST-800-53-SC-12 + - NIST-800-53-SC-12(2) + - NIST-800-53-SC-12(3) + - NIST-800-53-SC-13 + - enable_fips_mode + - high_severity + - medium_complexity + - medium_disruption + - reboot_required + - restrict_strategy + +- name: Enable FIPS mode + command: /usr/bin/fips-mode-setup --enable + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - is_fips_enabled.stdout.find('FIPS mode is enabled.') == -1 + tags: + - CCE-80942-6 + - DISA-STIG-RHEL-08-010020 + - NIST-800-53-CM-3(6) + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-7 + - NIST-800-53-SC-12 + - NIST-800-53-SC-12(2) + - NIST-800-53-SC-12(3) + - NIST-800-53-SC-13 + - enable_fips_mode + - high_severity + - medium_complexity + - medium_disruption + - reboot_required + - restrict_strategy + +- name: Enable FIPS Mode + lineinfile: + path: /etc/crypto-policies/config + regexp: ^(?!#)(\S+)$ + line: '{{ var_system_crypto_policy }}' + create: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80942-6 + - DISA-STIG-RHEL-08-010020 + - NIST-800-53-CM-3(6) + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-7 + - NIST-800-53-SC-12 + - NIST-800-53-SC-12(2) + - NIST-800-53-SC-12(3) + - NIST-800-53-SC-13 + - enable_fips_mode + - high_severity + - medium_complexity + - medium_disruption + - reboot_required + - restrict_strategy + +- name: Verify that Crypto Policy is Set (runtime) + command: /usr/bin/update-crypto-policies --set {{ var_system_crypto_policy }} + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80942-6 + - DISA-STIG-RHEL-08-010020 + - NIST-800-53-CM-3(6) + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-7 + - NIST-800-53-SC-12 + - NIST-800-53-SC-12(2) + - NIST-800-53-SC-12(3) + - NIST-800-53-SC-13 + - enable_fips_mode + - high_severity + - medium_complexity + - medium_disruption + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_system_crypto_policy='' + + +fips-mode-setup --enable + +stderr_of_call=$(update-crypto-policies --set ${var_system_crypto_policy} 2>&1 > /dev/null) +rc=$? + +if test "$rc" = 127; then + echo "$stderr_of_call" >&2 + echo "Make sure that the script is installed on the remediated system." >&2 + echo "See output of the 'dnf provides update-crypto-policies' command" >&2 + echo "to see what package to (re)install" >&2 + + false # end with an error code +elif test "$rc" != 0; then + echo "Error invoking the update-crypto-policies script: $stderr_of_call" >&2 + false # end with an error code +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Ensure '/etc/system-fips' exists + On a system where FIPS mode is enabled, /etc/system-fips must exist. +To enable FIPS mode, run the following command: +fips-mode-setup --enable + The system needs to be rebooted for these changes to take effect. + System Crypto Modules must be provided by a vendor that undergoes +FIPS-140 certifications. +FIPS-140 is applicable to all Federal agencies that use +cryptographic-based security systems to protect sensitive information +in computer and telecommunication systems (including voice systems) as +defined in Section 5131 of the Information Technology Management Reform +Act of 1996, Public Law 104-106. This standard shall be used in +designing and implementing cryptographic modules that Federal +departments and agencies operate or are operated for them under +contract. See https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.140-2.pdf +To meet this, the system has to have cryptographic software provided by +a vendor that has undergone this certification. This means providing +documentation, test results, design information, and independent third +party review by an accredited lab. While open source software is +capable of meeting this, it does not meet FIPS-140 unless the vendor +submits to this process. + CCI-000068 + CCI-000803 + CCI-002450 + CIP-003-8 R4.2 + CIP-007-3 R5.1 + SC-12(2) + SC-12(3) + IA-7 + SC-13 + CM-6(a) + SC-12 + SRG-OS-000120-VMM-000600 + SRG-OS-000478-VMM-001980 + SRG-OS-000396-VMM-001590 + Use of weak or untested encryption algorithms undermines the purposes of utilizing encryption to +protect data. The operating system must implement cryptographic modules adhering to the higher +standards approved by the federal government since this provides assurance they have been tested +and validated. + + + + + + + + + Set kernel parameter 'crypto.fips_enabled' to 1 + System running in FIPS mode is indicated by kernel parameter +'crypto.fips_enabled'. This parameter should be set to 1 in FIPS mode. +To enable FIPS mode, run the following command: +fips-mode-setup --enable + +To enable strict FIPS compliance, the fips=1 kernel option needs to be added to the kernel boot +parameters during system installation so key generation is done with FIPS-approved algorithms +and continuous monitoring tests in place. + The system needs to be rebooted for these changes to take effect. + System Crypto Modules must be provided by a vendor that undergoes FIPS-140 certifications. +FIPS-140 is applicable to all Federal agencies that use cryptographic-based security +systems to protect sensitive information in computer and telecommunication systems +(including voice systems) as defined in Section 5131 of the Information Technology +Management Reform Act of 1996, Public Law 104-106. This standard shall be used in designing +and implementing cryptographic modules that Federal departments and agencies operate or are +operated for them under contract. +See https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.140-2.pdf +To meet this, the system has to have cryptographic software provided by a vendor that has +undergone this certification. This means providing documentation, test results, design +information, and independent third party review by an accredited lab. While open source +software is capable of meeting this, it does not meet FIPS-140 unless the vendor submits to +this process. + CCI-000068 + CCI-000803 + CCI-000877 + CCI-001453 + CCI-002418 + CCI-002450 + CCI-002890 + CCI-003123 + CIP-003-8 R4.2 + CIP-007-3 R5.1 + SC-12(2) + SC-12(3) + IA-7 + SC-13 + CM-6(a) + SC-12 + SRG-OS-000033-GPOS-00014 + SRG-OS-000125-GPOS-00065 + SRG-OS-000250-GPOS-00093 + SRG-OS-000393-GPOS-00173 + SRG-OS-000394-GPOS-00174 + SRG-OS-000396-GPOS-00176 + SRG-OS-000423-GPOS-00187 + SRG-OS-000478-GPOS-00223 + SRG-OS-000120-VMM-000600 + SRG-OS-000478-VMM-001980 + SRG-OS-000396-VMM-001590 + RHEL-08-010020 + SV-230223r792855_rule + Use of weak or untested encryption algorithms undermines the purposes of utilizing encryption to +protect data. The operating system must implement cryptographic modules adhering to the higher +standards approved by the federal government since this provides assurance they have been tested +and validated. + + CCE-84027-2 + + + + + + + + + + System Cryptographic Policies + Linux has the capability to centrally configure cryptographic polices. The command +update-crypto-policies is used to set the policy applicable for the various +cryptographic back-ends, such as SSL/TLS libraries. The configured cryptographic +policies will be the default policy used by these backends unless the application +user configures them otherwise. When the system has been configured to use the +centralized cryptographic policies, the administrator is assured that any application +that utilizes the supported backends will follow a policy that adheres to the +configured profile. + +Currently the supported backends are: +GnuTLS libraryOpenSSL libraryNSS libraryOpenJDKLibkrb5BINDOpenSSH +Applications and languages which rely on any of these backends will follow the +system policies as well. Examples are apache httpd, nginx, php, and others. + + SSH client RekeyLimit - size + Specify the size component of the rekey limit. This limit signifies amount +of data. After this amount of data is transferred through the connection, +the session key is renegotiated. The number is followed by K, M or G for +kilobytes, megabytes or gigabytes. Note that the RekeyLimit can be also +configured according to elapsed time. + 512M + 512M + 1G + + + SSH client RekeyLimit - time + Specify the time component of the rekey limit. The session key is +renegotiated after the defined amount of time passes. The number is followed +by units such as H or M for hours or minutes. Note that the RekeyLimit can +be also configured according to amount of transfered data. + 1h + 1h + + + The system-provided crypto policies + Specify the crypto policy for the system. + DEFAULT + DEFAULT + DEFAULT:NO-SHA1 + FIPS + FIPS:OSPP + LEGACY + FUTURE + NEXT + + + Install crypto-policies package + The crypto-policies package can be installed with the following command: + +$ sudo yum install crypto-policies + FCS_COP.1(1) + FCS_COP.1(2) + FCS_COP.1(3) + FCS_COP.1(4) + FCS_CKM.1 + FCS_CKM.2 + FCS_TLSC_EXT.1 + SRG-OS-000396-GPOS-00176 + SRG-OS-000393-GPOS-00173 + SRG-OS-000394-GPOS-00174 + Centralized cryptographic policies simplify applying secure ciphers across an operating system and +the applications that run on that operating system. Use of weak or untested encryption algorithms +undermines the purposes of utilizing encryption to protect data. + CCE-82723-8 + +package --add=crypto-policies + + include install_crypto-policies + +class install_crypto-policies { + package { 'crypto-policies': + ensure => 'installed', + } +} + + - name: Ensure crypto-policies is installed + package: + name: crypto-policies + state: present + tags: + - CCE-82723-8 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_crypto-policies_installed + + +[[packages]] +name = "crypto-policies" +version = "*" + + +if ! rpm -q --quiet "crypto-policies" ; then + yum install -y "crypto-policies" +fi + + + + + + + + + + Configure BIND to use System Crypto Policy + Crypto Policies provide a centralized control over crypto algorithms usage of many packages. +BIND is supported by crypto policy, but the BIND configuration may be +set up to ignore it. + +To check that Crypto Policies settings are configured correctly, ensure that the /etc/named.conf +includes the appropriate configuration: +In the options section of /etc/named.conf, make sure that the following line +is not commented out or superseded by later includes: +include "/etc/crypto-policies/back-ends/bind.config"; + CIP-003-8 R4.2 + CIP-007-3 R5.1 + SC-13 + SC-12(2) + SC-12(3) + SRG-OS-000423-GPOS-00187 + SRG-OS-000426-GPOS-00190 + RHEL-08-010020 + SV-230223r792855_rule + Overriding the system crypto policy makes the behavior of the BIND service violate expectations, +and makes system configuration more fragmented. + CCE-80934-3 + +function remediate_bind_crypto_policy() { + CONFIG_FILE="/etc/named.conf" + if test -f "$CONFIG_FILE"; then + sed -i 's|options {|&\n\tinclude "/etc/crypto-policies/back-ends/bind.config";|' "$CONFIG_FILE" + return 0 + else + echo "Aborting remediation as '$CONFIG_FILE' was not even found." >&2 + return 1 + fi +} + +remediate_bind_crypto_policy + + + + + + + + + + Configure System Cryptography Policy + To configure the system cryptography policy to use ciphers only from the +policy, run the following command: +$ sudo update-crypto-policies --set +The rule checks if settings for selected crypto policy are configured as expected. Configuration files in the /etc/crypto-policies/back-ends are either symlinks to correct files provided by Crypto-policies package or they are regular files in case crypto policy customizations are applied. +Crypto policies may be customized by crypto policy modules, in which case it is delimited from the base policy using a colon. + The system needs to be rebooted for these changes to take effect. + System Crypto Modules must be provided by a vendor that undergoes +FIPS-140 certifications. +FIPS-140 is applicable to all Federal agencies that use +cryptographic-based security systems to protect sensitive information +in computer and telecommunication systems (including voice systems) as +defined in Section 5131 of the Information Technology Management Reform +Act of 1996, Public Law 104-106. This standard shall be used in +designing and implementing cryptographic modules that Federal +departments and agencies operate or are operated for them under +contract. See https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.140-2.pdf +To meet this, the system has to have cryptographic software provided by +a vendor that has undergone this certification. This means providing +documentation, test results, design information, and independent third +party review by an accredited lab. While open source software is +capable of meeting this, it does not meet FIPS-140 unless the vendor +submits to this process. + 164.308(a)(4)(i) + 164.308(b)(1) + 164.308(b)(3) + 164.312(e)(1) + 164.312(e)(2)(ii) + 1446 + CIP-003-8 R4.2 + CIP-007-3 R5.1 + CIP-007-3 R7.1 + AC-17(a) + AC-17(2) + CM-6(a) + MA-4(6) + SC-13 + SC-12(2) + SC-12(3) + FCS_COP.1(1) + FCS_COP.1(2) + FCS_COP.1(3) + FCS_COP.1(4) + FCS_CKM.1 + FCS_CKM.2 + FCS_TLSC_EXT.1 + SRG-OS-000396-GPOS-00176 + SRG-OS-000393-GPOS-00173 + SRG-OS-000394-GPOS-00174 + RHEL-08-010020 + 1.10 + 1.11 + SV-230223r792855_rule + Centralized cryptographic policies simplify applying secure ciphers across an operating system and +the applications that run on that operating system. Use of weak or untested encryption algorithms +undermines the purposes of utilizing encryption to protect data. + CCE-80935-0 + - name: XCCDF Value var_system_crypto_policy # promote to variable + set_fact: + var_system_crypto_policy: !!str + tags: + - always + +- name: Configure System Cryptography Policy + lineinfile: + path: /etc/crypto-policies/config + regexp: ^(?!#)(\S+)$ + line: '{{ var_system_crypto_policy }}' + create: true + tags: + - CCE-80935-0 + - DISA-STIG-RHEL-08-010020 + - NIST-800-53-AC-17(2) + - NIST-800-53-AC-17(a) + - NIST-800-53-CM-6(a) + - NIST-800-53-MA-4(6) + - NIST-800-53-SC-12(2) + - NIST-800-53-SC-12(3) + - NIST-800-53-SC-13 + - configure_crypto_policy + - high_severity + - low_complexity + - low_disruption + - no_reboot_needed + - restrict_strategy + +- name: Verify that Crypto Policy is Set (runtime) + command: /usr/bin/update-crypto-policies --set {{ var_system_crypto_policy }} + tags: + - CCE-80935-0 + - DISA-STIG-RHEL-08-010020 + - NIST-800-53-AC-17(2) + - NIST-800-53-AC-17(a) + - NIST-800-53-CM-6(a) + - NIST-800-53-MA-4(6) + - NIST-800-53-SC-12(2) + - NIST-800-53-SC-12(3) + - NIST-800-53-SC-13 + - configure_crypto_policy + - high_severity + - low_complexity + - low_disruption + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + systemd: + units: + - name: configure-crypto-policy.service + enabled: true + contents: | + [Unit] + Before=kubelet.service + [Service] + Type=oneshot + ExecStart=update-crypto-policies --set {{.var_system_crypto_policy}} + RemainAfterExit=yes + [Install] + WantedBy=multi-user.target + + +var_system_crypto_policy='' + + +stderr_of_call=$(update-crypto-policies --set ${var_system_crypto_policy} 2>&1 > /dev/null) +rc=$? + +if test "$rc" = 127; then + echo "$stderr_of_call" >&2 + echo "Make sure that the script is installed on the remediated system." >&2 + echo "See output of the 'dnf provides update-crypto-policies' command" >&2 + echo "to see what package to (re)install" >&2 + + false # end with an error code +elif test "$rc" != 0; then + echo "Error invoking the update-crypto-policies script: $stderr_of_call" >&2 + false # end with an error code +fi + + + + + + + + + + + Configure GnuTLS library to use DoD-approved TLS Encryption + Crypto Policies provide a centralized control over crypto algorithms usage of many packages. +GnuTLS is supported by system crypto policy, but the GnuTLS configuration may be +set up to ignore it. + +To check that Crypto Policies settings are configured correctly, ensure that +/etc/crypto-policies/back-ends/gnutls.config contains the following +line and is not commented out: ++VERS-ALL:-VERS-DTLS0.9:-VERS-SSL3.0:-VERS-TLS1.0:-VERS-TLS1.1:-VERS-DTLS1.0 + CCI-001453 + AC-17(2) + SRG-OS-000250-GPOS-00093 + SRG-OS-000423-GPOS-00187 + RHEL-08-010295 + SV-230256r792859_rule + Overriding the system crypto policy makes the behavior of the GnuTLS +library violate expectations, and makes system configuration more +fragmented. + CCE-84254-2 + - name: 'Configure GnuTLS library to use DoD-approved TLS Encryption: set_fact' + set_fact: + path: /etc/crypto-policies/back-ends/gnutls.config + correct_value: +VERS-ALL:-VERS-DTLS0.9:-VERS-SSL3.0:-VERS-TLS1.0:-VERS-TLS1.1:-VERS-DTLS1.0 + lineinfile_reg: \+VERS-ALL:-VERS-DTLS0\.9:-VERS-SSL3\.0:-VERS-TLS1\.0:-VERS-TLS1\.1:-VERS-DTLS1\.0 + tags: + - CCE-84254-2 + - DISA-STIG-RHEL-08-010295 + - NIST-800-53-AC-17(2) + - configure_gnutls_tls_crypto_policy + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: 'Configure GnuTLS library to use DoD-approved TLS Encryption: stat' + stat: + path: '{{ path }}' + follow: true + register: gnutls_file + tags: + - CCE-84254-2 + - DISA-STIG-RHEL-08-010295 + - NIST-800-53-AC-17(2) + - configure_gnutls_tls_crypto_policy + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: 'Configure GnuTLS library to use DoD-approved TLS Encryption: Add' + lineinfile: + path: '{{ path }}' + regexp: '{{ lineinfile_reg }}' + line: '{{ correct_value }}' + create: true + when: not gnutls_file.stat.exists or gnutls_file.stat.size <= correct_value|length + tags: + - CCE-84254-2 + - DISA-STIG-RHEL-08-010295 + - NIST-800-53-AC-17(2) + - configure_gnutls_tls_crypto_policy + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Configure GnuTLS library to use DoD-approved TLS Encryption + block: + + - name: 'Configure GnuTLS library to use DoD-approved TLS Encryption: Existing value + check' + lineinfile: + path: '{{ path }}' + create: false + regexp: '{{ lineinfile_reg }}' + state: absent + check_mode: true + changed_when: false + register: gnutls + + - name: 'Configure GnuTLS library to use DoD-approved TLS Encryption: Update' + replace: + path: '{{ path }}' + regexp: (\+VERS-ALL(?::-VERS-[A-Z]+\d\.\d)+) + replace: '{{ correct_value }}' + when: gnutls.found is defined and gnutls.found != 1 + when: gnutls_file.stat.exists and gnutls_file.stat.size > correct_value|length + tags: + - CCE-84254-2 + - DISA-STIG-RHEL-08-010295 + - NIST-800-53-AC-17(2) + - configure_gnutls_tls_crypto_policy + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + +CONF_FILE=/etc/crypto-policies/back-ends/gnutls.config +correct_value='+VERS-ALL:-VERS-DTLS0.9:-VERS-SSL3.0:-VERS-TLS1.0:-VERS-TLS1.1:-VERS-DTLS1.0' + +grep -q ${correct_value} ${CONF_FILE} + +if [[ $? -ne 0 ]]; then + # We need to get the existing value, using PCRE to maintain same regex + existing_value=$(grep -Po '(\+VERS-ALL(?::-VERS-[A-Z]+\d\.\d)+)' ${CONF_FILE}) + + if [[ ! -z ${existing_value} ]]; then + # replace existing_value with correct_value + sed -i "s/${existing_value}/${correct_value}/g" ${CONF_FILE} + else + # ***NOTE*** # + # This probably means this file is not here or it's been modified + # unintentionally. + # ********** # + # echo correct_value to end + echo ${correct_value} >> ${CONF_FILE} + fi +fi + + + + + + + + + + Configure Kerberos to use System Crypto Policy + Crypto Policies provide a centralized control over crypto algorithms usage of many packages. +Kerberos is supported by crypto policy, but it's configuration may be +set up to ignore it. +To check that Crypto Policies settings for Kerberos are configured correctly, examine that there is a symlink at +/etc/krb5.conf.d/crypto-policies targeting /etc/cypto-policies/back-ends/krb5.config. +If the symlink exists, Kerberos is configured to use the system-wide crypto policy settings. + 0418 + 1055 + 1402 + CIP-003-8 R4.2 + CIP-007-3 R5.1 + SC-13 + SC-12(2) + SC-12(3) + SRG-OS-000120-GPOS-00061 + RHEL-08-010020 + SV-230223r792855_rule + Overriding the system crypto policy makes the behavior of Kerberos violate expectations, +and makes system configuration more fragmented. + CCE-80936-8 + - name: Configure Kerberos to use System Crypto Policy + file: + src: /etc/crypto-policies/back-ends/krb5.config + path: /etc/krb5.conf.d/crypto-policies + state: link + tags: + - CCE-80936-8 + - DISA-STIG-RHEL-08-010020 + - NIST-800-53-SC-12(2) + - NIST-800-53-SC-12(3) + - NIST-800-53-SC-13 + - configure_kerberos_crypto_policy + - configure_strategy + - high_severity + - low_complexity + - low_disruption + - reboot_required + + +rm -f /etc/krb5.conf.d/crypto-policies +ln -s /etc/crypto-policies/back-ends/krb5.config /etc/krb5.conf.d/crypto-policies + + + + + + + + + + Configure Libreswan to use System Crypto Policy + Crypto Policies provide a centralized control over crypto algorithms usage of many packages. +Libreswan is supported by system crypto policy, but the Libreswan configuration may be +set up to ignore it. + +To check that Crypto Policies settings are configured correctly, ensure that the /etc/ipsec.conf +includes the appropriate configuration file. +In /etc/ipsec.conf, make sure that the following line +is not commented out or superseded by later includes: +include /etc/crypto-policies/back-ends/libreswan.config + CIP-003-8 R4.2 + CIP-007-3 R5.1 + CM-6(a) + MA-4(6) + SC-13 + SC-12(2) + SC-12(3) + FCS_IPSEC_EXT.1.4 + FCS_IPSEC_EXT.1.6 + SRG-OS-000033-GPOS-00014 + RHEL-08-010020 + SV-230223r792855_rule + Overriding the system crypto policy makes the behavior of the Libreswan +service violate expectations, and makes system configuration more +fragmented. + CCE-80937-6 + - name: Configure Libreswan to use System Crypto Policy + lineinfile: + path: /etc/ipsec.conf + line: include /etc/crypto-policies/back-ends/libreswan.config + create: true + tags: + - CCE-80937-6 + - DISA-STIG-RHEL-08-010020 + - NIST-800-53-CM-6(a) + - NIST-800-53-MA-4(6) + - NIST-800-53-SC-12(2) + - NIST-800-53-SC-12(3) + - NIST-800-53-SC-13 + - configure_libreswan_crypto_policy + - high_severity + - low_complexity + - low_disruption + - no_reboot_needed + - restrict_strategy + + +function remediate_libreswan_crypto_policy() { + CONFIG_FILE="/etc/ipsec.conf" + if ! grep -qP "^\s*include\s+/etc/crypto-policies/back-ends/libreswan.config\s*(?:#.*)?$" "$CONFIG_FILE" ; then + # the file might not end with a new line + echo -e '\ninclude /etc/crypto-policies/back-ends/libreswan.config' >> "$CONFIG_FILE" + fi + return 0 +} + +remediate_libreswan_crypto_policy + + + + + + + + + + Configure OpenSSL library to use System Crypto Policy + Crypto Policies provide a centralized control over crypto algorithms usage of many packages. +OpenSSL is supported by crypto policy, but the OpenSSL configuration may be +set up to ignore it. +To check that Crypto Policies settings are configured correctly, you have to examine the OpenSSL config file +available under /etc/pki/tls/openssl.cnf. +This file has the ini format, and it enables crypto policy support +if there is a [ crypto_policy ] section that contains the .include /etc/crypto-policies/back-ends/opensslcnf.config directive. + CCI-001453 + CIP-003-8 R4.2 + CIP-007-3 R5.1 + CIP-007-3 R7.1 + AC-17(a) + AC-17(2) + CM-6(a) + MA-4(6) + SC-13 + SC-12(2) + SC-12(3) + SRG-OS-000250-GPOS-00093 + RHEL-08-010293 + SV-230254r627750_rule + Overriding the system crypto policy makes the behavior of the Java runtime violates expectations, +and makes system configuration more fragmented. + CCE-80938-4 + - name: Test for crypto_policy group + command: grep '^\s*\[\s*crypto_policy\s*]' /etc/pki/tls/openssl.cnf + register: test_crypto_policy_group + ignore_errors: true + changed_when: false + check_mode: false + tags: + - CCE-80938-4 + - DISA-STIG-RHEL-08-010293 + - NIST-800-53-AC-17(2) + - NIST-800-53-AC-17(a) + - NIST-800-53-CM-6(a) + - NIST-800-53-MA-4(6) + - NIST-800-53-SC-12(2) + - NIST-800-53-SC-12(3) + - NIST-800-53-SC-13 + - configure_openssl_crypto_policy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Add .include for opensslcnf.config to crypto_policy section + lineinfile: + create: true + insertafter: ^\s*\[\s*crypto_policy\s*]\s* + line: .include = /etc/crypto-policies/back-ends/opensslcnf.config + path: /etc/pki/tls/openssl.cnf + when: + - test_crypto_policy_group.stdout is defined + - test_crypto_policy_group.stdout | length > 0 + tags: + - CCE-80938-4 + - DISA-STIG-RHEL-08-010293 + - NIST-800-53-AC-17(2) + - NIST-800-53-AC-17(a) + - NIST-800-53-CM-6(a) + - NIST-800-53-MA-4(6) + - NIST-800-53-SC-12(2) + - NIST-800-53-SC-12(3) + - NIST-800-53-SC-13 + - configure_openssl_crypto_policy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Add crypto_policy group and set include opensslcnf.config + lineinfile: + create: true + line: |- + [crypto_policy] + .include = /etc/crypto-policies/back-ends/opensslcnf.config + path: /etc/pki/tls/openssl.cnf + when: + - test_crypto_policy_group.stdout is defined + - test_crypto_policy_group.stdout | length < 1 + tags: + - CCE-80938-4 + - DISA-STIG-RHEL-08-010293 + - NIST-800-53-AC-17(2) + - NIST-800-53-AC-17(a) + - NIST-800-53-CM-6(a) + - NIST-800-53-MA-4(6) + - NIST-800-53-SC-12(2) + - NIST-800-53-SC-12(3) + - NIST-800-53-SC-13 + - configure_openssl_crypto_policy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + + +OPENSSL_CRYPTO_POLICY_SECTION='[ crypto_policy ]' +OPENSSL_CRYPTO_POLICY_SECTION_REGEX='\[\s*crypto_policy\s*\]' +OPENSSL_CRYPTO_POLICY_INCLUSION='.include = /etc/crypto-policies/back-ends/opensslcnf.config' +OPENSSL_CRYPTO_POLICY_INCLUSION_REGEX='^\s*\.include\s*(?:=\s*)?/etc/crypto-policies/back-ends/opensslcnf.config$' + + + + + +function remediate_openssl_crypto_policy() { + CONFIG_FILE=/etc/pki/tls/openssl.cnf + if test -f "$CONFIG_FILE"; then + if ! grep -q "^\\s*$OPENSSL_CRYPTO_POLICY_SECTION_REGEX" "$CONFIG_FILE"; then + printf '\n%s\n\n%s' "$OPENSSL_CRYPTO_POLICY_SECTION" "$OPENSSL_CRYPTO_POLICY_INCLUSION" >> "$CONFIG_FILE" + return 0 + elif ! grep -q "^\\s*$OPENSSL_CRYPTO_POLICY_INCLUSION_REGEX" "$CONFIG_FILE"; then + sed -i "s|$OPENSSL_CRYPTO_POLICY_SECTION_REGEX|&\\n\\n$OPENSSL_CRYPTO_POLICY_INCLUSION\\n|" "$CONFIG_FILE" + return 0 + fi + else + echo "Aborting remediation as '$CONFIG_FILE' was not even found." >&2 + return 1 + fi +} + +remediate_openssl_crypto_policy + + + + + + + + + + Configure OpenSSL library to use TLS Encryption + Crypto Policies are means of enforcing certain cryptographic settings for +selected applications including OpenSSL. OpenSSL is by default configured to +modify its configuration based on currently configured Crypto Policy. +Editing the Crypto Policy back-end is not recommended. + +Check the crypto-policies(7) man page and choose a policy that configures TLS +protocol to version 1.2 or higher, for example DEFAULT, FUTURE or FIPS policy. +Or create and apply a custom policy that restricts minimum TLS version to 1.2. + +For example for versions prior to crypto-policies-20210617-1.gitc776d3e.el8.noarch +this is expected: + +$ sudo grep -i MinProtocol /etc/crypto-policies/back-ends/opensslcnf.config + +MinProtocol = TLSv1.2 + + +Or for version crypto-policies-20210617-1.gitc776d3e.el8.noarch and newer this is +expected: + +$ sudo grep -i MinProtocol /etc/crypto-policies/back-ends/opensslcnf.config + +TLS.MinProtocol = TLSv1.2 +DTLS.MinProtocol = DTLSv1.2 + This rule doesn't come with a remediation, automatically changing the crypto-policies may be too disruptive. +Ensure the variable xccdf_org.ssgproject.content_value_var_system_crypto_policy is set to a +Crypto Policy that satisfies OpenSSL minimum TLS protocol version 1.2. Custom policies may be applied too. + CCI-001453 + AC-17(2) + SRG-OS-000125-GPOS-00065 + SRG-OS-000250-GPOS-00093 + SRG-OS-000393-GPOS-00173 + SRG-OS-000394-GPOS-00174 + RHEL-08-010294 + SV-230255r809382_rule + Without cryptographic integrity protections, information can be altered by +unauthorized users without detection. + CCE-84255-9 + + + + + + + + + Configure SSH to use System Crypto Policy + Crypto Policies provide a centralized control over crypto algorithms usage of many packages. +SSH is supported by crypto policy, but the SSH configuration may be +set up to ignore it. +To check that Crypto Policies settings are configured correctly, ensure that +the CRYPTO_POLICY variable is either commented or not set at all +in the /etc/sysconfig/sshd. + CCI-001453 + 164.308(a)(4)(i) + 164.308(b)(1) + 164.308(b)(3) + 164.312(e)(1) + 164.312(e)(2)(ii) + CIP-003-8 R4.2 + CIP-007-3 R5.1 + CIP-007-3 R7.1 + AC-17(a) + AC-17(2) + CM-6(a) + MA-4(6) + SC-13 + FCS_SSH_EXT.1 + FCS_SSHS_EXT.1 + FCS_SSHC_EXT.1 + SRG-OS-000250-GPOS-00093 + RHEL-08-010287 + 5.2.14 + SV-244526r809334_rule + Overriding the system crypto policy makes the behavior of the SSH service violate expectations, +and makes system configuration more fragmented. + CCE-80939-2 + - name: Configure SSH to use System Crypto Policy + lineinfile: + dest: /etc/sysconfig/sshd + state: absent + regexp: ^\s*(?i)CRYPTO_POLICY.*$ + tags: + - CCE-80939-2 + - DISA-STIG-RHEL-08-010287 + - NIST-800-53-AC-17(2) + - NIST-800-53-AC-17(a) + - NIST-800-53-CM-6(a) + - NIST-800-53-MA-4(6) + - NIST-800-53-SC-13 + - configure_ssh_crypto_policy + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + + +SSH_CONF="/etc/sysconfig/sshd" + +sed -i "/^\s*CRYPTO_POLICY.*$/Id" $SSH_CONF + + + + + + + + + + Harden OpenSSL Crypto Policy + Crypto Policies are means of enforcing certain cryptographic settings for +selected applications including OpenSSL. OpenSSL is by default configured to +modify its configuration based on currently configured Crypto Policy. +However, in certain cases it might be needed to override the Crypto Policy +specific to OpenSSL and leave rest of the Crypto Policy intact. This can +be done by dropping a file named opensslcnf-xxx.config, replacing +xxx with arbitrary identifier, into +/etc/crypto-policies/local.d. This has to be followed by running +update-crypto-policies so that changes are applied. Changes are +propagated into /etc/crypto-policies/back-ends/opensslcnf.config. +This rule checks if this file contains predefined Ciphersuites +variable configured with predefined value. + CIP-003-8 R4.2 + CIP-007-3 R5.1 + SC-8(1) + SC-13 + FCS_TLSC_EXT.1.1 + SRG-OS-000396-GPOS-00176 + SRG-OS-000424-GPOS-00188 + SRG-OS-000478-GPOS-00223 + The Common Criteria requirements specify that certain parameters for OpenSSL +are configured e.g. cipher suites. Currently particular requirements +specified by CC are stricter compared to any existing Crypto Policy. + CCE-84286-4 + - name: Remove configuration from backend file /etc/crypto-policies/back-ends/opensslcnf.config + lineinfile: + path: /etc/crypto-policies/back-ends/opensslcnf.config + regexp: Ciphersuites\s*=\s*.* + state: absent + tags: + - CCE-84286-4 + - NIST-800-53-SC-13 + - NIST-800-53-SC-8(1) + - harden_openssl_crypto_policy + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Ensure that the correct crypto policy configuration exists in /etc/crypto-policies/local.d/opensslcnf-ospp.config + copy: + dest: /etc/crypto-policies/local.d/opensslcnf-ospp.config + content: |2 + + Ciphersuites = TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256 + tags: + - CCE-84286-4 + - NIST-800-53-SC-13 + - NIST-800-53-SC-8(1) + - harden_openssl_crypto_policy + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Update system crypto policy for changes to take effect + command: + cmd: update-crypto-policies + tags: + - CCE-84286-4 + - NIST-800-53-SC-13 + - NIST-800-53-SC-8(1) + - harden_openssl_crypto_policy + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + +cp="Ciphersuites = TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256" +file="/etc/crypto-policies/local.d/opensslcnf-ospp.config" +backend_file="/etc/crypto-policies/back-ends/opensslcnf.config" + +sed -i "/Ciphersuites\s*=\s*/d" "$backend_file" +printf "\n%s\n" "$cp" >> "$file" +update-crypto-policies + + + + + + + + + + Harden SSH client Crypto Policy + Crypto Policies are means of enforcing certain cryptographic settings for selected applications including OpenSSH client. +To override the system wide crypto policy for Openssh client, place a file in the /etc/ssh/ssh_config.d/ so that it is loaded before the 05-redhat.conf. In this case it is file named 02-ospp.conf containing parameters which need to be changed with respect to the crypto policy. +This rule checks if the file exists and if it contains required parameters and values which modify the Crypto Policy. +During the parsing process, as soon as Openssh client parses some configuration option and its value, it remembers it and ignores any subsequent overrides. The customization mechanism provided by crypto policies appends eventual customizations at the end of the system wide crypto policy. Therefore, if the crypto policy customization overrides some parameter which is already configured in the system wide crypto policy, the SSH client will not honor that customized parameter. + CIP-003-8 R4.2 + CIP-007-3 R5.1 + CIP-007-3 R7.1 + AC-17(a) + AC-17(2) + CM-6(a) + MA-4(6) + SC-13 + FCS_SSHC_EXT.1 + SRG-OS-000033-GPOS-00014 + SRG-OS-000250-GPOS-00093 + SRG-OS-000393-GPOS-00173 + SRG-OS-000394-GPOS-00174 + The Common Criteria requirements specify how certain parameters for OpenSSH Client are configured. Particular parameters are RekeyLimit, GSSAPIAuthentication, Ciphers, PubkeyAcceptedKeyTypes, MACs and KexAlgorithms. Currently particular requirements specified by CC are stricter compared to any existing Crypto Policy. + CCE-82225-4 + +#the file starts with 02 so that it is loaded before the 05-redhat.conf which activates configuration provided by system vide crypto policy +file="/etc/ssh/ssh_config.d/02-ospp.conf" +echo -e "Match final all\n\ +RekeyLimit 512M 1h\n\ +GSSAPIAuthentication no\n\ +Ciphers aes256-ctr,aes256-cbc,aes128-ctr,aes128-cbc\n\ +PubkeyAcceptedKeyTypes ssh-rsa,ecdsa-sha2-nistp384,ecdsa-sha2-nistp256\n\ +MACs hmac-sha2-512,hmac-sha2-256\n\ +KexAlgorithms ecdh-sha2-nistp521,ecdh-sha2-nistp384,ecdh-sha2-nistp256,diffie-hellman-group14-sha1\n" > "$file" + + + + + + + + + + Configure SSH Client to Use FIPS 140-2 Validated Ciphers: openssh.config + Crypto Policies provide a centralized control over crypto algorithms usage of many packages. +OpenSSH is supported by system crypto policy, but the OpenSSH configuration may be +set up incorrectly. + +To check that Crypto Policies settings for ciphers are configured correctly, ensure that +/etc/crypto-policies/back-ends/openssh.config contains the following +line and is not commented out: +Ciphers + The system needs to be rebooted for these changes to take effect. + System Crypto Modules must be provided by a vendor that undergoes +FIPS-140 certifications. +FIPS-140 is applicable to all Federal agencies that use +cryptographic-based security systems to protect sensitive information +in computer and telecommunication systems (including voice systems) as +defined in Section 5131 of the Information Technology Management Reform +Act of 1996, Public Law 104-106. This standard shall be used in +designing and implementing cryptographic modules that Federal +departments and agencies operate or are operated for them under +contract. See https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.140-2.pdf +To meet this, the system has to have cryptographic software provided by +a vendor that has undergone this certification. This means providing +documentation, test results, design information, and independent third +party review by an accredited lab. While open source software is +capable of meeting this, it does not meet FIPS-140 unless the vendor +submits to this process. + CCI-000068 + CCI-000877 + CCI-001453 + CCI-002418 + CCI-002890 + CCI-003123 + AC-17(2) + SRG-OS-000033-GPOS-00014 + SRG-OS-000125-GPOS-00065 + SRG-OS-000250-GPOS-00093 + SRG-OS-000393-GPOS-00173 + SRG-OS-000394-GPOS-00174 + SRG-OS-000423-GPOS-00187 + RHEL-08-010020 + SV-230223r792855_rule + Overriding the system crypto policy makes the behavior of the OpenSSH client +violate expectations, and makes system configuration more fragmented. By +specifying a cipher list with the order of ciphers being in a “strongest to +weakest” orientation, the system will automatically attempt to use the +strongest cipher for securing SSH connections. + CCE-85902-5 + - name: XCCDF Value sshd_approved_ciphers # promote to variable + set_fact: + sshd_approved_ciphers: !!str + tags: + - always + +- name: 'Configure SSH Daemon to Use FIPS 140-2 Validated Ciphers: openssh.config' + block: + + - name: Check for duplicate values + lineinfile: + path: /etc/crypto-policies/back-ends/openssh.config + create: false + regexp: ^.*Ciphers\s+ + state: absent + check_mode: true + changed_when: false + register: dupes + + - name: Deduplicate values from /etc/crypto-policies/back-ends/openssh.config + lineinfile: + path: /etc/crypto-policies/back-ends/openssh.config + create: false + regexp: ^.*Ciphers\s+ + state: absent + when: dupes.found is defined and dupes.found > 1 + + - name: Insert correct line to /etc/crypto-policies/back-ends/openssh.config + lineinfile: + path: /etc/crypto-policies/back-ends/openssh.config + create: true + regexp: ^.*Ciphers\s+ + line: Ciphers {{ sshd_approved_ciphers }} + state: present + tags: + - CCE-85902-5 + - DISA-STIG-RHEL-08-010020 + - NIST-800-53-AC-17(2) + - harden_sshd_ciphers_openssh_conf_crypto_policy + - high_severity + - low_complexity + - low_disruption + - reboot_required + - restrict_strategy + + +sshd_approved_ciphers='' + + +if [ -e "/etc/crypto-policies/back-ends/openssh.config" ] ; then + + LC_ALL=C sed -i "/^.*Ciphers\s\+/d" "/etc/crypto-policies/back-ends/openssh.config" +else + touch "/etc/crypto-policies/back-ends/openssh.config" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/crypto-policies/back-ends/openssh.config" + +cp "/etc/crypto-policies/back-ends/openssh.config" "/etc/crypto-policies/back-ends/openssh.config.bak" +# Insert at the end of the file +printf '%s\n' "Ciphers ${sshd_approved_ciphers}" >> "/etc/crypto-policies/back-ends/openssh.config" +# Clean up after ourselves. +rm "/etc/crypto-policies/back-ends/openssh.config.bak" + + + + + + + + + + + Configure SSH Server to Use FIPS 140-2 Validated Ciphers: opensshserver.config + Crypto Policies provide a centralized control over crypto algorithms usage of many packages. +OpenSSH is supported by system crypto policy, but the OpenSSH configuration may be +set up incorrectly. + +To check that Crypto Policies settings for ciphers are configured correctly, ensure that +/etc/crypto-policies/back-ends/opensshserver.config contains the following +text and is not commented out: +-oCiphers= + The system needs to be rebooted for these changes to take effect. + System Crypto Modules must be provided by a vendor that undergoes +FIPS-140 certifications. +FIPS-140 is applicable to all Federal agencies that use +cryptographic-based security systems to protect sensitive information +in computer and telecommunication systems (including voice systems) as +defined in Section 5131 of the Information Technology Management Reform +Act of 1996, Public Law 104-106. This standard shall be used in +designing and implementing cryptographic modules that Federal +departments and agencies operate or are operated for them under +contract. See https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.140-2.pdf +To meet this, the system has to have cryptographic software provided by +a vendor that has undergone this certification. This means providing +documentation, test results, design information, and independent third +party review by an accredited lab. While open source software is +capable of meeting this, it does not meet FIPS-140 unless the vendor +submits to this process. + CCI-000877 + CCI-001453 + AC-17(2) + SRG-OS-000125-GPOS-00065 + SRG-OS-000250-GPOS-00093 + RHEL-08-010291 + SV-230252r743940_rule + Overriding the system crypto policy makes the behavior of the OpenSSH server +violate expectations, and makes system configuration more fragmented. By +specifying a cipher list with the order of ciphers being in a “strongest to +weakest” orientation, the system will automatically attempt to use the +strongest cipher for securing SSH connections. + CCE-85897-7 + - name: XCCDF Value sshd_approved_ciphers # promote to variable + set_fact: + sshd_approved_ciphers: !!str + tags: + - always + +- name: 'Configure SSH Server to Use FIPS 140-2 Validated Ciphers: opensshserver.config: + Set facts' + set_fact: + path: /etc/crypto-policies/back-ends/opensshserver.config + correct_value: -oCiphers={{ sshd_approved_ciphers }} + tags: + - CCE-85897-7 + - DISA-STIG-RHEL-08-010291 + - NIST-800-53-AC-17(2) + - harden_sshd_ciphers_opensshserver_conf_crypto_policy + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: 'Configure SSH Server to Use FIPS 140-2 Validated Ciphers: opensshserver.config: + Stat' + stat: + path: '{{ path }}' + follow: true + register: opensshserver_file + tags: + - CCE-85897-7 + - DISA-STIG-RHEL-08-010291 + - NIST-800-53-AC-17(2) + - harden_sshd_ciphers_opensshserver_conf_crypto_policy + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: 'Configure SSH Server to Use FIPS 140-2 Validated Ciphers: opensshserver.config: + Create' + lineinfile: + path: '{{ path }}' + line: CRYPTO_POLICY='{{ correct_value }}' + create: true + when: not opensshserver_file.stat.exists or opensshserver_file.stat.size <= correct_value|length + tags: + - CCE-85897-7 + - DISA-STIG-RHEL-08-010291 + - NIST-800-53-AC-17(2) + - harden_sshd_ciphers_opensshserver_conf_crypto_policy + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: 'Configure SSH Server to Use FIPS 140-2 Validated Ciphers: opensshserver.config' + block: + + - name: Existing value check + lineinfile: + path: '{{ path }}' + create: false + regexp: '{{ correct_value }}' + state: absent + check_mode: true + changed_when: false + register: opensshserver + + - name: Update/Correct value + replace: + path: '{{ path }}' + regexp: (-oCiphers=\S+) + replace: '{{ correct_value }}' + when: opensshserver.found is defined and opensshserver.found != 1 + when: opensshserver_file.stat.exists and opensshserver_file.stat.size > correct_value|length + tags: + - CCE-85897-7 + - DISA-STIG-RHEL-08-010291 + - NIST-800-53-AC-17(2) + - harden_sshd_ciphers_opensshserver_conf_crypto_policy + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + +sshd_approved_ciphers='' + + +CONF_FILE=/etc/crypto-policies/back-ends/opensshserver.config +correct_value="-oCiphers=${sshd_approved_ciphers}" + +# Test if file exists +test -f ${CONF_FILE} || touch ${CONF_FILE} + +# Ensure CRYPTO_POLICY is not commented out +sed -i 's/#CRYPTO_POLICY=/CRYPTO_POLICY=/' ${CONF_FILE} + +grep -q "'${correct_value}'" ${CONF_FILE} + +if [[ $? -ne 0 ]]; then + # We need to get the existing value, using PCRE to maintain same regex + existing_value=$(grep -Po '(-oCiphers=\S+)' ${CONF_FILE}) + + if [[ ! -z ${existing_value} ]]; then + # replace existing_value with correct_value + sed -i "s/${existing_value}/${correct_value}/g" ${CONF_FILE} + else + # ***NOTE*** # + # This probably means this file is not here or it's been modified + # unintentionally. + # ********** # + # echo correct_value to end + echo "CRYPTO_POLICY='${correct_value}'" >> ${CONF_FILE} + fi +fi + + + + + + + + + + + Harden SSHD Crypto Policy + Crypto Policies are means of enforcing certain cryptographic settings for selected applications including OpenSSH server. +The SSHD service is by default configured to modify its configuration based on currently configured Crypto-Policy. However, in certain cases it might be needed to override the Crypto Policy specific to OpenSSH Server and leave rest of the Crypto Policy intact. +This can be done by dropping a file named opensshserver-xxx.config, replacing xxx with arbitrary identifier, into /etc/crypto-policies/local.d. This has to be followed by running update-crypto-policies so that changes are applied. +Changes are propagated into /etc/crypto-policies/back-ends/opensshserver.config. This rule checks if this file contains predefined CRYPTO_POLICY environment variable configured with predefined value. + CIP-003-8 R4.2 + CIP-007-3 R5.1 + CIP-007-3 R7.1 + AC-17(a) + AC-17(2) + CM-6(a) + MA-4(6) + SC-13 + SC-12(2) + SC-12(3) + FCS_SSHS_EXT.1 + SRG-OS-000250-GPOS-00093 + SRG-OS-000033-GPOS-00014 + SRG-OS-000120-GPOS-00061 + The Common Criteria requirements specify that certain parameters for OpenSSH Server are configured e.g. supported ciphers, accepted host key algorithms, public key types, key exchange algorithms, HMACs and GSSAPI key exchange is disabled. Currently particular requirements specified by CC are stricter compared to any existing Crypto Policy. + CCE-82176-9 + +cp="CRYPTO_POLICY='-oCiphers=aes256-ctr,aes128-ctr,aes256-cbc,aes128-cbc -oMACs=hmac-sha2-512,hmac-sha2-256 -oGSSAPIKeyExchange=no -oKexAlgorithms=ecdh-sha2-nistp521,ecdh-sha2-nistp384,ecdh-sha2-nistp256,diffie-hellman-group14-sha1 -oHostKeyAlgorithms=ssh-rsa,ecdsa-sha2-nistp384,ecdsa-sha2-nistp256 -oPubkeyAcceptedKeyTypes=rsa-sha2-512,rsa-sha2-256,ssh-rsa,ecdsa-sha2-nistp384,ecdsa-sha2-nistp256'" +file=/etc/crypto-policies/local.d/opensshserver-ospp.config + +#blank line at the begining to ease later readibility +echo '' > "$file" +echo "$cp" >> "$file" +update-crypto-policies + + + + + + + + + + + Configure SSH Client to Use FIPS 140-2 Validated MACs: openssh.config + Crypto Policies provide a centralized control over crypto algorithms usage of many packages. +OpenSSH is supported by system crypto policy, but the OpenSSH configuration may be +set up incorrectly. + +To check that Crypto Policies settings are configured correctly, ensure that +/etc/crypto-policies/back-ends/openssh.config contains the following +line and is not commented out: +MACs hmac-sha2-512,hmac-sha2-256 + The system needs to be rebooted for these changes to take effect. + System Crypto Modules must be provided by a vendor that undergoes +FIPS-140 certifications. +FIPS-140 is applicable to all Federal agencies that use +cryptographic-based security systems to protect sensitive information +in computer and telecommunication systems (including voice systems) as +defined in Section 5131 of the Information Technology Management Reform +Act of 1996, Public Law 104-106. This standard shall be used in +designing and implementing cryptographic modules that Federal +departments and agencies operate or are operated for them under +contract. See https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.140-2.pdf +To meet this, the system has to have cryptographic software provided by +a vendor that has undergone this certification. This means providing +documentation, test results, design information, and independent third +party review by an accredited lab. While open source software is +capable of meeting this, it does not meet FIPS-140 unless the vendor +submits to this process. + CCI-000877 + CCI-001453 + AC-17(2) + SRG-OS-000125-GPOS-00065 + SRG-OS-000250-GPOS-00093 + RHEL-08-010020 + SV-230223r792855_rule + Overriding the system crypto policy makes the behavior of the OpenSSH +client violate expectations, and makes system configuration more +fragmented. + CCE-85870-4 + - name: XCCDF Value sshd_approved_macs # promote to variable + set_fact: + sshd_approved_macs: !!str + tags: + - always + +- name: 'Configure SSH Daemon to Use FIPS 140-2 Validated MACs: openssh.config' + block: + + - name: Check for duplicate values + lineinfile: + path: /etc/crypto-policies/back-ends/openssh.config + create: false + regexp: ^.*MACs\s+ + state: absent + check_mode: true + changed_when: false + register: dupes + + - name: Deduplicate values from /etc/crypto-policies/back-ends/openssh.config + lineinfile: + path: /etc/crypto-policies/back-ends/openssh.config + create: false + regexp: ^.*MACs\s+ + state: absent + when: dupes.found is defined and dupes.found > 1 + + - name: Insert correct line to /etc/crypto-policies/back-ends/openssh.config + lineinfile: + path: /etc/crypto-policies/back-ends/openssh.config + create: true + regexp: ^.*MACs\s+ + line: MACs {{ sshd_approved_macs }} + state: present + tags: + - CCE-85870-4 + - DISA-STIG-RHEL-08-010020 + - NIST-800-53-AC-17(2) + - harden_sshd_macs_openssh_conf_crypto_policy + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + +sshd_approved_macs='' + + +if [ -e "/etc/crypto-policies/back-ends/openssh.config" ] ; then + + LC_ALL=C sed -i "/^.*MACs\s\+/d" "/etc/crypto-policies/back-ends/openssh.config" +else + touch "/etc/crypto-policies/back-ends/openssh.config" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/crypto-policies/back-ends/openssh.config" + +cp "/etc/crypto-policies/back-ends/openssh.config" "/etc/crypto-policies/back-ends/openssh.config.bak" +# Insert at the end of the file +printf '%s\n' "MACs ${sshd_approved_macs}" >> "/etc/crypto-policies/back-ends/openssh.config" +# Clean up after ourselves. +rm "/etc/crypto-policies/back-ends/openssh.config.bak" + + + + + + + + + + + Configure SSH Server to Use FIPS 140-2 Validated MACs: opensshserver.config + Crypto Policies provide a centralized control over crypto algorithms usage of many packages. +OpenSSH is supported by system crypto policy, but the OpenSSH configuration may be +set up incorrectly. + +To check that Crypto Policies settings are configured correctly, ensure that +/etc/crypto-policies/back-ends/opensshserver.config contains the following +text and is not commented out: +-oMACS=hmac-sha2-512,hmac-sha2-256 + The system needs to be rebooted for these changes to take effect. + System Crypto Modules must be provided by a vendor that undergoes +FIPS-140 certifications. +FIPS-140 is applicable to all Federal agencies that use +cryptographic-based security systems to protect sensitive information +in computer and telecommunication systems (including voice systems) as +defined in Section 5131 of the Information Technology Management Reform +Act of 1996, Public Law 104-106. This standard shall be used in +designing and implementing cryptographic modules that Federal +departments and agencies operate or are operated for them under +contract. See https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.140-2.pdf +To meet this, the system has to have cryptographic software provided by +a vendor that has undergone this certification. This means providing +documentation, test results, design information, and independent third +party review by an accredited lab. While open source software is +capable of meeting this, it does not meet FIPS-140 unless the vendor +submits to this process. + CCI-000877 + CCI-001453 + AC-17(2) + SRG-OS-000125-GPOS-00065 + SRG-OS-000250-GPOS-00093 + RHEL-08-010290 + SV-230251r743937_rule + Overriding the system crypto policy makes the behavior of the OpenSSH +server violate expectations, and makes system configuration more +fragmented. + CCE-85899-3 + - name: XCCDF Value sshd_approved_macs # promote to variable + set_fact: + sshd_approved_macs: !!str + tags: + - always + +- name: 'Configure SSH Server to Use FIPS 140-2 Validated MACs: opensshserver.config: + Set facts' + set_fact: + path: /etc/crypto-policies/back-ends/opensshserver.config + correct_value: -oMACs={{ sshd_approved_macs }} + tags: + - CCE-85899-3 + - DISA-STIG-RHEL-08-010290 + - NIST-800-53-AC-17(2) + - harden_sshd_macs_opensshserver_conf_crypto_policy + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: 'Configure SSH Server to Use FIPS 140-2 Validated MACs: opensshserver.config: + Stat' + stat: + path: '{{ path }}' + follow: true + register: opensshserver_file + tags: + - CCE-85899-3 + - DISA-STIG-RHEL-08-010290 + - NIST-800-53-AC-17(2) + - harden_sshd_macs_opensshserver_conf_crypto_policy + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: 'Configure SSH Server to Use FIPS 140-2 Validated MACs: opensshserver.config: + Create' + lineinfile: + path: '{{ path }}' + line: CRYPTO_POLICY='{{ correct_value }}' + create: true + when: not opensshserver_file.stat.exists or opensshserver_file.stat.size <= correct_value|length + tags: + - CCE-85899-3 + - DISA-STIG-RHEL-08-010290 + - NIST-800-53-AC-17(2) + - harden_sshd_macs_opensshserver_conf_crypto_policy + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: 'Configure SSH Server to Use FIPS 140-2 Validated MACs: opensshserver.config' + block: + + - name: Existing value check + lineinfile: + path: '{{ path }}' + create: false + regexp: '{{ correct_value }}' + state: absent + check_mode: true + changed_when: false + register: opensshserver + + - name: Update/Correct value + replace: + path: '{{ path }}' + regexp: (-oMACs=\S+) + replace: '{{ correct_value }}' + when: opensshserver.found is defined and opensshserver.found != 1 + when: opensshserver_file.stat.exists and opensshserver_file.stat.size > correct_value|length + tags: + - CCE-85899-3 + - DISA-STIG-RHEL-08-010290 + - NIST-800-53-AC-17(2) + - harden_sshd_macs_opensshserver_conf_crypto_policy + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + +sshd_approved_macs='' + + +CONF_FILE=/etc/crypto-policies/back-ends/opensshserver.config +correct_value="-oMACs=${sshd_approved_macs}" + +# Test if file exists +test -f ${CONF_FILE} || touch ${CONF_FILE} + +# Ensure CRYPTO_POLICY is not commented out +sed -i 's/#CRYPTO_POLICY=/CRYPTO_POLICY=/' ${CONF_FILE} + +grep -q "'${correct_value}'" ${CONF_FILE} + +if [[ $? -ne 0 ]]; then + # We need to get the existing value, using PCRE to maintain same regex + existing_value=$(grep -Po '(-oMACs=\S+)' ${CONF_FILE}) + + if [[ ! -z ${existing_value} ]]; then + # replace existing_value with correct_value + sed -i "s/${existing_value}/${correct_value}/g" ${CONF_FILE} + else + # ***NOTE*** # + # This probably means this file is not here or it's been modified + # unintentionally. + # ********** # + # echo correct_value to end + echo "CRYPTO_POLICY='${correct_value}'" >> ${CONF_FILE} + fi +fi + + + + + + + + + + + OpenSSL uses strong entropy source + By default, OpenSSL doesn't always use a SP800-90A compliant random number generator. +A way to configure OpenSSL to always use a strong source is to setup a wrapper that +defines a shell function that shadows the actual openssl binary, +and that ensures that the -rand /dev/random option is added to every openssl invocation. + +To do so, place the following shell snippet exactly as-is to /etc/profile.d/openssl-rand.sh: + +# provide a default -rand /dev/random option to openssl commands that +# support it + +# written inefficiently for maximum shell compatibility +openssl() +( + openssl_bin=/usr/bin/openssl + + case "$*" in + # if user specified -rand, honor it + *\ -rand\ *|*\ -help*) exec $openssl_bin "$@" ;; + esac + + cmds=`$openssl_bin list -digest-commands -cipher-commands | tr '\n' ' '` + for i in `$openssl_bin list -commands`; do + if $openssl_bin list -options "$i" | grep -q '^rand '; then + cmds=" $i $cmds" + fi + done + + case "$cmds" in + *\ "$1"\ *) + cmd="$1"; shift + exec $openssl_bin "$cmd" -rand /dev/random "$@" ;; + esac + + exec $openssl_bin "$@" +) + + This setting can cause problems on computers without the hardware random generator, because insufficient entropy blocks the program until enough entropy is available. + 1277 + 1552 + FCS_RBG_EXT.1.2 + SRG-OS-000480-GPOS-00227 + This rule ensures that openssl invocations always uses SP800-90A compliant random number generator as a default behavior. + CCE-82721-2 + - name: Put a file with shell wrapper to configure OpenSSL to always use strong entropy + copy: + dest: /etc/profile.d/openssl-rand.sh + content: | + # provide a default -rand /dev/random option to openssl commands that + # support it + + # written inefficiently for maximum shell compatibility + openssl() + ( + openssl_bin=/usr/bin/openssl + + case "$*" in + # if user specified -rand, honor it + *\ -rand\ *|*\ -help*) exec $openssl_bin "$@" ;; + esac + + cmds=`$openssl_bin list -digest-commands -cipher-commands | tr '\n' ' '` + for i in `$openssl_bin list -commands`; do + if $openssl_bin list -options "$i" | grep -q '^rand '; then + cmds=" $i $cmds" + fi + done + + case "$cmds" in + *\ "$1"\ *) + cmd="$1"; shift + exec $openssl_bin "$cmd" -rand /dev/random "$@" ;; + esac + + exec $openssl_bin "$@" + ) + tags: + - CCE-82721-2 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - openssl_use_strong_entropy + - restrict_strategy + + +cat > /etc/profile.d/openssl-rand.sh <<- 'EOM' +# provide a default -rand /dev/random option to openssl commands that +# support it + +# written inefficiently for maximum shell compatibility +openssl() +( + openssl_bin=/usr/bin/openssl + + case "$*" in + # if user specified -rand, honor it + *\ -rand\ *|*\ -help*) exec $openssl_bin "$@" ;; + esac + + cmds=`$openssl_bin list -digest-commands -cipher-commands | tr '\n' ' '` + for i in `$openssl_bin list -commands`; do + if $openssl_bin list -options "$i" | grep -q '^rand '; then + cmds=" $i $cmds" + fi + done + + case "$cmds" in + *\ "$1"\ *) + cmd="$1"; shift + exec $openssl_bin "$cmd" -rand /dev/random "$@" ;; + esac + + exec $openssl_bin "$@" +) +EOM + + + + + + + + + + + Operating System Vendor Support and Certification + The assurance of a vendor to provide operating system support and maintenance +for their product is an important criterion to ensure product stability and +security over the life of the product. A certified product that follows the +necessary standards and government certification requirements guarantees that +known software vulnerabilities will be remediated, and proper guidance for +protecting and securing the operating system will be given. + + The Installed Operating System Is FIPS 140-2 Certified + To enable processing of sensitive information the operating system must +provide certified cryptographic modules compliant with FIPS 140-2 +standard. + There is no remediation besides switching to a different operating system. + System Crypto Modules must be provided by a vendor that undergoes +FIPS-140 certifications. +FIPS-140 is applicable to all Federal agencies that use +cryptographic-based security systems to protect sensitive information +in computer and telecommunication systems (including voice systems) as +defined in Section 5131 of the Information Technology Management Reform +Act of 1996, Public Law 104-106. This standard shall be used in +designing and implementing cryptographic modules that Federal +departments and agencies operate or are operated for them under +contract. See https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.140-2.pdf +To meet this, the system has to have cryptographic software provided by +a vendor that has undergone this certification. This means providing +documentation, test results, design information, and independent third +party review by an accredited lab. While open source software is +capable of meeting this, it does not meet FIPS-140 unless the vendor +submits to this process. + CCI-000803 + CCI-002450 + CIP-003-8 R4.2 + CIP-007-3 R5.1 + SC-12(2) + SC-12(3) + IA-7 + SC-13 + CM-6(a) + SC-12 + SRG-OS-000120-VMM-000600 + SRG-OS-000478-VMM-001980 + SRG-OS-000396-VMM-001590 + The Federal Information Processing Standard (FIPS) Publication 140-2, (FIPS +PUB 140-2) is a computer security standard. The standard specifies security +requirements for cryptographic modules used to protect sensitive +unclassified information. Refer to the full FIPS 140-2 standard at + + http://csrc.nist.gov/publications/fips/fips140-2/fips1402.pdf +for further details on the requirements. +FIPS 140-2 validation is required by U.S. law when information systems use +cryptography to protect sensitive government information. In order to +achieve FIPS 140-2 certification, cryptographic modules are subject to +extensive testing by independent laboratories, accredited by National +Institute of Standards and Technology (NIST). + CCE-80830-3 + + + + + + + + + The Installed Operating System Is Vendor Supported + The installed operating system must be maintained by a vendor. + +Red Hat Enterprise Linux is supported by Red Hat, Inc. As the Red Hat Enterprise +Linux vendor, Red Hat, Inc. is responsible for providing security patches. + There is no remediation besides switching to a different operating system. + 18 + 20 + 4 + APO12.01 + APO12.02 + APO12.03 + APO12.04 + BAI03.10 + DSS05.01 + DSS05.02 + CCI-000366 + 4.2.3 + 4.2.3.12 + 4.2.3.7 + 4.2.3.9 + A.12.6.1 + A.14.2.3 + A.16.1.3 + A.18.2.2 + A.18.2.3 + CM-6(a) + MA-6 + SA-13(a) + ID.RA-1 + PR.IP-12 + SRG-OS-000480-GPOS-00227 + RHEL-08-010000 + SV-230221r743913_rule + An operating system is considered "supported" if the vendor continues to +provide security patches for the product. With an unsupported release, it +will not be possible to resolve any security issue discovered in the system +software. + CCE-80947-5 + + + + + + + + + + Endpoint Protection Software + Endpoint protection security software that is not provided or supported + +by Red Hat can be installed to provide complementary or duplicative + +security capabilities to those provided by the base platform. Add-on +software may not be appropriate for some specialized systems. + + Configure Backups of User Data + The operating system must conduct backups of user data contained +in the operating system. The operating system provides utilities for +automating backups of user data. Commercial and open-source products +are also available. + Operating system backup is a critical step in maintaining data assurance and +availability. User-level information is data generated by information system +and/or application users. Backups shall be consistent with organizational +recovery time and recovery point objectives. + + + + + + Install Virus Scanning Software + Virus scanning software can be used to protect a system from penetration from +computer viruses and to limit their spread through intermediate systems. + +The virus scanning software should be configured to perform scans dynamically +on accessed files. If this capability is not available, the system must be +configured to scan, at a minimum, all altered files on the system on a daily +basis. + +If the system processes inbound SMTP mail, the virus scanner must be configured +to scan all received mail. + 12 + 13 + 14 + 4 + 7 + 8 + APO01.06 + APO13.02 + BAI02.01 + BAI06.01 + DSS04.07 + DSS05.01 + DSS05.02 + DSS05.03 + DSS06.06 + CCI-000366 + CCI-001239 + CCI-001668 + 4.3.4.3.8 + 4.4.3.2 + SR 3.2 + SR 3.3 + SR 3.4 + SR 4.1 + A.12.2.1 + A.14.2.8 + A.8.2.3 + CM-6(a) + DE.CM-4 + DE.DP-3 + PR.DS-1 + SRG-OS-000480-GPOS-00227 + Virus scanning software can be used to detect if a system has been compromised by +computer viruses, as well as to limit their spread to other systems. + + CCE-83879-7 + + + + + + + + + Install Intrusion Detection Software + The base Red Hat Enterprise Linux 8 platform already includes a sophisticated auditing system that +can detect intruder activity, as well as SELinux, which provides host-based +intrusion prevention capabilities by confining privileged programs and user +sessions which may become compromised. + In DoD environments, supplemental intrusion detection and antivirus tools, +such as the McAfee Host-based Security System, are available to integrate with +existing infrastructure. Per DISA guidance, when these supplemental tools interfere +with proper functioning of SELinux, SELinux takes precedence. Should further +clarification be required, DISA contact information is published publicly at +https://public.cyber.mil/stigs/ + 1 + 12 + 13 + 14 + 15 + 16 + 18 + 7 + 8 + 9 + APO01.06 + APO13.01 + DSS01.03 + DSS01.05 + DSS03.05 + DSS05.02 + DSS05.04 + DSS05.07 + DSS06.02 + CCI-001263 + 4.3.3.4 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.2 + SR 7.1 + SR 7.6 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-6(a) + DE.CM-1 + PR.AC-5 + PR.DS-5 + PR.PT-4 + Req-11.4 + Host-based intrusion detection tools provide a system-level defense when an +intruder gains access to a system or network. + + + CCE-80831-1 + + + + + + + + + McAfee Endpoint Security Software + In DoD environments, McAfee Host-based Security System (HBSS) and +VirusScan Enterprise for Linux (VSEL) is required to be installed on all systems. + + The age of McAfee defintion file before requiring updating + Specify the amount of time (in seconds) before McAfee definition files need to be +updated. + 2592000 + 86400 + 604800 + 2592000 + + + Enable nails Service + The nails service is used to run McAfee VirusScan Enterprise +for Linux and McAfee Host-based Security System (HBSS) services. + +The nails service can be enabled with the following command: +$ sudo systemctl enable nails.service + 12 + 13 + 14 + 4 + 7 + 8 + APO01.06 + APO13.02 + BAI02.01 + BAI06.01 + DSS04.07 + DSS05.01 + DSS05.02 + DSS05.03 + DSS06.06 + CCI-000366 + CCI-001239 + CCI-001668 + 4.3.4.3.8 + 4.4.3.2 + SR 3.2 + SR 3.3 + SR 3.4 + SR 4.1 + A.12.2.1 + A.14.2.8 + A.8.2.3 + CM-6(a) + SC-28 + SI-3(a) + DE.CM-4 + DE.DP-3 + PR.DS-1 + SRG-OS-000480-GPOS-00227 + Virus scanning software can be used to detect if a system has been compromised by +computer viruses, as well as to limit their spread to other systems. + + include enable_nails + +class enable_nails { + service {'nails': + enable => true, + ensure => 'running', + } +} + + - name: Enable service nails + block: + + - name: Gather the package facts + package_facts: + manager: auto + + - name: Enable service nails + service: + name: nails + enabled: 'yes' + state: started + masked: 'no' + when: + - '"nails" in ansible_facts.packages' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-CM-6(a) + - NIST-800-53-SC-28 + - NIST-800-53-SI-3(a) + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_nails_enabled + + +[customizations.services] +enabled = ["nails"] + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" unmask 'nails.service' +"$SYSTEMCTL_EXEC" start 'nails.service' +"$SYSTEMCTL_EXEC" enable 'nails.service' + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Install McAfee Virus Scanning Software + Install McAfee VirusScan Enterprise for Linux antivirus software +which is provided for DoD systems and uses signatures to search for the +presence of viruses on the filesystem. + Due to McAfee HIPS being 3rd party software, automated +remediation is not available for this configuration check. + 12 + 13 + 14 + 4 + 7 + 8 + APO01.06 + APO13.02 + BAI02.01 + BAI06.01 + DSS04.07 + DSS05.01 + DSS05.02 + DSS05.03 + DSS06.06 + CCI-000366 + CCI-001239 + CCI-001668 + 4.3.4.3.8 + 4.4.3.2 + SR 3.2 + SR 3.3 + SR 3.4 + SR 4.1 + A.12.2.1 + A.14.2.8 + A.8.2.3 + CM-6(a) + SC-28 + SI-3(a) + DE.CM-4 + DE.DP-3 + PR.DS-1 + SRG-OS-000480-GPOS-00227 + Virus scanning software can be used to detect if a system has been compromised by +computer viruses, as well as to limit their spread to other systems. + + + + + + + + + + Install the McAfee Runtime Libraries and Linux Agent + Install the McAfee Runtime Libraries (MFErt) and Linux Agent (MFEcma). + The McAfee Runtime Libraries (MFErt) and Linux Agent (MFEcma) are dependencies +for VirusScan Enterprise for Linux (VSEL) and Host-based Security System (HBSS) +to run. + + + + + + + + + Virus Scanning Software Definitions Are Updated + Ensure virus definition files are no older than 7 days or their last release. + 12 + 13 + 14 + 4 + 7 + 8 + APO01.06 + APO13.02 + BAI02.01 + BAI06.01 + DSS04.07 + DSS05.01 + DSS05.02 + DSS05.03 + DSS06.06 + CCI-000366 + CCI-001239 + CCI-001668 + 4.3.4.3.8 + 4.4.3.2 + SR 3.2 + SR 3.3 + SR 3.4 + SR 4.1 + A.12.2.1 + A.14.2.8 + A.8.2.3 + CM-6(a) + SC-28 + SI-3(a) + SI-3(b) + SI-3(2) + DE.CM-4 + DE.DP-3 + PR.DS-1 + SRG-OS-000480-GPOS-00227 + Virus scanning software can be used to detect if a system has been compromised by +computer viruses, as well as to limit their spread to other systems. + + + + + + + + + + + McAfee Endpoint Security for Linux (ENSL) + McAfee Endpoint Security for Linux (ENSL) is a suite of software applications +used to monitor, detect, and defend computer networks and systems. + + + Install McAfee Endpoint Security for Linux (ENSL) + Install McAfee Endpoint Security for Linux antivirus software +which is provided for DoD systems and uses signatures to search for the +presence of viruses on the filesystem. + +The McAfeeTP package can be installed with the following command: + +$ sudo yum install McAfeeTP + Due to McAfee Endpoint Security for Linux (ENSL) being 3rd party software, +automated remediation is not available for this configuration check. + CCI-001233 + SI-2(2) + SRG-OS-000191-GPOS-00080 + RHEL-08-010001 + SV-245540r754730_rule + Virus scanning software can be used to detect if a system has been compromised by +computer viruses, as well as to limit their spread to other systems. + CCE-86260-7 + + + + + + + + + Ensure McAfee Endpoint Security for Linux (ENSL) is running + Install McAfee Endpoint Security for Linux antivirus software +which is provided for DoD systems and uses signatures to search for the +presence of viruses on the filesystem. + Due to McAfee Endpoint Security for Linux (ENSL) being 3rd party software, +automated remediation is not available for this configuration check. + CCI-001233 + SI-2(2) + SRG-OS-000191-GPOS-00080 + RHEL-08-010001 + SV-245540r754730_rule + Virus scanning software can be used to detect if a system has been compromised by +computer viruses, as well as to limit their spread to other systems. + CCE-86261-5 + + + + + + + + + + McAfee Host-Based Intrusion Detection Software (HBSS) + McAfee Host-based Security System (HBSS) is a suite of software applications +used to monitor, detect, and defend computer networks and systems. + + Install the Host Intrusion Prevention System (HIPS) Module + Install the McAfee Host Intrusion Prevention System (HIPS) Module if it is absolutely +necessary. If SELinux is enabled, do not install or enable this module. + Installing and enabling this module conflicts with SELinux. +Per DoD/DISA guidance, SELinux takes precedence over this module. + Due to McAfee HIPS being 3rd party software, automated +remediation is not available for this configuration check. + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 18 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + APO01.06 + APO07.06 + APO08.04 + APO10.05 + APO11.06 + APO12.01 + APO12.02 + APO12.03 + APO12.04 + APO12.06 + APO13.01 + APO13.02 + BAI08.02 + BAI08.04 + DSS01.03 + DSS01.05 + DSS02.04 + DSS02.05 + DSS02.07 + DSS03.01 + DSS03.04 + DSS03.05 + DSS04.05 + DSS05.01 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + DSS06.01 + DSS06.02 + MEA03.03 + MEA03.04 + CCI-000366 + CCI-001233 + CCI-001263 + 4.2.3 + 4.2.3.12 + 4.2.3.7 + 4.2.3.9 + 4.3.3.4 + 4.3.4.5.2 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.3.4.5.9 + 4.4.3.2 + 4.4.3.3 + 4.4.3.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.4 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.3 + SR 3.5 + SR 3.8 + SR 3.9 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.12.1.1 + A.12.1.2 + A.12.4.1 + A.12.4.3 + A.12.5.1 + A.12.6.1 + A.12.6.2 + A.13.1.1 + A.13.1.2 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.14.2.7 + A.14.2.8 + A.15.2.1 + A.16.1.1 + A.16.1.2 + A.16.1.3 + A.16.1.4 + A.16.1.5 + A.16.1.6 + A.16.1.7 + A.18.1.4 + A.18.2.2 + A.18.2.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + Clause 16.1.2 + Clause 7.4 + CM-6(a) + DE.AE-1 + DE.AE-2 + DE.AE-3 + DE.AE-4 + DE.CM-1 + DE.CM-5 + DE.CM-6 + DE.CM-7 + DE.DP-2 + DE.DP-3 + DE.DP-4 + DE.DP-5 + ID.RA-1 + PR.AC-5 + PR.DS-5 + PR.IP-8 + PR.PT-4 + RS.AN-1 + RS.CO-3 + Req-11.4 + SRG-OS-000191-GPOS-00080 + SRG-OS-000196 + SRG-OS-000480-GPOS-00227 + Without a host-based intrusion detection tool, there is no system-level defense +when an intruder gains access to a system or network. Additionally, a host-based +intrusion prevention tool can provide methods to immediately lock out detected +intrusion attempts. + + +[[packages]] +name = "MFEhiplsm" +version = "*" + + + + + + + + + + Install the Asset Configuration Compliance Module (ACCM) + Install the Asset Configuration Compliance Module (ACCM). + Due to HBSS ACCM being 3rd party software, automated +remediation is not available for this configuration check. + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 18 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + APO01.06 + APO07.06 + APO08.04 + APO10.05 + APO11.06 + APO12.01 + APO12.02 + APO12.03 + APO12.04 + APO12.06 + APO13.01 + APO13.02 + BAI08.02 + BAI08.04 + DSS01.03 + DSS01.05 + DSS02.04 + DSS02.05 + DSS02.07 + DSS03.01 + DSS03.04 + DSS03.05 + DSS04.05 + DSS05.01 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + DSS06.01 + DSS06.02 + MEA03.03 + MEA03.04 + CCI-000366 + CCI-001263 + 4.2.3 + 4.2.3.12 + 4.2.3.7 + 4.2.3.9 + 4.3.3.4 + 4.3.4.5.2 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.3.4.5.9 + 4.4.3.2 + 4.4.3.3 + 4.4.3.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.4 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.3 + SR 3.5 + SR 3.8 + SR 3.9 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.12.1.1 + A.12.1.2 + A.12.4.1 + A.12.4.3 + A.12.5.1 + A.12.6.1 + A.12.6.2 + A.13.1.1 + A.13.1.2 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.14.2.7 + A.14.2.8 + A.15.2.1 + A.16.1.1 + A.16.1.2 + A.16.1.3 + A.16.1.4 + A.16.1.5 + A.16.1.6 + A.16.1.7 + A.18.1.4 + A.18.2.2 + A.18.2.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + Clause 16.1.2 + Clause 7.4 + CM-6(a) + DE.AE-1 + DE.AE-2 + DE.AE-3 + DE.AE-4 + DE.CM-1 + DE.CM-5 + DE.CM-6 + DE.CM-7 + DE.DP-2 + DE.DP-3 + DE.DP-4 + DE.DP-5 + ID.RA-1 + PR.AC-5 + PR.DS-5 + PR.IP-8 + PR.PT-4 + RS.AN-1 + RS.CO-3 + Req-11.4 + SRG-OS-000480-GPOS-00227 + Without a host-based intrusion detection tool, there is no system-level defense +when an intruder gains access to a system or network. Additionally, a host-based +intrusion prevention tool can provide methods to immediately lock out detected +intrusion attempts. + + + + + + + + + Install the Policy Auditor (PA) Module + Install the Policy Auditor (PA) Module. + Due to McAfee being 3rd party software, automated +remediation is not available for this configuration check. + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 18 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + APO01.06 + APO07.06 + APO08.04 + APO10.05 + APO11.06 + APO12.01 + APO12.02 + APO12.03 + APO12.04 + APO12.06 + APO13.01 + APO13.02 + BAI08.02 + BAI08.04 + DSS01.03 + DSS01.05 + DSS02.04 + DSS02.05 + DSS02.07 + DSS03.01 + DSS03.04 + DSS03.05 + DSS04.05 + DSS05.01 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + DSS06.01 + DSS06.02 + MEA03.03 + MEA03.04 + CCI-000366 + CCI-001263 + 4.2.3 + 4.2.3.12 + 4.2.3.7 + 4.2.3.9 + 4.3.3.4 + 4.3.4.5.2 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.3.4.5.9 + 4.4.3.2 + 4.4.3.3 + 4.4.3.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.4 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.3 + SR 3.5 + SR 3.8 + SR 3.9 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.12.1.1 + A.12.1.2 + A.12.4.1 + A.12.4.3 + A.12.5.1 + A.12.6.1 + A.12.6.2 + A.13.1.1 + A.13.1.2 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.14.2.7 + A.14.2.8 + A.15.2.1 + A.16.1.1 + A.16.1.2 + A.16.1.3 + A.16.1.4 + A.16.1.5 + A.16.1.6 + A.16.1.7 + A.18.1.4 + A.18.2.2 + A.18.2.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + Clause 16.1.2 + Clause 7.4 + CM-6(a) + DE.AE-1 + DE.AE-2 + DE.AE-3 + DE.AE-4 + DE.CM-1 + DE.CM-5 + DE.CM-6 + DE.CM-7 + DE.DP-2 + DE.DP-3 + DE.DP-4 + DE.DP-5 + ID.RA-1 + PR.AC-5 + PR.DS-5 + PR.IP-8 + PR.PT-4 + RS.AN-1 + RS.CO-3 + Req-11.4 + SRG-OS-000480-GPOS-00227 + Without a host-based intrusion detection tool, there is no system-level defense +when an intruder gains access to a system or network. Additionally, a host-based +intrusion prevention tool can provide methods to immediately lock out detected +intrusion attempts. + + + + + + + + + + + + + Disk Partitioning + To ensure separation and protection of data, there +are top-level system directories which should be placed on their +own physical partition or logical volume. The installer's default +partitioning scheme creates separate logical volumes for +/, /boot, and swap. +If starting with any of the default layouts, check the box to +\"Review and modify partitioning.\" This allows for the easy creation +of additional logical volumes inside the volume group already +created, though it may require making /'s logical volume smaller to +create space. In general, using logical volumes is preferable to +using partitions because they can be more easily adjusted +later.If creating a custom layout, create the partitions mentioned in +the previous paragraph (which the installer will require anyway), +as well as separate ones described in the following sections. +If a system has already been installed, and the default +partitioning +scheme was used, it is possible but nontrivial to +modify it to create separate logical volumes for the directories +listed above. The Logical Volume Manager (LVM) makes this possible. +See the LVM HOWTO at + http://tldp.org/HOWTO/LVM-HOWTO/ +for more detailed information on LVM. + + Encrypt Partitions + Red Hat Enterprise Linux 8 natively supports partition encryption through the +Linux Unified Key Setup-on-disk-format (LUKS) technology. The easiest way to +encrypt a partition is during installation time. + +For manual installations, select the Encrypt checkbox during +partition creation to encrypt the partition. When this +option is selected the system will prompt for a passphrase to use in +decrypting the partition. The passphrase will subsequently need to be entered manually +every time the system boots. + + +For automated/unattended installations, it is possible to use Kickstart by adding +the --encrypted and --passphrase= options to the definition of each partition to be +encrypted. For example, the following line would encrypt the root partition: +part / --fstype=ext4 --size=100 --onpart=hda1 --encrypted --passphrase=PASSPHRASE +Any PASSPHRASE is stored in the Kickstart in plaintext, and the Kickstart +must then be protected accordingly. +Omitting the --passphrase= option from the partition definition will cause the +installer to pause and interactively ask for the passphrase during installation. + +By default, the Anaconda installer uses aes-xts-plain64 cipher +with a minimum 512 bit key size which should be compatible with FIPS enabled. + + +Detailed information on encrypting partitions using LUKS or LUKS ciphers can be found on +the Red Hat Enterprise Linux 8 Documentation web site: + + + https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/8/html/security_hardening/encrypting-block-devices-using-luks_security-hardening +. + 13 + 14 + APO01.06 + BAI02.01 + BAI06.01 + DSS04.07 + DSS05.03 + DSS05.04 + DSS05.07 + DSS06.02 + DSS06.06 + 3.13.16 + CCI-001199 + CCI-002475 + CCI-002476 + 164.308(a)(1)(ii)(D) + 164.308(b)(1) + 164.310(d) + 164.312(a)(1) + 164.312(a)(2)(iii) + 164.312(a)(2)(iv) + 164.312(b) + 164.312(c) + 164.314(b)(2)(i) + 164.312(d) + SR 3.4 + SR 4.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R4.2 + CIP-007-3 R5.1 + CM-6(a) + SC-28 + SC-28(1) + SC-13 + AU-9(3) + PR.DS-1 + PR.DS-5 + SRG-OS-000405-GPOS-00184 + SRG-OS-000185-GPOS-00079 + SRG-OS-000404-GPOS-00183 + SRG-OS-000404-VMM-001650 + SRG-OS-000405-VMM-001660 + RHEL-08-010030 + SV-230224r809268_rule + The risk of a system's physical compromise, particularly mobile systems such as +laptops, places its data at risk of compromise. Encrypting this data mitigates +the risk of its loss if the system is lost. + + CCE-80789-1 + + + + + + Ensure /boot Located On Separate Partition + It is recommended that the /boot directory resides on a separate +partition. This makes it easier to apply restrictions e.g. through the +noexec mount option. Eventually, the /boot partition can +be configured not to be mounted automatically with the noauto mount +option. + BP28(R12) + The /boot partition contains the kernel and bootloader files. +Access to this partition should be restricted. + + CCE-83336-8 + +part /boot + + +[[customizations.filesystem]] +mountpoint = "/boot" +size = 1073741824 + + + + + + + + + + Ensure /home Located On Separate Partition + If user home directories will be stored locally, create a separate partition +for /home at installation time (or migrate it later using LVM). If +/home will be mounted from another system such as an NFS server, then +creating a separate partition is not necessary at installation time, and the +mountpoint can instead be configured later. + BP28(R12) + 12 + 15 + 8 + APO13.01 + DSS05.02 + CCI-000366 + CCI-001208 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + A.13.1.1 + A.13.2.1 + A.14.1.3 + CM-6(a) + SC-5(2) + PR.PT-4 + SRG-OS-000480-GPOS-00227 + RHEL-08-010800 + 1.1.7.1 + SV-230328r627750_rule + Ensuring that /home is mounted on its own partition enables the +setting of more restrictive mount options, and also helps ensure that +users cannot trivially fill partitions used for log or audit data storage. + + CCE-81044-0 + +part /home + + +[[customizations.filesystem]] +mountpoint = "/home" +size = 1073741824 + + + + + + + + + + Ensure /opt Located On Separate Partition + It is recommended that the /opt directory resides on a separate +partition. + BP28(R12) + The /opt partition contains additional software, usually installed +outside the packaging system. Putting this directory on a separate partition +makes it easier to apply restrictions e.g. through the nosuid mount +option. + + CCE-83340-0 + +part /opt + + +[[customizations.filesystem]] +mountpoint = "/opt" +size = 1073741824 + + + + + + + + + + Ensure /srv Located On Separate Partition + If a file server (FTP, TFTP...) is hosted locally, create a separate partition +for /srv at installation time (or migrate it later using LVM). If +/srv will be mounted from another system such as an NFS server, then +creating a separate partition is not necessary at installation time, and the +mountpoint can instead be configured later. + BP28(R12) + Srv deserves files for local network file server such as FTP. Ensuring +that /srv is mounted on its own partition enables the setting of +more restrictive mount options, and also helps ensure that +users cannot trivially fill partitions used for log or audit data storage. + + CCE-83387-1 + +part /srv + + +[[customizations.filesystem]] +mountpoint = "/srv" +size = 1073741824 + + + + + + + + + + Ensure /tmp Located On Separate Partition + The /tmp directory is a world-writable directory used +for temporary file storage. Ensure it has its own partition or +logical volume at installation time, or migrate it using LVM. + BP28(R12) + 12 + 15 + 8 + APO13.01 + DSS05.02 + CCI-000366 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + A.13.1.1 + A.13.2.1 + A.14.1.3 + CM-6(a) + SC-5(2) + PR.PT-4 + SRG-OS-000480-GPOS-00227 + RHEL-08-010543 + 1.1.2.1 + SV-230295r627750_rule + The /tmp partition is used as temporary storage by many programs. +Placing /tmp in its own partition enables the setting of more +restrictive mount options, which can help protect programs which use it. + + CCE-80851-9 + +part /tmp + + +[[customizations.filesystem]] +mountpoint = "/tmp" +size = 1073741824 + + + + + + + + + + Ensure /usr Located On Separate Partition + It is recommended that the /usr directory resides on a separate +partition. + BP28(R12) + The /usr partition contains system software, utilities and files. +Putting it on a separate partition allows limiting its size and applying +restrictions through mount options. + + CCE-83343-4 + +part /usr + + +[[customizations.filesystem]] +mountpoint = "/usr" +size = 5368709120 + + + + + + + + + + Ensure /var Located On Separate Partition + The /var directory is used by daemons and other system +services to store frequently-changing data. Ensure that /var has its own partition +or logical volume at installation time, or migrate it using LVM. + BP28(R12) + 12 + 15 + 8 + APO13.01 + DSS05.02 + CCI-000366 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + A.13.1.1 + A.13.2.1 + A.14.1.3 + CM-6(a) + SC-5(2) + PR.PT-4 + SRG-OS-000480-GPOS-00227 + SRG-OS-000341-VMM-001220 + RHEL-08-010540 + 1.1.3.1 + SV-230292r627750_rule + Ensuring that /var is mounted on its own partition enables the +setting of more restrictive mount options. This helps protect +system services such as daemons or other programs which use it. +It is not uncommon for the /var directory to contain +world-writable directories installed by other software packages. + + CCE-80852-7 + +part /var + + +[[customizations.filesystem]] +mountpoint = "/var" +size = 3221225472 + + + + + + + + + + Ensure /var/log Located On Separate Partition + System logs are stored in the /var/log directory. + +Ensure that /var/log has its own partition or logical +volume at installation time, or migrate it using LVM. + BP28(R12) + BP28(R47) + 1 + 12 + 14 + 15 + 16 + 3 + 5 + 6 + 8 + APO11.04 + APO13.01 + BAI03.05 + DSS05.02 + DSS05.04 + DSS05.07 + MEA02.01 + CCI-000366 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + CIP-007-3 R6.5 + CM-6(a) + AU-4 + SC-5(2) + PR.PT-1 + PR.PT-4 + SRG-OS-000480-GPOS-00227 + RHEL-08-010541 + 1.1.5.1 + SV-230293r627750_rule + Placing /var/log in its own partition +enables better separation between log files +and other files in /var/. + + CCE-80853-5 + +part /var/log + + +[[customizations.filesystem]] +mountpoint = "/var/log" +size = 5368709120 + + + + + + + + + + Ensure /var/log/audit Located On Separate Partition + Audit logs are stored in the /var/log/audit directory. + +Ensure that /var/log/audit has its own partition or logical +volume at installation time, or migrate it using LVM. +Make absolutely certain that it is large enough to store all +audit logs that will be created by the auditing daemon. + BP28(R43) + 1 + 12 + 13 + 14 + 15 + 16 + 2 + 3 + 5 + 6 + 8 + APO11.04 + APO13.01 + BAI03.05 + BAI04.04 + DSS05.02 + DSS05.04 + DSS05.07 + MEA02.01 + CCI-000366 + CCI-001849 + 164.312(a)(2)(ii) + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.2 + SR 7.6 + A.12.1.3 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.17.2.1 + CIP-007-3 R6.5 + CM-6(a) + AU-4 + SC-5(2) + PR.DS-4 + PR.PT-1 + PR.PT-4 + FMT_SMF_EXT.1 + SRG-OS-000341-GPOS-00132 + SRG-OS-000480-GPOS-00227 + SRG-OS-000341-VMM-001220 + RHEL-08-010542 + 1.1.6.1 + SV-230294r627750_rule + Placing /var/log/audit in its own partition +enables better separation between audit files +and other files, and helps ensure that +auditing cannot be halted due to the partition running out +of space. + + CCE-80854-3 + +part /var/log/audit + + +[[customizations.filesystem]] +mountpoint = "/var/log/audit" +size = 10737418240 + + + + + + + + + + Ensure /var/tmp Located On Separate Partition + The /var/tmp directory is a world-writable directory used +for temporary file storage. Ensure it has its own partition or +logical volume at installation time, or migrate it using LVM. + BP28(R12) + SRG-OS-000480-GPOS-00227 + RHEL-08-010544 + 1.1.4.1 + SV-244529r743836_rule + The /var/tmp partition is used as temporary storage by many programs. +Placing /var/tmp in its own partition enables the setting of more +restrictive mount options, which can help protect programs which use it. + + CCE-82730-3 + +part /var/tmp + + +[[customizations.filesystem]] +mountpoint = "/var/tmp" +size = 1073741824 + + + + + + + + + + + GNOME Desktop Environment + GNOME is a graphical desktop environment bundled with many Linux distributions that +allow users to easily interact with the operating system graphically rather than +textually. The GNOME Graphical Display Manager (GDM) provides login, logout, and user +switching contexts as well as display server management. + +GNOME is developed by the GNOME Project and is considered the default + +Red Hat Graphical environment. + + +For more information on GNOME and the GNOME Project, see https://www.gnome.org. + + + Remove the GDM Package Group + +By removing the gdm package, the system no longer has GNOME installed + +installed. If X Windows is not installed then the system cannot boot into graphical user mode. +This prevents the system from being accidentally or maliciously booted into a graphical.target +mode. To do so, run the following command: + +$ sudo yum remove gdm + CM-7(a) + CM-7(b) + CM-6(a) + SRG-OS-000480-GPOS-00227 + Unnecessary service packages must not be installed to decrease the attack surface of the system. +A graphical environment is unnecessary for certain types of systems including a virtualization +hypervisor. + CCE-82367-4 + +package --remove=gdm + + include remove_gdm + +class remove_gdm { + package { 'gdm': + ensure => 'purged', + } +} + + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-82367-4 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_gdm_removed + +- name: Ensure gdm is removed + package: + name: gdm + state: absent + when: '"gdm" in ansible_facts.packages' + tags: + - CCE-82367-4 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_gdm_removed + + # Remediation is applicable only in certain platforms +if rpm --quiet -q gdm; then + +# CAUTION: This remediation script will remove gdm +# from the system, and may remove any packages +# that depend on gdm. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "gdm" ; then + + yum remove -y "gdm" + +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Make sure that the dconf databases are up-to-date with regards to respective keyfiles + By default, DConf uses a binary database as a data backend. +The system-level database is compiled from keyfiles in the /etc/dconf/db/ +directory by the dconf update command. More specifically, content present +in the following directories: +/etc/dconf/db/gdm.d +/etc/dconf/db/local.d + 164.308(a)(1)(ii)(B) + 164.308(a)(5)(ii)(A) + SRG-OS-000480-GPOS-00227 + 1.8.2 + Unlike text-based keyfiles, the binary database is impossible to check by OVAL. +Therefore, in order to evaluate dconf configuration, both have to be true at the same time - +configuration files have to be compliant, and the database needs to be more recent than those keyfiles, +which gives confidence that it reflects them. + + CCE-81003-6 + # Remediation is applicable only in certain platforms +if rpm --quiet -q gdm && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +dconf update + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure GNOME3 DConf User Profile + By default, DConf provides a standard user profile. This profile contains a list +of DConf configuration databases. The user profile and database always take the +highest priority. As such the DConf User profile should always exist and be +configured correctly. + + +To make sure that the user profile is configured correctly, the /etc/dconf/profile/user +should be set as follows: +user-db:user +system-db:local +system-db:site +system-db:distro + + Failure to have a functional DConf profile prevents GNOME3 configuration settings +from being enforced for all users and allows various security risks. + + + + + + + + + + Configure GNOME Login Screen + In the default GNOME desktop, the login is displayed after system boot +and can display user accounts, allow users to reboot the system, and allow users to +login automatically and/or with a guest account. The login screen should be configured +to prevent such behavior. + + +For more information about enforcing preferences in the GNOME3 environment using the DConf +configuration system, see https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/7/html/desktop_migration_and_administration_guide/> and the man page dconf(1). + + Disable the GNOME3 Login Restart and Shutdown Buttons + In the default graphical environment, users logging directly into the +system are greeted with a login screen that allows any user, known or +unknown, the ability the ability to shutdown or restart the system. This +functionality should be disabled by setting +disable-restart-buttons to true. + +To disable, add or edit disable-restart-buttons to +/etc/dconf/db/gdm.d/00-security-settings. For example: +[org/gnome/login-screen] +disable-restart-buttons=true +Once the setting has been added, add a lock to +/etc/dconf/db/gdm.d/locks/00-security-settings-lock to prevent +user modification. For example: +/org/gnome/login-screen/disable-restart-buttons +After the settings have been set, run dconf update. + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + 3.1.2 + CCI-000366 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-6(a) + AC-6(1) + CM-7(b) + PR.AC-4 + PR.DS-5 + SRG-OS-000480-GPOS-00227 + A user who is at the console can reboot the system at the login screen. If restart or shutdown buttons +are pressed at the login screen, this can create the risk of short-term loss of availability of systems +due to reboot. + + - name: Gather the package facts + package_facts: + manager: auto + tags: + - NIST-800-171-3.1.2 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(b) + - dconf_gnome_disable_restart_shutdown + - high_severity + - low_complexity + - medium_disruption + - no_reboot_needed + - unknown_strategy + +- name: Disable the GNOME3 Login Restart and Shutdown Buttons + ini_file: + dest: /etc/dconf/db/gdm.d/00-security-settings + section: org/gnome/login-screen + option: disable-restart-buttons + value: 'true' + create: true + no_extra_spaces: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-171-3.1.2 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(b) + - dconf_gnome_disable_restart_shutdown + - high_severity + - low_complexity + - medium_disruption + - no_reboot_needed + - unknown_strategy + +- name: Prevent user modification of GNOME disablement of Login Restart and Shutdown + Buttons + lineinfile: + path: /etc/dconf/db/gdm.d/locks/00-security-settings-lock + regexp: ^/org/gnome/login-screen/disable-restart-buttons + line: /org/gnome/login-screen/disable-restart-buttons + create: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-171-3.1.2 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(b) + - dconf_gnome_disable_restart_shutdown + - high_severity + - low_complexity + - medium_disruption + - no_reboot_needed + - unknown_strategy + +- name: Dconf Update + command: dconf update + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-171-3.1.2 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(b) + - dconf_gnome_disable_restart_shutdown + - high_severity + - low_complexity + - medium_disruption + - no_reboot_needed + - unknown_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q gdm && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +# Check for setting in any of the DConf db directories +# If files contain ibus or distro, ignore them. +# The assignment assumes that individual filenames don't contain : +readarray -t SETTINGSFILES < <(grep -r "\\[org/gnome/login-screen\\]" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +DCONFFILE="/etc/dconf/db/gdm.d/00-security-settings" +DBDIR="/etc/dconf/db/gdm.d" + +mkdir -p "${DBDIR}" + +if [ "${#SETTINGSFILES[@]}" -eq 0 ] +then + [ ! -z ${DCONFFILE} ] || echo "" >> ${DCONFFILE} + printf '%s\n' "[org/gnome/login-screen]" >> ${DCONFFILE} + printf '%s=%s\n' "disable-restart-buttons" "true" >> ${DCONFFILE} +else + escaped_value="$(sed -e 's/\\/\\\\/g' <<< "true")" + if grep -q "^\\s*disable-restart-buttons\\s*=" "${SETTINGSFILES[@]}" + then + + sed -i "s/\\s*disable-restart-buttons\\s*=\\s*.*/disable-restart-buttons=${escaped_value}/g" "${SETTINGSFILES[@]}" + else + sed -i "\\|\\[org/gnome/login-screen\\]|a\\disable-restart-buttons=${escaped_value}" "${SETTINGSFILES[@]}" + fi +fi + +dconf update +# Check for setting in any of the DConf db directories +LOCKFILES=$(grep -r "^/org/gnome/login-screen/disable-restart-buttons$" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +LOCKSFOLDER="/etc/dconf/db/gdm.d/locks" + +mkdir -p "${LOCKSFOLDER}" + +if [[ -z "${LOCKFILES}" ]] +then + echo "/org/gnome/login-screen/disable-restart-buttons" >> "/etc/dconf/db/gdm.d/locks/00-security-settings-lock" +fi + +dconf update + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable the GNOME3 Login User List + In the default graphical environment, users logging directly into the +system are greeted with a login screen that displays all known users. +This functionality should be disabled by setting disable-user-list +to true. + +To disable, add or edit disable-user-list to +/etc/dconf/db/gdm.d/00-security-settings. For example: +[org/gnome/login-screen] +disable-user-list=true +Once the setting has been added, add a lock to +/etc/dconf/db/gdm.d/locks/00-security-settings-lock to prevent +user modification. For example: +/org/gnome/login-screen/disable-user-list +After the settings have been set, run dconf update. + CM-6(a) + AC-23 + SRG-OS-000480-GPOS-00227 + RHEL-08-020032 + SV-244536r743857_rule + Leaving the user list enabled is a security risk since it allows anyone +with physical access to the system to quickly enumerate known user accounts +without logging in. + + CCE-86195-5 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-86195-5 + - DISA-STIG-RHEL-08-020032 + - NIST-800-53-AC-23 + - NIST-800-53-CM-6(a) + - dconf_gnome_disable_user_list + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Disable the GNOME3 Login User List + ini_file: + dest: /etc/dconf/db/gdm.d/00-security-settings + section: org/gnome/login-screen + option: disable-user-list + value: 'true' + no_extra_spaces: true + create: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86195-5 + - DISA-STIG-RHEL-08-020032 + - NIST-800-53-AC-23 + - NIST-800-53-CM-6(a) + - dconf_gnome_disable_user_list + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Prevent user modification of GNOME3 disablement of Login User List + lineinfile: + path: /etc/dconf/db/gdm.d/locks/00-security-settings-lock + regexp: ^/org/gnome/login-screen/disable-user-list$ + line: /org/gnome/login-screen/disable-user-list + create: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86195-5 + - DISA-STIG-RHEL-08-020032 + - NIST-800-53-AC-23 + - NIST-800-53-CM-6(a) + - dconf_gnome_disable_user_list + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Dconf Update + command: dconf update + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86195-5 + - DISA-STIG-RHEL-08-020032 + - NIST-800-53-AC-23 + - NIST-800-53-CM-6(a) + - dconf_gnome_disable_user_list + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q gdm && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +# Check for setting in any of the DConf db directories +# If files contain ibus or distro, ignore them. +# The assignment assumes that individual filenames don't contain : +readarray -t SETTINGSFILES < <(grep -r "\\[org/gnome/login-screen\\]" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +DCONFFILE="/etc/dconf/db/gdm.d/00-security-settings" +DBDIR="/etc/dconf/db/gdm.d" + +mkdir -p "${DBDIR}" + +if [ "${#SETTINGSFILES[@]}" -eq 0 ] +then + [ ! -z ${DCONFFILE} ] || echo "" >> ${DCONFFILE} + printf '%s\n' "[org/gnome/login-screen]" >> ${DCONFFILE} + printf '%s=%s\n' "disable-user-list" "true" >> ${DCONFFILE} +else + escaped_value="$(sed -e 's/\\/\\\\/g' <<< "true")" + if grep -q "^\\s*disable-user-list\\s*=" "${SETTINGSFILES[@]}" + then + + sed -i "s/\\s*disable-user-list\\s*=\\s*.*/disable-user-list=${escaped_value}/g" "${SETTINGSFILES[@]}" + else + sed -i "\\|\\[org/gnome/login-screen\\]|a\\disable-user-list=${escaped_value}" "${SETTINGSFILES[@]}" + fi +fi + +dconf update +# Check for setting in any of the DConf db directories +LOCKFILES=$(grep -r "^/org/gnome/login-screen/disable-user-list$" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +LOCKSFOLDER="/etc/dconf/db/gdm.d/locks" + +mkdir -p "${LOCKSFOLDER}" + +if [[ -z "${LOCKFILES}" ]] +then + echo "/org/gnome/login-screen/disable-user-list" >> "/etc/dconf/db/gdm.d/locks/00-security-settings-lock" +fi + +dconf update + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Enable the GNOME3 Login Smartcard Authentication + In the default graphical environment, smart card authentication +can be enabled on the login screen by setting enable-smartcard-authentication +to true. + +To enable, add or edit enable-smartcard-authentication to +/etc/dconf/db/gdm.d/00-security-settings. For example: +[org/gnome/login-screen] +enable-smartcard-authentication=true +Once the setting has been added, add a lock to +/etc/dconf/db/gdm.d/locks/00-security-settings-lock to prevent user modification. +For example: +/org/gnome/login-screen/enable-smartcard-authentication +After the settings have been set, run dconf update. + CCI-000765 + CCI-000766 + CCI-000767 + CCI-000768 + CCI-000771 + CCI-000772 + CCI-000884 + CCI-001948 + CCI-001954 + IA-2(3) + IA-2(4) + IA-2(8) + IA-2(9) + IA-2(11) + Req-8.3 + SRG-OS-000375-GPOS-00160 + SRG-OS-000376-GPOS-00161 + SRG-OS-000377-GPOS-00162 + Smart card login provides two-factor authentication stronger than +that provided by a username and password combination. Smart cards leverage PKI +(public key infrastructure) in order to provide and verify credentials. + + - name: Gather the package facts + package_facts: + manager: auto + tags: + - NIST-800-53-IA-2(11) + - NIST-800-53-IA-2(3) + - NIST-800-53-IA-2(4) + - NIST-800-53-IA-2(8) + - NIST-800-53-IA-2(9) + - PCI-DSS-Req-8.3 + - dconf_gnome_enable_smartcard_auth + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Enable the GNOME3 Login Smartcard Authentication + ini_file: + dest: /etc/dconf/db/gdm.d/00-security-settings + section: org/gnome/login-screen + option: enable-smartcard-authentication + value: 'true' + create: true + no_extra_spaces: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-IA-2(11) + - NIST-800-53-IA-2(3) + - NIST-800-53-IA-2(4) + - NIST-800-53-IA-2(8) + - NIST-800-53-IA-2(9) + - PCI-DSS-Req-8.3 + - dconf_gnome_enable_smartcard_auth + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Prevent user modification of GNOME3 disablement of Smartcard Authentication + lineinfile: + path: /etc/dconf/db/gdm.d/locks/00-security-settings-lock + regexp: ^/org/gnome/login-screen/enable-smartcard-authentication$ + line: /org/gnome/login-screen/enable-smartcard-authentication + create: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-IA-2(11) + - NIST-800-53-IA-2(3) + - NIST-800-53-IA-2(4) + - NIST-800-53-IA-2(8) + - NIST-800-53-IA-2(9) + - PCI-DSS-Req-8.3 + - dconf_gnome_enable_smartcard_auth + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Dconf Update + command: dconf update + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-IA-2(11) + - NIST-800-53-IA-2(3) + - NIST-800-53-IA-2(4) + - NIST-800-53-IA-2(8) + - NIST-800-53-IA-2(9) + - PCI-DSS-Req-8.3 + - dconf_gnome_enable_smartcard_auth + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q gdm && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +# Check for setting in any of the DConf db directories +# If files contain ibus or distro, ignore them. +# The assignment assumes that individual filenames don't contain : +readarray -t SETTINGSFILES < <(grep -r "\\[org/gnome/login-screen\\]" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +DCONFFILE="/etc/dconf/db/gdm.d/00-security-settings" +DBDIR="/etc/dconf/db/gdm.d" + +mkdir -p "${DBDIR}" + +if [ "${#SETTINGSFILES[@]}" -eq 0 ] +then + [ ! -z ${DCONFFILE} ] || echo "" >> ${DCONFFILE} + printf '%s\n' "[org/gnome/login-screen]" >> ${DCONFFILE} + printf '%s=%s\n' "enable-smartcard-authentication" "true" >> ${DCONFFILE} +else + escaped_value="$(sed -e 's/\\/\\\\/g' <<< "true")" + if grep -q "^\\s*enable-smartcard-authentication\\s*=" "${SETTINGSFILES[@]}" + then + + sed -i "s/\\s*enable-smartcard-authentication\\s*=\\s*.*/enable-smartcard-authentication=${escaped_value}/g" "${SETTINGSFILES[@]}" + else + sed -i "\\|\\[org/gnome/login-screen\\]|a\\enable-smartcard-authentication=${escaped_value}" "${SETTINGSFILES[@]}" + fi +fi + +dconf update +# Check for setting in any of the DConf db directories +LOCKFILES=$(grep -r "^/org/gnome/login-screen/enable-smartcard-authentication$" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +LOCKSFOLDER="/etc/dconf/db/gdm.d/locks" + +mkdir -p "${LOCKSFOLDER}" + +if [[ -z "${LOCKFILES}" ]] +then + echo "/org/gnome/login-screen/enable-smartcard-authentication" >> "/etc/dconf/db/gdm.d/locks/00-security-settings-lock" +fi + +dconf update + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Enable the GNOME3 Screen Locking On Smartcard Removal + In the default graphical environment, screen locking on smartcard removal +can be enabled by setting removal-action +to 'lock-screen'. + +To enable, add or edit removal-action to +/etc/dconf/db/local.d/00-security-settings. For example: +[org/gnome/settings-daemon/peripherals/smartcard] +removal-action='lock-screen' +Once the setting has been added, add a lock to +/etc/dconf/db/local.d/locks/00-security-settings-lock to prevent user modification. +For example: +/org/gnome/settings-daemon/peripherals/smartcard/removal-action +After the settings have been set, run dconf update. + CCI-000056 + CCI-000058 + SRG-OS-000028-GPOS-00009 + SRG-OS-000030-GPOS-00011 + RHEL-08-020050 + SV-230351r792899_rule + Locking the screen automatically when removing the smartcard can +prevent undesired access to system. + + CCE-83910-0 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83910-0 + - DISA-STIG-RHEL-08-020050 + - dconf_gnome_lock_screen_on_smartcard_removal + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Detect if removal-action can be found on /etc/dconf/db/local.d/ + find: + path: /etc/dconf/db/local.d/ + contains: ^\s*removal-action + register: dconf_gnome_lock_screen_on_smartcard_removal_config_files + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83910-0 + - DISA-STIG-RHEL-08-020050 + - dconf_gnome_lock_screen_on_smartcard_removal + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Configure removal-action - default file + ini_file: + dest: /etc/dconf/db/local.d//00-security-settings + section: org/gnome/settings-daemon/peripherals/smartcard + option: removal-action + value: '''lock-screen''' + create: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - dconf_gnome_lock_screen_on_smartcard_removal_config_files is defined and dconf_gnome_lock_screen_on_smartcard_removal_config_files.matched + == 0 + tags: + - CCE-83910-0 + - DISA-STIG-RHEL-08-020050 + - dconf_gnome_lock_screen_on_smartcard_removal + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Configure removal-action - existing files + ini_file: + dest: '{{ item.path }}' + section: org/gnome/settings-daemon/peripherals/smartcard + option: removal-action + value: '''lock-screen''' + create: true + with_items: '{{ dconf_gnome_lock_screen_on_smartcard_removal_config_files.files + }}' + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - dconf_gnome_lock_screen_on_smartcard_removal_config_files is defined and dconf_gnome_lock_screen_on_smartcard_removal_config_files.matched + > 0 + tags: + - CCE-83910-0 + - DISA-STIG-RHEL-08-020050 + - dconf_gnome_lock_screen_on_smartcard_removal + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Detect if lock for removal-action can be found on /etc/dconf/db/local.d/ + find: + path: /etc/dconf/db/local.d/locks + contains: ^\s*removal-action + register: dconf_gnome_lock_screen_on_smartcard_removal_lock_files + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83910-0 + - DISA-STIG-RHEL-08-020050 + - dconf_gnome_lock_screen_on_smartcard_removal + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Prevent user modification removal-action - default file + lineinfile: + path: /etc/dconf/db/local.d/locks/00-security-settings-lock + regexp: ^/org/gnome/settings-daemon/peripherals/smartcard/removal-action$ + line: /org/gnome/settings-daemon/peripherals/smartcard/removal-action + create: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - dconf_gnome_lock_screen_on_smartcard_removal_lock_files is defined and dconf_gnome_lock_screen_on_smartcard_removal_lock_files.matched + == 0 + tags: + - CCE-83910-0 + - DISA-STIG-RHEL-08-020050 + - dconf_gnome_lock_screen_on_smartcard_removal + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Prevent user modification removal-action - existing files + lineinfile: + path: '{{ item.path }}' + regexp: ^/org/gnome/settings-daemon/peripherals/smartcard/removal-action$ + line: /org/gnome/settings-daemon/peripherals/smartcard/removal-action + create: true + with_items: '{{ dconf_gnome_lock_screen_on_smartcard_removal_lock_files.files }}' + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - dconf_gnome_lock_screen_on_smartcard_removal_lock_files is defined and dconf_gnome_lock_screen_on_smartcard_removal_lock_files.matched + > 0 + tags: + - CCE-83910-0 + - DISA-STIG-RHEL-08-020050 + - dconf_gnome_lock_screen_on_smartcard_removal + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Dconf Update - removal-action + command: dconf update + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83910-0 + - DISA-STIG-RHEL-08-020050 + - dconf_gnome_lock_screen_on_smartcard_removal + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q gdm && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +# Check for setting in any of the DConf db directories +# If files contain ibus or distro, ignore them. +# The assignment assumes that individual filenames don't contain : +readarray -t SETTINGSFILES < <(grep -r "\\[org/gnome/settings-daemon/peripherals/smartcard\\]" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +DCONFFILE="/etc/dconf/db/local.d/00-security-settings" +DBDIR="/etc/dconf/db/local.d" + +mkdir -p "${DBDIR}" + +if [ "${#SETTINGSFILES[@]}" -eq 0 ] +then + [ ! -z ${DCONFFILE} ] || echo "" >> ${DCONFFILE} + printf '%s\n' "[org/gnome/settings-daemon/peripherals/smartcard]" >> ${DCONFFILE} + printf '%s=%s\n' "removal-action" "'lock-screen'" >> ${DCONFFILE} +else + escaped_value="$(sed -e 's/\\/\\\\/g' <<< "'lock-screen'")" + if grep -q "^\\s*removal-action\\s*=" "${SETTINGSFILES[@]}" + then + + sed -i "s/\\s*removal-action\\s*=\\s*.*/removal-action=${escaped_value}/g" "${SETTINGSFILES[@]}" + else + sed -i "\\|\\[org/gnome/settings-daemon/peripherals/smartcard\\]|a\\removal-action=${escaped_value}" "${SETTINGSFILES[@]}" + fi +fi + +dconf update +# Check for setting in any of the DConf db directories +LOCKFILES=$(grep -r "^/org/gnome/settings-daemon/peripherals/smartcard/removal-action$" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +LOCKSFOLDER="/etc/dconf/db/local.d/locks" + +mkdir -p "${LOCKSFOLDER}" + +if [[ -z "${LOCKFILES}" ]] +then + echo "/org/gnome/settings-daemon/peripherals/smartcard/removal-action" >> "/etc/dconf/db/local.d/locks/00-security-settings-lock" +fi + +dconf update + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Set the GNOME3 Login Number of Failures + In the default graphical environment, the GNOME3 login +screen and be configured to restart the authentication process after +a configured number of attempts. This can be configured by setting +allowed-failures to 3 or less. + +To enable, add or edit allowed-failures to +/etc/dconf/db/gdm.d/00-security-settings. For example: +[org/gnome/login-screen] +allowed-failures=3 +Once the setting has been added, add a lock to +/etc/dconf/db/gdm.d/locks/00-security-settings-lock to prevent user modification. +For example: +/org/gnome/login-screen/allowed-failures +After the settings have been set, run dconf update. + 3.1.8 + FMT_MOF_EXT.1 + Setting the password retry prompts that are permitted on a per-session basis to a low value +requires some software, such as SSH, to re-connect. This can slow down and +draw additional attention to some types of password-guessing attacks. + + CCE-80771-9 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80771-9 + - NIST-800-171-3.1.8 + - dconf_gnome_login_retries + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Enable the GNOME3 Login Number of Failures + ini_file: + dest: /etc/dconf/db/gdm.d/00-security-settings + section: org/gnome/login-screen + option: allowed-failures + value: '3' + create: true + no_extra_spaces: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80771-9 + - NIST-800-171-3.1.8 + - dconf_gnome_login_retries + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Prevent user modification of GNOME3 Login Number of Failures + lineinfile: + path: /etc/dconf/db/gdm.d/locks/00-security-settings-lock + regexp: ^/org/gnome/login-screen/allowed-failures$ + line: /org/gnome/login-screen/allowed-failures + create: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80771-9 + - NIST-800-171-3.1.8 + - dconf_gnome_login_retries + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Dconf Update + command: dconf update + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80771-9 + - NIST-800-171-3.1.8 + - dconf_gnome_login_retries + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q gdm && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +# Check for setting in any of the DConf db directories +# If files contain ibus or distro, ignore them. +# The assignment assumes that individual filenames don't contain : +readarray -t SETTINGSFILES < <(grep -r "\\[org/gnome/login-screen\\]" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +DCONFFILE="/etc/dconf/db/gdm.d/00-security-settings" +DBDIR="/etc/dconf/db/gdm.d" + +mkdir -p "${DBDIR}" + +if [ "${#SETTINGSFILES[@]}" -eq 0 ] +then + [ ! -z ${DCONFFILE} ] || echo "" >> ${DCONFFILE} + printf '%s\n' "[org/gnome/login-screen]" >> ${DCONFFILE} + printf '%s=%s\n' "allowed-failures" "3" >> ${DCONFFILE} +else + escaped_value="$(sed -e 's/\\/\\\\/g' <<< "3")" + if grep -q "^\\s*allowed-failures\\s*=" "${SETTINGSFILES[@]}" + then + + sed -i "s/\\s*allowed-failures\\s*=\\s*.*/allowed-failures=${escaped_value}/g" "${SETTINGSFILES[@]}" + else + sed -i "\\|\\[org/gnome/login-screen\\]|a\\allowed-failures=${escaped_value}" "${SETTINGSFILES[@]}" + fi +fi + +dconf update +# Check for setting in any of the DConf db directories +LOCKFILES=$(grep -r "^/org/gnome/login-screen/allowed-failures$" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +LOCKSFOLDER="/etc/dconf/db/gdm.d/locks" + +mkdir -p "${LOCKSFOLDER}" + +if [[ -z "${LOCKFILES}" ]] +then + echo "/org/gnome/login-screen/allowed-failures" >> "/etc/dconf/db/gdm.d/locks/00-security-settings-lock" +fi + +dconf update + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable GDM Automatic Login + The GNOME Display Manager (GDM) can allow users to automatically login without +user interaction or credentials. User should always be required to authenticate themselves +to the system that they are authorized to use. To disable user ability to automatically +login to the system, set the AutomaticLoginEnable to false in the +[daemon] section in /etc/gdm/custom.conf. For example: +[daemon] +AutomaticLoginEnable=false + 11 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + 3.1.1 + CCI-000366 + 4.3.4.3.2 + 4.3.4.3.3 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + CM-6(a) + AC-6(1) + CM-7(b) + PR.IP-1 + FIA_UAU.1 + SRG-OS-000480-GPOS-00229 + RHEL-08-010820 + SV-230329r627750_rule + Failure to restrict system access to authenticated users negatively impacts operating +system security. + + CCE-80823-8 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80823-8 + - DISA-STIG-RHEL-08-010820 + - NIST-800-171-3.1.1 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(b) + - gnome_gdm_disable_automatic_login + - high_severity + - low_complexity + - medium_disruption + - no_reboot_needed + - unknown_strategy + +- name: Disable GDM Automatic Login + ini_file: + dest: /etc/gdm/custom.conf + section: daemon + option: AutomaticLoginEnable + value: 'false' + no_extra_spaces: true + create: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80823-8 + - DISA-STIG-RHEL-08-010820 + - NIST-800-171-3.1.1 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(b) + - gnome_gdm_disable_automatic_login + - high_severity + - low_complexity + - medium_disruption + - no_reboot_needed + - unknown_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q gdm && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +if rpm --quiet -q gdm +then + if ! grep -q "^AutomaticLoginEnable=" /etc/gdm/custom.conf + then + sed -i "/^\[daemon\]/a \ + AutomaticLoginEnable=False" /etc/gdm/custom.conf + else + sed -i "s/^AutomaticLoginEnable=.*/AutomaticLoginEnable=False/g" /etc/gdm/custom.conf + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable GDM Guest Login + The GNOME Display Manager (GDM) can allow users to login without credentials +which can be useful for public kiosk scenarios. Allowing users to login without credentials +or "guest" account access has inherent security risks and should be disabled. To do disable +timed logins or guest account access, set the TimedLoginEnable to false in +the [daemon] section in /etc/gdm/custom.conf. For example: +[daemon] +TimedLoginEnable=false + 11 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + 3.1.1 + CCI-000366 + 4.3.4.3.2 + 4.3.4.3.3 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + CM-7(a) + CM-7(b) + CM-6(a) + IA-2 + PR.IP-1 + FIA_UAU.1 + SRG-OS-000480-GPOS-00229 + Failure to restrict system access to authenticated users negatively impacts operating +system security. + + CCE-80824-6 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80824-6 + - NIST-800-171-3.1.1 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-IA-2 + - gnome_gdm_disable_guest_login + - high_severity + - low_complexity + - medium_disruption + - no_reboot_needed + - unknown_strategy + +- name: Disable GDM Guest Login + ini_file: + dest: /etc/gdm/custom.conf + section: daemon + option: TimedLoginEnable + value: 'false' + no_extra_spaces: true + create: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80824-6 + - NIST-800-171-3.1.1 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-IA-2 + - gnome_gdm_disable_guest_login + - high_severity + - low_complexity + - medium_disruption + - no_reboot_needed + - unknown_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q gdm && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +if rpm --quiet -q gdm +then + if ! grep -q "^TimedLoginEnable=" /etc/gdm/custom.conf + then + sed -i "/^\[daemon\]/a \ + TimedLoginEnable=false" /etc/gdm/custom.conf + else + sed -i "s/^TimedLoginEnable=.*/TimedLoginEnable=false/g" /etc/gdm/custom.conf + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable XDMCP in GDM + XDMCP is an unencrypted protocol, and therefore, presents a security risk, see e.g. +XDMCP Gnome docs. + +To disable XDMCP support in Gnome, set Enable to false under the [xdmcp] configuration section in /etc/gdm/custom.conf. For example: + +[xdmcp] +Enable=false + + XDMCP provides unencrypted remote access through the Gnome Display Manager (GDM) which does +not provide for the confidentiality and integrity of user passwords or the +remote session. If a privileged user were to login using XDMCP, the +privileged user password could be compromised due to typed XEvents +and keystrokes will traversing over the network in clear text. + - name: Gather the package facts + package_facts: + manager: auto + tags: + - gnome_gdm_disable_xdmcp + - high_severity + - low_complexity + - medium_disruption + - no_reboot_needed + - unknown_strategy + +- name: Disable XDMCP in GDM + ini_file: + path: /etc/gdm/custom.conf + section: xdmcp + option: Enable + value: 'false' + create: true + mode: 420 + when: '"gdm" in ansible_facts.packages' + tags: + - gnome_gdm_disable_xdmcp + - high_severity + - low_complexity + - medium_disruption + - no_reboot_needed + - unknown_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q gdm; then + +# Try find '[xdmcp]' and 'Enable' in '/etc/gdm/custom.conf', if it exists, set +# to 'false', if it isn't here, add it, if '[xdmcp]' doesn't exist, add it there +if grep -qzosP '[[:space:]]*\[xdmcp]([^\n\[]*\n+)+?[[:space:]]*Enable' '/etc/gdm/custom.conf'; then + + sed -i 's/Enable[^(\n)]*/Enable=false/' '/etc/gdm/custom.conf' +elif grep -qs '[[:space:]]*\[xdmcp]' '/etc/gdm/custom.conf'; then + sed -i '/[[:space:]]*\[xdmcp]/a Enable=false' '/etc/gdm/custom.conf' +else + if test -d "/etc/gdm"; then + printf '%s\n' '[xdmcp]' 'Enable=false' >> '/etc/gdm/custom.conf' + else + echo "Config file directory '/etc/gdm' doesnt exist, not remediating, assuming non-applicability." >&2 + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + GNOME Media Settings + GNOME media settings that apply to the graphical interface. + + Disable GNOME3 Automounting + The system's default desktop environment, GNOME3, will mount +devices and removable media (such as DVDs, CDs and USB flash drives) whenever +they are inserted into the system. To disable automount within GNOME3, add or set +automount to false in /etc/dconf/db/local.d/00-security-settings. +For example: +[org/gnome/desktop/media-handling] +automount=false +Once the settings have been added, add a lock to +/etc/dconf/db/local.d/locks/00-security-settings-lock to prevent user modification. +For example: +/org/gnome/desktop/media-handling/automount +After the settings have been set, run dconf update. + 12 + 16 + APO13.01 + DSS01.04 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + DSS06.03 + 3.1.7 + CCI-000366 + CCI-000778 + CCI-001958 + 4.3.3.2.2 + 4.3.3.5.2 + 4.3.3.6.6 + 4.3.3.7.2 + 4.3.3.7.4 + SR 1.1 + SR 1.13 + SR 1.2 + SR 1.4 + SR 1.5 + SR 1.9 + SR 2.1 + SR 2.6 + A.11.2.6 + A.13.1.1 + A.13.2.1 + A.6.2.1 + A.6.2.2 + A.7.1.1 + A.9.2.1 + CM-7(a) + CM-7(b) + CM-6(a) + PR.AC-3 + PR.AC-6 + SRG-OS-000114-GPOS-00059 + SRG-OS-000378-GPOS-00163 + SRG-OS-000480-GPOS-00227 + Disabling automatic mounting in GNOME3 can prevent +the introduction of malware via removable media. +It will, however, also prevent desktop users from legitimate use +of removable media. + + CCE-89904-7 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-89904-7 + - NIST-800-171-3.1.7 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - dconf_gnome_disable_automount + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Disable GNOME3 Automounting - automount + ini_file: + dest: /etc/dconf/db/local.d/00-security-settings + section: org/gnome/desktop/media-handling + option: automount + value: 'false' + create: true + no_extra_spaces: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-89904-7 + - NIST-800-171-3.1.7 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - dconf_gnome_disable_automount + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Prevent user modification of GNOME3 Automounting - automount + lineinfile: + path: /etc/dconf/db/local.d/locks/00-security-settings-lock + regexp: ^/org/gnome/desktop/media-handling/automount$ + line: /org/gnome/desktop/media-handling/automount + create: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-89904-7 + - NIST-800-171-3.1.7 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - dconf_gnome_disable_automount + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Dconf Update + command: dconf update + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-89904-7 + - NIST-800-171-3.1.7 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - dconf_gnome_disable_automount + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q gdm && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +# Check for setting in any of the DConf db directories +# If files contain ibus or distro, ignore them. +# The assignment assumes that individual filenames don't contain : +readarray -t SETTINGSFILES < <(grep -r "\\[org/gnome/desktop/media-handling\\]" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +DCONFFILE="/etc/dconf/db/local.d/00-security-settings" +DBDIR="/etc/dconf/db/local.d" + +mkdir -p "${DBDIR}" + +if [ "${#SETTINGSFILES[@]}" -eq 0 ] +then + [ ! -z ${DCONFFILE} ] || echo "" >> ${DCONFFILE} + printf '%s\n' "[org/gnome/desktop/media-handling]" >> ${DCONFFILE} + printf '%s=%s\n' "automount" "false" >> ${DCONFFILE} +else + escaped_value="$(sed -e 's/\\/\\\\/g' <<< "false")" + if grep -q "^\\s*automount\\s*=" "${SETTINGSFILES[@]}" + then + + sed -i "s/\\s*automount\\s*=\\s*.*/automount=${escaped_value}/g" "${SETTINGSFILES[@]}" + else + sed -i "\\|\\[org/gnome/desktop/media-handling\\]|a\\automount=${escaped_value}" "${SETTINGSFILES[@]}" + fi +fi + +dconf update +# Check for setting in any of the DConf db directories +LOCKFILES=$(grep -r "^/org/gnome/desktop/media-handling/automount$" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +LOCKSFOLDER="/etc/dconf/db/local.d/locks" + +mkdir -p "${LOCKSFOLDER}" + +if [[ -z "${LOCKFILES}" ]] +then + echo "/org/gnome/desktop/media-handling/automount" >> "/etc/dconf/db/local.d/locks/00-security-settings-lock" +fi + +dconf update + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable GNOME3 Automount Opening + The system's default desktop environment, GNOME3, will mount +devices and removable media (such as DVDs, CDs and USB flash drives) whenever +they are inserted into the system. To disable automount-open within GNOME3, add or set +automount-open to false in /etc/dconf/db/local.d/00-security-settings. +For example: +[org/gnome/desktop/media-handling] +automount-open=false +Once the settings have been added, add a lock to +/etc/dconf/db/local.d/locks/00-security-settings-lock to prevent user modification. +For example: +/org/gnome/desktop/media-handling/automount-open +After the settings have been set, run dconf update. + 12 + 16 + APO13.01 + DSS01.04 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + DSS06.03 + 3.1.7 + CCI-000366 + CCI-000778 + CCI-001958 + 4.3.3.2.2 + 4.3.3.5.2 + 4.3.3.6.6 + 4.3.3.7.2 + 4.3.3.7.4 + SR 1.1 + SR 1.13 + SR 1.2 + SR 1.4 + SR 1.5 + SR 1.9 + SR 2.1 + SR 2.6 + A.11.2.6 + A.13.1.1 + A.13.2.1 + A.6.2.1 + A.6.2.2 + A.7.1.1 + A.9.2.1 + CM-7(a) + CM-7(b) + CM-6(a) + PR.AC-3 + PR.AC-6 + SRG-OS-000114-GPOS-00059 + SRG-OS-000378-GPOS-00163 + SRG-OS-000480-GPOS-00227 + Automatically mounting file systems permits easy introduction of unknown devices, thereby facilitating malicious activity. +Disabling automatic mounting in GNOME3 can prevent +the introduction of malware via removable media. +It will, however, also prevent desktop users from legitimate use +of removable media. + + CCE-83693-2 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83693-2 + - NIST-800-171-3.1.7 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - dconf_gnome_disable_automount_open + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Disable GNOME3 Automounting - automount-open + ini_file: + dest: /etc/dconf/db/local.d/00-security-settings + section: org/gnome/desktop/media-handling + option: automount-open + value: 'false' + create: true + no_extra_spaces: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83693-2 + - NIST-800-171-3.1.7 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - dconf_gnome_disable_automount_open + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Prevent user modification of GNOME3 Automounting - automount-open + lineinfile: + path: /etc/dconf/db/local.d/locks/00-security-settings-lock + regexp: ^/org/gnome/desktop/media-handling/automount-open$ + line: /org/gnome/desktop/media-handling/automount-open + create: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83693-2 + - NIST-800-171-3.1.7 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - dconf_gnome_disable_automount_open + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Dconf Update + command: dconf update + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83693-2 + - NIST-800-171-3.1.7 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - dconf_gnome_disable_automount_open + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q gdm && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +# Check for setting in any of the DConf db directories +# If files contain ibus or distro, ignore them. +# The assignment assumes that individual filenames don't contain : +readarray -t SETTINGSFILES < <(grep -r "\\[org/gnome/desktop/media-handling\\]" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +DCONFFILE="/etc/dconf/db/local.d/00-security-settings" +DBDIR="/etc/dconf/db/local.d" + +mkdir -p "${DBDIR}" + +if [ "${#SETTINGSFILES[@]}" -eq 0 ] +then + [ ! -z ${DCONFFILE} ] || echo "" >> ${DCONFFILE} + printf '%s\n' "[org/gnome/desktop/media-handling]" >> ${DCONFFILE} + printf '%s=%s\n' "automount-open" "false" >> ${DCONFFILE} +else + escaped_value="$(sed -e 's/\\/\\\\/g' <<< "false")" + if grep -q "^\\s*automount-open\\s*=" "${SETTINGSFILES[@]}" + then + + sed -i "s/\\s*automount-open\\s*=\\s*.*/automount-open=${escaped_value}/g" "${SETTINGSFILES[@]}" + else + sed -i "\\|\\[org/gnome/desktop/media-handling\\]|a\\automount-open=${escaped_value}" "${SETTINGSFILES[@]}" + fi +fi + +dconf update +# Check for setting in any of the DConf db directories +LOCKFILES=$(grep -r "^/org/gnome/desktop/media-handling/automount-open$" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +LOCKSFOLDER="/etc/dconf/db/local.d/locks" + +mkdir -p "${LOCKSFOLDER}" + +if [[ -z "${LOCKFILES}" ]] +then + echo "/org/gnome/desktop/media-handling/automount-open" >> "/etc/dconf/db/local.d/locks/00-security-settings-lock" +fi + +dconf update + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable GNOME3 Automount running + The system's default desktop environment, GNOME3, will mount +devices and removable media (such as DVDs, CDs and USB flash drives) whenever +they are inserted into the system. To disable autorun-never within GNOME3, add or set +autorun-never to true in /etc/dconf/db/local.d/00-security-settings. +For example: +[org/gnome/desktop/media-handling] +autorun-never=true +Once the settings have been added, add a lock to +/etc/dconf/db/local.d/locks/00-security-settings-lock to prevent user modification. +For example: +/org/gnome/desktop/media-handling/autorun-never +After the settings have been set, run dconf update. + 12 + 16 + APO13.01 + DSS01.04 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + DSS06.03 + 3.1.7 + CCI-000366 + CCI-000778 + CCI-001958 + 4.3.3.2.2 + 4.3.3.5.2 + 4.3.3.6.6 + 4.3.3.7.2 + 4.3.3.7.4 + SR 1.1 + SR 1.13 + SR 1.2 + SR 1.4 + SR 1.5 + SR 1.9 + SR 2.1 + SR 2.6 + A.11.2.6 + A.13.1.1 + A.13.2.1 + A.6.2.1 + A.6.2.2 + A.7.1.1 + A.9.2.1 + CM-7(a) + CM-7(b) + CM-6(a) + PR.AC-3 + PR.AC-6 + SRG-OS-000114-GPOS-00059 + SRG-OS-000378-GPOS-00163 + SRG-OS-000480-GPOS-00227 + Automatically mounting file systems permits easy introduction of unknown devices, thereby facilitating malicious activity. +Disabling automatic mount running in GNOME3 can prevent +the introduction of malware via removable media. +It will, however, also prevent desktop users from legitimate use +of removable media. + + CCE-83742-7 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83742-7 + - NIST-800-171-3.1.7 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - dconf_gnome_disable_autorun + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Disable GNOME3 Automounting - autorun-never + ini_file: + dest: /etc/dconf/db/local.d/00-security-settings + section: org/gnome/desktop/media-handling + option: autorun-never + value: 'true' + create: true + no_extra_spaces: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83742-7 + - NIST-800-171-3.1.7 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - dconf_gnome_disable_autorun + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Prevent user modification of GNOME3 Automounting - autorun-never + lineinfile: + path: /etc/dconf/db/local.d/locks/00-security-settings-lock + regexp: ^/org/gnome/desktop/media-handling/autorun-never$ + line: /org/gnome/desktop/media-handling/autorun-never + create: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83742-7 + - NIST-800-171-3.1.7 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - dconf_gnome_disable_autorun + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Dconf Update + command: dconf update + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83742-7 + - NIST-800-171-3.1.7 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - dconf_gnome_disable_autorun + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q gdm && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +# Check for setting in any of the DConf db directories +# If files contain ibus or distro, ignore them. +# The assignment assumes that individual filenames don't contain : +readarray -t SETTINGSFILES < <(grep -r "\\[org/gnome/desktop/media-handling\\]" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +DCONFFILE="/etc/dconf/db/local.d/00-security-settings" +DBDIR="/etc/dconf/db/local.d" + +mkdir -p "${DBDIR}" + +if [ "${#SETTINGSFILES[@]}" -eq 0 ] +then + [ ! -z ${DCONFFILE} ] || echo "" >> ${DCONFFILE} + printf '%s\n' "[org/gnome/desktop/media-handling]" >> ${DCONFFILE} + printf '%s=%s\n' "autorun-never" "true" >> ${DCONFFILE} +else + escaped_value="$(sed -e 's/\\/\\\\/g' <<< "true")" + if grep -q "^\\s*autorun-never\\s*=" "${SETTINGSFILES[@]}" + then + + sed -i "s/\\s*autorun-never\\s*=\\s*.*/autorun-never=${escaped_value}/g" "${SETTINGSFILES[@]}" + else + sed -i "\\|\\[org/gnome/desktop/media-handling\\]|a\\autorun-never=${escaped_value}" "${SETTINGSFILES[@]}" + fi +fi + +dconf update +# Check for setting in any of the DConf db directories +LOCKFILES=$(grep -r "^/org/gnome/desktop/media-handling/autorun-never$" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +LOCKSFOLDER="/etc/dconf/db/local.d/locks" + +mkdir -p "${LOCKSFOLDER}" + +if [[ -z "${LOCKFILES}" ]] +then + echo "/org/gnome/desktop/media-handling/autorun-never" >> "/etc/dconf/db/local.d/locks/00-security-settings-lock" +fi + +dconf update + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable All GNOME3 Thumbnailers + The system's default desktop environment, GNOME3, uses +a number of different thumbnailer programs to generate thumbnails +for any new or modified content in an opened folder. To disable the +execution of these thumbnail applications, add or set disable-all +to true in /etc/dconf/db/local.d/00-security-settings. +For example: +[org/gnome/desktop/thumbnailers] +disable-all=true +Once the settings have been added, add a lock to +/etc/dconf/db/local.d/locks/00-security-settings-lock to prevent user modification. +For example: +/org/gnome/desktop/thumbnailers/disable-all +After the settings have been set, run dconf update. +This effectively prevents an attacker from gaining access to a +system through a flaw in GNOME3's Nautilus thumbnail creators. + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + PR.PT-3 + An attacker with knowledge of a flaw in a GNOME3 thumbnailer application could craft a malicious +file to exploit this flaw. Assuming the attacker could place the malicious file on the local filesystem +(via a web upload for example) and assuming a user browses the same location using Nautilus, the +malicious file would exploit the thumbnailer with the potential for malicious code execution. It +is best to disable these thumbnailer applications unless they are explicitly required. + + - name: Gather the package facts + package_facts: + manager: auto + tags: + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - dconf_gnome_disable_thumbnailers + - low_complexity + - medium_disruption + - no_reboot_needed + - unknown_severity + - unknown_strategy + +- name: Disable All GNOME3 Thumbnailers + ini_file: + dest: /etc/dconf/db/local.d/00-security-settings + section: org/gnome/desktop/thumbnailers + option: disable-all + value: 'true' + create: true + no_extra_spaces: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - dconf_gnome_disable_thumbnailers + - low_complexity + - medium_disruption + - no_reboot_needed + - unknown_severity + - unknown_strategy + +- name: Prevent user modification of GNOME3 Thumbnailers + lineinfile: + path: /etc/dconf/db/local.d/locks/00-security-settings-lock + regexp: ^/org/gnome/desktop/thumbnailers/disable-all$ + line: /org/gnome/desktop/thumbnailers/disable-all + create: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - dconf_gnome_disable_thumbnailers + - low_complexity + - medium_disruption + - no_reboot_needed + - unknown_severity + - unknown_strategy + +- name: Dconf Update + command: dconf update + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - dconf_gnome_disable_thumbnailers + - low_complexity + - medium_disruption + - no_reboot_needed + - unknown_severity + - unknown_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q gdm && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +# Check for setting in any of the DConf db directories +# If files contain ibus or distro, ignore them. +# The assignment assumes that individual filenames don't contain : +readarray -t SETTINGSFILES < <(grep -r "\\[org/gnome/desktop/thumbnailers\\]" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +DCONFFILE="/etc/dconf/db/local.d/00-security-settings" +DBDIR="/etc/dconf/db/local.d" + +mkdir -p "${DBDIR}" + +if [ "${#SETTINGSFILES[@]}" -eq 0 ] +then + [ ! -z ${DCONFFILE} ] || echo "" >> ${DCONFFILE} + printf '%s\n' "[org/gnome/desktop/thumbnailers]" >> ${DCONFFILE} + printf '%s=%s\n' "disable-all" "true" >> ${DCONFFILE} +else + escaped_value="$(sed -e 's/\\/\\\\/g' <<< "true")" + if grep -q "^\\s*disable-all\\s*=" "${SETTINGSFILES[@]}" + then + + sed -i "s/\\s*disable-all\\s*=\\s*.*/disable-all=${escaped_value}/g" "${SETTINGSFILES[@]}" + else + sed -i "\\|\\[org/gnome/desktop/thumbnailers\\]|a\\disable-all=${escaped_value}" "${SETTINGSFILES[@]}" + fi +fi + +dconf update +# Check for setting in any of the DConf db directories +LOCKFILES=$(grep -r "^/org/gnome/desktop/thumbnailers/disable-all$" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +LOCKSFOLDER="/etc/dconf/db/local.d/locks" + +mkdir -p "${LOCKSFOLDER}" + +if [[ -z "${LOCKFILES}" ]] +then + echo "/org/gnome/desktop/thumbnailers/disable-all" >> "/etc/dconf/db/local.d/locks/00-security-settings-lock" +fi + +dconf update + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + GNOME Network Settings + GNOME network settings that apply to the graphical interface. + + Disable WIFI Network Connection Creation in GNOME3 + GNOME allows users to create ad-hoc wireless connections through the +NetworkManager applet. Wireless connections should be disabled by +adding or setting disable-wifi-create to true in +/etc/dconf/db/local.d/00-security-settings. For example: +[org/gnome/nm-applet] +disable-wifi-create=true + +Once the settings have been added, add a lock to +/etc/dconf/db/local.d/locks/00-security-settings-lock to prevent user modification. +For example: +/org/gnome/nm-applet/disable-wifi-create +After the settings have been set, run dconf update. + 3.1.16 + Wireless network connections should not be allowed to be configured by general +users on a given system as it could open the system to backdoor attacks. + + - name: Gather the package facts + package_facts: + manager: auto + tags: + - NIST-800-171-3.1.16 + - dconf_gnome_disable_wifi_create + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Disable WiFi Network Connection Creation in GNOME3 + ini_file: + dest: /etc/dconf/db/local.d/00-security-settings + section: org/gnome/nm-applet + option: disable-wifi-create + value: 'true' + create: true + no_extra_spaces: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-171-3.1.16 + - dconf_gnome_disable_wifi_create + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Prevent user modification of GNOME3 disablement of WiFi + lineinfile: + path: /etc/dconf/db/local.d/locks/00-security-settings-lock + regexp: ^/org/gnome/nm-applet/disable-wifi-create$ + line: /org/gnome/nm-applet/disable-wifi-create + create: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-171-3.1.16 + - dconf_gnome_disable_wifi_create + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Dconf Update + command: dconf update + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-171-3.1.16 + - dconf_gnome_disable_wifi_create + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q gdm && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +# Check for setting in any of the DConf db directories +# If files contain ibus or distro, ignore them. +# The assignment assumes that individual filenames don't contain : +readarray -t SETTINGSFILES < <(grep -r "\\[org/gnome/nm-applet\\]" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +DCONFFILE="/etc/dconf/db/local.d/00-security-settings" +DBDIR="/etc/dconf/db/local.d" + +mkdir -p "${DBDIR}" + +if [ "${#SETTINGSFILES[@]}" -eq 0 ] +then + [ ! -z ${DCONFFILE} ] || echo "" >> ${DCONFFILE} + printf '%s\n' "[org/gnome/nm-applet]" >> ${DCONFFILE} + printf '%s=%s\n' "disable-wifi-create" "true" >> ${DCONFFILE} +else + escaped_value="$(sed -e 's/\\/\\\\/g' <<< "true")" + if grep -q "^\\s*disable-wifi-create\\s*=" "${SETTINGSFILES[@]}" + then + + sed -i "s/\\s*disable-wifi-create\\s*=\\s*.*/disable-wifi-create=${escaped_value}/g" "${SETTINGSFILES[@]}" + else + sed -i "\\|\\[org/gnome/nm-applet\\]|a\\disable-wifi-create=${escaped_value}" "${SETTINGSFILES[@]}" + fi +fi + +dconf update +# Check for setting in any of the DConf db directories +LOCKFILES=$(grep -r "^/org/gnome/nm-applet/disable-wifi-create$" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +LOCKSFOLDER="/etc/dconf/db/local.d/locks" + +mkdir -p "${LOCKSFOLDER}" + +if [[ -z "${LOCKFILES}" ]] +then + echo "/org/gnome/nm-applet/disable-wifi-create" >> "/etc/dconf/db/local.d/locks/00-security-settings-lock" +fi + +dconf update + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable WIFI Network Notification in GNOME3 + By default, GNOME disables WIFI notification. This should be permanently set +so that users do not connect to a wireless network when the system finds one. +While useful for mobile devices, this setting should be disabled for all other systems. +To configure the system to disable the WIFI notication, add or set +suppress-wireless-networks-available to true in +/etc/dconf/db/local.d/00-security-settings. For example: +[org/gnome/nm-applet] +suppress-wireless-networks-available=true + +Once the settings have been added, add a lock to +/etc/dconf/db/local.d/locks/00-security-settings-lock to prevent user modification. +For example: +/org/gnome/nm-applet/suppress-wireless-networks-available +After the settings have been set, run dconf update. + 3.1.16 + Wireless network connections should not be allowed to be configured by general +users on a given system as it could open the system to backdoor attacks. + + - name: Gather the package facts + package_facts: + manager: auto + tags: + - NIST-800-171-3.1.16 + - dconf_gnome_disable_wifi_notification + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Disable WiFi Network Notification in GNOME3 + ini_file: + dest: /etc/dconf/db/local.d/00-security-settings + section: org/gnome/nm-applet + option: suppress-wireless-networks-available + value: 'true' + create: true + no_extra_spaces: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-171-3.1.16 + - dconf_gnome_disable_wifi_notification + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Prevent user modification of GNOME3 disablement of WiFi + lineinfile: + path: /etc/dconf/db/local.d/locks/00-security-settings-lock + regexp: ^/org/gnome/nm-applet/suppress-wireless-networks-available$ + line: /org/gnome/nm-applet/suppress-wireless-networks-available + create: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-171-3.1.16 + - dconf_gnome_disable_wifi_notification + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Dconf Update + command: dconf update + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-171-3.1.16 + - dconf_gnome_disable_wifi_notification + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q gdm && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +# Check for setting in any of the DConf db directories +# If files contain ibus or distro, ignore them. +# The assignment assumes that individual filenames don't contain : +readarray -t SETTINGSFILES < <(grep -r "\\[org/gnome/nm-applet\\]" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +DCONFFILE="/etc/dconf/db/local.d/00-security-settings" +DBDIR="/etc/dconf/db/local.d" + +mkdir -p "${DBDIR}" + +if [ "${#SETTINGSFILES[@]}" -eq 0 ] +then + [ ! -z ${DCONFFILE} ] || echo "" >> ${DCONFFILE} + printf '%s\n' "[org/gnome/nm-applet]" >> ${DCONFFILE} + printf '%s=%s\n' "suppress-wireless-networks-available" "true" >> ${DCONFFILE} +else + escaped_value="$(sed -e 's/\\/\\\\/g' <<< "true")" + if grep -q "^\\s*suppress-wireless-networks-available\\s*=" "${SETTINGSFILES[@]}" + then + + sed -i "s/\\s*suppress-wireless-networks-available\\s*=\\s*.*/suppress-wireless-networks-available=${escaped_value}/g" "${SETTINGSFILES[@]}" + else + sed -i "\\|\\[org/gnome/nm-applet\\]|a\\suppress-wireless-networks-available=${escaped_value}" "${SETTINGSFILES[@]}" + fi +fi + +dconf update +# Check for setting in any of the DConf db directories +LOCKFILES=$(grep -r "^/org/gnome/nm-applet/suppress-wireless-networks-available$" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +LOCKSFOLDER="/etc/dconf/db/local.d/locks" + +mkdir -p "${LOCKSFOLDER}" + +if [[ -z "${LOCKFILES}" ]] +then + echo "/org/gnome/nm-applet/suppress-wireless-networks-available" >> "/etc/dconf/db/local.d/locks/00-security-settings-lock" +fi + +dconf update + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + GNOME Remote Access Settings + GNOME remote access settings that apply to the graphical interface. + + Require Credential Prompting for Remote Access in GNOME3 + By default, GNOME does not require credentials when using Vino for +remote access. To configure the system to require remote credentials, add or set +authentication-methods to ['vnc'] in +/etc/dconf/db/local.d/00-security-settings. For example: +[org/gnome/Vino] +authentication-methods=['vnc'] + +Once the settings have been added, add a lock to +/etc/dconf/db/local.d/locks/00-security-settings-lock to prevent user modification. +For example: +/org/gnome/Vino/authentication-methods +After the settings have been set, run dconf update. + 3.1.12 + 164.308(a)(4)(i) + 164.308(b)(1) + 164.308(b)(3) + 164.310(b) + 164.312(e)(1) + 164.312(e)(2)(ii) + Username and password prompting is required for remote access. Otherwise, non-authorized +and nefarious users can access the system freely. + + CCE-80772-7 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80772-7 + - NIST-800-171-3.1.12 + - dconf_gnome_remote_access_credential_prompt + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Require Credential Prompting for Remote Access in GNOME3 + ini_file: + dest: /etc/dconf/db/local.d/00-security-settings + section: org/gnome/Vino + option: authentication-methods + value: '[''vnc'']' + create: true + no_extra_spaces: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80772-7 + - NIST-800-171-3.1.12 + - dconf_gnome_remote_access_credential_prompt + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Prevent user modification of GNOME3 Credential Prompting for Remote Access + lineinfile: + path: /etc/dconf/db/local.d/locks/00-security-settings-lock + regexp: ^/org/gnome/Vino/authentication-methods$ + line: /org/gnome/Vino/authentication-methods + create: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80772-7 + - NIST-800-171-3.1.12 + - dconf_gnome_remote_access_credential_prompt + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Dconf Update + command: dconf update + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80772-7 + - NIST-800-171-3.1.12 + - dconf_gnome_remote_access_credential_prompt + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q gdm && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +# Check for setting in any of the DConf db directories +# If files contain ibus or distro, ignore them. +# The assignment assumes that individual filenames don't contain : +readarray -t SETTINGSFILES < <(grep -r "\\[org/gnome/Vino\\]" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +DCONFFILE="/etc/dconf/db/local.d/00-security-settings" +DBDIR="/etc/dconf/db/local.d" + +mkdir -p "${DBDIR}" + +if [ "${#SETTINGSFILES[@]}" -eq 0 ] +then + [ ! -z ${DCONFFILE} ] || echo "" >> ${DCONFFILE} + printf '%s\n' "[org/gnome/Vino]" >> ${DCONFFILE} + printf '%s=%s\n' "authentication-methods" "['vnc']" >> ${DCONFFILE} +else + escaped_value="$(sed -e 's/\\/\\\\/g' <<< "['vnc']")" + if grep -q "^\\s*authentication-methods\\s*=" "${SETTINGSFILES[@]}" + then + + sed -i "s/\\s*authentication-methods\\s*=\\s*.*/authentication-methods=${escaped_value}/g" "${SETTINGSFILES[@]}" + else + sed -i "\\|\\[org/gnome/Vino\\]|a\\authentication-methods=${escaped_value}" "${SETTINGSFILES[@]}" + fi +fi + +dconf update +# Check for setting in any of the DConf db directories +LOCKFILES=$(grep -r "^/org/gnome/Vino/authentication-methods$" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +LOCKSFOLDER="/etc/dconf/db/local.d/locks" + +mkdir -p "${LOCKSFOLDER}" + +if [[ -z "${LOCKFILES}" ]] +then + echo "/org/gnome/Vino/authentication-methods" >> "/etc/dconf/db/local.d/locks/00-security-settings-lock" +fi + +dconf update + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Require Encryption for Remote Access in GNOME3 + By default, GNOME requires encryption when using Vino for remote access. +To prevent remote access encryption from being disabled, add or set +require-encryption to true in +/etc/dconf/db/local.d/00-security-settings. For example: +[org/gnome/Vino] +require-encryption=true + +Once the settings have been added, add a lock to +/etc/dconf/db/local.d/locks/00-security-settings-lock to prevent user modification. +For example: +/org/gnome/Vino/require-encryption +After the settings have been set, run dconf update. + 1 + 11 + 12 + 13 + 15 + 16 + 18 + 20 + 3 + 4 + 6 + 9 + BAI03.08 + BAI07.04 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS03.01 + 3.1.13 + CCI-000366 + 164.308(a)(4)(i) + 164.308(b)(1) + 164.308(b)(3) + 164.310(b) + 164.312(e)(1) + 164.312(e)(2)(ii) + 4.3.4.3.2 + 4.3.4.3.3 + 4.4.3.3 + SR 7.6 + A.12.1.1 + A.12.1.2 + A.12.1.4 + A.12.5.1 + A.12.6.2 + A.13.1.1 + A.13.1.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + CM-6(a) + AC-17(a) + AC-17(2) + DE.AE-1 + PR.DS-7 + PR.IP-1 + SRG-OS-000480-GPOS-00227 + Open X displays allow an attacker to capture keystrokes and to execute commands +remotely. + + CCE-80773-5 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80773-5 + - NIST-800-171-3.1.13 + - NIST-800-53-AC-17(2) + - NIST-800-53-AC-17(a) + - NIST-800-53-CM-6(a) + - dconf_gnome_remote_access_encryption + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Require Encryption for Remote Access in GNOME3 + ini_file: + dest: /etc/dconf/db/local.d/00-security-settings + section: org/gnome/Vino + option: require-encryption + value: 'true' + create: true + no_extra_spaces: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80773-5 + - NIST-800-171-3.1.13 + - NIST-800-53-AC-17(2) + - NIST-800-53-AC-17(a) + - NIST-800-53-CM-6(a) + - dconf_gnome_remote_access_encryption + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Prevent user modification of GNOME3 Encryption for Remote Access + lineinfile: + path: /etc/dconf/db/local.d/locks/00-security-settings-lock + regexp: ^/org/gnome/Vino/require-encryption$ + line: /org/gnome/Vino/require-encryption + create: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80773-5 + - NIST-800-171-3.1.13 + - NIST-800-53-AC-17(2) + - NIST-800-53-AC-17(a) + - NIST-800-53-CM-6(a) + - dconf_gnome_remote_access_encryption + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Dconf Update + command: dconf update + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80773-5 + - NIST-800-171-3.1.13 + - NIST-800-53-AC-17(2) + - NIST-800-53-AC-17(a) + - NIST-800-53-CM-6(a) + - dconf_gnome_remote_access_encryption + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q gdm && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +# Check for setting in any of the DConf db directories +# If files contain ibus or distro, ignore them. +# The assignment assumes that individual filenames don't contain : +readarray -t SETTINGSFILES < <(grep -r "\\[org/gnome/Vino\\]" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +DCONFFILE="/etc/dconf/db/local.d/00-security-settings" +DBDIR="/etc/dconf/db/local.d" + +mkdir -p "${DBDIR}" + +if [ "${#SETTINGSFILES[@]}" -eq 0 ] +then + [ ! -z ${DCONFFILE} ] || echo "" >> ${DCONFFILE} + printf '%s\n' "[org/gnome/Vino]" >> ${DCONFFILE} + printf '%s=%s\n' "require-encryption" "true" >> ${DCONFFILE} +else + escaped_value="$(sed -e 's/\\/\\\\/g' <<< "true")" + if grep -q "^\\s*require-encryption\\s*=" "${SETTINGSFILES[@]}" + then + + sed -i "s/\\s*require-encryption\\s*=\\s*.*/require-encryption=${escaped_value}/g" "${SETTINGSFILES[@]}" + else + sed -i "\\|\\[org/gnome/Vino\\]|a\\require-encryption=${escaped_value}" "${SETTINGSFILES[@]}" + fi +fi + +dconf update +# Check for setting in any of the DConf db directories +LOCKFILES=$(grep -r "^/org/gnome/Vino/require-encryption$" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +LOCKSFOLDER="/etc/dconf/db/local.d/locks" + +mkdir -p "${LOCKSFOLDER}" + +if [[ -z "${LOCKFILES}" ]] +then + echo "/org/gnome/Vino/require-encryption" >> "/etc/dconf/db/local.d/locks/00-security-settings-lock" +fi + +dconf update + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Configure GNOME Screen Locking + In the default GNOME3 desktop, the screen can be locked +by selecting the user name in the far right corner of the main panel and +selecting Lock. + +The following sections detail commands to enforce idle activation of the screensaver, +screen locking, a blank-screen screensaver, and an idle activation time. + +Because users should be trained to lock the screen when they +step away from the computer, the automatic locking feature is only +meant as a backup. + +The root account can be screen-locked; however, the root account should +never be used to log into an X Windows environment and should only +be used to for direct login via console in emergency circumstances. + +For more information about enforcing preferences in the GNOME3 environment using the DConf +configuration system, see http://wiki.gnome.org/dconf and +the man page dconf(1). + + Screensaver Inactivity timeout + Choose allowed duration (in seconds) of inactive graphical sessions + 600 + 900 + 1800 + 300 + 900 + + + Screensaver Lock Delay + Choose allowed duration (in seconds) after a screensaver becomes active before displaying an authentication prompt + 10 + 5 + 0 + 0 + + + Enable GNOME3 Screensaver Idle Activation + To activate the screensaver in the GNOME3 desktop after a period of inactivity, +add or set idle-activation-enabled to true in +/etc/dconf/db/local.d/00-security-settings. For example: +[org/gnome/desktop/screensaver] +idle-activation-enabled=true +Once the setting has been added, add a lock to +/etc/dconf/db/local.d/locks/00-security-settings-lock to prevent user modification. +For example: +/org/gnome/desktop/screensaver/idle-activation-enabled +After the settings have been set, run dconf update. + 1 + 12 + 15 + 16 + 5.5.5 + DSS05.04 + DSS05.10 + DSS06.10 + 3.1.10 + CCI-000057 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + A.18.1.4 + A.9.2.1 + A.9.2.4 + A.9.3.1 + A.9.4.2 + A.9.4.3 + CM-6(a) + AC-11(a) + PR.AC-7 + FMT_MOF_EXT.1 + Req-8.1.8 + SRG-OS-000029-GPOS-00010 + A session time-out lock is a temporary action taken when a user stops work and moves away from the immediate +physical vicinity of the information system but does not logout because of the temporary nature of the absence. +Rather than relying on the user to manually lock their operating system session prior to vacating the vicinity, +GNOME desktops can be configured to identify when a user's session has idled and take action to initiate the +session lock. + +Enabling idle activation of the screensaver ensures the screensaver will +be activated after the idle delay. Applications requiring continuous, +real-time screen display (such as network management products) require the +login session does not have administrator rights and the display station is located in a +controlled-access area. + + CCE-80774-3 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80774-3 + - CJIS-5.5.5 + - NIST-800-171-3.1.10 + - NIST-800-53-AC-11(a) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.1.8 + - dconf_gnome_screensaver_idle_activation_enabled + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Enable GNOME3 Screensaver Idle Activation + ini_file: + dest: /etc/dconf/db/local.d/00-security-settings + section: org/gnome/desktop/screensaver + option: idle-activation-enabled + value: 'true' + create: true + no_extra_spaces: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80774-3 + - CJIS-5.5.5 + - NIST-800-171-3.1.10 + - NIST-800-53-AC-11(a) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.1.8 + - dconf_gnome_screensaver_idle_activation_enabled + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Prevent user modification of GNOME idle-activation-enabled + lineinfile: + path: /etc/dconf/db/local.d/locks/00-security-settings-lock + regexp: ^/org/gnome/desktop/screensaver/idle-activation-enabled$ + line: /org/gnome/desktop/screensaver/idle-activation-enabled + create: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80774-3 + - CJIS-5.5.5 + - NIST-800-171-3.1.10 + - NIST-800-53-AC-11(a) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.1.8 + - dconf_gnome_screensaver_idle_activation_enabled + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Dconf Update + command: dconf update + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80774-3 + - CJIS-5.5.5 + - NIST-800-171-3.1.10 + - NIST-800-53-AC-11(a) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.1.8 + - dconf_gnome_screensaver_idle_activation_enabled + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q gdm && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +# Check for setting in any of the DConf db directories +# If files contain ibus or distro, ignore them. +# The assignment assumes that individual filenames don't contain : +readarray -t SETTINGSFILES < <(grep -r "\\[org/gnome/desktop/screensaver\\]" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +DCONFFILE="/etc/dconf/db/local.d/00-security-settings" +DBDIR="/etc/dconf/db/local.d" + +mkdir -p "${DBDIR}" + +if [ "${#SETTINGSFILES[@]}" -eq 0 ] +then + [ ! -z ${DCONFFILE} ] || echo "" >> ${DCONFFILE} + printf '%s\n' "[org/gnome/desktop/screensaver]" >> ${DCONFFILE} + printf '%s=%s\n' "idle-activation-enabled" "true" >> ${DCONFFILE} +else + escaped_value="$(sed -e 's/\\/\\\\/g' <<< "true")" + if grep -q "^\\s*idle-activation-enabled\\s*=" "${SETTINGSFILES[@]}" + then + + sed -i "s/\\s*idle-activation-enabled\\s*=\\s*.*/idle-activation-enabled=${escaped_value}/g" "${SETTINGSFILES[@]}" + else + sed -i "\\|\\[org/gnome/desktop/screensaver\\]|a\\idle-activation-enabled=${escaped_value}" "${SETTINGSFILES[@]}" + fi +fi + +dconf update +# Check for setting in any of the DConf db directories +LOCKFILES=$(grep -r "^/org/gnome/desktop/screensaver/idle-activation-enabled$" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +LOCKSFOLDER="/etc/dconf/db/local.d/locks" + +mkdir -p "${LOCKSFOLDER}" + +if [[ -z "${LOCKFILES}" ]] +then + echo "/org/gnome/desktop/screensaver/idle-activation-enabled" >> "/etc/dconf/db/local.d/locks/00-security-settings-lock" +fi + +dconf update + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure Users Cannot Change GNOME3 Screensaver Idle Activation + If not already configured, ensure that users cannot change GNOME3 screensaver lock settings +by adding /org/gnome/desktop/screensaver/idle-activation-enabled +to /etc/dconf/db/local.d/00-security-settings. +For example: +/org/gnome/desktop/screensaver/idle-activation-enabled +After the settings have been set, run dconf update. + 1 + 12 + 15 + 16 + 5.5.5 + DSS05.04 + DSS05.10 + DSS06.10 + 3.1.10 + CCI-000057 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + A.18.1.4 + A.9.2.1 + A.9.2.4 + A.9.3.1 + A.9.4.2 + A.9.4.3 + CM-6(a) + PR.AC-7 + FMT_MOF_EXT.1 + Req-8.1.8 + SRG-OS-000029-GPOS-00010 + A session lock is a temporary action taken when a user stops work and moves away from the immediate physical vicinity +of the information system but does not want to logout because of the temporary nature of the absense. + CCE-83858-1 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83858-1 + - CJIS-5.5.5 + - NIST-800-171-3.1.10 + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.1.8 + - dconf_gnome_screensaver_idle_activation_locked + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Prevent user modification of GNOME Screensaver idle-activation-enabled + lineinfile: + path: /etc/dconf/db/local.d/locks/00-security-settings-lock + regexp: ^/org/gnome/desktop/screensaver/idle-activation-enabled$ + line: /org/gnome/desktop/screensaver/idle-activation-enabled + create: true + when: '"gdm" in ansible_facts.packages' + tags: + - CCE-83858-1 + - CJIS-5.5.5 + - NIST-800-171-3.1.10 + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.1.8 + - dconf_gnome_screensaver_idle_activation_locked + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Dconf Update + command: dconf update + when: '"gdm" in ansible_facts.packages' + tags: + - CCE-83858-1 + - CJIS-5.5.5 + - NIST-800-171-3.1.10 + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.1.8 + - dconf_gnome_screensaver_idle_activation_locked + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q gdm; then + +# Check for setting in any of the DConf db directories +LOCKFILES=$(grep -r "^/org/gnome/desktop/screensaver/idle-activation-enabled$" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +LOCKSFOLDER="/etc/dconf/db/local.d/locks" + +mkdir -p "${LOCKSFOLDER}" + +if [[ -z "${LOCKFILES}" ]] +then + echo "/org/gnome/desktop/screensaver/idle-activation-enabled" >> "/etc/dconf/db/local.d/locks/00-security-settings-lock" +fi + +dconf update + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Set GNOME3 Screensaver Inactivity Timeout + The idle time-out value for inactivity in the GNOME3 desktop is configured via the idle-delay +setting must be set under an appropriate configuration file(s) in the /etc/dconf/db/local.d directory +and locked in /etc/dconf/db/local.d/locks directory to prevent user modification. + +For example, to configure the system for a 15 minute delay, add the following to +/etc/dconf/db/local.d/00-security-settings: +[org/gnome/desktop/session] +idle-delay=uint32 900 + 1 + 12 + 15 + 16 + 5.5.5 + DSS05.04 + DSS05.10 + DSS06.10 + 3.1.10 + CCI-000057 + CCI-000060 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + A.18.1.4 + A.9.2.1 + A.9.2.4 + A.9.3.1 + A.9.4.2 + A.9.4.3 + AC-11(a) + CM-6(a) + PR.AC-7 + FMT_MOF_EXT.1 + Req-8.1.8 + SRG-OS-000029-GPOS-00010 + SRG-OS-000031-GPOS-00012 + RHEL-08-020060 + SV-230352r646876_rule + A session time-out lock is a temporary action taken when a user stops work and moves away from +the immediate physical vicinity of the information system but does not logout because of the +temporary nature of the absence. Rather than relying on the user to manually lock their operating +system session prior to vacating the vicinity, GNOME3 can be configured to identify when +a user's session has idled and take action to initiate a session lock. + + CCE-80775-0 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80775-0 + - CJIS-5.5.5 + - DISA-STIG-RHEL-08-020060 + - NIST-800-171-3.1.10 + - NIST-800-53-AC-11(a) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.1.8 + - dconf_gnome_screensaver_idle_delay + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy +- name: XCCDF Value inactivity_timeout_value # promote to variable + set_fact: + inactivity_timeout_value: !!str + tags: + - always + +- name: Set GNOME3 Screensaver Inactivity Timeout + ini_file: + dest: /etc/dconf/db/local.d/00-security-settings + section: org/gnome/desktop/session + option: idle-delay + value: uint32 {{ inactivity_timeout_value }} + create: true + no_extra_spaces: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80775-0 + - CJIS-5.5.5 + - DISA-STIG-RHEL-08-020060 + - NIST-800-171-3.1.10 + - NIST-800-53-AC-11(a) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.1.8 + - dconf_gnome_screensaver_idle_delay + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Dconf Update + command: dconf update + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80775-0 + - CJIS-5.5.5 + - DISA-STIG-RHEL-08-020060 + - NIST-800-171-3.1.10 + - NIST-800-53-AC-11(a) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.1.8 + - dconf_gnome_screensaver_idle_delay + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q gdm && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +inactivity_timeout_value='' + + +# Check for setting in any of the DConf db directories +# If files contain ibus or distro, ignore them. +# The assignment assumes that individual filenames don't contain : +readarray -t SETTINGSFILES < <(grep -r "\\[org/gnome/desktop/session\\]" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +DCONFFILE="/etc/dconf/db/local.d/00-security-settings" +DBDIR="/etc/dconf/db/local.d" + +mkdir -p "${DBDIR}" + +if [ "${#SETTINGSFILES[@]}" -eq 0 ] +then + [ ! -z ${DCONFFILE} ] || echo "" >> ${DCONFFILE} + printf '%s\n' "[org/gnome/desktop/session]" >> ${DCONFFILE} + printf '%s=%s\n' "idle-delay" "uint32 ${inactivity_timeout_value}" >> ${DCONFFILE} +else + escaped_value="$(sed -e 's/\\/\\\\/g' <<< "uint32 ${inactivity_timeout_value}")" + if grep -q "^\\s*idle-delay\\s*=" "${SETTINGSFILES[@]}" + then + + sed -i "s/\\s*idle-delay\\s*=\\s*.*/idle-delay=${escaped_value}/g" "${SETTINGSFILES[@]}" + else + sed -i "\\|\\[org/gnome/desktop/session\\]|a\\idle-delay=${escaped_value}" "${SETTINGSFILES[@]}" + fi +fi + +dconf update + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Set GNOME3 Screensaver Lock Delay After Activation Period + To activate the locking delay of the screensaver in the GNOME3 desktop when +the screensaver is activated, add or set lock-delay to uint32 in +/etc/dconf/db/local.d/00-security-settings. For example: +[org/gnome/desktop/screensaver] +lock-delay=uint32 + +After the settings have been set, run dconf update. + 1 + 12 + 15 + 16 + DSS05.04 + DSS05.10 + DSS06.10 + 3.1.10 + CCI-000056 + CCI-000057 + CCI-000060 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + A.18.1.4 + A.9.2.1 + A.9.2.4 + A.9.3.1 + A.9.4.2 + A.9.4.3 + AC-11(a) + CM-6(a) + PR.AC-7 + FMT_MOF_EXT.1 + Req-8.1.8 + SRG-OS-000029-GPOS-00010 + SRG-OS-000031-GPOS-00012 + RHEL-08-020031 + SV-244535r743854_rule + A session lock is a temporary action taken when a user stops work and moves away from the immediate physical vicinity +of the information system but does not want to logout because of the temporary nature of the absense. + + CCE-80776-8 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80776-8 + - DISA-STIG-RHEL-08-020031 + - NIST-800-171-3.1.10 + - NIST-800-53-AC-11(a) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.1.8 + - dconf_gnome_screensaver_lock_delay + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy +- name: XCCDF Value var_screensaver_lock_delay # promote to variable + set_fact: + var_screensaver_lock_delay: !!str + tags: + - always + +- name: Set GNOME3 Screensaver Lock Delay After Activation Period + ini_file: + dest: /etc/dconf/db/local.d/00-security-settings + section: org/gnome/desktop/screensaver + option: lock-delay + value: uint32 {{ var_screensaver_lock_delay }} + create: true + no_extra_spaces: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80776-8 + - DISA-STIG-RHEL-08-020031 + - NIST-800-171-3.1.10 + - NIST-800-53-AC-11(a) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.1.8 + - dconf_gnome_screensaver_lock_delay + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Dconf Update + command: dconf update + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80776-8 + - DISA-STIG-RHEL-08-020031 + - NIST-800-171-3.1.10 + - NIST-800-53-AC-11(a) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.1.8 + - dconf_gnome_screensaver_lock_delay + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q gdm && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +var_screensaver_lock_delay='' + + +# Check for setting in any of the DConf db directories +# If files contain ibus or distro, ignore them. +# The assignment assumes that individual filenames don't contain : +readarray -t SETTINGSFILES < <(grep -r "\\[org/gnome/desktop/screensaver\\]" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +DCONFFILE="/etc/dconf/db/local.d/00-security-settings" +DBDIR="/etc/dconf/db/local.d" + +mkdir -p "${DBDIR}" + +if [ "${#SETTINGSFILES[@]}" -eq 0 ] +then + [ ! -z ${DCONFFILE} ] || echo "" >> ${DCONFFILE} + printf '%s\n' "[org/gnome/desktop/screensaver]" >> ${DCONFFILE} + printf '%s=%s\n' "lock-delay" "uint32 ${var_screensaver_lock_delay}" >> ${DCONFFILE} +else + escaped_value="$(sed -e 's/\\/\\\\/g' <<< "uint32 ${var_screensaver_lock_delay}")" + if grep -q "^\\s*lock-delay\\s*=" "${SETTINGSFILES[@]}" + then + + sed -i "s/\\s*lock-delay\\s*=\\s*.*/lock-delay=${escaped_value}/g" "${SETTINGSFILES[@]}" + else + sed -i "\\|\\[org/gnome/desktop/screensaver\\]|a\\lock-delay=${escaped_value}" "${SETTINGSFILES[@]}" + fi +fi + +dconf update + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable GNOME3 Screensaver Lock After Idle Period + +To activate locking of the screensaver in the GNOME3 desktop when it is activated, +add or set lock-enabled to true in +/etc/dconf/db/local.d/00-security-settings. For example: +[org/gnome/desktop/screensaver] +lock-enabled=true + +Once the settings have been added, add a lock to +/etc/dconf/db/local.d/locks/00-security-settings-lock to prevent user modification. +For example: +/org/gnome/desktop/screensaver/lock-enabled +After the settings have been set, run dconf update. + 1 + 12 + 15 + 16 + 5.5.5 + DSS05.04 + DSS05.10 + DSS06.10 + 3.1.10 + CCI-000056 + CCI-000058 + CCI-000060 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + A.18.1.4 + A.9.2.1 + A.9.2.4 + A.9.3.1 + A.9.4.2 + A.9.4.3 + CM-6(a) + PR.AC-7 + FMT_MOF_EXT.1 + Req-8.1.8 + SRG-OS-000028-GPOS-00009 + SRG-OS-000030-GPOS-00011 + RHEL-08-020030 + SV-230347r627750_rule + A session lock is a temporary action taken when a user stops work and moves away from the immediate physical vicinity +of the information system but does not want to logout because of the temporary nature of the absense. + + CCE-80777-6 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80777-6 + - CJIS-5.5.5 + - DISA-STIG-RHEL-08-020030 + - NIST-800-171-3.1.10 + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.1.8 + - dconf_gnome_screensaver_lock_enabled + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Dconf Update + command: dconf update + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ansible_distribution == 'SLES' + tags: + - CCE-80777-6 + - CJIS-5.5.5 + - DISA-STIG-RHEL-08-020030 + - NIST-800-171-3.1.10 + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.1.8 + - dconf_gnome_screensaver_lock_enabled + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Enable GNOME3 Screensaver Lock After Idle Period + ini_file: + dest: /etc/dconf/db/local.d/00-security-settings + section: org/gnome/desktop/screensaver + option: lock-enabled + value: 'true' + create: true + no_extra_spaces: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ansible_distribution != 'SLES' + tags: + - CCE-80777-6 + - CJIS-5.5.5 + - DISA-STIG-RHEL-08-020030 + - NIST-800-171-3.1.10 + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.1.8 + - dconf_gnome_screensaver_lock_enabled + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Prevent user modification of GNOME lock-enabled + lineinfile: + path: /etc/dconf/db/local.d/locks/00-security-settings-lock + regexp: ^/org/gnome/desktop/screensaver/lock-enabled$ + line: /org/gnome/desktop/screensaver/lock-enabled + create: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ansible_distribution != 'SLES' + tags: + - CCE-80777-6 + - CJIS-5.5.5 + - DISA-STIG-RHEL-08-020030 + - NIST-800-171-3.1.10 + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.1.8 + - dconf_gnome_screensaver_lock_enabled + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Enable GNOME3 Screensaver Lock After Idle Period + ini_file: + dest: /etc/dconf/db/local.d/00-security-settings + section: org/gnome/desktop/lockdown + option: disable-lock-screen + value: 'false' + create: true + no_extra_spaces: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ansible_distribution == 'SLES' + tags: + - CCE-80777-6 + - CJIS-5.5.5 + - DISA-STIG-RHEL-08-020030 + - NIST-800-171-3.1.10 + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.1.8 + - dconf_gnome_screensaver_lock_enabled + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Prevent user modification of GNOME disable-lock-screen + lineinfile: + path: /etc/dconf/db/local.d/locks/00-security-settings-lock + regexp: ^/org/gnome/desktop/lockdown/disable-lock-screen$ + line: /org/gnome/desktop/lockdown/disable-lock-screen + create: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ansible_distribution == 'SLES' + tags: + - CCE-80777-6 + - CJIS-5.5.5 + - DISA-STIG-RHEL-08-020030 + - NIST-800-171-3.1.10 + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.1.8 + - dconf_gnome_screensaver_lock_enabled + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Check GNOME3 screenserver disable-lock-screen false + command: gsettings get org.gnome.desktop.lockdown disable-lock-screen + register: cmd_out + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ansible_distribution == 'SLES' + tags: + - CCE-80777-6 + - CJIS-5.5.5 + - DISA-STIG-RHEL-08-020030 + - NIST-800-171-3.1.10 + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.1.8 + - dconf_gnome_screensaver_lock_enabled + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Update GNOME3 screenserver disable-lock-screen false + command: gsettings set org.gnome.desktop.lockdown disable-lock-screen false + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ansible_distribution == 'SLES' + tags: + - CCE-80777-6 + - CJIS-5.5.5 + - DISA-STIG-RHEL-08-020030 + - NIST-800-171-3.1.10 + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.1.8 + - dconf_gnome_screensaver_lock_enabled + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Dconf Update + command: dconf update + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80777-6 + - CJIS-5.5.5 + - DISA-STIG-RHEL-08-020030 + - NIST-800-171-3.1.10 + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.1.8 + - dconf_gnome_screensaver_lock_enabled + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q gdm && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +# Check for setting in any of the DConf db directories +# If files contain ibus or distro, ignore them. +# The assignment assumes that individual filenames don't contain : +readarray -t SETTINGSFILES < <(grep -r "\\[org/gnome/desktop/screensaver\\]" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +DCONFFILE="/etc/dconf/db/local.d/00-security-settings" +DBDIR="/etc/dconf/db/local.d" + +mkdir -p "${DBDIR}" + +if [ "${#SETTINGSFILES[@]}" -eq 0 ] +then + [ ! -z ${DCONFFILE} ] || echo "" >> ${DCONFFILE} + printf '%s\n' "[org/gnome/desktop/screensaver]" >> ${DCONFFILE} + printf '%s=%s\n' "lock-enabled" "true" >> ${DCONFFILE} +else + escaped_value="$(sed -e 's/\\/\\\\/g' <<< "true")" + if grep -q "^\\s*lock-enabled\\s*=" "${SETTINGSFILES[@]}" + then + + sed -i "s/\\s*lock-enabled\\s*=\\s*.*/lock-enabled=${escaped_value}/g" "${SETTINGSFILES[@]}" + else + sed -i "\\|\\[org/gnome/desktop/screensaver\\]|a\\lock-enabled=${escaped_value}" "${SETTINGSFILES[@]}" + fi +fi + +dconf update +# Check for setting in any of the DConf db directories +LOCKFILES=$(grep -r "^/org/gnome/desktop/screensaver/lock-enabled$" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +LOCKSFOLDER="/etc/dconf/db/local.d/locks" + +mkdir -p "${LOCKSFOLDER}" + +if [[ -z "${LOCKFILES}" ]] +then + echo "/org/gnome/desktop/screensaver/lock-enabled" >> "/etc/dconf/db/local.d/locks/00-security-settings-lock" +fi + +dconf update + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure Users Cannot Change GNOME3 Screensaver Lock After Idle Period + If not already configured, ensure that users cannot change GNOME3 screensaver lock settings +by adding /org/gnome/desktop/screensaver/lock-enabled +to /etc/dconf/db/local.d/00-security-settings. +For example: +/org/gnome/desktop/screensaver/lock-enabled +After the settings have been set, run dconf update. + 1 + 12 + 15 + 16 + 5.5.5 + DSS05.04 + DSS05.10 + DSS06.10 + 3.1.10 + CCI-000056 + CCI-000057 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + A.18.1.4 + A.9.2.1 + A.9.2.4 + A.9.3.1 + A.9.4.2 + A.9.4.3 + CM-6(a) + PR.AC-7 + FMT_MOF_EXT.1 + Req-8.1.8 + SRG-OS-000028-GPOS-00009 + SRG-OS-000030-GPOS-00011 + RHEL-08-020082 + SV-244539r743866_rule + A session lock is a temporary action taken when a user stops work and moves away from the immediate physical vicinity +of the information system but does not want to logout because of the temporary nature of the absense. + CCE-87261-4 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-87261-4 + - CJIS-5.5.5 + - DISA-STIG-RHEL-08-020082 + - NIST-800-171-3.1.10 + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.1.8 + - dconf_gnome_screensaver_lock_locked + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Prevent user modification of GNOME Screensaver lock-enabled + lineinfile: + path: /etc/dconf/db/local.d/locks/00-security-settings-lock + regexp: ^/org/gnome/desktop/screensaver/lock-enabled$ + line: /org/gnome/desktop/screensaver/lock-enabled + create: true + when: '"gdm" in ansible_facts.packages' + tags: + - CCE-87261-4 + - CJIS-5.5.5 + - DISA-STIG-RHEL-08-020082 + - NIST-800-171-3.1.10 + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.1.8 + - dconf_gnome_screensaver_lock_locked + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Dconf Update + command: dconf update + when: '"gdm" in ansible_facts.packages' + tags: + - CCE-87261-4 + - CJIS-5.5.5 + - DISA-STIG-RHEL-08-020082 + - NIST-800-171-3.1.10 + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.1.8 + - dconf_gnome_screensaver_lock_locked + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q gdm; then + +# Check for setting in any of the DConf db directories +LOCKFILES=$(grep -r "^/org/gnome/desktop/screensaver/lock-enabled$" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +LOCKSFOLDER="/etc/dconf/db/local.d/locks" + +mkdir -p "${LOCKSFOLDER}" + +if [[ -z "${LOCKFILES}" ]] +then + echo "/org/gnome/desktop/screensaver/lock-enabled" >> "/etc/dconf/db/local.d/locks/00-security-settings-lock" +fi + +dconf update + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Implement Blank Screensaver + + + +To set the screensaver mode in the GNOME3 desktop to a blank screen, +add or set picture-uri to string '' in +/etc/dconf/db/local.d/00-security-settings. For example: +[org/gnome/desktop/screensaver] +picture-uri=string '' + +Once the settings have been added, add a lock to +/etc/dconf/db/local.d/locks/00-security-settings-lock to prevent user modification. +For example: +/org/gnome/desktop/screensaver/picture-uri +After the settings have been set, run dconf update. + 1 + 12 + 15 + 16 + 5.5.5 + DSS05.04 + DSS05.10 + DSS06.10 + 3.1.10 + CCI-000060 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + A.18.1.4 + A.9.2.1 + A.9.2.4 + A.9.3.1 + A.9.4.2 + A.9.4.3 + AC-11(1) + CM-6(a) + AC-11(1).1 + PR.AC-7 + FMT_MOF_EXT.1 + Req-8.1.8 + SRG-OS-000031-GPOS-00012 + Setting the screensaver mode to blank-only conceals the +contents of the display from passersby. + + CCE-80778-4 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80778-4 + - CJIS-5.5.5 + - NIST-800-171-3.1.10 + - NIST-800-53-AC-11(1) + - NIST-800-53-AC-11(1).1 + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.1.8 + - dconf_gnome_screensaver_mode_blank + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Implement Blank Screensaver + ini_file: + dest: /etc/dconf/db/local.d/00-security-settings + section: org/gnome/desktop/screensaver + option: picture-uri + value: string '' + create: true + no_extra_spaces: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80778-4 + - CJIS-5.5.5 + - NIST-800-171-3.1.10 + - NIST-800-53-AC-11(1) + - NIST-800-53-AC-11(1).1 + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.1.8 + - dconf_gnome_screensaver_mode_blank + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Prevent user modification of GNOME picture-uri + lineinfile: + path: /etc/dconf/db/local.d/locks/00-security-settings-lock + regexp: ^/org/gnome/desktop/screensaver/picture-uri$ + line: /org/gnome/desktop/screensaver/picture-uri + create: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80778-4 + - CJIS-5.5.5 + - NIST-800-171-3.1.10 + - NIST-800-53-AC-11(1) + - NIST-800-53-AC-11(1).1 + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.1.8 + - dconf_gnome_screensaver_mode_blank + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Dconf Update + command: dconf update + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80778-4 + - CJIS-5.5.5 + - NIST-800-171-3.1.10 + - NIST-800-53-AC-11(1) + - NIST-800-53-AC-11(1).1 + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.1.8 + - dconf_gnome_screensaver_mode_blank + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q gdm && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +# Check for setting in any of the DConf db directories +# If files contain ibus or distro, ignore them. +# The assignment assumes that individual filenames don't contain : +readarray -t SETTINGSFILES < <(grep -r "\\[org/gnome/desktop/screensaver\\]" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +DCONFFILE="/etc/dconf/db/local.d/00-security-settings" +DBDIR="/etc/dconf/db/local.d" + +mkdir -p "${DBDIR}" + +if [ "${#SETTINGSFILES[@]}" -eq 0 ] +then + [ ! -z ${DCONFFILE} ] || echo "" >> ${DCONFFILE} + printf '%s\n' "[org/gnome/desktop/screensaver]" >> ${DCONFFILE} + printf '%s=%s\n' "picture-uri" "string ''" >> ${DCONFFILE} +else + escaped_value="$(sed -e 's/\\/\\\\/g' <<< "string ''")" + if grep -q "^\\s*picture-uri\\s*=" "${SETTINGSFILES[@]}" + then + + sed -i "s/\\s*picture-uri\\s*=\\s*.*/picture-uri=${escaped_value}/g" "${SETTINGSFILES[@]}" + else + sed -i "\\|\\[org/gnome/desktop/screensaver\\]|a\\picture-uri=${escaped_value}" "${SETTINGSFILES[@]}" + fi +fi + +dconf update +# Check for setting in any of the DConf db directories +LOCKFILES=$(grep -r "^/org/gnome/desktop/screensaver/picture-uri$" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +LOCKSFOLDER="/etc/dconf/db/local.d/locks" + +mkdir -p "${LOCKSFOLDER}" + +if [[ -z "${LOCKFILES}" ]] +then + echo "/org/gnome/desktop/screensaver/picture-uri" >> "/etc/dconf/db/local.d/locks/00-security-settings-lock" +fi + +dconf update + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable Full User Name on Splash Shield + By default when the screen is locked, the splash shield will show the user's +full name. This should be disabled to prevent casual observers from seeing +who has access to the system. This can be disabled by adding or setting +show-full-name-in-top-bar to false in +/etc/dconf/db/local.d/00-security-settings. For example: +[org/gnome/desktop/screensaver] +show-full-name-in-top-bar=false + +Once the settings have been added, add a lock to +/etc/dconf/db/local.d/locks/00-security-settings-lock to prevent user modification. +For example: +/org/gnome/desktop/screensaver/show-full-name-in-top-bar +After the settings have been set, run dconf update. + FMT_MOF_EXT.1 + Setting the splash screen to not reveal the logged in user's name +conceals who has access to the system from passersby. + + CCE-80779-2 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80779-2 + - dconf_gnome_screensaver_user_info + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Disable Full Username on Splash Screen + ini_file: + dest: /etc/dconf/db/local.d/00-security-settings + section: org/gnome/desktop/screensaver + option: show-full-name-in-top-bar + value: 'false' + create: true + no_extra_spaces: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80779-2 + - dconf_gnome_screensaver_user_info + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Prevent user modification of GNOME show-full-name-in-top-bar + lineinfile: + path: /etc/dconf/db/local.d/locks/00-security-settings-lock + regexp: ^/org/gnome/desktop/screensaver/show-full-name-in-top-bar$ + line: /org/gnome/desktop/screensaver/show-full-name-in-top-bar + create: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80779-2 + - dconf_gnome_screensaver_user_info + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Dconf Update + command: dconf update + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80779-2 + - dconf_gnome_screensaver_user_info + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q gdm && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +# Check for setting in any of the DConf db directories +# If files contain ibus or distro, ignore them. +# The assignment assumes that individual filenames don't contain : +readarray -t SETTINGSFILES < <(grep -r "\\[org/gnome/desktop/screensaver\\]" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +DCONFFILE="/etc/dconf/db/local.d/00-security-settings" +DBDIR="/etc/dconf/db/local.d" + +mkdir -p "${DBDIR}" + +if [ "${#SETTINGSFILES[@]}" -eq 0 ] +then + [ ! -z ${DCONFFILE} ] || echo "" >> ${DCONFFILE} + printf '%s\n' "[org/gnome/desktop/screensaver]" >> ${DCONFFILE} + printf '%s=%s\n' "show-full-name-in-top-bar" "false" >> ${DCONFFILE} +else + escaped_value="$(sed -e 's/\\/\\\\/g' <<< "false")" + if grep -q "^\\s*show-full-name-in-top-bar\\s*=" "${SETTINGSFILES[@]}" + then + + sed -i "s/\\s*show-full-name-in-top-bar\\s*=\\s*.*/show-full-name-in-top-bar=${escaped_value}/g" "${SETTINGSFILES[@]}" + else + sed -i "\\|\\[org/gnome/desktop/screensaver\\]|a\\show-full-name-in-top-bar=${escaped_value}" "${SETTINGSFILES[@]}" + fi +fi + +dconf update +# Check for setting in any of the DConf db directories +LOCKFILES=$(grep -r "^/org/gnome/desktop/screensaver/show-full-name-in-top-bar$" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +LOCKSFOLDER="/etc/dconf/db/local.d/locks" + +mkdir -p "${LOCKSFOLDER}" + +if [[ -z "${LOCKFILES}" ]] +then + echo "/org/gnome/desktop/screensaver/show-full-name-in-top-bar" >> "/etc/dconf/db/local.d/locks/00-security-settings-lock" +fi + +dconf update + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure Users Cannot Change GNOME3 Screensaver Settings + If not already configured, ensure that users cannot change GNOME3 screensaver lock settings +by adding /org/gnome/desktop/screensaver/lock-delay +to /etc/dconf/db/local.d/locks/00-security-settings-lock to prevent user modification. +For example: +/org/gnome/desktop/screensaver/lock-delay +After the settings have been set, run dconf update. + 1 + 12 + 15 + 16 + DSS05.04 + DSS05.10 + DSS06.10 + 3.1.10 + CCI-000057 + CCI-000060 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + A.18.1.4 + A.9.2.1 + A.9.2.4 + A.9.3.1 + A.9.4.2 + A.9.4.3 + CM-6(a) + PR.AC-7 + FMT_MOF_EXT.1 + SRG-OS-000029-GPOS-00010 + SRG-OS-000031-GPOS-00012 + RHEL-08-020080 + SV-230354r743990_rule + A session time-out lock is a temporary action taken when a user stops work and moves away from the immediate +physical vicinity of the information system but does not logout because of the temporary nature of the absence. +Rather than relying on the user to manually lock their operating system session prior to vacating the vicinity, +GNOME desktops can be configured to identify when a user's session has idled and take action to initiate the +session lock. As such, users should not be allowed to change session settings. + + CCE-80780-0 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80780-0 + - DISA-STIG-RHEL-08-020080 + - NIST-800-171-3.1.10 + - NIST-800-53-CM-6(a) + - dconf_gnome_screensaver_user_locks + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Prevent user modification of GNOME lock-delay + lineinfile: + path: /etc/dconf/db/local.d/locks/00-security-settings-lock + regexp: ^/org/gnome/desktop/screensaver/lock-delay$ + line: /org/gnome/desktop/screensaver/lock-delay + create: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80780-0 + - DISA-STIG-RHEL-08-020080 + - NIST-800-171-3.1.10 + - NIST-800-53-CM-6(a) + - dconf_gnome_screensaver_user_locks + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Dconf Update + command: dconf update + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80780-0 + - DISA-STIG-RHEL-08-020080 + - NIST-800-171-3.1.10 + - NIST-800-53-CM-6(a) + - dconf_gnome_screensaver_user_locks + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q gdm && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +# Check for setting in any of the DConf db directories +LOCKFILES=$(grep -r "^/org/gnome/desktop/screensaver/lock-delay$" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +LOCKSFOLDER="/etc/dconf/db/local.d/locks" + +mkdir -p "${LOCKSFOLDER}" + +if [[ -z "${LOCKFILES}" ]] +then + echo "/org/gnome/desktop/screensaver/lock-delay" >> "/etc/dconf/db/local.d/locks/00-security-settings-lock" +fi + +dconf update + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure Users Cannot Change GNOME3 Session Idle Settings + If not already configured, ensure that users cannot change GNOME3 session idle settings +by adding /org/gnome/desktop/session/idle-delay +to /etc/dconf/db/local.d/locks/00-security-settings-lock to prevent user modification. +For example: +/org/gnome/desktop/session/idle-delay +After the settings have been set, run dconf update. + 1 + 12 + 15 + 16 + DSS05.04 + DSS05.10 + DSS06.10 + 3.1.10 + CCI-000057 + CCI-000060 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + A.18.1.4 + A.9.2.1 + A.9.2.4 + A.9.3.1 + A.9.4.2 + A.9.4.3 + CM-6(a) + PR.AC-7 + FMT_MOF_EXT.1 + SRG-OS-000029-GPOS-00010 + SRG-OS-000031-GPOS-00012 + RHEL-08-020081 + SV-244538r743863_rule + A session time-out lock is a temporary action taken when a user stops work and moves away from the immediate +physical vicinity of the information system but does not logout because of the temporary nature of the absence. +Rather than relying on the user to manually lock their operating system session prior to vacating the vicinity, +GNOME desktops can be configured to identify when a user's session has idled and take action to initiate the +session lock. As such, users should not be allowed to change session settings. + + CCE-80781-8 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80781-8 + - DISA-STIG-RHEL-08-020081 + - NIST-800-171-3.1.10 + - NIST-800-53-CM-6(a) + - dconf_gnome_session_idle_user_locks + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Prevent user modification of GNOME Session idle-delay + lineinfile: + path: /etc/dconf/db/local.d/locks/00-security-settings-lock + regexp: ^/org/gnome/desktop/session/idle-delay$ + line: /org/gnome/desktop/session/idle-delay + create: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80781-8 + - DISA-STIG-RHEL-08-020081 + - NIST-800-171-3.1.10 + - NIST-800-53-CM-6(a) + - dconf_gnome_session_idle_user_locks + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Dconf Update + command: dconf update + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80781-8 + - DISA-STIG-RHEL-08-020081 + - NIST-800-171-3.1.10 + - NIST-800-53-CM-6(a) + - dconf_gnome_session_idle_user_locks + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q gdm && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +# Check for setting in any of the DConf db directories +LOCKFILES=$(grep -r "^/org/gnome/desktop/session/idle-delay$" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +LOCKSFOLDER="/etc/dconf/db/local.d/locks" + +mkdir -p "${LOCKSFOLDER}" + +if [[ -z "${LOCKFILES}" ]] +then + echo "/org/gnome/desktop/session/idle-delay" >> "/etc/dconf/db/local.d/locks/00-security-settings-lock" +fi + +dconf update + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + GNOME System Settings + GNOME provides configuration and functionality to a graphical desktop environment +that changes grahical configurations or allow a user to perform +actions that users normally would not be able to do in non-graphical mode such as +remote access configuration, power policies, Geo-location, etc. +Configuring such settings in GNOME will prevent accidential graphical configuration +changes by users from taking place. + + Disable Ctrl-Alt-Del Reboot Key Sequence in GNOME3 + By default, GNOME will reboot the system if the +Ctrl-Alt-Del key sequence is pressed. + +To configure the system to ignore the Ctrl-Alt-Del key sequence +from the Graphical User Interface (GUI) instead of rebooting the system, +add or set logout to '' in +/etc/dconf/db/local.d/00-security-settings. For example: +[org/gnome/settings-daemon/plugins/media-keys] +logout='' +Once the settings have been added, add a lock to +/etc/dconf/db/local.d/locks/00-security-settings-lock to prevent +user modification. For example: +/org/gnome/settings-daemon/plugins/media-keys/logout +After the settings have been set, run dconf update. + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + 3.1.2 + CCI-000366 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-6(a) + AC-6(1) + CM-7(b) + PR.AC-4 + PR.DS-5 + SRG-OS-000480-GPOS-00227 + RHEL-08-040171 + SV-230530r646883_rule + A locally logged-in user who presses Ctrl-Alt-Del, when at the console, +can reboot the system. If accidentally pressed, as could happen in +the case of mixed OS environment, this can create the risk of short-term +loss of availability of systems due to unintentional reboot. + + CCE-84028-0 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-84028-0 + - DISA-STIG-RHEL-08-040171 + - NIST-800-171-3.1.2 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(b) + - dconf_gnome_disable_ctrlaltdel_reboot + - high_severity + - low_complexity + - medium_disruption + - no_reboot_needed + - unknown_strategy + +- name: Disable Ctrl-Alt-Del Reboot Key Sequence in GNOME3 + ini_file: + dest: /etc/dconf/db/local.d/00-security-settings + section: org/gnome/settings-daemon/plugins/media-keys + option: logout + value: '''''' + create: true + no_extra_spaces: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84028-0 + - DISA-STIG-RHEL-08-040171 + - NIST-800-171-3.1.2 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(b) + - dconf_gnome_disable_ctrlaltdel_reboot + - high_severity + - low_complexity + - medium_disruption + - no_reboot_needed + - unknown_strategy + +- name: Prevent user modification of GNOME disablement of Ctrl-Alt-Del + lineinfile: + path: /etc/dconf/db/local.d/locks/00-security-settings-lock + regexp: ^/org/gnome/settings-daemon/plugins/media-keys/logout$ + line: /org/gnome/settings-daemon/plugins/media-keys/logout + create: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84028-0 + - DISA-STIG-RHEL-08-040171 + - NIST-800-171-3.1.2 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(b) + - dconf_gnome_disable_ctrlaltdel_reboot + - high_severity + - low_complexity + - medium_disruption + - no_reboot_needed + - unknown_strategy + +- name: Dconf Update + command: dconf update + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84028-0 + - DISA-STIG-RHEL-08-040171 + - NIST-800-171-3.1.2 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(b) + - dconf_gnome_disable_ctrlaltdel_reboot + - high_severity + - low_complexity + - medium_disruption + - no_reboot_needed + - unknown_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q gdm && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +# Check for setting in any of the DConf db directories +# If files contain ibus or distro, ignore them. +# The assignment assumes that individual filenames don't contain : +readarray -t SETTINGSFILES < <(grep -r "\\[org/gnome/settings-daemon/plugins/media-keys\\]" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +DCONFFILE="/etc/dconf/db/local.d/00-security-settings" +DBDIR="/etc/dconf/db/local.d" + +mkdir -p "${DBDIR}" + +if [ "${#SETTINGSFILES[@]}" -eq 0 ] +then + [ ! -z ${DCONFFILE} ] || echo "" >> ${DCONFFILE} + printf '%s\n' "[org/gnome/settings-daemon/plugins/media-keys]" >> ${DCONFFILE} + printf '%s=%s\n' "logout" "''" >> ${DCONFFILE} +else + escaped_value="$(sed -e 's/\\/\\\\/g' <<< "''")" + if grep -q "^\\s*logout\\s*=" "${SETTINGSFILES[@]}" + then + + sed -i "s/\\s*logout\\s*=\\s*.*/logout=${escaped_value}/g" "${SETTINGSFILES[@]}" + else + sed -i "\\|\\[org/gnome/settings-daemon/plugins/media-keys\\]|a\\logout=${escaped_value}" "${SETTINGSFILES[@]}" + fi +fi + +dconf update +# Check for setting in any of the DConf db directories +LOCKFILES=$(grep -r "^/org/gnome/settings-daemon/plugins/media-keys/logout$" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +LOCKSFOLDER="/etc/dconf/db/local.d/locks" + +mkdir -p "${LOCKSFOLDER}" + +if [[ -z "${LOCKFILES}" ]] +then + echo "/org/gnome/settings-daemon/plugins/media-keys/logout" >> "/etc/dconf/db/local.d/locks/00-security-settings-lock" +fi + +dconf update + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable Geolocation in GNOME3 + GNOME allows the clock and applications to track and access +location information. This setting should be disabled as applications +should not track system location. To configure the system to disable +location tracking, add or set enabled to false in +/etc/dconf/db/local.d/00-security-settings. For example: +[org/gnome/system/location] +enabled=false +To configure the clock to disable location tracking, add or set +geolocation to false in +/etc/dconf/db/local.d/00-security-settings. For example: +[org/gnome/clocks] +geolocation=false +Once the settings have been added, add a lock to +/etc/dconf/db/local.d/locks/00-security-settings-lock to prevent +user modification. For example: +/org/gnome/system/location/enabled +/org/gnome/clocks/geolocation +After the settings have been set, run dconf update. + Power settings should not be enabled on systems that are not mobile devices. +Enabling power settings on non-mobile devices could have unintended processing +consequences on standard systems. + + - name: Gather the package facts + package_facts: + manager: auto + tags: + - dconf_gnome_disable_geolocation + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Disable Geolocation in GNOME3 - location tracking + ini_file: + dest: /etc/dconf/db/local.d/00-security-settings + section: org/gnome/system/location + option: enabled + value: 'false' + create: true + no_extra_spaces: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - dconf_gnome_disable_geolocation + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Disable Geolocation in GNOME3 - clock location tracking + ini_file: + dest: /etc/dconf/db/local.d/00-security-settings + section: org/gnome/clocks + option: gelocation + value: 'false' + create: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - dconf_gnome_disable_geolocation + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Prevent user modification of GNOME geolocation - location tracking + lineinfile: + path: /etc/dconf/db/local.d/locks/00-security-settings-lock + regexp: ^/org/gnome/system/location/enabled$ + line: /org/gnome/system/location/enabled + create: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - dconf_gnome_disable_geolocation + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Prevent user modification of GNOME geolocation - clock location tracking + lineinfile: + path: /etc/dconf/db/local.d/locks/00-security-settings-lock + regexp: ^/org/gnome/clocks/geolocation$ + line: /org/gnome/clocks/geolocation + create: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - dconf_gnome_disable_geolocation + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Dconf Update + command: dconf update + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - dconf_gnome_disable_geolocation + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q gdm && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +# Check for setting in any of the DConf db directories +# If files contain ibus or distro, ignore them. +# The assignment assumes that individual filenames don't contain : +readarray -t SETTINGSFILES < <(grep -r "\\[org/gnome/system/location\\]" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +DCONFFILE="/etc/dconf/db/local.d/00-security-settings" +DBDIR="/etc/dconf/db/local.d" + +mkdir -p "${DBDIR}" + +if [ "${#SETTINGSFILES[@]}" -eq 0 ] +then + [ ! -z ${DCONFFILE} ] || echo "" >> ${DCONFFILE} + printf '%s\n' "[org/gnome/system/location]" >> ${DCONFFILE} + printf '%s=%s\n' "enabled" "false" >> ${DCONFFILE} +else + escaped_value="$(sed -e 's/\\/\\\\/g' <<< "false")" + if grep -q "^\\s*enabled\\s*=" "${SETTINGSFILES[@]}" + then + + sed -i "s/\\s*enabled\\s*=\\s*.*/enabled=${escaped_value}/g" "${SETTINGSFILES[@]}" + else + sed -i "\\|\\[org/gnome/system/location\\]|a\\enabled=${escaped_value}" "${SETTINGSFILES[@]}" + fi +fi + +dconf update +# Check for setting in any of the DConf db directories +# If files contain ibus or distro, ignore them. +# The assignment assumes that individual filenames don't contain : +readarray -t SETTINGSFILES < <(grep -r "\\[org/gnome/clocks\\]" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +DCONFFILE="/etc/dconf/db/local.d/00-security-settings" +DBDIR="/etc/dconf/db/local.d" + +mkdir -p "${DBDIR}" + +if [ "${#SETTINGSFILES[@]}" -eq 0 ] +then + [ ! -z ${DCONFFILE} ] || echo "" >> ${DCONFFILE} + printf '%s\n' "[org/gnome/clocks]" >> ${DCONFFILE} + printf '%s=%s\n' "geolocation" "false" >> ${DCONFFILE} +else + escaped_value="$(sed -e 's/\\/\\\\/g' <<< "false")" + if grep -q "^\\s*geolocation\\s*=" "${SETTINGSFILES[@]}" + then + + sed -i "s/\\s*geolocation\\s*=\\s*.*/geolocation=${escaped_value}/g" "${SETTINGSFILES[@]}" + else + sed -i "\\|\\[org/gnome/clocks\\]|a\\geolocation=${escaped_value}" "${SETTINGSFILES[@]}" + fi +fi + +dconf update +# Check for setting in any of the DConf db directories +LOCKFILES=$(grep -r "^/org/gnome/system/location/enabled$" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +LOCKSFOLDER="/etc/dconf/db/local.d/locks" + +mkdir -p "${LOCKSFOLDER}" + +if [[ -z "${LOCKFILES}" ]] +then + echo "/org/gnome/system/location/enabled" >> "/etc/dconf/db/local.d/locks/00-security-settings-lock" +fi + +dconf update +# Check for setting in any of the DConf db directories +LOCKFILES=$(grep -r "^/org/gnome/clocks/geolocation$" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +LOCKSFOLDER="/etc/dconf/db/local.d/locks" + +mkdir -p "${LOCKSFOLDER}" + +if [[ -z "${LOCKFILES}" ]] +then + echo "/org/gnome/clocks/geolocation" >> "/etc/dconf/db/local.d/locks/00-security-settings-lock" +fi + +dconf update + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable Power Settings in GNOME3 + By default, GNOME enables a power profile designed for mobile devices +with battery usage. While useful for mobile devices, this setting should be disabled +for all other systems. To configure the system to disable the power setting, add or set +active to false in +/etc/dconf/db/local.d/00-security-settings. For example: +[org/gnome/settings-daemon/plugins/power] +active=false + +Once the settings have been added, add a lock to +/etc/dconf/db/local.d/locks/00-security-settings-lock to prevent user modification. +For example: +/org/gnome/settings-daemon/plugins/power +After the settings have been set, run dconf update. + Power settings should not be enabled on systems that are not mobile devices. +Enabling power settings on non-mobile devices could have unintended processing +consequences on standard systems. + + + + + + + + + + Disable User Administration in GNOME3 + By default, GNOME will allow all users to have some administratrion +capability. This should be disabled so that non-administrative users are not making +configuration changes. To configure the system to disable user administration +capability in the Graphical User Interface (GUI), add or set +user-administration-disabled to true in +/etc/dconf/db/local.d/00-security-settings. For example: +[org/gnome/desktop/lockdown] +user-administration-disabled=true + +Once the settings have been added, add a lock to +/etc/dconf/db/local.d/locks/00-security-settings-lock to prevent user modification. +For example: +/org/gnome/desktop/lockdown/user-administration-disabled +After the settings have been set, run dconf update. + 3.1.5 + FMT_MOD_EXT.1 + Allowing all users to have some administratrive capabilities to the system through +the Graphical User Interface (GUI) when they would not have them otherwise could allow +unintended configuration changes as well as a nefarious user the capability to make system +changes such as adding new accounts, etc. + + CCE-80769-3 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80769-3 + - NIST-800-171-3.1.5 + - dconf_gnome_disable_user_admin + - high_severity + - low_complexity + - medium_disruption + - no_reboot_needed + - unknown_strategy + +- name: Detect if user-administration-disabled can be found on /etc/dconf/db/local.d/ + find: + path: /etc/dconf/db/local.d/ + contains: ^\s*user-administration-disabled + register: dconf_gnome_disable_user_admin_config_files + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80769-3 + - NIST-800-171-3.1.5 + - dconf_gnome_disable_user_admin + - high_severity + - low_complexity + - medium_disruption + - no_reboot_needed + - unknown_strategy + +- name: Configure user-administration-disabled - default file + ini_file: + dest: /etc/dconf/db/local.d//00-security-settings + section: org/gnome/desktop/lockdown + option: user-administration-disabled + value: 'true' + create: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - dconf_gnome_disable_user_admin_config_files is defined and dconf_gnome_disable_user_admin_config_files.matched + == 0 + tags: + - CCE-80769-3 + - NIST-800-171-3.1.5 + - dconf_gnome_disable_user_admin + - high_severity + - low_complexity + - medium_disruption + - no_reboot_needed + - unknown_strategy + +- name: Configure user-administration-disabled - existing files + ini_file: + dest: '{{ item.path }}' + section: org/gnome/desktop/lockdown + option: user-administration-disabled + value: 'true' + create: true + with_items: '{{ dconf_gnome_disable_user_admin_config_files.files }}' + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - dconf_gnome_disable_user_admin_config_files is defined and dconf_gnome_disable_user_admin_config_files.matched + > 0 + tags: + - CCE-80769-3 + - NIST-800-171-3.1.5 + - dconf_gnome_disable_user_admin + - high_severity + - low_complexity + - medium_disruption + - no_reboot_needed + - unknown_strategy + +- name: Detect if lock for user-administration-disabled can be found on /etc/dconf/db/local.d/ + find: + path: /etc/dconf/db/local.d/locks + contains: ^\s*user-administration-disabled + register: dconf_gnome_disable_user_admin_lock_files + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80769-3 + - NIST-800-171-3.1.5 + - dconf_gnome_disable_user_admin + - high_severity + - low_complexity + - medium_disruption + - no_reboot_needed + - unknown_strategy + +- name: Prevent user modification user-administration-disabled - default file + lineinfile: + path: /etc/dconf/db/local.d/locks/00-security-settings-lock + regexp: ^/org/gnome/desktop/lockdown/user-administration-disabled$ + line: /org/gnome/desktop/lockdown/user-administration-disabled + create: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - dconf_gnome_disable_user_admin_lock_files is defined and dconf_gnome_disable_user_admin_lock_files.matched + == 0 + tags: + - CCE-80769-3 + - NIST-800-171-3.1.5 + - dconf_gnome_disable_user_admin + - high_severity + - low_complexity + - medium_disruption + - no_reboot_needed + - unknown_strategy + +- name: Prevent user modification user-administration-disabled - existing files + lineinfile: + path: '{{ item.path }}' + regexp: ^/org/gnome/desktop/lockdown/user-administration-disabled$ + line: /org/gnome/desktop/lockdown/user-administration-disabled + create: true + with_items: '{{ dconf_gnome_disable_user_admin_lock_files.files }}' + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - dconf_gnome_disable_user_admin_lock_files is defined and dconf_gnome_disable_user_admin_lock_files.matched + > 0 + tags: + - CCE-80769-3 + - NIST-800-171-3.1.5 + - dconf_gnome_disable_user_admin + - high_severity + - low_complexity + - medium_disruption + - no_reboot_needed + - unknown_strategy + +- name: Dconf Update - user-administration-disabled + command: dconf update + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80769-3 + - NIST-800-171-3.1.5 + - dconf_gnome_disable_user_admin + - high_severity + - low_complexity + - medium_disruption + - no_reboot_needed + - unknown_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q gdm && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +# Check for setting in any of the DConf db directories +# If files contain ibus or distro, ignore them. +# The assignment assumes that individual filenames don't contain : +readarray -t SETTINGSFILES < <(grep -r "\\[org/gnome/desktop/lockdown\\]" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +DCONFFILE="/etc/dconf/db/local.d/00-security-settings" +DBDIR="/etc/dconf/db/local.d" + +mkdir -p "${DBDIR}" + +if [ "${#SETTINGSFILES[@]}" -eq 0 ] +then + [ ! -z ${DCONFFILE} ] || echo "" >> ${DCONFFILE} + printf '%s\n' "[org/gnome/desktop/lockdown]" >> ${DCONFFILE} + printf '%s=%s\n' "user-administration-disabled" "true" >> ${DCONFFILE} +else + escaped_value="$(sed -e 's/\\/\\\\/g' <<< "true")" + if grep -q "^\\s*user-administration-disabled\\s*=" "${SETTINGSFILES[@]}" + then + + sed -i "s/\\s*user-administration-disabled\\s*=\\s*.*/user-administration-disabled=${escaped_value}/g" "${SETTINGSFILES[@]}" + else + sed -i "\\|\\[org/gnome/desktop/lockdown\\]|a\\user-administration-disabled=${escaped_value}" "${SETTINGSFILES[@]}" + fi +fi + +dconf update +# Check for setting in any of the DConf db directories +LOCKFILES=$(grep -r "^/org/gnome/desktop/lockdown/user-administration-disabled$" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +LOCKSFOLDER="/etc/dconf/db/local.d/locks" + +mkdir -p "${LOCKSFOLDER}" + +if [[ -z "${LOCKFILES}" ]] +then + echo "/org/gnome/desktop/lockdown/user-administration-disabled" >> "/etc/dconf/db/local.d/locks/00-security-settings-lock" +fi + +dconf update + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + + SAP Specific Requirement + SAP (Systems, Applications and Products in Data Processing) is enterprise +software to manage business operations and customer relations. The +following section contains SAP specific requirement that is not part +of standard or common OS setting. + + + Sudo + Sudo, which stands for "su 'do'", provides the ability to delegate authority +to certain users, groups of users, or system administrators. When configured for system +users and/or groups, Sudo can allow a user or group to execute privileged commands +that normally only root is allowed to execute. + +For more information on Sudo and addition Sudo configuration options, see +https://www.sudo.ws. + + Group name dedicated to the use of sudo + Specify the name of the group that should own /usr/bin/sudo. + root + sudogrp + + + Sudo - logfile value + Specify the sudo logfile to use. The default value used here matches the example +location from CIS, which uses /var/log/sudo.log. + /var/log/sudo.log + /var/log/sudo.log + + + Sudo - passwd_timeout value + Defines the number of minutes before the sudo password prompt times out. +Defining 0 means no timeout. The default timeout value is 5 minutes. + 5 + 0 + 1 + 2 + 3 + 5 + + + Sudo - timestamp_timeout value + Defines the number of minutes that can elapse before sudo will ask for a passwd again. +If set to a value less than 0 the user's time stamp will never expire. Defining 0 means always prompt for a +password. The default timeout value is 5 minutes. + 5 + 0 + 1 + 2 + 3 + 5 + 15 + + + Sudo - umask value + Specify the sudo umask to use. The actual umask value that is used is the union +of the user's umask and the sudo umask. +The default sudo umask is 0022. This guarantess sudo never lowers the umask when +running a command. + 0022 + 0022 + 0027 + + + Install sudo Package + The sudo package can be installed with the following command: + +$ sudo yum install sudo + BP28(R19) + 1382 + 1384 + 1386 + CM-6(a) + FMT_MOF_EXT.1 + SRG-OS-000324-GPOS-00125 + 5.3.1 + sudo is a program designed to allow a system administrator to give +limited root privileges to users and log root activity. The basic philosophy +is to give as few privileges as possible but still allow system users to +get their work done. + + CCE-82214-8 + +package --add=sudo + + include install_sudo + +class install_sudo { + package { 'sudo': + ensure => 'installed', + } +} + + - name: Ensure sudo is installed + package: + name: sudo + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82214-8 + - NIST-800-53-CM-6(a) + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_sudo_installed + + +[[packages]] +name = "sudo" +version = "*" + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if ! rpm -q --quiet "sudo" ; then + yum install -y "sudo" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure sudo Runs In A Minimal Environment - sudo env_reset + The sudo env_reset tag, when specified, will run the command in a minimal environment, +containing the TERM, PATH, HOME, MAIL, SHELL, LOGNAME, USER and SUDO_* variables. +On Red Hat Enterprise Linux 8, env_reset is enabled by default +This should be enabled by making sure that the env_reset tag exists in +/etc/sudoers configuration file or any sudo configuration snippets +in /etc/sudoers.d/. + BP28(R58) + Forcing sudo to reset the environment ensures that environment variables are not passed on to the +command accidentaly, preventing leak of potentially sensitive information. + CCE-83820-1 + - name: Ensure env_reset is enabled in /etc/sudoers + lineinfile: + path: /etc/sudoers + regexp: ^[\s]*Defaults.*\benv_reset\b.*$ + line: Defaults env_reset + validate: /usr/sbin/visudo -cf %s + tags: + - CCE-83820-1 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sudo_add_env_reset + + +if /usr/sbin/visudo -qcf /etc/sudoers; then + cp /etc/sudoers /etc/sudoers.bak + if ! grep -P '^[\s]*Defaults[\s]*\benv_reset\b.*$' /etc/sudoers; then + # sudoers file doesn't define Option env_reset + echo "Defaults env_reset" >> /etc/sudoers + fi + + # Check validity of sudoers and cleanup bak + if /usr/sbin/visudo -qcf /etc/sudoers; then + rm -f /etc/sudoers.bak + else + echo "Fail to validate remediated /etc/sudoers, reverting to original file." + mv /etc/sudoers.bak /etc/sudoers + false + fi +else + echo "Skipping remediation, /etc/sudoers failed to validate" + false +fi + + + + + + + + + + Ensure sudo Ignores Commands In Current Dir - sudo ignore_dot + The sudo ignore_dot tag, when specified, will ignore the current directory +in the PATH environment variable. +On Red Hat Enterprise Linux 8, env_reset is enabled by default +This should be enabled by making sure that the ignore_dot tag exists in +/etc/sudoers configuration file or any sudo configuration snippets +in /etc/sudoers.d/. + BP28(R58) + Ignoring the commands in the user's current directory prevents an attacker from executing commands +downloaded locally. + CCE-83810-2 + - name: Ensure ignore_dot is enabled in /etc/sudoers + lineinfile: + path: /etc/sudoers + regexp: ^[\s]*Defaults.*\bignore_dot\b.*$ + line: Defaults ignore_dot + validate: /usr/sbin/visudo -cf %s + tags: + - CCE-83810-2 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sudo_add_ignore_dot + + +if /usr/sbin/visudo -qcf /etc/sudoers; then + cp /etc/sudoers /etc/sudoers.bak + if ! grep -P '^[\s]*Defaults[\s]*\bignore_dot\b.*$' /etc/sudoers; then + # sudoers file doesn't define Option ignore_dot + echo "Defaults ignore_dot" >> /etc/sudoers + fi + + # Check validity of sudoers and cleanup bak + if /usr/sbin/visudo -qcf /etc/sudoers; then + rm -f /etc/sudoers.bak + else + echo "Fail to validate remediated /etc/sudoers, reverting to original file." + mv /etc/sudoers.bak /etc/sudoers + false + fi +else + echo "Skipping remediation, /etc/sudoers failed to validate" + false +fi + + + + + + + + + + Ensure Privileged Escalated Commands Cannot Execute Other Commands - sudo NOEXEC + The sudo NOEXEC tag, when specified, prevents user executed +commands from executing other commands, like a shell for example. +This should be enabled by making sure that the NOEXEC tag exists in +/etc/sudoers configuration file or any sudo configuration snippets +in /etc/sudoers.d/. + BP28(R58) + Restricting the capability of sudo allowed commands to execute sub-commands +prevents users from running programs with privileges they wouldn't have otherwise. + CCE-83747-6 + - name: Ensure noexec is enabled in /etc/sudoers + lineinfile: + path: /etc/sudoers + regexp: ^[\s]*Defaults.*\bnoexec\b.*$ + line: Defaults noexec + validate: /usr/sbin/visudo -cf %s + tags: + - CCE-83747-6 + - high_severity + - low_complexity + - low_disruption + - no_reboot_needed + - restrict_strategy + - sudo_add_noexec + + +if /usr/sbin/visudo -qcf /etc/sudoers; then + cp /etc/sudoers /etc/sudoers.bak + if ! grep -P '^[\s]*Defaults[\s]*\bnoexec\b.*$' /etc/sudoers; then + # sudoers file doesn't define Option noexec + echo "Defaults noexec" >> /etc/sudoers + fi + + # Check validity of sudoers and cleanup bak + if /usr/sbin/visudo -qcf /etc/sudoers; then + rm -f /etc/sudoers.bak + else + echo "Fail to validate remediated /etc/sudoers, reverting to original file." + mv /etc/sudoers.bak /etc/sudoers + false + fi +else + echo "Skipping remediation, /etc/sudoers failed to validate" + false +fi + + + + + + + + + + Ensure sudo passwd_timeout is appropriate - sudo passwd_timeout + The sudo passwd_timeout tag sets the amount of time sudo password prompt waits. +On Red Hat Enterprise Linux 8, the default passwd_timeout value is 5 minutes. + +The passwd_timeout should be configured by making sure that the +passwd_timeout= tag exists in +/etc/sudoers configuration file or any sudo configuration snippets +in /etc/sudoers.d/. + BP28(R58) + Reducing the time sudo waits for a a password reduces the time the process is exposed. + CCE-83964-7 + - name: XCCDF Value var_sudo_passwd_timeout # promote to variable + set_fact: + var_sudo_passwd_timeout: !!str + tags: + - always + +- name: Ensure passwd_timeout is enabled with the appropriate value in /etc/sudoers + lineinfile: + path: /etc/sudoers + regexp: ^[\s]*Defaults\s(.*)\bpasswd_timeout=[-]?.+\b(.*)$ + line: Defaults \1passwd_timeout={{ var_sudo_passwd_timeout }}\2 + validate: /usr/sbin/visudo -cf %s + backrefs: true + register: edit_sudoers_passwd_timeout_option + tags: + - CCE-83964-7 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sudo_add_passwd_timeout + +- name: Enable passwd_timeout option with appropriate value in /etc/sudoers + lineinfile: + path: /etc/sudoers + line: Defaults passwd_timeout={{ var_sudo_passwd_timeout }} + validate: /usr/sbin/visudo -cf %s + when: edit_sudoers_passwd_timeout_option is defined and not edit_sudoers_passwd_timeout_option.changed + tags: + - CCE-83964-7 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sudo_add_passwd_timeout + + + +var_sudo_passwd_timeout='' + + +if /usr/sbin/visudo -qcf /etc/sudoers; then + cp /etc/sudoers /etc/sudoers.bak + if ! grep -P '^[\s]*Defaults[\s]*\bpasswd_timeout=\w+\b\b.*$' /etc/sudoers; then + # sudoers file doesn't define Option passwd_timeout + echo "Defaults passwd_timeout=${var_sudo_passwd_timeout}" >> /etc/sudoers + else + # sudoers file defines Option passwd_timeout, remediate if appropriate value is not set + if ! grep -P "^[\s]*Defaults.*\bpasswd_timeout=${var_sudo_passwd_timeout}\b.*$" /etc/sudoers; then + + escaped_variable=${var_sudo_passwd_timeout//$'/'/$'\/'} + sed -Ei "s/(^[\s]*Defaults.*\bpasswd_timeout=)[-]?.+(\b.*$)/\1$escaped_variable\2/" /etc/sudoers + fi + fi + + # Check validity of sudoers and cleanup bak + if /usr/sbin/visudo -qcf /etc/sudoers; then + rm -f /etc/sudoers.bak + else + echo "Fail to validate remediated /etc/sudoers, reverting to original file." + mv /etc/sudoers.bak /etc/sudoers + false + fi +else + echo "Skipping remediation, /etc/sudoers failed to validate" + false +fi + + + + + + + + + + + Ensure Only Users Logged In To Real tty Can Execute Sudo - sudo requiretty + The sudo requiretty tag, when specified, will only execute sudo +commands from users logged in to a real tty. +This should be enabled by making sure that the requiretty tag exists in +/etc/sudoers configuration file or any sudo configuration snippets +in /etc/sudoers.d/. + BP28(R58) + Restricting the use cases in which a user is allowed to execute sudo commands +reduces the attack surface. + CCE-83790-6 + - name: Ensure requiretty is enabled in /etc/sudoers + lineinfile: + path: /etc/sudoers + regexp: ^[\s]*Defaults.*\brequiretty\b.*$ + line: Defaults requiretty + validate: /usr/sbin/visudo -cf %s + tags: + - CCE-83790-6 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sudo_add_requiretty + + +if /usr/sbin/visudo -qcf /etc/sudoers; then + cp /etc/sudoers /etc/sudoers.bak + if ! grep -P '^[\s]*Defaults[\s]*\brequiretty\b.*$' /etc/sudoers; then + # sudoers file doesn't define Option requiretty + echo "Defaults requiretty" >> /etc/sudoers + fi + + # Check validity of sudoers and cleanup bak + if /usr/sbin/visudo -qcf /etc/sudoers; then + rm -f /etc/sudoers.bak + else + echo "Fail to validate remediated /etc/sudoers, reverting to original file." + mv /etc/sudoers.bak /etc/sudoers + false + fi +else + echo "Skipping remediation, /etc/sudoers failed to validate" + false +fi + + + + + + + + + + Ensure sudo umask is appropriate - sudo umask + The sudo umask tag, when specified, will be added the to the user's umask in the +command environment. +On Red Hat Enterprise Linux 8, the default umask value is 0022. + +The umask should be configured by making sure that the umask= tag exists in +/etc/sudoers configuration file or any sudo configuration snippets +in /etc/sudoers.d/. + BP28(R58) + The umask value influences the permissions assigned to files when they are created. +A misconfigured umask value could result in files with excessive permissions that can be read or +written to by unauthorized users. + CCE-83860-7 + - name: XCCDF Value var_sudo_umask # promote to variable + set_fact: + var_sudo_umask: !!str + tags: + - always + +- name: Ensure umask is enabled with the appropriate value in /etc/sudoers + lineinfile: + path: /etc/sudoers + regexp: ^[\s]*Defaults\s(.*)\bumask=[-]?.+\b(.*)$ + line: Defaults \1umask={{ var_sudo_umask }}\2 + validate: /usr/sbin/visudo -cf %s + backrefs: true + register: edit_sudoers_umask_option + tags: + - CCE-83860-7 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sudo_add_umask + +- name: Enable umask option with appropriate value in /etc/sudoers + lineinfile: + path: /etc/sudoers + line: Defaults umask={{ var_sudo_umask }} + validate: /usr/sbin/visudo -cf %s + when: edit_sudoers_umask_option is defined and not edit_sudoers_umask_option.changed + tags: + - CCE-83860-7 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sudo_add_umask + + + +var_sudo_umask='' + + +if /usr/sbin/visudo -qcf /etc/sudoers; then + cp /etc/sudoers /etc/sudoers.bak + if ! grep -P '^[\s]*Defaults[\s]*\bumask=\w+\b\b.*$' /etc/sudoers; then + # sudoers file doesn't define Option umask + echo "Defaults umask=${var_sudo_umask}" >> /etc/sudoers + else + # sudoers file defines Option umask, remediate if appropriate value is not set + if ! grep -P "^[\s]*Defaults.*\bumask=${var_sudo_umask}\b.*$" /etc/sudoers; then + + escaped_variable=${var_sudo_umask//$'/'/$'\/'} + sed -Ei "s/(^[\s]*Defaults.*\bumask=)[-]?.+(\b.*$)/\1$escaped_variable\2/" /etc/sudoers + fi + fi + + # Check validity of sudoers and cleanup bak + if /usr/sbin/visudo -qcf /etc/sudoers; then + rm -f /etc/sudoers.bak + else + echo "Fail to validate remediated /etc/sudoers, reverting to original file." + mv /etc/sudoers.bak /etc/sudoers + false + fi +else + echo "Skipping remediation, /etc/sudoers failed to validate" + false +fi + + + + + + + + + + + Ensure Only Users Logged In To Real tty Can Execute Sudo - sudo use_pty + The sudo use_pty tag, when specified, will only execute sudo +commands from users logged in to a real tty. +This should be enabled by making sure that the use_pty tag exists in +/etc/sudoers configuration file or any sudo configuration snippets +in /etc/sudoers.d/. + BP28(R58) + 5.3.2 + Requiring that sudo commands be run in a pseudo-terminal can prevent an attacker from retaining +access to the user's terminal after the main program has finished executing. + CCE-83798-9 + - name: Ensure use_pty is enabled in /etc/sudoers + lineinfile: + path: /etc/sudoers + regexp: ^[\s]*Defaults.*\buse_pty\b.*$ + line: Defaults use_pty + validate: /usr/sbin/visudo -cf %s + tags: + - CCE-83798-9 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sudo_add_use_pty + + +if /usr/sbin/visudo -qcf /etc/sudoers; then + cp /etc/sudoers /etc/sudoers.bak + if ! grep -P '^[\s]*Defaults[\s]*\buse_pty\b.*$' /etc/sudoers; then + # sudoers file doesn't define Option use_pty + echo "Defaults use_pty" >> /etc/sudoers + fi + + # Check validity of sudoers and cleanup bak + if /usr/sbin/visudo -qcf /etc/sudoers; then + rm -f /etc/sudoers.bak + else + echo "Fail to validate remediated /etc/sudoers, reverting to original file." + mv /etc/sudoers.bak /etc/sudoers + false + fi +else + echo "Skipping remediation, /etc/sudoers failed to validate" + false +fi + + + + + + + + + + Ensure Sudo Logfile Exists - sudo logfile + A custom log sudo file can be configured with the 'logfile' tag. This rule configures +a sudo custom logfile at the default location suggested by CIS, which uses +/var/log/sudo.log. + 5.3.3 + A sudo log file simplifies auditing of sudo commands. + CCE-83601-5 + - name: XCCDF Value var_sudo_logfile # promote to variable + set_fact: + var_sudo_logfile: !!str + tags: + - always + +- name: Ensure logfile is enabled with the appropriate value in /etc/sudoers + lineinfile: + path: /etc/sudoers + regexp: ^[\s]*Defaults\s(.*)\blogfile=[-]?.+\b(.*)$ + line: Defaults \1logfile={{ var_sudo_logfile }}\2 + validate: /usr/sbin/visudo -cf %s + backrefs: true + register: edit_sudoers_logfile_option + tags: + - CCE-83601-5 + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - restrict_strategy + - sudo_custom_logfile + +- name: Enable logfile option with appropriate value in /etc/sudoers + lineinfile: + path: /etc/sudoers + line: Defaults logfile={{ var_sudo_logfile }} + validate: /usr/sbin/visudo -cf %s + when: edit_sudoers_logfile_option is defined and not edit_sudoers_logfile_option.changed + tags: + - CCE-83601-5 + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - restrict_strategy + - sudo_custom_logfile + + + +var_sudo_logfile='' + + +if /usr/sbin/visudo -qcf /etc/sudoers; then + cp /etc/sudoers /etc/sudoers.bak + if ! grep -P '^[\s]*Defaults[\s]*\blogfile=("(?:\\"|\\\\|[^"\\\n])*"\B|[^"](?:(?:\\,|\\"|\\ |\\\\|[^", \\\n])*)\b)\b.*$' /etc/sudoers; then + # sudoers file doesn't define Option logfile + echo "Defaults logfile=${var_sudo_logfile}" >> /etc/sudoers + else + # sudoers file defines Option logfile, remediate if appropriate value is not set + if ! grep -P "^[\s]*Defaults.*\blogfile=${var_sudo_logfile}\b.*$" /etc/sudoers; then + + escaped_variable=${var_sudo_logfile//$'/'/$'\/'} + sed -Ei "s/(^[\s]*Defaults.*\blogfile=)[-]?.+(\b.*$)/\1$escaped_variable\2/" /etc/sudoers + fi + fi + + # Check validity of sudoers and cleanup bak + if /usr/sbin/visudo -qcf /etc/sudoers; then + rm -f /etc/sudoers.bak + else + echo "Fail to validate remediated /etc/sudoers, reverting to original file." + mv /etc/sudoers.bak /etc/sudoers + false + fi +else + echo "Skipping remediation, /etc/sudoers failed to validate" + false +fi + + + + + + + + + + + Ensure a dedicated group owns sudo + Restrict the execution of privilege escalated commands to a dedicated group of users. +Ensure the group owner of /usr/bin/sudo is . + Changing group owner of /usr/bin/sudo to a group with no member users will prevent +any and all escalatation of privileges. +Additionally, the system may become unmanageable if root logins are not allowed. + This rule doesn't come with a remediation, before remediating the sysadmin needs to add users to the dedicated sudo group. + BP28(R57) + Restricting the set of users able to execute commands as privileged user reduces the attack surface. + CCE-83982-9 + + + + + + + + + + Ensure Users Re-Authenticate for Privilege Escalation - sudo !authenticate + The sudo !authenticate option, when specified, allows a user to execute commands using +sudo without having to authenticate. This should be disabled by making sure that the +!authenticate option does not exist in /etc/sudoers configuration file or +any sudo configuration snippets in /etc/sudoers.d/. + BP28(R5) + BP28(R59) + 1 + 12 + 15 + 16 + 5 + DSS05.04 + DSS05.10 + DSS06.03 + DSS06.10 + CCI-002038 + 4.3.3.5.1 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + A.18.1.4 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.2 + A.9.4.3 + IA-11 + CM-6(a) + PR.AC-1 + PR.AC-7 + SRG-OS-000373-GPOS-00156 + SRG-OS-000373-GPOS-00157 + SRG-OS-000373-GPOS-00158 + SRG-OS-000373-VMM-001470 + SRG-OS-000373-VMM-001480 + SRG-OS-000373-VMM-001490 + RHEL-08-010381 + SV-230272r627750_rule + Without re-authentication, users may access resources or perform tasks for which they +do not have authorization. + +When operating systems provide the capability to escalate a functional capability, it +is critical that the user re-authenticate. + CCE-82202-3 + - name: Find /etc/sudoers.d/ files + find: + paths: + - /etc/sudoers.d/ + register: sudoers + tags: + - CCE-82202-3 + - DISA-STIG-RHEL-08-010381 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-11 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sudo_remove_no_authenticate + +- name: Remove lines containing !authenticate from sudoers files + replace: + regexp: (^(?!#).*[\s]+\!authenticate.*$) + replace: '# \g<1>' + path: '{{ item.path }}' + validate: /usr/sbin/visudo -cf %s + with_items: + - path: /etc/sudoers + - '{{ sudoers.files }}' + tags: + - CCE-82202-3 + - DISA-STIG-RHEL-08-010381 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-11 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sudo_remove_no_authenticate + + +for f in /etc/sudoers /etc/sudoers.d/* ; do + if [ ! -e "$f" ] ; then + continue + fi + matching_list=$(grep -P '^(?!#).*[\s]+\!authenticate.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + # comment out "!authenticate" matches to preserve user data + sed -i "s/^${entry}$/# &/g" $f + done <<< "$matching_list" + + /usr/sbin/visudo -cf $f &> /dev/null || echo "Fail to validate $f with visudo" + fi +done + + + + + + + + + + Ensure Users Re-Authenticate for Privilege Escalation - sudo NOPASSWD + The sudo NOPASSWD tag, when specified, allows a user to execute +commands using sudo without having to authenticate. This should be disabled +by making sure that the NOPASSWD tag does not exist in +/etc/sudoers configuration file or any sudo configuration snippets +in /etc/sudoers.d/. + This rule is disabled on Red Hat Virtualization Hosts and Managers, it will report not applicable. +RHV requires to perform operations as root without being asked for password. + BP28(R5) + BP28(R59) + 1 + 12 + 15 + 16 + 5 + DSS05.04 + DSS05.10 + DSS06.03 + DSS06.10 + CCI-002038 + 4.3.3.5.1 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + A.18.1.4 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.2 + A.9.4.3 + IA-11 + CM-6(a) + PR.AC-1 + PR.AC-7 + SRG-OS-000373-GPOS-00156 + SRG-OS-000373-GPOS-00157 + SRG-OS-000373-GPOS-00158 + SRG-OS-000373-VMM-001470 + SRG-OS-000373-VMM-001480 + SRG-OS-000373-VMM-001490 + RHEL-08-010380 + SV-230271r833301_rule + Without re-authentication, users may access resources or perform tasks for which they +do not have authorization. + +When operating systems provide the capability to escalate a functional capability, it +is critical that the user re-authenticate. + + CCE-82197-5 + - name: Find /etc/sudoers.d/ files + find: + paths: + - /etc/sudoers.d/ + register: sudoers + tags: + - CCE-82197-5 + - DISA-STIG-RHEL-08-010380 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-11 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sudo_remove_nopasswd + +- name: Remove lines containing NOPASSWD from sudoers files + replace: + regexp: (^(?!#).*[\s]+NOPASSWD[\s]*\:.*$) + replace: '# \g<1>' + path: '{{ item.path }}' + validate: /usr/sbin/visudo -cf %s + with_items: + - path: /etc/sudoers + - '{{ sudoers.files }}' + tags: + - CCE-82197-5 + - DISA-STIG-RHEL-08-010380 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-11 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sudo_remove_nopasswd + + +for f in /etc/sudoers /etc/sudoers.d/* ; do + if [ ! -e "$f" ] ; then + continue + fi + matching_list=$(grep -P '^(?!#).*[\s]+NOPASSWD[\s]*\:.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + # comment out "NOPASSWD" matches to preserve user data + sed -i "s/^${entry}$/# &/g" $f + done <<< "$matching_list" + + /usr/sbin/visudo -cf $f &> /dev/null || echo "Fail to validate $f with visudo" + fi +done + + + + + + + + + + Ensure Users Re-Authenticate for Privilege Escalation - sudo + The sudo NOPASSWD and !authenticate option, when +specified, allows a user to execute commands using sudo without having to +authenticate. This should be disabled by making sure that +NOPASSWD and/or !authenticate do not exist in +/etc/sudoers configuration file or any sudo configuration snippets +in /etc/sudoers.d/." + 1 + 12 + 15 + 16 + 5 + DSS05.04 + DSS05.10 + DSS06.03 + DSS06.10 + CCI-002038 + 4.3.3.5.1 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + A.18.1.4 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.2 + A.9.4.3 + IA-11 + CM-6(a) + PR.AC-1 + PR.AC-7 + SRG-OS-000373-GPOS-00156 + 5.3.4 + Without re-authentication, users may access resources or perform tasks for which they +do not have authorization. + +When operating systems provide the capability to escalate a functional capability, it +is critical that the user re-authenticate. + CCE-82279-1 + - name: Find /etc/sudoers.d/ files + find: + paths: + - /etc/sudoers.d/ + register: sudoers + tags: + - CCE-82279-1 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-11 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sudo_require_authentication + +- name: Remove lines containing NOPASSWD from sudoers files + replace: + regexp: (^(?!#).*[\s]+NOPASSWD[\s]*\:.*$) + replace: '# \g<1>' + path: '{{ item.path }}' + validate: /usr/sbin/visudo -cf %s + with_items: + - path: /etc/sudoers + - '{{ sudoers.files }}' + tags: + - CCE-82279-1 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-11 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sudo_require_authentication + +- name: Find /etc/sudoers.d/ files + find: + paths: + - /etc/sudoers.d/ + register: sudoers + tags: + - CCE-82279-1 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-11 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sudo_require_authentication + +- name: Remove lines containing !authenticate from sudoers files + replace: + regexp: (^(?!#).*[\s]+\!authenticate.*$) + replace: '# \g<1>' + path: '{{ item.path }}' + validate: /usr/sbin/visudo -cf %s + with_items: + - path: /etc/sudoers + - '{{ sudoers.files }}' + tags: + - CCE-82279-1 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-11 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sudo_require_authentication + + +for f in /etc/sudoers /etc/sudoers.d/* ; do + if [ ! -e "$f" ] ; then + continue + fi + matching_list=$(grep -P '^(?!#).*[\s]+NOPASSWD[\s]*\:.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + # comment out "NOPASSWD" matches to preserve user data + sed -i "s/^${entry}$/# &/g" $f + done <<< "$matching_list" + + /usr/sbin/visudo -cf $f &> /dev/null || echo "Fail to validate $f with visudo" + fi +done + +for f in /etc/sudoers /etc/sudoers.d/* ; do + if [ ! -e "$f" ] ; then + continue + fi + matching_list=$(grep -P '^(?!#).*[\s]+\!authenticate.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + # comment out "!authenticate" matches to preserve user data + sed -i "s/^${entry}$/# &/g" $f + done <<< "$matching_list" + + /usr/sbin/visudo -cf $f &> /dev/null || echo "Fail to validate $f with visudo" + fi +done + + + + + + + + + + The operating system must require Re-Authentication when using the sudo command. Ensure sudo timestamp_timeout is appropriate - sudo timestamp_timeout + The sudo timestamp_timeout tag sets the amount of time sudo password prompt waits. +The default timestamp_timeout value is 5 minutes. +The timestamp_timeout should be configured by making sure that the +timestamp_timeout tag exists in +/etc/sudoers configuration file or any sudo configuration snippets +in /etc/sudoers.d/. +If the value is set to an integer less than 0, the user's time stamp will not expire +and the user will not have to re-authenticate for privileged actions until the user's session is terminated. + CCI-002038 + IA-11 + SRG-OS-000373-GPOS-00156 + SRG-OS-000373-GPOS-00157 + SRG-OS-000373-GPOS-00158 + RHEL-08-010384 + 5.3.5 + SV-237643r838720_rule + Without re-authentication, users may access resources or perform tasks for which they +do not have authorization. + +When operating systems provide the capability to escalate a functional capability, it +is critical that the user re-authenticate. + CCE-87838-9 + - name: XCCDF Value var_sudo_timestamp_timeout # promote to variable + set_fact: + var_sudo_timestamp_timeout: !!str + tags: + - always + +- name: Find out if /etc/sudoers.d/* files contain 'Defaults timestamp_timeout' to + be deduplicated + find: + path: /etc/sudoers.d + patterns: '*' + contains: ^[\s]*Defaults\s.*\btimestamp_timeout=.* + register: sudoers_d_defaults_timestamp_timeout + tags: + - CCE-87838-9 + - DISA-STIG-RHEL-08-010384 + - NIST-800-53-IA-11 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sudo_require_reauthentication + +- name: Remove found occurrences of 'Defaults timestamp_timeout' from /etc/sudoers.d/* + files + lineinfile: + path: '{{ item.path }}' + regexp: ^[\s]*Defaults\s.*\btimestamp_timeout=.* + state: absent + with_items: '{{ sudoers_d_defaults_timestamp_timeout.files }}' + tags: + - CCE-87838-9 + - DISA-STIG-RHEL-08-010384 + - NIST-800-53-IA-11 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sudo_require_reauthentication + +- name: Ensure timestamp_timeout is enabled with the appropriate value in /etc/sudoers + lineinfile: + path: /etc/sudoers + regexp: ^[\s]*Defaults\s(.*)\btimestamp_timeout=[-]?\w+\b(.*)$ + line: Defaults \1timestamp_timeout={{ var_sudo_timestamp_timeout }}\2 + validate: /usr/sbin/visudo -cf %s + backrefs: true + register: edit_sudoers_timestamp_timeout_option + tags: + - CCE-87838-9 + - DISA-STIG-RHEL-08-010384 + - NIST-800-53-IA-11 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sudo_require_reauthentication + +- name: Enable timestamp_timeout option with appropriate value in /etc/sudoers + lineinfile: + path: /etc/sudoers + line: Defaults timestamp_timeout={{ var_sudo_timestamp_timeout }} + validate: /usr/sbin/visudo -cf %s + when: edit_sudoers_timestamp_timeout_option is defined and not edit_sudoers_timestamp_timeout_option.changed + tags: + - CCE-87838-9 + - DISA-STIG-RHEL-08-010384 + - NIST-800-53-IA-11 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sudo_require_reauthentication + + + +var_sudo_timestamp_timeout='' + + +if grep -x '^[\s]*Defaults.*\btimestamp_timeout=.*' /etc/sudoers.d/*; then + find /etc/sudoers.d/ -type f -exec sed -i "/^[\s]*Defaults.*\btimestamp_timeout=.*/d" {} \; +fi + +if /usr/sbin/visudo -qcf /etc/sudoers; then + cp /etc/sudoers /etc/sudoers.bak + if ! grep -P '^[\s]*Defaults.*\btimestamp_timeout=[-]?\w+\b\b.*$' /etc/sudoers; then + # sudoers file doesn't define Option timestamp_timeout + echo "Defaults timestamp_timeout=${var_sudo_timestamp_timeout}" >> /etc/sudoers + else + # sudoers file defines Option timestamp_timeout, remediate if appropriate value is not set + if ! grep -P "^[\s]*Defaults.*\btimestamp_timeout=${var_sudo_timestamp_timeout}\b.*$" /etc/sudoers; then + + sed -Ei "s/(^[\s]*Defaults.*\btimestamp_timeout=)[-]?\w+(\b.*$)/\1${var_sudo_timestamp_timeout}\2/" /etc/sudoers + fi + fi + + # Check validity of sudoers and cleanup bak + if /usr/sbin/visudo -qcf /etc/sudoers; then + rm -f /etc/sudoers.bak + else + echo "Fail to validate remediated /etc/sudoers, reverting to original file." + mv /etc/sudoers.bak /etc/sudoers + false + fi +else + echo "Skipping remediation, /etc/sudoers failed to validate" + false +fi + + + + + + + + + + Ensure only owner and members of group owner of /usr/bin/sudo can execute it + Remove the execute permission bit of /etc/bin/sudo for the other users. + +To properly set the permissions of /usr/bin/sudo, run the command: +$ sudo chmod 4110 /usr/bin/sudo + BP28(R57) + Restricting the set of users able to execute commands as privileged user reduces the attack surface. + CCE-83574-4 + - name: Test for existence /usr/bin/sudo + stat: + path: /usr/bin/sudo + register: file_exists + tags: + - CCE-83574-4 + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sudo_restrict_others_executable_permission + +- name: Ensure permission u-wr,g-wrs,o-xwrt on /usr/bin/sudo + file: + path: /usr/bin/sudo + mode: u-wr,g-wrs,o-xwrt + when: file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-83574-4 + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sudo_restrict_others_executable_permission + + + + + +chmod u-wr,g-wrs,o-xwrt /usr/bin/sudo + + + + + + + + + + The operating system must restrict privilege elevation to authorized personnel + The sudo command allows a user to execute programs with elevated +(administrator) privileges. It prompts the user for their password +and confirms your request to execute a command by checking a file, +called sudoers. +Restrict privileged actions by removing the following entries from the sudoers file: +ALL ALL=(ALL) ALL +ALL ALL=(ALL:ALL) ALL + This rule doesn't come with a remediation, as the exact requirement allows exceptions, +and removing lines from the sudoers file can make the system non-administrable. + CCI-000366 + CM-6(b) + CM-6(iv) + SRG-OS-000480-GPOS-00227 + RHEL-08-010382 + SV-237641r646893_rule + If the "sudoers" file is not configured correctly, any user defined +on the system can initiate privileged actions on the target system. + + CCE-83425-9 + + + + + + + + + Only the VDSM User Can Use sudo NOPASSWD + The sudo NOPASSWD tag, when specified, allows a user to execute commands using sudo without having to authenticate. Only the vdsm user should have this capability in any sudo configuration snippets in /etc/sudoers.d/. + Without re-authentication, users may access resources or perform tasks for which they +do not have authorization. + +When operating systems provide the capability to escalate a functional capability, it +is critical that the user re-authenticate. + CCE-82365-8 + + + + + + + + + Ensure sudo only includes the default configuration directory + Administrators can configure authorized sudo users via drop-in files, and it is possible to include +other directories and configuration files from the file currently being parsed. + +Make sure that /etc/sudoers only includes drop-in configuration files from /etc/sudoers.d, +or that no drop-in file is included. +Either the /etc/sudoers should contain only one #includedir directive pointing to +/etc/sudoers.d, and no file in /etc/sudoers.d/ should include other files or directories; +Or the /etc/sudoers should not contain any #include, +@include, #includedir or @includedir directives. +Note that the '#' character doesn't denote a comment in the configuration file. + CCI-000366 + SRG-OS-000480-GPOS-00227 + RHEL-08-010379 + SV-251711r833385_rule + Some sudo configurtion options allow users to run programs without re-authenticating. +Use of these configuration options makes it easier for one compromised accound to be used to +compromise other accounts. + CCE-86377-9 + - name: Check for duplicate values + lineinfile: + path: /etc/sudoers + create: false + regexp: ^#includedir.*$ + state: absent + check_mode: true + changed_when: false + register: dupes + tags: + - CCE-86377-9 + - DISA-STIG-RHEL-08-010379 + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sudoers_default_includedir + +- name: Deduplicate values from /etc/sudoers + lineinfile: + path: /etc/sudoers + create: false + regexp: ^#includedir.*$ + state: absent + when: dupes.found is defined and dupes.found > 1 + tags: + - CCE-86377-9 + - DISA-STIG-RHEL-08-010379 + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sudoers_default_includedir + +- name: Insert correct line into /etc/sudoers + lineinfile: + path: /etc/sudoers + create: false + regexp: ^#includedir.*$ + line: '#includedir /etc/sudoers.d' + state: present + tags: + - CCE-86377-9 + - DISA-STIG-RHEL-08-010379 + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sudoers_default_includedir + +- name: Ensure sudoers doesn't include other non-default file + lineinfile: + path: /etc/sudoers + create: false + regexp: ^[#@]include[\s]+.*$ + state: absent + tags: + - CCE-86377-9 + - DISA-STIG-RHEL-08-010379 + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sudoers_default_includedir + +- name: Ensure sudoers doesn't have non-default includedir + lineinfile: + path: /etc/sudoers + create: false + regexp: ^@includedir[\s]+.*$ + state: absent + tags: + - CCE-86377-9 + - DISA-STIG-RHEL-08-010379 + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sudoers_default_includedir + +- name: Find out if /etc/sudoers.d/* files contain file or directory includes + find: + path: /etc/sudoers.d + patterns: '*' + contains: ^[#@]include(dir)?\s.*$ + register: sudoers_d_includes + tags: + - CCE-86377-9 + - DISA-STIG-RHEL-08-010379 + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sudoers_default_includedir + +- name: Remove found occurrences of file and directory includes from /etc/sudoers.d/* + files + lineinfile: + path: '{{ item.path }}' + regexp: ^[#@]include(dir)?\s.*$ + state: absent + with_items: '{{ sudoers_d_includes.files }}' + tags: + - CCE-86377-9 + - DISA-STIG-RHEL-08-010379 + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sudoers_default_includedir + + +sudoers_config_file="/etc/sudoers" +sudoers_config_dir="/etc/sudoers.d" +sudoers_includedir_count=$(grep -c "#includedir" "$sudoers_config_file") +if [ "$sudoers_includedir_count" -gt 1 ]; then + sed -i "/#includedir/d" "$sudoers_config_file" + echo "#includedir /etc/sudoers.d" >> "$sudoers_config_file" +elif [ "$sudoers_includedir_count" -eq 0 ]; then + echo "#includedir /etc/sudoers.d" >> "$sudoers_config_file" +else + if ! grep -q "^#includedir /etc/sudoers.d" "$sudoers_config_file"; then + sed -i "s|^#includedir.*|#includedir /etc/sudoers.d|g" "$sudoers_config_file" + fi +fi + +sed -Ei "/^#include\s/d; /^@includedir\s/d" "$sudoers_config_file" + +if grep -Pr "^[#@]include(dir)?\s" "$sudoers_config_dir" ; then + sed -Ei "/^[#@]include(dir)?\s/d" "$sudoers_config_dir"/* +fi + + + + + + + + + + Explicit arguments in sudo specifications + All commands in the sudoers file must strictly specify the arguments allowed to be used for a given user. +If the command is supposed to be executed only without arguments, pass "" as an argument in the corresponding user specification. + This rule doesn't come with a remediation, as absence of arguments in the user spec doesn't mean that the command is intended to be executed with no arguments. + The rule can produce false findings when an argument contains a comma - sudoers syntax allows comma escaping using backslash, but the check doesn't support that. For example, root ALL=(ALL) echo 1\,2 allows root to execute echo 1,2, but the check would interpret it as two commands echo 1\ and 2. + BP28(R63) + Any argument can modify quite significantly the behavior of a program, whether regarding the +realized operation (read, write, delete, etc.) or accessed resources (path in a file system tree). To +avoid any possibility of misuse of a command by a user, the ambiguities must be removed at the +level of its specification. + +For example, on some systems, the kernel messages are only accessible by root. +If a user nevertheless must have the privileges to read them, the argument of the dmesg command has to be restricted +in order to prevent the user from flushing the buffer through the -c option: + +user ALL = dmesg "" + + + CCE-83632-0 + + + + + + + + + Don't define allowed commands in sudoers by means of exclusion + Policies applied by sudo through the sudoers file should not involve negation. + +Each user specification in the sudoers file contains a comma-delimited list of command specifications. +The definition can make use glob patterns, as well as of negations. +Indirect definition of those commands by means of exclusion of a set of commands is trivial to bypass, so it is not allowed to use such constructs. + This rule doesn't come with a remediation, as negations indicate design issues with the sudoers user specifications design. Just removing negations doesn't increase the security - you typically have to rethink the definition of allowed commands to fix the issue. + BP28(R61) + Specifying access right using negation is inefficient and can be easily circumvented. +For example, it is expected that a specification like +# To avoid absolutely , this rule can be easily circumvented! +user ALL = ALL ,!/ bin/sh + prevents the execution of the shell +but that’s not the case: just copy the binary /bin/sh to a different name to make it executable +again through the rule keyword ALL. + + CCE-83518-1 + + + + + + + + + Don't target root user in the sudoers file + The targeted users of a user specification should be, as much as possible, non privileged users (i.e.: non-root). + +User specifications have to explicitly list the runas spec (i.e. the list of target users that can be impersonated), and ALL or root should not be used. + This rule doesn't come with a remediation, as the exact requirement allows exceptions, and removing lines from the sudoers file can make the system non-administrable. + BP28(R60) + It is common that the command to be executed does not require superuser rights (editing a file +whose the owner is not root, sending a signal to an unprivileged process,etc.). In order to limit +any attempt of privilege escalation through a command, it is better to apply normal user rights. + + CCE-83598-3 + + + + + + + + + Ensure invoking users password for privilege escalation when using sudo + The sudoers security policy requires that users authenticate themselves before they can use sudo. +When sudoers requires authentication, it validates the invoking user's credentials. +The expected output for: + sudo cvtsudoers -f sudoers /etc/sudoers | grep -E '^Defaults !?(rootpw|targetpw|runaspw)$' + Defaults !targetpw + Defaults !rootpw + Defaults !runaspw +or if cvtsudoers not supported: + sudo find /etc/sudoers /etc/sudoers.d \( \! -name '*~' -a \! -name '*.*' \) -exec grep -E --with-filename '^[[:blank:]]*Defaults[[:blank:]](.*[[:blank:]])?!?\b(rootpw|targetpw|runaspw)' -- {} \; + /etc/sudoers:Defaults !targetpw + /etc/sudoers:Defaults !rootpw + /etc/sudoers:Defaults !runaspw + CCI-000366 + CCI-002227 + CM-6(b) + CM-6.1(iv) + SRG-OS-000480-GPOS-00227 + RHEL-08-010383 + SV-237642r833369_rule + If the rootpw, targetpw, or runaspw flags are defined and not disabled, by default the operating system will prompt +the invoking user for the "root" user password. + CCE-83422-6 + - name: Find out if /etc/sudoers.d/* files contain Defaults targetpw to be deduplicated + find: + path: /etc/sudoers.d + patterns: '*' + contains: ^Defaults targetpw$ + register: sudoers_d_defaults + tags: + - CCE-83422-6 + - DISA-STIG-RHEL-08-010383 + - NIST-800-53-CM-6(b) + - NIST-800-53-CM-6.1(iv) + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sudoers_validate_passwd + +- name: Remove found occurrences of Defaults targetpw from /etc/sudoers.d/* files + lineinfile: + path: '{{ item.path }}' + regexp: ^Defaults targetpw$ + state: absent + with_items: '{{ sudoers_d_defaults.files }}' + tags: + - CCE-83422-6 + - DISA-STIG-RHEL-08-010383 + - NIST-800-53-CM-6(b) + - NIST-800-53-CM-6.1(iv) + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sudoers_validate_passwd + +- name: Find out if /etc/sudoers.d/* files contain Defaults rootpw to be deduplicated + find: + path: /etc/sudoers.d + patterns: '*' + contains: ^Defaults rootpw$ + register: sudoers_d_defaults + tags: + - CCE-83422-6 + - DISA-STIG-RHEL-08-010383 + - NIST-800-53-CM-6(b) + - NIST-800-53-CM-6.1(iv) + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sudoers_validate_passwd + +- name: Remove found occurrences of Defaults rootpw from /etc/sudoers.d/* files + lineinfile: + path: '{{ item.path }}' + regexp: ^Defaults rootpw$ + state: absent + with_items: '{{ sudoers_d_defaults.files }}' + tags: + - CCE-83422-6 + - DISA-STIG-RHEL-08-010383 + - NIST-800-53-CM-6(b) + - NIST-800-53-CM-6.1(iv) + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sudoers_validate_passwd + +- name: Find out if /etc/sudoers.d/* files contain Defaults runaspw to be deduplicated + find: + path: /etc/sudoers.d + patterns: '*' + contains: ^Defaults runaspw$ + register: sudoers_d_defaults + tags: + - CCE-83422-6 + - DISA-STIG-RHEL-08-010383 + - NIST-800-53-CM-6(b) + - NIST-800-53-CM-6.1(iv) + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sudoers_validate_passwd + +- name: Remove found occurrences of Defaults runaspw from /etc/sudoers.d/* files + lineinfile: + path: '{{ item.path }}' + regexp: ^Defaults runaspw$ + state: absent + with_items: '{{ sudoers_d_defaults.files }}' + tags: + - CCE-83422-6 + - DISA-STIG-RHEL-08-010383 + - NIST-800-53-CM-6(b) + - NIST-800-53-CM-6.1(iv) + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sudoers_validate_passwd + +- name: Remove any ocurrences of Defaults targetpw in /etc/sudoers + lineinfile: + path: /etc/sudoers + regexp: ^Defaults targetpw$ + validate: /usr/sbin/visudo -cf %s + state: absent + register: sudoers_file_defaults + tags: + - CCE-83422-6 + - DISA-STIG-RHEL-08-010383 + - NIST-800-53-CM-6(b) + - NIST-800-53-CM-6.1(iv) + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sudoers_validate_passwd + +- name: Remove any ocurrences of Defaults rootpw in /etc/sudoers + lineinfile: + path: /etc/sudoers + regexp: ^Defaults rootpw$ + validate: /usr/sbin/visudo -cf %s + state: absent + register: sudoers_file_defaults + tags: + - CCE-83422-6 + - DISA-STIG-RHEL-08-010383 + - NIST-800-53-CM-6(b) + - NIST-800-53-CM-6.1(iv) + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sudoers_validate_passwd + +- name: Remove any ocurrences of Defaults runaspw in /etc/sudoers + lineinfile: + path: /etc/sudoers + regexp: ^Defaults runaspw$ + validate: /usr/sbin/visudo -cf %s + state: absent + register: sudoers_file_defaults + tags: + - CCE-83422-6 + - DISA-STIG-RHEL-08-010383 + - NIST-800-53-CM-6(b) + - NIST-800-53-CM-6.1(iv) + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sudoers_validate_passwd + +- name: Check for duplicate values + lineinfile: + path: /etc/sudoers + create: false + regexp: ^Defaults !targetpw$ + state: absent + check_mode: true + changed_when: false + register: dupes + tags: + - CCE-83422-6 + - DISA-STIG-RHEL-08-010383 + - NIST-800-53-CM-6(b) + - NIST-800-53-CM-6.1(iv) + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sudoers_validate_passwd + +- name: Deduplicate values from /etc/sudoers + lineinfile: + path: /etc/sudoers + create: false + regexp: ^Defaults !targetpw$ + state: absent + when: dupes.found is defined and dupes.found > 1 + tags: + - CCE-83422-6 + - DISA-STIG-RHEL-08-010383 + - NIST-800-53-CM-6(b) + - NIST-800-53-CM-6.1(iv) + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sudoers_validate_passwd + +- name: Insert correct line into /etc/sudoers + lineinfile: + path: /etc/sudoers + create: false + regexp: ^Defaults !targetpw$ + line: Defaults !targetpw + state: present + tags: + - CCE-83422-6 + - DISA-STIG-RHEL-08-010383 + - NIST-800-53-CM-6(b) + - NIST-800-53-CM-6.1(iv) + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sudoers_validate_passwd + +- name: Check for duplicate values + lineinfile: + path: /etc/sudoers + create: false + regexp: ^Defaults !rootpw$ + state: absent + check_mode: true + changed_when: false + register: dupes + tags: + - CCE-83422-6 + - DISA-STIG-RHEL-08-010383 + - NIST-800-53-CM-6(b) + - NIST-800-53-CM-6.1(iv) + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sudoers_validate_passwd + +- name: Deduplicate values from /etc/sudoers + lineinfile: + path: /etc/sudoers + create: false + regexp: ^Defaults !rootpw$ + state: absent + when: dupes.found is defined and dupes.found > 1 + tags: + - CCE-83422-6 + - DISA-STIG-RHEL-08-010383 + - NIST-800-53-CM-6(b) + - NIST-800-53-CM-6.1(iv) + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sudoers_validate_passwd + +- name: Insert correct line into /etc/sudoers + lineinfile: + path: /etc/sudoers + create: false + regexp: ^Defaults !rootpw$ + line: Defaults !rootpw + state: present + tags: + - CCE-83422-6 + - DISA-STIG-RHEL-08-010383 + - NIST-800-53-CM-6(b) + - NIST-800-53-CM-6.1(iv) + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sudoers_validate_passwd + +- name: Check for duplicate values + lineinfile: + path: /etc/sudoers + create: false + regexp: ^Defaults !runaspw$ + state: absent + check_mode: true + changed_when: false + register: dupes + tags: + - CCE-83422-6 + - DISA-STIG-RHEL-08-010383 + - NIST-800-53-CM-6(b) + - NIST-800-53-CM-6.1(iv) + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sudoers_validate_passwd + +- name: Deduplicate values from /etc/sudoers + lineinfile: + path: /etc/sudoers + create: false + regexp: ^Defaults !runaspw$ + state: absent + when: dupes.found is defined and dupes.found > 1 + tags: + - CCE-83422-6 + - DISA-STIG-RHEL-08-010383 + - NIST-800-53-CM-6(b) + - NIST-800-53-CM-6.1(iv) + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sudoers_validate_passwd + +- name: Insert correct line into /etc/sudoers + lineinfile: + path: /etc/sudoers + create: false + regexp: ^Defaults !runaspw$ + line: Defaults !runaspw + state: present + tags: + - CCE-83422-6 + - DISA-STIG-RHEL-08-010383 + - NIST-800-53-CM-6(b) + - NIST-800-53-CM-6.1(iv) + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sudoers_validate_passwd + + if grep -x '^Defaults targetpw$' /etc/sudoers; then + sed -i "/Defaults targetpw/d" /etc/sudoers \; +fi +if grep -x '^Defaults targetpw$' /etc/sudoers.d/*; then + find /etc/sudoers.d/ -type f -exec sed -i "/Defaults targetpw/d" {} \; +fi +if grep -x '^Defaults rootpw$' /etc/sudoers; then + sed -i "/Defaults rootpw/d" /etc/sudoers \; +fi +if grep -x '^Defaults rootpw$' /etc/sudoers.d/*; then + find /etc/sudoers.d/ -type f -exec sed -i "/Defaults rootpw/d" {} \; +fi +if grep -x '^Defaults runaspw$' /etc/sudoers; then + sed -i "/Defaults runaspw/d" /etc/sudoers \; +fi +if grep -x '^Defaults runaspw$' /etc/sudoers.d/*; then + find /etc/sudoers.d/ -type f -exec sed -i "/Defaults runaspw/d" {} \; +fi + +if [ -e "/etc/sudoers" ] ; then + + LC_ALL=C sed -i "/Defaults !targetpw/d" "/etc/sudoers" +else + touch "/etc/sudoers" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/sudoers" + +cp "/etc/sudoers" "/etc/sudoers.bak" +# Insert at the end of the file +printf '%s\n' "Defaults !targetpw" >> "/etc/sudoers" +# Clean up after ourselves. +rm "/etc/sudoers.bak" +if [ -e "/etc/sudoers" ] ; then + + LC_ALL=C sed -i "/Defaults !rootpw/d" "/etc/sudoers" +else + touch "/etc/sudoers" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/sudoers" + +cp "/etc/sudoers" "/etc/sudoers.bak" +# Insert at the end of the file +printf '%s\n' "Defaults !rootpw" >> "/etc/sudoers" +# Clean up after ourselves. +rm "/etc/sudoers.bak" +if [ -e "/etc/sudoers" ] ; then + + LC_ALL=C sed -i "/Defaults !runaspw/d" "/etc/sudoers" +else + touch "/etc/sudoers" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/sudoers" + +cp "/etc/sudoers" "/etc/sudoers.bak" +# Insert at the end of the file +printf '%s\n' "Defaults !runaspw" >> "/etc/sudoers" +# Clean up after ourselves. +rm "/etc/sudoers.bak" + + + + + + + + + + + System Tooling / Utilities + The following checks evaluate the system for recommended base packages -- both for installation +and removal. + + Install binutils Package + The binutils package can be installed with the following command: + +$ sudo yum install binutils + binutils is a collection of binary utilities required for +foundational system operator activities, such as ld, +nm, objcopy and readelf. + CCE-82989-5 + +package --add=binutils + + include install_binutils + +class install_binutils { + package { 'binutils': + ensure => 'installed', + } +} + + - name: Ensure binutils is installed + package: + name: binutils + state: present + tags: + - CCE-82989-5 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_binutils_installed + + +[[packages]] +name = "binutils" +version = "*" + + +if ! rpm -q --quiet "binutils" ; then + yum install -y "binutils" +fi + + + + + + + + + + Install dnf-plugin-subscription-manager Package + The dnf-plugin-subscription-manager package can be installed with the following command: + +$ sudo yum install dnf-plugin-subscription-manager + 0940 + 1144 + 1467 + 1472 + 1483 + 1493 + 1494 + 1495 + FPT_TUD_EXT.1 + FPT_TUD_EXT.2 + SRG-OS-000366-GPOS-00153 + This package provides plugins to interact with repositories and subscriptions +from the Red Hat entitlement platform; contains subscription-manager and +product-id plugins. + CCE-82315-3 + +package --add=dnf-plugin-subscription-manager + + include install_dnf-plugin-subscription-manager + +class install_dnf-plugin-subscription-manager { + package { 'dnf-plugin-subscription-manager': + ensure => 'installed', + } +} + + - name: Ensure dnf-plugin-subscription-manager is installed + package: + name: dnf-plugin-subscription-manager + state: present + tags: + - CCE-82315-3 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_dnf-plugin-subscription-manager_installed + + +[[packages]] +name = "dnf-plugin-subscription-manager" +version = "*" + + +if ! rpm -q --quiet "dnf-plugin-subscription-manager" ; then + yum install -y "dnf-plugin-subscription-manager" +fi + + + + + + + + + + Ensure gnutls-utils is installed + The gnutls-utils package can be installed with the following command: + +$ sudo yum install gnutls-utils + FIA_X509_EXT.1 + FIA_X509_EXT.2 + SRG-OS-000480-GPOS-00227 + GnuTLS is a secure communications library implementing the SSL, TLS and DTLS +protocols and technologies around them. It provides a simple C language +application programming interface (API) to access the secure communications +protocols as well as APIs to parse and write X.509, PKCS #12, OpenPGP and +other required structures. +This package contains command line TLS client and server and certificate +manipulation tools. + CCE-82395-5 + +package --add=gnutls-utils + + include install_gnutls-utils + +class install_gnutls-utils { + package { 'gnutls-utils': + ensure => 'installed', + } +} + + - name: Ensure gnutls-utils is installed + package: + name: gnutls-utils + state: present + tags: + - CCE-82395-5 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_gnutls-utils_installed + + +[[packages]] +name = "gnutls-utils" +version = "*" + + +if ! rpm -q --quiet "gnutls-utils" ; then + yum install -y "gnutls-utils" +fi + + + + + + + + + + Install libcap-ng-utils Package + The libcap-ng-utils package can be installed with the following command: + +$ sudo yum install libcap-ng-utils + SRG-OS-000445-GPOS-00199 + libcap-ng-utils contains applications to analyze the posix +posix capabilities of all the programs running on a system. +libcap-ng-utils also lets system operators set the file +system based capabilities. + CCE-82979-6 + +package --add=libcap-ng-utils + + include install_libcap-ng-utils + +class install_libcap-ng-utils { + package { 'libcap-ng-utils': + ensure => 'installed', + } +} + + - name: Ensure libcap-ng-utils is installed + package: + name: libcap-ng-utils + state: present + tags: + - CCE-82979-6 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_libcap-ng-utils_installed + + +[[packages]] +name = "libcap-ng-utils" +version = "*" + + +if ! rpm -q --quiet "libcap-ng-utils" ; then + yum install -y "libcap-ng-utils" +fi + + + + + + + + + + Ensure nss-tools is installed + The nss-tools package can be installed with the following command: + +$ sudo yum install nss-tools + FMT_SMF_EXT.1 + SRG-OS-000480-GPOS-00227 + Network Security Services (NSS) is a set of libraries designed to +support cross-platform development of security-enabled client and +server applications. Install the nss-tools package +to install command-line tools to manipulate the NSS certificate +and key database. + CCE-82396-3 + +package --add=nss-tools + + include install_nss-tools + +class install_nss-tools { + package { 'nss-tools': + ensure => 'installed', + } +} + + - name: Ensure nss-tools is installed + package: + name: nss-tools + state: present + tags: + - CCE-82396-3 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_nss-tools_installed + + +[[packages]] +name = "nss-tools" +version = "*" + + +if ! rpm -q --quiet "nss-tools" ; then + yum install -y "nss-tools" +fi + + + + + + + + + + Install openscap-scanner Package + The openscap-scanner package can be installed with the following command: + +$ sudo yum install openscap-scanner + AGD_PRE.1 + AGD_OPE.1 + SRG-OS-000480-GPOS-00227 + SRG-OS-000191-GPOS-00080 + openscap-scanner contains the oscap command line tool. This tool is a +configuration and vulnerability scanner, capable of performing compliance checking using +SCAP content. + CCE-82220-5 + +package --add=openscap-scanner + + include install_openscap-scanner + +class install_openscap-scanner { + package { 'openscap-scanner': + ensure => 'installed', + } +} + + - name: Ensure openscap-scanner is installed + package: + name: openscap-scanner + state: present + tags: + - CCE-82220-5 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_openscap-scanner_installed + + +[[packages]] +name = "openscap-scanner" +version = "*" + + +if ! rpm -q --quiet "openscap-scanner" ; then + yum install -y "openscap-scanner" +fi + + + + + + + + + + Install rear Package + The rear package can be installed with the following command: + +$ sudo yum install rear + rear contains the Relax-and-Recover (ReaR) utility. ReaR produces a bootable +image of a system and restores from backup using this image. + CCE-82883-0 + +package --add=rear + + include install_rear + +class install_rear { + package { 'rear': + ensure => 'installed', + } +} + + - name: Ensure rear is installed + package: + name: rear + state: present + tags: + - CCE-82883-0 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_rear_installed + + +[[packages]] +name = "rear" +version = "*" + + +if ! rpm -q --quiet "rear" ; then + yum install -y "rear" +fi + + + + + + + + + + Install rng-tools Package + The rng-tools package can be installed with the following command: + +$ sudo yum install rng-tools + CCI-000366 + SRG-OS-000480-GPOS-00227 + RHEL-08-010472 + SV-244527r743830_rule + rng-tools provides hardware random number generator tools, +such as those used in the formation of x509/PKI certificates. + CCE-82968-9 + +package --add=rng-tools + + include install_rng-tools + +class install_rng-tools { + package { 'rng-tools': + ensure => 'installed', + } +} + + - name: Ensure rng-tools is installed + package: + name: rng-tools + state: present + tags: + - CCE-82968-9 + - DISA-STIG-RHEL-08-010472 + - enable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - package_rng-tools_installed + + +[[packages]] +name = "rng-tools" +version = "*" + + +if ! rpm -q --quiet "rng-tools" ; then + yum install -y "rng-tools" +fi + + + + + + + + + + Install scap-security-guide Package + The scap-security-guide package can be installed with the following command: + +$ sudo yum install scap-security-guide + AGD_PRE.1 + AGD_OPE.1 + SRG-OS-000480-GPOS-00227 + The scap-security-guide package provides a guide for configuration of the system +from the final system's security point of view. The guidance is specified in the Security +Content Automation Protocol (SCAP) format and constitutes a catalog of practical hardening +advice, linked to government requirements where applicable. The SCAP Security Guide project +bridges the gap between generalized policy requirements and specific implementation guidelines. +A system administrator can use the oscap CLI tool from the openscap-scanner +package, or the SCAP Workbench GUI tool from the scap-workbench package, to verify +that the system conforms to provided guidelines. Refer to the scap-security-guide(8) manual +page for futher information. + CCE-82949-9 + +package --add=scap-security-guide + + include install_scap-security-guide + +class install_scap-security-guide { + package { 'scap-security-guide': + ensure => 'installed', + } +} + + - name: Ensure scap-security-guide is installed + package: + name: scap-security-guide + state: present + tags: + - CCE-82949-9 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_scap-security-guide_installed + + +[[packages]] +name = "scap-security-guide" +version = "*" + + +if ! rpm -q --quiet "scap-security-guide" ; then + yum install -y "scap-security-guide" +fi + + + + + + + + + + Install subscription-manager Package + The subscription-manager package can be installed with the following command: + +$ sudo yum install subscription-manager + 0940 + 1144 + 1467 + 1472 + 1483 + 1493 + 1494 + 1495 + FPT_TUD_EXT.1 + FPT_TUD_EXT.2 + SRG-OS-000366-GPOS-00153 + Red Hat Subscription Manager is a local service which tracks installed products +and subscriptions on a local system to help manage subscription assignments. +It communicates with the backend subscription service (the Customer Portal +or an on-premise server such as Subscription Asset Manager) and works with +content management tools such as . + CCE-82316-1 + +package --add=subscription-manager + + include install_subscription-manager + +class install_subscription-manager { + package { 'subscription-manager': + ensure => 'installed', + } +} + + - name: Ensure subscription-manager is installed + package: + name: subscription-manager + state: present + tags: + - CCE-82316-1 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_subscription-manager_installed + + +[[packages]] +name = "subscription-manager" +version = "*" + + +if ! rpm -q --quiet "subscription-manager" ; then + yum install -y "subscription-manager" +fi + + + + + + + + + + Install tar Package + The tar package can be installed with the following command: + +$ sudo yum install tar + The GNU tar program saves many files together into one archive and +can restore individual files (or all of the files) from the archive. tar +includes multivolume support, automatic archive compression/decompression, the +the ability to perform incremental and full backups. If + CCE-82965-5 + +package --add=tar + + include install_tar + +class install_tar { + package { 'tar': + ensure => 'installed', + } +} + + - name: Ensure tar is installed + package: + name: tar + state: present + tags: + - CCE-82965-5 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_tar_installed + + +[[packages]] +name = "tar" +version = "*" + + +if ! rpm -q --quiet "tar" ; then + yum install -y "tar" +fi + + + + + + + + + + Install vim Package + The vim-enhanced package can be installed with the following command: + +$ sudo yum install vim-enhanced + Vim (Vi IMproved) is an almost compatible version of the UNIX editor vi. + CCE-82956-4 + +package --add=vim-enhanced + + include install_vim-enhanced + +class install_vim-enhanced { + package { 'vim-enhanced': + ensure => 'installed', + } +} + + - name: Ensure vim-enhanced is installed + package: + name: vim-enhanced + state: present + tags: + - CCE-82956-4 + - enable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - package_vim_installed + + +[[packages]] +name = "vim-enhanced" +version = "*" + + +if ! rpm -q --quiet "vim-enhanced" ; then + yum install -y "vim-enhanced" +fi + + + + + + + + + + Uninstall abrt-addon-ccpp Package + The abrt-addon-ccpp package can be removed with the following command: + +$ sudo yum erase abrt-addon-ccpp + CCI-000381 + SRG-OS-000095-GPOS-00049 + RHEL-08-040001 + SV-230488r627750_rule + abrt-addon-ccpp contains hooks for C/C++ crashed programs and abrt's +C/C++ analyzer plugin. + CCE-82919-2 + +package --remove=abrt-addon-ccpp + + include remove_abrt-addon-ccpp + +class remove_abrt-addon-ccpp { + package { 'abrt-addon-ccpp': + ensure => 'purged', + } +} + + - name: Ensure abrt-addon-ccpp is removed + package: + name: abrt-addon-ccpp + state: absent + tags: + - CCE-82919-2 + - DISA-STIG-RHEL-08-040001 + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - package_abrt-addon-ccpp_removed + + +# CAUTION: This remediation script will remove abrt-addon-ccpp +# from the system, and may remove any packages +# that depend on abrt-addon-ccpp. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "abrt-addon-ccpp" ; then + + yum remove -y "abrt-addon-ccpp" + +fi + + + + + + + + + + Uninstall abrt-addon-kerneloops Package + The abrt-addon-kerneloops package can be removed with the following command: + +$ sudo yum erase abrt-addon-kerneloops + CCI-000381 + SRG-OS-000095-GPOS-00049 + RHEL-08-040001 + SV-230488r627750_rule + abrt-addon-kerneloops contains plugins for collecting kernel crash information and +reporter plugin which sends this information to a specified server, usually to kerneloops.org. + CCE-82926-7 + +package --remove=abrt-addon-kerneloops + + include remove_abrt-addon-kerneloops + +class remove_abrt-addon-kerneloops { + package { 'abrt-addon-kerneloops': + ensure => 'purged', + } +} + + - name: Ensure abrt-addon-kerneloops is removed + package: + name: abrt-addon-kerneloops + state: absent + tags: + - CCE-82926-7 + - DISA-STIG-RHEL-08-040001 + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - package_abrt-addon-kerneloops_removed + + +# CAUTION: This remediation script will remove abrt-addon-kerneloops +# from the system, and may remove any packages +# that depend on abrt-addon-kerneloops. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "abrt-addon-kerneloops" ; then + + yum remove -y "abrt-addon-kerneloops" + +fi + + + + + + + + + + Uninstall abrt-cli Package + The abrt-cli package can be removed with the following command: + +$ sudo yum erase abrt-cli + CCI-000381 + SRG-OS-000095-GPOS-00049 + RHEL-08-040001 + SV-230488r627750_rule + abrt-cli contains a command line client for controlling abrt daemon +over sockets. + CCE-82907-7 + +package --remove=abrt-cli + + include remove_abrt-cli + +class remove_abrt-cli { + package { 'abrt-cli': + ensure => 'purged', + } +} + + - name: Ensure abrt-cli is removed + package: + name: abrt-cli + state: absent + tags: + - CCE-82907-7 + - DISA-STIG-RHEL-08-040001 + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - package_abrt-cli_removed + + +# CAUTION: This remediation script will remove abrt-cli +# from the system, and may remove any packages +# that depend on abrt-cli. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "abrt-cli" ; then + + yum remove -y "abrt-cli" + +fi + + + + + + + + + + Uninstall abrt-plugin-logger Package + The abrt-plugin-logger package can be removed with the following command: + +$ sudo yum erase abrt-plugin-logger + CCI-000381 + SRG-OS-000095-GPOS-00049 + abrt-plugin-logger is an ABRT plugin which writes a report +to a specified file. + CCE-82913-5 + +package --remove=abrt-plugin-logger + + include remove_abrt-plugin-logger + +class remove_abrt-plugin-logger { + package { 'abrt-plugin-logger': + ensure => 'purged', + } +} + + - name: Ensure abrt-plugin-logger is removed + package: + name: abrt-plugin-logger + state: absent + tags: + - CCE-82913-5 + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - package_abrt-plugin-logger_removed + + +# CAUTION: This remediation script will remove abrt-plugin-logger +# from the system, and may remove any packages +# that depend on abrt-plugin-logger. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "abrt-plugin-logger" ; then + + yum remove -y "abrt-plugin-logger" + +fi + + + + + + + + + + Uninstall abrt-plugin-rhtsupport Package + The abrt-plugin-rhtsupport package can be removed with the following command: + +$ sudo yum erase abrt-plugin-rhtsupport + CCI-000381 + SRG-OS-000095-GPOS-00049 + abrt-plugin-rhtsupport is a ABRT plugin to report bugs into the +Red Hat Support system. + CCE-82916-8 + +package --remove=abrt-plugin-rhtsupport + + include remove_abrt-plugin-rhtsupport + +class remove_abrt-plugin-rhtsupport { + package { 'abrt-plugin-rhtsupport': + ensure => 'purged', + } +} + + - name: Ensure abrt-plugin-rhtsupport is removed + package: + name: abrt-plugin-rhtsupport + state: absent + tags: + - CCE-82916-8 + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - package_abrt-plugin-rhtsupport_removed + + +# CAUTION: This remediation script will remove abrt-plugin-rhtsupport +# from the system, and may remove any packages +# that depend on abrt-plugin-rhtsupport. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "abrt-plugin-rhtsupport" ; then + + yum remove -y "abrt-plugin-rhtsupport" + +fi + + + + + + + + + + Uninstall abrt-plugin-sosreport Package + The abrt-plugin-sosreport package can be removed with the following command: + +$ sudo yum erase abrt-plugin-sosreport + CCI-000381 + SRG-OS-000095-GPOS-00049 + RHEL-08-040001 + SV-230488r627750_rule + abrt-plugin-sosreport provides a plugin to include an sosreport in an ABRT report. + CCE-82910-1 + +package --remove=abrt-plugin-sosreport + + include remove_abrt-plugin-sosreport + +class remove_abrt-plugin-sosreport { + package { 'abrt-plugin-sosreport': + ensure => 'purged', + } +} + + - name: Ensure abrt-plugin-sosreport is removed + package: + name: abrt-plugin-sosreport + state: absent + tags: + - CCE-82910-1 + - DISA-STIG-RHEL-08-040001 + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - package_abrt-plugin-sosreport_removed + + +# CAUTION: This remediation script will remove abrt-plugin-sosreport +# from the system, and may remove any packages +# that depend on abrt-plugin-sosreport. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "abrt-plugin-sosreport" ; then + + yum remove -y "abrt-plugin-sosreport" + +fi + + + + + + + + + + Uninstall geolite2-city Package + The geolite2-city package can be removed with the following command: + +$ sudo yum erase geolite2-city + geolite2-city is part of the GeoLite2 database packages, offering geolocation databases and tooling. + CCE-82939-0 + +package --remove=geolite2-city + + include remove_geolite2-city + +class remove_geolite2-city { + package { 'geolite2-city': + ensure => 'purged', + } +} + + - name: Ensure geolite2-city is removed + package: + name: geolite2-city + state: absent + tags: + - CCE-82939-0 + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - package_geolite2-city_removed + + +# CAUTION: This remediation script will remove geolite2-city +# from the system, and may remove any packages +# that depend on geolite2-city. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "geolite2-city" ; then + + yum remove -y "geolite2-city" + +fi + + + + + + + + + + Uninstall geolite2-country Package + The geolite2-country package can be removed with the following command: + +$ sudo yum erase geolite2-country + geolite2-country is part of the GeoLite2 database packages, offering geolocation databases and tooling. + CCE-82936-6 + +package --remove=geolite2-country + + include remove_geolite2-country + +class remove_geolite2-country { + package { 'geolite2-country': + ensure => 'purged', + } +} + + - name: Ensure geolite2-country is removed + package: + name: geolite2-country + state: absent + tags: + - CCE-82936-6 + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - package_geolite2-country_removed + + +# CAUTION: This remediation script will remove geolite2-country +# from the system, and may remove any packages +# that depend on geolite2-country. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "geolite2-country" ; then + + yum remove -y "geolite2-country" + +fi + + + + + + + + + + Uninstall gssproxy Package + The gssproxy package can be removed with the following command: + +$ sudo yum erase gssproxy + This rule is disabled on Red Hat Virtualization Hosts and Managers, it will report not applicable. +RHV uses NFS storage, which has dependency on gssproxy. + CCI-000381 + CCI-000366 + SRG-OS-000095-GPOS-00049 + SRG-OS-000480-GPOS-00227 + RHEL-08-040370 + SV-230559r646887_rule + gssproxy is a proxy for GSS API credential handling. + + CCE-82943-2 + include remove_gssproxy + +class remove_gssproxy { + package { 'gssproxy': + ensure => 'purged', + } +} + + - name: Ensure gssproxy is removed + package: + name: gssproxy + state: absent + tags: + - CCE-82943-2 + - DISA-STIG-RHEL-08-040370 + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_gssproxy_removed + + +# CAUTION: This remediation script will remove gssproxy +# from the system, and may remove any packages +# that depend on gssproxy. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "gssproxy" ; then + + yum remove -y "gssproxy" + +fi + + + + + + + + + + Uninstall iprutils Package + The iprutils package can be removed with the following command: + +$ sudo yum erase iprutils + CCI-000366 + SRG-OS-000095-GPOS-00049 + SRG-OS-000480-GPOS-00227 + RHEL-08-040380 + SV-230560r627750_rule + iprutils provides a suite of utlilities to manage and configure SCSI devices +supported by the ipr SCSI storage device driver. + CCE-82946-5 + +package --remove=iprutils + + include remove_iprutils + +class remove_iprutils { + package { 'iprutils': + ensure => 'purged', + } +} + + - name: Ensure iprutils is removed + package: + name: iprutils + state: absent + tags: + - CCE-82946-5 + - DISA-STIG-RHEL-08-040380 + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_iprutils_removed + + +# CAUTION: This remediation script will remove iprutils +# from the system, and may remove any packages +# that depend on iprutils. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "iprutils" ; then + + yum remove -y "iprutils" + +fi + + + + + + + + + + Uninstall krb5-workstation Package + The krb5-workstation package can be removed with the following command: + +$ sudo yum erase krb5-workstation + This rule is disabled on Red Hat Virtualization Hosts and Managers, it will report not applicable. +RHV hosts require ipa-client package, which has dependency on krb5-workstation. + CCI-000803 + SRG-OS-000095-GPOS-00049 + SRG-OS-000120-GPOS-00061 + RHEL-08-010162 + SV-230239r646864_rule + Kerberos is a network authentication system. The krb5-workstation package contains the basic +Kerberos programs (kinit, klist, kdestroy, kpasswd). + + + + CCE-82931-7 + +package --remove=krb5-workstation + + include remove_krb5-workstation + +class remove_krb5-workstation { + package { 'krb5-workstation': + ensure => 'purged', + } +} + + - name: Ensure krb5-workstation is removed + package: + name: krb5-workstation + state: absent + tags: + - CCE-82931-7 + - DISA-STIG-RHEL-08-010162 + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_krb5-workstation_removed + + +# CAUTION: This remediation script will remove krb5-workstation +# from the system, and may remove any packages +# that depend on krb5-workstation. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "krb5-workstation" ; then + + yum remove -y "krb5-workstation" + +fi + + + + + + + + + + Uninstall libreport-plugin-logger Package + The libreport-plugin-logger package can be removed with the following command: + +$ sudo yum erase libreport-plugin-logger + CCI-000381 + SRG-OS-000095-GPOS-00049 + RHEL-08-040001 + SV-230488r627750_rule + libreport-plugin-logger is a ABRT plugin to report bugs into the +Red Hat Support system. + CCE-89201-8 + +package --remove=libreport-plugin-logger + + include remove_libreport-plugin-logger + +class remove_libreport-plugin-logger { + package { 'libreport-plugin-logger': + ensure => 'purged', + } +} + + - name: Ensure libreport-plugin-logger is removed + package: + name: libreport-plugin-logger + state: absent + tags: + - CCE-89201-8 + - DISA-STIG-RHEL-08-040001 + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - package_libreport-plugin-logger_removed + + +# CAUTION: This remediation script will remove libreport-plugin-logger +# from the system, and may remove any packages +# that depend on libreport-plugin-logger. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "libreport-plugin-logger" ; then + + yum remove -y "libreport-plugin-logger" + +fi + + + + + + + + + + Uninstall libreport-plugin-rhtsupport Package + The libreport-plugin-rhtsupport package can be removed with the following command: + +$ sudo yum erase libreport-plugin-rhtsupport + CCI-000381 + SRG-OS-000095-GPOS-00049 + RHEL-08-040001 + SV-230488r627750_rule + libreport-plugin-rhtsupport is a ABRT plugin to report bugs into the +Red Hat Support system. + CCE-88955-0 + +package --remove=libreport-plugin-rhtsupport + + include remove_libreport-plugin-rhtsupport + +class remove_libreport-plugin-rhtsupport { + package { 'libreport-plugin-rhtsupport': + ensure => 'purged', + } +} + + - name: Ensure libreport-plugin-rhtsupport is removed + package: + name: libreport-plugin-rhtsupport + state: absent + tags: + - CCE-88955-0 + - DISA-STIG-RHEL-08-040001 + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - package_libreport-plugin-rhtsupport_removed + + +# CAUTION: This remediation script will remove libreport-plugin-rhtsupport +# from the system, and may remove any packages +# that depend on libreport-plugin-rhtsupport. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "libreport-plugin-rhtsupport" ; then + + yum remove -y "libreport-plugin-rhtsupport" + +fi + + + + + + + + + + Uninstall pigz Package + The pigz package can be removed with the following command: + +$ sudo yum erase pigz + SRG-OS-000433-GPOS-00192 + Binaries shipped in pigz package in Red Hat Enterprise Linux 8 +have not been compiled using recommended compiler flags. The binaries +are compiled without sufficient stack protection and its address space +layout randomization (ASLR) is weak. + CCE-82397-1 + +package --remove=pigz + + include remove_pigz + +class remove_pigz { + package { 'pigz': + ensure => 'purged', + } +} + + - name: Ensure pigz is removed + package: + name: pigz + state: absent + tags: + - CCE-82397-1 + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - package_pigz_removed + + +# CAUTION: This remediation script will remove pigz +# from the system, and may remove any packages +# that depend on pigz. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "pigz" ; then + + yum remove -y "pigz" + +fi + + + + + + + + + + Uninstall python3-abrt-addon Package + The python3-abrt-addon package can be removed with the following command: + +$ sudo yum erase python3-abrt-addon + CCI-000381 + SRG-OS-000095-GPOS-00049 + RHEL-08-040001 + SV-230488r627750_rule + python3-abrt-addon contains python hook and python analyzer +plugin for handling uncaught exceptions in python programs. + CCE-86084-1 + +package --remove=python3-abrt-addon + + include remove_python3-abrt-addon + +class remove_python3-abrt-addon { + package { 'python3-abrt-addon': + ensure => 'purged', + } +} + + - name: Ensure python3-abrt-addon is removed + package: + name: python3-abrt-addon + state: absent + tags: + - CCE-86084-1 + - DISA-STIG-RHEL-08-040001 + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - package_python3-abrt-addon_removed + + +# CAUTION: This remediation script will remove python3-abrt-addon +# from the system, and may remove any packages +# that depend on python3-abrt-addon. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "python3-abrt-addon" ; then + + yum remove -y "python3-abrt-addon" + +fi + + + + + + + + + + Uninstall tuned Package + The tuned package can be removed with the following command: + +$ sudo yum erase tuned + This rule is disabled on Red Hat Virtualization Hosts and Managers, it will report not applicable. +RHV requires tuned package for tuning profiles that can enhance virtualization performance. + CCI-000366 + SRG-OS-000095-GPOS-00049 + SRG-OS-000480-GPOS-00227 + RHEL-08-040390 + SV-230561r627750_rule + tuned contains a daemon that tunes the system settings dynamically. +It does so by monitoring the usage of several system components periodically. Based +on that information, components will then be put into lower or higher power savings +modes to adapt to the current usage. + + CCE-82904-4 + +package --remove=tuned + + include remove_tuned + +class remove_tuned { + package { 'tuned': + ensure => 'purged', + } +} + + - name: Ensure tuned is removed + package: + name: tuned + state: absent + tags: + - CCE-82904-4 + - DISA-STIG-RHEL-08-040390 + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_tuned_removed + + +# CAUTION: This remediation script will remove tuned +# from the system, and may remove any packages +# that depend on tuned. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "tuned" ; then + + yum remove -y "tuned" + +fi + + + + + + + + + + + Updating Software + The yum command line tool is used to install and +update software packages. The system also provides a graphical +software update tool in the System menu, in the Administration submenu, +called Software Update. + +Red Hat Enterprise Linux 8 systems contain an installed software catalog called +the RPM database, which records metadata of installed packages. Consistently using +yum or the graphical Software Update for all software installation +allows for insight into the current inventory of installed software on the system. + + + Install dnf-automatic Package + The dnf-automatic package can be installed with the following command: + +$ sudo yum install dnf-automatic + BP28(R8) + SRG-OS-000191-GPOS-00080 + dnf-automatic is an alternative command line interface (CLI) +to dnf upgrade suitable for automatic, regular execution. + CCE-82985-3 + +package --add=dnf-automatic + + include install_dnf-automatic + +class install_dnf-automatic { + package { 'dnf-automatic': + ensure => 'installed', + } +} + + - name: Ensure dnf-automatic is installed + package: + name: dnf-automatic + state: present + tags: + - CCE-82985-3 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_dnf-automatic_installed + + +[[packages]] +name = "dnf-automatic" +version = "*" + + +if ! rpm -q --quiet "dnf-automatic" ; then + yum install -y "dnf-automatic" +fi + + + + + + + + + + Ensure yum Removes Previous Package Versions + yum should be configured to remove previous software components after +new versions have been installed. To configure yum to remove the + +previous software components after updating, set the clean_requirements_on_remove + + +to 1 in /etc/yum.conf. + 18 + 20 + 4 + APO12.01 + APO12.02 + APO12.03 + APO12.04 + BAI03.10 + DSS05.01 + DSS05.02 + 3.4.8 + CCI-002617 + 4.2.3 + 4.2.3.12 + 4.2.3.7 + 4.2.3.9 + A.12.6.1 + A.14.2.3 + A.16.1.3 + A.18.2.2 + A.18.2.3 + SI-2(6) + CM-11(a) + CM-11(b) + CM-6(a) + ID.RA-1 + PR.IP-12 + SRG-OS-000437-GPOS-00194 + SRG-OS-000437-VMM-001760 + RHEL-08-010440 + SV-230281r627750_rule + Previous versions of software components that are not removed from the information +system after updates have been installed may be exploited by some adversaries. + + CCE-82476-3 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-82476-3 + - DISA-STIG-RHEL-08-010440 + - NIST-800-171-3.4.8 + - NIST-800-53-CM-11(a) + - NIST-800-53-CM-11(b) + - NIST-800-53-CM-6(a) + - NIST-800-53-SI-2(6) + - clean_components_post_updating + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - restrict_strategy + +- name: Ensure YUM Removes Previous Package Versions + lineinfile: + dest: /etc/yum.conf + regexp: ^#?clean_requirements_on_remove + line: clean_requirements_on_remove=1 + insertafter: \[main\] + create: true + when: '"yum" in ansible_facts.packages' + tags: + - CCE-82476-3 + - DISA-STIG-RHEL-08-010440 + - NIST-800-171-3.4.8 + - NIST-800-53-CM-11(a) + - NIST-800-53-CM-11(b) + - NIST-800-53-CM-6(a) + - NIST-800-53-SI-2(6) + - clean_components_post_updating + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q yum; then + +if grep --silent ^clean_requirements_on_remove /etc/yum.conf ; then + sed -i "s/^clean_requirements_on_remove.*/clean_requirements_on_remove=1/g" /etc/yum.conf +else + echo -e "\n# Set clean_requirements_on_remove to 1 per security requirements" >> /etc/yum.conf + echo "clean_requirements_on_remove=1" >> /etc/yum.conf +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure dnf-automatic to Install Available Updates Automatically + To ensure that the packages comprising the available updates will be automatically installed by dnf-automatic, set apply_updates to yes under [commands] section in /etc/dnf/automatic.conf. + BP28(R8) + 0940 + 1144 + 1467 + 1472 + 1483 + 1493 + 1494 + 1495 + SI-2(5) + CM-6(a) + SI-2(c) + FMT_SMF_EXT.1 + SRG-OS-000191-GPOS-00080 + Installing software updates is a fundamental mitigation against +the exploitation of publicly-known vulnerabilities. If the most +recent security patches and updates are not installed, unauthorized +users may take advantage of weaknesses in the unpatched software. The +lack of prompt attention to patching could result in a system compromise. +The automated installation of updates ensures that recent security patches +are applied in a timely manner. + CCE-82494-6 + - name: Configure dnf-automatic to Install Available Updates Automatically + ini_file: + dest: /etc/dnf/automatic.conf + section: commands + option: apply_updates + value: 'yes' + create: true + tags: + - CCE-82494-6 + - NIST-800-53-CM-6(a) + - NIST-800-53-SI-2(5) + - NIST-800-53-SI-2(c) + - dnf-automatic_apply_updates + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + + +found=false + +# set value in all files if they contain section or key +for f in $(echo -n "/etc/dnf/automatic.conf"); do + if [ ! -e "$f" ]; then + continue + fi + + # find key in section and change value + if grep -qzosP "[[:space:]]*\[commands\]([^\n\[]*\n+)+?[[:space:]]*apply_updates" "$f"; then + sed -i "s/apply_updates[^(\n)]*/apply_updates = yes/" "$f" + found=true + + # find section and add key = value to it + elif grep -qs "[[:space:]]*\[commands\]" "$f"; then + sed -i "/[[:space:]]*\[commands\]/a apply_updates = yes" "$f" + found=true + fi +done + +# if section not in any file, append section with key = value to FIRST file in files parameter +if ! $found ; then + file=$(echo "/etc/dnf/automatic.conf" | cut -f1 -d ' ') + mkdir -p "$(dirname "$file")" + echo -e "[commands]\napply_updates = yes" >> "$file" +fi + + + + + + + + + + Configure dnf-automatic to Install Only Security Updates + To configure dnf-automatic to install only security updates +automatically, set upgrade_type to security under +[commands] section in /etc/dnf/automatic.conf. + BP28(R8) + SI-2(5) + CM-6(a) + SI-2(c) + FMT_SMF_EXT.1 + SRG-OS-000191-GPOS-00080 + By default, dnf-automatic installs all available updates. +Reducing the amount of updated packages only to updates that were +issued as a part of a security advisory increases the system stability. + CCE-82267-6 + - name: Configure dnf-automatic to Install Only Security Updates + ini_file: + dest: /etc/dnf/automatic.conf + section: commands + option: upgrade_type + value: security + create: true + tags: + - CCE-82267-6 + - NIST-800-53-CM-6(a) + - NIST-800-53-SI-2(5) + - NIST-800-53-SI-2(c) + - dnf-automatic_security_updates_only + - low_complexity + - low_severity + - medium_disruption + - no_reboot_needed + - unknown_strategy + + +found=false + +# set value in all files if they contain section or key +for f in $(echo -n "/etc/dnf/automatic.conf"); do + if [ ! -e "$f" ]; then + continue + fi + + # find key in section and change value + if grep -qzosP "[[:space:]]*\[commands\]([^\n\[]*\n+)+?[[:space:]]*upgrade_type" "$f"; then + sed -i "s/upgrade_type[^(\n)]*/upgrade_type = security/" "$f" + found=true + + # find section and add key = value to it + elif grep -qs "[[:space:]]*\[commands\]" "$f"; then + sed -i "/[[:space:]]*\[commands\]/a upgrade_type = security" "$f" + found=true + fi +done + +# if section not in any file, append section with key = value to FIRST file in files parameter +if ! $found ; then + file=$(echo "/etc/dnf/automatic.conf" | cut -f1 -d ' ') + mkdir -p "$(dirname "$file")" + echo -e "[commands]\nupgrade_type = security" >> "$file" +fi + + + + + + + + + + Ensure gpgcheck Enabled In Main yum Configuration + The gpgcheck option controls whether +RPM packages' signatures are always checked prior to installation. +To configure yum to check package signatures before installing +them, ensure the following line appears in /etc/yum.conf in +the [main] section: +gpgcheck=1 + BP28(R15) + 11 + 2 + 3 + 9 + 5.10.4.1 + APO01.06 + BAI03.05 + BAI06.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS06.02 + 3.4.8 + CCI-001749 + 164.308(a)(1)(ii)(D) + 164.312(b) + 164.312(c)(1) + 164.312(c)(2) + 164.312(e)(2)(i) + 4.3.4.3.2 + 4.3.4.3.3 + 4.3.4.4.4 + SR 3.1 + SR 3.3 + SR 3.4 + SR 3.8 + SR 7.6 + A.11.2.4 + A.12.1.2 + A.12.2.1 + A.12.5.1 + A.12.6.2 + A.14.1.2 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + CM-5(3) + SI-7 + SC-12 + SC-12(3) + CM-6(a) + SA-12 + SA-12(10) + CM-11(a) + CM-11(b) + PR.DS-6 + PR.DS-8 + PR.IP-1 + FPT_TUD_EXT.1 + FPT_TUD_EXT.2 + Req-6.2 + SRG-OS-000366-GPOS-00153 + SRG-OS-000366-VMM-001430 + SRG-OS-000370-VMM-001460 + SRG-OS-000404-VMM-001650 + RHEL-08-010370 + 1.2.3 + SV-230264r627750_rule + Changes to any software components can have significant effects on the +overall security of the operating system. This requirement ensures the +software has not been tampered with and that it has been provided by a +trusted vendor. + +Accordingly, patches, service packs, device drivers, or operating system +components must be signed with a certificate recognized and approved by the +organization. +Verifying the authenticity of the software prior to installation +validates the integrity of the patch or upgrade received from a vendor. +This ensures the software has not been tampered with and that it has been +provided by a trusted vendor. Self-signed certificates are disallowed by +this requirement. Certificates used to verify the software must be from an +approved Certificate Authority (CA). + + CCE-80790-9 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80790-9 + - CJIS-5.10.4.1 + - DISA-STIG-RHEL-08-010370 + - NIST-800-171-3.4.8 + - NIST-800-53-CM-11(a) + - NIST-800-53-CM-11(b) + - NIST-800-53-CM-5(3) + - NIST-800-53-CM-6(a) + - NIST-800-53-SA-12 + - NIST-800-53-SA-12(10) + - NIST-800-53-SC-12 + - NIST-800-53-SC-12(3) + - NIST-800-53-SI-7 + - PCI-DSS-Req-6.2 + - configure_strategy + - ensure_gpgcheck_globally_activated + - high_severity + - low_complexity + - medium_disruption + - no_reboot_needed + +- name: Ensure GPG check is globally activated + ini_file: + dest: /etc/yum.conf + section: main + option: gpgcheck + value: 1 + no_extra_spaces: true + create: false + when: '"yum" in ansible_facts.packages' + tags: + - CCE-80790-9 + - CJIS-5.10.4.1 + - DISA-STIG-RHEL-08-010370 + - NIST-800-171-3.4.8 + - NIST-800-53-CM-11(a) + - NIST-800-53-CM-11(b) + - NIST-800-53-CM-5(3) + - NIST-800-53-CM-6(a) + - NIST-800-53-SA-12 + - NIST-800-53-SA-12(10) + - NIST-800-53-SC-12 + - NIST-800-53-SC-12(3) + - NIST-800-53-SI-7 + - PCI-DSS-Req-6.2 + - configure_strategy + - ensure_gpgcheck_globally_activated + - high_severity + - low_complexity + - medium_disruption + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if rpm --quiet -q yum; then + +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/yum.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^gpgcheck") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "1" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^gpgcheck\\>" "/etc/yum.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^gpgcheck\\>.*/$escaped_formatted_output/gi" "/etc/yum.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-80790-9" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/yum.conf" >> "/etc/yum.conf" + printf '%s\n' "$formatted_output" >> "/etc/yum.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure gpgcheck Enabled for Local Packages + yum should be configured to verify the signature(s) of local packages +prior to installation. To configure yum to verify signatures of local +packages, set the localpkg_gpgcheck to 1 in /etc/yum.conf. + BP28(R15) + 11 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + 3.4.8 + CCI-001749 + 164.308(a)(1)(ii)(D) + 164.312(b) + 164.312(c)(1) + 164.312(c)(2) + 164.312(e)(2)(i) + 4.3.4.3.2 + 4.3.4.3.3 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + CM-11(a) + CM-11(b) + CM-6(a) + CM-5(3) + SA-12 + SA-12(10) + PR.IP-1 + FPT_TUD_EXT.1 + FPT_TUD_EXT.2 + SRG-OS-000366-GPOS-00153 + SRG-OS-000366-VMM-001430 + SRG-OS-000370-VMM-001460 + SRG-OS-000404-VMM-001650 + RHEL-08-010371 + SV-230265r627750_rule + Changes to any software components can have significant effects to the overall security +of the operating system. This requirement ensures the software has not been tampered and +has been provided by a trusted vendor. + +Accordingly, patches, service packs, device drivers, or operating system components must +be signed with a certificate recognized and approved by the organization. + + CCE-80791-7 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80791-7 + - DISA-STIG-RHEL-08-010371 + - NIST-800-171-3.4.8 + - NIST-800-53-CM-11(a) + - NIST-800-53-CM-11(b) + - NIST-800-53-CM-5(3) + - NIST-800-53-CM-6(a) + - NIST-800-53-SA-12 + - NIST-800-53-SA-12(10) + - ensure_gpgcheck_local_packages + - high_severity + - low_complexity + - medium_disruption + - no_reboot_needed + - unknown_strategy + +- name: Ensure GPG check Enabled for Local Packages (yum) + block: + + - name: Check stats of yum + stat: + path: /etc/yum.conf + register: pkg + + - name: Check if config file of yum is a symlink + ansible.builtin.set_fact: + pkg_config_file_symlink: '{{ pkg.stat.lnk_target if pkg.stat.lnk_target is match("^/.*") + else "/etc/yum.conf" | dirname ~ "/" ~ pkg.stat.lnk_target }}' + when: pkg.stat.lnk_target is defined + + - name: Ensure GPG check Enabled for Local Packages (yum) + ini_file: + dest: '{{ pkg_config_file_symlink | default("/etc/yum.conf") }}' + section: main + option: localpkg_gpgcheck + value: 1 + no_extra_spaces: true + create: true + when: '"yum" in ansible_facts.packages' + tags: + - CCE-80791-7 + - DISA-STIG-RHEL-08-010371 + - NIST-800-171-3.4.8 + - NIST-800-53-CM-11(a) + - NIST-800-53-CM-11(b) + - NIST-800-53-CM-5(3) + - NIST-800-53-CM-6(a) + - NIST-800-53-SA-12 + - NIST-800-53-SA-12(10) + - ensure_gpgcheck_local_packages + - high_severity + - low_complexity + - medium_disruption + - no_reboot_needed + - unknown_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q yum; then + +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/yum.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^localpkg_gpgcheck") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "1" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^localpkg_gpgcheck\\>" "/etc/yum.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^localpkg_gpgcheck\\>.*/$escaped_formatted_output/gi" "/etc/yum.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-80791-7" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/yum.conf" >> "/etc/yum.conf" + printf '%s\n' "$formatted_output" >> "/etc/yum.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure gpgcheck Enabled for All yum Package Repositories + To ensure signature checking is not disabled for +any repos, remove any lines from files in /etc/yum.repos.d of the form: +gpgcheck=0 + BP28(R15) + 11 + 2 + 3 + 9 + 5.10.4.1 + APO01.06 + BAI03.05 + BAI06.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS06.02 + 3.4.8 + CCI-001749 + 164.308(a)(1)(ii)(D) + 164.312(b) + 164.312(c)(1) + 164.312(c)(2) + 164.312(e)(2)(i) + 4.3.4.3.2 + 4.3.4.3.3 + 4.3.4.4.4 + SR 3.1 + SR 3.3 + SR 3.4 + SR 3.8 + SR 7.6 + A.11.2.4 + A.12.1.2 + A.12.2.1 + A.12.5.1 + A.12.6.2 + A.14.1.2 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + CM-5(3) + SI-7 + SC-12 + SC-12(3) + CM-6(a) + SA-12 + SA-12(10) + CM-11(a) + CM-11(b) + PR.DS-6 + PR.DS-8 + PR.IP-1 + FPT_TUD_EXT.1 + FPT_TUD_EXT.2 + Req-6.2 + SRG-OS-000366-GPOS-00153 + SRG-OS-000366-VMM-001430 + SRG-OS-000370-VMM-001460 + SRG-OS-000404-VMM-001650 + RHEL-08-010370 + SV-230264r627750_rule + Verifying the authenticity of the software prior to installation validates +the integrity of the patch or upgrade received from a vendor. This ensures +the software has not been tampered with and that it has been provided by a +trusted vendor. Self-signed certificates are disallowed by this +requirement. Certificates used to verify the software must be from an +approved Certificate Authority (CA)." + CCE-80792-5 + - name: Grep for yum repo section names + shell: | + set -o pipefail + grep -HEr '^\[.+\]' -r /etc/yum.repos.d/ + register: repo_grep_results + ignore_errors: true + changed_when: false + tags: + - CCE-80792-5 + - CJIS-5.10.4.1 + - DISA-STIG-RHEL-08-010370 + - NIST-800-171-3.4.8 + - NIST-800-53-CM-11(a) + - NIST-800-53-CM-11(b) + - NIST-800-53-CM-5(3) + - NIST-800-53-CM-6(a) + - NIST-800-53-SA-12 + - NIST-800-53-SA-12(10) + - NIST-800-53-SC-12 + - NIST-800-53-SC-12(3) + - NIST-800-53-SI-7 + - PCI-DSS-Req-6.2 + - enable_strategy + - ensure_gpgcheck_never_disabled + - high_severity + - low_complexity + - medium_disruption + - no_reboot_needed + +- name: Set gpgcheck=1 for each yum repo + ini_file: + path: '{{ item[0] }}' + section: '{{ item[1] }}' + option: gpgcheck + value: '1' + no_extra_spaces: true + loop: '{{ repo_grep_results.stdout | regex_findall( ''(.+\.repo):\[(.+)\]\n?'' ) + }}' + tags: + - CCE-80792-5 + - CJIS-5.10.4.1 + - DISA-STIG-RHEL-08-010370 + - NIST-800-171-3.4.8 + - NIST-800-53-CM-11(a) + - NIST-800-53-CM-11(b) + - NIST-800-53-CM-5(3) + - NIST-800-53-CM-6(a) + - NIST-800-53-SA-12 + - NIST-800-53-SA-12(10) + - NIST-800-53-SC-12 + - NIST-800-53-SC-12(3) + - NIST-800-53-SI-7 + - PCI-DSS-Req-6.2 + - enable_strategy + - ensure_gpgcheck_never_disabled + - high_severity + - low_complexity + - medium_disruption + - no_reboot_needed + + +sed -i 's/gpgcheck\s*=.*/gpgcheck=1/g' /etc/yum.repos.d/* + + + + + + + + + + Ensure gpgcheck Enabled for Repository Metadata + Verify the operating system prevents the installation of patches, +service packs, device drivers, or operating system components of +local packages without verification of the repository metadata. +Check that yum verifies the repository +metadata prior to install with the following command. +This should be configured by setting repo_gpgcheck to 1 +in /etc/yum.conf. + 11 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + CCI-001749 + 164.308(a)(1)(ii)(D) + 164.312(b) + 164.312(c)(1) + 164.312(c)(2) + 164.312(e)(2)(i) + 4.3.4.3.2 + 4.3.4.3.3 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + CM-5(3) + SI-7 + SC-12 + SC-12(3) + CM-6(a) + SA-12 + SA-12(10) + CM-11(a) + CM-11(b) + PR.IP-1 + SRG-OS-000366-GPOS-00153 + Changes to any software components can have significant effects to the +overall security of the operating system. This requirement ensures the +software has not been tampered and has been provided by a trusted vendor. +Accordingly, patches, service packs, device drivers, or operating system +components must be signed with a certificate recognized and approved by +the organization. Verifying the authenticity of the software prior to +installation validates the integrity of the patch or upgrade received from +a vendor. This ensures the software has not been tampered with and that it +has been provided by a trusted vendor. Self-signed certificates are +disallowed by this requirement. The operating system should not have +to verify the software again. NOTE: For U.S. Military systems, this +requirement does not mandate DoD certificates for this purpose; however, +the certificate used to verify the software must be from an approved +Certificate Authority. + + CCE-80793-3 + + + + + + + + + Ensure Red Hat GPG Key Installed + To ensure the system can cryptographically verify base software packages +come from Red Hat (and to connect to the Red Hat Network to receive them), +the Red Hat GPG key must properly be installed. To install the Red Hat GPG +key, run: +$ sudo subscription-manager register + +If the system is not connected to the Internet or an RHN Satellite, then +install the Red Hat GPG key from trusted media such as the Red Hat +installation CD-ROM or DVD. Assuming the disc is mounted in +/media/cdrom, use the following command as the root user to import +it into the keyring: +$ sudo rpm --import /media/cdrom/RPM-GPG-KEY + +Alternatively, the key may be pre-loaded during the RHEL installation. In +such cases, the key can be installed by running the following command: +sudo rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release + BP28(R15) + 11 + 2 + 3 + 9 + 5.10.4.1 + APO01.06 + BAI03.05 + BAI06.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS06.02 + 3.4.8 + CCI-001749 + 164.308(a)(1)(ii)(D) + 164.312(b) + 164.312(c)(1) + 164.312(c)(2) + 164.312(e)(2)(i) + 4.3.4.3.2 + 4.3.4.3.3 + 4.3.4.4.4 + SR 3.1 + SR 3.3 + SR 3.4 + SR 3.8 + SR 7.6 + A.11.2.4 + A.12.1.2 + A.12.2.1 + A.12.5.1 + A.12.6.2 + A.14.1.2 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + CIP-003-8 R4.2 + CIP-003-8 R6 + CIP-007-3 R4 + CIP-007-3 R4.1 + CIP-007-3 R4.2 + CIP-007-3 R5.1 + CM-5(3) + SI-7 + SC-12 + SC-12(3) + CM-6(a) + PR.DS-6 + PR.DS-8 + PR.IP-1 + FPT_TUD_EXT.1 + FPT_TUD_EXT.2 + Req-6.2 + SRG-OS-000366-GPOS-00153 + SRG-OS-000366-VMM-001430 + SRG-OS-000370-VMM-001460 + SRG-OS-000404-VMM-001650 + 1.2.2 + Changes to software components can have significant effects on the overall +security of the operating system. This requirement ensures the software has +not been tampered with and that it has been provided by a trusted vendor. +The Red Hat GPG key is necessary to cryptographically verify packages are +from Red Hat. + CCE-80795-8 + - name: Read permission of GPG key directory + stat: + path: /etc/pki/rpm-gpg/ + register: gpg_key_directory_permission + check_mode: false + tags: + - CCE-80795-8 + - CJIS-5.10.4.1 + - NIST-800-171-3.4.8 + - NIST-800-53-CM-5(3) + - NIST-800-53-CM-6(a) + - NIST-800-53-SC-12 + - NIST-800-53-SC-12(3) + - NIST-800-53-SI-7 + - PCI-DSS-Req-6.2 + - ensure_redhat_gpgkey_installed + - high_severity + - medium_complexity + - medium_disruption + - no_reboot_needed + - restrict_strategy + +- name: Read signatures in GPG key + command: gpg --show-keys --with-fingerprint --with-colons "/etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release" + args: + warn: false + changed_when: false + register: gpg_fingerprints + check_mode: false + tags: + - CCE-80795-8 + - CJIS-5.10.4.1 + - NIST-800-171-3.4.8 + - NIST-800-53-CM-5(3) + - NIST-800-53-CM-6(a) + - NIST-800-53-SC-12 + - NIST-800-53-SC-12(3) + - NIST-800-53-SI-7 + - PCI-DSS-Req-6.2 + - ensure_redhat_gpgkey_installed + - high_severity + - medium_complexity + - medium_disruption + - no_reboot_needed + - restrict_strategy + +- name: Set Fact - Installed GPG Fingerprints + set_fact: + gpg_installed_fingerprints: |- + {{ gpg_fingerprints.stdout | regex_findall('^pub.* + (?:^fpr[:]*)([0-9A-Fa-f]*)', '\1') | list }} + tags: + - CCE-80795-8 + - CJIS-5.10.4.1 + - NIST-800-171-3.4.8 + - NIST-800-53-CM-5(3) + - NIST-800-53-CM-6(a) + - NIST-800-53-SC-12 + - NIST-800-53-SC-12(3) + - NIST-800-53-SI-7 + - PCI-DSS-Req-6.2 + - ensure_redhat_gpgkey_installed + - high_severity + - medium_complexity + - medium_disruption + - no_reboot_needed + - restrict_strategy + +- name: Set Fact - Valid fingerprints + set_fact: + gpg_valid_fingerprints: ("567E347AD0044ADE55BA8A5F199E2F91FD431D51" "6A6AA7C97C8890AEC6AEBFE2F76F66C3D4082792") + tags: + - CCE-80795-8 + - CJIS-5.10.4.1 + - NIST-800-171-3.4.8 + - NIST-800-53-CM-5(3) + - NIST-800-53-CM-6(a) + - NIST-800-53-SC-12 + - NIST-800-53-SC-12(3) + - NIST-800-53-SI-7 + - PCI-DSS-Req-6.2 + - ensure_redhat_gpgkey_installed + - high_severity + - medium_complexity + - medium_disruption + - no_reboot_needed + - restrict_strategy + +- name: Import RedHat GPG key + rpm_key: + state: present + key: /etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release + when: + - gpg_key_directory_permission.stat.mode <= '0755' + - (gpg_installed_fingerprints | difference(gpg_valid_fingerprints)) | length == + 0 + - gpg_installed_fingerprints | length > 0 + - ansible_distribution == "RedHat" + tags: + - CCE-80795-8 + - CJIS-5.10.4.1 + - NIST-800-171-3.4.8 + - NIST-800-53-CM-5(3) + - NIST-800-53-CM-6(a) + - NIST-800-53-SC-12 + - NIST-800-53-SC-12(3) + - NIST-800-53-SI-7 + - PCI-DSS-Req-6.2 + - ensure_redhat_gpgkey_installed + - high_severity + - medium_complexity + - medium_disruption + - no_reboot_needed + - restrict_strategy + + # The two fingerprints below are retrieved from https://access.redhat.com/security/team/key +readonly REDHAT_RELEASE_FINGERPRINT="567E347AD0044ADE55BA8A5F199E2F91FD431D51" +readonly REDHAT_AUXILIARY_FINGERPRINT="6A6AA7C97C8890AEC6AEBFE2F76F66C3D4082792" + +# Location of the key we would like to import (once it's integrity verified) +readonly REDHAT_RELEASE_KEY="/etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release" + +RPM_GPG_DIR_PERMS=$(stat -c %a "$(dirname "$REDHAT_RELEASE_KEY")") + +# Verify /etc/pki/rpm-gpg directory permissions are safe +if [ "${RPM_GPG_DIR_PERMS}" -le "755" ] +then + # If they are safe, try to obtain fingerprints from the key file + # (to ensure there won't be e.g. CRC error). + + readarray -t GPG_OUT < <(gpg --show-keys --with-fingerprint --with-colons "$REDHAT_RELEASE_KEY" | grep -A1 "^pub" | grep "^fpr" | cut -d ":" -f 10) + + GPG_RESULT=$? + # No CRC error, safe to proceed + if [ "${GPG_RESULT}" -eq "0" ] + then + echo "${GPG_OUT[*]}" | grep -vE "${REDHAT_RELEASE_FINGERPRINT}|${REDHAT_AUXILIARY_FINGERPRINT}" || { + # If $REDHAT_RELEASE_KEY file doesn't contain any keys with unknown fingerprint, import it + rpm --import "${REDHAT_RELEASE_KEY}" + } + fi +fi + + + + + + + + + + Ensure Software Patches Installed + +If the system is joined to the Red Hat Network, a Red Hat Satellite Server, +or a yum server, run the following command to install updates: +$ sudo yum update +If the system is not configured to use one of these sources, updates (in the form of RPM packages) +can be manually downloaded from the Red Hat Network and installed using rpm. + + +NOTE: U.S. Defense systems are required to be patched within 30 days or sooner as local policy +dictates. + The OVAL feed of Red Hat Enterprise Linux 8 is not a XML file, which may not be understood by all scanners. + BP28(R08) + 18 + 20 + 4 + 5.10.4.1 + APO12.01 + APO12.02 + APO12.03 + APO12.04 + BAI03.10 + DSS05.01 + DSS05.02 + CCI-000366 + CCI-001227 + 4.2.3 + 4.2.3.12 + 4.2.3.7 + 4.2.3.9 + A.12.6.1 + A.14.2.3 + A.16.1.3 + A.18.2.2 + A.18.2.3 + SI-2(5) + SI-2(c) + CM-6(a) + ID.RA-1 + PR.IP-12 + FMT_MOF_EXT.1 + Req-6.2 + SRG-OS-000480-GPOS-00227 + SRG-OS-000480-VMM-002000 + RHEL-08-010010 + 1.9 + SV-230222r627750_rule + Installing software updates is a fundamental mitigation against +the exploitation of publicly-known vulnerabilities. If the most +recent security patches and updates are not installed, unauthorized +users may take advantage of weaknesses in the unpatched software. The +lack of prompt attention to patching could result in a system compromise. + CCE-80865-9 + - name: Security patches are up to date + package: + name: '*' + state: latest + tags: + - CCE-80865-9 + - CJIS-5.10.4.1 + - DISA-STIG-RHEL-08-010010 + - NIST-800-53-CM-6(a) + - NIST-800-53-SI-2(5) + - NIST-800-53-SI-2(c) + - PCI-DSS-Req-6.2 + - high_disruption + - low_complexity + - medium_severity + - patch_strategy + - reboot_required + - security_patches_up_to_date + - skip_ansible_lint + + + +yum -y update + + + + + + + Enable dnf-automatic Timer + +The dnf-automatic timer can be enabled with the following command: +$ sudo systemctl enable dnf-automatic.timer + BP28(R8) + SI-2(5) + CM-6(a) + SI-2(c) + FMT_SMF_EXT.1 + SRG-OS-000191-GPOS-00080 + The dnf-automatic is an alternative command line interface (CLI) to dnf upgrade with specific facilities to make it suitable to be executed automatically and regularly from systemd timers, cron jobs and similar. +The tool is controlled by dnf-automatic.timer SystemD timer. + CCE-82360-9 + - name: Enable timer dnf-automatic + block: + + - name: Gather the package facts + package_facts: + manager: auto + + - name: Enable timer dnf-automatic + systemd: + name: dnf-automatic.timer + enabled: 'yes' + state: started + when: + - '"dnf-automatic" in ansible_facts.packages' + tags: + - CCE-82360-9 + - NIST-800-53-CM-6(a) + - NIST-800-53-SI-2(5) + - NIST-800-53-SI-2(c) + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - timer_dnf-automatic_enabled + + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" start 'dnf-automatic.timer' +"$SYSTEMCTL_EXEC" enable 'dnf-automatic.timer' + + + + + + + + + + + + Account and Access Control + In traditional Unix security, if an attacker gains +shell access to a certain login account, they can perform any action +or access any file to which that account has access. Therefore, +making it more difficult for unauthorized people to gain shell +access to accounts, particularly to privileged accounts, is a +necessary part of securing a system. This section introduces +mechanisms for restricting access to accounts under +Red Hat Enterprise Linux 8. + + Authselect profile + Specify the authselect profile to select + minimal + minimal + sssd + + + Enable authselect + Configure user authentication setup to use the authselect tool. +If authselect profile is selected, the rule will enable the profile. + If the sudo authselect select command returns an error informing that the chosen +profile cannot be selected, it is probably because PAM files have already been modified by +the administrator. If this is the case, in order to not overwrite the desired changes made +by the administrator, the current PAM settings should be investigated before forcing the +selection of the chosen authselect profile. + BP28(R5) + CCI-000213 + 164.308(a)(1)(ii)(B) + 164.308(a)(7)(i) + 164.308(a)(7)(ii)(A) + 164.310(a)(1) + 164.310(a)(2)(i) + 164.310(a)(2)(ii) + 164.310(a)(2)(iii) + 164.310(b) + 164.310(c) + 164.310(d)(1) + 164.310(d)(2)(iii) + AC-3 + FIA_UAU.1 + FIA_AFL.1 + SRG-OS-000480-GPOS-00227 + 1.2.3 + Authselect is a successor to authconfig. +It is a tool to select system authentication and identity sources from a list of supported +profiles instead of letting the administrator manually build the PAM stack. + +That way, it avoids potential breakage of configuration, as it ships several tested profiles +that are well tested and supported to solve different use-cases. + CCE-88248-0 + - name: XCCDF Value var_authselect_profile # promote to variable + set_fact: + var_authselect_profile: !!str + tags: + - always + +- name: Select authselect profile + ansible.builtin.command: + cmd: authselect select "{{ var_authselect_profile }}" + ignore_errors: true + register: result_authselect_select + tags: + - CCE-88248-0 + - NIST-800-53-AC-3 + - configure_strategy + - enable_authselect + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + +- name: Verify if PAM has been altered + ansible.builtin.command: + cmd: rpm -qV pam + register: result_altered_authselect + ignore_errors: true + args: + warn: false + when: result_authselect_select is failed + tags: + - CCE-88248-0 + - NIST-800-53-AC-3 + - configure_strategy + - enable_authselect + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + +- name: Informative message based on the authselect integrity check + ansible.builtin.assert: + that: + - result_altered_authselect is success + fail_msg: + - Files in the 'pam' package have been altered, so the authselect configuration + won't be forced. + tags: + - CCE-88248-0 + - NIST-800-53-AC-3 + - configure_strategy + - enable_authselect + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + +- name: Force authselect profile select + ansible.builtin.command: + cmd: authselect select --force "{{ var_authselect_profile }}" + when: + - result_altered_authselect is success + - result_authselect_select is failed + tags: + - CCE-88248-0 + - NIST-800-53-AC-3 + - configure_strategy + - enable_authselect + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + + +var_authselect_profile='' + + +authselect select "$var_authselect_profile" + +if test "$?" -ne 0; then + if rpm --quiet --verify pam; then + authselect select --force "$var_authselect_profile" + else + echo "Files in the 'pam' package have been altered, so the authselect configuration won't be forced" >&2 + fi +fi + + + + + + + + + + Warning Banners for System Accesses + Each system should expose as little information about +itself as possible. + +System banners, which are typically displayed just before a +login prompt, give out information about the service or the host's +operating system. This might include the distribution name and the +system kernel version, and the particular version of a network +service. This information can assist intruders in gaining access to +the system as it can reveal whether the system is running +vulnerable software. Most network services can be configured to +limit what information is displayed. + +Many organizations implement security policies that require a +system banner provide notice of the system's ownership, provide +warning to unauthorized users, and remind authorized users of their +consent to monitoring. + + Login Banner Verbiage + Enter an appropriate login banner for your organization. Please note that new lines must +be expressed by the '\n' character and special characters like parentheses and quotation marks must be escaped with '\\'. + ^(Authorized[\s\n]+uses[\s\n]+only\.[\s\n]+All[\s\n]+activity[\s\n]+may[\s\n]+be[\s\n]+monitored[\s\n]+and[\s\n]+reported\.|^(?!.*(\\|fedora|rhel|sle|ubuntu)).*)$ + ^Authorized[\s\n]+uses[\s\n]+only\.[\s\n]+All[\s\n]+activity[\s\n]+may[\s\n]+be[\s\n]+monitored[\s\n]+and[\s\n]+reported\.$ + ^(You[\s\n]+are[\s\n]+accessing[\s\n]+a[\s\n]+U\.S\.[\s\n]+Government[\s\n]+\(USG\)[\s\n]+Information[\s\n]+System[\s\n]+\(IS\)[\s\n]+that[\s\n]+is[\s\n]+provided[\s\n]+for[\s\n]+USG\-authorized[\s\n]+use[\s\n]+only\.[\s\n]+By[\s\n]+using[\s\n]+this[\s\n]+IS[\s\n]+\(which[\s\n]+includes[\s\n]+any[\s\n]+device[\s\n]+attached[\s\n]+to[\s\n]+this[\s\n]+IS\)\,[\s\n]+you[\s\n]+consent[\s\n]+to[\s\n]+the[\s\n]+following[\s\n]+conditions\:(?:[\n]+|(?:\\n)+)\-The[\s\n]+USG[\s\n]+routinely[\s\n]+intercepts[\s\n]+and[\s\n]+monitors[\s\n]+communications[\s\n]+on[\s\n]+this[\s\n]+IS[\s\n]+for[\s\n]+purposes[\s\n]+including\,[\s\n]+but[\s\n]+not[\s\n]+limited[\s\n]+to\,[\s\n]+penetration[\s\n]+testing\,[\s\n]+COMSEC[\s\n]+monitoring\,[\s\n]+network[\s\n]+operations[\s\n]+and[\s\n]+defense\,[\s\n]+personnel[\s\n]+misconduct[\s\n]+\(PM\)\,[\s\n]+law[\s\n]+enforcement[\s\n]+\(LE\)\,[\s\n]+and[\s\n]+counterintelligence[\s\n]+\(CI\)[\s\n]+investigations\.(?:[\n]+|(?:\\n)+)\-At[\s\n]+any[\s\n]+time\,[\s\n]+the[\s\n]+USG[\s\n]+may[\s\n]+inspect[\s\n]+and[\s\n]+seize[\s\n]+data[\s\n]+stored[\s\n]+on[\s\n]+this[\s\n]+IS\.(?:[\n]+|(?:\\n)+)\-Communications[\s\n]+using\,[\s\n]+or[\s\n]+data[\s\n]+stored[\s\n]+on\,[\s\n]+this[\s\n]+IS[\s\n]+are[\s\n]+not[\s\n]+private\,[\s\n]+are[\s\n]+subject[\s\n]+to[\s\n]+routine[\s\n]+monitoring\,[\s\n]+interception\,[\s\n]+and[\s\n]+search\,[\s\n]+and[\s\n]+may[\s\n]+be[\s\n]+disclosed[\s\n]+or[\s\n]+used[\s\n]+for[\s\n]+any[\s\n]+USG\-authorized[\s\n]+purpose\.(?:[\n]+|(?:\\n)+)\-This[\s\n]+IS[\s\n]+includes[\s\n]+security[\s\n]+measures[\s\n]+\(e\.g\.\,[\s\n]+authentication[\s\n]+and[\s\n]+access[\s\n]+controls\)[\s\n]+to[\s\n]+protect[\s\n]+USG[\s\n]+interests\-\-not[\s\n]+for[\s\n]+your[\s\n]+personal[\s\n]+benefit[\s\n]+or[\s\n]+privacy\.(?:[\n]+|(?:\\n)+)\-Notwithstanding[\s\n]+the[\s\n]+above\,[\s\n]+using[\s\n]+this[\s\n]+IS[\s\n]+does[\s\n]+not[\s\n]+constitute[\s\n]+consent[\s\n]+to[\s\n]+PM\,[\s\n]+LE[\s\n]+or[\s\n]+CI[\s\n]+investigative[\s\n]+searching[\s\n]+or[\s\n]+monitoring[\s\n]+of[\s\n]+the[\s\n]+content[\s\n]+of[\s\n]+privileged[\s\n]+communications\,[\s\n]+or[\s\n]+work[\s\n]+product\,[\s\n]+related[\s\n]+to[\s\n]+personal[\s\n]+representation[\s\n]+or[\s\n]+services[\s\n]+by[\s\n]+attorneys\,[\s\n]+psychotherapists\,[\s\n]+or[\s\n]+clergy\,[\s\n]+and[\s\n]+their[\s\n]+assistants\.[\s\n]+Such[\s\n]+communications[\s\n]+and[\s\n]+work[\s\n]+product[\s\n]+are[\s\n]+private[\s\n]+and[\s\n]+confidential\.[\s\n]+See[\s\n]+User[\s\n]+Agreement[\s\n]+for[\s\n]+details\.|I've[\s\n]+read[\s\n]+\&[\s\n]+consent[\s\n]+to[\s\n]+terms[\s\n]+in[\s\n]+IS[\s\n]+user[\s\n]+agreem't\.)$ + ^You[\s\n]+are[\s\n]+accessing[\s\n]+a[\s\n]+U\.S\.[\s\n]+Government[\s\n]+\(USG\)[\s\n]+Information[\s\n]+System[\s\n]+\(IS\)[\s\n]+that[\s\n]+is[\s\n]+provided[\s\n]+for[\s\n]+USG\-authorized[\s\n]+use[\s\n]+only\.[\s\n]+By[\s\n]+using[\s\n]+this[\s\n]+IS[\s\n]+\(which[\s\n]+includes[\s\n]+any[\s\n]+device[\s\n]+attached[\s\n]+to[\s\n]+this[\s\n]+IS\)\,[\s\n]+you[\s\n]+consent[\s\n]+to[\s\n]+the[\s\n]+following[\s\n]+conditions\:(?:[\n]+|(?:\\n)+)\-The[\s\n]+USG[\s\n]+routinely[\s\n]+intercepts[\s\n]+and[\s\n]+monitors[\s\n]+communications[\s\n]+on[\s\n]+this[\s\n]+IS[\s\n]+for[\s\n]+purposes[\s\n]+including\,[\s\n]+but[\s\n]+not[\s\n]+limited[\s\n]+to\,[\s\n]+penetration[\s\n]+testing\,[\s\n]+COMSEC[\s\n]+monitoring\,[\s\n]+network[\s\n]+operations[\s\n]+and[\s\n]+defense\,[\s\n]+personnel[\s\n]+misconduct[\s\n]+\(PM\)\,[\s\n]+law[\s\n]+enforcement[\s\n]+\(LE\)\,[\s\n]+and[\s\n]+counterintelligence[\s\n]+\(CI\)[\s\n]+investigations\.(?:[\n]+|(?:\\n)+)\-At[\s\n]+any[\s\n]+time\,[\s\n]+the[\s\n]+USG[\s\n]+may[\s\n]+inspect[\s\n]+and[\s\n]+seize[\s\n]+data[\s\n]+stored[\s\n]+on[\s\n]+this[\s\n]+IS\.(?:[\n]+|(?:\\n)+)\-Communications[\s\n]+using\,[\s\n]+or[\s\n]+data[\s\n]+stored[\s\n]+on\,[\s\n]+this[\s\n]+IS[\s\n]+are[\s\n]+not[\s\n]+private\,[\s\n]+are[\s\n]+subject[\s\n]+to[\s\n]+routine[\s\n]+monitoring\,[\s\n]+interception\,[\s\n]+and[\s\n]+search\,[\s\n]+and[\s\n]+may[\s\n]+be[\s\n]+disclosed[\s\n]+or[\s\n]+used[\s\n]+for[\s\n]+any[\s\n]+USG\-authorized[\s\n]+purpose\.(?:[\n]+|(?:\\n)+)\-This[\s\n]+IS[\s\n]+includes[\s\n]+security[\s\n]+measures[\s\n]+\(e\.g\.\,[\s\n]+authentication[\s\n]+and[\s\n]+access[\s\n]+controls\)[\s\n]+to[\s\n]+protect[\s\n]+USG[\s\n]+interests\-\-not[\s\n]+for[\s\n]+your[\s\n]+personal[\s\n]+benefit[\s\n]+or[\s\n]+privacy\.(?:[\n]+|(?:\\n)+)\-Notwithstanding[\s\n]+the[\s\n]+above\,[\s\n]+using[\s\n]+this[\s\n]+IS[\s\n]+does[\s\n]+not[\s\n]+constitute[\s\n]+consent[\s\n]+to[\s\n]+PM\,[\s\n]+LE[\s\n]+or[\s\n]+CI[\s\n]+investigative[\s\n]+searching[\s\n]+or[\s\n]+monitoring[\s\n]+of[\s\n]+the[\s\n]+content[\s\n]+of[\s\n]+privileged[\s\n]+communications\,[\s\n]+or[\s\n]+work[\s\n]+product\,[\s\n]+related[\s\n]+to[\s\n]+personal[\s\n]+representation[\s\n]+or[\s\n]+services[\s\n]+by[\s\n]+attorneys\,[\s\n]+psychotherapists\,[\s\n]+or[\s\n]+clergy\,[\s\n]+and[\s\n]+their[\s\n]+assistants\.[\s\n]+Such[\s\n]+communications[\s\n]+and[\s\n]+work[\s\n]+product[\s\n]+are[\s\n]+private[\s\n]+and[\s\n]+confidential\.[\s\n]+See[\s\n]+User[\s\n]+Agreement[\s\n]+for[\s\n]+details\.$ + ^I've[\s\n]+read[\s\n]+\&[\s\n]+consent[\s\n]+to[\s\n]+terms[\s\n]+in[\s\n]+IS[\s\n]+user[\s\n]+agreem't\.$ + ^Use[\s\n]+of[\s\n]+this[\s\n]+or[\s\n]+any[\s\n]+other[\s\n]+DoD[\s\n]+interest[\s\n]+computer[\s\n]+system[\s\n]+constitutes[\s\n]+consent[\s\n]+to[\s\n]+monitoring[\s\n]+at[\s\n]+all[\s\n]+times\.[\s\n]+This[\s\n]+is[\s\n]+a[\s\n]+DoD[\s\n]+interest[\s\n]+computer[\s\n]+system\.[\s\n]+All[\s\n]+DoD[\s\n]+interest[\s\n]+computer[\s\n]+systems[\s\n]+and[\s\n]+related[\s\n]+equipment[\s\n]+are[\s\n]+intended[\s\n]+for[\s\n]+the[\s\n]+communication\,[\s\n]+transmission\,[\s\n]+processing\,[\s\n]+and[\s\n]+storage[\s\n]+of[\s\n]+official[\s\n]+U\.S\.[\s\n]+Government[\s\n]+or[\s\n]+other[\s\n]+authorized[\s\n]+information[\s\n]+only\.[\s\n]+All[\s\n]+DoD[\s\n]+interest[\s\n]+computer[\s\n]+systems[\s\n]+are[\s\n]+subject[\s\n]+to[\s\n]+monitoring[\s\n]+at[\s\n]+all[\s\n]+times[\s\n]+to[\s\n]+ensure[\s\n]+proper[\s\n]+functioning[\s\n]+of[\s\n]+equipment[\s\n]+and[\s\n]+systems[\s\n]+including[\s\n]+security[\s\n]+devices[\s\n]+and[\s\n]+systems\,[\s\n]+to[\s\n]+prevent[\s\n]+unauthorized[\s\n]+use[\s\n]+and[\s\n]+violations[\s\n]+of[\s\n]+statutes[\s\n]+and[\s\n]+security[\s\n]+regulations\,[\s\n]+to[\s\n]+deter[\s\n]+criminal[\s\n]+activity\,[\s\n]+and[\s\n]+for[\s\n]+other[\s\n]+similar[\s\n]+purposes\.[\s\n]+Any[\s\n]+user[\s\n]+of[\s\n]+a[\s\n]+DoD[\s\n]+interest[\s\n]+computer[\s\n]+system[\s\n]+should[\s\n]+be[\s\n]+aware[\s\n]+that[\s\n]+any[\s\n]+information[\s\n]+placed[\s\n]+in[\s\n]+the[\s\n]+system[\s\n]+is[\s\n]+subject[\s\n]+to[\s\n]+monitoring[\s\n]+and[\s\n]+is[\s\n]+not[\s\n]+subject[\s\n]+to[\s\n]+any[\s\n]+expectation[\s\n]+of[\s\n]+privacy\.[\s\n]+If[\s\n]+monitoring[\s\n]+of[\s\n]+this[\s\n]+or[\s\n]+any[\s\n]+other[\s\n]+DoD[\s\n]+interest[\s\n]+computer[\s\n]+system[\s\n]+reveals[\s\n]+possible[\s\n]+evidence[\s\n]+of[\s\n]+violation[\s\n]+of[\s\n]+criminal[\s\n]+statutes\,[\s\n]+this[\s\n]+evidence[\s\n]+and[\s\n]+any[\s\n]+other[\s\n]+related[\s\n]+information\,[\s\n]+including[\s\n]+identification[\s\n]+information[\s\n]+about[\s\n]+the[\s\n]+user\,[\s\n]+may[\s\n]+be[\s\n]+provided[\s\n]+to[\s\n]+law[\s\n]+enforcement[\s\n]+officials\.[\s\n]+If[\s\n]+monitoring[\s\n]+of[\s\n]+this[\s\n]+or[\s\n]+any[\s\n]+other[\s\n]+DoD[\s\n]+interest[\s\n]+computer[\s\n]+systems[\s\n]+reveals[\s\n]+violations[\s\n]+of[\s\n]+security[\s\n]+regulations[\s\n]+or[\s\n]+unauthorized[\s\n]+use\,[\s\n]+employees[\s\n]+who[\s\n]+violate[\s\n]+security[\s\n]+regulations[\s\n]+or[\s\n]+make[\s\n]+unauthorized[\s\n]+use[\s\n]+of[\s\n]+DoD[\s\n]+interest[\s\n]+computer[\s\n]+systems[\s\n]+are[\s\n]+subject[\s\n]+to[\s\n]+appropriate[\s\n]+disciplinary[\s\n]+action\.[\s\n]+Use[\s\n]+of[\s\n]+this[\s\n]+or[\s\n]+any[\s\n]+other[\s\n]+DoD[\s\n]+interest[\s\n]+computer[\s\n]+system[\s\n]+constitutes[\s\n]+consent[\s\n]+to[\s\n]+monitoring[\s\n]+at[\s\n]+all[\s\n]+times\.$ + ^\-\-[\s\n]+WARNING[\s\n]+\-\-[\s\n]+This[\s\n]+system[\s\n]+is[\s\n]+for[\s\n]+the[\s\n]+use[\s\n]+of[\s\n]+authorized[\s\n]+users[\s\n]+only\.[\s\n]+Individuals[\s\n]+using[\s\n]+this[\s\n]+computer[\s\n]+system[\s\n]+without[\s\n]+authority[\s\n]+or[\s\n]+in[\s\n]+excess[\s\n]+of[\s\n]+their[\s\n]+authority[\s\n]+are[\s\n]+subject[\s\n]+to[\s\n]+having[\s\n]+all[\s\n]+their[\s\n]+activities[\s\n]+on[\s\n]+this[\s\n]+system[\s\n]+monitored[\s\n]+and[\s\n]+recorded[\s\n]+by[\s\n]+system[\s\n]+personnel\.[\s\n]+Anyone[\s\n]+using[\s\n]+this[\s\n]+system[\s\n]+expressly[\s\n]+consents[\s\n]+to[\s\n]+such[\s\n]+monitoring[\s\n]+and[\s\n]+is[\s\n]+advised[\s\n]+that[\s\n]+if[\s\n]+such[\s\n]+monitoring[\s\n]+reveals[\s\n]+possible[\s\n]+evidence[\s\n]+of[\s\n]+criminal[\s\n]+activity[\s\n]+system[\s\n]+personal[\s\n]+may[\s\n]+provide[\s\n]+the[\s\n]+evidence[\s\n]+of[\s\n]+such[\s\n]+monitoring[\s\n]+to[\s\n]+law[\s\n]+enforcement[\s\n]+officials\.$ + ^Authorized[\s\n]+uses[\s\n]+only\.[\s\n]+All[\s\n]+activity[\s\n]+may[\s\n]+be[\s\n]+monitored[\s\n]+and[\s\n]+reported\.$ + + + Modify the System Login Banner + +To configure the system login banner edit /etc/issue. Replace the +default text with a message compliant with the local site policy or a legal +disclaimer. + + +The DoD required text is either: + +You are accessing a U.S. Government (USG) Information System (IS) that +is provided for USG-authorized use only. By using this IS (which includes +any device attached to this IS), you consent to the following conditions: +-The USG routinely intercepts and monitors communications on this IS +for purposes including, but not limited to, penetration testing, COMSEC +monitoring, network operations and defense, personnel misconduct (PM), law +enforcement (LE), and counterintelligence (CI) investigations. +-At any time, the USG may inspect and seize data stored on this IS. +-Communications using, or data stored on, this IS are not private, +are subject to routine monitoring, interception, and search, and may be +disclosed or used for any USG-authorized purpose. +-This IS includes security measures (e.g., authentication and access +controls) to protect USG interests -- not for your personal benefit or +privacy. +-Notwithstanding the above, using this IS does not constitute consent +to PM, LE or CI investigative searching or monitoring of the content of +privileged communications, or work product, related to personal +representation or services by attorneys, psychotherapists, or clergy, and +their assistants. Such communications and work product are private and +confidential. See User Agreement for details. + +OR: + +I've read & consent to terms in IS user agreem't. + 1 + 12 + 15 + 16 + DSS05.04 + DSS05.10 + DSS06.10 + 3.1.9 + CCI-000048 + CCI-000050 + CCI-001384 + CCI-001385 + CCI-001386 + CCI-001387 + CCI-001388 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + A.18.1.4 + A.9.2.1 + A.9.2.4 + A.9.3.1 + A.9.4.2 + A.9.4.3 + AC-8(a) + AC-8(c) + PR.AC-7 + FMT_MOF_EXT.1 + SRG-OS-000023-GPOS-00006 + SRG-OS-000228-GPOS-00088 + SRG-OS-000023-VMM-000060 + SRG-OS-000024-VMM-000070 + RHEL-08-010060 + 1.7.2 + SV-230227r627750_rule + Display of a standardized and approved use notification before granting +access to the operating system ensures privacy and security notification +verbiage used is consistent with applicable federal laws, Executive Orders, +directives, policies, regulations, standards, and guidance. + +System use notifications are required only for access via login interfaces +with human users and are not required when such human interfaces do not +exist. + + CCE-80763-6 + + + + + + + + + + + + Modify the System Message of the Day Banner + To configure the system message banner edit /etc/motd. Replace the +default text with a message compliant with the local site policy or a legal +disclaimer. + +The DoD required text is either: + +You are accessing a U.S. Government (USG) Information System (IS) that +is provided for USG-authorized use only. By using this IS (which includes +any device attached to this IS), you consent to the following conditions: +-The USG routinely intercepts and monitors communications on this IS +for purposes including, but not limited to, penetration testing, COMSEC +monitoring, network operations and defense, personnel misconduct (PM), law +enforcement (LE), and counterintelligence (CI) investigations. +-At any time, the USG may inspect and seize data stored on this IS. +-Communications using, or data stored on, this IS are not private, +are subject to routine monitoring, interception, and search, and may be +disclosed or used for any USG-authorized purpose. +-This IS includes security measures (e.g., authentication and access +controls) to protect USG interests -- not for your personal benefit or +privacy. +-Notwithstanding the above, using this IS does not constitute consent +to PM, LE or CI investigative searching or monitoring of the content of +privileged communications, or work product, related to personal +representation or services by attorneys, psychotherapists, or clergy, and +their assistants. Such communications and work product are private and +confidential. See User Agreement for details. + +OR: + +I've read & consent to terms in IS user agreem't. + 1.7.1 + Display of a standardized and approved use notification before granting +access to the operating system ensures privacy and security notification +verbiage used is consistent with applicable federal laws, Executive Orders, +directives, policies, regulations, standards, and guidance. + +System use notifications are required only for access via login interfaces +with human users and are not required when such human interfaces do not +exist. + + CCE-83496-0 + + + + + + + + + + + + Verify Group Ownership of System Login Banner + +To properly set the group owner of /etc/issue, run the command: +$ sudo chgrp root /etc/issue + 1.7.5 + Display of a standardized and approved use notification before granting +access to the operating system ensures privacy and security notification +verbiage used is consistent with applicable federal laws, Executive Orders, +directives, policies, regulations, standards, and guidance. +Proper group ownership will ensure that only root user can modify the banner. + CCE-83708-8 + - name: Test for existence /etc/issue + stat: + path: /etc/issue + register: file_exists + tags: + - CCE-83708-8 + - configure_strategy + - file_groupowner_etc_issue + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure group owner 0 on /etc/issue + file: + path: /etc/issue + group: '0' + when: file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-83708-8 + - configure_strategy + - file_groupowner_etc_issue + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + + + +chgrp 0 /etc/issue + + + + + + + + + + Verify Group Ownership of Message of the Day Banner + +To properly set the group owner of /etc/motd, run the command: +$ sudo chgrp root /etc/motd + 1.7.4 + Display of a standardized and approved use notification before granting +access to the operating system ensures privacy and security notification +verbiage used is consistent with applicable federal laws, Executive Orders, +directives, policies, regulations, standards, and guidance. +Proper group ownership will ensure that only root user can modify the banner. + CCE-83728-6 + - name: Test for existence /etc/motd + stat: + path: /etc/motd + register: file_exists + tags: + - CCE-83728-6 + - configure_strategy + - file_groupowner_etc_motd + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure group owner 0 on /etc/motd + file: + path: /etc/motd + group: '0' + when: file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-83728-6 + - configure_strategy + - file_groupowner_etc_motd + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + + + +chgrp 0 /etc/motd + + + + + + + + + + Verify ownership of System Login Banner + +To properly set the owner of /etc/issue, run the command: +$ sudo chown root /etc/issue + 1.7.5 + Display of a standardized and approved use notification before granting +access to the operating system ensures privacy and security notification +verbiage used is consistent with applicable federal laws, Executive Orders, +directives, policies, regulations, standards, and guidance. +Proper ownership will ensure that only root user can modify the banner. + CCE-83718-7 + - name: Test for existence /etc/issue + stat: + path: /etc/issue + register: file_exists + tags: + - CCE-83718-7 + - configure_strategy + - file_owner_etc_issue + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure owner 0 on /etc/issue + file: + path: /etc/issue + owner: '0' + when: file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-83718-7 + - configure_strategy + - file_owner_etc_issue + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + + + +chown 0 /etc/issue + + + + + + + + + + Verify ownership of Message of the Day Banner + +To properly set the owner of /etc/motd, run the command: +$ sudo chown root /etc/motd + 1.7.4 + Display of a standardized and approved use notification before granting +access to the operating system ensures privacy and security notification +verbiage used is consistent with applicable federal laws, Executive Orders, +directives, policies, regulations, standards, and guidance. +Proper ownership will ensure that only root user can modify the banner. + CCE-83738-5 + - name: Test for existence /etc/motd + stat: + path: /etc/motd + register: file_exists + tags: + - CCE-83738-5 + - configure_strategy + - file_owner_etc_motd + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure owner 0 on /etc/motd + file: + path: /etc/motd + owner: '0' + when: file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-83738-5 + - configure_strategy + - file_owner_etc_motd + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + + + +chown 0 /etc/motd + + + + + + + + + + Verify permissions on System Login Banner + +To properly set the permissions of /etc/issue, run the command: +$ sudo chmod 0644 /etc/issue + 1.7.5 + Display of a standardized and approved use notification before granting +access to the operating system ensures privacy and security notification +verbiage used is consistent with applicable federal laws, Executive Orders, +directives, policies, regulations, standards, and guidance. +Proper permissions will ensure that only root user can modify the banner. + CCE-83348-3 + - name: Test for existence /etc/issue + stat: + path: /etc/issue + register: file_exists + tags: + - CCE-83348-3 + - configure_strategy + - file_permissions_etc_issue + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure permission u-xs,g-xws,o-xwt on /etc/issue + file: + path: /etc/issue + mode: u-xs,g-xws,o-xwt + when: file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-83348-3 + - configure_strategy + - file_permissions_etc_issue + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + + + + +chmod u-xs,g-xws,o-xwt /etc/issue + + + + + + + + + + Verify permissions on Message of the Day Banner + +To properly set the permissions of /etc/motd, run the command: +$ sudo chmod 0644 /etc/motd + 1.7.4 + Display of a standardized and approved use notification before granting +access to the operating system ensures privacy and security notification +verbiage used is consistent with applicable federal laws, Executive Orders, +directives, policies, regulations, standards, and guidance. +Proper permissions will ensure that only root user can modify the banner. + CCE-83338-4 + - name: Test for existence /etc/motd + stat: + path: /etc/motd + register: file_exists + tags: + - CCE-83338-4 + - configure_strategy + - file_permissions_etc_motd + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure permission u-xs,g-xws,o-xwt on /etc/motd + file: + path: /etc/motd + mode: u-xs,g-xws,o-xwt + when: file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-83338-4 + - configure_strategy + - file_permissions_etc_motd + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + + + + +chmod u-xs,g-xws,o-xwt /etc/motd + + + + + + + + + + Implement a GUI Warning Banner + In the default graphical environment, users logging +directly into the system are greeted with a login screen provided +by the GNOME Display Manager (GDM). The warning banner should be +displayed in this graphical environment for these users. +The following sections describe how to configure the GDM login +banner. + + + Enable GNOME3 Login Warning Banner + In the default graphical environment, displaying a login warning banner +in the GNOME Display Manager's login screen can be enabled on the login +screen by setting banner-message-enable to true. + +To enable, add or edit banner-message-enable to +/etc/dconf/db/gdm.d/00-security-settings. For example: +[org/gnome/login-screen] +banner-message-enable=true +Once the setting has been added, add a lock to +/etc/dconf/db/gdm.d/locks/00-security-settings-lock to prevent user modification. +For example: +/org/gnome/login-screen/banner-message-enable +After the settings have been set, run dconf update. +The banner text must also be set. + 1 + 12 + 15 + 16 + DSS05.04 + DSS05.10 + DSS06.10 + 3.1.9 + CCI-000048 + CCI-000050 + CCI-001384 + CCI-001385 + CCI-001386 + CCI-001387 + CCI-001388 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + A.18.1.4 + A.9.2.1 + A.9.2.4 + A.9.3.1 + A.9.4.2 + A.9.4.3 + AC-8(a) + AC-8(b) + AC-8(c) + PR.AC-7 + FMT_MOF_EXT.1 + SRG-OS-000023-GPOS-00006 + SRG-OS-000228-GPOS-00088 + RHEL-08-010049 + 1.8.2 + SV-244519r743806_rule + Display of a standardized and approved use notification before granting access to the operating system +ensures privacy and security notification verbiage used is consistent with applicable federal laws, +Executive Orders, directives, policies, regulations, standards, and guidance. + +For U.S. Government systems, system use notifications are required only for access via login interfaces +with human users and are not required when such human interfaces do not exist. + CCE-80768-5 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80768-5 + - DISA-STIG-RHEL-08-010049 + - NIST-800-171-3.1.9 + - NIST-800-53-AC-8(a) + - NIST-800-53-AC-8(b) + - NIST-800-53-AC-8(c) + - dconf_gnome_banner_enabled + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Enable GNOME3 Login Warning Banner + ini_file: + dest: /etc/dconf/db/gdm.d/00-security-settings + section: org/gnome/login-screen + option: banner-message-enable + value: 'true' + create: true + no_extra_spaces: true + when: '"gdm" in ansible_facts.packages' + tags: + - CCE-80768-5 + - DISA-STIG-RHEL-08-010049 + - NIST-800-171-3.1.9 + - NIST-800-53-AC-8(a) + - NIST-800-53-AC-8(b) + - NIST-800-53-AC-8(c) + - dconf_gnome_banner_enabled + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Prevent user modification of GNOME banner-message-enabled + lineinfile: + path: /etc/dconf/db/gdm.d/locks/00-security-settings-lock + regexp: ^/org/gnome/login-screen/banner-message-enable$ + line: /org/gnome/login-screen/banner-message-enable + create: true + when: '"gdm" in ansible_facts.packages' + tags: + - CCE-80768-5 + - DISA-STIG-RHEL-08-010049 + - NIST-800-171-3.1.9 + - NIST-800-53-AC-8(a) + - NIST-800-53-AC-8(b) + - NIST-800-53-AC-8(c) + - dconf_gnome_banner_enabled + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Dconf Update + command: dconf update + when: '"gdm" in ansible_facts.packages' + tags: + - CCE-80768-5 + - DISA-STIG-RHEL-08-010049 + - NIST-800-171-3.1.9 + - NIST-800-53-AC-8(a) + - NIST-800-53-AC-8(b) + - NIST-800-53-AC-8(c) + - dconf_gnome_banner_enabled + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q gdm; then + +# Check for setting in any of the DConf db directories +# If files contain ibus or distro, ignore them. +# The assignment assumes that individual filenames don't contain : +readarray -t SETTINGSFILES < <(grep -r "\\[org/gnome/login-screen\\]" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +DCONFFILE="/etc/dconf/db/gdm.d/00-security-settings" +DBDIR="/etc/dconf/db/gdm.d" + +mkdir -p "${DBDIR}" + +if [ "${#SETTINGSFILES[@]}" -eq 0 ] +then + [ ! -z ${DCONFFILE} ] || echo "" >> ${DCONFFILE} + printf '%s\n' "[org/gnome/login-screen]" >> ${DCONFFILE} + printf '%s=%s\n' "banner-message-enable" "true" >> ${DCONFFILE} +else + escaped_value="$(sed -e 's/\\/\\\\/g' <<< "true")" + if grep -q "^\\s*banner-message-enable\\s*=" "${SETTINGSFILES[@]}" + then + + sed -i "s/\\s*banner-message-enable\\s*=\\s*.*/banner-message-enable=${escaped_value}/g" "${SETTINGSFILES[@]}" + else + sed -i "\\|\\[org/gnome/login-screen\\]|a\\banner-message-enable=${escaped_value}" "${SETTINGSFILES[@]}" + fi +fi + +dconf update +# Check for setting in any of the DConf db directories +LOCKFILES=$(grep -r "^/org/gnome/login-screen/banner-message-enable$" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +LOCKSFOLDER="/etc/dconf/db/gdm.d/locks" + +mkdir -p "${LOCKSFOLDER}" + +if [[ -z "${LOCKFILES}" ]] +then + echo "/org/gnome/login-screen/banner-message-enable" >> "/etc/dconf/db/gdm.d/locks/00-security-settings-lock" +fi + +dconf update + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Set the GNOME3 Login Warning Banner Text + In the default graphical environment, configuring the login warning banner text +in the GNOME Display Manager's login screen can be configured on the login +screen by setting banner-message-text to 'APPROVED_BANNER' +where APPROVED_BANNER is the approved banner for your environment. + +To enable, add or edit banner-message-text to + +/etc/dconf/db/gdm.d/00-security-settings. For example: +[org/gnome/login-screen] +banner-message-text='APPROVED_BANNER' +Once the setting has been added, add a lock to +/etc/dconf/db/gdm.d/locks/00-security-settings-lock to prevent user modification. +For example: +/org/gnome/login-screen/banner-message-text + +After the settings have been set, run dconf update. +When entering a warning banner that spans several lines, remember +to begin and end the string with ' and use \n for new lines. + 1 + 12 + 15 + 16 + DSS05.04 + DSS05.10 + DSS06.10 + 3.1.9 + CCI-000048 + CCI-001384 + CCI-001385 + CCI-001386 + CCI-001387 + CCI-001388 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + A.18.1.4 + A.9.2.1 + A.9.2.4 + A.9.3.1 + A.9.4.2 + A.9.4.3 + AC-8(a) + AC-8(c) + PR.AC-7 + FMT_MOF_EXT.1 + SRG-OS-000023-GPOS-00006 + SRG-OS-000228-GPOS-00088 + RHEL-08-010050 + 1.8.2 + SV-230226r743916_rule + An appropriate warning message reinforces policy awareness during the logon +process and facilitates possible legal action against attackers. + CCE-80770-1 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80770-1 + - DISA-STIG-RHEL-08-010050 + - NIST-800-171-3.1.9 + - NIST-800-53-AC-8(a) + - NIST-800-53-AC-8(c) + - dconf_gnome_login_banner_text + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy +- name: XCCDF Value login_banner_text # promote to variable + set_fact: + login_banner_text: !!str + tags: + - always + +- name: Set the GNOME3 Login Warning Banner Text + file: + path: /etc/dconf/db/{{ item }} + owner: root + group: root + mode: 493 + state: directory + with_items: + - gdm.d + - gdm.d/locks + when: '"gdm" in ansible_facts.packages' + tags: + - CCE-80770-1 + - DISA-STIG-RHEL-08-010050 + - NIST-800-171-3.1.9 + - NIST-800-53-AC-8(a) + - NIST-800-53-AC-8(c) + - dconf_gnome_login_banner_text + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Set the GNOME3 Login Warning Banner Text + file: + path: /etc/dconf/db/gdm.d/{{ item }} + owner: root + group: root + mode: 420 + state: touch + with_items: + - 00-security-settings + - locks/00-security-settings-lock + when: '"gdm" in ansible_facts.packages' + tags: + - CCE-80770-1 + - DISA-STIG-RHEL-08-010050 + - NIST-800-171-3.1.9 + - NIST-800-53-AC-8(a) + - NIST-800-53-AC-8(c) + - dconf_gnome_login_banner_text + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Set the GNOME3 Login Warning Banner Text + ini_file: + dest: /etc/dconf/db/gdm.d/00-security-settings + section: org/gnome/login-screen + option: banner-message-text + value: '''{{ login_banner_text | regex_replace("^\^(.*)\$$", "\1") | regex_replace("^\((.*\.)\|.*\)$", + "\1") | regex_replace("\[\\s\\n\]\+"," ") | regex_replace("\(\?:\[\\n\]\+\|\(\?:\\\\n\)\+\)", + "(n)*") | regex_replace("\\", "") | regex_replace("\(n\)\*", "\\n") }}''' + create: true + no_extra_spaces: true + when: '"gdm" in ansible_facts.packages' + tags: + - CCE-80770-1 + - DISA-STIG-RHEL-08-010050 + - NIST-800-171-3.1.9 + - NIST-800-53-AC-8(a) + - NIST-800-53-AC-8(c) + - dconf_gnome_login_banner_text + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Prevent user modification of the GNOME3 Login Warning Banner Text + lineinfile: + path: /etc/dconf/db/gdm.d/locks/00-security-settings-lock + regexp: ^/org/gnome/login-screen/banner-message-text$ + line: /org/gnome/login-screen/banner-message-text + create: true + state: present + when: '"gdm" in ansible_facts.packages' + tags: + - CCE-80770-1 + - DISA-STIG-RHEL-08-010050 + - NIST-800-171-3.1.9 + - NIST-800-53-AC-8(a) + - NIST-800-53-AC-8(c) + - dconf_gnome_login_banner_text + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Dconf Update + command: dconf update + when: '"gdm" in ansible_facts.packages' + tags: + - CCE-80770-1 + - DISA-STIG-RHEL-08-010050 + - NIST-800-171-3.1.9 + - NIST-800-53-AC-8(a) + - NIST-800-53-AC-8(c) + - dconf_gnome_login_banner_text + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q gdm; then + +login_banner_text='' + + +# Multiple regexes transform the banner regex into a usable banner +# 0 - Remove anchors around the banner text +login_banner_text=$(echo "$login_banner_text" | sed 's/^\^\(.*\)\$$/\1/g') +# 1 - Keep only the first banners if there are multiple +# (dod_banners contains the long and short banner) +login_banner_text=$(echo "$login_banner_text" | sed 's/^(\(.*\.\)|.*)$/\1/g') +# 2 - Add spaces ' '. (Transforms regex for "space or newline" into a " ") +login_banner_text=$(echo "$login_banner_text" | sed 's/\[\\s\\n\]+/ /g') +# 3 - Adds newline "tokens". (Transforms "(?:\[\\n\]+|(?:\\n)+)" into "(n)*") +login_banner_text=$(echo "$login_banner_text" | sed 's/(?:\[\\n\]+|(?:\\\\n)+)/(n)*/g') +# 4 - Remove any leftover backslash. (From any parethesis in the banner, for example). +login_banner_text=$(echo "$login_banner_text" | sed 's/\\//g') +# 5 - Removes the newline "token." (Transforms them into newline escape sequences "\n"). +# ( Needs to be done after 4, otherwise the escapce sequence will become just "n". +login_banner_text=$(echo "$login_banner_text" | sed 's/(n)\*/\\n/g') + +# Check for setting in any of the DConf db directories +# If files contain ibus or distro, ignore them. +# The assignment assumes that individual filenames don't contain : +readarray -t SETTINGSFILES < <(grep -r "\\[org/gnome/login-screen\\]" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +DCONFFILE="/etc/dconf/db/gdm.d/00-security-settings" +DBDIR="/etc/dconf/db/gdm.d" + +mkdir -p "${DBDIR}" + +if [ "${#SETTINGSFILES[@]}" -eq 0 ] +then + [ ! -z ${DCONFFILE} ] || echo "" >> ${DCONFFILE} + printf '%s\n' "[org/gnome/login-screen]" >> ${DCONFFILE} + printf '%s=%s\n' "banner-message-text" "'${login_banner_text}'" >> ${DCONFFILE} +else + escaped_value="$(sed -e 's/\\/\\\\/g' <<< "'${login_banner_text}'")" + if grep -q "^\\s*banner-message-text\\s*=" "${SETTINGSFILES[@]}" + then + + sed -i "s/\\s*banner-message-text\\s*=\\s*.*/banner-message-text=${escaped_value}/g" "${SETTINGSFILES[@]}" + else + sed -i "\\|\\[org/gnome/login-screen\\]|a\\banner-message-text=${escaped_value}" "${SETTINGSFILES[@]}" + fi +fi + +dconf update +# Check for setting in any of the DConf db directories +LOCKFILES=$(grep -r "^/org/gnome/login-screen/banner-message-text$" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +LOCKSFOLDER="/etc/dconf/db/gdm.d/locks" + +mkdir -p "${LOCKSFOLDER}" + +if [[ -z "${LOCKFILES}" ]] +then + echo "/org/gnome/login-screen/banner-message-text" >> "/etc/dconf/db/gdm.d/locks/00-security-settings-lock" +fi + +dconf update + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + + + Protect Accounts by Configuring PAM + PAM, or Pluggable Authentication Modules, is a system +which implements modular authentication for Linux programs. PAM provides +a flexible and configurable architecture for authentication, and it should be configured +to minimize exposure to unnecessary risk. This section contains +guidance on how to accomplish that. + +PAM is implemented as a set of shared objects which are +loaded and invoked whenever an application wishes to authenticate a +user. Typically, the application must be running as root in order +to take advantage of PAM, because PAM's modules often need to be able +to access sensitive stores of account information, such as /etc/shadow. +Traditional privileged network listeners +(e.g. sshd) or SUID programs (e.g. sudo) already meet this +requirement. An SUID root application, userhelper, is provided so +that programs which are not SUID or privileged themselves can still +take advantage of PAM. + +PAM looks in the directory /etc/pam.d for +application-specific configuration information. For instance, if +the program login attempts to authenticate a user, then PAM's +libraries follow the instructions in the file /etc/pam.d/login +to determine what actions should be taken. + +One very important file in /etc/pam.d is +/etc/pam.d/system-auth. This file, which is included by +many other PAM configuration files, defines 'default' system authentication +measures. Modifying this file is a good way to make far-reaching +authentication changes, for instance when implementing a +centralized authentication service. + Be careful when making changes to PAM's configuration files. +The syntax for these files is complex, and modifications can +have unexpected consequences. The default configurations shipped +with applications should be sufficient for most users. + Running authconfig or system-config-authentication +will re-write the PAM configuration files, destroying any manually +made changes and replacing them with a series of system defaults. +One reference to the configuration file syntax can be found at + +https://fossies.org/linux/Linux-PAM-docs/doc/sag/Linux-PAM_SAG.pdf. + + Password Hashing algorithm + Specify the system default encryption algorithm for encrypting passwords. +Defines the value set as ENCRYPT_METHOD in /etc/login.defs. + SHA512 + SHA512 + SHA256 + + + remember + The last n passwords for each user are saved in +/etc/security/opasswd in order to force password change history and +keep the user from alternating between the same password too +frequently. + 0 + 10 + 24 + 2 + 4 + 5 + 5 + + + Install pam_pwquality Package + +The libpwquality package can be installed with the following command: + +$ sudo yum install libpwquality + CCI-000366 + SRG-OS-000480-GPOS-00225 + Use of a complex password helps to increase the time and resources required +to compromise the password. Password complexity, or strength, is a measure +of the effectiveness of a password in resisting attempts at guessing and +brute-force attacks. "pwquality" enforces complex password construction +configuration and has the ability to limit brute-force attacks on the system. + +package --add=libpwquality + + include install_libpwquality + +class install_libpwquality { + package { 'libpwquality': + ensure => 'installed', + } +} + + - name: Ensure libpwquality is installed + package: + name: libpwquality + state: present + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_pam_pwquality_installed + + +[[packages]] +name = "libpwquality" +version = "*" + + +if ! rpm -q --quiet "libpwquality" ; then + yum install -y "libpwquality" +fi + + + + + + + + + + Disallow Configuration to Bypass Password Requirements for Privilege Escalation + Verify the operating system is not configured to bypass password requirements for privilege +escalation. Check the configuration of the "/etc/pam.d/sudo" file with the following command: +$ sudo grep pam_succeed_if /etc/pam.d/sudo +If any occurrences of "pam_succeed_if" is returned from the command, this is a finding. + CCI-002038 + IA-11 + SRG-OS-000373-GPOS-00156 + SRG-OS-000373-GPOS-00157 + SRG-OS-000373-GPOS-00158 + Without re-authentication, users may access resources or perform tasks for which they do not +have authorization. When operating systems provide the capability to escalate a functional +capability, it is critical the user re-authenticate. + + + + + + + + + + Ensure PAM Displays Last Logon/Access Notification + To configure the system to notify users of last logon/access +using pam_lastlog, add or correct the pam_lastlog +settings in +/etc/pam.d/postlogin to read as follows: +session required pam_lastlog.so showfailed +And make sure that the silent option is not set for +pam_lastlog module. + 1 + 12 + 15 + 16 + 5.5.2 + DSS05.04 + DSS05.10 + DSS06.10 + CCI-000366 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + 0582 + 0584 + 05885 + 0586 + 0846 + 0957 + A.18.1.4 + A.9.2.1 + A.9.2.4 + A.9.3.1 + A.9.4.2 + A.9.4.3 + AC-9(1) + CM-6(a) + PR.AC-7 + Req-10.2.4 + SRG-OS-000480-GPOS-00227 + RHEL-08-020340 + SV-230381r627750_rule + Users need to be aware of activity that occurs regarding +their account. Providing users with information regarding the number +of unsuccessful attempts that were made to login to their account +allows the user to determine if any unauthorized activity has occurred +and gives them an opportunity to notify administrators. + + CCE-80788-3 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80788-3 + - CJIS-5.5.2 + - DISA-STIG-RHEL-08-020340 + - NIST-800-53-AC-9(1) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.4 + - configure_strategy + - display_login_attempts + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + +- name: Ensure PAM Displays Last Logon/Access Notification - Check if /etc/pam.d/postlogin + file is present + ansible.builtin.stat: + path: /etc/pam.d/postlogin + register: result_pam_file_present + when: '"pam" in ansible_facts.packages' + tags: + - CCE-80788-3 + - CJIS-5.5.2 + - DISA-STIG-RHEL-08-020340 + - NIST-800-53-AC-9(1) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.4 + - configure_strategy + - display_login_attempts + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + +- name: Ensure PAM Displays Last Logon/Access Notification - Check the proper remediation + for the system + block: + + - name: Ensure PAM Displays Last Logon/Access Notification - Define the PAM file + to be edited as a local fact + ansible.builtin.set_fact: + pam_file_path: /etc/pam.d/postlogin + + - name: Ensure PAM Displays Last Logon/Access Notification - Check if system relies + on authselect + ansible.builtin.stat: + path: /usr/bin/authselect + register: result_authselect_present + + - name: Ensure PAM Displays Last Logon/Access Notification - Remediate using authselect + block: + + - name: Ensure PAM Displays Last Logon/Access Notification - Check integrity of + authselect current profile + ansible.builtin.command: + cmd: authselect check + register: result_authselect_check_cmd + changed_when: false + ignore_errors: true + + - name: Ensure PAM Displays Last Logon/Access Notification - Informative message + based on the authselect integrity check result + ansible.builtin.assert: + that: + - result_authselect_check_cmd is success + fail_msg: + - authselect integrity check failed. Remediation aborted! + - This remediation could not be applied because an authselect profile was + not selected or the selected profile is not intact. + - It is not recommended to manually edit the PAM files when authselect tool + is available. + - In cases where the default authselect profile does not cover a specific + demand, a custom authselect profile is recommended. + success_msg: + - authselect integrity check passed + + - name: Ensure PAM Displays Last Logon/Access Notification - Get authselect current + profile + ansible.builtin.shell: + cmd: authselect current -r | awk '{ print $1 }' + register: result_authselect_profile + changed_when: false + when: + - result_authselect_check_cmd is success + + - name: Ensure PAM Displays Last Logon/Access Notification - Define the current + authselect profile as a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: '{{ result_authselect_profile.stdout }}' + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is match("custom/") + + - name: Ensure PAM Displays Last Logon/Access Notification - Define the new authselect + custom profile as a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: custom/hardening + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is not match("custom/") + + - name: Ensure PAM Displays Last Logon/Access Notification - Get authselect current + features to also enable them in the custom profile + ansible.builtin.shell: + cmd: authselect current | tail -n+3 | awk '{ print $2 }' + register: result_authselect_features + changed_when: false + when: + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + + - name: Ensure PAM Displays Last Logon/Access Notification - Check if any custom + profile with the same name was already created + ansible.builtin.stat: + path: /etc/authselect/{{ authselect_custom_profile }} + register: result_authselect_custom_profile_present + changed_when: false + when: + - authselect_current_profile is not match("custom/") + + - name: Ensure PAM Displays Last Logon/Access Notification - Create an authselect + custom profile based on the current profile + ansible.builtin.command: + cmd: authselect create-profile hardening -b {{ authselect_current_profile + }} + when: + - result_authselect_check_cmd is success + - authselect_current_profile is not match("custom/") + - not result_authselect_custom_profile_present.stat.exists + + - name: Ensure PAM Displays Last Logon/Access Notification - Ensure authselect + changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=before-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Ensure PAM Displays Last Logon/Access Notification - Ensure the authselect + custom profile is selected + ansible.builtin.command: + cmd: authselect select {{ authselect_custom_profile }} + register: result_pam_authselect_select_profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Ensure PAM Displays Last Logon/Access Notification - Restore the authselect + features in the custom profile + ansible.builtin.command: + cmd: authselect enable-feature {{ item }} + loop: '{{ result_authselect_features.stdout_lines }}' + register: result_pam_authselect_restore_features + when: + - result_authselect_profile is not skipped + - result_authselect_features is not skipped + - result_pam_authselect_select_profile is not skipped + + - name: Ensure PAM Displays Last Logon/Access Notification - Ensure authselect + changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=after-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - result_pam_authselect_restore_features is not skipped + + - name: Ensure PAM Displays Last Logon/Access Notification - Change the PAM file + to be edited according to the custom authselect profile + ansible.builtin.set_fact: + pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path + | basename }} + when: + - result_authselect_present.stat.exists + + - name: Ensure PAM Displays Last Logon/Access Notification - Check if expected PAM + module line is present in {{ pam_file_path }} + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + regexp: ^\s*session\s+required\s+pam_lastlog.so\s*.* + state: absent + check_mode: true + changed_when: false + register: result_pam_line_present + + - name: Ensure PAM Displays Last Logon/Access Notification - Include or update the + PAM module line in {{ pam_file_path }} + block: + + - name: Ensure PAM Displays Last Logon/Access Notification - Check if required + PAM module line is present in {{ pam_file_path }} with different control + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + regexp: ^\s*session\s+.*\s+pam_lastlog.so\s* + state: absent + check_mode: true + changed_when: false + register: result_pam_line_other_control_present + + - name: Ensure PAM Displays Last Logon/Access Notification - Ensure the correct + control for the required PAM module line in {{ pam_file_path }} + ansible.builtin.replace: + dest: '{{ pam_file_path }}' + regexp: ^(\s*session\s+).*(\bpam_lastlog.so.*) + replace: \1required \2 + register: result_pam_module_edit + when: + - result_pam_line_other_control_present.found == 1 + + - name: Ensure PAM Displays Last Logon/Access Notification - Ensure the required + PAM module line is included in {{ pam_file_path }} + ansible.builtin.lineinfile: + dest: '{{ pam_file_path }}' + insertafter: BOF + line: session required pam_lastlog.so + register: result_pam_module_add + when: + - result_pam_line_other_control_present.found == 0 or result_pam_line_other_control_present.found + > 1 + + - name: Ensure PAM Displays Last Logon/Access Notification - Ensure authselect + changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: | + result_authselect_present is defined and result_authselect_present.stat.exists and ((result_pam_module_add is defined and result_pam_module_add.changed) or (result_pam_module_edit is defined and result_pam_module_edit.changed)) + when: + - result_pam_line_present.found is defined + - result_pam_line_present.found == 0 + + - name: Ensure PAM Displays Last Logon/Access Notification - Check if the required + PAM module option is present in {{ pam_file_path }} + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + regexp: ^\s*session\s+required\s+pam_lastlog.so\s*.*\sshowfailed\b + state: absent + check_mode: true + changed_when: false + register: result_pam_module_showfailed_option_present + + - name: Ensure PAM Displays Last Logon/Access Notification - Ensure the "showfailed" + PAM option for "pam_lastlog.so" is included in {{ pam_file_path }} + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + backrefs: true + regexp: ^(\s*session\s+required\s+pam_lastlog.so.*) + line: \1 showfailed + state: present + register: result_pam_showfailed_add + when: + - result_pam_module_showfailed_option_present.found == 0 + + - name: Ensure PAM Displays Last Logon/Access Notification - Ensure authselect changes + are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: + - result_authselect_present.stat.exists + - (result_pam_showfailed_add is defined and result_pam_showfailed_add.changed) + or (result_pam_showfailed_edit is defined and result_pam_showfailed_edit.changed) + when: + - '"pam" in ansible_facts.packages' + - result_pam_file_present.stat.exists + tags: + - CCE-80788-3 + - CJIS-5.5.2 + - DISA-STIG-RHEL-08-020340 + - NIST-800-53-AC-9(1) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.4 + - configure_strategy + - display_login_attempts + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + +- name: Ensure PAM Displays Last Logon/Access Notification - Check if /etc/pam.d/postlogin + file is present + ansible.builtin.stat: + path: /etc/pam.d/postlogin + register: result_pam_file_present + when: '"pam" in ansible_facts.packages' + tags: + - CCE-80788-3 + - CJIS-5.5.2 + - DISA-STIG-RHEL-08-020340 + - NIST-800-53-AC-9(1) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.4 + - configure_strategy + - display_login_attempts + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + +- name: Ensure PAM Displays Last Logon/Access Notification - Check the proper remediation + for the system + block: + + - name: Ensure PAM Displays Last Logon/Access Notification - Define the PAM file + to be edited as a local fact + ansible.builtin.set_fact: + pam_file_path: /etc/pam.d/postlogin + + - name: Ensure PAM Displays Last Logon/Access Notification - Check if system relies + on authselect + ansible.builtin.stat: + path: /usr/bin/authselect + register: result_authselect_present + + - name: Ensure PAM Displays Last Logon/Access Notification - Remediate using authselect + block: + + - name: Ensure PAM Displays Last Logon/Access Notification - Check integrity of + authselect current profile + ansible.builtin.command: + cmd: authselect check + register: result_authselect_check_cmd + changed_when: false + ignore_errors: true + + - name: Ensure PAM Displays Last Logon/Access Notification - Informative message + based on the authselect integrity check result + ansible.builtin.assert: + that: + - result_authselect_check_cmd is success + fail_msg: + - authselect integrity check failed. Remediation aborted! + - This remediation could not be applied because an authselect profile was + not selected or the selected profile is not intact. + - It is not recommended to manually edit the PAM files when authselect tool + is available. + - In cases where the default authselect profile does not cover a specific + demand, a custom authselect profile is recommended. + success_msg: + - authselect integrity check passed + + - name: Ensure PAM Displays Last Logon/Access Notification - Get authselect current + profile + ansible.builtin.shell: + cmd: authselect current -r | awk '{ print $1 }' + register: result_authselect_profile + changed_when: false + when: + - result_authselect_check_cmd is success + + - name: Ensure PAM Displays Last Logon/Access Notification - Define the current + authselect profile as a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: '{{ result_authselect_profile.stdout }}' + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is match("custom/") + + - name: Ensure PAM Displays Last Logon/Access Notification - Define the new authselect + custom profile as a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: custom/hardening + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is not match("custom/") + + - name: Ensure PAM Displays Last Logon/Access Notification - Get authselect current + features to also enable them in the custom profile + ansible.builtin.shell: + cmd: authselect current | tail -n+3 | awk '{ print $2 }' + register: result_authselect_features + changed_when: false + when: + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + + - name: Ensure PAM Displays Last Logon/Access Notification - Check if any custom + profile with the same name was already created + ansible.builtin.stat: + path: /etc/authselect/{{ authselect_custom_profile }} + register: result_authselect_custom_profile_present + changed_when: false + when: + - authselect_current_profile is not match("custom/") + + - name: Ensure PAM Displays Last Logon/Access Notification - Create an authselect + custom profile based on the current profile + ansible.builtin.command: + cmd: authselect create-profile hardening -b {{ authselect_current_profile + }} + when: + - result_authselect_check_cmd is success + - authselect_current_profile is not match("custom/") + - not result_authselect_custom_profile_present.stat.exists + + - name: Ensure PAM Displays Last Logon/Access Notification - Ensure authselect + changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=before-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Ensure PAM Displays Last Logon/Access Notification - Ensure the authselect + custom profile is selected + ansible.builtin.command: + cmd: authselect select {{ authselect_custom_profile }} + register: result_pam_authselect_select_profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Ensure PAM Displays Last Logon/Access Notification - Restore the authselect + features in the custom profile + ansible.builtin.command: + cmd: authselect enable-feature {{ item }} + loop: '{{ result_authselect_features.stdout_lines }}' + register: result_pam_authselect_restore_features + when: + - result_authselect_profile is not skipped + - result_authselect_features is not skipped + - result_pam_authselect_select_profile is not skipped + + - name: Ensure PAM Displays Last Logon/Access Notification - Ensure authselect + changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=after-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - result_pam_authselect_restore_features is not skipped + + - name: Ensure PAM Displays Last Logon/Access Notification - Change the PAM file + to be edited according to the custom authselect profile + ansible.builtin.set_fact: + pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path + | basename }} + when: + - result_authselect_present.stat.exists + + - name: Ensure PAM Displays Last Logon/Access Notification - Ensure the "silent" + option from "pam_lastlog.so" is not present in {{ pam_file_path }} + ansible.builtin.replace: + dest: '{{ pam_file_path }}' + regexp: (.*session.*pam_lastlog.so.*)\bsilent\b=?[0-9a-zA-Z]*(.*) + replace: \1\2 + register: result_pam_option_removal + + - name: Ensure PAM Displays Last Logon/Access Notification - Ensure authselect changes + are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: + - result_authselect_present.stat.exists + - result_pam_option_removal is changed + when: + - '"pam" in ansible_facts.packages' + - result_pam_file_present.stat.exists + tags: + - CCE-80788-3 + - CJIS-5.5.2 + - DISA-STIG-RHEL-08-020340 + - NIST-800-53-AC-9(1) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.4 + - configure_strategy + - display_login_attempts + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if rpm --quiet -q pam; then + +if [ -e "/etc/pam.d/postlogin" ] ; then + PAM_FILE_PATH="/etc/pam.d/postlogin" + if [ -f /usr/bin/authselect ]; then + if ! authselect check; then + echo " + authselect integrity check failed. Remediation aborted! + This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact. + It is not recommended to manually edit the PAM files when authselect tool is available. + In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended." + exit 1 + fi + CURRENT_PROFILE=$(authselect current -r | awk '{ print $1 }') + # If not already in use, a custom profile is created preserving the enabled features. + if [[ ! $CURRENT_PROFILE == custom/* ]]; then + ENABLED_FEATURES=$(authselect current | tail -n+3 | awk '{ print $2 }') + authselect create-profile hardening -b $CURRENT_PROFILE + CURRENT_PROFILE="custom/hardening" + + authselect apply-changes -b --backup=before-hardening-custom-profile + authselect select $CURRENT_PROFILE + for feature in $ENABLED_FEATURES; do + authselect enable-feature $feature; + done + + authselect apply-changes -b --backup=after-hardening-custom-profile + fi + PAM_FILE_NAME=$(basename "/etc/pam.d/postlogin") + PAM_FILE_PATH="/etc/authselect/$CURRENT_PROFILE/$PAM_FILE_NAME" + + authselect apply-changes -b + fi + if ! grep -qP '^\s*session\s+'"required"'\s+pam_lastlog.so\s*.*' "$PAM_FILE_PATH"; then + # Line matching group + control + module was not found. Check group + module. + if [ "$(grep -cP '^\s*session\s+.*\s+pam_lastlog.so\s*' "$PAM_FILE_PATH")" -eq 1 ]; then + # The control is updated only if one single line matches. + sed -i -E --follow-symlinks 's/^(\s*session\s+).*(\bpam_lastlog.so.*)/\1'"required"' \2/' "$PAM_FILE_PATH" + else + sed -i --follow-symlinks '1i session '"required"' pam_lastlog.so' "$PAM_FILE_PATH" + fi + fi + # Check the option + if ! grep -qP '^\s*session\s+'"required"'\s+pam_lastlog.so\s*.*\sshowfailed\b' "$PAM_FILE_PATH"; then + sed -i -E --follow-symlinks '/\s*session\s+'"required"'\s+pam_lastlog.so.*/ s/$/ showfailed/' "$PAM_FILE_PATH" + fi + if [ -f /usr/bin/authselect ]; then + + authselect apply-changes -b + fi +else + echo "/etc/pam.d/postlogin was not found" >&2 +fi +if [ -e "/etc/pam.d/postlogin" ] ; then + PAM_FILE_PATH="/etc/pam.d/postlogin" + if [ -f /usr/bin/authselect ]; then + if ! authselect check; then + echo " + authselect integrity check failed. Remediation aborted! + This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact. + It is not recommended to manually edit the PAM files when authselect tool is available. + In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended." + exit 1 + fi + CURRENT_PROFILE=$(authselect current -r | awk '{ print $1 }') + # If not already in use, a custom profile is created preserving the enabled features. + if [[ ! $CURRENT_PROFILE == custom/* ]]; then + ENABLED_FEATURES=$(authselect current | tail -n+3 | awk '{ print $2 }') + authselect create-profile hardening -b $CURRENT_PROFILE + CURRENT_PROFILE="custom/hardening" + + authselect apply-changes -b --backup=before-hardening-custom-profile + authselect select $CURRENT_PROFILE + for feature in $ENABLED_FEATURES; do + authselect enable-feature $feature; + done + + authselect apply-changes -b --backup=after-hardening-custom-profile + fi + PAM_FILE_NAME=$(basename "/etc/pam.d/postlogin") + PAM_FILE_PATH="/etc/authselect/$CURRENT_PROFILE/$PAM_FILE_NAME" + + authselect apply-changes -b + fi + +if grep -qP '^\s*session\s.*\bpam_lastlog.so\s.*\bsilent\b' "$PAM_FILE_PATH"; then + sed -i -E --follow-symlinks 's/(.*session.*pam_lastlog.so.*)\bsilent\b=?[[:alnum:]]*(.*)/\1\2/g' "$PAM_FILE_PATH" +fi + if [ -f /usr/bin/authselect ]; then + + authselect apply-changes -b + fi +else + echo "/etc/pam.d/postlogin was not found" >&2 +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Set Up a Private Namespace in PAM Configuration + To setup a private namespace add the following line to /etc/pam.d/login: +session required pam_namespace.so + BP28(R39) + The pam_namespace PAM module sets up a private namespace for a +session with polyinstantiated directories. A polyinstantiated directory +provides a different instance of itself based on user name, or when using +SELinux, user name, security context or both. The polyinstatied directories +can be used to dedicate separate temporary directories to each account. + + CCE-83744-3 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83744-3 + - enable_pam_namespace + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - restrict_strategy + +- name: Make changes to /etc/pam.d/login + lineinfile: + path: /etc/pam.d/login + create: false + regexp: ^\s*session\s+required\s+pam_namespace.so\s*$ + line: session required pam_namespace.so + state: present + when: '"pam" in ansible_facts.packages' + tags: + - CCE-83744-3 + - enable_pam_namespace + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q pam; then + +if ! grep -Eq '^\s*session\s+required\s+pam_namespace.so\s*$' '/etc/pam.d/login' ; then + echo "session required pam_namespace.so" >> "/etc/pam.d/login" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Set Lockouts for Failed Password Attempts + The pam_faillock PAM module provides the capability to +lock out user accounts after a number of failed login attempts. Its +documentation is available in +/usr/share/doc/pam-VERSION/txts/README.pam_faillock. + + Locking out user accounts presents the +risk of a denial-of-service attack. The lockout policy +must weigh whether the risk of such a +denial-of-service attack outweighs the benefits of thwarting +password guessing attacks. + + fail_deny + Number of failed login attempts before account lockout + 10 + 3 + 5 + 6 + 3 + + + faillock directory + The directory where the user files with the failure records are kept + /var/log/faillock + /var/log/faillock + + + fail_interval + Interval for counting failed login attempts before account lockout + 100000000 + 1800 + 3600 + 86400 + 900 + 900 + + + fail_unlock_time + Seconds before automatic unlocking or permanently locking after excessive failed logins + 1800 + 3600 + 600 + 604800 + 86400 + 900 + 0 + 0 + + + faildelay_delay + Delay next login attempt after a failed login + 0 + 4000000 + 4000000 + + + pwhistory_remember + Prevent password re-use using password history lookup + 0 + 5 + 6 + 7 + 8 + 9 + 5 + + + PAM pwhistory remember - control flag + 'Specify the control flag required for password remember requirement. If multiple +values are allowed write them separated by commas as in "required,requisite", +for remediations the first value will be taken' + required + user + requisite + sufficient + binding + required,requisite + requisite + + + tally2 + Number of failed login attempts + 1 + 2 + 3 + 4 + 5 + 3 + + + Configure the Use of the pam_faillock.so Module in the /etc/pam.d/password-auth File. + The pam_faillock.so module must be loaded in preauth in /etc/pam.d/password-auth. + CCI-000044 + AC-7 (a) + SRG-OS-000021-GPOS-00005 + RHEL-08-020026 + SV-244534r743851_rule + If the pam_faillock.so module is not loaded the system will not correctly lockout accounts to prevent +password guessing attacks. + CCE-86931-3 + + + + + + Configure the Use of the pam_faillock.so Module in the /etc/pam.d/system-auth File. + The pam_faillock.so module must be loaded in preauth in /etc/pam.d/system-auth. + CCI-000044 + AC-7 (a) + SRG-OS-000021-GPOS-00005 + RHEL-08-020025 + SV-244533r743848_rule + If the pam_faillock.so module is not loaded the system will not correctly lockout accounts to prevent +password guessing attacks. + CCE-86916-4 + + + + + + An SELinux Context must be configured for the pam_faillock.so records directory + The dir configuration option in PAM pam_faillock.so module defines where the lockout +records is stored. The configured directory must have the correct SELinux context. + CCI-000044 + AC-7 (a) + SRG-OS-000021-GPOS-00005 + RHEL-08-020027 + SV-250315r793009_rule + Not having the correct SELinux context on the pam_faillock.so records directory may lead to +unauthorized access to the directory. + + CCE-86248-2 + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +#!/bin/bash + +FAILLOCK_CONF_FILES="/etc/security/faillock.conf /etc/pam.d/system-auth /etc/pam.d/password-auth" +faillock_dirs=$(grep -oP "^\s*(?:auth.*pam_faillock.so.*)?dir\s*=\s*(\S+)" $FAILLOCK_CONF_FILES \ + | sed -r 's/.*=\s*(\S+)/\1/') + +if [ -n "$faillock_dirs" ]; then + for dir in $faillock_dirs; do + if ! semanage fcontext -a -t faillog_t "$dir(/.*)?"; then + semanage fcontext -m -t faillog_t "$dir(/.*)?" + fi + if [ ! -e $dir ]; then + mkdir -p $dir + fi + restorecon -R -v $dir + done +else +echo " +The pam_faillock.so dir option is not set in the system. +If this is not expected, make sure pam_faillock.so is properly configured." +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Account Lockouts Must Be Logged + PAM faillock locks an account due to excessive password failures, this event must be logged. + CCI-000044 + AC-7 (a) + SRG-OS-000021-GPOS-00005 + RHEL-08-020021 + SV-230343r743981_rule + Without auditing of these events it may be harder or impossible to identify what an attacker did after an attack. + CCE-86107-0 + - name: Account Lockouts Must Be Logged - Check if system relies on authselect tool + ansible.builtin.stat: + path: /usr/bin/authselect + register: result_authselect_present + tags: + - CCE-86107-0 + - DISA-STIG-RHEL-08-020021 + - NIST-800-53-AC-7 (a) + - account_passwords_pam_faillock_audit + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Account Lockouts Must Be Logged - Remediation where authselect tool is present + block: + + - name: Account Lockouts Must Be Logged - Check integrity of authselect current + profile + ansible.builtin.command: + cmd: authselect check + register: result_authselect_check_cmd + changed_when: false + ignore_errors: true + + - name: Account Lockouts Must Be Logged - Informative message based on the authselect + integrity check result + ansible.builtin.assert: + that: + - result_authselect_check_cmd is success + fail_msg: + - authselect integrity check failed. Remediation aborted! + - This remediation could not be applied because an authselect profile was not + selected or the selected profile is not intact. + - It is not recommended to manually edit the PAM files when authselect tool + is available. + - In cases where the default authselect profile does not cover a specific demand, + a custom authselect profile is recommended. + success_msg: + - authselect integrity check passed + + - name: Account Lockouts Must Be Logged - Get authselect current features + ansible.builtin.shell: + cmd: authselect current | tail -n+3 | awk '{ print $2 }' + register: result_authselect_features + changed_when: false + when: + - result_authselect_check_cmd is success + + - name: Account Lockouts Must Be Logged - Ensure "with-faillock" feature is enabled + using authselect tool + ansible.builtin.command: + cmd: authselect enable-feature with-faillock + register: result_authselect_enable_feature_cmd + when: + - result_authselect_check_cmd is success + - result_authselect_features.stdout is not search("with-faillock") + + - name: Account Lockouts Must Be Logged - Ensure authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: + - result_authselect_enable_feature_cmd is not skipped + - result_authselect_enable_feature_cmd is success + when: result_authselect_present.stat.exists + tags: + - CCE-86107-0 + - DISA-STIG-RHEL-08-020021 + - NIST-800-53-AC-7 (a) + - account_passwords_pam_faillock_audit + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Account Lockouts Must Be Logged - Remediation where authselect tool is not + present + block: + + - name: Account Lockouts Must Be Logged - Check if pam_faillock.so is already enabled + ansible.builtin.lineinfile: + path: /etc/pam.d/system-auth + regexp: .*auth.*pam_faillock\.so (preauth|authfail) + state: absent + check_mode: true + changed_when: false + register: result_pam_faillock_is_enabled + + - name: Account Lockouts Must Be Logged - Enable pam_faillock.so preauth editing + PAM files + ansible.builtin.lineinfile: + path: '{{ item }}' + line: auth required pam_faillock.so preauth + insertbefore: ^auth.*sufficient.*pam_unix\.so.* + state: present + loop: + - /etc/pam.d/system-auth + - /etc/pam.d/password-auth + when: + - result_pam_faillock_is_enabled.found == 0 + + - name: Account Lockouts Must Be Logged - Enable pam_faillock.so authfail editing + PAM files + ansible.builtin.lineinfile: + path: '{{ item }}' + line: auth required pam_faillock.so authfail + insertbefore: ^auth.*required.*pam_deny\.so.* + state: present + loop: + - /etc/pam.d/system-auth + - /etc/pam.d/password-auth + when: + - result_pam_faillock_is_enabled.found == 0 + + - name: Account Lockouts Must Be Logged - Enable pam_faillock.so account section + editing PAM files + ansible.builtin.lineinfile: + path: '{{ item }}' + line: account required pam_faillock.so + insertbefore: ^account.*required.*pam_unix\.so.* + state: present + loop: + - /etc/pam.d/system-auth + - /etc/pam.d/password-auth + when: + - result_pam_faillock_is_enabled.found == 0 + when: not result_authselect_present.stat.exists + tags: + - CCE-86107-0 + - DISA-STIG-RHEL-08-020021 + - NIST-800-53-AC-7 (a) + - account_passwords_pam_faillock_audit + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Account Lockouts Must Be Logged - Check the presence of /etc/security/faillock.conf + file + ansible.builtin.stat: + path: /etc/security/faillock.conf + register: result_faillock_conf_check + tags: + - CCE-86107-0 + - DISA-STIG-RHEL-08-020021 + - NIST-800-53-AC-7 (a) + - account_passwords_pam_faillock_audit + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Account Lockouts Must Be Logged - Ensure the pam_faillock.so audit parameter + in /etc/security/faillock.conf + ansible.builtin.lineinfile: + path: /etc/security/faillock.conf + regexp: ^\s*audit + line: audit + state: present + when: result_faillock_conf_check.stat.exists + tags: + - CCE-86107-0 + - DISA-STIG-RHEL-08-020021 + - NIST-800-53-AC-7 (a) + - account_passwords_pam_faillock_audit + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Account Lockouts Must Be Logged - Ensure the pam_faillock.so audit parameter + not in PAM files + block: + + - name: Account Lockouts Must Be Logged - Check if /etc/pam.d/system-auth file is + present + ansible.builtin.stat: + path: /etc/pam.d/system-auth + register: result_pam_file_present + + - name: Account Lockouts Must Be Logged - Check the proper remediation for the system + block: + + - name: Account Lockouts Must Be Logged - Define the PAM file to be edited as + a local fact + ansible.builtin.set_fact: + pam_file_path: /etc/pam.d/system-auth + + - name: Account Lockouts Must Be Logged - Check if system relies on authselect + ansible.builtin.stat: + path: /usr/bin/authselect + register: result_authselect_present + + - name: Account Lockouts Must Be Logged - Remediate using authselect + block: + + - name: Account Lockouts Must Be Logged - Check integrity of authselect current + profile + ansible.builtin.command: + cmd: authselect check + register: result_authselect_check_cmd + changed_when: false + ignore_errors: true + + - name: Account Lockouts Must Be Logged - Informative message based on the authselect + integrity check result + ansible.builtin.assert: + that: + - result_authselect_check_cmd is success + fail_msg: + - authselect integrity check failed. Remediation aborted! + - This remediation could not be applied because an authselect profile was + not selected or the selected profile is not intact. + - It is not recommended to manually edit the PAM files when authselect tool + is available. + - In cases where the default authselect profile does not cover a specific + demand, a custom authselect profile is recommended. + success_msg: + - authselect integrity check passed + + - name: Account Lockouts Must Be Logged - Get authselect current profile + ansible.builtin.shell: + cmd: authselect current -r | awk '{ print $1 }' + register: result_authselect_profile + changed_when: false + when: + - result_authselect_check_cmd is success + + - name: Account Lockouts Must Be Logged - Define the current authselect profile + as a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: '{{ result_authselect_profile.stdout }}' + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is match("custom/") + + - name: Account Lockouts Must Be Logged - Define the new authselect custom profile + as a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: custom/hardening + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is not match("custom/") + + - name: Account Lockouts Must Be Logged - Get authselect current features to + also enable them in the custom profile + ansible.builtin.shell: + cmd: authselect current | tail -n+3 | awk '{ print $2 }' + register: result_authselect_features + changed_when: false + when: + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + + - name: Account Lockouts Must Be Logged - Check if any custom profile with the + same name was already created + ansible.builtin.stat: + path: /etc/authselect/{{ authselect_custom_profile }} + register: result_authselect_custom_profile_present + changed_when: false + when: + - authselect_current_profile is not match("custom/") + + - name: Account Lockouts Must Be Logged - Create an authselect custom profile + based on the current profile + ansible.builtin.command: + cmd: authselect create-profile hardening -b {{ authselect_current_profile + }} + when: + - result_authselect_check_cmd is success + - authselect_current_profile is not match("custom/") + - not result_authselect_custom_profile_present.stat.exists + + - name: Account Lockouts Must Be Logged - Ensure authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=before-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Account Lockouts Must Be Logged - Ensure the authselect custom profile + is selected + ansible.builtin.command: + cmd: authselect select {{ authselect_custom_profile }} + register: result_pam_authselect_select_profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Account Lockouts Must Be Logged - Restore the authselect features in + the custom profile + ansible.builtin.command: + cmd: authselect enable-feature {{ item }} + loop: '{{ result_authselect_features.stdout_lines }}' + register: result_pam_authselect_restore_features + when: + - result_authselect_profile is not skipped + - result_authselect_features is not skipped + - result_pam_authselect_select_profile is not skipped + + - name: Account Lockouts Must Be Logged - Ensure authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=after-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - result_pam_authselect_restore_features is not skipped + + - name: Account Lockouts Must Be Logged - Change the PAM file to be edited according + to the custom authselect profile + ansible.builtin.set_fact: + pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path + | basename }} + when: + - result_authselect_present.stat.exists + + - name: Account Lockouts Must Be Logged - Ensure the "audit" option from "pam_faillock.so" + is not present in {{ pam_file_path }} + ansible.builtin.replace: + dest: '{{ pam_file_path }}' + regexp: (.*auth.*pam_faillock.so.*)\baudit\b=?[0-9a-zA-Z]*(.*) + replace: \1\2 + register: result_pam_option_removal + + - name: Account Lockouts Must Be Logged - Ensure authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: + - result_authselect_present.stat.exists + - result_pam_option_removal is changed + when: + - result_pam_file_present.stat.exists + + - name: Account Lockouts Must Be Logged - Check if /etc/pam.d/password-auth file + is present + ansible.builtin.stat: + path: /etc/pam.d/password-auth + register: result_pam_file_present + + - name: Account Lockouts Must Be Logged - Check the proper remediation for the system + block: + + - name: Account Lockouts Must Be Logged - Define the PAM file to be edited as + a local fact + ansible.builtin.set_fact: + pam_file_path: /etc/pam.d/password-auth + + - name: Account Lockouts Must Be Logged - Check if system relies on authselect + ansible.builtin.stat: + path: /usr/bin/authselect + register: result_authselect_present + + - name: Account Lockouts Must Be Logged - Remediate using authselect + block: + + - name: Account Lockouts Must Be Logged - Check integrity of authselect current + profile + ansible.builtin.command: + cmd: authselect check + register: result_authselect_check_cmd + changed_when: false + ignore_errors: true + + - name: Account Lockouts Must Be Logged - Informative message based on the authselect + integrity check result + ansible.builtin.assert: + that: + - result_authselect_check_cmd is success + fail_msg: + - authselect integrity check failed. Remediation aborted! + - This remediation could not be applied because an authselect profile was + not selected or the selected profile is not intact. + - It is not recommended to manually edit the PAM files when authselect tool + is available. + - In cases where the default authselect profile does not cover a specific + demand, a custom authselect profile is recommended. + success_msg: + - authselect integrity check passed + + - name: Account Lockouts Must Be Logged - Get authselect current profile + ansible.builtin.shell: + cmd: authselect current -r | awk '{ print $1 }' + register: result_authselect_profile + changed_when: false + when: + - result_authselect_check_cmd is success + + - name: Account Lockouts Must Be Logged - Define the current authselect profile + as a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: '{{ result_authselect_profile.stdout }}' + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is match("custom/") + + - name: Account Lockouts Must Be Logged - Define the new authselect custom profile + as a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: custom/hardening + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is not match("custom/") + + - name: Account Lockouts Must Be Logged - Get authselect current features to + also enable them in the custom profile + ansible.builtin.shell: + cmd: authselect current | tail -n+3 | awk '{ print $2 }' + register: result_authselect_features + changed_when: false + when: + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + + - name: Account Lockouts Must Be Logged - Check if any custom profile with the + same name was already created + ansible.builtin.stat: + path: /etc/authselect/{{ authselect_custom_profile }} + register: result_authselect_custom_profile_present + changed_when: false + when: + - authselect_current_profile is not match("custom/") + + - name: Account Lockouts Must Be Logged - Create an authselect custom profile + based on the current profile + ansible.builtin.command: + cmd: authselect create-profile hardening -b {{ authselect_current_profile + }} + when: + - result_authselect_check_cmd is success + - authselect_current_profile is not match("custom/") + - not result_authselect_custom_profile_present.stat.exists + + - name: Account Lockouts Must Be Logged - Ensure authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=before-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Account Lockouts Must Be Logged - Ensure the authselect custom profile + is selected + ansible.builtin.command: + cmd: authselect select {{ authselect_custom_profile }} + register: result_pam_authselect_select_profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Account Lockouts Must Be Logged - Restore the authselect features in + the custom profile + ansible.builtin.command: + cmd: authselect enable-feature {{ item }} + loop: '{{ result_authselect_features.stdout_lines }}' + register: result_pam_authselect_restore_features + when: + - result_authselect_profile is not skipped + - result_authselect_features is not skipped + - result_pam_authselect_select_profile is not skipped + + - name: Account Lockouts Must Be Logged - Ensure authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=after-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - result_pam_authselect_restore_features is not skipped + + - name: Account Lockouts Must Be Logged - Change the PAM file to be edited according + to the custom authselect profile + ansible.builtin.set_fact: + pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path + | basename }} + when: + - result_authselect_present.stat.exists + + - name: Account Lockouts Must Be Logged - Ensure the "audit" option from "pam_faillock.so" + is not present in {{ pam_file_path }} + ansible.builtin.replace: + dest: '{{ pam_file_path }}' + regexp: (.*auth.*pam_faillock.so.*)\baudit\b=?[0-9a-zA-Z]*(.*) + replace: \1\2 + register: result_pam_option_removal + + - name: Account Lockouts Must Be Logged - Ensure authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: + - result_authselect_present.stat.exists + - result_pam_option_removal is changed + when: + - result_pam_file_present.stat.exists + when: result_faillock_conf_check.stat.exists + tags: + - CCE-86107-0 + - DISA-STIG-RHEL-08-020021 + - NIST-800-53-AC-7 (a) + - account_passwords_pam_faillock_audit + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Account Lockouts Must Be Logged - Ensure the pam_faillock.so audit parameter + in PAM files + block: + + - name: Account Lockouts Must Be Logged - Check if pam_faillock.so audit parameter + is already enabled in pam files + ansible.builtin.lineinfile: + path: /etc/pam.d/system-auth + regexp: .*auth.*pam_faillock\.so (preauth|authfail).*audit + state: absent + check_mode: true + changed_when: false + register: result_pam_faillock_audit_parameter_is_present + + - name: Account Lockouts Must Be Logged - Ensure the inclusion of pam_faillock.so + preauth audit parameter in auth section + ansible.builtin.lineinfile: + path: '{{ item }}' + backrefs: true + regexp: (^\s*auth\s+)([\w\[].*\b)(\s+pam_faillock.so preauth.*) + line: \1required\3 audit + state: present + loop: + - /etc/pam.d/system-auth + - /etc/pam.d/password-auth + when: + - result_pam_faillock_audit_parameter_is_present.found == 0 + when: not result_faillock_conf_check.stat.exists + tags: + - CCE-86107-0 + - DISA-STIG-RHEL-08-020021 + - NIST-800-53-AC-7 (a) + - account_passwords_pam_faillock_audit + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + +if [ -f /usr/bin/authselect ]; then + if ! authselect check; then +echo " +authselect integrity check failed. Remediation aborted! +This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact. +It is not recommended to manually edit the PAM files when authselect tool is available. +In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended." +exit 1 +fi +authselect enable-feature with-faillock + +authselect apply-changes -b +else + AUTH_FILES=("/etc/pam.d/system-auth" "/etc/pam.d/password-auth") +for pam_file in "${AUTH_FILES[@]}" +do + if ! grep -qE '^\s*auth\s+required\s+pam_faillock\.so\s+(preauth silent|authfail).*$' "$pam_file" ; then + sed -i --follow-symlinks '/^auth.*sufficient.*pam_unix\.so.*/i auth required pam_faillock.so preauth silent' "$pam_file" + sed -i --follow-symlinks '/^auth.*required.*pam_deny\.so.*/i auth required pam_faillock.so authfail' "$pam_file" + sed -i --follow-symlinks '/^account.*required.*pam_unix\.so.*/i account required pam_faillock.so' "$pam_file" + fi + sed -Ei 's/(auth.*)(\[default=die\])(.*pam_faillock\.so)/\1required \3/g' "$pam_file" +done +fi +AUTH_FILES=("/etc/pam.d/system-auth" "/etc/pam.d/password-auth") +FAILLOCK_CONF="/etc/security/faillock.conf" +if [ -f $FAILLOCK_CONF ]; then + regex="^\s*audit" + line="audit" + if ! grep -q $regex $FAILLOCK_CONF; then + echo $line >> $FAILLOCK_CONF + fi + for pam_file in "${AUTH_FILES[@]}" + do + if [ -e "$pam_file" ] ; then + PAM_FILE_PATH="$pam_file" + if [ -f /usr/bin/authselect ]; then + if ! authselect check; then + echo " + authselect integrity check failed. Remediation aborted! + This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact. + It is not recommended to manually edit the PAM files when authselect tool is available. + In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended." + exit 1 + fi + CURRENT_PROFILE=$(authselect current -r | awk '{ print $1 }') + # If not already in use, a custom profile is created preserving the enabled features. + if [[ ! $CURRENT_PROFILE == custom/* ]]; then + ENABLED_FEATURES=$(authselect current | tail -n+3 | awk '{ print $2 }') + authselect create-profile hardening -b $CURRENT_PROFILE + CURRENT_PROFILE="custom/hardening" + + authselect apply-changes -b --backup=before-hardening-custom-profile + authselect select $CURRENT_PROFILE + for feature in $ENABLED_FEATURES; do + authselect enable-feature $feature; + done + + authselect apply-changes -b --backup=after-hardening-custom-profile + fi + PAM_FILE_NAME=$(basename "$pam_file") + PAM_FILE_PATH="/etc/authselect/$CURRENT_PROFILE/$PAM_FILE_NAME" + + authselect apply-changes -b + fi + + if grep -qP '^\s*auth\s.*\bpam_faillock.so\s.*\baudit\b' "$PAM_FILE_PATH"; then + sed -i -E --follow-symlinks 's/(.*auth.*pam_faillock.so.*)\baudit\b=?[[:alnum:]]*(.*)/\1\2/g' "$PAM_FILE_PATH" + fi + if [ -f /usr/bin/authselect ]; then + + authselect apply-changes -b + fi + else + echo "$pam_file was not found" >&2 + fi + done +else + for pam_file in "${AUTH_FILES[@]}" + do + if ! grep -qE '^\s*auth.*pam_faillock\.so (preauth|authfail).*audit' "$pam_file"; then + sed -i --follow-symlinks '/^auth.*required.*pam_faillock\.so.*preauth.*silent.*/ s/$/ audit/' "$pam_file" + fi + done +fi + + + + + + + + + + Account Lockouts Must Persist + By setting a `dir` in the faillock configuration account lockouts will persist across reboots. + CCI-000044 + AC-7 (a) + SRG-OS-000021-GPOS-00005 + RHEL-08-020017 + SV-230339r743975_rule + Having lockouts persist across reboots ensures that account is only unlocked by an administrator. +If the lockouts did not persist across reboots an attack could simply reboot the system to continue brute force attacks against the accounts on the system. + + CCE-86079-1 + + + + + + Limit Password Reuse: password-auth + Do not allow users to reuse recent passwords. This can be accomplished by using the +remember option for the pam_pwhistory PAM module. + +In the file /etc/pam.d/password-auth, make sure the parameter +remember is present and it has a value equal to or greater than +. For example: +password control_flag pam_pwhistory.so ...existing_options... remember= use_authtok +control_flag should be one of the next values: + If the system relies on authselect tool to manage PAM settings, the remediation +will also use authselect tool. However, if any manual modification was made in +PAM files, the authselect integrity check will fail and the remediation will be +aborted in order to preserve intentional changes. In this case, an informative message will +be shown in the remediation report. + 1 + 12 + 15 + 16 + 5 + 5.6.2.1.1 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.03 + DSS06.10 + 3.5.8 + CCI-000200 + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.2 + 4.3.3.7.4 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + A.18.1.4 + A.7.1.1 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.2 + A.9.4.3 + IA-5(f) + IA-5(1)(e) + PR.AC-1 + PR.AC-6 + PR.AC-7 + Req-8.2.5 + SRG-OS-000077-GPOS-00045 + SRG-OS-000077-VMM-000440 + RHEL-08-020220 + 5.5.3 + SV-230368r810414_rule + Preventing re-use of previous passwords helps ensure that a compromised password is not re-used by a user. + + CCE-83478-8 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83478-8 + - CJIS-5.6.2.1.1 + - DISA-STIG-RHEL-08-020220 + - NIST-800-171-3.5.8 + - NIST-800-53-IA-5(1)(e) + - NIST-800-53-IA-5(f) + - PCI-DSS-Req-8.2.5 + - accounts_password_pam_pwhistory_remember_password_auth + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed +- name: XCCDF Value var_password_pam_remember # promote to variable + set_fact: + var_password_pam_remember: !!str + tags: + - always +- name: XCCDF Value var_password_pam_remember_control_flag # promote to variable + set_fact: + var_password_pam_remember_control_flag: !!str + tags: + - always + +- name: 'Limit Password Reuse: password-auth - Check if /etc/pam.d/password-auth file + is present' + ansible.builtin.stat: + path: /etc/pam.d/password-auth + register: result_pam_file_present + when: '"pam" in ansible_facts.packages' + tags: + - CCE-83478-8 + - CJIS-5.6.2.1.1 + - DISA-STIG-RHEL-08-020220 + - NIST-800-171-3.5.8 + - NIST-800-53-IA-5(1)(e) + - NIST-800-53-IA-5(f) + - PCI-DSS-Req-8.2.5 + - accounts_password_pam_pwhistory_remember_password_auth + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + +- name: 'Limit Password Reuse: password-auth - Check the proper remediation for the + system' + block: + + - name: 'Limit Password Reuse: password-auth - Define the PAM file to be edited + as a local fact' + ansible.builtin.set_fact: + pam_file_path: /etc/pam.d/password-auth + + - name: 'Limit Password Reuse: password-auth - Check if system relies on authselect' + ansible.builtin.stat: + path: /usr/bin/authselect + register: result_authselect_present + + - name: 'Limit Password Reuse: password-auth - Remediate using authselect' + block: + + - name: 'Limit Password Reuse: password-auth - Check integrity of authselect current + profile' + ansible.builtin.command: + cmd: authselect check + register: result_authselect_check_cmd + changed_when: false + ignore_errors: true + + - name: 'Limit Password Reuse: password-auth - Informative message based on the + authselect integrity check result' + ansible.builtin.assert: + that: + - result_authselect_check_cmd is success + fail_msg: + - authselect integrity check failed. Remediation aborted! + - This remediation could not be applied because an authselect profile was + not selected or the selected profile is not intact. + - It is not recommended to manually edit the PAM files when authselect tool + is available. + - In cases where the default authselect profile does not cover a specific + demand, a custom authselect profile is recommended. + success_msg: + - authselect integrity check passed + + - name: 'Limit Password Reuse: password-auth - Get authselect current profile' + ansible.builtin.shell: + cmd: authselect current -r | awk '{ print $1 }' + register: result_authselect_profile + changed_when: false + when: + - result_authselect_check_cmd is success + + - name: 'Limit Password Reuse: password-auth - Define the current authselect profile + as a local fact' + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: '{{ result_authselect_profile.stdout }}' + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is match("custom/") + + - name: 'Limit Password Reuse: password-auth - Define the new authselect custom + profile as a local fact' + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: custom/hardening + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is not match("custom/") + + - name: 'Limit Password Reuse: password-auth - Get authselect current features + to also enable them in the custom profile' + ansible.builtin.shell: + cmd: authselect current | tail -n+3 | awk '{ print $2 }' + register: result_authselect_features + changed_when: false + when: + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + + - name: 'Limit Password Reuse: password-auth - Check if any custom profile with + the same name was already created' + ansible.builtin.stat: + path: /etc/authselect/{{ authselect_custom_profile }} + register: result_authselect_custom_profile_present + changed_when: false + when: + - authselect_current_profile is not match("custom/") + + - name: 'Limit Password Reuse: password-auth - Create an authselect custom profile + based on the current profile' + ansible.builtin.command: + cmd: authselect create-profile hardening -b {{ authselect_current_profile + }} + when: + - result_authselect_check_cmd is success + - authselect_current_profile is not match("custom/") + - not result_authselect_custom_profile_present.stat.exists + + - name: 'Limit Password Reuse: password-auth - Ensure authselect changes are applied' + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=before-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: 'Limit Password Reuse: password-auth - Ensure the authselect custom profile + is selected' + ansible.builtin.command: + cmd: authselect select {{ authselect_custom_profile }} + register: result_pam_authselect_select_profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: 'Limit Password Reuse: password-auth - Restore the authselect features + in the custom profile' + ansible.builtin.command: + cmd: authselect enable-feature {{ item }} + loop: '{{ result_authselect_features.stdout_lines }}' + register: result_pam_authselect_restore_features + when: + - result_authselect_profile is not skipped + - result_authselect_features is not skipped + - result_pam_authselect_select_profile is not skipped + + - name: 'Limit Password Reuse: password-auth - Ensure authselect changes are applied' + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=after-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - result_pam_authselect_restore_features is not skipped + + - name: 'Limit Password Reuse: password-auth - Change the PAM file to be edited + according to the custom authselect profile' + ansible.builtin.set_fact: + pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path + | basename }} + when: + - result_authselect_present.stat.exists + + - name: 'Limit Password Reuse: password-auth - Check if expected PAM module line + is present in {{ pam_file_path }}' + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + regexp: ^\s*password\s+{{ var_password_pam_remember_control_flag.split(",")[0] + }}\s+pam_pwhistory.so\s*.* + state: absent + check_mode: true + changed_when: false + register: result_pam_line_present + + - name: 'Limit Password Reuse: password-auth - Include or update the PAM module + line in {{ pam_file_path }}' + block: + + - name: 'Limit Password Reuse: password-auth - Check if required PAM module line + is present in {{ pam_file_path }} with different control' + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + regexp: ^\s*password\s+.*\s+pam_pwhistory.so\s* + state: absent + check_mode: true + changed_when: false + register: result_pam_line_other_control_present + + - name: 'Limit Password Reuse: password-auth - Ensure the correct control for + the required PAM module line in {{ pam_file_path }}' + ansible.builtin.replace: + dest: '{{ pam_file_path }}' + regexp: ^(\s*password\s+).*(\bpam_pwhistory.so.*) + replace: \1{{ var_password_pam_remember_control_flag.split(",")[0] }} \2 + register: result_pam_module_edit + when: + - result_pam_line_other_control_present.found == 1 + + - name: 'Limit Password Reuse: password-auth - Ensure the required PAM module + line is included in {{ pam_file_path }}' + ansible.builtin.lineinfile: + dest: '{{ pam_file_path }}' + insertafter: ^password.*requisite.*pam_pwquality\.so + line: password {{ var_password_pam_remember_control_flag.split(",")[0] + }} pam_pwhistory.so + register: result_pam_module_add + when: + - result_pam_line_other_control_present.found == 0 or result_pam_line_other_control_present.found + > 1 + + - name: 'Limit Password Reuse: password-auth - Ensure authselect changes are applied' + ansible.builtin.command: + cmd: authselect apply-changes -b + when: | + result_authselect_present is defined and result_authselect_present.stat.exists and ((result_pam_module_add is defined and result_pam_module_add.changed) or (result_pam_module_edit is defined and result_pam_module_edit.changed)) + when: + - result_pam_line_present.found is defined + - result_pam_line_present.found == 0 + + - name: 'Limit Password Reuse: password-auth - Check if the required PAM module + option is present in {{ pam_file_path }}' + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + regexp: ^\s*password\s+{{ var_password_pam_remember_control_flag.split(",")[0] + }}\s+pam_pwhistory.so\s*.*\sremember\b + state: absent + check_mode: true + changed_when: false + register: result_pam_module_remember_option_present + + - name: 'Limit Password Reuse: password-auth - Ensure the "remember" PAM option + for "pam_pwhistory.so" is included in {{ pam_file_path }}' + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + backrefs: true + regexp: ^(\s*password\s+{{ var_password_pam_remember_control_flag.split(",")[0] + }}\s+pam_pwhistory.so.*) + line: \1 remember={{ var_password_pam_remember }} + state: present + register: result_pam_remember_add + when: + - result_pam_module_remember_option_present.found == 0 + + - name: 'Limit Password Reuse: password-auth - Ensure the required value for "remember" + PAM option from "pam_pwhistory.so" in {{ pam_file_path }}' + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + backrefs: true + regexp: ^(\s*password\s+{{ var_password_pam_remember_control_flag.split(",")[0] + }}\s+pam_pwhistory.so\s+.*)(remember)=[0-9a-zA-Z]+\s*(.*) + line: \1\2={{ var_password_pam_remember }} \3 + register: result_pam_remember_edit + when: + - result_pam_module_remember_option_present.found > 0 + + - name: 'Limit Password Reuse: password-auth - Ensure authselect changes are applied' + ansible.builtin.command: + cmd: authselect apply-changes -b + when: + - result_authselect_present.stat.exists + - (result_pam_remember_add is defined and result_pam_remember_add.changed) or + (result_pam_remember_edit is defined and result_pam_remember_edit.changed) + when: + - '"pam" in ansible_facts.packages' + - result_pam_file_present.stat.exists + tags: + - CCE-83478-8 + - CJIS-5.6.2.1.1 + - DISA-STIG-RHEL-08-020220 + - NIST-800-171-3.5.8 + - NIST-800-53-IA-5(1)(e) + - NIST-800-53-IA-5(f) + - PCI-DSS-Req-8.2.5 + - accounts_password_pam_pwhistory_remember_password_auth + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if rpm --quiet -q pam; then + +var_password_pam_remember='' +var_password_pam_remember_control_flag='' + + +var_password_pam_remember_control_flag="$(echo $var_password_pam_remember_control_flag | cut -d \, -f 1)" + +if [ -e "/etc/pam.d/password-auth" ] ; then + PAM_FILE_PATH="/etc/pam.d/password-auth" + if [ -f /usr/bin/authselect ]; then + if ! authselect check; then + echo " + authselect integrity check failed. Remediation aborted! + This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact. + It is not recommended to manually edit the PAM files when authselect tool is available. + In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended." + exit 1 + fi + CURRENT_PROFILE=$(authselect current -r | awk '{ print $1 }') + # If not already in use, a custom profile is created preserving the enabled features. + if [[ ! $CURRENT_PROFILE == custom/* ]]; then + ENABLED_FEATURES=$(authselect current | tail -n+3 | awk '{ print $2 }') + authselect create-profile hardening -b $CURRENT_PROFILE + CURRENT_PROFILE="custom/hardening" + + authselect apply-changes -b --backup=before-hardening-custom-profile + authselect select $CURRENT_PROFILE + for feature in $ENABLED_FEATURES; do + authselect enable-feature $feature; + done + + authselect apply-changes -b --backup=after-hardening-custom-profile + fi + PAM_FILE_NAME=$(basename "/etc/pam.d/password-auth") + PAM_FILE_PATH="/etc/authselect/$CURRENT_PROFILE/$PAM_FILE_NAME" + + authselect apply-changes -b + fi + if ! grep -qP '^\s*password\s+'"$var_password_pam_remember_control_flag"'\s+pam_pwhistory.so\s*.*' "$PAM_FILE_PATH"; then + # Line matching group + control + module was not found. Check group + module. + if [ "$(grep -cP '^\s*password\s+.*\s+pam_pwhistory.so\s*' "$PAM_FILE_PATH")" -eq 1 ]; then + # The control is updated only if one single line matches. + sed -i -E --follow-symlinks 's/^(\s*password\s+).*(\bpam_pwhistory.so.*)/\1'"$var_password_pam_remember_control_flag"' \2/' "$PAM_FILE_PATH" + else + LAST_MATCH_LINE=$(grep -nP "^password.*requisite.*pam_pwquality\.so" "$PAM_FILE_PATH" | tail -n 1 | cut -d: -f 1) + if [ ! -z $LAST_MATCH_LINE ]; then + sed -i --follow-symlinks $LAST_MATCH_LINE' a password '"$var_password_pam_remember_control_flag"' pam_pwhistory.so' "$PAM_FILE_PATH" + else + echo 'password '"$var_password_pam_remember_control_flag"' pam_pwhistory.so' >> "$PAM_FILE_PATH" + fi + fi + fi + # Check the option + if ! grep -qP '^\s*password\s+'"$var_password_pam_remember_control_flag"'\s+pam_pwhistory.so\s*.*\sremember\b' "$PAM_FILE_PATH"; then + sed -i -E --follow-symlinks '/\s*password\s+'"$var_password_pam_remember_control_flag"'\s+pam_pwhistory.so.*/ s/$/ remember='"$var_password_pam_remember"'/' "$PAM_FILE_PATH" + else + sed -i -E --follow-symlinks 's/(\s*password\s+'"$var_password_pam_remember_control_flag"'\s+pam_pwhistory.so\s+.*)('"remember"'=)[[:alnum:]]+\s*(.*)/\1\2'"$var_password_pam_remember"' \3/' "$PAM_FILE_PATH" + fi + if [ -f /usr/bin/authselect ]; then + + authselect apply-changes -b + fi +else + echo "/etc/pam.d/password-auth was not found" >&2 +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + + Limit Password Reuse: system-auth + Do not allow users to reuse recent passwords. This can be accomplished by using the +remember option for the pam_pwhistory PAM module. + +In the file /etc/pam.d/system-auth, make sure the parameter +remember is present and it has a value equal to or greater than + +For example: +password control_flag pam_pwhistory.so ...existing_options... remember= use_authtok +control_flag should be one of the next values: + If the system relies on authselect tool to manage PAM settings, the remediation +will also use authselect tool. However, if any manual modification was made in +PAM files, the authselect integrity check will fail and the remediation will be +aborted in order to preserve intentional changes. In this case, an informative message will +be shown in the remediation report. + 1 + 12 + 15 + 16 + 5 + 5.6.2.1.1 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.03 + DSS06.10 + 3.5.8 + CCI-000200 + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.2 + 4.3.3.7.4 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + A.18.1.4 + A.7.1.1 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.2 + A.9.4.3 + IA-5(f) + IA-5(1)(e) + PR.AC-1 + PR.AC-6 + PR.AC-7 + Req-8.2.5 + SRG-OS-000077-GPOS-00045 + SRG-OS-000077-VMM-000440 + RHEL-08-020221 + 5.5.3 + SV-251717r810415_rule + Preventing re-use of previous passwords helps ensure that a compromised password is not re-used by a user. + + CCE-83480-4 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83480-4 + - CJIS-5.6.2.1.1 + - DISA-STIG-RHEL-08-020221 + - NIST-800-171-3.5.8 + - NIST-800-53-IA-5(1)(e) + - NIST-800-53-IA-5(f) + - PCI-DSS-Req-8.2.5 + - accounts_password_pam_pwhistory_remember_system_auth + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed +- name: XCCDF Value var_password_pam_remember # promote to variable + set_fact: + var_password_pam_remember: !!str + tags: + - always +- name: XCCDF Value var_password_pam_remember_control_flag # promote to variable + set_fact: + var_password_pam_remember_control_flag: !!str + tags: + - always + +- name: 'Limit Password Reuse: system-auth - Check if /etc/pam.d/system-auth file + is present' + ansible.builtin.stat: + path: /etc/pam.d/system-auth + register: result_pam_file_present + when: '"pam" in ansible_facts.packages' + tags: + - CCE-83480-4 + - CJIS-5.6.2.1.1 + - DISA-STIG-RHEL-08-020221 + - NIST-800-171-3.5.8 + - NIST-800-53-IA-5(1)(e) + - NIST-800-53-IA-5(f) + - PCI-DSS-Req-8.2.5 + - accounts_password_pam_pwhistory_remember_system_auth + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + +- name: 'Limit Password Reuse: system-auth - Check the proper remediation for the + system' + block: + + - name: 'Limit Password Reuse: system-auth - Define the PAM file to be edited as + a local fact' + ansible.builtin.set_fact: + pam_file_path: /etc/pam.d/system-auth + + - name: 'Limit Password Reuse: system-auth - Check if system relies on authselect' + ansible.builtin.stat: + path: /usr/bin/authselect + register: result_authselect_present + + - name: 'Limit Password Reuse: system-auth - Remediate using authselect' + block: + + - name: 'Limit Password Reuse: system-auth - Check integrity of authselect current + profile' + ansible.builtin.command: + cmd: authselect check + register: result_authselect_check_cmd + changed_when: false + ignore_errors: true + + - name: 'Limit Password Reuse: system-auth - Informative message based on the + authselect integrity check result' + ansible.builtin.assert: + that: + - result_authselect_check_cmd is success + fail_msg: + - authselect integrity check failed. Remediation aborted! + - This remediation could not be applied because an authselect profile was + not selected or the selected profile is not intact. + - It is not recommended to manually edit the PAM files when authselect tool + is available. + - In cases where the default authselect profile does not cover a specific + demand, a custom authselect profile is recommended. + success_msg: + - authselect integrity check passed + + - name: 'Limit Password Reuse: system-auth - Get authselect current profile' + ansible.builtin.shell: + cmd: authselect current -r | awk '{ print $1 }' + register: result_authselect_profile + changed_when: false + when: + - result_authselect_check_cmd is success + + - name: 'Limit Password Reuse: system-auth - Define the current authselect profile + as a local fact' + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: '{{ result_authselect_profile.stdout }}' + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is match("custom/") + + - name: 'Limit Password Reuse: system-auth - Define the new authselect custom + profile as a local fact' + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: custom/hardening + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is not match("custom/") + + - name: 'Limit Password Reuse: system-auth - Get authselect current features to + also enable them in the custom profile' + ansible.builtin.shell: + cmd: authselect current | tail -n+3 | awk '{ print $2 }' + register: result_authselect_features + changed_when: false + when: + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + + - name: 'Limit Password Reuse: system-auth - Check if any custom profile with + the same name was already created' + ansible.builtin.stat: + path: /etc/authselect/{{ authselect_custom_profile }} + register: result_authselect_custom_profile_present + changed_when: false + when: + - authselect_current_profile is not match("custom/") + + - name: 'Limit Password Reuse: system-auth - Create an authselect custom profile + based on the current profile' + ansible.builtin.command: + cmd: authselect create-profile hardening -b {{ authselect_current_profile + }} + when: + - result_authselect_check_cmd is success + - authselect_current_profile is not match("custom/") + - not result_authselect_custom_profile_present.stat.exists + + - name: 'Limit Password Reuse: system-auth - Ensure authselect changes are applied' + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=before-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: 'Limit Password Reuse: system-auth - Ensure the authselect custom profile + is selected' + ansible.builtin.command: + cmd: authselect select {{ authselect_custom_profile }} + register: result_pam_authselect_select_profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: 'Limit Password Reuse: system-auth - Restore the authselect features in + the custom profile' + ansible.builtin.command: + cmd: authselect enable-feature {{ item }} + loop: '{{ result_authselect_features.stdout_lines }}' + register: result_pam_authselect_restore_features + when: + - result_authselect_profile is not skipped + - result_authselect_features is not skipped + - result_pam_authselect_select_profile is not skipped + + - name: 'Limit Password Reuse: system-auth - Ensure authselect changes are applied' + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=after-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - result_pam_authselect_restore_features is not skipped + + - name: 'Limit Password Reuse: system-auth - Change the PAM file to be edited + according to the custom authselect profile' + ansible.builtin.set_fact: + pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path + | basename }} + when: + - result_authselect_present.stat.exists + + - name: 'Limit Password Reuse: system-auth - Check if expected PAM module line is + present in {{ pam_file_path }}' + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + regexp: ^\s*password\s+{{ var_password_pam_remember_control_flag.split(",")[0] + }}\s+pam_pwhistory.so\s*.* + state: absent + check_mode: true + changed_when: false + register: result_pam_line_present + + - name: 'Limit Password Reuse: system-auth - Include or update the PAM module line + in {{ pam_file_path }}' + block: + + - name: 'Limit Password Reuse: system-auth - Check if required PAM module line + is present in {{ pam_file_path }} with different control' + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + regexp: ^\s*password\s+.*\s+pam_pwhistory.so\s* + state: absent + check_mode: true + changed_when: false + register: result_pam_line_other_control_present + + - name: 'Limit Password Reuse: system-auth - Ensure the correct control for the + required PAM module line in {{ pam_file_path }}' + ansible.builtin.replace: + dest: '{{ pam_file_path }}' + regexp: ^(\s*password\s+).*(\bpam_pwhistory.so.*) + replace: \1{{ var_password_pam_remember_control_flag.split(",")[0] }} \2 + register: result_pam_module_edit + when: + - result_pam_line_other_control_present.found == 1 + + - name: 'Limit Password Reuse: system-auth - Ensure the required PAM module line + is included in {{ pam_file_path }}' + ansible.builtin.lineinfile: + dest: '{{ pam_file_path }}' + insertafter: ^password.*requisite.*pam_pwquality\.so + line: password {{ var_password_pam_remember_control_flag.split(",")[0] + }} pam_pwhistory.so + register: result_pam_module_add + when: + - result_pam_line_other_control_present.found == 0 or result_pam_line_other_control_present.found + > 1 + + - name: 'Limit Password Reuse: system-auth - Ensure authselect changes are applied' + ansible.builtin.command: + cmd: authselect apply-changes -b + when: | + result_authselect_present is defined and result_authselect_present.stat.exists and ((result_pam_module_add is defined and result_pam_module_add.changed) or (result_pam_module_edit is defined and result_pam_module_edit.changed)) + when: + - result_pam_line_present.found is defined + - result_pam_line_present.found == 0 + + - name: 'Limit Password Reuse: system-auth - Check if the required PAM module option + is present in {{ pam_file_path }}' + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + regexp: ^\s*password\s+{{ var_password_pam_remember_control_flag.split(",")[0] + }}\s+pam_pwhistory.so\s*.*\sremember\b + state: absent + check_mode: true + changed_when: false + register: result_pam_module_remember_option_present + + - name: 'Limit Password Reuse: system-auth - Ensure the "remember" PAM option for + "pam_pwhistory.so" is included in {{ pam_file_path }}' + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + backrefs: true + regexp: ^(\s*password\s+{{ var_password_pam_remember_control_flag.split(",")[0] + }}\s+pam_pwhistory.so.*) + line: \1 remember={{ var_password_pam_remember }} + state: present + register: result_pam_remember_add + when: + - result_pam_module_remember_option_present.found == 0 + + - name: 'Limit Password Reuse: system-auth - Ensure the required value for "remember" + PAM option from "pam_pwhistory.so" in {{ pam_file_path }}' + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + backrefs: true + regexp: ^(\s*password\s+{{ var_password_pam_remember_control_flag.split(",")[0] + }}\s+pam_pwhistory.so\s+.*)(remember)=[0-9a-zA-Z]+\s*(.*) + line: \1\2={{ var_password_pam_remember }} \3 + register: result_pam_remember_edit + when: + - result_pam_module_remember_option_present.found > 0 + + - name: 'Limit Password Reuse: system-auth - Ensure authselect changes are applied' + ansible.builtin.command: + cmd: authselect apply-changes -b + when: + - result_authselect_present.stat.exists + - (result_pam_remember_add is defined and result_pam_remember_add.changed) or + (result_pam_remember_edit is defined and result_pam_remember_edit.changed) + when: + - '"pam" in ansible_facts.packages' + - result_pam_file_present.stat.exists + tags: + - CCE-83480-4 + - CJIS-5.6.2.1.1 + - DISA-STIG-RHEL-08-020221 + - NIST-800-171-3.5.8 + - NIST-800-53-IA-5(1)(e) + - NIST-800-53-IA-5(f) + - PCI-DSS-Req-8.2.5 + - accounts_password_pam_pwhistory_remember_system_auth + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if rpm --quiet -q pam; then + +var_password_pam_remember='' +var_password_pam_remember_control_flag='' + + +var_password_pam_remember_control_flag="$(echo $var_password_pam_remember_control_flag | cut -d \, -f 1)" + +if [ -e "/etc/pam.d/system-auth" ] ; then + PAM_FILE_PATH="/etc/pam.d/system-auth" + if [ -f /usr/bin/authselect ]; then + if ! authselect check; then + echo " + authselect integrity check failed. Remediation aborted! + This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact. + It is not recommended to manually edit the PAM files when authselect tool is available. + In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended." + exit 1 + fi + CURRENT_PROFILE=$(authselect current -r | awk '{ print $1 }') + # If not already in use, a custom profile is created preserving the enabled features. + if [[ ! $CURRENT_PROFILE == custom/* ]]; then + ENABLED_FEATURES=$(authselect current | tail -n+3 | awk '{ print $2 }') + authselect create-profile hardening -b $CURRENT_PROFILE + CURRENT_PROFILE="custom/hardening" + + authselect apply-changes -b --backup=before-hardening-custom-profile + authselect select $CURRENT_PROFILE + for feature in $ENABLED_FEATURES; do + authselect enable-feature $feature; + done + + authselect apply-changes -b --backup=after-hardening-custom-profile + fi + PAM_FILE_NAME=$(basename "/etc/pam.d/system-auth") + PAM_FILE_PATH="/etc/authselect/$CURRENT_PROFILE/$PAM_FILE_NAME" + + authselect apply-changes -b + fi + if ! grep -qP '^\s*password\s+'"$var_password_pam_remember_control_flag"'\s+pam_pwhistory.so\s*.*' "$PAM_FILE_PATH"; then + # Line matching group + control + module was not found. Check group + module. + if [ "$(grep -cP '^\s*password\s+.*\s+pam_pwhistory.so\s*' "$PAM_FILE_PATH")" -eq 1 ]; then + # The control is updated only if one single line matches. + sed -i -E --follow-symlinks 's/^(\s*password\s+).*(\bpam_pwhistory.so.*)/\1'"$var_password_pam_remember_control_flag"' \2/' "$PAM_FILE_PATH" + else + LAST_MATCH_LINE=$(grep -nP "^password.*requisite.*pam_pwquality\.so" "$PAM_FILE_PATH" | tail -n 1 | cut -d: -f 1) + if [ ! -z $LAST_MATCH_LINE ]; then + sed -i --follow-symlinks $LAST_MATCH_LINE' a password '"$var_password_pam_remember_control_flag"' pam_pwhistory.so' "$PAM_FILE_PATH" + else + echo 'password '"$var_password_pam_remember_control_flag"' pam_pwhistory.so' >> "$PAM_FILE_PATH" + fi + fi + fi + # Check the option + if ! grep -qP '^\s*password\s+'"$var_password_pam_remember_control_flag"'\s+pam_pwhistory.so\s*.*\sremember\b' "$PAM_FILE_PATH"; then + sed -i -E --follow-symlinks '/\s*password\s+'"$var_password_pam_remember_control_flag"'\s+pam_pwhistory.so.*/ s/$/ remember='"$var_password_pam_remember"'/' "$PAM_FILE_PATH" + else + sed -i -E --follow-symlinks 's/(\s*password\s+'"$var_password_pam_remember_control_flag"'\s+pam_pwhistory.so\s+.*)('"remember"'=)[[:alnum:]]+\s*(.*)/\1\2'"$var_password_pam_remember"' \3/' "$PAM_FILE_PATH" + fi + if [ -f /usr/bin/authselect ]; then + + authselect apply-changes -b + fi +else + echo "/etc/pam.d/system-auth was not found" >&2 +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + + Limit Password Reuse + Do not allow users to reuse recent passwords. This can be accomplished by using the +remember option for the pam_unix or pam_pwhistory PAM modules. + If the system relies on authselect tool to manage PAM settings, the remediation +will also use authselect tool. However, if any manual modification was made in +PAM files, the authselect integrity check will fail and the remediation will be +aborted in order to preserve intentional changes. In this case, an informative message will +be shown in the remediation report. + BP28(R18) + 1 + 12 + 15 + 16 + 5 + 5.6.2.1.1 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.03 + DSS06.10 + 3.5.8 + CCI-000200 + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.2 + 4.3.3.7.4 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + A.18.1.4 + A.7.1.1 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.2 + A.9.4.3 + IA-5(f) + IA-5(1)(e) + PR.AC-1 + PR.AC-6 + PR.AC-7 + Req-8.2.5 + SRG-OS-000077-GPOS-00045 + SRG-OS-000077-VMM-000440 + 5.4.3 + Preventing re-use of previous passwords helps ensure that a compromised password is not re-used by a user. + + CCE-80666-1 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80666-1 + - CJIS-5.6.2.1.1 + - NIST-800-171-3.5.8 + - NIST-800-53-IA-5(1)(e) + - NIST-800-53-IA-5(f) + - PCI-DSS-Req-8.2.5 + - accounts_password_pam_unix_remember + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed +- name: XCCDF Value var_password_pam_unix_remember # promote to variable + set_fact: + var_password_pam_unix_remember: !!str + tags: + - always + +- name: Limit Password Reuse - Check if /etc/pam.d/system-auth file is present + ansible.builtin.stat: + path: /etc/pam.d/system-auth + register: result_pam_file_present + when: '"pam" in ansible_facts.packages' + tags: + - CCE-80666-1 + - CJIS-5.6.2.1.1 + - NIST-800-171-3.5.8 + - NIST-800-53-IA-5(1)(e) + - NIST-800-53-IA-5(f) + - PCI-DSS-Req-8.2.5 + - accounts_password_pam_unix_remember + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + +- name: Limit Password Reuse - Check the proper remediation for the system + block: + + - name: Limit Password Reuse - Define the PAM file to be edited as a local fact + ansible.builtin.set_fact: + pam_file_path: /etc/pam.d/system-auth + + - name: Limit Password Reuse - Check if system relies on authselect + ansible.builtin.stat: + path: /usr/bin/authselect + register: result_authselect_present + + - name: Limit Password Reuse - Remediate using authselect + block: + + - name: Limit Password Reuse - Check integrity of authselect current profile + ansible.builtin.command: + cmd: authselect check + register: result_authselect_check_cmd + changed_when: false + ignore_errors: true + + - name: Limit Password Reuse - Informative message based on the authselect integrity + check result + ansible.builtin.assert: + that: + - result_authselect_check_cmd is success + fail_msg: + - authselect integrity check failed. Remediation aborted! + - This remediation could not be applied because an authselect profile was + not selected or the selected profile is not intact. + - It is not recommended to manually edit the PAM files when authselect tool + is available. + - In cases where the default authselect profile does not cover a specific + demand, a custom authselect profile is recommended. + success_msg: + - authselect integrity check passed + + - name: Limit Password Reuse - Get authselect current profile + ansible.builtin.shell: + cmd: authselect current -r | awk '{ print $1 }' + register: result_authselect_profile + changed_when: false + when: + - result_authselect_check_cmd is success + + - name: Limit Password Reuse - Define the current authselect profile as a local + fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: '{{ result_authselect_profile.stdout }}' + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is match("custom/") + + - name: Limit Password Reuse - Define the new authselect custom profile as a local + fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: custom/hardening + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is not match("custom/") + + - name: Limit Password Reuse - Get authselect current features to also enable + them in the custom profile + ansible.builtin.shell: + cmd: authselect current | tail -n+3 | awk '{ print $2 }' + register: result_authselect_features + changed_when: false + when: + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + + - name: Limit Password Reuse - Check if any custom profile with the same name + was already created + ansible.builtin.stat: + path: /etc/authselect/{{ authselect_custom_profile }} + register: result_authselect_custom_profile_present + changed_when: false + when: + - authselect_current_profile is not match("custom/") + + - name: Limit Password Reuse - Create an authselect custom profile based on the + current profile + ansible.builtin.command: + cmd: authselect create-profile hardening -b {{ authselect_current_profile + }} + when: + - result_authselect_check_cmd is success + - authselect_current_profile is not match("custom/") + - not result_authselect_custom_profile_present.stat.exists + + - name: Limit Password Reuse - Ensure authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=before-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Limit Password Reuse - Ensure the authselect custom profile is selected + ansible.builtin.command: + cmd: authselect select {{ authselect_custom_profile }} + register: result_pam_authselect_select_profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Limit Password Reuse - Restore the authselect features in the custom profile + ansible.builtin.command: + cmd: authselect enable-feature {{ item }} + loop: '{{ result_authselect_features.stdout_lines }}' + register: result_pam_authselect_restore_features + when: + - result_authselect_profile is not skipped + - result_authselect_features is not skipped + - result_pam_authselect_select_profile is not skipped + + - name: Limit Password Reuse - Ensure authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=after-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - result_pam_authselect_restore_features is not skipped + + - name: Limit Password Reuse - Change the PAM file to be edited according to the + custom authselect profile + ansible.builtin.set_fact: + pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path + | basename }} + when: + - result_authselect_present.stat.exists + + - name: Limit Password Reuse - Check if expected PAM module line is present in {{ + pam_file_path }} + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + regexp: ^\s*password\s+requisite\s+pam_pwhistory.so\s*.* + state: absent + check_mode: true + changed_when: false + register: result_pam_line_present + + - name: Limit Password Reuse - Include or update the PAM module line in {{ pam_file_path + }} + block: + + - name: Limit Password Reuse - Check if required PAM module line is present in + {{ pam_file_path }} with different control + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + regexp: ^\s*password\s+.*\s+pam_pwhistory.so\s* + state: absent + check_mode: true + changed_when: false + register: result_pam_line_other_control_present + + - name: Limit Password Reuse - Ensure the correct control for the required PAM + module line in {{ pam_file_path }} + ansible.builtin.replace: + dest: '{{ pam_file_path }}' + regexp: ^(\s*password\s+).*(\bpam_pwhistory.so.*) + replace: \1requisite \2 + register: result_pam_module_edit + when: + - result_pam_line_other_control_present.found == 1 + + - name: Limit Password Reuse - Ensure the required PAM module line is included + in {{ pam_file_path }} + ansible.builtin.lineinfile: + dest: '{{ pam_file_path }}' + insertafter: ^password.*requisite.*pam_pwquality\.so + line: password requisite pam_pwhistory.so + register: result_pam_module_add + when: + - result_pam_line_other_control_present.found == 0 or result_pam_line_other_control_present.found + > 1 + + - name: Limit Password Reuse - Ensure authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: | + result_authselect_present is defined and result_authselect_present.stat.exists and ((result_pam_module_add is defined and result_pam_module_add.changed) or (result_pam_module_edit is defined and result_pam_module_edit.changed)) + when: + - result_pam_line_present.found is defined + - result_pam_line_present.found == 0 + + - name: Limit Password Reuse - Check if the required PAM module option is present + in {{ pam_file_path }} + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + regexp: ^\s*password\s+requisite\s+pam_pwhistory.so\s*.*\sremember\b + state: absent + check_mode: true + changed_when: false + register: result_pam_module_remember_option_present + + - name: Limit Password Reuse - Ensure the "remember" PAM option for "pam_pwhistory.so" + is included in {{ pam_file_path }} + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + backrefs: true + regexp: ^(\s*password\s+requisite\s+pam_pwhistory.so.*) + line: \1 remember={{ var_password_pam_unix_remember }} + state: present + register: result_pam_remember_add + when: + - result_pam_module_remember_option_present.found == 0 + + - name: Limit Password Reuse - Ensure the required value for "remember" PAM option + from "pam_pwhistory.so" in {{ pam_file_path }} + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + backrefs: true + regexp: ^(\s*password\s+requisite\s+pam_pwhistory.so\s+.*)(remember)=[0-9a-zA-Z]+\s*(.*) + line: \1\2={{ var_password_pam_unix_remember }} \3 + register: result_pam_remember_edit + when: + - result_pam_module_remember_option_present.found > 0 + + - name: Limit Password Reuse - Ensure authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: + - result_authselect_present.stat.exists + - (result_pam_remember_add is defined and result_pam_remember_add.changed) or + (result_pam_remember_edit is defined and result_pam_remember_edit.changed) + when: + - '"pam" in ansible_facts.packages' + - result_pam_file_present.stat.exists + tags: + - CCE-80666-1 + - CJIS-5.6.2.1.1 + - NIST-800-171-3.5.8 + - NIST-800-53-IA-5(1)(e) + - NIST-800-53-IA-5(f) + - PCI-DSS-Req-8.2.5 + - accounts_password_pam_unix_remember + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + +- name: Limit Password Reuse - Check if /etc/pam.d/password-auth file is present + ansible.builtin.stat: + path: /etc/pam.d/password-auth + register: result_pam_file_present + when: '"pam" in ansible_facts.packages' + tags: + - CCE-80666-1 + - CJIS-5.6.2.1.1 + - NIST-800-171-3.5.8 + - NIST-800-53-IA-5(1)(e) + - NIST-800-53-IA-5(f) + - PCI-DSS-Req-8.2.5 + - accounts_password_pam_unix_remember + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + +- name: Limit Password Reuse - Check the proper remediation for the system + block: + + - name: Limit Password Reuse - Define the PAM file to be edited as a local fact + ansible.builtin.set_fact: + pam_file_path: /etc/pam.d/password-auth + + - name: Limit Password Reuse - Check if system relies on authselect + ansible.builtin.stat: + path: /usr/bin/authselect + register: result_authselect_present + + - name: Limit Password Reuse - Remediate using authselect + block: + + - name: Limit Password Reuse - Check integrity of authselect current profile + ansible.builtin.command: + cmd: authselect check + register: result_authselect_check_cmd + changed_when: false + ignore_errors: true + + - name: Limit Password Reuse - Informative message based on the authselect integrity + check result + ansible.builtin.assert: + that: + - result_authselect_check_cmd is success + fail_msg: + - authselect integrity check failed. Remediation aborted! + - This remediation could not be applied because an authselect profile was + not selected or the selected profile is not intact. + - It is not recommended to manually edit the PAM files when authselect tool + is available. + - In cases where the default authselect profile does not cover a specific + demand, a custom authselect profile is recommended. + success_msg: + - authselect integrity check passed + + - name: Limit Password Reuse - Get authselect current profile + ansible.builtin.shell: + cmd: authselect current -r | awk '{ print $1 }' + register: result_authselect_profile + changed_when: false + when: + - result_authselect_check_cmd is success + + - name: Limit Password Reuse - Define the current authselect profile as a local + fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: '{{ result_authselect_profile.stdout }}' + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is match("custom/") + + - name: Limit Password Reuse - Define the new authselect custom profile as a local + fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: custom/hardening + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is not match("custom/") + + - name: Limit Password Reuse - Get authselect current features to also enable + them in the custom profile + ansible.builtin.shell: + cmd: authselect current | tail -n+3 | awk '{ print $2 }' + register: result_authselect_features + changed_when: false + when: + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + + - name: Limit Password Reuse - Check if any custom profile with the same name + was already created + ansible.builtin.stat: + path: /etc/authselect/{{ authselect_custom_profile }} + register: result_authselect_custom_profile_present + changed_when: false + when: + - authselect_current_profile is not match("custom/") + + - name: Limit Password Reuse - Create an authselect custom profile based on the + current profile + ansible.builtin.command: + cmd: authselect create-profile hardening -b {{ authselect_current_profile + }} + when: + - result_authselect_check_cmd is success + - authselect_current_profile is not match("custom/") + - not result_authselect_custom_profile_present.stat.exists + + - name: Limit Password Reuse - Ensure authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=before-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Limit Password Reuse - Ensure the authselect custom profile is selected + ansible.builtin.command: + cmd: authselect select {{ authselect_custom_profile }} + register: result_pam_authselect_select_profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Limit Password Reuse - Restore the authselect features in the custom profile + ansible.builtin.command: + cmd: authselect enable-feature {{ item }} + loop: '{{ result_authselect_features.stdout_lines }}' + register: result_pam_authselect_restore_features + when: + - result_authselect_profile is not skipped + - result_authselect_features is not skipped + - result_pam_authselect_select_profile is not skipped + + - name: Limit Password Reuse - Ensure authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=after-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - result_pam_authselect_restore_features is not skipped + + - name: Limit Password Reuse - Change the PAM file to be edited according to the + custom authselect profile + ansible.builtin.set_fact: + pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path + | basename }} + when: + - result_authselect_present.stat.exists + + - name: Limit Password Reuse - Check if expected PAM module line is present in {{ + pam_file_path }} + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + regexp: ^\s*password\s+requisite\s+pam_pwhistory.so\s*.* + state: absent + check_mode: true + changed_when: false + register: result_pam_line_present + + - name: Limit Password Reuse - Include or update the PAM module line in {{ pam_file_path + }} + block: + + - name: Limit Password Reuse - Check if required PAM module line is present in + {{ pam_file_path }} with different control + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + regexp: ^\s*password\s+.*\s+pam_pwhistory.so\s* + state: absent + check_mode: true + changed_when: false + register: result_pam_line_other_control_present + + - name: Limit Password Reuse - Ensure the correct control for the required PAM + module line in {{ pam_file_path }} + ansible.builtin.replace: + dest: '{{ pam_file_path }}' + regexp: ^(\s*password\s+).*(\bpam_pwhistory.so.*) + replace: \1requisite \2 + register: result_pam_module_edit + when: + - result_pam_line_other_control_present.found == 1 + + - name: Limit Password Reuse - Ensure the required PAM module line is included + in {{ pam_file_path }} + ansible.builtin.lineinfile: + dest: '{{ pam_file_path }}' + insertafter: ^password.*requisite.*pam_pwquality\.so + line: password requisite pam_pwhistory.so + register: result_pam_module_add + when: + - result_pam_line_other_control_present.found == 0 or result_pam_line_other_control_present.found + > 1 + + - name: Limit Password Reuse - Ensure authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: | + result_authselect_present is defined and result_authselect_present.stat.exists and ((result_pam_module_add is defined and result_pam_module_add.changed) or (result_pam_module_edit is defined and result_pam_module_edit.changed)) + when: + - result_pam_line_present.found is defined + - result_pam_line_present.found == 0 + + - name: Limit Password Reuse - Check if the required PAM module option is present + in {{ pam_file_path }} + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + regexp: ^\s*password\s+requisite\s+pam_pwhistory.so\s*.*\sremember\b + state: absent + check_mode: true + changed_when: false + register: result_pam_module_remember_option_present + + - name: Limit Password Reuse - Ensure the "remember" PAM option for "pam_pwhistory.so" + is included in {{ pam_file_path }} + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + backrefs: true + regexp: ^(\s*password\s+requisite\s+pam_pwhistory.so.*) + line: \1 remember={{ var_password_pam_unix_remember }} + state: present + register: result_pam_remember_add + when: + - result_pam_module_remember_option_present.found == 0 + + - name: Limit Password Reuse - Ensure the required value for "remember" PAM option + from "pam_pwhistory.so" in {{ pam_file_path }} + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + backrefs: true + regexp: ^(\s*password\s+requisite\s+pam_pwhistory.so\s+.*)(remember)=[0-9a-zA-Z]+\s*(.*) + line: \1\2={{ var_password_pam_unix_remember }} \3 + register: result_pam_remember_edit + when: + - result_pam_module_remember_option_present.found > 0 + + - name: Limit Password Reuse - Ensure authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: + - result_authselect_present.stat.exists + - (result_pam_remember_add is defined and result_pam_remember_add.changed) or + (result_pam_remember_edit is defined and result_pam_remember_edit.changed) + when: + - '"pam" in ansible_facts.packages' + - result_pam_file_present.stat.exists + tags: + - CCE-80666-1 + - CJIS-5.6.2.1.1 + - NIST-800-171-3.5.8 + - NIST-800-53-IA-5(1)(e) + - NIST-800-53-IA-5(f) + - PCI-DSS-Req-8.2.5 + - accounts_password_pam_unix_remember + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if rpm --quiet -q pam; then + +var_password_pam_unix_remember='' + + + +if [ -e "/etc/pam.d/system-auth" ] ; then + PAM_FILE_PATH="/etc/pam.d/system-auth" + if [ -f /usr/bin/authselect ]; then + if ! authselect check; then + echo " + authselect integrity check failed. Remediation aborted! + This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact. + It is not recommended to manually edit the PAM files when authselect tool is available. + In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended." + exit 1 + fi + CURRENT_PROFILE=$(authselect current -r | awk '{ print $1 }') + # If not already in use, a custom profile is created preserving the enabled features. + if [[ ! $CURRENT_PROFILE == custom/* ]]; then + ENABLED_FEATURES=$(authselect current | tail -n+3 | awk '{ print $2 }') + authselect create-profile hardening -b $CURRENT_PROFILE + CURRENT_PROFILE="custom/hardening" + + authselect apply-changes -b --backup=before-hardening-custom-profile + authselect select $CURRENT_PROFILE + for feature in $ENABLED_FEATURES; do + authselect enable-feature $feature; + done + + authselect apply-changes -b --backup=after-hardening-custom-profile + fi + PAM_FILE_NAME=$(basename "/etc/pam.d/system-auth") + PAM_FILE_PATH="/etc/authselect/$CURRENT_PROFILE/$PAM_FILE_NAME" + + authselect apply-changes -b + fi + if ! grep -qP '^\s*password\s+'"requisite"'\s+pam_pwhistory.so\s*.*' "$PAM_FILE_PATH"; then + # Line matching group + control + module was not found. Check group + module. + if [ "$(grep -cP '^\s*password\s+.*\s+pam_pwhistory.so\s*' "$PAM_FILE_PATH")" -eq 1 ]; then + # The control is updated only if one single line matches. + sed -i -E --follow-symlinks 's/^(\s*password\s+).*(\bpam_pwhistory.so.*)/\1'"requisite"' \2/' "$PAM_FILE_PATH" + else + LAST_MATCH_LINE=$(grep -nP "^password.*requisite.*pam_pwquality\.so" "$PAM_FILE_PATH" | tail -n 1 | cut -d: -f 1) + if [ ! -z $LAST_MATCH_LINE ]; then + sed -i --follow-symlinks $LAST_MATCH_LINE' a password '"requisite"' pam_pwhistory.so' "$PAM_FILE_PATH" + else + echo 'password '"requisite"' pam_pwhistory.so' >> "$PAM_FILE_PATH" + fi + fi + fi + # Check the option + if ! grep -qP '^\s*password\s+'"requisite"'\s+pam_pwhistory.so\s*.*\sremember\b' "$PAM_FILE_PATH"; then + sed -i -E --follow-symlinks '/\s*password\s+'"requisite"'\s+pam_pwhistory.so.*/ s/$/ remember='"$var_password_pam_unix_remember"'/' "$PAM_FILE_PATH" + else + sed -i -E --follow-symlinks 's/(\s*password\s+'"requisite"'\s+pam_pwhistory.so\s+.*)('"remember"'=)[[:alnum:]]+\s*(.*)/\1\2'"$var_password_pam_unix_remember"' \3/' "$PAM_FILE_PATH" + fi + if [ -f /usr/bin/authselect ]; then + + authselect apply-changes -b + fi +else + echo "/etc/pam.d/system-auth was not found" >&2 +fi + +if [ -e "/etc/pam.d/password-auth" ] ; then + PAM_FILE_PATH="/etc/pam.d/password-auth" + if [ -f /usr/bin/authselect ]; then + if ! authselect check; then + echo " + authselect integrity check failed. Remediation aborted! + This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact. + It is not recommended to manually edit the PAM files when authselect tool is available. + In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended." + exit 1 + fi + CURRENT_PROFILE=$(authselect current -r | awk '{ print $1 }') + # If not already in use, a custom profile is created preserving the enabled features. + if [[ ! $CURRENT_PROFILE == custom/* ]]; then + ENABLED_FEATURES=$(authselect current | tail -n+3 | awk '{ print $2 }') + authselect create-profile hardening -b $CURRENT_PROFILE + CURRENT_PROFILE="custom/hardening" + + authselect apply-changes -b --backup=before-hardening-custom-profile + authselect select $CURRENT_PROFILE + for feature in $ENABLED_FEATURES; do + authselect enable-feature $feature; + done + + authselect apply-changes -b --backup=after-hardening-custom-profile + fi + PAM_FILE_NAME=$(basename "/etc/pam.d/password-auth") + PAM_FILE_PATH="/etc/authselect/$CURRENT_PROFILE/$PAM_FILE_NAME" + + authselect apply-changes -b + fi + if ! grep -qP '^\s*password\s+'"requisite"'\s+pam_pwhistory.so\s*.*' "$PAM_FILE_PATH"; then + # Line matching group + control + module was not found. Check group + module. + if [ "$(grep -cP '^\s*password\s+.*\s+pam_pwhistory.so\s*' "$PAM_FILE_PATH")" -eq 1 ]; then + # The control is updated only if one single line matches. + sed -i -E --follow-symlinks 's/^(\s*password\s+).*(\bpam_pwhistory.so.*)/\1'"requisite"' \2/' "$PAM_FILE_PATH" + else + LAST_MATCH_LINE=$(grep -nP "^password.*requisite.*pam_pwquality\.so" "$PAM_FILE_PATH" | tail -n 1 | cut -d: -f 1) + if [ ! -z $LAST_MATCH_LINE ]; then + sed -i --follow-symlinks $LAST_MATCH_LINE' a password '"requisite"' pam_pwhistory.so' "$PAM_FILE_PATH" + else + echo 'password '"requisite"' pam_pwhistory.so' >> "$PAM_FILE_PATH" + fi + fi + fi + # Check the option + if ! grep -qP '^\s*password\s+'"requisite"'\s+pam_pwhistory.so\s*.*\sremember\b' "$PAM_FILE_PATH"; then + sed -i -E --follow-symlinks '/\s*password\s+'"requisite"'\s+pam_pwhistory.so.*/ s/$/ remember='"$var_password_pam_unix_remember"'/' "$PAM_FILE_PATH" + else + sed -i -E --follow-symlinks 's/(\s*password\s+'"requisite"'\s+pam_pwhistory.so\s+.*)('"remember"'=)[[:alnum:]]+\s*(.*)/\1\2'"$var_password_pam_unix_remember"' \3/' "$PAM_FILE_PATH" + fi + if [ -f /usr/bin/authselect ]; then + + authselect apply-changes -b + fi +else + echo "/etc/pam.d/password-auth was not found" >&2 +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Lock Accounts After Failed Password Attempts + This rule configures the system to lock out accounts after a number of incorrect login attempts +using pam_faillock.so. + +pam_faillock.so module requires multiple entries in pam files. These entries must be carefully +defined to work as expected. In order to avoid errors when manually editing these files, it is +recommended to use the appropriate tools, such as authselect or authconfig, +depending on the OS version. + If the system relies on authselect tool to manage PAM settings, the remediation +will also use authselect tool. However, if any manual modification was made in +PAM files, the authselect integrity check will fail and the remediation will be +aborted in order to preserve intentional changes. In this case, an informative message will +be shown in the remediation report. +If the system supports the /etc/security/faillock.conf file, the pam_faillock +parameters should be defined in faillock.conf file. + BP28(R18) + 1 + 12 + 15 + 16 + 5.5.3 + DSS05.04 + DSS05.10 + DSS06.10 + 3.1.8 + CCI-000044 + CCI-002236 + CCI-002237 + CCI-002238 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + 0421 + 0422 + 0431 + 0974 + 1173 + 1401 + 1504 + 1505 + 1546 + 1557 + 1558 + 1559 + 1560 + 1561 + A.18.1.4 + A.9.2.1 + A.9.2.4 + A.9.3.1 + A.9.4.2 + A.9.4.3 + CM-6(a) + AC-7(a) + PR.AC-7 + FIA_AFL.1 + Req-8.1.6 + SRG-OS-000329-GPOS-00128 + SRG-OS-000021-GPOS-00005 + SRG-OS-000021-VMM-000050 + RHEL-08-020010 + 5.4.2 + 5.5.2 + SV-230332r627750_rule + By limiting the number of failed logon attempts, the risk of unauthorized system access via +user password guessing, also known as brute-forcing, is reduced. Limits are imposed by locking +the account. + + CCE-80667-9 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80667-9 + - CJIS-5.5.3 + - DISA-STIG-RHEL-08-020010 + - NIST-800-171-3.1.8 + - NIST-800-53-AC-7(a) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.1.6 + - accounts_passwords_pam_faillock_deny + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Lock Accounts After Failed Password Attempts - Check if system relies on authselect + tool + ansible.builtin.stat: + path: /usr/bin/authselect + register: result_authselect_present + when: '"pam" in ansible_facts.packages' + tags: + - CCE-80667-9 + - CJIS-5.5.3 + - DISA-STIG-RHEL-08-020010 + - NIST-800-171-3.1.8 + - NIST-800-53-AC-7(a) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.1.6 + - accounts_passwords_pam_faillock_deny + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Lock Accounts After Failed Password Attempts - Remediation where authselect + tool is present + block: + + - name: Lock Accounts After Failed Password Attempts - Check integrity of authselect + current profile + ansible.builtin.command: + cmd: authselect check + register: result_authselect_check_cmd + changed_when: false + ignore_errors: true + + - name: Lock Accounts After Failed Password Attempts - Informative message based + on the authselect integrity check result + ansible.builtin.assert: + that: + - result_authselect_check_cmd is success + fail_msg: + - authselect integrity check failed. Remediation aborted! + - This remediation could not be applied because an authselect profile was not + selected or the selected profile is not intact. + - It is not recommended to manually edit the PAM files when authselect tool + is available. + - In cases where the default authselect profile does not cover a specific demand, + a custom authselect profile is recommended. + success_msg: + - authselect integrity check passed + + - name: Lock Accounts After Failed Password Attempts - Get authselect current features + ansible.builtin.shell: + cmd: authselect current | tail -n+3 | awk '{ print $2 }' + register: result_authselect_features + changed_when: false + when: + - result_authselect_check_cmd is success + + - name: Lock Accounts After Failed Password Attempts - Ensure "with-faillock" feature + is enabled using authselect tool + ansible.builtin.command: + cmd: authselect enable-feature with-faillock + register: result_authselect_enable_feature_cmd + when: + - result_authselect_check_cmd is success + - result_authselect_features.stdout is not search("with-faillock") + + - name: Lock Accounts After Failed Password Attempts - Ensure authselect changes + are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: + - result_authselect_enable_feature_cmd is not skipped + - result_authselect_enable_feature_cmd is success + when: + - '"pam" in ansible_facts.packages' + - result_authselect_present.stat.exists + tags: + - CCE-80667-9 + - CJIS-5.5.3 + - DISA-STIG-RHEL-08-020010 + - NIST-800-171-3.1.8 + - NIST-800-53-AC-7(a) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.1.6 + - accounts_passwords_pam_faillock_deny + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Lock Accounts After Failed Password Attempts - Remediation where authselect + tool is not present + block: + + - name: Lock Accounts After Failed Password Attempts - Check if pam_faillock.so + is already enabled + ansible.builtin.lineinfile: + path: /etc/pam.d/system-auth + regexp: .*auth.*pam_faillock\.so (preauth|authfail) + state: absent + check_mode: true + changed_when: false + register: result_pam_faillock_is_enabled + + - name: Lock Accounts After Failed Password Attempts - Enable pam_faillock.so preauth + editing PAM files + ansible.builtin.lineinfile: + path: '{{ item }}' + line: auth required pam_faillock.so preauth + insertbefore: ^auth.*sufficient.*pam_unix\.so.* + state: present + loop: + - /etc/pam.d/system-auth + - /etc/pam.d/password-auth + when: + - result_pam_faillock_is_enabled.found == 0 + + - name: Lock Accounts After Failed Password Attempts - Enable pam_faillock.so authfail + editing PAM files + ansible.builtin.lineinfile: + path: '{{ item }}' + line: auth required pam_faillock.so authfail + insertbefore: ^auth.*required.*pam_deny\.so.* + state: present + loop: + - /etc/pam.d/system-auth + - /etc/pam.d/password-auth + when: + - result_pam_faillock_is_enabled.found == 0 + + - name: Lock Accounts After Failed Password Attempts - Enable pam_faillock.so account + section editing PAM files + ansible.builtin.lineinfile: + path: '{{ item }}' + line: account required pam_faillock.so + insertbefore: ^account.*required.*pam_unix\.so.* + state: present + loop: + - /etc/pam.d/system-auth + - /etc/pam.d/password-auth + when: + - result_pam_faillock_is_enabled.found == 0 + when: + - '"pam" in ansible_facts.packages' + - not result_authselect_present.stat.exists + tags: + - CCE-80667-9 + - CJIS-5.5.3 + - DISA-STIG-RHEL-08-020010 + - NIST-800-171-3.1.8 + - NIST-800-53-AC-7(a) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.1.6 + - accounts_passwords_pam_faillock_deny + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy +- name: XCCDF Value var_accounts_passwords_pam_faillock_deny # promote to variable + set_fact: + var_accounts_passwords_pam_faillock_deny: !!str + tags: + - always + +- name: Lock Accounts After Failed Password Attempts - Check the presence of /etc/security/faillock.conf + file + ansible.builtin.stat: + path: /etc/security/faillock.conf + register: result_faillock_conf_check + when: '"pam" in ansible_facts.packages' + tags: + - CCE-80667-9 + - CJIS-5.5.3 + - DISA-STIG-RHEL-08-020010 + - NIST-800-171-3.1.8 + - NIST-800-53-AC-7(a) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.1.6 + - accounts_passwords_pam_faillock_deny + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Lock Accounts After Failed Password Attempts - Ensure the pam_faillock.so + deny parameter in /etc/security/faillock.conf + ansible.builtin.lineinfile: + path: /etc/security/faillock.conf + regexp: ^\s*deny\s*= + line: deny = {{ var_accounts_passwords_pam_faillock_deny }} + state: present + when: + - '"pam" in ansible_facts.packages' + - result_faillock_conf_check.stat.exists + tags: + - CCE-80667-9 + - CJIS-5.5.3 + - DISA-STIG-RHEL-08-020010 + - NIST-800-171-3.1.8 + - NIST-800-53-AC-7(a) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.1.6 + - accounts_passwords_pam_faillock_deny + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Lock Accounts After Failed Password Attempts - Ensure the pam_faillock.so + deny parameter not in PAM files + block: + + - name: Lock Accounts After Failed Password Attempts - Check if /etc/pam.d/system-auth + file is present + ansible.builtin.stat: + path: /etc/pam.d/system-auth + register: result_pam_file_present + + - name: Lock Accounts After Failed Password Attempts - Check the proper remediation + for the system + block: + + - name: Lock Accounts After Failed Password Attempts - Define the PAM file to + be edited as a local fact + ansible.builtin.set_fact: + pam_file_path: /etc/pam.d/system-auth + + - name: Lock Accounts After Failed Password Attempts - Check if system relies + on authselect + ansible.builtin.stat: + path: /usr/bin/authselect + register: result_authselect_present + + - name: Lock Accounts After Failed Password Attempts - Remediate using authselect + block: + + - name: Lock Accounts After Failed Password Attempts - Check integrity of authselect + current profile + ansible.builtin.command: + cmd: authselect check + register: result_authselect_check_cmd + changed_when: false + ignore_errors: true + + - name: Lock Accounts After Failed Password Attempts - Informative message based + on the authselect integrity check result + ansible.builtin.assert: + that: + - result_authselect_check_cmd is success + fail_msg: + - authselect integrity check failed. Remediation aborted! + - This remediation could not be applied because an authselect profile was + not selected or the selected profile is not intact. + - It is not recommended to manually edit the PAM files when authselect tool + is available. + - In cases where the default authselect profile does not cover a specific + demand, a custom authselect profile is recommended. + success_msg: + - authselect integrity check passed + + - name: Lock Accounts After Failed Password Attempts - Get authselect current + profile + ansible.builtin.shell: + cmd: authselect current -r | awk '{ print $1 }' + register: result_authselect_profile + changed_when: false + when: + - result_authselect_check_cmd is success + + - name: Lock Accounts After Failed Password Attempts - Define the current authselect + profile as a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: '{{ result_authselect_profile.stdout }}' + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is match("custom/") + + - name: Lock Accounts After Failed Password Attempts - Define the new authselect + custom profile as a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: custom/hardening + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is not match("custom/") + + - name: Lock Accounts After Failed Password Attempts - Get authselect current + features to also enable them in the custom profile + ansible.builtin.shell: + cmd: authselect current | tail -n+3 | awk '{ print $2 }' + register: result_authselect_features + changed_when: false + when: + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + + - name: Lock Accounts After Failed Password Attempts - Check if any custom profile + with the same name was already created + ansible.builtin.stat: + path: /etc/authselect/{{ authselect_custom_profile }} + register: result_authselect_custom_profile_present + changed_when: false + when: + - authselect_current_profile is not match("custom/") + + - name: Lock Accounts After Failed Password Attempts - Create an authselect + custom profile based on the current profile + ansible.builtin.command: + cmd: authselect create-profile hardening -b {{ authselect_current_profile + }} + when: + - result_authselect_check_cmd is success + - authselect_current_profile is not match("custom/") + - not result_authselect_custom_profile_present.stat.exists + + - name: Lock Accounts After Failed Password Attempts - Ensure authselect changes + are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=before-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Lock Accounts After Failed Password Attempts - Ensure the authselect + custom profile is selected + ansible.builtin.command: + cmd: authselect select {{ authselect_custom_profile }} + register: result_pam_authselect_select_profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Lock Accounts After Failed Password Attempts - Restore the authselect + features in the custom profile + ansible.builtin.command: + cmd: authselect enable-feature {{ item }} + loop: '{{ result_authselect_features.stdout_lines }}' + register: result_pam_authselect_restore_features + when: + - result_authselect_profile is not skipped + - result_authselect_features is not skipped + - result_pam_authselect_select_profile is not skipped + + - name: Lock Accounts After Failed Password Attempts - Ensure authselect changes + are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=after-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - result_pam_authselect_restore_features is not skipped + + - name: Lock Accounts After Failed Password Attempts - Change the PAM file to + be edited according to the custom authselect profile + ansible.builtin.set_fact: + pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path + | basename }} + when: + - result_authselect_present.stat.exists + + - name: Lock Accounts After Failed Password Attempts - Ensure the "deny" option + from "pam_faillock.so" is not present in {{ pam_file_path }} + ansible.builtin.replace: + dest: '{{ pam_file_path }}' + regexp: (.*auth.*pam_faillock.so.*)\bdeny\b=?[0-9a-zA-Z]*(.*) + replace: \1\2 + register: result_pam_option_removal + + - name: Lock Accounts After Failed Password Attempts - Ensure authselect changes + are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: + - result_authselect_present.stat.exists + - result_pam_option_removal is changed + when: + - result_pam_file_present.stat.exists + + - name: Lock Accounts After Failed Password Attempts - Check if /etc/pam.d/password-auth + file is present + ansible.builtin.stat: + path: /etc/pam.d/password-auth + register: result_pam_file_present + + - name: Lock Accounts After Failed Password Attempts - Check the proper remediation + for the system + block: + + - name: Lock Accounts After Failed Password Attempts - Define the PAM file to + be edited as a local fact + ansible.builtin.set_fact: + pam_file_path: /etc/pam.d/password-auth + + - name: Lock Accounts After Failed Password Attempts - Check if system relies + on authselect + ansible.builtin.stat: + path: /usr/bin/authselect + register: result_authselect_present + + - name: Lock Accounts After Failed Password Attempts - Remediate using authselect + block: + + - name: Lock Accounts After Failed Password Attempts - Check integrity of authselect + current profile + ansible.builtin.command: + cmd: authselect check + register: result_authselect_check_cmd + changed_when: false + ignore_errors: true + + - name: Lock Accounts After Failed Password Attempts - Informative message based + on the authselect integrity check result + ansible.builtin.assert: + that: + - result_authselect_check_cmd is success + fail_msg: + - authselect integrity check failed. Remediation aborted! + - This remediation could not be applied because an authselect profile was + not selected or the selected profile is not intact. + - It is not recommended to manually edit the PAM files when authselect tool + is available. + - In cases where the default authselect profile does not cover a specific + demand, a custom authselect profile is recommended. + success_msg: + - authselect integrity check passed + + - name: Lock Accounts After Failed Password Attempts - Get authselect current + profile + ansible.builtin.shell: + cmd: authselect current -r | awk '{ print $1 }' + register: result_authselect_profile + changed_when: false + when: + - result_authselect_check_cmd is success + + - name: Lock Accounts After Failed Password Attempts - Define the current authselect + profile as a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: '{{ result_authselect_profile.stdout }}' + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is match("custom/") + + - name: Lock Accounts After Failed Password Attempts - Define the new authselect + custom profile as a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: custom/hardening + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is not match("custom/") + + - name: Lock Accounts After Failed Password Attempts - Get authselect current + features to also enable them in the custom profile + ansible.builtin.shell: + cmd: authselect current | tail -n+3 | awk '{ print $2 }' + register: result_authselect_features + changed_when: false + when: + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + + - name: Lock Accounts After Failed Password Attempts - Check if any custom profile + with the same name was already created + ansible.builtin.stat: + path: /etc/authselect/{{ authselect_custom_profile }} + register: result_authselect_custom_profile_present + changed_when: false + when: + - authselect_current_profile is not match("custom/") + + - name: Lock Accounts After Failed Password Attempts - Create an authselect + custom profile based on the current profile + ansible.builtin.command: + cmd: authselect create-profile hardening -b {{ authselect_current_profile + }} + when: + - result_authselect_check_cmd is success + - authselect_current_profile is not match("custom/") + - not result_authselect_custom_profile_present.stat.exists + + - name: Lock Accounts After Failed Password Attempts - Ensure authselect changes + are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=before-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Lock Accounts After Failed Password Attempts - Ensure the authselect + custom profile is selected + ansible.builtin.command: + cmd: authselect select {{ authselect_custom_profile }} + register: result_pam_authselect_select_profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Lock Accounts After Failed Password Attempts - Restore the authselect + features in the custom profile + ansible.builtin.command: + cmd: authselect enable-feature {{ item }} + loop: '{{ result_authselect_features.stdout_lines }}' + register: result_pam_authselect_restore_features + when: + - result_authselect_profile is not skipped + - result_authselect_features is not skipped + - result_pam_authselect_select_profile is not skipped + + - name: Lock Accounts After Failed Password Attempts - Ensure authselect changes + are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=after-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - result_pam_authselect_restore_features is not skipped + + - name: Lock Accounts After Failed Password Attempts - Change the PAM file to + be edited according to the custom authselect profile + ansible.builtin.set_fact: + pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path + | basename }} + when: + - result_authselect_present.stat.exists + + - name: Lock Accounts After Failed Password Attempts - Ensure the "deny" option + from "pam_faillock.so" is not present in {{ pam_file_path }} + ansible.builtin.replace: + dest: '{{ pam_file_path }}' + regexp: (.*auth.*pam_faillock.so.*)\bdeny\b=?[0-9a-zA-Z]*(.*) + replace: \1\2 + register: result_pam_option_removal + + - name: Lock Accounts After Failed Password Attempts - Ensure authselect changes + are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: + - result_authselect_present.stat.exists + - result_pam_option_removal is changed + when: + - result_pam_file_present.stat.exists + when: + - '"pam" in ansible_facts.packages' + - result_faillock_conf_check.stat.exists + tags: + - CCE-80667-9 + - CJIS-5.5.3 + - DISA-STIG-RHEL-08-020010 + - NIST-800-171-3.1.8 + - NIST-800-53-AC-7(a) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.1.6 + - accounts_passwords_pam_faillock_deny + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Lock Accounts After Failed Password Attempts - Ensure the pam_faillock.so + deny parameter in PAM files + block: + + - name: Lock Accounts After Failed Password Attempts - Check if pam_faillock.so + deny parameter is already enabled in pam files + ansible.builtin.lineinfile: + path: /etc/pam.d/system-auth + regexp: .*auth.*pam_faillock\.so (preauth|authfail).*deny + state: absent + check_mode: true + changed_when: false + register: result_pam_faillock_deny_parameter_is_present + + - name: Lock Accounts After Failed Password Attempts - Ensure the inclusion of pam_faillock.so + preauth deny parameter in auth section + ansible.builtin.lineinfile: + path: '{{ item }}' + backrefs: true + regexp: (^\s*auth\s+)([\w\[].*\b)(\s+pam_faillock.so preauth.*) + line: \1required\3 deny={{ var_accounts_passwords_pam_faillock_deny }} + state: present + loop: + - /etc/pam.d/system-auth + - /etc/pam.d/password-auth + when: + - result_pam_faillock_deny_parameter_is_present.found == 0 + + - name: Lock Accounts After Failed Password Attempts - Ensure the inclusion of pam_faillock.so + authfail deny parameter in auth section + ansible.builtin.lineinfile: + path: '{{ item }}' + backrefs: true + regexp: (^\s*auth\s+)([\w\[].*\b)(\s+pam_faillock.so authfail.*) + line: \1required\3 deny={{ var_accounts_passwords_pam_faillock_deny }} + state: present + loop: + - /etc/pam.d/system-auth + - /etc/pam.d/password-auth + when: + - result_pam_faillock_deny_parameter_is_present.found == 0 + + - name: Lock Accounts After Failed Password Attempts - Ensure the desired value + for pam_faillock.so preauth deny parameter in auth section + ansible.builtin.lineinfile: + path: '{{ item }}' + backrefs: true + regexp: (^\s*auth\s+)([\w\[].*\b)(\s+pam_faillock.so preauth.*)(deny)=[0-9]+(.*) + line: \1required\3\4={{ var_accounts_passwords_pam_faillock_deny }}\5 + state: present + loop: + - /etc/pam.d/system-auth + - /etc/pam.d/password-auth + when: + - result_pam_faillock_deny_parameter_is_present.found > 0 + + - name: Lock Accounts After Failed Password Attempts - Ensure the desired value + for pam_faillock.so authfail deny parameter in auth section + ansible.builtin.lineinfile: + path: '{{ item }}' + backrefs: true + regexp: (^\s*auth\s+)([\w\[].*\b)(\s+pam_faillock.so authfail.*)(deny)=[0-9]+(.*) + line: \1required\3\4={{ var_accounts_passwords_pam_faillock_deny }}\5 + state: present + loop: + - /etc/pam.d/system-auth + - /etc/pam.d/password-auth + when: + - result_pam_faillock_deny_parameter_is_present.found > 0 + when: + - '"pam" in ansible_facts.packages' + - not result_faillock_conf_check.stat.exists + tags: + - CCE-80667-9 + - CJIS-5.5.3 + - DISA-STIG-RHEL-08-020010 + - NIST-800-171-3.1.8 + - NIST-800-53-AC-7(a) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.1.6 + - accounts_passwords_pam_faillock_deny + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q pam; then + +var_accounts_passwords_pam_faillock_deny='' + + +if [ -f /usr/bin/authselect ]; then + if ! authselect check; then +echo " +authselect integrity check failed. Remediation aborted! +This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact. +It is not recommended to manually edit the PAM files when authselect tool is available. +In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended." +exit 1 +fi +authselect enable-feature with-faillock + +authselect apply-changes -b +else + AUTH_FILES=("/etc/pam.d/system-auth" "/etc/pam.d/password-auth") +for pam_file in "${AUTH_FILES[@]}" +do + if ! grep -qE '^\s*auth\s+required\s+pam_faillock\.so\s+(preauth silent|authfail).*$' "$pam_file" ; then + sed -i --follow-symlinks '/^auth.*sufficient.*pam_unix\.so.*/i auth required pam_faillock.so preauth silent' "$pam_file" + sed -i --follow-symlinks '/^auth.*required.*pam_deny\.so.*/i auth required pam_faillock.so authfail' "$pam_file" + sed -i --follow-symlinks '/^account.*required.*pam_unix\.so.*/i account required pam_faillock.so' "$pam_file" + fi + sed -Ei 's/(auth.*)(\[default=die\])(.*pam_faillock\.so)/\1required \3/g' "$pam_file" +done +fi +AUTH_FILES=("/etc/pam.d/system-auth" "/etc/pam.d/password-auth") +FAILLOCK_CONF="/etc/security/faillock.conf" +if [ -f $FAILLOCK_CONF ]; then + regex="^\s*deny\s*=" + line="deny = $var_accounts_passwords_pam_faillock_deny" + if ! grep -q $regex $FAILLOCK_CONF; then + echo $line >> $FAILLOCK_CONF + else + sed -i --follow-symlinks 's|^\s*\(deny\s*=\s*\)\(\S\+\)|\1'"$var_accounts_passwords_pam_faillock_deny"'|g' $FAILLOCK_CONF + fi + for pam_file in "${AUTH_FILES[@]}" + do + if [ -e "$pam_file" ] ; then + PAM_FILE_PATH="$pam_file" + if [ -f /usr/bin/authselect ]; then + if ! authselect check; then + echo " + authselect integrity check failed. Remediation aborted! + This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact. + It is not recommended to manually edit the PAM files when authselect tool is available. + In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended." + exit 1 + fi + CURRENT_PROFILE=$(authselect current -r | awk '{ print $1 }') + # If not already in use, a custom profile is created preserving the enabled features. + if [[ ! $CURRENT_PROFILE == custom/* ]]; then + ENABLED_FEATURES=$(authselect current | tail -n+3 | awk '{ print $2 }') + authselect create-profile hardening -b $CURRENT_PROFILE + CURRENT_PROFILE="custom/hardening" + + authselect apply-changes -b --backup=before-hardening-custom-profile + authselect select $CURRENT_PROFILE + for feature in $ENABLED_FEATURES; do + authselect enable-feature $feature; + done + + authselect apply-changes -b --backup=after-hardening-custom-profile + fi + PAM_FILE_NAME=$(basename "$pam_file") + PAM_FILE_PATH="/etc/authselect/$CURRENT_PROFILE/$PAM_FILE_NAME" + + authselect apply-changes -b + fi + + if grep -qP '^\s*auth\s.*\bpam_faillock.so\s.*\bdeny\b' "$PAM_FILE_PATH"; then + sed -i -E --follow-symlinks 's/(.*auth.*pam_faillock.so.*)\bdeny\b=?[[:alnum:]]*(.*)/\1\2/g' "$PAM_FILE_PATH" + fi + if [ -f /usr/bin/authselect ]; then + + authselect apply-changes -b + fi + else + echo "$pam_file was not found" >&2 + fi + done +else + for pam_file in "${AUTH_FILES[@]}" + do + if ! grep -qE '^\s*auth.*pam_faillock\.so (preauth|authfail).*deny' "$pam_file"; then + sed -i --follow-symlinks '/^auth.*required.*pam_faillock\.so.*preauth.*silent.*/ s/$/ deny='"$var_accounts_passwords_pam_faillock_deny"'/' "$pam_file" + sed -i --follow-symlinks '/^auth.*required.*pam_faillock\.so.*authfail.*/ s/$/ deny='"$var_accounts_passwords_pam_faillock_deny"'/' "$pam_file" + else + sed -i --follow-symlinks 's/\(^auth.*required.*pam_faillock\.so.*preauth.*silent.*\)\('"deny"'=\)[0-9]\+\(.*\)/\1\2'"$var_accounts_passwords_pam_faillock_deny"'\3/' "$pam_file" + sed -i --follow-symlinks 's/\(^auth.*required.*pam_faillock\.so.*authfail.*\)\('"deny"'=\)[0-9]\+\(.*\)/\1\2'"$var_accounts_passwords_pam_faillock_deny"'\3/' "$pam_file" + fi + done +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Configure the root Account for Failed Password Attempts + This rule configures the system to lock out the root account after a number of +incorrect login attempts using pam_faillock.so. + +pam_faillock.so module requires multiple entries in pam files. These entries must be carefully +defined to work as expected. In order to avoid errors when manually editing these files, it is +recommended to use the appropriate tools, such as authselect or authconfig, +depending on the OS version. + If the system relies on authselect tool to manage PAM settings, the remediation +will also use authselect tool. However, if any manual modification was made in +PAM files, the authselect integrity check will fail and the remediation will be +aborted in order to preserve intentional changes. In this case, an informative message will +be shown in the remediation report. +If the system supports the /etc/security/faillock.conf file, the pam_faillock +parameters should be defined in faillock.conf file. + BP28(R18) + 1 + 12 + 15 + 16 + DSS05.04 + DSS05.10 + DSS06.10 + CCI-002238 + CCI-000044 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + 0421 + 0422 + 0431 + 0974 + 1173 + 1401 + 1504 + 1505 + 1546 + 1557 + 1558 + 1559 + 1560 + 1561 + A.18.1.4 + A.9.2.1 + A.9.2.4 + A.9.3.1 + A.9.4.2 + A.9.4.3 + CM-6(a) + AC-7(b) + IA-5(c) + PR.AC-7 + FMT_MOF_EXT.1 + SRG-OS-000329-GPOS-00128 + SRG-OS-000021-GPOS-00005 + RHEL-08-020022 + SV-230344r646874_rule + By limiting the number of failed logon attempts, the risk of unauthorized system access via +user password guessing, also known as brute-forcing, is reduced. Limits are imposed by locking +the account. + + CCE-80668-7 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80668-7 + - DISA-STIG-RHEL-08-020022 + - NIST-800-53-AC-7(b) + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(c) + - accounts_passwords_pam_faillock_deny_root + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Configure the root Account for Failed Password Attempts - Check if system + relies on authselect tool + ansible.builtin.stat: + path: /usr/bin/authselect + register: result_authselect_present + when: '"pam" in ansible_facts.packages' + tags: + - CCE-80668-7 + - DISA-STIG-RHEL-08-020022 + - NIST-800-53-AC-7(b) + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(c) + - accounts_passwords_pam_faillock_deny_root + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Configure the root Account for Failed Password Attempts - Remediation where + authselect tool is present + block: + + - name: Configure the root Account for Failed Password Attempts - Check integrity + of authselect current profile + ansible.builtin.command: + cmd: authselect check + register: result_authselect_check_cmd + changed_when: false + ignore_errors: true + + - name: Configure the root Account for Failed Password Attempts - Informative message + based on the authselect integrity check result + ansible.builtin.assert: + that: + - result_authselect_check_cmd is success + fail_msg: + - authselect integrity check failed. Remediation aborted! + - This remediation could not be applied because an authselect profile was not + selected or the selected profile is not intact. + - It is not recommended to manually edit the PAM files when authselect tool + is available. + - In cases where the default authselect profile does not cover a specific demand, + a custom authselect profile is recommended. + success_msg: + - authselect integrity check passed + + - name: Configure the root Account for Failed Password Attempts - Get authselect + current features + ansible.builtin.shell: + cmd: authselect current | tail -n+3 | awk '{ print $2 }' + register: result_authselect_features + changed_when: false + when: + - result_authselect_check_cmd is success + + - name: Configure the root Account for Failed Password Attempts - Ensure "with-faillock" + feature is enabled using authselect tool + ansible.builtin.command: + cmd: authselect enable-feature with-faillock + register: result_authselect_enable_feature_cmd + when: + - result_authselect_check_cmd is success + - result_authselect_features.stdout is not search("with-faillock") + + - name: Configure the root Account for Failed Password Attempts - Ensure authselect + changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: + - result_authselect_enable_feature_cmd is not skipped + - result_authselect_enable_feature_cmd is success + when: + - '"pam" in ansible_facts.packages' + - result_authselect_present.stat.exists + tags: + - CCE-80668-7 + - DISA-STIG-RHEL-08-020022 + - NIST-800-53-AC-7(b) + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(c) + - accounts_passwords_pam_faillock_deny_root + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Configure the root Account for Failed Password Attempts - Remediation where + authselect tool is not present + block: + + - name: Configure the root Account for Failed Password Attempts - Check if pam_faillock.so + is already enabled + ansible.builtin.lineinfile: + path: /etc/pam.d/system-auth + regexp: .*auth.*pam_faillock\.so (preauth|authfail) + state: absent + check_mode: true + changed_when: false + register: result_pam_faillock_is_enabled + + - name: Configure the root Account for Failed Password Attempts - Enable pam_faillock.so + preauth editing PAM files + ansible.builtin.lineinfile: + path: '{{ item }}' + line: auth required pam_faillock.so preauth + insertbefore: ^auth.*sufficient.*pam_unix\.so.* + state: present + loop: + - /etc/pam.d/system-auth + - /etc/pam.d/password-auth + when: + - result_pam_faillock_is_enabled.found == 0 + + - name: Configure the root Account for Failed Password Attempts - Enable pam_faillock.so + authfail editing PAM files + ansible.builtin.lineinfile: + path: '{{ item }}' + line: auth required pam_faillock.so authfail + insertbefore: ^auth.*required.*pam_deny\.so.* + state: present + loop: + - /etc/pam.d/system-auth + - /etc/pam.d/password-auth + when: + - result_pam_faillock_is_enabled.found == 0 + + - name: Configure the root Account for Failed Password Attempts - Enable pam_faillock.so + account section editing PAM files + ansible.builtin.lineinfile: + path: '{{ item }}' + line: account required pam_faillock.so + insertbefore: ^account.*required.*pam_unix\.so.* + state: present + loop: + - /etc/pam.d/system-auth + - /etc/pam.d/password-auth + when: + - result_pam_faillock_is_enabled.found == 0 + when: + - '"pam" in ansible_facts.packages' + - not result_authselect_present.stat.exists + tags: + - CCE-80668-7 + - DISA-STIG-RHEL-08-020022 + - NIST-800-53-AC-7(b) + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(c) + - accounts_passwords_pam_faillock_deny_root + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Configure the root Account for Failed Password Attempts - Check the presence + of /etc/security/faillock.conf file + ansible.builtin.stat: + path: /etc/security/faillock.conf + register: result_faillock_conf_check + when: '"pam" in ansible_facts.packages' + tags: + - CCE-80668-7 + - DISA-STIG-RHEL-08-020022 + - NIST-800-53-AC-7(b) + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(c) + - accounts_passwords_pam_faillock_deny_root + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Configure the root Account for Failed Password Attempts - Ensure the pam_faillock.so + even_deny_root parameter in /etc/security/faillock.conf + ansible.builtin.lineinfile: + path: /etc/security/faillock.conf + regexp: ^\s*even_deny_root + line: even_deny_root + state: present + when: + - '"pam" in ansible_facts.packages' + - result_faillock_conf_check.stat.exists + tags: + - CCE-80668-7 + - DISA-STIG-RHEL-08-020022 + - NIST-800-53-AC-7(b) + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(c) + - accounts_passwords_pam_faillock_deny_root + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Configure the root Account for Failed Password Attempts - Ensure the pam_faillock.so + even_deny_root parameter not in PAM files + block: + + - name: Configure the root Account for Failed Password Attempts - Check if /etc/pam.d/system-auth + file is present + ansible.builtin.stat: + path: /etc/pam.d/system-auth + register: result_pam_file_present + + - name: Configure the root Account for Failed Password Attempts - Check the proper + remediation for the system + block: + + - name: Configure the root Account for Failed Password Attempts - Define the PAM + file to be edited as a local fact + ansible.builtin.set_fact: + pam_file_path: /etc/pam.d/system-auth + + - name: Configure the root Account for Failed Password Attempts - Check if system + relies on authselect + ansible.builtin.stat: + path: /usr/bin/authselect + register: result_authselect_present + + - name: Configure the root Account for Failed Password Attempts - Remediate using + authselect + block: + + - name: Configure the root Account for Failed Password Attempts - Check integrity + of authselect current profile + ansible.builtin.command: + cmd: authselect check + register: result_authselect_check_cmd + changed_when: false + ignore_errors: true + + - name: Configure the root Account for Failed Password Attempts - Informative + message based on the authselect integrity check result + ansible.builtin.assert: + that: + - result_authselect_check_cmd is success + fail_msg: + - authselect integrity check failed. Remediation aborted! + - This remediation could not be applied because an authselect profile was + not selected or the selected profile is not intact. + - It is not recommended to manually edit the PAM files when authselect tool + is available. + - In cases where the default authselect profile does not cover a specific + demand, a custom authselect profile is recommended. + success_msg: + - authselect integrity check passed + + - name: Configure the root Account for Failed Password Attempts - Get authselect + current profile + ansible.builtin.shell: + cmd: authselect current -r | awk '{ print $1 }' + register: result_authselect_profile + changed_when: false + when: + - result_authselect_check_cmd is success + + - name: Configure the root Account for Failed Password Attempts - Define the + current authselect profile as a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: '{{ result_authselect_profile.stdout }}' + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is match("custom/") + + - name: Configure the root Account for Failed Password Attempts - Define the + new authselect custom profile as a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: custom/hardening + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is not match("custom/") + + - name: Configure the root Account for Failed Password Attempts - Get authselect + current features to also enable them in the custom profile + ansible.builtin.shell: + cmd: authselect current | tail -n+3 | awk '{ print $2 }' + register: result_authselect_features + changed_when: false + when: + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + + - name: Configure the root Account for Failed Password Attempts - Check if any + custom profile with the same name was already created + ansible.builtin.stat: + path: /etc/authselect/{{ authselect_custom_profile }} + register: result_authselect_custom_profile_present + changed_when: false + when: + - authselect_current_profile is not match("custom/") + + - name: Configure the root Account for Failed Password Attempts - Create an + authselect custom profile based on the current profile + ansible.builtin.command: + cmd: authselect create-profile hardening -b {{ authselect_current_profile + }} + when: + - result_authselect_check_cmd is success + - authselect_current_profile is not match("custom/") + - not result_authselect_custom_profile_present.stat.exists + + - name: Configure the root Account for Failed Password Attempts - Ensure authselect + changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=before-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Configure the root Account for Failed Password Attempts - Ensure the + authselect custom profile is selected + ansible.builtin.command: + cmd: authselect select {{ authselect_custom_profile }} + register: result_pam_authselect_select_profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Configure the root Account for Failed Password Attempts - Restore the + authselect features in the custom profile + ansible.builtin.command: + cmd: authselect enable-feature {{ item }} + loop: '{{ result_authselect_features.stdout_lines }}' + register: result_pam_authselect_restore_features + when: + - result_authselect_profile is not skipped + - result_authselect_features is not skipped + - result_pam_authselect_select_profile is not skipped + + - name: Configure the root Account for Failed Password Attempts - Ensure authselect + changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=after-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - result_pam_authselect_restore_features is not skipped + + - name: Configure the root Account for Failed Password Attempts - Change the + PAM file to be edited according to the custom authselect profile + ansible.builtin.set_fact: + pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path + | basename }} + when: + - result_authselect_present.stat.exists + + - name: Configure the root Account for Failed Password Attempts - Ensure the "even_deny_root" + option from "pam_faillock.so" is not present in {{ pam_file_path }} + ansible.builtin.replace: + dest: '{{ pam_file_path }}' + regexp: (.*auth.*pam_faillock.so.*)\beven_deny_root\b=?[0-9a-zA-Z]*(.*) + replace: \1\2 + register: result_pam_option_removal + + - name: Configure the root Account for Failed Password Attempts - Ensure authselect + changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: + - result_authselect_present.stat.exists + - result_pam_option_removal is changed + when: + - result_pam_file_present.stat.exists + + - name: Configure the root Account for Failed Password Attempts - Check if /etc/pam.d/password-auth + file is present + ansible.builtin.stat: + path: /etc/pam.d/password-auth + register: result_pam_file_present + + - name: Configure the root Account for Failed Password Attempts - Check the proper + remediation for the system + block: + + - name: Configure the root Account for Failed Password Attempts - Define the PAM + file to be edited as a local fact + ansible.builtin.set_fact: + pam_file_path: /etc/pam.d/password-auth + + - name: Configure the root Account for Failed Password Attempts - Check if system + relies on authselect + ansible.builtin.stat: + path: /usr/bin/authselect + register: result_authselect_present + + - name: Configure the root Account for Failed Password Attempts - Remediate using + authselect + block: + + - name: Configure the root Account for Failed Password Attempts - Check integrity + of authselect current profile + ansible.builtin.command: + cmd: authselect check + register: result_authselect_check_cmd + changed_when: false + ignore_errors: true + + - name: Configure the root Account for Failed Password Attempts - Informative + message based on the authselect integrity check result + ansible.builtin.assert: + that: + - result_authselect_check_cmd is success + fail_msg: + - authselect integrity check failed. Remediation aborted! + - This remediation could not be applied because an authselect profile was + not selected or the selected profile is not intact. + - It is not recommended to manually edit the PAM files when authselect tool + is available. + - In cases where the default authselect profile does not cover a specific + demand, a custom authselect profile is recommended. + success_msg: + - authselect integrity check passed + + - name: Configure the root Account for Failed Password Attempts - Get authselect + current profile + ansible.builtin.shell: + cmd: authselect current -r | awk '{ print $1 }' + register: result_authselect_profile + changed_when: false + when: + - result_authselect_check_cmd is success + + - name: Configure the root Account for Failed Password Attempts - Define the + current authselect profile as a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: '{{ result_authselect_profile.stdout }}' + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is match("custom/") + + - name: Configure the root Account for Failed Password Attempts - Define the + new authselect custom profile as a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: custom/hardening + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is not match("custom/") + + - name: Configure the root Account for Failed Password Attempts - Get authselect + current features to also enable them in the custom profile + ansible.builtin.shell: + cmd: authselect current | tail -n+3 | awk '{ print $2 }' + register: result_authselect_features + changed_when: false + when: + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + + - name: Configure the root Account for Failed Password Attempts - Check if any + custom profile with the same name was already created + ansible.builtin.stat: + path: /etc/authselect/{{ authselect_custom_profile }} + register: result_authselect_custom_profile_present + changed_when: false + when: + - authselect_current_profile is not match("custom/") + + - name: Configure the root Account for Failed Password Attempts - Create an + authselect custom profile based on the current profile + ansible.builtin.command: + cmd: authselect create-profile hardening -b {{ authselect_current_profile + }} + when: + - result_authselect_check_cmd is success + - authselect_current_profile is not match("custom/") + - not result_authselect_custom_profile_present.stat.exists + + - name: Configure the root Account for Failed Password Attempts - Ensure authselect + changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=before-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Configure the root Account for Failed Password Attempts - Ensure the + authselect custom profile is selected + ansible.builtin.command: + cmd: authselect select {{ authselect_custom_profile }} + register: result_pam_authselect_select_profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Configure the root Account for Failed Password Attempts - Restore the + authselect features in the custom profile + ansible.builtin.command: + cmd: authselect enable-feature {{ item }} + loop: '{{ result_authselect_features.stdout_lines }}' + register: result_pam_authselect_restore_features + when: + - result_authselect_profile is not skipped + - result_authselect_features is not skipped + - result_pam_authselect_select_profile is not skipped + + - name: Configure the root Account for Failed Password Attempts - Ensure authselect + changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=after-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - result_pam_authselect_restore_features is not skipped + + - name: Configure the root Account for Failed Password Attempts - Change the + PAM file to be edited according to the custom authselect profile + ansible.builtin.set_fact: + pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path + | basename }} + when: + - result_authselect_present.stat.exists + + - name: Configure the root Account for Failed Password Attempts - Ensure the "even_deny_root" + option from "pam_faillock.so" is not present in {{ pam_file_path }} + ansible.builtin.replace: + dest: '{{ pam_file_path }}' + regexp: (.*auth.*pam_faillock.so.*)\beven_deny_root\b=?[0-9a-zA-Z]*(.*) + replace: \1\2 + register: result_pam_option_removal + + - name: Configure the root Account for Failed Password Attempts - Ensure authselect + changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: + - result_authselect_present.stat.exists + - result_pam_option_removal is changed + when: + - result_pam_file_present.stat.exists + when: + - '"pam" in ansible_facts.packages' + - result_faillock_conf_check.stat.exists + tags: + - CCE-80668-7 + - DISA-STIG-RHEL-08-020022 + - NIST-800-53-AC-7(b) + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(c) + - accounts_passwords_pam_faillock_deny_root + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Configure the root Account for Failed Password Attempts - Ensure the pam_faillock.so + even_deny_root parameter in PAM files + block: + + - name: Configure the root Account for Failed Password Attempts - Check if pam_faillock.so + even_deny_root parameter is already enabled in pam files + ansible.builtin.lineinfile: + path: /etc/pam.d/system-auth + regexp: .*auth.*pam_faillock\.so (preauth|authfail).*even_deny_root + state: absent + check_mode: true + changed_when: false + register: result_pam_faillock_even_deny_root_parameter_is_present + + - name: Configure the root Account for Failed Password Attempts - Ensure the inclusion + of pam_faillock.so preauth even_deny_root parameter in auth section + ansible.builtin.lineinfile: + path: '{{ item }}' + backrefs: true + regexp: (^\s*auth\s+)([\w\[].*\b)(\s+pam_faillock.so preauth.*) + line: \1required\3 even_deny_root + state: present + loop: + - /etc/pam.d/system-auth + - /etc/pam.d/password-auth + when: + - result_pam_faillock_even_deny_root_parameter_is_present.found == 0 + + - name: Configure the root Account for Failed Password Attempts - Ensure the inclusion + of pam_faillock.so authfail even_deny_root parameter in auth section + ansible.builtin.lineinfile: + path: '{{ item }}' + backrefs: true + regexp: (^\s*auth\s+)([\w\[].*\b)(\s+pam_faillock.so authfail.*) + line: \1required\3 even_deny_root + state: present + loop: + - /etc/pam.d/system-auth + - /etc/pam.d/password-auth + when: + - result_pam_faillock_even_deny_root_parameter_is_present.found == 0 + when: + - '"pam" in ansible_facts.packages' + - not result_faillock_conf_check.stat.exists + tags: + - CCE-80668-7 + - DISA-STIG-RHEL-08-020022 + - NIST-800-53-AC-7(b) + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(c) + - accounts_passwords_pam_faillock_deny_root + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q pam; then + +if [ -f /usr/bin/authselect ]; then + if ! authselect check; then +echo " +authselect integrity check failed. Remediation aborted! +This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact. +It is not recommended to manually edit the PAM files when authselect tool is available. +In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended." +exit 1 +fi +authselect enable-feature with-faillock + +authselect apply-changes -b +else + AUTH_FILES=("/etc/pam.d/system-auth" "/etc/pam.d/password-auth") +for pam_file in "${AUTH_FILES[@]}" +do + if ! grep -qE '^\s*auth\s+required\s+pam_faillock\.so\s+(preauth silent|authfail).*$' "$pam_file" ; then + sed -i --follow-symlinks '/^auth.*sufficient.*pam_unix\.so.*/i auth required pam_faillock.so preauth silent' "$pam_file" + sed -i --follow-symlinks '/^auth.*required.*pam_deny\.so.*/i auth required pam_faillock.so authfail' "$pam_file" + sed -i --follow-symlinks '/^account.*required.*pam_unix\.so.*/i account required pam_faillock.so' "$pam_file" + fi + sed -Ei 's/(auth.*)(\[default=die\])(.*pam_faillock\.so)/\1required \3/g' "$pam_file" +done +fi +AUTH_FILES=("/etc/pam.d/system-auth" "/etc/pam.d/password-auth") +FAILLOCK_CONF="/etc/security/faillock.conf" +if [ -f $FAILLOCK_CONF ]; then + regex="^\s*even_deny_root" + line="even_deny_root" + if ! grep -q $regex $FAILLOCK_CONF; then + echo $line >> $FAILLOCK_CONF + fi + for pam_file in "${AUTH_FILES[@]}" + do + if [ -e "$pam_file" ] ; then + PAM_FILE_PATH="$pam_file" + if [ -f /usr/bin/authselect ]; then + if ! authselect check; then + echo " + authselect integrity check failed. Remediation aborted! + This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact. + It is not recommended to manually edit the PAM files when authselect tool is available. + In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended." + exit 1 + fi + CURRENT_PROFILE=$(authselect current -r | awk '{ print $1 }') + # If not already in use, a custom profile is created preserving the enabled features. + if [[ ! $CURRENT_PROFILE == custom/* ]]; then + ENABLED_FEATURES=$(authselect current | tail -n+3 | awk '{ print $2 }') + authselect create-profile hardening -b $CURRENT_PROFILE + CURRENT_PROFILE="custom/hardening" + + authselect apply-changes -b --backup=before-hardening-custom-profile + authselect select $CURRENT_PROFILE + for feature in $ENABLED_FEATURES; do + authselect enable-feature $feature; + done + + authselect apply-changes -b --backup=after-hardening-custom-profile + fi + PAM_FILE_NAME=$(basename "$pam_file") + PAM_FILE_PATH="/etc/authselect/$CURRENT_PROFILE/$PAM_FILE_NAME" + + authselect apply-changes -b + fi + + if grep -qP '^\s*auth\s.*\bpam_faillock.so\s.*\beven_deny_root\b' "$PAM_FILE_PATH"; then + sed -i -E --follow-symlinks 's/(.*auth.*pam_faillock.so.*)\beven_deny_root\b=?[[:alnum:]]*(.*)/\1\2/g' "$PAM_FILE_PATH" + fi + if [ -f /usr/bin/authselect ]; then + + authselect apply-changes -b + fi + else + echo "$pam_file was not found" >&2 + fi + done +else + for pam_file in "${AUTH_FILES[@]}" + do + if ! grep -qE '^\s*auth.*pam_faillock\.so (preauth|authfail).*even_deny_root' "$pam_file"; then + sed -i --follow-symlinks '/^auth.*required.*pam_faillock\.so.*preauth.*silent.*/ s/$/ even_deny_root/' "$pam_file" + sed -i --follow-symlinks '/^auth.*required.*pam_faillock\.so.*authfail.*/ s/$/ even_deny_root/' "$pam_file" + fi + done +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Lock Accounts Must Persist + This rule ensures that the system lock out accounts using pam_faillock.so persist +after system reboot. From "Pam_Faillock" man pages: +Note that the default directory that "pam_faillock" uses is usually cleared on system +boot so the access will be reenabled after system reboot. If that is undesirable, a different +tally directory must be set with the "dir" option. + +pam_faillock.so module requires multiple entries in pam files. These entries must be carefully +defined to work as expected. In order to avoid errors when manually editing these files, it is +recommended to use the appropriate tools, such as authselect or authconfig, +depending on the OS version. + If the system relies on authselect tool to manage PAM settings, the remediation +will also use authselect tool. However, if any manual modification was made in +PAM files, the authselect integrity check will fail and the remediation will be +aborted in order to preserve intentional changes. In this case, an informative message will +be shown in the remediation report. +If the system supports the /etc/security/faillock.conf file, the pam_faillock +parameters should be defined in faillock.conf file. + CCI-000044 + CCI-002238 + AC-7(b) + AC-7(a) + AC-7.1(ii) + SRG-OS-000021-GPOS-00005 + SRG-OS-000329-GPOS-00128 + RHEL-08-020016 + SV-230338r627750_rule + Locking out user accounts after a number of incorrect attempts prevents direct password +guessing attacks. In combination with the silent option, user enumeration attacks +are also mitigated. + + - name: Gather the package facts + package_facts: + manager: auto + tags: + - DISA-STIG-RHEL-08-020016 + - NIST-800-53-AC-7(a) + - NIST-800-53-AC-7(b) + - NIST-800-53-AC-7.1(ii) + - accounts_passwords_pam_faillock_dir + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Lock Accounts Must Persist - Check if system relies on authselect tool + ansible.builtin.stat: + path: /usr/bin/authselect + register: result_authselect_present + when: '"pam" in ansible_facts.packages' + tags: + - DISA-STIG-RHEL-08-020016 + - NIST-800-53-AC-7(a) + - NIST-800-53-AC-7(b) + - NIST-800-53-AC-7.1(ii) + - accounts_passwords_pam_faillock_dir + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Lock Accounts Must Persist - Remediation where authselect tool is present + block: + + - name: Lock Accounts Must Persist - Check integrity of authselect current profile + ansible.builtin.command: + cmd: authselect check + register: result_authselect_check_cmd + changed_when: false + ignore_errors: true + + - name: Lock Accounts Must Persist - Informative message based on the authselect + integrity check result + ansible.builtin.assert: + that: + - result_authselect_check_cmd is success + fail_msg: + - authselect integrity check failed. Remediation aborted! + - This remediation could not be applied because an authselect profile was not + selected or the selected profile is not intact. + - It is not recommended to manually edit the PAM files when authselect tool + is available. + - In cases where the default authselect profile does not cover a specific demand, + a custom authselect profile is recommended. + success_msg: + - authselect integrity check passed + + - name: Lock Accounts Must Persist - Get authselect current features + ansible.builtin.shell: + cmd: authselect current | tail -n+3 | awk '{ print $2 }' + register: result_authselect_features + changed_when: false + when: + - result_authselect_check_cmd is success + + - name: Lock Accounts Must Persist - Ensure "with-faillock" feature is enabled using + authselect tool + ansible.builtin.command: + cmd: authselect enable-feature with-faillock + register: result_authselect_enable_feature_cmd + when: + - result_authselect_check_cmd is success + - result_authselect_features.stdout is not search("with-faillock") + + - name: Lock Accounts Must Persist - Ensure authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: + - result_authselect_enable_feature_cmd is not skipped + - result_authselect_enable_feature_cmd is success + when: + - '"pam" in ansible_facts.packages' + - result_authselect_present.stat.exists + tags: + - DISA-STIG-RHEL-08-020016 + - NIST-800-53-AC-7(a) + - NIST-800-53-AC-7(b) + - NIST-800-53-AC-7.1(ii) + - accounts_passwords_pam_faillock_dir + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Lock Accounts Must Persist - Remediation where authselect tool is not present + block: + + - name: Lock Accounts Must Persist - Check if pam_faillock.so is already enabled + ansible.builtin.lineinfile: + path: /etc/pam.d/system-auth + regexp: .*auth.*pam_faillock\.so (preauth|authfail) + state: absent + check_mode: true + changed_when: false + register: result_pam_faillock_is_enabled + + - name: Lock Accounts Must Persist - Enable pam_faillock.so preauth editing PAM + files + ansible.builtin.lineinfile: + path: '{{ item }}' + line: auth required pam_faillock.so preauth + insertbefore: ^auth.*sufficient.*pam_unix\.so.* + state: present + loop: + - /etc/pam.d/system-auth + - /etc/pam.d/password-auth + when: + - result_pam_faillock_is_enabled.found == 0 + + - name: Lock Accounts Must Persist - Enable pam_faillock.so authfail editing PAM + files + ansible.builtin.lineinfile: + path: '{{ item }}' + line: auth required pam_faillock.so authfail + insertbefore: ^auth.*required.*pam_deny\.so.* + state: present + loop: + - /etc/pam.d/system-auth + - /etc/pam.d/password-auth + when: + - result_pam_faillock_is_enabled.found == 0 + + - name: Lock Accounts Must Persist - Enable pam_faillock.so account section editing + PAM files + ansible.builtin.lineinfile: + path: '{{ item }}' + line: account required pam_faillock.so + insertbefore: ^account.*required.*pam_unix\.so.* + state: present + loop: + - /etc/pam.d/system-auth + - /etc/pam.d/password-auth + when: + - result_pam_faillock_is_enabled.found == 0 + when: + - '"pam" in ansible_facts.packages' + - not result_authselect_present.stat.exists + tags: + - DISA-STIG-RHEL-08-020016 + - NIST-800-53-AC-7(a) + - NIST-800-53-AC-7(b) + - NIST-800-53-AC-7.1(ii) + - accounts_passwords_pam_faillock_dir + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed +- name: XCCDF Value var_accounts_passwords_pam_faillock_dir # promote to variable + set_fact: + var_accounts_passwords_pam_faillock_dir: !!str + tags: + - always + +- name: Lock Accounts Must Persist - Check the presence of /etc/security/faillock.conf + file + ansible.builtin.stat: + path: /etc/security/faillock.conf + register: result_faillock_conf_check + when: '"pam" in ansible_facts.packages' + tags: + - DISA-STIG-RHEL-08-020016 + - NIST-800-53-AC-7(a) + - NIST-800-53-AC-7(b) + - NIST-800-53-AC-7.1(ii) + - accounts_passwords_pam_faillock_dir + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Lock Accounts Must Persist - Ensure the pam_faillock.so dir parameter in /etc/security/faillock.conf + ansible.builtin.lineinfile: + path: /etc/security/faillock.conf + regexp: ^\s*dir\s*= + line: dir = {{ var_accounts_passwords_pam_faillock_dir }} + state: present + when: + - '"pam" in ansible_facts.packages' + - result_faillock_conf_check.stat.exists + tags: + - DISA-STIG-RHEL-08-020016 + - NIST-800-53-AC-7(a) + - NIST-800-53-AC-7(b) + - NIST-800-53-AC-7.1(ii) + - accounts_passwords_pam_faillock_dir + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Lock Accounts Must Persist - Ensure the pam_faillock.so dir parameter not + in PAM files + block: + + - name: Lock Accounts Must Persist - Check if /etc/pam.d/system-auth file is present + ansible.builtin.stat: + path: /etc/pam.d/system-auth + register: result_pam_file_present + + - name: Lock Accounts Must Persist - Check the proper remediation for the system + block: + + - name: Lock Accounts Must Persist - Define the PAM file to be edited as a local + fact + ansible.builtin.set_fact: + pam_file_path: /etc/pam.d/system-auth + + - name: Lock Accounts Must Persist - Check if system relies on authselect + ansible.builtin.stat: + path: /usr/bin/authselect + register: result_authselect_present + + - name: Lock Accounts Must Persist - Remediate using authselect + block: + + - name: Lock Accounts Must Persist - Check integrity of authselect current profile + ansible.builtin.command: + cmd: authselect check + register: result_authselect_check_cmd + changed_when: false + ignore_errors: true + + - name: Lock Accounts Must Persist - Informative message based on the authselect + integrity check result + ansible.builtin.assert: + that: + - result_authselect_check_cmd is success + fail_msg: + - authselect integrity check failed. Remediation aborted! + - This remediation could not be applied because an authselect profile was + not selected or the selected profile is not intact. + - It is not recommended to manually edit the PAM files when authselect tool + is available. + - In cases where the default authselect profile does not cover a specific + demand, a custom authselect profile is recommended. + success_msg: + - authselect integrity check passed + + - name: Lock Accounts Must Persist - Get authselect current profile + ansible.builtin.shell: + cmd: authselect current -r | awk '{ print $1 }' + register: result_authselect_profile + changed_when: false + when: + - result_authselect_check_cmd is success + + - name: Lock Accounts Must Persist - Define the current authselect profile as + a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: '{{ result_authselect_profile.stdout }}' + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is match("custom/") + + - name: Lock Accounts Must Persist - Define the new authselect custom profile + as a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: custom/hardening + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is not match("custom/") + + - name: Lock Accounts Must Persist - Get authselect current features to also + enable them in the custom profile + ansible.builtin.shell: + cmd: authselect current | tail -n+3 | awk '{ print $2 }' + register: result_authselect_features + changed_when: false + when: + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + + - name: Lock Accounts Must Persist - Check if any custom profile with the same + name was already created + ansible.builtin.stat: + path: /etc/authselect/{{ authselect_custom_profile }} + register: result_authselect_custom_profile_present + changed_when: false + when: + - authselect_current_profile is not match("custom/") + + - name: Lock Accounts Must Persist - Create an authselect custom profile based + on the current profile + ansible.builtin.command: + cmd: authselect create-profile hardening -b {{ authselect_current_profile + }} + when: + - result_authselect_check_cmd is success + - authselect_current_profile is not match("custom/") + - not result_authselect_custom_profile_present.stat.exists + + - name: Lock Accounts Must Persist - Ensure authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=before-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Lock Accounts Must Persist - Ensure the authselect custom profile is + selected + ansible.builtin.command: + cmd: authselect select {{ authselect_custom_profile }} + register: result_pam_authselect_select_profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Lock Accounts Must Persist - Restore the authselect features in the + custom profile + ansible.builtin.command: + cmd: authselect enable-feature {{ item }} + loop: '{{ result_authselect_features.stdout_lines }}' + register: result_pam_authselect_restore_features + when: + - result_authselect_profile is not skipped + - result_authselect_features is not skipped + - result_pam_authselect_select_profile is not skipped + + - name: Lock Accounts Must Persist - Ensure authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=after-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - result_pam_authselect_restore_features is not skipped + + - name: Lock Accounts Must Persist - Change the PAM file to be edited according + to the custom authselect profile + ansible.builtin.set_fact: + pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path + | basename }} + when: + - result_authselect_present.stat.exists + + - name: Lock Accounts Must Persist - Ensure the "dir" option from "pam_faillock.so" + is not present in {{ pam_file_path }} + ansible.builtin.replace: + dest: '{{ pam_file_path }}' + regexp: (.*auth.*pam_faillock.so.*)\bdir\b=?[0-9a-zA-Z]*(.*) + replace: \1\2 + register: result_pam_option_removal + + - name: Lock Accounts Must Persist - Ensure authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: + - result_authselect_present.stat.exists + - result_pam_option_removal is changed + when: + - result_pam_file_present.stat.exists + + - name: Lock Accounts Must Persist - Check if /etc/pam.d/password-auth file is present + ansible.builtin.stat: + path: /etc/pam.d/password-auth + register: result_pam_file_present + + - name: Lock Accounts Must Persist - Check the proper remediation for the system + block: + + - name: Lock Accounts Must Persist - Define the PAM file to be edited as a local + fact + ansible.builtin.set_fact: + pam_file_path: /etc/pam.d/password-auth + + - name: Lock Accounts Must Persist - Check if system relies on authselect + ansible.builtin.stat: + path: /usr/bin/authselect + register: result_authselect_present + + - name: Lock Accounts Must Persist - Remediate using authselect + block: + + - name: Lock Accounts Must Persist - Check integrity of authselect current profile + ansible.builtin.command: + cmd: authselect check + register: result_authselect_check_cmd + changed_when: false + ignore_errors: true + + - name: Lock Accounts Must Persist - Informative message based on the authselect + integrity check result + ansible.builtin.assert: + that: + - result_authselect_check_cmd is success + fail_msg: + - authselect integrity check failed. Remediation aborted! + - This remediation could not be applied because an authselect profile was + not selected or the selected profile is not intact. + - It is not recommended to manually edit the PAM files when authselect tool + is available. + - In cases where the default authselect profile does not cover a specific + demand, a custom authselect profile is recommended. + success_msg: + - authselect integrity check passed + + - name: Lock Accounts Must Persist - Get authselect current profile + ansible.builtin.shell: + cmd: authselect current -r | awk '{ print $1 }' + register: result_authselect_profile + changed_when: false + when: + - result_authselect_check_cmd is success + + - name: Lock Accounts Must Persist - Define the current authselect profile as + a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: '{{ result_authselect_profile.stdout }}' + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is match("custom/") + + - name: Lock Accounts Must Persist - Define the new authselect custom profile + as a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: custom/hardening + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is not match("custom/") + + - name: Lock Accounts Must Persist - Get authselect current features to also + enable them in the custom profile + ansible.builtin.shell: + cmd: authselect current | tail -n+3 | awk '{ print $2 }' + register: result_authselect_features + changed_when: false + when: + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + + - name: Lock Accounts Must Persist - Check if any custom profile with the same + name was already created + ansible.builtin.stat: + path: /etc/authselect/{{ authselect_custom_profile }} + register: result_authselect_custom_profile_present + changed_when: false + when: + - authselect_current_profile is not match("custom/") + + - name: Lock Accounts Must Persist - Create an authselect custom profile based + on the current profile + ansible.builtin.command: + cmd: authselect create-profile hardening -b {{ authselect_current_profile + }} + when: + - result_authselect_check_cmd is success + - authselect_current_profile is not match("custom/") + - not result_authselect_custom_profile_present.stat.exists + + - name: Lock Accounts Must Persist - Ensure authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=before-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Lock Accounts Must Persist - Ensure the authselect custom profile is + selected + ansible.builtin.command: + cmd: authselect select {{ authselect_custom_profile }} + register: result_pam_authselect_select_profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Lock Accounts Must Persist - Restore the authselect features in the + custom profile + ansible.builtin.command: + cmd: authselect enable-feature {{ item }} + loop: '{{ result_authselect_features.stdout_lines }}' + register: result_pam_authselect_restore_features + when: + - result_authselect_profile is not skipped + - result_authselect_features is not skipped + - result_pam_authselect_select_profile is not skipped + + - name: Lock Accounts Must Persist - Ensure authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=after-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - result_pam_authselect_restore_features is not skipped + + - name: Lock Accounts Must Persist - Change the PAM file to be edited according + to the custom authselect profile + ansible.builtin.set_fact: + pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path + | basename }} + when: + - result_authselect_present.stat.exists + + - name: Lock Accounts Must Persist - Ensure the "dir" option from "pam_faillock.so" + is not present in {{ pam_file_path }} + ansible.builtin.replace: + dest: '{{ pam_file_path }}' + regexp: (.*auth.*pam_faillock.so.*)\bdir\b=?[0-9a-zA-Z]*(.*) + replace: \1\2 + register: result_pam_option_removal + + - name: Lock Accounts Must Persist - Ensure authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: + - result_authselect_present.stat.exists + - result_pam_option_removal is changed + when: + - result_pam_file_present.stat.exists + when: + - '"pam" in ansible_facts.packages' + - result_faillock_conf_check.stat.exists + tags: + - DISA-STIG-RHEL-08-020016 + - NIST-800-53-AC-7(a) + - NIST-800-53-AC-7(b) + - NIST-800-53-AC-7.1(ii) + - accounts_passwords_pam_faillock_dir + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Lock Accounts Must Persist - Ensure the pam_faillock.so dir parameter in PAM + files + block: + + - name: Lock Accounts Must Persist - Check if pam_faillock.so dir parameter is already + enabled in pam files + ansible.builtin.lineinfile: + path: /etc/pam.d/system-auth + regexp: .*auth.*pam_faillock\.so (preauth|authfail).*dir + state: absent + check_mode: true + changed_when: false + register: result_pam_faillock_dir_parameter_is_present + + - name: Lock Accounts Must Persist - Ensure the inclusion of pam_faillock.so preauth + dir parameter in auth section + ansible.builtin.lineinfile: + path: '{{ item }}' + backrefs: true + regexp: (^\s*auth\s+)([\w\[].*\b)(\s+pam_faillock.so preauth.*) + line: \1required\3 dir={{ var_accounts_passwords_pam_faillock_dir }} + state: present + loop: + - /etc/pam.d/system-auth + - /etc/pam.d/password-auth + when: + - result_pam_faillock_dir_parameter_is_present.found == 0 + + - name: Lock Accounts Must Persist - Ensure the inclusion of pam_faillock.so authfail + dir parameter in auth section + ansible.builtin.lineinfile: + path: '{{ item }}' + backrefs: true + regexp: (^\s*auth\s+)([\w\[].*\b)(\s+pam_faillock.so authfail.*) + line: \1required\3 dir={{ var_accounts_passwords_pam_faillock_dir }} + state: present + loop: + - /etc/pam.d/system-auth + - /etc/pam.d/password-auth + when: + - result_pam_faillock_dir_parameter_is_present.found == 0 + + - name: Lock Accounts Must Persist - Ensure the desired value for pam_faillock.so + preauth dir parameter in auth section + ansible.builtin.lineinfile: + path: '{{ item }}' + backrefs: true + regexp: (^\s*auth\s+)([\w\[].*\b)(\s+pam_faillock.so preauth.*)(dir)=[0-9]+(.*) + line: \1required\3\4={{ var_accounts_passwords_pam_faillock_dir }}\5 + state: present + loop: + - /etc/pam.d/system-auth + - /etc/pam.d/password-auth + when: + - result_pam_faillock_dir_parameter_is_present.found > 0 + + - name: Lock Accounts Must Persist - Ensure the desired value for pam_faillock.so + authfail dir parameter in auth section + ansible.builtin.lineinfile: + path: '{{ item }}' + backrefs: true + regexp: (^\s*auth\s+)([\w\[].*\b)(\s+pam_faillock.so authfail.*)(dir)=[0-9]+(.*) + line: \1required\3\4={{ var_accounts_passwords_pam_faillock_dir }}\5 + state: present + loop: + - /etc/pam.d/system-auth + - /etc/pam.d/password-auth + when: + - result_pam_faillock_dir_parameter_is_present.found > 0 + when: + - '"pam" in ansible_facts.packages' + - not result_faillock_conf_check.stat.exists + tags: + - DISA-STIG-RHEL-08-020016 + - NIST-800-53-AC-7(a) + - NIST-800-53-AC-7(b) + - NIST-800-53-AC-7.1(ii) + - accounts_passwords_pam_faillock_dir + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if rpm --quiet -q pam; then + +if [ -f /usr/bin/authselect ]; then + if ! authselect check; then +echo " +authselect integrity check failed. Remediation aborted! +This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact. +It is not recommended to manually edit the PAM files when authselect tool is available. +In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended." +exit 1 +fi +authselect enable-feature with-faillock + +authselect apply-changes -b +else + AUTH_FILES=("/etc/pam.d/system-auth" "/etc/pam.d/password-auth") +for pam_file in "${AUTH_FILES[@]}" +do + if ! grep -qE '^\s*auth\s+required\s+pam_faillock\.so\s+(preauth silent|authfail).*$' "$pam_file" ; then + sed -i --follow-symlinks '/^auth.*sufficient.*pam_unix\.so.*/i auth required pam_faillock.so preauth silent' "$pam_file" + sed -i --follow-symlinks '/^auth.*required.*pam_deny\.so.*/i auth required pam_faillock.so authfail' "$pam_file" + sed -i --follow-symlinks '/^account.*required.*pam_unix\.so.*/i account required pam_faillock.so' "$pam_file" + fi + sed -Ei 's/(auth.*)(\[default=die\])(.*pam_faillock\.so)/\1required \3/g' "$pam_file" +done +fi + +AUTH_FILES=("/etc/pam.d/system-auth" "/etc/pam.d/password-auth") +FAILLOCK_CONF="/etc/security/faillock.conf" +if [ -f $FAILLOCK_CONF ]; then + regex="^\s*dir\s*=" + line="dir = /var/log/faillock" + if ! grep -q $regex $FAILLOCK_CONF; then + echo $line >> $FAILLOCK_CONF + else + sed -i --follow-symlinks 's|^\s*\(dir\s*=\s*\)\(\S\+\)|\1'"/var/log/faillock"'|g' $FAILLOCK_CONF + fi + for pam_file in "${AUTH_FILES[@]}" + do + if [ -e "$pam_file" ] ; then + PAM_FILE_PATH="$pam_file" + if [ -f /usr/bin/authselect ]; then + if ! authselect check; then + echo " + authselect integrity check failed. Remediation aborted! + This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact. + It is not recommended to manually edit the PAM files when authselect tool is available. + In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended." + exit 1 + fi + CURRENT_PROFILE=$(authselect current -r | awk '{ print $1 }') + # If not already in use, a custom profile is created preserving the enabled features. + if [[ ! $CURRENT_PROFILE == custom/* ]]; then + ENABLED_FEATURES=$(authselect current | tail -n+3 | awk '{ print $2 }') + authselect create-profile hardening -b $CURRENT_PROFILE + CURRENT_PROFILE="custom/hardening" + + authselect apply-changes -b --backup=before-hardening-custom-profile + authselect select $CURRENT_PROFILE + for feature in $ENABLED_FEATURES; do + authselect enable-feature $feature; + done + + authselect apply-changes -b --backup=after-hardening-custom-profile + fi + PAM_FILE_NAME=$(basename "$pam_file") + PAM_FILE_PATH="/etc/authselect/$CURRENT_PROFILE/$PAM_FILE_NAME" + + authselect apply-changes -b + fi + + if grep -qP '^\s*auth\s.*\bpam_faillock.so\s.*\bdir\b' "$PAM_FILE_PATH"; then + sed -i -E --follow-symlinks 's/(.*auth.*pam_faillock.so.*)\bdir\b=?[[:alnum:]]*(.*)/\1\2/g' "$PAM_FILE_PATH" + fi + if [ -f /usr/bin/authselect ]; then + + authselect apply-changes -b + fi + else + echo "$pam_file was not found" >&2 + fi + done +else + for pam_file in "${AUTH_FILES[@]}" + do + if ! grep -qE '^\s*auth.*pam_faillock\.so (preauth|authfail).*dir' "$pam_file"; then + sed -i --follow-symlinks '/^auth.*required.*pam_faillock\.so.*preauth.*silent.*/ s/$/ dir='"/var/log/faillock"'/' "$pam_file" + sed -i --follow-symlinks '/^auth.*required.*pam_faillock\.so.*authfail.*/ s/$/ dir='"/var/log/faillock"'/' "$pam_file" + else + sed -i --follow-symlinks 's/\(^auth.*required.*pam_faillock\.so.*preauth.*silent.*\)\('"dir"'=\)[0-9]\+\(.*\)/\1\2'"/var/log/faillock"'\3/' "$pam_file" + sed -i --follow-symlinks 's/\(^auth.*required.*pam_faillock\.so.*authfail.*\)\('"dir"'=\)[0-9]\+\(.*\)/\1\2'"/var/log/faillock"'\3/' "$pam_file" + fi + done +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Enforce pam_faillock for Local Accounts Only + The pam_faillock module's local_users_only parameter controls requirements for +enforcing failed lockout attempts only for local user accounts and ignoring centralized user +account management failed attempt configurations. + If the system relies on authselect tool to manage PAM settings, the remediation +will also use authselect tool. However, if any manual modification was made in +PAM files, the authselect integrity check will fail and the remediation will be +aborted in order to preserve intentional changes. In this case, an informative message will +be shown in the remediation report. +If the system supports the /etc/security/faillock.conf file, the pam_faillock +parameters should be defined in faillock.conf file. + Using this rule bypasses pam_faillock's functionality and should be used in cases +where centralized management such as LDAP or Active Directory is in use. + CCI-000015 + AC-2(1) + SRG-OS-000001-GPOS-00001 + The operating system must provide automated mechanisms for supporting account management +functions. Enterprise environments make application account management challenging and +complex. A manual process for account management functions adds the risk of a potential +oversight or other error. Locking out remote accounts may cause unintentional DoS. + + CCE-83401-0 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83401-0 + - NIST-800-53-AC-2(1) + - accounts_passwords_pam_faillock_enforce_local + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Enforce pam_faillock for Local Accounts Only - Check if system relies on authselect + tool + ansible.builtin.stat: + path: /usr/bin/authselect + register: result_authselect_present + when: '"pam" in ansible_facts.packages' + tags: + - CCE-83401-0 + - NIST-800-53-AC-2(1) + - accounts_passwords_pam_faillock_enforce_local + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Enforce pam_faillock for Local Accounts Only - Remediation where authselect + tool is present + block: + + - name: Enforce pam_faillock for Local Accounts Only - Check integrity of authselect + current profile + ansible.builtin.command: + cmd: authselect check + register: result_authselect_check_cmd + changed_when: false + ignore_errors: true + + - name: Enforce pam_faillock for Local Accounts Only - Informative message based + on the authselect integrity check result + ansible.builtin.assert: + that: + - result_authselect_check_cmd is success + fail_msg: + - authselect integrity check failed. Remediation aborted! + - This remediation could not be applied because an authselect profile was not + selected or the selected profile is not intact. + - It is not recommended to manually edit the PAM files when authselect tool + is available. + - In cases where the default authselect profile does not cover a specific demand, + a custom authselect profile is recommended. + success_msg: + - authselect integrity check passed + + - name: Enforce pam_faillock for Local Accounts Only - Get authselect current features + ansible.builtin.shell: + cmd: authselect current | tail -n+3 | awk '{ print $2 }' + register: result_authselect_features + changed_when: false + when: + - result_authselect_check_cmd is success + + - name: Enforce pam_faillock for Local Accounts Only - Ensure "with-faillock" feature + is enabled using authselect tool + ansible.builtin.command: + cmd: authselect enable-feature with-faillock + register: result_authselect_enable_feature_cmd + when: + - result_authselect_check_cmd is success + - result_authselect_features.stdout is not search("with-faillock") + + - name: Enforce pam_faillock for Local Accounts Only - Ensure authselect changes + are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: + - result_authselect_enable_feature_cmd is not skipped + - result_authselect_enable_feature_cmd is success + when: + - '"pam" in ansible_facts.packages' + - result_authselect_present.stat.exists + tags: + - CCE-83401-0 + - NIST-800-53-AC-2(1) + - accounts_passwords_pam_faillock_enforce_local + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Enforce pam_faillock for Local Accounts Only - Remediation where authselect + tool is not present + block: + + - name: Enforce pam_faillock for Local Accounts Only - Check if pam_faillock.so + is already enabled + ansible.builtin.lineinfile: + path: /etc/pam.d/system-auth + regexp: .*auth.*pam_faillock\.so (preauth|authfail) + state: absent + check_mode: true + changed_when: false + register: result_pam_faillock_is_enabled + + - name: Enforce pam_faillock for Local Accounts Only - Enable pam_faillock.so preauth + editing PAM files + ansible.builtin.lineinfile: + path: '{{ item }}' + line: auth required pam_faillock.so preauth + insertbefore: ^auth.*sufficient.*pam_unix\.so.* + state: present + loop: + - /etc/pam.d/system-auth + - /etc/pam.d/password-auth + when: + - result_pam_faillock_is_enabled.found == 0 + + - name: Enforce pam_faillock for Local Accounts Only - Enable pam_faillock.so authfail + editing PAM files + ansible.builtin.lineinfile: + path: '{{ item }}' + line: auth required pam_faillock.so authfail + insertbefore: ^auth.*required.*pam_deny\.so.* + state: present + loop: + - /etc/pam.d/system-auth + - /etc/pam.d/password-auth + when: + - result_pam_faillock_is_enabled.found == 0 + + - name: Enforce pam_faillock for Local Accounts Only - Enable pam_faillock.so account + section editing PAM files + ansible.builtin.lineinfile: + path: '{{ item }}' + line: account required pam_faillock.so + insertbefore: ^account.*required.*pam_unix\.so.* + state: present + loop: + - /etc/pam.d/system-auth + - /etc/pam.d/password-auth + when: + - result_pam_faillock_is_enabled.found == 0 + when: + - '"pam" in ansible_facts.packages' + - not result_authselect_present.stat.exists + tags: + - CCE-83401-0 + - NIST-800-53-AC-2(1) + - accounts_passwords_pam_faillock_enforce_local + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Enforce pam_faillock for Local Accounts Only - Check the presence of /etc/security/faillock.conf + file + ansible.builtin.stat: + path: /etc/security/faillock.conf + register: result_faillock_conf_check + when: '"pam" in ansible_facts.packages' + tags: + - CCE-83401-0 + - NIST-800-53-AC-2(1) + - accounts_passwords_pam_faillock_enforce_local + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Enforce pam_faillock for Local Accounts Only - Ensure the pam_faillock.so + local_users_only parameter in /etc/security/faillock.conf + ansible.builtin.lineinfile: + path: /etc/security/faillock.conf + regexp: ^\s*local_users_only + line: local_users_only + state: present + when: + - '"pam" in ansible_facts.packages' + - result_faillock_conf_check.stat.exists + tags: + - CCE-83401-0 + - NIST-800-53-AC-2(1) + - accounts_passwords_pam_faillock_enforce_local + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Enforce pam_faillock for Local Accounts Only - Ensure the pam_faillock.so + local_users_only parameter not in PAM files + block: + + - name: Enforce pam_faillock for Local Accounts Only - Check if /etc/pam.d/system-auth + file is present + ansible.builtin.stat: + path: /etc/pam.d/system-auth + register: result_pam_file_present + + - name: Enforce pam_faillock for Local Accounts Only - Check the proper remediation + for the system + block: + + - name: Enforce pam_faillock for Local Accounts Only - Define the PAM file to + be edited as a local fact + ansible.builtin.set_fact: + pam_file_path: /etc/pam.d/system-auth + + - name: Enforce pam_faillock for Local Accounts Only - Check if system relies + on authselect + ansible.builtin.stat: + path: /usr/bin/authselect + register: result_authselect_present + + - name: Enforce pam_faillock for Local Accounts Only - Remediate using authselect + block: + + - name: Enforce pam_faillock for Local Accounts Only - Check integrity of authselect + current profile + ansible.builtin.command: + cmd: authselect check + register: result_authselect_check_cmd + changed_when: false + ignore_errors: true + + - name: Enforce pam_faillock for Local Accounts Only - Informative message based + on the authselect integrity check result + ansible.builtin.assert: + that: + - result_authselect_check_cmd is success + fail_msg: + - authselect integrity check failed. Remediation aborted! + - This remediation could not be applied because an authselect profile was + not selected or the selected profile is not intact. + - It is not recommended to manually edit the PAM files when authselect tool + is available. + - In cases where the default authselect profile does not cover a specific + demand, a custom authselect profile is recommended. + success_msg: + - authselect integrity check passed + + - name: Enforce pam_faillock for Local Accounts Only - Get authselect current + profile + ansible.builtin.shell: + cmd: authselect current -r | awk '{ print $1 }' + register: result_authselect_profile + changed_when: false + when: + - result_authselect_check_cmd is success + + - name: Enforce pam_faillock for Local Accounts Only - Define the current authselect + profile as a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: '{{ result_authselect_profile.stdout }}' + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is match("custom/") + + - name: Enforce pam_faillock for Local Accounts Only - Define the new authselect + custom profile as a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: custom/hardening + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is not match("custom/") + + - name: Enforce pam_faillock for Local Accounts Only - Get authselect current + features to also enable them in the custom profile + ansible.builtin.shell: + cmd: authselect current | tail -n+3 | awk '{ print $2 }' + register: result_authselect_features + changed_when: false + when: + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + + - name: Enforce pam_faillock for Local Accounts Only - Check if any custom profile + with the same name was already created + ansible.builtin.stat: + path: /etc/authselect/{{ authselect_custom_profile }} + register: result_authselect_custom_profile_present + changed_when: false + when: + - authselect_current_profile is not match("custom/") + + - name: Enforce pam_faillock for Local Accounts Only - Create an authselect + custom profile based on the current profile + ansible.builtin.command: + cmd: authselect create-profile hardening -b {{ authselect_current_profile + }} + when: + - result_authselect_check_cmd is success + - authselect_current_profile is not match("custom/") + - not result_authselect_custom_profile_present.stat.exists + + - name: Enforce pam_faillock for Local Accounts Only - Ensure authselect changes + are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=before-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Enforce pam_faillock for Local Accounts Only - Ensure the authselect + custom profile is selected + ansible.builtin.command: + cmd: authselect select {{ authselect_custom_profile }} + register: result_pam_authselect_select_profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Enforce pam_faillock for Local Accounts Only - Restore the authselect + features in the custom profile + ansible.builtin.command: + cmd: authselect enable-feature {{ item }} + loop: '{{ result_authselect_features.stdout_lines }}' + register: result_pam_authselect_restore_features + when: + - result_authselect_profile is not skipped + - result_authselect_features is not skipped + - result_pam_authselect_select_profile is not skipped + + - name: Enforce pam_faillock for Local Accounts Only - Ensure authselect changes + are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=after-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - result_pam_authselect_restore_features is not skipped + + - name: Enforce pam_faillock for Local Accounts Only - Change the PAM file to + be edited according to the custom authselect profile + ansible.builtin.set_fact: + pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path + | basename }} + when: + - result_authselect_present.stat.exists + + - name: Enforce pam_faillock for Local Accounts Only - Ensure the "local_users_only" + option from "pam_faillock.so" is not present in {{ pam_file_path }} + ansible.builtin.replace: + dest: '{{ pam_file_path }}' + regexp: (.*auth.*pam_faillock.so.*)\blocal_users_only\b=?[0-9a-zA-Z]*(.*) + replace: \1\2 + register: result_pam_option_removal + + - name: Enforce pam_faillock for Local Accounts Only - Ensure authselect changes + are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: + - result_authselect_present.stat.exists + - result_pam_option_removal is changed + when: + - result_pam_file_present.stat.exists + + - name: Enforce pam_faillock for Local Accounts Only - Check if /etc/pam.d/password-auth + file is present + ansible.builtin.stat: + path: /etc/pam.d/password-auth + register: result_pam_file_present + + - name: Enforce pam_faillock for Local Accounts Only - Check the proper remediation + for the system + block: + + - name: Enforce pam_faillock for Local Accounts Only - Define the PAM file to + be edited as a local fact + ansible.builtin.set_fact: + pam_file_path: /etc/pam.d/password-auth + + - name: Enforce pam_faillock for Local Accounts Only - Check if system relies + on authselect + ansible.builtin.stat: + path: /usr/bin/authselect + register: result_authselect_present + + - name: Enforce pam_faillock for Local Accounts Only - Remediate using authselect + block: + + - name: Enforce pam_faillock for Local Accounts Only - Check integrity of authselect + current profile + ansible.builtin.command: + cmd: authselect check + register: result_authselect_check_cmd + changed_when: false + ignore_errors: true + + - name: Enforce pam_faillock for Local Accounts Only - Informative message based + on the authselect integrity check result + ansible.builtin.assert: + that: + - result_authselect_check_cmd is success + fail_msg: + - authselect integrity check failed. Remediation aborted! + - This remediation could not be applied because an authselect profile was + not selected or the selected profile is not intact. + - It is not recommended to manually edit the PAM files when authselect tool + is available. + - In cases where the default authselect profile does not cover a specific + demand, a custom authselect profile is recommended. + success_msg: + - authselect integrity check passed + + - name: Enforce pam_faillock for Local Accounts Only - Get authselect current + profile + ansible.builtin.shell: + cmd: authselect current -r | awk '{ print $1 }' + register: result_authselect_profile + changed_when: false + when: + - result_authselect_check_cmd is success + + - name: Enforce pam_faillock for Local Accounts Only - Define the current authselect + profile as a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: '{{ result_authselect_profile.stdout }}' + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is match("custom/") + + - name: Enforce pam_faillock for Local Accounts Only - Define the new authselect + custom profile as a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: custom/hardening + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is not match("custom/") + + - name: Enforce pam_faillock for Local Accounts Only - Get authselect current + features to also enable them in the custom profile + ansible.builtin.shell: + cmd: authselect current | tail -n+3 | awk '{ print $2 }' + register: result_authselect_features + changed_when: false + when: + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + + - name: Enforce pam_faillock for Local Accounts Only - Check if any custom profile + with the same name was already created + ansible.builtin.stat: + path: /etc/authselect/{{ authselect_custom_profile }} + register: result_authselect_custom_profile_present + changed_when: false + when: + - authselect_current_profile is not match("custom/") + + - name: Enforce pam_faillock for Local Accounts Only - Create an authselect + custom profile based on the current profile + ansible.builtin.command: + cmd: authselect create-profile hardening -b {{ authselect_current_profile + }} + when: + - result_authselect_check_cmd is success + - authselect_current_profile is not match("custom/") + - not result_authselect_custom_profile_present.stat.exists + + - name: Enforce pam_faillock for Local Accounts Only - Ensure authselect changes + are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=before-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Enforce pam_faillock for Local Accounts Only - Ensure the authselect + custom profile is selected + ansible.builtin.command: + cmd: authselect select {{ authselect_custom_profile }} + register: result_pam_authselect_select_profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Enforce pam_faillock for Local Accounts Only - Restore the authselect + features in the custom profile + ansible.builtin.command: + cmd: authselect enable-feature {{ item }} + loop: '{{ result_authselect_features.stdout_lines }}' + register: result_pam_authselect_restore_features + when: + - result_authselect_profile is not skipped + - result_authselect_features is not skipped + - result_pam_authselect_select_profile is not skipped + + - name: Enforce pam_faillock for Local Accounts Only - Ensure authselect changes + are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=after-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - result_pam_authselect_restore_features is not skipped + + - name: Enforce pam_faillock for Local Accounts Only - Change the PAM file to + be edited according to the custom authselect profile + ansible.builtin.set_fact: + pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path + | basename }} + when: + - result_authselect_present.stat.exists + + - name: Enforce pam_faillock for Local Accounts Only - Ensure the "local_users_only" + option from "pam_faillock.so" is not present in {{ pam_file_path }} + ansible.builtin.replace: + dest: '{{ pam_file_path }}' + regexp: (.*auth.*pam_faillock.so.*)\blocal_users_only\b=?[0-9a-zA-Z]*(.*) + replace: \1\2 + register: result_pam_option_removal + + - name: Enforce pam_faillock for Local Accounts Only - Ensure authselect changes + are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: + - result_authselect_present.stat.exists + - result_pam_option_removal is changed + when: + - result_pam_file_present.stat.exists + when: + - '"pam" in ansible_facts.packages' + - result_faillock_conf_check.stat.exists + tags: + - CCE-83401-0 + - NIST-800-53-AC-2(1) + - accounts_passwords_pam_faillock_enforce_local + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Enforce pam_faillock for Local Accounts Only - Ensure the pam_faillock.so + local_users_only parameter in PAM files + block: + + - name: Enforce pam_faillock for Local Accounts Only - Check if pam_faillock.so + local_users_only parameter is already enabled in pam files + ansible.builtin.lineinfile: + path: /etc/pam.d/system-auth + regexp: .*auth.*pam_faillock\.so (preauth|authfail).*local_users_only + state: absent + check_mode: true + changed_when: false + register: result_pam_faillock_local_users_only_parameter_is_present + + - name: Enforce pam_faillock for Local Accounts Only - Ensure the inclusion of pam_faillock.so + preauth local_users_only parameter in auth section + ansible.builtin.lineinfile: + path: '{{ item }}' + backrefs: true + regexp: (^\s*auth\s+)([\w\[].*\b)(\s+pam_faillock.so preauth.*) + line: \1required\3 local_users_only + state: present + loop: + - /etc/pam.d/system-auth + - /etc/pam.d/password-auth + when: + - result_pam_faillock_local_users_only_parameter_is_present.found == 0 + + - name: Enforce pam_faillock for Local Accounts Only - Ensure the inclusion of pam_faillock.so + authfail local_users_only parameter in auth section + ansible.builtin.lineinfile: + path: '{{ item }}' + backrefs: true + regexp: (^\s*auth\s+)([\w\[].*\b)(\s+pam_faillock.so authfail.*) + line: \1required\3 local_users_only + state: present + loop: + - /etc/pam.d/system-auth + - /etc/pam.d/password-auth + when: + - result_pam_faillock_local_users_only_parameter_is_present.found == 0 + when: + - '"pam" in ansible_facts.packages' + - not result_faillock_conf_check.stat.exists + tags: + - CCE-83401-0 + - NIST-800-53-AC-2(1) + - accounts_passwords_pam_faillock_enforce_local + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q pam; then + +if [ -f /usr/bin/authselect ]; then + if ! authselect check; then +echo " +authselect integrity check failed. Remediation aborted! +This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact. +It is not recommended to manually edit the PAM files when authselect tool is available. +In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended." +exit 1 +fi +authselect enable-feature with-faillock + +authselect apply-changes -b +else + AUTH_FILES=("/etc/pam.d/system-auth" "/etc/pam.d/password-auth") +for pam_file in "${AUTH_FILES[@]}" +do + if ! grep -qE '^\s*auth\s+required\s+pam_faillock\.so\s+(preauth silent|authfail).*$' "$pam_file" ; then + sed -i --follow-symlinks '/^auth.*sufficient.*pam_unix\.so.*/i auth required pam_faillock.so preauth silent' "$pam_file" + sed -i --follow-symlinks '/^auth.*required.*pam_deny\.so.*/i auth required pam_faillock.so authfail' "$pam_file" + sed -i --follow-symlinks '/^account.*required.*pam_unix\.so.*/i account required pam_faillock.so' "$pam_file" + fi + sed -Ei 's/(auth.*)(\[default=die\])(.*pam_faillock\.so)/\1required \3/g' "$pam_file" +done +fi +AUTH_FILES=("/etc/pam.d/system-auth" "/etc/pam.d/password-auth") +FAILLOCK_CONF="/etc/security/faillock.conf" +if [ -f $FAILLOCK_CONF ]; then + regex="^\s*local_users_only" + line="local_users_only" + if ! grep -q $regex $FAILLOCK_CONF; then + echo $line >> $FAILLOCK_CONF + fi + for pam_file in "${AUTH_FILES[@]}" + do + if [ -e "$pam_file" ] ; then + PAM_FILE_PATH="$pam_file" + if [ -f /usr/bin/authselect ]; then + if ! authselect check; then + echo " + authselect integrity check failed. Remediation aborted! + This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact. + It is not recommended to manually edit the PAM files when authselect tool is available. + In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended." + exit 1 + fi + CURRENT_PROFILE=$(authselect current -r | awk '{ print $1 }') + # If not already in use, a custom profile is created preserving the enabled features. + if [[ ! $CURRENT_PROFILE == custom/* ]]; then + ENABLED_FEATURES=$(authselect current | tail -n+3 | awk '{ print $2 }') + authselect create-profile hardening -b $CURRENT_PROFILE + CURRENT_PROFILE="custom/hardening" + + authselect apply-changes -b --backup=before-hardening-custom-profile + authselect select $CURRENT_PROFILE + for feature in $ENABLED_FEATURES; do + authselect enable-feature $feature; + done + + authselect apply-changes -b --backup=after-hardening-custom-profile + fi + PAM_FILE_NAME=$(basename "$pam_file") + PAM_FILE_PATH="/etc/authselect/$CURRENT_PROFILE/$PAM_FILE_NAME" + + authselect apply-changes -b + fi + + if grep -qP '^\s*auth\s.*\bpam_faillock.so\s.*\blocal_users_only\b' "$PAM_FILE_PATH"; then + sed -i -E --follow-symlinks 's/(.*auth.*pam_faillock.so.*)\blocal_users_only\b=?[[:alnum:]]*(.*)/\1\2/g' "$PAM_FILE_PATH" + fi + if [ -f /usr/bin/authselect ]; then + + authselect apply-changes -b + fi + else + echo "$pam_file was not found" >&2 + fi + done +else + for pam_file in "${AUTH_FILES[@]}" + do + if ! grep -qE '^\s*auth.*pam_faillock\.so (preauth|authfail).*local_users_only' "$pam_file"; then + sed -i --follow-symlinks '/^auth.*required.*pam_faillock\.so.*preauth.*silent.*/ s/$/ local_users_only/' "$pam_file" + sed -i --follow-symlinks '/^auth.*required.*pam_faillock\.so.*authfail.*/ s/$/ local_users_only/' "$pam_file" + fi + done +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Set Interval For Counting Failed Password Attempts + Utilizing pam_faillock.so, the fail_interval directive configures the system +to lock out an account after a number of incorrect login attempts within a specified time +period. + If the system relies on authselect tool to manage PAM settings, the remediation +will also use authselect tool. However, if any manual modification was made in +PAM files, the authselect integrity check will fail and the remediation will be +aborted in order to preserve intentional changes. In this case, an informative message will +be shown in the remediation report. +If the system supports the /etc/security/faillock.conf file, the pam_faillock +parameters should be defined in faillock.conf file. + BP28(R18) + 1 + 12 + 15 + 16 + DSS05.04 + DSS05.10 + DSS06.10 + CCI-000044 + CCI-002236 + CCI-002237 + CCI-002238 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + 0421 + 0422 + 0431 + 0974 + 1173 + 1401 + 1504 + 1505 + 1546 + 1557 + 1558 + 1559 + 1560 + 1561 + A.18.1.4 + A.9.2.1 + A.9.2.4 + A.9.3.1 + A.9.4.2 + A.9.4.3 + CM-6(a) + AC-7(a) + PR.AC-7 + FIA_AFL.1 + SRG-OS-000329-GPOS-00128 + SRG-OS-000021-GPOS-00005 + SRG-OS-000021-VMM-000050 + RHEL-08-020012 + SV-230334r627750_rule + By limiting the number of failed logon attempts the risk of unauthorized system +access via user password guessing, otherwise known as brute-forcing, is reduced. +Limits are imposed by locking the account. + + CCE-80669-5 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80669-5 + - DISA-STIG-RHEL-08-020012 + - NIST-800-53-AC-7(a) + - NIST-800-53-CM-6(a) + - accounts_passwords_pam_faillock_interval + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Set Interval For Counting Failed Password Attempts - Check if system relies + on authselect tool + ansible.builtin.stat: + path: /usr/bin/authselect + register: result_authselect_present + when: '"pam" in ansible_facts.packages' + tags: + - CCE-80669-5 + - DISA-STIG-RHEL-08-020012 + - NIST-800-53-AC-7(a) + - NIST-800-53-CM-6(a) + - accounts_passwords_pam_faillock_interval + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Set Interval For Counting Failed Password Attempts - Remediation where authselect + tool is present + block: + + - name: Set Interval For Counting Failed Password Attempts - Check integrity of + authselect current profile + ansible.builtin.command: + cmd: authselect check + register: result_authselect_check_cmd + changed_when: false + ignore_errors: true + + - name: Set Interval For Counting Failed Password Attempts - Informative message + based on the authselect integrity check result + ansible.builtin.assert: + that: + - result_authselect_check_cmd is success + fail_msg: + - authselect integrity check failed. Remediation aborted! + - This remediation could not be applied because an authselect profile was not + selected or the selected profile is not intact. + - It is not recommended to manually edit the PAM files when authselect tool + is available. + - In cases where the default authselect profile does not cover a specific demand, + a custom authselect profile is recommended. + success_msg: + - authselect integrity check passed + + - name: Set Interval For Counting Failed Password Attempts - Get authselect current + features + ansible.builtin.shell: + cmd: authselect current | tail -n+3 | awk '{ print $2 }' + register: result_authselect_features + changed_when: false + when: + - result_authselect_check_cmd is success + + - name: Set Interval For Counting Failed Password Attempts - Ensure "with-faillock" + feature is enabled using authselect tool + ansible.builtin.command: + cmd: authselect enable-feature with-faillock + register: result_authselect_enable_feature_cmd + when: + - result_authselect_check_cmd is success + - result_authselect_features.stdout is not search("with-faillock") + + - name: Set Interval For Counting Failed Password Attempts - Ensure authselect changes + are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: + - result_authselect_enable_feature_cmd is not skipped + - result_authselect_enable_feature_cmd is success + when: + - '"pam" in ansible_facts.packages' + - result_authselect_present.stat.exists + tags: + - CCE-80669-5 + - DISA-STIG-RHEL-08-020012 + - NIST-800-53-AC-7(a) + - NIST-800-53-CM-6(a) + - accounts_passwords_pam_faillock_interval + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Set Interval For Counting Failed Password Attempts - Remediation where authselect + tool is not present + block: + + - name: Set Interval For Counting Failed Password Attempts - Check if pam_faillock.so + is already enabled + ansible.builtin.lineinfile: + path: /etc/pam.d/system-auth + regexp: .*auth.*pam_faillock\.so (preauth|authfail) + state: absent + check_mode: true + changed_when: false + register: result_pam_faillock_is_enabled + + - name: Set Interval For Counting Failed Password Attempts - Enable pam_faillock.so + preauth editing PAM files + ansible.builtin.lineinfile: + path: '{{ item }}' + line: auth required pam_faillock.so preauth + insertbefore: ^auth.*sufficient.*pam_unix\.so.* + state: present + loop: + - /etc/pam.d/system-auth + - /etc/pam.d/password-auth + when: + - result_pam_faillock_is_enabled.found == 0 + + - name: Set Interval For Counting Failed Password Attempts - Enable pam_faillock.so + authfail editing PAM files + ansible.builtin.lineinfile: + path: '{{ item }}' + line: auth required pam_faillock.so authfail + insertbefore: ^auth.*required.*pam_deny\.so.* + state: present + loop: + - /etc/pam.d/system-auth + - /etc/pam.d/password-auth + when: + - result_pam_faillock_is_enabled.found == 0 + + - name: Set Interval For Counting Failed Password Attempts - Enable pam_faillock.so + account section editing PAM files + ansible.builtin.lineinfile: + path: '{{ item }}' + line: account required pam_faillock.so + insertbefore: ^account.*required.*pam_unix\.so.* + state: present + loop: + - /etc/pam.d/system-auth + - /etc/pam.d/password-auth + when: + - result_pam_faillock_is_enabled.found == 0 + when: + - '"pam" in ansible_facts.packages' + - not result_authselect_present.stat.exists + tags: + - CCE-80669-5 + - DISA-STIG-RHEL-08-020012 + - NIST-800-53-AC-7(a) + - NIST-800-53-CM-6(a) + - accounts_passwords_pam_faillock_interval + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy +- name: XCCDF Value var_accounts_passwords_pam_faillock_fail_interval # promote to variable + set_fact: + var_accounts_passwords_pam_faillock_fail_interval: !!str + tags: + - always + +- name: Set Interval For Counting Failed Password Attempts - Check the presence of + /etc/security/faillock.conf file + ansible.builtin.stat: + path: /etc/security/faillock.conf + register: result_faillock_conf_check + when: '"pam" in ansible_facts.packages' + tags: + - CCE-80669-5 + - DISA-STIG-RHEL-08-020012 + - NIST-800-53-AC-7(a) + - NIST-800-53-CM-6(a) + - accounts_passwords_pam_faillock_interval + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Set Interval For Counting Failed Password Attempts - Ensure the pam_faillock.so + fail_interval parameter in /etc/security/faillock.conf + ansible.builtin.lineinfile: + path: /etc/security/faillock.conf + regexp: ^\s*fail_interval\s*= + line: fail_interval = {{ var_accounts_passwords_pam_faillock_fail_interval }} + state: present + when: + - '"pam" in ansible_facts.packages' + - result_faillock_conf_check.stat.exists + tags: + - CCE-80669-5 + - DISA-STIG-RHEL-08-020012 + - NIST-800-53-AC-7(a) + - NIST-800-53-CM-6(a) + - accounts_passwords_pam_faillock_interval + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Set Interval For Counting Failed Password Attempts - Ensure the pam_faillock.so + fail_interval parameter not in PAM files + block: + + - name: Set Interval For Counting Failed Password Attempts - Check if /etc/pam.d/system-auth + file is present + ansible.builtin.stat: + path: /etc/pam.d/system-auth + register: result_pam_file_present + + - name: Set Interval For Counting Failed Password Attempts - Check the proper remediation + for the system + block: + + - name: Set Interval For Counting Failed Password Attempts - Define the PAM file + to be edited as a local fact + ansible.builtin.set_fact: + pam_file_path: /etc/pam.d/system-auth + + - name: Set Interval For Counting Failed Password Attempts - Check if system relies + on authselect + ansible.builtin.stat: + path: /usr/bin/authselect + register: result_authselect_present + + - name: Set Interval For Counting Failed Password Attempts - Remediate using authselect + block: + + - name: Set Interval For Counting Failed Password Attempts - Check integrity + of authselect current profile + ansible.builtin.command: + cmd: authselect check + register: result_authselect_check_cmd + changed_when: false + ignore_errors: true + + - name: Set Interval For Counting Failed Password Attempts - Informative message + based on the authselect integrity check result + ansible.builtin.assert: + that: + - result_authselect_check_cmd is success + fail_msg: + - authselect integrity check failed. Remediation aborted! + - This remediation could not be applied because an authselect profile was + not selected or the selected profile is not intact. + - It is not recommended to manually edit the PAM files when authselect tool + is available. + - In cases where the default authselect profile does not cover a specific + demand, a custom authselect profile is recommended. + success_msg: + - authselect integrity check passed + + - name: Set Interval For Counting Failed Password Attempts - Get authselect + current profile + ansible.builtin.shell: + cmd: authselect current -r | awk '{ print $1 }' + register: result_authselect_profile + changed_when: false + when: + - result_authselect_check_cmd is success + + - name: Set Interval For Counting Failed Password Attempts - Define the current + authselect profile as a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: '{{ result_authselect_profile.stdout }}' + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is match("custom/") + + - name: Set Interval For Counting Failed Password Attempts - Define the new + authselect custom profile as a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: custom/hardening + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is not match("custom/") + + - name: Set Interval For Counting Failed Password Attempts - Get authselect + current features to also enable them in the custom profile + ansible.builtin.shell: + cmd: authselect current | tail -n+3 | awk '{ print $2 }' + register: result_authselect_features + changed_when: false + when: + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + + - name: Set Interval For Counting Failed Password Attempts - Check if any custom + profile with the same name was already created + ansible.builtin.stat: + path: /etc/authselect/{{ authselect_custom_profile }} + register: result_authselect_custom_profile_present + changed_when: false + when: + - authselect_current_profile is not match("custom/") + + - name: Set Interval For Counting Failed Password Attempts - Create an authselect + custom profile based on the current profile + ansible.builtin.command: + cmd: authselect create-profile hardening -b {{ authselect_current_profile + }} + when: + - result_authselect_check_cmd is success + - authselect_current_profile is not match("custom/") + - not result_authselect_custom_profile_present.stat.exists + + - name: Set Interval For Counting Failed Password Attempts - Ensure authselect + changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=before-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Set Interval For Counting Failed Password Attempts - Ensure the authselect + custom profile is selected + ansible.builtin.command: + cmd: authselect select {{ authselect_custom_profile }} + register: result_pam_authselect_select_profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Set Interval For Counting Failed Password Attempts - Restore the authselect + features in the custom profile + ansible.builtin.command: + cmd: authselect enable-feature {{ item }} + loop: '{{ result_authselect_features.stdout_lines }}' + register: result_pam_authselect_restore_features + when: + - result_authselect_profile is not skipped + - result_authselect_features is not skipped + - result_pam_authselect_select_profile is not skipped + + - name: Set Interval For Counting Failed Password Attempts - Ensure authselect + changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=after-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - result_pam_authselect_restore_features is not skipped + + - name: Set Interval For Counting Failed Password Attempts - Change the PAM + file to be edited according to the custom authselect profile + ansible.builtin.set_fact: + pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path + | basename }} + when: + - result_authselect_present.stat.exists + + - name: Set Interval For Counting Failed Password Attempts - Ensure the "fail_interval" + option from "pam_faillock.so" is not present in {{ pam_file_path }} + ansible.builtin.replace: + dest: '{{ pam_file_path }}' + regexp: (.*auth.*pam_faillock.so.*)\bfail_interval\b=?[0-9a-zA-Z]*(.*) + replace: \1\2 + register: result_pam_option_removal + + - name: Set Interval For Counting Failed Password Attempts - Ensure authselect + changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: + - result_authselect_present.stat.exists + - result_pam_option_removal is changed + when: + - result_pam_file_present.stat.exists + + - name: Set Interval For Counting Failed Password Attempts - Check if /etc/pam.d/password-auth + file is present + ansible.builtin.stat: + path: /etc/pam.d/password-auth + register: result_pam_file_present + + - name: Set Interval For Counting Failed Password Attempts - Check the proper remediation + for the system + block: + + - name: Set Interval For Counting Failed Password Attempts - Define the PAM file + to be edited as a local fact + ansible.builtin.set_fact: + pam_file_path: /etc/pam.d/password-auth + + - name: Set Interval For Counting Failed Password Attempts - Check if system relies + on authselect + ansible.builtin.stat: + path: /usr/bin/authselect + register: result_authselect_present + + - name: Set Interval For Counting Failed Password Attempts - Remediate using authselect + block: + + - name: Set Interval For Counting Failed Password Attempts - Check integrity + of authselect current profile + ansible.builtin.command: + cmd: authselect check + register: result_authselect_check_cmd + changed_when: false + ignore_errors: true + + - name: Set Interval For Counting Failed Password Attempts - Informative message + based on the authselect integrity check result + ansible.builtin.assert: + that: + - result_authselect_check_cmd is success + fail_msg: + - authselect integrity check failed. Remediation aborted! + - This remediation could not be applied because an authselect profile was + not selected or the selected profile is not intact. + - It is not recommended to manually edit the PAM files when authselect tool + is available. + - In cases where the default authselect profile does not cover a specific + demand, a custom authselect profile is recommended. + success_msg: + - authselect integrity check passed + + - name: Set Interval For Counting Failed Password Attempts - Get authselect + current profile + ansible.builtin.shell: + cmd: authselect current -r | awk '{ print $1 }' + register: result_authselect_profile + changed_when: false + when: + - result_authselect_check_cmd is success + + - name: Set Interval For Counting Failed Password Attempts - Define the current + authselect profile as a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: '{{ result_authselect_profile.stdout }}' + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is match("custom/") + + - name: Set Interval For Counting Failed Password Attempts - Define the new + authselect custom profile as a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: custom/hardening + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is not match("custom/") + + - name: Set Interval For Counting Failed Password Attempts - Get authselect + current features to also enable them in the custom profile + ansible.builtin.shell: + cmd: authselect current | tail -n+3 | awk '{ print $2 }' + register: result_authselect_features + changed_when: false + when: + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + + - name: Set Interval For Counting Failed Password Attempts - Check if any custom + profile with the same name was already created + ansible.builtin.stat: + path: /etc/authselect/{{ authselect_custom_profile }} + register: result_authselect_custom_profile_present + changed_when: false + when: + - authselect_current_profile is not match("custom/") + + - name: Set Interval For Counting Failed Password Attempts - Create an authselect + custom profile based on the current profile + ansible.builtin.command: + cmd: authselect create-profile hardening -b {{ authselect_current_profile + }} + when: + - result_authselect_check_cmd is success + - authselect_current_profile is not match("custom/") + - not result_authselect_custom_profile_present.stat.exists + + - name: Set Interval For Counting Failed Password Attempts - Ensure authselect + changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=before-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Set Interval For Counting Failed Password Attempts - Ensure the authselect + custom profile is selected + ansible.builtin.command: + cmd: authselect select {{ authselect_custom_profile }} + register: result_pam_authselect_select_profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Set Interval For Counting Failed Password Attempts - Restore the authselect + features in the custom profile + ansible.builtin.command: + cmd: authselect enable-feature {{ item }} + loop: '{{ result_authselect_features.stdout_lines }}' + register: result_pam_authselect_restore_features + when: + - result_authselect_profile is not skipped + - result_authselect_features is not skipped + - result_pam_authselect_select_profile is not skipped + + - name: Set Interval For Counting Failed Password Attempts - Ensure authselect + changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=after-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - result_pam_authselect_restore_features is not skipped + + - name: Set Interval For Counting Failed Password Attempts - Change the PAM + file to be edited according to the custom authselect profile + ansible.builtin.set_fact: + pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path + | basename }} + when: + - result_authselect_present.stat.exists + + - name: Set Interval For Counting Failed Password Attempts - Ensure the "fail_interval" + option from "pam_faillock.so" is not present in {{ pam_file_path }} + ansible.builtin.replace: + dest: '{{ pam_file_path }}' + regexp: (.*auth.*pam_faillock.so.*)\bfail_interval\b=?[0-9a-zA-Z]*(.*) + replace: \1\2 + register: result_pam_option_removal + + - name: Set Interval For Counting Failed Password Attempts - Ensure authselect + changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: + - result_authselect_present.stat.exists + - result_pam_option_removal is changed + when: + - result_pam_file_present.stat.exists + when: + - '"pam" in ansible_facts.packages' + - result_faillock_conf_check.stat.exists + tags: + - CCE-80669-5 + - DISA-STIG-RHEL-08-020012 + - NIST-800-53-AC-7(a) + - NIST-800-53-CM-6(a) + - accounts_passwords_pam_faillock_interval + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Set Interval For Counting Failed Password Attempts - Ensure the pam_faillock.so + fail_interval parameter in PAM files + block: + + - name: Set Interval For Counting Failed Password Attempts - Check if pam_faillock.so + fail_interval parameter is already enabled in pam files + ansible.builtin.lineinfile: + path: /etc/pam.d/system-auth + regexp: .*auth.*pam_faillock\.so (preauth|authfail).*fail_interval + state: absent + check_mode: true + changed_when: false + register: result_pam_faillock_fail_interval_parameter_is_present + + - name: Set Interval For Counting Failed Password Attempts - Ensure the inclusion + of pam_faillock.so preauth fail_interval parameter in auth section + ansible.builtin.lineinfile: + path: '{{ item }}' + backrefs: true + regexp: (^\s*auth\s+)([\w\[].*\b)(\s+pam_faillock.so preauth.*) + line: \1required\3 fail_interval={{ var_accounts_passwords_pam_faillock_fail_interval + }} + state: present + loop: + - /etc/pam.d/system-auth + - /etc/pam.d/password-auth + when: + - result_pam_faillock_fail_interval_parameter_is_present.found == 0 + + - name: Set Interval For Counting Failed Password Attempts - Ensure the inclusion + of pam_faillock.so authfail fail_interval parameter in auth section + ansible.builtin.lineinfile: + path: '{{ item }}' + backrefs: true + regexp: (^\s*auth\s+)([\w\[].*\b)(\s+pam_faillock.so authfail.*) + line: \1required\3 fail_interval={{ var_accounts_passwords_pam_faillock_fail_interval + }} + state: present + loop: + - /etc/pam.d/system-auth + - /etc/pam.d/password-auth + when: + - result_pam_faillock_fail_interval_parameter_is_present.found == 0 + + - name: Set Interval For Counting Failed Password Attempts - Ensure the desired + value for pam_faillock.so preauth fail_interval parameter in auth section + ansible.builtin.lineinfile: + path: '{{ item }}' + backrefs: true + regexp: (^\s*auth\s+)([\w\[].*\b)(\s+pam_faillock.so preauth.*)(fail_interval)=[0-9]+(.*) + line: \1required\3\4={{ var_accounts_passwords_pam_faillock_fail_interval }}\5 + state: present + loop: + - /etc/pam.d/system-auth + - /etc/pam.d/password-auth + when: + - result_pam_faillock_fail_interval_parameter_is_present.found > 0 + + - name: Set Interval For Counting Failed Password Attempts - Ensure the desired + value for pam_faillock.so authfail fail_interval parameter in auth section + ansible.builtin.lineinfile: + path: '{{ item }}' + backrefs: true + regexp: (^\s*auth\s+)([\w\[].*\b)(\s+pam_faillock.so authfail.*)(fail_interval)=[0-9]+(.*) + line: \1required\3\4={{ var_accounts_passwords_pam_faillock_fail_interval }}\5 + state: present + loop: + - /etc/pam.d/system-auth + - /etc/pam.d/password-auth + when: + - result_pam_faillock_fail_interval_parameter_is_present.found > 0 + when: + - '"pam" in ansible_facts.packages' + - not result_faillock_conf_check.stat.exists + tags: + - CCE-80669-5 + - DISA-STIG-RHEL-08-020012 + - NIST-800-53-AC-7(a) + - NIST-800-53-CM-6(a) + - accounts_passwords_pam_faillock_interval + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q pam; then + +var_accounts_passwords_pam_faillock_fail_interval='' + + +if [ -f /usr/bin/authselect ]; then + if ! authselect check; then +echo " +authselect integrity check failed. Remediation aborted! +This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact. +It is not recommended to manually edit the PAM files when authselect tool is available. +In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended." +exit 1 +fi +authselect enable-feature with-faillock + +authselect apply-changes -b +else + AUTH_FILES=("/etc/pam.d/system-auth" "/etc/pam.d/password-auth") +for pam_file in "${AUTH_FILES[@]}" +do + if ! grep -qE '^\s*auth\s+required\s+pam_faillock\.so\s+(preauth silent|authfail).*$' "$pam_file" ; then + sed -i --follow-symlinks '/^auth.*sufficient.*pam_unix\.so.*/i auth required pam_faillock.so preauth silent' "$pam_file" + sed -i --follow-symlinks '/^auth.*required.*pam_deny\.so.*/i auth required pam_faillock.so authfail' "$pam_file" + sed -i --follow-symlinks '/^account.*required.*pam_unix\.so.*/i account required pam_faillock.so' "$pam_file" + fi + sed -Ei 's/(auth.*)(\[default=die\])(.*pam_faillock\.so)/\1required \3/g' "$pam_file" +done +fi +AUTH_FILES=("/etc/pam.d/system-auth" "/etc/pam.d/password-auth") +FAILLOCK_CONF="/etc/security/faillock.conf" +if [ -f $FAILLOCK_CONF ]; then + regex="^\s*fail_interval\s*=" + line="fail_interval = $var_accounts_passwords_pam_faillock_fail_interval" + if ! grep -q $regex $FAILLOCK_CONF; then + echo $line >> $FAILLOCK_CONF + else + sed -i --follow-symlinks 's|^\s*\(fail_interval\s*=\s*\)\(\S\+\)|\1'"$var_accounts_passwords_pam_faillock_fail_interval"'|g' $FAILLOCK_CONF + fi + for pam_file in "${AUTH_FILES[@]}" + do + if [ -e "$pam_file" ] ; then + PAM_FILE_PATH="$pam_file" + if [ -f /usr/bin/authselect ]; then + if ! authselect check; then + echo " + authselect integrity check failed. Remediation aborted! + This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact. + It is not recommended to manually edit the PAM files when authselect tool is available. + In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended." + exit 1 + fi + CURRENT_PROFILE=$(authselect current -r | awk '{ print $1 }') + # If not already in use, a custom profile is created preserving the enabled features. + if [[ ! $CURRENT_PROFILE == custom/* ]]; then + ENABLED_FEATURES=$(authselect current | tail -n+3 | awk '{ print $2 }') + authselect create-profile hardening -b $CURRENT_PROFILE + CURRENT_PROFILE="custom/hardening" + + authselect apply-changes -b --backup=before-hardening-custom-profile + authselect select $CURRENT_PROFILE + for feature in $ENABLED_FEATURES; do + authselect enable-feature $feature; + done + + authselect apply-changes -b --backup=after-hardening-custom-profile + fi + PAM_FILE_NAME=$(basename "$pam_file") + PAM_FILE_PATH="/etc/authselect/$CURRENT_PROFILE/$PAM_FILE_NAME" + + authselect apply-changes -b + fi + + if grep -qP '^\s*auth\s.*\bpam_faillock.so\s.*\bfail_interval\b' "$PAM_FILE_PATH"; then + sed -i -E --follow-symlinks 's/(.*auth.*pam_faillock.so.*)\bfail_interval\b=?[[:alnum:]]*(.*)/\1\2/g' "$PAM_FILE_PATH" + fi + if [ -f /usr/bin/authselect ]; then + + authselect apply-changes -b + fi + else + echo "$pam_file was not found" >&2 + fi + done +else + for pam_file in "${AUTH_FILES[@]}" + do + if ! grep -qE '^\s*auth.*pam_faillock\.so (preauth|authfail).*fail_interval' "$pam_file"; then + sed -i --follow-symlinks '/^auth.*required.*pam_faillock\.so.*preauth.*silent.*/ s/$/ fail_interval='"$var_accounts_passwords_pam_faillock_fail_interval"'/' "$pam_file" + sed -i --follow-symlinks '/^auth.*required.*pam_faillock\.so.*authfail.*/ s/$/ fail_interval='"$var_accounts_passwords_pam_faillock_fail_interval"'/' "$pam_file" + else + sed -i --follow-symlinks 's/\(^auth.*required.*pam_faillock\.so.*preauth.*silent.*\)\('"fail_interval"'=\)[0-9]\+\(.*\)/\1\2'"$var_accounts_passwords_pam_faillock_fail_interval"'\3/' "$pam_file" + sed -i --follow-symlinks 's/\(^auth.*required.*pam_faillock\.so.*authfail.*\)\('"fail_interval"'=\)[0-9]\+\(.*\)/\1\2'"$var_accounts_passwords_pam_faillock_fail_interval"'\3/' "$pam_file" + fi + done +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Set Lockout Time for Failed Password Attempts + This rule configures the system to lock out accounts during a specified time period after a +number of incorrect login attempts using pam_faillock.so. + +pam_faillock.so module requires multiple entries in pam files. These entries must be carefully +defined to work as expected. In order to avoid any errors when manually editing these files, +it is recommended to use the appropriate tools, such as authselect or authconfig, +depending on the OS version. + +If unlock_time is set to 0, manual intervention by an administrator is required +to unlock a user. This should be done using the faillock tool. + If the system supports the new /etc/security/faillock.conf file but the +pam_faillock.so parameters are defined directly in /etc/pam.d/system-auth and +/etc/pam.d/password-auth, the remediation will migrate the unlock_time parameter +to /etc/security/faillock.conf to ensure compatibility with authselect tool. +The parameters deny and fail_interval, if used, also have to be migrated +by their respective remediation. + If the system relies on authselect tool to manage PAM settings, the remediation +will also use authselect tool. However, if any manual modification was made in +PAM files, the authselect integrity check will fail and the remediation will be +aborted in order to preserve intentional changes. In this case, an informative message will +be shown in the remediation report. +If the system supports the /etc/security/faillock.conf file, the pam_faillock +parameters should be defined in faillock.conf file. + BP28(R18) + 1 + 12 + 15 + 16 + 5.5.3 + DSS05.04 + DSS05.10 + DSS06.10 + 3.1.8 + CCI-000044 + CCI-002236 + CCI-002237 + CCI-002238 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + 0421 + 0422 + 0431 + 0974 + 1173 + 1401 + 1504 + 1505 + 1546 + 1557 + 1558 + 1559 + 1560 + 1561 + A.18.1.4 + A.9.2.1 + A.9.2.4 + A.9.3.1 + A.9.4.2 + A.9.4.3 + CM-6(a) + AC-7(b) + PR.AC-7 + FIA_AFL.1 + Req-8.1.7 + SRG-OS-000329-GPOS-00128 + SRG-OS-000021-GPOS-00005 + SRG-OS-000329-VMM-001180 + RHEL-08-020016 + 5.4.2 + SV-230338r627750_rule + By limiting the number of failed logon attempts the risk of unauthorized system +access via user password guessing, otherwise known as brute-forcing, is reduced. +Limits are imposed by locking the account. + + CCE-80670-3 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80670-3 + - CJIS-5.5.3 + - DISA-STIG-RHEL-08-020016 + - NIST-800-171-3.1.8 + - NIST-800-53-AC-7(b) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.1.7 + - accounts_passwords_pam_faillock_unlock_time + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Set Lockout Time for Failed Password Attempts - Check if system relies on + authselect tool + ansible.builtin.stat: + path: /usr/bin/authselect + register: result_authselect_present + when: '"pam" in ansible_facts.packages' + tags: + - CCE-80670-3 + - CJIS-5.5.3 + - DISA-STIG-RHEL-08-020016 + - NIST-800-171-3.1.8 + - NIST-800-53-AC-7(b) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.1.7 + - accounts_passwords_pam_faillock_unlock_time + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Set Lockout Time for Failed Password Attempts - Remediation where authselect + tool is present + block: + + - name: Set Lockout Time for Failed Password Attempts - Check integrity of authselect + current profile + ansible.builtin.command: + cmd: authselect check + register: result_authselect_check_cmd + changed_when: false + ignore_errors: true + + - name: Set Lockout Time for Failed Password Attempts - Informative message based + on the authselect integrity check result + ansible.builtin.assert: + that: + - result_authselect_check_cmd is success + fail_msg: + - authselect integrity check failed. Remediation aborted! + - This remediation could not be applied because an authselect profile was not + selected or the selected profile is not intact. + - It is not recommended to manually edit the PAM files when authselect tool + is available. + - In cases where the default authselect profile does not cover a specific demand, + a custom authselect profile is recommended. + success_msg: + - authselect integrity check passed + + - name: Set Lockout Time for Failed Password Attempts - Get authselect current features + ansible.builtin.shell: + cmd: authselect current | tail -n+3 | awk '{ print $2 }' + register: result_authselect_features + changed_when: false + when: + - result_authselect_check_cmd is success + + - name: Set Lockout Time for Failed Password Attempts - Ensure "with-faillock" feature + is enabled using authselect tool + ansible.builtin.command: + cmd: authselect enable-feature with-faillock + register: result_authselect_enable_feature_cmd + when: + - result_authselect_check_cmd is success + - result_authselect_features.stdout is not search("with-faillock") + + - name: Set Lockout Time for Failed Password Attempts - Ensure authselect changes + are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: + - result_authselect_enable_feature_cmd is not skipped + - result_authselect_enable_feature_cmd is success + when: + - '"pam" in ansible_facts.packages' + - result_authselect_present.stat.exists + tags: + - CCE-80670-3 + - CJIS-5.5.3 + - DISA-STIG-RHEL-08-020016 + - NIST-800-171-3.1.8 + - NIST-800-53-AC-7(b) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.1.7 + - accounts_passwords_pam_faillock_unlock_time + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Set Lockout Time for Failed Password Attempts - Remediation where authselect + tool is not present + block: + + - name: Set Lockout Time for Failed Password Attempts - Check if pam_faillock.so + is already enabled + ansible.builtin.lineinfile: + path: /etc/pam.d/system-auth + regexp: .*auth.*pam_faillock\.so (preauth|authfail) + state: absent + check_mode: true + changed_when: false + register: result_pam_faillock_is_enabled + + - name: Set Lockout Time for Failed Password Attempts - Enable pam_faillock.so preauth + editing PAM files + ansible.builtin.lineinfile: + path: '{{ item }}' + line: auth required pam_faillock.so preauth + insertbefore: ^auth.*sufficient.*pam_unix\.so.* + state: present + loop: + - /etc/pam.d/system-auth + - /etc/pam.d/password-auth + when: + - result_pam_faillock_is_enabled.found == 0 + + - name: Set Lockout Time for Failed Password Attempts - Enable pam_faillock.so authfail + editing PAM files + ansible.builtin.lineinfile: + path: '{{ item }}' + line: auth required pam_faillock.so authfail + insertbefore: ^auth.*required.*pam_deny\.so.* + state: present + loop: + - /etc/pam.d/system-auth + - /etc/pam.d/password-auth + when: + - result_pam_faillock_is_enabled.found == 0 + + - name: Set Lockout Time for Failed Password Attempts - Enable pam_faillock.so account + section editing PAM files + ansible.builtin.lineinfile: + path: '{{ item }}' + line: account required pam_faillock.so + insertbefore: ^account.*required.*pam_unix\.so.* + state: present + loop: + - /etc/pam.d/system-auth + - /etc/pam.d/password-auth + when: + - result_pam_faillock_is_enabled.found == 0 + when: + - '"pam" in ansible_facts.packages' + - not result_authselect_present.stat.exists + tags: + - CCE-80670-3 + - CJIS-5.5.3 + - DISA-STIG-RHEL-08-020016 + - NIST-800-171-3.1.8 + - NIST-800-53-AC-7(b) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.1.7 + - accounts_passwords_pam_faillock_unlock_time + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy +- name: XCCDF Value var_accounts_passwords_pam_faillock_unlock_time # promote to variable + set_fact: + var_accounts_passwords_pam_faillock_unlock_time: !!str + tags: + - always + +- name: Set Lockout Time for Failed Password Attempts - Check the presence of /etc/security/faillock.conf + file + ansible.builtin.stat: + path: /etc/security/faillock.conf + register: result_faillock_conf_check + when: '"pam" in ansible_facts.packages' + tags: + - CCE-80670-3 + - CJIS-5.5.3 + - DISA-STIG-RHEL-08-020016 + - NIST-800-171-3.1.8 + - NIST-800-53-AC-7(b) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.1.7 + - accounts_passwords_pam_faillock_unlock_time + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Set Lockout Time for Failed Password Attempts - Ensure the pam_faillock.so + unlock_time parameter in /etc/security/faillock.conf + ansible.builtin.lineinfile: + path: /etc/security/faillock.conf + regexp: ^\s*unlock_time\s*= + line: unlock_time = {{ var_accounts_passwords_pam_faillock_unlock_time }} + state: present + when: + - '"pam" in ansible_facts.packages' + - result_faillock_conf_check.stat.exists + tags: + - CCE-80670-3 + - CJIS-5.5.3 + - DISA-STIG-RHEL-08-020016 + - NIST-800-171-3.1.8 + - NIST-800-53-AC-7(b) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.1.7 + - accounts_passwords_pam_faillock_unlock_time + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Set Lockout Time for Failed Password Attempts - Ensure the pam_faillock.so + unlock_time parameter not in PAM files + block: + + - name: Set Lockout Time for Failed Password Attempts - Check if /etc/pam.d/system-auth + file is present + ansible.builtin.stat: + path: /etc/pam.d/system-auth + register: result_pam_file_present + + - name: Set Lockout Time for Failed Password Attempts - Check the proper remediation + for the system + block: + + - name: Set Lockout Time for Failed Password Attempts - Define the PAM file to + be edited as a local fact + ansible.builtin.set_fact: + pam_file_path: /etc/pam.d/system-auth + + - name: Set Lockout Time for Failed Password Attempts - Check if system relies + on authselect + ansible.builtin.stat: + path: /usr/bin/authselect + register: result_authselect_present + + - name: Set Lockout Time for Failed Password Attempts - Remediate using authselect + block: + + - name: Set Lockout Time for Failed Password Attempts - Check integrity of authselect + current profile + ansible.builtin.command: + cmd: authselect check + register: result_authselect_check_cmd + changed_when: false + ignore_errors: true + + - name: Set Lockout Time for Failed Password Attempts - Informative message + based on the authselect integrity check result + ansible.builtin.assert: + that: + - result_authselect_check_cmd is success + fail_msg: + - authselect integrity check failed. Remediation aborted! + - This remediation could not be applied because an authselect profile was + not selected or the selected profile is not intact. + - It is not recommended to manually edit the PAM files when authselect tool + is available. + - In cases where the default authselect profile does not cover a specific + demand, a custom authselect profile is recommended. + success_msg: + - authselect integrity check passed + + - name: Set Lockout Time for Failed Password Attempts - Get authselect current + profile + ansible.builtin.shell: + cmd: authselect current -r | awk '{ print $1 }' + register: result_authselect_profile + changed_when: false + when: + - result_authselect_check_cmd is success + + - name: Set Lockout Time for Failed Password Attempts - Define the current authselect + profile as a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: '{{ result_authselect_profile.stdout }}' + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is match("custom/") + + - name: Set Lockout Time for Failed Password Attempts - Define the new authselect + custom profile as a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: custom/hardening + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is not match("custom/") + + - name: Set Lockout Time for Failed Password Attempts - Get authselect current + features to also enable them in the custom profile + ansible.builtin.shell: + cmd: authselect current | tail -n+3 | awk '{ print $2 }' + register: result_authselect_features + changed_when: false + when: + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + + - name: Set Lockout Time for Failed Password Attempts - Check if any custom + profile with the same name was already created + ansible.builtin.stat: + path: /etc/authselect/{{ authselect_custom_profile }} + register: result_authselect_custom_profile_present + changed_when: false + when: + - authselect_current_profile is not match("custom/") + + - name: Set Lockout Time for Failed Password Attempts - Create an authselect + custom profile based on the current profile + ansible.builtin.command: + cmd: authselect create-profile hardening -b {{ authselect_current_profile + }} + when: + - result_authselect_check_cmd is success + - authselect_current_profile is not match("custom/") + - not result_authselect_custom_profile_present.stat.exists + + - name: Set Lockout Time for Failed Password Attempts - Ensure authselect changes + are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=before-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Set Lockout Time for Failed Password Attempts - Ensure the authselect + custom profile is selected + ansible.builtin.command: + cmd: authselect select {{ authselect_custom_profile }} + register: result_pam_authselect_select_profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Set Lockout Time for Failed Password Attempts - Restore the authselect + features in the custom profile + ansible.builtin.command: + cmd: authselect enable-feature {{ item }} + loop: '{{ result_authselect_features.stdout_lines }}' + register: result_pam_authselect_restore_features + when: + - result_authselect_profile is not skipped + - result_authselect_features is not skipped + - result_pam_authselect_select_profile is not skipped + + - name: Set Lockout Time for Failed Password Attempts - Ensure authselect changes + are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=after-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - result_pam_authselect_restore_features is not skipped + + - name: Set Lockout Time for Failed Password Attempts - Change the PAM file + to be edited according to the custom authselect profile + ansible.builtin.set_fact: + pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path + | basename }} + when: + - result_authselect_present.stat.exists + + - name: Set Lockout Time for Failed Password Attempts - Ensure the "unlock_time" + option from "pam_faillock.so" is not present in {{ pam_file_path }} + ansible.builtin.replace: + dest: '{{ pam_file_path }}' + regexp: (.*auth.*pam_faillock.so.*)\bunlock_time\b=?[0-9a-zA-Z]*(.*) + replace: \1\2 + register: result_pam_option_removal + + - name: Set Lockout Time for Failed Password Attempts - Ensure authselect changes + are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: + - result_authselect_present.stat.exists + - result_pam_option_removal is changed + when: + - result_pam_file_present.stat.exists + + - name: Set Lockout Time for Failed Password Attempts - Check if /etc/pam.d/password-auth + file is present + ansible.builtin.stat: + path: /etc/pam.d/password-auth + register: result_pam_file_present + + - name: Set Lockout Time for Failed Password Attempts - Check the proper remediation + for the system + block: + + - name: Set Lockout Time for Failed Password Attempts - Define the PAM file to + be edited as a local fact + ansible.builtin.set_fact: + pam_file_path: /etc/pam.d/password-auth + + - name: Set Lockout Time for Failed Password Attempts - Check if system relies + on authselect + ansible.builtin.stat: + path: /usr/bin/authselect + register: result_authselect_present + + - name: Set Lockout Time for Failed Password Attempts - Remediate using authselect + block: + + - name: Set Lockout Time for Failed Password Attempts - Check integrity of authselect + current profile + ansible.builtin.command: + cmd: authselect check + register: result_authselect_check_cmd + changed_when: false + ignore_errors: true + + - name: Set Lockout Time for Failed Password Attempts - Informative message + based on the authselect integrity check result + ansible.builtin.assert: + that: + - result_authselect_check_cmd is success + fail_msg: + - authselect integrity check failed. Remediation aborted! + - This remediation could not be applied because an authselect profile was + not selected or the selected profile is not intact. + - It is not recommended to manually edit the PAM files when authselect tool + is available. + - In cases where the default authselect profile does not cover a specific + demand, a custom authselect profile is recommended. + success_msg: + - authselect integrity check passed + + - name: Set Lockout Time for Failed Password Attempts - Get authselect current + profile + ansible.builtin.shell: + cmd: authselect current -r | awk '{ print $1 }' + register: result_authselect_profile + changed_when: false + when: + - result_authselect_check_cmd is success + + - name: Set Lockout Time for Failed Password Attempts - Define the current authselect + profile as a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: '{{ result_authselect_profile.stdout }}' + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is match("custom/") + + - name: Set Lockout Time for Failed Password Attempts - Define the new authselect + custom profile as a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: custom/hardening + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is not match("custom/") + + - name: Set Lockout Time for Failed Password Attempts - Get authselect current + features to also enable them in the custom profile + ansible.builtin.shell: + cmd: authselect current | tail -n+3 | awk '{ print $2 }' + register: result_authselect_features + changed_when: false + when: + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + + - name: Set Lockout Time for Failed Password Attempts - Check if any custom + profile with the same name was already created + ansible.builtin.stat: + path: /etc/authselect/{{ authselect_custom_profile }} + register: result_authselect_custom_profile_present + changed_when: false + when: + - authselect_current_profile is not match("custom/") + + - name: Set Lockout Time for Failed Password Attempts - Create an authselect + custom profile based on the current profile + ansible.builtin.command: + cmd: authselect create-profile hardening -b {{ authselect_current_profile + }} + when: + - result_authselect_check_cmd is success + - authselect_current_profile is not match("custom/") + - not result_authselect_custom_profile_present.stat.exists + + - name: Set Lockout Time for Failed Password Attempts - Ensure authselect changes + are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=before-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Set Lockout Time for Failed Password Attempts - Ensure the authselect + custom profile is selected + ansible.builtin.command: + cmd: authselect select {{ authselect_custom_profile }} + register: result_pam_authselect_select_profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Set Lockout Time for Failed Password Attempts - Restore the authselect + features in the custom profile + ansible.builtin.command: + cmd: authselect enable-feature {{ item }} + loop: '{{ result_authselect_features.stdout_lines }}' + register: result_pam_authselect_restore_features + when: + - result_authselect_profile is not skipped + - result_authselect_features is not skipped + - result_pam_authselect_select_profile is not skipped + + - name: Set Lockout Time for Failed Password Attempts - Ensure authselect changes + are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=after-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - result_pam_authselect_restore_features is not skipped + + - name: Set Lockout Time for Failed Password Attempts - Change the PAM file + to be edited according to the custom authselect profile + ansible.builtin.set_fact: + pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path + | basename }} + when: + - result_authselect_present.stat.exists + + - name: Set Lockout Time for Failed Password Attempts - Ensure the "unlock_time" + option from "pam_faillock.so" is not present in {{ pam_file_path }} + ansible.builtin.replace: + dest: '{{ pam_file_path }}' + regexp: (.*auth.*pam_faillock.so.*)\bunlock_time\b=?[0-9a-zA-Z]*(.*) + replace: \1\2 + register: result_pam_option_removal + + - name: Set Lockout Time for Failed Password Attempts - Ensure authselect changes + are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: + - result_authselect_present.stat.exists + - result_pam_option_removal is changed + when: + - result_pam_file_present.stat.exists + when: + - '"pam" in ansible_facts.packages' + - result_faillock_conf_check.stat.exists + tags: + - CCE-80670-3 + - CJIS-5.5.3 + - DISA-STIG-RHEL-08-020016 + - NIST-800-171-3.1.8 + - NIST-800-53-AC-7(b) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.1.7 + - accounts_passwords_pam_faillock_unlock_time + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Set Lockout Time for Failed Password Attempts - Ensure the pam_faillock.so + unlock_time parameter in PAM files + block: + + - name: Set Lockout Time for Failed Password Attempts - Check if pam_faillock.so + unlock_time parameter is already enabled in pam files + ansible.builtin.lineinfile: + path: /etc/pam.d/system-auth + regexp: .*auth.*pam_faillock\.so (preauth|authfail).*unlock_time + state: absent + check_mode: true + changed_when: false + register: result_pam_faillock_unlock_time_parameter_is_present + + - name: Set Lockout Time for Failed Password Attempts - Ensure the inclusion of + pam_faillock.so preauth unlock_time parameter in auth section + ansible.builtin.lineinfile: + path: '{{ item }}' + backrefs: true + regexp: (^\s*auth\s+)([\w\[].*\b)(\s+pam_faillock.so preauth.*) + line: \1required\3 unlock_time={{ var_accounts_passwords_pam_faillock_unlock_time + }} + state: present + loop: + - /etc/pam.d/system-auth + - /etc/pam.d/password-auth + when: + - result_pam_faillock_unlock_time_parameter_is_present.found == 0 + + - name: Set Lockout Time for Failed Password Attempts - Ensure the inclusion of + pam_faillock.so authfail unlock_time parameter in auth section + ansible.builtin.lineinfile: + path: '{{ item }}' + backrefs: true + regexp: (^\s*auth\s+)([\w\[].*\b)(\s+pam_faillock.so authfail.*) + line: \1required\3 unlock_time={{ var_accounts_passwords_pam_faillock_unlock_time + }} + state: present + loop: + - /etc/pam.d/system-auth + - /etc/pam.d/password-auth + when: + - result_pam_faillock_unlock_time_parameter_is_present.found == 0 + + - name: Set Lockout Time for Failed Password Attempts - Ensure the desired value + for pam_faillock.so preauth unlock_time parameter in auth section + ansible.builtin.lineinfile: + path: '{{ item }}' + backrefs: true + regexp: (^\s*auth\s+)([\w\[].*\b)(\s+pam_faillock.so preauth.*)(unlock_time)=[0-9]+(.*) + line: \1required\3\4={{ var_accounts_passwords_pam_faillock_unlock_time }}\5 + state: present + loop: + - /etc/pam.d/system-auth + - /etc/pam.d/password-auth + when: + - result_pam_faillock_unlock_time_parameter_is_present.found > 0 + + - name: Set Lockout Time for Failed Password Attempts - Ensure the desired value + for pam_faillock.so authfail unlock_time parameter in auth section + ansible.builtin.lineinfile: + path: '{{ item }}' + backrefs: true + regexp: (^\s*auth\s+)([\w\[].*\b)(\s+pam_faillock.so authfail.*)(unlock_time)=[0-9]+(.*) + line: \1required\3\4={{ var_accounts_passwords_pam_faillock_unlock_time }}\5 + state: present + loop: + - /etc/pam.d/system-auth + - /etc/pam.d/password-auth + when: + - result_pam_faillock_unlock_time_parameter_is_present.found > 0 + when: + - '"pam" in ansible_facts.packages' + - not result_faillock_conf_check.stat.exists + tags: + - CCE-80670-3 + - CJIS-5.5.3 + - DISA-STIG-RHEL-08-020016 + - NIST-800-171-3.1.8 + - NIST-800-53-AC-7(b) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.1.7 + - accounts_passwords_pam_faillock_unlock_time + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q pam; then + +var_accounts_passwords_pam_faillock_unlock_time='' + + +if [ -f /usr/bin/authselect ]; then + if ! authselect check; then +echo " +authselect integrity check failed. Remediation aborted! +This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact. +It is not recommended to manually edit the PAM files when authselect tool is available. +In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended." +exit 1 +fi +authselect enable-feature with-faillock + +authselect apply-changes -b +else + AUTH_FILES=("/etc/pam.d/system-auth" "/etc/pam.d/password-auth") +for pam_file in "${AUTH_FILES[@]}" +do + if ! grep -qE '^\s*auth\s+required\s+pam_faillock\.so\s+(preauth silent|authfail).*$' "$pam_file" ; then + sed -i --follow-symlinks '/^auth.*sufficient.*pam_unix\.so.*/i auth required pam_faillock.so preauth silent' "$pam_file" + sed -i --follow-symlinks '/^auth.*required.*pam_deny\.so.*/i auth required pam_faillock.so authfail' "$pam_file" + sed -i --follow-symlinks '/^account.*required.*pam_unix\.so.*/i account required pam_faillock.so' "$pam_file" + fi + sed -Ei 's/(auth.*)(\[default=die\])(.*pam_faillock\.so)/\1required \3/g' "$pam_file" +done +fi +AUTH_FILES=("/etc/pam.d/system-auth" "/etc/pam.d/password-auth") +FAILLOCK_CONF="/etc/security/faillock.conf" +if [ -f $FAILLOCK_CONF ]; then + regex="^\s*unlock_time\s*=" + line="unlock_time = $var_accounts_passwords_pam_faillock_unlock_time" + if ! grep -q $regex $FAILLOCK_CONF; then + echo $line >> $FAILLOCK_CONF + else + sed -i --follow-symlinks 's|^\s*\(unlock_time\s*=\s*\)\(\S\+\)|\1'"$var_accounts_passwords_pam_faillock_unlock_time"'|g' $FAILLOCK_CONF + fi + for pam_file in "${AUTH_FILES[@]}" + do + if [ -e "$pam_file" ] ; then + PAM_FILE_PATH="$pam_file" + if [ -f /usr/bin/authselect ]; then + if ! authselect check; then + echo " + authselect integrity check failed. Remediation aborted! + This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact. + It is not recommended to manually edit the PAM files when authselect tool is available. + In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended." + exit 1 + fi + CURRENT_PROFILE=$(authselect current -r | awk '{ print $1 }') + # If not already in use, a custom profile is created preserving the enabled features. + if [[ ! $CURRENT_PROFILE == custom/* ]]; then + ENABLED_FEATURES=$(authselect current | tail -n+3 | awk '{ print $2 }') + authselect create-profile hardening -b $CURRENT_PROFILE + CURRENT_PROFILE="custom/hardening" + + authselect apply-changes -b --backup=before-hardening-custom-profile + authselect select $CURRENT_PROFILE + for feature in $ENABLED_FEATURES; do + authselect enable-feature $feature; + done + + authselect apply-changes -b --backup=after-hardening-custom-profile + fi + PAM_FILE_NAME=$(basename "$pam_file") + PAM_FILE_PATH="/etc/authselect/$CURRENT_PROFILE/$PAM_FILE_NAME" + + authselect apply-changes -b + fi + + if grep -qP '^\s*auth\s.*\bpam_faillock.so\s.*\bunlock_time\b' "$PAM_FILE_PATH"; then + sed -i -E --follow-symlinks 's/(.*auth.*pam_faillock.so.*)\bunlock_time\b=?[[:alnum:]]*(.*)/\1\2/g' "$PAM_FILE_PATH" + fi + if [ -f /usr/bin/authselect ]; then + + authselect apply-changes -b + fi + else + echo "$pam_file was not found" >&2 + fi + done +else + for pam_file in "${AUTH_FILES[@]}" + do + if ! grep -qE '^\s*auth.*pam_faillock\.so (preauth|authfail).*unlock_time' "$pam_file"; then + sed -i --follow-symlinks '/^auth.*required.*pam_faillock\.so.*preauth.*silent.*/ s/$/ unlock_time='"$var_accounts_passwords_pam_faillock_unlock_time"'/' "$pam_file" + sed -i --follow-symlinks '/^auth.*required.*pam_faillock\.so.*authfail.*/ s/$/ unlock_time='"$var_accounts_passwords_pam_faillock_unlock_time"'/' "$pam_file" + else + sed -i --follow-symlinks 's/\(^auth.*required.*pam_faillock\.so.*preauth.*silent.*\)\('"unlock_time"'=\)[0-9]\+\(.*\)/\1\2'"$var_accounts_passwords_pam_faillock_unlock_time"'\3/' "$pam_file" + sed -i --follow-symlinks 's/\(^auth.*required.*pam_faillock\.so.*authfail.*\)\('"unlock_time"'=\)[0-9]\+\(.*\)/\1\2'"$var_accounts_passwords_pam_faillock_unlock_time"'\3/' "$pam_file" + fi + done +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + + Set Password Quality Requirements + The default pam_pwquality PAM module provides strength +checking for passwords. It performs a number of checks, such as +making sure passwords are not similar to dictionary words, are of +at least a certain length, are not the previous password reversed, +and are not simply a change of case from the previous password. It +can also require passwords to be in certain character classes. The +pam_pwquality module is the preferred way of configuring +password requirements. + +The man pages pam_pwquality(8) +provide information on the capabilities and configuration of +each. + + Set Password Quality Requirements, if using +pam_cracklib + The pam_cracklib PAM module can be configured to meet +requirements for a variety of policies. + +For example, to configure pam_cracklib to require at least one uppercase +character, lowercase character, digit, and other (special) +character, locate the following line in /etc/pam.d/system-auth: +password requisite pam_cracklib.so try_first_pass retry=3 +and then alter it to read: +password required pam_cracklib.so try_first_pass retry=3 maxrepeat=3 minlen=14 dcredit=-1 ucredit=-1 ocredit=-1 lcredit=-1 difok=4 +If no such line exists, add one as the first line of the password section in /etc/pam.d/system-auth. +The arguments can be modified to ensure compliance with +your organization's security policy. Discussion of each parameter follows. + Note that the password quality requirements are not enforced for the +root account for some reason. + + + Set Password Quality Requirements with pam_pwquality + The pam_pwquality PAM module can be configured to meet +requirements for a variety of policies. + +For example, to configure pam_pwquality to require at least one uppercase +character, lowercase character, digit, and other (special) +character, make sure that pam_pwquality exists in /etc/pam.d/system-auth: +password requisite pam_pwquality.so try_first_pass local_users_only retry=3 authtok_type= +If no such line exists, add one as the first line of the password section in /etc/pam.d/system-auth. +Next, modify the settings in /etc/security/pwquality.conf to match the following: +difok = 4 +minlen = 14 +dcredit = -1 +ucredit = -1 +lcredit = -1 +ocredit = -1 +maxrepeat = 3 +The arguments can be modified to ensure compliance with +your organization's security policy. Discussion of each parameter follows. + + dcredit + Minimum number of digits in password + 0 + -1 + -2 + -1 + + + dictcheck + Prevent the use of dictionary words for passwords. + 1 + 1 + + + difok + Minimum number of characters not present in old +password + 15 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 8 + + + lcredit + Minimum number of lower case in password + 0 + -1 + -2 + -1 + + + maxclassrepeat + Maximum Number of Consecutive Repeating Characters in a Password From the Same Character Class + 1 + 2 + 3 + 4 + 4 + + + maxrepeat + Maximum Number of Consecutive Repeating Characters in a Password + 1 + 2 + 3 + 3 + + + minclass + Minimum number of categories of characters that must exist in a password + 1 + 2 + 3 + 4 + 3 + + + minlen + Minimum number of characters in password + 10 + 12 + 14 + 15 + 18 + 20 + 6 + 7 + 8 + 15 + + + ocredit + Minimum number of other (special characters) in +password + 0 + -1 + -2 + -1 + + + retry + Number of retry attempts before erroring out + 1 + 2 + 3 + 4 + 5 + 3 + + + ucredit + Minimum number of upper case in password + 0 + -1 + -2 + -1 + + + Ensure PAM Enforces Password Requirements - Minimum Digit Characters + The pam_pwquality module's dcredit parameter controls requirements for +usage of digits in a password. When set to a negative number, any password will be required to +contain that many digits. When set to a positive number, pam_pwquality will grant +1 additional +length credit for each digit. Modify the dcredit setting in +/etc/security/pwquality.conf to require the use of a digit in passwords. + BP28(R18) + 1 + 12 + 15 + 16 + 5 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.03 + DSS06.10 + CCI-000194 + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.2 + 4.3.3.7.4 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + 0421 + 0422 + 0431 + 0974 + 1173 + 1401 + 1504 + 1505 + 1546 + 1557 + 1558 + 1559 + 1560 + 1561 + A.18.1.4 + A.7.1.1 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.2 + A.9.4.3 + IA-5(c) + IA-5(1)(a) + CM-6(a) + IA-5(4) + PR.AC-1 + PR.AC-6 + PR.AC-7 + FMT_SMF_EXT.1 + Req-8.2.3 + SRG-OS-000071-GPOS-00039 + SRG-OS-000071-VMM-000380 + RHEL-08-020130 + SV-230359r833317_rule + Use of a complex password helps to increase the time and resources required +to compromise the password. Password complexity, or strength, is a measure of +the effectiveness of a password in resisting attempts at guessing and brute-force +attacks. + +Password complexity is one factor of several that determines how long it takes +to crack a password. The more complex the password, the greater the number of +possible combinations that need to be tested before the password is compromised. +Requiring digits makes password guessing attacks more difficult by ensuring a larger +search space. + + CCE-80653-9 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80653-9 + - DISA-STIG-RHEL-08-020130 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(a) + - NIST-800-53-IA-5(4) + - NIST-800-53-IA-5(c) + - PCI-DSS-Req-8.2.3 + - accounts_password_pam_dcredit + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy +- name: XCCDF Value var_password_pam_dcredit # promote to variable + set_fact: + var_password_pam_dcredit: !!str + tags: + - always + +- name: Ensure PAM variable dcredit is set accordingly + lineinfile: + create: true + dest: /etc/security/pwquality.conf + regexp: ^#?\s*dcredit + line: dcredit = {{ var_password_pam_dcredit }} + when: '"pam" in ansible_facts.packages' + tags: + - CCE-80653-9 + - DISA-STIG-RHEL-08-020130 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(a) + - NIST-800-53-IA-5(4) + - NIST-800-53-IA-5(c) + - PCI-DSS-Req-8.2.3 + - accounts_password_pam_dcredit + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q pam; then + +var_password_pam_dcredit='' + + +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/security/pwquality.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^dcredit") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$var_password_pam_dcredit" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^dcredit\\>" "/etc/security/pwquality.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^dcredit\\>.*/$escaped_formatted_output/gi" "/etc/security/pwquality.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-80653-9" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/security/pwquality.conf" >> "/etc/security/pwquality.conf" + printf '%s\n' "$formatted_output" >> "/etc/security/pwquality.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Ensure PAM Enforces Password Requirements - Prevent the Use of Dictionary Words + The pam_pwquality module's dictcheck check if passwords contains dictionary words. When +dictcheck is set to 1 passwords will be checked for dictionary words. + CCI-000366 + IA-5(c) + IA-5(1)(a) + CM-6(a) + IA-5(4) + SRG-OS-000480-GPOS-00225 + RHEL-08-020300 + SV-230377r833331_rule + Use of a complex password helps to increase the time and resources required to compromise the password. +Password complexity, or strength, is a measure of the effectiveness of a password in resisting attempts at +guessing and brute-force attacks. + +Password complexity is one factor of several that determines how long it takes to crack a password. The more +complex the password, the greater the number of possible combinations that need to be tested before the +password is compromised. + +Passwords with dictionary words may be more vulnerable to password-guessing attacks. + + CCE-86233-4 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-86233-4 + - DISA-STIG-RHEL-08-020300 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(a) + - NIST-800-53-IA-5(4) + - NIST-800-53-IA-5(c) + - accounts_password_pam_dictcheck + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy +- name: XCCDF Value var_password_pam_dictcheck # promote to variable + set_fact: + var_password_pam_dictcheck: !!str + tags: + - always + +- name: Ensure PAM variable dictcheck is set accordingly + lineinfile: + create: true + dest: /etc/security/pwquality.conf + regexp: ^#?\s*dictcheck + line: dictcheck = {{ var_password_pam_dictcheck }} + when: '"pam" in ansible_facts.packages' + tags: + - CCE-86233-4 + - DISA-STIG-RHEL-08-020300 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(a) + - NIST-800-53-IA-5(4) + - NIST-800-53-IA-5(c) + - accounts_password_pam_dictcheck + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q pam; then + +var_password_pam_dictcheck='' + + +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/security/pwquality.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^dictcheck") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$var_password_pam_dictcheck" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^dictcheck\\>" "/etc/security/pwquality.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^dictcheck\\>.*/$escaped_formatted_output/gi" "/etc/security/pwquality.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-86233-4" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/security/pwquality.conf" >> "/etc/security/pwquality.conf" + printf '%s\n' "$formatted_output" >> "/etc/security/pwquality.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Ensure PAM Enforces Password Requirements - Minimum Different Characters + The pam_pwquality module's difok parameter sets the number of characters +in a password that must not be present in and old password during a password change. + +Modify the difok setting in /etc/security/pwquality.conf +to equal to require differing characters +when changing passwords. + 1 + 12 + 15 + 16 + 5 + 5.6.2.1.1 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.03 + DSS06.10 + CCI-000195 + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.2 + 4.3.3.7.4 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + A.18.1.4 + A.7.1.1 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.2 + A.9.4.3 + IA-5(c) + IA-5(1)(b) + CM-6(a) + IA-5(4) + PR.AC-1 + PR.AC-6 + PR.AC-7 + SRG-OS-000072-GPOS-00040 + SRG-OS-000072-VMM-000390 + RHEL-08-020170 + SV-230363r833325_rule + Use of a complex password helps to increase the time and resources +required to compromise the password. Password complexity, or strength, +is a measure of the effectiveness of a password in resisting attempts +at guessing and brute–force attacks. + +Password complexity is one factor of several that determines how long +it takes to crack a password. The more complex the password, the +greater the number of possible combinations that need to be tested +before the password is compromised. + +Requiring a minimum number of different characters during password changes ensures that +newly changed passwords should not resemble previously compromised ones. +Note that passwords which are changed on compromised systems will still be compromised, however. + + CCE-80654-7 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80654-7 + - CJIS-5.6.2.1.1 + - DISA-STIG-RHEL-08-020170 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(b) + - NIST-800-53-IA-5(4) + - NIST-800-53-IA-5(c) + - accounts_password_pam_difok + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy +- name: XCCDF Value var_password_pam_difok # promote to variable + set_fact: + var_password_pam_difok: !!str + tags: + - always + +- name: Ensure PAM variable difok is set accordingly + lineinfile: + create: true + dest: /etc/security/pwquality.conf + regexp: ^#?\s*difok + line: difok = {{ var_password_pam_difok }} + when: '"pam" in ansible_facts.packages' + tags: + - CCE-80654-7 + - CJIS-5.6.2.1.1 + - DISA-STIG-RHEL-08-020170 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(b) + - NIST-800-53-IA-5(4) + - NIST-800-53-IA-5(c) + - accounts_password_pam_difok + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q pam; then + +var_password_pam_difok='' + + +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/security/pwquality.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^difok") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$var_password_pam_difok" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^difok\\>" "/etc/security/pwquality.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^difok\\>.*/$escaped_formatted_output/gi" "/etc/security/pwquality.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-80654-7" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/security/pwquality.conf" >> "/etc/security/pwquality.conf" + printf '%s\n' "$formatted_output" >> "/etc/security/pwquality.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Ensure PAM Enforces Password Requirements - Enforce for Local Accounts Only + The pam_pwquality module's local_users_only parameter controls requirements for +enforcing password complexity by pam_pwquality only for local user accounts and ignoring +centralized user account management password complexity configurations. Enable the local_users_only +setting in /etc/security/pwquality.conf to require password complexity enforcement +for only local user accounts. + Using this rule bypasses pam_faillock's functionality and should be used in cases +where centralized management such as LDAP or Active Directory is in use. + CCI-000015 + AC-2(1) + SRG-OS-000001-GPOS-00001 + The operating system must provide automated mechanisms for supporting account management +functions. Enterprise environments make application account management challenging and +complex. A manual process for account management functions adds the risk of a potential +oversight or other error. + + CCE-83364-0 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83364-0 + - NIST-800-53-AC-2(1) + - accounts_password_pam_enforce_local + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Ensure PAM Enforces Password Requirements - Enforce for Local Accounts Only + lineinfile: + path: /etc/security/pwquality.conf + create: true + line: local_users_only + state: present + when: '"pam" in ansible_facts.packages' + tags: + - CCE-83364-0 + - NIST-800-53-AC-2(1) + - accounts_password_pam_enforce_local + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q pam; then + +if [ -e "/etc/security/pwquality.conf" ] ; then + + LC_ALL=C sed -i "/^\s*local_users_only/Id" "/etc/security/pwquality.conf" +else + touch "/etc/security/pwquality.conf" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/security/pwquality.conf" + +cp "/etc/security/pwquality.conf" "/etc/security/pwquality.conf.bak" +# Insert at the end of the file +printf '%s\n' "local_users_only" >> "/etc/security/pwquality.conf" +# Clean up after ourselves. +rm "/etc/security/pwquality.conf.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure PAM Enforces Password Requirements - Enforce for root User + The pam_pwquality module's enforce_for_root parameter controls requirements for +enforcing password complexity for the root user. Enable the enforce_for_root +setting in /etc/security/pwquality.conf to require the root user +to use complex passwords. + CCI-000194 + CCI-000193 + CCI-001619 + CCI-000205 + CCI-000195 + CCI-000192 + CCI-000366 + IA-5(c) + IA-5(1)(a) + CM-6(a) + IA-5(4) + SRG-OS-000072-GPOS-00040 + SRG-OS-000071-GPOS-00039 + SRG-OS-000070-GPOS-00038 + SRG-OS-000266-GPOS-00101 + SRG-OS-000078-GPOS-00046 + SRG-OS-000480-GPOS-00225 + SRG-OS-000069-GPOS-00037 + Use of a complex password helps to increase the time and resources required to compromise +the password. Password complexity, or strength, is a measure of the effectiveness of a +password in resisting attempts at guessing and brute-force attacks. + +Password complexity is one factor of several that determines how long it takes to crack a +password. The more complex the password, the greater the number of possible combinations +that need to be tested before the password is compromised. + + CCE-83377-2 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83377-2 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(a) + - NIST-800-53-IA-5(4) + - NIST-800-53-IA-5(c) + - accounts_password_pam_enforce_root + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Ensure PAM Enforces Password Requirements - Enforce for root User + lineinfile: + path: /etc/security/pwquality.conf + create: true + line: enforce_for_root + state: present + when: '"pam" in ansible_facts.packages' + tags: + - CCE-83377-2 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(a) + - NIST-800-53-IA-5(4) + - NIST-800-53-IA-5(c) + - accounts_password_pam_enforce_root + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q pam; then + +if [ -e "/etc/security/pwquality.conf" ] ; then + + LC_ALL=C sed -i "/^\s*enforce_for_root/Id" "/etc/security/pwquality.conf" +else + touch "/etc/security/pwquality.conf" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/security/pwquality.conf" + +cp "/etc/security/pwquality.conf" "/etc/security/pwquality.conf.bak" +# Insert at the end of the file +printf '%s\n' "enforce_for_root" >> "/etc/security/pwquality.conf" +# Clean up after ourselves. +rm "/etc/security/pwquality.conf.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure PAM Enforces Password Requirements - Minimum Lowercase Characters + The pam_pwquality module's lcredit parameter controls requirements for +usage of lowercase letters in a password. When set to a negative number, any password will be required to +contain that many lowercase characters. When set to a positive number, pam_pwquality will grant +1 additional +length credit for each lowercase character. Modify the lcredit setting in +/etc/security/pwquality.conf to require the use of a lowercase character in passwords. + BP28(R18) + 1 + 12 + 15 + 16 + 5 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.03 + DSS06.10 + CCI-000193 + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.2 + 4.3.3.7.4 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + 0421 + 0422 + 0431 + 0974 + 1173 + 1401 + 1504 + 1505 + 1546 + 1557 + 1558 + 1559 + 1560 + 1561 + A.18.1.4 + A.7.1.1 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.2 + A.9.4.3 + IA-5(c) + IA-5(1)(a) + CM-6(a) + IA-5(4) + PR.AC-1 + PR.AC-6 + PR.AC-7 + FMT_SMF_EXT.1 + Req-8.2.3 + SRG-OS-000070-GPOS-00038 + SRG-OS-000070-VMM-000370 + RHEL-08-020120 + SV-230358r833315_rule + Use of a complex password helps to increase the time and resources required +to compromise the password. Password complexity, or strength, is a measure of +the effectiveness of a password in resisting attempts at guessing and brute-force +attacks. + +Password complexity is one factor of several that determines how long it takes +to crack a password. The more complex the password, the greater the number of +possble combinations that need to be tested before the password is compromised. +Requiring a minimum number of lowercase characters makes password guessing attacks +more difficult by ensuring a larger search space. + + CCE-80655-4 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80655-4 + - DISA-STIG-RHEL-08-020120 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(a) + - NIST-800-53-IA-5(4) + - NIST-800-53-IA-5(c) + - PCI-DSS-Req-8.2.3 + - accounts_password_pam_lcredit + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy +- name: XCCDF Value var_password_pam_lcredit # promote to variable + set_fact: + var_password_pam_lcredit: !!str + tags: + - always + +- name: Ensure PAM variable lcredit is set accordingly + lineinfile: + create: true + dest: /etc/security/pwquality.conf + regexp: ^#?\s*lcredit + line: lcredit = {{ var_password_pam_lcredit }} + when: '"pam" in ansible_facts.packages' + tags: + - CCE-80655-4 + - DISA-STIG-RHEL-08-020120 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(a) + - NIST-800-53-IA-5(4) + - NIST-800-53-IA-5(c) + - PCI-DSS-Req-8.2.3 + - accounts_password_pam_lcredit + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q pam; then + +var_password_pam_lcredit='' + + +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/security/pwquality.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^lcredit") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$var_password_pam_lcredit" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^lcredit\\>" "/etc/security/pwquality.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^lcredit\\>.*/$escaped_formatted_output/gi" "/etc/security/pwquality.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-80655-4" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/security/pwquality.conf" >> "/etc/security/pwquality.conf" + printf '%s\n' "$formatted_output" >> "/etc/security/pwquality.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Ensure PAM Enforces Password Requirements - Maximum Consecutive Repeating Characters from Same Character Class + The pam_pwquality module's maxclassrepeat parameter controls requirements for +consecutive repeating characters from the same character class. When set to a positive number, it will reject passwords +which contain more than that number of consecutive characters from the same character class. Modify the +maxclassrepeat setting in /etc/security/pwquality.conf to equal +to prevent a run of ( + 1) or more identical characters. + 1 + 12 + 15 + 16 + 5 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.03 + DSS06.10 + CCI-000195 + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.2 + 4.3.3.7.4 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + A.18.1.4 + A.7.1.1 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.2 + A.9.4.3 + IA-5(c) + IA-5(1)(a) + CM-6(a) + IA-5(4) + PR.AC-1 + PR.AC-6 + PR.AC-7 + SRG-OS-000072-GPOS-00040 + RHEL-08-020140 + SV-230360r833319_rule + Use of a complex password helps to increase the time and resources required to compromise the password. +Password complexity, or strength, is a measure of the effectiveness of a password in resisting +attempts at guessing and brute-force attacks. + +Password complexity is one factor of several that determines how long it takes to crack a password. The +more complex a password, the greater the number of possible combinations that need to be tested before the +password is compromised. + + CCE-81034-1 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-81034-1 + - DISA-STIG-RHEL-08-020140 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(a) + - NIST-800-53-IA-5(4) + - NIST-800-53-IA-5(c) + - accounts_password_pam_maxclassrepeat + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy +- name: XCCDF Value var_password_pam_maxclassrepeat # promote to variable + set_fact: + var_password_pam_maxclassrepeat: !!str + tags: + - always + +- name: Ensure PAM variable maxclassrepeat is set accordingly + lineinfile: + create: true + dest: /etc/security/pwquality.conf + regexp: ^#?\s*maxclassrepeat + line: maxclassrepeat = {{ var_password_pam_maxclassrepeat }} + when: '"pam" in ansible_facts.packages' + tags: + - CCE-81034-1 + - DISA-STIG-RHEL-08-020140 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(a) + - NIST-800-53-IA-5(4) + - NIST-800-53-IA-5(c) + - accounts_password_pam_maxclassrepeat + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q pam; then + +var_password_pam_maxclassrepeat='' + + +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/security/pwquality.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^maxclassrepeat") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$var_password_pam_maxclassrepeat" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^maxclassrepeat\\>" "/etc/security/pwquality.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^maxclassrepeat\\>.*/$escaped_formatted_output/gi" "/etc/security/pwquality.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-81034-1" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/security/pwquality.conf" >> "/etc/security/pwquality.conf" + printf '%s\n' "$formatted_output" >> "/etc/security/pwquality.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Set Password Maximum Consecutive Repeating Characters + The pam_pwquality module's maxrepeat parameter controls requirements for +consecutive repeating characters. When set to a positive number, it will reject passwords +which contain more than that number of consecutive characters. Modify the maxrepeat setting +in /etc/security/pwquality.conf to equal to prevent a +run of ( + 1) or more identical characters. + 1 + 12 + 15 + 16 + 5 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.03 + DSS06.10 + CCI-000195 + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.2 + 4.3.3.7.4 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + A.18.1.4 + A.7.1.1 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.2 + A.9.4.3 + IA-5(c) + CM-6(a) + IA-5(4) + PR.AC-1 + PR.AC-6 + PR.AC-7 + SRG-OS-000072-GPOS-00040 + RHEL-08-020150 + SV-230361r833321_rule + Use of a complex password helps to increase the time and resources required to compromise the password. +Password complexity, or strength, is a measure of the effectiveness of a password in resisting attempts at +guessing and brute-force attacks. + +Password complexity is one factor of several that determines how long it takes to crack a password. The more +complex the password, the greater the number of possible combinations that need to be tested before the +password is compromised. + +Passwords with excessive repeating characters may be more vulnerable to password-guessing attacks. + + CCE-82066-2 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-82066-2 + - DISA-STIG-RHEL-08-020150 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(4) + - NIST-800-53-IA-5(c) + - accounts_password_pam_maxrepeat + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy +- name: XCCDF Value var_password_pam_maxrepeat # promote to variable + set_fact: + var_password_pam_maxrepeat: !!str + tags: + - always + +- name: Ensure PAM variable maxrepeat is set accordingly + lineinfile: + create: true + dest: /etc/security/pwquality.conf + regexp: ^#?\s*maxrepeat + line: maxrepeat = {{ var_password_pam_maxrepeat }} + when: '"pam" in ansible_facts.packages' + tags: + - CCE-82066-2 + - DISA-STIG-RHEL-08-020150 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(4) + - NIST-800-53-IA-5(c) + - accounts_password_pam_maxrepeat + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q pam; then + +var_password_pam_maxrepeat='' + + +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/security/pwquality.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^maxrepeat") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$var_password_pam_maxrepeat" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^maxrepeat\\>" "/etc/security/pwquality.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^maxrepeat\\>.*/$escaped_formatted_output/gi" "/etc/security/pwquality.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-82066-2" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/security/pwquality.conf" >> "/etc/security/pwquality.conf" + printf '%s\n' "$formatted_output" >> "/etc/security/pwquality.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Ensure PAM Enforces Password Requirements - Minimum Different Categories + The pam_pwquality module's minclass parameter controls +requirements for usage of different character classes, or types, of character +that must exist in a password before it is considered valid. For example, +setting this value to three (3) requires that any password must have characters +from at least three different categories in order to be approved. The default +value is zero (0), meaning there are no required classes. There are four +categories available: + +* Upper-case characters +* Lower-case characters +* Digits +* Special characters (for example, punctuation) + +Modify the minclass setting in /etc/security/pwquality.conf entry +to require +differing categories of characters when changing passwords. + 1 + 12 + 15 + 16 + 5 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.03 + DSS06.10 + CCI-000195 + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.2 + 4.3.3.7.4 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + 0421 + 0422 + 0431 + 0974 + 1173 + 1401 + 1504 + 1505 + 1546 + 1557 + 1558 + 1559 + 1560 + 1561 + A.18.1.4 + A.7.1.1 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.2 + A.9.4.3 + IA-5(c) + IA-5(1)(a) + CM-6(a) + IA-5(4) + PR.AC-1 + PR.AC-6 + PR.AC-7 + SRG-OS-000072-GPOS-00040 + RHEL-08-020160 + 5.5.1 + SV-230362r833323_rule + Use of a complex password helps to increase the time and resources required to compromise the password. +Password complexity, or strength, is a measure of the effectiveness of a password in resisting attempts +at guessing and brute-force attacks. + +Password complexity is one factor of several that determines how long it takes to crack a password. The +more complex the password, the greater the number of possible combinations that need to be tested before +the password is compromised. + +Requiring a minimum number of character categories makes password guessing attacks more difficult +by ensuring a larger search space. + + CCE-82046-4 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-82046-4 + - DISA-STIG-RHEL-08-020160 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(a) + - NIST-800-53-IA-5(4) + - NIST-800-53-IA-5(c) + - accounts_password_pam_minclass + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy +- name: XCCDF Value var_password_pam_minclass # promote to variable + set_fact: + var_password_pam_minclass: !!str + tags: + - always + +- name: Ensure PAM variable minclass is set accordingly + lineinfile: + create: true + dest: /etc/security/pwquality.conf + regexp: ^#?\s*minclass + line: minclass = {{ var_password_pam_minclass }} + when: '"pam" in ansible_facts.packages' + tags: + - CCE-82046-4 + - DISA-STIG-RHEL-08-020160 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(a) + - NIST-800-53-IA-5(4) + - NIST-800-53-IA-5(c) + - accounts_password_pam_minclass + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q pam; then + +var_password_pam_minclass='' + + +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/security/pwquality.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^minclass") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$var_password_pam_minclass" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^minclass\\>" "/etc/security/pwquality.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^minclass\\>.*/$escaped_formatted_output/gi" "/etc/security/pwquality.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-82046-4" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/security/pwquality.conf" >> "/etc/security/pwquality.conf" + printf '%s\n' "$formatted_output" >> "/etc/security/pwquality.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Ensure PAM Enforces Password Requirements - Minimum Length + The pam_pwquality module's minlen parameter controls requirements for +minimum characters required in a password. Add minlen= +after pam_pwquality to set minimum password length requirements. + BP28(R18) + 1 + 12 + 15 + 16 + 5 + 5.6.2.1.1 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.03 + DSS06.10 + CCI-000205 + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.2 + 4.3.3.7.4 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + 0421 + 0422 + 0431 + 0974 + 1173 + 1401 + 1504 + 1505 + 1546 + 1557 + 1558 + 1559 + 1560 + 1561 + A.18.1.4 + A.7.1.1 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.2 + A.9.4.3 + IA-5(c) + IA-5(1)(a) + CM-6(a) + IA-5(4) + PR.AC-1 + PR.AC-6 + PR.AC-7 + FMT_SMF_EXT.1 + Req-8.2.3 + SRG-OS-000078-GPOS-00046 + SRG-OS-000072-VMM-000390 + SRG-OS-000078-VMM-000450 + RHEL-08-020230 + 5.5.1 + SV-230369r833327_rule + The shorter the password, the lower the number of possible combinations +that need to be tested before the password is compromised. + +Password complexity, or strength, is a measure of the effectiveness of a +password in resisting attempts at guessing and brute-force attacks. +Password length is one factor of several that helps to determine strength +and how long it takes to crack a password. Use of more characters in a password +helps to exponentially increase the time and/or resources required to +compromise the password. + + CCE-80656-2 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80656-2 + - CJIS-5.6.2.1.1 + - DISA-STIG-RHEL-08-020230 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(a) + - NIST-800-53-IA-5(4) + - NIST-800-53-IA-5(c) + - PCI-DSS-Req-8.2.3 + - accounts_password_pam_minlen + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy +- name: XCCDF Value var_password_pam_minlen # promote to variable + set_fact: + var_password_pam_minlen: !!str + tags: + - always + +- name: Ensure PAM variable minlen is set accordingly + lineinfile: + create: true + dest: /etc/security/pwquality.conf + regexp: ^#?\s*minlen + line: minlen = {{ var_password_pam_minlen }} + when: '"pam" in ansible_facts.packages' + tags: + - CCE-80656-2 + - CJIS-5.6.2.1.1 + - DISA-STIG-RHEL-08-020230 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(a) + - NIST-800-53-IA-5(4) + - NIST-800-53-IA-5(c) + - PCI-DSS-Req-8.2.3 + - accounts_password_pam_minlen + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q pam; then + +var_password_pam_minlen='' + + +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/security/pwquality.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^minlen") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$var_password_pam_minlen" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^minlen\\>" "/etc/security/pwquality.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^minlen\\>.*/$escaped_formatted_output/gi" "/etc/security/pwquality.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-80656-2" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/security/pwquality.conf" >> "/etc/security/pwquality.conf" + printf '%s\n' "$formatted_output" >> "/etc/security/pwquality.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Ensure PAM Enforces Password Requirements - Minimum Special Characters + The pam_pwquality module's ocredit= parameter controls requirements for +usage of special (or "other") characters in a password. When set to a negative number, +any password will be required to contain that many special characters. +When set to a positive number, pam_pwquality will grant +1 +additional length credit for each special character. Modify the ocredit setting +in /etc/security/pwquality.conf to equal +to require use of a special character in passwords. + BP28(R18) + 1 + 12 + 15 + 16 + 5 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.03 + DSS06.10 + CCI-001619 + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.2 + 4.3.3.7.4 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + 0421 + 0422 + 0431 + 0974 + 1173 + 1401 + 1504 + 1505 + 1546 + 1557 + 1558 + 1559 + 1560 + 1561 + A.18.1.4 + A.7.1.1 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.2 + A.9.4.3 + IA-5(c) + IA-5(1)(a) + CM-6(a) + IA-5(4) + PR.AC-1 + PR.AC-6 + PR.AC-7 + FMT_SMF_EXT.1 + SRG-OS-000266-GPOS-00101 + SRG-OS-000266-VMM-000940 + RHEL-08-020280 + SV-230375r833329_rule + Use of a complex password helps to increase the time and resources required +to compromise the password. Password complexity, or strength, is a measure of +the effectiveness of a password in resisting attempts at guessing and brute-force +attacks. + +Password complexity is one factor of several that determines how long it takes +to crack a password. The more complex the password, the greater the number of +possible combinations that need to be tested before the password is compromised. +Requiring a minimum number of special characters makes password guessing attacks +more difficult by ensuring a larger search space. + + CCE-80663-8 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80663-8 + - DISA-STIG-RHEL-08-020280 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(a) + - NIST-800-53-IA-5(4) + - NIST-800-53-IA-5(c) + - accounts_password_pam_ocredit + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy +- name: XCCDF Value var_password_pam_ocredit # promote to variable + set_fact: + var_password_pam_ocredit: !!str + tags: + - always + +- name: Ensure PAM variable ocredit is set accordingly + lineinfile: + create: true + dest: /etc/security/pwquality.conf + regexp: ^#?\s*ocredit + line: ocredit = {{ var_password_pam_ocredit }} + when: '"pam" in ansible_facts.packages' + tags: + - CCE-80663-8 + - DISA-STIG-RHEL-08-020280 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(a) + - NIST-800-53-IA-5(4) + - NIST-800-53-IA-5(c) + - accounts_password_pam_ocredit + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q pam; then + +var_password_pam_ocredit='' + + +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/security/pwquality.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^ocredit") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$var_password_pam_ocredit" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^ocredit\\>" "/etc/security/pwquality.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^ocredit\\>.*/$escaped_formatted_output/gi" "/etc/security/pwquality.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-80663-8" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/security/pwquality.conf" >> "/etc/security/pwquality.conf" + printf '%s\n' "$formatted_output" >> "/etc/security/pwquality.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Ensure PAM password complexity module is enabled in password-auth + To enable PAM password complexity in password-auth file: +Edit the password section in +/etc/pam.d/password-auth to show +password requisite pam_pwquality.so. + CCI-000366 + SRG-OS-000069-GPOS-00037 + SRG-OS-000070-GPOS-00038 + SRG-OS-000480-GPOS-00227 + RHEL-08-020100 + SV-230356r809379_rule + Enabling PAM password complexity permits to enforce strong passwords and consequently +makes the system less prone to dictionary attacks. + + CCE-85877-9 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-85877-9 + - DISA-STIG-RHEL-08-020100 + - accounts_password_pam_pwquality_password_auth + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure PAM password complexity module is enabled in password-auth - Check + if /etc/pam.d/password-auth file is present + ansible.builtin.stat: + path: /etc/pam.d/password-auth + register: result_pam_file_present + when: '"pam" in ansible_facts.packages' + tags: + - CCE-85877-9 + - DISA-STIG-RHEL-08-020100 + - accounts_password_pam_pwquality_password_auth + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure PAM password complexity module is enabled in password-auth - Check + the proper remediation for the system + block: + + - name: Ensure PAM password complexity module is enabled in password-auth - Define + the PAM file to be edited as a local fact + ansible.builtin.set_fact: + pam_file_path: /etc/pam.d/password-auth + + - name: Ensure PAM password complexity module is enabled in password-auth - Check + if system relies on authselect + ansible.builtin.stat: + path: /usr/bin/authselect + register: result_authselect_present + + - name: Ensure PAM password complexity module is enabled in password-auth - Remediate + using authselect + block: + + - name: Ensure PAM password complexity module is enabled in password-auth - Check + integrity of authselect current profile + ansible.builtin.command: + cmd: authselect check + register: result_authselect_check_cmd + changed_when: false + ignore_errors: true + + - name: Ensure PAM password complexity module is enabled in password-auth - Informative + message based on the authselect integrity check result + ansible.builtin.assert: + that: + - result_authselect_check_cmd is success + fail_msg: + - authselect integrity check failed. Remediation aborted! + - This remediation could not be applied because an authselect profile was + not selected or the selected profile is not intact. + - It is not recommended to manually edit the PAM files when authselect tool + is available. + - In cases where the default authselect profile does not cover a specific + demand, a custom authselect profile is recommended. + success_msg: + - authselect integrity check passed + + - name: Ensure PAM password complexity module is enabled in password-auth - Get + authselect current profile + ansible.builtin.shell: + cmd: authselect current -r | awk '{ print $1 }' + register: result_authselect_profile + changed_when: false + when: + - result_authselect_check_cmd is success + + - name: Ensure PAM password complexity module is enabled in password-auth - Define + the current authselect profile as a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: '{{ result_authselect_profile.stdout }}' + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is match("custom/") + + - name: Ensure PAM password complexity module is enabled in password-auth - Define + the new authselect custom profile as a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: custom/hardening + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is not match("custom/") + + - name: Ensure PAM password complexity module is enabled in password-auth - Get + authselect current features to also enable them in the custom profile + ansible.builtin.shell: + cmd: authselect current | tail -n+3 | awk '{ print $2 }' + register: result_authselect_features + changed_when: false + when: + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + + - name: Ensure PAM password complexity module is enabled in password-auth - Check + if any custom profile with the same name was already created + ansible.builtin.stat: + path: /etc/authselect/{{ authselect_custom_profile }} + register: result_authselect_custom_profile_present + changed_when: false + when: + - authselect_current_profile is not match("custom/") + + - name: Ensure PAM password complexity module is enabled in password-auth - Create + an authselect custom profile based on the current profile + ansible.builtin.command: + cmd: authselect create-profile hardening -b {{ authselect_current_profile + }} + when: + - result_authselect_check_cmd is success + - authselect_current_profile is not match("custom/") + - not result_authselect_custom_profile_present.stat.exists + + - name: Ensure PAM password complexity module is enabled in password-auth - Ensure + authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=before-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Ensure PAM password complexity module is enabled in password-auth - Ensure + the authselect custom profile is selected + ansible.builtin.command: + cmd: authselect select {{ authselect_custom_profile }} + register: result_pam_authselect_select_profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Ensure PAM password complexity module is enabled in password-auth - Restore + the authselect features in the custom profile + ansible.builtin.command: + cmd: authselect enable-feature {{ item }} + loop: '{{ result_authselect_features.stdout_lines }}' + register: result_pam_authselect_restore_features + when: + - result_authselect_profile is not skipped + - result_authselect_features is not skipped + - result_pam_authselect_select_profile is not skipped + + - name: Ensure PAM password complexity module is enabled in password-auth - Ensure + authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=after-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - result_pam_authselect_restore_features is not skipped + + - name: Ensure PAM password complexity module is enabled in password-auth - Change + the PAM file to be edited according to the custom authselect profile + ansible.builtin.set_fact: + pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path + | basename }} + when: + - result_authselect_present.stat.exists + + - name: Ensure PAM password complexity module is enabled in password-auth - Check + if expected PAM module line is present in {{ pam_file_path }} + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + regexp: ^\s*password\s+requisite\s+pam_pwquality.so\s*.* + state: absent + check_mode: true + changed_when: false + register: result_pam_line_present + + - name: Ensure PAM password complexity module is enabled in password-auth - Include + or update the PAM module line in {{ pam_file_path }} + block: + + - name: Ensure PAM password complexity module is enabled in password-auth - Check + if required PAM module line is present in {{ pam_file_path }} with different + control + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + regexp: ^\s*password\s+.*\s+pam_pwquality.so\s* + state: absent + check_mode: true + changed_when: false + register: result_pam_line_other_control_present + + - name: Ensure PAM password complexity module is enabled in password-auth - Ensure + the correct control for the required PAM module line in {{ pam_file_path }} + ansible.builtin.replace: + dest: '{{ pam_file_path }}' + regexp: ^(\s*password\s+).*(\bpam_pwquality.so.*) + replace: \1requisite \2 + register: result_pam_module_edit + when: + - result_pam_line_other_control_present.found == 1 + + - name: Ensure PAM password complexity module is enabled in password-auth - Ensure + the required PAM module line is included in {{ pam_file_path }} + ansible.builtin.lineinfile: + dest: '{{ pam_file_path }}' + insertafter: ^account.*required.*pam_permit\.so + line: password requisite pam_pwquality.so + register: result_pam_module_add + when: + - result_pam_line_other_control_present.found == 0 or result_pam_line_other_control_present.found + > 1 + + - name: Ensure PAM password complexity module is enabled in password-auth - Ensure + authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: | + result_authselect_present is defined and result_authselect_present.stat.exists and ((result_pam_module_add is defined and result_pam_module_add.changed) or (result_pam_module_edit is defined and result_pam_module_edit.changed)) + when: + - result_pam_line_present.found is defined + - result_pam_line_present.found == 0 + + - name: Ensure PAM password complexity module is enabled in password-auth - Ensure + authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: + - result_authselect_present.stat.exists + - (result_pam__add is defined and result_pam__add.changed) or (result_pam__edit + is defined and result_pam__edit.changed) + when: + - '"pam" in ansible_facts.packages' + - result_pam_file_present.stat.exists + tags: + - CCE-85877-9 + - DISA-STIG-RHEL-08-020100 + - accounts_password_pam_pwquality_password_auth + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if rpm --quiet -q pam; then + +if [ -e "/etc/pam.d/password-auth" ] ; then + PAM_FILE_PATH="/etc/pam.d/password-auth" + if [ -f /usr/bin/authselect ]; then + if ! authselect check; then + echo " + authselect integrity check failed. Remediation aborted! + This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact. + It is not recommended to manually edit the PAM files when authselect tool is available. + In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended." + exit 1 + fi + CURRENT_PROFILE=$(authselect current -r | awk '{ print $1 }') + # If not already in use, a custom profile is created preserving the enabled features. + if [[ ! $CURRENT_PROFILE == custom/* ]]; then + ENABLED_FEATURES=$(authselect current | tail -n+3 | awk '{ print $2 }') + authselect create-profile hardening -b $CURRENT_PROFILE + CURRENT_PROFILE="custom/hardening" + + authselect apply-changes -b --backup=before-hardening-custom-profile + authselect select $CURRENT_PROFILE + for feature in $ENABLED_FEATURES; do + authselect enable-feature $feature; + done + + authselect apply-changes -b --backup=after-hardening-custom-profile + fi + PAM_FILE_NAME=$(basename "/etc/pam.d/password-auth") + PAM_FILE_PATH="/etc/authselect/$CURRENT_PROFILE/$PAM_FILE_NAME" + + authselect apply-changes -b + fi + if ! grep -qP '^\s*password\s+'"requisite"'\s+pam_pwquality.so\s*.*' "$PAM_FILE_PATH"; then + # Line matching group + control + module was not found. Check group + module. + if [ "$(grep -cP '^\s*password\s+.*\s+pam_pwquality.so\s*' "$PAM_FILE_PATH")" -eq 1 ]; then + # The control is updated only if one single line matches. + sed -i -E --follow-symlinks 's/^(\s*password\s+).*(\bpam_pwquality.so.*)/\1'"requisite"' \2/' "$PAM_FILE_PATH" + else + LAST_MATCH_LINE=$(grep -nP "^account.*required.*pam_permit\.so" "$PAM_FILE_PATH" | tail -n 1 | cut -d: -f 1) + if [ ! -z $LAST_MATCH_LINE ]; then + sed -i --follow-symlinks $LAST_MATCH_LINE' a password '"requisite"' pam_pwquality.so' "$PAM_FILE_PATH" + else + echo 'password '"requisite"' pam_pwquality.so' >> "$PAM_FILE_PATH" + fi + fi +fi + if [ -f /usr/bin/authselect ]; then + + authselect apply-changes -b + fi +else + echo "/etc/pam.d/password-auth was not found" >&2 +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure PAM password complexity module is enabled in system-auth + To enable PAM password complexity in system-auth file: +Edit the password section in +/etc/pam.d/system-auth to show +password requisite pam_pwquality.so. + CCI-000366 + SRG-OS-000480-GPOS-00227 + RHEL-08-020101 + SV-251713r810407_rule + Enabling PAM password complexity permits to enforce strong passwords and consequently +makes the system less prone to dictionary attacks. + + CCE-85872-0 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-85872-0 + - DISA-STIG-RHEL-08-020101 + - accounts_password_pam_pwquality_system_auth + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure PAM password complexity module is enabled in system-auth - Check if + /etc/pam.d/system-auth file is present + ansible.builtin.stat: + path: /etc/pam.d/system-auth + register: result_pam_file_present + when: '"pam" in ansible_facts.packages' + tags: + - CCE-85872-0 + - DISA-STIG-RHEL-08-020101 + - accounts_password_pam_pwquality_system_auth + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure PAM password complexity module is enabled in system-auth - Check the + proper remediation for the system + block: + + - name: Ensure PAM password complexity module is enabled in system-auth - Define + the PAM file to be edited as a local fact + ansible.builtin.set_fact: + pam_file_path: /etc/pam.d/system-auth + + - name: Ensure PAM password complexity module is enabled in system-auth - Check + if system relies on authselect + ansible.builtin.stat: + path: /usr/bin/authselect + register: result_authselect_present + + - name: Ensure PAM password complexity module is enabled in system-auth - Remediate + using authselect + block: + + - name: Ensure PAM password complexity module is enabled in system-auth - Check + integrity of authselect current profile + ansible.builtin.command: + cmd: authselect check + register: result_authselect_check_cmd + changed_when: false + ignore_errors: true + + - name: Ensure PAM password complexity module is enabled in system-auth - Informative + message based on the authselect integrity check result + ansible.builtin.assert: + that: + - result_authselect_check_cmd is success + fail_msg: + - authselect integrity check failed. Remediation aborted! + - This remediation could not be applied because an authselect profile was + not selected or the selected profile is not intact. + - It is not recommended to manually edit the PAM files when authselect tool + is available. + - In cases where the default authselect profile does not cover a specific + demand, a custom authselect profile is recommended. + success_msg: + - authselect integrity check passed + + - name: Ensure PAM password complexity module is enabled in system-auth - Get + authselect current profile + ansible.builtin.shell: + cmd: authselect current -r | awk '{ print $1 }' + register: result_authselect_profile + changed_when: false + when: + - result_authselect_check_cmd is success + + - name: Ensure PAM password complexity module is enabled in system-auth - Define + the current authselect profile as a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: '{{ result_authselect_profile.stdout }}' + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is match("custom/") + + - name: Ensure PAM password complexity module is enabled in system-auth - Define + the new authselect custom profile as a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: custom/hardening + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is not match("custom/") + + - name: Ensure PAM password complexity module is enabled in system-auth - Get + authselect current features to also enable them in the custom profile + ansible.builtin.shell: + cmd: authselect current | tail -n+3 | awk '{ print $2 }' + register: result_authselect_features + changed_when: false + when: + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + + - name: Ensure PAM password complexity module is enabled in system-auth - Check + if any custom profile with the same name was already created + ansible.builtin.stat: + path: /etc/authselect/{{ authselect_custom_profile }} + register: result_authselect_custom_profile_present + changed_when: false + when: + - authselect_current_profile is not match("custom/") + + - name: Ensure PAM password complexity module is enabled in system-auth - Create + an authselect custom profile based on the current profile + ansible.builtin.command: + cmd: authselect create-profile hardening -b {{ authselect_current_profile + }} + when: + - result_authselect_check_cmd is success + - authselect_current_profile is not match("custom/") + - not result_authselect_custom_profile_present.stat.exists + + - name: Ensure PAM password complexity module is enabled in system-auth - Ensure + authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=before-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Ensure PAM password complexity module is enabled in system-auth - Ensure + the authselect custom profile is selected + ansible.builtin.command: + cmd: authselect select {{ authselect_custom_profile }} + register: result_pam_authselect_select_profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Ensure PAM password complexity module is enabled in system-auth - Restore + the authselect features in the custom profile + ansible.builtin.command: + cmd: authselect enable-feature {{ item }} + loop: '{{ result_authselect_features.stdout_lines }}' + register: result_pam_authselect_restore_features + when: + - result_authselect_profile is not skipped + - result_authselect_features is not skipped + - result_pam_authselect_select_profile is not skipped + + - name: Ensure PAM password complexity module is enabled in system-auth - Ensure + authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=after-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - result_pam_authselect_restore_features is not skipped + + - name: Ensure PAM password complexity module is enabled in system-auth - Change + the PAM file to be edited according to the custom authselect profile + ansible.builtin.set_fact: + pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path + | basename }} + when: + - result_authselect_present.stat.exists + + - name: Ensure PAM password complexity module is enabled in system-auth - Check + if expected PAM module line is present in {{ pam_file_path }} + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + regexp: ^\s*password\s+requisite\s+pam_pwquality.so\s*.* + state: absent + check_mode: true + changed_when: false + register: result_pam_line_present + + - name: Ensure PAM password complexity module is enabled in system-auth - Include + or update the PAM module line in {{ pam_file_path }} + block: + + - name: Ensure PAM password complexity module is enabled in system-auth - Check + if required PAM module line is present in {{ pam_file_path }} with different + control + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + regexp: ^\s*password\s+.*\s+pam_pwquality.so\s* + state: absent + check_mode: true + changed_when: false + register: result_pam_line_other_control_present + + - name: Ensure PAM password complexity module is enabled in system-auth - Ensure + the correct control for the required PAM module line in {{ pam_file_path }} + ansible.builtin.replace: + dest: '{{ pam_file_path }}' + regexp: ^(\s*password\s+).*(\bpam_pwquality.so.*) + replace: \1requisite \2 + register: result_pam_module_edit + when: + - result_pam_line_other_control_present.found == 1 + + - name: Ensure PAM password complexity module is enabled in system-auth - Ensure + the required PAM module line is included in {{ pam_file_path }} + ansible.builtin.lineinfile: + dest: '{{ pam_file_path }}' + insertafter: ^account.*required.*pam_permit\.so + line: password requisite pam_pwquality.so + register: result_pam_module_add + when: + - result_pam_line_other_control_present.found == 0 or result_pam_line_other_control_present.found + > 1 + + - name: Ensure PAM password complexity module is enabled in system-auth - Ensure + authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: | + result_authselect_present is defined and result_authselect_present.stat.exists and ((result_pam_module_add is defined and result_pam_module_add.changed) or (result_pam_module_edit is defined and result_pam_module_edit.changed)) + when: + - result_pam_line_present.found is defined + - result_pam_line_present.found == 0 + + - name: Ensure PAM password complexity module is enabled in system-auth - Ensure + authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: + - result_authselect_present.stat.exists + - (result_pam__add is defined and result_pam__add.changed) or (result_pam__edit + is defined and result_pam__edit.changed) + when: + - '"pam" in ansible_facts.packages' + - result_pam_file_present.stat.exists + tags: + - CCE-85872-0 + - DISA-STIG-RHEL-08-020101 + - accounts_password_pam_pwquality_system_auth + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if rpm --quiet -q pam; then + +if [ -e "/etc/pam.d/system-auth" ] ; then + PAM_FILE_PATH="/etc/pam.d/system-auth" + if [ -f /usr/bin/authselect ]; then + if ! authselect check; then + echo " + authselect integrity check failed. Remediation aborted! + This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact. + It is not recommended to manually edit the PAM files when authselect tool is available. + In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended." + exit 1 + fi + CURRENT_PROFILE=$(authselect current -r | awk '{ print $1 }') + # If not already in use, a custom profile is created preserving the enabled features. + if [[ ! $CURRENT_PROFILE == custom/* ]]; then + ENABLED_FEATURES=$(authselect current | tail -n+3 | awk '{ print $2 }') + authselect create-profile hardening -b $CURRENT_PROFILE + CURRENT_PROFILE="custom/hardening" + + authselect apply-changes -b --backup=before-hardening-custom-profile + authselect select $CURRENT_PROFILE + for feature in $ENABLED_FEATURES; do + authselect enable-feature $feature; + done + + authselect apply-changes -b --backup=after-hardening-custom-profile + fi + PAM_FILE_NAME=$(basename "/etc/pam.d/system-auth") + PAM_FILE_PATH="/etc/authselect/$CURRENT_PROFILE/$PAM_FILE_NAME" + + authselect apply-changes -b + fi + if ! grep -qP '^\s*password\s+'"requisite"'\s+pam_pwquality.so\s*.*' "$PAM_FILE_PATH"; then + # Line matching group + control + module was not found. Check group + module. + if [ "$(grep -cP '^\s*password\s+.*\s+pam_pwquality.so\s*' "$PAM_FILE_PATH")" -eq 1 ]; then + # The control is updated only if one single line matches. + sed -i -E --follow-symlinks 's/^(\s*password\s+).*(\bpam_pwquality.so.*)/\1'"requisite"' \2/' "$PAM_FILE_PATH" + else + LAST_MATCH_LINE=$(grep -nP "^account.*required.*pam_permit\.so" "$PAM_FILE_PATH" | tail -n 1 | cut -d: -f 1) + if [ ! -z $LAST_MATCH_LINE ]; then + sed -i --follow-symlinks $LAST_MATCH_LINE' a password '"requisite"' pam_pwquality.so' "$PAM_FILE_PATH" + else + echo 'password '"requisite"' pam_pwquality.so' >> "$PAM_FILE_PATH" + fi + fi +fi + if [ -f /usr/bin/authselect ]; then + + authselect apply-changes -b + fi +else + echo "/etc/pam.d/system-auth was not found" >&2 +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure PAM Enforces Password Requirements - Authentication Retry Prompts Permitted Per-Session + To configure the number of retry prompts that are permitted per-session: + +Edit the /etc/security/pwquality.conf to include + +retry=, or a lower value if site +policy is more restrictive. The DoD requirement is a maximum of 3 prompts +per session. + 1 + 11 + 12 + 15 + 16 + 3 + 5 + 9 + 5.5.3 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.03 + DSS06.10 + CCI-000192 + CCI-000366 + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.2 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.18.1.4 + A.7.1.1 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.2 + A.9.4.3 + CM-6(a) + AC-7(a) + IA-5(4) + PR.AC-1 + PR.AC-6 + PR.AC-7 + PR.IP-1 + FMT_MOF_EXT.1 + SRG-OS-000069-GPOS-00037 + SRG-OS-000480-GPOS-00227 + RHEL-08-020104 + 5.4.1 + SV-251716r833387_rule + Setting the password retry prompts that are permitted on a per-session basis to a low value +requires some software, such as SSH, to re-connect. This can slow down and +draw additional attention to some types of password-guessing attacks. Note that this +is different from account lockout, which is provided by the pam_faillock module. + + CCE-80664-6 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80664-6 + - CJIS-5.5.3 + - DISA-STIG-RHEL-08-020104 + - NIST-800-53-AC-7(a) + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(4) + - accounts_password_pam_retry + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed +- name: XCCDF Value var_password_pam_retry # promote to variable + set_fact: + var_password_pam_retry: !!str + tags: + - always + +- name: Ensure PAM variable retry is set accordingly + ansible.builtin.lineinfile: + create: true + dest: /etc/security/pwquality.conf + regexp: ^\s*retry + line: retry = {{ var_password_pam_retry }} + when: '"pam" in ansible_facts.packages' + tags: + - CCE-80664-6 + - CJIS-5.5.3 + - DISA-STIG-RHEL-08-020104 + - NIST-800-53-AC-7(a) + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(4) + - accounts_password_pam_retry + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts Permitted + Per-Session - Check if /etc/pam.d/password-auth file is present + ansible.builtin.stat: + path: /etc/pam.d/password-auth + register: result_pam_file_present + when: '"pam" in ansible_facts.packages' + tags: + - CCE-80664-6 + - CJIS-5.5.3 + - DISA-STIG-RHEL-08-020104 + - NIST-800-53-AC-7(a) + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(4) + - accounts_password_pam_retry + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts Permitted + Per-Session - Check the proper remediation for the system + block: + + - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts + Permitted Per-Session - Define the PAM file to be edited as a local fact + ansible.builtin.set_fact: + pam_file_path: /etc/pam.d/password-auth + + - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts + Permitted Per-Session - Check if system relies on authselect + ansible.builtin.stat: + path: /usr/bin/authselect + register: result_authselect_present + + - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts + Permitted Per-Session - Remediate using authselect + block: + + - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts + Permitted Per-Session - Check integrity of authselect current profile + ansible.builtin.command: + cmd: authselect check + register: result_authselect_check_cmd + changed_when: false + ignore_errors: true + + - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts + Permitted Per-Session - Informative message based on the authselect integrity + check result + ansible.builtin.assert: + that: + - result_authselect_check_cmd is success + fail_msg: + - authselect integrity check failed. Remediation aborted! + - This remediation could not be applied because an authselect profile was + not selected or the selected profile is not intact. + - It is not recommended to manually edit the PAM files when authselect tool + is available. + - In cases where the default authselect profile does not cover a specific + demand, a custom authselect profile is recommended. + success_msg: + - authselect integrity check passed + + - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts + Permitted Per-Session - Get authselect current profile + ansible.builtin.shell: + cmd: authselect current -r | awk '{ print $1 }' + register: result_authselect_profile + changed_when: false + when: + - result_authselect_check_cmd is success + + - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts + Permitted Per-Session - Define the current authselect profile as a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: '{{ result_authselect_profile.stdout }}' + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is match("custom/") + + - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts + Permitted Per-Session - Define the new authselect custom profile as a local + fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: custom/hardening + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is not match("custom/") + + - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts + Permitted Per-Session - Get authselect current features to also enable them + in the custom profile + ansible.builtin.shell: + cmd: authselect current | tail -n+3 | awk '{ print $2 }' + register: result_authselect_features + changed_when: false + when: + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + + - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts + Permitted Per-Session - Check if any custom profile with the same name was + already created + ansible.builtin.stat: + path: /etc/authselect/{{ authselect_custom_profile }} + register: result_authselect_custom_profile_present + changed_when: false + when: + - authselect_current_profile is not match("custom/") + + - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts + Permitted Per-Session - Create an authselect custom profile based on the current + profile + ansible.builtin.command: + cmd: authselect create-profile hardening -b {{ authselect_current_profile + }} + when: + - result_authselect_check_cmd is success + - authselect_current_profile is not match("custom/") + - not result_authselect_custom_profile_present.stat.exists + + - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts + Permitted Per-Session - Ensure authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=before-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts + Permitted Per-Session - Ensure the authselect custom profile is selected + ansible.builtin.command: + cmd: authselect select {{ authselect_custom_profile }} + register: result_pam_authselect_select_profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts + Permitted Per-Session - Restore the authselect features in the custom profile + ansible.builtin.command: + cmd: authselect enable-feature {{ item }} + loop: '{{ result_authselect_features.stdout_lines }}' + register: result_pam_authselect_restore_features + when: + - result_authselect_profile is not skipped + - result_authselect_features is not skipped + - result_pam_authselect_select_profile is not skipped + + - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts + Permitted Per-Session - Ensure authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=after-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - result_pam_authselect_restore_features is not skipped + + - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts + Permitted Per-Session - Change the PAM file to be edited according to the + custom authselect profile + ansible.builtin.set_fact: + pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path + | basename }} + when: + - result_authselect_present.stat.exists + + - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts + Permitted Per-Session - Ensure the "retry" option from "pam_pwquality.so" is + not present in {{ pam_file_path }} + ansible.builtin.replace: + dest: '{{ pam_file_path }}' + regexp: (.*password.*.*.*pam_pwquality.so.*)\bretry\b=?[0-9a-zA-Z]*(.*) + replace: \1\2 + register: result_pam_option_removal + + - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts + Permitted Per-Session - Ensure authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: + - result_authselect_present.stat.exists + - result_pam_option_removal is changed + when: + - '"pam" in ansible_facts.packages' + - result_pam_file_present.stat.exists + tags: + - CCE-80664-6 + - CJIS-5.5.3 + - DISA-STIG-RHEL-08-020104 + - NIST-800-53-AC-7(a) + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(4) + - accounts_password_pam_retry + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts Permitted + Per-Session - Check if /etc/pam.d/system-auth file is present + ansible.builtin.stat: + path: /etc/pam.d/system-auth + register: result_pam_file_present + when: '"pam" in ansible_facts.packages' + tags: + - CCE-80664-6 + - CJIS-5.5.3 + - DISA-STIG-RHEL-08-020104 + - NIST-800-53-AC-7(a) + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(4) + - accounts_password_pam_retry + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts Permitted + Per-Session - Check the proper remediation for the system + block: + + - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts + Permitted Per-Session - Define the PAM file to be edited as a local fact + ansible.builtin.set_fact: + pam_file_path: /etc/pam.d/system-auth + + - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts + Permitted Per-Session - Check if system relies on authselect + ansible.builtin.stat: + path: /usr/bin/authselect + register: result_authselect_present + + - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts + Permitted Per-Session - Remediate using authselect + block: + + - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts + Permitted Per-Session - Check integrity of authselect current profile + ansible.builtin.command: + cmd: authselect check + register: result_authselect_check_cmd + changed_when: false + ignore_errors: true + + - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts + Permitted Per-Session - Informative message based on the authselect integrity + check result + ansible.builtin.assert: + that: + - result_authselect_check_cmd is success + fail_msg: + - authselect integrity check failed. Remediation aborted! + - This remediation could not be applied because an authselect profile was + not selected or the selected profile is not intact. + - It is not recommended to manually edit the PAM files when authselect tool + is available. + - In cases where the default authselect profile does not cover a specific + demand, a custom authselect profile is recommended. + success_msg: + - authselect integrity check passed + + - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts + Permitted Per-Session - Get authselect current profile + ansible.builtin.shell: + cmd: authselect current -r | awk '{ print $1 }' + register: result_authselect_profile + changed_when: false + when: + - result_authselect_check_cmd is success + + - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts + Permitted Per-Session - Define the current authselect profile as a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: '{{ result_authselect_profile.stdout }}' + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is match("custom/") + + - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts + Permitted Per-Session - Define the new authselect custom profile as a local + fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: custom/hardening + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is not match("custom/") + + - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts + Permitted Per-Session - Get authselect current features to also enable them + in the custom profile + ansible.builtin.shell: + cmd: authselect current | tail -n+3 | awk '{ print $2 }' + register: result_authselect_features + changed_when: false + when: + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + + - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts + Permitted Per-Session - Check if any custom profile with the same name was + already created + ansible.builtin.stat: + path: /etc/authselect/{{ authselect_custom_profile }} + register: result_authselect_custom_profile_present + changed_when: false + when: + - authselect_current_profile is not match("custom/") + + - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts + Permitted Per-Session - Create an authselect custom profile based on the current + profile + ansible.builtin.command: + cmd: authselect create-profile hardening -b {{ authselect_current_profile + }} + when: + - result_authselect_check_cmd is success + - authselect_current_profile is not match("custom/") + - not result_authselect_custom_profile_present.stat.exists + + - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts + Permitted Per-Session - Ensure authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=before-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts + Permitted Per-Session - Ensure the authselect custom profile is selected + ansible.builtin.command: + cmd: authselect select {{ authselect_custom_profile }} + register: result_pam_authselect_select_profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts + Permitted Per-Session - Restore the authselect features in the custom profile + ansible.builtin.command: + cmd: authselect enable-feature {{ item }} + loop: '{{ result_authselect_features.stdout_lines }}' + register: result_pam_authselect_restore_features + when: + - result_authselect_profile is not skipped + - result_authselect_features is not skipped + - result_pam_authselect_select_profile is not skipped + + - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts + Permitted Per-Session - Ensure authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=after-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - result_pam_authselect_restore_features is not skipped + + - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts + Permitted Per-Session - Change the PAM file to be edited according to the + custom authselect profile + ansible.builtin.set_fact: + pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path + | basename }} + when: + - result_authselect_present.stat.exists + + - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts + Permitted Per-Session - Ensure the "retry" option from "pam_pwquality.so" is + not present in {{ pam_file_path }} + ansible.builtin.replace: + dest: '{{ pam_file_path }}' + regexp: (.*password.*.*.*pam_pwquality.so.*)\bretry\b=?[0-9a-zA-Z]*(.*) + replace: \1\2 + register: result_pam_option_removal + + - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts + Permitted Per-Session - Ensure authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: + - result_authselect_present.stat.exists + - result_pam_option_removal is changed + when: + - '"pam" in ansible_facts.packages' + - result_pam_file_present.stat.exists + tags: + - CCE-80664-6 + - CJIS-5.5.3 + - DISA-STIG-RHEL-08-020104 + - NIST-800-53-AC-7(a) + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(4) + - accounts_password_pam_retry + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if rpm --quiet -q pam; then + +var_password_pam_retry='' + + +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/security/pwquality.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^retry") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$var_password_pam_retry" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^retry\\>" "/etc/security/pwquality.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^retry\\>.*/$escaped_formatted_output/gi" "/etc/security/pwquality.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-80664-6" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/security/pwquality.conf" >> "/etc/security/pwquality.conf" + printf '%s\n' "$formatted_output" >> "/etc/security/pwquality.conf" +fi + + if [ -e "/etc/pam.d/password-auth" ] ; then + PAM_FILE_PATH="/etc/pam.d/password-auth" + if [ -f /usr/bin/authselect ]; then + if ! authselect check; then + echo " + authselect integrity check failed. Remediation aborted! + This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact. + It is not recommended to manually edit the PAM files when authselect tool is available. + In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended." + exit 1 + fi + CURRENT_PROFILE=$(authselect current -r | awk '{ print $1 }') + # If not already in use, a custom profile is created preserving the enabled features. + if [[ ! $CURRENT_PROFILE == custom/* ]]; then + ENABLED_FEATURES=$(authselect current | tail -n+3 | awk '{ print $2 }') + authselect create-profile hardening -b $CURRENT_PROFILE + CURRENT_PROFILE="custom/hardening" + + authselect apply-changes -b --backup=before-hardening-custom-profile + authselect select $CURRENT_PROFILE + for feature in $ENABLED_FEATURES; do + authselect enable-feature $feature; + done + + authselect apply-changes -b --backup=after-hardening-custom-profile + fi + PAM_FILE_NAME=$(basename "/etc/pam.d/password-auth") + PAM_FILE_PATH="/etc/authselect/$CURRENT_PROFILE/$PAM_FILE_NAME" + + authselect apply-changes -b + fi + +if grep -qP '^\s*password\s+'".*"'\s+pam_pwquality.so\s.*\bretry\b' "$PAM_FILE_PATH"; then + sed -i -E --follow-symlinks 's/(.*password.*'".*"'.*pam_pwquality.so.*)\sretry=?[[:alnum:]]*(.*)/\1\2/g' "$PAM_FILE_PATH" +fi + if [ -f /usr/bin/authselect ]; then + + authselect apply-changes -b + fi +else + echo "/etc/pam.d/password-auth was not found" >&2 +fi + + if [ -e "/etc/pam.d/system-auth" ] ; then + PAM_FILE_PATH="/etc/pam.d/system-auth" + if [ -f /usr/bin/authselect ]; then + if ! authselect check; then + echo " + authselect integrity check failed. Remediation aborted! + This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact. + It is not recommended to manually edit the PAM files when authselect tool is available. + In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended." + exit 1 + fi + CURRENT_PROFILE=$(authselect current -r | awk '{ print $1 }') + # If not already in use, a custom profile is created preserving the enabled features. + if [[ ! $CURRENT_PROFILE == custom/* ]]; then + ENABLED_FEATURES=$(authselect current | tail -n+3 | awk '{ print $2 }') + authselect create-profile hardening -b $CURRENT_PROFILE + CURRENT_PROFILE="custom/hardening" + + authselect apply-changes -b --backup=before-hardening-custom-profile + authselect select $CURRENT_PROFILE + for feature in $ENABLED_FEATURES; do + authselect enable-feature $feature; + done + + authselect apply-changes -b --backup=after-hardening-custom-profile + fi + PAM_FILE_NAME=$(basename "/etc/pam.d/system-auth") + PAM_FILE_PATH="/etc/authselect/$CURRENT_PROFILE/$PAM_FILE_NAME" + + authselect apply-changes -b + fi + +if grep -qP '^\s*password\s+'".*"'\s+pam_pwquality.so\s.*\bretry\b' "$PAM_FILE_PATH"; then + sed -i -E --follow-symlinks 's/(.*password.*'".*"'.*pam_pwquality.so.*)\sretry=?[[:alnum:]]*(.*)/\1\2/g' "$PAM_FILE_PATH" +fi + if [ -f /usr/bin/authselect ]; then + + authselect apply-changes -b + fi +else + echo "/etc/pam.d/system-auth was not found" >&2 +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Ensure PAM Enforces Password Requirements - Minimum Uppercase Characters + The pam_pwquality module's ucredit= parameter controls requirements for +usage of uppercase letters in a password. When set to a negative number, any password will be required to +contain that many uppercase characters. When set to a positive number, pam_pwquality will grant +1 additional +length credit for each uppercase character. Modify the ucredit setting in +/etc/security/pwquality.conf to require the use of an uppercase character in passwords. + BP28(R18) + 1 + 12 + 15 + 16 + 5 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.03 + DSS06.10 + CCI-000192 + CCI-000193 + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.2 + 4.3.3.7.4 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + 0421 + 0422 + 0431 + 0974 + 1173 + 1401 + 1504 + 1505 + 1546 + 1557 + 1558 + 1559 + 1560 + 1561 + A.18.1.4 + A.7.1.1 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.2 + A.9.4.3 + IA-5(c) + IA-5(1)(a) + CM-6(a) + IA-5(4) + PR.AC-1 + PR.AC-6 + PR.AC-7 + FMT_SMF_EXT.1 + Req-8.2.3 + SRG-OS-000069-GPOS-00037 + SRG-OS-000070-GPOS-00038 + SRG-OS-000069-VMM-000360 + RHEL-08-020110 + SV-230357r833313_rule + Use of a complex password helps to increase the time and resources required to compromise the password. +Password complexity, or strength, is a measure of the effectiveness of a password in resisting attempts +at guessing and brute-force attacks. + +Password complexity is one factor of several that determines how long it takes to crack a password. The more +complex the password, the greater the number of possible combinations that need to be tested before +the password is compromised. + + CCE-80665-3 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80665-3 + - DISA-STIG-RHEL-08-020110 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(a) + - NIST-800-53-IA-5(4) + - NIST-800-53-IA-5(c) + - PCI-DSS-Req-8.2.3 + - accounts_password_pam_ucredit + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy +- name: XCCDF Value var_password_pam_ucredit # promote to variable + set_fact: + var_password_pam_ucredit: !!str + tags: + - always + +- name: Ensure PAM variable ucredit is set accordingly + lineinfile: + create: true + dest: /etc/security/pwquality.conf + regexp: ^#?\s*ucredit + line: ucredit = {{ var_password_pam_ucredit }} + when: '"pam" in ansible_facts.packages' + tags: + - CCE-80665-3 + - DISA-STIG-RHEL-08-020110 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(a) + - NIST-800-53-IA-5(4) + - NIST-800-53-IA-5(c) + - PCI-DSS-Req-8.2.3 + - accounts_password_pam_ucredit + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q pam; then + +var_password_pam_ucredit='' + + +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/security/pwquality.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^ucredit") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$var_password_pam_ucredit" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^ucredit\\>" "/etc/security/pwquality.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^ucredit\\>.*/$escaped_formatted_output/gi" "/etc/security/pwquality.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-80665-3" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/security/pwquality.conf" >> "/etc/security/pwquality.conf" + printf '%s\n' "$formatted_output" >> "/etc/security/pwquality.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + + + Set Password Hashing Algorithm + The system's default algorithm for storing password hashes in +/etc/shadow is SHA-512. This can be configured in several +locations. + + Set Password Hashing Algorithm in /etc/libuser.conf + In /etc/libuser.conf, add or correct the following line in its +[defaults] section to ensure the system will use the SHA-512 +algorithm for password hashing: +crypt_style = sha512 + 1 + 12 + 15 + 16 + 5 + 5.6.2.2 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.03 + DSS06.10 + 3.13.11 + CCI-000196 + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.2 + 4.3.3.7.4 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + 0418 + 1055 + 1402 + A.18.1.4 + A.7.1.1 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.2 + A.9.4.3 + IA-5(c) + IA-5(1)(c) + CM-6(a) + PR.AC-1 + PR.AC-6 + PR.AC-7 + Req-8.2.1 + SRG-OS-000073-GPOS-00041 + SRG-OS-000480-VMM-002000 + Passwords need to be protected at all times, and encryption is the standard +method for protecting passwords. If passwords are not encrypted, they can +be plainly read (i.e., clear text) and easily compromised. Passwords that +are encrypted with a weak algorithm are no more protected than if they are +kepy in plain text. + +This setting ensures user and group account administration utilities are +configured to store only encrypted representations of passwords. +Additionally, the crypt_style configuration option ensures the use +of a strong hashing algorithm that makes password cracking attacks more +difficult. + + CCE-80891-5 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80891-5 + - CJIS-5.6.2.2 + - NIST-800-171-3.13.11 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(c) + - NIST-800-53-IA-5(c) + - PCI-DSS-Req-8.2.1 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - set_password_hashing_algorithm_libuserconf + +- name: Set Password Hashing Algorithm in /etc/libuser.conf + lineinfile: + dest: /etc/libuser.conf + insertafter: ^\s*\[defaults] + regexp: ^#?crypt_style + line: crypt_style = sha512 + state: present + create: true + when: '"libuser" in ansible_facts.packages' + tags: + - CCE-80891-5 + - CJIS-5.6.2.2 + - NIST-800-171-3.13.11 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(c) + - NIST-800-53-IA-5(c) + - PCI-DSS-Req-8.2.1 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - set_password_hashing_algorithm_libuserconf + + # Remediation is applicable only in certain platforms +if rpm --quiet -q libuser; then + +LIBUSER_CONF="/etc/libuser.conf" +CRYPT_STYLE_REGEX='[[:space:]]*\[defaults](.*(\n)+)+?[[:space:]]*crypt_style[[:space:]]*' + +# Try find crypt_style in [defaults] section. If it is here, then change algorithm to sha512. +# If it isn't here, then add it to [defaults] section. +if grep -qzosP $CRYPT_STYLE_REGEX $LIBUSER_CONF ; then + sed -i "s/\(crypt_style[[:space:]]*=[[:space:]]*\).*/\1sha512/g" $LIBUSER_CONF +elif grep -qs "\[defaults]" $LIBUSER_CONF ; then + sed -i "/[[:space:]]*\[defaults]/a crypt_style = sha512" $LIBUSER_CONF +else + echo -e "[defaults]\ncrypt_style = sha512" >> $LIBUSER_CONF +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Set Password Hashing Algorithm in /etc/login.defs + In /etc/login.defs, add or correct the following line to ensure +the system will use SHA-512 as the hashing algorithm: +ENCRYPT_METHOD SHA512 + BP28(R32) + 1 + 12 + 15 + 16 + 5 + 5.6.2.2 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.03 + DSS06.10 + 3.13.11 + CCI-000196 + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.2 + 4.3.3.7.4 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + 0418 + 1055 + 1402 + A.18.1.4 + A.7.1.1 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.2 + A.9.4.3 + IA-5(c) + IA-5(1)(c) + CM-6(a) + PR.AC-1 + PR.AC-6 + PR.AC-7 + Req-8.2.1 + SRG-OS-000073-GPOS-00041 + RHEL-08-010110 + SV-230231r627750_rule + Passwords need to be protected at all times, and encryption is the standard method for protecting passwords. +If passwords are not encrypted, they can be plainly read (i.e., clear text) and easily compromised. Passwords +that are encrypted with a weak algorithm are no more protected than if they are kept in plain text. + +Using a stronger hashing algorithm makes password cracking attacks more difficult. + + CCE-80892-3 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80892-3 + - CJIS-5.6.2.2 + - DISA-STIG-RHEL-08-010110 + - NIST-800-171-3.13.11 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(c) + - NIST-800-53-IA-5(c) + - PCI-DSS-Req-8.2.1 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - set_password_hashing_algorithm_logindefs +- name: XCCDF Value var_password_hashing_algorithm # promote to variable + set_fact: + var_password_hashing_algorithm: !!str + tags: + - always + +- name: Set Password Hashing Algorithm in /etc/login.defs + lineinfile: + dest: /etc/login.defs + regexp: ^#?ENCRYPT_METHOD + line: ENCRYPT_METHOD {{ var_password_hashing_algorithm }} + state: present + create: true + when: '"shadow-utils" in ansible_facts.packages' + tags: + - CCE-80892-3 + - CJIS-5.6.2.2 + - DISA-STIG-RHEL-08-010110 + - NIST-800-171-3.13.11 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(c) + - NIST-800-53-IA-5(c) + - PCI-DSS-Req-8.2.1 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - set_password_hashing_algorithm_logindefs + + # Remediation is applicable only in certain platforms +if rpm --quiet -q shadow-utils; then + +var_password_hashing_algorithm='' + + +if grep --silent ^ENCRYPT_METHOD /etc/login.defs ; then + sed -i "s/^ENCRYPT_METHOD .*/ENCRYPT_METHOD $var_password_hashing_algorithm/g" /etc/login.defs +else + echo "" >> /etc/login.defs + echo "ENCRYPT_METHOD $var_password_hashing_algorithm" >> /etc/login.defs +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Set PAM''s Password Hashing Algorithm - password-auth + The PAM system service can be configured to only store encrypted +representations of passwords. In +/etc/pam.d/password-auth, +the +password section of the file controls which PAM modules execute +during a password change. Set the pam_unix.so module in the +password section to include the argument sha512, as shown +below: + +password sufficient pam_unix.so sha512 other arguments... + +This will help ensure when local users change their passwords, hashes for +the new passwords will be generated using the SHA-512 algorithm. This is +the default. + BP28(R32) + 1 + 12 + 15 + 16 + 5 + 5.6.2.2 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.03 + DSS06.10 + 3.13.11 + CCI-000196 + CCI-000803 + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.2 + 4.3.3.7.4 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + 0418 + 1055 + 1402 + A.18.1.4 + A.7.1.1 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.2 + A.9.4.3 + IA-5(c) + IA-5(1)(c) + CM-6(a) + PR.AC-1 + PR.AC-6 + PR.AC-7 + Req-8.2.1 + SRG-OS-000073-GPOS-00041 + SRG-OS-000120-GPOS-00061 + SRG-OS-000480-VMM-002000 + RHEL-08-010160 + 5.4.4 + SV-230237r809276_rule + Passwords need to be protected at all times, and encryption is the standard +method for protecting passwords. If passwords are not encrypted, they can +be plainly read (i.e., clear text) and easily compromised. Passwords that +are encrypted with a weak algorithm are no more protected than if they are +kepy in plain text. + +This setting ensures user and group account administration utilities are +configured to store only encrypted representations of passwords. +Additionally, the crypt_style configuration option ensures the use +of a strong hashing algorithm that makes password cracking attacks more +difficult. + + CCE-85945-4 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-85945-4 + - CJIS-5.6.2.2 + - DISA-STIG-RHEL-08-010160 + - NIST-800-171-3.13.11 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(c) + - NIST-800-53-IA-5(c) + - PCI-DSS-Req-8.2.1 + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - set_password_hashing_algorithm_passwordauth + +- name: Set PAM's Password Hashing Algorithm - password-auth - Check if /etc/pam.d/password-auth + file is present + ansible.builtin.stat: + path: /etc/pam.d/password-auth + register: result_pam_file_present + when: '"pam" in ansible_facts.packages' + tags: + - CCE-85945-4 + - CJIS-5.6.2.2 + - DISA-STIG-RHEL-08-010160 + - NIST-800-171-3.13.11 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(c) + - NIST-800-53-IA-5(c) + - PCI-DSS-Req-8.2.1 + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - set_password_hashing_algorithm_passwordauth + +- name: Set PAM's Password Hashing Algorithm - password-auth - Check the proper remediation + for the system + block: + + - name: Set PAM's Password Hashing Algorithm - password-auth - Define the PAM file + to be edited as a local fact + ansible.builtin.set_fact: + pam_file_path: /etc/pam.d/password-auth + + - name: Set PAM's Password Hashing Algorithm - password-auth - Check if system relies + on authselect + ansible.builtin.stat: + path: /usr/bin/authselect + register: result_authselect_present + + - name: Set PAM's Password Hashing Algorithm - password-auth - Remediate using authselect + block: + + - name: Set PAM's Password Hashing Algorithm - password-auth - Check integrity + of authselect current profile + ansible.builtin.command: + cmd: authselect check + register: result_authselect_check_cmd + changed_when: false + ignore_errors: true + + - name: Set PAM's Password Hashing Algorithm - password-auth - Informative message + based on the authselect integrity check result + ansible.builtin.assert: + that: + - result_authselect_check_cmd is success + fail_msg: + - authselect integrity check failed. Remediation aborted! + - This remediation could not be applied because an authselect profile was + not selected or the selected profile is not intact. + - It is not recommended to manually edit the PAM files when authselect tool + is available. + - In cases where the default authselect profile does not cover a specific + demand, a custom authselect profile is recommended. + success_msg: + - authselect integrity check passed + + - name: Set PAM's Password Hashing Algorithm - password-auth - Get authselect + current profile + ansible.builtin.shell: + cmd: authselect current -r | awk '{ print $1 }' + register: result_authselect_profile + changed_when: false + when: + - result_authselect_check_cmd is success + + - name: Set PAM's Password Hashing Algorithm - password-auth - Define the current + authselect profile as a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: '{{ result_authselect_profile.stdout }}' + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is match("custom/") + + - name: Set PAM's Password Hashing Algorithm - password-auth - Define the new + authselect custom profile as a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: custom/hardening + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is not match("custom/") + + - name: Set PAM's Password Hashing Algorithm - password-auth - Get authselect + current features to also enable them in the custom profile + ansible.builtin.shell: + cmd: authselect current | tail -n+3 | awk '{ print $2 }' + register: result_authselect_features + changed_when: false + when: + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + + - name: Set PAM's Password Hashing Algorithm - password-auth - Check if any custom + profile with the same name was already created + ansible.builtin.stat: + path: /etc/authselect/{{ authselect_custom_profile }} + register: result_authselect_custom_profile_present + changed_when: false + when: + - authselect_current_profile is not match("custom/") + + - name: Set PAM's Password Hashing Algorithm - password-auth - Create an authselect + custom profile based on the current profile + ansible.builtin.command: + cmd: authselect create-profile hardening -b {{ authselect_current_profile + }} + when: + - result_authselect_check_cmd is success + - authselect_current_profile is not match("custom/") + - not result_authselect_custom_profile_present.stat.exists + + - name: Set PAM's Password Hashing Algorithm - password-auth - Ensure authselect + changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=before-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Set PAM's Password Hashing Algorithm - password-auth - Ensure the authselect + custom profile is selected + ansible.builtin.command: + cmd: authselect select {{ authselect_custom_profile }} + register: result_pam_authselect_select_profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Set PAM's Password Hashing Algorithm - password-auth - Restore the authselect + features in the custom profile + ansible.builtin.command: + cmd: authselect enable-feature {{ item }} + loop: '{{ result_authselect_features.stdout_lines }}' + register: result_pam_authselect_restore_features + when: + - result_authselect_profile is not skipped + - result_authselect_features is not skipped + - result_pam_authselect_select_profile is not skipped + + - name: Set PAM's Password Hashing Algorithm - password-auth - Ensure authselect + changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=after-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - result_pam_authselect_restore_features is not skipped + + - name: Set PAM's Password Hashing Algorithm - password-auth - Change the PAM + file to be edited according to the custom authselect profile + ansible.builtin.set_fact: + pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path + | basename }} + when: + - result_authselect_present.stat.exists + + - name: Set PAM's Password Hashing Algorithm - password-auth - Check if expected + PAM module line is present in {{ pam_file_path }} + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + regexp: ^\s*password\s+sufficient\s+pam_unix.so\s*.* + state: absent + check_mode: true + changed_when: false + register: result_pam_line_present + + - name: Set PAM's Password Hashing Algorithm - password-auth - Include or update + the PAM module line in {{ pam_file_path }} + block: + + - name: Set PAM's Password Hashing Algorithm - password-auth - Check if required + PAM module line is present in {{ pam_file_path }} with different control + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + regexp: ^\s*password\s+.*\s+pam_unix.so\s* + state: absent + check_mode: true + changed_when: false + register: result_pam_line_other_control_present + + - name: Set PAM's Password Hashing Algorithm - password-auth - Ensure the correct + control for the required PAM module line in {{ pam_file_path }} + ansible.builtin.replace: + dest: '{{ pam_file_path }}' + regexp: ^(\s*password\s+).*(\bpam_unix.so.*) + replace: \1sufficient \2 + register: result_pam_module_edit + when: + - result_pam_line_other_control_present.found == 1 + + - name: Set PAM's Password Hashing Algorithm - password-auth - Ensure the required + PAM module line is included in {{ pam_file_path }} + ansible.builtin.lineinfile: + dest: '{{ pam_file_path }}' + line: password sufficient pam_unix.so + register: result_pam_module_add + when: + - result_pam_line_other_control_present.found == 0 or result_pam_line_other_control_present.found + > 1 + + - name: Set PAM's Password Hashing Algorithm - password-auth - Ensure authselect + changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: | + result_authselect_present is defined and result_authselect_present.stat.exists and ((result_pam_module_add is defined and result_pam_module_add.changed) or (result_pam_module_edit is defined and result_pam_module_edit.changed)) + when: + - result_pam_line_present.found is defined + - result_pam_line_present.found == 0 + + - name: Set PAM's Password Hashing Algorithm - password-auth - Check if the required + PAM module option is present in {{ pam_file_path }} + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + regexp: ^\s*password\s+sufficient\s+pam_unix.so\s*.*\ssha512\b + state: absent + check_mode: true + changed_when: false + register: result_pam_module_sha512_option_present + + - name: Set PAM's Password Hashing Algorithm - password-auth - Ensure the "sha512" + PAM option for "pam_unix.so" is included in {{ pam_file_path }} + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + backrefs: true + regexp: ^(\s*password\s+sufficient\s+pam_unix.so.*) + line: \1 sha512 + state: present + register: result_pam_sha512_add + when: + - result_pam_module_sha512_option_present.found == 0 + + - name: Set PAM's Password Hashing Algorithm - password-auth - Ensure authselect + changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: + - result_authselect_present.stat.exists + - (result_pam_sha512_add is defined and result_pam_sha512_add.changed) or (result_pam_sha512_edit + is defined and result_pam_sha512_edit.changed) + when: + - '"pam" in ansible_facts.packages' + - result_pam_file_present.stat.exists + tags: + - CCE-85945-4 + - CJIS-5.6.2.2 + - DISA-STIG-RHEL-08-010160 + - NIST-800-171-3.13.11 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(c) + - NIST-800-53-IA-5(c) + - PCI-DSS-Req-8.2.1 + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - set_password_hashing_algorithm_passwordauth + + # Remediation is applicable only in certain platforms +if rpm --quiet -q pam; then + +if [ -e "/etc/pam.d/password-auth" ] ; then + PAM_FILE_PATH="/etc/pam.d/password-auth" + if [ -f /usr/bin/authselect ]; then + if ! authselect check; then + echo " + authselect integrity check failed. Remediation aborted! + This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact. + It is not recommended to manually edit the PAM files when authselect tool is available. + In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended." + exit 1 + fi + CURRENT_PROFILE=$(authselect current -r | awk '{ print $1 }') + # If not already in use, a custom profile is created preserving the enabled features. + if [[ ! $CURRENT_PROFILE == custom/* ]]; then + ENABLED_FEATURES=$(authselect current | tail -n+3 | awk '{ print $2 }') + authselect create-profile hardening -b $CURRENT_PROFILE + CURRENT_PROFILE="custom/hardening" + + authselect apply-changes -b --backup=before-hardening-custom-profile + authselect select $CURRENT_PROFILE + for feature in $ENABLED_FEATURES; do + authselect enable-feature $feature; + done + + authselect apply-changes -b --backup=after-hardening-custom-profile + fi + PAM_FILE_NAME=$(basename "/etc/pam.d/password-auth") + PAM_FILE_PATH="/etc/authselect/$CURRENT_PROFILE/$PAM_FILE_NAME" + + authselect apply-changes -b + fi + if ! grep -qP '^\s*password\s+'"sufficient"'\s+pam_unix.so\s*.*' "$PAM_FILE_PATH"; then + # Line matching group + control + module was not found. Check group + module. + if [ "$(grep -cP '^\s*password\s+.*\s+pam_unix.so\s*' "$PAM_FILE_PATH")" -eq 1 ]; then + # The control is updated only if one single line matches. + sed -i -E --follow-symlinks 's/^(\s*password\s+).*(\bpam_unix.so.*)/\1'"sufficient"' \2/' "$PAM_FILE_PATH" + else + echo 'password '"sufficient"' pam_unix.so' >> "$PAM_FILE_PATH" + fi + fi + # Check the option + if ! grep -qP '^\s*password\s+'"sufficient"'\s+pam_unix.so\s*.*\ssha512\b' "$PAM_FILE_PATH"; then + sed -i -E --follow-symlinks '/\s*password\s+'"sufficient"'\s+pam_unix.so.*/ s/$/ sha512/' "$PAM_FILE_PATH" + fi + if [ -f /usr/bin/authselect ]; then + + authselect apply-changes -b + fi +else + echo "/etc/pam.d/password-auth was not found" >&2 +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Set PAM''s Password Hashing Algorithm + The PAM system service can be configured to only store encrypted +representations of passwords. In "/etc/pam.d/system-auth", the +password section of the file controls which PAM modules execute +during a password change. Set the pam_unix.so module in the +password section to include the argument sha512, as shown +below: + + +password sufficient pam_unix.so sha512 other arguments... + + +This will help ensure when local users change their passwords, hashes for +the new passwords will be generated using the SHA-512 algorithm. This is +the default. + BP28(R32) + 1 + 12 + 15 + 16 + 5 + 5.6.2.2 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.03 + DSS06.10 + 3.13.11 + CCI-000196 + CCI-000803 + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.2 + 4.3.3.7.4 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + 0418 + 1055 + 1402 + A.18.1.4 + A.7.1.1 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.2 + A.9.4.3 + IA-5(c) + IA-5(1)(c) + CM-6(a) + PR.AC-1 + PR.AC-6 + PR.AC-7 + Req-8.2.1 + SRG-OS-000073-GPOS-00041 + SRG-OS-000120-GPOS-00061 + SRG-OS-000480-VMM-002000 + RHEL-08-010159 + 5.4.4 + SV-244524r809331_rule + Passwords need to be protected at all times, and encryption is the standard +method for protecting passwords. If passwords are not encrypted, they can +be plainly read (i.e., clear text) and easily compromised. Passwords that +are encrypted with a weak algorithm are no more protected than if they are +kepy in plain text. + +This setting ensures user and group account administration utilities are +configured to store only encrypted representations of passwords. +Additionally, the crypt_style configuration option ensures the use +of a strong hashing algorithm that makes password cracking attacks more +difficult. + + CCE-80893-1 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80893-1 + - CJIS-5.6.2.2 + - DISA-STIG-RHEL-08-010159 + - NIST-800-171-3.13.11 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(c) + - NIST-800-53-IA-5(c) + - PCI-DSS-Req-8.2.1 + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - set_password_hashing_algorithm_systemauth + +- name: Set PAM's Password Hashing Algorithm - Check if /etc/pam.d/system-auth file + is present + ansible.builtin.stat: + path: /etc/pam.d/system-auth + register: result_pam_file_present + when: '"pam" in ansible_facts.packages' + tags: + - CCE-80893-1 + - CJIS-5.6.2.2 + - DISA-STIG-RHEL-08-010159 + - NIST-800-171-3.13.11 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(c) + - NIST-800-53-IA-5(c) + - PCI-DSS-Req-8.2.1 + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - set_password_hashing_algorithm_systemauth + +- name: Set PAM's Password Hashing Algorithm - Check the proper remediation for the + system + block: + + - name: Set PAM's Password Hashing Algorithm - Define the PAM file to be edited + as a local fact + ansible.builtin.set_fact: + pam_file_path: /etc/pam.d/system-auth + + - name: Set PAM's Password Hashing Algorithm - Check if system relies on authselect + ansible.builtin.stat: + path: /usr/bin/authselect + register: result_authselect_present + + - name: Set PAM's Password Hashing Algorithm - Remediate using authselect + block: + + - name: Set PAM's Password Hashing Algorithm - Check integrity of authselect current + profile + ansible.builtin.command: + cmd: authselect check + register: result_authselect_check_cmd + changed_when: false + ignore_errors: true + + - name: Set PAM's Password Hashing Algorithm - Informative message based on the + authselect integrity check result + ansible.builtin.assert: + that: + - result_authselect_check_cmd is success + fail_msg: + - authselect integrity check failed. Remediation aborted! + - This remediation could not be applied because an authselect profile was + not selected or the selected profile is not intact. + - It is not recommended to manually edit the PAM files when authselect tool + is available. + - In cases where the default authselect profile does not cover a specific + demand, a custom authselect profile is recommended. + success_msg: + - authselect integrity check passed + + - name: Set PAM's Password Hashing Algorithm - Get authselect current profile + ansible.builtin.shell: + cmd: authselect current -r | awk '{ print $1 }' + register: result_authselect_profile + changed_when: false + when: + - result_authselect_check_cmd is success + + - name: Set PAM's Password Hashing Algorithm - Define the current authselect profile + as a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: '{{ result_authselect_profile.stdout }}' + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is match("custom/") + + - name: Set PAM's Password Hashing Algorithm - Define the new authselect custom + profile as a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: custom/hardening + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is not match("custom/") + + - name: Set PAM's Password Hashing Algorithm - Get authselect current features + to also enable them in the custom profile + ansible.builtin.shell: + cmd: authselect current | tail -n+3 | awk '{ print $2 }' + register: result_authselect_features + changed_when: false + when: + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + + - name: Set PAM's Password Hashing Algorithm - Check if any custom profile with + the same name was already created + ansible.builtin.stat: + path: /etc/authselect/{{ authselect_custom_profile }} + register: result_authselect_custom_profile_present + changed_when: false + when: + - authselect_current_profile is not match("custom/") + + - name: Set PAM's Password Hashing Algorithm - Create an authselect custom profile + based on the current profile + ansible.builtin.command: + cmd: authselect create-profile hardening -b {{ authselect_current_profile + }} + when: + - result_authselect_check_cmd is success + - authselect_current_profile is not match("custom/") + - not result_authselect_custom_profile_present.stat.exists + + - name: Set PAM's Password Hashing Algorithm - Ensure authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=before-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Set PAM's Password Hashing Algorithm - Ensure the authselect custom profile + is selected + ansible.builtin.command: + cmd: authselect select {{ authselect_custom_profile }} + register: result_pam_authselect_select_profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Set PAM's Password Hashing Algorithm - Restore the authselect features + in the custom profile + ansible.builtin.command: + cmd: authselect enable-feature {{ item }} + loop: '{{ result_authselect_features.stdout_lines }}' + register: result_pam_authselect_restore_features + when: + - result_authselect_profile is not skipped + - result_authselect_features is not skipped + - result_pam_authselect_select_profile is not skipped + + - name: Set PAM's Password Hashing Algorithm - Ensure authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=after-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - result_pam_authselect_restore_features is not skipped + + - name: Set PAM's Password Hashing Algorithm - Change the PAM file to be edited + according to the custom authselect profile + ansible.builtin.set_fact: + pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path + | basename }} + when: + - result_authselect_present.stat.exists + + - name: Set PAM's Password Hashing Algorithm - Check if expected PAM module line + is present in {{ pam_file_path }} + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + regexp: ^\s*password\s+sufficient\s+pam_unix.so\s*.* + state: absent + check_mode: true + changed_when: false + register: result_pam_line_present + + - name: Set PAM's Password Hashing Algorithm - Include or update the PAM module + line in {{ pam_file_path }} + block: + + - name: Set PAM's Password Hashing Algorithm - Check if required PAM module line + is present in {{ pam_file_path }} with different control + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + regexp: ^\s*password\s+.*\s+pam_unix.so\s* + state: absent + check_mode: true + changed_when: false + register: result_pam_line_other_control_present + + - name: Set PAM's Password Hashing Algorithm - Ensure the correct control for + the required PAM module line in {{ pam_file_path }} + ansible.builtin.replace: + dest: '{{ pam_file_path }}' + regexp: ^(\s*password\s+).*(\bpam_unix.so.*) + replace: \1sufficient \2 + register: result_pam_module_edit + when: + - result_pam_line_other_control_present.found == 1 + + - name: Set PAM's Password Hashing Algorithm - Ensure the required PAM module + line is included in {{ pam_file_path }} + ansible.builtin.lineinfile: + dest: '{{ pam_file_path }}' + line: password sufficient pam_unix.so + register: result_pam_module_add + when: + - result_pam_line_other_control_present.found == 0 or result_pam_line_other_control_present.found + > 1 + + - name: Set PAM's Password Hashing Algorithm - Ensure authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: | + result_authselect_present is defined and result_authselect_present.stat.exists and ((result_pam_module_add is defined and result_pam_module_add.changed) or (result_pam_module_edit is defined and result_pam_module_edit.changed)) + when: + - result_pam_line_present.found is defined + - result_pam_line_present.found == 0 + + - name: Set PAM's Password Hashing Algorithm - Check if the required PAM module + option is present in {{ pam_file_path }} + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + regexp: ^\s*password\s+sufficient\s+pam_unix.so\s*.*\ssha512\b + state: absent + check_mode: true + changed_when: false + register: result_pam_module_sha512_option_present + + - name: Set PAM's Password Hashing Algorithm - Ensure the "sha512" PAM option for + "pam_unix.so" is included in {{ pam_file_path }} + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + backrefs: true + regexp: ^(\s*password\s+sufficient\s+pam_unix.so.*) + line: \1 sha512 + state: present + register: result_pam_sha512_add + when: + - result_pam_module_sha512_option_present.found == 0 + + - name: Set PAM's Password Hashing Algorithm - Ensure authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: + - result_authselect_present.stat.exists + - (result_pam_sha512_add is defined and result_pam_sha512_add.changed) or (result_pam_sha512_edit + is defined and result_pam_sha512_edit.changed) + when: + - '"pam" in ansible_facts.packages' + - result_pam_file_present.stat.exists + tags: + - CCE-80893-1 + - CJIS-5.6.2.2 + - DISA-STIG-RHEL-08-010159 + - NIST-800-171-3.13.11 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(c) + - NIST-800-53-IA-5(c) + - PCI-DSS-Req-8.2.1 + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - set_password_hashing_algorithm_systemauth + + # Remediation is applicable only in certain platforms +if rpm --quiet -q pam; then + +if [ -e "/etc/pam.d/system-auth" ] ; then + PAM_FILE_PATH="/etc/pam.d/system-auth" + if [ -f /usr/bin/authselect ]; then + if ! authselect check; then + echo " + authselect integrity check failed. Remediation aborted! + This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact. + It is not recommended to manually edit the PAM files when authselect tool is available. + In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended." + exit 1 + fi + CURRENT_PROFILE=$(authselect current -r | awk '{ print $1 }') + # If not already in use, a custom profile is created preserving the enabled features. + if [[ ! $CURRENT_PROFILE == custom/* ]]; then + ENABLED_FEATURES=$(authselect current | tail -n+3 | awk '{ print $2 }') + authselect create-profile hardening -b $CURRENT_PROFILE + CURRENT_PROFILE="custom/hardening" + + authselect apply-changes -b --backup=before-hardening-custom-profile + authselect select $CURRENT_PROFILE + for feature in $ENABLED_FEATURES; do + authselect enable-feature $feature; + done + + authselect apply-changes -b --backup=after-hardening-custom-profile + fi + PAM_FILE_NAME=$(basename "/etc/pam.d/system-auth") + PAM_FILE_PATH="/etc/authselect/$CURRENT_PROFILE/$PAM_FILE_NAME" + + authselect apply-changes -b + fi + if ! grep -qP '^\s*password\s+'"sufficient"'\s+pam_unix.so\s*.*' "$PAM_FILE_PATH"; then + # Line matching group + control + module was not found. Check group + module. + if [ "$(grep -cP '^\s*password\s+.*\s+pam_unix.so\s*' "$PAM_FILE_PATH")" -eq 1 ]; then + # The control is updated only if one single line matches. + sed -i -E --follow-symlinks 's/^(\s*password\s+).*(\bpam_unix.so.*)/\1'"sufficient"' \2/' "$PAM_FILE_PATH" + else + echo 'password '"sufficient"' pam_unix.so' >> "$PAM_FILE_PATH" + fi + fi + # Check the option + if ! grep -qP '^\s*password\s+'"sufficient"'\s+pam_unix.so\s*.*\ssha512\b' "$PAM_FILE_PATH"; then + sed -i -E --follow-symlinks '/\s*password\s+'"sufficient"'\s+pam_unix.so.*/ s/$/ sha512/' "$PAM_FILE_PATH" + fi + if [ -f /usr/bin/authselect ]; then + + authselect apply-changes -b + fi +else + echo "/etc/pam.d/system-auth was not found" >&2 +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Set Password Hashing Rounds in /etc/login.defs + In /etc/login.defs, ensure SHA_CRYPT_MIN_ROUNDS and +SHA_CRYPT_MAX_ROUNDS has the minimum value of 5000. +For example: +SHA_CRYPT_MIN_ROUNDS 5000 +SHA_CRYPT_MAX_ROUNDS 5000 +Notice that if neither are set, they already have the default value of 5000. +If either is set, they must have the minimum value of 5000. + CCI-000196 + CCI-000803 + SRG-OS-000073-GPOS-00041 + SRG-OS-000120-GPOS-00061 + RHEL-08-010130 + SV-230233r809273_rule + Passwords need to be protected at all times, and encryption is the standard +method for protecting passwords. If passwords are not encrypted, they can +be plainly read (i.e., clear text) and easily compromised. Passwords +that are encrypted with a weak algorithm are no more protected than if +they are kept in plain text. + +Using more hashing rounds makes password cracking attacks more difficult. + CCE-89707-4 + + + + + + + + + + + Protect Physical Console Access + It is impossible to fully protect a system from an +attacker with physical access, so securing the space in which the +system is located should be considered a necessary step. However, +there are some steps which, if taken, make it more difficult for an +attacker to quickly or undetectably modify a system from its +console. + + Disable debug-shell SystemD Service + SystemD's debug-shell service is intended to +diagnose SystemD related boot issues with various systemctl +commands. Once enabled and following a system reboot, the root shell +will be available on tty9 which is access by pressing +CTRL-ALT-F9. The debug-shell service should only be used +for SystemD related issues and should otherwise be disabled. + +By default, the debug-shell SystemD service is already disabled. + +The debug-shell service can be disabled with the following command: +$ sudo systemctl mask --now debug-shell.service + 3.4.5 + CCI-000366 + 164.308(a)(1)(ii)(B) + 164.308(a)(7)(i) + 164.308(a)(7)(ii)(A) + 164.310(a)(1) + 164.310(a)(2)(i) + 164.310(a)(2)(ii) + 164.310(a)(2)(iii) + 164.310(b) + 164.310(c) + 164.310(d)(1) + 164.310(d)(2)(iii) + CM-6 + FIA_UAU.1 + SRG-OS-000324-GPOS-00125 + SRG-OS-000480-GPOS-00227 + RHEL-08-040180 + SV-230532r627750_rule + This prevents attackers with physical access from trivially bypassing security +on the machine through valid troubleshooting configurations and gaining root +access when the system is rebooted. + + CCE-80876-6 + include disable_debug-shell + +class disable_debug-shell { + service {'debug-shell': + enable => false, + ensure => 'stopped', + } +} + + - name: Disable service debug-shell + block: + + - name: Disable service debug-shell + systemd: + name: debug-shell.service + enabled: 'no' + state: stopped + masked: 'yes' + ignore_errors: 'yes' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80876-6 + - DISA-STIG-RHEL-08-040180 + - NIST-800-171-3.4.5 + - NIST-800-53-CM-6 + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_debug-shell_disabled + +- name: Unit Socket Exists - debug-shell.socket + command: systemctl list-unit-files debug-shell.socket + args: + warn: false + register: socket_file_exists + changed_when: false + ignore_errors: true + check_mode: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80876-6 + - DISA-STIG-RHEL-08-040180 + - NIST-800-171-3.4.5 + - NIST-800-53-CM-6 + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_debug-shell_disabled + +- name: Disable socket debug-shell + systemd: + name: debug-shell.socket + enabled: 'no' + state: stopped + masked: 'yes' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"debug-shell.socket" in socket_file_exists.stdout_lines[1]' + tags: + - CCE-80876-6 + - DISA-STIG-RHEL-08-040180 + - NIST-800-171-3.4.5 + - NIST-800-53-CM-6 + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_debug-shell_disabled + + +[customizations.services] +disabled = ["debug-shell"] + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + systemd: + units: + - enabled: false + name: debug-shell.service + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" stop 'debug-shell.service' +"$SYSTEMCTL_EXEC" disable 'debug-shell.service' +"$SYSTEMCTL_EXEC" mask 'debug-shell.service' +# Disable socket activation if we have a unit file for it +if "$SYSTEMCTL_EXEC" list-unit-files | grep -q '^debug-shell.socket'; then + "$SYSTEMCTL_EXEC" stop 'debug-shell.socket' + "$SYSTEMCTL_EXEC" mask 'debug-shell.socket' +fi +# The service may not be running because it has been started and failed, +# so let's reset the state so OVAL checks pass. +# Service should be 'inactive', not 'failed' after reboot though. +"$SYSTEMCTL_EXEC" reset-failed 'debug-shell.service' || true + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable Ctrl-Alt-Del Burst Action + By default, SystemD will reboot the system if the Ctrl-Alt-Del +key sequence is pressed Ctrl-Alt-Delete more than 7 times in 2 seconds. + +To configure the system to ignore the CtrlAltDelBurstAction + +setting, add or modify the following to /etc/systemd/system.conf: +CtrlAltDelBurstAction=none + Disabling the Ctrl-Alt-Del key sequence +in /etc/init/control-alt-delete.conf DOES NOT disable the Ctrl-Alt-Del +key sequence if running in runlevel 6 (e.g. in GNOME, KDE, etc.)! The +Ctrl-Alt-Del key sequence will only be disabled if running in +the non-graphical runlevel 3. + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + 3.4.5 + CCI-000366 + 164.308(a)(1)(ii)(B) + 164.308(a)(7)(i) + 164.308(a)(7)(ii)(A) + 164.310(a)(1) + 164.310(a)(2)(i) + 164.310(a)(2)(ii) + 164.310(a)(2)(iii) + 164.310(b) + 164.310(c) + 164.310(d)(1) + 164.310(d)(2)(iii) + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-6(a) + AC-6(1) + CM-6(a) + PR.AC-4 + PR.DS-5 + FAU_GEN.1.2 + SRG-OS-000324-GPOS-00125 + SRG-OS-000480-GPOS-00227 + RHEL-08-040172 + SV-230531r627750_rule + A locally logged-in user who presses Ctrl-Alt-Del, when at the console, +can reboot the system. If accidentally pressed, as could happen in +the case of mixed OS environment, this can create the risk of short-term +loss of availability of systems due to unintentional reboot. + + CCE-80784-2 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80784-2 + - DISA-STIG-RHEL-08-040172 + - NIST-800-171-3.4.5 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-6(a) + - disable_ctrlaltdel_burstaction + - disable_strategy + - high_severity + - low_complexity + - low_disruption + - no_reboot_needed + +- name: Disable Ctrl-Alt-Del Burst Action + lineinfile: + dest: /etc/systemd/system.conf + state: present + regexp: ^CtrlAltDelBurstAction + line: CtrlAltDelBurstAction=none + create: true + when: '"systemd" in ansible_facts.packages' + tags: + - CCE-80784-2 + - DISA-STIG-RHEL-08-040172 + - NIST-800-171-3.4.5 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-6(a) + - disable_ctrlaltdel_burstaction + - disable_strategy + - high_severity + - low_complexity + - low_disruption + - no_reboot_needed + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,CtrlAltDelBurstAction%3Dnone + mode: 0644 + path: /etc/systemd/system.conf.d/disable_ctrlaltdelete_burstaction.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if rpm --quiet -q systemd; then + +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/systemd/system.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^CtrlAltDelBurstAction=") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s=%s" "$stripped_key" "none" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^CtrlAltDelBurstAction=\\>" "/etc/systemd/system.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^CtrlAltDelBurstAction=\\>.*/$escaped_formatted_output/gi" "/etc/systemd/system.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-80784-2" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/systemd/system.conf" >> "/etc/systemd/system.conf" + printf '%s\n' "$formatted_output" >> "/etc/systemd/system.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable Ctrl-Alt-Del Reboot Activation + By default, SystemD will reboot the system if the Ctrl-Alt-Del +key sequence is pressed. + +To configure the system to ignore the Ctrl-Alt-Del key sequence from the + +command line instead of rebooting the system, do either of the following: +ln -sf /dev/null /etc/systemd/system/ctrl-alt-del.target +or +systemctl mask ctrl-alt-del.target + +Do not simply delete the /usr/lib/systemd/system/ctrl-alt-del.service file, +as this file may be restored during future system updates. + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + 3.4.5 + CCI-000366 + 164.308(a)(1)(ii)(B) + 164.308(a)(7)(i) + 164.308(a)(7)(ii)(A) + 164.310(a)(1) + 164.310(a)(2)(i) + 164.310(a)(2)(ii) + 164.310(a)(2)(iii) + 164.310(b) + 164.310(c) + 164.310(d)(1) + 164.310(d)(2)(iii) + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + FAU_GEN.1.2 + SRG-OS-000324-GPOS-00125 + SRG-OS-000480-GPOS-00227 + RHEL-08-040170 + SV-230529r833338_rule + A locally logged-in user who presses Ctrl-Alt-Del, when at the console, +can reboot the system. If accidentally pressed, as could happen in +the case of mixed OS environment, this can create the risk of short-term +loss of availability of systems due to unintentional reboot. + + CCE-80785-9 + - name: Disable Ctrl-Alt-Del Reboot Activation + systemd: + name: ctrl-alt-del.target + force: true + masked: true + state: stopped + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80785-9 + - DISA-STIG-RHEL-08-040170 + - NIST-800-171-3.4.5 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - disable_ctrlaltdel_reboot + - disable_strategy + - high_severity + - low_complexity + - low_disruption + - no_reboot_needed + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + systemd: + units: + - name: ctrl-alt-del.target + mask: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +systemctl disable --now ctrl-alt-del.target +systemctl mask --now ctrl-alt-del.target + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Verify that Interactive Boot is Disabled + Red Hat Enterprise Linux 8 systems support an "interactive boot" option that can +be used to prevent services from being started. On a Red Hat Enterprise Linux 8 +system, interactive boot can be enabled by providing a 1, +yes, true, or on value to the +systemd.confirm_spawn kernel argument in /etc/default/grub. +Remove any instance of systemd.confirm_spawn=(1|yes|true|on) from +the kernel arguments in that file to disable interactive boot. +Recovery booting must also be disabled. Confirm that +GRUB_DISABLE_RECOVERY=true is set in /etc/default/grub. +It is also required to change the runtime configuration, run: + +/sbin/grubby --update-kernel=ALL --remove-args="systemd.confirm_spawn" + +grub2-mkconfig -o /boot/grub2/grub.cfg + 11 + 12 + 14 + 15 + 16 + 18 + 3 + 5 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + DSS06.03 + DSS06.06 + 3.1.2 + 3.4.5 + CCI-000213 + 164.308(a)(1)(ii)(B) + 164.308(a)(7)(i) + 164.308(a)(7)(ii)(A) + 164.310(a)(1) + 164.310(a)(2)(i) + 164.310(a)(2)(ii) + 164.310(a)(2)(iii) + 164.310(b) + 164.310(c) + 164.310(d)(1) + 164.310(d)(2)(iii) + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + A.6.1.2 + A.7.1.1 + A.9.1.2 + A.9.2.1 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + SC-2(1) + CM-6(a) + PR.AC-4 + PR.AC-6 + PR.PT-3 + FIA_UAU.1 + SRG-OS-000480-GPOS-00227 + Using interactive or recovery boot, the console user could disable auditing, firewalls, +or other services, weakening system security. + + CCE-80826-1 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80826-1 + - NIST-800-171-3.1.2 + - NIST-800-171-3.4.5 + - NIST-800-53-CM-6(a) + - NIST-800-53-SC-2(1) + - grub2_disable_interactive_boot + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Verify GRUB_DISABLE_RECOVERY=true + lineinfile: + path: /etc/default/grub + regexp: ^GRUB_DISABLE_RECOVERY=.* + line: GRUB_DISABLE_RECOVERY=true + state: present + when: '"grub2-common" in ansible_facts.packages' + tags: + - CCE-80826-1 + - NIST-800-171-3.1.2 + - NIST-800-171-3.4.5 + - NIST-800-53-CM-6(a) + - NIST-800-53-SC-2(1) + - grub2_disable_interactive_boot + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Verify that Interactive Boot is Disabled in /etc/default/grub + replace: + dest: /etc/default/grub + regexp: systemd.confirm_spawn(=(1|yes|true|on)|\b) + replace: systemd.confirm_spawn=no + when: '"grub2-common" in ansible_facts.packages' + tags: + - CCE-80826-1 + - NIST-800-171-3.1.2 + - NIST-800-171-3.4.5 + - NIST-800-53-CM-6(a) + - NIST-800-53-SC-2(1) + - grub2_disable_interactive_boot + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Verify that Interactive Boot is Disabled (runtime) + command: /sbin/grubby --update-kernel=ALL --remove-args="systemd.confirm_spawn" + when: '"grub2-common" in ansible_facts.packages' + tags: + - CCE-80826-1 + - NIST-800-171-3.1.2 + - NIST-800-171-3.4.5 + - NIST-800-53-CM-6(a) + - NIST-800-53-SC-2(1) + - grub2_disable_interactive_boot + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Regen grub.cfg handle updated GRUB_DISABLE_RECOVERY and confirm_spawn + command: grub2-mkconfig -o /boot/grub2/grub.cfg + when: '"grub2-common" in ansible_facts.packages' + tags: + - CCE-80826-1 + - NIST-800-171-3.1.2 + - NIST-800-171-3.4.5 + - NIST-800-53-CM-6(a) + - NIST-800-53-SC-2(1) + - grub2_disable_interactive_boot + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q grub2-common; then + +# Verify that Interactive Boot is Disabled in /etc/default/grub +CONFIRM_SPAWN_YES="systemd.confirm_spawn\(=\(1\|yes\|true\|on\)\|\b\)" +CONFIRM_SPAWN_NO="systemd.confirm_spawn=no" + +if grep -q "\(GRUB_CMDLINE_LINUX\|GRUB_CMDLINE_LINUX_DEFAULT\)" /etc/default/grub +then + sed -i "s/${CONFIRM_SPAWN_YES}/${CONFIRM_SPAWN_NO}/" /etc/default/grub +fi + +# make sure GRUB_DISABLE_RECOVERY=true +if grep -q '^GRUB_DISABLE_RECOVERY=.*' '/etc/default/grub' ; then + # modify the GRUB command-line if an GRUB_DISABLE_RECOVERY= arg already exists + sed -i 's/GRUB_DISABLE_RECOVERY=.*/GRUB_DISABLE_RECOVERY=true/' /etc/default/grub +else + # no GRUB_DISABLE_RECOVERY=arg is present, append it to file + echo "GRUB_DISABLE_RECOVERY=true" >> '/etc/default/grub' +fi + + + +# Remove 'systemd.confirm_spawn' kernel argument also from runtime settings +/sbin/grubby --update-kernel=ALL --remove-args="systemd.confirm_spawn" + + +#Regen grub.cfg handle updated GRUB_DISABLE_RECOVERY and confirm_spawn +grub2-mkconfig -o /boot/grub2/grub.cfg + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Require Authentication for Emergency Systemd Target + Emergency mode is intended as a system recovery +method, providing a single user root access to the system +during a failed boot sequence. + +By default, Emergency mode is protected by requiring a password and is set +in /usr/lib/systemd/system/emergency.service. + 1 + 11 + 12 + 14 + 15 + 16 + 18 + 3 + 5 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.03 + DSS06.06 + DSS06.10 + 3.1.1 + 3.4.5 + CCI-000213 + 164.308(a)(1)(ii)(B) + 164.308(a)(7)(i) + 164.308(a)(7)(ii)(A) + 164.310(a)(1) + 164.310(a)(2)(i) + 164.310(a)(2)(ii) + 164.310(a)(2)(iii) + 164.310(b) + 164.310(c) + 164.310(d)(1) + 164.310(d)(2)(iii) + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + 0421 + 0422 + 0431 + 0974 + 1173 + 1401 + 1504 + 1505 + 1546 + 1557 + 1558 + 1559 + 1560 + 1561 + A.18.1.4 + A.6.1.2 + A.7.1.1 + A.9.1.2 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.1 + A.9.4.2 + A.9.4.3 + A.9.4.4 + A.9.4.5 + IA-2 + AC-3 + CM-6(a) + PR.AC-1 + PR.AC-4 + PR.AC-6 + PR.AC-7 + PR.PT-3 + FIA_UAU.1 + SRG-OS-000080-GPOS-00048 + RHEL-08-010152 + 1.4.3 + SV-244523r743818_rule + This prevents attackers with physical access from trivially bypassing security +on the machine and gaining root access. Such accesses are further prevented +by configuring the bootloader password. + + CCE-82186-8 + - name: require emergency mode password + lineinfile: + create: true + dest: /usr/lib/systemd/system/emergency.service + regexp: ^#?ExecStart= + line: ExecStart=-/usr/lib/systemd/systemd-sulogin-shell emergency + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82186-8 + - DISA-STIG-RHEL-08-010152 + - NIST-800-171-3.1.1 + - NIST-800-171-3.4.5 + - NIST-800-53-AC-3 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-2 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - require_emergency_target_auth + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +service_file="/usr/lib/systemd/system/emergency.service" + +sulogin="/usr/lib/systemd/systemd-sulogin-shell emergency" + +if grep "^ExecStart=.*" "$service_file" ; then + sed -i "s%^ExecStart=.*%ExecStart=-$sulogin%" "$service_file" +else + echo "ExecStart=-$sulogin" >> "$service_file" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Require Authentication for Single User Mode + Single-user mode is intended as a system recovery +method, providing a single user root access to the system by +providing a boot option at startup. + +By default, single-user mode is protected by requiring a password and is set +in /usr/lib/systemd/system/rescue.service. + 1 + 11 + 12 + 14 + 15 + 16 + 18 + 3 + 5 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.03 + DSS06.06 + DSS06.10 + 3.1.1 + 3.4.5 + CCI-000213 + 164.308(a)(1)(ii)(B) + 164.308(a)(7)(i) + 164.308(a)(7)(ii)(A) + 164.310(a)(1) + 164.310(a)(2)(i) + 164.310(a)(2)(ii) + 164.310(a)(2)(iii) + 164.310(b) + 164.310(c) + 164.310(d)(1) + 164.310(d)(2)(iii) + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + 0421 + 0422 + 0431 + 0974 + 1173 + 1401 + 1504 + 1505 + 1546 + 1557 + 1558 + 1559 + 1560 + 1561 + A.18.1.4 + A.6.1.2 + A.7.1.1 + A.9.1.2 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.1 + A.9.4.2 + A.9.4.3 + A.9.4.4 + A.9.4.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.2.3 + CIP-004-6 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.2 + CIP-007-3 R5.2 + CIP-007-3 R5.3.1 + CIP-007-3 R5.3.2 + CIP-007-3 R5.3.3 + IA-2 + AC-3 + CM-6(a) + PR.AC-1 + PR.AC-4 + PR.AC-6 + PR.AC-7 + PR.PT-3 + FIA_UAU.1 + SRG-OS-000080-GPOS-00048 + RHEL-08-010151 + 1.4.3 + SV-230236r743928_rule + This prevents attackers with physical access from trivially bypassing security +on the machine and gaining root access. Such accesses are further prevented +by configuring the bootloader password. + + CCE-80855-0 + - name: require single user mode password + lineinfile: + create: true + dest: /usr/lib/systemd/system/rescue.service + regexp: ^#?ExecStart= + line: ExecStart=-/usr/lib/systemd/systemd-sulogin-shell rescue + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80855-0 + - DISA-STIG-RHEL-08-010151 + - NIST-800-171-3.1.1 + - NIST-800-171-3.4.5 + - NIST-800-53-AC-3 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-2 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - require_singleuser_auth + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +service_file="/usr/lib/systemd/system/rescue.service" + +sulogin="/usr/lib/systemd/systemd-sulogin-shell rescue" + +if grep "^ExecStart=.*" "$service_file" ; then + sed -i "s%^ExecStart=.*%ExecStart=-$sulogin%" "$service_file" +else + echo "ExecStart=-$sulogin" >> "$service_file" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure Screen Locking + When a user must temporarily leave an account +logged-in, screen locking should be employed to prevent passersby +from abusing the account. User education and training is +particularly important for screen locking to be effective, and policies +can be implemented to reinforce this. + +Automatic screen locking is only meant as a safeguard for +those cases where a user forgot to lock the screen. + + Configure Console Screen Locking + A console screen locking mechanism is a temporary action taken when a user +stops work and moves away from the immediate physical vicinity of the +information system but does not logout because of the temporary nature of +the absence. Rather than relying on the user to manually lock their +operation system session prior to vacating the vicinity, operating systems +need to be able to identify when a user's session has idled and take action +to initiate the session lock. + + Install the tmux Package + To enable console screen locking, install the tmux package. +A session lock is a temporary action taken when a user stops work and moves away from the immediate physical vicinity of the information system but does not want to log out because of the temporary nature of the absence. +The session lock is implemented at the point where session activity can be determined. +Rather than be forced to wait for a period of time to expire before the user session can be locked, Red Hat Enterprise Linux 8 needs to provide users with the ability to manually invoke a session lock so users can secure their session if it is necessary to temporarily vacate the immediate physical vicinity. +Instruct users to begin new terminal sessions with the following command: +$ tmux +The console can now be locked with the following key combination: +ctrl+b :lock-session + 1 + 12 + 15 + 16 + DSS05.04 + DSS05.10 + DSS06.10 + 3.1.10 + CCI-000058 + CCI-000056 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + A.18.1.4 + A.9.2.1 + A.9.2.4 + A.9.3.1 + A.9.4.2 + A.9.4.3 + CM-6(a) + PR.AC-7 + FMT_SMF_EXT.1 + FMT_MOF_EXT.1 + FTA_SSL.1 + SRG-OS-000030-GPOS-00011 + SRG-OS-000028-GPOS-00009 + SRG-OS-000030-VMM-000110 + RHEL-08-020039 + SV-244537r743860_rule + A session time-out lock is a temporary action taken when a user stops work and moves away from the immediate +physical vicinity of the information system but does not logout because of the temporary nature of the absence. +Rather than relying on the user to manually lock their operation system session prior to vacating the vicinity, +operating systems need to be able to identify when a user's session has idled and take action to initiate the +session lock. + +The tmux package allows for a session lock to be implemented and configured. + + CCE-80644-8 + +package --add=tmux + + include install_tmux + +class install_tmux { + package { 'tmux': + ensure => 'installed', + } +} + + - name: Ensure tmux is installed + package: + name: tmux + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80644-8 + - DISA-STIG-RHEL-08-020039 + - NIST-800-171-3.1.10 + - NIST-800-53-CM-6(a) + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_tmux_installed + + +[[packages]] +name = "tmux" +version = "*" + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if ! rpm -q --quiet "tmux" ; then + yum install -y "tmux" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Support session locking with tmux + The tmux terminal multiplexer is used to implement +automatic session locking. It should be started from +/etc/bashrc or drop-in files within /etc/profile.d/. + CCI-000056 + CCI-000058 + FMT_SMF_EXT.1 + FMT_MOF_EXT.1 + FTA_SSL.1 + SRG-OS-000031-GPOS-00012 + SRG-OS-000028-GPOS-00009 + SRG-OS-000030-GPOS-00011 + RHEL-08-020041 + SV-230349r833388_rule + Unlike bash itself, the tmux terminal multiplexer +provides a mechanism to lock sessions after period of inactivity. +A session lock is a temporary action taken when a user stops work and moves away from the +immediate physical vicinity of the information system but does not want to +log out because of the temporary nature of the absence. + + CCE-82266-8 + # Remediation is applicable only in certain platforms +if rpm --quiet -q tmux; then + +if ! grep -x ' case "$name" in sshd|login) exec tmux ;; esac' /etc/bashrc; then + cat >> /etc/profile.d/tmux.sh <<'EOF' +if [ "$PS1" ]; then + parent=$(ps -o ppid= -p $$) + name=$(ps -o comm= -p $parent) + case "$name" in sshd|login) exec tmux ;; esac +fi +EOF + chmod 0644 /etc/profile.d/tmux.sh +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure tmux to lock session after inactivity + To enable console screen locking in tmux terminal multiplexer +after a period of inactivity, +the lock-after-time option has to be set to a value greater than 0 and less than +or equal to 900 in /etc/tmux.conf. + CCI-000057 + CCI-000060 + FMT_SMF_EXT.1 + FMT_MOF_EXT.1 + FTA_SSL.1 + SRG-OS-000029-GPOS-00010 + SRG-OS-000031-GPOS-00012 + RHEL-08-020070 + SV-230353r627750_rule + Locking the session after a period of inactivity limits the +potential exposure if the session is left unattended. + + CCE-82199-1 + - name: Configure tmux to lock session after inactivity + block: + + - name: Check for duplicate values + lineinfile: + path: /etc/tmux.conf + create: false + regexp: ^\s*set -g lock-after-time\s+ + mode: '0644' + state: absent + check_mode: true + changed_when: false + register: dupes + + - name: Deduplicate values from /etc/tmux.conf + lineinfile: + path: /etc/tmux.conf + create: false + regexp: ^\s*set -g lock-after-time\s+ + mode: '0644' + state: absent + when: dupes.found is defined and dupes.found > 1 + + - name: Insert correct line to /etc/tmux.conf + lineinfile: + path: /etc/tmux.conf + create: true + regexp: ^\s*set -g lock-after-time\s+ + mode: '0644' + line: set -g lock-after-time 900 + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82199-1 + - DISA-STIG-RHEL-08-020070 + - configure_tmux_lock_after_time + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +tmux_conf="/etc/tmux.conf" + +if grep -qP '^\s*set\s+-g\s+lock-after-time' "$tmux_conf" ; then + sed -i 's/^\s*set\s\+-g\s\+lock-after-time.*$/set -g lock-after-time 900/' "$tmux_conf" +else + echo "set -g lock-after-time 900" >> "$tmux_conf" +fi +chmod 0644 "$tmux_conf" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure the tmux Lock Command + To enable console screen locking in tmux terminal multiplexer, +the vlock command must be configured to be used as a locking +mechanism. +Add the following line to /etc/tmux.conf: +set -g lock-command vlock. +The console can now be locked with the following key combination: +ctrl+b :lock-session + CCI-000056 + CCI-000058 + AC-11(a) + AC-11(b) + CM-6(a) + FMT_SMF_EXT.1 + FMT_MOF_EXT.1 + FTA_SSL.1 + SRG-OS-000028-GPOS-00009 + SRG-OS-000030-GPOS-00011 + SRG-OS-000028-VMM-000090 + SRG-OS-000030-VMM-000110 + RHEL-08-020040 + SV-230348r743987_rule + The tmux package allows for a session lock to be implemented and configured. +However, the session lock is implemented by an external command. The tmux +default configuration does not contain an effective session lock. + + CCE-80940-0 + - name: Configure the tmux Lock Command + block: + + - name: Check for duplicate values + lineinfile: + path: /etc/tmux.conf + create: false + regexp: ^\s*set -g lock-command\s+ + mode: '0644' + state: absent + check_mode: true + changed_when: false + register: dupes + + - name: Deduplicate values from /etc/tmux.conf + lineinfile: + path: /etc/tmux.conf + create: false + regexp: ^\s*set -g lock-command\s+ + mode: '0644' + state: absent + when: dupes.found is defined and dupes.found > 1 + + - name: Insert correct line to /etc/tmux.conf + lineinfile: + path: /etc/tmux.conf + create: true + regexp: ^\s*set -g lock-command\s+ + mode: '0644' + line: set -g lock-command vlock + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80940-0 + - DISA-STIG-RHEL-08-020040 + - NIST-800-53-AC-11(a) + - NIST-800-53-AC-11(b) + - NIST-800-53-CM-6(a) + - configure_tmux_lock_command + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +tmux_conf="/etc/tmux.conf" + +if grep -qP '^\s*set\s+-g\s+lock-command' "$tmux_conf" ; then + sed -i 's/^\s*set\s\+-g\s\+lock-command.*$/set -g lock-command vlock/' "$tmux_conf" +else + echo "set -g lock-command vlock" >> "$tmux_conf" +fi +chmod 0644 "$tmux_conf" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Prevent user from disabling the screen lock + The tmux terminal multiplexer is used to implement +automatic session locking. It should not be listed in +/etc/shells. + CCI-000056 + CCI-000058 + CM-6 + FMT_SMF_EXT.1 + FMT_MOF_EXT.1 + FTA_SSL.1 + SRG-OS-000324-GPOS-00125 + SRG-OS-000028-GPOS-00009 + SRG-OS-000030-GPOS-00011 + RHEL-08-020042 + SV-230350r627750_rule + Not listing tmux among permitted shells +prevents malicious program running as user +from lowering security by disabling the screen lock. + + CCE-82361-7 + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,/bin/sh%0A/bin/bash%0A/usr/bin/sh%0A/usr/bin/bash%0A + mode: 0644 + path: /etc/shells + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if grep -q 'tmux\s*$' /etc/shells ; then + sed -i '/tmux\s*$/d' /etc/shells +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Hardware Tokens for Authentication + The use of hardware tokens such as smart cards for system login +provides stronger, two-factor authentication than using a username and password. + +In Red Hat Enterprise Linux servers and workstations, hardware token login + +is not enabled by default and must be enabled in the system settings. + + + OpenSC Smart Card Drivers + Choose the Smart Card Driver in use by your organization. +For DoD, choose the cac driver. +If your driver is not listed and you don't want to use the +default driver, use the other option and +manually specify your driver. + default + acos5 + akis + asepcos + atrust-acos + authentic + belpic + cac + cardos + coolkey + cyberflex + dnie + entersafe + epass2003 + flex + gemsafeV1 + gids + gpk + iasecc + incrypto34 + isoApplet + itacns + jpki + MaskTech + mcrd + muscle + myeid + npa + oberthur + openpgp + None + PIV-II + rutoken_ecp + rutoken + sc-hsm + setcos + starcos + tcos + westcos + + + Install the opensc Package For Multifactor Authentication + +The opensc package can be installed with the following command: + +$ sudo yum install opensc + CCI-001954 + CCI-001953 + 1382 + 1384 + 1386 + CM-6(a) + SRG-OS-000375-GPOS-00160 + SRG-OS-000376-GPOS-00161 + SRG-OS-000376-VMM-001520 + RHEL-08-010410 + SV-230275r627750_rule + Using an authentication device, such as a CAC or token that is separate from +the information system, ensures that even if the information system is +compromised, that compromise will not affect credentials stored on the +authentication device. + +Multifactor solutions that require devices separate from +information systems gaining access include, for example, hardware tokens +providing time-based or challenge-response authenticators and smart cards such +as the U.S. Government Personal Identity Verification card and the DoD Common +Access Card. + CCE-80846-9 + +package --add=opensc + + include install_opensc + +class install_opensc { + package { 'opensc': + ensure => 'installed', + } +} + + - name: Ensure opensc is installed + package: + name: opensc + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80846-9 + - DISA-STIG-RHEL-08-010410 + - NIST-800-53-CM-6(a) + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_opensc_installed + + +[[packages]] +name = "opensc" +version = "*" + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if ! rpm -q --quiet "opensc" ; then + yum install -y "opensc" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Install the pcsc-lite package + The pcsc-lite package can be installed with the following command: + +$ sudo yum install pcsc-lite + CCI-001954 + 1382 + 1384 + 1386 + CM-6(a) + SRG-OS-000375-GPOS-00160 + SRG-OS-000377-VMM-001530 + The pcsc-lite package must be installed if it is to be available for +multifactor authentication using smartcards. + CCE-80993-9 + +package --add=pcsc-lite + + include install_pcsc-lite + +class install_pcsc-lite { + package { 'pcsc-lite': + ensure => 'installed', + } +} + + - name: Ensure pcsc-lite is installed + package: + name: pcsc-lite + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80993-9 + - NIST-800-53-CM-6(a) + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_pcsc-lite_installed + + +[[packages]] +name = "pcsc-lite" +version = "*" + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if ! rpm -q --quiet "pcsc-lite" ; then + yum install -y "pcsc-lite" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Install Smart Card Packages For Multifactor Authentication + Configure the operating system to implement multifactor authentication by +installing the required package with the following command: + +The openssl-pkcs11 package can be installed with the following command: + +$ sudo yum install openssl-pkcs11 + CCI-000765 + CCI-001948 + CCI-001953 + CCI-001954 + CM-6(a) + SRG-OS-000105-GPOS-00052 + SRG-OS-000375-GPOS-00160 + SRG-OS-000375-GPOS-00161 + SRG-OS-000377-GPOS-00162 + RHEL-08-010390 + SV-230273r743943_rule + Using an authentication device, such as a CAC or token that is separate from +the information system, ensures that even if the information system is +compromised, that compromise will not affect credentials stored on the +authentication device. + +Multifactor solutions that require devices separate from +information systems gaining access include, for example, hardware tokens +providing time-based or challenge-response authenticators and smart cards such +as the U.S. Government Personal Identity Verification card and the DoD Common +Access Card. + + CCE-84029-8 + +package --add=openssl-pkcs11 + + include install_openssl-pkcs11 + +class install_openssl-pkcs11 { + package { 'openssl-pkcs11': + ensure => 'installed', + } +} + + - name: Ensure openssl-pkcs11 is installed + package: + name: openssl-pkcs11 + state: present + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ansible_architecture != "s390x" + tags: + - CCE-84029-8 + - DISA-STIG-RHEL-08-010390 + - NIST-800-53-CM-6(a) + - enable_strategy + - install_smartcard_packages + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + +[[packages]] +name = "openssl-pkcs11" +version = "*" + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && { ! grep -q s390x /proc/sys/kernel/osrelease; }; then + +if ! rpm -q --quiet "openssl-pkcs11" ; then + yum install -y "openssl-pkcs11" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Enable the pcscd Service + +The pcscd service can be enabled with the following command: +$ sudo systemctl enable pcscd.service + CCI-001954 + 1382 + 1384 + 1386 + IA-2(1) + IA-2(2) + IA-2(3) + IA-2(4) + IA-2(6) + IA-2(7) + IA-2(11) + CM-6(a) + SRG-OS-000375-GPOS-00160 + SRG-OS-000377-VMM-001530 + Using an authentication device, such as a CAC or token that is separate from +the information system, ensures that even if the information system is +compromised, that compromise will not affect credentials stored on the +authentication device. + +Multifactor solutions that require devices separate from +information systems gaining access include, for example, hardware tokens +providing time-based or challenge-response authenticators and smart cards such +as the U.S. Government Personal Identity Verification card and the DoD Common +Access Card. + CCE-80881-6 + include enable_pcscd + +class enable_pcscd { + service {'pcscd': + enable => true, + ensure => 'running', + } +} + + - name: Enable service pcscd + block: + + - name: Gather the package facts + package_facts: + manager: auto + + - name: Start service pcscd + service: + name: pcscd + state: started + masked: 'no' + when: + - '"pcsc-lite" in ansible_facts.packages' + + - name: Enable service pcscd + ansible.builtin.command: + cmd: systemctl enable pcscd + when: + - '"pcsc-lite" in ansible_facts.packages' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80881-6 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-2(1) + - NIST-800-53-IA-2(11) + - NIST-800-53-IA-2(2) + - NIST-800-53-IA-2(3) + - NIST-800-53-IA-2(4) + - NIST-800-53-IA-2(6) + - NIST-800-53-IA-2(7) + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_pcscd_enabled + + +[customizations.services] +enabled = ["pcscd"] + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" unmask 'pcscd.service' +"$SYSTEMCTL_EXEC" start 'pcscd.service' +"$SYSTEMCTL_EXEC" enable 'pcscd.service' + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure opensc Smart Card Drivers + The OpenSC smart card tool can auto-detect smart card drivers; however, +setting the smart card drivers in use by your organization helps to prevent +users from using unauthorized smart cards. The default smart card driver for this +profile is . +To configure the OpenSC driver, edit the /etc/opensc.conf +and add the following line into the file in the app default block, +so it will look like: + + +app default { + ... + card_drivers = ; +} + + 1 + 12 + 15 + 16 + 5 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.03 + DSS06.10 + CCI-000765 + CCI-000766 + CCI-000767 + CCI-000768 + CCI-000771 + CCI-000772 + CCI-000884 + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.2 + 4.3.3.7.4 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + 1382 + 1384 + 1386 + A.18.1.4 + A.7.1.1 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.2 + A.9.4.3 + IA-2(1) + IA-2(2) + IA-2(3) + IA-2(4) + IA-2(6) + IA-2(7) + IA-2(11) + CM-6(a) + PR.AC-1 + PR.AC-6 + PR.AC-7 + Req-8.3 + SRG-OS-000104-GPOS-00051 + SRG-OS-000106-GPOS-00053 + SRG-OS-000107-GPOS-00054 + SRG-OS-000109-GPOS-00056 + SRG-OS-000108-GPOS-00055 + SRG-OS-000108-GPOS-00057 + SRG-OS-000108-GPOS-00058 + SRG-OS-000376-VMM-001520 + Smart card login provides two-factor authentication stronger than +that provided by a username and password combination. Smart cards leverage PKI +(public key infrastructure) in order to provide and verify credentials. +Configuring the smart card driver in use by your organization helps to prevent +users from using unauthorized smart cards. + CCE-80766-9 + - name: XCCDF Value var_smartcard_drivers # promote to variable + set_fact: + var_smartcard_drivers: !!str + tags: + - always + +- name: Check existence of opensc conf + stat: + path: /etc/opensc-{{ ansible_architecture }}.conf + register: opensc_conf_cd + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80766-9 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-2(1) + - NIST-800-53-IA-2(11) + - NIST-800-53-IA-2(2) + - NIST-800-53-IA-2(3) + - NIST-800-53-IA-2(4) + - NIST-800-53-IA-2(6) + - NIST-800-53-IA-2(7) + - PCI-DSS-Req-8.3 + - configure_opensc_card_drivers + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Configure smartcard driver block + block: + + - name: Check if card_drivers is defined + command: /usr/bin/opensc-tool -G app:default:card_drivers + changed_when: false + register: card_drivers + + - name: Configure opensc Smart Card Drivers + command: | + /usr/bin/opensc-tool -S app:default:card_drivers:{{ var_smartcard_drivers }} + when: + - card_drivers.stdout != var_smartcard_drivers + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - opensc_conf_cd.stat.exists + tags: + - CCE-80766-9 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-2(1) + - NIST-800-53-IA-2(11) + - NIST-800-53-IA-2(2) + - NIST-800-53-IA-2(3) + - NIST-800-53-IA-2(4) + - NIST-800-53-IA-2(6) + - NIST-800-53-IA-2(7) + - PCI-DSS-Req-8.3 + - configure_opensc_card_drivers + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_smartcard_drivers='' + + +OPENSC_TOOL="/usr/bin/opensc-tool" + +if [ -f "${OPENSC_TOOL}" ]; then + ${OPENSC_TOOL} -S app:default:card_drivers:$var_smartcard_drivers +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Force opensc To Use Defined Smart Card Driver + The OpenSC smart card middleware can auto-detect smart card drivers; however by +forcing the smart card driver in use by your organization, opensc will no longer +autodetect or use other drivers unless specified. This helps to prevent +users from using unauthorized smart cards. The default smart card driver for this +profile is . +To force the OpenSC driver, edit the /etc/opensc.conf. +Look for a line similar to: +# force_card_driver = customcos; +and change it to: +force_card_driver = ; + 1 + 12 + 15 + 16 + 5 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.03 + DSS06.10 + CCI-000765 + CCI-000766 + CCI-000767 + CCI-000768 + CCI-000771 + CCI-000772 + CCI-000884 + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.2 + 4.3.3.7.4 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + 1382 + 1384 + 1386 + A.18.1.4 + A.7.1.1 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.2 + A.9.4.3 + IA-2(1) + IA-2(2) + IA-2(3) + IA-2(4) + IA-2(6) + IA-2(7) + IA-2(11) + CM-6(a) + PR.AC-1 + PR.AC-6 + PR.AC-7 + Req-8.3 + SRG-OS-000104-GPOS-00051 + SRG-OS-000106-GPOS-00053 + SRG-OS-000107-GPOS-00054 + SRG-OS-000109-GPOS-00056 + SRG-OS-000108-GPOS-00055 + SRG-OS-000108-GPOS-00057 + SRG-OS-000108-GPOS-00058 + SRG-OS-000376-VMM-001520 + Smart card login provides two-factor authentication stronger than +that provided by a username and password combination. Smart cards leverage PKI +(public key infrastructure) in order to provide and verify credentials. +Forcing the smart card driver in use by your organization helps to prevent +users from using unauthorized smart cards. + CCE-80821-2 + - name: XCCDF Value var_smartcard_drivers # promote to variable + set_fact: + var_smartcard_drivers: !!str + tags: + - always + +- name: Check existence of opensc conf + stat: + path: /etc/opensc-{{ ansible_architecture }}.conf + register: opensc_conf_fcd + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80821-2 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-2(1) + - NIST-800-53-IA-2(11) + - NIST-800-53-IA-2(2) + - NIST-800-53-IA-2(3) + - NIST-800-53-IA-2(4) + - NIST-800-53-IA-2(6) + - NIST-800-53-IA-2(7) + - PCI-DSS-Req-8.3 + - configure_strategy + - force_opensc_card_drivers + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Force smartcard driver block + block: + + - name: Check if force_card_driver is defined + command: /usr/bin/opensc-tool -G app:default:force_card_driver + changed_when: false + register: force_card_driver + + - name: Force opensc To Use Defined Smart Card Driver + command: | + /usr/bin/opensc-tool -S app:default:force_card_driver:{{ var_smartcard_drivers }} + when: + - force_card_driver.stdout != var_smartcard_drivers + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - opensc_conf_fcd.stat.exists + tags: + - CCE-80821-2 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-2(1) + - NIST-800-53-IA-2(11) + - NIST-800-53-IA-2(2) + - NIST-800-53-IA-2(3) + - NIST-800-53-IA-2(4) + - NIST-800-53-IA-2(6) + - NIST-800-53-IA-2(7) + - PCI-DSS-Req-8.3 + - configure_strategy + - force_opensc_card_drivers + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_smartcard_drivers='' + + +OPENSC_TOOL="/usr/bin/opensc-tool" + +if [ -f "${OPENSC_TOOL}" ]; then + ${OPENSC_TOOL} -S app:default:force_card_driver:$var_smartcard_drivers +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + + + + Protect Accounts by Restricting Password-Based Login + Conventionally, Unix shell accounts are accessed by +providing a username and password to a login program, which tests +these values for correctness using the /etc/passwd and +/etc/shadow files. Password-based login is vulnerable to +guessing of weak passwords, and to sniffing and man-in-the-middle +attacks against passwords entered over a network or at an insecure +console. Therefore, mechanisms for accessing accounts by entering +usernames and passwords should be restricted to those which are +operationally necessary. + + Accounts Authorized Local Users on the Operating System + List the user accounts that are authorized locally on the operating system. This list +includes both users requried by the operating system and by the installed applications. +Depending on the Operating System distribution, version, software groups and applications, +the user list is different and can be customized with scap-workbench. +OVAL regular expression is used for the user list. +The list starts with '^' and ends with '$' so that it matches exactly the +username, not any string that includes the username. Users are separated with '|'. +For example, three users: bin, oracle and sapadm are allowed, then the list is +^(bin|oracle|sapadm)$. The user root is the only user that is hard coded +in OVAL that is always allowed on the operating system. + ^(abrt|adm|avahi|bin|chrony|clevis|cockpit-ws|cockpit-wsinstance|colord|daemon|dbus|dnsmasq|flatpak|ftp|games|gdm|geoclue|gluster|gnome-initial-setup|halt|libstoragemgmt|lp|mail|nfsnobody|nobody|ntp|operator|oprofile|oracle|pcp|pegasus|pipewire|polkitd|postfix|pulse|qemu|radvd|rngd|root|rpc|rpcuser|rtkit|saned|saslauth|setroubleshoot|shutdown|sshd|sssd|sync|systemd-bus-proxy|systemd-coredump|systemd-network|systemd-resolve|tcpdump|tss|unbound|usbmuxd$|uuidd)$ + ^(abrt|adm|avahi|bin|chrony|clevis|cockpit-ws|cockpit-wsinstance|colord|daemon|dbus|dnsmasq|flatpak|ftp|games|gdm|geoclue|gluster|gnome-initial-setup|halt|libstoragemgmt|lp|mail|nfsnobody|nobody|ntp|operator|oprofile|oracle|pcp|pegasus|pipewire|polkitd|postfix|pulse|qemu|radvd|rngd|root|rpc|rpcuser|rtkit|saned|saslauth|setroubleshoot|shutdown|sshd|sssd|sync|systemd-bus-proxy|systemd-coredump|systemd-network|systemd-resolve|tcpdump|tss|unbound|usbmuxd$|uuidd)$ + ^(root|bin|daemon|adm|lp|sync|shutdown|halt|mail|operator|games|ftp|nobody|pegasus|systemd-bus-proxy|systemd-network|dbus|polkitd|abrt|unbound|tss|libstoragemgmt|rpc|colord|usbmuxd$|pcp|saslauth|geoclue|setroubleshoot|rtkit|chrony|qemu|radvd|rpcuser|nfsnobody|pulse|gdm|gnome-initial-setup|postfix|avahi|ntp|sshd|tcpdump|oprofile|uuidd)$ + ^(root|bin|daemon|adm|lp|sync|shutdown|halt|mail|operator|games|ftp|nobody|pegasus|systemd-bus-proxy|systemd-network|dbus|polkitd|abrt|unbound|tss|libstoragemgmt|rpc|colord|usbmuxd$|pcp|saslauth|geoclue|setroubleshoot|rtkit|chrony|qemu|radvd|rpcuser|nfsnobody|pulse|gdm|gnome-initial-setup|postfix|avahi|ntp|sshd|tcpdump|oprofile|uuidd|systemd-resolve|systemd-coredump|sssd|rngd)$ + ^(root|bin|daemon|adm|lp|sync|shutdown|halt|mail|operator|games|ftp|nobody|pegasus|systemd-bus-proxy|systemd-network|dbus|polkitd|abrt|unbound|tss|libstoragemgmt|rpc|colord|usbmuxd$|pcp|saslauth|geoclue|setroubleshoot|rtkit|chrony|qemu|radvd|rpcuser|nfsnobody|pulse|gdm|gnome-initial-setup|postfix|avahi|ntp|sshd|tcpdump|oprofile|uuidd|systemd-resolve|systemd-coredump|sssd|rngd)$ + ^(root|bin|daemon|adm|lp|sync|shutdown|halt|mail|operator|games|ftp|nobody|pegasus|systemd-bus-proxy|systemd-network|dbus|polkitd|abrt|unbound|tss|libstoragemgmt|rpc|colord|usbmuxd$|pcp|saslauth|geoclue|setroubleshoot|rtkit|chrony|qemu|radvd|rpcuser|nfsnobody|pulse|gdm|gnome-initial-setup|postfix|avahi|ntp|sshd|tcpdump|oprofile|uuidd|systemd-resolve|systemd-coredump|sssd|rngd|man|systemd-timesync|scard|hacluster|statd|at|dockremap|vnc)$ + ^(root|bin|daemon|adm|lp|sync|shutdown|halt|mail|operator|games|ftp|nobody|pegasus|systemd-bus-proxy|systemd-network|dbus|polkitd|abrt|unbound|tss|libstoragemgmt|rpc|colord|usbmuxd$|pcp|saslauth|geoclue|setroubleshoot|rtkit|chrony|qemu|radvd|rpcuser|nfsnobody|pulse|gdm|gnome-initial-setup|postfix|avahi|ntp|sshd|tcpdump|oprofile|uuidd|systemd-resolve|systemd-coredump|sssd|rngd|man|systemd-timesync|scard|hacluster|statd|at|dockremap|vnc|messagebus|nscd)$ + + + Ensure All Accounts on the System Have Unique User IDs + Change user IDs (UIDs), or delete accounts, so each has a unique name. + Automatic remediation of this control is not available due to unique requirements of each +system. + CCI-000135 + CCI-000764 + CCI-000804 + SRG-OS-000104-GPOS-00051 + SRG-OS-000121-GPOS-00062 + SRG-OS-000042-GPOS-00020 + RHEL-08-020240 + 6.2.3 + SV-230371r627750_rule + To assure accountability and prevent unauthenticated access, interactive users must be identified and authenticated to prevent potential misuse and compromise of the system. + + CCE-89903-9 + + + + + + + + + Only Authorized Local User Accounts Exist on Operating System + Enterprise Application tends to use the server or virtual machine exclusively. +Besides the default operating system user, there should be only authorized local +users required by the installed software groups and applications that exist on +the operating system. The authorized user list can be customized in the refine +value variable var_accounts_authorized_local_users_regex. +OVAL regular expression is used for the user list. +Configure the system so all accounts on the system are assigned to an active system, +application, or user account. Remove accounts that do not support approved system +activities or that allow for a normal user to perform administrative-level actions. +To remove unauthorized system accounts, use the following command: +$ sudo userdel unauthorized_user + Automatic remediation of this control is not available due to the unique +requirements of each system. + CCI-000366 + SRG-OS-000480-GPOS-00227 + RHEL-08-020320 + SV-230379r627750_rule + Accounts providing no operational purpose provide additional opportunities for +system compromise. Unnecessary accounts include user accounts for individuals not +requiring access to the system and application accounts for applications not installed +on the system. + CCE-85987-6 + + + + + + + + + + Ensure All Groups on the System Have Unique Group ID + Change the group name or delete groups, so each has a unique id. + Automatic remediation of this control is not available due to the unique requirements of each system. + CCI-000764 + SRG-OS-000104-GPOS-00051 + 6.2.4 + To assure accountability and prevent unauthenticated access, groups must be identified uniquely to prevent potential misuse and compromise of the system. + CCE-86201-1 + + + + + + + + + Ensure All Groups on the System Have Unique Group Names + Change the group name or delete groups, so each has a unique name. + Automatic remediation of this control is not available due to the unique requirements of each system. + 6.2.6 + To assure accountability and prevent unauthenticated access, groups must be identified uniquely to prevent potential misuse and compromise of the system. + CCE-86328-2 + + + + + + + + + Set Account Expiration Parameters + Accounts can be configured to be automatically disabled +after a certain time period, +meaning that they will require administrator interaction to become usable again. +Expiration of accounts after inactivity can be set for all accounts by default +and also on a per-account basis, such as for accounts that are known to be temporary. +To configure automatic expiration of an account following +the expiration of its password (that is, after the password has expired and not been changed), +run the following command, substituting NUM_DAYS and USER appropriately: +$ sudo chage -I NUM_DAYS USER +Accounts, such as temporary accounts, can also be configured to expire on an explicitly-set date with the +-E option. +The file /etc/default/useradd controls +default settings for all newly-created accounts created with the system's +normal command line utilities. + This will only apply to newly created accounts + + number of days after the last login of the user when the user will be locked out + 'This option is specific for the auth or account phase. It specifies the number of days after +the last login of the user when the user will be locked out by the pam_lastlog module.' + 0 + 180 + 30 + 35 + 40 + 60 + 90 + 35 + + + number of days after a password expires until the account is permanently disabled + The number of days to wait after a password expires, until the account will be permanently disabled. + 0 + 180 + 30 + 35 + 40 + 60 + 90 + 35 + + + Set Account Expiration Following Inactivity + To specify the number of days after a password expires (which +signifies inactivity) until an account is permanently disabled, add or correct +the following line in /etc/default/useradd: +INACTIVE= +If a password is currently on the verge of expiration, then + +day(s) remain(s) until the account is automatically +disabled. However, if the password will not expire for another 60 days, then 60 +days plus day(s) could +elapse until the account would be automatically disabled. See the +useradd man page for more information. + 1 + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + 7 + 8 + 5.6.2.1.1 + DSS01.03 + DSS03.05 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.03 + DSS06.10 + 3.5.6 + CCI-000017 + CCI-000795 + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 6.2 + A.12.4.1 + A.12.4.3 + A.18.1.4 + A.6.1.2 + A.7.1.1 + A.9.1.2 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.1 + A.9.4.2 + A.9.4.3 + A.9.4.4 + A.9.4.5 + CIP-004-6 R2.2.2 + CIP-004-6 R2.2.3 + CIP-007-3 R.1.3 + CIP-007-3 R5 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.3 + CIP-007-3 R5.2.1 + CIP-007-3 R5.2.3 + IA-4(e) + AC-2(3) + CM-6(a) + DE.CM-1 + DE.CM-3 + PR.AC-1 + PR.AC-4 + PR.AC-6 + PR.AC-7 + Req-8.1.4 + SRG-OS-000118-GPOS-00060 + SRG-OS-000003-VMM-000030 + SRG-OS-000118-VMM-000590 + RHEL-08-020260 + 5.6.1.4 + SV-230373r627750_rule + Inactive identifiers pose a risk to systems and applications because attackers may exploit an inactive identifier and potentially obtain undetected access to the system. +Disabling inactive accounts ensures that accounts which may not have been responsibly removed are not available to attackers who may have compromised their credentials. +Owners of inactive accounts will not notice if unauthorized access to their user account has been obtained. + + CCE-80954-1 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80954-1 + - CJIS-5.6.2.1.1 + - DISA-STIG-RHEL-08-020260 + - NIST-800-171-3.5.6 + - NIST-800-53-AC-2(3) + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-4(e) + - PCI-DSS-Req-8.1.4 + - account_disable_post_pw_expiration + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy +- name: XCCDF Value var_account_disable_post_pw_expiration # promote to variable + set_fact: + var_account_disable_post_pw_expiration: !!str + tags: + - always + +- name: Set Account Expiration Following Inactivity + lineinfile: + create: true + dest: /etc/default/useradd + regexp: ^INACTIVE + line: INACTIVE={{ var_account_disable_post_pw_expiration }} + when: '"shadow-utils" in ansible_facts.packages' + tags: + - CCE-80954-1 + - CJIS-5.6.2.1.1 + - DISA-STIG-RHEL-08-020260 + - NIST-800-171-3.5.6 + - NIST-800-53-AC-2(3) + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-4(e) + - PCI-DSS-Req-8.1.4 + - account_disable_post_pw_expiration + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q shadow-utils; then + +var_account_disable_post_pw_expiration='' + + +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/default/useradd"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^INACTIVE") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s=%s" "$stripped_key" "$var_account_disable_post_pw_expiration" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^INACTIVE\\>" "/etc/default/useradd"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^INACTIVE\\>.*/$escaped_formatted_output/gi" "/etc/default/useradd" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-80954-1" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/default/useradd" >> "/etc/default/useradd" + printf '%s\n' "$formatted_output" >> "/etc/default/useradd" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Assign Expiration Date to Emergency Accounts + Emergency accounts are privileged accounts established in response to +crisis situations where the need for rapid account activation is required. +In the event emergency accounts are required, configure the system to +terminate them after a documented time period. For every emergency account, +run the following command to set an expiration date on it, substituting +ACCOUNT_NAME and YYYY-MM-DD +appropriately: +$ sudo chage -E YYYY-MM-DD ACCOUNT_NAME +YYYY-MM-DD indicates the documented expiration date for the +account. For U.S. Government systems, the operating system must be +configured to automatically terminate these types of accounts after a +period of 72 hours. + Due to the unique requirements of each sysetem, automated +remediation is not available for this configuration check. + 1 + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + 7 + 8 + DSS01.03 + DSS03.05 + DSS05.04 + DSS05.05 + DSS05.07 + DSS06.03 + CCI-000016 + CCI-001682 + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + SR 1.1 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 6.2 + A.12.4.1 + A.12.4.3 + A.6.1.2 + A.7.1.1 + A.9.1.2 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.1 + A.9.4.2 + A.9.4.3 + A.9.4.4 + A.9.4.5 + AC-2(2) + AC-2(3) + CM-6(a) + DE.CM-1 + DE.CM-3 + PR.AC-1 + PR.AC-4 + PR.AC-6 + SRG-OS-000123-GPOS-00064 + SRG-OS-000002-GPOS-00002 + SRG-OS-000002-VMM-000020 + SRG-OS-000123-VMM-000620 + RHEL-08-020270 + SV-230374r627750_rule + If emergency user accounts remain active when no longer needed or for +an excessive period, these accounts may be used to gain unauthorized access. +To mitigate this risk, automated termination of all emergency accounts +must be set upon account creation. + + CCE-85910-8 + + + + + + Assign Expiration Date to Temporary Accounts + Temporary accounts are established as part of normal account activation +procedures when there is a need for short-term accounts. In the event +temporary accounts are required, configure the system to +terminate them after a documented time period. For every temporary account, run the following command to set an expiration date on +it, substituting USER and YYYY-MM-DD +appropriately: +$ sudo chage -E YYYY-MM-DD USER +YYYY-MM-DD indicates the documented expiration date for the +account. For U.S. Government systems, the operating system must be +configured to automatically terminate these types of accounts after a +period of 72 hours. + 1 + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + 7 + 8 + DSS01.03 + DSS03.05 + DSS05.04 + DSS05.05 + DSS05.07 + DSS06.03 + CCI-000016 + CCI-001682 + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + SR 1.1 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 6.2 + A.12.4.1 + A.12.4.3 + A.6.1.2 + A.7.1.1 + A.9.1.2 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.1 + A.9.4.2 + A.9.4.3 + A.9.4.4 + A.9.4.5 + AC-2(2) + AC-2(3) + CM-6(a) + DE.CM-1 + DE.CM-3 + PR.AC-1 + PR.AC-4 + PR.AC-6 + SRG-OS-000123-GPOS-00064 + SRG-OS-000002-GPOS-00002 + SRG-OS-000002-VMM-000020 + SRG-OS-000123-VMM-000620 + RHEL-08-020000 + SV-230331r627750_rule + If temporary user accounts remain active when no longer needed or for +an excessive period, these accounts may be used to gain unauthorized access. +To mitigate this risk, automated termination of all temporary accounts +must be set upon account creation. + + CCE-82474-8 + + + + + + Ensure All Accounts on the System Have Unique Names + Ensure accounts on the system have unique names. + +To ensure all accounts have unique names, run the following command: +$ sudo getent passwd | awk -F: '{ print $1}' | uniq -d +If a username is returned, change or delete the username. + 5.5.2 + CCI-000770 + CCI-000804 + Req-8.1.1 + 6.2.5 + Unique usernames allow for accountability on the system. + CCE-80674-5 + + + + + + + + + Use Centralized and Automated Authentication + Implement an automated system for managing user accounts that minimizes the +risk of errors, either intentional or deliberate. This system +should integrate with an existing enterprise user management system, such as +one based on Identity Management tools such as Active Directory, Kerberos, +Directory Server, etc. + A comprehensive account management process that includes automation helps to +ensure the accounts designated as requiring attention are consistently and +promptly addressed. Enterprise environments make user account management +challenging and complex. A user management process requiring administrators to +manually address account management functions adds risk of potential +oversight. + + + + + + + Set Password Expiration Parameters + The file /etc/login.defs controls several +password-related settings. Programs such as passwd, +su, and +login consult /etc/login.defs to determine +behavior with regard to password aging, expiration warnings, +and length. See the man page login.defs(5) for more information. + +Users should be forced to change their passwords, in order to +decrease the utility of compromised passwords. However, the need to +change passwords often should be balanced against the risk that +users will reuse or write down passwords if forced to change them +too often. Forcing password changes every 90-360 days, depending on +the environment, is recommended. Set the appropriate value as +PASS_MAX_DAYS and apply it to existing accounts with the +-M flag. + +The PASS_MIN_DAYS (-m) setting prevents password +changes for 7 days after the first change, to discourage password +cycling. If you use this setting, train users to contact an administrator +for an emergency password change in case a new password becomes +compromised. The PASS_WARN_AGE (-W) setting gives +users 7 days of warnings at login time that their passwords are about to expire. + +For example, for each existing human user USER, expiration parameters +could be adjusted to a 180 day maximum password age, 7 day minimum password +age, and 7 day warning period with the following command: +$ sudo chage -M 180 -m 7 -W 7 USER + + maximum password age + Maximum age of password in days + 365 + 120 + 180 + 60 + 90 + 60 + + + minimum password age + Minimum age of password in days + 0 + 1 + 2 + 5 + 7 + 7 + + + minimum password length + Minimum number of characters in password + This will only check new passwords + 10 + 12 + 14 + 15 + 18 + 20 + 6 + 8 + 15 + + + warning days before password expires + The number of days' warning given before a password expires. + This will only apply to newly created accounts + 0 + 14 + 7 + 7 + + + Set Password Maximum Age + To specify password maximum age for new accounts, +edit the file /etc/login.defs +and add or correct the following line: +PASS_MAX_DAYS +A value of 180 days is sufficient for many environments. +The DoD requirement is 60. +The profile requirement is . + BP28(R18) + 1 + 12 + 15 + 16 + 5 + 5.6.2.1 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.03 + DSS06.10 + 3.5.6 + CCI-000199 + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.2 + 4.3.3.7.4 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + 0418 + 1055 + 1402 + A.18.1.4 + A.7.1.1 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.2 + A.9.4.3 + IA-5(f) + IA-5(1)(d) + CM-6(a) + PR.AC-1 + PR.AC-6 + PR.AC-7 + Req-8.2.4 + SRG-OS-000076-GPOS-00044 + RHEL-08-020200 + 5.6.1.1 + SV-230366r646878_rule + Any password, no matter how complex, can eventually be cracked. Therefore, passwords +need to be changed periodically. If the operating system does not limit the lifetime +of passwords and force users to change their passwords, there is the risk that the +operating system passwords could be compromised. + +Setting the password maximum age ensures users are required to +periodically change their passwords. Requiring shorter password lifetimes +increases the risk of users writing down the password in a convenient +location subject to physical compromise. + + CCE-80647-1 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80647-1 + - CJIS-5.6.2.1 + - DISA-STIG-RHEL-08-020200 + - NIST-800-171-3.5.6 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(d) + - NIST-800-53-IA-5(f) + - PCI-DSS-Req-8.2.4 + - accounts_maximum_age_login_defs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy +- name: XCCDF Value var_accounts_maximum_age_login_defs # promote to variable + set_fact: + var_accounts_maximum_age_login_defs: !!str + tags: + - always + +- name: Set Password Maximum Age + lineinfile: + create: true + dest: /etc/login.defs + regexp: ^#?PASS_MAX_DAYS + line: PASS_MAX_DAYS {{ var_accounts_maximum_age_login_defs }} + when: '"shadow-utils" in ansible_facts.packages' + tags: + - CCE-80647-1 + - CJIS-5.6.2.1 + - DISA-STIG-RHEL-08-020200 + - NIST-800-171-3.5.6 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(d) + - NIST-800-53-IA-5(f) + - PCI-DSS-Req-8.2.4 + - accounts_maximum_age_login_defs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q shadow-utils; then + +var_accounts_maximum_age_login_defs='' + + +grep -q ^PASS_MAX_DAYS /etc/login.defs && \ + sed -i "s/PASS_MAX_DAYS.*/PASS_MAX_DAYS $var_accounts_maximum_age_login_defs/g" /etc/login.defs +if ! [ $? -eq 0 ]; then + echo "PASS_MAX_DAYS $var_accounts_maximum_age_login_defs" >> /etc/login.defs +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Set Password Minimum Age + To specify password minimum age for new accounts, +edit the file /etc/login.defs +and add or correct the following line: +PASS_MIN_DAYS +A value of 1 day is considered sufficient for many +environments. The DoD requirement is 1. +The profile requirement is . + 1 + 12 + 15 + 16 + 5 + 5.6.2.1.1 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.03 + DSS06.10 + 3.5.8 + CCI-000198 + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.2 + 4.3.3.7.4 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + 0418 + 1055 + 1402 + A.18.1.4 + A.7.1.1 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.2 + A.9.4.3 + IA-5(f) + IA-5(1)(d) + CM-6(a) + PR.AC-1 + PR.AC-6 + PR.AC-7 + SRG-OS-000075-GPOS-00043 + RHEL-08-020190 + 5.6.1.2 + SV-230365r627750_rule + Enforcing a minimum password lifetime helps to prevent repeated password +changes to defeat the password reuse or history enforcement requirement. If +users are allowed to immediately and continually change their password, +then the password could be repeatedly changed in a short period of time to +defeat the organization's policy regarding password reuse. + +Setting the minimum password age protects against users cycling back to a +favorite password after satisfying the password reuse requirement. + + CCE-80648-9 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80648-9 + - CJIS-5.6.2.1.1 + - DISA-STIG-RHEL-08-020190 + - NIST-800-171-3.5.8 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(d) + - NIST-800-53-IA-5(f) + - accounts_minimum_age_login_defs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy +- name: XCCDF Value var_accounts_minimum_age_login_defs # promote to variable + set_fact: + var_accounts_minimum_age_login_defs: !!str + tags: + - always + +- name: Set Password Minimum Age + lineinfile: + create: true + dest: /etc/login.defs + regexp: ^#?PASS_MIN_DAYS + line: PASS_MIN_DAYS {{ var_accounts_minimum_age_login_defs }} + when: '"shadow-utils" in ansible_facts.packages' + tags: + - CCE-80648-9 + - CJIS-5.6.2.1.1 + - DISA-STIG-RHEL-08-020190 + - NIST-800-171-3.5.8 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(d) + - NIST-800-53-IA-5(f) + - accounts_minimum_age_login_defs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q shadow-utils; then + +var_accounts_minimum_age_login_defs='' + + +grep -q ^PASS_MIN_DAYS /etc/login.defs && \ + sed -i "s/PASS_MIN_DAYS.*/PASS_MIN_DAYS $var_accounts_minimum_age_login_defs/g" /etc/login.defs +if ! [ $? -eq 0 ]; then + echo "PASS_MIN_DAYS $var_accounts_minimum_age_login_defs" >> /etc/login.defs +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Set Password Minimum Length in login.defs + To specify password length requirements for new accounts, edit the file +/etc/login.defs and add or correct the following line: +PASS_MIN_LEN + +The DoD requirement is 15. +The FISMA requirement is 12. +The profile requirement is +. +If a program consults /etc/login.defs and also another PAM module +(such as pam_pwquality) during a password change operation, then +the most restrictive must be satisfied. See PAM section for more +information about enforcing password quality requirements. + BP28(R18) + 1 + 12 + 15 + 16 + 5 + 5.6.2.1 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.03 + DSS06.10 + 3.5.7 + CCI-000205 + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.2 + 4.3.3.7.4 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + 0421 + 0422 + 0431 + 0974 + 1173 + 1401 + 1504 + 1505 + 1546 + 1557 + 1558 + 1559 + 1560 + 1561 + A.18.1.4 + A.7.1.1 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.2 + A.9.4.3 + IA-5(f) + IA-5(1)(a) + CM-6(a) + PR.AC-1 + PR.AC-6 + PR.AC-7 + SRG-OS-000078-GPOS-00046 + Requiring a minimum password length makes password +cracking attacks more difficult by ensuring a larger +search space. However, any security benefit from an onerous requirement +must be carefully weighed against usability problems, support costs, or counterproductive +behavior that may result. + + CCE-80652-1 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80652-1 + - CJIS-5.6.2.1 + - NIST-800-171-3.5.7 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(a) + - NIST-800-53-IA-5(f) + - accounts_password_minlen_login_defs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy +- name: XCCDF Value var_accounts_password_minlen_login_defs # promote to variable + set_fact: + var_accounts_password_minlen_login_defs: !!str + tags: + - always + +- name: Set Password Minimum Length in login.defs + lineinfile: + dest: /etc/login.defs + regexp: ^PASS_MIN_LEN *[0-9]* + state: present + line: PASS_MIN_LEN {{ var_accounts_password_minlen_login_defs }} + create: true + when: '"shadow-utils" in ansible_facts.packages' + tags: + - CCE-80652-1 + - CJIS-5.6.2.1 + - NIST-800-171-3.5.7 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(a) + - NIST-800-53-IA-5(f) + - accounts_password_minlen_login_defs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q shadow-utils; then + +var_accounts_password_minlen_login_defs='' + + +grep -q ^PASS_MIN_LEN /etc/login.defs && \ +sed -i "s/PASS_MIN_LEN.*/PASS_MIN_LEN\t$var_accounts_password_minlen_login_defs/g" /etc/login.defs +if ! [ $? -eq 0 ] +then + echo -e "PASS_MIN_LEN\t$var_accounts_password_minlen_login_defs" >> /etc/login.defs +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Set Existing Passwords Maximum Age + Configure non-compliant accounts to enforce a -day maximum password lifetime +restriction by running the following command: +$ sudo chage -M USER + CCI-000199 + IA-5(f) + IA-5(1)(d) + CM-6(a) + SRG-OS-000076-GPOS-00044 + SRG-OS-000076-VMM-000430 + RHEL-08-020210 + 5.6.1.1 + SV-230367r627750_rule + Any password, no matter how complex, can eventually be cracked. Therefore, +passwords need to be changed periodically. If the operating system does +not limit the lifetime of passwords and force users to change their +passwords, there is the risk that the operating system passwords could be +compromised. + CCE-82473-0 + - name: XCCDF Value var_accounts_maximum_age_login_defs # promote to variable + set_fact: + var_accounts_maximum_age_login_defs: !!str + tags: + - always + +- name: Collect users with not correct maximum time period between password changes + ansible.builtin.command: | + awk -F: '$5 > {{ var_accounts_maximum_age_login_defs }} || $5 == "" {print $1}' /etc/shadow + register: user_names + tags: + - CCE-82473-0 + - DISA-STIG-RHEL-08-020210 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(d) + - NIST-800-53-IA-5(f) + - accounts_password_set_max_life_existing + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Change the maximum time period between password changes + ansible.builtin.user: + user: '{{ item }}' + password_expire_max: '{{ var_accounts_maximum_age_login_defs }}' + with_items: '{{ user_names.stdout_lines }}' + when: user_names.stdout_lines | length > 0 + tags: + - CCE-82473-0 + - DISA-STIG-RHEL-08-020210 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(d) + - NIST-800-53-IA-5(f) + - accounts_password_set_max_life_existing + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + +var_accounts_maximum_age_login_defs='' + + +while IFS= read -r i; do + chage -M $var_accounts_maximum_age_login_defs $i +done < <(awk -v var="$var_accounts_maximum_age_login_defs" -F: '$5 > var || $5 == "" {print $1}' /etc/shadow) + + + + + + + + + + + + Set Existing Passwords Minimum Age + Configure non-compliant accounts to enforce a 24 hours/1 day minimum password +lifetime by running the following command: +$ sudo chage -m 1 USER + CCI-000198 + IA-5(f) + IA-5(1)(d) + CM-6(a) + SRG-OS-000075-GPOS-00043 + SRG-OS-000075-VMM000420 + RHEL-08-020180 + 5.6.1.2 + SV-230364r627750_rule + Enforcing a minimum password lifetime helps to prevent repeated password +changes to defeat the password reuse or history enforcement requirement. If +users are allowed to immediately and continually change their password, the +password could be repeatedly changed in a short period of time to defeat the +organization's policy regarding password reuse. + CCE-82472-2 + +var_accounts_minimum_age_login_defs='' + + +while IFS= read -r i; do + passwd -n $var_accounts_minimum_age_login_defs $i +done < <(awk -v var="$var_accounts_minimum_age_login_defs" -F: '$4 < var || $4 == "" {print $1}' /etc/shadow) + + + + + + + + + + + + Set Password Warning Age + To specify how many days prior to password +expiration that a warning will be issued to users, +edit the file /etc/login.defs and add or correct + the following line: +PASS_WARN_AGE +The DoD requirement is 7. +The profile requirement is . + 1 + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + 7 + 8 + DSS01.03 + DSS03.05 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.03 + DSS06.10 + 3.5.8 + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 6.2 + 0418 + 1055 + 1402 + A.12.4.1 + A.12.4.3 + A.18.1.4 + A.6.1.2 + A.7.1.1 + A.9.1.2 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.1 + A.9.4.2 + A.9.4.3 + A.9.4.4 + A.9.4.5 + IA-5(f) + IA-5(1)(d) + CM-6(a) + DE.CM-1 + DE.CM-3 + PR.AC-1 + PR.AC-4 + PR.AC-6 + PR.AC-7 + 5.6.1.3 + Setting the password warning age enables users to +make the change at a practical time. + + CCE-80671-1 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80671-1 + - NIST-800-171-3.5.8 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(d) + - NIST-800-53-IA-5(f) + - accounts_password_warn_age_login_defs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy +- name: XCCDF Value var_accounts_password_warn_age_login_defs # promote to variable + set_fact: + var_accounts_password_warn_age_login_defs: !!str + tags: + - always + +- name: Set Password Warning Age + lineinfile: + dest: /etc/login.defs + regexp: ^PASS_WARN_AGE *[0-9]* + state: present + line: PASS_WARN_AGE {{ var_accounts_password_warn_age_login_defs }} + create: true + when: '"shadow-utils" in ansible_facts.packages' + tags: + - CCE-80671-1 + - NIST-800-171-3.5.8 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(d) + - NIST-800-53-IA-5(f) + - accounts_password_warn_age_login_defs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q shadow-utils; then + +var_accounts_password_warn_age_login_defs='' + + +grep -q ^PASS_WARN_AGE /etc/login.defs && \ +sed -i "s/PASS_WARN_AGE.*/PASS_WARN_AGE\t$var_accounts_password_warn_age_login_defs/g" /etc/login.defs +if ! [ $? -eq 0 ] +then + echo -e "PASS_WARN_AGE\t$var_accounts_password_warn_age_login_defs" >> /etc/login.defs +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + + Verify Proper Storage and Existence of Password +Hashes + By default, password hashes for local accounts are stored +in the second field (colon-separated) in +/etc/shadow. This file should be readable only by +processes running with root credentials, preventing users from +casually accessing others' password hashes and attempting +to crack them. +However, it remains possible to misconfigure the system +and store password hashes +in world-readable files such as /etc/passwd, or +to even store passwords themselves in plaintext on the system. +Using system-provided tools for password change/creation +should allow administrators to avoid such misconfiguration. + + Password Hashing algorithm + Specify the number of SHA rounds for the system password encryption algorithm. +Defines the value set in /etc/pam.d/system-auth and /etc/pam.d/password-auth + 5000 + 5000 + 65536 + + + Verify All Account Password Hashes are Shadowed + If any password hashes are stored in /etc/passwd (in the second field, +instead of an x or *), the cause of this misconfiguration should be +investigated. The account should have its password reset and the hash should be +properly stored, or the account should be deleted entirely. + 1 + 12 + 15 + 16 + 5 + 5.5.2 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.03 + DSS06.10 + 3.5.10 + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.2 + 4.3.3.7.4 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + 1410 + A.18.1.4 + A.7.1.1 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.2 + A.9.4.3 + IA-5(h) + CM-6(a) + PR.AC-1 + PR.AC-6 + PR.AC-7 + Req-8.2.1 + The hashes for all user account passwords should be stored in +the file /etc/shadow and never in /etc/passwd, +which is readable by all users. + + CCE-80651-3 + + + + + + + + + Verify All Account Password Hashes are Shadowed with SHA512 + Verify the operating system requires the shadow password suite +configuration be set to encrypt interactive user passwords using a strong +cryptographic hash. +Check that the interactive user account passwords are using a strong +password hash with the following command: +$ sudo cut -d: -f2 /etc/shadow +$6$kcOnRq/5$NUEYPuyL.wghQwWssXRcLRFiiru7f5JPV6GaJhNC2aK5F3PZpE/BCCtwrxRc/AInKMNX3CdMw11m9STiql12f/ +Password hashes ! or * indicate inactive accounts not +available for logon and are not evaluated. +If any interactive user password hash does not begin with $6, +this is a finding. + CCI-000196 + CCI-000803 + IA-5(1)(c) + IA-5(1).1(v) + IA-7 + IA-7.1 + SRG-OS-000073-GPOS-00041 + SRG-OS-000120-GPOS-00061 + RHEL-08-010120 + SV-230232r627750_rule + Passwords need to be protected at all times, and encryption is the standard method for +protecting passwords. If passwords are not encrypted, they can be plainly read +(i.e., clear text) and easily compromised. + CCE-83484-6 + + + + + + + + + Set number of Password Hashing Rounds - password-auth + Configure the number or rounds for the password hashing algorithm. This can be +accomplished by using the rounds option for the pam_unix PAM module. + +In file /etc/pam.d/password-auth append rounds= +to the pam_unix.so entry, as shown below: +password sufficient pam_unix.so ...existing_options... rounds= +The system's default number of rounds is 5000. + Setting a high number of hashing rounds makes it more difficult to brute force the password, +but requires more CPU resources to authenticate users. + BP28(R32) + CCI-000196 + SRG-OS-000073-GPOS-00041 + Using a higher number of rounds makes password cracking attacks more difficult. + + CCE-83403-6 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83403-6 + - accounts_password_pam_unix_rounds_password_auth + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed +- name: XCCDF Value var_password_pam_unix_rounds # promote to variable + set_fact: + var_password_pam_unix_rounds: !!str + tags: + - always + +- name: Set number of Password Hashing Rounds - password-auth - Check if /etc/pam.d/password-auth + file is present + ansible.builtin.stat: + path: /etc/pam.d/password-auth + register: result_pam_file_present + when: '"pam" in ansible_facts.packages' + tags: + - CCE-83403-6 + - accounts_password_pam_unix_rounds_password_auth + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + +- name: Set number of Password Hashing Rounds - password-auth - Check the proper remediation + for the system + block: + + - name: Set number of Password Hashing Rounds - password-auth - Define the PAM file + to be edited as a local fact + ansible.builtin.set_fact: + pam_file_path: /etc/pam.d/password-auth + + - name: Set number of Password Hashing Rounds - password-auth - Check if system + relies on authselect + ansible.builtin.stat: + path: /usr/bin/authselect + register: result_authselect_present + + - name: Set number of Password Hashing Rounds - password-auth - Remediate using + authselect + block: + + - name: Set number of Password Hashing Rounds - password-auth - Check integrity + of authselect current profile + ansible.builtin.command: + cmd: authselect check + register: result_authselect_check_cmd + changed_when: false + ignore_errors: true + + - name: Set number of Password Hashing Rounds - password-auth - Informative message + based on the authselect integrity check result + ansible.builtin.assert: + that: + - result_authselect_check_cmd is success + fail_msg: + - authselect integrity check failed. Remediation aborted! + - This remediation could not be applied because an authselect profile was + not selected or the selected profile is not intact. + - It is not recommended to manually edit the PAM files when authselect tool + is available. + - In cases where the default authselect profile does not cover a specific + demand, a custom authselect profile is recommended. + success_msg: + - authselect integrity check passed + + - name: Set number of Password Hashing Rounds - password-auth - Get authselect + current profile + ansible.builtin.shell: + cmd: authselect current -r | awk '{ print $1 }' + register: result_authselect_profile + changed_when: false + when: + - result_authselect_check_cmd is success + + - name: Set number of Password Hashing Rounds - password-auth - Define the current + authselect profile as a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: '{{ result_authselect_profile.stdout }}' + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is match("custom/") + + - name: Set number of Password Hashing Rounds - password-auth - Define the new + authselect custom profile as a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: custom/hardening + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is not match("custom/") + + - name: Set number of Password Hashing Rounds - password-auth - Get authselect + current features to also enable them in the custom profile + ansible.builtin.shell: + cmd: authselect current | tail -n+3 | awk '{ print $2 }' + register: result_authselect_features + changed_when: false + when: + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + + - name: Set number of Password Hashing Rounds - password-auth - Check if any custom + profile with the same name was already created + ansible.builtin.stat: + path: /etc/authselect/{{ authselect_custom_profile }} + register: result_authselect_custom_profile_present + changed_when: false + when: + - authselect_current_profile is not match("custom/") + + - name: Set number of Password Hashing Rounds - password-auth - Create an authselect + custom profile based on the current profile + ansible.builtin.command: + cmd: authselect create-profile hardening -b {{ authselect_current_profile + }} + when: + - result_authselect_check_cmd is success + - authselect_current_profile is not match("custom/") + - not result_authselect_custom_profile_present.stat.exists + + - name: Set number of Password Hashing Rounds - password-auth - Ensure authselect + changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=before-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Set number of Password Hashing Rounds - password-auth - Ensure the authselect + custom profile is selected + ansible.builtin.command: + cmd: authselect select {{ authselect_custom_profile }} + register: result_pam_authselect_select_profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Set number of Password Hashing Rounds - password-auth - Restore the authselect + features in the custom profile + ansible.builtin.command: + cmd: authselect enable-feature {{ item }} + loop: '{{ result_authselect_features.stdout_lines }}' + register: result_pam_authselect_restore_features + when: + - result_authselect_profile is not skipped + - result_authselect_features is not skipped + - result_pam_authselect_select_profile is not skipped + + - name: Set number of Password Hashing Rounds - password-auth - Ensure authselect + changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=after-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - result_pam_authselect_restore_features is not skipped + + - name: Set number of Password Hashing Rounds - password-auth - Change the PAM + file to be edited according to the custom authselect profile + ansible.builtin.set_fact: + pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path + | basename }} + when: + - result_authselect_present.stat.exists + + - name: Set number of Password Hashing Rounds - password-auth - Check if expected + PAM module line is present in {{ pam_file_path }} + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + regexp: ^\s*password\s+sufficient\s+pam_unix.so\s*.* + state: absent + check_mode: true + changed_when: false + register: result_pam_line_present + + - name: Set number of Password Hashing Rounds - password-auth - Include or update + the PAM module line in {{ pam_file_path }} + block: + + - name: Set number of Password Hashing Rounds - password-auth - Check if required + PAM module line is present in {{ pam_file_path }} with different control + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + regexp: ^\s*password\s+.*\s+pam_unix.so\s* + state: absent + check_mode: true + changed_when: false + register: result_pam_line_other_control_present + + - name: Set number of Password Hashing Rounds - password-auth - Ensure the correct + control for the required PAM module line in {{ pam_file_path }} + ansible.builtin.replace: + dest: '{{ pam_file_path }}' + regexp: ^(\s*password\s+).*(\bpam_unix.so.*) + replace: \1sufficient \2 + register: result_pam_module_edit + when: + - result_pam_line_other_control_present.found == 1 + + - name: Set number of Password Hashing Rounds - password-auth - Ensure the required + PAM module line is included in {{ pam_file_path }} + ansible.builtin.lineinfile: + dest: '{{ pam_file_path }}' + line: password sufficient pam_unix.so + register: result_pam_module_add + when: + - result_pam_line_other_control_present.found == 0 or result_pam_line_other_control_present.found + > 1 + + - name: Set number of Password Hashing Rounds - password-auth - Ensure authselect + changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: | + result_authselect_present is defined and result_authselect_present.stat.exists and ((result_pam_module_add is defined and result_pam_module_add.changed) or (result_pam_module_edit is defined and result_pam_module_edit.changed)) + when: + - result_pam_line_present.found is defined + - result_pam_line_present.found == 0 + + - name: Set number of Password Hashing Rounds - password-auth - Check if the required + PAM module option is present in {{ pam_file_path }} + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + regexp: ^\s*password\s+sufficient\s+pam_unix.so\s*.*\srounds\b + state: absent + check_mode: true + changed_when: false + register: result_pam_module_rounds_option_present + + - name: Set number of Password Hashing Rounds - password-auth - Ensure the "rounds" + PAM option for "pam_unix.so" is included in {{ pam_file_path }} + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + backrefs: true + regexp: ^(\s*password\s+sufficient\s+pam_unix.so.*) + line: \1 rounds={{ var_password_pam_unix_rounds }} + state: present + register: result_pam_rounds_add + when: + - result_pam_module_rounds_option_present.found == 0 + + - name: Set number of Password Hashing Rounds - password-auth - Ensure the required + value for "rounds" PAM option from "pam_unix.so" in {{ pam_file_path }} + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + backrefs: true + regexp: ^(\s*password\s+sufficient\s+pam_unix.so\s+.*)(rounds)=[0-9a-zA-Z]+\s*(.*) + line: \1\2={{ var_password_pam_unix_rounds }} \3 + register: result_pam_rounds_edit + when: + - result_pam_module_rounds_option_present.found > 0 + + - name: Set number of Password Hashing Rounds - password-auth - Ensure authselect + changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: + - result_authselect_present.stat.exists + - (result_pam_rounds_add is defined and result_pam_rounds_add.changed) or (result_pam_rounds_edit + is defined and result_pam_rounds_edit.changed) + when: + - '"pam" in ansible_facts.packages' + - result_pam_file_present.stat.exists + tags: + - CCE-83403-6 + - accounts_password_pam_unix_rounds_password_auth + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if rpm --quiet -q pam; then + +var_password_pam_unix_rounds='' + + + +if [ -e "/etc/pam.d/password-auth" ] ; then + PAM_FILE_PATH="/etc/pam.d/password-auth" + if [ -f /usr/bin/authselect ]; then + if ! authselect check; then + echo " + authselect integrity check failed. Remediation aborted! + This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact. + It is not recommended to manually edit the PAM files when authselect tool is available. + In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended." + exit 1 + fi + CURRENT_PROFILE=$(authselect current -r | awk '{ print $1 }') + # If not already in use, a custom profile is created preserving the enabled features. + if [[ ! $CURRENT_PROFILE == custom/* ]]; then + ENABLED_FEATURES=$(authselect current | tail -n+3 | awk '{ print $2 }') + authselect create-profile hardening -b $CURRENT_PROFILE + CURRENT_PROFILE="custom/hardening" + + authselect apply-changes -b --backup=before-hardening-custom-profile + authselect select $CURRENT_PROFILE + for feature in $ENABLED_FEATURES; do + authselect enable-feature $feature; + done + + authselect apply-changes -b --backup=after-hardening-custom-profile + fi + PAM_FILE_NAME=$(basename "/etc/pam.d/password-auth") + PAM_FILE_PATH="/etc/authselect/$CURRENT_PROFILE/$PAM_FILE_NAME" + + authselect apply-changes -b + fi + if ! grep -qP '^\s*password\s+'"sufficient"'\s+pam_unix.so\s*.*' "$PAM_FILE_PATH"; then + # Line matching group + control + module was not found. Check group + module. + if [ "$(grep -cP '^\s*password\s+.*\s+pam_unix.so\s*' "$PAM_FILE_PATH")" -eq 1 ]; then + # The control is updated only if one single line matches. + sed -i -E --follow-symlinks 's/^(\s*password\s+).*(\bpam_unix.so.*)/\1'"sufficient"' \2/' "$PAM_FILE_PATH" + else + echo 'password '"sufficient"' pam_unix.so' >> "$PAM_FILE_PATH" + fi + fi + # Check the option + if ! grep -qP '^\s*password\s+'"sufficient"'\s+pam_unix.so\s*.*\srounds\b' "$PAM_FILE_PATH"; then + sed -i -E --follow-symlinks '/\s*password\s+'"sufficient"'\s+pam_unix.so.*/ s/$/ rounds='"$var_password_pam_unix_rounds"'/' "$PAM_FILE_PATH" + else + sed -i -E --follow-symlinks 's/(\s*password\s+'"sufficient"'\s+pam_unix.so\s+.*)('"rounds"'=)[[:alnum:]]+\s*(.*)/\1\2'"$var_password_pam_unix_rounds"' \3/' "$PAM_FILE_PATH" + fi + if [ -f /usr/bin/authselect ]; then + + authselect apply-changes -b + fi +else + echo "/etc/pam.d/password-auth was not found" >&2 +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Set number of Password Hashing Rounds - system-auth + Configure the number or rounds for the password hashing algorithm. This can be +accomplished by using the rounds option for the pam_unix PAM module. + +In file /etc/pam.d/system-auth append rounds= +to the pam_unix.so entry, as shown below: +password sufficient pam_unix.so ...existing_options... rounds= +The system's default number of rounds is 5000. + Setting a high number of hashing rounds makes it more difficult to brute force the password, +but requires more CPU resources to authenticate users. + BP28(R32) + CCI-000196 + SRG-OS-000073-GPOS-00041 + Using a higher number of rounds makes password cracking attacks more difficult. + + CCE-83386-3 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83386-3 + - accounts_password_pam_unix_rounds_system_auth + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed +- name: XCCDF Value var_password_pam_unix_rounds # promote to variable + set_fact: + var_password_pam_unix_rounds: !!str + tags: + - always + +- name: Set number of Password Hashing Rounds - system-auth - Check if /etc/pam.d/system-auth + file is present + ansible.builtin.stat: + path: /etc/pam.d/system-auth + register: result_pam_file_present + when: '"pam" in ansible_facts.packages' + tags: + - CCE-83386-3 + - accounts_password_pam_unix_rounds_system_auth + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + +- name: Set number of Password Hashing Rounds - system-auth - Check the proper remediation + for the system + block: + + - name: Set number of Password Hashing Rounds - system-auth - Define the PAM file + to be edited as a local fact + ansible.builtin.set_fact: + pam_file_path: /etc/pam.d/system-auth + + - name: Set number of Password Hashing Rounds - system-auth - Check if system relies + on authselect + ansible.builtin.stat: + path: /usr/bin/authselect + register: result_authselect_present + + - name: Set number of Password Hashing Rounds - system-auth - Remediate using authselect + block: + + - name: Set number of Password Hashing Rounds - system-auth - Check integrity + of authselect current profile + ansible.builtin.command: + cmd: authselect check + register: result_authselect_check_cmd + changed_when: false + ignore_errors: true + + - name: Set number of Password Hashing Rounds - system-auth - Informative message + based on the authselect integrity check result + ansible.builtin.assert: + that: + - result_authselect_check_cmd is success + fail_msg: + - authselect integrity check failed. Remediation aborted! + - This remediation could not be applied because an authselect profile was + not selected or the selected profile is not intact. + - It is not recommended to manually edit the PAM files when authselect tool + is available. + - In cases where the default authselect profile does not cover a specific + demand, a custom authselect profile is recommended. + success_msg: + - authselect integrity check passed + + - name: Set number of Password Hashing Rounds - system-auth - Get authselect current + profile + ansible.builtin.shell: + cmd: authselect current -r | awk '{ print $1 }' + register: result_authselect_profile + changed_when: false + when: + - result_authselect_check_cmd is success + + - name: Set number of Password Hashing Rounds - system-auth - Define the current + authselect profile as a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: '{{ result_authselect_profile.stdout }}' + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is match("custom/") + + - name: Set number of Password Hashing Rounds - system-auth - Define the new authselect + custom profile as a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: custom/hardening + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is not match("custom/") + + - name: Set number of Password Hashing Rounds - system-auth - Get authselect current + features to also enable them in the custom profile + ansible.builtin.shell: + cmd: authselect current | tail -n+3 | awk '{ print $2 }' + register: result_authselect_features + changed_when: false + when: + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + + - name: Set number of Password Hashing Rounds - system-auth - Check if any custom + profile with the same name was already created + ansible.builtin.stat: + path: /etc/authselect/{{ authselect_custom_profile }} + register: result_authselect_custom_profile_present + changed_when: false + when: + - authselect_current_profile is not match("custom/") + + - name: Set number of Password Hashing Rounds - system-auth - Create an authselect + custom profile based on the current profile + ansible.builtin.command: + cmd: authselect create-profile hardening -b {{ authselect_current_profile + }} + when: + - result_authselect_check_cmd is success + - authselect_current_profile is not match("custom/") + - not result_authselect_custom_profile_present.stat.exists + + - name: Set number of Password Hashing Rounds - system-auth - Ensure authselect + changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=before-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Set number of Password Hashing Rounds - system-auth - Ensure the authselect + custom profile is selected + ansible.builtin.command: + cmd: authselect select {{ authselect_custom_profile }} + register: result_pam_authselect_select_profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Set number of Password Hashing Rounds - system-auth - Restore the authselect + features in the custom profile + ansible.builtin.command: + cmd: authselect enable-feature {{ item }} + loop: '{{ result_authselect_features.stdout_lines }}' + register: result_pam_authselect_restore_features + when: + - result_authselect_profile is not skipped + - result_authselect_features is not skipped + - result_pam_authselect_select_profile is not skipped + + - name: Set number of Password Hashing Rounds - system-auth - Ensure authselect + changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=after-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - result_pam_authselect_restore_features is not skipped + + - name: Set number of Password Hashing Rounds - system-auth - Change the PAM file + to be edited according to the custom authselect profile + ansible.builtin.set_fact: + pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path + | basename }} + when: + - result_authselect_present.stat.exists + + - name: Set number of Password Hashing Rounds - system-auth - Check if expected + PAM module line is present in {{ pam_file_path }} + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + regexp: ^\s*password\s+sufficient\s+pam_unix.so\s*.* + state: absent + check_mode: true + changed_when: false + register: result_pam_line_present + + - name: Set number of Password Hashing Rounds - system-auth - Include or update + the PAM module line in {{ pam_file_path }} + block: + + - name: Set number of Password Hashing Rounds - system-auth - Check if required + PAM module line is present in {{ pam_file_path }} with different control + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + regexp: ^\s*password\s+.*\s+pam_unix.so\s* + state: absent + check_mode: true + changed_when: false + register: result_pam_line_other_control_present + + - name: Set number of Password Hashing Rounds - system-auth - Ensure the correct + control for the required PAM module line in {{ pam_file_path }} + ansible.builtin.replace: + dest: '{{ pam_file_path }}' + regexp: ^(\s*password\s+).*(\bpam_unix.so.*) + replace: \1sufficient \2 + register: result_pam_module_edit + when: + - result_pam_line_other_control_present.found == 1 + + - name: Set number of Password Hashing Rounds - system-auth - Ensure the required + PAM module line is included in {{ pam_file_path }} + ansible.builtin.lineinfile: + dest: '{{ pam_file_path }}' + line: password sufficient pam_unix.so + register: result_pam_module_add + when: + - result_pam_line_other_control_present.found == 0 or result_pam_line_other_control_present.found + > 1 + + - name: Set number of Password Hashing Rounds - system-auth - Ensure authselect + changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: | + result_authselect_present is defined and result_authselect_present.stat.exists and ((result_pam_module_add is defined and result_pam_module_add.changed) or (result_pam_module_edit is defined and result_pam_module_edit.changed)) + when: + - result_pam_line_present.found is defined + - result_pam_line_present.found == 0 + + - name: Set number of Password Hashing Rounds - system-auth - Check if the required + PAM module option is present in {{ pam_file_path }} + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + regexp: ^\s*password\s+sufficient\s+pam_unix.so\s*.*\srounds\b + state: absent + check_mode: true + changed_when: false + register: result_pam_module_rounds_option_present + + - name: Set number of Password Hashing Rounds - system-auth - Ensure the "rounds" + PAM option for "pam_unix.so" is included in {{ pam_file_path }} + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + backrefs: true + regexp: ^(\s*password\s+sufficient\s+pam_unix.so.*) + line: \1 rounds={{ var_password_pam_unix_rounds }} + state: present + register: result_pam_rounds_add + when: + - result_pam_module_rounds_option_present.found == 0 + + - name: Set number of Password Hashing Rounds - system-auth - Ensure the required + value for "rounds" PAM option from "pam_unix.so" in {{ pam_file_path }} + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + backrefs: true + regexp: ^(\s*password\s+sufficient\s+pam_unix.so\s+.*)(rounds)=[0-9a-zA-Z]+\s*(.*) + line: \1\2={{ var_password_pam_unix_rounds }} \3 + register: result_pam_rounds_edit + when: + - result_pam_module_rounds_option_present.found > 0 + + - name: Set number of Password Hashing Rounds - system-auth - Ensure authselect + changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: + - result_authselect_present.stat.exists + - (result_pam_rounds_add is defined and result_pam_rounds_add.changed) or (result_pam_rounds_edit + is defined and result_pam_rounds_edit.changed) + when: + - '"pam" in ansible_facts.packages' + - result_pam_file_present.stat.exists + tags: + - CCE-83386-3 + - accounts_password_pam_unix_rounds_system_auth + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if rpm --quiet -q pam; then + +var_password_pam_unix_rounds='' + + +if [ -e "/etc/pam.d/system-auth" ] ; then + PAM_FILE_PATH="/etc/pam.d/system-auth" + if [ -f /usr/bin/authselect ]; then + if ! authselect check; then + echo " + authselect integrity check failed. Remediation aborted! + This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact. + It is not recommended to manually edit the PAM files when authselect tool is available. + In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended." + exit 1 + fi + CURRENT_PROFILE=$(authselect current -r | awk '{ print $1 }') + # If not already in use, a custom profile is created preserving the enabled features. + if [[ ! $CURRENT_PROFILE == custom/* ]]; then + ENABLED_FEATURES=$(authselect current | tail -n+3 | awk '{ print $2 }') + authselect create-profile hardening -b $CURRENT_PROFILE + CURRENT_PROFILE="custom/hardening" + + authselect apply-changes -b --backup=before-hardening-custom-profile + authselect select $CURRENT_PROFILE + for feature in $ENABLED_FEATURES; do + authselect enable-feature $feature; + done + + authselect apply-changes -b --backup=after-hardening-custom-profile + fi + PAM_FILE_NAME=$(basename "/etc/pam.d/system-auth") + PAM_FILE_PATH="/etc/authselect/$CURRENT_PROFILE/$PAM_FILE_NAME" + + authselect apply-changes -b + fi + if ! grep -qP '^\s*password\s+'"sufficient"'\s+pam_unix.so\s*.*' "$PAM_FILE_PATH"; then + # Line matching group + control + module was not found. Check group + module. + if [ "$(grep -cP '^\s*password\s+.*\s+pam_unix.so\s*' "$PAM_FILE_PATH")" -eq 1 ]; then + # The control is updated only if one single line matches. + sed -i -E --follow-symlinks 's/^(\s*password\s+).*(\bpam_unix.so.*)/\1'"sufficient"' \2/' "$PAM_FILE_PATH" + else + echo 'password '"sufficient"' pam_unix.so' >> "$PAM_FILE_PATH" + fi + fi + # Check the option + if ! grep -qP '^\s*password\s+'"sufficient"'\s+pam_unix.so\s*.*\srounds\b' "$PAM_FILE_PATH"; then + sed -i -E --follow-symlinks '/\s*password\s+'"sufficient"'\s+pam_unix.so.*/ s/$/ rounds='"$var_password_pam_unix_rounds"'/' "$PAM_FILE_PATH" + else + sed -i -E --follow-symlinks 's/(\s*password\s+'"sufficient"'\s+pam_unix.so\s+.*)('"rounds"'=)[[:alnum:]]+\s*(.*)/\1\2'"$var_password_pam_unix_rounds"' \3/' "$PAM_FILE_PATH" + fi + if [ -f /usr/bin/authselect ]; then + + authselect apply-changes -b + fi +else + echo "/etc/pam.d/system-auth was not found" >&2 +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + All GIDs referenced in /etc/passwd must be defined in /etc/group + Add a group to the system for each GID referenced without a corresponding group. + 1 + 12 + 15 + 16 + 5 + 5.5.2 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.03 + DSS06.10 + CCI-000764 + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.2 + 4.3.3.7.4 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + A.18.1.4 + A.7.1.1 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.2 + A.9.4.3 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.2.3 + CIP-004-6 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.2 + CIP-007-3 R5.2 + CIP-007-3 R5.3.1 + CIP-007-3 R5.3.2 + CIP-007-3 R5.3.3 + IA-2 + CM-6(a) + PR.AC-1 + PR.AC-6 + PR.AC-7 + Req-8.5.a + SRG-OS-000104-GPOS-00051 + 6.2.2 + If a user is assigned the Group Identifier (GID) of a group not existing on the system, and a group +with the Group Identifier (GID) is subsequently created, the user may have unintended rights to +any files associated with the group. + CCE-80822-0 + + + + + + + + + Prevent Login to Accounts With Empty Password + If an account is configured for password authentication +but does not have an assigned password, it may be possible to log +into the account without authentication. Remove any instances of the +nullok in + +/etc/pam.d/system-auth and +/etc/pam.d/password-auth + +to prevent logins with empty passwords. + If the system relies on authselect tool to manage PAM settings, the remediation +will also use authselect tool. However, if any manual modification was made in +PAM files, the authselect integrity check will fail and the remediation will be +aborted in order to preserve intentional changes. In this case, an informative message will +be shown in the remediation report. +Note that this rule is not applicable for systems running within a +container. Having user with empty password within a container is not +considered a risk, because it should not be possible to directly login into +a container anyway. + 1 + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + 5.5.2 + APO01.06 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.02 + DSS06.03 + DSS06.10 + 3.1.1 + 3.1.5 + CCI-000366 + 164.308(a)(1)(ii)(B) + 164.308(a)(7)(i) + 164.308(a)(7)(ii)(A) + 164.310(a)(1) + 164.310(a)(2)(i) + 164.310(a)(2)(ii) + 164.310(a)(2)(iii) + 164.310(b) + 164.310(c) + 164.310(d)(1) + 164.310(d)(2)(iii) + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.18.1.4 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.1 + A.9.4.2 + A.9.4.3 + A.9.4.4 + A.9.4.5 + IA-5(1)(a) + IA-5(c) + CM-6(a) + PR.AC-1 + PR.AC-4 + PR.AC-6 + PR.AC-7 + PR.DS-5 + FIA_UAU.1 + Req-8.2.3 + SRG-OS-000480-GPOS-00227 + RHEL-08-020331 + SV-244540r743869_rule + If an account has an empty password, anyone could log in and +run commands with the privileges of that account. Accounts with +empty passwords should never be used in operational environments. + + CCE-80841-0 + - name: Prevent Login to Accounts With Empty Password - Check if system relies on + authselect + ansible.builtin.stat: + path: /usr/bin/authselect + register: result_authselect_present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80841-0 + - CJIS-5.5.2 + - DISA-STIG-RHEL-08-020331 + - NIST-800-171-3.1.1 + - NIST-800-171-3.1.5 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(a) + - NIST-800-53-IA-5(c) + - PCI-DSS-Req-8.2.3 + - configure_strategy + - high_severity + - low_complexity + - medium_disruption + - no_empty_passwords + - no_reboot_needed + +- name: Prevent Login to Accounts With Empty Password - Remediate using authselect + block: + + - name: Prevent Login to Accounts With Empty Password - Check integrity of authselect + current profile + ansible.builtin.command: + cmd: authselect check + register: result_authselect_check_cmd + changed_when: false + ignore_errors: true + + - name: Prevent Login to Accounts With Empty Password - Informative message based + on the authselect integrity check result + ansible.builtin.assert: + that: + - result_authselect_check_cmd is success + fail_msg: + - authselect integrity check failed. Remediation aborted! + - This remediation could not be applied because an authselect profile was not + selected or the selected profile is not intact. + - It is not recommended to manually edit the PAM files when authselect tool + is available. + - In cases where the default authselect profile does not cover a specific demand, + a custom authselect profile is recommended. + success_msg: + - authselect integrity check passed + + - name: Prevent Login to Accounts With Empty Password - Get authselect current features + ansible.builtin.shell: + cmd: authselect current | tail -n+3 | awk '{ print $2 }' + register: result_authselect_features + changed_when: false + when: + - result_authselect_check_cmd is success + + - name: Prevent Login to Accounts With Empty Password - Ensure "without-nullok" + feature is enabled using authselect tool + ansible.builtin.command: + cmd: authselect enable-feature without-nullok + register: result_authselect_enable_feature_cmd + when: + - result_authselect_check_cmd is success + - result_authselect_features.stdout is not search("without-nullok") + + - name: Prevent Login to Accounts With Empty Password - Ensure authselect changes + are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: + - result_authselect_enable_feature_cmd is not skipped + - result_authselect_enable_feature_cmd is success + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - result_authselect_present.stat.exists + tags: + - CCE-80841-0 + - CJIS-5.5.2 + - DISA-STIG-RHEL-08-020331 + - NIST-800-171-3.1.1 + - NIST-800-171-3.1.5 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(a) + - NIST-800-53-IA-5(c) + - PCI-DSS-Req-8.2.3 + - configure_strategy + - high_severity + - low_complexity + - medium_disruption + - no_empty_passwords + - no_reboot_needed + +- name: Prevent Login to Accounts With Empty Password - Remediate directly editing + PAM files + ansible.builtin.replace: + dest: '{{ item }}' + regexp: nullok + loop: + - /etc/pam.d/system-auth + - /etc/pam.d/password-auth + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - not result_authselect_present.stat.exists + tags: + - CCE-80841-0 + - CJIS-5.5.2 + - DISA-STIG-RHEL-08-020331 + - NIST-800-171-3.1.1 + - NIST-800-171-3.1.5 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(a) + - NIST-800-53-IA-5(c) + - PCI-DSS-Req-8.2.3 + - configure_strategy + - high_severity + - low_complexity + - medium_disruption + - no_empty_passwords + - no_reboot_needed + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,%23%20Generated%20by%20authselect%20on%20Sat%20Oct%2027%2014%3A59%3A36%202018%0A%23%20Do%20not%20modify%20this%20file%20manually.%0A%0Aauth%20%20%20%20%20%20%20%20required%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_env.so%0Aauth%20%20%20%20%20%20%20%20required%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_faildelay.so%20delay%3D2000000%0Aauth%20%20%20%20%20%20%20%20sufficient%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_fprintd.so%0Aauth%20%20%20%20%20%20%20%20%5Bdefault%3D1%20ignore%3Dignore%20success%3Dok%5D%20%20%20%20%20%20%20%20%20pam_succeed_if.so%20uid%20%3E%3D%201000%20quiet%0Aauth%20%20%20%20%20%20%20%20%5Bdefault%3D1%20ignore%3Dignore%20success%3Dok%5D%20%20%20%20%20%20%20%20%20pam_localuser.so%0Aauth%20%20%20%20%20%20%20%20sufficient%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_unix.so%20try_first_pass%0Aauth%20%20%20%20%20%20%20%20requisite%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_succeed_if.so%20uid%20%3E%3D%201000%20quiet_success%0Aauth%20%20%20%20%20%20%20%20sufficient%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_sss.so%20forward_pass%0Aauth%20%20%20%20%20%20%20%20required%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_deny.so%0A%0Aaccount%20%20%20%20%20required%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_unix.so%0Aaccount%20%20%20%20%20sufficient%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_localuser.so%0Aaccount%20%20%20%20%20sufficient%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_succeed_if.so%20uid%20%3C%201000%20quiet%0Aaccount%20%20%20%20%20%5Bdefault%3Dbad%20success%3Dok%20user_unknown%3Dignore%5D%20pam_sss.so%0Aaccount%20%20%20%20%20required%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_permit.so%0A%0Apassword%20%20%20%20requisite%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_pwquality.so%20try_first_pass%20local_users_only%0Apassword%20%20%20%20sufficient%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_unix.so%20sha512%20shadow%20try_first_pass%20use_authtok%0Apassword%20%20%20%20sufficient%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_sss.so%20use_authtok%0Apassword%20%20%20%20required%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_deny.so%0A%0Asession%20%20%20%20%20user%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_keyinit.so%20revoke%0Asession%20%20%20%20%20required%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_limits.so%0A-session%20%20%20%20user%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_systemd.so%0Asession%20%20%20%20%20%5Bsuccess%3D1%20default%3Dignore%5D%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_succeed_if.so%20service%20in%20crond%20quiet%20use_uid%0Asession%20%20%20%20%20required%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_unix.so%0Asession%20%20%20%20%20user%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_sss.so%0A + mode: 0644 + path: /etc/pam.d/password-auth + overwrite: true + - contents: + source: data:,%23%20Generated%20by%20authselect%20on%20Sat%20Oct%2027%2014%3A59%3A36%202018%0A%23%20Do%20not%20modify%20this%20file%20manually.%0A%0Aauth%20%20%20%20%20%20%20%20required%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_env.so%0Aauth%20%20%20%20%20%20%20%20required%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_faildelay.so%20delay%3D2000000%0Aauth%20%20%20%20%20%20%20%20sufficient%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_fprintd.so%0Aauth%20%20%20%20%20%20%20%20%5Bdefault%3D1%20ignore%3Dignore%20success%3Dok%5D%20%20%20%20%20%20%20%20%20pam_succeed_if.so%20uid%20%3E%3D%201000%20quiet%0Aauth%20%20%20%20%20%20%20%20%5Bdefault%3D1%20ignore%3Dignore%20success%3Dok%5D%20%20%20%20%20%20%20%20%20pam_localuser.so%0Aauth%20%20%20%20%20%20%20%20sufficient%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_unix.so%20try_first_pass%0Aauth%20%20%20%20%20%20%20%20requisite%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_succeed_if.so%20uid%20%3E%3D%201000%20quiet_success%0Aauth%20%20%20%20%20%20%20%20sufficient%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_sss.so%20forward_pass%0Aauth%20%20%20%20%20%20%20%20required%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_deny.so%0A%0Aaccount%20%20%20%20%20required%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_unix.so%0Aaccount%20%20%20%20%20sufficient%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_localuser.so%0Aaccount%20%20%20%20%20sufficient%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_succeed_if.so%20uid%20%3C%201000%20quiet%0Aaccount%20%20%20%20%20%5Bdefault%3Dbad%20success%3Dok%20user_unknown%3Dignore%5D%20pam_sss.so%0Aaccount%20%20%20%20%20required%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_permit.so%0A%0Apassword%20%20%20%20requisite%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_pwquality.so%20try_first_pass%20local_users_only%0Apassword%20%20%20%20sufficient%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_unix.so%20sha512%20shadow%20try_first_pass%20use_authtok%0Apassword%20%20%20%20sufficient%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_sss.so%20use_authtok%0Apassword%20%20%20%20required%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_deny.so%0A%0Asession%20%20%20%20%20user%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_keyinit.so%20revoke%0Asession%20%20%20%20%20required%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_limits.so%0A-session%20%20%20%20user%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_systemd.so%0Asession%20%20%20%20%20%5Bsuccess%3D1%20default%3Dignore%5D%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_succeed_if.so%20service%20in%20crond%20quiet%20use_uid%0Asession%20%20%20%20%20required%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_unix.so%0Asession%20%20%20%20%20user%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_sss.so%0A + mode: 0644 + path: /etc/pam.d/system-auth + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if [ -f /usr/bin/authselect ]; then + if ! authselect check; then +echo " +authselect integrity check failed. Remediation aborted! +This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact. +It is not recommended to manually edit the PAM files when authselect tool is available. +In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended." +exit 1 +fi +authselect enable-feature without-nullok + +authselect apply-changes -b +else + +if grep -qP '^\s*auth\s+'"sufficient"'\s+pam_unix.so\s.*\bnullok\b' "/etc/pam.d/system-auth"; then + sed -i -E --follow-symlinks 's/(.*auth.*'"sufficient"'.*pam_unix.so.*)\snullok=?[[:alnum:]]*(.*)/\1\2/g' "/etc/pam.d/system-auth" +fi + +if grep -qP '^\s*password\s+'"sufficient"'\s+pam_unix.so\s.*\bnullok\b' "/etc/pam.d/system-auth"; then + sed -i -E --follow-symlinks 's/(.*password.*'"sufficient"'.*pam_unix.so.*)\snullok=?[[:alnum:]]*(.*)/\1\2/g' "/etc/pam.d/system-auth" +fi + +if grep -qP '^\s*auth\s+'"sufficient"'\s+pam_unix.so\s.*\bnullok\b' "/etc/pam.d/password-auth"; then + sed -i -E --follow-symlinks 's/(.*auth.*'"sufficient"'.*pam_unix.so.*)\snullok=?[[:alnum:]]*(.*)/\1\2/g' "/etc/pam.d/password-auth" +fi + +if grep -qP '^\s*password\s+'"sufficient"'\s+pam_unix.so\s.*\bnullok\b' "/etc/pam.d/password-auth"; then + sed -i -E --follow-symlinks 's/(.*password.*'"sufficient"'.*pam_unix.so.*)\snullok=?[[:alnum:]]*(.*)/\1\2/g' "/etc/pam.d/password-auth" +fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure There Are No Accounts With Blank or Null Passwords + Check the "/etc/shadow" file for blank passwords with the +following command: +$ sudo awk -F: '!$2 {print $1}' /etc/shadow +If the command returns any results, this is a finding. +Configure all accounts on the system to have a password or lock +the account with the following commands: +Perform a password reset: +$ sudo passwd [username] +Lock an account: +$ sudo passwd -l [username] + Note that this rule is not applicable for systems running within a container. Having user with empty password within a container is not considered a risk, because it should not be possible to directly login into a container anyway. + CCI-000366 + CM-6(b) + CM-6.1(iv) + SRG-OS-000480-GPOS-00227 + 6.2.1 + If an account has an empty password, anyone could log in and +run commands with the privileges of that account. Accounts with +empty passwords should never be used in operational environments. + + CCE-85953-8 + - name: Collect users with no password + command: | + awk -F: '!$2 {print $1}' /etc/shadow + register: users_nopasswd + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-85953-8 + - NIST-800-53-CM-6(b) + - NIST-800-53-CM-6.1(iv) + - high_severity + - low_complexity + - low_disruption + - no_empty_passwords_etc_shadow + - no_reboot_needed + - restrict_strategy + +- name: Lock users with no password + command: | + passwd -l {{ item }} + with_items: '{{ users_nopasswd.stdout_lines }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - users_nopasswd.stdout_lines | length > 0 + tags: + - CCE-85953-8 + - NIST-800-53-CM-6(b) + - NIST-800-53-CM-6.1(iv) + - high_severity + - low_complexity + - low_disruption + - no_empty_passwords_etc_shadow + - no_reboot_needed + - restrict_strategy + + + + + + + + + + Ensure there are no legacy + NIS entries in /etc/group + The + character in /etc/group file marks a place where +entries from a network information service (NIS) should be directly inserted. + Using this method to include entries into /etc/group is considered legacy +and should be avoided. These entries may provide a way for an attacker +to gain access to the system. + CCE-83389-7 + - name: Backup the old /etc/group file + copy: + src: /etc/group + dest: /etc/group- + remote_src: true + tags: + - CCE-83389-7 + - low_complexity + - medium_disruption + - medium_severity + - no_legacy_plus_entries_etc_group + - no_reboot_needed + - restrict_strategy + +- name: Remove lines starting with + from /etc/group + lineinfile: + regexp: ^\+.*$ + state: absent + path: /etc/group + tags: + - CCE-83389-7 + - low_complexity + - medium_disruption + - medium_severity + - no_legacy_plus_entries_etc_group + - no_reboot_needed + - restrict_strategy + + +if grep -q '^\+' /etc/group; then +# backup old file to /etc/group- + cp /etc/group /etc/group- + sed -i '/^\+.*$/d' /etc/group +fi + + + + + + + + + + Ensure there are no legacy + NIS entries in /etc/passwd + The + character in /etc/passwd file marks a place where +entries from a network information service (NIS) should be directly inserted. + Using this method to include entries into /etc/passwd is considered legacy +and should be avoided. These entries may provide a way for an attacker +to gain access to the system. + CCE-82890-5 + - name: Backup the old /etc/passwd file + copy: + src: /etc/passwd + dest: /etc/passwd- + remote_src: true + tags: + - CCE-82890-5 + - low_complexity + - medium_disruption + - medium_severity + - no_legacy_plus_entries_etc_passwd + - no_reboot_needed + - restrict_strategy + +- name: Remove lines starting with + from /etc/passwd + lineinfile: + regexp: ^\+.*$ + state: absent + path: /etc/passwd + tags: + - CCE-82890-5 + - low_complexity + - medium_disruption + - medium_severity + - no_legacy_plus_entries_etc_passwd + - no_reboot_needed + - restrict_strategy + + +if grep -q '^\+' /etc/passwd; then +# backup old file to /etc/passwd- + cp /etc/passwd /etc/passwd- + sed -i '/^\+.*$/d' /etc/passwd +fi + + + + + + + + + + Ensure there are no legacy + NIS entries in /etc/shadow + The + character in /etc/shadow file marks a place where +entries from a network information service (NIS) should be directly inserted. + Using this method to include entries into /etc/shadow is considered legacy +and should be avoided. These entries may provide a way for an attacker +to gain access to the system. + CCE-84290-6 + - name: Backup the old /etc/shadow file + copy: + src: /etc/shadow + dest: /etc/shadow- + remote_src: true + tags: + - CCE-84290-6 + - low_complexity + - medium_disruption + - medium_severity + - no_legacy_plus_entries_etc_shadow + - no_reboot_needed + - restrict_strategy + +- name: Remove lines starting with + from /etc/shadow + lineinfile: + regexp: ^\+.*$ + state: absent + path: /etc/shadow + tags: + - CCE-84290-6 + - low_complexity + - medium_disruption + - medium_severity + - no_legacy_plus_entries_etc_shadow + - no_reboot_needed + - restrict_strategy + + +if grep -q '^\+' /etc/shadow; then +# backup old file to /etc/shadow- + cp /etc/shadow /etc/shadow- + sed -i '/^\+.*$/d' /etc/shadow +fi + + + + + + + + + + Verify No netrc Files Exist + The .netrc files contain login information +used to auto-login into FTP servers and reside in the user's home +directory. These files may contain unencrypted passwords to +remote FTP servers making them susceptible to access by unauthorized +users and should not be used. Any .netrc files should be removed. + 1 + 11 + 12 + 14 + 15 + 16 + 18 + 3 + 5 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.03 + DSS06.06 + DSS06.10 + CCI-000196 + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + A.18.1.4 + A.6.1.2 + A.7.1.1 + A.9.1.2 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.1 + A.9.4.2 + A.9.4.3 + A.9.4.4 + A.9.4.5 + CIP-003-8 R1.3 + CIP-003-8 R3 + CIP-003-8 R3.1 + CIP-003-8 R3.2 + CIP-003-8 R3.3 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.2.3 + CIP-004-6 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.2 + CIP-007-3 R5.2 + CIP-007-3 R5.3.1 + CIP-007-3 R5.3.2 + CIP-007-3 R5.3.3 + IA-5(h) + IA-5(1)(c) + CM-6(a) + IA-5(7) + PR.AC-1 + PR.AC-4 + PR.AC-6 + PR.AC-7 + PR.PT-3 + 6.2.13 + 6.2.15 + Unencrypted passwords for remote FTP servers may be stored in .netrc +files. + CCE-83444-0 + + + + + + + + + + Restrict Root Logins + Direct root logins should be allowed only for emergency use. +In normal situations, the administrator should access the system +via a unique unprivileged account, and then use su or sudo to execute +privileged commands. Discouraging administrators from accessing the +root account directly ensures an audit trail in organizations with +multiple administrators. Locking down the channels through which +root can connect directly also reduces opportunities for +password-guessing against the root account. The login program +uses the file /etc/securetty to determine which interfaces +should allow root logins. + +The virtual devices /dev/console +and /dev/tty* represent the system consoles (accessible via +the Ctrl-Alt-F1 through Ctrl-Alt-F6 keyboard sequences on a default +installation). The default securetty file also contains /dev/vc/*. +These are likely to be deprecated in most environments, but may be retained +for compatibility. Root should also be prohibited from connecting +via network protocols. Other sections of this document +include guidance describing how to prevent root from logging in via SSH. + + Verify Only Root Has UID 0 + If any account other than root has a UID of 0, this misconfiguration should +be investigated and the accounts other than root should be removed or have +their UID changed. + +If the account is associated with system commands or applications the UID +should be changed to one greater than "0" but less than "1000." +Otherwise assign a UID greater than "1000" that has not already been +assigned. + 1 + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.02 + DSS06.03 + DSS06.10 + 3.1.1 + 3.1.5 + CCI-000366 + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.18.1.4 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.1 + A.9.4.2 + A.9.4.3 + A.9.4.4 + A.9.4.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.2.3 + CIP-004-6 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.2 + CIP-007-3 R5.2 + CIP-007-3 R5.3.1 + CIP-007-3 R5.3.2 + CIP-007-3 R5.3.3 + IA-2 + AC-6(5) + IA-4(b) + PR.AC-1 + PR.AC-4 + PR.AC-6 + PR.AC-7 + PR.DS-5 + SRG-OS-000480-GPOS-00227 + RHEL-08-040200 + 6.2.8 + SV-230534r627750_rule + An account has root authority if it has a UID of 0. Multiple accounts +with a UID of 0 afford more opportunity for potential intruders to +guess a password for a privileged account. Proper configuration of +sudo is recommended to afford multiple system administrators +access to root privileges in an accountable manner. + CCE-80649-7 + awk -F: '$3 == 0 && $1 != "root" { print $1 }' /etc/passwd | xargs --no-run-if-empty --max-lines=1 passwd -l + + + + + + + + + + Verify Root Has A Primary GID 0 + The root user should have a primary group of 0. + 5.6.4 + To help ensure that root-owned files are not inadvertently exposed to other users. + CCE-86297-9 + + + + + + + + + Direct root Logins Not Allowed + To further limit access to the root account, administrators +can disable root logins at the console by editing the /etc/securetty file. +This file lists all devices the root user is allowed to login to. If the file does +not exist at all, the root user can login through any communication device on the +system, whether via the console or via a raw network interface. This is dangerous +as user can login to the system as root via Telnet, which sends the password in +plain text over the network. By default, Red Hat Enterprise Linux 8's +/etc/securetty file only allows the root user to login at the console +physically attached to the system. To prevent root from logging in, remove the +contents of this file. To prevent direct root logins, remove the contents of this +file by typing the following command: + +$ sudo echo > /etc/securetty + + This rule only checks the /etc/securetty file existence and its content. +If you need to restrict user access using the /etc/securetty file, make sure +the pam_securetty.so PAM module is properly enabled in relevant PAM files. + BP28(R19) + 1 + 12 + 15 + 16 + 5 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.03 + DSS06.10 + 3.1.1 + 3.1.6 + 164.308(a)(1)(ii)(B) + 164.308(a)(7)(i) + 164.308(a)(7)(ii)(A) + 164.310(a)(1) + 164.310(a)(2)(i) + 164.310(a)(2)(ii) + 164.310(a)(2)(iii) + 164.310(b) + 164.310(c) + 164.310(d)(1) + 164.310(d)(2)(iii) + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.2 + 4.3.3.7.4 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + A.18.1.4 + A.7.1.1 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.2 + A.9.4.3 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.2.3 + CIP-004-6 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.2 + CIP-007-3 R5.2 + CIP-007-3 R5.3.1 + CIP-007-3 R5.3.2 + CIP-007-3 R5.3.3 + IA-2 + CM-6(a) + PR.AC-1 + PR.AC-6 + PR.AC-7 + Disabling direct root logins ensures proper accountability and multifactor +authentication to privileged accounts. Users will first login, then escalate +to privileged (root) access via su / sudo. This is required for FISMA Low +and FISMA Moderate systems. + + CCE-80840-2 + - name: Direct root Logins Not Allowed + copy: + dest: /etc/securetty + content: '' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80840-2 + - NIST-800-171-3.1.1 + - NIST-800-171-3.1.6 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-2 + - low_complexity + - low_disruption + - medium_severity + - no_direct_root_logins + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:, + mode: 0600 + path: /etc/securetty + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +echo > /etc/securetty + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure that System Accounts Are Locked + Some accounts are not associated with a human user of the system, and exist to +perform some administrative function. An attacker should not be able to log into +these accounts. + +System accounts are those user accounts with a user ID +less than UID_MIN, where value of the UID_MIN directive is set in +/etc/login.defs configuration file. In the default configuration UID_MIN is set +to 500, thus system accounts are those user accounts with a user ID less than +500. If any system account SYSACCT (other than root) has an unlocked password, +disable it with the command: +$ sudo passwd -l SYSACCT + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + AC-6 + CM-6(a) + Disabling authentication for default system accounts makes it more difficult +for attackers to make use of them to compromise a system.false + + + + + + Restrict Web Browser Use for Administrative Accounts + Enforce policy requiring administrative accounts use web browsers only for +local service administration. + If a browser vulnerability is exploited while running with administrative privileges, +the entire system could be compromised. Specific exceptions for local service +administration should be documented in site-defined policy. + + + + + + Ensure that System Accounts Do Not Run a Shell Upon Login + Some accounts are not associated with a human user of the system, and exist to +perform some administrative function. Should an attacker be able to log into +these accounts, they should not be granted access to a shell. + +The login shell for each local account is stored in the last field of each line +in /etc/passwd. System accounts are those user accounts with a user ID +less than UID_MIN, where value of UID_MIN directive is set in +/etc/login.defs configuration file. In the default configuration UID_MIN is set +to 1000, thus system accounts are those user accounts with a user ID less than +1000. The user ID is stored in the third field. If any system account +SYSACCT (other than root) has a login shell, disable it with the +command: $ sudo usermod -s /sbin/nologin SYSACCT + Do not perform the steps in this section on the root account. Doing so might +cause the system to become inaccessible. + 1 + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + 7 + 8 + DSS01.03 + DSS03.05 + DSS05.04 + DSS05.05 + DSS05.07 + DSS06.03 + CCI-000366 + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + SR 1.1 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 6.2 + 1491 + A.12.4.1 + A.12.4.3 + A.6.1.2 + A.7.1.1 + A.9.1.2 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.1 + A.9.4.2 + A.9.4.3 + A.9.4.4 + A.9.4.5 + AC-6 + CM-6(a) + CM-6(b) + CM-6.1(iv) + DE.CM-1 + DE.CM-3 + PR.AC-1 + PR.AC-4 + PR.AC-6 + SRG-OS-000480-GPOS-00227 + 5.6.2 + Ensuring shells are not given to system accounts upon login makes it more +difficult for attackers to make use of system accounts. + CCE-80843-6 + + + + + + + + + Restrict Serial Port Root Logins + To restrict root logins on serial ports, +ensure lines of this form do not appear in /etc/securetty: +ttyS0 +ttyS1 + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + 3.1.1 + 3.1.5 + CCI-000770 + 164.308(a)(1)(ii)(B) + 164.308(a)(7)(i) + 164.308(a)(7)(ii)(A) + 164.310(a)(1) + 164.310(a)(2)(i) + 164.310(a)(2)(ii) + 164.310(a)(2)(iii) + 164.310(b) + 164.310(c) + 164.310(d)(1) + 164.310(d)(2)(iii) + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + AC-6 + CM-6(a) + PR.AC-4 + PR.DS-5 + Preventing direct root login to serial port interfaces +helps ensure accountability for actions taken on the systems +using the root account. + CCE-80856-8 + - name: Restrict Serial Port Root Logins + lineinfile: + dest: /etc/securetty + regexp: ttyS[0-9] + state: absent + tags: + - CCE-80856-8 + - NIST-800-171-3.1.1 + - NIST-800-171-3.1.5 + - NIST-800-53-AC-6 + - NIST-800-53-CM-6(a) + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_serial_port_logins + - restrict_strategy + + sed -i '/ttyS/d' /etc/securetty + + + + + + + + + + Root Path Must Be Vendor Default + Assuming root shell is bash, edit the following files: +~/.profile +~/.bashrc +Change any PATH variables to the vendor default for root and remove any +empty PATH entries or references to relative paths. + 18 + APO13.01 + BAI03.01 + BAI03.02 + BAI03.03 + 4.3.4.3.3 + A.14.1.1 + A.14.2.1 + A.14.2.5 + A.6.1.5 + CM-6(a) + PR.IP-2 + The root account's executable search path must be the vendor default, and must +contain only absolute paths. + + + + + + Restrict Virtual Console Root Logins + To restrict root logins through the (deprecated) virtual console devices, +ensure lines of this form do not appear in /etc/securetty: +vc/1 +vc/2 +vc/3 +vc/4 + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + 3.1.1 + 3.1.5 + CCI-000770 + 164.308(a)(1)(ii)(B) + 164.308(a)(7)(i) + 164.308(a)(7)(ii)(A) + 164.310(a)(1) + 164.310(a)(2)(i) + 164.310(a)(2)(ii) + 164.310(a)(2)(iii) + 164.310(b) + 164.310(c) + 164.310(d)(1) + 164.310(d)(2)(iii) + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + AC-6 + CM-6(a) + PR.AC-4 + PR.DS-5 + SRG-OS-000324-GPOS-00125 + Preventing direct root login to virtual console devices +helps ensure accountability for actions taken on the system +using the root account. + CCE-80864-2 + - name: Restrict Virtual Console Root Logins + lineinfile: + dest: /etc/securetty + regexp: ^vc + state: absent + tags: + - CCE-80864-2 + - NIST-800-171-3.1.1 + - NIST-800-171-3.1.5 + - NIST-800-53-AC-6 + - NIST-800-53-CM-6(a) + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - securetty_root_login_console_only + + sed -i '/^vc\//d' /etc/securetty + + + + + + + + + + Enforce usage of pam_wheel for su authentication + To ensure that only users who are members of the wheel group can +run commands with altered privileges through the su command, make +sure that the following line exists in the file /etc/pam.d/su: +auth required pam_wheel.so use_uid + FMT_SMF_EXT.1.1 + SRG-OS-000373-GPOS-00156 + SRG-OS-000312-GPOS-00123 + 5.3.7 + The su program allows to run commands with a substitute user and +group ID. It is commonly used to run commands as the root user. Limiting +access to such command is considered a good security practice. + CCE-83318-6 + - name: restrict usage of su command only to members of wheel group + replace: + path: /etc/pam.d/su + regexp: ^[\s]*#[\s]*auth[\s]+required[\s]+pam_wheel\.so[\s]+use_uid$ + replace: auth required pam_wheel.so use_uid + tags: + - CCE-83318-6 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - use_pam_wheel_for_su + + +# uncomment the option if commented + sed '/^[[:space:]]*#[[:space:]]*auth[[:space:]]\+required[[:space:]]\+pam_wheel\.so[[:space:]]\+use_uid$/s/^[[:space:]]*#//' -i /etc/pam.d/su + + + + + + + + + + + + Secure Session Configuration Files for Login Accounts + When a user logs into a Unix account, the system +configures the user's session by reading a number of files. Many of +these files are located in the user's home directory, and may have +weak permissions as a result of user error or misconfiguration. If +an attacker can modify or even read certain types of account +configuration information, they can often gain full access to the +affected user's account. Therefore, it is important to test and +correct configuration file permissions for interactive accounts, +particularly those of privileged users such as root or system +administrators. + + Maximum login attempts delay + Maximum time in seconds between fail login attempts before re-prompting. + 1 + 2 + 3 + 4 + 5 + 4 + + + Maximum concurrent login sessions + Maximum number of concurrent sessions by a user + 1 + 10 + 15 + 20 + 3 + 5 + 1 + + + Account Inactivity Timeout (seconds) + In an interactive shell, the value is interpreted as the +number of seconds to wait for input after issuing the primary prompt. +Bash terminates after waiting for that number of seconds if input does +not arrive. + 1800 + 600 + 900 + 300 + 600 + + + Interactive users initialization files + 'A regular expression describing a list of file names +for files that are sourced at login time for interactive users' + (\.bashrc|\.zshrc|\.cshrc|\.profile|\.bash_login|\.bash_profile) + + + Ensure Home Directories are Created for New Users + All local interactive user accounts, upon creation, should be assigned a home directory. + +Configure the operating system to assign home directories to all new local interactive users by setting the CREATE_HOME +parameter in /etc/login.defs to yes as follows: + +CREATE_HOME yes + CCI-000366 + SRG-OS-000480-GPOS-00227 + RHEL-08-010760 + SV-230324r627750_rule + If local interactive users are not assigned a valid home directory, there is no place +for the storage and control of files they should own. + + CCE-83789-8 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83789-8 + - DISA-STIG-RHEL-08-010760 + - accounts_have_homedir_login_defs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Ensure new users receive home directories + block: + + - name: Check for duplicate values + lineinfile: + path: /etc/login.defs + create: false + regexp: ^\s*CREATE_HOME\s+ + state: absent + check_mode: true + changed_when: false + register: dupes + + - name: Deduplicate values from /etc/login.defs + lineinfile: + path: /etc/login.defs + create: false + regexp: ^\s*CREATE_HOME\s+ + state: absent + when: dupes.found is defined and dupes.found > 1 + + - name: Insert correct line to /etc/login.defs + lineinfile: + path: /etc/login.defs + create: true + regexp: ^\s*CREATE_HOME\s+ + line: CREATE_HOME yes + state: present + when: '"shadow-utils" in ansible_facts.packages' + tags: + - CCE-83789-8 + - DISA-STIG-RHEL-08-010760 + - accounts_have_homedir_login_defs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q shadow-utils; then + +if [ -e "/etc/login.defs" ] ; then + + LC_ALL=C sed -i "/^\s*CREATE_HOME\s\+/Id" "/etc/login.defs" +else + touch "/etc/login.defs" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/login.defs" + +cp "/etc/login.defs" "/etc/login.defs.bak" +# Insert before the line matching the regex '^\s*CREATE_HOME'. +line_number="$(LC_ALL=C grep -n "^\s*CREATE_HOME" "/etc/login.defs.bak" | LC_ALL=C sed 's/:.*//g')" +if [ -z "$line_number" ]; then + # There was no match of '^\s*CREATE_HOME', insert at + # the end of the file. + printf '%s\n' "CREATE_HOME yes" >> "/etc/login.defs" +else + head -n "$(( line_number - 1 ))" "/etc/login.defs.bak" > "/etc/login.defs" + printf '%s\n' "CREATE_HOME yes" >> "/etc/login.defs" + tail -n "+$(( line_number ))" "/etc/login.defs.bak" >> "/etc/login.defs" +fi +# Clean up after ourselves. +rm "/etc/login.defs.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure the Logon Failure Delay is Set Correctly in login.defs + To ensure the logon failure delay controlled by /etc/login.defs is set properly, +add or correct the FAIL_DELAY setting in /etc/login.defs to read as follows: +FAIL_DELAY + 11 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + CCI-000366 + 4.3.4.3.2 + 4.3.4.3.3 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + AC-7(b) + CM-6(a) + PR.IP-1 + SRG-OS-000480-GPOS-00226 + RHEL-08-020310 + SV-230378r627750_rule + Increasing the time between a failed authentication attempt and re-prompting to +enter credentials helps to slow a single-threaded brute force attack. + + CCE-84037-1 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-84037-1 + - DISA-STIG-RHEL-08-020310 + - NIST-800-53-AC-7(b) + - NIST-800-53-CM-6(a) + - accounts_logon_fail_delay + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy +- name: XCCDF Value var_accounts_fail_delay # promote to variable + set_fact: + var_accounts_fail_delay: !!str + tags: + - always + +- name: Set accounts logon fail delay + lineinfile: + dest: /etc/login.defs + regexp: ^FAIL_DELAY + line: FAIL_DELAY {{ var_accounts_fail_delay }} + create: true + when: '"shadow-utils" in ansible_facts.packages' + tags: + - CCE-84037-1 + - DISA-STIG-RHEL-08-020310 + - NIST-800-53-AC-7(b) + - NIST-800-53-CM-6(a) + - accounts_logon_fail_delay + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q shadow-utils; then + +var_accounts_fail_delay='' + + +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/login.defs"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^FAIL_DELAY") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s %s" "$stripped_key" "$var_accounts_fail_delay" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^FAIL_DELAY\\>" "/etc/login.defs"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^FAIL_DELAY\\>.*/$escaped_formatted_output/gi" "/etc/login.defs" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-84037-1" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/login.defs" >> "/etc/login.defs" + printf '%s\n' "$formatted_output" >> "/etc/login.defs" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Limit the Number of Concurrent Login Sessions Allowed Per User + Limiting the number of allowed users and sessions per user can limit risks related to Denial of +Service attacks. This addresses concurrent sessions for a single account and does not address +concurrent sessions by a single user via multiple accounts. To set the number of concurrent +sessions per user add the following line in /etc/security/limits.conf or +a file under /etc/security/limits.d/: +* hard maxlogins + 14 + 15 + 18 + 9 + 5.5.2.2 + DSS01.05 + DSS05.02 + CCI-000054 + 4.3.3.4 + SR 3.1 + SR 3.8 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.14.1.2 + A.14.1.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.2 + AC-10 + CM-6(a) + PR.AC-5 + SRG-OS-000027-GPOS-00008 + SRG-OS-000027-VMM-000080 + RHEL-08-020024 + SV-230346r627750_rule + Limiting simultaneous user logins can insulate the system from denial of service +problems caused by excessive logins. Automated login processes operating improperly or +maliciously may result in an exceptional number of simultaneous login sessions. + + CCE-80955-8 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80955-8 + - CJIS-5.5.2.2 + - DISA-STIG-RHEL-08-020024 + - NIST-800-53-AC-10 + - NIST-800-53-CM-6(a) + - accounts_max_concurrent_login_sessions + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - restrict_strategy +- name: XCCDF Value var_accounts_max_concurrent_login_sessions # promote to variable + set_fact: + var_accounts_max_concurrent_login_sessions: !!str + tags: + - always + +- name: Find /etc/security/limits.d files containing maxlogins configuration + find: + paths: /etc/security/limits.d + contains: ^[\s]*\*[\s]+(?:(?:hard)|(?:-))[\s]+maxlogins + patterns: '*.conf' + register: maxlogins + when: '"pam" in ansible_facts.packages' + tags: + - CCE-80955-8 + - CJIS-5.5.2.2 + - DISA-STIG-RHEL-08-020024 + - NIST-800-53-AC-10 + - NIST-800-53-CM-6(a) + - accounts_max_concurrent_login_sessions + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - restrict_strategy + +- name: Limit the Number of Concurrent Login Sessions Allowed Per User in files from + limits.d + replace: + dest: '{{ item.path }}' + regexp: ^#?\*.*maxlogins.* + replace: '* hard maxlogins {{ var_accounts_max_concurrent_login_sessions + }}' + with_items: + - '{{ maxlogins.files }}' + when: '"pam" in ansible_facts.packages' + tags: + - CCE-80955-8 + - CJIS-5.5.2.2 + - DISA-STIG-RHEL-08-020024 + - NIST-800-53-AC-10 + - NIST-800-53-CM-6(a) + - accounts_max_concurrent_login_sessions + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - restrict_strategy + +- name: Limit the Number of Concurrent Login Sessions Allowed Per User + lineinfile: + state: present + dest: /etc/security/limits.conf + insertbefore: ^# End of file + regexp: ^#?\*.*maxlogins + line: '* hard maxlogins {{ var_accounts_max_concurrent_login_sessions + }}' + create: true + when: + - '"pam" in ansible_facts.packages' + - maxlogins.matched == 0 + tags: + - CCE-80955-8 + - CJIS-5.5.2.2 + - DISA-STIG-RHEL-08-020024 + - NIST-800-53-AC-10 + - NIST-800-53-CM-6(a) + - accounts_max_concurrent_login_sessions + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q pam; then + +var_accounts_max_concurrent_login_sessions='' + + +if grep -q '^[^#]*\<maxlogins\>' /etc/security/limits.d/*.conf; then + sed -i "/^[^#]*\<maxlogins\>/ s/maxlogins.*/maxlogins $var_accounts_max_concurrent_login_sessions/" /etc/security/limits.d/*.conf +elif grep -q '^[^#]*\<maxlogins\>' /etc/security/limits.conf; then + sed -i "/^[^#]*\<maxlogins\>/ s/maxlogins.*/maxlogins $var_accounts_max_concurrent_login_sessions/" /etc/security/limits.conf +else + echo "* hard maxlogins $var_accounts_max_concurrent_login_sessions" >> /etc/security/limits.conf +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Configure Polyinstantiation of /tmp Directories + To configure polyinstantiated /tmp directories, first create the parent directories +which will hold the polyinstantiation child directories. Use the following command: +$ sudo mkdir --mode 000 /tmp/tmp-inst +Then, add the following entry to /etc/security/namespace.conf: +/tmp /tmp/tmp-inst/ level root,adm + BP28(R39) + Polyinstantiation of temporary directories is a proactive security measure +which reduces chances of attacks that are made possible by /tmp +directories being world-writable. + CCE-83732-8 + - name: Create /tmp/tmp-inst directory + file: + path: /tmp/tmp-inst + state: directory + mode: '000' + seuser: system_u + serole: object_r + setype: tmp_t + tags: + - CCE-83732-8 + - accounts_polyinstantiated_tmp + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - restrict_strategy + +- name: Make changes to /etc/security/namespace.conf + lineinfile: + path: /etc/security/namespace.conf + create: false + regexp: ^\s*/tmp\s+/tmp/tmp-inst/\s+level\s+root,adm$ + line: /tmp /tmp/tmp-inst/ level root,adm + state: present + tags: + - CCE-83732-8 + - accounts_polyinstantiated_tmp + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - restrict_strategy + + if ! [ -d /tmp/tmp-inst ] ; then + mkdir --mode 000 /tmp/tmp-inst +fi +chmod 000 /tmp/tmp-inst +chcon --reference=/tmp /tmp/tmp-inst + +if ! grep -Eq '^\s*/tmp\s+/tmp/tmp-inst/\s+level\s+root,adm$' /etc/security/namespace.conf ; then + if grep -Eq '^\s*/tmp\s+' /etc/security/namespace.conf ; then + sed -i '/^\s*\/tmp/d' /etc/security/namespace.conf + fi + echo "/tmp /tmp/tmp-inst/ level root,adm" >> /etc/security/namespace.conf +fi + + + + + + + + + + Configure Polyinstantiation of /var/tmp Directories + To configure polyinstantiated /tmp directories, first create the parent directories +which will hold the polyinstantiation child directories. Use the following command: +$ sudo mkdir --mode 000 /var/tmp/tmp-inst +Then, add the following entry to /etc/security/namespace.conf: +/var/tmp /var/tmp/tmp-inst/ level root,adm + BP28(R39) + Polyinstantiation of temporary directories is a proactive security measure +which reduces chances of attacks that are made possible by /var/tmp +directories being world-writable. + CCE-83778-1 + - name: Create /var/tmp/tmp-inst directory + file: + path: /var/tmp/tmp-inst + state: directory + mode: '000' + seuser: system_u + serole: object_r + setype: tmp_t + tags: + - CCE-83778-1 + - accounts_polyinstantiated_var_tmp + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - restrict_strategy + +- name: Make changes to /etc/security/namespace.conf + lineinfile: + path: /etc/security/namespace.conf + create: false + regexp: ^\s*/var/tmp\s+/var/tmp/tmp-inst/\s+level\s+root,adm$ + line: /var/tmp /var/tmp/tmp-inst/ level root,adm + state: present + tags: + - CCE-83778-1 + - accounts_polyinstantiated_var_tmp + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - restrict_strategy + + if ! [ -d /tmp-inst ] ; then + mkdir --mode 000 /var/tmp/tmp-inst +fi +chmod 000 /var/tmp/tmp-inst +chcon --reference=/var/tmp/ /var/tmp/tmp-inst + +if ! grep -Eq '^\s*/var/tmp\s+/var/tmp/tmp-inst/\s+level\s+root,adm$' /etc/security/namespace.conf ; then + if grep -Eq '^\s*/var/tmp\s+' /etc/security/namespace.conf ; then + sed -i '/^\s*\/var\/tmp/d' /etc/security/namespace.conf + fi + echo "/var/tmp /var/tmp/tmp-inst/ level root,adm" >> /etc/security/namespace.conf +fi + + + + + + + + + + Set Interactive Session Timeout + Setting the TMOUT option in /etc/profile ensures that +all user sessions will terminate based on inactivity. +The value of TMOUT should be exported and read only. +The TMOUT + +setting in a file loaded by /etc/profile, e.g. +/etc/profile.d/tmout.sh should read as follows: +declare -xr TMOUT= + BP28(R29) + 1 + 12 + 15 + 16 + DSS05.04 + DSS05.10 + DSS06.10 + 3.1.11 + CCI-000057 + CCI-001133 + CCI-002361 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + A.18.1.4 + A.9.2.1 + A.9.2.4 + A.9.3.1 + A.9.4.2 + A.9.4.3 + CIP-004-6 R2.2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.2 + CIP-007-3 R5.3.1 + CIP-007-3 R5.3.2 + CIP-007-3 R5.3.3 + AC-12 + SC-10 + AC-2(5) + CM-6(a) + PR.AC-7 + FMT_MOF_EXT.1 + SRG-OS-000163-GPOS-00072 + SRG-OS-000029-GPOS-00010 + SRG-OS-000163-VMM-000700 + SRG-OS-000279-VMM-001010 + 5.6.3 + Terminating an idle session within a short time period reduces +the window of opportunity for unauthorized personnel to take control of a +management session enabled on the console or console port that has been +left unattended. + + CCE-80673-7 + - name: XCCDF Value var_accounts_tmout # promote to variable + set_fact: + var_accounts_tmout: !!str + tags: + - always + +- name: Check for duplicate values + lineinfile: + path: /etc/profile.d/tmout.sh + create: false + regexp: TMOUT= + state: absent + check_mode: true + changed_when: false + register: dupes + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80673-7 + - NIST-800-171-3.1.11 + - NIST-800-53-AC-12 + - NIST-800-53-AC-2(5) + - NIST-800-53-CM-6(a) + - NIST-800-53-SC-10 + - accounts_tmout + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Deduplicate values from /etc/profile.d/tmout.sh + lineinfile: + path: /etc/profile.d/tmout.sh + create: false + regexp: TMOUT= + state: absent + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - dupes.found is defined and dupes.found > 1 + tags: + - CCE-80673-7 + - NIST-800-171-3.1.11 + - NIST-800-53-AC-12 + - NIST-800-53-AC-2(5) + - NIST-800-53-CM-6(a) + - NIST-800-53-SC-10 + - accounts_tmout + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Insert correct line into /etc/profile.d/tmout.sh + lineinfile: + path: /etc/profile.d/tmout.sh + create: true + regexp: TMOUT= + line: declare -xr TMOUT={{ var_accounts_tmout }} + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80673-7 + - NIST-800-171-3.1.11 + - NIST-800-53-AC-12 + - NIST-800-53-AC-2(5) + - NIST-800-53-CM-6(a) + - NIST-800-53-SC-10 + - accounts_tmout + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_accounts_tmout='' + + +# if 0, no occurence of tmout found, if 1, occurence found +tmout_found=0 + +for f in /etc/profile /etc/profile.d/*.sh; do + if grep --silent '^\s*TMOUT' $f; then + sed -i -E "s/^(\s*)TMOUT\s*=\s*(\w|\$)*(.*)$/declare -xr TMOUT=$var_accounts_tmout\3/g" $f + tmout_found=1 + fi +done + +if [ $tmout_found -eq 0 ]; then + echo -e "\n# Set TMOUT to $var_accounts_tmout per security requirements" >> /etc/profile.d/tmout.sh + echo "declare -xr TMOUT=$var_accounts_tmout" >> /etc/profile.d/tmout.sh +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + User Initialization Files Must Be Group-Owned By The Primary User + Change the group owner of interactive users files to the group found +in /etc/passwd for the user. To change the group owner of a local +interactive user home directory, use the following command: +$ sudo chgrp USER_GROUP /home/USER/.INIT_FILE + +This rule ensures every initialization file related to an interactive user +is group-owned by an interactive user. + Due to OVAL limitation, this rule can report a false negative in a +specific situation where two interactive users swap the group-ownership +of their respective initialization files. + CCI-000366 + SRG-OS-000480-GPOS-00227 + Local initialization files for interactive users are used to configure the +user's shell environment upon logon. Malicious modification of these files could +compromise accounts upon logon. + - name: Ensure interactive local users are the group-owners of their respective initialization + files + ansible.builtin.command: + cmd: awk -F':' '{ if ($3 >= 1000 && $3 != 65534) system("chgrp -f " $3" "$6"/.[^\.]?*") + }' /etc/passwd + tags: + - accounts_user_dot_group_ownership + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + +awk -F':' '{ if ($3 >= 1000 && $3 != 65534) system("chgrp -f " $3" "$6"/.[^\.]?*") }' /etc/passwd + + + + + + + + + + User Initialization Files Must Not Run World-Writable Programs + Set the mode on files being executed by the user initialization files with the +following command: +$ sudo chmod o-w FILE + CCI-000366 + SRG-OS-000480-GPOS-00227 + RHEL-08-010660 + 6.2.12 + SV-230309r627750_rule + If user start-up files execute world-writable programs, especially in +unprotected directories, they could be maliciously modified to destroy user +files or otherwise compromise the system at the user level. If the system is +compromised at the user level, it is easier to elevate privileges to eventually +compromise the system at the root and network level. + CCE-84039-7 + +readarray -t world_writable_files < <(find / -xdev -type f -perm -0002 2> /dev/null) +readarray -t interactive_home_dirs < <(awk -F':' '{ if ($3 >= 1000 && $3 != 65534) print $6 }' /etc/passwd) + +for world_writable in "${world_writable_files[@]}"; do + for homedir in "${interactive_home_dirs[@]}"; do + if grep -q -d skip "$world_writable" "$homedir"/.*; then + chmod o-w $world_writable + break + fi + done +done + + + + + + + + + + + User Initialization Files Must Be Owned By the Primary User + Set the owner of the user initialization files for interactive users to +the primary owner with the following command: +$ sudo chown USER /home/USER/.* + +This rule ensures every initialization file related to an interactive user +is owned by an interactive user. + Due to OVAL limitation, this rule can report a false negative in a +specific situation where two interactive users swap the ownership of +their respective initialization files. + CCI-000366 + SRG-OS-000480-GPOS-00227 + Local initialization files are used to configure the user's shell environment +upon logon. Malicious modification of these files could compromise accounts upon +logon. + - name: Ensure interactive local users are the owners of their respective initialization + files + ansible.builtin.command: + cmd: awk -F':' '{ if ($3 >= 1000 && $3 != 65534) system("chown -f " $3" "$6"/.[^\.]?*") + }' /etc/passwd + tags: + - accounts_user_dot_user_ownership + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + +awk -F':' '{ if ($3 >= 1000 && $3 != 65534) system("chown -f " $3" "$6"/.[^\.]?*") }' /etc/passwd + + + + + + + + + + Ensure that Users Path Contains Only Local Directories + Ensure that all interactive user initialization files executable search +path statements do not contain statements that will reference a working +directory other than the users home directory. + CCI-000366 + SRG-OS-000480-GPOS-00227 + RHEL-08-010690 + SV-230317r792896_rule + The executable search path (typically the PATH environment variable) contains a +list of directories for the shell to search to find executables. If this path +includes the current working directory (other than the users home directory), +executables in these directories may be executed instead of system commands. +This variable is formatted as a colon-separated list of directories. If there is +an empty entry, such as a leading or trailing colon or two consecutive colons, +this is interpreted as the current working directory. If deviations from the +default system search path for the local interactive user are required, they +must be documented with the Information System Security Officer (ISSO). + CCE-84040-5 + + + + + + All Interactive Users Must Have A Home Directory Defined + Assign home directories to all interactive users that currently do not +have a home directory assigned. + +This rule checks if the home directory is properly defined in a folder which has +at least one parent folder, like "user" in "/home/user" or "/remote/users/user". +Therefore, this rule will report a finding for home directories like /users, +/tmp or /. + CCI-000366 + SRG-OS-000480-GPOS-00227 + RHEL-08-010720 + SV-230320r627750_rule + If local interactive users are not assigned a valid home directory, there is no +place for the storage and control of files they should own. + CCE-84036-3 + - name: Get all local users from /etc/passwd + ansible.builtin.getent: + database: passwd + split: ':' + tags: + - CCE-84036-3 + - DISA-STIG-RHEL-08-010720 + - accounts_user_interactive_home_directory_defined + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Create local_users variable from the getent output + ansible.builtin.set_fact: + local_users: '{{ ansible_facts.getent_passwd|dict2items }}' + tags: + - CCE-84036-3 + - DISA-STIG-RHEL-08-010720 + - accounts_user_interactive_home_directory_defined + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Ensure interactive users have an exclusive home directory defined + ansible.builtin.user: + name: '{{ item.key }}' + home: /home/{{ item.key }} + create_home: false + loop: '{{ local_users }}' + when: + - item.value[2]|int >= 1000 + - item.value[2]|int != 65534 + - not item.value[4] | regex_search('^\/\w*\/\w{1,}') + tags: + - CCE-84036-3 + - DISA-STIG-RHEL-08-010720 + - accounts_user_interactive_home_directory_defined + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + +for user in $(awk -F':' '{ if ($4 >= 1000 && $4 != 65534) print $1 }' /etc/passwd); do + # This follows the same logic of evaluation of home directories as used in OVAL. + if ! grep -q $user /etc/passwd | cut -d: -f6 | grep '^\/\w*\/\w\{1,\}'; then + sed -i "s/\($user:x:[0-9]*:[0-9]*:.*:\).*\(:.*\)$/\1\/home\/$user\2/g" /etc/passwd; + fi +done + + + + + + + + + + All Interactive Users Home Directories Must Exist + Create home directories to all interactive users that currently do not +have a home directory assigned. Use the following commands to create the user +home directory assigned in /etc/passwd: +$ sudo mkdir /home/USER + CCI-000366 + SRG-OS-000480-GPOS-00227 + RHEL-08-010750 + 6.2.9 + SV-230323r627750_rule + If a local interactive user has a home directory defined that does not exist, +the user may be given access to the / directory as the current working directory +upon logon. This could create a Denial of Service because the user would not be +able to access their logon configuration files, and it may give them visibility +to system files they normally would not be able to access. + CCE-83424-2 + - name: Get all local users from /etc/passwd + ansible.builtin.getent: + database: passwd + split: ':' + tags: + - CCE-83424-2 + - DISA-STIG-RHEL-08-010750 + - accounts_user_interactive_home_directory_exists + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Create local_users variable from the getent output + ansible.builtin.set_fact: + local_users: '{{ ansible_facts.getent_passwd|dict2items }}' + tags: + - CCE-83424-2 + - DISA-STIG-RHEL-08-010750 + - accounts_user_interactive_home_directory_exists + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Ensure interactive users have a home directory exists + ansible.builtin.user: + name: '{{ item.key }}' + create_home: true + loop: '{{ local_users }}' + when: + - item.value[2]|int >= 1000 + - item.value[2]|int != 65534 + tags: + - CCE-83424-2 + - DISA-STIG-RHEL-08-010750 + - accounts_user_interactive_home_directory_exists + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + +for user in $(awk -F':' '{ if ($3 >= 1000 && $3 != 65534) print $1}' /etc/passwd); do + mkhomedir_helper $user 0077; +done + + + + + + + + + + All User Files and Directories In The Home Directory Must Be Group-Owned By The Primary User + Change the group of a local interactive users files and directories to a +group that the interactive user is a member of. To change the group owner of a +local interactive users files and directories, use the following command: +$ sudo chgrp USER_GROUP /home/USER/FILE_DIR + +This rule ensures every file or directory under the home directory related +to an interactive user is group-owned by an interactive user. + Due to OVAL limitation, this rule can report a false negative in a +specific situation where two interactive users swap the group-ownership +of folders or files in their respective home directories. + CCI-000366 + SRG-OS-000480-GPOS-00227 + RHEL-08-010741 + SV-244532r743845_rule + If a local interactive users files are group-owned by a group of which the +user is not a member, unintended users may be able to access them. + CCE-86534-5 + - name: Get all local users from /etc/passwd + ansible.builtin.getent: + database: passwd + split: ':' + tags: + - CCE-86534-5 + - DISA-STIG-RHEL-08-010741 + - accounts_users_home_files_groupownership + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Create local_users variable from the getent output + ansible.builtin.set_fact: + local_users: '{{ ansible_facts.getent_passwd|dict2items }}' + tags: + - CCE-86534-5 + - DISA-STIG-RHEL-08-010741 + - accounts_users_home_files_groupownership + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Test for existence home directories to avoid creating them, but only fixing + ownership + ansible.builtin.stat: + path: '{{ item.value[4] }}' + register: path_exists + loop: '{{ local_users }}' + when: + - item.value[2]|int >= 1000 + - item.value[2]|int != 65534 + tags: + - CCE-86534-5 + - DISA-STIG-RHEL-08-010741 + - accounts_users_home_files_groupownership + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Ensure interactive local users are the owners of their respective home directories + ansible.builtin.file: + path: '{{ item.0.value[4] }}' + group: '{{ item.0.value[2] }}' + recurse: true + loop: '{{ local_users|zip(path_exists.results)|list }}' + when: item.1.stat is defined and item.1.stat.exists + tags: + - CCE-86534-5 + - DISA-STIG-RHEL-08-010741 + - accounts_users_home_files_groupownership + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + +for user in $(awk -F':' '{ if ($4 >= 1000 && $4 != 65534) print $1 }' /etc/passwd); do + home_dir=$(getent passwd $user | cut -d: -f6) + group=$(getent passwd $user | cut -d: -f4) + # Only update the group-ownership when necessary. This will avoid changing the inode timestamp + # when the group is already defined as expected, therefore not impacting in possible integrity + # check systems that also check inodes timestamps. + find $home_dir -not -group $group -exec chgrp -f $group {} \; +done + + + + + + + + + + All User Files and Directories In The Home Directory Must Have a Valid Owner + Either remove all files and directories from the system that +do not have a valid user, or assign a valid user to all unowned +files and directories. To assign a valid owner to a local +interactive user's files and directories, use the following command: +$ sudo chown -R USER /home/USER + +This rule ensures every file or directory under the home directory related +to an interactive user is owned by an interactive user. + Due to OVAL limitation, this rule can report a false negative in a +specific situation where two interactive users swap the ownership of +folders or files in their respective home directories. + CCI-000366 + SRG-OS-000480-GPOS-00227 + 6.2.8 + If local interactive users do not own the files in their directories, +unauthorized users may be able to access them. Additionally, if files are not +owned by the user, this could be an indication of system compromise. + - name: Get all local users from /etc/passwd + ansible.builtin.getent: + database: passwd + split: ':' + tags: + - accounts_users_home_files_ownership + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Create local_users variable from the getent output + ansible.builtin.set_fact: + local_users: '{{ ansible_facts.getent_passwd|dict2items }}' + tags: + - accounts_users_home_files_ownership + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Test for existence home directories to avoid creating them, but only fixing + ownership + ansible.builtin.stat: + path: '{{ item.value[4] }}' + register: path_exists + loop: '{{ local_users }}' + when: + - item.value[1]|int >= 1000 + - item.value[1]|int != 65534 + tags: + - accounts_users_home_files_ownership + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Ensure interactive local users are the owners of their respective home directories + ansible.builtin.file: + path: '{{ item.0.value[4] }}' + owner: '{{ item.0.value[1] }}' + recurse: true + loop: '{{ local_users|zip(path_exists.results)|list }}' + when: item.1.stat is defined and item.1.stat.exists + tags: + - accounts_users_home_files_ownership + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + +for user in $(awk -F':' '{ if ($3 >= 1000 && $3 != 65534) print $1 }' /etc/passwd); do + home_dir=$(getent passwd $user | cut -d: -f6) + # Only update the ownership when necessary. This will avoid changing the inode timestamp + # when the owner is already defined as expected, therefore not impacting in possible integrity + # check systems that also check inodes timestamps. + find $home_dir -not -user $user -exec chown -f $user {} \; +done + + + + + + + + + + All User Files and Directories In The Home Directory Must Have Mode 0750 Or Less Permissive + Set the mode on files and directories in the local interactive user home +directory with the following command: +$ sudo chmod 0750 /home/USER/FILE_DIR +Files that begin with a "." are excluded from this requirement. + CCI-000366 + SRG-OS-000480-GPOS-00227 + RHEL-08-010731 + SV-244531r743842_rule + If a local interactive user files have excessive permissions, unintended users +may be able to access or modify them. + CCE-85888-6 + - name: Get all local users from /etc/passwd + ansible.builtin.getent: + database: passwd + split: ':' + tags: + - CCE-85888-6 + - DISA-STIG-RHEL-08-010731 + - accounts_users_home_files_permissions + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Create local_users variable from the getent output + ansible.builtin.set_fact: + local_users: '{{ ansible_facts.getent_passwd|dict2items }}' + tags: + - CCE-85888-6 + - DISA-STIG-RHEL-08-010731 + - accounts_users_home_files_permissions + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Test for existence home directories to avoid creating them. + ansible.builtin.stat: + path: '{{ item.value[4] }}' + register: path_exists + loop: '{{ local_users }}' + when: + - item.value[1]|int >= 1000 + - item.value[1]|int != 65534 + tags: + - CCE-85888-6 + - DISA-STIG-RHEL-08-010731 + - accounts_users_home_files_permissions + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Ensure interactive local users have proper permissions on their respective + home directories + ansible.builtin.file: + path: '{{ item.0.value[4] }}' + mode: u-s,g-w-s,o=- + follow: false + recurse: true + loop: '{{ local_users|zip(path_exists.results)|list }}' + when: item.1.stat is defined and item.1.stat.exists + tags: + - CCE-85888-6 + - DISA-STIG-RHEL-08-010731 + - accounts_users_home_files_permissions + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + +for home_dir in $(awk -F':' '{ if ($3 >= 1000 && $3 != 65534) print $6 }' /etc/passwd); do + # Only update the permissions when necessary. This will avoid changing the inode timestamp when + # the permission is already defined as expected, therefore not impacting in possible integrity + # check systems that also check inodes timestamps. + find "$home_dir" -perm /7027 -exec chmod u-s,g-w-s,o=- {} \; +done + + + + + + + + + + All Interactive User Home Directories Must Be Group-Owned By The Primary User + Change the group owner of interactive users home directory to the +group found in /etc/passwd. To change the group owner of +interactive users home directory, use the following command: +$ sudo chgrp USER_GROUP /home/USER + +This rule ensures every home directory related to an interactive user is +group-owned by an interactive user. It also ensures that interactive users +are group-owners of one and only one home directory. + Due to OVAL limitation, this rule can report a false negative in a +specific situation where two interactive users swap the group-ownership +of their respective home directories. + CCI-000366 + SRG-OS-000480-GPOS-00227 + RHEL-08-010740 + 6.2.10 + SV-230322r743963_rule + If the Group Identifier (GID) of a local interactive users home directory is +not the same as the primary GID of the user, this would allow unauthorized +access to the users files, and users that share the same group may not be +able to access files that they legitimately should. + CCE-83434-1 + - name: Get all local users from /etc/passwd + ansible.builtin.getent: + database: passwd + split: ':' + tags: + - CCE-83434-1 + - DISA-STIG-RHEL-08-010740 + - file_groupownership_home_directories + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Create local_users variable from the getent output + ansible.builtin.set_fact: + local_users: '{{ ansible_facts.getent_passwd|dict2items }}' + tags: + - CCE-83434-1 + - DISA-STIG-RHEL-08-010740 + - file_groupownership_home_directories + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Test for existence of home directories to avoid creating them, but only fixing + group ownership + ansible.builtin.stat: + path: '{{ item.value[4] }}' + register: path_exists + loop: '{{ local_users }}' + when: + - item.value[2]|int >= 1000 + - item.value[2]|int != 65534 + tags: + - CCE-83434-1 + - DISA-STIG-RHEL-08-010740 + - file_groupownership_home_directories + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Ensure interactive local users are the group-owners of their respective home + directories + ansible.builtin.file: + path: '{{ item.0.value[4] }}' + group: '{{ item.0.value[2] }}' + loop: '{{ local_users|zip(path_exists.results)|list }}' + when: item.1.stat is defined and item.1.stat.exists + tags: + - CCE-83434-1 + - DISA-STIG-RHEL-08-010740 + - file_groupownership_home_directories + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + +awk -F':' '{ if ($4 >= 1000 && $4 != 65534) system("chgrp -f " $4" "$6) }' /etc/passwd + + + + + + + + + + All Interactive User Home Directories Must Be Owned By The Primary User + Change the owner of interactive users home directories to that correct +owner. To change the owner of a interactive users home directory, use +the following command: +$ sudo chown USER /home/USER + +This rule ensures every home directory related to an interactive user is +owned by an interactive user. It also ensures that interactive users are +owners of one and only one home directory. + Due to OVAL limitation, this rule can report a false negative in a +specific situation where two interactive users swap the ownership of +their respective home directories. + CCI-000366 + SRG-OS-000480-GPOS-00227 + 6.2.10 + If a local interactive user does not own their home directory, unauthorized +users could access or modify the user's files, and the users may not be able to +access their own files. + CCE-86131-0 + - name: Get all local users from /etc/passwd + ansible.builtin.getent: + database: passwd + split: ':' + tags: + - CCE-86131-0 + - file_ownership_home_directories + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Create local_users variable from the getent output + ansible.builtin.set_fact: + local_users: '{{ ansible_facts.getent_passwd|dict2items }}' + tags: + - CCE-86131-0 + - file_ownership_home_directories + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Test for existence home directories to avoid creating them, but only fixing + ownership + ansible.builtin.stat: + path: '{{ item.value[4] }}' + register: path_exists + loop: '{{ local_users }}' + when: + - item.value[1]|int >= 1000 + - item.value[1]|int != 65534 + tags: + - CCE-86131-0 + - file_ownership_home_directories + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Ensure interactive local users are the owners of their respective home directories + ansible.builtin.file: + path: '{{ item.0.value[4] }}' + owner: '{{ item.0.value[1] }}' + loop: '{{ local_users|zip(path_exists.results)|list }}' + when: item.1.stat is defined and item.1.stat.exists + tags: + - CCE-86131-0 + - file_ownership_home_directories + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + +awk -F':' '{ if ($3 >= 1000 && $3 != 65534) system("chown -f " $3" "$6) }' /etc/passwd + + + + + + + + + + Ensure All User Initialization Files Have Mode 0740 Or Less Permissive + Set the mode of the user initialization files to 0740 with the +following command: +$ sudo chmod 0740 /home/USER/.INIT_FILE + CCI-000366 + SRG-OS-000480-GPOS-00227 + RHEL-08-010770 + SV-230325r627750_rule + Local initialization files are used to configure the user's shell environment +upon logon. Malicious modification of these files could compromise accounts upon +logon. + CCE-84043-9 + + + + + + All Interactive User Home Directories Must Have mode 0750 Or Less Permissive + Change the mode of interactive users home directories to 0750. To +change the mode of interactive users home directory, use the +following command: +$ sudo chmod 0750 /home/USER + CCI-000366 + SRG-OS-000480-GPOS-00227 + RHEL-08-010730 + 6.2.11 + SV-230321r627750_rule + Excessive permissions on local interactive user home directories may allow +unauthorized access to user files by other users. + CCE-84038-9 + - name: Get all local users from /etc/passwd + ansible.builtin.getent: + database: passwd + split: ':' + tags: + - CCE-84038-9 + - DISA-STIG-RHEL-08-010730 + - file_permissions_home_directories + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Create local_users variable from the getent output + ansible.builtin.set_fact: + local_users: '{{ ansible_facts.getent_passwd|dict2items }}' + tags: + - CCE-84038-9 + - DISA-STIG-RHEL-08-010730 + - file_permissions_home_directories + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Test for existence home directories to avoid creating them. + ansible.builtin.stat: + path: '{{ item.value[4] }}' + register: path_exists + loop: '{{ local_users }}' + when: + - item.value[1]|int >= 1000 + - item.value[1]|int != 65534 + tags: + - CCE-84038-9 + - DISA-STIG-RHEL-08-010730 + - file_permissions_home_directories + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Ensure interactive local users have proper permissions on their respective + home directories + ansible.builtin.file: + path: '{{ item.0.value[4] }}' + mode: u-s,g-w-s,o=- + follow: false + recurse: false + loop: '{{ local_users|zip(path_exists.results)|list }}' + when: item.1.stat is defined and item.1.stat.exists + tags: + - CCE-84038-9 + - DISA-STIG-RHEL-08-010730 + - file_permissions_home_directories + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + +for home_dir in $(awk -F':' '{ if ($3 >= 1000 && $3 != 65534) print $6 }' /etc/passwd); do + # Only update the permissions when necessary. This will avoid changing the inode timestamp when + # the permission is already defined as expected, therefore not impacting in possible integrity + # check systems that also check inodes timestamps. + find "$home_dir" -maxdepth 0 -perm /7027 -exec chmod u-s,g-w-s,o=- {} \; +done + + + + + + + + + + Ensure that User Home Directories are not Group-Writable or World-Readable + For each human user of the system, view the +permissions of the user's home directory: +# ls -ld /home/USER +Ensure that the directory is not group-writable and that it +is not world-readable. If necessary, repair the permissions: +# chmod g-w /home/USER +# chmod o-rwx /home/USER + This action may involve modifying user home directories. +Notify your user community, and solicit input if appropriate, +before making this type of change. + This rule is deprecated in favor of the file_permissions_home_directories rule. +Please consider replacing this rule in your files as it is not expected to receive +updates as of version 0.1.62. + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + CCI-000225 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-6(a) + AC-6(1) + CM-6(a) + PR.AC-4 + PR.DS-5 + User home directories contain many configuration files which +affect the behavior of a user's account. No user should ever have +write permission to another user's home directory. Group shared +directories can be configured in sub-directories or elsewhere in the +filesystem if they are needed. Typically, user home directories +should not be world-readable, as it would disclose file names +to other users. If a subset of users need read access +to one another's home directories, this can be provided using +groups or ACLs. + CCE-84274-0 + - name: Get all local users from /etc/passwd + ansible.builtin.getent: + database: passwd + split: ':' + tags: + - CCE-84274-0 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-6(a) + - file_permissions_home_dirs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Create local_users variable from the getent output + ansible.builtin.set_fact: + local_users: '{{ ansible_facts.getent_passwd|dict2items }}' + tags: + - CCE-84274-0 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-6(a) + - file_permissions_home_dirs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Test for existence home directories to avoid creating them. + ansible.builtin.stat: + path: '{{ item.value[4] }}' + register: path_exists + loop: '{{ local_users }}' + when: + - item.value[1]|int >= 1000 + - item.value[1]|int != 65534 + tags: + - CCE-84274-0 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-6(a) + - file_permissions_home_dirs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Ensure interactive local users have proper permissions on their respective + home directories + ansible.builtin.file: + path: '{{ item.0.value[4] }}' + mode: u-s,g-w-s,o=- + follow: false + recurse: false + loop: '{{ local_users|zip(path_exists.results)|list }}' + when: item.1.stat is defined and item.1.stat.exists + tags: + - CCE-84274-0 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-6(a) + - file_permissions_home_dirs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + +for home_dir in $(awk -F':' '{ if ($3 >= 1000 && $3 != 65534) print $6 }' /etc/passwd); do + # Only update the permissions when necessary. This will avoid changing the inode timestamp when + # the permission is already defined as expected, therefore not impacting in possible integrity + # check systems that also check inodes timestamps. + find "$home_dir" -maxdepth 0 -perm /7027 -exec chmod u-s,g-w-s,o=- {} \; +done + + + + + + + + + + Ensure that No Dangerous Directories Exist in Root's Path + The active path of the root account can be obtained by +starting a new root shell and running: +# echo $PATH +This will produce a colon-separated list of +directories in the path. + +Certain path elements could be considered dangerous, as they could lead +to root executing unknown or +untrusted programs, which could contain malicious +code. +Since root may sometimes work inside +untrusted directories, the . character, which represents the +current directory, should never be in the root path, nor should any +directory which can be written to by an unprivileged or +semi-privileged (system) user. + +It is a good practice for administrators to always execute +privileged commands by typing the full path to the +command. + + Ensure that Root's Path Does Not Include World or Group-Writable Directories + For each element in root's path, run: +# ls -ld DIR +and ensure that write permissions are disabled for group and +other. + 11 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + CCI-000366 + 4.3.4.3.2 + 4.3.4.3.3 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + CM-6(a) + CM-6(a) + PR.IP-1 + 6.2.7 + Such entries increase the risk that root could +execute code provided by unprivileged users, +and potentially malicious code. + CCE-80672-9 + - name: Print error message if user is not root + fail: + msg: Root account required to read root $PATH + when: ansible_env.USER != "root" + ignore_errors: true + tags: + - CCE-80672-9 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-6(a) + - accounts_root_path_dirs_no_write + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Get root paths which are not symbolic links + stat: + path: '{{ item }}' + changed_when: false + failed_when: false + register: root_paths + with_items: '{{ ansible_env.PATH.split('':'') }}' + when: ansible_env.USER == "root" + tags: + - CCE-80672-9 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-6(a) + - accounts_root_path_dirs_no_write + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Disable writability to root directories + file: + path: '{{ item.item }}' + mode: g-w,o-w + with_items: '{{ root_paths.results }}' + when: + - root_paths.results is defined + - item.stat.exists + - not item.stat.islnk + tags: + - CCE-80672-9 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-6(a) + - accounts_root_path_dirs_no_write + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + + + + + + + + + Ensure that Root's Path Does Not Include Relative Paths or Null Directories + Ensure that none of the directories in root's path is equal to a single +. character, or +that it contains any instances that lead to relative path traversal, such as +.. or beginning a path without the slash (/) character. +Also ensure that there are no "empty" elements in the path, such as in these examples: +PATH=:/bin +PATH=/bin: +PATH=/bin::/sbin +These empty elements have the same effect as a single . character. + 11 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + CCI-000366 + 4.3.4.3.2 + 4.3.4.3.3 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + CM-6(a) + CM-6(a) + PR.IP-1 + 6.2.7 + Including these entries increases the risk that root could +execute code from an untrusted location. + CCE-85914-0 + + + + + + + Ensure that Users Have Sensible Umask Values + The umask setting controls the default permissions +for the creation of new files. +With a default umask setting of 077, files and directories +created by users will not be readable by any other user on the +system. Users who wish to make specific files group- or +world-readable can accomplish this by using the chmod command. +Additionally, users can make all their files readable to their +group by default by setting a umask of 027 in their shell +configuration files. If default per-user groups exist (that is, if +every user has a default group whose name is the same as that +user's username and whose only member is the user), then it may +even be safe for users to select a umask of 007, making it very +easy to intentionally share files with groups of which the user is +a member. + + + Sensible umask + Enter default user umask + 007 + 022 + 027 + 077 + 027 + + + Ensure the Default Bash Umask is Set Correctly + To ensure the default umask for users of the Bash shell is set properly, +add or correct the umask setting in /etc/bashrc to read +as follows: +umask + BP28(R35) + 18 + APO13.01 + BAI03.01 + BAI03.02 + BAI03.03 + CCI-000366 + 4.3.4.3.3 + A.14.1.1 + A.14.2.1 + A.14.2.5 + A.6.1.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + AC-6(1) + CM-6(a) + PR.IP-2 + SRG-OS-000480-GPOS-00228 + SRG-OS-000480-GPOS-00227 + RHEL-08-020353 + 5.6.5 + SV-230385r792902_rule + The umask value influences the permissions assigned to files when they are created. +A misconfigured umask value could result in files with excessive permissions that can be read or +written to by unauthorized users. + CCE-81036-6 + - name: XCCDF Value var_accounts_user_umask # promote to variable + set_fact: + var_accounts_user_umask: !!str + tags: + - always + +- name: Replace user umask in /etc/bashrc + replace: + path: /etc/bashrc + regexp: umask.* + replace: umask {{ var_accounts_user_umask }} + register: umask_replace + tags: + - CCE-81036-6 + - DISA-STIG-RHEL-08-020353 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - accounts_umask_etc_bashrc + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Append user umask in /etc/bashrc + lineinfile: + create: true + path: /etc/bashrc + line: umask {{ var_accounts_user_umask }} + when: umask_replace is not changed + tags: + - CCE-81036-6 + - DISA-STIG-RHEL-08-020353 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - accounts_umask_etc_bashrc + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + +var_accounts_user_umask='' + + + + + + +grep -q "^\s*umask" /etc/bashrc && \ + sed -i -E -e "s/^(\s*umask).*/\1 $var_accounts_user_umask/g" /etc/bashrc +if ! [ $? -eq 0 ]; then + echo "umask $var_accounts_user_umask" >> /etc/bashrc +fi + + + + + + + + + + + Ensure the Default C Shell Umask is Set Correctly + To ensure the default umask for users of the C shell is set properly, +add or correct the umask setting in /etc/csh.cshrc to read as follows: +umask + 18 + APO13.01 + BAI03.01 + BAI03.02 + BAI03.03 + CCI-000366 + 4.3.4.3.3 + A.14.1.1 + A.14.2.1 + A.14.2.5 + A.6.1.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + AC-6(1) + CM-6(a) + PR.IP-2 + SRG-OS-000480-GPOS-00228 + SRG-OS-000480-GPOS-00227 + RHEL-08-020353 + SV-230385r792902_rule + The umask value influences the permissions assigned to files when they are created. +A misconfigured umask value could result in files with excessive permissions that can be read or +written to by unauthorized users. + CCE-81037-4 + - name: XCCDF Value var_accounts_user_umask # promote to variable + set_fact: + var_accounts_user_umask: !!str + tags: + - always + +- name: Replace user umask in /etc/csh.cshrc + replace: + path: /etc/csh.cshrc + regexp: umask.* + replace: umask {{ var_accounts_user_umask }} + register: umask_replace + tags: + - CCE-81037-4 + - DISA-STIG-RHEL-08-020353 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - accounts_umask_etc_csh_cshrc + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Append user umask in /etc/csh.cshrc + lineinfile: + create: true + path: /etc/csh.cshrc + line: umask {{ var_accounts_user_umask }} + when: umask_replace is not changed + tags: + - CCE-81037-4 + - DISA-STIG-RHEL-08-020353 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - accounts_umask_etc_csh_cshrc + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + +var_accounts_user_umask='' + + +grep -q "^\s*umask" /etc/csh.cshrc && \ + sed -i -E -e "s/^(\s*umask).*/\1 $var_accounts_user_umask/g" /etc/csh.cshrc +if ! [ $? -eq 0 ]; then + echo "umask $var_accounts_user_umask" >> /etc/csh.cshrc +fi + + + + + + + + + + + Ensure the Default Umask is Set Correctly in login.defs + To ensure the default umask controlled by /etc/login.defs is set properly, +add or correct the UMASK setting in /etc/login.defs to read as follows: +UMASK + BP28(R35) + 11 + 18 + 3 + 9 + APO13.01 + BAI03.01 + BAI03.02 + BAI03.03 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + CCI-000366 + 4.3.4.3.2 + 4.3.4.3.3 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.1.1 + A.14.2.1 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.14.2.5 + A.6.1.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + AC-6(1) + CM-6(a) + PR.IP-1 + PR.IP-2 + SRG-OS-000480-GPOS-00228 + RHEL-08-020351 + 5.6.5 + SV-230383r627750_rule + The umask value influences the permissions assigned to files when they are created. +A misconfigured umask value could result in files with excessive permissions that can be read and +written to by unauthorized users. + + CCE-82888-9 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-82888-9 + - DISA-STIG-RHEL-08-020351 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - accounts_umask_etc_login_defs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy +- name: XCCDF Value var_accounts_user_umask # promote to variable + set_fact: + var_accounts_user_umask: !!str + tags: + - always + +- name: Ensure the Default UMASK is Set Correctly + replace: + path: /etc/login.defs + regexp: ^UMASK + replace: UMASK {{ var_accounts_user_umask }} + register: umask_replace + when: '"shadow-utils" in ansible_facts.packages' + tags: + - CCE-82888-9 + - DISA-STIG-RHEL-08-020351 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - accounts_umask_etc_login_defs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Ensure the Default UMASK is Appended Correctly + lineinfile: + create: true + path: /etc/login.defs + line: UMASK {{ var_accounts_user_umask }} + when: + - '"shadow-utils" in ansible_facts.packages' + - umask_replace is not changed + tags: + - CCE-82888-9 + - DISA-STIG-RHEL-08-020351 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - accounts_umask_etc_login_defs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q shadow-utils; then + +var_accounts_user_umask='' + + +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/login.defs"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^UMASK") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s %s" "$stripped_key" "$var_accounts_user_umask" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^UMASK\\>" "/etc/login.defs"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^UMASK\\>.*/$escaped_formatted_output/gi" "/etc/login.defs" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-82888-9" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/login.defs" >> "/etc/login.defs" + printf '%s\n' "$formatted_output" >> "/etc/login.defs" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Ensure the Default Umask is Set Correctly in /etc/profile + To ensure the default umask controlled by /etc/profile is set properly, +add or correct the umask setting in /etc/profile to read as follows: +umask + BP28(R35) + 18 + APO13.01 + BAI03.01 + BAI03.02 + BAI03.03 + CCI-000366 + 4.3.4.3.3 + A.14.1.1 + A.14.2.1 + A.14.2.5 + A.6.1.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + AC-6(1) + CM-6(a) + PR.IP-2 + SRG-OS-000480-GPOS-00228 + SRG-OS-000480-GPOS-00227 + RHEL-08-020353 + 5.6.5 + SV-230385r792902_rule + The umask value influences the permissions assigned to files when they are created. +A misconfigured umask value could result in files with excessive permissions that can be read or +written to by unauthorized users. + CCE-81035-8 + - name: XCCDF Value var_accounts_user_umask # promote to variable + set_fact: + var_accounts_user_umask: !!str + tags: + - always + +- name: Check if umask is already set + ansible.builtin.lineinfile: + path: /etc/profile + regexp: (^[\s]*umask)\s+(\d+) + state: absent + check_mode: true + changed_when: false + register: result_umask_is_set + tags: + - CCE-81035-8 + - DISA-STIG-RHEL-08-020353 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - accounts_umask_etc_profile + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Replace user umask in /etc/profile + ansible.builtin.replace: + path: /etc/profile + regexp: ^(\s*)umask\s+\d+ + replace: \1umask {{ var_accounts_user_umask }} + tags: + - CCE-81035-8 + - DISA-STIG-RHEL-08-020353 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - accounts_umask_etc_profile + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Append user umask in /etc/profile + ansible.builtin.lineinfile: + create: true + path: /etc/profile + line: umask {{ var_accounts_user_umask }} + when: result_umask_is_set.found == 0 + tags: + - CCE-81035-8 + - DISA-STIG-RHEL-08-020353 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - accounts_umask_etc_profile + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + +var_accounts_user_umask='' + + +grep -qE '^[^#]*umask' /etc/profile && \ + sed -i "s/umask.*/umask $var_accounts_user_umask/g" /etc/profile +if ! [ $? -eq 0 ]; then + echo "umask $var_accounts_user_umask" >> /etc/profile +fi + + + + + + + + + + + Ensure the Default Umask is Set Correctly For Interactive Users + Remove the UMASK environment variable from all interactive users initialization files. + CCI-000366 + CCI-001814 + SRG-OS-000480-GPOS-00227 + SRG-OS-000480-GPOS-00228 + RHEL-08-020352 + SV-230384r627750_rule + The umask controls the default access mode assigned to newly created files. A +umask of 077 limits new files to mode 700 or less permissive. Although umask can +be represented as a four-digit number, the first digit representing special +access modes is typically ignored or required to be 0. This requirement +applies to the globally configured system defaults and the local interactive +user defaults for each account on the system. + CCE-84044-7 + - name: Ensure interactive local users are the owners of their respective initialization + files + ansible.builtin.shell: + cmd: |- + for dir in $(awk -F':' '{ if ($3 >= 1000 && $3 != 65534) print $6}' /etc/passwd); do + for file in $(find $dir -maxdepth 1 -type f -name ".*"); do + sed -i 's/^\([\s]*umask\s*\)/#\1/g' $file + done + done + tags: + - CCE-84044-7 + - DISA-STIG-RHEL-08-020352 + - accounts_umask_interactive_users + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + +while IFS= read -r dir; do + while IFS= read -r -d '' file; do + sed -i 's/^\([\s]*umask\s*\)/#\1/g' "$file" + done < <(find $dir -maxdepth 1 -type f -name ".*" -print0) +done < <(awk -F':' '{ if ($3 >= 1000 && $3 != 65534) print $6}' /etc/passwd) + + + + + + + + + + + + + System Accounting with auditd + The audit service provides substantial capabilities +for recording system activities. By default, the service audits about +SELinux AVC denials and certain types of security-relevant events +such as system logins, account modifications, and authentication +events performed by programs such as sudo. +Under its default configuration, auditd has modest disk space +requirements, and should not noticeably impact system performance. + +NOTE: The Linux Audit daemon auditd can be configured to use +the augenrules program to read audit rules files (*.rules) +located in /etc/audit/rules.d location and compile them to create +the resulting form of the /etc/audit/audit.rules configuration file +during the daemon startup (default configuration). Alternatively, the auditd +daemon can use the auditctl utility to read audit rules from the +/etc/audit/audit.rules configuration file during daemon startup, +and load them into the kernel. The expected behavior is configured via the +appropriate ExecStartPost directive setting in the +/usr/lib/systemd/system/auditd.service configuration file. +To instruct the auditd daemon to use the augenrules program +to read audit rules (default configuration), use the following setting: + ExecStartPost=-/sbin/augenrules --load +in the /usr/lib/systemd/system/auditd.service configuration file. +In order to instruct the auditd daemon to use the auditctl +utility to read audit rules, use the following setting: + ExecStartPost=-/sbin/auditctl -R /etc/audit/audit.rules +in the /usr/lib/systemd/system/auditd.service configuration file. +Refer to [Service] section of the /usr/lib/systemd/system/auditd.service +configuration file for further details. + +Government networks often have substantial auditing +requirements and auditd can be configured to meet these +requirements. +Examining some example audit records demonstrates how the Linux audit system +satisfies common requirements. +The following example from Red Hat Enterprise Linux 7 Documentation available at +https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/7/html-single/selinux_users_and_administrators_guide/index#sect-Security-Enhanced_Linux-Fixing_Problems-Raw_Audit_Messages +shows the substantial amount of information captured in a +two typical "raw" audit messages, followed by a breakdown of the most important +fields. In this example the message is SELinux-related and reports an AVC +denial (and the associated system call) that occurred when the Apache HTTP +Server attempted to access the /var/www/html/file1 file (labeled with +the samba_share_t type): +type=AVC msg=audit(1226874073.147:96): avc: denied { getattr } for pid=2465 comm="httpd" +path="/var/www/html/file1" dev=dm-0 ino=284133 scontext=unconfined_u:system_r:httpd_t:s0 +tcontext=unconfined_u:object_r:samba_share_t:s0 tclass=file + +type=SYSCALL msg=audit(1226874073.147:96): arch=40000003 syscall=196 success=no exit=-13 +a0=b98df198 a1=bfec85dc a2=54dff4 a3=2008171 items=0 ppid=2463 pid=2465 auid=502 uid=48 +gid=48 euid=48 suid=48 fsuid=48 egid=48 sgid=48 fsgid=48 tty=(none) ses=6 comm="httpd" +exe="/usr/sbin/httpd" subj=unconfined_u:system_r:httpd_t:s0 key=(null) + +msg=audit(1226874073.147:96)The number in parentheses is the unformatted time stamp (Epoch time) +for the event, which can be converted to standard time by using the +date command. +{ getattr }The item in braces indicates the permission that was denied. getattr +indicates the source process was trying to read the target file's status information. +This occurs before reading files. This action is denied due to the file being +accessed having the wrong label. Commonly seen permissions include getattr, +read, and write.comm="httpd"The executable that launched the process. The full path of the executable is +found in the exe= section of the system call (SYSCALL) message, +which in this case, is exe="/usr/sbin/httpd". +path="/var/www/html/file1"The path to the object (target) the process attempted to access. +scontext="unconfined_u:system_r:httpd_t:s0"The SELinux context of the process that attempted the denied action. In +this case, it is the SELinux context of the Apache HTTP Server, which is running +in the httpd_t domain. +tcontext="unconfined_u:object_r:samba_share_t:s0"The SELinux context of the object (target) the process attempted to access. +In this case, it is the SELinux context of file1. Note: the samba_share_t +type is not accessible to processes running in the httpd_t domain. From the system call (SYSCALL) message, two items are of interest: +success=no: indicates whether the denial (AVC) was enforced or not. +success=no indicates the system call was not successful (SELinux denied +access). success=yes indicates the system call was successful - this can +be seen for permissive domains or unconfined domains, such as initrc_t +and kernel_t. +exe="/usr/sbin/httpd": the full path to the executable that launched +the process, which in this case, is exe="/usr/sbin/httpd". + + + + + Install audispd-plugins Package + The audispd-plugins package can be installed with the following command: + +$ sudo yum install audispd-plugins + FMT_SMF_EXT.1 + SRG-OS-000342-GPOS-00133 + audispd-plugins provides plugins for the real-time interface to the +audit subsystem, audispd. These plugins can do things like relay events +to remote machines or analyze events for suspicious behavior. + CCE-82953-1 + +package --add=audispd-plugins + + include install_audispd-plugins + +class install_audispd-plugins { + package { 'audispd-plugins': + ensure => 'installed', + } +} + + - name: Ensure audispd-plugins is installed + package: + name: audispd-plugins + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82953-1 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_audispd-plugins_installed + + +[[packages]] +name = "audispd-plugins" +version = "*" + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if ! rpm -q --quiet "audispd-plugins" ; then + yum install -y "audispd-plugins" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure the default plugins for the audit dispatcher are Installed + The audit-audispd-plugins package should be installed. + CCI-001851 + SRG-OS-000342-GPOS-00133 + Information stored in one location is vulnerable to accidental or incidental deletion or alteration. Off-loading is a common process in information systems with limited audit storage capacity. + +package --add=audit-audispd-plugins + + include install_audit-audispd-plugins + +class install_audit-audispd-plugins { + package { 'audit-audispd-plugins': + ensure => 'installed', + } +} + + - name: Ensure audit-audispd-plugins is installed + package: + name: audit-audispd-plugins + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_audit-audispd-plugins_installed + + +[[packages]] +name = "audit-audispd-plugins" +version = "*" + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if ! rpm -q --quiet "audit-audispd-plugins" ; then + yum install -y "audit-audispd-plugins" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure the audit Subsystem is Installed + The audit package should be installed. + BP28(R50) + CCI-000130 + CCI-000131 + CCI-000132 + CCI-000133 + CCI-000134 + CCI-000135 + CCI-000154 + CCI-000158 + CCI-000172 + CCI-001464 + CCI-001487 + CCI-001814 + CCI-001875 + CCI-001876 + CCI-001877 + CCI-001878 + CCI-001879 + CCI-001880 + CCI-001881 + CCI-001882 + CCI-001889 + CCI-001914 + CCI-002884 + CCI-000169 + CIP-004-6 R3.3 + CIP-007-3 R6.5 + AC-7(a) + AU-7(1) + AU-7(2) + AU-14 + AU-12(2) + AU-2(a) + CM-6(a) + FAU_GEN.1 + SRG-OS-000062-GPOS-00031 + SRG-OS-000037-GPOS-00015 + SRG-OS-000038-GPOS-00016 + SRG-OS-000039-GPOS-00017 + SRG-OS-000040-GPOS-00018 + SRG-OS-000041-GPOS-00019 + SRG-OS-000042-GPOS-00021 + SRG-OS-000051-GPOS-00024 + SRG-OS-000054-GPOS-00025 + SRG-OS-000122-GPOS-00063 + SRG-OS-000254-GPOS-00095 + SRG-OS-000255-GPOS-00096 + SRG-OS-000337-GPOS-00129 + SRG-OS-000348-GPOS-00136 + SRG-OS-000349-GPOS-00137 + SRG-OS-000350-GPOS-00138 + SRG-OS-000351-GPOS-00139 + SRG-OS-000352-GPOS-00140 + SRG-OS-000353-GPOS-00141 + SRG-OS-000354-GPOS-00142 + SRG-OS-000358-GPOS-00145 + SRG-OS-000365-GPOS-00152 + SRG-OS-000392-GPOS-00172 + SRG-OS-000475-GPOS-00220 + RHEL-08-030180 + 4.1.1.1 + SV-230411r744000_rule + The auditd service is an access monitoring and accounting daemon, watching system calls to audit any access, in comparison with potential local access control policy such as SELinux policy. + CCE-81043-2 + +package --add=audit + + include install_audit + +class install_audit { + package { 'audit': + ensure => 'installed', + } +} + + - name: Ensure audit is installed + package: + name: audit + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-81043-2 + - DISA-STIG-RHEL-08-030180 + - NIST-800-53-AC-7(a) + - NIST-800-53-AU-12(2) + - NIST-800-53-AU-14 + - NIST-800-53-AU-2(a) + - NIST-800-53-AU-7(1) + - NIST-800-53-AU-7(2) + - NIST-800-53-CM-6(a) + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_audit_installed + + +[[packages]] +name = "audit" +version = "*" + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if ! rpm -q --quiet "audit" ; then + yum install -y "audit" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Enable auditd Service + The auditd service is an essential userspace component of +the Linux Auditing System, as it is responsible for writing audit records to +disk. + +The auditd service can be enabled with the following command: +$ sudo systemctl enable auditd.service + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 5.4.1.1 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.3.1 + 3.3.2 + 3.3.6 + CCI-000126 + CCI-000130 + CCI-000131 + CCI-000132 + CCI-000133 + CCI-000134 + CCI-000135 + CCI-000154 + CCI-000158 + CCI-000172 + CCI-000366 + CCI-001464 + CCI-001487 + CCI-001814 + CCI-001875 + CCI-001876 + CCI-001877 + CCI-002884 + CCI-001878 + CCI-001879 + CCI-001880 + CCI-001881 + CCI-001882 + CCI-001889 + CCI-001914 + CCI-000169 + 164.308(a)(1)(ii)(D) + 164.308(a)(5)(ii)(C) + 164.310(a)(2)(iv) + 164.310(d)(2)(iii) + 164.312(b) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + CIP-004-6 R3.3 + CIP-007-3 R6.5 + AC-2(g) + AU-3 + AU-10 + AU-2(d) + AU-12(c) + AU-14(1) + AC-6(9) + CM-6(a) + SI-4(23) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1 + Req-10.1 + SRG-OS-000062-GPOS-00031 + SRG-OS-000037-GPOS-00015 + SRG-OS-000038-GPOS-00016 + SRG-OS-000039-GPOS-00017 + SRG-OS-000040-GPOS-00018 + SRG-OS-000041-GPOS-00019 + SRG-OS-000042-GPOS-00021 + SRG-OS-000051-GPOS-00024 + SRG-OS-000054-GPOS-00025 + SRG-OS-000122-GPOS-00063 + SRG-OS-000254-GPOS-00095 + SRG-OS-000255-GPOS-00096 + SRG-OS-000337-GPOS-00129 + SRG-OS-000348-GPOS-00136 + SRG-OS-000349-GPOS-00137 + SRG-OS-000350-GPOS-00138 + SRG-OS-000351-GPOS-00139 + SRG-OS-000352-GPOS-00140 + SRG-OS-000353-GPOS-00141 + SRG-OS-000354-GPOS-00142 + SRG-OS-000358-GPOS-00145 + SRG-OS-000365-GPOS-00152 + SRG-OS-000392-GPOS-00172 + SRG-OS-000475-GPOS-00220 + SRG-OS-000037-VMM-000150 + SRG-OS-000063-VMM-000310 + SRG-OS-000038-VMM-000160 + SRG-OS-000039-VMM-000170 + SRG-OS-000040-VMM-000180 + SRG-OS-000041-VMM-000190 + RHEL-08-030181 + 4.1.1.2 + SV-244542r818838_rule + Without establishing what type of events occurred, it would be difficult +to establish, correlate, and investigate the events leading up to an outage or attack. +Ensuring the auditd service is active ensures audit records +generated by the kernel are appropriately recorded. + +Additionally, a properly configured audit subsystem ensures that actions of +individual system users can be uniquely traced to those users so they +can be held accountable for their actions. + + + CCE-80872-5 + include enable_auditd + +class enable_auditd { + service {'auditd': + enable => true, + ensure => 'running', + } +} + + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80872-5 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030181 + - NIST-800-171-3.3.1 + - NIST-800-171-3.3.2 + - NIST-800-171-3.3.6 + - NIST-800-53-AC-2(g) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-10 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-14(1) + - NIST-800-53-AU-2(d) + - NIST-800-53-AU-3 + - NIST-800-53-CM-6(a) + - NIST-800-53-SI-4(23) + - PCI-DSS-Req-10.1 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_auditd_enabled + +- name: Enable service auditd + block: + + - name: Gather the package facts + package_facts: + manager: auto + + - name: Enable service auditd + service: + name: auditd + enabled: 'yes' + state: started + masked: 'no' + when: + - '"audit" in ansible_facts.packages' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"audit" in ansible_facts.packages' + tags: + - CCE-80872-5 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030181 + - NIST-800-171-3.3.1 + - NIST-800-171-3.3.2 + - NIST-800-171-3.3.6 + - NIST-800-53-AC-2(g) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-10 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-14(1) + - NIST-800-53-AU-2(d) + - NIST-800-53-AU-3 + - NIST-800-53-CM-6(a) + - NIST-800-53-SI-4(23) + - PCI-DSS-Req-10.1 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_auditd_enabled + + +[customizations.services] +enabled = ["auditd"] + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + systemd: + units: + - name: auditd.service + enabled: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && { rpm --quiet -q audit; }; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" unmask 'auditd.service' +"$SYSTEMCTL_EXEC" start 'auditd.service' +"$SYSTEMCTL_EXEC" enable 'auditd.service' + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Enable Auditing for Processes Which Start Prior to the Audit Daemon + To ensure all processes can be audited, even those which start +prior to the audit daemon, add the argument audit=1 to the default +GRUB 2 command line for the Linux operating system. +To ensure that audit=1 is added as a kernel command line +argument to newly installed kernels, add audit=1 to the +default Grub2 command line for Linux operating systems. Modify the line within +/etc/default/grub as shown below: +GRUB_CMDLINE_LINUX="... audit=1 ..." +Run the following command to update command line for already installed kernels:# grubby --update-kernel=ALL --args="audit=1" + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 3 + 4 + 5 + 6 + 7 + 8 + 5.4.1.1 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.3.1 + CCI-001464 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(5)(ii)(C) + 164.310(a)(2)(iv) + 164.310(d)(2)(iii) + 164.312(b) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AC-17(1) + AU-14(1) + AU-10 + CM-6(a) + IR-5(1) + DE.AE-3 + DE.AE-5 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1 + Req-10.3 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + SRG-OS-000473-GPOS-00218 + SRG-OS-000254-GPOS-00095 + SRG-OS-000254-VMM-000880 + RHEL-08-030601 + 4.1.1.3 + SV-230468r792904_rule + Each process on the system carries an "auditable" flag which indicates whether +its activities can be audited. Although auditd takes care of enabling +this for all processes which launch after it does, adding the kernel argument +ensures it is set for every process during boot. + + CCE-80825-3 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80825-3 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030601 + - NIST-800-171-3.3.1 + - NIST-800-53-AC-17(1) + - NIST-800-53-AU-10 + - NIST-800-53-AU-14(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-IR-5(1) + - PCI-DSS-Req-10.3 + - grub2_audit_argument + - low_disruption + - medium_complexity + - medium_severity + - reboot_required + - restrict_strategy + +- name: Update grub defaults and the bootloader menu + command: /sbin/grubby --update-kernel=ALL --args="audit=1" + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"grub2-common" in ansible_facts.packages' + tags: + - CCE-80825-3 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030601 + - NIST-800-171-3.3.1 + - NIST-800-53-AC-17(1) + - NIST-800-53-AU-10 + - NIST-800-53-AU-14(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-IR-5(1) + - PCI-DSS-Req-10.3 + - grub2_audit_argument + - low_disruption + - medium_complexity + - medium_severity + - reboot_required + - restrict_strategy + + [customizations.kernel] +append = "audit=1" + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && { rpm --quiet -q grub2-common; }; then + +grubby --update-kernel=ALL --args=audit=1 --env=/boot/grub2/grubenv + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Extend Audit Backlog Limit for the Audit Daemon + To improve the kernel capacity to queue all log events, even those which occurred +prior to the audit daemon, add the argument audit_backlog_limit=8192 to the default +GRUB 2 command line for the Linux operating system. +To ensure that audit_backlog_limit=8192 is added as a kernel command line +argument to newly installed kernels, add audit_backlog_limit=8192 to the +default Grub2 command line for Linux operating systems. Modify the line within +/etc/default/grub as shown below: +GRUB_CMDLINE_LINUX="... audit_backlog_limit=8192 ..." +Run the following command to update command line for already installed kernels:# grubby --update-kernel=ALL --args="audit_backlog_limit=8192" + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-001849 + CCI-002884 + CM-6(a) + FAU_STG.1 + FAU_STG.3 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000254-GPOS-00095 + SRG-OS-000341-GPOS-00132 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + RHEL-08-030602 + 4.1.1.4 + SV-230469r792906_rule + audit_backlog_limit sets the queue length for audit events awaiting transfer +to the audit daemon. Until the audit daemon is up and running, all log messages +are stored in this queue. If the queue is overrun during boot process, the action +defined by audit failure flag is taken. + + CCE-80943-4 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80943-4 + - DISA-STIG-RHEL-08-030602 + - NIST-800-53-CM-6(a) + - grub2_audit_backlog_limit_argument + - low_disruption + - low_severity + - medium_complexity + - reboot_required + - restrict_strategy + +- name: Update grub defaults and the bootloader menu + command: /sbin/grubby --update-kernel=ALL --args="audit_backlog_limit=8192" + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"grub2-common" in ansible_facts.packages' + tags: + - CCE-80943-4 + - DISA-STIG-RHEL-08-030602 + - NIST-800-53-CM-6(a) + - grub2_audit_backlog_limit_argument + - low_disruption + - low_severity + - medium_complexity + - reboot_required + - restrict_strategy + + [customizations.kernel] +append = "audit_backlog_limit=8192" + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && { rpm --quiet -q grub2-common; }; then + +grubby --update-kernel=ALL --args=audit_backlog_limit=8192 --env=/boot/grub2/grubenv + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure auditd Rules for Comprehensive Auditing + The auditd program can perform comprehensive +monitoring of system activity. This section describes recommended +configuration settings for comprehensive auditing, but a full +description of the auditing system's capabilities is beyond the +scope of this guide. The mailing list linux-audit@redhat.com exists +to facilitate community discussion of the auditing system. + +The audit subsystem supports extensive collection of events, including: + +Tracing of arbitrary system calls (identified by name or number) +on entry or exit.Filtering by PID, UID, call success, system call argument (with +some limitations), etc.Monitoring of specific files for modifications to the file's +contents or metadata. + +Auditing rules at startup are controlled by the file /etc/audit/audit.rules. +Add rules to it to meet the auditing requirements for your organization. +Each line in /etc/audit/audit.rules represents a series of arguments +that can be passed to auditctl and can be individually tested +during runtime. See documentation in /usr/share/doc/audit-VERSION and +in the related man pages for more details. + +If copying any example audit rulesets from /usr/share/doc/audit-VERSION, +be sure to comment out the +lines containing arch= which are not appropriate for your system's +architecture. Then review and understand the following rules, +ensuring rules are activated as needed for the appropriate +architecture. + +After reviewing all the rules, reading the following sections, and +editing as needed, the new rules can be activated as follows: +$ sudo service auditd restart + + + Record Events that Modify User/Group Information via open syscall - /etc/group + The audit system should collect write events to /etc/group file for all users and root. +If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: +-a always,exit -F arch=b32 -S open -F a1&03 -F path=/etc/group -F auid>=1000 -F auid!=unset -F key=modify +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S open -F a1&03 -F path=/etc/group -F auid>=1000 -F auid!=unset -F key=modify +If the system is 64 bit then also add the following line: +-a always,exit -F arch=b64 -S open -F a1&03 -F path=/etc/group -F auid>=1000 -F auid!=unset -F key=modify + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping system calls related +to the same event is more efficient. See the following example: +-a always,exit -F arch=b32 -S open -F a1&03 -F path=/etc/group -F auid>=1000 -F auid!=unset -F key=modify + CIP-004-6 R2.2.2 + CIP-004-6 R2.2.3 + CIP-007-3 R.1.3 + CIP-007-3 R5 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.3 + CIP-007-3 R5.2.1 + CIP-007-3 R5.2.3 + AC-2(4) + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + FAU_GEN.1.1.c + Creation of groups through direct edition of /etc/group could be an indicator of malicious activity on a system. +Auditing these events could serve as evidence of potential system compromise. + CCE-80927-7 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80927-7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_group_open + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit open tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80927-7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_group_open + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for open for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - open + syscall_grouping: [] + + - name: Check existence of open in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a1&03 -F path=/etc/group -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/modify.rules + set_fact: audit_file="/etc/audit/rules.d/modify.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F a1&03 -F path=/etc/group -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F a1&03 -F path=/etc/group + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - open + syscall_grouping: [] + + - name: Check existence of open in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a1&03 -F path=/etc/group -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F a1&03 -F path=/etc/group -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F a1&03 -F path=/etc/group + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80927-7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_group_open + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for open for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - open + syscall_grouping: [] + + - name: Check existence of open in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a1&03 -F path=/etc/group -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/modify.rules + set_fact: audit_file="/etc/audit/rules.d/modify.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F a1&03 -F path=/etc/group -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F a1&03 -F path=/etc/group + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - open + syscall_grouping: [] + + - name: Check existence of open in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a1&03 -F path=/etc/group -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F a1&03 -F path=/etc/group -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F a1&03 -F path=/etc/group + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-80927-7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_group_open + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F a1&03 -F path=/etc/group" + AUID_FILTERS="-F auid>=1000 -F auid!=unset" + SYSCALL="open" + KEY="user-modify" + SYSCALL_GROUPING="" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Events that Modify User/Group Information via open_by_handle_at syscall - /etc/group + The audit system should collect write events to /etc/group file for all group and root. +If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: +-a always,exit -F arch=b32 -S open_by_handle_at -F a2&03 -F path=/etc/group -F auid>=1000 -F auid!=unset -F key=modify +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S open_by_handle_at -F a2&03 -F path=/etc/group -F auid>=1000 -F auid!=unset -F key=modify +If the system is 64 bit then also add the following line: +-a always,exit -F arch=b64 -S open_by_handle_at -F a2&03 -F path=/etc/group -F auid>=1000 -F auid!=unset -F key=modify + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping system calls related +to the same event is more efficient. See the following example: +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&03 -F path=/etc/group -F auid>=1000 -F auid!=unset -F key=modify + CIP-004-6 R2.2.2 + CIP-004-6 R2.2.3 + CIP-007-3 R.1.3 + CIP-007-3 R5 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.3 + CIP-007-3 R5.2.1 + CIP-007-3 R5.2.3 + AC-2(4) + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + FAU_GEN.1.1.c + Creation of groups through direct edition of /etc/group could be an indicator of malicious activity on a system. +Auditing these events could serve as evidence of potential system compromise. + CCE-80929-3 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80929-3 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_group_open_by_handle_at + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit open_by_handle_at tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80929-3 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_group_open_by_handle_at + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for open_by_handle_at for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - open_by_handle_at + syscall_grouping: [] + + - name: Check existence of open_by_handle_at in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a2&03 -F path=/etc/group -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/modify.rules + set_fact: audit_file="/etc/audit/rules.d/modify.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F a2&03 -F path=/etc/group -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F a2&03 -F path=/etc/group + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - open_by_handle_at + syscall_grouping: [] + + - name: Check existence of open_by_handle_at in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a2&03 -F path=/etc/group -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F a2&03 -F path=/etc/group -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F a2&03 -F path=/etc/group + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80929-3 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_group_open_by_handle_at + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for open_by_handle_at for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - open_by_handle_at + syscall_grouping: [] + + - name: Check existence of open_by_handle_at in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a2&03 -F path=/etc/group -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/modify.rules + set_fact: audit_file="/etc/audit/rules.d/modify.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F a2&03 -F path=/etc/group -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F a2&03 -F path=/etc/group + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - open_by_handle_at + syscall_grouping: [] + + - name: Check existence of open_by_handle_at in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a2&03 -F path=/etc/group -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F a2&03 -F path=/etc/group -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F a2&03 -F path=/etc/group + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-80929-3 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_group_open_by_handle_at + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F a2&03 -F path=/etc/group" + AUID_FILTERS="-F auid>=1000 -F auid!=unset" + SYSCALL="open_by_handle_at" + KEY="user-modify" + SYSCALL_GROUPING="" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Events that Modify User/Group Information via openat syscall - /etc/group + The audit system should collect write events to /etc/group file for all users and root. +If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: +-a always,exit -F arch=b32 -S openat -F a2&03 -F path=/etc/group -F auid>=1000 -F auid!=unset -F key=modify +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S openat -F a2&03 -F path=/etc/group -F auid>=1000 -F auid!=unset -F key=modify +If the system is 64 bit then also add the following line: +-a always,exit -F arch=b64 -S openat -F a2&03 -F path=/etc/group -F auid>=1000 -F auid!=unset -F key=modify + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping system calls related +to the same event is more efficient. See the following example: +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&03 -F path=/etc/group -F auid>=1000 -F auid!=unset -F key=modify + CIP-004-6 R2.2.2 + CIP-004-6 R2.2.3 + CIP-007-3 R.1.3 + CIP-007-3 R5 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.3 + CIP-007-3 R5.2.1 + CIP-007-3 R5.2.3 + AC-2(4) + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + FAU_GEN.1.1.c + Creation of groups through direct edition of /etc/group could be an indicator of malicious activity on a system. +Auditing these events could serve as evidence of potential system compromise. + CCE-80928-5 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80928-5 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_group_openat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit openat tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80928-5 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_group_openat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for openat for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - openat + syscall_grouping: [] + + - name: Check existence of openat in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a2&03 -F path=/etc/group -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/modify.rules + set_fact: audit_file="/etc/audit/rules.d/modify.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F a2&03 -F path=/etc/group -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F a2&03 -F path=/etc/group + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - openat + syscall_grouping: [] + + - name: Check existence of openat in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a2&03 -F path=/etc/group -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F a2&03 -F path=/etc/group -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F a2&03 -F path=/etc/group + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80928-5 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_group_openat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for openat for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - openat + syscall_grouping: [] + + - name: Check existence of openat in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a2&03 -F path=/etc/group -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/modify.rules + set_fact: audit_file="/etc/audit/rules.d/modify.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F a2&03 -F path=/etc/group -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F a2&03 -F path=/etc/group + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - openat + syscall_grouping: [] + + - name: Check existence of openat in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a2&03 -F path=/etc/group -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F a2&03 -F path=/etc/group -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F a2&03 -F path=/etc/group + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-80928-5 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_group_openat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F a2&03 -F path=/etc/group" + AUID_FILTERS="-F auid>=1000 -F auid!=unset" + SYSCALL="openat" + KEY="user-modify" + SYSCALL_GROUPING="" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Events that Modify User/Group Information via open syscall - /etc/gshadow + The audit system should collect write events to /etc/gshadow file for all users and root. +If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: +-a always,exit -F arch=b32 -S open -F a1&03 -F path=/etc/gshadow -F auid>=1000 -F auid!=unset -F key=user-modify +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S open -F a1&03 -F path=/etc/gshadow -F auid>=1000 -F auid!=unset -F key=user-modify +If the system is 64 bit then also add the following line: +-a always,exit -F arch=b64 -S open -F a1&03 -F path=/etc/gshadow -F auid>=1000 -F auid!=unset -F key=user-modify + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping system calls related +to the same event is more efficient. See the following example: +-a always,exit -F arch=b32 -S open -F a1&03 -F path=/etc/gshadow -F auid>=1000 -F auid!=unset -F key=user-modify + CIP-004-6 R2.2.2 + CIP-004-6 R2.2.3 + CIP-007-3 R.1.3 + CIP-007-3 R5 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.3 + CIP-007-3 R5.2.1 + CIP-007-3 R5.2.3 + AC-2(4) + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + FAU_GEN.1.1.c + Creation of users through direct edition of /etc/gshadow could be an indicator of malicious activity on a system. +Auditing these events could serve as evidence of potential system compromise. + CCE-80959-0 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80959-0 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_gshadow_open + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit open tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80959-0 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_gshadow_open + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for open for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - open + syscall_grouping: [] + + - name: Check existence of open in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a1&03 -F path=/etc/gshadow -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/modify.rules + set_fact: audit_file="/etc/audit/rules.d/modify.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F a1&03 -F path=/etc/gshadow -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F a1&03 -F path=/etc/gshadow + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - open + syscall_grouping: [] + + - name: Check existence of open in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a1&03 -F path=/etc/gshadow -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F a1&03 -F path=/etc/gshadow -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F a1&03 -F path=/etc/gshadow + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80959-0 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_gshadow_open + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for open for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - open + syscall_grouping: [] + + - name: Check existence of open in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a1&03 -F path=/etc/gshadow -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/modify.rules + set_fact: audit_file="/etc/audit/rules.d/modify.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F a1&03 -F path=/etc/gshadow -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F a1&03 -F path=/etc/gshadow + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - open + syscall_grouping: [] + + - name: Check existence of open in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a1&03 -F path=/etc/gshadow -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F a1&03 -F path=/etc/gshadow -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F a1&03 -F path=/etc/gshadow + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-80959-0 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_gshadow_open + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F a1&03 -F path=/etc/gshadow" + AUID_FILTERS="-F auid>=1000 -F auid!=unset" + SYSCALL="open" + KEY="user-modify" + SYSCALL_GROUPING="" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Events that Modify User/Group Information via open_by_handle_at syscall - /etc/gshadow + The audit system should collect write events to /etc/gshadow file for all users and root. +If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: +-a always,exit -F arch=b32 -S open_by_handle_at -F a2&03 -F path=/etc/gshadow -F auid>=1000 -F auid!=unset -F key=user-modify +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S open_by_handle_at -F a2&03 -F path=/etc/gshadow -F auid>=1000 -F auid!=unset -F key=user-modify +If the system is 64 bit then also add the following line: +-a always,exit -F arch=b64 -S open_by_handle_at -F a2&03 -F path=/etc/gshadow -F auid>=1000 -F auid!=unset -F key=user-modify + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping system calls related +to the same event is more efficient. See the following example: +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&03 -F path=/etc/gshadow -F auid>=1000 -F auid!=unset -F key=user-modify + CIP-004-6 R2.2.2 + CIP-004-6 R2.2.3 + CIP-007-3 R.1.3 + CIP-007-3 R5 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.3 + CIP-007-3 R5.2.1 + CIP-007-3 R5.2.3 + AC-2(4) + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + FAU_GEN.1.1.c + Creation of users through direct edition of /etc/gshadow could be an indicator of malicious activity on a system. +Auditing these events could serve as evidence of potential system compromise. + CCE-80960-8 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80960-8 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_gshadow_open_by_handle_at + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit open_by_handle_at tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80960-8 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_gshadow_open_by_handle_at + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for open_by_handle_at for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - open_by_handle_at + syscall_grouping: [] + + - name: Check existence of open_by_handle_at in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a2&03 -F path=/etc/gshadow -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/modify.rules + set_fact: audit_file="/etc/audit/rules.d/modify.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F a2&03 -F path=/etc/gshadow -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F a2&03 -F path=/etc/gshadow + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - open_by_handle_at + syscall_grouping: [] + + - name: Check existence of open_by_handle_at in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a2&03 -F path=/etc/gshadow -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F a2&03 -F path=/etc/gshadow -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F a2&03 -F path=/etc/gshadow + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80960-8 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_gshadow_open_by_handle_at + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for open_by_handle_at for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - open_by_handle_at + syscall_grouping: [] + + - name: Check existence of open_by_handle_at in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a2&03 -F path=/etc/gshadow -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/modify.rules + set_fact: audit_file="/etc/audit/rules.d/modify.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F a2&03 -F path=/etc/gshadow -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F a2&03 -F path=/etc/gshadow + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - open_by_handle_at + syscall_grouping: [] + + - name: Check existence of open_by_handle_at in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a2&03 -F path=/etc/gshadow -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F a2&03 -F path=/etc/gshadow -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F a2&03 -F path=/etc/gshadow + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-80960-8 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_gshadow_open_by_handle_at + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F a2&03 -F path=/etc/gshadow" + AUID_FILTERS="-F auid>=1000 -F auid!=unset" + SYSCALL="open_by_handle_at" + KEY="user-modify" + SYSCALL_GROUPING="" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Events that Modify User/Group Information via openat syscall - /etc/gshadow + The audit system should collect write events to /etc/gshadow file for all users and root. +If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: +-a always,exit -F arch=b32 -S openat -F a2&03 -F path=/etc/gshadow -F auid>=1000 -F auid!=unset -F key=user-modify +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S openat -F a2&03 -F path=/etc/gshadow -F auid>=1000 -F auid!=unset -F key=user-modify +If the system is 64 bit then also add the following line: +-a always,exit -F arch=b64 -S openat -F a2&03 -F path=/etc/gshadow -F auid>=1000 -F auid!=unset -F key=user-modify + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping system calls related +to the same event is more efficient. See the following example: +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&03 -F path=/etc/gshadow -F auid>=1000 -F auid!=unset -F key=user-modify + CIP-004-6 R2.2.2 + CIP-004-6 R2.2.3 + CIP-007-3 R.1.3 + CIP-007-3 R5 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.3 + CIP-007-3 R5.2.1 + CIP-007-3 R5.2.3 + AC-2(4) + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + FAU_GEN.1.1.c + Creation of users through direct edition of /etc/gshadow could be an indicator of malicious activity on a system. +Auditing these events could serve as evidence of potential system compromise. + CCE-80961-6 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80961-6 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_gshadow_openat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit openat tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80961-6 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_gshadow_openat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for openat for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - openat + syscall_grouping: [] + + - name: Check existence of openat in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a2&03 -F path=/etc/gshadow -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/modify.rules + set_fact: audit_file="/etc/audit/rules.d/modify.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F a2&03 -F path=/etc/gshadow -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F a2&03 -F path=/etc/gshadow + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - openat + syscall_grouping: [] + + - name: Check existence of openat in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a2&03 -F path=/etc/gshadow -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F a2&03 -F path=/etc/gshadow -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F a2&03 -F path=/etc/gshadow + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80961-6 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_gshadow_openat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for openat for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - openat + syscall_grouping: [] + + - name: Check existence of openat in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a2&03 -F path=/etc/gshadow -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/modify.rules + set_fact: audit_file="/etc/audit/rules.d/modify.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F a2&03 -F path=/etc/gshadow -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F a2&03 -F path=/etc/gshadow + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - openat + syscall_grouping: [] + + - name: Check existence of openat in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a2&03 -F path=/etc/gshadow -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F a2&03 -F path=/etc/gshadow -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F a2&03 -F path=/etc/gshadow + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-80961-6 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_gshadow_openat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F a2&03 -F path=/etc/gshadow" + AUID_FILTERS="-F auid>=1000 -F auid!=unset" + SYSCALL="openat" + KEY="user-modify" + SYSCALL_GROUPING="" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Events that Modify User/Group Information via open syscall - /etc/passwd + The audit system should collect write events to /etc/passwd file for all users and root. +If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: +-a always,exit -F arch=b32 -S open -F a1&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=modify +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S open -F a1&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=modify +If the system is 64 bit then also add the following line: +-a always,exit -F arch=b64 -S open -F a1&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=modify + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping system calls related +to the same event is more efficient. See the following example: +-a always,exit -F arch=b32 -S open -F a1&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=modify + CIP-004-6 R2.2.2 + CIP-004-6 R2.2.3 + CIP-007-3 R.1.3 + CIP-007-3 R5 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.3 + CIP-007-3 R5.2.1 + CIP-007-3 R5.2.3 + AC-2(4) + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + FAU_GEN.1.1.c + Creation of users through direct edition of /etc/passwd could be an indicator of malicious activity on a system. +Auditing these events could serve as evidence of potential system compromise. + CCE-80930-1 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80930-1 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_passwd_open + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit open tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80930-1 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_passwd_open + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for open for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - open + syscall_grouping: [] + + - name: Check existence of open in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a1&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/modify.rules + set_fact: audit_file="/etc/audit/rules.d/modify.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F a1&03 -F path=/etc/passwd -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F a1&03 -F path=/etc/passwd + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - open + syscall_grouping: [] + + - name: Check existence of open in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a1&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F a1&03 -F path=/etc/passwd -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F a1&03 -F path=/etc/passwd + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80930-1 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_passwd_open + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for open for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - open + syscall_grouping: [] + + - name: Check existence of open in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a1&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/modify.rules + set_fact: audit_file="/etc/audit/rules.d/modify.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F a1&03 -F path=/etc/passwd -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F a1&03 -F path=/etc/passwd + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - open + syscall_grouping: [] + + - name: Check existence of open in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a1&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F a1&03 -F path=/etc/passwd -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F a1&03 -F path=/etc/passwd + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-80930-1 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_passwd_open + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F a1&03 -F path=/etc/passwd" + AUID_FILTERS="-F auid>=1000 -F auid!=unset" + SYSCALL="open" + KEY="user-modify" + SYSCALL_GROUPING="" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Events that Modify User/Group Information via open_by_handle_at syscall - /etc/passwd + The audit system should collect write events to /etc/passwd file for all users and root. +If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: +-a always,exit -F arch=b32 -S open_by_handle_at -F a2&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=modify +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S open_by_handle_at -F a2&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=modify +If the system is 64 bit then also add the following line: +-a always,exit -F arch=b64 -S open_by_handle_at -F a2&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=modify + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping system calls related +to the same event is more efficient. See the following example: +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=modify + CIP-004-6 R2.2.2 + CIP-004-6 R2.2.3 + CIP-007-3 R.1.3 + CIP-007-3 R5 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.3 + CIP-007-3 R5.2.1 + CIP-007-3 R5.2.3 + AC-2(4) + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + FAU_GEN.1.1.c + Creation of users through direct edition of /etc/passwd could be an indicator of malicious activity on a system. +Auditing these events could serve as evidence of potential system compromise. + CCE-80932-7 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80932-7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_passwd_open_by_handle_at + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit open_by_handle_at tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80932-7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_passwd_open_by_handle_at + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for open_by_handle_at for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - open_by_handle_at + syscall_grouping: [] + + - name: Check existence of open_by_handle_at in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a2&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/modify.rules + set_fact: audit_file="/etc/audit/rules.d/modify.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F a2&03 -F path=/etc/passwd -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F a2&03 -F path=/etc/passwd + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - open_by_handle_at + syscall_grouping: [] + + - name: Check existence of open_by_handle_at in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a2&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F a2&03 -F path=/etc/passwd -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F a2&03 -F path=/etc/passwd + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80932-7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_passwd_open_by_handle_at + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for open_by_handle_at for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - open_by_handle_at + syscall_grouping: [] + + - name: Check existence of open_by_handle_at in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a2&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/modify.rules + set_fact: audit_file="/etc/audit/rules.d/modify.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F a2&03 -F path=/etc/passwd -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F a2&03 -F path=/etc/passwd + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - open_by_handle_at + syscall_grouping: [] + + - name: Check existence of open_by_handle_at in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a2&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F a2&03 -F path=/etc/passwd -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F a2&03 -F path=/etc/passwd + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-80932-7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_passwd_open_by_handle_at + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F a2&03 -F path=/etc/passwd" + AUID_FILTERS="-F auid>=1000 -F auid!=unset" + SYSCALL="open_by_handle_at" + KEY="user-modify" + SYSCALL_GROUPING="" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Events that Modify User/Group Information via openat syscall - /etc/passwd + The audit system should collect write events to /etc/passwd file for all users and root. +If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: +-a always,exit -F arch=b32 -S openat -F a2&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=modify +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S openat -F a2&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=modify +If the system is 64 bit then also add the following line: +-a always,exit -F arch=b64 -S openat -F a2&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=modify + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping system calls related +to the same event is more efficient. See the following example: +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=modify + CIP-004-6 R2.2.2 + CIP-004-6 R2.2.3 + CIP-007-3 R.1.3 + CIP-007-3 R5 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.3 + CIP-007-3 R5.2.1 + CIP-007-3 R5.2.3 + AC-2(4) + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + FAU_GEN.1.1.c + Creation of users through direct edition of /etc/passwd could be an indicator of malicious activity on a system. +Auditing these events could serve as evidence of potential system compromise. + CCE-80931-9 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80931-9 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_passwd_openat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit openat tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80931-9 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_passwd_openat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for openat for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - openat + syscall_grouping: [] + + - name: Check existence of openat in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a2&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/modify.rules + set_fact: audit_file="/etc/audit/rules.d/modify.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F a2&03 -F path=/etc/passwd -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F a2&03 -F path=/etc/passwd + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - openat + syscall_grouping: [] + + - name: Check existence of openat in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a2&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F a2&03 -F path=/etc/passwd -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F a2&03 -F path=/etc/passwd + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80931-9 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_passwd_openat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for openat for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - openat + syscall_grouping: [] + + - name: Check existence of openat in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a2&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/modify.rules + set_fact: audit_file="/etc/audit/rules.d/modify.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F a2&03 -F path=/etc/passwd -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F a2&03 -F path=/etc/passwd + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - openat + syscall_grouping: [] + + - name: Check existence of openat in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a2&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F a2&03 -F path=/etc/passwd -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F a2&03 -F path=/etc/passwd + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-80931-9 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_passwd_openat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F a2&03 -F path=/etc/passwd" + AUID_FILTERS="-F auid>=1000 -F auid!=unset" + SYSCALL="openat" + KEY="user-modify" + SYSCALL_GROUPING="" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Events that Modify User/Group Information via open syscall - /etc/shadow + The audit system should collect write events to /etc/shadow file for all users and root. +If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: +-a always,exit -F arch=b32 -S open -F a1&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S open -F a1&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify +If the system is 64 bit then also add the following line: +-a always,exit -F arch=b64 -S open -F a1&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping system calls related +to the same event is more efficient. See the following example: +-a always,exit -F arch=b32 -S open,openat,open_by_handle_at -F a1&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify + CIP-004-6 R2.2.2 + CIP-004-6 R2.2.3 + CIP-007-3 R.1.3 + CIP-007-3 R5 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.3 + CIP-007-3 R5.2.1 + CIP-007-3 R5.2.3 + AC-2(4) + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + FAU_GEN.1.1.c + Creation of users through direct edition of /etc/shadow could be an indicator of malicious activity on a system. +Auditing these events could serve as evidence of potential system compromise. + CCE-80956-6 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80956-6 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_shadow_open + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit open tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80956-6 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_shadow_open + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for open for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - open + syscall_grouping: [] + + - name: Check existence of open in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a1&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/modify.rules + set_fact: audit_file="/etc/audit/rules.d/modify.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F a1&03 -F path=/etc/shadow -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F a1&03 -F path=/etc/shadow + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - open + syscall_grouping: [] + + - name: Check existence of open in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a1&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F a1&03 -F path=/etc/shadow -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F a1&03 -F path=/etc/shadow + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80956-6 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_shadow_open + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for open for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - open + syscall_grouping: [] + + - name: Check existence of open in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a1&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/modify.rules + set_fact: audit_file="/etc/audit/rules.d/modify.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F a1&03 -F path=/etc/shadow -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F a1&03 -F path=/etc/shadow + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - open + syscall_grouping: [] + + - name: Check existence of open in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a1&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F a1&03 -F path=/etc/shadow -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F a1&03 -F path=/etc/shadow + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-80956-6 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_shadow_open + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F a1&03 -F path=/etc/shadow" + AUID_FILTERS="-F auid>=1000 -F auid!=unset" + SYSCALL="open" + KEY="user-modify" + SYSCALL_GROUPING="" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Events that Modify User/Group Information via open_by_handle_at syscall - /etc/shadow + The audit system should collect write events to /etc/shadow file for all users and root. +If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: +-a always,exit -F arch=b32 -S open_by_handle_at -F a2&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S open_by_handle_at -F a2&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify +If the system is 64 bit then also add the following line: +-a always,exit -F arch=b64 -S open_by_handle_at -F a2&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping system calls related +to the same event is more efficient. See the following example: +-a always,exit -F arch=b32 -S open,openat,open_by_handle_at -F a2&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify + CIP-004-6 R2.2.2 + CIP-004-6 R2.2.3 + CIP-007-3 R.1.3 + CIP-007-3 R5 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.3 + CIP-007-3 R5.2.1 + CIP-007-3 R5.2.3 + AC-2(4) + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + FAU_GEN.1.1.c + Creation of users through direct edition of /etc/shadow could be an indicator of malicious activity on a system. +Auditing these events could serve as evidence of potential system compromise. + CCE-80957-4 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80957-4 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_shadow_open_by_handle_at + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit open_by_handle_at tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80957-4 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_shadow_open_by_handle_at + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for open_by_handle_at for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - open_by_handle_at + syscall_grouping: [] + + - name: Check existence of open_by_handle_at in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a2&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/modify.rules + set_fact: audit_file="/etc/audit/rules.d/modify.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F a2&03 -F path=/etc/shadow -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F a2&03 -F path=/etc/shadow + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - open_by_handle_at + syscall_grouping: [] + + - name: Check existence of open_by_handle_at in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a2&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F a2&03 -F path=/etc/shadow -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F a2&03 -F path=/etc/shadow + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80957-4 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_shadow_open_by_handle_at + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for open_by_handle_at for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - open_by_handle_at + syscall_grouping: [] + + - name: Check existence of open_by_handle_at in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a2&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/modify.rules + set_fact: audit_file="/etc/audit/rules.d/modify.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F a2&03 -F path=/etc/shadow -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F a2&03 -F path=/etc/shadow + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - open_by_handle_at + syscall_grouping: [] + + - name: Check existence of open_by_handle_at in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a2&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F a2&03 -F path=/etc/shadow -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F a2&03 -F path=/etc/shadow + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-80957-4 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_shadow_open_by_handle_at + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F a2&03 -F path=/etc/shadow" + AUID_FILTERS="-F auid>=1000 -F auid!=unset" + SYSCALL="open_by_handle_at" + KEY="user-modify" + SYSCALL_GROUPING="" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Events that Modify User/Group Information via openat syscall - /etc/shadow + The audit system should collect write events to /etc/shadow file for all users and root. +If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: +-a always,exit -F arch=b32 -S openat -F a2&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S openat -F a2&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify +If the system is 64 bit then also add the following line: +-a always,exit -F arch=b64 -S openat -F a2&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping system calls related +to the same event is more efficient. See the following example: +-a always,exit -F arch=b32 -S open,openat,open_by_handle_at -F a2&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify + CIP-004-6 R2.2.2 + CIP-004-6 R2.2.3 + CIP-007-3 R.1.3 + CIP-007-3 R5 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.3 + CIP-007-3 R5.2.1 + CIP-007-3 R5.2.3 + AC-2(4) + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + FAU_GEN.1.1.c + Creation of users through direct edition of /etc/shadow could be an indicator of malicious activity on a system. +Auditing these events could serve as evidence of potential system compromise. + CCE-80958-2 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80958-2 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_shadow_openat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit openat tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80958-2 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_shadow_openat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for openat for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - openat + syscall_grouping: [] + + - name: Check existence of openat in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a2&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/modify.rules + set_fact: audit_file="/etc/audit/rules.d/modify.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F a2&03 -F path=/etc/shadow -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F a2&03 -F path=/etc/shadow + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - openat + syscall_grouping: [] + + - name: Check existence of openat in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a2&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F a2&03 -F path=/etc/shadow -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F a2&03 -F path=/etc/shadow + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80958-2 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_shadow_openat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for openat for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - openat + syscall_grouping: [] + + - name: Check existence of openat in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a2&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/modify.rules + set_fact: audit_file="/etc/audit/rules.d/modify.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F a2&03 -F path=/etc/shadow -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F a2&03 -F path=/etc/shadow + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - openat + syscall_grouping: [] + + - name: Check existence of openat in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a2&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F a2&03 -F path=/etc/shadow -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F a2&03 -F path=/etc/shadow + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-80958-2 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_shadow_openat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F a2&03 -F path=/etc/shadow" + AUID_FILTERS="-F auid>=1000 -F auid!=unset" + SYSCALL="openat" + KEY="user-modify" + SYSCALL_GROUPING="" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Make the auditd Configuration Immutable + If the auditd daemon is configured to use the +augenrules program to read audit rules during daemon startup (the +default), add the following line to a file with suffix .rules in the +directory /etc/audit/rules.d in order to make the auditd configuration +immutable: +-e 2 +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following line to +/etc/audit/audit.rules file in order to make the auditd configuration +immutable: +-e 2 +With this setting, a reboot will be required to change any audit rules. + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 18 + 19 + 3 + 4 + 5 + 6 + 7 + 8 + 5.4.1.1 + APO01.06 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + BAI03.05 + BAI08.02 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS05.04 + DSS05.07 + DSS06.02 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.3.1 + 3.4.3 + CCI-000162 + CCI-000163 + CCI-000164 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.310(a)(2)(iv) + 164.312(d) + 164.310(d)(2)(iii) + 164.312(b) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.7.3 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.1 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 5.2 + SR 6.1 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + AC-6(9) + CM-6(a) + DE.AE-3 + DE.AE-5 + ID.SC-4 + PR.AC-4 + PR.DS-5 + PR.PT-1 + RS.AN-1 + RS.AN-4 + Req-10.5.2 + SRG-OS-000057-GPOS-00027 + SRG-OS-000058-GPOS-00028 + SRG-OS-000059-GPOS-00029 + RHEL-08-030121 + 4.1.3.20 + SV-230402r627750_rule + Making the audit configuration immutable prevents accidental as +well as malicious modification of the audit rules, although it may be +problematic if legitimate changes are needed during system +operation. + CCE-80708-1 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80708-1 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030121 + - NIST-800-171-3.3.1 + - NIST-800-171-3.4.3 + - NIST-800-53-AC-6(9) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.2 + - audit_rules_immutable + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Collect all files from /etc/audit/rules.d with .rules extension + find: + paths: /etc/audit/rules.d/ + patterns: '*.rules' + register: find_rules_d + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80708-1 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030121 + - NIST-800-171-3.3.1 + - NIST-800-171-3.4.3 + - NIST-800-53-AC-6(9) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.2 + - audit_rules_immutable + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Remove the -e option from all Audit config files + lineinfile: + path: '{{ item }}' + regexp: ^\s*(?:-e)\s+.*$ + state: absent + loop: '{{ find_rules_d.files | map(attribute=''path'') | list + [''/etc/audit/audit.rules''] + }}' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80708-1 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030121 + - NIST-800-171-3.3.1 + - NIST-800-171-3.4.3 + - NIST-800-53-AC-6(9) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.2 + - audit_rules_immutable + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Add Audit -e option into /etc/audit/rules.d/immutable.rules and /etc/audit/audit.rules + lineinfile: + path: '{{ item }}' + create: true + line: -e 2 + mode: o-rwx + loop: + - /etc/audit/audit.rules + - /etc/audit/rules.d/immutable.rules + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80708-1 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030121 + - NIST-800-171-3.3.1 + - NIST-800-171-3.4.3 + - NIST-800-53-AC-6(9) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.2 + - audit_rules_immutable + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,-e%202%0A + mode: 0600 + path: /etc/audit/rules.d/90-immutable.rules + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# Traverse all of: +# +# /etc/audit/audit.rules, (for auditctl case) +# /etc/audit/rules.d/*.rules (for augenrules case) +# +# files to check if '-e .*' setting is present in that '*.rules' file already. +# If found, delete such occurrence since auditctl(8) manual page instructs the +# '-e 2' rule should be placed as the last rule in the configuration +find /etc/audit /etc/audit/rules.d -maxdepth 1 -type f -name '*.rules' -exec sed -i '/-e[[:space:]]\+.*/d' {} ';' + +# Append '-e 2' requirement at the end of both: +# * /etc/audit/audit.rules file (for auditctl case) +# * /etc/audit/rules.d/immutable.rules (for augenrules case) + +for AUDIT_FILE in "/etc/audit/audit.rules" "/etc/audit/rules.d/immutable.rules" +do + echo '' >> $AUDIT_FILE + echo '# Set the audit.rules configuration immutable per security requirements' >> $AUDIT_FILE + echo '# Reboot is required to change audit rules once this setting is applied' >> $AUDIT_FILE + echo '-e 2' >> $AUDIT_FILE + chmod o-rwx $AUDIT_FILE +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Events that Modify the System's Mandatory Access Controls + If the auditd daemon is configured to use the +augenrules program to read audit rules during daemon startup (the +default), add the following line to a file with suffix .rules in the +directory /etc/audit/rules.d: +-w /etc/selinux/ -p wa -k MAC-policy +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following line to +/etc/audit/audit.rules file: +-w /etc/selinux/ -p wa -k MAC-policy + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 5.4.1.1 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.8 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.5.5 + 4.1.3.14 + The system's rhisam access policy (SELinux) should not be +arbitrarily changed by anything other than administrator action. All changes to +MAC policy should be audited. + CCE-80721-4 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80721-4 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.8 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_mac_modification + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Check if watch rule for /etc/selinux/ already exists in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: ^\s*-w\s+/etc/selinux/\s+-p\s+wa(\s|$)+ + patterns: '*.rules' + register: find_existing_watch_rules_d + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80721-4 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.8 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_mac_modification + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Search /etc/audit/rules.d for other rules with specified key MAC-policy + find: + paths: /etc/audit/rules.d + contains: ^.*(?:-F key=|-k\s+)MAC-policy$ + patterns: '*.rules' + register: find_watch_key + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched + == 0 + tags: + - CCE-80721-4 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.8 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_mac_modification + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Use /etc/audit/rules.d/MAC-policy.rules as the recipient for the rule + set_fact: + all_files: + - /etc/audit/rules.d/MAC-policy.rules + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_watch_key.matched is defined and find_watch_key.matched == 0 and find_existing_watch_rules_d.matched + is defined and find_existing_watch_rules_d.matched == 0 + tags: + - CCE-80721-4 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.8 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_mac_modification + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Use matched file as the recipient for the rule + set_fact: + all_files: + - '{{ find_watch_key.files | map(attribute=''path'') | list | first }}' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_watch_key.matched is defined and find_watch_key.matched > 0 and find_existing_watch_rules_d.matched + is defined and find_existing_watch_rules_d.matched == 0 + tags: + - CCE-80721-4 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.8 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_mac_modification + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Add watch rule for /etc/selinux/ in /etc/audit/rules.d/ + lineinfile: + path: '{{ all_files[0] }}' + line: -w /etc/selinux/ -p wa -k MAC-policy + create: true + mode: '0640' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched + == 0 + tags: + - CCE-80721-4 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.8 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_mac_modification + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Check if watch rule for /etc/selinux/ already exists in /etc/audit/audit.rules + find: + paths: /etc/audit/ + contains: ^\s*-w\s+/etc/selinux/\s+-p\s+wa(\s|$)+ + patterns: audit.rules + register: find_existing_watch_audit_rules + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80721-4 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.8 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_mac_modification + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Add watch rule for /etc/selinux/ in /etc/audit/audit.rules + lineinfile: + line: -w /etc/selinux/ -p wa -k MAC-policy + state: present + dest: /etc/audit/audit.rules + create: true + mode: '0640' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_audit_rules.matched is defined and find_existing_watch_audit_rules.matched + == 0 + tags: + - CCE-80721-4 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.8 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_mac_modification + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + --- + +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,{{ -w%20/etc/selinux/%20-p%20wa%20-k%20MAC-policy%0A }} + mode: 0600 + path: /etc/audit/rules.d/75-etcselinux-wa-MAC-policy.rules + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + + +# If the audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# into the list of files to be inspected +files_to_inspect+=('/etc/audit/audit.rules') + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/etc/selinux/" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/selinux/ $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/etc/selinux/$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /etc/selinux/ -p wa -k MAC-policy" >> "$audit_rules_file" + fi +done +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + +# If the audit is 'augenrules', then check if rule is already defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection. +# If rule isn't defined, add '/etc/audit/rules.d/MAC-policy.rules' to list of files for inspection. +readarray -t matches < <(grep -HP "[\s]*-w[\s]+/etc/selinux/" /etc/audit/rules.d/*.rules) + +# For each of the matched entries +for match in "${matches[@]}" +do + # Extract filepath from the match + rulesd_audit_file=$(echo $match | cut -f1 -d ':') + # Append that path into list of files for inspection + files_to_inspect+=("$rulesd_audit_file") +done +# Case when particular audit rule isn't defined yet +if [ "${#files_to_inspect[@]}" -eq "0" ] +then + # Append '/etc/audit/rules.d/MAC-policy.rules' into list of files for inspection + key_rule_file="/etc/audit/rules.d/MAC-policy.rules" + # If the MAC-policy.rules file doesn't exist yet, create it with correct permissions + if [ ! -e "$key_rule_file" ] + then + touch "$key_rule_file" + chmod 0640 "$key_rule_file" + fi + files_to_inspect+=("$key_rule_file") +fi + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/etc/selinux/" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/selinux/ $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/etc/selinux/$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /etc/selinux/ -p wa -k MAC-policy" >> "$audit_rules_file" + fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure auditd Collects Information on Exporting to Media (successful) + At a minimum, the audit system should collect media exportation +events for all users and root. If the auditd daemon is configured to +use the augenrules program to read audit rules during daemon startup +(the default), add the following line to a file with suffix .rules in +the directory /etc/audit/rules.d, setting ARCH to either b32 or b64 as +appropriate for your system: +-a always,exit -F arch=ARCH -S mount -F auid>=1000 -F auid!=unset -F key=export +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following line to +/etc/audit/audit.rules file, setting ARCH to either b32 or b64 as +appropriate for your system: +-a always,exit -F arch=ARCH -S mount -F auid>=1000 -F auid!=unset -F key=export + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 5.4.1.1 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + Req-10.2.7 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + RHEL-08-030302 + 4.1.3.10 + SV-230425r627750_rule + The unauthorized exportation of data to external media could result in an information leak +where classified information, Privacy Act information, and intellectual property could be lost. An audit +trail should be created each time a filesystem is mounted to help identify and guard against information +loss. + CCE-80722-2 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80722-2 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030302 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.7 + - audit_rules_media_export + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit mount tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80722-2 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030302 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.7 + - audit_rules_media_export + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for mount for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - mount + syscall_grouping: [] + + - name: Check existence of mount in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules + set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - mount + syscall_grouping: [] + + - name: Check existence of mount in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80722-2 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030302 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.7 + - audit_rules_media_export + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for mount for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - mount + syscall_grouping: [] + + - name: Check existence of mount in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules + set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - mount + syscall_grouping: [] + + - name: Check existence of mount in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-80722-2 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030302 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.7 + - audit_rules_media_export + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="" + AUID_FILTERS="-F auid>=1000 -F auid!=unset" + SYSCALL="mount" + KEY="perm_mod" + SYSCALL_GROUPING="" + + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Events that Modify the System's Network Environment + If the auditd daemon is configured to use the +augenrules program to read audit rules during daemon startup (the +default), add the following lines to a file with suffix .rules in the +directory /etc/audit/rules.d, setting ARCH to either b32 or b64 as +appropriate for your system: +-a always,exit -F arch=ARCH -S sethostname,setdomainname -F key=audit_rules_networkconfig_modification +-w /etc/issue -p wa -k audit_rules_networkconfig_modification +-w /etc/issue.net -p wa -k audit_rules_networkconfig_modification +-w /etc/hosts -p wa -k audit_rules_networkconfig_modification +-w /etc/sysconfig/network -p wa -k audit_rules_networkconfig_modification +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file, setting ARCH to either b32 or b64 as +appropriate for your system: +-a always,exit -F arch=ARCH -S sethostname,setdomainname -F key=audit_rules_networkconfig_modification +-w /etc/issue -p wa -k audit_rules_networkconfig_modification +-w /etc/issue.net -p wa -k audit_rules_networkconfig_modification +-w /etc/hosts -p wa -k audit_rules_networkconfig_modification +-w /etc/sysconfig/network -p wa -k audit_rules_networkconfig_modification + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 5.4.1.1 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + Req-10.5.5 + 4.1.3.5 + The network environment should not be modified by anything other +than administrator action. Any change to network parameters should be +audited. + CCE-80723-0 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80723-0 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_networkconfig_modification + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Set architecture for audit tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80723-0 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_networkconfig_modification + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Remediate audit rules for network configuration for x86 + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - sethostname + - setdomainname + syscall_grouping: + - sethostname + - setdomainname + + - name: Check existence of sethostname, setdomainname in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/audit_rules_networkconfig_modification.rules + set_fact: audit_file="/etc/audit/rules.d/audit_rules_networkconfig_modification.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F key=audit_rules_networkconfig_modification + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - sethostname + - setdomainname + syscall_grouping: + - sethostname + - setdomainname + + - name: Check existence of sethostname, setdomainname in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F key=audit_rules_networkconfig_modification + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80723-0 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_networkconfig_modification + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Remediate audit rules for network configuration for x86_64 + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - sethostname + - setdomainname + syscall_grouping: + - sethostname + - setdomainname + + - name: Check existence of sethostname, setdomainname in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/audit_rules_networkconfig_modification.rules + set_fact: audit_file="/etc/audit/rules.d/audit_rules_networkconfig_modification.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F key=audit_rules_networkconfig_modification + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - sethostname + - setdomainname + syscall_grouping: + - sethostname + - setdomainname + + - name: Check existence of sethostname, setdomainname in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F key=audit_rules_networkconfig_modification + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-80723-0 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_networkconfig_modification + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Check if watch rule for /etc/issue already exists in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: ^\s*-w\s+/etc/issue\s+-p\s+wa(\s|$)+ + patterns: '*.rules' + register: find_existing_watch_rules_d + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80723-0 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_networkconfig_modification + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Search /etc/audit/rules.d for other rules with specified key audit_rules_networkconfig_modification + find: + paths: /etc/audit/rules.d + contains: ^.*(?:-F key=|-k\s+)audit_rules_networkconfig_modification$ + patterns: '*.rules' + register: find_watch_key + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched + == 0 + tags: + - CCE-80723-0 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_networkconfig_modification + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Use /etc/audit/rules.d/audit_rules_networkconfig_modification.rules as the + recipient for the rule + set_fact: + all_files: + - /etc/audit/rules.d/audit_rules_networkconfig_modification.rules + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_watch_key.matched is defined and find_watch_key.matched == 0 and find_existing_watch_rules_d.matched + is defined and find_existing_watch_rules_d.matched == 0 + tags: + - CCE-80723-0 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_networkconfig_modification + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Use matched file as the recipient for the rule + set_fact: + all_files: + - '{{ find_watch_key.files | map(attribute=''path'') | list | first }}' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_watch_key.matched is defined and find_watch_key.matched > 0 and find_existing_watch_rules_d.matched + is defined and find_existing_watch_rules_d.matched == 0 + tags: + - CCE-80723-0 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_networkconfig_modification + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Add watch rule for /etc/issue in /etc/audit/rules.d/ + lineinfile: + path: '{{ all_files[0] }}' + line: -w /etc/issue -p wa -k audit_rules_networkconfig_modification + create: true + mode: '0640' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched + == 0 + tags: + - CCE-80723-0 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_networkconfig_modification + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Check if watch rule for /etc/issue already exists in /etc/audit/audit.rules + find: + paths: /etc/audit/ + contains: ^\s*-w\s+/etc/issue\s+-p\s+wa(\s|$)+ + patterns: audit.rules + register: find_existing_watch_audit_rules + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80723-0 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_networkconfig_modification + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Add watch rule for /etc/issue in /etc/audit/audit.rules + lineinfile: + line: -w /etc/issue -p wa -k audit_rules_networkconfig_modification + state: present + dest: /etc/audit/audit.rules + create: true + mode: '0640' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_audit_rules.matched is defined and find_existing_watch_audit_rules.matched + == 0 + tags: + - CCE-80723-0 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_networkconfig_modification + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Check if watch rule for /etc/issue.net already exists in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: ^\s*-w\s+/etc/issue.net\s+-p\s+wa(\s|$)+ + patterns: '*.rules' + register: find_existing_watch_rules_d + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80723-0 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_networkconfig_modification + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Search /etc/audit/rules.d for other rules with specified key audit_rules_networkconfig_modification + find: + paths: /etc/audit/rules.d + contains: ^.*(?:-F key=|-k\s+)audit_rules_networkconfig_modification$ + patterns: '*.rules' + register: find_watch_key + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched + == 0 + tags: + - CCE-80723-0 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_networkconfig_modification + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Use /etc/audit/rules.d/audit_rules_networkconfig_modification.rules as the + recipient for the rule + set_fact: + all_files: + - /etc/audit/rules.d/audit_rules_networkconfig_modification.rules + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_watch_key.matched is defined and find_watch_key.matched == 0 and find_existing_watch_rules_d.matched + is defined and find_existing_watch_rules_d.matched == 0 + tags: + - CCE-80723-0 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_networkconfig_modification + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Use matched file as the recipient for the rule + set_fact: + all_files: + - '{{ find_watch_key.files | map(attribute=''path'') | list | first }}' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_watch_key.matched is defined and find_watch_key.matched > 0 and find_existing_watch_rules_d.matched + is defined and find_existing_watch_rules_d.matched == 0 + tags: + - CCE-80723-0 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_networkconfig_modification + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Add watch rule for /etc/issue.net in /etc/audit/rules.d/ + lineinfile: + path: '{{ all_files[0] }}' + line: -w /etc/issue.net -p wa -k audit_rules_networkconfig_modification + create: true + mode: '0640' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched + == 0 + tags: + - CCE-80723-0 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_networkconfig_modification + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Check if watch rule for /etc/issue.net already exists in /etc/audit/audit.rules + find: + paths: /etc/audit/ + contains: ^\s*-w\s+/etc/issue.net\s+-p\s+wa(\s|$)+ + patterns: audit.rules + register: find_existing_watch_audit_rules + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80723-0 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_networkconfig_modification + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Add watch rule for /etc/issue.net in /etc/audit/audit.rules + lineinfile: + line: -w /etc/issue.net -p wa -k audit_rules_networkconfig_modification + state: present + dest: /etc/audit/audit.rules + create: true + mode: '0640' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_audit_rules.matched is defined and find_existing_watch_audit_rules.matched + == 0 + tags: + - CCE-80723-0 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_networkconfig_modification + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Check if watch rule for /etc/hosts already exists in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: ^\s*-w\s+/etc/hosts\s+-p\s+wa(\s|$)+ + patterns: '*.rules' + register: find_existing_watch_rules_d + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80723-0 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_networkconfig_modification + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Search /etc/audit/rules.d for other rules with specified key audit_rules_networkconfig_modification + find: + paths: /etc/audit/rules.d + contains: ^.*(?:-F key=|-k\s+)audit_rules_networkconfig_modification$ + patterns: '*.rules' + register: find_watch_key + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched + == 0 + tags: + - CCE-80723-0 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_networkconfig_modification + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Use /etc/audit/rules.d/audit_rules_networkconfig_modification.rules as the + recipient for the rule + set_fact: + all_files: + - /etc/audit/rules.d/audit_rules_networkconfig_modification.rules + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_watch_key.matched is defined and find_watch_key.matched == 0 and find_existing_watch_rules_d.matched + is defined and find_existing_watch_rules_d.matched == 0 + tags: + - CCE-80723-0 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_networkconfig_modification + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Use matched file as the recipient for the rule + set_fact: + all_files: + - '{{ find_watch_key.files | map(attribute=''path'') | list | first }}' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_watch_key.matched is defined and find_watch_key.matched > 0 and find_existing_watch_rules_d.matched + is defined and find_existing_watch_rules_d.matched == 0 + tags: + - CCE-80723-0 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_networkconfig_modification + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Add watch rule for /etc/hosts in /etc/audit/rules.d/ + lineinfile: + path: '{{ all_files[0] }}' + line: -w /etc/hosts -p wa -k audit_rules_networkconfig_modification + create: true + mode: '0640' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched + == 0 + tags: + - CCE-80723-0 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_networkconfig_modification + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Check if watch rule for /etc/hosts already exists in /etc/audit/audit.rules + find: + paths: /etc/audit/ + contains: ^\s*-w\s+/etc/hosts\s+-p\s+wa(\s|$)+ + patterns: audit.rules + register: find_existing_watch_audit_rules + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80723-0 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_networkconfig_modification + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Add watch rule for /etc/hosts in /etc/audit/audit.rules + lineinfile: + line: -w /etc/hosts -p wa -k audit_rules_networkconfig_modification + state: present + dest: /etc/audit/audit.rules + create: true + mode: '0640' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_audit_rules.matched is defined and find_existing_watch_audit_rules.matched + == 0 + tags: + - CCE-80723-0 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_networkconfig_modification + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Check if watch rule for /etc/sysconfig/network already exists in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: ^\s*-w\s+/etc/sysconfig/network\s+-p\s+wa(\s|$)+ + patterns: '*.rules' + register: find_existing_watch_rules_d + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80723-0 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_networkconfig_modification + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Search /etc/audit/rules.d for other rules with specified key audit_rules_networkconfig_modification + find: + paths: /etc/audit/rules.d + contains: ^.*(?:-F key=|-k\s+)audit_rules_networkconfig_modification$ + patterns: '*.rules' + register: find_watch_key + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched + == 0 + tags: + - CCE-80723-0 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_networkconfig_modification + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Use /etc/audit/rules.d/audit_rules_networkconfig_modification.rules as the + recipient for the rule + set_fact: + all_files: + - /etc/audit/rules.d/audit_rules_networkconfig_modification.rules + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_watch_key.matched is defined and find_watch_key.matched == 0 and find_existing_watch_rules_d.matched + is defined and find_existing_watch_rules_d.matched == 0 + tags: + - CCE-80723-0 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_networkconfig_modification + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Use matched file as the recipient for the rule + set_fact: + all_files: + - '{{ find_watch_key.files | map(attribute=''path'') | list | first }}' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_watch_key.matched is defined and find_watch_key.matched > 0 and find_existing_watch_rules_d.matched + is defined and find_existing_watch_rules_d.matched == 0 + tags: + - CCE-80723-0 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_networkconfig_modification + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Add watch rule for /etc/sysconfig/network in /etc/audit/rules.d/ + lineinfile: + path: '{{ all_files[0] }}' + line: -w /etc/sysconfig/network -p wa -k audit_rules_networkconfig_modification + create: true + mode: '0640' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched + == 0 + tags: + - CCE-80723-0 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_networkconfig_modification + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Check if watch rule for /etc/sysconfig/network already exists in /etc/audit/audit.rules + find: + paths: /etc/audit/ + contains: ^\s*-w\s+/etc/sysconfig/network\s+-p\s+wa(\s|$)+ + patterns: audit.rules + register: find_existing_watch_audit_rules + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80723-0 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_networkconfig_modification + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Add watch rule for /etc/sysconfig/network in /etc/audit/audit.rules + lineinfile: + line: -w /etc/sysconfig/network -p wa -k audit_rules_networkconfig_modification + state: present + dest: /etc/audit/audit.rules + create: true + mode: '0640' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_audit_rules.matched is defined and find_existing_watch_audit_rules.matched + == 0 + tags: + - CCE-80723-0 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_networkconfig_modification + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="" + AUID_FILTERS="" + SYSCALL="sethostname setdomainname" + KEY="audit_rules_networkconfig_modification" + SYSCALL_GROUPING="sethostname setdomainname" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +# Then perform the remediations for the watch rules +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + + +# If the audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# into the list of files to be inspected +files_to_inspect+=('/etc/audit/audit.rules') + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/etc/issue" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/issue $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/etc/issue$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /etc/issue -p wa -k audit_rules_networkconfig_modification" >> "$audit_rules_file" + fi +done +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + +# If the audit is 'augenrules', then check if rule is already defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection. +# If rule isn't defined, add '/etc/audit/rules.d/audit_rules_networkconfig_modification.rules' to list of files for inspection. +readarray -t matches < <(grep -HP "[\s]*-w[\s]+/etc/issue" /etc/audit/rules.d/*.rules) + +# For each of the matched entries +for match in "${matches[@]}" +do + # Extract filepath from the match + rulesd_audit_file=$(echo $match | cut -f1 -d ':') + # Append that path into list of files for inspection + files_to_inspect+=("$rulesd_audit_file") +done +# Case when particular audit rule isn't defined yet +if [ "${#files_to_inspect[@]}" -eq "0" ] +then + # Append '/etc/audit/rules.d/audit_rules_networkconfig_modification.rules' into list of files for inspection + key_rule_file="/etc/audit/rules.d/audit_rules_networkconfig_modification.rules" + # If the audit_rules_networkconfig_modification.rules file doesn't exist yet, create it with correct permissions + if [ ! -e "$key_rule_file" ] + then + touch "$key_rule_file" + chmod 0640 "$key_rule_file" + fi + files_to_inspect+=("$key_rule_file") +fi + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/etc/issue" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/issue $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/etc/issue$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /etc/issue -p wa -k audit_rules_networkconfig_modification" >> "$audit_rules_file" + fi +done +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + + +# If the audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# into the list of files to be inspected +files_to_inspect+=('/etc/audit/audit.rules') + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/etc/issue.net" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/issue.net $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/etc/issue.net$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /etc/issue.net -p wa -k audit_rules_networkconfig_modification" >> "$audit_rules_file" + fi +done +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + +# If the audit is 'augenrules', then check if rule is already defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection. +# If rule isn't defined, add '/etc/audit/rules.d/audit_rules_networkconfig_modification.rules' to list of files for inspection. +readarray -t matches < <(grep -HP "[\s]*-w[\s]+/etc/issue.net" /etc/audit/rules.d/*.rules) + +# For each of the matched entries +for match in "${matches[@]}" +do + # Extract filepath from the match + rulesd_audit_file=$(echo $match | cut -f1 -d ':') + # Append that path into list of files for inspection + files_to_inspect+=("$rulesd_audit_file") +done +# Case when particular audit rule isn't defined yet +if [ "${#files_to_inspect[@]}" -eq "0" ] +then + # Append '/etc/audit/rules.d/audit_rules_networkconfig_modification.rules' into list of files for inspection + key_rule_file="/etc/audit/rules.d/audit_rules_networkconfig_modification.rules" + # If the audit_rules_networkconfig_modification.rules file doesn't exist yet, create it with correct permissions + if [ ! -e "$key_rule_file" ] + then + touch "$key_rule_file" + chmod 0640 "$key_rule_file" + fi + files_to_inspect+=("$key_rule_file") +fi + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/etc/issue.net" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/issue.net $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/etc/issue.net$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /etc/issue.net -p wa -k audit_rules_networkconfig_modification" >> "$audit_rules_file" + fi +done +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + + +# If the audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# into the list of files to be inspected +files_to_inspect+=('/etc/audit/audit.rules') + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/etc/hosts" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/hosts $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/etc/hosts$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /etc/hosts -p wa -k audit_rules_networkconfig_modification" >> "$audit_rules_file" + fi +done +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + +# If the audit is 'augenrules', then check if rule is already defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection. +# If rule isn't defined, add '/etc/audit/rules.d/audit_rules_networkconfig_modification.rules' to list of files for inspection. +readarray -t matches < <(grep -HP "[\s]*-w[\s]+/etc/hosts" /etc/audit/rules.d/*.rules) + +# For each of the matched entries +for match in "${matches[@]}" +do + # Extract filepath from the match + rulesd_audit_file=$(echo $match | cut -f1 -d ':') + # Append that path into list of files for inspection + files_to_inspect+=("$rulesd_audit_file") +done +# Case when particular audit rule isn't defined yet +if [ "${#files_to_inspect[@]}" -eq "0" ] +then + # Append '/etc/audit/rules.d/audit_rules_networkconfig_modification.rules' into list of files for inspection + key_rule_file="/etc/audit/rules.d/audit_rules_networkconfig_modification.rules" + # If the audit_rules_networkconfig_modification.rules file doesn't exist yet, create it with correct permissions + if [ ! -e "$key_rule_file" ] + then + touch "$key_rule_file" + chmod 0640 "$key_rule_file" + fi + files_to_inspect+=("$key_rule_file") +fi + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/etc/hosts" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/hosts $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/etc/hosts$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /etc/hosts -p wa -k audit_rules_networkconfig_modification" >> "$audit_rules_file" + fi +done +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + + +# If the audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# into the list of files to be inspected +files_to_inspect+=('/etc/audit/audit.rules') + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/etc/sysconfig/network" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/sysconfig/network $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/etc/sysconfig/network$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /etc/sysconfig/network -p wa -k audit_rules_networkconfig_modification" >> "$audit_rules_file" + fi +done +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + +# If the audit is 'augenrules', then check if rule is already defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection. +# If rule isn't defined, add '/etc/audit/rules.d/audit_rules_networkconfig_modification.rules' to list of files for inspection. +readarray -t matches < <(grep -HP "[\s]*-w[\s]+/etc/sysconfig/network" /etc/audit/rules.d/*.rules) + +# For each of the matched entries +for match in "${matches[@]}" +do + # Extract filepath from the match + rulesd_audit_file=$(echo $match | cut -f1 -d ':') + # Append that path into list of files for inspection + files_to_inspect+=("$rulesd_audit_file") +done +# Case when particular audit rule isn't defined yet +if [ "${#files_to_inspect[@]}" -eq "0" ] +then + # Append '/etc/audit/rules.d/audit_rules_networkconfig_modification.rules' into list of files for inspection + key_rule_file="/etc/audit/rules.d/audit_rules_networkconfig_modification.rules" + # If the audit_rules_networkconfig_modification.rules file doesn't exist yet, create it with correct permissions + if [ ! -e "$key_rule_file" ] + then + touch "$key_rule_file" + chmod 0640 "$key_rule_file" + fi + files_to_inspect+=("$key_rule_file") +fi + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/etc/sysconfig/network" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/sysconfig/network $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/etc/sysconfig/network$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /etc/sysconfig/network -p wa -k audit_rules_networkconfig_modification" >> "$audit_rules_file" + fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Attempts to Alter Process and Session Initiation Information + The audit system already collects process information for all +users and root. If the auditd daemon is configured to use the +augenrules program to read audit rules during daemon startup (the +default), add the following lines to a file with suffix .rules in the +directory /etc/audit/rules.d in order to watch for attempted manual +edits of files involved in storing such process information: +-w /var/run/utmp -p wa -k session +-w /var/log/btmp -p wa -k session +-w /var/log/wtmp -p wa -k session +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file in order to watch for attempted manual +edits of files involved in storing such process information: +-w /var/run/utmp -p wa -k session +-w /var/log/btmp -p wa -k session +-w /var/log/wtmp -p wa -k session + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 5.4.1.1 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + 0582 + 0584 + 05885 + 0586 + 0846 + 0957 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.2.3 + 4.1.3.11 + Manual editing of these files may indicate nefarious activity, such +as an attacker attempting to remove evidence of an intrusion. + CCE-80742-0 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80742-0 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.3 + - audit_rules_session_events + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Check if watch rule for /var/run/utmp already exists in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: ^\s*-w\s+/var/run/utmp\s+-p\s+wa(\s|$)+ + patterns: '*.rules' + register: find_existing_watch_rules_d + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80742-0 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.3 + - audit_rules_session_events + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Search /etc/audit/rules.d for other rules with specified key session + find: + paths: /etc/audit/rules.d + contains: ^.*(?:-F key=|-k\s+)session$ + patterns: '*.rules' + register: find_watch_key + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched + == 0 + tags: + - CCE-80742-0 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.3 + - audit_rules_session_events + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Use /etc/audit/rules.d/session.rules as the recipient for the rule + set_fact: + all_files: + - /etc/audit/rules.d/session.rules + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_watch_key.matched is defined and find_watch_key.matched == 0 and find_existing_watch_rules_d.matched + is defined and find_existing_watch_rules_d.matched == 0 + tags: + - CCE-80742-0 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.3 + - audit_rules_session_events + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Use matched file as the recipient for the rule + set_fact: + all_files: + - '{{ find_watch_key.files | map(attribute=''path'') | list | first }}' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_watch_key.matched is defined and find_watch_key.matched > 0 and find_existing_watch_rules_d.matched + is defined and find_existing_watch_rules_d.matched == 0 + tags: + - CCE-80742-0 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.3 + - audit_rules_session_events + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Add watch rule for /var/run/utmp in /etc/audit/rules.d/ + lineinfile: + path: '{{ all_files[0] }}' + line: -w /var/run/utmp -p wa -k session + create: true + mode: '0640' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched + == 0 + tags: + - CCE-80742-0 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.3 + - audit_rules_session_events + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Check if watch rule for /var/run/utmp already exists in /etc/audit/audit.rules + find: + paths: /etc/audit/ + contains: ^\s*-w\s+/var/run/utmp\s+-p\s+wa(\s|$)+ + patterns: audit.rules + register: find_existing_watch_audit_rules + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80742-0 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.3 + - audit_rules_session_events + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Add watch rule for /var/run/utmp in /etc/audit/audit.rules + lineinfile: + line: -w /var/run/utmp -p wa -k session + state: present + dest: /etc/audit/audit.rules + create: true + mode: '0640' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_audit_rules.matched is defined and find_existing_watch_audit_rules.matched + == 0 + tags: + - CCE-80742-0 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.3 + - audit_rules_session_events + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Check if watch rule for /var/log/btmp already exists in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: ^\s*-w\s+/var/log/btmp\s+-p\s+wa(\s|$)+ + patterns: '*.rules' + register: find_existing_watch_rules_d + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80742-0 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.3 + - audit_rules_session_events + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Search /etc/audit/rules.d for other rules with specified key session + find: + paths: /etc/audit/rules.d + contains: ^.*(?:-F key=|-k\s+)session$ + patterns: '*.rules' + register: find_watch_key + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched + == 0 + tags: + - CCE-80742-0 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.3 + - audit_rules_session_events + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Use /etc/audit/rules.d/session.rules as the recipient for the rule + set_fact: + all_files: + - /etc/audit/rules.d/session.rules + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_watch_key.matched is defined and find_watch_key.matched == 0 and find_existing_watch_rules_d.matched + is defined and find_existing_watch_rules_d.matched == 0 + tags: + - CCE-80742-0 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.3 + - audit_rules_session_events + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Use matched file as the recipient for the rule + set_fact: + all_files: + - '{{ find_watch_key.files | map(attribute=''path'') | list | first }}' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_watch_key.matched is defined and find_watch_key.matched > 0 and find_existing_watch_rules_d.matched + is defined and find_existing_watch_rules_d.matched == 0 + tags: + - CCE-80742-0 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.3 + - audit_rules_session_events + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Add watch rule for /var/log/btmp in /etc/audit/rules.d/ + lineinfile: + path: '{{ all_files[0] }}' + line: -w /var/log/btmp -p wa -k session + create: true + mode: '0640' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched + == 0 + tags: + - CCE-80742-0 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.3 + - audit_rules_session_events + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Check if watch rule for /var/log/btmp already exists in /etc/audit/audit.rules + find: + paths: /etc/audit/ + contains: ^\s*-w\s+/var/log/btmp\s+-p\s+wa(\s|$)+ + patterns: audit.rules + register: find_existing_watch_audit_rules + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80742-0 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.3 + - audit_rules_session_events + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Add watch rule for /var/log/btmp in /etc/audit/audit.rules + lineinfile: + line: -w /var/log/btmp -p wa -k session + state: present + dest: /etc/audit/audit.rules + create: true + mode: '0640' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_audit_rules.matched is defined and find_existing_watch_audit_rules.matched + == 0 + tags: + - CCE-80742-0 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.3 + - audit_rules_session_events + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Check if watch rule for /var/log/wtmp already exists in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: ^\s*-w\s+/var/log/wtmp\s+-p\s+wa(\s|$)+ + patterns: '*.rules' + register: find_existing_watch_rules_d + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80742-0 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.3 + - audit_rules_session_events + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Search /etc/audit/rules.d for other rules with specified key session + find: + paths: /etc/audit/rules.d + contains: ^.*(?:-F key=|-k\s+)session$ + patterns: '*.rules' + register: find_watch_key + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched + == 0 + tags: + - CCE-80742-0 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.3 + - audit_rules_session_events + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Use /etc/audit/rules.d/session.rules as the recipient for the rule + set_fact: + all_files: + - /etc/audit/rules.d/session.rules + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_watch_key.matched is defined and find_watch_key.matched == 0 and find_existing_watch_rules_d.matched + is defined and find_existing_watch_rules_d.matched == 0 + tags: + - CCE-80742-0 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.3 + - audit_rules_session_events + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Use matched file as the recipient for the rule + set_fact: + all_files: + - '{{ find_watch_key.files | map(attribute=''path'') | list | first }}' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_watch_key.matched is defined and find_watch_key.matched > 0 and find_existing_watch_rules_d.matched + is defined and find_existing_watch_rules_d.matched == 0 + tags: + - CCE-80742-0 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.3 + - audit_rules_session_events + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Add watch rule for /var/log/wtmp in /etc/audit/rules.d/ + lineinfile: + path: '{{ all_files[0] }}' + line: -w /var/log/wtmp -p wa -k session + create: true + mode: '0640' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched + == 0 + tags: + - CCE-80742-0 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.3 + - audit_rules_session_events + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Check if watch rule for /var/log/wtmp already exists in /etc/audit/audit.rules + find: + paths: /etc/audit/ + contains: ^\s*-w\s+/var/log/wtmp\s+-p\s+wa(\s|$)+ + patterns: audit.rules + register: find_existing_watch_audit_rules + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80742-0 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.3 + - audit_rules_session_events + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Add watch rule for /var/log/wtmp in /etc/audit/audit.rules + lineinfile: + line: -w /var/log/wtmp -p wa -k session + state: present + dest: /etc/audit/audit.rules + create: true + mode: '0640' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_audit_rules.matched is defined and find_existing_watch_audit_rules.matched + == 0 + tags: + - CCE-80742-0 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.3 + - audit_rules_session_events + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + --- + + +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,{{ %0A-w%20/var/run/utmp%20-p%20wa%20-k%20session%0A-w%20/var/log/btmp%20-p%20wa%20-k%20session%0A-w%20/var/log/wtmp%20-p%20wa%20-k%20session%0A }} + mode: 0600 + path: /etc/audit/rules.d/75-audit-session-events.rules + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + + +# If the audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# into the list of files to be inspected +files_to_inspect+=('/etc/audit/audit.rules') + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/var/run/utmp" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/var/run/utmp $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/var/run/utmp$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /var/run/utmp -p wa -k session" >> "$audit_rules_file" + fi +done +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + +# If the audit is 'augenrules', then check if rule is already defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection. +# If rule isn't defined, add '/etc/audit/rules.d/session.rules' to list of files for inspection. +readarray -t matches < <(grep -HP "[\s]*-w[\s]+/var/run/utmp" /etc/audit/rules.d/*.rules) + +# For each of the matched entries +for match in "${matches[@]}" +do + # Extract filepath from the match + rulesd_audit_file=$(echo $match | cut -f1 -d ':') + # Append that path into list of files for inspection + files_to_inspect+=("$rulesd_audit_file") +done +# Case when particular audit rule isn't defined yet +if [ "${#files_to_inspect[@]}" -eq "0" ] +then + # Append '/etc/audit/rules.d/session.rules' into list of files for inspection + key_rule_file="/etc/audit/rules.d/session.rules" + # If the session.rules file doesn't exist yet, create it with correct permissions + if [ ! -e "$key_rule_file" ] + then + touch "$key_rule_file" + chmod 0640 "$key_rule_file" + fi + files_to_inspect+=("$key_rule_file") +fi + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/var/run/utmp" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/var/run/utmp $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/var/run/utmp$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /var/run/utmp -p wa -k session" >> "$audit_rules_file" + fi +done +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + + +# If the audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# into the list of files to be inspected +files_to_inspect+=('/etc/audit/audit.rules') + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/var/log/btmp" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/var/log/btmp $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/var/log/btmp$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /var/log/btmp -p wa -k session" >> "$audit_rules_file" + fi +done +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + +# If the audit is 'augenrules', then check if rule is already defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection. +# If rule isn't defined, add '/etc/audit/rules.d/session.rules' to list of files for inspection. +readarray -t matches < <(grep -HP "[\s]*-w[\s]+/var/log/btmp" /etc/audit/rules.d/*.rules) + +# For each of the matched entries +for match in "${matches[@]}" +do + # Extract filepath from the match + rulesd_audit_file=$(echo $match | cut -f1 -d ':') + # Append that path into list of files for inspection + files_to_inspect+=("$rulesd_audit_file") +done +# Case when particular audit rule isn't defined yet +if [ "${#files_to_inspect[@]}" -eq "0" ] +then + # Append '/etc/audit/rules.d/session.rules' into list of files for inspection + key_rule_file="/etc/audit/rules.d/session.rules" + # If the session.rules file doesn't exist yet, create it with correct permissions + if [ ! -e "$key_rule_file" ] + then + touch "$key_rule_file" + chmod 0640 "$key_rule_file" + fi + files_to_inspect+=("$key_rule_file") +fi + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/var/log/btmp" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/var/log/btmp $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/var/log/btmp$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /var/log/btmp -p wa -k session" >> "$audit_rules_file" + fi +done +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + + +# If the audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# into the list of files to be inspected +files_to_inspect+=('/etc/audit/audit.rules') + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/var/log/wtmp" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/var/log/wtmp $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/var/log/wtmp$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /var/log/wtmp -p wa -k session" >> "$audit_rules_file" + fi +done +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + +# If the audit is 'augenrules', then check if rule is already defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection. +# If rule isn't defined, add '/etc/audit/rules.d/session.rules' to list of files for inspection. +readarray -t matches < <(grep -HP "[\s]*-w[\s]+/var/log/wtmp" /etc/audit/rules.d/*.rules) + +# For each of the matched entries +for match in "${matches[@]}" +do + # Extract filepath from the match + rulesd_audit_file=$(echo $match | cut -f1 -d ':') + # Append that path into list of files for inspection + files_to_inspect+=("$rulesd_audit_file") +done +# Case when particular audit rule isn't defined yet +if [ "${#files_to_inspect[@]}" -eq "0" ] +then + # Append '/etc/audit/rules.d/session.rules' into list of files for inspection + key_rule_file="/etc/audit/rules.d/session.rules" + # If the session.rules file doesn't exist yet, create it with correct permissions + if [ ! -e "$key_rule_file" ] + then + touch "$key_rule_file" + chmod 0640 "$key_rule_file" + fi + files_to_inspect+=("$key_rule_file") +fi + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/var/log/wtmp" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/var/log/wtmp $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/var/log/wtmp$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /var/log/wtmp -p wa -k session" >> "$audit_rules_file" + fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + Ensure auditd Collects System Administrator Actions - /etc/sudoers + At a minimum, the audit system should collect administrator actions +for all users and root. If the auditd daemon is configured to use the +augenrules program to read audit rules during daemon startup (the default), +add the following line to a file with suffix .rules in the directory +/etc/audit/rules.d: +-w /etc/sudoers -p wa -k actions +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following line to +/etc/audit/audit.rules file: +-w /etc/sudoers -p wa -k actions + CCI-000018 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-001403 + CCI-001404 + CCI-002130 + CCI-002132 + CCI-002884 + SRG-OS-000004-GPOS-00004 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000304-GPOS-00121 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000470-GPOS-00214 + SRG-OS-000471-GPOS-00215 + SRG-OS-000239-GPOS-00089 + SRG-OS-000240-GPOS-00090 + SRG-OS-000241-GPOS-00091 + SRG-OS-000303-GPOS-00120 + SRG-OS-000466-GPOS-00210 + SRG-OS-000476-GPOS-00221 + RHEL-08-030171 + SV-230409r627750_rule + The actions taken by system administrators should be audited to keep a record +of what was executed on the system, as well as, for accountability purposes. +Editing the sudoers file may be sign of an attacker trying to +establish persistent methods to a system, auditing the editing of the sudoers +files mitigates this risk. + CCE-90175-1 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-90175-1 + - DISA-STIG-RHEL-08-030171 + - audit_rules_sudoers + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Check if watch rule for /etc/sudoers already exists in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: ^\s*-w\s+/etc/sudoers\s+-p\s+wa(\s|$)+ + patterns: '*.rules' + register: find_existing_watch_rules_d + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-90175-1 + - DISA-STIG-RHEL-08-030171 + - audit_rules_sudoers + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Search /etc/audit/rules.d for other rules with specified key actions + find: + paths: /etc/audit/rules.d + contains: ^.*(?:-F key=|-k\s+)actions$ + patterns: '*.rules' + register: find_watch_key + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched + == 0 + tags: + - CCE-90175-1 + - DISA-STIG-RHEL-08-030171 + - audit_rules_sudoers + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Use /etc/audit/rules.d/actions.rules as the recipient for the rule + set_fact: + all_files: + - /etc/audit/rules.d/actions.rules + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_watch_key.matched is defined and find_watch_key.matched == 0 and find_existing_watch_rules_d.matched + is defined and find_existing_watch_rules_d.matched == 0 + tags: + - CCE-90175-1 + - DISA-STIG-RHEL-08-030171 + - audit_rules_sudoers + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Use matched file as the recipient for the rule + set_fact: + all_files: + - '{{ find_watch_key.files | map(attribute=''path'') | list | first }}' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_watch_key.matched is defined and find_watch_key.matched > 0 and find_existing_watch_rules_d.matched + is defined and find_existing_watch_rules_d.matched == 0 + tags: + - CCE-90175-1 + - DISA-STIG-RHEL-08-030171 + - audit_rules_sudoers + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Add watch rule for /etc/sudoers in /etc/audit/rules.d/ + lineinfile: + path: '{{ all_files[0] }}' + line: -w /etc/sudoers -p wa -k actions + create: true + mode: '0640' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched + == 0 + tags: + - CCE-90175-1 + - DISA-STIG-RHEL-08-030171 + - audit_rules_sudoers + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Check if watch rule for /etc/sudoers already exists in /etc/audit/audit.rules + find: + paths: /etc/audit/ + contains: ^\s*-w\s+/etc/sudoers\s+-p\s+wa(\s|$)+ + patterns: audit.rules + register: find_existing_watch_audit_rules + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-90175-1 + - DISA-STIG-RHEL-08-030171 + - audit_rules_sudoers + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Add watch rule for /etc/sudoers in /etc/audit/audit.rules + lineinfile: + line: -w /etc/sudoers -p wa -k actions + state: present + dest: /etc/audit/audit.rules + create: true + mode: '0640' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_audit_rules.matched is defined and find_existing_watch_audit_rules.matched + == 0 + tags: + - CCE-90175-1 + - DISA-STIG-RHEL-08-030171 + - audit_rules_sudoers + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + + +# If the audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# into the list of files to be inspected +files_to_inspect+=('/etc/audit/audit.rules') + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/etc/sudoers" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/sudoers $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/etc/sudoers$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /etc/sudoers -p wa -k actions" >> "$audit_rules_file" + fi +done +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + +# If the audit is 'augenrules', then check if rule is already defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection. +# If rule isn't defined, add '/etc/audit/rules.d/actions.rules' to list of files for inspection. +readarray -t matches < <(grep -HP "[\s]*-w[\s]+/etc/sudoers" /etc/audit/rules.d/*.rules) + +# For each of the matched entries +for match in "${matches[@]}" +do + # Extract filepath from the match + rulesd_audit_file=$(echo $match | cut -f1 -d ':') + # Append that path into list of files for inspection + files_to_inspect+=("$rulesd_audit_file") +done +# Case when particular audit rule isn't defined yet +if [ "${#files_to_inspect[@]}" -eq "0" ] +then + # Append '/etc/audit/rules.d/actions.rules' into list of files for inspection + key_rule_file="/etc/audit/rules.d/actions.rules" + # If the actions.rules file doesn't exist yet, create it with correct permissions + if [ ! -e "$key_rule_file" ] + then + touch "$key_rule_file" + chmod 0640 "$key_rule_file" + fi + files_to_inspect+=("$key_rule_file") +fi + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/etc/sudoers" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/sudoers $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/etc/sudoers$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /etc/sudoers -p wa -k actions" >> "$audit_rules_file" + fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure auditd Collects System Administrator Actions - /etc/sudoers.d/ + At a minimum, the audit system should collect administrator actions +for all users and root. If the auditd daemon is configured to use the +augenrules program to read audit rules during daemon startup (the default), +add the following line to a file with suffix .rules in the directory +/etc/audit/rules.d: +-w /etc/sudoers.d/ -p wa -k actions +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following line to +/etc/audit/audit.rules file: +-w /etc/sudoers.d/ -p wa -k actions + CCI-000018 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-001403 + CCI-001404 + CCI-002130 + CCI-002132 + CCI-002884 + SRG-OS-000004-GPOS-00004 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000304-GPOS-00121 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000470-GPOS-00214 + SRG-OS-000471-GPOS-00215 + SRG-OS-000239-GPOS-00089 + SRG-OS-000240-GPOS-00090 + SRG-OS-000241-GPOS-00091 + SRG-OS-000303-GPOS-00120 + SRG-OS-000466-GPOS-00210 + SRG-OS-000476-GPOS-00221 + RHEL-08-030172 + SV-230410r627750_rule + The actions taken by system administrators should be audited to keep a record +of what was executed on the system, as well as, for accountability purposes. +Editing the sudoers file may be sign of an attacker trying to +establish persistent methods to a system, auditing the editing of the sudoers +files mitigates this risk. + CCE-89497-2 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-89497-2 + - DISA-STIG-RHEL-08-030172 + - audit_rules_sudoers_d + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Check if watch rule for /etc/sudoers.d/ already exists in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: ^\s*-w\s+/etc/sudoers.d/\s+-p\s+wa(\s|$)+ + patterns: '*.rules' + register: find_existing_watch_rules_d + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-89497-2 + - DISA-STIG-RHEL-08-030172 + - audit_rules_sudoers_d + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Search /etc/audit/rules.d for other rules with specified key actions + find: + paths: /etc/audit/rules.d + contains: ^.*(?:-F key=|-k\s+)actions$ + patterns: '*.rules' + register: find_watch_key + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched + == 0 + tags: + - CCE-89497-2 + - DISA-STIG-RHEL-08-030172 + - audit_rules_sudoers_d + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Use /etc/audit/rules.d/actions.rules as the recipient for the rule + set_fact: + all_files: + - /etc/audit/rules.d/actions.rules + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_watch_key.matched is defined and find_watch_key.matched == 0 and find_existing_watch_rules_d.matched + is defined and find_existing_watch_rules_d.matched == 0 + tags: + - CCE-89497-2 + - DISA-STIG-RHEL-08-030172 + - audit_rules_sudoers_d + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Use matched file as the recipient for the rule + set_fact: + all_files: + - '{{ find_watch_key.files | map(attribute=''path'') | list | first }}' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_watch_key.matched is defined and find_watch_key.matched > 0 and find_existing_watch_rules_d.matched + is defined and find_existing_watch_rules_d.matched == 0 + tags: + - CCE-89497-2 + - DISA-STIG-RHEL-08-030172 + - audit_rules_sudoers_d + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Add watch rule for /etc/sudoers.d/ in /etc/audit/rules.d/ + lineinfile: + path: '{{ all_files[0] }}' + line: -w /etc/sudoers.d/ -p wa -k actions + create: true + mode: '0640' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched + == 0 + tags: + - CCE-89497-2 + - DISA-STIG-RHEL-08-030172 + - audit_rules_sudoers_d + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Check if watch rule for /etc/sudoers.d/ already exists in /etc/audit/audit.rules + find: + paths: /etc/audit/ + contains: ^\s*-w\s+/etc/sudoers.d/\s+-p\s+wa(\s|$)+ + patterns: audit.rules + register: find_existing_watch_audit_rules + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-89497-2 + - DISA-STIG-RHEL-08-030172 + - audit_rules_sudoers_d + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Add watch rule for /etc/sudoers.d/ in /etc/audit/audit.rules + lineinfile: + line: -w /etc/sudoers.d/ -p wa -k actions + state: present + dest: /etc/audit/audit.rules + create: true + mode: '0640' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_audit_rules.matched is defined and find_existing_watch_audit_rules.matched + == 0 + tags: + - CCE-89497-2 + - DISA-STIG-RHEL-08-030172 + - audit_rules_sudoers_d + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + + +# If the audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# into the list of files to be inspected +files_to_inspect+=('/etc/audit/audit.rules') + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/etc/sudoers.d/" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/sudoers.d/ $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/etc/sudoers.d/$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /etc/sudoers.d/ -p wa -k actions" >> "$audit_rules_file" + fi +done +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + +# If the audit is 'augenrules', then check if rule is already defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection. +# If rule isn't defined, add '/etc/audit/rules.d/actions.rules' to list of files for inspection. +readarray -t matches < <(grep -HP "[\s]*-w[\s]+/etc/sudoers.d/" /etc/audit/rules.d/*.rules) + +# For each of the matched entries +for match in "${matches[@]}" +do + # Extract filepath from the match + rulesd_audit_file=$(echo $match | cut -f1 -d ':') + # Append that path into list of files for inspection + files_to_inspect+=("$rulesd_audit_file") +done +# Case when particular audit rule isn't defined yet +if [ "${#files_to_inspect[@]}" -eq "0" ] +then + # Append '/etc/audit/rules.d/actions.rules' into list of files for inspection + key_rule_file="/etc/audit/rules.d/actions.rules" + # If the actions.rules file doesn't exist yet, create it with correct permissions + if [ ! -e "$key_rule_file" ] + then + touch "$key_rule_file" + chmod 0640 "$key_rule_file" + fi + files_to_inspect+=("$key_rule_file") +fi + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/etc/sudoers.d/" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/sudoers.d/ $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/etc/sudoers.d/$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /etc/sudoers.d/ -p wa -k actions" >> "$audit_rules_file" + fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Events When Privileged Executables Are Run + Verify the system generates an audit record when privileged functions are executed. + +If audit is using the "auditctl" tool to load the rules, run the following command: + +$ sudo grep execve /etc/audit/audit.rules + +If audit is using the "augenrules" tool to load the rules, run the following command: + +$ sudo grep -r execve /etc/audit/rules.d + + +-a always,exit -F arch=b32 -S execve -C uid!=euid -F euid=0 -k setuid +-a always,exit -F arch=b64 -S execve -C uid!=euid -F euid=0 -k setuid +-a always,exit -F arch=b32 -S execve -C gid!=egid -F egid=0 -k setgid +-a always,exit -F arch=b64 -S execve -C gid!=egid -F egid=0 -k setgid + + +If both the "b32" and "b64" audit rules for "SUID" files are not defined, this is a finding. +If both the "b32" and "b64" audit rules for "SGID" files are not defined, this is a finding. + Note that these rules can be configured in a +number of ways while still achieving the desired effect. + CCI-001814 + CCI-001882 + CCI-001889 + CCI-001880 + CCI-001881 + CCI-001878 + CCI-001879 + CCI-001875 + CCI-001877 + CCI-001914 + CCI-002233 + CCI-002234 + CM-5(1) + AU-7(a) + AU-7(b) + AU-8(b) + AU-12(3) + AC-6(9) + SRG-OS-000326-GPOS-00126 + SRG-OS-000327-GPOS-00127 + RHEL-08-030000 + SV-230386r627750_rule + Misuse of privileged functions, either intentionally or unintentionally by +authorized users, or by unauthorized external entities that have +compromised information system accounts, is a serious and ongoing concern +and can have significant adverse impacts on organizations. Auditing the use +of privileged functions is one way to detect such misuse and identify the +risk from insider threats and the advanced persistent threat. + + CCE-83556-1 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83556-1 + - DISA-STIG-RHEL-08-030000 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(3) + - NIST-800-53-AU-7(a) + - NIST-800-53-AU-7(b) + - NIST-800-53-AU-8(b) + - NIST-800-53-CM-5(1) + - audit_rules_suid_privilege_function + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Service facts + service_facts: null + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83556-1 + - DISA-STIG-RHEL-08-030000 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(3) + - NIST-800-53-AU-7(a) + - NIST-800-53-AU-7(b) + - NIST-800-53-AU-8(b) + - NIST-800-53-CM-5(1) + - audit_rules_suid_privilege_function + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Check the rules script being used + command: grep '^ExecStartPost' /usr/lib/systemd/system/auditd.service + register: check_rules_scripts_result + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83556-1 + - DISA-STIG-RHEL-08-030000 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(3) + - NIST-800-53-AU-7(a) + - NIST-800-53-AU-7(b) + - NIST-800-53-AU-8(b) + - NIST-800-53-CM-5(1) + - audit_rules_suid_privilege_function + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Set suid_audit_rules fact + set_fact: + suid_audit_rules: + - -a always,exit -F arch=b32 -S execve -C gid!=egid -F egid=0 -k setgid + - -a always,exit -F arch=b64 -S execve -C gid!=egid -F egid=0 -k setgid + - -a always,exit -F arch=b32 -S execve -C uid!=euid -F euid=0 -k setuid + - -a always,exit -F arch=b64 -S execve -C uid!=euid -F euid=0 -k setuid + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83556-1 + - DISA-STIG-RHEL-08-030000 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(3) + - NIST-800-53-AU-7(a) + - NIST-800-53-AU-7(b) + - NIST-800-53-AU-8(b) + - NIST-800-53-CM-5(1) + - audit_rules_suid_privilege_function + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Update /etc/audit/rules.d/privileged.rules to audit privileged functions + lineinfile: + path: /etc/audit/rules.d/privileged.rules + line: '{{ item }}' + create: true + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"auditd.service" in ansible_facts.services' + - '"augenrules" in check_rules_scripts_result.stdout' + register: augenrules_audit_rules_privilege_function_update_result + with_items: '{{ suid_audit_rules }}' + tags: + - CCE-83556-1 + - DISA-STIG-RHEL-08-030000 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(3) + - NIST-800-53-AU-7(a) + - NIST-800-53-AU-7(b) + - NIST-800-53-AU-8(b) + - NIST-800-53-CM-5(1) + - audit_rules_suid_privilege_function + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Update Update /etc/audit/audit.rules to audit privileged functions + lineinfile: + path: /etc/audit/audit.rules + line: '{{ item }}' + create: true + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"auditd.service" in ansible_facts.services' + - '"auditctl" in check_rules_scripts_result.stdout' + register: auditctl_audit_rules_privilege_function_update_result + with_items: '{{ suid_audit_rules }}' + tags: + - CCE-83556-1 + - DISA-STIG-RHEL-08-030000 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(3) + - NIST-800-53-AU-7(a) + - NIST-800-53-AU-7(b) + - NIST-800-53-AU-8(b) + - NIST-800-53-CM-5(1) + - audit_rules_suid_privilege_function + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Restart Auditd + command: /usr/sbin/service auditd restart + args: + warn: false + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - (augenrules_audit_rules_privilege_function_update_result.changed or auditctl_audit_rules_privilege_function_update_result.changed) + - ansible_facts.services["auditd.service"].state == "running" + tags: + - CCE-83556-1 + - DISA-STIG-RHEL-08-030000 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(3) + - NIST-800-53-AU-7(a) + - NIST-800-53-AU-7(b) + - NIST-800-53-AU-8(b) + - NIST-800-53-CM-5(1) + - audit_rules_suid_privilege_function + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + + OTHER_FILTERS="-C uid!=euid -F euid=0" + + AUID_FILTERS="" + SYSCALL="execve" + KEY="setuid" + SYSCALL_GROUPING="" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + + OTHER_FILTERS="-C gid!=egid -F egid=0" + + AUID_FILTERS="" + SYSCALL="execve" + KEY="setgid" + SYSCALL_GROUPING="" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure auditd Collects System Administrator Actions + At a minimum, the audit system should collect administrator actions +for all users and root. If the auditd daemon is configured to use the +augenrules program to read audit rules during daemon startup (the default), +add the following line to a file with suffix .rules in the directory +/etc/audit/rules.d: +-w /etc/sudoers -p wa -k actions +-w /etc/sudoers.d/ -p wa -k actions +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following line to +/etc/audit/audit.rules file: +-w /etc/sudoers -p wa -k actions +-w /etc/sudoers.d/ -p wa -k actions + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 18 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 5.4.1.1 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + DSS06.03 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000126 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.2.2 + 4.3.3.3.9 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.1 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.1.2 + A.6.2.1 + A.6.2.2 + A.7.1.1 + A.9.1.2 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.1 + A.9.4.2 + A.9.4.3 + A.9.4.4 + A.9.4.5 + AC-2(7)(b) + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-1 + PR.AC-3 + PR.AC-4 + PR.AC-6 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.2.2 + Req-10.2.5.b + SRG-OS-000004-GPOS-00004 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000304-GPOS-00121 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000470-GPOS-00214 + SRG-OS-000471-GPOS-00215 + SRG-OS-000239-GPOS-00089 + SRG-OS-000240-GPOS-00090 + SRG-OS-000241-GPOS-00091 + SRG-OS-000303-GPOS-00120 + SRG-OS-000304-GPOS-00121 + SRG-OS-000466-GPOS-00210 + SRG-OS-000476-GPOS-00221 + SRG-OS-000462-VMM-001840 + SRG-OS-000471-VMM-001910 + 4.1.3.1 + The actions taken by system administrators should be audited to keep a record +of what was executed on the system, as well as, for accountability purposes. + CCE-80743-8 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80743-8 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(7)(b) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.2 + - PCI-DSS-Req-10.2.5.b + - audit_rules_sysadmin_actions + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Check if watch rule for /etc/sudoers already exists in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: ^\s*-w\s+/etc/sudoers\s+-p\s+wa(\s|$)+ + patterns: '*.rules' + register: find_existing_watch_rules_d + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80743-8 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(7)(b) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.2 + - PCI-DSS-Req-10.2.5.b + - audit_rules_sysadmin_actions + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Search /etc/audit/rules.d for other rules with specified key actions + find: + paths: /etc/audit/rules.d + contains: ^.*(?:-F key=|-k\s+)actions$ + patterns: '*.rules' + register: find_watch_key + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched + == 0 + tags: + - CCE-80743-8 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(7)(b) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.2 + - PCI-DSS-Req-10.2.5.b + - audit_rules_sysadmin_actions + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Use /etc/audit/rules.d/actions.rules as the recipient for the rule + set_fact: + all_files: + - /etc/audit/rules.d/actions.rules + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_watch_key.matched is defined and find_watch_key.matched == 0 and find_existing_watch_rules_d.matched + is defined and find_existing_watch_rules_d.matched == 0 + tags: + - CCE-80743-8 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(7)(b) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.2 + - PCI-DSS-Req-10.2.5.b + - audit_rules_sysadmin_actions + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Use matched file as the recipient for the rule + set_fact: + all_files: + - '{{ find_watch_key.files | map(attribute=''path'') | list | first }}' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_watch_key.matched is defined and find_watch_key.matched > 0 and find_existing_watch_rules_d.matched + is defined and find_existing_watch_rules_d.matched == 0 + tags: + - CCE-80743-8 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(7)(b) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.2 + - PCI-DSS-Req-10.2.5.b + - audit_rules_sysadmin_actions + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Add watch rule for /etc/sudoers in /etc/audit/rules.d/ + lineinfile: + path: '{{ all_files[0] }}' + line: -w /etc/sudoers -p wa -k actions + create: true + mode: '0640' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched + == 0 + tags: + - CCE-80743-8 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(7)(b) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.2 + - PCI-DSS-Req-10.2.5.b + - audit_rules_sysadmin_actions + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Check if watch rule for /etc/sudoers already exists in /etc/audit/audit.rules + find: + paths: /etc/audit/ + contains: ^\s*-w\s+/etc/sudoers\s+-p\s+wa(\s|$)+ + patterns: audit.rules + register: find_existing_watch_audit_rules + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80743-8 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(7)(b) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.2 + - PCI-DSS-Req-10.2.5.b + - audit_rules_sysadmin_actions + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Add watch rule for /etc/sudoers in /etc/audit/audit.rules + lineinfile: + line: -w /etc/sudoers -p wa -k actions + state: present + dest: /etc/audit/audit.rules + create: true + mode: '0640' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_audit_rules.matched is defined and find_existing_watch_audit_rules.matched + == 0 + tags: + - CCE-80743-8 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(7)(b) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.2 + - PCI-DSS-Req-10.2.5.b + - audit_rules_sysadmin_actions + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Check if watch rule for /etc/sudoers.d/ already exists in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: ^\s*-w\s+/etc/sudoers.d/\s+-p\s+wa(\s|$)+ + patterns: '*.rules' + register: find_existing_watch_rules_d + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80743-8 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(7)(b) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.2 + - PCI-DSS-Req-10.2.5.b + - audit_rules_sysadmin_actions + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Search /etc/audit/rules.d for other rules with specified key actions + find: + paths: /etc/audit/rules.d + contains: ^.*(?:-F key=|-k\s+)actions$ + patterns: '*.rules' + register: find_watch_key + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched + == 0 + tags: + - CCE-80743-8 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(7)(b) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.2 + - PCI-DSS-Req-10.2.5.b + - audit_rules_sysadmin_actions + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Use /etc/audit/rules.d/actions.rules as the recipient for the rule + set_fact: + all_files: + - /etc/audit/rules.d/actions.rules + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_watch_key.matched is defined and find_watch_key.matched == 0 and find_existing_watch_rules_d.matched + is defined and find_existing_watch_rules_d.matched == 0 + tags: + - CCE-80743-8 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(7)(b) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.2 + - PCI-DSS-Req-10.2.5.b + - audit_rules_sysadmin_actions + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Use matched file as the recipient for the rule + set_fact: + all_files: + - '{{ find_watch_key.files | map(attribute=''path'') | list | first }}' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_watch_key.matched is defined and find_watch_key.matched > 0 and find_existing_watch_rules_d.matched + is defined and find_existing_watch_rules_d.matched == 0 + tags: + - CCE-80743-8 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(7)(b) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.2 + - PCI-DSS-Req-10.2.5.b + - audit_rules_sysadmin_actions + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Add watch rule for /etc/sudoers.d/ in /etc/audit/rules.d/ + lineinfile: + path: '{{ all_files[0] }}' + line: -w /etc/sudoers.d/ -p wa -k actions + create: true + mode: '0640' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched + == 0 + tags: + - CCE-80743-8 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(7)(b) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.2 + - PCI-DSS-Req-10.2.5.b + - audit_rules_sysadmin_actions + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Check if watch rule for /etc/sudoers.d/ already exists in /etc/audit/audit.rules + find: + paths: /etc/audit/ + contains: ^\s*-w\s+/etc/sudoers.d/\s+-p\s+wa(\s|$)+ + patterns: audit.rules + register: find_existing_watch_audit_rules + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80743-8 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(7)(b) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.2 + - PCI-DSS-Req-10.2.5.b + - audit_rules_sysadmin_actions + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Add watch rule for /etc/sudoers.d/ in /etc/audit/audit.rules + lineinfile: + line: -w /etc/sudoers.d/ -p wa -k actions + state: present + dest: /etc/audit/audit.rules + create: true + mode: '0640' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_audit_rules.matched is defined and find_existing_watch_audit_rules.matched + == 0 + tags: + - CCE-80743-8 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(7)(b) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.2 + - PCI-DSS-Req-10.2.5.b + - audit_rules_sysadmin_actions + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,{{ -w%20/etc/sudoers.d/%20-p%20wa%20-k%20actions%0A-w%20/etc/sudoers%20-p%20wa%20-k%20actions%0A }} + mode: 0600 + path: /etc/audit/rules.d/75-audit-sysadmin-actions.rules + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + + +# If the audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# into the list of files to be inspected +files_to_inspect+=('/etc/audit/audit.rules') + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/etc/sudoers" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/sudoers $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/etc/sudoers$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /etc/sudoers -p wa -k actions" >> "$audit_rules_file" + fi +done +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + +# If the audit is 'augenrules', then check if rule is already defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection. +# If rule isn't defined, add '/etc/audit/rules.d/actions.rules' to list of files for inspection. +readarray -t matches < <(grep -HP "[\s]*-w[\s]+/etc/sudoers" /etc/audit/rules.d/*.rules) + +# For each of the matched entries +for match in "${matches[@]}" +do + # Extract filepath from the match + rulesd_audit_file=$(echo $match | cut -f1 -d ':') + # Append that path into list of files for inspection + files_to_inspect+=("$rulesd_audit_file") +done +# Case when particular audit rule isn't defined yet +if [ "${#files_to_inspect[@]}" -eq "0" ] +then + # Append '/etc/audit/rules.d/actions.rules' into list of files for inspection + key_rule_file="/etc/audit/rules.d/actions.rules" + # If the actions.rules file doesn't exist yet, create it with correct permissions + if [ ! -e "$key_rule_file" ] + then + touch "$key_rule_file" + chmod 0640 "$key_rule_file" + fi + files_to_inspect+=("$key_rule_file") +fi + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/etc/sudoers" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/sudoers $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/etc/sudoers$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /etc/sudoers -p wa -k actions" >> "$audit_rules_file" + fi +done + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + + +# If the audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# into the list of files to be inspected +files_to_inspect+=('/etc/audit/audit.rules') + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/etc/sudoers.d/" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/sudoers.d/ $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/etc/sudoers.d/$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /etc/sudoers.d/ -p wa -k actions" >> "$audit_rules_file" + fi +done +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + +# If the audit is 'augenrules', then check if rule is already defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection. +# If rule isn't defined, add '/etc/audit/rules.d/actions.rules' to list of files for inspection. +readarray -t matches < <(grep -HP "[\s]*-w[\s]+/etc/sudoers.d/" /etc/audit/rules.d/*.rules) + +# For each of the matched entries +for match in "${matches[@]}" +do + # Extract filepath from the match + rulesd_audit_file=$(echo $match | cut -f1 -d ':') + # Append that path into list of files for inspection + files_to_inspect+=("$rulesd_audit_file") +done +# Case when particular audit rule isn't defined yet +if [ "${#files_to_inspect[@]}" -eq "0" ] +then + # Append '/etc/audit/rules.d/actions.rules' into list of files for inspection + key_rule_file="/etc/audit/rules.d/actions.rules" + # If the actions.rules file doesn't exist yet, create it with correct permissions + if [ ! -e "$key_rule_file" ] + then + touch "$key_rule_file" + chmod 0640 "$key_rule_file" + fi + files_to_inspect+=("$key_rule_file") +fi + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/etc/sudoers.d/" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/sudoers.d/ $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/etc/sudoers.d/$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /etc/sudoers.d/ -p wa -k actions" >> "$audit_rules_file" + fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Shutdown System When Auditing Failures Occur + If the auditd daemon is configured to use the +augenrules program to read audit rules during daemon startup (the +default), add the following line to to the bottom of a file with suffix +.rules in the directory /etc/audit/rules.d: +-f 2 +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following line to the +bottom of the /etc/audit/audit.rules file: +-f 2 + 1 + 14 + 15 + 16 + 3 + 5 + 6 + APO11.04 + BAI03.05 + DSS05.04 + DSS05.07 + MEA02.01 + 3.3.1 + 3.3.4 + CCI-000139 + CCI-000140 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + AU-5(b) + SC-24 + CM-6(a) + PR.PT-1 + SRG-OS-000046-GPOS-00022 + SRG-OS-000047-GPOS-00023 + SRG-OS-000047-VMM-000220 + It is critical for the appropriate personnel to be aware if a system +is at risk of failing to process audit logs as required. Without this +notification, the security personnel may be unaware of an impending failure of +the audit capability, and system operation may be adversely affected. + +Audit processing failures include software/hardware errors, failures in the +audit capturing mechanisms, and audit storage capacity being reached or +exceeded. + CCE-80744-6 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80744-6 + - NIST-800-171-3.3.1 + - NIST-800-171-3.3.4 + - NIST-800-53-AU-5(b) + - NIST-800-53-CM-6(a) + - NIST-800-53-SC-24 + - audit_rules_system_shutdown + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Collect all files from /etc/audit/rules.d with .rules extension + find: + paths: /etc/audit/rules.d/ + patterns: '*.rules' + register: find_rules_d + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80744-6 + - NIST-800-171-3.3.1 + - NIST-800-171-3.3.4 + - NIST-800-53-AU-5(b) + - NIST-800-53-CM-6(a) + - NIST-800-53-SC-24 + - audit_rules_system_shutdown + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Remove the -f option from all Audit config files + lineinfile: + path: '{{ item }}' + regexp: ^\s*(?:-f)\s+.*$ + state: absent + loop: '{{ find_rules_d.files | map(attribute=''path'') | list + [''/etc/audit/audit.rules''] + }}' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80744-6 + - NIST-800-171-3.3.1 + - NIST-800-171-3.3.4 + - NIST-800-53-AU-5(b) + - NIST-800-53-CM-6(a) + - NIST-800-53-SC-24 + - audit_rules_system_shutdown + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Add Audit -f option into /etc/audit/rules.d/immutable.rules and /etc/audit/audit.rules + lineinfile: + path: '{{ item }}' + create: true + line: -f 2 + loop: + - /etc/audit/audit.rules + - /etc/audit/rules.d/immutable.rules + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80744-6 + - NIST-800-171-3.3.1 + - NIST-800-171-3.3.4 + - NIST-800-53-AU-5(b) + - NIST-800-53-CM-6(a) + - NIST-800-53-SC-24 + - audit_rules_system_shutdown + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# Traverse all of: +# +# /etc/audit/audit.rules, (for auditctl case) +# /etc/audit/rules.d/*.rules (for augenrules case) +find /etc/audit /etc/audit/rules.d -maxdepth 1 -type f -name '*.rules' -exec sed -i '/-f[[:space:]]\+.*/d' {} ';' + +for AUDIT_FILE in "/etc/audit/audit.rules" "/etc/audit/rules.d/immutable.rules" +do + echo '' >> $AUDIT_FILE + echo '# Set the audit.rules configuration to halt system upon audit failure per security requirements' >> $AUDIT_FILE + echo '-f 2' >> $AUDIT_FILE +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Events that Modify User/Group Information + If the auditd daemon is configured to use the +augenrules program to read audit rules during daemon startup (the +default), add the following lines to a file with suffix .rules in the +directory /etc/audit/rules.d, in order to capture events that modify +account changes: +-w /etc/group -p wa -k audit_rules_usergroup_modification +-w /etc/passwd -p wa -k audit_rules_usergroup_modification +-w /etc/gshadow -p wa -k audit_rules_usergroup_modification +-w /etc/shadow -p wa -k audit_rules_usergroup_modification +-w /etc/security/opasswd -p wa -k audit_rules_usergroup_modification + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file, in order to capture events that modify +account changes: +-w /etc/group -p wa -k audit_rules_usergroup_modification +-w /etc/passwd -p wa -k audit_rules_usergroup_modification +-w /etc/gshadow -p wa -k audit_rules_usergroup_modification +-w /etc/shadow -p wa -k audit_rules_usergroup_modification +-w /etc/security/opasswd -p wa -k audit_rules_usergroup_modification + This rule checks for multiple syscalls related to account changes; +it was written with DISA STIG in mind. Other policies should use a +separate rule for each syscall that needs to be checked. For example: +audit_rules_usergroup_modification_groupaudit_rules_usergroup_modification_gshadowaudit_rules_usergroup_modification_passwd + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 18 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 5.4.1.1 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + DSS06.03 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000018 + CCI-000130 + CCI-000172 + CCI-001403 + CCI-002130 + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.2.2 + 4.3.3.3.9 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.1 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.1.2 + A.6.2.1 + A.6.2.2 + A.7.1.1 + A.9.1.2 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.1 + A.9.4.2 + A.9.4.3 + A.9.4.4 + A.9.4.5 + CIP-004-6 R2.2.2 + CIP-004-6 R2.2.3 + CIP-007-3 R.1.3 + CIP-007-3 R5 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.3 + CIP-007-3 R5.2.1 + CIP-007-3 R5.2.3 + AC-2(4) + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-1 + PR.AC-3 + PR.AC-4 + PR.AC-6 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + Req-10.2.5 + SRG-OS-000004-GPOS-00004 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000239-GPOS-00089 + SRG-OS-000241-GPOS-00090 + SRG-OS-000241-GPOS-00091 + SRG-OS-000303-GPOS-00120 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + SRG-OS-000476-GPOS-00221 + In addition to auditing new user and group accounts, these watches +will alert the system administrator(s) to any modifications. Any unexpected +users, groups, or modifications should be investigated for legitimacy. + CCE-80757-8 + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + + +# If the audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# into the list of files to be inspected +files_to_inspect+=('/etc/audit/audit.rules') + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/etc/group" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/group $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/etc/group$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /etc/group -p wa -k audit_rules_usergroup_modification" >> "$audit_rules_file" + fi +done +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + +# If the audit is 'augenrules', then check if rule is already defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection. +# If rule isn't defined, add '/etc/audit/rules.d/audit_rules_usergroup_modification.rules' to list of files for inspection. +readarray -t matches < <(grep -HP "[\s]*-w[\s]+/etc/group" /etc/audit/rules.d/*.rules) + +# For each of the matched entries +for match in "${matches[@]}" +do + # Extract filepath from the match + rulesd_audit_file=$(echo $match | cut -f1 -d ':') + # Append that path into list of files for inspection + files_to_inspect+=("$rulesd_audit_file") +done +# Case when particular audit rule isn't defined yet +if [ "${#files_to_inspect[@]}" -eq "0" ] +then + # Append '/etc/audit/rules.d/audit_rules_usergroup_modification.rules' into list of files for inspection + key_rule_file="/etc/audit/rules.d/audit_rules_usergroup_modification.rules" + # If the audit_rules_usergroup_modification.rules file doesn't exist yet, create it with correct permissions + if [ ! -e "$key_rule_file" ] + then + touch "$key_rule_file" + chmod 0640 "$key_rule_file" + fi + files_to_inspect+=("$key_rule_file") +fi + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/etc/group" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/group $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/etc/group$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /etc/group -p wa -k audit_rules_usergroup_modification" >> "$audit_rules_file" + fi +done +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + + +# If the audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# into the list of files to be inspected +files_to_inspect+=('/etc/audit/audit.rules') + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/etc/passwd" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/passwd $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/etc/passwd$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /etc/passwd -p wa -k audit_rules_usergroup_modification" >> "$audit_rules_file" + fi +done +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + +# If the audit is 'augenrules', then check if rule is already defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection. +# If rule isn't defined, add '/etc/audit/rules.d/audit_rules_usergroup_modification.rules' to list of files for inspection. +readarray -t matches < <(grep -HP "[\s]*-w[\s]+/etc/passwd" /etc/audit/rules.d/*.rules) + +# For each of the matched entries +for match in "${matches[@]}" +do + # Extract filepath from the match + rulesd_audit_file=$(echo $match | cut -f1 -d ':') + # Append that path into list of files for inspection + files_to_inspect+=("$rulesd_audit_file") +done +# Case when particular audit rule isn't defined yet +if [ "${#files_to_inspect[@]}" -eq "0" ] +then + # Append '/etc/audit/rules.d/audit_rules_usergroup_modification.rules' into list of files for inspection + key_rule_file="/etc/audit/rules.d/audit_rules_usergroup_modification.rules" + # If the audit_rules_usergroup_modification.rules file doesn't exist yet, create it with correct permissions + if [ ! -e "$key_rule_file" ] + then + touch "$key_rule_file" + chmod 0640 "$key_rule_file" + fi + files_to_inspect+=("$key_rule_file") +fi + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/etc/passwd" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/passwd $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/etc/passwd$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /etc/passwd -p wa -k audit_rules_usergroup_modification" >> "$audit_rules_file" + fi +done +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + + +# If the audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# into the list of files to be inspected +files_to_inspect+=('/etc/audit/audit.rules') + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/etc/gshadow" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/gshadow $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/etc/gshadow$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /etc/gshadow -p wa -k audit_rules_usergroup_modification" >> "$audit_rules_file" + fi +done +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + +# If the audit is 'augenrules', then check if rule is already defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection. +# If rule isn't defined, add '/etc/audit/rules.d/audit_rules_usergroup_modification.rules' to list of files for inspection. +readarray -t matches < <(grep -HP "[\s]*-w[\s]+/etc/gshadow" /etc/audit/rules.d/*.rules) + +# For each of the matched entries +for match in "${matches[@]}" +do + # Extract filepath from the match + rulesd_audit_file=$(echo $match | cut -f1 -d ':') + # Append that path into list of files for inspection + files_to_inspect+=("$rulesd_audit_file") +done +# Case when particular audit rule isn't defined yet +if [ "${#files_to_inspect[@]}" -eq "0" ] +then + # Append '/etc/audit/rules.d/audit_rules_usergroup_modification.rules' into list of files for inspection + key_rule_file="/etc/audit/rules.d/audit_rules_usergroup_modification.rules" + # If the audit_rules_usergroup_modification.rules file doesn't exist yet, create it with correct permissions + if [ ! -e "$key_rule_file" ] + then + touch "$key_rule_file" + chmod 0640 "$key_rule_file" + fi + files_to_inspect+=("$key_rule_file") +fi + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/etc/gshadow" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/gshadow $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/etc/gshadow$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /etc/gshadow -p wa -k audit_rules_usergroup_modification" >> "$audit_rules_file" + fi +done +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + + +# If the audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# into the list of files to be inspected +files_to_inspect+=('/etc/audit/audit.rules') + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/etc/shadow" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/shadow $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/etc/shadow$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /etc/shadow -p wa -k audit_rules_usergroup_modification" >> "$audit_rules_file" + fi +done +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + +# If the audit is 'augenrules', then check if rule is already defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection. +# If rule isn't defined, add '/etc/audit/rules.d/audit_rules_usergroup_modification.rules' to list of files for inspection. +readarray -t matches < <(grep -HP "[\s]*-w[\s]+/etc/shadow" /etc/audit/rules.d/*.rules) + +# For each of the matched entries +for match in "${matches[@]}" +do + # Extract filepath from the match + rulesd_audit_file=$(echo $match | cut -f1 -d ':') + # Append that path into list of files for inspection + files_to_inspect+=("$rulesd_audit_file") +done +# Case when particular audit rule isn't defined yet +if [ "${#files_to_inspect[@]}" -eq "0" ] +then + # Append '/etc/audit/rules.d/audit_rules_usergroup_modification.rules' into list of files for inspection + key_rule_file="/etc/audit/rules.d/audit_rules_usergroup_modification.rules" + # If the audit_rules_usergroup_modification.rules file doesn't exist yet, create it with correct permissions + if [ ! -e "$key_rule_file" ] + then + touch "$key_rule_file" + chmod 0640 "$key_rule_file" + fi + files_to_inspect+=("$key_rule_file") +fi + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/etc/shadow" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/shadow $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/etc/shadow$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /etc/shadow -p wa -k audit_rules_usergroup_modification" >> "$audit_rules_file" + fi +done +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + + +# If the audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# into the list of files to be inspected +files_to_inspect+=('/etc/audit/audit.rules') + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/etc/security/opasswd" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/security/opasswd $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/etc/security/opasswd$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /etc/security/opasswd -p wa -k audit_rules_usergroup_modification" >> "$audit_rules_file" + fi +done +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + +# If the audit is 'augenrules', then check if rule is already defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection. +# If rule isn't defined, add '/etc/audit/rules.d/audit_rules_usergroup_modification.rules' to list of files for inspection. +readarray -t matches < <(grep -HP "[\s]*-w[\s]+/etc/security/opasswd" /etc/audit/rules.d/*.rules) + +# For each of the matched entries +for match in "${matches[@]}" +do + # Extract filepath from the match + rulesd_audit_file=$(echo $match | cut -f1 -d ':') + # Append that path into list of files for inspection + files_to_inspect+=("$rulesd_audit_file") +done +# Case when particular audit rule isn't defined yet +if [ "${#files_to_inspect[@]}" -eq "0" ] +then + # Append '/etc/audit/rules.d/audit_rules_usergroup_modification.rules' into list of files for inspection + key_rule_file="/etc/audit/rules.d/audit_rules_usergroup_modification.rules" + # If the audit_rules_usergroup_modification.rules file doesn't exist yet, create it with correct permissions + if [ ! -e "$key_rule_file" ] + then + touch "$key_rule_file" + chmod 0640 "$key_rule_file" + fi + files_to_inspect+=("$key_rule_file") +fi + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/etc/security/opasswd" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/security/opasswd $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/etc/security/opasswd$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /etc/security/opasswd -p wa -k audit_rules_usergroup_modification" >> "$audit_rules_file" + fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Events that Modify User/Group Information - /etc/group + If the auditd daemon is configured to use the +augenrules program to read audit rules during daemon startup (the +default), add the following lines to a file with suffix .rules in the +directory /etc/audit/rules.d, in order to capture events that modify +account changes: + +-w /etc/group -p wa -k audit_rules_usergroup_modification + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file, in order to capture events that modify +account changes: + +-w /etc/group -p wa -k audit_rules_usergroup_modification + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 18 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 5.4.1.1 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + DSS06.03 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000018 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-001403 + CCI-001404 + CCI-001405 + CCI-001683 + CCI-001684 + CCI-001685 + CCI-001686 + CCI-002130 + CCI-002132 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.2.2 + 4.3.3.3.9 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.1 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.1.2 + A.6.2.1 + A.6.2.2 + A.7.1.1 + A.9.1.2 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.1 + A.9.4.2 + A.9.4.3 + A.9.4.4 + A.9.4.5 + CIP-004-6 R2.2.2 + CIP-004-6 R2.2.3 + CIP-007-3 R.1.3 + CIP-007-3 R5 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.3 + CIP-007-3 R5.2.1 + CIP-007-3 R5.2.3 + AC-2(4) + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-1 + PR.AC-3 + PR.AC-4 + PR.AC-6 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.2.5 + SRG-OS-000004-GPOS-00004 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000304-GPOS-00121 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000470-GPOS-00214 + SRG-OS-000471-GPOS-00215 + SRG-OS-000239-GPOS-00089 + SRG-OS-000240-GPOS-00090 + SRG-OS-000241-GPOS-00091 + SRG-OS-000303-GPOS-00120 + SRG-OS-000466-GPOS-00210 + SRG-OS-000476-GPOS-00221 + SRG-OS-000004-VMM-000040 + SRG-OS-000239-VMM-000810 + SRG-OS-000240-VMM-000820 + SRG-OS-000241-VMM-000830 + SRG-OS-000274-VMM-000960 + SRG-OS-000275-VMM-000970 + SRG-OS-000276-VMM-000980 + SRG-OS-000277-VMM-000990 + SRG-OS-000303-VMM-001090 + SRG-OS-000304-VMM-001100 + SRG-OS-000476-VMM-001960 + RHEL-08-030170 + 4.1.3.8 + SV-230408r627750_rule + In addition to auditing new user and group accounts, these watches +will alert the system administrator(s) to any modifications. Any unexpected +users, groups, or modifications should be investigated for legitimacy. + CCE-80758-6 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80758-6 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030170 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.5 + - audit_rules_usergroup_modification_group + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Check if watch rule for /etc/group already exists in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: ^\s*-w\s+/etc/group\s+-p\s+wa(\s|$)+ + patterns: '*.rules' + register: find_existing_watch_rules_d + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80758-6 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030170 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.5 + - audit_rules_usergroup_modification_group + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Search /etc/audit/rules.d for other rules with specified key audit_rules_usergroup_modification + find: + paths: /etc/audit/rules.d + contains: ^.*(?:-F key=|-k\s+)audit_rules_usergroup_modification$ + patterns: '*.rules' + register: find_watch_key + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched + == 0 + tags: + - CCE-80758-6 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030170 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.5 + - audit_rules_usergroup_modification_group + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Use /etc/audit/rules.d/audit_rules_usergroup_modification.rules as the recipient + for the rule + set_fact: + all_files: + - /etc/audit/rules.d/audit_rules_usergroup_modification.rules + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_watch_key.matched is defined and find_watch_key.matched == 0 and find_existing_watch_rules_d.matched + is defined and find_existing_watch_rules_d.matched == 0 + tags: + - CCE-80758-6 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030170 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.5 + - audit_rules_usergroup_modification_group + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Use matched file as the recipient for the rule + set_fact: + all_files: + - '{{ find_watch_key.files | map(attribute=''path'') | list | first }}' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_watch_key.matched is defined and find_watch_key.matched > 0 and find_existing_watch_rules_d.matched + is defined and find_existing_watch_rules_d.matched == 0 + tags: + - CCE-80758-6 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030170 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.5 + - audit_rules_usergroup_modification_group + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Add watch rule for /etc/group in /etc/audit/rules.d/ + lineinfile: + path: '{{ all_files[0] }}' + line: -w /etc/group -p wa -k audit_rules_usergroup_modification + create: true + mode: '0640' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched + == 0 + tags: + - CCE-80758-6 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030170 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.5 + - audit_rules_usergroup_modification_group + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Check if watch rule for /etc/group already exists in /etc/audit/audit.rules + find: + paths: /etc/audit/ + contains: ^\s*-w\s+/etc/group\s+-p\s+wa(\s|$)+ + patterns: audit.rules + register: find_existing_watch_audit_rules + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80758-6 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030170 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.5 + - audit_rules_usergroup_modification_group + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Add watch rule for /etc/group in /etc/audit/audit.rules + lineinfile: + line: -w /etc/group -p wa -k audit_rules_usergroup_modification + state: present + dest: /etc/audit/audit.rules + create: true + mode: '0640' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_audit_rules.matched is defined and find_existing_watch_audit_rules.matched + == 0 + tags: + - CCE-80758-6 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030170 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.5 + - audit_rules_usergroup_modification_group + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + + +# If the audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# into the list of files to be inspected +files_to_inspect+=('/etc/audit/audit.rules') + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/etc/group" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/group $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/etc/group$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /etc/group -p wa -k audit_rules_usergroup_modification" >> "$audit_rules_file" + fi +done +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + +# If the audit is 'augenrules', then check if rule is already defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection. +# If rule isn't defined, add '/etc/audit/rules.d/audit_rules_usergroup_modification.rules' to list of files for inspection. +readarray -t matches < <(grep -HP "[\s]*-w[\s]+/etc/group" /etc/audit/rules.d/*.rules) + +# For each of the matched entries +for match in "${matches[@]}" +do + # Extract filepath from the match + rulesd_audit_file=$(echo $match | cut -f1 -d ':') + # Append that path into list of files for inspection + files_to_inspect+=("$rulesd_audit_file") +done +# Case when particular audit rule isn't defined yet +if [ "${#files_to_inspect[@]}" -eq "0" ] +then + # Append '/etc/audit/rules.d/audit_rules_usergroup_modification.rules' into list of files for inspection + key_rule_file="/etc/audit/rules.d/audit_rules_usergroup_modification.rules" + # If the audit_rules_usergroup_modification.rules file doesn't exist yet, create it with correct permissions + if [ ! -e "$key_rule_file" ] + then + touch "$key_rule_file" + chmod 0640 "$key_rule_file" + fi + files_to_inspect+=("$key_rule_file") +fi + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/etc/group" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/group $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/etc/group$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /etc/group -p wa -k audit_rules_usergroup_modification" >> "$audit_rules_file" + fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Events that Modify User/Group Information - /etc/gshadow + If the auditd daemon is configured to use the +augenrules program to read audit rules during daemon startup (the +default), add the following lines to a file with suffix .rules in the +directory /etc/audit/rules.d, in order to capture events that modify +account changes: + +-w /etc/gshadow -p wa -k audit_rules_usergroup_modification + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file, in order to capture events that modify +account changes: + +-w /etc/gshadow -p wa -k audit_rules_usergroup_modification + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 18 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 5.4.1.1 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + DSS06.03 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000018 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-001403 + CCI-001404 + CCI-001405 + CCI-001683 + CCI-001684 + CCI-001685 + CCI-001686 + CCI-002130 + CCI-002132 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.2.2 + 4.3.3.3.9 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.1 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.1.2 + A.6.2.1 + A.6.2.2 + A.7.1.1 + A.9.1.2 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.1 + A.9.4.2 + A.9.4.3 + A.9.4.4 + A.9.4.5 + CIP-004-6 R2.2.2 + CIP-004-6 R2.2.3 + CIP-007-3 R.1.3 + CIP-007-3 R5 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.3 + CIP-007-3 R5.2.1 + CIP-007-3 R5.2.3 + AC-2(4) + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-1 + PR.AC-3 + PR.AC-4 + PR.AC-6 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.2.5 + SRG-OS-000004-GPOS-00004 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000304-GPOS-00121 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000470-GPOS-00214 + SRG-OS-000471-GPOS-00215 + SRG-OS-000239-GPOS-00089 + SRG-OS-000240-GPOS-00090 + SRG-OS-000241-GPOS-00091 + SRG-OS-000303-GPOS-00120 + SRG-OS-000466-GPOS-00210 + SRG-OS-000476-GPOS-00221 + SRG-OS-000004-VMM-000040 + SRG-OS-000239-VMM-000810 + SRG-OS-000240-VMM-000820 + SRG-OS-000241-VMM-000830 + SRG-OS-000274-VMM-000960 + SRG-OS-000275-VMM-000970 + SRG-OS-000276-VMM-000980 + SRG-OS-000277-VMM-000990 + SRG-OS-000303-VMM-001090 + SRG-OS-000304-VMM-001100 + SRG-OS-000476-VMM-001960 + RHEL-08-030160 + 4.1.3.8 + SV-230407r627750_rule + In addition to auditing new user and group accounts, these watches +will alert the system administrator(s) to any modifications. Any unexpected +users, groups, or modifications should be investigated for legitimacy. + CCE-80759-4 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80759-4 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030160 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.5 + - audit_rules_usergroup_modification_gshadow + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Check if watch rule for /etc/gshadow already exists in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: ^\s*-w\s+/etc/gshadow\s+-p\s+wa(\s|$)+ + patterns: '*.rules' + register: find_existing_watch_rules_d + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80759-4 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030160 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.5 + - audit_rules_usergroup_modification_gshadow + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Search /etc/audit/rules.d for other rules with specified key audit_rules_usergroup_modification + find: + paths: /etc/audit/rules.d + contains: ^.*(?:-F key=|-k\s+)audit_rules_usergroup_modification$ + patterns: '*.rules' + register: find_watch_key + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched + == 0 + tags: + - CCE-80759-4 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030160 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.5 + - audit_rules_usergroup_modification_gshadow + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Use /etc/audit/rules.d/audit_rules_usergroup_modification.rules as the recipient + for the rule + set_fact: + all_files: + - /etc/audit/rules.d/audit_rules_usergroup_modification.rules + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_watch_key.matched is defined and find_watch_key.matched == 0 and find_existing_watch_rules_d.matched + is defined and find_existing_watch_rules_d.matched == 0 + tags: + - CCE-80759-4 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030160 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.5 + - audit_rules_usergroup_modification_gshadow + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Use matched file as the recipient for the rule + set_fact: + all_files: + - '{{ find_watch_key.files | map(attribute=''path'') | list | first }}' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_watch_key.matched is defined and find_watch_key.matched > 0 and find_existing_watch_rules_d.matched + is defined and find_existing_watch_rules_d.matched == 0 + tags: + - CCE-80759-4 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030160 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.5 + - audit_rules_usergroup_modification_gshadow + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Add watch rule for /etc/gshadow in /etc/audit/rules.d/ + lineinfile: + path: '{{ all_files[0] }}' + line: -w /etc/gshadow -p wa -k audit_rules_usergroup_modification + create: true + mode: '0640' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched + == 0 + tags: + - CCE-80759-4 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030160 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.5 + - audit_rules_usergroup_modification_gshadow + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Check if watch rule for /etc/gshadow already exists in /etc/audit/audit.rules + find: + paths: /etc/audit/ + contains: ^\s*-w\s+/etc/gshadow\s+-p\s+wa(\s|$)+ + patterns: audit.rules + register: find_existing_watch_audit_rules + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80759-4 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030160 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.5 + - audit_rules_usergroup_modification_gshadow + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Add watch rule for /etc/gshadow in /etc/audit/audit.rules + lineinfile: + line: -w /etc/gshadow -p wa -k audit_rules_usergroup_modification + state: present + dest: /etc/audit/audit.rules + create: true + mode: '0640' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_audit_rules.matched is defined and find_existing_watch_audit_rules.matched + == 0 + tags: + - CCE-80759-4 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030160 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.5 + - audit_rules_usergroup_modification_gshadow + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + + +# If the audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# into the list of files to be inspected +files_to_inspect+=('/etc/audit/audit.rules') + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/etc/gshadow" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/gshadow $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/etc/gshadow$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /etc/gshadow -p wa -k audit_rules_usergroup_modification" >> "$audit_rules_file" + fi +done +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + +# If the audit is 'augenrules', then check if rule is already defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection. +# If rule isn't defined, add '/etc/audit/rules.d/audit_rules_usergroup_modification.rules' to list of files for inspection. +readarray -t matches < <(grep -HP "[\s]*-w[\s]+/etc/gshadow" /etc/audit/rules.d/*.rules) + +# For each of the matched entries +for match in "${matches[@]}" +do + # Extract filepath from the match + rulesd_audit_file=$(echo $match | cut -f1 -d ':') + # Append that path into list of files for inspection + files_to_inspect+=("$rulesd_audit_file") +done +# Case when particular audit rule isn't defined yet +if [ "${#files_to_inspect[@]}" -eq "0" ] +then + # Append '/etc/audit/rules.d/audit_rules_usergroup_modification.rules' into list of files for inspection + key_rule_file="/etc/audit/rules.d/audit_rules_usergroup_modification.rules" + # If the audit_rules_usergroup_modification.rules file doesn't exist yet, create it with correct permissions + if [ ! -e "$key_rule_file" ] + then + touch "$key_rule_file" + chmod 0640 "$key_rule_file" + fi + files_to_inspect+=("$key_rule_file") +fi + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/etc/gshadow" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/gshadow $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/etc/gshadow$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /etc/gshadow -p wa -k audit_rules_usergroup_modification" >> "$audit_rules_file" + fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Events that Modify User/Group Information - /etc/security/opasswd + If the auditd daemon is configured to use the +augenrules program to read audit rules during daemon startup (the +default), add the following lines to a file with suffix .rules in the +directory /etc/audit/rules.d, in order to capture events that modify +account changes: + +-w /etc/security/opasswd -p wa -k audit_rules_usergroup_modification + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file, in order to capture events that modify +account changes: + +-w /etc/security/opasswd -p wa -k audit_rules_usergroup_modification + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 18 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 5.4.1.1 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + DSS06.03 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000018 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-001403 + CCI-001404 + CCI-001405 + CCI-001683 + CCI-001684 + CCI-001685 + CCI-001686 + CCI-002130 + CCI-002132 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.2.2 + 4.3.3.3.9 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.1 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.1.2 + A.6.2.1 + A.6.2.2 + A.7.1.1 + A.9.1.2 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.1 + A.9.4.2 + A.9.4.3 + A.9.4.4 + A.9.4.5 + CIP-004-6 R2.2.2 + CIP-004-6 R2.2.3 + CIP-007-3 R.1.3 + CIP-007-3 R5 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.3 + CIP-007-3 R5.2.1 + CIP-007-3 R5.2.3 + AC-2(4) + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-1 + PR.AC-3 + PR.AC-4 + PR.AC-6 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.2.5 + SRG-OS-000004-GPOS-00004 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000304-GPOS-00121 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000470-GPOS-00214 + SRG-OS-000471-GPOS-00215 + SRG-OS-000239-GPOS-00089 + SRG-OS-000240-GPOS-00090 + SRG-OS-000241-GPOS-00091 + SRG-OS-000303-GPOS-00120 + SRG-OS-000466-GPOS-00210 + SRG-OS-000476-GPOS-00221 + SRG-OS-000004-VMM-000040 + SRG-OS-000239-VMM-000810 + SRG-OS-000240-VMM-000820 + SRG-OS-000241-VMM-000830 + SRG-OS-000274-VMM-000960 + SRG-OS-000275-VMM-000970 + SRG-OS-000276-VMM-000980 + SRG-OS-000277-VMM-000990 + SRG-OS-000303-VMM-001090 + SRG-OS-000304-VMM-001100 + SRG-OS-000476-VMM-001960 + RHEL-08-030140 + 4.1.3.8 + SV-230405r627750_rule + In addition to auditing new user and group accounts, these watches +will alert the system administrator(s) to any modifications. Any unexpected +users, groups, or modifications should be investigated for legitimacy. + CCE-80760-2 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80760-2 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030140 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.5 + - audit_rules_usergroup_modification_opasswd + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Check if watch rule for /etc/security/opasswd already exists in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: ^\s*-w\s+/etc/security/opasswd\s+-p\s+wa(\s|$)+ + patterns: '*.rules' + register: find_existing_watch_rules_d + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80760-2 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030140 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.5 + - audit_rules_usergroup_modification_opasswd + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Search /etc/audit/rules.d for other rules with specified key audit_rules_usergroup_modification + find: + paths: /etc/audit/rules.d + contains: ^.*(?:-F key=|-k\s+)audit_rules_usergroup_modification$ + patterns: '*.rules' + register: find_watch_key + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched + == 0 + tags: + - CCE-80760-2 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030140 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.5 + - audit_rules_usergroup_modification_opasswd + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Use /etc/audit/rules.d/audit_rules_usergroup_modification.rules as the recipient + for the rule + set_fact: + all_files: + - /etc/audit/rules.d/audit_rules_usergroup_modification.rules + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_watch_key.matched is defined and find_watch_key.matched == 0 and find_existing_watch_rules_d.matched + is defined and find_existing_watch_rules_d.matched == 0 + tags: + - CCE-80760-2 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030140 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.5 + - audit_rules_usergroup_modification_opasswd + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Use matched file as the recipient for the rule + set_fact: + all_files: + - '{{ find_watch_key.files | map(attribute=''path'') | list | first }}' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_watch_key.matched is defined and find_watch_key.matched > 0 and find_existing_watch_rules_d.matched + is defined and find_existing_watch_rules_d.matched == 0 + tags: + - CCE-80760-2 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030140 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.5 + - audit_rules_usergroup_modification_opasswd + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Add watch rule for /etc/security/opasswd in /etc/audit/rules.d/ + lineinfile: + path: '{{ all_files[0] }}' + line: -w /etc/security/opasswd -p wa -k audit_rules_usergroup_modification + create: true + mode: '0640' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched + == 0 + tags: + - CCE-80760-2 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030140 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.5 + - audit_rules_usergroup_modification_opasswd + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Check if watch rule for /etc/security/opasswd already exists in /etc/audit/audit.rules + find: + paths: /etc/audit/ + contains: ^\s*-w\s+/etc/security/opasswd\s+-p\s+wa(\s|$)+ + patterns: audit.rules + register: find_existing_watch_audit_rules + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80760-2 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030140 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.5 + - audit_rules_usergroup_modification_opasswd + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Add watch rule for /etc/security/opasswd in /etc/audit/audit.rules + lineinfile: + line: -w /etc/security/opasswd -p wa -k audit_rules_usergroup_modification + state: present + dest: /etc/audit/audit.rules + create: true + mode: '0640' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_audit_rules.matched is defined and find_existing_watch_audit_rules.matched + == 0 + tags: + - CCE-80760-2 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030140 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.5 + - audit_rules_usergroup_modification_opasswd + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + + +# If the audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# into the list of files to be inspected +files_to_inspect+=('/etc/audit/audit.rules') + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/etc/security/opasswd" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/security/opasswd $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/etc/security/opasswd$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /etc/security/opasswd -p wa -k audit_rules_usergroup_modification" >> "$audit_rules_file" + fi +done +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + +# If the audit is 'augenrules', then check if rule is already defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection. +# If rule isn't defined, add '/etc/audit/rules.d/audit_rules_usergroup_modification.rules' to list of files for inspection. +readarray -t matches < <(grep -HP "[\s]*-w[\s]+/etc/security/opasswd" /etc/audit/rules.d/*.rules) + +# For each of the matched entries +for match in "${matches[@]}" +do + # Extract filepath from the match + rulesd_audit_file=$(echo $match | cut -f1 -d ':') + # Append that path into list of files for inspection + files_to_inspect+=("$rulesd_audit_file") +done +# Case when particular audit rule isn't defined yet +if [ "${#files_to_inspect[@]}" -eq "0" ] +then + # Append '/etc/audit/rules.d/audit_rules_usergroup_modification.rules' into list of files for inspection + key_rule_file="/etc/audit/rules.d/audit_rules_usergroup_modification.rules" + # If the audit_rules_usergroup_modification.rules file doesn't exist yet, create it with correct permissions + if [ ! -e "$key_rule_file" ] + then + touch "$key_rule_file" + chmod 0640 "$key_rule_file" + fi + files_to_inspect+=("$key_rule_file") +fi + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/etc/security/opasswd" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/security/opasswd $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/etc/security/opasswd$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /etc/security/opasswd -p wa -k audit_rules_usergroup_modification" >> "$audit_rules_file" + fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Events that Modify User/Group Information - /etc/passwd + If the auditd daemon is configured to use the +augenrules program to read audit rules during daemon startup (the +default), add the following lines to a file with suffix .rules in the +directory /etc/audit/rules.d, in order to capture events that modify +account changes: + +-w /etc/passwd -p wa -k audit_rules_usergroup_modification + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file, in order to capture events that modify +account changes: + +-w /etc/passwd -p wa -k audit_rules_usergroup_modification + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 18 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 5.4.1.1 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + DSS06.03 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000018 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-001403 + CCI-001404 + CCI-001405 + CCI-001683 + CCI-001684 + CCI-001685 + CCI-001686 + CCI-002130 + CCI-002132 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.2.2 + 4.3.3.3.9 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.1 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.1.2 + A.6.2.1 + A.6.2.2 + A.7.1.1 + A.9.1.2 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.1 + A.9.4.2 + A.9.4.3 + A.9.4.4 + A.9.4.5 + CIP-004-6 R2.2.2 + CIP-004-6 R2.2.3 + CIP-007-3 R.1.3 + CIP-007-3 R5 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.3 + CIP-007-3 R5.2.1 + CIP-007-3 R5.2.3 + AC-2(4) + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-1 + PR.AC-3 + PR.AC-4 + PR.AC-6 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.2.5 + SRG-OS-000004-GPOS-00004 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000304-GPOS-00121 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000470-GPOS-00214 + SRG-OS-000471-GPOS-00215 + SRG-OS-000239-GPOS-00089 + SRG-OS-000240-GPOS-00090 + SRG-OS-000241-GPOS-00091 + SRG-OS-000303-GPOS-00120 + SRG-OS-000304-GPOS-00121 + SRG-OS-000466-GPOS-00210 + SRG-OS-000476-GPOS-00221 + SRG-OS-000274-GPOS-00104 + SRG-OS-000275-GPOS-00105 + SRG-OS-000276-GPOS-00106 + SRG-OS-000277-GPOS-00107 + SRG-OS-000004-VMM-000040 + SRG-OS-000239-VMM-000810 + SRG-OS-000240-VMM-000820 + SRG-OS-000241-VMM-000830 + SRG-OS-000274-VMM-000960 + SRG-OS-000275-VMM-000970 + SRG-OS-000276-VMM-000980 + SRG-OS-000277-VMM-000990 + SRG-OS-000303-VMM-001090 + SRG-OS-000304-VMM-001100 + SRG-OS-000476-VMM-001960 + RHEL-08-030150 + 4.1.3.8 + SV-230406r627750_rule + In addition to auditing new user and group accounts, these watches +will alert the system administrator(s) to any modifications. Any unexpected +users, groups, or modifications should be investigated for legitimacy. + CCE-80761-0 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80761-0 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030150 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.5 + - audit_rules_usergroup_modification_passwd + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Check if watch rule for /etc/passwd already exists in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: ^\s*-w\s+/etc/passwd\s+-p\s+wa(\s|$)+ + patterns: '*.rules' + register: find_existing_watch_rules_d + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80761-0 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030150 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.5 + - audit_rules_usergroup_modification_passwd + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Search /etc/audit/rules.d for other rules with specified key audit_rules_usergroup_modification + find: + paths: /etc/audit/rules.d + contains: ^.*(?:-F key=|-k\s+)audit_rules_usergroup_modification$ + patterns: '*.rules' + register: find_watch_key + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched + == 0 + tags: + - CCE-80761-0 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030150 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.5 + - audit_rules_usergroup_modification_passwd + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Use /etc/audit/rules.d/audit_rules_usergroup_modification.rules as the recipient + for the rule + set_fact: + all_files: + - /etc/audit/rules.d/audit_rules_usergroup_modification.rules + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_watch_key.matched is defined and find_watch_key.matched == 0 and find_existing_watch_rules_d.matched + is defined and find_existing_watch_rules_d.matched == 0 + tags: + - CCE-80761-0 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030150 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.5 + - audit_rules_usergroup_modification_passwd + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Use matched file as the recipient for the rule + set_fact: + all_files: + - '{{ find_watch_key.files | map(attribute=''path'') | list | first }}' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_watch_key.matched is defined and find_watch_key.matched > 0 and find_existing_watch_rules_d.matched + is defined and find_existing_watch_rules_d.matched == 0 + tags: + - CCE-80761-0 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030150 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.5 + - audit_rules_usergroup_modification_passwd + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Add watch rule for /etc/passwd in /etc/audit/rules.d/ + lineinfile: + path: '{{ all_files[0] }}' + line: -w /etc/passwd -p wa -k audit_rules_usergroup_modification + create: true + mode: '0640' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched + == 0 + tags: + - CCE-80761-0 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030150 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.5 + - audit_rules_usergroup_modification_passwd + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Check if watch rule for /etc/passwd already exists in /etc/audit/audit.rules + find: + paths: /etc/audit/ + contains: ^\s*-w\s+/etc/passwd\s+-p\s+wa(\s|$)+ + patterns: audit.rules + register: find_existing_watch_audit_rules + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80761-0 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030150 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.5 + - audit_rules_usergroup_modification_passwd + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Add watch rule for /etc/passwd in /etc/audit/audit.rules + lineinfile: + line: -w /etc/passwd -p wa -k audit_rules_usergroup_modification + state: present + dest: /etc/audit/audit.rules + create: true + mode: '0640' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_audit_rules.matched is defined and find_existing_watch_audit_rules.matched + == 0 + tags: + - CCE-80761-0 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030150 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.5 + - audit_rules_usergroup_modification_passwd + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + + +# If the audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# into the list of files to be inspected +files_to_inspect+=('/etc/audit/audit.rules') + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/etc/passwd" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/passwd $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/etc/passwd$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /etc/passwd -p wa -k audit_rules_usergroup_modification" >> "$audit_rules_file" + fi +done +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + +# If the audit is 'augenrules', then check if rule is already defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection. +# If rule isn't defined, add '/etc/audit/rules.d/audit_rules_usergroup_modification.rules' to list of files for inspection. +readarray -t matches < <(grep -HP "[\s]*-w[\s]+/etc/passwd" /etc/audit/rules.d/*.rules) + +# For each of the matched entries +for match in "${matches[@]}" +do + # Extract filepath from the match + rulesd_audit_file=$(echo $match | cut -f1 -d ':') + # Append that path into list of files for inspection + files_to_inspect+=("$rulesd_audit_file") +done +# Case when particular audit rule isn't defined yet +if [ "${#files_to_inspect[@]}" -eq "0" ] +then + # Append '/etc/audit/rules.d/audit_rules_usergroup_modification.rules' into list of files for inspection + key_rule_file="/etc/audit/rules.d/audit_rules_usergroup_modification.rules" + # If the audit_rules_usergroup_modification.rules file doesn't exist yet, create it with correct permissions + if [ ! -e "$key_rule_file" ] + then + touch "$key_rule_file" + chmod 0640 "$key_rule_file" + fi + files_to_inspect+=("$key_rule_file") +fi + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/etc/passwd" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/passwd $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/etc/passwd$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /etc/passwd -p wa -k audit_rules_usergroup_modification" >> "$audit_rules_file" + fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Events that Modify User/Group Information - /etc/shadow + If the auditd daemon is configured to use the +augenrules program to read audit rules during daemon startup (the +default), add the following lines to a file with suffix .rules in the +directory /etc/audit/rules.d, in order to capture events that modify +account changes: + +-w /etc/shadow -p wa -k audit_rules_usergroup_modification + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file, in order to capture events that modify +account changes: + +-w /etc/shadow -p wa -k audit_rules_usergroup_modification + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 18 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 5.4.1.1 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + DSS06.03 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000018 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-001403 + CCI-001404 + CCI-001405 + CCI-001683 + CCI-001684 + CCI-001685 + CCI-001686 + CCI-002130 + CCI-002132 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.2.2 + 4.3.3.3.9 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.1 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.1.2 + A.6.2.1 + A.6.2.2 + A.7.1.1 + A.9.1.2 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.1 + A.9.4.2 + A.9.4.3 + A.9.4.4 + A.9.4.5 + CIP-004-6 R2.2.2 + CIP-004-6 R2.2.3 + CIP-007-3 R.1.3 + CIP-007-3 R5 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.3 + CIP-007-3 R5.2.1 + CIP-007-3 R5.2.3 + AC-2(4) + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-1 + PR.AC-3 + PR.AC-4 + PR.AC-6 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.2.5 + SRG-OS-000004-GPOS-00004 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000304-GPOS-00121 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000470-GPOS-00214 + SRG-OS-000471-GPOS-00215 + SRG-OS-000239-GPOS-00089 + SRG-OS-000240-GPOS-00090 + SRG-OS-000241-GPOS-00091 + SRG-OS-000303-GPOS-00120 + SRG-OS-000466-GPOS-00210 + SRG-OS-000476-GPOS-00221 + SRG-OS-000004-VMM-000040 + SRG-OS-000239-VMM-000810 + SRG-OS-000240-VMM-000820 + SRG-OS-000241-VMM-000830 + SRG-OS-000274-VMM-000960 + SRG-OS-000275-VMM-000970 + SRG-OS-000276-VMM-000980 + SRG-OS-000277-VMM-000990 + SRG-OS-000303-VMM-001090 + SRG-OS-000304-VMM-001100 + SRG-OS-000476-VMM-001960 + RHEL-08-030130 + 4.1.3.8 + SV-230404r627750_rule + In addition to auditing new user and group accounts, these watches +will alert the system administrator(s) to any modifications. Any unexpected +users, groups, or modifications should be investigated for legitimacy. + CCE-80762-8 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80762-8 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030130 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.5 + - audit_rules_usergroup_modification_shadow + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Check if watch rule for /etc/shadow already exists in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: ^\s*-w\s+/etc/shadow\s+-p\s+wa(\s|$)+ + patterns: '*.rules' + register: find_existing_watch_rules_d + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80762-8 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030130 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.5 + - audit_rules_usergroup_modification_shadow + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Search /etc/audit/rules.d for other rules with specified key audit_rules_usergroup_modification + find: + paths: /etc/audit/rules.d + contains: ^.*(?:-F key=|-k\s+)audit_rules_usergroup_modification$ + patterns: '*.rules' + register: find_watch_key + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched + == 0 + tags: + - CCE-80762-8 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030130 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.5 + - audit_rules_usergroup_modification_shadow + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Use /etc/audit/rules.d/audit_rules_usergroup_modification.rules as the recipient + for the rule + set_fact: + all_files: + - /etc/audit/rules.d/audit_rules_usergroup_modification.rules + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_watch_key.matched is defined and find_watch_key.matched == 0 and find_existing_watch_rules_d.matched + is defined and find_existing_watch_rules_d.matched == 0 + tags: + - CCE-80762-8 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030130 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.5 + - audit_rules_usergroup_modification_shadow + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Use matched file as the recipient for the rule + set_fact: + all_files: + - '{{ find_watch_key.files | map(attribute=''path'') | list | first }}' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_watch_key.matched is defined and find_watch_key.matched > 0 and find_existing_watch_rules_d.matched + is defined and find_existing_watch_rules_d.matched == 0 + tags: + - CCE-80762-8 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030130 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.5 + - audit_rules_usergroup_modification_shadow + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Add watch rule for /etc/shadow in /etc/audit/rules.d/ + lineinfile: + path: '{{ all_files[0] }}' + line: -w /etc/shadow -p wa -k audit_rules_usergroup_modification + create: true + mode: '0640' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched + == 0 + tags: + - CCE-80762-8 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030130 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.5 + - audit_rules_usergroup_modification_shadow + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Check if watch rule for /etc/shadow already exists in /etc/audit/audit.rules + find: + paths: /etc/audit/ + contains: ^\s*-w\s+/etc/shadow\s+-p\s+wa(\s|$)+ + patterns: audit.rules + register: find_existing_watch_audit_rules + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80762-8 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030130 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.5 + - audit_rules_usergroup_modification_shadow + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Add watch rule for /etc/shadow in /etc/audit/audit.rules + lineinfile: + line: -w /etc/shadow -p wa -k audit_rules_usergroup_modification + state: present + dest: /etc/audit/audit.rules + create: true + mode: '0640' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_audit_rules.matched is defined and find_existing_watch_audit_rules.matched + == 0 + tags: + - CCE-80762-8 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030130 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.5 + - audit_rules_usergroup_modification_shadow + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + + +# If the audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# into the list of files to be inspected +files_to_inspect+=('/etc/audit/audit.rules') + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/etc/shadow" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/shadow $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/etc/shadow$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /etc/shadow -p wa -k audit_rules_usergroup_modification" >> "$audit_rules_file" + fi +done +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + +# If the audit is 'augenrules', then check if rule is already defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection. +# If rule isn't defined, add '/etc/audit/rules.d/audit_rules_usergroup_modification.rules' to list of files for inspection. +readarray -t matches < <(grep -HP "[\s]*-w[\s]+/etc/shadow" /etc/audit/rules.d/*.rules) + +# For each of the matched entries +for match in "${matches[@]}" +do + # Extract filepath from the match + rulesd_audit_file=$(echo $match | cut -f1 -d ':') + # Append that path into list of files for inspection + files_to_inspect+=("$rulesd_audit_file") +done +# Case when particular audit rule isn't defined yet +if [ "${#files_to_inspect[@]}" -eq "0" ] +then + # Append '/etc/audit/rules.d/audit_rules_usergroup_modification.rules' into list of files for inspection + key_rule_file="/etc/audit/rules.d/audit_rules_usergroup_modification.rules" + # If the audit_rules_usergroup_modification.rules file doesn't exist yet, create it with correct permissions + if [ ! -e "$key_rule_file" ] + then + touch "$key_rule_file" + chmod 0640 "$key_rule_file" + fi + files_to_inspect+=("$key_rule_file") +fi + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/etc/shadow" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/shadow $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/etc/shadow$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /etc/shadow -p wa -k audit_rules_usergroup_modification" >> "$audit_rules_file" + fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Access Events to Audit Log Directory + The audit system should collect access events to read audit log directory. +The following audit rule will assure that access to audit log directory are +collected. +-a always,exit -F dir=/var/log/audit/ -F perm=r -F auid>=1000 -F auid!=unset -F key=access-audit-trail +If the auditd daemon is configured to use the augenrules +program to read audit rules during daemon startup (the default), add the +rule to a file with suffix .rules in the directory +/etc/audit/rules.d. +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the rule to +/etc/audit/audit.rules file. + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + FAU_GEN.1.1.c + Attempts to read the logs should be recorded, suspicious access to audit log files could be an indicator of malicious activity on a system. +Auditing these events could serve as evidence of potential system compromise.' + + CCE-80941-8 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80941-8 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - directory_access_var_log_audit + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Perform remediation of Audit rules for /var/log/audit + block: + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + dir=/var/log/audit/ -F perm=r -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access-audit-trail.rules + set_fact: audit_file="/etc/audit/rules.d/access-audit-trail.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F dir=/var/log/audit/ -F perm=r -F + auid>=1000 -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F dir=/var/log/audit/ -F perm=r + -F auid>=1000 -F auid!=unset -F key=access-audit-trail + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + dir=/var/log/audit/ -F perm=r -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:( + -S |,)\w+)+)( -F dir=/var/log/audit/ -F perm=r -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F dir=/var/log/audit/ -F perm=r + -F auid>=1000 -F auid!=unset -F key=access-audit-trail + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80941-8 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - directory_access_var_log_audit + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +ACTION_ARCH_FILTERS="-a always,exit" +OTHER_FILTERS="-F dir=/var/log/audit/ -F perm=r" +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="" +KEY="access-audit-trail" +SYSCALL_GROUPING="" +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + System Audit Directories Must Be Group Owned By Root + All audit directories must be group owned by root user. By default, the path for audit log is /var/log/audit/. + +To properly set the group owner of /var/log/audit, run the command: +$ sudo chgrp root /var/log/audit + +If log_group in /etc/audit/auditd.conf is set to a group other than the root +group account, change the group ownership of the audit directories to this specific group. + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 18 + 19 + 3 + 4 + 5 + 6 + 7 + 8 + 5.4.1.1 + APO01.06 + APO11.04 + APO12.06 + BAI03.05 + BAI08.02 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS05.04 + DSS05.07 + DSS06.02 + MEA02.01 + 3.3.1 + CCI-000162 + CCI-000163 + CCI-000164 + CCI-001314 + 4.2.3.10 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.7.3 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.1 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 5.2 + SR 6.1 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-6(a) + AC-6(1) + AU-9(4) + DE.AE-3 + DE.AE-5 + PR.AC-4 + PR.DS-5 + PR.PT-1 + RS.AN-1 + RS.AN-4 + Req-10.5.1 + SRG-OS-000057-GPOS-00027 + SRG-OS-000058-GPOS-00028 + SRG-OS-000059-GPOS-00029 + SRG-OS-000206-GPOS-00084 + RHEL-08-030110 + SV-230400r627750_rule + Unauthorized disclosure of audit records can reveal system and configuration data to +attackers, thus compromising its confidentiality. + CCE-88225-8 + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +if LC_ALL=C grep -m 1 -q ^log_group /etc/audit/auditd.conf; then + GROUP=$(awk -F "=" '/log_group/ {print $2}' /etc/audit/auditd.conf | tr -d ' ') +else + GROUP=root +fi +if LC_ALL=C grep -iw ^log_file /etc/audit/auditd.conf; then + DIR=$(awk -F "=" '/^log_file/ {print $2}' /etc/audit/auditd.conf | tr -d ' ' | rev | cut -d"/" -f2- | rev) +else + DIR="/var/log/audit" +fi + + +find ${DIR} -type d -exec chgrp ${GROUP} {} \; + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + System Audit Directories Must Be Owned By Root + All audit directories must be owned by root user. By default, the path for audit log is /var/log/audit/. + +To properly set the owner of /var/log/audit, run the command: +$ sudo chown root /var/log/audit + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 18 + 19 + 3 + 4 + 5 + 6 + 7 + 8 + 5.4.1.1 + APO01.06 + APO11.04 + APO12.06 + BAI03.05 + BAI08.02 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS05.04 + DSS05.07 + DSS06.02 + MEA02.01 + 3.3.1 + CCI-000162 + CCI-000163 + CCI-000164 + CCI-001314 + 4.2.3.10 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.7.3 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.1 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 5.2 + SR 6.1 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-6(a) + AC-6(1) + AU-9(4) + DE.AE-3 + DE.AE-5 + PR.AC-4 + PR.DS-5 + PR.PT-1 + RS.AN-1 + RS.AN-4 + Req-10.5.1 + SRG-OS-000057-GPOS-00027 + SRG-OS-000058-GPOS-00028 + SRG-OS-000059-GPOS-00029 + SRG-OS-000206-GPOS-00084 + RHEL-08-030100 + SV-230399r627750_rule + Unauthorized disclosure of audit records can reveal system and configuration data to +attackers, thus compromising its confidentiality. + CCE-88226-6 + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +if LC_ALL=C grep -iw ^log_file /etc/audit/auditd.conf; then + FILE=$(awk -F "=" '/^log_file/ {print $2}' /etc/audit/auditd.conf | tr -d ' ') + LOGPATH="$(dirname "$FILE")" + chown root $LOGPATH +else + chown root /var/log/audit +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + System Audit Logs Must Have Mode 0750 or Less Permissive + +Verify the audit log directories have a mode of "0700" or less permissive by first determining +where the audit logs are stored with the following command: +$ sudo grep -iw log_file /etc/audit/auditd.conf + +log_file = /var/log/audit/audit.log +Configure the audit log directory to be protected from unauthorized read access by setting the +correct permissive mode with the following command: +$ sudo chmod 0700 audit_log_directory +By default, audit_log_directory is "/var/log/audit". + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 18 + 19 + 3 + 4 + 5 + 6 + 7 + 8 + APO01.06 + APO11.04 + APO12.06 + BAI03.05 + BAI08.02 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS05.04 + DSS05.07 + DSS06.02 + MEA02.01 + CCI-000162 + CCI-000163 + CCI-000164 + 4.2.3.10 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.7.3 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.1 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 5.2 + SR 6.1 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.2 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-004-6 R3.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CIP-007-3 R6.5 + CM-6(a) + AC-6(1) + AU-9 + DE.AE-3 + DE.AE-5 + PR.AC-4 + PR.DS-5 + PR.PT-1 + RS.AN-1 + RS.AN-4 + SRG-OS-000057-GPOS-00027 + SRG-OS-000058-GPOS-00028 + SRG-OS-000059-GPOS-00029 + RHEL-08-030120 + SV-230401r627750_rule + If users can write to audit logs, audit trails can be modified or destroyed. + CCE-84048-8 + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +if LC_ALL=C grep -iw ^log_file /etc/audit/auditd.conf; then + DIR=$(awk -F "=" '/^log_file/ {print $2}' /etc/audit/auditd.conf | tr -d ' ' | rev | cut -d"/" -f2- | rev) +else + DIR="/var/log/audit" +fi + + +chmod 0700 $DIR + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + System Audit Logs Must Be Group Owned By Root + All audit logs must be group owned by root user. The path for audit log can +be configured via log_file parameter in /etc/audit/auditd.conf +or, by default, the path for audit log is /var/log/audit/. + +To properly set the group owner of /var/log/audit/*, run the command: +$ sudo chgrp root /var/log/audit/* + +If log_group in /etc/audit/auditd.conf is set to a group other +than the root group account, change the group ownership of the audit logs +to this specific group. + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 18 + 19 + 3 + 4 + 5 + 6 + 7 + 8 + 5.4.1.1 + APO01.06 + APO11.04 + APO12.06 + BAI03.05 + BAI08.02 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS05.04 + DSS05.07 + DSS06.02 + MEA02.01 + 3.3.1 + CCI-000162 + CCI-000163 + CCI-000164 + CCI-001314 + 4.2.3.10 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.7.3 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.1 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 5.2 + SR 6.1 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-6(a) + AC-6(1) + AU-9(4) + DE.AE-3 + DE.AE-5 + PR.AC-4 + PR.DS-5 + PR.PT-1 + RS.AN-1 + RS.AN-4 + Req-10.5.1 + SRG-OS-000057-GPOS-00027 + SRG-OS-000058-GPOS-00028 + SRG-OS-000059-GPOS-00029 + SRG-OS-000206-GPOS-00084 + RHEL-08-030090 + SV-230398r627750_rule + Unauthorized disclosure of audit records can reveal system and configuration data to +attackers, thus compromising its confidentiality. + CCE-88227-4 + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +if LC_ALL=C grep -iw log_file /etc/audit/auditd.conf; then + FILE=$(awk -F "=" '/^log_file/ {print $2}' /etc/audit/auditd.conf | tr -d ' ') +else + FILE="/var/log/audit/audit.log" +fi + + +if LC_ALL=C grep -m 1 -q ^log_group /etc/audit/auditd.conf; then + GROUP=$(awk -F "=" '/log_group/ {print $2}' /etc/audit/auditd.conf | tr -d ' ') + if ! [ "${GROUP}" == 'root' ]; then + chgrp ${GROUP} $FILE* + else + chgrp root $FILE* + fi +else + chgrp root $FILE* +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Audit Configuration Files Must Be Owned By Group root + All audit configuration files must be owned by group root. +chown :root /etc/audit/audit*.{rules,conf} /etc/audit/rules.d/* + CCI-000171 + SRG-OS-000063-GPOS-00032 + Without the capability to restrict which roles and individuals can +select which events are audited, unauthorized personnel may be able +to prevent the auditing of critical events. +Misconfigured audits may degrade the system's performance by +overwhelming the audit log. Misconfigured audits may also make it more +difficult to establish, correlate, and investigate the events relating +to an incident or identify those responsible for one. + - name: Gather the package facts + package_facts: + manager: auto + tags: + - configure_strategy + - file_groupownership_audit_configuration + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Find /etc/audit/ file(s) matching ^audit(\.rules|d\.conf)$ + command: find -H /etc/audit/ -maxdepth 1 -type f ! -gid 0 -regex "^audit(\.rules|d\.conf)$" + register: files_found + changed_when: false + failed_when: false + check_mode: false + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - configure_strategy + - file_groupownership_audit_configuration + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure group owner on /etc/audit/ file(s) matching ^audit(\.rules|d\.conf)$ + file: + path: '{{ item }}' + group: '0' + state: file + with_items: + - '{{ files_found.stdout_lines }}' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - configure_strategy + - file_groupownership_audit_configuration + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Find /etc/audit/rules.d/ file(s) matching ^.*\.rules$ + command: find -H /etc/audit/rules.d/ -maxdepth 1 -type f ! -gid 0 -regex "^.*\.rules$" + register: files_found + changed_when: false + failed_when: false + check_mode: false + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - configure_strategy + - file_groupownership_audit_configuration + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure group owner on /etc/audit/rules.d/ file(s) matching ^.*\.rules$ + file: + path: '{{ item }}' + group: '0' + state: file + with_items: + - '{{ files_found.stdout_lines }}' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - configure_strategy + - file_groupownership_audit_configuration + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +find /etc/audit/ -maxdepth 1 -type f ! -gid 0 -regex '^audit(\.rules|d\.conf)$' -exec chgrp 0 {} \; + + +find /etc/audit/rules.d/ -maxdepth 1 -type f ! -gid 0 -regex '^.*\.rules$' -exec chgrp 0 {} \; + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Audit Configuration Files Must Be Owned By Root + All audit configuration files must be owned by root user. + +To properly set the owner of /etc/audit/, run the command: +$ sudo chown root /etc/audit/ + +To properly set the owner of /etc/audit/rules.d/, run the command: +$ sudo chown root /etc/audit/rules.d/ + CCI-000171 + SRG-OS-000063-GPOS-00032 + Without the capability to restrict which roles and individuals can +select which events are audited, unauthorized personnel may be able +to prevent the auditing of critical events. +Misconfigured audits may degrade the system's performance by +overwhelming the audit log. Misconfigured audits may also make it more +difficult to establish, correlate, and investigate the events relating +to an incident or identify those responsible for one. + - name: Gather the package facts + package_facts: + manager: auto + tags: + - configure_strategy + - file_ownership_audit_configuration + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Find /etc/audit/ file(s) matching ^audit(\.rules|d\.conf)$ + command: find -H /etc/audit/ -maxdepth 1 -type f ! -uid 0 -regex "^audit(\.rules|d\.conf)$" + register: files_found + changed_when: false + failed_when: false + check_mode: false + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - configure_strategy + - file_ownership_audit_configuration + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure owner on /etc/audit/ file(s) matching ^audit(\.rules|d\.conf)$ + file: + path: '{{ item }}' + owner: '0' + state: file + with_items: + - '{{ files_found.stdout_lines }}' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - configure_strategy + - file_ownership_audit_configuration + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Find /etc/audit/rules.d/ file(s) matching ^.*\.rules$ + command: find -H /etc/audit/rules.d/ -maxdepth 1 -type f ! -uid 0 -regex "^.*\.rules$" + register: files_found + changed_when: false + failed_when: false + check_mode: false + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - configure_strategy + - file_ownership_audit_configuration + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure owner on /etc/audit/rules.d/ file(s) matching ^.*\.rules$ + file: + path: '{{ item }}' + owner: '0' + state: file + with_items: + - '{{ files_found.stdout_lines }}' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - configure_strategy + - file_ownership_audit_configuration + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +find /etc/audit/ -maxdepth 1 -type f ! -uid 0 -regex '^audit(\.rules|d\.conf)$' -exec chown 0 {} \; + +find /etc/audit/rules.d/ -maxdepth 1 -type f ! -uid 0 -regex '^.*\.rules$' -exec chown 0 {} \; + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + System Audit Logs Must Be Owned By Root + All audit logs must be owned by root user and group. By default, the path for audit log is /var/log/audit/. + +To properly set the owner of /var/log/audit, run the command: +$ sudo chown root /var/log/audit + +To properly set the owner of /var/log/audit/*, run the command: +$ sudo chown root /var/log/audit/* + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 18 + 19 + 3 + 4 + 5 + 6 + 7 + 8 + 5.4.1.1 + APO01.06 + APO11.04 + APO12.06 + BAI03.05 + BAI08.02 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS05.04 + DSS05.07 + DSS06.02 + MEA02.01 + 3.3.1 + CCI-000162 + CCI-000163 + CCI-000164 + CCI-001314 + 4.2.3.10 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.7.3 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.1 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 5.2 + SR 6.1 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-6(a) + AC-6(1) + AU-9(4) + DE.AE-3 + DE.AE-5 + PR.AC-4 + PR.DS-5 + PR.PT-1 + RS.AN-1 + RS.AN-4 + Req-10.5.1 + SRG-OS-000057-GPOS-00027 + SRG-OS-000058-GPOS-00028 + SRG-OS-000059-GPOS-00029 + Unauthorized disclosure of audit records can reveal system and configuration data to +attackers, thus compromising its confidentiality. + CCE-80808-9 + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +if LC_ALL=C grep -m 1 -q ^log_group /etc/audit/auditd.conf; then + GROUP=$(awk -F "=" '/log_group/ {print $2}' /etc/audit/auditd.conf | tr -d ' ') + if ! [ "${GROUP}" == 'root' ] ; then + chown root:${GROUP} /var/log/audit + chown root:${GROUP} /var/log/audit/audit.log* + else + chown root:root /var/log/audit + chown root:root /var/log/audit/audit.log* + fi +else + chown root:root /var/log/audit + chown root:root /var/log/audit/audit.log* +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + System Audit Logs Must Be Owned By Root + All audit logs must be owned by root user. The path for audit log can be +configured via log_file parameter in /etc/audit/auditd.conf +or by default, the path for audit log is /var/log/audit/. + +To properly set the owner of /var/log/audit/*, run the command: +$ sudo chown root /var/log/audit/* + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 18 + 19 + 3 + 4 + 5 + 6 + 7 + 8 + 5.4.1.1 + APO01.06 + APO11.04 + APO12.06 + BAI03.05 + BAI08.02 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS05.04 + DSS05.07 + DSS06.02 + MEA02.01 + 3.3.1 + CCI-000162 + CCI-000163 + CCI-000164 + CCI-001314 + 4.2.3.10 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.7.3 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.1 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 5.2 + SR 6.1 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-6(a) + AC-6(1) + AU-9(4) + DE.AE-3 + DE.AE-5 + PR.AC-4 + PR.DS-5 + PR.PT-1 + RS.AN-1 + RS.AN-4 + Req-10.5.1 + SRG-OS-000057-GPOS-00027 + SRG-OS-000058-GPOS-00028 + SRG-OS-000059-GPOS-00029 + SRG-OS-000206-GPOS-00084 + RHEL-08-030080 + SV-230397r627750_rule + Unauthorized disclosure of audit records can reveal system and configuration data to +attackers, thus compromising its confidentiality. + CCE-88228-2 + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +if LC_ALL=C grep -iw log_file /etc/audit/auditd.conf; then + FILE=$(awk -F "=" '/^log_file/ {print $2}' /etc/audit/auditd.conf | tr -d ' ') + chown root $FILE* +else + chown root /var/log/audit/audit.log* +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + System Audit Logs Must Have Mode 0640 or Less Permissive + +Determine where the audit logs are stored with the following command: +$ sudo grep -iw log_file /etc/audit/auditd.conf +log_file = /var/log/audit/audit.log +Configure the audit log to be protected from unauthorized read access by setting the correct +permissive mode with the following command: +$ sudo chmod 0600 audit_log_file +By default, audit_log_file is "/var/log/audit/audit.log". + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 18 + 19 + 3 + 4 + 5 + 6 + 7 + 8 + 5.4.1.1 + APO01.06 + APO11.04 + APO12.06 + BAI03.05 + BAI08.02 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS05.04 + DSS05.07 + DSS06.02 + MEA02.01 + 3.3.1 + CCI-000162 + CCI-000163 + CCI-000164 + CCI-001314 + 4.2.3.10 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.7.3 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.1 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 5.2 + SR 6.1 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-6(a) + AC-6(1) + AU-9(4) + DE.AE-3 + DE.AE-5 + PR.AC-4 + PR.DS-5 + PR.PT-1 + RS.AN-1 + RS.AN-4 + Req-10.5 + SRG-OS-000057-GPOS-00027 + SRG-OS-000058-GPOS-00028 + SRG-OS-000059-GPOS-00029 + SRG-OS-000206-GPOS-00084 + RHEL-08-030070 + SV-230396r627750_rule + If users can write to audit logs, audit trails can be modified or destroyed. + CCE-80819-6 + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +if LC_ALL=C grep -iw ^log_file /etc/audit/auditd.conf; then + FILE=$(awk -F "=" '/^log_file/ {print $2}' /etc/audit/auditd.conf | tr -d ' ') +else + FILE="/var/log/audit/audit.log" +fi + + +chmod 0600 $FILE + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Events that Modify the System's Discretionary Access Controls + At a minimum, the audit system should collect file permission +changes for all users and root. Note that the "-F arch=b32" lines should be +present even on a 64 bit system. These commands identify system calls for +auditing. Even if the system is 64 bit it can still execute 32 bit system +calls. Additionally, these rules can be configured in a number of ways while +still achieving the desired effect. An example of this is that the "-S" calls +could be split up and placed on separate lines, however, this is less efficient. +Add the following to /etc/audit/audit.rules: +-a always,exit -F arch=b32 -S chmod,fchmod,fchmodat -F auid>=1000 -F auid!=unset -F key=perm_mod + -a always,exit -F arch=b32 -S chown,fchown,fchownat,lchown -F auid>=1000 -F auid!=unset -F key=perm_mod + -a always,exit -F arch=b32 -S setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F auid>=1000 -F auid!=unset -F key=perm_mod +If your system is 64 bit then these lines should be duplicated and the +arch=b32 replaced with arch=b64 as follows: +-a always,exit -F arch=b64 -S chmod,fchmod,fchmodat -F auid>=1000 -F auid!=unset -F key=perm_mod + -a always,exit -F arch=b64 -S chown,fchown,fchownat,lchown -F auid>=1000 -F auid!=unset -F key=perm_mod + -a always,exit -F arch=b64 -S setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F auid>=1000 -F auid!=unset -F key=perm_mod + + Record Events that Modify the System's Discretionary Access Controls - chmod + At a minimum, the audit system should collect file permission +changes for all users and root. If the auditd daemon is configured to +use the augenrules program to read audit rules during daemon startup +(the default), add the following line to a file with suffix .rules in +the directory /etc/audit/rules.d: +-a always,exit -F arch=b32 -S chmod -F auid>=1000 -F auid!=unset -F key=perm_mod +If the system is 64 bit then also add the following line: +-a always,exit -F arch=b64 -S chmod -F auid>=1000 -F auid!=unset -F key=perm_mod +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following line to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S chmod -F auid>=1000 -F auid!=unset -F key=perm_mod +If the system is 64 bit then also add the following line: +-a always,exit -F arch=b64 -S chmod -F auid>=1000 -F auid!=unset -F key=perm_mod + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping these system +calls with others as identifying earlier in this guide is more efficient. + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 5.4.1.1 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000126 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.5.5 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + SRG-OS-000064-GPOS-00033 + SRG-OS-000466-GPOS-00210 + SRG-OS-000458-GPOS-00203 + SRG-OS-000458-VMM-001810 + SRG-OS-000474-VMM-001940 + RHEL-08-030490 + 4.1.3.9 + SV-230456r810462_rule + The changing of file permissions could indicate that a user is attempting to +gain access to information that would otherwise be disallowed. Auditing DAC modifications +can facilitate the identification of patterns of abuse among both authorized and +unauthorized users. + CCE-80685-1 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80685-1 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030490 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_chmod + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit chmod tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80685-1 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030490 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_chmod + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for chmod for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - chmod + syscall_grouping: + - chmod + - fchmod + - fchmodat + + - name: Check existence of chmod in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules + set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - chmod + syscall_grouping: + - chmod + - fchmod + - fchmodat + + - name: Check existence of chmod in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80685-1 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030490 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_chmod + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for chmod for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - chmod + syscall_grouping: + - chmod + - fchmod + - fchmodat + + - name: Check existence of chmod in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules + set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - chmod + syscall_grouping: + - chmod + - fchmod + - fchmodat + + - name: Check existence of chmod in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-80685-1 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030490 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_chmod + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="" + AUID_FILTERS="-F auid>=1000 -F auid!=unset" + SYSCALL="chmod" + KEY="perm_mod" + SYSCALL_GROUPING="chmod fchmod fchmodat" + + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Events that Modify the System's Discretionary Access Controls - chown + At a minimum, the audit system should collect file permission +changes for all users and root. If the auditd daemon is configured to +use the augenrules program to read audit rules during daemon startup +(the default), add the following line to a file with suffix .rules in +the directory /etc/audit/rules.d: +-a always,exit -F arch=b32 -S chown -F auid>=1000 -F auid!=unset -F key=perm_mod +If the system is 64 bit then also add the following line: +-a always,exit -F arch=b64 -S chown -F auid>=1000 -F auid!=unset -F key=perm_mod +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following line to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S chown -F auid>=1000 -F auid!=unset -F key=perm_mod +If the system is 64 bit then also add the following line: +-a always,exit -F arch=b64 -S chown -F auid>=1000 -F auid!=unset -F key=perm_mod + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping these system +calls with others as identifying earlier in this guide is more efficient. + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 5.4.1.1 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000126 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.5.5 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + SRG-OS-000064-GPOS-00033 + SRG-OS-000466-GPOS-00210 + SRG-OS-000458-GPOS-00203 + SRG-OS-000474-GPOS-00219 + SRG-OS-000458-VMM-001810 + SRG-OS-000474-VMM-001940 + RHEL-08-030480 + 4.1.3.9 + SV-230455r810459_rule + The changing of file permissions could indicate that a user is attempting to +gain access to information that would otherwise be disallowed. Auditing DAC modifications +can facilitate the identification of patterns of abuse among both authorized and +unauthorized users. + CCE-80686-9 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80686-9 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030480 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_chown + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit chown tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80686-9 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030480 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_chown + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for chown for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - chown + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of chown in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules + set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - chown + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of chown in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80686-9 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030480 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_chown + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for chown for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - chown + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of chown in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules + set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - chown + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of chown in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-80686-9 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030480 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_chown + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="" + AUID_FILTERS="-F auid>=1000 -F auid!=unset" + SYSCALL="chown" + KEY="perm_mod" + SYSCALL_GROUPING="chown fchown fchownat lchown" + + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Events that Modify the System's Discretionary Access Controls - fchmod + At a minimum, the audit system should collect file permission +changes for all users and root. If the auditd daemon is configured to +use the augenrules program to read audit rules during daemon startup +(the default), add the following line to a file with suffix .rules in +the directory /etc/audit/rules.d: +-a always,exit -F arch=b32 -S fchmod -F auid>=1000 -F auid!=unset -F key=perm_mod +If the system is 64 bit then also add the following line: +-a always,exit -F arch=b64 -S fchmod -F auid>=1000 -F auid!=unset -F key=perm_mod +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following line to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S fchmod -F auid>=1000 -F auid!=unset -F key=perm_mod +If the system is 64 bit then also add the following line: +-a always,exit -F arch=b64 -S fchmod -F auid>=1000 -F auid!=unset -F key=perm_mod + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping these system +calls with others as identifying earlier in this guide is more efficient. + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 5.4.1.1 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000126 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.5.5 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + SRG-OS-000064-GPOS-00033 + SRG-OS-000466-GPOS-00210 + SRG-OS-000458-GPOS-00203 + SRG-OS-000458-VMM-001810 + SRG-OS-000474-VMM-001940 + RHEL-08-030490 + 4.1.3.9 + SV-230456r810462_rule + The changing of file permissions could indicate that a user is attempting to +gain access to information that would otherwise be disallowed. Auditing DAC modifications +can facilitate the identification of patterns of abuse among both authorized and +unauthorized users. + CCE-80687-7 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80687-7 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030490 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_fchmod + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit fchmod tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80687-7 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030490 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_fchmod + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for fchmod for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchmod + syscall_grouping: + - chmod + - fchmod + - fchmodat + + - name: Check existence of fchmod in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules + set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchmod + syscall_grouping: + - chmod + - fchmod + - fchmodat + + - name: Check existence of fchmod in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80687-7 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030490 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_fchmod + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for fchmod for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchmod + syscall_grouping: + - chmod + - fchmod + - fchmodat + + - name: Check existence of fchmod in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules + set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchmod + syscall_grouping: + - chmod + - fchmod + - fchmodat + + - name: Check existence of fchmod in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-80687-7 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030490 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_fchmod + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="" + AUID_FILTERS="-F auid>=1000 -F auid!=unset" + SYSCALL="fchmod" + KEY="perm_mod" + SYSCALL_GROUPING="chmod fchmod fchmodat" + + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Events that Modify the System's Discretionary Access Controls - fchmodat + At a minimum, the audit system should collect file permission +changes for all users and root. If the auditd daemon is configured to +use the augenrules program to read audit rules during daemon startup +(the default), add the following line to a file with suffix .rules in +the directory /etc/audit/rules.d: +-a always,exit -F arch=b32 -S fchmodat -F auid>=1000 -F auid!=unset -F key=perm_mod +If the system is 64 bit then also add the following line: +-a always,exit -F arch=b64 -S fchmodat -F auid>=1000 -F auid!=unset -F key=perm_mod +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following line to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S fchmodat -F auid>=1000 -F auid!=unset -F key=perm_mod +If the system is 64 bit then also add the following line: +-a always,exit -F arch=b64 -S fchmodat -F auid>=1000 -F auid!=unset -F key=perm_mod + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping these system +calls with others as identifying earlier in this guide is more efficient. + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 5.4.1.1 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000126 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.5.5 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + SRG-OS-000064-GPOS-00033 + SRG-OS-000466-GPOS-00210 + SRG-OS-000458-GPOS-00203 + SRG-OS-000458-VMM-001810 + SRG-OS-000474-VMM-001940 + RHEL-08-030490 + 4.1.3.9 + SV-230456r810462_rule + The changing of file permissions could indicate that a user is attempting to +gain access to information that would otherwise be disallowed. Auditing DAC modifications +can facilitate the identification of patterns of abuse among both authorized and +unauthorized users. + CCE-80688-5 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80688-5 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030490 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_fchmodat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit fchmodat tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80688-5 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030490 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_fchmodat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for fchmodat for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchmodat + syscall_grouping: + - chmod + - fchmod + - fchmodat + + - name: Check existence of fchmodat in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules + set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchmodat + syscall_grouping: + - chmod + - fchmod + - fchmodat + + - name: Check existence of fchmodat in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80688-5 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030490 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_fchmodat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for fchmodat for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchmodat + syscall_grouping: + - chmod + - fchmod + - fchmodat + + - name: Check existence of fchmodat in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules + set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchmodat + syscall_grouping: + - chmod + - fchmod + - fchmodat + + - name: Check existence of fchmodat in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-80688-5 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030490 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_fchmodat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="" + AUID_FILTERS="-F auid>=1000 -F auid!=unset" + SYSCALL="fchmodat" + KEY="perm_mod" + SYSCALL_GROUPING="chmod fchmod fchmodat" + + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Events that Modify the System's Discretionary Access Controls - fchown + At a minimum, the audit system should collect file permission +changes for all users and root. If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following line to a file with suffix +.rules in the directory /etc/audit/rules.d: +-a always,exit -F arch=b32 -S fchown -F auid>=1000 -F auid!=unset -F key=perm_mod + +If the system is 64 bit then also add the following line: +-a always,exit -F arch=b64 -S fchown -F auid>=1000 -F auid!=unset -F key=perm_mod + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following line to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S fchown -F auid>=1000 -F auid!=unset -F key=perm_mod + +If the system is 64 bit then also add the following line: +-a always,exit -F arch=b64 -S fchown -F auid>=1000 -F auid!=unset -F key=perm_mod + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping these system +calls with others as identifying earlier in this guide is more efficient. + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 5.4.1.1 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000126 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.5.5 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + SRG-OS-000064-GPOS-00033 + SRG-OS-000466-GPOS-00210 + SRG-OS-000458-GPOS-00203 + SRG-OS-000474-GPOS-00219 + SRG-OS-000458-VMM-001810 + SRG-OS-000474-VMM-001940 + RHEL-08-030480 + 4.1.3.9 + SV-230455r810459_rule + The changing of file permissions could indicate that a user is attempting to +gain access to information that would otherwise be disallowed. Auditing DAC modifications +can facilitate the identification of patterns of abuse among both authorized and +unauthorized users. + CCE-80689-3 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80689-3 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030480 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_fchown + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit fchown tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80689-3 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030480 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_fchown + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for fchown for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchown + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of fchown in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules + set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchown + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of fchown in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80689-3 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030480 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_fchown + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for fchown for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchown + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of fchown in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules + set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchown + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of fchown in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-80689-3 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030480 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_fchown + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="" + AUID_FILTERS="-F auid>=1000 -F auid!=unset" + SYSCALL="fchown" + KEY="perm_mod" + SYSCALL_GROUPING="chown fchown fchownat lchown" + + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Events that Modify the System's Discretionary Access Controls - fchownat + At a minimum, the audit system should collect file permission +changes for all users and root. If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following line to a file with suffix +.rules in the directory /etc/audit/rules.d: +-a always,exit -F arch=b32 -S fchownat -F auid>=1000 -F auid!=unset -F key=perm_mod +If the system is 64 bit then also add the following line: +-a always,exit -F arch=b64 -S fchownat -F auid>=1000 -F auid!=unset -F key=perm_mod +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following line to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S fchownat -F auid>=1000 -F auid!=unset -F key=perm_mod +If the system is 64 bit then also add the following line: +-a always,exit -F arch=b64 -S fchownat -F auid>=1000 -F auid!=unset -F key=perm_mod + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping these system +calls with others as identifying earlier in this guide is more efficient. + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 5.4.1.1 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000126 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.5.5 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + SRG-OS-000064-GPOS-00033 + SRG-OS-000466-GPOS-00210 + SRG-OS-000458-GPOS-00203 + SRG-OS-000474-GPOS-00219 + SRG-OS-000458-VMM-001810 + SRG-OS-000474-VMM-001940 + RHEL-08-030480 + 4.1.3.9 + SV-230455r810459_rule + The changing of file permissions could indicate that a user is attempting to +gain access to information that would otherwise be disallowed. Auditing DAC modifications +can facilitate the identification of patterns of abuse among both authorized and +unauthorized users. + CCE-80690-1 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80690-1 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030480 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_fchownat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit fchownat tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80690-1 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030480 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_fchownat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for fchownat for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchownat + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of fchownat in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules + set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchownat + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of fchownat in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80690-1 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030480 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_fchownat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for fchownat for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchownat + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of fchownat in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules + set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchownat + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of fchownat in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-80690-1 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030480 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_fchownat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="" + AUID_FILTERS="-F auid>=1000 -F auid!=unset" + SYSCALL="fchownat" + KEY="perm_mod" + SYSCALL_GROUPING="chown fchown fchownat lchown" + + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Events that Modify the System's Discretionary Access Controls - fremovexattr + At a minimum, the audit system should collect file permission +changes for all users and root. + +If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following line to a file with suffix +.rules in the directory /etc/audit/rules.d: +-a always,exit -F arch=b32 -S fremovexattr -F auid>=1000 -F auid!=unset -F key=perm_mod +-a always,exit -F arch=b32 -S fremovexattr -F auid=0 -F key=perm_mod + +If the system is 64 bit then also add the following line: +-a always,exit -F arch=b64 -S fremovexattr -F auid>=1000 -F auid!=unset -F key=perm_mod +-a always,exit -F arch=b64 -S fremovexattr -F auid=0 -F key=perm_mod + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following line to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S fremovexattr -F auid>=1000 -F auid!=unset -F key=perm_mod +-a always,exit -F arch=b32 -S fremovexattr -F auid=0 -F key=perm_mod + +If the system is 64 bit then also add the following line: +-a always,exit -F arch=b64 -S fremovexattr -F auid>=1000 -F auid!=unset -F key=perm_mod +-a always,exit -F arch=b64 -S fremovexattr -F auid=0 -F key=perm_mod + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping these system +calls with others as identifying earlier in this guide is more efficient. + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 5.4.1.1 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.5.5 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000458-GPOS-00203 + SRG-OS-000462-GPOS-00206 + SRG-OS-000463-GPOS-00207 + SRG-OS-000471-GPOS-00215 + SRG-OS-000474-GPOS-00219 + SRG-OS-000466-GPOS-00210 + SRG-OS-000468-GPOS-00212 + SRG-OS-000064-GPOS-00033 + SRG-OS-000458-VMM-001810 + SRG-OS-000474-VMM-001940 + RHEL-08-030200 + 4.1.3.9 + SV-230413r810463_rule + The changing of file permissions could indicate that a user is attempting to +gain access to information that would otherwise be disallowed. Auditing DAC modifications +can facilitate the identification of patterns of abuse among both authorized and +unauthorized users. + CCE-80691-9 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80691-9 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030200 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_fremovexattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit fremovexattr tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80691-9 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030200 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_fremovexattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for fremovexattr for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - fremovexattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of fremovexattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules + set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - fremovexattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of fremovexattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - fremovexattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of fremovexattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid=0 (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules + set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid=0 (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid=0 -F + key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - fremovexattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of fremovexattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid=0 (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid=0 (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid=0 -F + key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80691-9 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030200 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_fremovexattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for fremovexattr for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - fremovexattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of fremovexattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules + set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - fremovexattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of fremovexattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - fremovexattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of fremovexattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid=0 (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules + set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid=0 (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid=0 -F + key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - fremovexattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of fremovexattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid=0 (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid=0 (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid=0 -F + key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-80691-9 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030200 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_fremovexattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="" + AUID_FILTERS="-F auid>=1000 -F auid!=unset" + SYSCALL="fremovexattr" + KEY="perm_mod" + SYSCALL_GROUPING="fremovexattr lremovexattr removexattr fsetxattr lsetxattr setxattr" + + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + + + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="" + AUID_FILTERS="-F auid=0" + SYSCALL="fremovexattr" + KEY="perm_mod" + SYSCALL_GROUPING="fremovexattr lremovexattr removexattr fsetxattr lsetxattr setxattr" + + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Events that Modify the System's Discretionary Access Controls - fsetxattr + At a minimum, the audit system should collect file permission +changes for all users and root. If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following line to a file with suffix +.rules in the directory /etc/audit/rules.d: +-a always,exit -F arch=b32 -S fsetxattr -F auid>=1000 -F auid!=unset -F key=perm_mod +-a always,exit -F arch=b32 -S fsetxattr -F auid=0 -F key=perm_mod +If the system is 64 bit then also add the following line: +-a always,exit -F arch=b64 -S fsetxattr -F auid>=1000 -F auid!=unset -F key=perm_mod +-a always,exit -F arch=b64 -S fsetxattr -F auid=0 -F key=perm_mod +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following line to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S fsetxattr -F auid>=1000 -F auid!=unset -F key=perm_mod +-a always,exit -F arch=b32 -S fsetxattr -F auid=0 -F key=perm_mod +If the system is 64 bit then also add the following line: +-a always,exit -F arch=b64 -S fsetxattr -F auid>=1000 -F auid!=unset -F key=perm_mod +-a always,exit -F arch=b64 -S fsetxattr -F auid=0 -F key=perm_mod + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping these system +calls with others as identifying earlier in this guide is more efficient. + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 5.4.1.1 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000126 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.5.5 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000458-GPOS-00203 + SRG-OS-000462-GPOS-00206 + SRG-OS-000463-GPOS-00207 + SRG-OS-000466-GPOS-00210 + SRG-OS-000468-GPOS-00212 + SRG-OS-000471-GPOS-00215 + SRG-OS-000474-GPOS-00219 + SRG-OS-000064-GPOS-00033 + SRG-OS-000458-VMM-001810 + SRG-OS-000474-VMM-001940 + RHEL-08-030200 + 4.1.3.9 + SV-230413r810463_rule + The changing of file permissions could indicate that a user is attempting to +gain access to information that would otherwise be disallowed. Auditing DAC modifications +can facilitate the identification of patterns of abuse among both authorized and +unauthorized users. + CCE-80692-7 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80692-7 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030200 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_fsetxattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit fsetxattr tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80692-7 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030200 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_fsetxattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for fsetxattr for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - fsetxattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of fsetxattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules + set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - fsetxattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of fsetxattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - fsetxattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of fsetxattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid=0 (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules + set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid=0 (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid=0 -F + key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - fsetxattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of fsetxattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid=0 (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid=0 (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid=0 -F + key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80692-7 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030200 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_fsetxattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for fsetxattr for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - fsetxattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of fsetxattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules + set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - fsetxattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of fsetxattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - fsetxattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of fsetxattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid=0 (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules + set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid=0 (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid=0 -F + key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - fsetxattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of fsetxattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid=0 (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid=0 (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid=0 -F + key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-80692-7 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030200 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_fsetxattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="" + AUID_FILTERS="-F auid>=1000 -F auid!=unset" + SYSCALL="fsetxattr" + KEY="perm_mod" + SYSCALL_GROUPING="fremovexattr lremovexattr removexattr fsetxattr lsetxattr setxattr" + + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + + + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="" + AUID_FILTERS="-F auid=0" + SYSCALL="fsetxattr" + KEY="perm_mod" + SYSCALL_GROUPING="fremovexattr lremovexattr removexattr fsetxattr lsetxattr setxattr" + + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Events that Modify the System's Discretionary Access Controls - lchown + At a minimum, the audit system should collect file permission +changes for all users and root. If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following line to a file with suffix +.rules in the directory /etc/audit/rules.d: +-a always,exit -F arch=b32 -S lchown -F auid>=1000 -F auid!=unset -F key=perm_mod +If the system is 64 bit then also add the following line: +-a always,exit -F arch=b64 -S lchown -F auid>=1000 -F auid!=unset -F key=perm_mod +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following line to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S lchown -F auid>=1000 -F auid!=unset -F key=perm_mod +If the system is 64 bit then also add the following line: +-a always,exit -F arch=b64 -S lchown -F auid>=1000 -F auid!=unset -F key=perm_mod + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping these system +calls with others as identifying earlier in this guide is more efficient. + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 5.4.1.1 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000126 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.5.5 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + SRG-OS-000064-GPOS-00033 + SRG-OS-000466-GPOS-00210 + SRG-OS-000458-GPOS-00203 + SRG-OS-000474-GPOS-00219 + SRG-OS-000458-VMM-001810 + SRG-OS-000474-VMM-001940 + RHEL-08-030480 + 4.1.3.9 + SV-230455r810459_rule + The changing of file permissions could indicate that a user is attempting to +gain access to information that would otherwise be disallowed. Auditing DAC modifications +can facilitate the identification of patterns of abuse among both authorized and +unauthorized users. + CCE-80693-5 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80693-5 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030480 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_lchown + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit lchown tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80693-5 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030480 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_lchown + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for lchown for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - lchown + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of lchown in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules + set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - lchown + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of lchown in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80693-5 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030480 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_lchown + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for lchown for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - lchown + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of lchown in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules + set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - lchown + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of lchown in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-80693-5 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030480 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_lchown + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="" + AUID_FILTERS="-F auid>=1000 -F auid!=unset" + SYSCALL="lchown" + KEY="perm_mod" + SYSCALL_GROUPING="chown fchown fchownat lchown" + + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Events that Modify the System's Discretionary Access Controls - lremovexattr + At a minimum, the audit system should collect file permission +changes for all users and root. + +If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following line to a file with suffix +.rules in the directory /etc/audit/rules.d: +-a always,exit -F arch=b32 -S lremovexattr -F auid>=1000 -F auid!=unset -F key=perm_mod +-a always,exit -F arch=b32 -S lremovexattr -F auid=0 -F key=perm_mod + +If the system is 64 bit then also add the following line: +-a always,exit -F arch=b64 -S lremovexattr -F auid>=1000 -F auid!=unset -F key=perm_mod +-a always,exit -F arch=b64 -S lremovexattr -F auid=0 -F key=perm_mod + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following line to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S lremovexattr -F auid>=1000 -F auid!=unset -F key=perm_mod +-a always,exit -F arch=b32 -S lremovexattr -F auid=0 -F key=perm_mod + +If the system is 64 bit then also add the following line: +-a always,exit -F arch=b64 -S lremovexattr -F auid>=1000 -F auid!=unset -F key=perm_mod +-a always,exit -F arch=b64 -S lremovexattr -F auid=0 -F key=perm_mod + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping these system +calls with others as identifying earlier in this guide is more efficient. + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 5.4.1.1 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.5.5 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000458-GPOS-00203 + SRG-OS-000462-GPOS-00206 + SRG-OS-000463-GPOS-00207 + SRG-OS-000468-GPOS-00212 + SRG-OS-000471-GPOS-00215 + SRG-OS-000474-GPOS-00219 + SRG-OS-000466-GPOS-00210 + SRG-OS-000064-GPOS-00033 + SRG-OS-000458-VMM-001810 + SRG-OS-000474-VMM-001940 + RHEL-08-030200 + 4.1.3.9 + SV-230413r810463_rule + The changing of file permissions could indicate that a user is attempting to +gain access to information that would otherwise be disallowed. Auditing DAC modifications +can facilitate the identification of patterns of abuse among both authorized and +unauthorized users. + CCE-80694-3 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80694-3 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030200 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_lremovexattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit lremovexattr tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80694-3 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030200 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_lremovexattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for lremovexattr for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - lremovexattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of lremovexattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules + set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - lremovexattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of lremovexattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - lremovexattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of lremovexattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid=0 (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules + set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid=0 (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid=0 -F + key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - lremovexattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of lremovexattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid=0 (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid=0 (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid=0 -F + key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80694-3 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030200 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_lremovexattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for lremovexattr for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - lremovexattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of lremovexattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules + set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - lremovexattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of lremovexattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - lremovexattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of lremovexattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid=0 (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules + set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid=0 (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid=0 -F + key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - lremovexattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of lremovexattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid=0 (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid=0 (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid=0 -F + key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-80694-3 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030200 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_lremovexattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="" + AUID_FILTERS="-F auid>=1000 -F auid!=unset" + SYSCALL="lremovexattr" + KEY="perm_mod" + SYSCALL_GROUPING="fremovexattr lremovexattr removexattr fsetxattr lsetxattr setxattr" + + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + + + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="" + AUID_FILTERS="-F auid=0" + SYSCALL="lremovexattr" + KEY="perm_mod" + SYSCALL_GROUPING="fremovexattr lremovexattr removexattr fsetxattr lsetxattr setxattr" + + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Events that Modify the System's Discretionary Access Controls - lsetxattr + At a minimum, the audit system should collect file permission +changes for all users and root. If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following line to a file with suffix +.rules in the directory /etc/audit/rules.d: +-a always,exit -F arch=b32 -S lsetxattr -F auid>=1000 -F auid!=unset -F key=perm_mod +-a always,exit -F arch=b32 -S lsetxattr -F auid=0 -F key=perm_mod +If the system is 64 bit then also add the following line: +-a always,exit -F arch=b64 -S lsetxattr -F auid>=1000 -F auid!=unset -F key=perm_mod +-a always,exit -F arch=b64 -S lsetxattr -F auid=0 -F key=perm_mod +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following line to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S lsetxattr -F auid>=1000 -F auid!=unset -F key=perm_mod +-a always,exit -F arch=b32 -S lsetxattr -F auid=0 -F key=perm_mod +If the system is 64 bit then also add the following line: +-a always,exit -F arch=b64 -S lsetxattr -F auid>=1000 -F auid!=unset -F key=perm_mod +-a always,exit -F arch=b64 -S lsetxattr -F auid=0 -F key=perm_mod + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping these system +calls with others as identifying earlier in this guide is more efficient. + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 5.4.1.1 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000126 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.5.5 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000458-GPOS-00203 + SRG-OS-000462-GPOS-00206 + SRG-OS-000463-GPOS-00207 + SRG-OS-000466-GPOS-00210 + SRG-OS-000468-GPOS-00212 + SRG-OS-000471-GPOS-00215 + SRG-OS-000474-GPOS-00219 + SRG-OS-000064-GPOS-00033 + SRG-OS-000458-VMM-001810 + SRG-OS-000474-VMM-001940 + RHEL-08-030200 + 4.1.3.9 + SV-230413r810463_rule + The changing of file permissions could indicate that a user is attempting to +gain access to information that would otherwise be disallowed. Auditing DAC modifications +can facilitate the identification of patterns of abuse among both authorized and +unauthorized users. + CCE-80695-0 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80695-0 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030200 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_lsetxattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit lsetxattr tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80695-0 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030200 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_lsetxattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for lsetxattr for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - lsetxattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of lsetxattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules + set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - lsetxattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of lsetxattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - lsetxattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of lsetxattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid=0 (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules + set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid=0 (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid=0 -F + key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - lsetxattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of lsetxattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid=0 (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid=0 (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid=0 -F + key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80695-0 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030200 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_lsetxattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for lsetxattr for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - lsetxattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of lsetxattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules + set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - lsetxattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of lsetxattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - lsetxattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of lsetxattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid=0 (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules + set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid=0 (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid=0 -F + key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - lsetxattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of lsetxattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid=0 (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid=0 (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid=0 -F + key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-80695-0 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030200 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_lsetxattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="" + AUID_FILTERS="-F auid>=1000 -F auid!=unset" + SYSCALL="lsetxattr" + KEY="perm_mod" + SYSCALL_GROUPING="fremovexattr lremovexattr removexattr fsetxattr lsetxattr setxattr" + + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + + + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="" + AUID_FILTERS="-F auid=0" + SYSCALL="lsetxattr" + KEY="perm_mod" + SYSCALL_GROUPING="fremovexattr lremovexattr removexattr fsetxattr lsetxattr setxattr" + + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Events that Modify the System's Discretionary Access Controls - removexattr + At a minimum, the audit system should collect file permission +changes for all users and root. + +If the auditd daemon is configured to use the augenrules +program to read audit rules during daemon startup (the default), add the +following line to a file with suffix .rules in the directory /etc/audit/rules.d: +-a always,exit -F arch=b32 -S removexattr -F auid>=1000 -F auid!=unset -F key=perm_mod +-a always,exit -F arch=b32 -S removexattr -F auid=0 -F key=perm_mod + +If the system is 64 bit then also add the following line: +-a always,exit -F arch=b64 -S removexattr -F auid>=1000 -F auid!=unset -F key=perm_mod +-a always,exit -F arch=b64 -S removexattr -F auid=0 -F key=perm_mod + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following line to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S removexattr -F auid>=1000 -F auid!=unset -F key=perm_mod +-a always,exit -F arch=b32 -S removexattr -F auid=0 -F key=perm_mod + +If the system is 64 bit then also add the following line: +-a always,exit -F arch=b64 -S removexattr -F auid>=1000 -F auid!=unset -F key=perm_mod +-a always,exit -F arch=b64 -S removexattr -F auid=0 -F key=perm_mod + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping these system +calls with others as identifying earlier in this guide is more efficient. + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 5.4.1.1 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.5.5 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000458-GPOS-00203 + SRG-OS-000462-GPOS-00206 + SRG-OS-000463-GPOS-00207 + SRG-OS-000468-GPOS-00212 + SRG-OS-000471-GPOS-00215 + SRG-OS-000474-GPOS-00219 + SRG-OS-000466-GPOS-00210 + SRG-OS-000064-GPOS-00033 + SRG-OS-000458-VMM-001810 + SRG-OS-000474-VMM-001940 + RHEL-08-030200 + 4.1.3.9 + SV-230413r810463_rule + The changing of file permissions could indicate that a user is attempting to +gain access to information that would otherwise be disallowed. Auditing DAC modifications +can facilitate the identification of patterns of abuse among both authorized and +unauthorized users. + CCE-80696-8 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80696-8 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030200 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_removexattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit removexattr tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80696-8 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030200 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_removexattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for removexattr for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - removexattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of removexattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules + set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - removexattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of removexattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - removexattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of removexattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid=0 (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules + set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid=0 (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid=0 -F + key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - removexattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of removexattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid=0 (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid=0 (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid=0 -F + key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80696-8 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030200 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_removexattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for removexattr for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - removexattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of removexattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules + set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - removexattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of removexattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - removexattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of removexattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid=0 (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules + set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid=0 (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid=0 -F + key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - removexattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of removexattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid=0 (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid=0 (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid=0 -F + key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-80696-8 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030200 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_removexattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="" + AUID_FILTERS="-F auid>=1000 -F auid!=unset" + SYSCALL="removexattr" + KEY="perm_mod" + SYSCALL_GROUPING="fremovexattr lremovexattr removexattr fsetxattr lsetxattr setxattr" + + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + + + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="" + AUID_FILTERS="-F auid=0" + SYSCALL="removexattr" + KEY="perm_mod" + SYSCALL_GROUPING="fremovexattr lremovexattr removexattr fsetxattr lsetxattr setxattr" + + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Events that Modify the System's Discretionary Access Controls - setxattr + At a minimum, the audit system should collect file permission +changes for all users and root. If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following line to a file with suffix +.rules in the directory /etc/audit/rules.d: +-a always,exit -F arch=b32 -S setxattr -F auid>=1000 -F auid!=unset -F key=perm_mod +-a always,exit -F arch=b32 -S setxattr -F auid=0 -F key=perm_mod +If the system is 64 bit then also add the following line: +-a always,exit -F arch=b64 -S setxattr -F auid>=1000 -F auid!=unset -F key=perm_mod +-a always,exit -F arch=b64 -S setxattr -F auid=0 -F key=perm_mod +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following line to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S setxattr -F auid>=1000 -F auid!=unset -F key=perm_mod +-a always,exit -F arch=b32 -S setxattr -F auid=0 -F key=perm_mod +If the system is 64 bit then also add the following line: +-a always,exit -F arch=b64 -S setxattr -F auid>=1000 -F auid!=unset -F key=perm_mod +-a always,exit -F arch=b64 -S setxattr -F auid=0 -F key=perm_mod + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping these system +calls with others as identifying earlier in this guide is more efficient. + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 5.4.1.1 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000126 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.5.5 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000466-GPOS-00210 + SRG-OS-000471-GPOS-00215 + SRG-OS-000064-GPOS-00033 + SRG-OS-000458-GPOS-00203 + SRG-OS-000458-VMM-001810 + SRG-OS-000474-VMM-001940 + RHEL-08-030200 + 4.1.3.9 + SV-230413r810463_rule + The changing of file permissions could indicate that a user is attempting to +gain access to information that would otherwise be disallowed. Auditing DAC modifications +can facilitate the identification of patterns of abuse among both authorized and +unauthorized users. + CCE-80697-6 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80697-6 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030200 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_setxattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit setxattr tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80697-6 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030200 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_setxattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for setxattr for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - setxattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of setxattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules + set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - setxattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of setxattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - setxattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of setxattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid=0 (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules + set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid=0 (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid=0 -F + key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - setxattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of setxattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid=0 (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid=0 (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid=0 -F + key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80697-6 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030200 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_setxattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for setxattr for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - setxattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of setxattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules + set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - setxattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of setxattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - setxattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of setxattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid=0 (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules + set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid=0 (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid=0 -F + key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - setxattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of setxattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid=0 (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid=0 (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid=0 -F + key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-80697-6 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030200 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_setxattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="" + AUID_FILTERS="-F auid>=1000 -F auid!=unset" + SYSCALL="setxattr" + KEY="perm_mod" + SYSCALL_GROUPING="fremovexattr lremovexattr removexattr fsetxattr lsetxattr setxattr" + + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + + + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="" + AUID_FILTERS="-F auid=0" + SYSCALL="setxattr" + KEY="perm_mod" + SYSCALL_GROUPING="fremovexattr lremovexattr removexattr fsetxattr lsetxattr setxattr" + + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Events that Modify the System's Discretionary Access Controls - umount + At a minimum, the audit system should collect file system umount +changes. If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following line to a file with suffix +.rules in the directory /etc/audit/rules.d: +-a always,exit -F arch=b32 -S umount -F auid>=1000 -F auid!=unset -F key=perm_mod +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following line to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S umount -F auid>=1000 -F auid!=unset -F key=perm_mod + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping these system +calls with others as identifying earlier in this guide is more efficient. + CCI-000130 + CCI-000169 + CCI-000172 + CCI-002884 + SRG-OS-000037-GPOS-00015 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + The changing of file permissions could indicate that a user is attempting to +gain access to information that would otherwise be disallowed. Auditing DAC modifications +can facilitate the identification of patterns of abuse among both authorized and +unauthorized users. + - name: Gather the package facts + package_facts: + manager: auto + tags: + - audit_rules_dac_modification_umount + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for umount for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - umount + syscall_grouping: [] + + - name: Check existence of umount in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules + set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - umount + syscall_grouping: [] + + - name: Check existence of umount in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - audit_rules_dac_modification_umount + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +ACTION_ARCH_FILTERS="-a always,exit -F arch=b32" +OTHER_FILTERS="" +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="umount" +KEY="perm_mod" +SYSCALL_GROUPING="" + +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Events that Modify the System's Discretionary Access Controls - umount2 + At a minimum, the audit system should collect file system umount2 +changes. If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following line to a file with suffix +.rules in the directory /etc/audit/rules.d: +-a always,exit -F arch=b32 -S umount2 -F auid>=1000 -F auid!=unset -F key=perm_mod +If the system is 64 bit then also add the following line: +-a always,exit -F arch=b64 -S umount2 -F auid>=1000 -F auid!=unset -F key=perm_mod +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following line to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S umount2 -F auid>=1000 -F auid!=unset -F key=perm_mod +If the system is 64 bit then also add the following line: +-a always,exit -F arch=b64 -S umount2 -F auid>=1000 -F auid!=unset -F key=perm_mod + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping these system +calls with others as identifying earlier in this guide is more efficient. + CCI-000130 + CCI-000169 + CCI-000172 + CCI-002884 + SRG-OS-000037-GPOS-00015 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + The changing of file permissions could indicate that a user is attempting to +gain access to information that would otherwise be disallowed. Auditing DAC modifications +can facilitate the identification of patterns of abuse among both authorized and +unauthorized users. + - name: Gather the package facts + package_facts: + manager: auto + tags: + - audit_rules_dac_modification_umount2 + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit umount2 tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - audit_rules_dac_modification_umount2 + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for umount2 for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - umount2 + syscall_grouping: [] + + - name: Check existence of umount2 in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules + set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - umount2 + syscall_grouping: [] + + - name: Check existence of umount2 in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - audit_rules_dac_modification_umount2 + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for umount2 for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - umount2 + syscall_grouping: [] + + - name: Check existence of umount2 in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules + set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - umount2 + syscall_grouping: [] + + - name: Check existence of umount2 in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - audit_rules_dac_modification_umount2 + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="" + AUID_FILTERS="-F auid>=1000 -F auid!=unset" + SYSCALL="umount2" + KEY="perm_mod" + SYSCALL_GROUPING="" + + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Record Execution Attempts to Run ACL Privileged Commands + At a minimum, the audit system should collect the execution of +ACL privileged commands for all users and root. + + Record Any Attempts to Run chacl + At a minimum, the audit system should collect any execution attempt +of the chacl command for all users and root. If the auditd +daemon is configured to use the augenrules program to read audit rules +during daemon startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: +-a always,exit -F path=/usr/bin/chacl -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F path=/usr/bin/chacl -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + SRG-OS-000466-GPOS-00210 + RHEL-08-030570 + SV-230464r627750_rule + Without generating audit records that are specific to the security and +mission needs of the organization, it would be difficult to establish, +correlate, and investigate the events relating to an incident or identify +those responsible for one. +Audit records can be generated from various components within the +information system (e.g., module or policy filter). + CCE-89446-9 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-89446-9 + - DISA-STIG-RHEL-08-030570 + - audit_rules_execution_chacl + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Perform remediation of Audit rules for /usr/bin/chacl + block: + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/bin/chacl -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules + set_fact: audit_file="/etc/audit/rules.d/privileged.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/bin/chacl -F perm=x -F + auid>=1000 -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/chacl -F perm=x + -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/bin/chacl -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:( + -S |,)\w+)+)( -F path=/usr/bin/chacl -F perm=x -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/chacl -F perm=x + -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-89446-9 + - DISA-STIG-RHEL-08-030570 + - audit_rules_execution_chacl + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +ACTION_ARCH_FILTERS="-a always,exit" +OTHER_FILTERS="-F path=/usr/bin/chacl -F perm=x" +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="" +KEY="privileged" +SYSCALL_GROUPING="" +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Any Attempts to Run setfacl + At a minimum, the audit system should collect any execution attempt +of the setfacl command for all users and root. If the auditd +daemon is configured to use the augenrules program to read audit rules +during daemon startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: +-a always,exit -F path=/usr/bin/setfacl -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F path=/usr/bin/setfacl -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + RHEL-08-030330 + SV-230435r627750_rule + Without generating audit records that are specific to the security and +mission needs of the organization, it would be difficult to establish, +correlate, and investigate the events relating to an incident or identify +those responsible for one. +Audit records can be generated from various components within the +information system (e.g., module or policy filter). + CCE-88437-9 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-88437-9 + - DISA-STIG-RHEL-08-030330 + - audit_rules_execution_setfacl + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Perform remediation of Audit rules for /usr/bin/setfacl + block: + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/bin/setfacl -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules + set_fact: audit_file="/etc/audit/rules.d/privileged.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/bin/setfacl -F perm=x + -F auid>=1000 -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/setfacl -F perm=x + -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/bin/setfacl -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:( + -S |,)\w+)+)( -F path=/usr/bin/setfacl -F perm=x -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/setfacl -F perm=x + -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-88437-9 + - DISA-STIG-RHEL-08-030330 + - audit_rules_execution_setfacl + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +ACTION_ARCH_FILTERS="-a always,exit" +OTHER_FILTERS="-F path=/usr/bin/setfacl -F perm=x" +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="" +KEY="privileged" +SYSCALL_GROUPING="" +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Record Execution Attempts to Run SELinux Privileged Commands + At a minimum, the audit system should collect the execution of +SELinux privileged commands for all users and root. + + Record Any Attempts to Run chcon + At a minimum, the audit system should collect any execution attempt +of the chcon command for all users and root. If the auditd +daemon is configured to use the augenrules program to read audit rules +during daemon startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: +-a always,exit -F path=/usr/bin/chcon -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F path=/usr/bin/chcon -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + 1 + 12 + 13 + 14 + 15 + 16 + 2 + 3 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + BAI03.05 + DSS01.03 + DSS03.05 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 6.1 + SR 6.2 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.14.2.7 + A.15.2.1 + A.15.2.2 + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.PT-1 + FAU_GEN.1.1.c + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000468-GPOS-00212 + SRG-OS-000471-GPOS-00215 + SRG-OS-000463-GPOS-00207 + SRG-OS-000465-GPOS-00209 + SRG-OS-000463-VMM-001850 + RHEL-08-030260 + SV-230419r627750_rule + Misuse of privileged functions, either intentionally or unintentionally by +authorized users, or by unauthorized external entities that have compromised system accounts, +is a serious and ongoing concern and can have significant adverse impacts on organizations. +Auditing the use of privileged functions is one way to detect such misuse and identify +the risk from insider and advanced persistent threats. + +Privileged programs are subject to escalation-of-privilege attacks, +which attempt to subvert their normal role of providing some necessary but +limited capability. As such, motivation exists to monitor these programs for +unusual activity. + CCE-80698-4 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80698-4 + - DISA-STIG-RHEL-08-030260 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_execution_chcon + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Perform remediation of Audit rules for /usr/bin/chcon + block: + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/bin/chcon -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules + set_fact: audit_file="/etc/audit/rules.d/privileged.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/bin/chcon -F perm=x -F + auid>=1000 -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/chcon -F perm=x + -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/bin/chcon -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:( + -S |,)\w+)+)( -F path=/usr/bin/chcon -F perm=x -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/chcon -F perm=x + -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80698-4 + - DISA-STIG-RHEL-08-030260 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_execution_chcon + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +ACTION_ARCH_FILTERS="-a always,exit" +OTHER_FILTERS="-F path=/usr/bin/chcon -F perm=x" +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="" +KEY="privileged" +SYSCALL_GROUPING="" +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Any Attempts to Run restorecon + At a minimum, the audit system should collect any execution attempt +of the restorecon command for all users and root. If the auditd +daemon is configured to use the augenrules program to read audit rules +during daemon startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: +-a always,exit -F path=/usr/sbin/restorecon -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F path=/usr/sbin/restorecon -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + 1 + 12 + 13 + 14 + 15 + 16 + 2 + 3 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + BAI03.05 + DSS01.03 + DSS03.05 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 6.1 + SR 6.2 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.14.2.7 + A.15.2.1 + A.15.2.2 + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.PT-1 + FAU_GEN.1.1.c + SRG-OS-000392-GPOS-00172 + SRG-OS-000463-GPOS-00207 + SRG-OS-000465-GPOS-00209 + SRG-OS-000463-VMM-001850 + Misuse of privileged functions, either intentionally or unintentionally by +authorized users, or by unauthorized external entities that have compromised system accounts, +is a serious and ongoing concern and can have significant adverse impacts on organizations. +Auditing the use of privileged functions is one way to detect such misuse and identify +the risk from insider and advanced persistent threats. + +Privileged programs are subject to escalation-of-privilege attacks, +which attempt to subvert their normal role of providing some necessary but +limited capability. As such, motivation exists to monitor these programs for +unusual activity. + CCE-80699-2 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80699-2 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_execution_restorecon + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Perform remediation of Audit rules for /usr/sbin/restorecon + block: + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/sbin/restorecon -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules + set_fact: audit_file="/etc/audit/rules.d/privileged.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/sbin/restorecon -F perm=x + -F auid>=1000 -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/sbin/restorecon + -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/sbin/restorecon -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:( + -S |,)\w+)+)( -F path=/usr/sbin/restorecon -F perm=x -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/sbin/restorecon + -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80699-2 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_execution_restorecon + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +ACTION_ARCH_FILTERS="-a always,exit" +OTHER_FILTERS="-F path=/usr/sbin/restorecon -F perm=x" +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="" +KEY="privileged" +SYSCALL_GROUPING="" +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Any Attempts to Run semanage + At a minimum, the audit system should collect any execution attempt +of the semanage command for all users and root. If the auditd +daemon is configured to use the augenrules program to read audit rules +during daemon startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: +-a always,exit -F path=/usr/sbin/semanage -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F path=/usr/sbin/semanage -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + 1 + 12 + 13 + 14 + 15 + 16 + 2 + 3 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + BAI03.05 + DSS01.03 + DSS03.05 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000169 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 6.1 + SR 6.2 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.14.2.7 + A.15.2.1 + A.15.2.2 + CIP-004-6 R2.2.2 + CIP-004-6 R2.2.3 + CIP-007-3 R.1.3 + CIP-007-3 R5 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.3 + CIP-007-3 R5.2.1 + CIP-007-3 R5.2.3 + AC-2(4) + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.PT-1 + FAU_GEN.1.1.c + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + SRG-OS-000463-GPOS-00207 + SRG-OS-000465-GPOS-00209 + SRG-OS-000463-VMM-001850 + RHEL-08-030313 + SV-230429r627750_rule + Misuse of privileged functions, either intentionally or unintentionally by +authorized users, or by unauthorized external entities that have compromised system accounts, +is a serious and ongoing concern and can have significant adverse impacts on organizations. +Auditing the use of privileged functions is one way to detect such misuse and identify +the risk from insider and advanced persistent threats. + +Privileged programs are subject to escalation-of-privilege attacks, +which attempt to subvert their normal role of providing some necessary but +limited capability. As such, motivation exists to monitor these programs for +unusual activity. + CCE-80700-8 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80700-8 + - DISA-STIG-RHEL-08-030313 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_execution_semanage + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Perform remediation of Audit rules for /usr/sbin/semanage + block: + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/sbin/semanage -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules + set_fact: audit_file="/etc/audit/rules.d/privileged.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/sbin/semanage -F perm=x + -F auid>=1000 -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/sbin/semanage -F + perm=x -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/sbin/semanage -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:( + -S |,)\w+)+)( -F path=/usr/sbin/semanage -F perm=x -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/sbin/semanage -F + perm=x -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80700-8 + - DISA-STIG-RHEL-08-030313 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_execution_semanage + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +ACTION_ARCH_FILTERS="-a always,exit" +OTHER_FILTERS="-F path=/usr/sbin/semanage -F perm=x" +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="" +KEY="privileged" +SYSCALL_GROUPING="" +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Any Attempts to Run setfiles + At a minimum, the audit system should collect any execution attempt +of the setfiles command for all users and root. If the auditd +daemon is configured to use the augenrules program to read audit rules +during daemon startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: +-a always,exit -F path=/usr/sbin/setfiles -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F path=/usr/sbin/setfiles -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + CCI-000169 + CCI-000172 + CCI-002884 + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + SRG-OS-000463-GPOS-00207 + SRG-OS-000465-GPOS-00209 + SRG-OS-000463-VMM-001850 + RHEL-08-030314 + SV-230430r627750_rule + Misuse of privileged functions, either intentionally or unintentionally by +authorized users, or by unauthorized external entities that have compromised system accounts, +is a serious and ongoing concern and can have significant adverse impacts on organizations. +Auditing the use of privileged functions is one way to detect such misuse and identify +the risk from insider and advanced persistent threats. + +Privileged programs are subject to escalation-of-privilege attacks, +which attempt to subvert their normal role of providing some necessary but +limited capability. As such, motivation exists to monitor these programs for +unusual activity. + CCE-82280-9 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-82280-9 + - DISA-STIG-RHEL-08-030314 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_execution_setfiles + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Perform remediation of Audit rules for /usr/sbin/setfiles + block: + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/sbin/setfiles -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules + set_fact: audit_file="/etc/audit/rules.d/privileged.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/sbin/setfiles -F perm=x + -F auid>=1000 -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/sbin/setfiles -F + perm=x -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/sbin/setfiles -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:( + -S |,)\w+)+)( -F path=/usr/sbin/setfiles -F perm=x -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/sbin/setfiles -F + perm=x -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82280-9 + - DISA-STIG-RHEL-08-030314 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_execution_setfiles + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +ACTION_ARCH_FILTERS="-a always,exit" +OTHER_FILTERS="-F path=/usr/sbin/setfiles -F perm=x" +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="" +KEY="privileged" +SYSCALL_GROUPING="" +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Any Attempts to Run setsebool + At a minimum, the audit system should collect any execution attempt +of the setsebool command for all users and root. If the auditd +daemon is configured to use the augenrules program to read audit rules +during daemon startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: +-a always,exit -F path=/usr/sbin/setsebool -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F path=/usr/sbin/setsebool -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + 1 + 12 + 13 + 14 + 15 + 16 + 2 + 3 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + BAI03.05 + DSS01.03 + DSS03.05 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 6.1 + SR 6.2 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.14.2.7 + A.15.2.1 + A.15.2.2 + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.PT-1 + FAU_GEN.1.1.c + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + SRG-OS-000463-GPOS-00207 + SRG-OS-000465-GPOS-00209 + SRG-OS-000463-VMM-001850 + RHEL-08-030316 + SV-230432r627750_rule + Misuse of privileged functions, either intentionally or unintentionally by +authorized users, or by unauthorized external entities that have compromised system accounts, +is a serious and ongoing concern and can have significant adverse impacts on organizations. +Auditing the use of privileged functions is one way to detect such misuse and identify +the risk from insider and advanced persistent threats. + +Privileged programs are subject to escalation-of-privilege attacks, +which attempt to subvert their normal role of providing some necessary but +limited capability. As such, motivation exists to monitor these programs for +unusual activity. + CCE-80701-6 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80701-6 + - DISA-STIG-RHEL-08-030316 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_execution_setsebool + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Perform remediation of Audit rules for /usr/sbin/setsebool + block: + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/sbin/setsebool -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules + set_fact: audit_file="/etc/audit/rules.d/privileged.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/sbin/setsebool -F perm=x + -F auid>=1000 -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/sbin/setsebool -F + perm=x -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/sbin/setsebool -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:( + -S |,)\w+)+)( -F path=/usr/sbin/setsebool -F perm=x -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/sbin/setsebool -F + perm=x -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80701-6 + - DISA-STIG-RHEL-08-030316 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_execution_setsebool + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +ACTION_ARCH_FILTERS="-a always,exit" +OTHER_FILTERS="-F path=/usr/sbin/setsebool -F perm=x" +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="" +KEY="privileged" +SYSCALL_GROUPING="" +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Any Attempts to Run seunshare + At a minimum, the audit system should collect any execution attempt +of the seunshare command for all users and root. If the auditd +daemon is configured to use the augenrules program to read audit rules +during daemon startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: +-a always,exit -F path=/usr/sbin/seunshare -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F path=/usr/sbin/seunshare -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + CCI-000172 + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + FAU_GEN.1.1.c + SRG-OS-000463-VMM-001850 + Misuse of privileged functions, either intentionally or unintentionally by +authorized users, or by unauthorized external entities that have compromised system accounts, +is a serious and ongoing concern and can have significant adverse impacts on organizations. +Auditing the use of privileged functions is one way to detect such misuse and identify +the risk from insider and advanced persistent threats. + +Privileged programs are subject to escalation-of-privilege attacks, +which attempt to subvert their normal role of providing some necessary but +limited capability. As such, motivation exists to monitor these programs for +unusual activity. + CCE-80933-5 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80933-5 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_execution_seunshare + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Perform remediation of Audit rules for /usr/sbin/seunshare + block: + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/sbin/seunshare -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules + set_fact: audit_file="/etc/audit/rules.d/privileged.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/sbin/seunshare -F perm=x + -F auid>=1000 -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/sbin/seunshare -F + perm=x -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/sbin/seunshare -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:( + -S |,)\w+)+)( -F path=/usr/sbin/seunshare -F perm=x -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/sbin/seunshare -F + perm=x -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80933-5 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_execution_seunshare + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +ACTION_ARCH_FILTERS="-a always,exit" +OTHER_FILTERS="-F path=/usr/sbin/seunshare -F perm=x" +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="" +KEY="privileged" +SYSCALL_GROUPING="" +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Record File Deletion Events by User + At a minimum, the audit system should collect file deletion events +for all users and root. If the auditd daemon is configured to use the +augenrules program to read audit rules during daemon startup (the +default), add the following line to a file with suffix .rules in the +directory /etc/audit/rules.d, setting ARCH to either b32 or b64 as +appropriate for your system: +-a always,exit -F arch=ARCH -S rmdir,unlink,unlinkat,rename,renameat -F auid>=1000 -F auid!=unset -F key=delete +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following line to +/etc/audit/audit.rules file, setting ARCH to either b32 or b64 as +appropriate for your system: +-a always,exit -F arch=ARCH -S rmdir,unlink,unlinkat,rename,renameat -F auid>=1000 -F auid!=unset -F key=delete + + Ensure auditd Collects File Deletion Events by User + At a minimum the audit system should collect file deletion events +for all users and root. If the auditd daemon is configured to use the +augenrules program to read audit rules during daemon startup (the +default), add the following line to a file with suffix .rules in the +directory /etc/audit/rules.d, setting ARCH to either b32 or b64 as +appropriate for your system: +-a always,exit -F arch=ARCH -S rmdir,unlink,unlinkat,rename,renameat -F auid>=1000 -F auid!=unset -F key=delete +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following line to +/etc/audit/audit.rules file, setting ARCH to either b32 or b64 as +appropriate for your system: +-a always,exit -F arch=ARCH -S rmdir,unlink,unlinkat,rename -S renameat -F auid>=1000 -F auid!=unset -F key=delete + This rule checks for multiple syscalls related to file deletion; +it was written with DISA STIG in mind. Other policies should use a +separate rule for each syscall that needs to be checked. For example: +audit_rules_file_deletion_events_rmdiraudit_rules_file_deletion_events_unlinkaudit_rules_file_deletion_events_unlinkat + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 5.4.1.1 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000366 + CCI-000172 + CCI-002884 + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.2.7 + 4.1.14 + Auditing file deletions will create an audit trail for files that are removed +from the system. The audit trail could aid in system troubleshooting, as well as, detecting +malicious processes that attempt to delete log files to conceal their presence. + CCE-80702-4 + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# Perform the remediation for the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="" + AUID_FILTERS="-F auid>=1000 -F auid!=unset" + SYSCALL="rmdir unlink unlinkat rename renameat" + KEY="delete" + SYSCALL_GROUPING="rmdir unlink unlinkat rename renameat" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure auditd Collects File Deletion Events by User - rename + At a minimum, the audit system should collect file deletion events +for all users and root. If the auditd daemon is configured to use the +augenrules program to read audit rules during daemon startup (the +default), add the following line to a file with suffix .rules in the +directory /etc/audit/rules.d, setting ARCH to either b32 or b64 as +appropriate for your system: +-a always,exit -F arch=ARCH -S rename -F auid>=1000 -F auid!=unset -F key=delete +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following line to +/etc/audit/audit.rules file, setting ARCH to either b32 or b64 as +appropriate for your system: +-a always,exit -F arch=ARCH -S rename -F auid>=1000 -F auid!=unset -F key=delete + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-000366 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.4 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.1.1 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.MA-2 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.2.7 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + SRG-OS-000466-GPOS-00210 + SRG-OS-000467-GPOS-00211 + SRG-OS-000468-GPOS-00212 + SRG-OS-000466-VMM-001870 + SRG-OS-000468-VMM-001890 + RHEL-08-030361 + 4.1.3.13 + SV-230439r810465_rule + Auditing file deletions will create an audit trail for files that are removed +from the system. The audit trail could aid in system troubleshooting, as well as, detecting +malicious processes that attempt to delete log files to conceal their presence. + CCE-80703-2 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80703-2 + - DISA-STIG-RHEL-08-030361 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.7 + - audit_rules_file_deletion_events_rename + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit rename tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80703-2 + - DISA-STIG-RHEL-08-030361 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.7 + - audit_rules_file_deletion_events_rename + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for rename for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - rename + syscall_grouping: + - unlink + - unlinkat + - rename + - renameat + - rmdir + + - name: Check existence of rename in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/delete.rules + set_fact: audit_file="/etc/audit/rules.d/delete.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=delete + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - rename + syscall_grouping: + - unlink + - unlinkat + - rename + - renameat + - rmdir + + - name: Check existence of rename in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=delete + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80703-2 + - DISA-STIG-RHEL-08-030361 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.7 + - audit_rules_file_deletion_events_rename + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for rename for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - rename + syscall_grouping: + - unlink + - unlinkat + - rename + - renameat + - rmdir + + - name: Check existence of rename in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/delete.rules + set_fact: audit_file="/etc/audit/rules.d/delete.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=delete + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - rename + syscall_grouping: + - unlink + - unlinkat + - rename + - renameat + - rmdir + + - name: Check existence of rename in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=delete + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-80703-2 + - DISA-STIG-RHEL-08-030361 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.7 + - audit_rules_file_deletion_events_rename + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="" + AUID_FILTERS="-F auid>=1000 -F auid!=unset" + SYSCALL="rename" + KEY="delete" + SYSCALL_GROUPING="unlink unlinkat rename renameat rmdir" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure auditd Collects File Deletion Events by User - renameat + At a minimum, the audit system should collect file deletion events +for all users and root. If the auditd daemon is configured to use the +augenrules program to read audit rules during daemon startup (the +default), add the following line to a file with suffix .rules in the +directory /etc/audit/rules.d, setting ARCH to either b32 or b64 as +appropriate for your system: +-a always,exit -F arch=ARCH -S renameat -F auid>=1000 -F auid!=unset -F key=delete +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following line to +/etc/audit/audit.rules file, setting ARCH to either b32 or b64 as +appropriate for your system: +-a always,exit -F arch=ARCH -S renameat -F auid>=1000 -F auid!=unset -F key=delete + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-000366 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.4 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.1.1 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.MA-2 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.2.7 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + SRG-OS-000466-GPOS-00210 + SRG-OS-000467-GPOS-00211 + SRG-OS-000468-GPOS-00212 + SRG-OS-000466-VMM-001870 + SRG-OS-000468-VMM-001890 + RHEL-08-030361 + 4.1.3.13 + SV-230439r810465_rule + Auditing file deletions will create an audit trail for files that are removed +from the system. The audit trail could aid in system troubleshooting, as well as, detecting +malicious processes that attempt to delete log files to conceal their presence. + CCE-80704-0 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80704-0 + - DISA-STIG-RHEL-08-030361 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.7 + - audit_rules_file_deletion_events_renameat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit renameat tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80704-0 + - DISA-STIG-RHEL-08-030361 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.7 + - audit_rules_file_deletion_events_renameat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for renameat for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - renameat + syscall_grouping: + - unlink + - unlinkat + - rename + - renameat + - rmdir + + - name: Check existence of renameat in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/delete.rules + set_fact: audit_file="/etc/audit/rules.d/delete.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=delete + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - renameat + syscall_grouping: + - unlink + - unlinkat + - rename + - renameat + - rmdir + + - name: Check existence of renameat in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=delete + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80704-0 + - DISA-STIG-RHEL-08-030361 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.7 + - audit_rules_file_deletion_events_renameat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for renameat for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - renameat + syscall_grouping: + - unlink + - unlinkat + - rename + - renameat + - rmdir + + - name: Check existence of renameat in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/delete.rules + set_fact: audit_file="/etc/audit/rules.d/delete.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=delete + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - renameat + syscall_grouping: + - unlink + - unlinkat + - rename + - renameat + - rmdir + + - name: Check existence of renameat in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=delete + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-80704-0 + - DISA-STIG-RHEL-08-030361 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.7 + - audit_rules_file_deletion_events_renameat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="" + AUID_FILTERS="-F auid>=1000 -F auid!=unset" + SYSCALL="renameat" + KEY="delete" + SYSCALL_GROUPING="unlink unlinkat rename renameat rmdir" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure auditd Collects File Deletion Events by User - rmdir + At a minimum, the audit system should collect file deletion events +for all users and root. If the auditd daemon is configured to use the +augenrules program to read audit rules during daemon startup (the +default), add the following line to a file with suffix .rules in the +directory /etc/audit/rules.d, setting ARCH to either b32 or b64 as +appropriate for your system: +-a always,exit -F arch=ARCH -S rmdir -F auid>=1000 -F auid!=unset -F key=delete +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following line to +/etc/audit/audit.rules file, setting ARCH to either b32 or b64 as +appropriate for your system: +-a always,exit -F arch=ARCH -S rmdir -F auid>=1000 -F auid!=unset -F key=delete + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-000366 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.4 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.1.1 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.MA-2 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.2.7 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + SRG-OS-000466-GPOS-00210 + SRG-OS-000467-GPOS-00211 + SRG-OS-000468-GPOS-00212 + SRG-OS-000466-VMM-001870 + SRG-OS-000468-VMM-001890 + RHEL-08-030361 + 4.1.14 + SV-230439r810465_rule + Auditing file deletions will create an audit trail for files that are removed +from the system. The audit trail could aid in system troubleshooting, as well as, detecting +malicious processes that attempt to delete log files to conceal their presence. + CCE-80705-7 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80705-7 + - DISA-STIG-RHEL-08-030361 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.7 + - audit_rules_file_deletion_events_rmdir + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit rmdir tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80705-7 + - DISA-STIG-RHEL-08-030361 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.7 + - audit_rules_file_deletion_events_rmdir + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for rmdir for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - rmdir + syscall_grouping: + - unlink + - unlinkat + - rename + - renameat + - rmdir + + - name: Check existence of rmdir in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/delete.rules + set_fact: audit_file="/etc/audit/rules.d/delete.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=delete + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - rmdir + syscall_grouping: + - unlink + - unlinkat + - rename + - renameat + - rmdir + + - name: Check existence of rmdir in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=delete + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80705-7 + - DISA-STIG-RHEL-08-030361 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.7 + - audit_rules_file_deletion_events_rmdir + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for rmdir for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - rmdir + syscall_grouping: + - unlink + - unlinkat + - rename + - renameat + - rmdir + + - name: Check existence of rmdir in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/delete.rules + set_fact: audit_file="/etc/audit/rules.d/delete.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=delete + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - rmdir + syscall_grouping: + - unlink + - unlinkat + - rename + - renameat + - rmdir + + - name: Check existence of rmdir in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=delete + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-80705-7 + - DISA-STIG-RHEL-08-030361 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.7 + - audit_rules_file_deletion_events_rmdir + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="" + AUID_FILTERS="-F auid>=1000 -F auid!=unset" + SYSCALL="rmdir" + KEY="delete" + SYSCALL_GROUPING="unlink unlinkat rename renameat rmdir" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure auditd Collects File Deletion Events by User - unlink + At a minimum, the audit system should collect file deletion events +for all users and root. If the auditd daemon is configured to use the +augenrules program to read audit rules during daemon startup (the +default), add the following line to a file with suffix .rules in the +directory /etc/audit/rules.d, setting ARCH to either b32 or b64 as +appropriate for your system: +-a always,exit -F arch=ARCH -S unlink -F auid>=1000 -F auid!=unset -F key=delete +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following line to +/etc/audit/audit.rules file, setting ARCH to either b32 or b64 as +appropriate for your system: +-a always,exit -F arch=ARCH -S unlink -F auid>=1000 -F auid!=unset -F key=delete + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-000366 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.4 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.1.1 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.MA-2 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.2.7 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + SRG-OS-000466-GPOS-00210 + SRG-OS-000467-GPOS-00211 + SRG-OS-000468-GPOS-00212 + SRG-OS-000466-VMM-001870 + SRG-OS-000468-VMM-001890 + RHEL-08-030361 + 4.1.3.13 + SV-230439r810465_rule + Auditing file deletions will create an audit trail for files that are removed +from the system. The audit trail could aid in system troubleshooting, as well as, detecting +malicious processes that attempt to delete log files to conceal their presence. + CCE-80706-5 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80706-5 + - DISA-STIG-RHEL-08-030361 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.7 + - audit_rules_file_deletion_events_unlink + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit unlink tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80706-5 + - DISA-STIG-RHEL-08-030361 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.7 + - audit_rules_file_deletion_events_unlink + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for unlink for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - unlink + syscall_grouping: + - unlink + - unlinkat + - rename + - renameat + - rmdir + + - name: Check existence of unlink in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/delete.rules + set_fact: audit_file="/etc/audit/rules.d/delete.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=delete + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - unlink + syscall_grouping: + - unlink + - unlinkat + - rename + - renameat + - rmdir + + - name: Check existence of unlink in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=delete + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80706-5 + - DISA-STIG-RHEL-08-030361 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.7 + - audit_rules_file_deletion_events_unlink + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for unlink for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - unlink + syscall_grouping: + - unlink + - unlinkat + - rename + - renameat + - rmdir + + - name: Check existence of unlink in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/delete.rules + set_fact: audit_file="/etc/audit/rules.d/delete.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=delete + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - unlink + syscall_grouping: + - unlink + - unlinkat + - rename + - renameat + - rmdir + + - name: Check existence of unlink in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=delete + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-80706-5 + - DISA-STIG-RHEL-08-030361 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.7 + - audit_rules_file_deletion_events_unlink + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="" + AUID_FILTERS="-F auid>=1000 -F auid!=unset" + SYSCALL="unlink" + KEY="delete" + SYSCALL_GROUPING="unlink unlinkat rename renameat rmdir" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure auditd Collects File Deletion Events by User - unlinkat + At a minimum, the audit system should collect file deletion events +for all users and root. If the auditd daemon is configured to use the +augenrules program to read audit rules during daemon startup (the +default), add the following line to a file with suffix .rules in the +directory /etc/audit/rules.d, setting ARCH to either b32 or b64 as +appropriate for your system: +-a always,exit -F arch=ARCH -S unlinkat -F auid>=1000 -F auid!=unset -F key=delete +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following line to +/etc/audit/audit.rules file, setting ARCH to either b32 or b64 as +appropriate for your system: +-a always,exit -F arch=ARCH -S unlinkat -F auid>=1000 -F auid!=unset -F key=delete + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-000366 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.4 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.1.1 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.MA-2 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.2.7 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + SRG-OS-000466-GPOS-00210 + SRG-OS-000467-GPOS-00211 + SRG-OS-000468-GPOS-00212 + SRG-OS-000466-VMM-001870 + SRG-OS-000468-VMM-001890 + RHEL-08-030361 + 4.1.3.13 + SV-230439r810465_rule + Auditing file deletions will create an audit trail for files that are removed +from the system. The audit trail could aid in system troubleshooting, as well as, detecting +malicious processes that attempt to delete log files to conceal their presence. + CCE-80707-3 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80707-3 + - DISA-STIG-RHEL-08-030361 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.7 + - audit_rules_file_deletion_events_unlinkat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit unlinkat tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80707-3 + - DISA-STIG-RHEL-08-030361 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.7 + - audit_rules_file_deletion_events_unlinkat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for unlinkat for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - unlinkat + syscall_grouping: + - unlink + - unlinkat + - rename + - renameat + - rmdir + + - name: Check existence of unlinkat in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/delete.rules + set_fact: audit_file="/etc/audit/rules.d/delete.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=delete + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - unlinkat + syscall_grouping: + - unlink + - unlinkat + - rename + - renameat + - rmdir + + - name: Check existence of unlinkat in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=delete + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80707-3 + - DISA-STIG-RHEL-08-030361 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.7 + - audit_rules_file_deletion_events_unlinkat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for unlinkat for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - unlinkat + syscall_grouping: + - unlink + - unlinkat + - rename + - renameat + - rmdir + + - name: Check existence of unlinkat in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/delete.rules + set_fact: audit_file="/etc/audit/rules.d/delete.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=delete + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - unlinkat + syscall_grouping: + - unlink + - unlinkat + - rename + - renameat + - rmdir + + - name: Check existence of unlinkat in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=delete + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-80707-3 + - DISA-STIG-RHEL-08-030361 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.7 + - audit_rules_file_deletion_events_unlinkat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="" + AUID_FILTERS="-F auid>=1000 -F auid!=unset" + SYSCALL="unlinkat" + KEY="delete" + SYSCALL_GROUPING="unlink unlinkat rename renameat rmdir" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Record Unauthorized Access Attempts Events to Files (unsuccessful) + At a minimum, the audit system should collect unauthorized file +accesses for all users and root. Note that the "-F arch=b32" lines should be +present even on a 64 bit system. These commands identify system calls for +auditing. Even if the system is 64 bit it can still execute 32 bit system +calls. Additionally, these rules can be configured in a number of ways while +still achieving the desired effect. An example of this is that the "-S" calls +could be split up and placed on separate lines, however, this is less efficient. +Add the following to /etc/audit/audit.rules: +-a always,exit -F arch=b32 -S creat,open,openat,open_by_handle_at,truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=access + -a always,exit -F arch=b32 -S creat,open,openat,open_by_handle_at,truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=access +If your system is 64 bit then these lines should be duplicated and the +arch=b32 replaced with arch=b64 as follows: +-a always,exit -F arch=b64 -S creat,open,openat,open_by_handle_at,truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=access + -a always,exit -F arch=b64 -S creat,open,openat,open_by_handle_at,truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=access + + Record Successful Permission Changes to Files - chmod + At a minimum, the audit system should collect file permission changes +for all users and root. If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: + +-a always,exit -F arch=b32 -S chmod -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S chmod -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S chmod -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S chmod -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping these system +calls with others as identifying earlier in this guide is more efficient. + File permission changes could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + CCE-82098-5 + + + + + + Record Successful Ownership Changes to Files - chown + At a minimum, the audit system should collect file ownership changes +for all users and root. If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: + +-a always,exit -F arch=b32 -S chown -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S chown -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S chown -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S chown -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping these system +calls with others as identifying earlier in this guide is more efficient. + File ownership attempts could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + CCE-82131-4 + + + + + + Record Successful Access Attempts to Files - creat + At a minimum, the audit system should collect unauthorized file +accesses for all users and root. If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: + +-a always,exit -F arch=b32 -S creat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-access + +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S creat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-access + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S creat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-access + +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S creat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-access + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping these system +calls with others as identifying earlier in this guide is more efficient. + File access attempts could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + CCE-81150-5 + + + + + + Record Successful Permission Changes to Files - fchmod + At a minimum, the audit system should collect file permission changes +for all users and root. If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: + +-a always,exit -F arch=b32 -S fchmod -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S fchmod -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S fchmod -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S fchmod -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping these system +calls with others as identifying earlier in this guide is more efficient. + File permission changes could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + CCE-82101-7 + + + + + + Record Successful Permission Changes to Files - fchmodat + At a minimum, the audit system should collect file permission changes +for all users and root. If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: + +-a always,exit -F arch=b32 -S fchmodat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S fchmodat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S fchmodat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S fchmodat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping these system +calls with others as identifying earlier in this guide is more efficient. + File permission changes could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + CCE-82104-1 + + + + + + Record Successful Ownership Changes to Files - fchown + At a minimum, the audit system should collect file ownership changes +for all users and root. If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: + +-a always,exit -F arch=b32 -S fchown -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S fchown -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S fchown -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S fchown -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping these system +calls with others as identifying earlier in this guide is more efficient. + File ownership attempts could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + CCE-82128-0 + + + + + + Record Successful Ownership Changes to Files - fchownat + At a minimum, the audit system should collect file ownership changes +for all users and root. If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: + +-a always,exit -F arch=b32 -S fchownat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S fchownat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S fchownat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S fchownat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping these system +calls with others as identifying earlier in this guide is more efficient. + File ownership attempts could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + CCE-82134-8 + + + + + + Record Successful Permission Changes to Files - fremovexattr + At a minimum, the audit system should collect file permission changes +for all users and root. If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: + +-a always,exit -F arch=b32 -S fremovexattr -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S fremovexattr -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S fremovexattr -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S fremovexattr -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping these system +calls with others as identifying earlier in this guide is more efficient. + File permission changes could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + CCE-82122-3 + + + + + + Record Successful Permission Changes to Files - fsetxattr + At a minimum, the audit system should collect file permission changes +for all users and root. If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: + +-a always,exit -F arch=b32 -S fsetxattr -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S fsetxattr -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S fsetxattr -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S fsetxattr -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping these system +calls with others as identifying earlier in this guide is more efficient. + File permission changes could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + CCE-82113-2 + + + + + + Record Successful Access Attempts to Files - ftruncate + At a minimum, the audit system should collect unauthorized file +accesses for all users and root. If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: + +-a always,exit -F arch=b32 -S ftruncate -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-access + +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S ftruncate -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-access + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S ftruncate -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-access + +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S ftruncate -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-access + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping these system +calls with others as identifying earlier in this guide is more efficient. + File access attempts could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + CCE-82006-8 + + + + + + Record Successful Ownership Changes to Files - lchown + At a minimum, the audit system should collect file ownership changes +for all users and root. If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: + +-a always,exit -F arch=b32 -S lchown -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S lchown -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S lchown -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S lchown -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping these system +calls with others as identifying earlier in this guide is more efficient. + File ownership attempts could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + CCE-82125-6 + + + + + + Record Successful Permission Changes to Files - lremovexattr + At a minimum, the audit system should collect file permission changes +for all users and root. If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: + +-a always,exit -F arch=b32 -S lremovexattr -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S lremovexattr -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S lremovexattr -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S lremovexattr -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping these system +calls with others as identifying earlier in this guide is more efficient. + File permission changes could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + CCE-82119-9 + + + + + + Record Successful Permission Changes to Files - lsetxattr + At a minimum, the audit system should collect file permission changes +for all users and root. If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: + +-a always,exit -F arch=b32 -S lsetxattr -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S lsetxattr -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S lsetxattr -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S lsetxattr -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping these system +calls with others as identifying earlier in this guide is more efficient. + File permission changes could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + CCE-82110-8 + + + + + + Record Successful Access Attempts to Files - open + At a minimum, the audit system should collect unauthorized file +accesses for all users and root. If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: + +-a always,exit -F arch=b32 -S open -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-access + +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S open -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-access + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S open -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-access + +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S open -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-access + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping these system +calls with others as identifying earlier in this guide is more efficient. + File access attempts could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + CCE-81147-1 + + + + + + Record Successful Access Attempts to Files - open_by_handle_at + At a minimum, the audit system should collect unauthorized file +accesses for all users and root. If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: + +-a always,exit -F arch=b32 -S open_by_handle_at -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-access + +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S open_by_handle_at -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-access + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S open_by_handle_at -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-access + +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S open_by_handle_at -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-access + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping these system +calls with others as identifying earlier in this guide is more efficient. + File access attempts could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + CCE-82013-4 + + + + + + Record Successful Creation Attempts to Files - open_by_handle_at O_CREAT + The open_by_handle_at syscall can be used to create new files +when O_CREAT flag is specified. + +The following audit rules will assure that successful attempts to create a +file via open_by_handle_at syscall are collected. + +If the auditd daemon is configured to use the augenrules +program to read audit rules during daemon startup (the default), add the +rules below to a file with suffix .rules in the directory +/etc/audit/rules.d. + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the rules below to +/etc/audit/audit.rules file. + + +-a always,exit -F arch=b32 -S open_by_handle_at -F a2&0100 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create + + +If the system is 64 bit then also add the following lines: + +-a always,exit -F arch=b64 -S open_by_handle_at -F a2&0100 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create + + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping system calls related +to the same event is more efficient. See the following example: +-a always,exit -F arch=b32 -S open_by_handle_at,open_by_handle_at -F a2&0100 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create + Successful attempts to access files could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + CCE-81132-3 + + + + + + Record Successful Creation Attempts to Files - open_by_handle_at O_TRUNC_WRITE + The audit system should collect detailed file access records for +all users and root. The open_by_handle_at syscall can be used to modify +files if called for write operation with the O_TRUNC_WRITE flag. + +The following audit rules will assure that successful attempts to create a +file via open_by_handle_at syscall are collected. + +If the auditd daemon is configured to use the augenrules +program to read audit rules during daemon startup (the default), add the +rules below to a file with suffix .rules in the directory +/etc/audit/rules.d. + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the rules below to +/etc/audit/audit.rules file. + + +-a always,exit -F arch=b32 -S open_by_handle_at -F a2&01003 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification + + +If the system is 64 bit then also add the following lines: + +-a always,exit -F arch=b64 -S open_by_handle_at -F a2&01003 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification + + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping system calls related +to the same event is more efficient. See the following example: +-a always,exit -F arch=b32 -S open,open_by_handle_at -F a2&01003 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification + Successful attempts to access files could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + CCE-81141-4 + + + + + + Record Successful Creation Attempts to Files - open O_CREAT + The open syscall can be used to create new files +when O_CREAT flag is specified. + +The following audit rules will assure that successful attempts to create a +file via open syscall are collected. + +If the auditd daemon is configured to use the augenrules +program to read audit rules during daemon startup (the default), add the +rules below to a file with suffix .rules in the directory +/etc/audit/rules.d. + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the rules below to +/etc/audit/audit.rules file. + + +-a always,exit -F arch=b32 -S open -F a2&0100 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create + + +If the system is 64 bit then also add the following lines: + +-a always,exit -F arch=b64 -S open -F a2&0100 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create + + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping system calls related +to the same event is more efficient. See the following example: +-a always,exit -F arch=b32 -S open,open -F a2&0100 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create + Successful attempts to access files could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + CCE-81135-6 + + + + + + Record Successful Creation Attempts to Files - open O_TRUNC_WRITE + The audit system should collect detailed file access records for +all users and root. The open syscall can be used to modify +files if called for write operation with the O_TRUNC_WRITE flag. + +The following audit rules will assure that successful attempts to create a +file via open syscall are collected. + +If the auditd daemon is configured to use the augenrules +program to read audit rules during daemon startup (the default), add the +rules below to a file with suffix .rules in the directory +/etc/audit/rules.d. + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the rules below to +/etc/audit/audit.rules file. + + +-a always,exit -F arch=b32 -S open -F a2&01003 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification + + +If the system is 64 bit then also add the following lines: + +-a always,exit -F arch=b64 -S open -F a2&01003 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification + + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping system calls related +to the same event is more efficient. See the following example: +-a always,exit -F arch=b32 -S open,openat -F a2&01003 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification + Successful attempts to access files could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + CCE-81144-8 + + + + + + Record Successful Access Attempts to Files - openat + At a minimum, the audit system should collect unauthorized file +accesses for all users and root. If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: + +-a always,exit -F arch=b32 -S openat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-access + +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S openat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-access + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S openat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-access + +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S openat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-access + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping these system +calls with others as identifying earlier in this guide is more efficient. + File access attempts could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + CCE-82010-0 + + + + + + Record Successful Creation Attempts to Files - openat O_CREAT + The openat syscall can be used to create new files +when O_CREAT flag is specified. + +The following audit rules will assure that successful attempts to create a +file via openat syscall are collected. + +If the auditd daemon is configured to use the augenrules +program to read audit rules during daemon startup (the default), add the +rules below to a file with suffix .rules in the directory +/etc/audit/rules.d. + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the rules below to +/etc/audit/audit.rules file. + + +-a always,exit -F arch=b32 -S openat -F a2&0100 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create + + +If the system is 64 bit then also add the following lines: + +-a always,exit -F arch=b64 -S openat -F a2&0100 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create + + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping system calls related +to the same event is more efficient. See the following example: +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create + Successful attempts to access files could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + CCE-81128-1 + + + + + + Record Successful Creation Attempts to Files - openat O_TRUNC_WRITE + The audit system should collect detailed file access records for +all users and root. The openat syscall can be used to modify +files if called for write operation with the O_TRUNC_WRITE flag. + +The following audit rules will assure that successful attempts to create a +file via openat syscall are collected. + +If the auditd daemon is configured to use the augenrules +program to read audit rules during daemon startup (the default), add the +rules below to a file with suffix .rules in the directory +/etc/audit/rules.d. + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the rules below to +/etc/audit/audit.rules file. + + +-a always,exit -F arch=b32 -S openat -F a2&01003 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification + + +If the system is 64 bit then also add the following lines: + +-a always,exit -F arch=b64 -S openat -F a2&01003 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification + + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping system calls related +to the same event is more efficient. See the following example: +-a always,exit -F arch=b32 -S open,openat -F a2&01003 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification + Successful attempts to access files could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + CCE-81138-0 + + + + + + Record Successful Permission Changes to Files - removexattr + At a minimum, the audit system should collect file permission changes +for all users and root. If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: + +-a always,exit -F arch=b32 -S removexattr -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S removexattr -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S removexattr -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S removexattr -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping these system +calls with others as identifying earlier in this guide is more efficient. + File permission changes could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + CCE-82116-5 + + + + + + Record Successful Delete Attempts to Files - rename + At a minimum, the audit system should collect file +deletion for all users and root. If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: + +-a always,exit -F arch=b32 -S rename -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-delete + +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S rename -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-delete + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S rename -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-delete + +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S rename -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-delete + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping these system +calls with others as identifying earlier in this guide is more efficient. + File deletion attempts could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + CCE-82092-8 + + + + + + Record Successful Delete Attempts to Files - renameat + At a minimum, the audit system should collect file +deletion for all users and root. If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: + +-a always,exit -F arch=b32 -S renameat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-delete + +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S renameat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-delete + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S renameat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-delete + +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S renameat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-delete + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping these system +calls with others as identifying earlier in this guide is more efficient. + File deletion attempts could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + CCE-82095-1 + + + + + + Record Successful Permission Changes to Files - setxattr + At a minimum, the audit system should collect file permission changes +for all users and root. If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: + +-a always,exit -F arch=b32 -S setxattr -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S setxattr -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S setxattr -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S setxattr -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping these system +calls with others as identifying earlier in this guide is more efficient. + File deletion attempts could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + CCE-82107-4 + + + + + + Record Successful Access Attempts to Files - truncate + At a minimum, the audit system should collect unauthorized file +accesses for all users and root. If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: + +-a always,exit -F arch=b32 -S truncate -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-access + +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S truncate -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-access + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S truncate -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-access + +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S truncate -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-access + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping these system +calls with others as identifying earlier in this guide is more efficient. + File access attempts could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + CCE-82002-7 + + + + + + Record Successful Delete Attempts to Files - unlink + At a minimum, the audit system should collect file +deletion for all users and root. If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: + +-a always,exit -F arch=b32 -S unlink -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-delete + +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S unlink -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-delete + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S unlink -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-delete + +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S unlink -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-delete + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping these system +calls with others as identifying earlier in this guide is more efficient. + File deletion attempts could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + CCE-82086-0 + + + + + + Record Successful Delete Attempts to Files - unlinkat + At a minimum, the audit system should collect file +deletion for all users and root. If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: + +-a always,exit -F arch=b32 -S unlinkat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-delete + +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S unlinkat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-delete + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S unlinkat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-delete + +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S unlinkat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-delete + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping these system +calls with others as identifying earlier in this guide is more efficient. + File deletion attempts could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + CCE-82089-4 + + + + + + Ensure auditd Collects Unauthorized Access Attempts to Files (unsuccessful) + At a minimum the audit system should collect unauthorized file +accesses for all users and root. If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: +-a always,exit -F arch=b32 -S creat,open,openat,open_by_handle_at,truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=access +-a always,exit -F arch=b32 -S creat,open,openat,open_by_handle_at,truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=access +If the system is 64 bit then also add the following lines: + +-a always,exit -F arch=b64 -S creat,open,openat,open_by_handle_at,truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=access +-a always,exit -F arch=b64 -S creat,open,openat,open_by_handle_at,truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=access +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S creat,open,openat,open_by_handle_at,truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=access +-a always,exit -F arch=b32 -S creat,open,openat,open_by_handle_at,truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=access +If the system is 64 bit then also add the following lines: + +-a always,exit -F arch=b64 -S creat,open,openat,open_by_handle_at,truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=access +-a always,exit -F arch=b64 -S creat,open,openat,open_by_handle_at,truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=access + This rule checks for multiple syscalls related to unsuccessful file modification; +it was written with DISA STIG in mind. Other policies should use a +separate rule for each syscall that needs to be checked. For example: +audit_rules_unsuccessful_file_modification_openaudit_rules_unsuccessful_file_modification_ftruncateaudit_rules_unsuccessful_file_modification_creat + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 5.4.1.1 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000172 + CCI-002884 + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + 0582 + 0584 + 05885 + 0586 + 0846 + 0957 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + Req-10.2.4 + Req-10.2.1 + Unsuccessful attempts to access files could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + CCE-80750-3 + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# Perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +for ARCH in "${RULE_ARCHS[@]}" +do + + # First fix the -EACCES requirement + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EACCES" + AUID_FILTERS="-F auid>=1000 -F auid!=unset" + SYSCALL="creat open openat open_by_handle_at truncate ftruncate" + KEY="access" + SYSCALL_GROUPING="creat open openat open_by_handle_at truncate ftruncate" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + + # Then fix the -EPERM requirement + # No need to change content of $GROUP variable - it's the same as for -EACCES case above + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EPERM" + AUID_FILTERS="-F auid>=1000 -F auid!=unset" + SYSCALL="creat open openat open_by_handle_at truncate ftruncate" + KEY="access" + SYSCALL_GROUPING="creat open openat open_by_handle_at truncate ftruncate" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Unsuccessful Permission Changes to Files - chmod + The audit system should collect unsuccessful file permission change +attempts for all users and root. +If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d. +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file. +-a always,exit -F arch=b32 -S chmod -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change +-a always,exit -F arch=b32 -S chmod -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S chmod -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change +-a always,exit -F arch=b64 -S chmod -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the audit rule checks a +system call independently of other system calls. Grouping system calls related +to the same event is more efficient. See the following example: +-a always,exit -F arch=b32 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change + CCI-000172 + AU-2(d) + AU-12(c) + CM-6(a) + SRG-OS-000458-VMM-001810 + SRG-OS-000461-VMM-001830 + Unsuccessful attempts to change permissions of files could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + CCE-80975-6 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80975-6 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_chmod + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit chmod tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80975-6 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_chmod + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for chmod EACCES for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - chmod + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of chmod in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - chmod + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of chmod in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80975-6 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_chmod + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for chmod EACCES for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - chmod + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of chmod in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - chmod + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of chmod in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-80975-6 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_chmod + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for chmod EPERM for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - chmod + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of chmod in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - chmod + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of chmod in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80975-6 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_chmod + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for chmod EPERM for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - chmod + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of chmod in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - chmod + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of chmod in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-80975-6 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_chmod + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="chmod" +KEY="access" +SYSCALL_GROUPING="chmod fchmod fchmodat fsetxattr lsetxattr setxattr" + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EACCES" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EPERM" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Unsuccessful Ownership Changes to Files - chown + The audit system should collect unsuccessful file ownership change +attempts for all users and root. +If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d. +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file. +-a always,exit -F arch=b32 -S chown -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change +-a always,exit -F arch=b32 -S chown -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S chown -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change +-a always,exit -F arch=b64 -S chown -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the audit rule checks a +system call independently of other system calls. Grouping system calls related +to the same event is more efficient. See the following example: +-a always,exit -F arch=b32 -S lchown,fchown,chown,fchownat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change + CCI-000172 + AU-2(d) + AU-12(c) + CM-6(a) + SRG-OS-000458-VMM-001810 + SRG-OS-000461-VMM-001830 + Unsuccessful attempts to change ownership of files could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + CCE-80984-8 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80984-8 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_chown + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit chown tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80984-8 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_chown + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for chown EACCES for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - chown + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of chown in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - chown + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of chown in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80984-8 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_chown + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for chown EACCES for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - chown + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of chown in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - chown + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of chown in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-80984-8 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_chown + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for chown EPERM for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - chown + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of chown in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - chown + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of chown in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80984-8 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_chown + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for chown EPERM for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - chown + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of chown in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - chown + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of chown in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-80984-8 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_chown + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="chown" +KEY="access" +SYSCALL_GROUPING="chown fchown fchownat lchown" + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EACCES" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EPERM" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Unsuccessful Access Attempts to Files - creat + At a minimum, the audit system should collect unauthorized file +accesses for all users and root. If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: +-a always,exit -F arch=b32 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=access +-a always,exit -F arch=b32 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=access +If the system is 64 bit then also add the following lines: + +-a always,exit -F arch=b64 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=access +-a always,exit -F arch=b64 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=access +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=access +-a always,exit -F arch=b32 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=access +If the system is 64 bit then also add the following lines: + +-a always,exit -F arch=b64 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=access +-a always,exit -F arch=b64 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=access + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping these system +calls with others as identifying earlier in this guide is more efficient. + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.2.4 + Req-10.2.1 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + SRG-OS-000064-GPOS-00033 + SRG-OS-000458-GPOS-00203 + SRG-OS-000461-GPOS-00205 + SRG-OS-000458-VMM-001810 + SRG-OS-000461-VMM-001830 + RHEL-08-030420 + 4.1.3.7 + SV-230449r810455_rule + Unsuccessful attempts to access files could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + CCE-80751-1 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80751-1 + - DISA-STIG-RHEL-08-030420 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_creat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit creat tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80751-1 + - DISA-STIG-RHEL-08-030420 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_creat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for creat EACCES for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - creat + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of creat in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - creat + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of creat in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80751-1 + - DISA-STIG-RHEL-08-030420 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_creat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for creat EACCES for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - creat + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of creat in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - creat + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of creat in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-80751-1 + - DISA-STIG-RHEL-08-030420 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_creat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for creat EPERM for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - creat + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of creat in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - creat + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of creat in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80751-1 + - DISA-STIG-RHEL-08-030420 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_creat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for creat EPERM for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - creat + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of creat in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - creat + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of creat in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-80751-1 + - DISA-STIG-RHEL-08-030420 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_creat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="creat" +KEY="access" +SYSCALL_GROUPING="creat ftruncate truncate open openat open_by_handle_at" + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EACCES" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EPERM" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Unsuccessful Permission Changes to Files - fchmod + The audit system should collect unsuccessful file permission change +attempts for all users and root. +If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d. +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file. +-a always,exit -F arch=b32 -S fchmod -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change +-a always,exit -F arch=b32 -S fchmod -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S fchmod -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change +-a always,exit -F arch=b64 -S fchmod -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the audit rule checks a +system call independently of other system calls. Grouping system calls related +to the same event is more efficient. See the following example: +-a always,exit -F arch=b32 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change + CCI-000172 + AU-2(d) + AU-12(c) + CM-6(a) + SRG-OS-000458-VMM-001810 + SRG-OS-000461-VMM-001830 + Unsuccessful attempts to change permissions of files could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + CCE-80977-2 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80977-2 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_fchmod + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit fchmod tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80977-2 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_fchmod + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for fchmod EACCES for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchmod + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of fchmod in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchmod + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of fchmod in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80977-2 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_fchmod + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for fchmod EACCES for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchmod + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of fchmod in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchmod + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of fchmod in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-80977-2 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_fchmod + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for fchmod EPERM for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchmod + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of fchmod in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchmod + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of fchmod in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80977-2 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_fchmod + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for fchmod EPERM for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchmod + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of fchmod in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchmod + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of fchmod in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-80977-2 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_fchmod + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="fchmod" +KEY="access" +SYSCALL_GROUPING="chmod fchmod fchmodat fsetxattr lsetxattr setxattr" + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EACCES" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EPERM" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Unsuccessful Permission Changes to Files - fchmodat + The audit system should collect unsuccessful file permission change +attempts for all users and root. +If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d. +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file. +-a always,exit -F arch=b32 -S fchmodat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change +-a always,exit -F arch=b32 -S fchmodat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S fchmodat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change +-a always,exit -F arch=b64 -S fchmodat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the audit rule checks a +system call independently of other system calls. Grouping system calls related +to the same event is more efficient. See the following example: +-a always,exit -F arch=b32 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change + CCI-000172 + AU-2(d) + AU-12(c) + CM-6(a) + SRG-OS-000458-VMM-001810 + SRG-OS-000461-VMM-001830 + Unsuccessful attempts to change permissions of files could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + CCE-80976-4 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80976-4 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_fchmodat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit fchmodat tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80976-4 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_fchmodat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for fchmodat EACCES for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchmodat + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of fchmodat in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchmodat + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of fchmodat in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80976-4 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_fchmodat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for fchmodat EACCES for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchmodat + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of fchmodat in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchmodat + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of fchmodat in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-80976-4 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_fchmodat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for fchmodat EPERM for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchmodat + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of fchmodat in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchmodat + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of fchmodat in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80976-4 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_fchmodat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for fchmodat EPERM for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchmodat + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of fchmodat in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchmodat + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of fchmodat in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-80976-4 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_fchmodat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="fchmodat" +KEY="access" +SYSCALL_GROUPING="chmod fchmod fchmodat fsetxattr lsetxattr setxattr" + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EACCES" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EPERM" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Unsuccessful Ownership Changes to Files - fchown + The audit system should collect unsuccessful file ownership change +attempts for all users and root. +If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d. +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file. +-a always,exit -F arch=b32 -S fchown -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change +-a always,exit -F arch=b32 -S fchown -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S fchown -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change +-a always,exit -F arch=b64 -S fchown -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the audit rule checks a +system call independently of other system calls. Grouping system calls related +to the same event is more efficient. See the following example: +-a always,exit -F arch=b32 -S lchown,fchown,chown,fchownat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change + CCI-000172 + AU-2(d) + AU-12(c) + CM-6(a) + SRG-OS-000458-VMM-001810 + SRG-OS-000461-VMM-001830 + Unsuccessful attempts to change ownership of files could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + CCE-80986-3 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80986-3 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_fchown + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit fchown tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80986-3 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_fchown + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for fchown EACCES for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchown + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of fchown in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchown + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of fchown in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80986-3 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_fchown + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for fchown EACCES for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchown + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of fchown in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchown + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of fchown in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-80986-3 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_fchown + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for fchown EPERM for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchown + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of fchown in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchown + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of fchown in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80986-3 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_fchown + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for fchown EPERM for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchown + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of fchown in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchown + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of fchown in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-80986-3 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_fchown + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="fchown" +KEY="access" +SYSCALL_GROUPING="chown fchown fchownat lchown" + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EACCES" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EPERM" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Unsuccessful Ownership Changes to Files - fchownat + The audit system should collect unsuccessful file ownership change +attempts for all users and root. +If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d. +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file. +-a always,exit -F arch=b32 -S fchownat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change +-a always,exit -F arch=b32 -S fchownat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S fchownat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change +-a always,exit -F arch=b64 -S fchownat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the audit rule checks a +system call independently of other system calls. Grouping system calls related +to the same event is more efficient. See the following example: +-a always,exit -F arch=b32 -S lchown,fchown,chown,fchownat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change + CCI-000172 + AU-2(d) + AU-12(c) + CM-6(a) + SRG-OS-000458-VMM-001810 + SRG-OS-000461-VMM-001830 + Unsuccessful attempts to change ownership of files could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + CCE-80985-5 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80985-5 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_fchownat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit fchownat tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80985-5 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_fchownat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for fchownat EACCES for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchownat + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of fchownat in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchownat + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of fchownat in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80985-5 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_fchownat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for fchownat EACCES for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchownat + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of fchownat in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchownat + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of fchownat in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-80985-5 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_fchownat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for fchownat EPERM for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchownat + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of fchownat in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchownat + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of fchownat in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80985-5 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_fchownat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for fchownat EPERM for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchownat + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of fchownat in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchownat + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of fchownat in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-80985-5 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_fchownat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="fchownat" +KEY="access" +SYSCALL_GROUPING="chown fchown fchownat lchown" + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EACCES" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EPERM" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Unsuccessful Permission Changes to Files - fremovexattr + The audit system should collect unsuccessful file permission change +attempts for all users and root. +If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d. +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file. +-a always,exit -F arch=b32 -S fremovexattr -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change +-a always,exit -F arch=b32 -S fremovexattr -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S fremovexattr -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change +-a always,exit -F arch=b64 -S fremovexattr -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the audit rule checks a +system call independently of other system calls. Grouping system calls related +to the same event is more efficient. See the following example: +-a always,exit -F arch=b32 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change + CCI-000172 + AU-2(d) + AU-12(c) + CM-6(a) + SRG-OS-000458-VMM-001810 + SRG-OS-000461-VMM-001830 + Unsuccessful attempts to change permissions of files could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + CCE-80978-0 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80978-0 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_fremovexattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit fremovexattr tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80978-0 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_fremovexattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for fremovexattr EACCES for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - fremovexattr + syscall_grouping: [] + + - name: Check existence of fremovexattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - fremovexattr + syscall_grouping: [] + + - name: Check existence of fremovexattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80978-0 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_fremovexattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for fremovexattr EACCES for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - fremovexattr + syscall_grouping: [] + + - name: Check existence of fremovexattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - fremovexattr + syscall_grouping: [] + + - name: Check existence of fremovexattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-80978-0 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_fremovexattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for fremovexattr EPERM for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - fremovexattr + syscall_grouping: [] + + - name: Check existence of fremovexattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - fremovexattr + syscall_grouping: [] + + - name: Check existence of fremovexattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80978-0 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_fremovexattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for fremovexattr EPERM for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - fremovexattr + syscall_grouping: [] + + - name: Check existence of fremovexattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - fremovexattr + syscall_grouping: [] + + - name: Check existence of fremovexattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-80978-0 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_fremovexattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="fremovexattr" +KEY="access" +SYSCALL_GROUPING="" + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EACCES" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EPERM" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Unsuccessful Permission Changes to Files - fsetxattr + The audit system should collect unsuccessful file permission change +attempts for all users and root. +If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d. +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file. +-a always,exit -F arch=b32 -S fsetxattr -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change +-a always,exit -F arch=b32 -S fsetxattr -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S fsetxattr -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change +-a always,exit -F arch=b64 -S fsetxattr -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the audit rule checks a +system call independently of other system calls. Grouping system calls related +to the same event is more efficient. See the following example: +-a always,exit -F arch=b32 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change + CCI-000172 + AU-2(d) + AU-12(c) + CM-6(a) + SRG-OS-000458-VMM-001810 + SRG-OS-000461-VMM-001830 + Unsuccessful attempts to change permissions of files could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + CCE-80979-8 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80979-8 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_fsetxattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit fsetxattr tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80979-8 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_fsetxattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for fsetxattr EACCES for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - fsetxattr + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of fsetxattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - fsetxattr + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of fsetxattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80979-8 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_fsetxattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for fsetxattr EACCES for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - fsetxattr + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of fsetxattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - fsetxattr + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of fsetxattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-80979-8 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_fsetxattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for fsetxattr EPERM for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - fsetxattr + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of fsetxattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - fsetxattr + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of fsetxattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80979-8 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_fsetxattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for fsetxattr EPERM for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - fsetxattr + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of fsetxattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - fsetxattr + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of fsetxattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-80979-8 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_fsetxattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="fsetxattr" +KEY="access" +SYSCALL_GROUPING="chmod fchmod fchmodat fsetxattr lsetxattr setxattr" + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EACCES" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EPERM" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Unsuccessful Access Attempts to Files - ftruncate + At a minimum, the audit system should collect unauthorized file +accesses for all users and root. If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: +-a always,exit -F arch=b32 -S ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=access +-a always,exit -F arch=b32 -S ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=access + +If the system is 64 bit then also add the following lines: + +-a always,exit -F arch=b64 -S ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=access +-a always,exit -F arch=b64 -S ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=access + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=access +-a always,exit -F arch=b32 -S ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=access + +If the system is 64 bit then also add the following lines: + +-a always,exit -F arch=b64 -S ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=access +-a always,exit -F arch=b64 -S ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=access + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping these system +calls with others as identifying earlier in this guide is more efficient. + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.2.4 + Req-10.2.1 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + SRG-OS-000064-GPOS-00033 + SRG-OS-000458-GPOS-00203 + SRG-OS-000461-GPOS-00205 + SRG-OS-000458-VMM-001810 + SRG-OS-000461-VMM-001830 + RHEL-08-030420 + 4.1.3.7 + SV-230449r810455_rule + Unsuccessful attempts to access files could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + CCE-80752-9 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80752-9 + - DISA-STIG-RHEL-08-030420 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_ftruncate + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit ftruncate tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80752-9 + - DISA-STIG-RHEL-08-030420 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_ftruncate + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for ftruncate EACCES for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - ftruncate + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of ftruncate in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - ftruncate + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of ftruncate in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80752-9 + - DISA-STIG-RHEL-08-030420 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_ftruncate + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for ftruncate EACCES for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - ftruncate + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of ftruncate in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - ftruncate + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of ftruncate in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-80752-9 + - DISA-STIG-RHEL-08-030420 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_ftruncate + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for ftruncate EPERM for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - ftruncate + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of ftruncate in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - ftruncate + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of ftruncate in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80752-9 + - DISA-STIG-RHEL-08-030420 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_ftruncate + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for ftruncate EPERM for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - ftruncate + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of ftruncate in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - ftruncate + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of ftruncate in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-80752-9 + - DISA-STIG-RHEL-08-030420 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_ftruncate + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="ftruncate" +KEY="access" +SYSCALL_GROUPING="creat ftruncate truncate open openat open_by_handle_at" + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EACCES" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EPERM" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Unsuccessful Ownership Changes to Files - lchown + The audit system should collect unsuccessful file ownership change +attempts for all users and root. + +If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d. + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file. + +-a always,exit -F arch=b32 -S lchown -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change +-a always,exit -F arch=b32 -S lchown -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change + +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S lchown -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change +-a always,exit -F arch=b64 -S lchown -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the audit rule checks a +system call independently of other system calls. Grouping system calls related +to the same event is more efficient. See the following example: +-a always,exit -F arch=b32 -S lchown,fchown,chown,fchownat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change + CCI-000172 + AU-2(d) + AU-12(c) + CM-6(a) + SRG-OS-000458-VMM-001810 + SRG-OS-000461-VMM-001830 + Unsuccessful attempts to change ownership of files could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + CCE-80987-1 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80987-1 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_lchown + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit lchown tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80987-1 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_lchown + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for lchown EACCES for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - lchown + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of lchown in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - lchown + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of lchown in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80987-1 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_lchown + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for lchown EACCES for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - lchown + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of lchown in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - lchown + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of lchown in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-80987-1 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_lchown + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for lchown EPERM for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - lchown + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of lchown in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - lchown + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of lchown in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80987-1 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_lchown + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for lchown EPERM for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - lchown + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of lchown in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - lchown + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of lchown in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-80987-1 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_lchown + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="lchown" +KEY="access" +SYSCALL_GROUPING="chown fchown fchownat lchown" + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EACCES" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EPERM" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Unsuccessful Permission Changes to Files - lremovexattr + The audit system should collect unsuccessful file permission change +attempts for all users and root. +If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d. +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file. +-a always,exit -F arch=b32 -S lremovexattr -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change +-a always,exit -F arch=b32 -S lremovexattr -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S lremovexattr -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change +-a always,exit -F arch=b64 -S lremovexattr -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the audit rule checks a +system call independently of other system calls. Grouping system calls related +to the same event is more efficient. See the following example: +-a always,exit -F arch=b32 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change + CCI-000172 + AU-2(d) + AU-12(c) + CM-6(a) + SRG-OS-000458-VMM-001810 + SRG-OS-000461-VMM-001830 + Unsuccessful attempts to change permissions of files could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + CCE-80980-6 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80980-6 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_lremovexattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit lremovexattr tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80980-6 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_lremovexattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for lremovexattr EACCES for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - lremovexattr + syscall_grouping: [] + + - name: Check existence of lremovexattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - lremovexattr + syscall_grouping: [] + + - name: Check existence of lremovexattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80980-6 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_lremovexattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for lremovexattr EACCES for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - lremovexattr + syscall_grouping: [] + + - name: Check existence of lremovexattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - lremovexattr + syscall_grouping: [] + + - name: Check existence of lremovexattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-80980-6 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_lremovexattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for lremovexattr EPERM for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - lremovexattr + syscall_grouping: [] + + - name: Check existence of lremovexattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - lremovexattr + syscall_grouping: [] + + - name: Check existence of lremovexattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80980-6 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_lremovexattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for lremovexattr EPERM for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - lremovexattr + syscall_grouping: [] + + - name: Check existence of lremovexattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - lremovexattr + syscall_grouping: [] + + - name: Check existence of lremovexattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-80980-6 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_lremovexattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="lremovexattr" +KEY="access" +SYSCALL_GROUPING="" + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EACCES" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EPERM" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Unsuccessful Permission Changes to Files - lsetxattr + The audit system should collect unsuccessful file permission change +attempts for all users and root. +If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d. +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file. +-a always,exit -F arch=b32 -S lsetxattr -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change +-a always,exit -F arch=b32 -S lsetxattr -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S lsetxattr -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change +-a always,exit -F arch=b64 -S lsetxattr -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the audit rule checks a +system call independently of other system calls. Grouping system calls related +to the same event is more efficient. See the following example: +-a always,exit -F arch=b32 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change + CCI-000172 + AU-2(d) + AU-12(c) + CM-6(a) + SRG-OS-000458-VMM-001810 + SRG-OS-000461-VMM-001830 + Unsuccessful attempts to change permissions of files could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + CCE-80981-4 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80981-4 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_lsetxattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit lsetxattr tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80981-4 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_lsetxattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for lsetxattr EACCES for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - lsetxattr + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of lsetxattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - lsetxattr + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of lsetxattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80981-4 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_lsetxattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for lsetxattr EACCES for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - lsetxattr + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of lsetxattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - lsetxattr + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of lsetxattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-80981-4 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_lsetxattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for lsetxattr EPERM for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - lsetxattr + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of lsetxattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - lsetxattr + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of lsetxattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80981-4 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_lsetxattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for lsetxattr EPERM for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - lsetxattr + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of lsetxattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - lsetxattr + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of lsetxattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-80981-4 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_lsetxattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="lsetxattr" +KEY="access" +SYSCALL_GROUPING="chmod fchmod fchmodat fsetxattr lsetxattr setxattr" + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EACCES" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EPERM" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Unsuccessful Access Attempts to Files - open + At a minimum, the audit system should collect unauthorized file +accesses for all users and root. If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: +-a always,exit -F arch=b32 -S open -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=access +-a always,exit -F arch=b32 -S open -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=access + +If the system is 64 bit then also add the following lines: + +-a always,exit -F arch=b64 -S open -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=access +-a always,exit -F arch=b64 -S open -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=access + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S open -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=access +-a always,exit -F arch=b32 -S open -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=access + +If the system is 64 bit then also add the following lines: + +-a always,exit -F arch=b64 -S open -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=access +-a always,exit -F arch=b64 -S open -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=access + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping these system +calls with others as identifying earlier in this guide is more efficient. + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.2.4 + Req-10.2.1 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + SRG-OS-000064-GPOS-00033 + SRG-OS-000458-GPOS-00203 + SRG-OS-000461-GPOS-00205 + SRG-OS-000458-VMM-001810 + SRG-OS-000461-VMM-001830 + RHEL-08-030420 + 4.1.3.7 + SV-230449r810455_rule + Unsuccessful attempts to access files could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + CCE-80753-7 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80753-7 + - DISA-STIG-RHEL-08-030420 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_open + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit open tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80753-7 + - DISA-STIG-RHEL-08-030420 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_open + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for open EACCES for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - open + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of open in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - open + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of open in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80753-7 + - DISA-STIG-RHEL-08-030420 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_open + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for open EACCES for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - open + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of open in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - open + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of open in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-80753-7 + - DISA-STIG-RHEL-08-030420 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_open + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for open EPERM for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - open + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of open in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - open + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of open in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80753-7 + - DISA-STIG-RHEL-08-030420 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_open + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for open EPERM for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - open + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of open in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - open + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of open in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-80753-7 + - DISA-STIG-RHEL-08-030420 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_open + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="open" +KEY="access" +SYSCALL_GROUPING="creat ftruncate truncate open openat open_by_handle_at" + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EACCES" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EPERM" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Unsuccessful Access Attempts to Files - open_by_handle_at + At a minimum, the audit system should collect unauthorized file +accesses for all users and root. If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: +-a always,exit -F arch=b32 -S open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=access +-a always,exit -F arch=b32 -S open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=access +If the system is 64 bit then also add the following lines: + +-a always,exit -F arch=b64 -S open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=access +-a always,exit -F arch=b64 -S open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=access +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S open_by_handle_at,truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=access +-a always,exit -F arch=b32 -S open_by_handle_at,truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=access +If the system is 64 bit then also add the following lines: + +-a always,exit -F arch=b64 -S open_by_handle_at,truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=access +-a always,exit -F arch=b64 -S open_by_handle_at,truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=access + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping these system +calls with others as identifying earlier in this guide is more efficient. + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.2.4 + Req-10.2.1 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + SRG-OS-000064-GPOS-00033 + SRG-OS-000458-GPOS-00203 + SRG-OS-000461-GPOS-00205 + SRG-OS-000458-VMM-001810 + SRG-OS-000461-VMM-001830 + RHEL-08-030420 + 4.1.10 + SV-230449r810455_rule + Unsuccessful attempts to access files could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + CCE-80755-2 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80755-2 + - DISA-STIG-RHEL-08-030420 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_open_by_handle_at + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit open_by_handle_at tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80755-2 + - DISA-STIG-RHEL-08-030420 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_open_by_handle_at + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for open_by_handle_at EACCES for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - open_by_handle_at + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of open_by_handle_at in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - open_by_handle_at + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of open_by_handle_at in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80755-2 + - DISA-STIG-RHEL-08-030420 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_open_by_handle_at + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for open_by_handle_at EACCES for x86_64 + platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - open_by_handle_at + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of open_by_handle_at in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - open_by_handle_at + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of open_by_handle_at in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-80755-2 + - DISA-STIG-RHEL-08-030420 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_open_by_handle_at + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for open_by_handle_at EPERM for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - open_by_handle_at + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of open_by_handle_at in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - open_by_handle_at + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of open_by_handle_at in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80755-2 + - DISA-STIG-RHEL-08-030420 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_open_by_handle_at + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for open_by_handle_at EPERM for x86_64 + platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - open_by_handle_at + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of open_by_handle_at in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - open_by_handle_at + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of open_by_handle_at in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-80755-2 + - DISA-STIG-RHEL-08-030420 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_open_by_handle_at + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="open_by_handle_at" +KEY="access" +SYSCALL_GROUPING="creat ftruncate truncate open openat open_by_handle_at" + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EACCES" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EPERM" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Unsuccessful Creation Attempts to Files - open_by_handle_at O_CREAT + The audit system should collect unauthorized file accesses for +all users and root. The open_by_handle_at syscall can be used to create new files +when O_CREAT flag is specified. + +The following auidt rules will asure that unsuccessful attempts to create a +file via open_by_handle_at syscall are collected. + +If the auditd daemon is configured to use the augenrules +program to read audit rules during daemon startup (the default), add the +rules below to a file with suffix .rules in the directory +/etc/audit/rules.d. + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the rules below to +/etc/audit/audit.rules file. + +-a always,exit -F arch=b32 -S open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + + +If the system is 64 bit then also add the following lines: + +-a always,exit -F arch=b64 -S open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping system calls related +to the same event is more efficient. See the following example: +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.2.4 + Req-10.2.1 + SRG-OS-000064-GPOS-00033 + SRG-OS-000458-GPOS-00203 + SRG-OS-000461-GPOS-00205 + SRG-OS-000392-GPOS-00172 + SRG-OS-000458-VMM-001810 + SRG-OS-000461-VMM-001830 + Unsuccessful attempts to access files could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + CCE-80965-7 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80965-7 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_open_by_handle_at_o_creat + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Add unsuccessful file operations audit rules + blockinfile: + path: /etc/audit/rules.d/30-ospp-v42-remediation.rules + create: true + block: |- + ## This content is a section of an Audit config snapshot recommended for Red Hat Enterprise Linux 8 systems that target OSPP compliance. + ## The following content has been retreived on 2019-03-11 from: https://github.com/linux-audit/audit-userspace/blob/master/rules/30-ospp-v42.rules + + ## The purpose of these rules is to meet the requirements for Operating + ## System Protection Profile (OSPP)v4.2. These rules depends on having + ## 10-base-config.rules, 11-loginuid.rules, and 43-module-load.rules installed. + + ## Unsuccessful file creation (open with O_CREAT) + -a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b32 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b64 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b32 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b64 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + + ## Unsuccessful file modifications (open for write or truncate) + -a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b32 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b32 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + + ## Unsuccessful file access (any other opens) This has to go last. + -a always,exit -F arch=b32 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-access + -a always,exit -F arch=b64 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-access + -a always,exit -F arch=b32 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-access + -a always,exit -F arch=b64 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-access + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80965-7 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_open_by_handle_at_o_creat + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +mkdir -p "$(dirname '/etc/audit/rules.d/30-ospp-v42-remediation.rules')" +cat <<EOF > "/etc/audit/rules.d/30-ospp-v42-remediation.rules" +## This content is a section of an Audit config snapshot recommended for linux systems that target OSPP compliance. +## The following content has been retreived on 2019-03-11 from: https://github.com/linux-audit/audit-userspace/blob/master/rules/30-ospp-v42.rules + +## The purpose of these rules is to meet the requirements for Operating +## System Protection Profile (OSPP)v4.2. These rules depends on having +## 10-base-config.rules, 11-loginuid.rules, and 43-module-load.rules installed. + +## Unsuccessful file creation (open with O_CREAT) +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + +## Unsuccessful file modifications (open for write or truncate) +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + +## Unsuccessful file access (any other opens) This has to go last. +-a always,exit -F arch=b32 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-access +-a always,exit -F arch=b64 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-access +-a always,exit -F arch=b32 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-access +-a always,exit -F arch=b64 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-access +EOF + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Unsuccessful Modification Attempts to Files - open_by_handle_at O_TRUNC_WRITE + The audit system should collect detailed unauthorized file accesses for +all users and root. The open_by_handle_at syscall can be used to modify files +if called for write operation of with O_TRUNC_WRITE flag. + +The following auidt rules will asure that unsuccessful attempts to modify a +file via open_by_handle_at syscall are collected. + +If the auditd daemon is configured to use the augenrules +program to read audit rules during daemon startup (the default), add the +rules below to a file with suffix .rules in the directory +/etc/audit/rules.d. + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the rules below to +/etc/audit/audit.rules file. + +-a always,exit -F arch=b32 -S open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + + +If the system is 64 bit then also add the following lines: + +-a always,exit -F arch=b64 -S open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping system calls related +to the same event is more efficient. See the following example: +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.2.4 + Req-10.2.1 + SRG-OS-000064-GPOS-00033 + SRG-OS-000458-GPOS-00203 + SRG-OS-000461-GPOS-00205 + SRG-OS-000392-GPOS-00172 + SRG-OS-000458-VMM-001810 + SRG-OS-000461-VMM-001830 + Unsuccessful attempts to access files could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + CCE-80966-5 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80966-5 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_open_by_handle_at_o_trunc_write + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Add unsuccessful file operations audit rules + blockinfile: + path: /etc/audit/rules.d/30-ospp-v42-remediation.rules + create: true + block: |- + ## This content is a section of an Audit config snapshot recommended for Red Hat Enterprise Linux 8 systems that target OSPP compliance. + ## The following content has been retreived on 2019-03-11 from: https://github.com/linux-audit/audit-userspace/blob/master/rules/30-ospp-v42.rules + + ## The purpose of these rules is to meet the requirements for Operating + ## System Protection Profile (OSPP)v4.2. These rules depends on having + ## 10-base-config.rules, 11-loginuid.rules, and 43-module-load.rules installed. + + ## Unsuccessful file creation (open with O_CREAT) + -a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b32 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b64 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b32 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b64 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + + ## Unsuccessful file modifications (open for write or truncate) + -a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b32 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b32 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + + ## Unsuccessful file access (any other opens) This has to go last. + -a always,exit -F arch=b32 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-access + -a always,exit -F arch=b64 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-access + -a always,exit -F arch=b32 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-access + -a always,exit -F arch=b64 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-access + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80966-5 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_open_by_handle_at_o_trunc_write + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +mkdir -p "$(dirname '/etc/audit/rules.d/30-ospp-v42-remediation.rules')" +cat <<EOF > "/etc/audit/rules.d/30-ospp-v42-remediation.rules" +## This content is a section of an Audit config snapshot recommended for linux systems that target OSPP compliance. +## The following content has been retreived on 2019-03-11 from: https://github.com/linux-audit/audit-userspace/blob/master/rules/30-ospp-v42.rules + +## The purpose of these rules is to meet the requirements for Operating +## System Protection Profile (OSPP)v4.2. These rules depends on having +## 10-base-config.rules, 11-loginuid.rules, and 43-module-load.rules installed. + +## Unsuccessful file creation (open with O_CREAT) +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + +## Unsuccessful file modifications (open for write or truncate) +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + +## Unsuccessful file access (any other opens) This has to go last. +-a always,exit -F arch=b32 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-access +-a always,exit -F arch=b64 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-access +-a always,exit -F arch=b32 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-access +-a always,exit -F arch=b64 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-access +EOF + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure auditd Unauthorized Access Attempts To open_by_handle_at Are Ordered Correctly + The audit system should collect detailed unauthorized file +accesses for all users and root. +To correctly identify unsuccessful creation, unsuccessful modification and unsuccessful access +of files via open_by_handle_at syscall the audit rules collecting these events need to be in certain order. +The more specific rules need to come before the less specific rules. The reason for that is that more +specific rules cover a subset of events covered in the less specific rules, thus, they need to come +before to not be overshadowed by less specific rules, which match a bigger set of events. +Make sure that rules for unsuccessful calls of open_by_handle_at syscall are in the order shown below. +If the auditd daemon is configured to use the augenrules +program to read audit rules during daemon startup (the default), check the order of +rules below in a file with suffix .rules in the directory +/etc/audit/rules.d. +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, check the order of rules below in +/etc/audit/audit.rules file. + +-a always,exit -F arch=b32 -S open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-access +-a always,exit -F arch=b32 -S open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-access + +If the system is 64 bit then also add the following lines: + +-a always,exit -F arch=b64 -S open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-access +-a always,exit -F arch=b64 -S open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-access + + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.2.4 + Req-10.2.1 + SRG-OS-000064-GPOS-00033 + SRG-OS-000458-GPOS-00203 + SRG-OS-000461-GPOS-00205 + SRG-OS-000392-GPOS-00172 + SRG-OS-000458-VMM-001810 + SRG-OS-000461-VMM-001830 + The more specific rules cover a subset of events covered by the less specific rules. +By ordering them from more specific to less specific, it is assured that the less specific +rule will not catch events better recorded by the more specific rule. + CCE-80967-3 + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +mkdir -p "$(dirname '/etc/audit/rules.d/30-ospp-v42-remediation.rules')" +cat <<EOF > "/etc/audit/rules.d/30-ospp-v42-remediation.rules" +## This content is a section of an Audit config snapshot recommended for linux systems that target OSPP compliance. +## The following content has been retreived on 2019-03-11 from: https://github.com/linux-audit/audit-userspace/blob/master/rules/30-ospp-v42.rules + +## The purpose of these rules is to meet the requirements for Operating +## System Protection Profile (OSPP)v4.2. These rules depends on having +## 10-base-config.rules, 11-loginuid.rules, and 43-module-load.rules installed. + +## Unsuccessful file creation (open with O_CREAT) +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + +## Unsuccessful file modifications (open for write or truncate) +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + +## Unsuccessful file access (any other opens) This has to go last. +-a always,exit -F arch=b32 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-access +-a always,exit -F arch=b64 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-access +-a always,exit -F arch=b32 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-access +-a always,exit -F arch=b64 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-access +EOF + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Unsuccessful Creation Attempts to Files - open O_CREAT + The audit system should collect unauthorized file accesses for +all users and root. The open syscall can be used to create new files +when O_CREAT flag is specified. + +The following auidt rules will asure that unsuccessful attempts to create a +file via open syscall are collected. + +If the auditd daemon is configured to use the augenrules +program to read audit rules during daemon startup (the default), add the +rules below to a file with suffix .rules in the directory +/etc/audit/rules.d. + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the rules below to +/etc/audit/audit.rules file. + +-a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + + +If the system is 64 bit then also add the following lines: + +-a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping system calls related +to the same event is more efficient. See the following example: +-a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.2.4 + Req-10.2.1 + SRG-OS-000064-GPOS-00033 + SRG-OS-000458-GPOS-00203 + SRG-OS-000461-GPOS-00205 + SRG-OS-000392-GPOS-00172 + SRG-OS-000458-VMM-001810 + SRG-OS-000461-VMM-001830 + Unsuccessful attempts to access files could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + CCE-80968-1 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80968-1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_open_o_creat + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Add unsuccessful file operations audit rules + blockinfile: + path: /etc/audit/rules.d/30-ospp-v42-remediation.rules + create: true + block: |- + ## This content is a section of an Audit config snapshot recommended for Red Hat Enterprise Linux 8 systems that target OSPP compliance. + ## The following content has been retreived on 2019-03-11 from: https://github.com/linux-audit/audit-userspace/blob/master/rules/30-ospp-v42.rules + + ## The purpose of these rules is to meet the requirements for Operating + ## System Protection Profile (OSPP)v4.2. These rules depends on having + ## 10-base-config.rules, 11-loginuid.rules, and 43-module-load.rules installed. + + ## Unsuccessful file creation (open with O_CREAT) + -a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b32 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b64 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b32 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b64 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + + ## Unsuccessful file modifications (open for write or truncate) + -a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b32 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b32 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + + ## Unsuccessful file access (any other opens) This has to go last. + -a always,exit -F arch=b32 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-access + -a always,exit -F arch=b64 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-access + -a always,exit -F arch=b32 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-access + -a always,exit -F arch=b64 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-access + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80968-1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_open_o_creat + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +mkdir -p "$(dirname '/etc/audit/rules.d/30-ospp-v42-remediation.rules')" +cat <<EOF > "/etc/audit/rules.d/30-ospp-v42-remediation.rules" +## This content is a section of an Audit config snapshot recommended for linux systems that target OSPP compliance. +## The following content has been retreived on 2019-03-11 from: https://github.com/linux-audit/audit-userspace/blob/master/rules/30-ospp-v42.rules + +## The purpose of these rules is to meet the requirements for Operating +## System Protection Profile (OSPP)v4.2. These rules depends on having +## 10-base-config.rules, 11-loginuid.rules, and 43-module-load.rules installed. + +## Unsuccessful file creation (open with O_CREAT) +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + +## Unsuccessful file modifications (open for write or truncate) +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + +## Unsuccessful file access (any other opens) This has to go last. +-a always,exit -F arch=b32 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-access +-a always,exit -F arch=b64 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-access +-a always,exit -F arch=b32 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-access +-a always,exit -F arch=b64 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-access +EOF + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Unsuccessful Modification Attempts to Files - open O_TRUNC_WRITE + The audit system should collect detailed unauthorized file accesses for +all users and root. The open syscall can be used to modify files +if called for write operation of with O_TRUNC_WRITE flag. +The following auidt rules will asure that unsuccessful attempts to modify a +file via open syscall are collected. +If the auditd daemon is configured to use the augenrules +program to read audit rules during daemon startup (the default), add the +rules below to a file with suffix .rules in the directory +/etc/audit/rules.d. +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the rules below to +/etc/audit/audit.rules file. + +-a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + +If the system is 64 bit then also add the following lines: + +-a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping system calls related +to the same event is more efficient. See the following example: +-a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.2.4 + Req-10.2.1 + SRG-OS-000064-GPOS-00033 + SRG-OS-000458-GPOS-00203 + SRG-OS-000461-GPOS-00205 + SRG-OS-000392-GPOS-00172 + SRG-OS-000458-VMM-001810 + SRG-OS-000461-VMM-001830 + Unsuccessful attempts to access files could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + CCE-80969-9 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80969-9 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_open_o_trunc_write + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Add unsuccessful file operations audit rules + blockinfile: + path: /etc/audit/rules.d/30-ospp-v42-remediation.rules + create: true + block: |- + ## This content is a section of an Audit config snapshot recommended for Red Hat Enterprise Linux 8 systems that target OSPP compliance. + ## The following content has been retreived on 2019-03-11 from: https://github.com/linux-audit/audit-userspace/blob/master/rules/30-ospp-v42.rules + + ## The purpose of these rules is to meet the requirements for Operating + ## System Protection Profile (OSPP)v4.2. These rules depends on having + ## 10-base-config.rules, 11-loginuid.rules, and 43-module-load.rules installed. + + ## Unsuccessful file creation (open with O_CREAT) + -a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b32 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b64 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b32 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b64 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + + ## Unsuccessful file modifications (open for write or truncate) + -a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b32 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b32 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + + ## Unsuccessful file access (any other opens) This has to go last. + -a always,exit -F arch=b32 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-access + -a always,exit -F arch=b64 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-access + -a always,exit -F arch=b32 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-access + -a always,exit -F arch=b64 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-access + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80969-9 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_open_o_trunc_write + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +mkdir -p "$(dirname '/etc/audit/rules.d/30-ospp-v42-remediation.rules')" +cat <<EOF > "/etc/audit/rules.d/30-ospp-v42-remediation.rules" +## This content is a section of an Audit config snapshot recommended for linux systems that target OSPP compliance. +## The following content has been retreived on 2019-03-11 from: https://github.com/linux-audit/audit-userspace/blob/master/rules/30-ospp-v42.rules + +## The purpose of these rules is to meet the requirements for Operating +## System Protection Profile (OSPP)v4.2. These rules depends on having +## 10-base-config.rules, 11-loginuid.rules, and 43-module-load.rules installed. + +## Unsuccessful file creation (open with O_CREAT) +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + +## Unsuccessful file modifications (open for write or truncate) +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + +## Unsuccessful file access (any other opens) This has to go last. +-a always,exit -F arch=b32 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-access +-a always,exit -F arch=b64 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-access +-a always,exit -F arch=b32 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-access +-a always,exit -F arch=b64 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-access +EOF + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure auditd Rules For Unauthorized Attempts To open Are Ordered Correctly + The audit system should collect detailed unauthorized file +accesses for all users and root. +To correctly identify unsuccessful creation, unsuccessful modification and unsuccessful access +of files via open syscall the audit rules collecting these events need to be in certain order. +The more specific rules need to come before the less specific rules. The reason for that is that more +specific rules cover a subset of events covered in the less specific rules, thus, they need to come +before to not be overshadowed by less specific rules, which match a bigger set of events. +Make sure that rules for unsuccessful calls of open syscall are in the order shown below. +If the auditd daemon is configured to use the augenrules +program to read audit rules during daemon startup (the default), check the order of +rules below in a file with suffix .rules in the directory +/etc/audit/rules.d. +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, check the order of rules below in +/etc/audit/audit.rules file. + +-a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S open -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-access +-a always,exit -F arch=b32 -S open -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-access + +If the system is 64 bit then also add the following lines: + +-a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S open -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-access +-a always,exit -F arch=b64 -S open -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-access + + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.2.4 + Req-10.2.1 + SRG-OS-000064-GPOS-00033 + SRG-OS-000458-GPOS-00203 + SRG-OS-000461-GPOS-00205 + SRG-OS-000392-GPOS-00172 + SRG-OS-000458-VMM-001810 + SRG-OS-000461-VMM-001830 + The more specific rules cover a subset of events covered by the less specific rules. +By ordering them from more specific to less specific, it is assured that the less specific +rule will not catch events better recorded by the more specific rule. + CCE-80970-7 + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +mkdir -p "$(dirname '/etc/audit/rules.d/30-ospp-v42-remediation.rules')" +cat <<EOF > "/etc/audit/rules.d/30-ospp-v42-remediation.rules" +## This content is a section of an Audit config snapshot recommended for linux systems that target OSPP compliance. +## The following content has been retreived on 2019-03-11 from: https://github.com/linux-audit/audit-userspace/blob/master/rules/30-ospp-v42.rules + +## The purpose of these rules is to meet the requirements for Operating +## System Protection Profile (OSPP)v4.2. These rules depends on having +## 10-base-config.rules, 11-loginuid.rules, and 43-module-load.rules installed. + +## Unsuccessful file creation (open with O_CREAT) +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + +## Unsuccessful file modifications (open for write or truncate) +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + +## Unsuccessful file access (any other opens) This has to go last. +-a always,exit -F arch=b32 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-access +-a always,exit -F arch=b64 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-access +-a always,exit -F arch=b32 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-access +-a always,exit -F arch=b64 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-access +EOF + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Unsuccessful Access Attempts to Files - openat + At a minimum, the audit system should collect unauthorized file +accesses for all users and root. If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: +-a always,exit -F arch=b32 -S openat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=access +-a always,exit -F arch=b32 -S openat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=access + +If the system is 64 bit then also add the following lines: + +-a always,exit -F arch=b64 -S openat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=access +-a always,exit -F arch=b64 -S openat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=access + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S openat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=access +-a always,exit -F arch=b32 -S openat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=access + +If the system is 64 bit then also add the following lines: + +-a always,exit -F arch=b64 -S openat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=access +-a always,exit -F arch=b64 -S openat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=access + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping these system +calls with others as identifying earlier in this guide is more efficient. + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.2.4 + Req-10.2.1 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + SRG-OS-000064-GPOS-00033 + SRG-OS-000458-GPOS-00203 + SRG-OS-000461-GPOS-00205 + SRG-OS-000458-VMM-001810 + SRG-OS-000461-VMM-001830 + RHEL-08-030420 + 4.1.3.7 + SV-230449r810455_rule + Unsuccessful attempts to access files could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + CCE-80754-5 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80754-5 + - DISA-STIG-RHEL-08-030420 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_openat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit openat tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80754-5 + - DISA-STIG-RHEL-08-030420 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_openat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for openat EACCES for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - openat + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of openat in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - openat + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of openat in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80754-5 + - DISA-STIG-RHEL-08-030420 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_openat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for openat EACCES for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - openat + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of openat in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - openat + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of openat in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-80754-5 + - DISA-STIG-RHEL-08-030420 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_openat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for openat EPERM for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - openat + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of openat in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - openat + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of openat in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80754-5 + - DISA-STIG-RHEL-08-030420 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_openat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for openat EPERM for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - openat + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of openat in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - openat + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of openat in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-80754-5 + - DISA-STIG-RHEL-08-030420 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_openat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="openat" +KEY="access" +SYSCALL_GROUPING="creat ftruncate truncate open openat open_by_handle_at" + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EACCES" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EPERM" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Unsuccessful Creation Attempts to Files - openat O_CREAT + The audit system should collect unauthorized file accesses for +all users and root. The openat syscall can be used to create new files +when O_CREAT flag is specified. + +The following auidt rules will asure that unsuccessful attempts to create a +file via openat syscall are collected. + +If the auditd daemon is configured to use the augenrules +program to read audit rules during daemon startup (the default), add the +rules below to a file with suffix .rules in the directory +/etc/audit/rules.d. + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the rules below to +/etc/audit/audit.rules file. + +-a always,exit -F arch=b32 -S openat -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S openat -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + + +If the system is 64 bit then also add the following lines: + +-a always,exit -F arch=b64 -S openat -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S openat -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping system calls related +to the same event is more efficient. See the following example: +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.2.4 + Req-10.2.1 + SRG-OS-000064-GPOS-00033 + SRG-OS-000458-GPOS-00203 + SRG-OS-000461-GPOS-00205 + SRG-OS-000392-GPOS-00172 + SRG-OS-000458-VMM-001810 + SRG-OS-000461-VMM-001830 + Unsuccessful attempts to access files could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + CCE-80962-4 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80962-4 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_openat_o_creat + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Add unsuccessful file operations audit rules + blockinfile: + path: /etc/audit/rules.d/30-ospp-v42-remediation.rules + create: true + block: |- + ## This content is a section of an Audit config snapshot recommended for Red Hat Enterprise Linux 8 systems that target OSPP compliance. + ## The following content has been retreived on 2019-03-11 from: https://github.com/linux-audit/audit-userspace/blob/master/rules/30-ospp-v42.rules + + ## The purpose of these rules is to meet the requirements for Operating + ## System Protection Profile (OSPP)v4.2. These rules depends on having + ## 10-base-config.rules, 11-loginuid.rules, and 43-module-load.rules installed. + + ## Unsuccessful file creation (open with O_CREAT) + -a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b32 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b64 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b32 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b64 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + + ## Unsuccessful file modifications (open for write or truncate) + -a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b32 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b32 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + + ## Unsuccessful file access (any other opens) This has to go last. + -a always,exit -F arch=b32 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-access + -a always,exit -F arch=b64 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-access + -a always,exit -F arch=b32 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-access + -a always,exit -F arch=b64 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-access + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80962-4 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_openat_o_creat + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +mkdir -p "$(dirname '/etc/audit/rules.d/30-ospp-v42-remediation.rules')" +cat <<EOF > "/etc/audit/rules.d/30-ospp-v42-remediation.rules" +## This content is a section of an Audit config snapshot recommended for linux systems that target OSPP compliance. +## The following content has been retreived on 2019-03-11 from: https://github.com/linux-audit/audit-userspace/blob/master/rules/30-ospp-v42.rules + +## The purpose of these rules is to meet the requirements for Operating +## System Protection Profile (OSPP)v4.2. These rules depends on having +## 10-base-config.rules, 11-loginuid.rules, and 43-module-load.rules installed. + +## Unsuccessful file creation (open with O_CREAT) +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + +## Unsuccessful file modifications (open for write or truncate) +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + +## Unsuccessful file access (any other opens) This has to go last. +-a always,exit -F arch=b32 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-access +-a always,exit -F arch=b64 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-access +-a always,exit -F arch=b32 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-access +-a always,exit -F arch=b64 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-access +EOF + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Unsuccessful Modification Attempts to Files - openat O_TRUNC_WRITE + The audit system should collect detailed unauthorized file accesses for +all users and root. The openat syscall can be used to modify files +if called for write operation of with O_TRUNC_WRITE flag. + +The following auidt rules will asure that unsuccessful attempts to modify a +file via openat syscall are collected. + +If the auditd daemon is configured to use the augenrules +program to read audit rules during daemon startup (the default), add the +rules below to a file with suffix .rules in the directory +/etc/audit/rules.d. + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the rules below to +/etc/audit/audit.rules file. + +-a always,exit -F arch=b32 -S openat -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S openat -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + + +If the system is 64 bit then also add the following lines: + +-a always,exit -F arch=b64 -S openat -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S openat -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping system calls related +to the same event is more efficient. See the following example: +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.2.4 + Req-10.2.1 + SRG-OS-000064-GPOS-00033 + SRG-OS-000458-GPOS-00203 + SRG-OS-000461-GPOS-00205 + SRG-OS-000392-GPOS-00172 + SRG-OS-000458-VMM-001810 + SRG-OS-000461-VMM-001830 + Unsuccessful attempts to access files could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + CCE-80963-2 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80963-2 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_openat_o_trunc_write + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Add unsuccessful file operations audit rules + blockinfile: + path: /etc/audit/rules.d/30-ospp-v42-remediation.rules + create: true + block: |- + ## This content is a section of an Audit config snapshot recommended for Red Hat Enterprise Linux 8 systems that target OSPP compliance. + ## The following content has been retreived on 2019-03-11 from: https://github.com/linux-audit/audit-userspace/blob/master/rules/30-ospp-v42.rules + + ## The purpose of these rules is to meet the requirements for Operating + ## System Protection Profile (OSPP)v4.2. These rules depends on having + ## 10-base-config.rules, 11-loginuid.rules, and 43-module-load.rules installed. + + ## Unsuccessful file creation (open with O_CREAT) + -a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b32 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b64 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b32 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b64 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + + ## Unsuccessful file modifications (open for write or truncate) + -a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b32 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b32 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + + ## Unsuccessful file access (any other opens) This has to go last. + -a always,exit -F arch=b32 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-access + -a always,exit -F arch=b64 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-access + -a always,exit -F arch=b32 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-access + -a always,exit -F arch=b64 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-access + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80963-2 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_openat_o_trunc_write + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +mkdir -p "$(dirname '/etc/audit/rules.d/30-ospp-v42-remediation.rules')" +cat <<EOF > "/etc/audit/rules.d/30-ospp-v42-remediation.rules" +## This content is a section of an Audit config snapshot recommended for linux systems that target OSPP compliance. +## The following content has been retreived on 2019-03-11 from: https://github.com/linux-audit/audit-userspace/blob/master/rules/30-ospp-v42.rules + +## The purpose of these rules is to meet the requirements for Operating +## System Protection Profile (OSPP)v4.2. These rules depends on having +## 10-base-config.rules, 11-loginuid.rules, and 43-module-load.rules installed. + +## Unsuccessful file creation (open with O_CREAT) +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + +## Unsuccessful file modifications (open for write or truncate) +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + +## Unsuccessful file access (any other opens) This has to go last. +-a always,exit -F arch=b32 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-access +-a always,exit -F arch=b64 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-access +-a always,exit -F arch=b32 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-access +-a always,exit -F arch=b64 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-access +EOF + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure auditd Rules For Unauthorized Attempts To openat Are Ordered Correctly + The audit system should collect detailed unauthorized file +accesses for all users and root. +To correctly identify unsuccessful creation, unsuccessful modification and unsuccessful access +of files via openat syscall the audit rules collecting these events need to be in certain order. +The more specific rules need to come before the less specific rules. The reason for that is that more +specific rules cover a subset of events covered in the less specific rules, thus, they need to come +before to not be overshadowed by less specific rules, which match a bigger set of events. +Make sure that rules for unsuccessful calls of openat syscall are in the order shown below. +If the auditd daemon is configured to use the augenrules +program to read audit rules during daemon startup (the default), check the order of +rules below in a file with suffix .rules in the directory +/etc/audit/rules.d. +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, check the order of rules below in +/etc/audit/audit.rules file. + +-a always,exit -F arch=b32 -S openat -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S openat -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S openat -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S openat -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S openat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-access +-a always,exit -F arch=b32 -S openat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-access + +If the system is 64 bit then also add the following lines: + +-a always,exit -F arch=b64 -S openat -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S openat -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S openat -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S openat -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S openat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-access +-a always,exit -F arch=b64 -S openat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-access + + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.2.4 + Req-10.2.1 + SRG-OS-000064-GPOS-00033 + SRG-OS-000458-GPOS-00203 + SRG-OS-000461-GPOS-00205 + SRG-OS-000392-GPOS-00172 + SRG-OS-000458-VMM-001810 + SRG-OS-000461-VMM-001830 + The more specific rules cover a subset of events covered by the less specific rules. +By ordering them from more specific to less specific, it is assured that the less specific +rule will not catch events better recorded by the more specific rule. + CCE-80964-0 + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +mkdir -p "$(dirname '/etc/audit/rules.d/30-ospp-v42-remediation.rules')" +cat <<EOF > "/etc/audit/rules.d/30-ospp-v42-remediation.rules" +## This content is a section of an Audit config snapshot recommended for linux systems that target OSPP compliance. +## The following content has been retreived on 2019-03-11 from: https://github.com/linux-audit/audit-userspace/blob/master/rules/30-ospp-v42.rules + +## The purpose of these rules is to meet the requirements for Operating +## System Protection Profile (OSPP)v4.2. These rules depends on having +## 10-base-config.rules, 11-loginuid.rules, and 43-module-load.rules installed. + +## Unsuccessful file creation (open with O_CREAT) +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + +## Unsuccessful file modifications (open for write or truncate) +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + +## Unsuccessful file access (any other opens) This has to go last. +-a always,exit -F arch=b32 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-access +-a always,exit -F arch=b64 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-access +-a always,exit -F arch=b32 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-access +-a always,exit -F arch=b64 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-access +EOF + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Unsuccessful Permission Changes to Files - removexattr + The audit system should collect unsuccessful file permission change +attempts for all users and root. +If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d. +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file. +-a always,exit -F arch=b32 -S removexattr -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change +-a always,exit -F arch=b32 -S removexattr -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S removexattr -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change +-a always,exit -F arch=b64 -S removexattr -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the audit rule checks a +system call independently of other system calls. Grouping system calls related +to the same event is more efficient. See the following example: +-a always,exit -F arch=b32 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change + CCI-000172 + AU-2(d) + AU-12(c) + CM-6(a) + SRG-OS-000458-VMM-001810 + SRG-OS-000461-VMM-001830 + Unsuccessful attempts to change permissions of files could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + CCE-80982-2 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80982-2 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_removexattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit removexattr tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80982-2 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_removexattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for removexattr EACCES for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - removexattr + syscall_grouping: [] + + - name: Check existence of removexattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - removexattr + syscall_grouping: [] + + - name: Check existence of removexattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80982-2 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_removexattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for removexattr EACCES for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - removexattr + syscall_grouping: [] + + - name: Check existence of removexattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - removexattr + syscall_grouping: [] + + - name: Check existence of removexattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-80982-2 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_removexattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for removexattr EPERM for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - removexattr + syscall_grouping: [] + + - name: Check existence of removexattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - removexattr + syscall_grouping: [] + + - name: Check existence of removexattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80982-2 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_removexattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for removexattr EPERM for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - removexattr + syscall_grouping: [] + + - name: Check existence of removexattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - removexattr + syscall_grouping: [] + + - name: Check existence of removexattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-80982-2 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_removexattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="removexattr" +KEY="access" +SYSCALL_GROUPING="" + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EACCES" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EPERM" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Unsuccessful Delete Attempts to Files - rename + The audit system should collect unsuccessful file deletion +attempts for all users and root. If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d. +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file. +-a always,exit -F arch=b32 -S rename -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete +-a always,exit -F arch=b32 -S rename -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete +If the system is 64 bit then also add the following lines: + +-a always,exit -F arch=b64 -S rename -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete +-a always,exit -F arch=b64 -S rename -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping system calls related +to the same event is more efficient. See the following example: +-a always,exit -F arch=b32 -S unlink,unlinkat,rename,renameat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-delete + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.2.4 + Req-10.2.1 + SRG-OS-000064-GPOS-00033 + SRG-OS-000392-GPOS-00172 + SRG-OS-000458-GPOS-00203 + SRG-OS-000461-GPOS-00205 + SRG-OS-000468-GPOS-00212 + SRG-OS-000458-VMM-001810 + SRG-OS-000461-VMM-001830 + Unsuccessful attempts to delete files could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + CCE-80973-1 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80973-1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_rename + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit rename tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80973-1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_rename + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for rename EACCES for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - rename + syscall_grouping: + - rename + - renameat + - unlink + - unlinkat + + - name: Check existence of rename in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - rename + syscall_grouping: + - rename + - renameat + - unlink + - unlinkat + + - name: Check existence of rename in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80973-1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_rename + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for rename EACCES for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - rename + syscall_grouping: + - rename + - renameat + - unlink + - unlinkat + + - name: Check existence of rename in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - rename + syscall_grouping: + - rename + - renameat + - unlink + - unlinkat + + - name: Check existence of rename in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-80973-1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_rename + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for rename EPERM for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - rename + syscall_grouping: + - rename + - renameat + - unlink + - unlinkat + + - name: Check existence of rename in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - rename + syscall_grouping: + - rename + - renameat + - unlink + - unlinkat + + - name: Check existence of rename in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80973-1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_rename + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for rename EPERM for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - rename + syscall_grouping: + - rename + - renameat + - unlink + - unlinkat + + - name: Check existence of rename in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - rename + syscall_grouping: + - rename + - renameat + - unlink + - unlinkat + + - name: Check existence of rename in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-80973-1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_rename + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="rename" +KEY="access" +SYSCALL_GROUPING="rename renameat unlink unlinkat" + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EACCES" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EPERM" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Unsuccessful Delete Attempts to Files - renameat + +The audit system should collect unsuccessful file deletion +attempts for all users and root. If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d. +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file. +-a always,exit -F arch=b32 -S renameat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete +-a always,exit -F arch=b32 -S renameat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete +If the system is 64 bit then also add the following lines: + +-a always,exit -F arch=b64 -S renameat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete +-a always,exit -F arch=b64 -S renameat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping system calls related +to the same event is more efficient. See the following example: + +-a always,exit -F arch=b32 -S unlink,unlinkat,rename,renameat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-delete + + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.2.4 + Req-10.2.1 + SRG-OS-000064-GPOS-00033 + SRG-OS-000392-GPOS-00172 + SRG-OS-000458-GPOS-00203 + SRG-OS-000461-GPOS-00205 + SRG-OS-000468-GPOS-00212 + SRG-OS-000458-VMM-001810 + SRG-OS-000461-VMM-001830 + Unsuccessful attempts to delete files could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + CCE-80974-9 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80974-9 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_renameat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit renameat tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80974-9 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_renameat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for renameat EACCES for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - renameat + syscall_grouping: + - rename + - renameat + - unlink + - unlinkat + + - name: Check existence of renameat in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - renameat + syscall_grouping: + - rename + - renameat + - unlink + - unlinkat + + - name: Check existence of renameat in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80974-9 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_renameat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for renameat EACCES for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - renameat + syscall_grouping: + - rename + - renameat + - unlink + - unlinkat + + - name: Check existence of renameat in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - renameat + syscall_grouping: + - rename + - renameat + - unlink + - unlinkat + + - name: Check existence of renameat in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-80974-9 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_renameat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for renameat EPERM for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - renameat + syscall_grouping: + - rename + - renameat + - unlink + - unlinkat + + - name: Check existence of renameat in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - renameat + syscall_grouping: + - rename + - renameat + - unlink + - unlinkat + + - name: Check existence of renameat in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80974-9 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_renameat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for renameat EPERM for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - renameat + syscall_grouping: + - rename + - renameat + - unlink + - unlinkat + + - name: Check existence of renameat in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - renameat + syscall_grouping: + - rename + - renameat + - unlink + - unlinkat + + - name: Check existence of renameat in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-80974-9 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_renameat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="renameat" +KEY="access" +SYSCALL_GROUPING="rename renameat unlink unlinkat" + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EACCES" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EPERM" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Unsuccessful Permission Changes to Files - setxattr + The audit system should collect unsuccessful file permission change +attempts for all users and root. +If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d. +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file. +-a always,exit -F arch=b32 -S setxattr -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change +-a always,exit -F arch=b32 -S setxattr -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S setxattr -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change +-a always,exit -F arch=b64 -S setxattr -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the audit rule checks a +system call independently of other system calls. Grouping system calls related +to the same event is more efficient. See the following example: +-a always,exit -F arch=b32 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change + CCI-000172 + AU-2(d) + AU-12(c) + CM-6(a) + SRG-OS-000458-VMM-001810 + SRG-OS-000461-VMM-001830 + Unsuccessful attempts to change permissions of files could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + CCE-80983-0 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80983-0 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_setxattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit setxattr tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80983-0 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_setxattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for setxattr EACCES for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - setxattr + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of setxattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - setxattr + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of setxattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80983-0 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_setxattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for setxattr EACCES for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - setxattr + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of setxattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - setxattr + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of setxattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-80983-0 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_setxattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for setxattr EPERM for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - setxattr + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of setxattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - setxattr + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of setxattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80983-0 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_setxattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for setxattr EPERM for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - setxattr + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of setxattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - setxattr + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of setxattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-80983-0 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_setxattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="setxattr" +KEY="access" +SYSCALL_GROUPING="chmod fchmod fchmodat fsetxattr lsetxattr setxattr" + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EACCES" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EPERM" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Unsuccessful Access Attempts to Files - truncate + At a minimum, the audit system should collect unauthorized file +accesses for all users and root. If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: +-a always,exit -F arch=b32 -S truncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=access +-a always,exit -F arch=b32 -S truncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=access + +If the system is 64 bit then also add the following lines: + +-a always,exit -F arch=b64 -S truncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=access +-a always,exit -F arch=b64 -S truncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=access + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S truncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=access +-a always,exit -F arch=b32 -S truncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=access + +If the system is 64 bit then also add the following lines: + +-a always,exit -F arch=b64 -S truncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=access +-a always,exit -F arch=b64 -S truncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=access + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping these system +calls with others as identifying earlier in this guide is more efficient. + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.2.4 + Req-10.2.1 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + SRG-OS-000064-GPOS-00033 + SRG-OS-000458-GPOS-00203 + SRG-OS-000461-GPOS-00205 + SRG-OS-000458-VMM-001810 + SRG-OS-000461-VMM-001830 + RHEL-08-030420 + 4.1.3.7 + SV-230449r810455_rule + Unsuccessful attempts to access files could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + CCE-80756-0 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80756-0 + - DISA-STIG-RHEL-08-030420 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_truncate + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit truncate tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80756-0 + - DISA-STIG-RHEL-08-030420 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_truncate + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for truncate EACCES for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - truncate + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of truncate in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - truncate + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of truncate in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80756-0 + - DISA-STIG-RHEL-08-030420 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_truncate + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for truncate EACCES for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - truncate + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of truncate in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - truncate + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of truncate in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-80756-0 + - DISA-STIG-RHEL-08-030420 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_truncate + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for truncate EPERM for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - truncate + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of truncate in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - truncate + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of truncate in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80756-0 + - DISA-STIG-RHEL-08-030420 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_truncate + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for truncate EPERM for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - truncate + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of truncate in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - truncate + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of truncate in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-80756-0 + - DISA-STIG-RHEL-08-030420 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_truncate + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="truncate" +KEY="access" +SYSCALL_GROUPING="creat ftruncate truncate open openat open_by_handle_at" + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EACCES" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EPERM" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Unsuccessful Delete Attempts to Files - unlink + +The audit system should collect unsuccessful file deletion +attempts for all users and root. If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d. + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file. +-a always,exit -F arch=b32 -S unlink -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete +-a always,exit -F arch=b32 -S unlink -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete + +If the system is 64 bit then also add the following lines: + +-a always,exit -F arch=b64 -S unlink -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete +-a always,exit -F arch=b64 -S unlink -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping system calls related +to the same event is more efficient. See the following example: + +-a always,exit -F arch=b32 -S unlink,unlinkat,rename,renameat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-delete + + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.2.4 + Req-10.2.1 + SRG-OS-000064-GPOS-00033 + SRG-OS-000392-GPOS-00172 + SRG-OS-000458-GPOS-00203 + SRG-OS-000461-GPOS-00205 + SRG-OS-000468-GPOS-00212 + SRG-OS-000458-VMM-001810 + SRG-OS-000461-VMM-001830 + Unsuccessful attempts to delete files could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + CCE-80971-5 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80971-5 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_unlink + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit unlink tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80971-5 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_unlink + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for unlink EACCES for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - unlink + syscall_grouping: + - rename + - renameat + - unlink + - unlinkat + + - name: Check existence of unlink in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - unlink + syscall_grouping: + - rename + - renameat + - unlink + - unlinkat + + - name: Check existence of unlink in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80971-5 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_unlink + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for unlink EACCES for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - unlink + syscall_grouping: + - rename + - renameat + - unlink + - unlinkat + + - name: Check existence of unlink in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - unlink + syscall_grouping: + - rename + - renameat + - unlink + - unlinkat + + - name: Check existence of unlink in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-80971-5 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_unlink + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for unlink EPERM for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - unlink + syscall_grouping: + - rename + - renameat + - unlink + - unlinkat + + - name: Check existence of unlink in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - unlink + syscall_grouping: + - rename + - renameat + - unlink + - unlinkat + + - name: Check existence of unlink in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80971-5 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_unlink + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for unlink EPERM for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - unlink + syscall_grouping: + - rename + - renameat + - unlink + - unlinkat + + - name: Check existence of unlink in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - unlink + syscall_grouping: + - rename + - renameat + - unlink + - unlinkat + + - name: Check existence of unlink in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-80971-5 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_unlink + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="unlink" +KEY="access" +SYSCALL_GROUPING="rename renameat unlink unlinkat" + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EACCES" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EPERM" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Unsuccessful Delete Attempts to Files - unlinkat + +The audit system should collect unsuccessful file deletion +attempts for all users and root. If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d. + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file. +-a always,exit -F arch=b32 -S unlinkat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete +-a always,exit -F arch=b32 -S unlinkat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete + +If the system is 64 bit then also add the following lines: + +-a always,exit -F arch=b64 -S unlinkat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete +-a always,exit -F arch=b64 -S unlinkat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping system calls related +to the same event is more efficient. See the following example: + +-a always,exit -F arch=b32 -S unlink,unlinkat,rename,renameat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-delete + + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.2.4 + Req-10.2.1 + SRG-OS-000064-GPOS-00033 + SRG-OS-000392-GPOS-00172 + SRG-OS-000458-GPOS-00203 + SRG-OS-000461-GPOS-00205 + SRG-OS-000468-GPOS-00212 + SRG-OS-000458-VMM-001810 + SRG-OS-000461-VMM-001830 + Unsuccessful attempts to delete files could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + CCE-80972-3 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80972-3 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_unlinkat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit unlinkat tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80972-3 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_unlinkat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for unlinkat EACCES for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - unlinkat + syscall_grouping: + - rename + - renameat + - unlink + - unlinkat + + - name: Check existence of unlinkat in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - unlinkat + syscall_grouping: + - rename + - renameat + - unlink + - unlinkat + + - name: Check existence of unlinkat in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80972-3 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_unlinkat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for unlinkat EACCES for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - unlinkat + syscall_grouping: + - rename + - renameat + - unlink + - unlinkat + + - name: Check existence of unlinkat in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - unlinkat + syscall_grouping: + - rename + - renameat + - unlink + - unlinkat + + - name: Check existence of unlinkat in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-80972-3 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_unlinkat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for unlinkat EPERM for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - unlinkat + syscall_grouping: + - rename + - renameat + - unlink + - unlinkat + + - name: Check existence of unlinkat in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - unlinkat + syscall_grouping: + - rename + - renameat + - unlink + - unlinkat + + - name: Check existence of unlinkat in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80972-3 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_unlinkat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for unlinkat EPERM for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - unlinkat + syscall_grouping: + - rename + - renameat + - unlink + - unlinkat + + - name: Check existence of unlinkat in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - unlinkat + syscall_grouping: + - rename + - renameat + - unlink + - unlinkat + + - name: Check existence of unlinkat in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-80972-3 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_unlinkat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="unlinkat" +KEY="access" +SYSCALL_GROUPING="rename renameat unlink unlinkat" + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EACCES" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EPERM" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Record Information on Kernel Modules Loading and Unloading + To capture kernel module loading and unloading events, use following lines, setting ARCH to +either b32 for 32-bit system, or having two lines for both b32 and b64 in case your system is 64-bit: + +-a always,exit -F arch=ARCH -S init_module,delete_module -F key=modules + + +Place to add the lines depends on a way auditd daemon is configured. If it is configured +to use the augenrules program (the default), add the lines to a file with suffix +.rules in the directory /etc/audit/rules.d. + +If the auditd daemon is configured to use the auditctl utility, +add the lines to file /etc/audit/audit.rules. + + Ensure auditd Collects Information on Kernel Module Loading and Unloading + To capture kernel module loading and unloading events, use following lines, setting ARCH to +either b32 for 32-bit system, or having two lines for both b32 and b64 in case your system is 64-bit: + +-a always,exit -F arch=ARCH -S init_module,finit_module,delete_module -F key=modules + + +The place to add the lines depends on a way auditd daemon is configured. If it is configured +to use the augenrules program (the default), add the lines to a file with suffix +.rules in the directory /etc/audit/rules.d. + +If the auditd daemon is configured to use the auditctl utility, +add the lines to file /etc/audit/audit.rules. + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 5.4.1.1 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000172 + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + Req-10.2.7 + 4.1.15 + The addition/removal of kernel modules can be used to alter the behavior of +the kernel and potentially introduce malicious code into kernel space. It is important +to have an audit trail of modules that have been introduced into the kernel. + CCE-80709-9 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80709-9 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.7 + - audit_rules_kernel_module_loading + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80709-9 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.7 + - audit_rules_kernel_module_loading + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for kernel module loading for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - init_module + - delete_module + - finit_module + syscall_grouping: + - init_module + - delete_module + - finit_module + + - name: Check existence of init_module, delete_module, finit_module in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/modules.rules + set_fact: audit_file="/etc/audit/rules.d/modules.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=modules + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - init_module + - delete_module + - finit_module + syscall_grouping: + - init_module + - delete_module + - finit_module + + - name: Check existence of init_module, delete_module, finit_module in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=modules + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80709-9 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.7 + - audit_rules_kernel_module_loading + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for kernel module loading for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - init_module + - delete_module + - finit_module + syscall_grouping: + - init_module + - delete_module + - finit_module + + - name: Check existence of init_module, delete_module, finit_module in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/modules.rules + set_fact: audit_file="/etc/audit/rules.d/modules.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=modules + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - init_module + - delete_module + - finit_module + syscall_grouping: + - init_module + - delete_module + - finit_module + + - name: Check existence of init_module, delete_module, finit_module in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=modules + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-80709-9 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.7 + - audit_rules_kernel_module_loading + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +# Note: 32-bit and 64-bit kernel syscall numbers not always line up => +# it's required on a 64-bit system to check also for the presence +# of 32-bit's equivalent of the corresponding rule. +# (See `man 7 audit.rules` for details ) +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="" + + AUID_FILTERS="-F auid>=1000 -F auid!=unset" + + SYSCALL="init_module finit_module delete_module" + KEY="modules" + SYSCALL_GROUPING="init_module finit_module delete_module" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure auditd Collects Information on Kernel Module Unloading - delete_module + To capture kernel module unloading events, use following line, setting ARCH to +either b32 for 32-bit system, or having two lines for both b32 and b64 in case your system is 64-bit: + +-a always,exit -F arch=ARCH -S delete_module -F auid>=1000 -F auid!=unset -F key=modules + + +Place to add the line depends on a way auditd daemon is configured. If it is configured +to use the augenrules program (the default), add the line to a file with suffix +.rules in the directory /etc/audit/rules.d. + +If the auditd daemon is configured to use the auditctl utility, +add the line to file /etc/audit/audit.rules. + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.2.7 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + SRG-OS-000471-GPOS-00216 + SRG-OS-000477-GPOS-00222 + SRG-OS-000477-VMM-001970 + RHEL-08-030390 + 4.1.3.19 + SV-230446r627750_rule + The removal of kernel modules can be used to alter the behavior of +the kernel and potentially introduce malicious code into kernel space. It is important +to have an audit trail of modules that have been introduced into the kernel. + CCE-80711-5 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80711-5 + - DISA-STIG-RHEL-08-030390 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.7 + - audit_rules_kernel_module_loading_delete + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Set architecture for audit delete_module tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80711-5 + - DISA-STIG-RHEL-08-030390 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.7 + - audit_rules_kernel_module_loading_delete + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Perform remediation of Audit rules for delete_module for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - delete_module + syscall_grouping: [] + + - name: Check existence of delete_module in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/module-change.rules + set_fact: audit_file="/etc/audit/rules.d/module-change.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=module-change + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - delete_module + syscall_grouping: [] + + - name: Check existence of delete_module in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=module-change + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80711-5 + - DISA-STIG-RHEL-08-030390 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.7 + - audit_rules_kernel_module_loading_delete + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Perform remediation of Audit rules for delete_module for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - delete_module + syscall_grouping: [] + + - name: Check existence of delete_module in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/module-change.rules + set_fact: audit_file="/etc/audit/rules.d/module-change.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=module-change + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - delete_module + syscall_grouping: [] + + - name: Check existence of delete_module in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=module-change + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-80711-5 + - DISA-STIG-RHEL-08-030390 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.7 + - audit_rules_kernel_module_loading_delete + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,-a%20always%2Cexit%20-F%20arch%3Db32%20-S%20delete_module%20-k%20module-change%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20delete_module%20-k%20module-change%0A + mode: 0600 + path: /etc/audit/rules.d/75-kernel-module-loading-delete.rules + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +# Note: 32-bit and 64-bit kernel syscall numbers not always line up => +# it's required on a 64-bit system to check also for the presence +# of 32-bit's equivalent of the corresponding rule. +# (See `man 7 audit.rules` for details ) +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="" + + AUID_FILTERS="-F auid>=1000 -F auid!=unset" + + SYSCALL="delete_module" + KEY="modules" + SYSCALL_GROUPING="delete_module" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure auditd Collects Information on Kernel Module Loading and Unloading - finit_module + If the auditd daemon is configured to use the augenrules program +to read audit rules during daemon startup (the default), add the following lines to a file +with suffix .rules in the directory /etc/audit/rules.d to capture kernel module +loading and unloading events, setting ARCH to either b32 or b64 as appropriate for your system: + +-a always,exit -F arch=ARCH -S finit_module -F auid>=1000 -F auid!=unset -F key=modules + If the auditd daemon is configured to use the auditctl utility to read audit +rules during daemon startup, add the following lines to /etc/audit/audit.rules file +in order to capture kernel module loading and unloading events, setting ARCH to either b32 or +b64 as appropriate for your system: + +-a always,exit -F arch=ARCH -S finit_module -F auid>=1000 -F auid!=unset -F key=modules + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.2.7 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + SRG-OS-000471-GPOS-00216 + SRG-OS-000477-GPOS-00222 + SRG-OS-000477-VMM-001970 + RHEL-08-030360 + 4.1.15 + SV-230438r810464_rule + The addition/removal of kernel modules can be used to alter the behavior of +the kernel and potentially introduce malicious code into kernel space. It is important +to have an audit trail of modules that have been introduced into the kernel. + CCE-80712-3 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80712-3 + - DISA-STIG-RHEL-08-030360 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.7 + - audit_rules_kernel_module_loading_finit + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Set architecture for audit finit_module tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80712-3 + - DISA-STIG-RHEL-08-030360 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.7 + - audit_rules_kernel_module_loading_finit + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Perform remediation of Audit rules for finit_module for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - finit_module + syscall_grouping: + - init_module + - finit_module + + - name: Check existence of finit_module in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/module-change.rules + set_fact: audit_file="/etc/audit/rules.d/module-change.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=module-change + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - finit_module + syscall_grouping: + - init_module + - finit_module + + - name: Check existence of finit_module in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=module-change + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80712-3 + - DISA-STIG-RHEL-08-030360 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.7 + - audit_rules_kernel_module_loading_finit + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Perform remediation of Audit rules for finit_module for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - finit_module + syscall_grouping: + - init_module + - finit_module + + - name: Check existence of finit_module in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/module-change.rules + set_fact: audit_file="/etc/audit/rules.d/module-change.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=module-change + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - finit_module + syscall_grouping: + - init_module + - finit_module + + - name: Check existence of finit_module in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=module-change + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-80712-3 + - DISA-STIG-RHEL-08-030360 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.7 + - audit_rules_kernel_module_loading_finit + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,-a%20always%2Cexit%20-F%20arch%3Db32%20-S%20finit_module%20-k%20module-change%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20finit_module%20-k%20module-change%0A + mode: 0600 + path: /etc/audit/rules.d/75-kernel-module-loading-finit.rules + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +# Note: 32-bit and 64-bit kernel syscall numbers not always line up => +# it's required on a 64-bit system to check also for the presence +# of 32-bit's equivalent of the corresponding rule. +# (See `man 7 audit.rules` for details ) +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="" + + AUID_FILTERS="-F auid>=1000 -F auid!=unset" + + SYSCALL="finit_module" + KEY="modules" + SYSCALL_GROUPING="init_module finit_module" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure auditd Collects Information on Kernel Module Loading - init_module + To capture kernel module loading events, use following line, setting ARCH to +either b32 for 32-bit system, or having two lines for both b32 and b64 in case your system is 64-bit: + +-a always,exit -F arch=ARCH -S init_module -F auid>=1000 -F auid!=unset -F key=modules + + +Place to add the line depends on a way auditd daemon is configured. If it is configured +to use the augenrules program (the default), add the line to a file with suffix +.rules in the directory /etc/audit/rules.d. + +If the auditd daemon is configured to use the auditctl utility, +add the line to file /etc/audit/audit.rules. + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.2.7 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + SRG-OS-000471-GPOS-00216 + SRG-OS-000477-GPOS-00222 + SRG-OS-000477-VMM-001970 + RHEL-08-030360 + 4.1.3.19 + SV-230438r810464_rule + The addition of kernel modules can be used to alter the behavior of +the kernel and potentially introduce malicious code into kernel space. It is important +to have an audit trail of modules that have been introduced into the kernel. + CCE-80713-1 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80713-1 + - DISA-STIG-RHEL-08-030360 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.7 + - audit_rules_kernel_module_loading_init + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Set architecture for audit init_module tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80713-1 + - DISA-STIG-RHEL-08-030360 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.7 + - audit_rules_kernel_module_loading_init + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Perform remediation of Audit rules for init_module for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - init_module + syscall_grouping: + - init_module + - finit_module + + - name: Check existence of init_module in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/module-change.rules + set_fact: audit_file="/etc/audit/rules.d/module-change.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=module-change + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - init_module + syscall_grouping: + - init_module + - finit_module + + - name: Check existence of init_module in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=module-change + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80713-1 + - DISA-STIG-RHEL-08-030360 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.7 + - audit_rules_kernel_module_loading_init + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Perform remediation of Audit rules for init_module for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - init_module + syscall_grouping: + - init_module + - finit_module + + - name: Check existence of init_module in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/module-change.rules + set_fact: audit_file="/etc/audit/rules.d/module-change.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=module-change + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - init_module + syscall_grouping: + - init_module + - finit_module + + - name: Check existence of init_module in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=module-change + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-80713-1 + - DISA-STIG-RHEL-08-030360 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.7 + - audit_rules_kernel_module_loading_init + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,-a%20always%2Cexit%20-F%20arch%3Db32%20-S%20init_module%20-k%20module-change%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20init_module%20-k%20module-change%0A + mode: 0600 + path: /etc/audit/rules.d/75-kernel-module-loading-init.rules + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +# Note: 32-bit and 64-bit kernel syscall numbers not always line up => +# it's required on a 64-bit system to check also for the presence +# of 32-bit's equivalent of the corresponding rule. +# (See `man 7 audit.rules` for details ) +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="" + + AUID_FILTERS="-F auid>=1000 -F auid!=unset" + + SYSCALL="init_module" + KEY="modules" + SYSCALL_GROUPING="init_module finit_module" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Record Attempts to Alter Logon and Logout Events + The audit system already collects login information for all users +and root. If the auditd daemon is configured to use the +augenrules program to read audit rules during daemon startup (the +default), add the following lines to a file with suffix .rules in the +directory /etc/audit/rules.d in order to watch for attempted manual +edits of files involved in storing logon events: +-w /var/log/tallylog -p wa -k logins +-w /var/log/faillock -p wa -k logins +-w /var/log/lastlog -p wa -k logins +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file in order to watch for unattempted manual +edits of files involved in storing logon events: +-w /var/log/tallylog -p wa -k logins +-w /var/log/faillock -p wa -k logins +-w /var/log/lastlog -p wa -k logins + + Record Attempts to Alter Logon and Logout Events + The audit system already collects login information for all users +and root. If the auditd daemon is configured to use the +augenrules program to read audit rules during daemon startup (the +default), add the following lines to a file with suffix .rules in the +directory /etc/audit/rules.d in order to watch for attempted manual +edits of files involved in storing logon events: +-w /var/log/tallylog -p wa -k logins +-w /var/log/faillock -p wa -k logins +-w /var/log/lastlog -p wa -k logins +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file in order to watch for unattempted manual +edits of files involved in storing logon events: +-w /var/log/tallylog -p wa -k logins +-w /var/log/faillock -p wa -k logins +-w /var/log/lastlog -p wa -k logins + This rule checks for multiple syscalls related to login events; +it was written with DISA STIG in mind. Other policies should use a +separate rule for each syscall that needs to be checked. For example: +audit_rules_login_events_tallylogaudit_rules_login_events_faillockaudit_rules_login_events_lastlog + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 5.4.1.1 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000172 + CCI-002884 + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + Req-10.2.3 + Manual editing of these files may indicate nefarious activity, such +as an attacker attempting to remove evidence of an intrusion. + CCE-80717-2 + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + + + + + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + + +# If the audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# into the list of files to be inspected +files_to_inspect+=('/etc/audit/audit.rules') + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/var/log/tallylog" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/var/log/tallylog $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/var/log/tallylog$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /var/log/tallylog -p wa -k logins" >> "$audit_rules_file" + fi +done +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + +# If the audit is 'augenrules', then check if rule is already defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection. +# If rule isn't defined, add '/etc/audit/rules.d/logins.rules' to list of files for inspection. +readarray -t matches < <(grep -HP "[\s]*-w[\s]+/var/log/tallylog" /etc/audit/rules.d/*.rules) + +# For each of the matched entries +for match in "${matches[@]}" +do + # Extract filepath from the match + rulesd_audit_file=$(echo $match | cut -f1 -d ':') + # Append that path into list of files for inspection + files_to_inspect+=("$rulesd_audit_file") +done +# Case when particular audit rule isn't defined yet +if [ "${#files_to_inspect[@]}" -eq "0" ] +then + # Append '/etc/audit/rules.d/logins.rules' into list of files for inspection + key_rule_file="/etc/audit/rules.d/logins.rules" + # If the logins.rules file doesn't exist yet, create it with correct permissions + if [ ! -e "$key_rule_file" ] + then + touch "$key_rule_file" + chmod 0640 "$key_rule_file" + fi + files_to_inspect+=("$key_rule_file") +fi + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/var/log/tallylog" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/var/log/tallylog $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/var/log/tallylog$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /var/log/tallylog -p wa -k logins" >> "$audit_rules_file" + fi +done + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + + +# If the audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# into the list of files to be inspected +files_to_inspect+=('/etc/audit/audit.rules') + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/var/log/faillock" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/var/log/faillock $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/var/log/faillock$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /var/log/faillock -p wa -k logins" >> "$audit_rules_file" + fi +done +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + +# If the audit is 'augenrules', then check if rule is already defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection. +# If rule isn't defined, add '/etc/audit/rules.d/logins.rules' to list of files for inspection. +readarray -t matches < <(grep -HP "[\s]*-w[\s]+/var/log/faillock" /etc/audit/rules.d/*.rules) + +# For each of the matched entries +for match in "${matches[@]}" +do + # Extract filepath from the match + rulesd_audit_file=$(echo $match | cut -f1 -d ':') + # Append that path into list of files for inspection + files_to_inspect+=("$rulesd_audit_file") +done +# Case when particular audit rule isn't defined yet +if [ "${#files_to_inspect[@]}" -eq "0" ] +then + # Append '/etc/audit/rules.d/logins.rules' into list of files for inspection + key_rule_file="/etc/audit/rules.d/logins.rules" + # If the logins.rules file doesn't exist yet, create it with correct permissions + if [ ! -e "$key_rule_file" ] + then + touch "$key_rule_file" + chmod 0640 "$key_rule_file" + fi + files_to_inspect+=("$key_rule_file") +fi + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/var/log/faillock" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/var/log/faillock $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/var/log/faillock$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /var/log/faillock -p wa -k logins" >> "$audit_rules_file" + fi +done + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + + +# If the audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# into the list of files to be inspected +files_to_inspect+=('/etc/audit/audit.rules') + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/var/log/lastlog" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/var/log/lastlog $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/var/log/lastlog$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /var/log/lastlog -p wa -k logins" >> "$audit_rules_file" + fi +done +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + +# If the audit is 'augenrules', then check if rule is already defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection. +# If rule isn't defined, add '/etc/audit/rules.d/logins.rules' to list of files for inspection. +readarray -t matches < <(grep -HP "[\s]*-w[\s]+/var/log/lastlog" /etc/audit/rules.d/*.rules) + +# For each of the matched entries +for match in "${matches[@]}" +do + # Extract filepath from the match + rulesd_audit_file=$(echo $match | cut -f1 -d ':') + # Append that path into list of files for inspection + files_to_inspect+=("$rulesd_audit_file") +done +# Case when particular audit rule isn't defined yet +if [ "${#files_to_inspect[@]}" -eq "0" ] +then + # Append '/etc/audit/rules.d/logins.rules' into list of files for inspection + key_rule_file="/etc/audit/rules.d/logins.rules" + # If the logins.rules file doesn't exist yet, create it with correct permissions + if [ ! -e "$key_rule_file" ] + then + touch "$key_rule_file" + chmod 0640 "$key_rule_file" + fi + files_to_inspect+=("$key_rule_file") +fi + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/var/log/lastlog" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/var/log/lastlog $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/var/log/lastlog$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /var/log/lastlog -p wa -k logins" >> "$audit_rules_file" + fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + Record Attempts to Alter Logon and Logout Events - faillock + The audit system already collects login information for all users +and root. If the auditd daemon is configured to use the +augenrules program to read audit rules during daemon startup (the +default), add the following lines to a file with suffix .rules in the +directory /etc/audit/rules.d in order to watch for attempted manual +edits of files involved in storing logon events: +-w /var/log/faillock -p wa -k logins +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file in order to watch for unattempted manual +edits of files involved in storing logon events: +-w /var/log/faillock -p wa -k logins + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000126 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.2.3 + SRG-OS-000392-GPOS-00172 + SRG-OS-000470-GPOS-00214 + SRG-OS-000473-GPOS-00218 + SRG-OS-000473-VMM-001930 + SRG-OS-000470-VMM-001900 + RHEL-08-030590 + 4.1.3.12 + SV-230466r627750_rule + Manual editing of these files may indicate nefarious activity, such +as an attacker attempting to remove evidence of an intrusion. + CCE-80718-0 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80718-0 + - DISA-STIG-RHEL-08-030590 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.3 + - audit_rules_login_events_faillock + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Check if watch rule for /var/log/faillock already exists in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: ^\s*-w\s+/var/log/faillock\s+-p\s+wa(\s|$)+ + patterns: '*.rules' + register: find_existing_watch_rules_d + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80718-0 + - DISA-STIG-RHEL-08-030590 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.3 + - audit_rules_login_events_faillock + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Search /etc/audit/rules.d for other rules with specified key logins + find: + paths: /etc/audit/rules.d + contains: ^.*(?:-F key=|-k\s+)logins$ + patterns: '*.rules' + register: find_watch_key + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched + == 0 + tags: + - CCE-80718-0 + - DISA-STIG-RHEL-08-030590 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.3 + - audit_rules_login_events_faillock + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Use /etc/audit/rules.d/logins.rules as the recipient for the rule + set_fact: + all_files: + - /etc/audit/rules.d/logins.rules + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_watch_key.matched is defined and find_watch_key.matched == 0 and find_existing_watch_rules_d.matched + is defined and find_existing_watch_rules_d.matched == 0 + tags: + - CCE-80718-0 + - DISA-STIG-RHEL-08-030590 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.3 + - audit_rules_login_events_faillock + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Use matched file as the recipient for the rule + set_fact: + all_files: + - '{{ find_watch_key.files | map(attribute=''path'') | list | first }}' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_watch_key.matched is defined and find_watch_key.matched > 0 and find_existing_watch_rules_d.matched + is defined and find_existing_watch_rules_d.matched == 0 + tags: + - CCE-80718-0 + - DISA-STIG-RHEL-08-030590 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.3 + - audit_rules_login_events_faillock + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Add watch rule for /var/log/faillock in /etc/audit/rules.d/ + lineinfile: + path: '{{ all_files[0] }}' + line: -w /var/log/faillock -p wa -k logins + create: true + mode: '0640' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched + == 0 + tags: + - CCE-80718-0 + - DISA-STIG-RHEL-08-030590 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.3 + - audit_rules_login_events_faillock + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Check if watch rule for /var/log/faillock already exists in /etc/audit/audit.rules + find: + paths: /etc/audit/ + contains: ^\s*-w\s+/var/log/faillock\s+-p\s+wa(\s|$)+ + patterns: audit.rules + register: find_existing_watch_audit_rules + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80718-0 + - DISA-STIG-RHEL-08-030590 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.3 + - audit_rules_login_events_faillock + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Add watch rule for /var/log/faillock in /etc/audit/audit.rules + lineinfile: + line: -w /var/log/faillock -p wa -k logins + state: present + dest: /etc/audit/audit.rules + create: true + mode: '0640' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_audit_rules.matched is defined and find_existing_watch_audit_rules.matched + == 0 + tags: + - CCE-80718-0 + - DISA-STIG-RHEL-08-030590 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.3 + - audit_rules_login_events_faillock + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + + +# If the audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# into the list of files to be inspected +files_to_inspect+=('/etc/audit/audit.rules') + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/var/log/faillock" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/var/log/faillock $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/var/log/faillock$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /var/log/faillock -p wa -k logins" >> "$audit_rules_file" + fi +done +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + +# If the audit is 'augenrules', then check if rule is already defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection. +# If rule isn't defined, add '/etc/audit/rules.d/logins.rules' to list of files for inspection. +readarray -t matches < <(grep -HP "[\s]*-w[\s]+/var/log/faillock" /etc/audit/rules.d/*.rules) + +# For each of the matched entries +for match in "${matches[@]}" +do + # Extract filepath from the match + rulesd_audit_file=$(echo $match | cut -f1 -d ':') + # Append that path into list of files for inspection + files_to_inspect+=("$rulesd_audit_file") +done +# Case when particular audit rule isn't defined yet +if [ "${#files_to_inspect[@]}" -eq "0" ] +then + # Append '/etc/audit/rules.d/logins.rules' into list of files for inspection + key_rule_file="/etc/audit/rules.d/logins.rules" + # If the logins.rules file doesn't exist yet, create it with correct permissions + if [ ! -e "$key_rule_file" ] + then + touch "$key_rule_file" + chmod 0640 "$key_rule_file" + fi + files_to_inspect+=("$key_rule_file") +fi + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/var/log/faillock" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/var/log/faillock $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/var/log/faillock$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /var/log/faillock -p wa -k logins" >> "$audit_rules_file" + fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Attempts to Alter Logon and Logout Events - lastlog + The audit system already collects login information for all users +and root. If the auditd daemon is configured to use the +augenrules program to read audit rules during daemon startup (the +default), add the following lines to a file with suffix .rules in the +directory /etc/audit/rules.d in order to watch for attempted manual +edits of files involved in storing logon events: +-w /var/log/lastlog -p wa -k logins +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file in order to watch for unattempted manual +edits of files involved in storing logon events: +-w /var/log/lastlog -p wa -k logins + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000126 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.2.3 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + SRG-OS-000473-GPOS-00218 + SRG-OS-000470-GPOS-00214 + SRG-OS-000473-VMM-001930 + SRG-OS-000470-VMM-001900 + RHEL-08-030600 + 4.1.3.12 + SV-230467r627750_rule + Manual editing of these files may indicate nefarious activity, such +as an attacker attempting to remove evidence of an intrusion. + CCE-80719-8 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80719-8 + - DISA-STIG-RHEL-08-030600 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.3 + - audit_rules_login_events_lastlog + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Check if watch rule for /var/log/lastlog already exists in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: ^\s*-w\s+/var/log/lastlog\s+-p\s+wa(\s|$)+ + patterns: '*.rules' + register: find_existing_watch_rules_d + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80719-8 + - DISA-STIG-RHEL-08-030600 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.3 + - audit_rules_login_events_lastlog + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Search /etc/audit/rules.d for other rules with specified key logins + find: + paths: /etc/audit/rules.d + contains: ^.*(?:-F key=|-k\s+)logins$ + patterns: '*.rules' + register: find_watch_key + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched + == 0 + tags: + - CCE-80719-8 + - DISA-STIG-RHEL-08-030600 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.3 + - audit_rules_login_events_lastlog + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Use /etc/audit/rules.d/logins.rules as the recipient for the rule + set_fact: + all_files: + - /etc/audit/rules.d/logins.rules + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_watch_key.matched is defined and find_watch_key.matched == 0 and find_existing_watch_rules_d.matched + is defined and find_existing_watch_rules_d.matched == 0 + tags: + - CCE-80719-8 + - DISA-STIG-RHEL-08-030600 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.3 + - audit_rules_login_events_lastlog + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Use matched file as the recipient for the rule + set_fact: + all_files: + - '{{ find_watch_key.files | map(attribute=''path'') | list | first }}' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_watch_key.matched is defined and find_watch_key.matched > 0 and find_existing_watch_rules_d.matched + is defined and find_existing_watch_rules_d.matched == 0 + tags: + - CCE-80719-8 + - DISA-STIG-RHEL-08-030600 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.3 + - audit_rules_login_events_lastlog + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Add watch rule for /var/log/lastlog in /etc/audit/rules.d/ + lineinfile: + path: '{{ all_files[0] }}' + line: -w /var/log/lastlog -p wa -k logins + create: true + mode: '0640' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched + == 0 + tags: + - CCE-80719-8 + - DISA-STIG-RHEL-08-030600 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.3 + - audit_rules_login_events_lastlog + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Check if watch rule for /var/log/lastlog already exists in /etc/audit/audit.rules + find: + paths: /etc/audit/ + contains: ^\s*-w\s+/var/log/lastlog\s+-p\s+wa(\s|$)+ + patterns: audit.rules + register: find_existing_watch_audit_rules + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80719-8 + - DISA-STIG-RHEL-08-030600 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.3 + - audit_rules_login_events_lastlog + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Add watch rule for /var/log/lastlog in /etc/audit/audit.rules + lineinfile: + line: -w /var/log/lastlog -p wa -k logins + state: present + dest: /etc/audit/audit.rules + create: true + mode: '0640' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_audit_rules.matched is defined and find_existing_watch_audit_rules.matched + == 0 + tags: + - CCE-80719-8 + - DISA-STIG-RHEL-08-030600 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.3 + - audit_rules_login_events_lastlog + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + + +# If the audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# into the list of files to be inspected +files_to_inspect+=('/etc/audit/audit.rules') + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/var/log/lastlog" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/var/log/lastlog $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/var/log/lastlog$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /var/log/lastlog -p wa -k logins" >> "$audit_rules_file" + fi +done +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + +# If the audit is 'augenrules', then check if rule is already defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection. +# If rule isn't defined, add '/etc/audit/rules.d/logins.rules' to list of files for inspection. +readarray -t matches < <(grep -HP "[\s]*-w[\s]+/var/log/lastlog" /etc/audit/rules.d/*.rules) + +# For each of the matched entries +for match in "${matches[@]}" +do + # Extract filepath from the match + rulesd_audit_file=$(echo $match | cut -f1 -d ':') + # Append that path into list of files for inspection + files_to_inspect+=("$rulesd_audit_file") +done +# Case when particular audit rule isn't defined yet +if [ "${#files_to_inspect[@]}" -eq "0" ] +then + # Append '/etc/audit/rules.d/logins.rules' into list of files for inspection + key_rule_file="/etc/audit/rules.d/logins.rules" + # If the logins.rules file doesn't exist yet, create it with correct permissions + if [ ! -e "$key_rule_file" ] + then + touch "$key_rule_file" + chmod 0640 "$key_rule_file" + fi + files_to_inspect+=("$key_rule_file") +fi + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/var/log/lastlog" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/var/log/lastlog $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/var/log/lastlog$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /var/log/lastlog -p wa -k logins" >> "$audit_rules_file" + fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Attempts to Alter Logon and Logout Events - tallylog + The audit system already collects login information for all users +and root. If the auditd daemon is configured to use the +augenrules program to read audit rules during daemon startup (the +default), add the following lines to a file with suffix .rules in the +directory /etc/audit/rules.d in order to watch for attempted manual +edits of files involved in storing logon events: +-w /var/log/tallylog -p wa -k logins +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file in order to watch for unattempted manual +edits of files involved in storing logon events: +-w /var/log/tallylog -p wa -k logins + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000172 + CCI-002884 + CCI-000126 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.2.3 + SRG-OS-000392-GPOS-00172 + SRG-OS-000470-GPOS-00214 + SRG-OS-000473-GPOS-00218 + SRG-OS-000473-VMM-001930 + SRG-OS-000470-VMM-001900 + Manual editing of these files may indicate nefarious activity, such +as an attacker attempting to remove evidence of an intrusion. + CCE-80720-6 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80720-6 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.3 + - audit_rules_login_events_tallylog + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Check if watch rule for /var/log/tallylog already exists in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: ^\s*-w\s+/var/log/tallylog\s+-p\s+wa(\s|$)+ + patterns: '*.rules' + register: find_existing_watch_rules_d + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80720-6 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.3 + - audit_rules_login_events_tallylog + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Search /etc/audit/rules.d for other rules with specified key logins + find: + paths: /etc/audit/rules.d + contains: ^.*(?:-F key=|-k\s+)logins$ + patterns: '*.rules' + register: find_watch_key + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched + == 0 + tags: + - CCE-80720-6 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.3 + - audit_rules_login_events_tallylog + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Use /etc/audit/rules.d/logins.rules as the recipient for the rule + set_fact: + all_files: + - /etc/audit/rules.d/logins.rules + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_watch_key.matched is defined and find_watch_key.matched == 0 and find_existing_watch_rules_d.matched + is defined and find_existing_watch_rules_d.matched == 0 + tags: + - CCE-80720-6 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.3 + - audit_rules_login_events_tallylog + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Use matched file as the recipient for the rule + set_fact: + all_files: + - '{{ find_watch_key.files | map(attribute=''path'') | list | first }}' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_watch_key.matched is defined and find_watch_key.matched > 0 and find_existing_watch_rules_d.matched + is defined and find_existing_watch_rules_d.matched == 0 + tags: + - CCE-80720-6 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.3 + - audit_rules_login_events_tallylog + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Add watch rule for /var/log/tallylog in /etc/audit/rules.d/ + lineinfile: + path: '{{ all_files[0] }}' + line: -w /var/log/tallylog -p wa -k logins + create: true + mode: '0640' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched + == 0 + tags: + - CCE-80720-6 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.3 + - audit_rules_login_events_tallylog + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Check if watch rule for /var/log/tallylog already exists in /etc/audit/audit.rules + find: + paths: /etc/audit/ + contains: ^\s*-w\s+/var/log/tallylog\s+-p\s+wa(\s|$)+ + patterns: audit.rules + register: find_existing_watch_audit_rules + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80720-6 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.3 + - audit_rules_login_events_tallylog + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Add watch rule for /var/log/tallylog in /etc/audit/audit.rules + lineinfile: + line: -w /var/log/tallylog -p wa -k logins + state: present + dest: /etc/audit/audit.rules + create: true + mode: '0640' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_audit_rules.matched is defined and find_existing_watch_audit_rules.matched + == 0 + tags: + - CCE-80720-6 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.3 + - audit_rules_login_events_tallylog + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + + +# If the audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# into the list of files to be inspected +files_to_inspect+=('/etc/audit/audit.rules') + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/var/log/tallylog" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/var/log/tallylog $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/var/log/tallylog$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /var/log/tallylog -p wa -k logins" >> "$audit_rules_file" + fi +done +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + +# If the audit is 'augenrules', then check if rule is already defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection. +# If rule isn't defined, add '/etc/audit/rules.d/logins.rules' to list of files for inspection. +readarray -t matches < <(grep -HP "[\s]*-w[\s]+/var/log/tallylog" /etc/audit/rules.d/*.rules) + +# For each of the matched entries +for match in "${matches[@]}" +do + # Extract filepath from the match + rulesd_audit_file=$(echo $match | cut -f1 -d ':') + # Append that path into list of files for inspection + files_to_inspect+=("$rulesd_audit_file") +done +# Case when particular audit rule isn't defined yet +if [ "${#files_to_inspect[@]}" -eq "0" ] +then + # Append '/etc/audit/rules.d/logins.rules' into list of files for inspection + key_rule_file="/etc/audit/rules.d/logins.rules" + # If the logins.rules file doesn't exist yet, create it with correct permissions + if [ ! -e "$key_rule_file" ] + then + touch "$key_rule_file" + chmod 0640 "$key_rule_file" + fi + files_to_inspect+=("$key_rule_file") +fi + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/var/log/tallylog" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/var/log/tallylog $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/var/log/tallylog$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /var/log/tallylog -p wa -k logins" >> "$audit_rules_file" + fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Record Information on the Use of Privileged Commands + At a minimum, the audit system should collect the execution of +privileged commands for all users and root. + + Ensure auditd Collects Information on the Use of Privileged Commands - init + At a minimum, the audit system should collect the execution of +privileged commands for all users and root. If the auditd daemon is +configured to use the augenrules program to read audit rules during +daemon startup (the default), add a line of the following form to a file with +suffix .rules in the directory /etc/audit/rules.d: +-a always,exit -F path=/usr/sbin/init -F auid>=1000 -F auid!=unset -F key=privileged +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add a line of the following +form to /etc/audit/audit.rules: +-a always,exit -F path=/usr/sbin/init -F auid>=1000 -F auid!=unset -F key=privileged + CCI-000172 + AU-12(c) + SRG-OS-000477-GPOS-00222 + Misuse of the init command may cause availability issues for the system. + - name: Gather the package facts + package_facts: + manager: auto + tags: + - NIST-800-53-AU-12(c) + - audit_privileged_commands_init + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Perform remediation of Audit rules for /usr/sbin/init + block: + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/sbin/init -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules + set_fact: audit_file="/etc/audit/rules.d/privileged.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/sbin/init -F perm=x -F + auid>=1000 -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/sbin/init -F perm=x + -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/sbin/init -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:( + -S |,)\w+)+)( -F path=/usr/sbin/init -F perm=x -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/sbin/init -F perm=x + -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AU-12(c) + - audit_privileged_commands_init + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +ACTION_ARCH_FILTERS="-a always,exit" +OTHER_FILTERS="-F path=/usr/sbin/init -F perm=x" +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="" +KEY="privileged" +SYSCALL_GROUPING="" +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - poweroff + At a minimum, the audit system should collect the execution of +privileged commands for all users and root. If the auditd daemon is +configured to use the augenrules program to read audit rules during +daemon startup (the default), add a line of the following form to a file with +suffix .rules in the directory /etc/audit/rules.d: +-a always,exit -F path=/usr/sbin/poweroff -F auid>=1000 -F auid!=unset -F key=privileged +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add a line of the following +form to /etc/audit/audit.rules: +-a always,exit -F path=/usr/sbin/poweroff -F auid>=1000 -F auid!=unset -F key=privileged + CCI-000172 + AU-12(c) + SRG-OS-000477-GPOS-00222 + Misuse of the poweroff command may cause availability issues for the system. + - name: Gather the package facts + package_facts: + manager: auto + tags: + - NIST-800-53-AU-12(c) + - audit_privileged_commands_poweroff + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Perform remediation of Audit rules for /usr/sbin/poweroff + block: + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/sbin/poweroff -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules + set_fact: audit_file="/etc/audit/rules.d/privileged.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/sbin/poweroff -F perm=x + -F auid>=1000 -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/sbin/poweroff -F + perm=x -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/sbin/poweroff -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:( + -S |,)\w+)+)( -F path=/usr/sbin/poweroff -F perm=x -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/sbin/poweroff -F + perm=x -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AU-12(c) + - audit_privileged_commands_poweroff + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +ACTION_ARCH_FILTERS="-a always,exit" +OTHER_FILTERS="-F path=/usr/sbin/poweroff -F perm=x" +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="" +KEY="privileged" +SYSCALL_GROUPING="" +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - reboot + At a minimum, the audit system should collect the execution of +privileged commands for all users and root. If the auditd daemon is +configured to use the augenrules program to read audit rules during +daemon startup (the default), add a line of the following form to a file with +suffix .rules in the directory /etc/audit/rules.d: +-a always,exit -F path=/usr/sbin/reboot -F auid>=1000 -F auid!=unset -F key=privileged +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add a line of the following +form to /etc/audit/audit.rules: +-a always,exit -F path=/usr/sbin/reboot -F auid>=1000 -F auid!=unset -F key=privileged + CCI-000172 + AU-12(c) + SRG-OS-000477-GPOS-00222 + Misuse of the reboot command may cause availability issues for the system. + - name: Gather the package facts + package_facts: + manager: auto + tags: + - NIST-800-53-AU-12(c) + - audit_privileged_commands_reboot + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Perform remediation of Audit rules for /usr/sbin/reboot + block: + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/sbin/reboot -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules + set_fact: audit_file="/etc/audit/rules.d/privileged.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/sbin/reboot -F perm=x + -F auid>=1000 -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/sbin/reboot -F perm=x + -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/sbin/reboot -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:( + -S |,)\w+)+)( -F path=/usr/sbin/reboot -F perm=x -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/sbin/reboot -F perm=x + -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AU-12(c) + - audit_privileged_commands_reboot + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +ACTION_ARCH_FILTERS="-a always,exit" +OTHER_FILTERS="-F path=/usr/sbin/reboot -F perm=x" +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="" +KEY="privileged" +SYSCALL_GROUPING="" +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - shutdown + At a minimum, the audit system should collect the execution of +privileged commands for all users and root. If the auditd daemon is +configured to use the augenrules program to read audit rules during +daemon startup (the default), add a line of the following form to a file with +suffix .rules in the directory /etc/audit/rules.d: +-a always,exit -F path=/usr/sbin/shutdown -F auid>=1000 -F auid!=unset -F key=privileged +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add a line of the following +form to /etc/audit/audit.rules: +-a always,exit -F path=/usr/sbin/shutdown -F auid>=1000 -F auid!=unset -F key=privileged + CCI-000172 + AU-12(c) + SRG-OS-000477-GPOS-00222 + Misuse of the shutdown command may cause availability issues for the system. + - name: Gather the package facts + package_facts: + manager: auto + tags: + - NIST-800-53-AU-12(c) + - audit_privileged_commands_shutdown + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Perform remediation of Audit rules for /usr/sbin/shutdown + block: + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/sbin/shutdown -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules + set_fact: audit_file="/etc/audit/rules.d/privileged.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/sbin/shutdown -F perm=x + -F auid>=1000 -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/sbin/shutdown -F + perm=x -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/sbin/shutdown -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:( + -S |,)\w+)+)( -F path=/usr/sbin/shutdown -F perm=x -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/sbin/shutdown -F + perm=x -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AU-12(c) + - audit_privileged_commands_shutdown + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +ACTION_ARCH_FILTERS="-a always,exit" +OTHER_FILTERS="-F path=/usr/sbin/shutdown -F perm=x" +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="" +KEY="privileged" +SYSCALL_GROUPING="" +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands + The audit system should collect information about usage of privileged +commands for all users and root. To find the relevant setuid / +setgid programs, run the following command for each local partition +PART: +$ sudo find PART -xdev -type f -perm -4000 -o -type f -perm -2000 2>/dev/null +If the auditd daemon is configured to use the augenrules +program to read audit rules during daemon startup (the default), add a line of +the following form to a file with suffix .rules in the directory +/etc/audit/rules.d for each setuid / setgid program on the system, +replacing the SETUID_PROG_PATH part with the full path of that setuid / +setgid program in the list: +-a always,exit -F path=SETUID_PROG_PATH -F auid>=1000 -F auid!=unset -F key=privileged +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add a line of the following +form to /etc/audit/audit.rules for each setuid / setgid program on the +system, replacing the SETUID_PROG_PATH part with the full path of that +setuid / setgid program in the list: +-a always,exit -F path=SETUID_PROG_PATH -F auid>=1000 -F auid!=unset -F key=privileged + This rule checks for multiple syscalls related to privileged commands; +it was written with DISA STIG in mind. Other policies should use a +separate rule for each syscall that needs to be checked. For example: +audit_rules_privileged_commands_suaudit_rules_privileged_commands_umountaudit_rules_privileged_commands_passwd + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 5.4.1.1 + APO08.04 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.05 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-002234 + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.5 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.3.4.5.9 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 3.9 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + 0582 + 0584 + 05885 + 0586 + 0846 + 0957 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.1 + A.16.1.2 + A.16.1.3 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.1.3 + A.6.2.1 + A.6.2.2 + CIP-004-6 R2.2.2 + CIP-004-6 R2.2.3 + CIP-007-3 R.1.3 + CIP-007-3 R5 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.3 + CIP-007-3 R5.2.1 + CIP-007-3 R5.2.3 + AC-2(4) + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + DE.AE-2 + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + DE.DP-4 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + RS.CO-2 + Req-10.2.2 + SRG-OS-000327-GPOS-00127 + SRG-OS-000471-VMM-001910 + 4.1.13 + Misuse of privileged functions, either intentionally or unintentionally by +authorized users, or by unauthorized external entities that have compromised system accounts, +is a serious and ongoing concern and can have significant adverse impacts on organizations. +Auditing the use of privileged functions is one way to detect such misuse and identify +the risk from insider and advanced persistent threats. + +Privileged programs are subject to escalation-of-privilege attacks, +which attempt to subvert their normal role of providing some necessary but +limited capability. As such, motivation exists to monitor these programs for +unusual activity. + CCE-80724-8 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80724-8 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.2 + - audit_rules_privileged_commands + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Search for privileged commands + shell: | + set -o pipefail + find / -not \( -fstype afs -o -fstype ceph -o -fstype cifs -o -fstype smb3 -o -fstype smbfs -o -fstype sshfs -o -fstype ncpfs -o -fstype ncp -o -fstype nfs -o -fstype nfs4 -o -fstype gfs -o -fstype gfs2 -o -fstype glusterfs -o -fstype gpfs -o -fstype pvfs2 -o -fstype ocfs2 -o -fstype lustre -o -fstype davfs -o -fstype fuse.sshfs \) -type f \( -perm -4000 -o -perm -2000 \) 2> /dev/null + args: + warn: false + executable: /bin/bash + check_mode: false + register: find_result + changed_when: false + failed_when: false + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80724-8 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.2 + - audit_rules_privileged_commands + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Search /etc/audit/rules.d for audit rule entries + find: + paths: /etc/audit/rules.d + recurse: false + contains: ^.*path={{ item }} .*$ + patterns: '*.rules' + with_items: + - '{{ find_result.stdout_lines }}' + register: files_result + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80724-8 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.2 + - audit_rules_privileged_commands + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Overwrites the rule in rules.d + lineinfile: + path: '{{ item.1.path }}' + line: -a always,exit -F path={{ item.0.item }} -F auid>=1000 -F auid!=unset -F + key=privileged + create: false + regexp: ^.*path={{ item.0.item }} .*$ + with_subelements: + - '{{ files_result.results }}' + - files + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80724-8 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.2 + - audit_rules_privileged_commands + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Adds the rule in rules.d + lineinfile: + path: /etc/audit/rules.d/privileged.rules + line: -a always,exit -F path={{ item.item }} -F auid>=1000 -F auid!=unset -F key=privileged + create: true + with_items: + - '{{ files_result.results }}' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - files_result.results is defined and item.matched == 0 + tags: + - CCE-80724-8 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.2 + - audit_rules_privileged_commands + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Inserts/replaces the rule in audit.rules + lineinfile: + path: /etc/audit/audit.rules + line: -a always,exit -F path={{ item.item }} -F auid>=1000 -F auid!=unset -F key=privileged + create: true + regexp: ^.*path={{ item.item }} .*$ + with_items: + - '{{ files_result.results }}' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80724-8 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.2 + - audit_rules_privileged_commands + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +files_to_inspect=() + +# If the audit tool is 'auditctl', then: +# * add '/etc/audit/audit.rules'to the list of files to be inspected, +# * specify '/etc/audit/audit.rules' as the output audit file, where +# missing rules should be inserted +files_to_inspect=("/etc/audit/audit.rules") +output_audit_file="/etc/audit/audit.rules" + +# Obtain the list of SUID/SGID binaries on the particular system (split by newline) +# into privileged_binaries array +privileged_binaries=() +readarray -t privileged_binaries < <(find / -not \( -fstype afs -o -fstype ceph -o -fstype cifs -o -fstype smb3 -o -fstype smbfs -o -fstype sshfs -o -fstype ncpfs -o -fstype ncp -o -fstype nfs -o -fstype nfs4 -o -fstype gfs -o -fstype gfs2 -o -fstype glusterfs -o -fstype gpfs -o -fstype pvfs2 -o -fstype ocfs2 -o -fstype lustre -o -fstype davfs -o -fstype fuse.sshfs \) -type f \( -perm -4000 -o -perm -2000 \) 2> /dev/null) + +# Keep list of SUID/SGID binaries that have been already handled within some previous iteration +sbinaries_to_skip=() + +# For each found sbinary in privileged_binaries list +for sbinary in "${privileged_binaries[@]}" +do + + # Check if this sbinary wasn't already handled in some of the previous sbinary iterations + # Return match only if whole sbinary definition matched (not in the case just prefix matched!!!) + if [[ $(sed -ne "\|${sbinary}|p" <<< "${sbinaries_to_skip[*]}") ]] + then + # If so, don't process it second time & go to process next sbinary + continue + fi + + # Reset the counter of inspected files when starting to check + # presence of existing audit rule for new sbinary + count_of_inspected_files=0 + + # Define expected rule form for this binary + expected_rule="-a always,exit -F path=${sbinary} -F auid>=1000 -F auid!=unset -F key=privileged" + + # If list of audit rules files to be inspected is empty, just add new rule and move on to next binary + if [[ ${#files_to_inspect[@]} -eq 0 ]]; then + echo "$expected_rule" >> "$output_audit_file" + continue + fi + + # Replace possible slash '/' character in sbinary definition so we could use it in sed expressions below + sbinary_esc=${sbinary//$'/'/$'\/'} + + # For each audit rules file from the list of files to be inspected + for afile in "${files_to_inspect[@]}" + do + # Search current audit rules file's content for match. Match criteria: + # * existing rule is for the same SUID/SGID binary we are currently processing (but + # can contain multiple -F path= elements covering multiple SUID/SGID binaries) + # * existing rule contains all arguments from expected rule form (though can contain + # them in arbitrary order) + + base_search=$(sed -e '/-a always,exit/!d' -e '/-F path='"${sbinary_esc}"'[^[:graph:]]/!d' \ + -e '/-F path=[^[:space:]]\+/!d' \ + -e '/-F auid>='"1000"'/!d' -e '/-F auid!=\(4294967295\|unset\)/!d' \ + -e '/-k \|-F key=/!d' "$afile") + + # Increase the count of inspected files for this sbinary + count_of_inspected_files=$((count_of_inspected_files + 1)) + + # Search current audit rules file's content for presence of rule pattern for this sbinary + if [[ $base_search ]] + then + + # Current audit rules file already contains rule for this binary => + # Store the exact form of found rule for this binary for further processing + concrete_rule=$base_search + + # Select all other SUID/SGID binaries possibly also present in the found rule + + readarray -t handled_sbinaries < <(grep -o -e "-F path=[^[:space:]]\+" <<< "$concrete_rule") + handled_sbinaries=("${handled_sbinaries[@]//-F path=/}") + + # Merge the list of such SUID/SGID binaries found in this iteration with global list ignoring duplicates + readarray -t sbinaries_to_skip < <(for i in "${sbinaries_to_skip[@]}" "${handled_sbinaries[@]}"; do echo "$i"; done | sort -du) + + # if there is a -F perm flag, remove it + if grep -q '.*-F\s\+perm=[rwxa]\+.*' <<< "$concrete_rule"; then + + # Separate concrete_rule into three sections using hash '#' + # sign as a delimiter around rule's permission section borders + # note that the trailing space after perm flag is captured because there would be + # two consecutive spaces after joining remaining parts of the rule together + concrete_rule="$(echo "$concrete_rule" | sed -n "s/\(.*\)\+\(-F perm=[rwax]\+\ \?\)\+/\1#\2#/p")" + + # Split concrete_rule into head and tail sections using hash '#' delimiter + # The second column contains the permission section, which we don't need to extract + rule_head=$(cut -d '#' -f 1 <<< "$concrete_rule") + rule_tail=$(cut -d '#' -f 3 <<< "$concrete_rule") + + # Remove permissions section from existing rule in the file + sed -i "s#${rule_head}\(.*\)${rule_tail}#${rule_head}${rule_tail}#" "$afile" + fi + # If the required audit rule for particular sbinary wasn't found yet, insert it under following conditions: + # + # * in the "auditctl" mode of operation insert particular rule each time + # (because in this mode there's only one file -- /etc/audit/audit.rules to be inspected for presence of this rule), + # + # * in the "augenrules" mode of operation insert particular rule only once and only in case we have already + # searched all of the files from /etc/audit/rules.d/*.rules location (since that audit rule can be defined + # in any of those files and if not, we want it to be inserted only once into /etc/audit/rules.d/privileged.rules file) + # + + else + # Check if this sbinary wasn't already handled in some of the previous afile iterations + # Return match only if whole sbinary definition matched (not in the case just prefix matched!!!) + if [[ ! $(sed -ne "\|${sbinary}|p" <<< "${sbinaries_to_skip[*]}") ]] + then + # Current audit rules file's content doesn't contain expected rule for this + # SUID/SGID binary yet => append it + echo "$expected_rule" >> "$output_audit_file" + fi + continue + fi + done +done +files_to_inspect=() +# If the audit tool is 'augenrules', then: +# * add '/etc/audit/rules.d/*.rules' to the list of files to be inspected +# (split by newline), +# * specify /etc/audit/rules.d/privileged.rules' as the output file, where +# missing rules should be inserted +readarray -t files_to_inspect < <(find /etc/audit/rules.d -maxdepth 1 -type f -name '*.rules' -print) +output_audit_file="/etc/audit/rules.d/privileged.rules" + +# Obtain the list of SUID/SGID binaries on the particular system (split by newline) +# into privileged_binaries array +privileged_binaries=() +readarray -t privileged_binaries < <(find / -not \( -fstype afs -o -fstype ceph -o -fstype cifs -o -fstype smb3 -o -fstype smbfs -o -fstype sshfs -o -fstype ncpfs -o -fstype ncp -o -fstype nfs -o -fstype nfs4 -o -fstype gfs -o -fstype gfs2 -o -fstype glusterfs -o -fstype gpfs -o -fstype pvfs2 -o -fstype ocfs2 -o -fstype lustre -o -fstype davfs -o -fstype fuse.sshfs \) -type f \( -perm -4000 -o -perm -2000 \) 2> /dev/null) + +# Keep list of SUID/SGID binaries that have been already handled within some previous iteration +sbinaries_to_skip=() + +# For each found sbinary in privileged_binaries list +for sbinary in "${privileged_binaries[@]}" +do + + # Check if this sbinary wasn't already handled in some of the previous sbinary iterations + # Return match only if whole sbinary definition matched (not in the case just prefix matched!!!) + if [[ $(sed -ne "\|${sbinary}|p" <<< "${sbinaries_to_skip[*]}") ]] + then + # If so, don't process it second time & go to process next sbinary + continue + fi + + # Reset the counter of inspected files when starting to check + # presence of existing audit rule for new sbinary + count_of_inspected_files=0 + + # Define expected rule form for this binary + expected_rule="-a always,exit -F path=${sbinary} -F auid>=1000 -F auid!=unset -F key=privileged" + + # If list of audit rules files to be inspected is empty, just add new rule and move on to next binary + if [[ ${#files_to_inspect[@]} -eq 0 ]]; then + echo "$expected_rule" >> "$output_audit_file" + continue + fi + + # Replace possible slash '/' character in sbinary definition so we could use it in sed expressions below + sbinary_esc=${sbinary//$'/'/$'\/'} + + # For each audit rules file from the list of files to be inspected + for afile in "${files_to_inspect[@]}" + do + # Search current audit rules file's content for match. Match criteria: + # * existing rule is for the same SUID/SGID binary we are currently processing (but + # can contain multiple -F path= elements covering multiple SUID/SGID binaries) + # * existing rule contains all arguments from expected rule form (though can contain + # them in arbitrary order) + + base_search=$(sed -e '/-a always,exit/!d' -e '/-F path='"${sbinary_esc}"'[^[:graph:]]/!d' \ + -e '/-F path=[^[:space:]]\+/!d' \ + -e '/-F auid>='"1000"'/!d' -e '/-F auid!=\(4294967295\|unset\)/!d' \ + -e '/-k \|-F key=/!d' "$afile") + + # Increase the count of inspected files for this sbinary + count_of_inspected_files=$((count_of_inspected_files + 1)) + + # Search current audit rules file's content for presence of rule pattern for this sbinary + if [[ $base_search ]] + then + + # Current audit rules file already contains rule for this binary => + # Store the exact form of found rule for this binary for further processing + concrete_rule=$base_search + + # Select all other SUID/SGID binaries possibly also present in the found rule + + readarray -t handled_sbinaries < <(grep -o -e "-F path=[^[:space:]]\+" <<< "$concrete_rule") + handled_sbinaries=("${handled_sbinaries[@]//-F path=/}") + + # Merge the list of such SUID/SGID binaries found in this iteration with global list ignoring duplicates + readarray -t sbinaries_to_skip < <(for i in "${sbinaries_to_skip[@]}" "${handled_sbinaries[@]}"; do echo "$i"; done | sort -du) + + # if there is a -F perm flag, remove it + if grep -q '.*-F\s\+perm=[rwxa]\+.*' <<< "$concrete_rule"; then + + # Separate concrete_rule into three sections using hash '#' + # sign as a delimiter around rule's permission section borders + # note that the trailing space after perm flag is captured because there would be + # two consecutive spaces after joining remaining parts of the rule together + concrete_rule="$(echo "$concrete_rule" | sed -n "s/\(.*\)\+\(-F perm=[rwax]\+\ \?\)\+/\1#\2#/p")" + + # Split concrete_rule into head and tail sections using hash '#' delimiter + # The second column contains the permission section, which we don't need to extract + rule_head=$(cut -d '#' -f 1 <<< "$concrete_rule") + rule_tail=$(cut -d '#' -f 3 <<< "$concrete_rule") + + # Remove permissions section from existing rule in the file + sed -i "s#${rule_head}\(.*\)${rule_tail}#${rule_head}${rule_tail}#" "$afile" + fi + # If the required audit rule for particular sbinary wasn't found yet, insert it under following conditions: + # + # * in the "auditctl" mode of operation insert particular rule each time + # (because in this mode there's only one file -- /etc/audit/audit.rules to be inspected for presence of this rule), + # + # * in the "augenrules" mode of operation insert particular rule only once and only in case we have already + # searched all of the files from /etc/audit/rules.d/*.rules location (since that audit rule can be defined + # in any of those files and if not, we want it to be inserted only once into /etc/audit/rules.d/privileged.rules file) + # + elif [[ $count_of_inspected_files -eq "${#files_to_inspect[@]}" ]] + then + + # Check if this sbinary wasn't already handled in some of the previous afile iterations + # Return match only if whole sbinary definition matched (not in the case just prefix matched!!!) + if [[ ! $(sed -ne "\|${sbinary}|p" <<< "${sbinaries_to_skip[*]}") ]] + then + # Current audit rules file's content doesn't contain expected rule for this + # SUID/SGID binary yet => append it + echo "$expected_rule" >> "$output_audit_file" + fi + continue + fi + done +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - at + At a minimum, the audit system should collect the execution of +privileged commands for all users and root. If the auditd daemon is +configured to use the augenrules program to read audit rules during +daemon startup (the default), add a line of the following form to a file with +suffix .rules in the directory /etc/audit/rules.d: +-a always,exit -F path=/usr/bin/at -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add a line of the following +form to /etc/audit/audit.rules: +-a always,exit -F path=/usr/bin/at -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + CCI-000172 + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + FAU_GEN.1.1.c + SRG-OS-000471-VMM-001910 + Misuse of privileged functions, either intentionally or unintentionally by +authorized users, or by unauthorized external entities that have compromised system accounts, +is a serious and ongoing concern and can have significant adverse impacts on organizations. +Auditing the use of privileged functions is one way to detect such misuse and identify +the risk from insider and advanced persistent threats. + +Privileged programs are subject to escalation-of-privilege attacks, +which attempt to subvert their normal role of providing some necessary but +limited capability. As such, motivation exists to monitor these programs for +unusual activity. + CCE-80988-9 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80988-9 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_privileged_commands_at + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Perform remediation of Audit rules for /usr/bin/at + block: + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/bin/at -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules + set_fact: audit_file="/etc/audit/rules.d/privileged.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/bin/at -F perm=x -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/at -F perm=x + -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/bin/at -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:( + -S |,)\w+)+)( -F path=/usr/bin/at -F perm=x -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/at -F perm=x + -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80988-9 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_privileged_commands_at + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +ACTION_ARCH_FILTERS="-a always,exit" +OTHER_FILTERS="-F path=/usr/bin/at -F perm=x" +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="" +KEY="privileged" +SYSCALL_GROUPING="" +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - chage + At a minimum, the audit system should collect the execution of +privileged commands for all users and root. If the auditd daemon is +configured to use the augenrules program to read audit rules during +daemon startup (the default), add a line of the following form to a file with +suffix .rules in the directory /etc/audit/rules.d: +-a always,exit -F path=/usr/bin/chage -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add a line of the following +form to /etc/audit/audit.rules: +-a always,exit -F path=/usr/bin/chage -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + 1 + 12 + 13 + 14 + 15 + 16 + 2 + 3 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + BAI03.05 + DSS01.03 + DSS03.05 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 6.1 + SR 6.2 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.14.2.7 + A.15.2.1 + A.15.2.2 + CIP-004-6 R2.2.2 + CIP-004-6 R2.2.3 + CIP-007-3 R.1.3 + CIP-007-3 R5 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.3 + CIP-007-3 R5.2.1 + CIP-007-3 R5.2.3 + AC-2(4) + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.PT-1 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000468-GPOS-00212 + SRG-OS-000471-GPOS-00215 + SRG-OS-000471-VMM-001910 + RHEL-08-030250 + SV-230418r627750_rule + Misuse of privileged functions, either intentionally or unintentionally by +authorized users, or by unauthorized external entities that have compromised system accounts, +is a serious and ongoing concern and can have significant adverse impacts on organizations. +Auditing the use of privileged functions is one way to detect such misuse and identify +the risk from insider and advanced persistent threats. + +Privileged programs are subject to escalation-of-privilege attacks, +which attempt to subvert their normal role of providing some necessary but +limited capability. As such, motivation exists to monitor these programs for +unusual activity. + CCE-80725-5 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80725-5 + - DISA-STIG-RHEL-08-030250 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_privileged_commands_chage + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Perform remediation of Audit rules for /usr/bin/chage + block: + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/bin/chage -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules + set_fact: audit_file="/etc/audit/rules.d/privileged.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/bin/chage -F perm=x -F + auid>=1000 -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/chage -F perm=x + -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/bin/chage -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:( + -S |,)\w+)+)( -F path=/usr/bin/chage -F perm=x -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/chage -F perm=x + -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80725-5 + - DISA-STIG-RHEL-08-030250 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_privileged_commands_chage + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +ACTION_ARCH_FILTERS="-a always,exit" +OTHER_FILTERS="-F path=/usr/bin/chage -F perm=x" +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="" +KEY="privileged" +SYSCALL_GROUPING="" +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - chsh + At a minimum, the audit system should collect the execution of +privileged commands for all users and root. If the auditd daemon is +configured to use the augenrules program to read audit rules during +daemon startup (the default), add a line of the following form to a file with +suffix .rules in the directory /etc/audit/rules.d: +-a always,exit -F path=/usr/bin/chsh -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add a line of the following +form to /etc/audit/audit.rules: +-a always,exit -F path=/usr/bin/chsh -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + 1 + 12 + 13 + 14 + 15 + 16 + 2 + 3 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + BAI03.05 + DSS01.03 + DSS03.05 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 6.1 + SR 6.2 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.14.2.7 + A.15.2.1 + A.15.2.2 + CIP-004-6 R2.2.2 + CIP-004-6 R2.2.3 + CIP-007-3 R.1.3 + CIP-007-3 R5 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.3 + CIP-007-3 R5.2.1 + CIP-007-3 R5.2.3 + AC-2(4) + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.PT-1 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + SRG-OS-000471-VMM-001910 + RHEL-08-030410 + SV-230448r627750_rule + Misuse of privileged functions, either intentionally or unintentionally by +authorized users, or by unauthorized external entities that have compromised system accounts, +is a serious and ongoing concern and can have significant adverse impacts on organizations. +Auditing the use of privileged functions is one way to detect such misuse and identify +the risk from insider and advanced persistent threats. + +Privileged programs are subject to escalation-of-privilege attacks, +which attempt to subvert their normal role of providing some necessary but +limited capability. As such, motivation exists to monitor these programs for +unusual activity. + CCE-80726-3 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80726-3 + - DISA-STIG-RHEL-08-030410 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_privileged_commands_chsh + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Perform remediation of Audit rules for /usr/bin/chsh + block: + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/bin/chsh -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules + set_fact: audit_file="/etc/audit/rules.d/privileged.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/bin/chsh -F perm=x -F + auid>=1000 -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/chsh -F perm=x + -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/bin/chsh -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:( + -S |,)\w+)+)( -F path=/usr/bin/chsh -F perm=x -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/chsh -F perm=x + -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80726-3 + - DISA-STIG-RHEL-08-030410 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_privileged_commands_chsh + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +ACTION_ARCH_FILTERS="-a always,exit" +OTHER_FILTERS="-F path=/usr/bin/chsh -F perm=x" +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="" +KEY="privileged" +SYSCALL_GROUPING="" +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - crontab + At a minimum, the audit system should collect the execution of +privileged commands for all users and root. If the auditd daemon is +configured to use the augenrules program to read audit rules during +daemon startup (the default), add a line of the following form to a file with +suffix .rules in the directory /etc/audit/rules.d: +-a always,exit -F path=/usr/bin/crontab -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add a line of the following +form to /etc/audit/audit.rules: +-a always,exit -F path=/usr/bin/crontab -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + 1 + 12 + 13 + 14 + 15 + 16 + 2 + 3 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + BAI03.05 + DSS01.03 + DSS03.05 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 6.1 + SR 6.2 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.14.2.7 + A.15.2.1 + A.15.2.2 + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.PT-1 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + SRG-OS-000471-VMM-001910 + RHEL-08-030400 + SV-230447r627750_rule + Misuse of privileged functions, either intentionally or unintentionally by +authorized users, or by unauthorized external entities that have compromised system accounts, +is a serious and ongoing concern and can have significant adverse impacts on organizations. +Auditing the use of privileged functions is one way to detect such misuse and identify +the risk from insider and advanced persistent threats. + +Privileged programs are subject to escalation-of-privilege attacks, +which attempt to subvert their normal role of providing some necessary but +limited capability. As such, motivation exists to monitor these programs for +unusual activity. + CCE-80727-1 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80727-1 + - DISA-STIG-RHEL-08-030400 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_privileged_commands_crontab + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Perform remediation of Audit rules for /usr/bin/crontab + block: + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/bin/crontab -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules + set_fact: audit_file="/etc/audit/rules.d/privileged.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/bin/crontab -F perm=x + -F auid>=1000 -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/crontab -F perm=x + -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/bin/crontab -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:( + -S |,)\w+)+)( -F path=/usr/bin/crontab -F perm=x -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/crontab -F perm=x + -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80727-1 + - DISA-STIG-RHEL-08-030400 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_privileged_commands_crontab + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +ACTION_ARCH_FILTERS="-a always,exit" +OTHER_FILTERS="-F path=/usr/bin/crontab -F perm=x" +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="" +KEY="privileged" +SYSCALL_GROUPING="" +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - gpasswd + At a minimum, the audit system should collect the execution of +privileged commands for all users and root. If the auditd daemon is +configured to use the augenrules program to read audit rules during +daemon startup (the default), add a line of the following form to a file with +suffix .rules in the directory /etc/audit/rules.d: +-a always,exit -F path=/usr/bin/gpasswd -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add a line of the following +form to /etc/audit/audit.rules: +-a always,exit -F path=/usr/bin/gpasswd -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + 1 + 12 + 13 + 14 + 15 + 16 + 2 + 3 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + BAI03.05 + DSS01.03 + DSS03.05 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 6.1 + SR 6.2 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.14.2.7 + A.15.2.1 + A.15.2.2 + CIP-004-6 R2.2.2 + CIP-004-6 R2.2.3 + CIP-007-3 R.1.3 + CIP-007-3 R5 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.3 + CIP-007-3 R5.2.1 + CIP-007-3 R5.2.3 + AC-2(4) + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.PT-1 + FAU_GEN.1.1.c + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + SRG-OS-000471-VMM-001910 + RHEL-08-030370 + SV-230444r627750_rule + Misuse of privileged functions, either intentionally or unintentionally by +authorized users, or by unauthorized external entities that have compromised system accounts, +is a serious and ongoing concern and can have significant adverse impacts on organizations. +Auditing the use of privileged functions is one way to detect such misuse and identify +the risk from insider and advanced persistent threats. + +Privileged programs are subject to escalation-of-privilege attacks, +which attempt to subvert their normal role of providing some necessary but +limited capability. As such, motivation exists to monitor these programs for +unusual activity. + CCE-80728-9 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80728-9 + - DISA-STIG-RHEL-08-030370 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_privileged_commands_gpasswd + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Perform remediation of Audit rules for /usr/bin/gpasswd + block: + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/bin/gpasswd -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules + set_fact: audit_file="/etc/audit/rules.d/privileged.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/bin/gpasswd -F perm=x + -F auid>=1000 -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/gpasswd -F perm=x + -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/bin/gpasswd -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:( + -S |,)\w+)+)( -F path=/usr/bin/gpasswd -F perm=x -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/gpasswd -F perm=x + -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80728-9 + - DISA-STIG-RHEL-08-030370 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_privileged_commands_gpasswd + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +ACTION_ARCH_FILTERS="-a always,exit" +OTHER_FILTERS="-F path=/usr/bin/gpasswd -F perm=x" +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="" +KEY="privileged" +SYSCALL_GROUPING="" +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - kmod + At a minimum, the audit system should collect the execution of +privileged commands for all users and root. If the auditd daemon is +configured to use the augenrules program to read audit rules during +daemon startup (the default), add a line of the following form to a file with +suffix .rules in the directory /etc/audit/rules.d: +-a always,exit -F path=/usr/bin/kmod -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add a line of the following +form to /etc/audit/audit.rules: +-a always,exit -F path=/usr/bin/kmod -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + AU-3 + AU-3.1 + AU-12(a) + AU-12.1(ii) + AU-12.1(iv)AU-12(c) + MA-4(1)(a) + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + SRG-OS-000471-GPOS-00216 + SRG-OS-000477-GPOS-00222 + RHEL-08-030580 + SV-230465r627750_rule + Without generating audit records that are specific to the security and +mission needs of the organization, it would be difficult to establish, +correlate, and investigate the events relating to an incident or identify +those responsible for one. + +Audit records can be generated from various components within the +information system (e.g., module or policy filter). + + CCE-89455-0 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-89455-0 + - DISA-STIG-RHEL-08-030580 + - NIST-800-53-AU-12(a) + - NIST-800-53-AU-12.1(ii) + - NIST-800-53-AU-12.1(iv)AU-12(c) + - NIST-800-53-AU-3 + - NIST-800-53-AU-3.1 + - NIST-800-53-MA-4(1)(a) + - audit_rules_privileged_commands_kmod + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Perform remediation of Audit rules for /usr/bin/kmod + block: + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/bin/kmod -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules + set_fact: audit_file="/etc/audit/rules.d/privileged.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/bin/kmod -F perm=x -F + auid>=1000 -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/kmod -F perm=x + -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/bin/kmod -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:( + -S |,)\w+)+)( -F path=/usr/bin/kmod -F perm=x -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/kmod -F perm=x + -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-89455-0 + - DISA-STIG-RHEL-08-030580 + - NIST-800-53-AU-12(a) + - NIST-800-53-AU-12.1(ii) + - NIST-800-53-AU-12.1(iv)AU-12(c) + - NIST-800-53-AU-3 + - NIST-800-53-AU-3.1 + - NIST-800-53-MA-4(1)(a) + - audit_rules_privileged_commands_kmod + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +ACTION_ARCH_FILTERS="-a always,exit" +OTHER_FILTERS="-F path=/usr/bin/kmod -F perm=x" +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="" +KEY="privileged" +SYSCALL_GROUPING="" +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - mount + At a minimum, the audit system should collect the execution of +privileged commands for all users and root. If the auditd daemon is +configured to use the augenrules program to read audit rules during +daemon startup (the default), add a line of the following form to a file with +suffix .rules in the directory /etc/audit/rules.d: +-a always,exit -F path=/usr/bin/mount -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add a line of the following +form to /etc/audit/audit.rules: +-a always,exit -F path=/usr/bin/mount -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + FAU_GEN.1.1.c + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + SRG-OS-000471-VMM-001910 + RHEL-08-030300 + SV-230423r627750_rule + Misuse of privileged functions, either intentionally or unintentionally by +authorized users, or by unauthorized external entities that have compromised system accounts, +is a serious and ongoing concern and can have significant adverse impacts on organizations. +Auditing the use of privileged functions is one way to detect such misuse and identify +the risk from insider and advanced persistent threats. + +Privileged programs are subject to escalation-of-privilege attacks, +which attempt to subvert their normal role of providing some necessary but +limited capability. As such, motivation exists to monitor these programs for +unusual activity. + CCE-80989-7 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80989-7 + - DISA-STIG-RHEL-08-030300 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_privileged_commands_mount + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Perform remediation of Audit rules for /usr/bin/mount + block: + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/bin/mount -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules + set_fact: audit_file="/etc/audit/rules.d/privileged.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/bin/mount -F perm=x -F + auid>=1000 -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/mount -F perm=x + -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/bin/mount -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:( + -S |,)\w+)+)( -F path=/usr/bin/mount -F perm=x -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/mount -F perm=x + -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80989-7 + - DISA-STIG-RHEL-08-030300 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_privileged_commands_mount + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +ACTION_ARCH_FILTERS="-a always,exit" +OTHER_FILTERS="-F path=/usr/bin/mount -F perm=x" +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="" +KEY="privileged" +SYSCALL_GROUPING="" +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - newgidmap + At a minimum, the audit system should collect the execution of +privileged commands for all users and root. If the auditd daemon is +configured to use the augenrules program to read audit rules during +daemon startup (the default), add a line of the following form to a file with +suffix .rules in the directory /etc/audit/rules.d: +-a always,exit -F path=/usr/bin/newgidmap -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add a line of the following +form to /etc/audit/audit.rules: +-a always,exit -F path=/usr/bin/newgidmap -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + CCI-000172 + CIP-004-6 R2.2.2 + CIP-004-6 R2.2.3 + CIP-007-3 R.1.3 + CIP-007-3 R5 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.3 + CIP-007-3 R5.2.1 + CIP-007-3 R5.2.3 + AC-2(4) + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + FAU_GEN.1.1.c + SRG-OS-000471-VMM-001910 + Misuse of privileged functions, either intentionally or unintentionally by +authorized users, or by unauthorized external entities that have compromised system accounts, +is a serious and ongoing concern and can have significant adverse impacts on organizations. +Auditing the use of privileged functions is one way to detect such misuse and identify +the risk from insider and advanced persistent threats. + +Privileged programs are subject to escalation-of-privilege attacks, +which attempt to subvert their normal role of providing some necessary but +limited capability. As such, motivation exists to monitor these programs for +unusual activity. + CCE-80991-3 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80991-3 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_privileged_commands_newgidmap + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Perform remediation of Audit rules for /usr/bin/newgidmap + block: + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/bin/newgidmap -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules + set_fact: audit_file="/etc/audit/rules.d/privileged.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/bin/newgidmap -F perm=x + -F auid>=1000 -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/newgidmap -F + perm=x -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/bin/newgidmap -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:( + -S |,)\w+)+)( -F path=/usr/bin/newgidmap -F perm=x -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/newgidmap -F + perm=x -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80991-3 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_privileged_commands_newgidmap + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +ACTION_ARCH_FILTERS="-a always,exit" +OTHER_FILTERS="-F path=/usr/bin/newgidmap -F perm=x" +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="" +KEY="privileged" +SYSCALL_GROUPING="" +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - newgrp + At a minimum, the audit system should collect the execution of +privileged commands for all users and root. If the auditd daemon is +configured to use the augenrules program to read audit rules during +daemon startup (the default), add a line of the following form to a file with +suffix .rules in the directory /etc/audit/rules.d: +-a always,exit -F path=/usr/bin/newgrp -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add a line of the following +form to /etc/audit/audit.rules: +-a always,exit -F path=/usr/bin/newgrp -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + 1 + 12 + 13 + 14 + 15 + 16 + 2 + 3 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + BAI03.05 + DSS01.03 + DSS03.05 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000130 + CCI-000169 + CCI-000135 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 6.1 + SR 6.2 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.14.2.7 + A.15.2.1 + A.15.2.2 + CIP-004-6 R2.2.2 + CIP-004-6 R2.2.3 + CIP-007-3 R.1.3 + CIP-007-3 R5 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.3 + CIP-007-3 R5.2.1 + CIP-007-3 R5.2.3 + AC-2(4) + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.PT-1 + FAU_GEN.1.1.c + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + SRG-OS-000471-VMM-001910 + RHEL-08-030350 + SV-230437r627750_rule + Misuse of privileged functions, either intentionally or unintentionally by +authorized users, or by unauthorized external entities that have compromised system accounts, +is a serious and ongoing concern and can have significant adverse impacts on organizations. +Auditing the use of privileged functions is one way to detect such misuse and identify +the risk from insider and advanced persistent threats. + +Privileged programs are subject to escalation-of-privilege attacks, +which attempt to subvert their normal role of providing some necessary but +limited capability. As such, motivation exists to monitor these programs for +unusual activity. + CCE-80729-7 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80729-7 + - DISA-STIG-RHEL-08-030350 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_privileged_commands_newgrp + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Perform remediation of Audit rules for /usr/bin/newgrp + block: + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/bin/newgrp -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules + set_fact: audit_file="/etc/audit/rules.d/privileged.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/bin/newgrp -F perm=x -F + auid>=1000 -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/newgrp -F perm=x + -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/bin/newgrp -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:( + -S |,)\w+)+)( -F path=/usr/bin/newgrp -F perm=x -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/newgrp -F perm=x + -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80729-7 + - DISA-STIG-RHEL-08-030350 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_privileged_commands_newgrp + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +ACTION_ARCH_FILTERS="-a always,exit" +OTHER_FILTERS="-F path=/usr/bin/newgrp -F perm=x" +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="" +KEY="privileged" +SYSCALL_GROUPING="" +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - newuidmap + At a minimum, the audit system should collect the execution of +privileged commands for all users and root. If the auditd daemon is +configured to use the augenrules program to read audit rules during +daemon startup (the default), add a line of the following form to a file with +suffix .rules in the directory /etc/audit/rules.d: +-a always,exit -F path=/usr/bin/newuidmap -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add a line of the following +form to /etc/audit/audit.rules: +-a always,exit -F path=/usr/bin/newuidmap -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + CCI-000172 + CIP-004-6 R2.2.2 + CIP-004-6 R2.2.3 + CIP-007-3 R.1.3 + CIP-007-3 R5 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.3 + CIP-007-3 R5.2.1 + CIP-007-3 R5.2.3 + AC-2(4) + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + FAU_GEN.1.1.c + SRG-OS-000471-VMM-001910 + Misuse of privileged functions, either intentionally or unintentionally by +authorized users, or by unauthorized external entities that have compromised system accounts, +is a serious and ongoing concern and can have significant adverse impacts on organizations. +Auditing the use of privileged functions is one way to detect such misuse and identify +the risk from insider and advanced persistent threats. + +Privileged programs are subject to escalation-of-privilege attacks, +which attempt to subvert their normal role of providing some necessary but +limited capability. As such, motivation exists to monitor these programs for +unusual activity. + CCE-80992-1 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80992-1 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_privileged_commands_newuidmap + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Perform remediation of Audit rules for /usr/bin/newuidmap + block: + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/bin/newuidmap -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules + set_fact: audit_file="/etc/audit/rules.d/privileged.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/bin/newuidmap -F perm=x + -F auid>=1000 -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/newuidmap -F + perm=x -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/bin/newuidmap -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:( + -S |,)\w+)+)( -F path=/usr/bin/newuidmap -F perm=x -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/newuidmap -F + perm=x -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80992-1 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_privileged_commands_newuidmap + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +ACTION_ARCH_FILTERS="-a always,exit" +OTHER_FILTERS="-F path=/usr/bin/newuidmap -F perm=x" +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="" +KEY="privileged" +SYSCALL_GROUPING="" +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - pam_timestamp_check + At a minimum, the audit system should collect the execution of +privileged commands for all users and root. If the auditd daemon is +configured to use the augenrules program to read audit rules during +daemon startup (the default), add a line of the following form to a file with +suffix .rules in the directory /etc/audit/rules.d: +-a always,exit -F path=/usr/sbin/pam_timestamp_check +-F perm=x -F auid>=1000 -F auid!=unset -F key=privileged +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add a line of the following +form to /etc/audit/audit.rules: +-a always,exit -F path=/usr/sbin/pam_timestamp_check +-F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + 1 + 12 + 13 + 14 + 15 + 16 + 2 + 3 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + BAI03.05 + DSS01.03 + DSS03.05 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 6.1 + SR 6.2 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.14.2.7 + A.15.2.1 + A.15.2.2 + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.PT-1 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + SRG-OS-000471-VMM-001910 + RHEL-08-030340 + SV-230436r627750_rule + Misuse of privileged functions, either intentionally or unintentionally by +authorized users, or by unauthorized external entities that have compromised system accounts, +is a serious and ongoing concern and can have significant adverse impacts on organizations. +Auditing the use of privileged functions is one way to detect such misuse and identify +the risk from insider and advanced persistent threats. + +Privileged programs are subject to escalation-of-privilege attacks, +which attempt to subvert their normal role of providing some necessary but +limited capability. As such, motivation exists to monitor these programs for +unusual activity. + CCE-80730-5 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80730-5 + - DISA-STIG-RHEL-08-030340 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_privileged_commands_pam_timestamp_check + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Perform remediation of Audit rules for /usr/sbin/pam_timestamp_check + block: + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/sbin/pam_timestamp_check -F perm=x -F auid>=1000 -F auid!=unset + (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules + set_fact: audit_file="/etc/audit/rules.d/privileged.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/sbin/pam_timestamp_check + -F perm=x -F auid>=1000 -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/sbin/pam_timestamp_check + -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/sbin/pam_timestamp_check -F perm=x -F auid>=1000 -F auid!=unset + (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:( + -S |,)\w+)+)( -F path=/usr/sbin/pam_timestamp_check -F perm=x -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/sbin/pam_timestamp_check + -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80730-5 + - DISA-STIG-RHEL-08-030340 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_privileged_commands_pam_timestamp_check + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +ACTION_ARCH_FILTERS="-a always,exit" +OTHER_FILTERS="-F path=/usr/sbin/pam_timestamp_check -F perm=x" +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="" +KEY="privileged" +SYSCALL_GROUPING="" +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - passwd + At a minimum, the audit system should collect the execution of +privileged commands for all users and root. If the auditd daemon is +configured to use the augenrules program to read audit rules during +daemon startup (the default), add a line of the following form to a file with +suffix .rules in the directory /etc/audit/rules.d: +-a always,exit -F path=/usr/bin/passwd -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add a line of the following +form to /etc/audit/audit.rules: +-a always,exit -F path=/usr/bin/passwd -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + 1 + 12 + 13 + 14 + 15 + 16 + 2 + 3 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + BAI03.05 + DSS01.03 + DSS03.05 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 6.1 + SR 6.2 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.14.2.7 + A.15.2.1 + A.15.2.2 + CIP-004-6 R2.2.2 + CIP-004-6 R2.2.3 + CIP-007-3 R.1.3 + CIP-007-3 R5 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.3 + CIP-007-3 R5.2.1 + CIP-007-3 R5.2.3 + AC-2(4) + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.PT-1 + FAU_GEN.1.1.c + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + SRG-OS-000471-VMM-001910 + RHEL-08-030290 + SV-230422r627750_rule + Misuse of privileged functions, either intentionally or unintentionally by +authorized users, or by unauthorized external entities that have compromised system accounts, +is a serious and ongoing concern and can have significant adverse impacts on organizations. +Auditing the use of privileged functions is one way to detect such misuse and identify +the risk from insider and advanced persistent threats. + +Privileged programs are subject to escalation-of-privilege attacks, +which attempt to subvert their normal role of providing some necessary but +limited capability. As such, motivation exists to monitor these programs for +unusual activity. + CCE-80731-3 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80731-3 + - DISA-STIG-RHEL-08-030290 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_privileged_commands_passwd + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Perform remediation of Audit rules for /usr/bin/passwd + block: + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/bin/passwd -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules + set_fact: audit_file="/etc/audit/rules.d/privileged.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/bin/passwd -F perm=x -F + auid>=1000 -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/passwd -F perm=x + -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/bin/passwd -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:( + -S |,)\w+)+)( -F path=/usr/bin/passwd -F perm=x -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/passwd -F perm=x + -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80731-3 + - DISA-STIG-RHEL-08-030290 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_privileged_commands_passwd + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +ACTION_ARCH_FILTERS="-a always,exit" +OTHER_FILTERS="-F path=/usr/bin/passwd -F perm=x" +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="" +KEY="privileged" +SYSCALL_GROUPING="" +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - postdrop + At a minimum, the audit system should collect the execution of +privileged commands for all users and root. If the auditd daemon is +configured to use the augenrules program to read audit rules during +daemon startup (the default), add a line of the following form to a file with +suffix .rules in the directory /etc/audit/rules.d: +-a always,exit -F path=/usr/sbin/postdrop -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add a line of the following +form to /etc/audit/audit.rules: +-a always,exit -F path=/usr/sbin/postdrop -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + 1 + 12 + 13 + 14 + 15 + 16 + 2 + 3 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + BAI03.05 + DSS01.03 + DSS03.05 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 6.1 + SR 6.2 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.14.2.7 + A.15.2.1 + A.15.2.2 + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.PT-1 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + SRG-OS-000471-VMM-001910 + RHEL-08-030311 + SV-230427r627750_rule + Misuse of privileged functions, either intentionally or unintentionally by +authorized users, or by unauthorized external entities that have compromised system accounts, +is a serious and ongoing concern and can have significant adverse impacts on organizations. +Auditing the use of privileged functions is one way to detect such misuse and identify +the risk from insider and advanced persistent threats. + +Privileged programs are subject to escalation-of-privilege attacks, +which attempt to subvert their normal role of providing some necessary but +limited capability. As such, motivation exists to monitor these programs for +unusual activity. + CCE-80732-1 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80732-1 + - DISA-STIG-RHEL-08-030311 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_privileged_commands_postdrop + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Perform remediation of Audit rules for /usr/sbin/postdrop + block: + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/sbin/postdrop -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules + set_fact: audit_file="/etc/audit/rules.d/privileged.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/sbin/postdrop -F perm=x + -F auid>=1000 -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/sbin/postdrop -F + perm=x -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/sbin/postdrop -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:( + -S |,)\w+)+)( -F path=/usr/sbin/postdrop -F perm=x -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/sbin/postdrop -F + perm=x -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80732-1 + - DISA-STIG-RHEL-08-030311 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_privileged_commands_postdrop + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +ACTION_ARCH_FILTERS="-a always,exit" +OTHER_FILTERS="-F path=/usr/sbin/postdrop -F perm=x" +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="" +KEY="privileged" +SYSCALL_GROUPING="" +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - postqueue + At a minimum, the audit system should collect the execution of +privileged commands for all users and root. If the auditd daemon is +configured to use the augenrules program to read audit rules during +daemon startup (the default), add a line of the following form to a file with +suffix .rules in the directory /etc/audit/rules.d: +-a always,exit -F path=/usr/sbin/postqueue -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add a line of the following +form to /etc/audit/audit.rules: +-a always,exit -F path=/usr/sbin/postqueue -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + 1 + 12 + 13 + 14 + 15 + 16 + 2 + 3 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + BAI03.05 + DSS01.03 + DSS03.05 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 6.1 + SR 6.2 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.14.2.7 + A.15.2.1 + A.15.2.2 + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.PT-1 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + SRG-OS-000471-VMM-001910 + RHEL-08-030312 + SV-230428r627750_rule + Misuse of privileged functions, either intentionally or unintentionally by +authorized users, or by unauthorized external entities that have compromised system accounts, +is a serious and ongoing concern and can have significant adverse impacts on organizations. +Auditing the use of privileged functions is one way to detect such misuse and identify +the risk from insider and advanced persistent threats. + +Privileged programs are subject to escalation-of-privilege attacks, +which attempt to subvert their normal role of providing some necessary but +limited capability. As such, motivation exists to monitor these programs for +unusual activity. + CCE-80733-9 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80733-9 + - DISA-STIG-RHEL-08-030312 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_privileged_commands_postqueue + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Perform remediation of Audit rules for /usr/sbin/postqueue + block: + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/sbin/postqueue -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules + set_fact: audit_file="/etc/audit/rules.d/privileged.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/sbin/postqueue -F perm=x + -F auid>=1000 -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/sbin/postqueue -F + perm=x -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/sbin/postqueue -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:( + -S |,)\w+)+)( -F path=/usr/sbin/postqueue -F perm=x -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/sbin/postqueue -F + perm=x -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80733-9 + - DISA-STIG-RHEL-08-030312 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_privileged_commands_postqueue + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +ACTION_ARCH_FILTERS="-a always,exit" +OTHER_FILTERS="-F path=/usr/sbin/postqueue -F perm=x" +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="" +KEY="privileged" +SYSCALL_GROUPING="" +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - pt_chown + At a minimum, the audit system should collect the execution of +privileged commands for all users and root. If the auditd daemon is +configured to use the augenrules program to read audit rules during +daemon startup (the default), add a line of the following form to a file with +suffix .rules in the directory /etc/audit/rules.d: +-a always,exit -F path=/usr/libexec/pt_chown -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add a line of the following +form to /etc/audit/audit.rules: +-a always,exit -F path=/usr/libexec/pt_chown -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + 1 + 12 + 13 + 14 + 15 + 16 + 2 + 3 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + BAI03.05 + DSS01.03 + DSS03.05 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000135 + CCI-000172 + CCI-002884 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 6.1 + SR 6.2 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.14.2.7 + A.15.2.1 + A.15.2.2 + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.PT-1 + SRG-OS-000042-GPOS-00020 + SRG-OS-000392-GPOS-00172 + SRG-OS-000471-GPOS-00215 + SRG-OS-000471-VMM-001910 + Misuse of privileged functions, either intentionally or unintentionally by +authorized users, or by unauthorized external entities that have compromised system accounts, +is a serious and ongoing concern and can have significant adverse impacts on organizations. +Auditing the use of privileged functions is one way to detect such misuse and identify +the risk from insider and advanced persistent threats. + +Privileged programs are subject to escalation-of-privilege attacks, +which attempt to subvert their normal role of providing some necessary but +limited capability. As such, motivation exists to monitor these programs for +unusual activity. + CCE-80734-7 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80734-7 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_privileged_commands_pt_chown + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Perform remediation of Audit rules for /usr/libexec/pt_chown + block: + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/libexec/pt_chown -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules + set_fact: audit_file="/etc/audit/rules.d/privileged.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/libexec/pt_chown -F perm=x + -F auid>=1000 -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/libexec/pt_chown + -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/libexec/pt_chown -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:( + -S |,)\w+)+)( -F path=/usr/libexec/pt_chown -F perm=x -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/libexec/pt_chown + -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80734-7 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_privileged_commands_pt_chown + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +ACTION_ARCH_FILTERS="-a always,exit" +OTHER_FILTERS="-F path=/usr/libexec/pt_chown -F perm=x" +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="" +KEY="privileged" +SYSCALL_GROUPING="" +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Any Attempts to Run ssh-agent + At a minimum, the audit system should collect any execution attempt +of the ssh-agent command for all users and root. If the auditd +daemon is configured to use the augenrules program to read audit rules +during daemon startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: +-a always,exit -F path=/usr/bin/ssh-agent -F perm=x -F auid>=1000 -F auid!=unset -k privileged-ssh-agent +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F path=/usr/bin/ssh-agent -F perm=x -F auid>=1000 -F auid!=unset -k privileged-ssh-agent + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + RHEL-08-030280 + SV-230421r627750_rule + Without generating audit records that are specific to the security and +mission needs of the organization, it would be difficult to establish, +correlate, and investigate the events relating to an incident or identify +those responsible for one. + +Audit records can be generated from various components within the +information system (e.g., module or policy filter). + CCE-85944-7 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-85944-7 + - DISA-STIG-RHEL-08-030280 + - audit_rules_privileged_commands_ssh_agent + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Perform remediation of Audit rules for /usr/bin/ssh-agent + block: + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/bin/ssh-agent -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules + set_fact: audit_file="/etc/audit/rules.d/privileged.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/bin/ssh-agent -F perm=x + -F auid>=1000 -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/ssh-agent -F + perm=x -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/bin/ssh-agent -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:( + -S |,)\w+)+)( -F path=/usr/bin/ssh-agent -F perm=x -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/ssh-agent -F + perm=x -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-85944-7 + - DISA-STIG-RHEL-08-030280 + - audit_rules_privileged_commands_ssh_agent + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +ACTION_ARCH_FILTERS="-a always,exit" +OTHER_FILTERS="-F path=/usr/bin/ssh-agent -F perm=x" +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="" +KEY="privileged" +SYSCALL_GROUPING="" +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - ssh-keysign + At a minimum, the audit system should collect the execution of +privileged commands for all users and root. If the auditd daemon is +configured to use the augenrules program to read audit rules during +daemon startup (the default), add a line of the following form to a file with +suffix .rules in the directory /etc/audit/rules.d: +-a always,exit -F path=/usr/libexec/openssh/ssh-keysign -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add a line of the following +form to /etc/audit/audit.rules: +-a always,exit -F path=/usr/libexec/openssh/ssh-keysign -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + 1 + 12 + 13 + 14 + 15 + 16 + 2 + 3 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + BAI03.05 + DSS01.03 + DSS03.05 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 6.1 + SR 6.2 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.14.2.7 + A.15.2.1 + A.15.2.2 + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.PT-1 + FAU_GEN.1.1.c + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + SRG-OS-000471-VMM-001910 + RHEL-08-030320 + SV-230434r744002_rule + Misuse of privileged functions, either intentionally or unintentionally by +authorized users, or by unauthorized external entities that have compromised system accounts, +is a serious and ongoing concern and can have significant adverse impacts on organizations. +Auditing the use of privileged functions is one way to detect such misuse and identify +the risk from insider and advanced persistent threats. + +Privileged programs are subject to escalation-of-privilege attacks, +which attempt to subvert their normal role of providing some necessary but +limited capability. As such, motivation exists to monitor these programs for +unusual activity. + CCE-80735-4 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80735-4 + - DISA-STIG-RHEL-08-030320 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_privileged_commands_ssh_keysign + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Perform remediation of Audit rules for /usr/libexec/openssh/ssh-keysign + block: + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/libexec/openssh/ssh-keysign -F perm=x -F auid>=1000 -F auid!=unset + (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules + set_fact: audit_file="/etc/audit/rules.d/privileged.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/libexec/openssh/ssh-keysign + -F perm=x -F auid>=1000 -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/libexec/openssh/ssh-keysign + -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/libexec/openssh/ssh-keysign -F perm=x -F auid>=1000 -F auid!=unset + (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:( + -S |,)\w+)+)( -F path=/usr/libexec/openssh/ssh-keysign -F perm=x -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/libexec/openssh/ssh-keysign + -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80735-4 + - DISA-STIG-RHEL-08-030320 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_privileged_commands_ssh_keysign + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +ACTION_ARCH_FILTERS="-a always,exit" +OTHER_FILTERS="-F path=/usr/libexec/openssh/ssh-keysign -F perm=x" +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="" +KEY="privileged" +SYSCALL_GROUPING="" +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - su + At a minimum, the audit system should collect the execution of +privileged commands for all users and root. If the auditd daemon is +configured to use the augenrules program to read audit rules during +daemon startup (the default), add a line of the following form to a file with +suffix .rules in the directory /etc/audit/rules.d: +-a always,exit -F path=/usr/bin/su -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add a line of the following +form to /etc/audit/audit.rules: +-a always,exit -F path=/usr/bin/su -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + 1 + 12 + 13 + 14 + 15 + 16 + 2 + 3 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + BAI03.05 + DSS01.03 + DSS03.05 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 6.1 + SR 6.2 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.14.2.7 + A.15.2.1 + A.15.2.2 + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.PT-1 + FAU_GEN.1.1.c + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000064-GPOS-0003 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + SRG-OS-000466-GPOS-00210 + SRG-OS-000471-VMM-001910 + RHEL-08-030190 + SV-230412r627750_rule + Misuse of privileged functions, either intentionally or unintentionally by +authorized users, or by unauthorized external entities that have compromised system accounts, +is a serious and ongoing concern and can have significant adverse impacts on organizations. +Auditing the use of privileged functions is one way to detect such misuse and identify +the risk from insider and advanced persistent threats. + +Privileged programs are subject to escalation-of-privilege attacks, +which attempt to subvert their normal role of providing some necessary but +limited capability. As such, motivation exists to monitor these programs for +unusual activity. + CCE-80736-2 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80736-2 + - DISA-STIG-RHEL-08-030190 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_privileged_commands_su + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Perform remediation of Audit rules for /usr/bin/su + block: + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/bin/su -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules + set_fact: audit_file="/etc/audit/rules.d/privileged.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/bin/su -F perm=x -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/su -F perm=x + -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/bin/su -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:( + -S |,)\w+)+)( -F path=/usr/bin/su -F perm=x -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/su -F perm=x + -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80736-2 + - DISA-STIG-RHEL-08-030190 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_privileged_commands_su + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +ACTION_ARCH_FILTERS="-a always,exit" +OTHER_FILTERS="-F path=/usr/bin/su -F perm=x" +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="" +KEY="privileged" +SYSCALL_GROUPING="" +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - sudo + At a minimum, the audit system should collect the execution of +privileged commands for all users and root. If the auditd daemon is +configured to use the augenrules program to read audit rules during +daemon startup (the default), add a line of the following form to a file with +suffix .rules in the directory /etc/audit/rules.d: +-a always,exit -F path=/usr/bin/sudo -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add a line of the following +form to /etc/audit/audit.rules: +-a always,exit -F path=/usr/bin/sudo -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + BP28(R19) + 1 + 12 + 13 + 14 + 15 + 16 + 2 + 3 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + BAI03.05 + DSS01.03 + DSS03.05 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 6.1 + SR 6.2 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.14.2.7 + A.15.2.1 + A.15.2.2 + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.PT-1 + FAU_GEN.1.1.c + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + SRG-OS-000466-GPOS-00210 + SRG-OS-000471-VMM-001910 + RHEL-08-030550 + SV-230462r627750_rule + Misuse of privileged functions, either intentionally or unintentionally by +authorized users, or by unauthorized external entities that have compromised system accounts, +is a serious and ongoing concern and can have significant adverse impacts on organizations. +Auditing the use of privileged functions is one way to detect such misuse and identify +the risk from insider and advanced persistent threats. + +Privileged programs are subject to escalation-of-privilege attacks, +which attempt to subvert their normal role of providing some necessary but +limited capability. As such, motivation exists to monitor these programs for +unusual activity. + CCE-80737-0 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80737-0 + - DISA-STIG-RHEL-08-030550 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_privileged_commands_sudo + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Perform remediation of Audit rules for /usr/bin/sudo + block: + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/bin/sudo -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules + set_fact: audit_file="/etc/audit/rules.d/privileged.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/bin/sudo -F perm=x -F + auid>=1000 -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/sudo -F perm=x + -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/bin/sudo -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:( + -S |,)\w+)+)( -F path=/usr/bin/sudo -F perm=x -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/sudo -F perm=x + -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80737-0 + - DISA-STIG-RHEL-08-030550 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_privileged_commands_sudo + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +ACTION_ARCH_FILTERS="-a always,exit" +OTHER_FILTERS="-F path=/usr/bin/sudo -F perm=x" +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="" +KEY="privileged" +SYSCALL_GROUPING="" +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - sudoedit + At a minimum, the audit system should collect the execution of +privileged commands for all users and root. If the auditd daemon is +configured to use the augenrules program to read audit rules during +daemon startup (the default), add a line of the following form to a file with +suffix .rules in the directory /etc/audit/rules.d: +-a always,exit -F path=/usr/bin/sudoedit -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add a line of the following +form to /etc/audit/audit.rules: +-a always,exit -F path=/usr/bin/sudoedit -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + 1 + 12 + 13 + 14 + 15 + 16 + 2 + 3 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + BAI03.05 + DSS01.03 + DSS03.05 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 6.1 + SR 6.2 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.14.2.7 + A.15.2.1 + A.15.2.2 + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.PT-1 + FAU_GEN.1.1.c + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + SRG-OS-000471-VMM-001910 + Misuse of privileged functions, either intentionally or unintentionally by +authorized users, or by unauthorized external entities that have compromised system accounts, +is a serious and ongoing concern and can have significant adverse impacts on organizations. +Auditing the use of privileged functions is one way to detect such misuse and identify +the risk from insider and advanced persistent threats. + +Privileged programs are subject to escalation-of-privilege attacks, +which attempt to subvert their normal role of providing some necessary but +limited capability. As such, motivation exists to monitor these programs for +unusual activity. + CCE-80738-8 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80738-8 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_privileged_commands_sudoedit + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Perform remediation of Audit rules for /usr/bin/sudoedit + block: + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/bin/sudoedit -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules + set_fact: audit_file="/etc/audit/rules.d/privileged.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/bin/sudoedit -F perm=x + -F auid>=1000 -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/sudoedit -F + perm=x -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/bin/sudoedit -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:( + -S |,)\w+)+)( -F path=/usr/bin/sudoedit -F perm=x -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/sudoedit -F + perm=x -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80738-8 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_privileged_commands_sudoedit + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +ACTION_ARCH_FILTERS="-a always,exit" +OTHER_FILTERS="-F path=/usr/bin/sudoedit -F perm=x" +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="" +KEY="privileged" +SYSCALL_GROUPING="" +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - umount + At a minimum, the audit system should collect the execution of +privileged commands for all users and root. If the auditd daemon is +configured to use the augenrules program to read audit rules during +daemon startup (the default), add a line of the following form to a file with +suffix .rules in the directory /etc/audit/rules.d: +-a always,exit -F path=/usr/bin/umount -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add a line of the following +form to /etc/audit/audit.rules: +-a always,exit -F path=/usr/bin/umount -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + 1 + 12 + 13 + 14 + 15 + 16 + 2 + 3 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + BAI03.05 + DSS01.03 + DSS03.05 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000130 + CCI-000169 + CCI-000135 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 6.1 + SR 6.2 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.14.2.7 + A.15.2.1 + A.15.2.2 + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.PT-1 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + SRG-OS-000471-VMM-001910 + RHEL-08-030301 + SV-230424r627750_rule + Misuse of privileged functions, either intentionally or unintentionally by +authorized users, or by unauthorized external entities that have compromised system accounts, +is a serious and ongoing concern and can have significant adverse impacts on organizations. +Auditing the use of privileged functions is one way to detect such misuse and identify +the risk from insider and advanced persistent threats. + +Privileged programs are subject to escalation-of-privilege attacks, +which attempt to subvert their normal role of providing some necessary but +limited capability. As such, motivation exists to monitor these programs for +unusual activity. + CCE-80739-6 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80739-6 + - DISA-STIG-RHEL-08-030301 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_privileged_commands_umount + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Perform remediation of Audit rules for /usr/bin/umount + block: + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/bin/umount -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules + set_fact: audit_file="/etc/audit/rules.d/privileged.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/bin/umount -F perm=x -F + auid>=1000 -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/umount -F perm=x + -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/bin/umount -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:( + -S |,)\w+)+)( -F path=/usr/bin/umount -F perm=x -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/umount -F perm=x + -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80739-6 + - DISA-STIG-RHEL-08-030301 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_privileged_commands_umount + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +ACTION_ARCH_FILTERS="-a always,exit" +OTHER_FILTERS="-F path=/usr/bin/umount -F perm=x" +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="" +KEY="privileged" +SYSCALL_GROUPING="" +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - unix_chkpwd + At a minimum, the audit system should collect the execution of +privileged commands for all users and root. If the auditd daemon is +configured to use the augenrules program to read audit rules during +daemon startup (the default), add a line of the following form to a file with +suffix .rules in the directory /etc/audit/rules.d: +-a always,exit -F path=/usr/sbin/unix_chkpwd -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add a line of the following +form to /etc/audit/audit.rules: +-a always,exit -F path=/usr/sbin/unix_chkpwd -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + 1 + 12 + 13 + 14 + 15 + 16 + 2 + 3 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + BAI03.05 + DSS01.03 + DSS03.05 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 6.1 + SR 6.2 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.14.2.7 + A.15.2.1 + A.15.2.2 + CIP-004-6 R2.2.2 + CIP-004-6 R2.2.3 + CIP-007-3 R.1.3 + CIP-007-3 R5 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.3 + CIP-007-3 R5.2.1 + CIP-007-3 R5.2.3 + CIP-007-3 R6.5 + AC-2(4) + AU-2(d) + AU-3 + AU-3.1 + AU-12(a) + AU-12(c) + AU-12.1(ii) + AU-12.1(iv) + AC-6(9) + CM-6(a) + MA-4(1)(a) + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.PT-1 + FAU_GEN.1.1.c + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + SRG-OS-000471-VMM-001910 + RHEL-08-030317 + SV-230433r627750_rule + Misuse of privileged functions, either intentionally or unintentionally by +authorized users, or by unauthorized external entities that have compromised system accounts, +is a serious and ongoing concern and can have significant adverse impacts on organizations. +Auditing the use of privileged functions is one way to detect such misuse and identify +the risk from insider and advanced persistent threats. + +Privileged programs are subject to escalation-of-privilege attacks, +which attempt to subvert their normal role of providing some necessary but +limited capability. As such, motivation exists to monitor these programs for +unusual activity. + CCE-80740-4 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80740-4 + - DISA-STIG-RHEL-08-030317 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(a) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-12.1(ii) + - NIST-800-53-AU-12.1(iv) + - NIST-800-53-AU-2(d) + - NIST-800-53-AU-3 + - NIST-800-53-AU-3.1 + - NIST-800-53-CM-6(a) + - NIST-800-53-MA-4(1)(a) + - audit_rules_privileged_commands_unix_chkpwd + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Perform remediation of Audit rules for /usr/sbin/unix_chkpwd + block: + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/sbin/unix_chkpwd -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules + set_fact: audit_file="/etc/audit/rules.d/privileged.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/sbin/unix_chkpwd -F perm=x + -F auid>=1000 -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/sbin/unix_chkpwd + -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/sbin/unix_chkpwd -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:( + -S |,)\w+)+)( -F path=/usr/sbin/unix_chkpwd -F perm=x -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/sbin/unix_chkpwd + -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80740-4 + - DISA-STIG-RHEL-08-030317 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(a) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-12.1(ii) + - NIST-800-53-AU-12.1(iv) + - NIST-800-53-AU-2(d) + - NIST-800-53-AU-3 + - NIST-800-53-AU-3.1 + - NIST-800-53-CM-6(a) + - NIST-800-53-MA-4(1)(a) + - audit_rules_privileged_commands_unix_chkpwd + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +ACTION_ARCH_FILTERS="-a always,exit" +OTHER_FILTERS="-F path=/usr/sbin/unix_chkpwd -F perm=x" +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="" +KEY="privileged" +SYSCALL_GROUPING="" +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - unix_update + At a minimum, the audit system should collect the execution of +privileged commands for all users and root. If the auditd daemon is +configured to use the augenrules program to read audit rules during +daemon startup (the default), add a line of the following form to a file with +suffix .rules in the directory /etc/audit/rules.d: +-a always,exit -F path=/usr/sbin/unix_update -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add a line of the following +form to /etc/audit/audit.rules: +-a always,exit -F path=/usr/sbin/unix_update -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000064-GPOS-00033 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + RHEL-08-030310 + SV-230426r627750_rule + Misuse of privileged functions, either intentionally or unintentionally by +authorized users, or by unauthorized external entities that have compromised system accounts, +is a serious and ongoing concern and can have significant adverse impacts on organizations. +Auditing the use of privileged functions is one way to detect such misuse and identify +the risk from insider and advanced persistent threats. + +Privileged programs are subject to escalation-of-privilege attacks, +which attempt to subvert their normal role of providing some necessary but +limited capability. As such, motivation exists to monitor these programs for +unusual activity. + CCE-89480-8 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-89480-8 + - DISA-STIG-RHEL-08-030310 + - audit_rules_privileged_commands_unix_update + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Perform remediation of Audit rules for /usr/sbin/unix_update + block: + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/sbin/unix_update -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules + set_fact: audit_file="/etc/audit/rules.d/privileged.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/sbin/unix_update -F perm=x + -F auid>=1000 -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/sbin/unix_update + -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/sbin/unix_update -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:( + -S |,)\w+)+)( -F path=/usr/sbin/unix_update -F perm=x -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/sbin/unix_update + -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-89480-8 + - DISA-STIG-RHEL-08-030310 + - audit_rules_privileged_commands_unix_update + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +ACTION_ARCH_FILTERS="-a always,exit" +OTHER_FILTERS="-F path=/usr/sbin/unix_update -F perm=x" +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="" +KEY="privileged" +SYSCALL_GROUPING="" +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - userhelper + At a minimum, the audit system should collect the execution of +privileged commands for all users and root. If the auditd daemon is +configured to use the augenrules program to read audit rules during +daemon startup (the default), add a line of the following form to a file with +suffix .rules in the directory /etc/audit/rules.d: +-a always,exit -F path=/usr/sbin/userhelper -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add a line of the following +form to /etc/audit/audit.rules: +-a always,exit -F path=/usr/sbin/userhelper -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + 1 + 12 + 13 + 14 + 15 + 16 + 2 + 3 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + BAI03.05 + DSS01.03 + DSS03.05 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 6.1 + SR 6.2 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.14.2.7 + A.15.2.1 + A.15.2.2 + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.PT-1 + FAU_GEN.1.1.c + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + SRG-OS-000471-VMM-001910 + RHEL-08-030315 + SV-230431r627750_rule + Misuse of privileged functions, either intentionally or unintentionally by +authorized users, or by unauthorized external entities that have compromised system accounts, +is a serious and ongoing concern and can have significant adverse impacts on organizations. +Auditing the use of privileged functions is one way to detect such misuse and identify +the risk from insider and advanced persistent threats. + +Privileged programs are subject to escalation-of-privilege attacks, +which attempt to subvert their normal role of providing some necessary but +limited capability. As such, motivation exists to monitor these programs for +unusual activity. + CCE-80741-2 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80741-2 + - DISA-STIG-RHEL-08-030315 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_privileged_commands_userhelper + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Perform remediation of Audit rules for /usr/sbin/userhelper + block: + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/sbin/userhelper -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules + set_fact: audit_file="/etc/audit/rules.d/privileged.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/sbin/userhelper -F perm=x + -F auid>=1000 -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/sbin/userhelper + -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/sbin/userhelper -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:( + -S |,)\w+)+)( -F path=/usr/sbin/userhelper -F perm=x -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/sbin/userhelper + -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80741-2 + - DISA-STIG-RHEL-08-030315 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_privileged_commands_userhelper + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +ACTION_ARCH_FILTERS="-a always,exit" +OTHER_FILTERS="-F path=/usr/sbin/userhelper -F perm=x" +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="" +KEY="privileged" +SYSCALL_GROUPING="" +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - usermod + At a minimum, the audit system should collect the execution of +privileged commands for all users and root. If the auditd daemon is +configured to use the augenrules program to read audit rules during +daemon startup (the default), add a line of the following form to a file with +suffix .rules in the directory /etc/audit/rules.d: +-a always,exit -F path=/usr/sbin/usermod -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add a line of the following +form to /etc/audit/audit.rules: +-a always,exit -F path=/usr/sbin/usermod -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + SRG-OS-000466-GPOS-00210 + RHEL-08-030560 + SV-230463r627750_rule + Misuse of privileged functions, either intentionally or unintentionally by +authorized users, or by unauthorized external entities that have compromised system accounts, +is a serious and ongoing concern and can have significant adverse impacts on organizations. +Auditing the use of privileged functions is one way to detect such misuse and identify +the risk from insider and advanced persistent threats. + +Privileged programs are subject to escalation-of-privilege attacks, +which attempt to subvert their normal role of providing some necessary but +limited capability. As such, motivation exists to monitor these programs for +unusual activity. + CCE-86027-0 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-86027-0 + - DISA-STIG-RHEL-08-030560 + - audit_rules_privileged_commands_usermod + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Perform remediation of Audit rules for /usr/sbin/usermod + block: + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/sbin/usermod -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules + set_fact: audit_file="/etc/audit/rules.d/privileged.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/sbin/usermod -F perm=x + -F auid>=1000 -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/sbin/usermod -F + perm=x -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/sbin/usermod -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:( + -S |,)\w+)+)( -F path=/usr/sbin/usermod -F perm=x -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/sbin/usermod -F + perm=x -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86027-0 + - DISA-STIG-RHEL-08-030560 + - audit_rules_privileged_commands_usermod + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +ACTION_ARCH_FILTERS="-a always,exit" +OTHER_FILTERS="-F path=/usr/sbin/usermod -F perm=x" +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="" +KEY="privileged" +SYSCALL_GROUPING="" +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - usernetctl + At a minimum, the audit system should collect the execution of +privileged commands for all users and root. If the auditd daemon is +configured to use the augenrules program to read audit rules during +daemon startup (the default), add a line of the following form to a file with +suffix .rules in the directory /etc/audit/rules.d: +-a always,exit -F path=/usr/sbin/usernetctl -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add a line of the following +form to /etc/audit/audit.rules: +-a always,exit -F path=/usr/sbin/usernetctl -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + CCI-000172 + CIP-004-6 R2.2.2 + CIP-004-6 R2.2.3 + CIP-007-3 R.1.3 + CIP-007-3 R5 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.3 + CIP-007-3 R5.2.1 + CIP-007-3 R5.2.3 + AC-2(4) + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + FAU_GEN.1.1.c + SRG-OS-000471-VMM-001910 + Misuse of privileged functions, either intentionally or unintentionally by +authorized users, or by unauthorized external entities that have compromised system accounts, +is a serious and ongoing concern and can have significant adverse impacts on organizations. +Auditing the use of privileged functions is one way to detect such misuse and identify +the risk from insider and advanced persistent threats. + +Privileged programs are subject to escalation-of-privilege attacks, +which attempt to subvert their normal role of providing some necessary but +limited capability. As such, motivation exists to monitor these programs for +unusual activity. + CCE-80990-5 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80990-5 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_privileged_commands_usernetctl + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Perform remediation of Audit rules for /usr/sbin/usernetctl + block: + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/sbin/usernetctl -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules + set_fact: audit_file="/etc/audit/rules.d/privileged.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/sbin/usernetctl -F perm=x + -F auid>=1000 -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/sbin/usernetctl + -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/sbin/usernetctl -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:( + -S |,)\w+)+)( -F path=/usr/sbin/usernetctl -F perm=x -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/sbin/usernetctl + -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80990-5 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_privileged_commands_usernetctl + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +ACTION_ARCH_FILTERS="-a always,exit" +OTHER_FILTERS="-F path=/usr/sbin/usernetctl -F perm=x" +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="" +KEY="privileged" +SYSCALL_GROUPING="" +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Records Events that Modify Date and Time Information + Arbitrary changes to the system time can be used to obfuscate +nefarious activities in log files, as well as to confuse network services that +are highly dependent upon an accurate system time. All changes to the system +time should be audited. + + Record attempts to alter time through adjtimex + If the auditd daemon is configured to use the +augenrules program to read audit rules during daemon startup (the +default), add the following line to a file with suffix .rules in the +directory /etc/audit/rules.d: +-a always,exit -F arch=b32 -S adjtimex -F key=audit_time_rules +If the system is 64 bit then also add the following line: +-a always,exit -F arch=b64 -S adjtimex -F key=audit_time_rules +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following line to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S adjtimex -F key=audit_time_rules +If the system is 64 bit then also add the following line: +-a always,exit -F arch=b64 -S adjtimex -F key=audit_time_rules +The -k option allows for the specification of a key in string form that can be +used for better reporting capability through ausearch and aureport. Multiple +system calls can be defined on the same line to save space if desired, but is +not required. See an example of multiple combined syscalls: +-a always,exit -F arch=b64 -S adjtimex,settimeofday -F key=audit_time_rules + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 5.4.1.1 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-001487 + CCI-000169 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + Req-10.4.2.b + 4.1.3.4 + Arbitrary changes to the system time can be used to obfuscate +nefarious activities in log files, as well as to confuse network services that +are highly dependent upon an accurate system time (such as sshd). All changes +to the system time should be audited. + CCE-80745-3 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80745-3 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.4.2.b + - audit_rules_time_adjtimex + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Set architecture for audit tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80745-3 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.4.2.b + - audit_rules_time_adjtimex + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Perform remediation of Audit rules for adjtimex for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - adjtimex + syscall_grouping: + - adjtimex + - settimeofday + - stime + + - name: Check existence of adjtimex in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/audit_time_rules.rules + set_fact: audit_file="/etc/audit/rules.d/audit_time_rules.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F key=audit_time_rules + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - adjtimex + syscall_grouping: + - adjtimex + - settimeofday + - stime + + - name: Check existence of adjtimex in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F key=audit_time_rules + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80745-3 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.4.2.b + - audit_rules_time_adjtimex + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Perform remediation of Audit rules for adjtimex for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - adjtimex + syscall_grouping: + - adjtimex + - settimeofday + + - name: Check existence of adjtimex in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/audit_time_rules.rules + set_fact: audit_file="/etc/audit/rules.d/audit_time_rules.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F key=audit_time_rules + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - adjtimex + syscall_grouping: + - adjtimex + - settimeofday + - stime + + - name: Check existence of adjtimex in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F key=audit_time_rules + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-80745-3 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.4.2.b + - audit_rules_time_adjtimex + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,{{ -a%20always%2Cexit%20-F%20arch%3Db64%20-S%20adjtimex%20-k%20audit_time_rules%0A-a%20always%2Cexit%20-F%20arch%3Db32%20-S%20adjtimex%20-k%20audit_time_rules%0A }} + mode: 0600 + path: /etc/audit/rules.d/75-syscall-adjtimex.rules + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +for ARCH in "${RULE_ARCHS[@]}" +do + # Create expected audit group and audit rule form for particular system call & architecture + if [ ${ARCH} = "b32" ] + then + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + # stime system call is known at 32-bit arch (see e.g "$ ausyscall i386 stime" 's output) + # so append it to the list of time group system calls to be audited + SYSCALL="adjtimex settimeofday stime" + SYSCALL_GROUPING="adjtimex settimeofday stime" + elif [ ${ARCH} = "b64" ] + then + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + # stime system call isn't known at 64-bit arch (see "$ ausyscall x86_64 stime" 's output) + # therefore don't add it to the list of time group system calls to be audited + SYSCALL="adjtimex settimeofday" + SYSCALL_GROUPING="adjtimex settimeofday" + fi + OTHER_FILTERS="" + AUID_FILTERS="" + KEY="audit_time_rules" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a + unset syscall_grouping + unset syscall_string + unset syscall + unset file_to_edit + unset rule_to_edit + unset rule_syscalls_to_edit + unset other_string + unset auid_string + unset full_rule + + # Load macro arguments into arrays + read -a syscall_a <<< $SYSCALL + read -a syscall_grouping <<< $SYSCALL_GROUPING + + # Create a list of audit *.rules files that should be inspected for presence and correctness + # of a particular audit rule. The scheme is as follows: + # + # ----------------------------------------------------------------------------------------- + # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | + # ----------------------------------------------------------------------------------------- + # auditctl | Doesn't matter | /etc/audit/audit.rules | + # ----------------------------------------------------------------------------------------- + # augenrules | Yes | /etc/audit/rules.d/*.rules | + # augenrules | No | /etc/audit/rules.d/$key.rules | + # ----------------------------------------------------------------------------------------- + # + files_to_inspect=() + + + # If audit tool is 'augenrules', then check if the audit rule is defined + # If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection + # If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection + default_file="/etc/audit/rules.d/$KEY.rules" + # As other_filters may include paths, lets use a different delimiter for it + # The "F" script expression tells sed to print the filenames where the expressions matched + readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) + # Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet + if [ ${#files_to_inspect[@]} -eq "0" ] + then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi + fi + + # After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead + skip=1 + + for audit_file in "${files_to_inspect[@]}" + do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi + done + + if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi + fi + unset syscall_a + unset syscall_grouping + unset syscall_string + unset syscall + unset file_to_edit + unset rule_to_edit + unset rule_syscalls_to_edit + unset other_string + unset auid_string + unset full_rule + + # Load macro arguments into arrays + read -a syscall_a <<< $SYSCALL + read -a syscall_grouping <<< $SYSCALL_GROUPING + + # Create a list of audit *.rules files that should be inspected for presence and correctness + # of a particular audit rule. The scheme is as follows: + # + # ----------------------------------------------------------------------------------------- + # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | + # ----------------------------------------------------------------------------------------- + # auditctl | Doesn't matter | /etc/audit/audit.rules | + # ----------------------------------------------------------------------------------------- + # augenrules | Yes | /etc/audit/rules.d/*.rules | + # augenrules | No | /etc/audit/rules.d/$key.rules | + # ----------------------------------------------------------------------------------------- + # + files_to_inspect=() + + + + # If audit tool is 'auditctl', then add '/etc/audit/audit.rules' + # file to the list of files to be inspected + default_file="/etc/audit/audit.rules" + files_to_inspect+=('/etc/audit/audit.rules' ) + + # After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead + skip=1 + + for audit_file in "${files_to_inspect[@]}" + do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi + done + + if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi + fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Attempts to Alter Time Through clock_settime + If the auditd daemon is configured to use the +augenrules program to read audit rules during daemon startup (the +default), add the following line to a file with suffix .rules in the +directory /etc/audit/rules.d: +-a always,exit -F arch=b32 -S clock_settime -F a0=0x0 -F key=time-change +If the system is 64 bit then also add the following line: +-a always,exit -F arch=b64 -S clock_settime -F a0=0x0 -F key=time-change +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following line to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S clock_settime -F a0=0x0 -F key=time-change +If the system is 64 bit then also add the following line: +-a always,exit -F arch=b64 -S clock_settime -F a0=0x0 -F key=time-change +The -k option allows for the specification of a key in string form that can +be used for better reporting capability through ausearch and aureport. +Multiple system calls can be defined on the same line to save space if +desired, but is not required. See an example of multiple combined syscalls: +-a always,exit -F arch=b64 -S adjtimex,settimeofday -F key=audit_time_rules + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 5.4.1.1 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-001487 + CCI-000169 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + Req-10.4.2.b + 4.1.3.4 + Arbitrary changes to the system time can be used to obfuscate +nefarious activities in log files, as well as to confuse network services that +are highly dependent upon an accurate system time (such as sshd). All changes +to the system time should be audited. + CCE-80746-1 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80746-1 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.4.2.b + - audit_rules_time_clock_settime + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Set architecture for audit tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80746-1 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.4.2.b + - audit_rules_time_clock_settime + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Perform remediation of Audit rules for clock_settime for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - clock_settime + syscall_grouping: [] + + - name: Check existence of clock_settime in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a0=0x0 (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/time-change.rules + set_fact: audit_file="/etc/audit/rules.d/time-change.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F a0=0x0 (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F a0=0x0 -F + key=time-change + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - clock_settime + syscall_grouping: [] + + - name: Check existence of clock_settime in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a0=0x0 (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F a0=0x0 (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F a0=0x0 -F + key=time-change + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80746-1 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.4.2.b + - audit_rules_time_clock_settime + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Perform remediation of Audit rules for clock_settime for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - clock_settime + syscall_grouping: [] + + - name: Check existence of clock_settime in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a0=0x0 (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/time-change.rules + set_fact: audit_file="/etc/audit/rules.d/time-change.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F a0=0x0 (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F a0=0x0 -F + key=time-change + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - clock_settime + syscall_grouping: [] + + - name: Check existence of clock_settime in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a0=0x0 (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F a0=0x0 (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F a0=0x0 -F + key=time-change + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-80746-1 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.4.2.b + - audit_rules_time_clock_settime + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,{{ -a%20always%2Cexit%20-F%20arch%3Db64%20-S%20clock_settime%20-F%20a0%3D0x0%20-k%20time-change%0A-a%20always%2Cexit%20-F%20arch%3Db32%20-S%20clock_settime%20-F%20a0%3D0x0%20-k%20time-change%0A }} + mode: 0600 + path: /etc/audit/rules.d/75-syscall-clock-settime.rules + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F a0=0x0" + AUID_FILTERS="" + SYSCALL="clock_settime" + KEY="time-change" + SYSCALL_GROUPING="" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record attempts to alter time through settimeofday + If the auditd daemon is configured to use the +augenrules program to read audit rules during daemon startup (the +default), add the following line to a file with suffix .rules in the +directory /etc/audit/rules.d: +-a always,exit -F arch=b32 -S settimeofday -F key=audit_time_rules +If the system is 64 bit then also add the following line: +-a always,exit -F arch=b64 -S settimeofday -F key=audit_time_rules +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following line to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S settimeofday -F key=audit_time_rules +If the system is 64 bit then also add the following line: +-a always,exit -F arch=b64 -S settimeofday -F key=audit_time_rules +The -k option allows for the specification of a key in string form that can be +used for better reporting capability through ausearch and aureport. Multiple +system calls can be defined on the same line to save space if desired, but is +not required. See an example of multiple combined syscalls: +-a always,exit -F arch=b64 -S adjtimex,settimeofday -F key=audit_time_rules + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 5.4.1.1 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-001487 + CCI-000169 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + Req-10.4.2.b + 4.1.6 + Arbitrary changes to the system time can be used to obfuscate +nefarious activities in log files, as well as to confuse network services that +are highly dependent upon an accurate system time (such as sshd). All changes +to the system time should be audited. + CCE-80747-9 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80747-9 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.4.2.b + - audit_rules_time_settimeofday + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Set architecture for audit tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80747-9 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.4.2.b + - audit_rules_time_settimeofday + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Perform remediation of Audit rules for settimeofday for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - settimeofday + syscall_grouping: + - adjtimex + - settimeofday + - stime + + - name: Check existence of settimeofday in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/audit_time_rules.rules + set_fact: audit_file="/etc/audit/rules.d/audit_time_rules.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F key=audit_time_rules + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - settimeofday + syscall_grouping: + - adjtimex + - settimeofday + - stime + + - name: Check existence of settimeofday in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F key=audit_time_rules + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80747-9 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.4.2.b + - audit_rules_time_settimeofday + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Perform remediation of Audit rules for settimeofday for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - settimeofday + syscall_grouping: + - adjtimex + - settimeofday + - stime + + - name: Check existence of settimeofday in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/audit_time_rules.rules + set_fact: audit_file="/etc/audit/rules.d/audit_time_rules.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F key=audit_time_rules + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - settimeofday + syscall_grouping: + - adjtimex + - settimeofday + - stime + + - name: Check existence of settimeofday in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F key=audit_time_rules + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-80747-9 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.4.2.b + - audit_rules_time_settimeofday + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,{{ -a%20always%2Cexit%20-F%20arch%3Db64%20-S%20settimeofday%20-k%20audit_time_rules%0A-a%20always%2Cexit%20-F%20arch%3Db32%20-S%20settimeofday%20-k%20audit_time_rules%0A }} + mode: 0600 + path: /etc/audit/rules.d/75-syscall-settimeofday.rules + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +for ARCH in "${RULE_ARCHS[@]}" +do + # Create expected audit group and audit rule form for particular system call & architecture + if [ ${ARCH} = "b32" ] + then + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + # stime system call is known at 32-bit arch (see e.g "$ ausyscall i386 stime" 's output) + # so append it to the list of time group system calls to be audited + SYSCALL="adjtimex settimeofday stime" + SYSCALL_GROUPING="adjtimex settimeofday stime" + elif [ ${ARCH} = "b64" ] + then + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + # stime system call isn't known at 64-bit arch (see "$ ausyscall x86_64 stime" 's output) + # therefore don't add it to the list of time group system calls to be audited + SYSCALL="adjtimex settimeofday" + SYSCALL_GROUPING="adjtimex settimeofday" + fi + OTHER_FILTERS="" + AUID_FILTERS="" + KEY="audit_time_rules" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a + unset syscall_grouping + unset syscall_string + unset syscall + unset file_to_edit + unset rule_to_edit + unset rule_syscalls_to_edit + unset other_string + unset auid_string + unset full_rule + + # Load macro arguments into arrays + read -a syscall_a <<< $SYSCALL + read -a syscall_grouping <<< $SYSCALL_GROUPING + + # Create a list of audit *.rules files that should be inspected for presence and correctness + # of a particular audit rule. The scheme is as follows: + # + # ----------------------------------------------------------------------------------------- + # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | + # ----------------------------------------------------------------------------------------- + # auditctl | Doesn't matter | /etc/audit/audit.rules | + # ----------------------------------------------------------------------------------------- + # augenrules | Yes | /etc/audit/rules.d/*.rules | + # augenrules | No | /etc/audit/rules.d/$key.rules | + # ----------------------------------------------------------------------------------------- + # + files_to_inspect=() + + + # If audit tool is 'augenrules', then check if the audit rule is defined + # If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection + # If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection + default_file="/etc/audit/rules.d/$KEY.rules" + # As other_filters may include paths, lets use a different delimiter for it + # The "F" script expression tells sed to print the filenames where the expressions matched + readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) + # Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet + if [ ${#files_to_inspect[@]} -eq "0" ] + then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi + fi + + # After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead + skip=1 + + for audit_file in "${files_to_inspect[@]}" + do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi + done + + if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi + fi + unset syscall_a + unset syscall_grouping + unset syscall_string + unset syscall + unset file_to_edit + unset rule_to_edit + unset rule_syscalls_to_edit + unset other_string + unset auid_string + unset full_rule + + # Load macro arguments into arrays + read -a syscall_a <<< $SYSCALL + read -a syscall_grouping <<< $SYSCALL_GROUPING + + # Create a list of audit *.rules files that should be inspected for presence and correctness + # of a particular audit rule. The scheme is as follows: + # + # ----------------------------------------------------------------------------------------- + # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | + # ----------------------------------------------------------------------------------------- + # auditctl | Doesn't matter | /etc/audit/audit.rules | + # ----------------------------------------------------------------------------------------- + # augenrules | Yes | /etc/audit/rules.d/*.rules | + # augenrules | No | /etc/audit/rules.d/$key.rules | + # ----------------------------------------------------------------------------------------- + # + files_to_inspect=() + + + + # If audit tool is 'auditctl', then add '/etc/audit/audit.rules' + # file to the list of files to be inspected + default_file="/etc/audit/audit.rules" + files_to_inspect+=('/etc/audit/audit.rules' ) + + # After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead + skip=1 + + for audit_file in "${files_to_inspect[@]}" + do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi + done + + if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi + fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Attempts to Alter Time Through stime + If the auditd daemon is configured to use the +augenrules program to read audit rules during daemon startup (the +default), add the following line to a file with suffix .rules in the +directory /etc/audit/rules.d for both 32 bit and 64 bit systems: +-a always,exit -F arch=b32 -S stime -F key=audit_time_rules +Since the 64 bit version of the "stime" system call is not defined in the audit +lookup table, the corresponding "-F arch=b64" form of this rule is not expected +to be defined on 64 bit systems (the aforementioned "-F arch=b32" stime rule +form itself is sufficient for both 32 bit and 64 bit systems). If the +auditd daemon is configured to use the auditctl utility to +read audit rules during daemon startup, add the following line to +/etc/audit/audit.rules file for both 32 bit and 64 bit systems: +-a always,exit -F arch=b32 -S stime -F key=audit_time_rules +Since the 64 bit version of the "stime" system call is not defined in the audit +lookup table, the corresponding "-F arch=b64" form of this rule is not expected +to be defined on 64 bit systems (the aforementioned "-F arch=b32" stime rule +form itself is sufficient for both 32 bit and 64 bit systems). The -k option +allows for the specification of a key in string form that can be used for +better reporting capability through ausearch and aureport. Multiple system +calls can be defined on the same line to save space if desired, but is not +required. See an example of multiple combined system calls: +-a always,exit -F arch=b64 -S adjtimex,settimeofday -F key=audit_time_rules + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 5.4.1.1 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-001487 + CCI-000169 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + Req-10.4.2.b + 4.1.3.4 + Arbitrary changes to the system time can be used to obfuscate +nefarious activities in log files, as well as to confuse network services that +are highly dependent upon an accurate system time (such as sshd). All changes +to the system time should be audited. + CCE-80748-7 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80748-7 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.4.2.b + - audit_rules_time_stime + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Perform remediation of Audit rules for stime syscall for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - stime + syscall_grouping: + - adjtimex + - settimeofday + - stime + + - name: Check existence of stime in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/audit_time_rules.rules + set_fact: audit_file="/etc/audit/rules.d/audit_time_rules.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F key=audit_time_rules + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - stime + syscall_grouping: + - adjtimex + - settimeofday + - stime + + - name: Check existence of stime in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F key=audit_time_rules + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80748-7 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.4.2.b + - audit_rules_time_stime + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,{{ -a%20always%2Cexit%20-F%20arch%3Db64%20-S%20stime%20-k%20audit_time_rules%0A-a%20always%2Cexit%20-F%20arch%3Db32%20-S%20stime%20-k%20audit_time_rules%0A }} + mode: 0600 + path: /etc/audit/rules.d/75-syscall-stime.rules + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +for ARCH in "${RULE_ARCHS[@]}" +do + # Create expected audit group and audit rule form for particular system call & architecture + if [ ${ARCH} = "b32" ] + then + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + # stime system call is known at 32-bit arch (see e.g "$ ausyscall i386 stime" 's output) + # so append it to the list of time group system calls to be audited + SYSCALL="adjtimex settimeofday stime" + SYSCALL_GROUPING="adjtimex settimeofday stime" + elif [ ${ARCH} = "b64" ] + then + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + # stime system call isn't known at 64-bit arch (see "$ ausyscall x86_64 stime" 's output) + # therefore don't add it to the list of time group system calls to be audited + SYSCALL="adjtimex settimeofday" + SYSCALL_GROUPING="adjtimex settimeofday" + fi + OTHER_FILTERS="" + AUID_FILTERS="" + KEY="audit_time_rules" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a + unset syscall_grouping + unset syscall_string + unset syscall + unset file_to_edit + unset rule_to_edit + unset rule_syscalls_to_edit + unset other_string + unset auid_string + unset full_rule + + # Load macro arguments into arrays + read -a syscall_a <<< $SYSCALL + read -a syscall_grouping <<< $SYSCALL_GROUPING + + # Create a list of audit *.rules files that should be inspected for presence and correctness + # of a particular audit rule. The scheme is as follows: + # + # ----------------------------------------------------------------------------------------- + # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | + # ----------------------------------------------------------------------------------------- + # auditctl | Doesn't matter | /etc/audit/audit.rules | + # ----------------------------------------------------------------------------------------- + # augenrules | Yes | /etc/audit/rules.d/*.rules | + # augenrules | No | /etc/audit/rules.d/$key.rules | + # ----------------------------------------------------------------------------------------- + # + files_to_inspect=() + + + # If audit tool is 'augenrules', then check if the audit rule is defined + # If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection + # If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection + default_file="/etc/audit/rules.d/$KEY.rules" + # As other_filters may include paths, lets use a different delimiter for it + # The "F" script expression tells sed to print the filenames where the expressions matched + readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) + # Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet + if [ ${#files_to_inspect[@]} -eq "0" ] + then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi + fi + + # After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead + skip=1 + + for audit_file in "${files_to_inspect[@]}" + do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi + done + + if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi + fi + unset syscall_a + unset syscall_grouping + unset syscall_string + unset syscall + unset file_to_edit + unset rule_to_edit + unset rule_syscalls_to_edit + unset other_string + unset auid_string + unset full_rule + + # Load macro arguments into arrays + read -a syscall_a <<< $SYSCALL + read -a syscall_grouping <<< $SYSCALL_GROUPING + + # Create a list of audit *.rules files that should be inspected for presence and correctness + # of a particular audit rule. The scheme is as follows: + # + # ----------------------------------------------------------------------------------------- + # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | + # ----------------------------------------------------------------------------------------- + # auditctl | Doesn't matter | /etc/audit/audit.rules | + # ----------------------------------------------------------------------------------------- + # augenrules | Yes | /etc/audit/rules.d/*.rules | + # augenrules | No | /etc/audit/rules.d/$key.rules | + # ----------------------------------------------------------------------------------------- + # + files_to_inspect=() + + + + # If audit tool is 'auditctl', then add '/etc/audit/audit.rules' + # file to the list of files to be inspected + default_file="/etc/audit/audit.rules" + files_to_inspect+=('/etc/audit/audit.rules' ) + + # After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead + skip=1 + + for audit_file in "${files_to_inspect[@]}" + do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi + done + + if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi + fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Attempts to Alter the localtime File + If the auditd daemon is configured to use the +augenrules program to read audit rules during daemon startup (the default), +add the following line to a file with suffix .rules in the directory +/etc/audit/rules.d: +-w /etc/localtime -p wa -k audit_time_rules +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following line to +/etc/audit/audit.rules file: +-w /etc/localtime -p wa -k audit_time_rules +The -k option allows for the specification of a key in string form that can +be used for better reporting capability through ausearch and aureport and +should always be used. + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 5.4.1.1 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-001487 + CCI-000169 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + Req-10.4.2.b + 4.1.3.4 + Arbitrary changes to the system time can be used to obfuscate +nefarious activities in log files, as well as to confuse network services that +are highly dependent upon an accurate system time (such as sshd). All changes +to the system time should be audited. + CCE-80749-5 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80749-5 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.4.2.b + - audit_rules_time_watch_localtime + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Check if watch rule for /etc/localtime already exists in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: ^\s*-w\s+/etc/localtime\s+-p\s+wa(\s|$)+ + patterns: '*.rules' + register: find_existing_watch_rules_d + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80749-5 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.4.2.b + - audit_rules_time_watch_localtime + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Search /etc/audit/rules.d for other rules with specified key audit_time_rules + find: + paths: /etc/audit/rules.d + contains: ^.*(?:-F key=|-k\s+)audit_time_rules$ + patterns: '*.rules' + register: find_watch_key + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched + == 0 + tags: + - CCE-80749-5 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.4.2.b + - audit_rules_time_watch_localtime + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Use /etc/audit/rules.d/audit_time_rules.rules as the recipient for the rule + set_fact: + all_files: + - /etc/audit/rules.d/audit_time_rules.rules + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_watch_key.matched is defined and find_watch_key.matched == 0 and find_existing_watch_rules_d.matched + is defined and find_existing_watch_rules_d.matched == 0 + tags: + - CCE-80749-5 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.4.2.b + - audit_rules_time_watch_localtime + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Use matched file as the recipient for the rule + set_fact: + all_files: + - '{{ find_watch_key.files | map(attribute=''path'') | list | first }}' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_watch_key.matched is defined and find_watch_key.matched > 0 and find_existing_watch_rules_d.matched + is defined and find_existing_watch_rules_d.matched == 0 + tags: + - CCE-80749-5 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.4.2.b + - audit_rules_time_watch_localtime + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Add watch rule for /etc/localtime in /etc/audit/rules.d/ + lineinfile: + path: '{{ all_files[0] }}' + line: -w /etc/localtime -p wa -k audit_time_rules + create: true + mode: '0640' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched + == 0 + tags: + - CCE-80749-5 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.4.2.b + - audit_rules_time_watch_localtime + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Check if watch rule for /etc/localtime already exists in /etc/audit/audit.rules + find: + paths: /etc/audit/ + contains: ^\s*-w\s+/etc/localtime\s+-p\s+wa(\s|$)+ + patterns: audit.rules + register: find_existing_watch_audit_rules + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80749-5 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.4.2.b + - audit_rules_time_watch_localtime + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Add watch rule for /etc/localtime in /etc/audit/audit.rules + lineinfile: + line: -w /etc/localtime -p wa -k audit_time_rules + state: present + dest: /etc/audit/audit.rules + create: true + mode: '0640' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_audit_rules.matched is defined and find_existing_watch_audit_rules.matched + == 0 + tags: + - CCE-80749-5 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.4.2.b + - audit_rules_time_watch_localtime + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,{{ -w%20/etc/localtime%20-p%20wa%20-k%20audit_time_rules%0A }} + mode: 0600 + path: /etc/audit/rules.d/75-etclocaltime-wa-audit_time_rules.rules + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + + +# If the audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# into the list of files to be inspected +files_to_inspect+=('/etc/audit/audit.rules') + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/etc/localtime" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/localtime $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/etc/localtime$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /etc/localtime -p wa -k audit_time_rules" >> "$audit_rules_file" + fi +done +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + +# If the audit is 'augenrules', then check if rule is already defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection. +# If rule isn't defined, add '/etc/audit/rules.d/audit_time_rules.rules' to list of files for inspection. +readarray -t matches < <(grep -HP "[\s]*-w[\s]+/etc/localtime" /etc/audit/rules.d/*.rules) + +# For each of the matched entries +for match in "${matches[@]}" +do + # Extract filepath from the match + rulesd_audit_file=$(echo $match | cut -f1 -d ':') + # Append that path into list of files for inspection + files_to_inspect+=("$rulesd_audit_file") +done +# Case when particular audit rule isn't defined yet +if [ "${#files_to_inspect[@]}" -eq "0" ] +then + # Append '/etc/audit/rules.d/audit_time_rules.rules' into list of files for inspection + key_rule_file="/etc/audit/rules.d/audit_time_rules.rules" + # If the audit_time_rules.rules file doesn't exist yet, create it with correct permissions + if [ ! -e "$key_rule_file" ] + then + touch "$key_rule_file" + chmod 0640 "$key_rule_file" + fi + files_to_inspect+=("$key_rule_file") +fi + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/etc/localtime" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/localtime $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/etc/localtime$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /etc/localtime -p wa -k audit_time_rules" >> "$audit_rules_file" + fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + + Configure auditd Data Retention + The audit system writes data to /var/log/audit/audit.log. By default, +auditd rotates 5 logs by size (6MB), retaining a maximum of 30MB of +data in total, and refuses to write entries when the disk is too +full. This minimizes the risk of audit data filling its partition +and impacting other services. This also minimizes the risk of the audit +daemon temporarily disabling the system if it cannot write audit log (which +it can be configured to do). + +For a busy +system or a system which is thoroughly auditing system activity, the default settings +for data retention may be + insufficient. The log file size needed will depend heavily on what types +of events are being audited. First configure auditing to log all the events of +interest. Then monitor the log size manually for awhile to determine what file +size will allow you to keep the required data for the correct time period. + +Using a dedicated partition for /var/log/audit prevents the +auditd logs from disrupting system functionality if they fill, and, +more importantly, prevents other activity in /var from filling the +partition and stopping the audit trail. (The audit logs are size-limited and +therefore unlikely to grow without bound unless configured to do so.) Some +machines may have requirements that no actions occur which cannot be audited. +If this is the case, then auditd can be configured to halt the machine +if it runs out of space. Note: Since older logs are rotated, +configuring auditd this way does not prevent older logs from being +rotated away before they can be viewed. + +If your system is configured to halt when logging cannot be performed, make +sure this can never happen under normal circumstances! Ensure that +/var/log/audit is on its own partition, and that this partition is +larger than the maximum amount of data auditd will retain +normally. + + + Action for audispd to take when disk is full + The setting for disk_full_action in /etc/audisp/audisp-remote.conf + single + exec + halt + single + suspend + syslog + warn_once + stop + + + Action for audispd to take when network fails + The setting for network_failure_action in /etc/audisp/audisp-remote.conf + single + exec + halt + single + suspend + syslog + warn_once + stop + ignore + + + Remote server for audispd to send audit records + +The setting for remote_server in /etc/audit/audisp-remote.conf + logcollector + + + Account for auditd to send email when actions occurs + The setting for action_mail_acct in /etc/audit/auditd.conf + admin + root + root + + + Action for auditd to take when disk space is low + The setting for admin_space_left_action in /etc/audit/auditd.conf + single + email + exec + halt + single + suspend + syslog + rotate + ignore + + + Action for auditd to take when disk errors + 'The setting for disk_error_action in /etc/audit/auditd.conf, if multiple +values are allowed write them separated by pipes as in "syslog|single|halt", +for remediations the first value will be taken' + single + exec + halt + single + suspend + syslog + ignore + syslog|single|halt + syslog|single|halt + + + Action for auditd to take when disk is full + 'The setting for disk_full_action in /etc/audit/auditd.conf, if multiple +values are allowed write them separated by pipes as in "syslog|single|halt", +for remediations the first value will be taken' + single + exec + halt + single + suspend + syslog + ignore + rotate + syslog|single|halt + syslog|single|halt + + + Auditd priority for flushing data to disk + The setting for flush in /etc/audit/auditd.conf + data + data + incremental + incremental_async + none + sync + + + Number of Record to Retain Before Flushing to Disk + The setting for freq in /etc/audit/auditd.conf + 50 + 100 + 50 + + + Maximum audit log file size for auditd + The setting for max_log_file in /etc/audit/auditd.conf + 1 + 10 + 20 + 5 + 6 + 6 + + + Action for auditd to take when log files reach their maximum size + The setting for max_log_file_action in /etc/audit/auditd.conf. The following options are available: +ignore - audit daemon does nothing. +syslog - audit daemon will issue a warning to syslog. +suspend - audit daemon will stop writing records to the disk. +rotate - audit daemon will rotate logs in the same convention used by logrotate. +keep_logs - similar to rotate but prevents audit logs to be overwritten. May trigger space_left_action if volume is full. + rotate + keep_logs + rotate + suspend + syslog + ignore + + + Number of log files for auditd to retain + The setting for num_logs in /etc/audit/auditd.conf + 0 + 1 + 2 + 3 + 4 + 5 + 10 + 20 + 50 + 100 + 5 + + + Size remaining in disk space before prompting space_left_action + The setting for space_left (MB) in /etc/audit/auditd.conf + 1000 + 100 + 250 + 500 + 750 + 100 + + + Action for auditd to take when disk space just starts to run low + The setting for space_left_action in /etc/audit/auditd.conf + email + email + exec + halt + single + suspend + syslog + rotate + ignore + + + The percentage remaining in disk space before prompting space_left_action + The setting for space_left as a percentage in /etc/audit/auditd.conf + 25 + 50 + 75 + 25 + + + Configure audispd Plugin To Send Logs To Remote Server + Configure the audispd plugin to off-load audit records onto a different +system or media from the system being audited. + +Set the remote_server option in /etc/audit/audisp-remote.conf +with an IP address or hostname of the system that the audispd plugin should +send audit records to. For example +remote_server = + CCI-001851 + FAU_GEN.1.1.c + SRG-OS-000342-GPOS-00133 + SRG-OS-000479-GPOS-00224 + SRG-OS-000051-VMM-000230 + SRG-OS-000058-VMM-000270 + SRG-OS-000059-VMM-000280 + SRG-OS-000479-VMM-001990 + SRG-OS-000479-VMM-001990 + Information stored in one location is vulnerable to accidental or incidental +deletion or alteration.Off-loading is a common process in information systems +with limited audit storage capacity. + CCE-80925-1 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80925-1 + - auditd_audispd_configure_remote_server + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed +- name: XCCDF Value var_audispd_remote_server # promote to variable + set_fact: + var_audispd_remote_server: !!str + tags: + - always + +- name: Make sure that a remote server is configured for Audispd + lineinfile: + path: /etc/audit/audisp-remote.conf + line: remote_server = {{ var_audispd_remote_server }} + regexp: ^\s*remote_server\s*=.*$ + create: true + state: present + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80925-1 + - auditd_audispd_configure_remote_server + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +var_audispd_remote_server='' + + +AUDITCONFIG=/etc/audit/audisp-remote.conf + + + +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "$AUDITCONFIG"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^remote_server") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$var_audispd_remote_server" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^remote_server\\>" "$AUDITCONFIG"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^remote_server\\>.*/$escaped_formatted_output/gi" "$AUDITCONFIG" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-80925-1" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "$AUDITCONFIG" >> "$AUDITCONFIG" + printf '%s\n' "$formatted_output" >> "$AUDITCONFIG" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Configure a Sufficiently Large Partition for Audit Logs + The Red Hat Enterprise Linux 8 operating system must allocate audit record storage +capacity to store at least one weeks worth of audit records when audit +records are not immediately sent to a central audit record storage +facility. + +The partition size needed to capture a week's worth of audit records is +based on the activity level of the system and the total storage capacity +available. In normal circumstances, 10.0 GB of storage space for audit +records will be sufficient. + +Determine which partition the audit records are being written to with the +following command: + +$ sudo grep log_file /etc/audit/auditd.conf +log_file = /var/log/audit/audit.log + +Check the size of the partition that audit records are written to with the +following command: + +$ sudo df -h /var/log/audit/ +/dev/sda2 24G 10.4G 13.6G 43% /var/log/audit + CCI-001849 + SRG-OS-000341-GPOS-00132 + SRG-OS-000342-GPOS-00133 + RHEL-08-030660 + SV-230476r809313_rule + Information stored in one location is vulnerable to accidental or incidental +deletion or alteration. Off-loading is a common process in information +systems with limited audit storage capacity. + + CCE-84005-8 + + + + + + Configure audispd's Plugin disk_full_action When Disk Is Full + Configure the action the operating system takes if the disk the audit records +are written to becomes full. Edit the file /etc/audit/audisp-remote.conf. +Add or modify the following line, substituting ACTION appropriately: +disk_full_action = ACTION +Set this value to single to cause the system to switch to single user +mode for corrective action. Acceptable values also include syslog and +halt. For certain systems, the need for availability +outweighs the need to log all actions, and a different setting should be +determined. + CCI-001851 + AU-5(b) + AU-5(2) + AU-5(1) + AU-5(4) + CM-6(a) + SRG-OS-000342-GPOS-00133 + SRG-OS-000479-GPOS-00224 + Taking appropriate action in case of a filled audit storage volume will +minimize the possibility of losing audit records. + - name: Gather the package facts + package_facts: + manager: auto + tags: + - NIST-800-53-AU-5(1) + - NIST-800-53-AU-5(2) + - NIST-800-53-AU-5(4) + - NIST-800-53-AU-5(b) + - NIST-800-53-CM-6(a) + - auditd_audispd_disk_full_action + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed +- name: XCCDF Value var_audispd_disk_full_action # promote to variable + set_fact: + var_audispd_disk_full_action: !!str + tags: + - always + +- name: Make sure that disk full action is configured for Audispd + lineinfile: + path: /etc/audit/audisp-remote.conf + line: disk_full_action = {{ var_audispd_disk_full_action }} + regexp: ^\s*disk_full_action\s*=.*$ + create: true + state: present + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AU-5(1) + - NIST-800-53-AU-5(2) + - NIST-800-53-AU-5(4) + - NIST-800-53-AU-5(b) + - NIST-800-53-CM-6(a) + - auditd_audispd_disk_full_action + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +var_audispd_disk_full_action='' + + +AUDITCONFIG=/etc/audit/audisp-remote.conf + +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "$AUDITCONFIG"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^disk_full_action") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$var_audispd_disk_full_action" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^disk_full_action\\>" "$AUDITCONFIG"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^disk_full_action\\>.*/$escaped_formatted_output/gi" "$AUDITCONFIG" +else + # \n is precaution for case where file ends without trailing newline + + printf '%s\n' "$formatted_output" >> "$AUDITCONFIG" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Encrypt Audit Records Sent With audispd Plugin + Configure the operating system to encrypt the transfer of off-loaded audit +records onto a different system or media from the system being audited. + +Set the transport option in /etc/audit/audisp-remote.conf +to KRB5. + CCI-001851 + AU-9(3) + CM-6(a) + FAU_GEN.1.1.c + SRG-OS-000342-GPOS-00133 + SRG-OS-000479-GPOS-00224 + Information stored in one location is vulnerable to accidental or incidental deletion +or alteration. Off-loading is a common process in information systems with limited +audit storage capacity. + CCE-80926-9 + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +AUDISP_REMOTE_CONFIG="/etc/audit/audisp-remote.conf" + +option="^transport" +value="KRB5" + + +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "$AUDISP_REMOTE_CONFIG"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "$option") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$value" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "$option\\>" "$AUDISP_REMOTE_CONFIG"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/$option\\>.*/$escaped_formatted_output/gi" "$AUDISP_REMOTE_CONFIG" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-80926-9" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "$AUDISP_REMOTE_CONFIG" >> "$AUDISP_REMOTE_CONFIG" + printf '%s\n' "$formatted_output" >> "$AUDISP_REMOTE_CONFIG" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure audispd's Plugin network_failure_action On Network Failure + Configure the action the operating system takes if there is an error sending +audit records to a remote system. Edit the file /etc/audit/audisp-remote.conf. +Add or modify the following line, substituting ACTION appropriately: +network_failure_action = ACTION +Set this value to single to cause the system to switch to single user +mode for corrective action. Acceptable values also include syslog and +halt. For certain systems, the need for availability +outweighs the need to log all actions, and a different setting should be +determined. +This profile configures the action to be . + CCI-001851 + AU-5(b) + AU-5(2) + AU-5(1) + AU-5(4) + CM-6(a) + SRG-OS-000342-GPOS-00133 + SRG-OS-000479-GPOS-00224 + Taking appropriate action when there is an error sending audit records to a +remote system will minimize the possibility of losing audit records. + - name: Gather the package facts + package_facts: + manager: auto + tags: + - NIST-800-53-AU-5(1) + - NIST-800-53-AU-5(2) + - NIST-800-53-AU-5(4) + - NIST-800-53-AU-5(b) + - NIST-800-53-CM-6(a) + - auditd_audispd_network_failure_action + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed +- name: XCCDF Value var_audispd_network_failure_action # promote to variable + set_fact: + var_audispd_network_failure_action: !!str + tags: + - always + +- name: Make sure that network failure action is configured for Audispd + lineinfile: + path: /etc/audit/audisp-remote.conf + line: network_failure_action = {{ var_audispd_network_failure_action }} + regexp: ^\s*network_failure_action\s*=.*$ + create: true + state: present + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AU-5(1) + - NIST-800-53-AU-5(2) + - NIST-800-53-AU-5(4) + - NIST-800-53-AU-5(b) + - NIST-800-53-CM-6(a) + - auditd_audispd_network_failure_action + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +var_audispd_network_failure_action='' + + +AUDITCONFIG=/etc/audit/audisp-remote.conf + +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "$AUDITCONFIG"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^network_failure_action") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$var_audispd_network_failure_action" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^network_failure_action\\>" "$AUDITCONFIG"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^network_failure_action\\>.*/$escaped_formatted_output/gi" "$AUDITCONFIG" +else + # \n is precaution for case where file ends without trailing newline + + printf '%s\n' "$formatted_output" >> "$AUDITCONFIG" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Configure auditd to use audispd's syslog plugin + To configure the auditd service to use the +syslog plug-in of the audispd audit event multiplexor, set +the active line in /etc/audit/plugins.d/syslog.conf to yes. +Restart the auditd service: +$ sudo service auditd restart + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 3 + 4 + 5 + 6 + 7 + 8 + 5.4.1.1 + APO11.04 + APO12.06 + BAI03.05 + BAI08.02 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS05.04 + DSS05.07 + MEA02.01 + 3.3.1 + CCI-000136 + 164.308(a)(1)(ii)(D) + 164.308(a)(5)(ii)(B) + 164.308(a)(5)(ii)(C) + 164.308(a)(6)(ii) + 164.308(a)(8) + 164.310(d)(2)(iii) + 164.312(b) + 164.314(a)(2)(i)(C) + 164.314(a)(2)(iii) + 4.2.3.10 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 6.1 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.16.1.4 + A.16.1.5 + A.16.1.7 + AU-4(1) + CM-6(a) + DE.AE-3 + DE.AE-5 + PR.PT-1 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.5.3 + SRG-OS-000479-GPOS-00224 + SRG-OS-000342-GPOS-00133 + SRG-OS-000051-VMM-000230 + SRG-OS-000058-VMM-000270 + SRG-OS-000059-VMM-000280 + SRG-OS-000479-VMM-001990 + SRG-OS-000479-VMM-001990 + The auditd service does not include the ability to send audit +records to a centralized server for management directly. It does, however, +include a plug-in for audit event multiplexor (audispd) to pass audit records +to the local syslog server. + CCE-80677-8 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80677-8 + - CJIS-5.4.1.1 + - NIST-800-171-3.3.1 + - NIST-800-53-AU-4(1) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.3 + - auditd_audispd_syslog_plugin_activated + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: enable syslog plugin + lineinfile: + dest: /etc/audit/plugins.d/syslog.conf + regexp: ^active + line: active = yes + create: true + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80677-8 + - CJIS-5.4.1.1 + - NIST-800-171-3.3.1 + - NIST-800-53-AU-4(1) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.3 + - auditd_audispd_syslog_plugin_activated + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +var_syslog_active="yes" + +AUDISP_SYSLOGCONFIG=/etc/audit/plugins.d/syslog.conf + +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "$AUDISP_SYSLOGCONFIG"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^active") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$var_syslog_active" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^active\\>" "$AUDISP_SYSLOGCONFIG"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^active\\>.*/$escaped_formatted_output/gi" "$AUDISP_SYSLOGCONFIG" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-80677-8" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "$AUDISP_SYSLOGCONFIG" >> "$AUDISP_SYSLOGCONFIG" + printf '%s\n' "$formatted_output" >> "$AUDISP_SYSLOGCONFIG" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure auditd Disk Error Action on Disk Error + The auditd service can be configured to take an action +when there is a disk error. +Edit the file /etc/audit/auditd.conf. Add or modify the following line, +substituting ACTION appropriately: +disk_error_action = ACTION +Set this value to single to cause the system to switch to single-user +mode for corrective action. Acceptable values also include syslog, +exec, single, and halt. For certain systems, the need for availability +outweighs the need to log all actions, and a different setting should be +determined. Details regarding all possible values for ACTION are described in the +auditd.conf man page. + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI04.04 + BAI08.02 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS05.04 + DSS05.07 + MEA02.01 + CCI-000140 + 4.2.3.10 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 6.1 + SR 7.1 + SR 7.2 + A.12.1.3 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.17.2.1 + AU-5(b) + AU-5(2) + AU-5(1) + AU-5(4) + CM-6(a) + DE.AE-3 + DE.AE-5 + PR.DS-4 + PR.PT-1 + RS.AN-1 + RS.AN-4 + SRG-OS-000047-GPOS-00023 + RHEL-08-030040 + SV-230390r627750_rule + Taking appropriate action in case of disk errors will minimize the possibility of +losing audit records. + CCE-84046-2 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-84046-2 + - DISA-STIG-RHEL-08-030040 + - NIST-800-53-AU-5(1) + - NIST-800-53-AU-5(2) + - NIST-800-53-AU-5(4) + - NIST-800-53-AU-5(b) + - NIST-800-53-CM-6(a) + - auditd_data_disk_error_action + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy +- name: XCCDF Value var_auditd_disk_error_action # promote to variable + set_fact: + var_auditd_disk_error_action: !!str + tags: + - always + +- name: Configure auditd Disk Error Action on Disk Error + lineinfile: + dest: /etc/audit/auditd.conf + line: disk_error_action = {{ var_auditd_disk_error_action.split('|')[0] }} + regexp: ^\s*disk_error_action\s*=\s*.*$ + state: present + create: true + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84046-2 + - DISA-STIG-RHEL-08-030040 + - NIST-800-53-AU-5(1) + - NIST-800-53-AU-5(2) + - NIST-800-53-AU-5(4) + - NIST-800-53-AU-5(b) + - NIST-800-53-CM-6(a) + - auditd_data_disk_error_action + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,{{ %23%0A%23%20This%20file%20controls%20the%20configuration%20of%20the%20audit%20daemon%0A%23%0A%0Alocal_events%20%3D%20yes%0Awrite_logs%20%3D%20yes%0Alog_file%20%3D%20/var/log/audit/audit.log%0Alog_group%20%3D%20root%0Alog_format%20%3D%20ENRICHED%0Aflush%20%3D%20%7B%7B.var_auditd_flush%7D%7D%0Afreq%20%3D%2050%0Amax_log_file%20%3D%20%7B%7B.var_auditd_max_log_file%7D%7D%0Anum_logs%20%3D%20%7B%7B.var_auditd_num_logs%7D%7D%0Apriority_boost%20%3D%204%0Aname_format%20%3D%20hostname%0A%23%23name%20%3D%20mydomain%0Amax_log_file_action%20%3D%20%7B%7B.var_auditd_max_log_file_action%7D%7D%0Aspace_left%20%3D%20%7B%7B.var_auditd_space_left%7D%7D%0Aspace_left_action%20%3D%20%7B%7B.var_auditd_space_left_action%7D%7D%0Averify_email%20%3D%20yes%0Aaction_mail_acct%20%3D%20%7B%7B.var_auditd_action_mail_acct%7D%7D%0Aadmin_space_left%20%3D%2050%0Aadmin_space_left_action%20%3D%20syslog%0Adisk_full_action%20%3D%20%7B%7B.var_auditd_disk_full_action%7D%7D%0Adisk_error_action%20%3D%20%7B%7B.var_auditd_disk_error_action%7D%7D%0Ause_libwrap%20%3D%20yes%0A%23%23tcp_listen_port%20%3D%2060%0Atcp_listen_queue%20%3D%205%0Atcp_max_per_addr%20%3D%201%0A%23%23tcp_client_ports%20%3D%201024-65535%0Atcp_client_max_idle%20%3D%200%0Atransport%20%3D%20TCP%0Akrb5_principal%20%3D%20auditd%0A%23%23krb5_key_file%20%3D%20/etc/audit/audit.key%0Adistribute_network%20%3D%20no%0Aq_depth%20%3D%20400%0Aoverflow_action%20%3D%20syslog%0Amax_restarts%20%3D%2010%0Aplugin_dir%20%3D%20/etc/audit/plugins.d }} + mode: 0640 + path: /etc/audit/auditd.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +var_auditd_disk_error_action='' + + +# +# If disk_error_action present in /etc/audit/auditd.conf, change value +# to var_auditd_disk_error_action, else +# add "disk_error_action = $var_auditd_disk_error_action" to /etc/audit/auditd.conf +# +var_auditd_disk_error_action="$(echo $var_auditd_disk_error_action | cut -d \| -f 1)" + +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/audit/auditd.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^disk_error_action") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$var_auditd_disk_error_action" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^disk_error_action\\>" "/etc/audit/auditd.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^disk_error_action\\>.*/$escaped_formatted_output/gi" "/etc/audit/auditd.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-84046-2" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/audit/auditd.conf" >> "/etc/audit/auditd.conf" + printf '%s\n' "$formatted_output" >> "/etc/audit/auditd.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Configure auditd Disk Error Action on Disk Error + The auditd service can be configured to take an action +when there is a disk error. +Edit the file /etc/audit/auditd.conf. Add or modify the following line, +substituting ACTION appropriately: +disk_error_action = ACTION +Set this value to single to cause the system to switch to single-user +mode for corrective action. Acceptable values also include syslog, +exec, single, and halt. For certain systems, the need for availability +outweighs the need to log all actions, and a different setting should be +determined. Details regarding all possible values for ACTION are described in the +auditd.conf man page. + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI04.04 + BAI08.02 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS05.04 + DSS05.07 + MEA02.01 + CCI-000140 + 4.2.3.10 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 6.1 + SR 7.1 + SR 7.2 + A.12.1.3 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.17.2.1 + AU-5(b) + AU-5(2) + AU-5(1) + AU-5(4) + CM-6(a) + DE.AE-3 + DE.AE-5 + PR.DS-4 + PR.PT-1 + RS.AN-1 + RS.AN-4 + SRG-OS-000047-GPOS-00023 + Taking appropriate action in case of disk errors will minimize the possibility of +losing audit records. + - name: Gather the package facts + package_facts: + manager: auto + tags: + - NIST-800-53-AU-5(1) + - NIST-800-53-AU-5(2) + - NIST-800-53-AU-5(4) + - NIST-800-53-AU-5(b) + - NIST-800-53-CM-6(a) + - auditd_data_disk_error_action_stig + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy +- name: XCCDF Value var_auditd_disk_error_action # promote to variable + set_fact: + var_auditd_disk_error_action: !!str + tags: + - always + +- name: Configure auditd Disk Error Action on Disk Error + lineinfile: + dest: /etc/audit/auditd.conf + line: disk_error_action = {{ var_auditd_disk_error_action }} + regexp: ^\s*disk_error_action\s*=\s*.*$ + state: present + create: true + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AU-5(1) + - NIST-800-53-AU-5(2) + - NIST-800-53-AU-5(4) + - NIST-800-53-AU-5(b) + - NIST-800-53-CM-6(a) + - auditd_data_disk_error_action_stig + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,{{ %23%0A%23%20This%20file%20controls%20the%20configuration%20of%20the%20audit%20daemon%0A%23%0A%0Alocal_events%20%3D%20yes%0Awrite_logs%20%3D%20yes%0Alog_file%20%3D%20/var/log/audit/audit.log%0Alog_group%20%3D%20root%0Alog_format%20%3D%20ENRICHED%0Aflush%20%3D%20%7B%7B.var_auditd_flush%7D%7D%0Afreq%20%3D%2050%0Amax_log_file%20%3D%20%7B%7B.var_auditd_max_log_file%7D%7D%0Anum_logs%20%3D%20%7B%7B.var_auditd_num_logs%7D%7D%0Apriority_boost%20%3D%204%0Aname_format%20%3D%20hostname%0A%23%23name%20%3D%20mydomain%0Amax_log_file_action%20%3D%20%7B%7B.var_auditd_max_log_file_action%7D%7D%0Aspace_left%20%3D%20%7B%7B.var_auditd_space_left%7D%7D%0Aspace_left_action%20%3D%20%7B%7B.var_auditd_space_left_action%7D%7D%0Averify_email%20%3D%20yes%0Aaction_mail_acct%20%3D%20%7B%7B.var_auditd_action_mail_acct%7D%7D%0Aadmin_space_left%20%3D%2050%0Aadmin_space_left_action%20%3D%20syslog%0Adisk_full_action%20%3D%20%7B%7B.var_auditd_disk_full_action%7D%7D%0Adisk_error_action%20%3D%20%7B%7B.var_auditd_disk_error_action%7D%7D%0Ause_libwrap%20%3D%20yes%0A%23%23tcp_listen_port%20%3D%2060%0Atcp_listen_queue%20%3D%205%0Atcp_max_per_addr%20%3D%201%0A%23%23tcp_client_ports%20%3D%201024-65535%0Atcp_client_max_idle%20%3D%200%0Atransport%20%3D%20TCP%0Akrb5_principal%20%3D%20auditd%0A%23%23krb5_key_file%20%3D%20/etc/audit/audit.key%0Adistribute_network%20%3D%20no%0Aq_depth%20%3D%20400%0Aoverflow_action%20%3D%20syslog%0Amax_restarts%20%3D%2010%0Aplugin_dir%20%3D%20/etc/audit/plugins.d }} + mode: 0640 + path: /etc/audit/auditd.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +var_auditd_disk_error_action='' + + +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/audit/auditd.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^disk_error_action") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$var_auditd_disk_error_action" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^disk_error_action\\>" "/etc/audit/auditd.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^disk_error_action\\>.*/$escaped_formatted_output/gi" "/etc/audit/auditd.conf" +else + # \n is precaution for case where file ends without trailing newline + + printf '%s\n' "$formatted_output" >> "/etc/audit/auditd.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure auditd Disk Full Action when Disk Space Is Full + The auditd service can be configured to take an action +when disk space is running low but prior to running out of space completely. +Edit the file /etc/audit/auditd.conf. Add or modify the following line, +substituting ACTION appropriately: +disk_full_action = ACTION +Set this value to single to cause the system to switch to single-user +mode for corrective action. Acceptable values also include syslog, + +exec, + +single, and halt. For certain systems, the need for availability +outweighs the need to log all actions, and a different setting should be +determined. Details regarding all possible values for ACTION are described in the +auditd.conf man page. + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI04.04 + BAI08.02 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS05.04 + DSS05.07 + MEA02.01 + CCI-000140 + 4.2.3.10 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 6.1 + SR 7.1 + SR 7.2 + A.12.1.3 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.17.2.1 + AU-5(b) + AU-5(2) + AU-5(1) + AU-5(4) + CM-6(a) + DE.AE-3 + DE.AE-5 + PR.DS-4 + PR.PT-1 + RS.AN-1 + RS.AN-4 + SRG-OS-000047-GPOS-00023 + RHEL-08-030060 + SV-230392r627750_rule + Taking appropriate action in case of a filled audit storage volume will minimize +the possibility of losing audit records. + CCE-84045-4 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-84045-4 + - DISA-STIG-RHEL-08-030060 + - NIST-800-53-AU-5(1) + - NIST-800-53-AU-5(2) + - NIST-800-53-AU-5(4) + - NIST-800-53-AU-5(b) + - NIST-800-53-CM-6(a) + - auditd_data_disk_full_action + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy +- name: XCCDF Value var_auditd_disk_full_action # promote to variable + set_fact: + var_auditd_disk_full_action: !!str + tags: + - always + +- name: Configure auditd Disk Full Action when Disk Space Is Full + lineinfile: + dest: /etc/audit/auditd.conf + line: disk_full_action = {{ var_auditd_disk_full_action.split('|')[0] }} + regexp: ^\s*disk_full_action\s*=\s*.*$ + state: present + create: true + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84045-4 + - DISA-STIG-RHEL-08-030060 + - NIST-800-53-AU-5(1) + - NIST-800-53-AU-5(2) + - NIST-800-53-AU-5(4) + - NIST-800-53-AU-5(b) + - NIST-800-53-CM-6(a) + - auditd_data_disk_full_action + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,{{ %23%0A%23%20This%20file%20controls%20the%20configuration%20of%20the%20audit%20daemon%0A%23%0A%0Alocal_events%20%3D%20yes%0Awrite_logs%20%3D%20yes%0Alog_file%20%3D%20/var/log/audit/audit.log%0Alog_group%20%3D%20root%0Alog_format%20%3D%20ENRICHED%0Aflush%20%3D%20%7B%7B.var_auditd_flush%7D%7D%0Afreq%20%3D%2050%0Amax_log_file%20%3D%20%7B%7B.var_auditd_max_log_file%7D%7D%0Anum_logs%20%3D%20%7B%7B.var_auditd_num_logs%7D%7D%0Apriority_boost%20%3D%204%0Aname_format%20%3D%20hostname%0A%23%23name%20%3D%20mydomain%0Amax_log_file_action%20%3D%20%7B%7B.var_auditd_max_log_file_action%7D%7D%0Aspace_left%20%3D%20%7B%7B.var_auditd_space_left%7D%7D%0Aspace_left_action%20%3D%20%7B%7B.var_auditd_space_left_action%7D%7D%0Averify_email%20%3D%20yes%0Aaction_mail_acct%20%3D%20%7B%7B.var_auditd_action_mail_acct%7D%7D%0Aadmin_space_left%20%3D%2050%0Aadmin_space_left_action%20%3D%20syslog%0Adisk_full_action%20%3D%20%7B%7B.var_auditd_disk_full_action%7D%7D%0Adisk_error_action%20%3D%20%7B%7B.var_auditd_disk_error_action%7D%7D%0Ause_libwrap%20%3D%20yes%0A%23%23tcp_listen_port%20%3D%2060%0Atcp_listen_queue%20%3D%205%0Atcp_max_per_addr%20%3D%201%0A%23%23tcp_client_ports%20%3D%201024-65535%0Atcp_client_max_idle%20%3D%200%0Atransport%20%3D%20TCP%0Akrb5_principal%20%3D%20auditd%0A%23%23krb5_key_file%20%3D%20/etc/audit/audit.key%0Adistribute_network%20%3D%20no%0Aq_depth%20%3D%20400%0Aoverflow_action%20%3D%20syslog%0Amax_restarts%20%3D%2010%0Aplugin_dir%20%3D%20/etc/audit/plugins.d }} + mode: 0640 + path: /etc/audit/auditd.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +var_auditd_disk_full_action='' + + +var_auditd_disk_full_action="$(echo $var_auditd_disk_full_action | cut -d \| -f 1)" + +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/audit/auditd.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^disk_full_action") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$var_auditd_disk_full_action" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^disk_full_action\\>" "/etc/audit/auditd.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^disk_full_action\\>.*/$escaped_formatted_output/gi" "/etc/audit/auditd.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-84045-4" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/audit/auditd.conf" >> "/etc/audit/auditd.conf" + printf '%s\n' "$formatted_output" >> "/etc/audit/auditd.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Configure auditd Disk Full Action when Disk Space Is Full + The auditd service can be configured to take an action +when disk space is running low but prior to running out of space completely. +Edit the file /etc/audit/auditd.conf. Add or modify the following line, +substituting ACTION appropriately: +disk_full_action = ACTION +Set this value to single to cause the system to switch to single-user +mode for corrective action. Acceptable values also include syslog, +single, and halt. For certain systems, the need for availability +outweighs the need to log all actions, and a different setting should be +determined. Details regarding all possible values for ACTION are described in the +auditd.conf man page. + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI04.04 + BAI08.02 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS05.04 + DSS05.07 + MEA02.01 + CCI-000140 + 4.2.3.10 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 6.1 + SR 7.1 + SR 7.2 + A.12.1.3 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.17.2.1 + AU-5(b) + AU-5(2) + AU-5(1) + AU-5(4) + CM-6(a) + DE.AE-3 + DE.AE-5 + PR.DS-4 + PR.PT-1 + RS.AN-1 + RS.AN-4 + SRG-OS-000047-GPOS-00023 + Taking appropriate action in case of a filled audit storage volume will minimize +the possibility of losing audit records. + - name: Gather the package facts + package_facts: + manager: auto + tags: + - NIST-800-53-AU-5(1) + - NIST-800-53-AU-5(2) + - NIST-800-53-AU-5(4) + - NIST-800-53-AU-5(b) + - NIST-800-53-CM-6(a) + - auditd_data_disk_full_action_stig + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy +- name: XCCDF Value var_auditd_disk_full_action # promote to variable + set_fact: + var_auditd_disk_full_action: !!str + tags: + - always + +- name: Configure auditd Disk Full Action when Disk Space Is Full + lineinfile: + dest: /etc/audit/auditd.conf + line: disk_full_action = {{ var_auditd_disk_full_action }} + regexp: ^\s*disk_full_action\s*=\s*.*$ + state: present + create: true + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AU-5(1) + - NIST-800-53-AU-5(2) + - NIST-800-53-AU-5(4) + - NIST-800-53-AU-5(b) + - NIST-800-53-CM-6(a) + - auditd_data_disk_full_action_stig + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,{{ %23%0A%23%20This%20file%20controls%20the%20configuration%20of%20the%20audit%20daemon%0A%23%0A%0Alocal_events%20%3D%20yes%0Awrite_logs%20%3D%20yes%0Alog_file%20%3D%20/var/log/audit/audit.log%0Alog_group%20%3D%20root%0Alog_format%20%3D%20ENRICHED%0Aflush%20%3D%20%7B%7B.var_auditd_flush%7D%7D%0Afreq%20%3D%2050%0Amax_log_file%20%3D%20%7B%7B.var_auditd_max_log_file%7D%7D%0Anum_logs%20%3D%20%7B%7B.var_auditd_num_logs%7D%7D%0Apriority_boost%20%3D%204%0Aname_format%20%3D%20hostname%0A%23%23name%20%3D%20mydomain%0Amax_log_file_action%20%3D%20%7B%7B.var_auditd_max_log_file_action%7D%7D%0Aspace_left%20%3D%20%7B%7B.var_auditd_space_left%7D%7D%0Aspace_left_action%20%3D%20%7B%7B.var_auditd_space_left_action%7D%7D%0Averify_email%20%3D%20yes%0Aaction_mail_acct%20%3D%20%7B%7B.var_auditd_action_mail_acct%7D%7D%0Aadmin_space_left%20%3D%2050%0Aadmin_space_left_action%20%3D%20syslog%0Adisk_full_action%20%3D%20%7B%7B.var_auditd_disk_full_action%7D%7D%0Adisk_error_action%20%3D%20%7B%7B.var_auditd_disk_error_action%7D%7D%0Ause_libwrap%20%3D%20yes%0A%23%23tcp_listen_port%20%3D%2060%0Atcp_listen_queue%20%3D%205%0Atcp_max_per_addr%20%3D%201%0A%23%23tcp_client_ports%20%3D%201024-65535%0Atcp_client_max_idle%20%3D%200%0Atransport%20%3D%20TCP%0Akrb5_principal%20%3D%20auditd%0A%23%23krb5_key_file%20%3D%20/etc/audit/audit.key%0Adistribute_network%20%3D%20no%0Aq_depth%20%3D%20400%0Aoverflow_action%20%3D%20syslog%0Amax_restarts%20%3D%2010%0Aplugin_dir%20%3D%20/etc/audit/plugins.d }} + mode: 0640 + path: /etc/audit/auditd.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +var_auditd_disk_full_action='' + + +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/audit/auditd.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^disk_full_action") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$var_auditd_disk_full_action" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^disk_full_action\\>" "/etc/audit/auditd.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^disk_full_action\\>.*/$escaped_formatted_output/gi" "/etc/audit/auditd.conf" +else + # \n is precaution for case where file ends without trailing newline + + printf '%s\n' "$formatted_output" >> "/etc/audit/auditd.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure auditd mail_acct Action on Low Disk Space + The auditd service can be configured to send email to +a designated account in certain situations. Add or correct the following line +in /etc/audit/auditd.conf to ensure that administrators are notified +via email for those situations: +action_mail_acct = + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 5.4.1.1 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI04.04 + BAI08.02 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS05.04 + DSS05.07 + MEA02.01 + 3.3.1 + CCI-000139 + CCI-001855 + 164.312(a)(2)(ii) + 4.2.3.10 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 6.1 + SR 7.1 + SR 7.2 + A.12.1.3 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.17.2.1 + CIP-003-8 R1.3 + CIP-003-8 R3 + CIP-003-8 R3.1 + CIP-003-8 R3.2 + CIP-003-8 R3.3 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.2.3 + CIP-004-6 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.2 + CIP-007-3 R5.2 + CIP-007-3 R5.3.1 + CIP-007-3 R5.3.2 + CIP-007-3 R5.3.3 + IA-5(1) + AU-5(a) + AU-5(2) + CM-6(a) + DE.AE-3 + DE.AE-5 + PR.DS-4 + PR.PT-1 + RS.AN-1 + RS.AN-4 + Req-10.7.a + SRG-OS-000046-GPOS-00022 + SRG-OS-000343-GPOS-00134 + SRG-OS-000046-VMM-000210 + SRG-OS-000343-VMM-001240 + RHEL-08-030020 + 4.1.2.3 + SV-230388r627750_rule + Email sent to the root account is typically aliased to the +administrators of the system, who can take appropriate action. + CCE-80678-6 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80678-6 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030020 + - NIST-800-171-3.3.1 + - NIST-800-53-AU-5(2) + - NIST-800-53-AU-5(a) + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1) + - PCI-DSS-Req-10.7.a + - auditd_data_retention_action_mail_acct + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy +- name: XCCDF Value var_auditd_action_mail_acct # promote to variable + set_fact: + var_auditd_action_mail_acct: !!str + tags: + - always + +- name: Configure auditd mail_acct Action on Low Disk Space + lineinfile: + dest: /etc/audit/auditd.conf + line: action_mail_acct = {{ var_auditd_action_mail_acct }} + state: present + create: true + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80678-6 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030020 + - NIST-800-171-3.3.1 + - NIST-800-53-AU-5(2) + - NIST-800-53-AU-5(a) + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1) + - PCI-DSS-Req-10.7.a + - auditd_data_retention_action_mail_acct + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +var_auditd_action_mail_acct='' + + +AUDITCONFIG=/etc/audit/auditd.conf + +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "$AUDITCONFIG"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^action_mail_acct") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$var_auditd_action_mail_acct" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^action_mail_acct\\>" "$AUDITCONFIG"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^action_mail_acct\\>.*/$escaped_formatted_output/gi" "$AUDITCONFIG" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-80678-6" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "$AUDITCONFIG" >> "$AUDITCONFIG" + printf '%s\n' "$formatted_output" >> "$AUDITCONFIG" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Configure auditd admin_space_left Action on Low Disk Space + The auditd service can be configured to take an action +when disk space is running low but prior to running out of space completely. +Edit the file /etc/audit/auditd.conf. Add or modify the following line, +substituting ACTION appropriately: +admin_space_left_action = ACTION +Set this value to single to cause the system to switch to single user +mode for corrective action. Acceptable values also include suspend and +halt. For certain systems, the need for availability +outweighs the need to log all actions, and a different setting should be +determined. Details regarding all possible values for ACTION are described in the +auditd.conf man page. + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 5.4.1.1 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI04.04 + BAI08.02 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS05.04 + DSS05.07 + MEA02.01 + 3.3.1 + CCI-000140 + CCI-001343 + CCI-001855 + 164.312(a)(2)(ii) + 4.2.3.10 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 6.1 + SR 7.1 + SR 7.2 + A.12.1.3 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.17.2.1 + AU-5(b) + AU-5(2) + AU-5(1) + AU-5(4) + CM-6(a) + DE.AE-3 + DE.AE-5 + PR.DS-4 + PR.PT-1 + RS.AN-1 + RS.AN-4 + Req-10.7 + SRG-OS-000343-GPOS-00134 + 4.1.2.3 + Administrators should be made aware of an inability to record +audit records. If a separate partition or logical volume of adequate size +is used, running low on space for audit records should never occur. + CCE-80679-4 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80679-4 + - CJIS-5.4.1.1 + - NIST-800-171-3.3.1 + - NIST-800-53-AU-5(1) + - NIST-800-53-AU-5(2) + - NIST-800-53-AU-5(4) + - NIST-800-53-AU-5(b) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.7 + - auditd_data_retention_admin_space_left_action + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy +- name: XCCDF Value var_auditd_admin_space_left_action # promote to variable + set_fact: + var_auditd_admin_space_left_action: !!str + tags: + - always + +- name: Configure auditd admin_space_left Action on Low Disk Space + lineinfile: + dest: /etc/audit/auditd.conf + line: admin_space_left_action = {{ var_auditd_admin_space_left_action }} + regexp: ^\s*admin_space_left_action\s*=\s*.*$ + state: present + create: true + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80679-4 + - CJIS-5.4.1.1 + - NIST-800-171-3.3.1 + - NIST-800-53-AU-5(1) + - NIST-800-53-AU-5(2) + - NIST-800-53-AU-5(4) + - NIST-800-53-AU-5(b) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.7 + - auditd_data_retention_admin_space_left_action + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,{{ %23%0A%23%20This%20file%20controls%20the%20configuration%20of%20the%20audit%20daemon%0A%23%0A%0Alocal_events%20%3D%20yes%0Awrite_logs%20%3D%20yes%0Alog_file%20%3D%20/var/log/audit/audit.log%0Alog_group%20%3D%20root%0Alog_format%20%3D%20ENRICHED%0Aflush%20%3D%20%7B%7B.var_auditd_flush%7D%7D%0Afreq%20%3D%2050%0Amax_log_file%20%3D%20%7B%7B.var_auditd_max_log_file%7D%7D%0Anum_logs%20%3D%20%7B%7B.var_auditd_num_logs%7D%7D%0Apriority_boost%20%3D%204%0Aname_format%20%3D%20hostname%0A%23%23name%20%3D%20mydomain%0Amax_log_file_action%20%3D%20%7B%7B.var_auditd_max_log_file_action%7D%7D%0Aspace_left%20%3D%20%7B%7B.var_auditd_space_left%7D%7D%0Aspace_left_action%20%3D%20%7B%7B.var_auditd_space_left_action%7D%7D%0Averify_email%20%3D%20yes%0Aaction_mail_acct%20%3D%20%7B%7B.var_auditd_action_mail_acct%7D%7D%0Aadmin_space_left%20%3D%2050%0Aadmin_space_left_action%20%3D%20syslog%0Adisk_full_action%20%3D%20%7B%7B.var_auditd_disk_full_action%7D%7D%0Adisk_error_action%20%3D%20%7B%7B.var_auditd_disk_error_action%7D%7D%0Ause_libwrap%20%3D%20yes%0A%23%23tcp_listen_port%20%3D%2060%0Atcp_listen_queue%20%3D%205%0Atcp_max_per_addr%20%3D%201%0A%23%23tcp_client_ports%20%3D%201024-65535%0Atcp_client_max_idle%20%3D%200%0Atransport%20%3D%20TCP%0Akrb5_principal%20%3D%20auditd%0A%23%23krb5_key_file%20%3D%20/etc/audit/audit.key%0Adistribute_network%20%3D%20no%0Aq_depth%20%3D%20400%0Aoverflow_action%20%3D%20syslog%0Amax_restarts%20%3D%2010%0Aplugin_dir%20%3D%20/etc/audit/plugins.d }} + mode: 0640 + path: /etc/audit/auditd.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +var_auditd_admin_space_left_action='' + + +AUDITCONFIG=/etc/audit/auditd.conf + +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "$AUDITCONFIG"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^admin_space_left_action") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$var_auditd_admin_space_left_action" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^admin_space_left_action\\>" "$AUDITCONFIG"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^admin_space_left_action\\>.*/$escaped_formatted_output/gi" "$AUDITCONFIG" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-80679-4" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "$AUDITCONFIG" >> "$AUDITCONFIG" + printf '%s\n' "$formatted_output" >> "$AUDITCONFIG" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Configure auditd flush priority + The auditd service can be configured to +synchronously write audit event data to disk. Add or correct the following +line in /etc/audit/auditd.conf to ensure that audit event data is +fully synchronized with the log files on the disk: +flush = + 1 + 12 + 13 + 14 + 15 + 16 + 2 + 3 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + BAI03.05 + DSS01.03 + DSS03.05 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.3.1 + CCI-001576 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 6.1 + SR 6.2 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.14.2.7 + A.15.2.1 + A.15.2.2 + CIP-004-6 R2.2.3 + CIP-004-6 R3.3 + CIP-007-3 R5.2 + CIP-007-3 R5.3.1 + CIP-007-3 R5.3.2 + CIP-007-3 R5.3.3 + CIP-007-3 R6.5 + AU-11 + CM-6(a) + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.PT-1 + SRG-OS-000480-GPOS-00227 + Audit data should be synchronously written to disk to ensure +log integrity. These parameters assure that all audit event data is fully +synchronized with the log files on the disk. + CCE-80680-2 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80680-2 + - NIST-800-171-3.3.1 + - NIST-800-53-AU-11 + - NIST-800-53-CM-6(a) + - auditd_data_retention_flush + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy +- name: XCCDF Value var_auditd_flush # promote to variable + set_fact: + var_auditd_flush: !!str + tags: + - always + +- name: Configure auditd Flush Priority + lineinfile: + dest: /etc/audit/auditd.conf + regexp: ^\s*flush\s*=\s*.*$ + line: flush = {{ var_auditd_flush }} + state: present + create: true + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80680-2 + - NIST-800-171-3.3.1 + - NIST-800-53-AU-11 + - NIST-800-53-CM-6(a) + - auditd_data_retention_flush + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,{{ %23%0A%23%20This%20file%20controls%20the%20configuration%20of%20the%20audit%20daemon%0A%23%0A%0Alocal_events%20%3D%20yes%0Awrite_logs%20%3D%20yes%0Alog_file%20%3D%20/var/log/audit/audit.log%0Alog_group%20%3D%20root%0Alog_format%20%3D%20ENRICHED%0Aflush%20%3D%20%7B%7B.var_auditd_flush%7D%7D%0Afreq%20%3D%2050%0Amax_log_file%20%3D%20%7B%7B.var_auditd_max_log_file%7D%7D%0Anum_logs%20%3D%20%7B%7B.var_auditd_num_logs%7D%7D%0Apriority_boost%20%3D%204%0Aname_format%20%3D%20hostname%0A%23%23name%20%3D%20mydomain%0Amax_log_file_action%20%3D%20%7B%7B.var_auditd_max_log_file_action%7D%7D%0Aspace_left%20%3D%20%7B%7B.var_auditd_space_left%7D%7D%0Aspace_left_action%20%3D%20%7B%7B.var_auditd_space_left_action%7D%7D%0Averify_email%20%3D%20yes%0Aaction_mail_acct%20%3D%20%7B%7B.var_auditd_action_mail_acct%7D%7D%0Aadmin_space_left%20%3D%2050%0Aadmin_space_left_action%20%3D%20syslog%0Adisk_full_action%20%3D%20%7B%7B.var_auditd_disk_full_action%7D%7D%0Adisk_error_action%20%3D%20%7B%7B.var_auditd_disk_error_action%7D%7D%0Ause_libwrap%20%3D%20yes%0A%23%23tcp_listen_port%20%3D%2060%0Atcp_listen_queue%20%3D%205%0Atcp_max_per_addr%20%3D%201%0A%23%23tcp_client_ports%20%3D%201024-65535%0Atcp_client_max_idle%20%3D%200%0Atransport%20%3D%20TCP%0Akrb5_principal%20%3D%20auditd%0A%23%23krb5_key_file%20%3D%20/etc/audit/audit.key%0Adistribute_network%20%3D%20no%0Aq_depth%20%3D%20400%0Aoverflow_action%20%3D%20syslog%0Amax_restarts%20%3D%2010%0Aplugin_dir%20%3D%20/etc/audit/plugins.d }} + mode: 0640 + path: /etc/audit/auditd.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +var_auditd_flush='' + + +AUDITCONFIG=/etc/audit/auditd.conf + +# if flush is present, flush param edited to var_auditd_flush +# else flush param is defined by var_auditd_flush +# +# the freq param is only used for values 'incremental' and 'incremental_async' and will be +# commented out if flush != incremental or flush != incremental_async +# +# if flush == incremental or flush == incremental_async && freq param is not defined, it +# will be defined as the package-default value of 20 + +grep -q ^flush $AUDITCONFIG && \ + sed -i 's/^flush.*/flush = '"$var_auditd_flush"'/g' $AUDITCONFIG +if ! [ $? -eq 0 ]; then + echo "flush = $var_auditd_flush" >> $AUDITCONFIG +fi + +if ! [ "$var_auditd_flush" == "incremental" ] && ! [ "$var_auditd_flush" == "incremental_async" ]; then + sed -i 's/^freq/##freq/g' $AUDITCONFIG +elif [ "$var_auditd_flush" == "incremental" ] || [ "$var_auditd_flush" == "incremental_async" ]; then + grep -q freq $AUDITCONFIG && \ + sed -i 's/^#\+freq/freq/g' $AUDITCONFIG + if ! [ $? -eq 0 ]; then + echo "freq = 20" >> $AUDITCONFIG + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Configure auditd Max Log File Size + Determine the amount of audit data (in megabytes) +which should be retained in each log file. Edit the file +/etc/audit/auditd.conf. Add or modify the following line, substituting +the correct value of for STOREMB: +max_log_file = STOREMB +Set the value to 6 (MB) or higher for general-purpose systems. +Larger values, of course, +support retention of even more audit data. + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 3 + 4 + 5 + 6 + 7 + 8 + 5.4.1.1 + APO11.04 + APO12.06 + BAI03.05 + BAI08.02 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS05.04 + DSS05.07 + MEA02.01 + 4.2.3.10 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 6.1 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.16.1.4 + A.16.1.5 + A.16.1.7 + CIP-004-6 R2.2.3 + CIP-004-6 R3.3 + CIP-007-3 R5.2 + CIP-007-3 R5.3.1 + CIP-007-3 R5.3.2 + CIP-007-3 R5.3.3 + CIP-007-3 R6.5 + AU-11 + CM-6(a) + DE.AE-3 + DE.AE-5 + PR.PT-1 + RS.AN-1 + RS.AN-4 + Req-10.7 + 4.1.2.1 + The total storage for audit log files must be large enough to retain +log information over the period required. This is a function of the maximum +log file size and the number of logs retained. + CCE-80681-0 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80681-0 + - CJIS-5.4.1.1 + - NIST-800-53-AU-11 + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.7 + - auditd_data_retention_max_log_file + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy +- name: XCCDF Value var_auditd_max_log_file # promote to variable + set_fact: + var_auditd_max_log_file: !!str + tags: + - always + +- name: Configure auditd Max Log File Size + lineinfile: + dest: /etc/audit/auditd.conf + regexp: ^\s*max_log_file\s*=\s*.*$ + line: max_log_file = {{ var_auditd_max_log_file }} + state: present + create: true + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80681-0 + - CJIS-5.4.1.1 + - NIST-800-53-AU-11 + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.7 + - auditd_data_retention_max_log_file + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,{{ %23%0A%23%20This%20file%20controls%20the%20configuration%20of%20the%20audit%20daemon%0A%23%0A%0Alocal_events%20%3D%20yes%0Awrite_logs%20%3D%20yes%0Alog_file%20%3D%20/var/log/audit/audit.log%0Alog_group%20%3D%20root%0Alog_format%20%3D%20ENRICHED%0Aflush%20%3D%20%7B%7B.var_auditd_flush%7D%7D%0Afreq%20%3D%2050%0Amax_log_file%20%3D%20%7B%7B.var_auditd_max_log_file%7D%7D%0Anum_logs%20%3D%20%7B%7B.var_auditd_num_logs%7D%7D%0Apriority_boost%20%3D%204%0Aname_format%20%3D%20hostname%0A%23%23name%20%3D%20mydomain%0Amax_log_file_action%20%3D%20%7B%7B.var_auditd_max_log_file_action%7D%7D%0Aspace_left%20%3D%20%7B%7B.var_auditd_space_left%7D%7D%0Aspace_left_action%20%3D%20%7B%7B.var_auditd_space_left_action%7D%7D%0Averify_email%20%3D%20yes%0Aaction_mail_acct%20%3D%20%7B%7B.var_auditd_action_mail_acct%7D%7D%0Aadmin_space_left%20%3D%2050%0Aadmin_space_left_action%20%3D%20syslog%0Adisk_full_action%20%3D%20%7B%7B.var_auditd_disk_full_action%7D%7D%0Adisk_error_action%20%3D%20%7B%7B.var_auditd_disk_error_action%7D%7D%0Ause_libwrap%20%3D%20yes%0A%23%23tcp_listen_port%20%3D%2060%0Atcp_listen_queue%20%3D%205%0Atcp_max_per_addr%20%3D%201%0A%23%23tcp_client_ports%20%3D%201024-65535%0Atcp_client_max_idle%20%3D%200%0Atransport%20%3D%20TCP%0Akrb5_principal%20%3D%20auditd%0A%23%23krb5_key_file%20%3D%20/etc/audit/audit.key%0Adistribute_network%20%3D%20no%0Aq_depth%20%3D%20400%0Aoverflow_action%20%3D%20syslog%0Amax_restarts%20%3D%2010%0Aplugin_dir%20%3D%20/etc/audit/plugins.d }} + mode: 0640 + path: /etc/audit/auditd.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +var_auditd_max_log_file='' + + +AUDITCONFIG=/etc/audit/auditd.conf + +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "$AUDITCONFIG"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^max_log_file") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$var_auditd_max_log_file" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^max_log_file\\>" "$AUDITCONFIG"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^max_log_file\\>.*/$escaped_formatted_output/gi" "$AUDITCONFIG" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-80681-0" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "$AUDITCONFIG" >> "$AUDITCONFIG" + printf '%s\n' "$formatted_output" >> "$AUDITCONFIG" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Configure auditd max_log_file_action Upon Reaching Maximum Log Size + The default action to take when the logs reach their maximum size +is to rotate the log files, discarding the oldest one. To configure the action taken +by auditd, add or correct the line in /etc/audit/auditd.conf: +max_log_file_action = ACTION +Possible values for ACTION are described in the auditd.conf man +page. These include: +ignoresyslogsuspendrotatekeep_logs +Set the ACTION to rotate to ensure log rotation +occurs. This is the default. The setting is case-insensitive. + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 5.4.1.1 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI04.04 + BAI08.02 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS05.04 + DSS05.07 + MEA02.01 + CCI-000140 + 164.312(a)(2)(ii) + 4.2.3.10 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 6.1 + SR 7.1 + SR 7.2 + A.12.1.3 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.17.2.1 + AU-5(b) + AU-5(2) + AU-5(1) + AU-5(4) + CM-6(a) + DE.AE-3 + DE.AE-5 + PR.DS-4 + PR.PT-1 + RS.AN-1 + RS.AN-4 + Req-10.7 + SRG-OS-000047-GPOS-00023 + 4.1.2.2 + Automatically rotating logs (by setting this to rotate) +minimizes the chances of the system unexpectedly running out of disk space by +being overwhelmed with log data. However, for systems that must never discard +log data, or which use external processes to transfer it and reclaim space, +keep_logs can be employed. + CCE-80682-8 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80682-8 + - CJIS-5.4.1.1 + - NIST-800-53-AU-5(1) + - NIST-800-53-AU-5(2) + - NIST-800-53-AU-5(4) + - NIST-800-53-AU-5(b) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.7 + - auditd_data_retention_max_log_file_action + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy +- name: XCCDF Value var_auditd_max_log_file_action # promote to variable + set_fact: + var_auditd_max_log_file_action: !!str + tags: + - always + +- name: Configure auditd max_log_file_action Upon Reaching Maximum Log Size + lineinfile: + dest: /etc/audit/auditd.conf + line: max_log_file_action = {{ var_auditd_max_log_file_action }} + regexp: ^\s*max_log_file_action\s*=\s*.*$ + state: present + create: true + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80682-8 + - CJIS-5.4.1.1 + - NIST-800-53-AU-5(1) + - NIST-800-53-AU-5(2) + - NIST-800-53-AU-5(4) + - NIST-800-53-AU-5(b) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.7 + - auditd_data_retention_max_log_file_action + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,{{ %23%0A%23%20This%20file%20controls%20the%20configuration%20of%20the%20audit%20daemon%0A%23%0A%0Alocal_events%20%3D%20yes%0Awrite_logs%20%3D%20yes%0Alog_file%20%3D%20/var/log/audit/audit.log%0Alog_group%20%3D%20root%0Alog_format%20%3D%20ENRICHED%0Aflush%20%3D%20%7B%7B.var_auditd_flush%7D%7D%0Afreq%20%3D%2050%0Amax_log_file%20%3D%20%7B%7B.var_auditd_max_log_file%7D%7D%0Anum_logs%20%3D%20%7B%7B.var_auditd_num_logs%7D%7D%0Apriority_boost%20%3D%204%0Aname_format%20%3D%20hostname%0A%23%23name%20%3D%20mydomain%0Amax_log_file_action%20%3D%20%7B%7B.var_auditd_max_log_file_action%7D%7D%0Aspace_left%20%3D%20%7B%7B.var_auditd_space_left%7D%7D%0Aspace_left_action%20%3D%20%7B%7B.var_auditd_space_left_action%7D%7D%0Averify_email%20%3D%20yes%0Aaction_mail_acct%20%3D%20%7B%7B.var_auditd_action_mail_acct%7D%7D%0Aadmin_space_left%20%3D%2050%0Aadmin_space_left_action%20%3D%20syslog%0Adisk_full_action%20%3D%20%7B%7B.var_auditd_disk_full_action%7D%7D%0Adisk_error_action%20%3D%20%7B%7B.var_auditd_disk_error_action%7D%7D%0Ause_libwrap%20%3D%20yes%0A%23%23tcp_listen_port%20%3D%2060%0Atcp_listen_queue%20%3D%205%0Atcp_max_per_addr%20%3D%201%0A%23%23tcp_client_ports%20%3D%201024-65535%0Atcp_client_max_idle%20%3D%200%0Atransport%20%3D%20TCP%0Akrb5_principal%20%3D%20auditd%0A%23%23krb5_key_file%20%3D%20/etc/audit/audit.key%0Adistribute_network%20%3D%20no%0Aq_depth%20%3D%20400%0Aoverflow_action%20%3D%20syslog%0Amax_restarts%20%3D%2010%0Aplugin_dir%20%3D%20/etc/audit/plugins.d }} + mode: 0640 + path: /etc/audit/auditd.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +var_auditd_max_log_file_action='' + + +AUDITCONFIG=/etc/audit/auditd.conf + +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "$AUDITCONFIG"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^max_log_file_action") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$var_auditd_max_log_file_action" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^max_log_file_action\\>" "$AUDITCONFIG"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^max_log_file_action\\>.*/$escaped_formatted_output/gi" "$AUDITCONFIG" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-80682-8" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "$AUDITCONFIG" >> "$AUDITCONFIG" + printf '%s\n' "$formatted_output" >> "$AUDITCONFIG" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Configure auditd max_log_file_action Upon Reaching Maximum Log Size + The default action to take when the logs reach their maximum size +is to rotate the log files, discarding the oldest one. To configure the action taken +by auditd, add or correct the line in /etc/audit/auditd.conf: +max_log_file_action = ACTION +Possible values for ACTION are described in the auditd.conf man +page. These include: +ignoresyslogsuspendrotatekeep_logs +Set the ACTION to rotate to ensure log rotation +occurs. This is the default. The setting is case-insensitive. + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI04.04 + BAI08.02 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS05.04 + DSS05.07 + MEA02.01 + CCI-000140 + 164.312(a)(2)(ii) + 4.2.3.10 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 6.1 + SR 7.1 + SR 7.2 + A.12.1.3 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.17.2.1 + AU-5(b) + AU-5(2) + AU-5(1) + AU-5(4) + CM-6(a) + DE.AE-3 + DE.AE-5 + PR.DS-4 + PR.PT-1 + RS.AN-1 + RS.AN-4 + Req-10.7 + SRG-OS-000047-GPOS-00023 + Automatically rotating logs (by setting this to rotate) +minimizes the chances of the system unexpectedly running out of disk space by +being overwhelmed with log data. However, for systems that must never discard +log data, or which use external processes to transfer it and reclaim space, +keep_logs can be employed. + - name: Gather the package facts + package_facts: + manager: auto + tags: + - NIST-800-53-AU-5(1) + - NIST-800-53-AU-5(2) + - NIST-800-53-AU-5(4) + - NIST-800-53-AU-5(b) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.7 + - auditd_data_retention_max_log_file_action_stig + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy +- name: XCCDF Value var_auditd_max_log_file_action # promote to variable + set_fact: + var_auditd_max_log_file_action: !!str + tags: + - always + +- name: Configure auditd max_log_file_action Upon Reaching Maximum Log Size + lineinfile: + dest: /etc/audit/auditd.conf + line: max_log_file_action = {{ var_auditd_max_log_file_action }} + regexp: ^\s*max_log_file_action\s*=\s*.*$ + state: present + create: true + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AU-5(1) + - NIST-800-53-AU-5(2) + - NIST-800-53-AU-5(4) + - NIST-800-53-AU-5(b) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.7 + - auditd_data_retention_max_log_file_action_stig + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,{{ %23%0A%23%20This%20file%20controls%20the%20configuration%20of%20the%20audit%20daemon%0A%23%0A%0Alocal_events%20%3D%20yes%0Awrite_logs%20%3D%20yes%0Alog_file%20%3D%20/var/log/audit/audit.log%0Alog_group%20%3D%20root%0Alog_format%20%3D%20ENRICHED%0Aflush%20%3D%20%7B%7B.var_auditd_flush%7D%7D%0Afreq%20%3D%2050%0Amax_log_file%20%3D%20%7B%7B.var_auditd_max_log_file%7D%7D%0Anum_logs%20%3D%20%7B%7B.var_auditd_num_logs%7D%7D%0Apriority_boost%20%3D%204%0Aname_format%20%3D%20hostname%0A%23%23name%20%3D%20mydomain%0Amax_log_file_action%20%3D%20%7B%7B.var_auditd_max_log_file_action%7D%7D%0Aspace_left%20%3D%20%7B%7B.var_auditd_space_left%7D%7D%0Aspace_left_action%20%3D%20%7B%7B.var_auditd_space_left_action%7D%7D%0Averify_email%20%3D%20yes%0Aaction_mail_acct%20%3D%20%7B%7B.var_auditd_action_mail_acct%7D%7D%0Aadmin_space_left%20%3D%2050%0Aadmin_space_left_action%20%3D%20syslog%0Adisk_full_action%20%3D%20%7B%7B.var_auditd_disk_full_action%7D%7D%0Adisk_error_action%20%3D%20%7B%7B.var_auditd_disk_error_action%7D%7D%0Ause_libwrap%20%3D%20yes%0A%23%23tcp_listen_port%20%3D%2060%0Atcp_listen_queue%20%3D%205%0Atcp_max_per_addr%20%3D%201%0A%23%23tcp_client_ports%20%3D%201024-65535%0Atcp_client_max_idle%20%3D%200%0Atransport%20%3D%20TCP%0Akrb5_principal%20%3D%20auditd%0A%23%23krb5_key_file%20%3D%20/etc/audit/audit.key%0Adistribute_network%20%3D%20no%0Aq_depth%20%3D%20400%0Aoverflow_action%20%3D%20syslog%0Amax_restarts%20%3D%2010%0Aplugin_dir%20%3D%20/etc/audit/plugins.d }} + mode: 0640 + path: /etc/audit/auditd.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +var_auditd_max_log_file_action='' + + +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/audit/auditd.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^max_log_file_action") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$var_auditd_max_log_file_action" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^max_log_file_action\\>" "/etc/audit/auditd.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^max_log_file_action\\>.*/$escaped_formatted_output/gi" "/etc/audit/auditd.conf" +else + # \n is precaution for case where file ends without trailing newline + + printf '%s\n' "$formatted_output" >> "/etc/audit/auditd.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure auditd Number of Logs Retained + Determine how many log files +auditd should retain when it rotates logs. +Edit the file /etc/audit/auditd.conf. Add or modify the following +line, substituting NUMLOGS with the correct value of : +num_logs = NUMLOGS +Set the value to 5 for general-purpose systems. +Note that values less than 2 result in no log rotation. + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 3 + 4 + 5 + 6 + 7 + 8 + 5.4.1.1 + APO11.04 + APO12.06 + BAI03.05 + BAI08.02 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS05.04 + DSS05.07 + MEA02.01 + 3.3.1 + 4.2.3.10 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 6.1 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.16.1.4 + A.16.1.5 + A.16.1.7 + CIP-004-6 R2.2.3 + CIP-004-6 R3.3 + CIP-007-3 R5.2 + CIP-007-3 R5.3.1 + CIP-007-3 R5.3.2 + CIP-007-3 R5.3.3 + CIP-007-3 R6.5 + AU-11 + CM-6(a) + DE.AE-3 + DE.AE-5 + PR.PT-1 + RS.AN-1 + RS.AN-4 + Req-10.7 + The total storage for audit log files must be large enough to retain +log information over the period required. This is a function of the maximum log +file size and the number of logs retained. + CCE-80683-6 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80683-6 + - CJIS-5.4.1.1 + - NIST-800-171-3.3.1 + - NIST-800-53-AU-11 + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.7 + - auditd_data_retention_num_logs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy +- name: XCCDF Value var_auditd_num_logs # promote to variable + set_fact: + var_auditd_num_logs: !!str + tags: + - always + +- name: Configure auditd Number of Logs Retained + lineinfile: + dest: /etc/audit/auditd.conf + line: num_logs = {{ var_auditd_num_logs }} + regexp: ^\s*num_logs\s*=\s*.*$ + state: present + create: true + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80683-6 + - CJIS-5.4.1.1 + - NIST-800-171-3.3.1 + - NIST-800-53-AU-11 + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.7 + - auditd_data_retention_num_logs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,{{ %23%0A%23%20This%20file%20controls%20the%20configuration%20of%20the%20audit%20daemon%0A%23%0A%0Alocal_events%20%3D%20yes%0Awrite_logs%20%3D%20yes%0Alog_file%20%3D%20/var/log/audit/audit.log%0Alog_group%20%3D%20root%0Alog_format%20%3D%20ENRICHED%0Aflush%20%3D%20%7B%7B.var_auditd_flush%7D%7D%0Afreq%20%3D%2050%0Amax_log_file%20%3D%20%7B%7B.var_auditd_max_log_file%7D%7D%0Anum_logs%20%3D%20%7B%7B.var_auditd_num_logs%7D%7D%0Apriority_boost%20%3D%204%0Aname_format%20%3D%20hostname%0A%23%23name%20%3D%20mydomain%0Amax_log_file_action%20%3D%20%7B%7B.var_auditd_max_log_file_action%7D%7D%0Aspace_left%20%3D%20%7B%7B.var_auditd_space_left%7D%7D%0Aspace_left_action%20%3D%20%7B%7B.var_auditd_space_left_action%7D%7D%0Averify_email%20%3D%20yes%0Aaction_mail_acct%20%3D%20%7B%7B.var_auditd_action_mail_acct%7D%7D%0Aadmin_space_left%20%3D%2050%0Aadmin_space_left_action%20%3D%20syslog%0Adisk_full_action%20%3D%20%7B%7B.var_auditd_disk_full_action%7D%7D%0Adisk_error_action%20%3D%20%7B%7B.var_auditd_disk_error_action%7D%7D%0Ause_libwrap%20%3D%20yes%0A%23%23tcp_listen_port%20%3D%2060%0Atcp_listen_queue%20%3D%205%0Atcp_max_per_addr%20%3D%201%0A%23%23tcp_client_ports%20%3D%201024-65535%0Atcp_client_max_idle%20%3D%200%0Atransport%20%3D%20TCP%0Akrb5_principal%20%3D%20auditd%0A%23%23krb5_key_file%20%3D%20/etc/audit/audit.key%0Adistribute_network%20%3D%20no%0Aq_depth%20%3D%20400%0Aoverflow_action%20%3D%20syslog%0Amax_restarts%20%3D%2010%0Aplugin_dir%20%3D%20/etc/audit/plugins.d }} + mode: 0640 + path: /etc/audit/auditd.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +var_auditd_num_logs='' + + +AUDITCONFIG=/etc/audit/auditd.conf + +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "$AUDITCONFIG"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^num_logs") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$var_auditd_num_logs" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^num_logs\\>" "$AUDITCONFIG"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^num_logs\\>.*/$escaped_formatted_output/gi" "$AUDITCONFIG" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-80683-6" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "$AUDITCONFIG" >> "$AUDITCONFIG" + printf '%s\n' "$formatted_output" >> "$AUDITCONFIG" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Configure auditd space_left on Low Disk Space + The auditd service can be configured to take an action +when disk space is running low but prior to running out of space completely. +Edit the file /etc/audit/auditd.conf. Add or modify the following line, +substituting SIZE_in_MB appropriately: +space_left = SIZE_in_MB +Set this value to the appropriate size in Megabytes cause the system to +notify the user of an issue. + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI04.04 + BAI08.02 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS05.04 + DSS05.07 + MEA02.01 + CCI-001855 + 4.2.3.10 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 6.1 + SR 7.1 + SR 7.2 + A.12.1.3 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.17.2.1 + AU-5(b) + AU-5(2) + AU-5(1) + AU-5(4) + CM-6(a) + DE.AE-3 + DE.AE-5 + PR.DS-4 + PR.PT-1 + RS.AN-1 + RS.AN-4 + Req-10.7 + SRG-OS-000343-GPOS-00134 + SRG-OS-000343-VMM-001240 + Notifying administrators of an impending disk space problem may allow them to +take corrective action prior to any disruption. + CCE-83619-7 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83619-7 + - NIST-800-53-AU-5(1) + - NIST-800-53-AU-5(2) + - NIST-800-53-AU-5(4) + - NIST-800-53-AU-5(b) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.7 + - auditd_data_retention_space_left + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy +- name: XCCDF Value var_auditd_space_left # promote to variable + set_fact: + var_auditd_space_left: !!str + tags: + - always + +- name: Configure auditd space_left on Low Disk Space + lineinfile: + dest: /etc/audit/auditd.conf + line: space_left = {{ var_auditd_space_left }} + regexp: ^\s*space_left\s*=\s*.*$ + state: present + create: true + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83619-7 + - NIST-800-53-AU-5(1) + - NIST-800-53-AU-5(2) + - NIST-800-53-AU-5(4) + - NIST-800-53-AU-5(b) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.7 + - auditd_data_retention_space_left + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,{{ %23%0A%23%20This%20file%20controls%20the%20configuration%20of%20the%20audit%20daemon%0A%23%0A%0Alocal_events%20%3D%20yes%0Awrite_logs%20%3D%20yes%0Alog_file%20%3D%20/var/log/audit/audit.log%0Alog_group%20%3D%20root%0Alog_format%20%3D%20ENRICHED%0Aflush%20%3D%20%7B%7B.var_auditd_flush%7D%7D%0Afreq%20%3D%2050%0Amax_log_file%20%3D%20%7B%7B.var_auditd_max_log_file%7D%7D%0Anum_logs%20%3D%20%7B%7B.var_auditd_num_logs%7D%7D%0Apriority_boost%20%3D%204%0Aname_format%20%3D%20hostname%0A%23%23name%20%3D%20mydomain%0Amax_log_file_action%20%3D%20%7B%7B.var_auditd_max_log_file_action%7D%7D%0Aspace_left%20%3D%20%7B%7B.var_auditd_space_left%7D%7D%0Aspace_left_action%20%3D%20%7B%7B.var_auditd_space_left_action%7D%7D%0Averify_email%20%3D%20yes%0Aaction_mail_acct%20%3D%20%7B%7B.var_auditd_action_mail_acct%7D%7D%0Aadmin_space_left%20%3D%2050%0Aadmin_space_left_action%20%3D%20syslog%0Adisk_full_action%20%3D%20%7B%7B.var_auditd_disk_full_action%7D%7D%0Adisk_error_action%20%3D%20%7B%7B.var_auditd_disk_error_action%7D%7D%0Ause_libwrap%20%3D%20yes%0A%23%23tcp_listen_port%20%3D%2060%0Atcp_listen_queue%20%3D%205%0Atcp_max_per_addr%20%3D%201%0A%23%23tcp_client_ports%20%3D%201024-65535%0Atcp_client_max_idle%20%3D%200%0Atransport%20%3D%20TCP%0Akrb5_principal%20%3D%20auditd%0A%23%23krb5_key_file%20%3D%20/etc/audit/audit.key%0Adistribute_network%20%3D%20no%0Aq_depth%20%3D%20400%0Aoverflow_action%20%3D%20syslog%0Amax_restarts%20%3D%2010%0Aplugin_dir%20%3D%20/etc/audit/plugins.d }} + mode: 0640 + path: /etc/audit/auditd.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +var_auditd_space_left='' + + +grep -q "^space_left[[:space:]]*=.*$" /etc/audit/auditd.conf && \ + sed -i "s/^space_left[[:space:]]*=.*$/space_left = $var_auditd_space_left/g" /etc/audit/auditd.conf || \ + echo "space_left = $var_auditd_space_left" >> /etc/audit/auditd.conf + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Configure auditd space_left Action on Low Disk Space + The auditd service can be configured to take an action +when disk space starts to run low. +Edit the file /etc/audit/auditd.conf. Modify the following line, +substituting ACTION appropriately: +space_left_action = ACTION +Possible values for ACTION are described in the auditd.conf man page. +These include: +syslogemailexecsuspendsinglehalt +Set this to email (instead of the default, +which is suspend) as it is more likely to get prompt attention. Acceptable values +also include suspend, single, and halt. + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 5.4.1.1 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI04.04 + BAI08.02 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS05.04 + DSS05.07 + MEA02.01 + 3.3.1 + CCI-001855 + 164.312(a)(2)(ii) + 4.2.3.10 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 6.1 + SR 7.1 + SR 7.2 + A.12.1.3 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.17.2.1 + AU-5(b) + AU-5(2) + AU-5(1) + AU-5(4) + CM-6(a) + DE.AE-3 + DE.AE-5 + PR.DS-4 + PR.PT-1 + RS.AN-1 + RS.AN-4 + Req-10.7 + SRG-OS-000343-GPOS-00134 + SRG-OS-000343-VMM-001240 + RHEL-08-030731 + 4.1.2.3 + SV-244543r743878_rule + Notifying administrators of an impending disk space problem may +allow them to take corrective action prior to any disruption. + CCE-80684-4 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80684-4 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030731 + - NIST-800-171-3.3.1 + - NIST-800-53-AU-5(1) + - NIST-800-53-AU-5(2) + - NIST-800-53-AU-5(4) + - NIST-800-53-AU-5(b) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.7 + - auditd_data_retention_space_left_action + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy +- name: XCCDF Value var_auditd_space_left_action # promote to variable + set_fact: + var_auditd_space_left_action: !!str + tags: + - always + +- name: Configure auditd space_left Action on Low Disk Space + lineinfile: + dest: /etc/audit/auditd.conf + line: space_left_action = {{ var_auditd_space_left_action }} + regexp: ^\s*space_left_action\s*=\s*.*$ + state: present + create: true + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80684-4 + - CJIS-5.4.1.1 + - DISA-STIG-RHEL-08-030731 + - NIST-800-171-3.3.1 + - NIST-800-53-AU-5(1) + - NIST-800-53-AU-5(2) + - NIST-800-53-AU-5(4) + - NIST-800-53-AU-5(b) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.7 + - auditd_data_retention_space_left_action + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,{{ %23%0A%23%20This%20file%20controls%20the%20configuration%20of%20the%20audit%20daemon%0A%23%0A%0Alocal_events%20%3D%20yes%0Awrite_logs%20%3D%20yes%0Alog_file%20%3D%20/var/log/audit/audit.log%0Alog_group%20%3D%20root%0Alog_format%20%3D%20ENRICHED%0Aflush%20%3D%20%7B%7B.var_auditd_flush%7D%7D%0Afreq%20%3D%2050%0Amax_log_file%20%3D%20%7B%7B.var_auditd_max_log_file%7D%7D%0Anum_logs%20%3D%20%7B%7B.var_auditd_num_logs%7D%7D%0Apriority_boost%20%3D%204%0Aname_format%20%3D%20hostname%0A%23%23name%20%3D%20mydomain%0Amax_log_file_action%20%3D%20%7B%7B.var_auditd_max_log_file_action%7D%7D%0Aspace_left%20%3D%20%7B%7B.var_auditd_space_left%7D%7D%0Aspace_left_action%20%3D%20%7B%7B.var_auditd_space_left_action%7D%7D%0Averify_email%20%3D%20yes%0Aaction_mail_acct%20%3D%20%7B%7B.var_auditd_action_mail_acct%7D%7D%0Aadmin_space_left%20%3D%2050%0Aadmin_space_left_action%20%3D%20syslog%0Adisk_full_action%20%3D%20%7B%7B.var_auditd_disk_full_action%7D%7D%0Adisk_error_action%20%3D%20%7B%7B.var_auditd_disk_error_action%7D%7D%0Ause_libwrap%20%3D%20yes%0A%23%23tcp_listen_port%20%3D%2060%0Atcp_listen_queue%20%3D%205%0Atcp_max_per_addr%20%3D%201%0A%23%23tcp_client_ports%20%3D%201024-65535%0Atcp_client_max_idle%20%3D%200%0Atransport%20%3D%20TCP%0Akrb5_principal%20%3D%20auditd%0A%23%23krb5_key_file%20%3D%20/etc/audit/audit.key%0Adistribute_network%20%3D%20no%0Aq_depth%20%3D%20400%0Aoverflow_action%20%3D%20syslog%0Amax_restarts%20%3D%2010%0Aplugin_dir%20%3D%20/etc/audit/plugins.d }} + mode: 0640 + path: /etc/audit/auditd.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +var_auditd_space_left_action='' + + +# +# If space_left_action present in /etc/audit/auditd.conf, change value +# to var_auditd_space_left_action, else +# add "space_left_action = $var_auditd_space_left_action" to /etc/audit/auditd.conf +# + +AUDITCONFIG=/etc/audit/auditd.conf + +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "$AUDITCONFIG"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^space_left_action") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$var_auditd_space_left_action" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^space_left_action\\>" "$AUDITCONFIG"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^space_left_action\\>.*/$escaped_formatted_output/gi" "$AUDITCONFIG" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-80684-4" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "$AUDITCONFIG" >> "$AUDITCONFIG" + printf '%s\n' "$formatted_output" >> "$AUDITCONFIG" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Configure auditd space_left on Low Disk Space + The auditd service can be configured to take an action +when disk space is running low but prior to running out of space completely. +Edit the file /etc/audit/auditd.conf. Add or modify the following line, +substituting PERCENTAGE appropriately: +space_left = PERCENTAGE% +Set this value to at least 25 to cause the system to +notify the user of an issue. + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI04.04 + BAI08.02 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS05.04 + DSS05.07 + MEA02.01 + CCI-001855 + 4.2.3.10 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 6.1 + SR 7.1 + SR 7.2 + A.12.1.3 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.17.2.1 + AU-5(b) + AU-5(2) + AU-5(1) + AU-5(4) + CM-6(a) + DE.AE-3 + DE.AE-5 + PR.DS-4 + PR.PT-1 + RS.AN-1 + RS.AN-4 + Req-10.7 + SRG-OS-000343-GPOS-00134 + SRG-OS-000343-VMM-001240 + RHEL-08-030730 + SV-230483r744014_rule + Notifying administrators of an impending disk space problem may allow them to +take corrective action prior to any disruption. + CCE-86055-1 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-86055-1 + - DISA-STIG-RHEL-08-030730 + - NIST-800-53-AU-5(1) + - NIST-800-53-AU-5(2) + - NIST-800-53-AU-5(4) + - NIST-800-53-AU-5(b) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.7 + - auditd_data_retention_space_left_percentage + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy +- name: XCCDF Value var_auditd_space_left_percentage # promote to variable + set_fact: + var_auditd_space_left_percentage: !!str + tags: + - always + +- name: Configure auditd space_left on Low Disk Space + lineinfile: + dest: /etc/audit/auditd.conf + line: space_left = {{ var_auditd_space_left_percentage }}% + regexp: ^\s*space_left\s*=\s*.*$ + state: present + create: true + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86055-1 + - DISA-STIG-RHEL-08-030730 + - NIST-800-53-AU-5(1) + - NIST-800-53-AU-5(2) + - NIST-800-53-AU-5(4) + - NIST-800-53-AU-5(b) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.7 + - auditd_data_retention_space_left_percentage + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +var_auditd_space_left_percentage='' + + +grep -q "^space_left[[:space:]]*=.*$" /etc/audit/auditd.conf && \ + sed -i "s/^space_left[[:space:]]*=.*$/space_left = $var_auditd_space_left_percentage%/g" /etc/audit/auditd.conf || \ + echo "space_left = $var_auditd_space_left_percentage%" >> /etc/audit/auditd.conf + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Set number of records to cause an explicit flush to audit logs + To configure Audit daemon to issue an explicit flush to disk command +after writing records, set freq to +in /etc/audit/auditd.conf. + CM-6 + FAU_GEN.1 + SRG-OS-000051-GPOS-00024 + If option freq isn't set to , the flush to disk +may happen after higher number of records, increasing the danger +of audit loss. + CCE-82258-5 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-82258-5 + - NIST-800-53-CM-6 + - auditd_freq + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Set number of records to cause an explicit flush to audit logs + block: + + - name: Check for duplicate values + lineinfile: + path: /etc/audit/auditd.conf + create: false + regexp: (?i)^\s*freq\s*=\s* + state: absent + check_mode: true + changed_when: false + register: dupes + + - name: Deduplicate values from /etc/audit/auditd.conf + lineinfile: + path: /etc/audit/auditd.conf + create: false + regexp: (?i)^\s*freq\s*=\s* + state: absent + when: dupes.found is defined and dupes.found > 1 + + - name: Insert correct line to /etc/audit/auditd.conf + lineinfile: + path: /etc/audit/auditd.conf + create: true + regexp: (?i)^\s*freq\s*=\s* + line: freq = 50 + state: present + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82258-5 + - NIST-800-53-CM-6 + - auditd_freq + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,{{ %23%0A%23%20This%20file%20controls%20the%20configuration%20of%20the%20audit%20daemon%0A%23%0A%0Alocal_events%20%3D%20yes%0Awrite_logs%20%3D%20yes%0Alog_file%20%3D%20/var/log/audit/audit.log%0Alog_group%20%3D%20root%0Alog_format%20%3D%20ENRICHED%0Aflush%20%3D%20%7B%7B.var_auditd_flush%7D%7D%0Afreq%20%3D%2050%0Amax_log_file%20%3D%20%7B%7B.var_auditd_max_log_file%7D%7D%0Anum_logs%20%3D%20%7B%7B.var_auditd_num_logs%7D%7D%0Apriority_boost%20%3D%204%0Aname_format%20%3D%20hostname%0A%23%23name%20%3D%20mydomain%0Amax_log_file_action%20%3D%20%7B%7B.var_auditd_max_log_file_action%7D%7D%0Aspace_left%20%3D%20%7B%7B.var_auditd_space_left%7D%7D%0Aspace_left_action%20%3D%20%7B%7B.var_auditd_space_left_action%7D%7D%0Averify_email%20%3D%20yes%0Aaction_mail_acct%20%3D%20%7B%7B.var_auditd_action_mail_acct%7D%7D%0Aadmin_space_left%20%3D%2050%0Aadmin_space_left_action%20%3D%20syslog%0Adisk_full_action%20%3D%20%7B%7B.var_auditd_disk_full_action%7D%7D%0Adisk_error_action%20%3D%20%7B%7B.var_auditd_disk_error_action%7D%7D%0Ause_libwrap%20%3D%20yes%0A%23%23tcp_listen_port%20%3D%2060%0Atcp_listen_queue%20%3D%205%0Atcp_max_per_addr%20%3D%201%0A%23%23tcp_client_ports%20%3D%201024-65535%0Atcp_client_max_idle%20%3D%200%0Atransport%20%3D%20TCP%0Akrb5_principal%20%3D%20auditd%0A%23%23krb5_key_file%20%3D%20/etc/audit/audit.key%0Adistribute_network%20%3D%20no%0Aq_depth%20%3D%20400%0Aoverflow_action%20%3D%20syslog%0Amax_restarts%20%3D%2010%0Aplugin_dir%20%3D%20/etc/audit/plugins.d }} + mode: 0640 + path: /etc/audit/auditd.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +if [ -e "/etc/audit/auditd.conf" ] ; then + + LC_ALL=C sed -i "/^\s*freq\s*=\s*/Id" "/etc/audit/auditd.conf" +else + touch "/etc/audit/auditd.conf" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/audit/auditd.conf" + +cp "/etc/audit/auditd.conf" "/etc/audit/auditd.conf.bak" +# Insert at the end of the file +printf '%s\n' "freq = 50" >> "/etc/audit/auditd.conf" +# Clean up after ourselves. +rm "/etc/audit/auditd.conf.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Include Local Events in Audit Logs + To configure Audit daemon to include local events in Audit logs, set +local_events to yes in /etc/audit/auditd.conf. +This is the default setting. + CCI-000366 + CM-6 + FAU_GEN.1 + SRG-OS-000062-GPOS-00031 + SRG-OS-000480-GPOS-00227 + RHEL-08-030061 + SV-230393r627750_rule + If option local_events isn't set to yes only events from +network will be aggregated. + CCE-82233-8 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-82233-8 + - DISA-STIG-RHEL-08-030061 + - NIST-800-53-CM-6 + - auditd_local_events + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Include Local Events in Audit Logs + block: + + - name: Check for duplicate values + lineinfile: + path: /etc/audit/auditd.conf + create: false + regexp: (?i)^\s*local_events\s*=\s* + state: absent + check_mode: true + changed_when: false + register: dupes + + - name: Deduplicate values from /etc/audit/auditd.conf + lineinfile: + path: /etc/audit/auditd.conf + create: false + regexp: (?i)^\s*local_events\s*=\s* + state: absent + when: dupes.found is defined and dupes.found > 1 + + - name: Insert correct line to /etc/audit/auditd.conf + lineinfile: + path: /etc/audit/auditd.conf + create: true + regexp: (?i)^\s*local_events\s*=\s* + line: local_events = yes + state: present + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82233-8 + - DISA-STIG-RHEL-08-030061 + - NIST-800-53-CM-6 + - auditd_local_events + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,{{ %23%0A%23%20This%20file%20controls%20the%20configuration%20of%20the%20audit%20daemon%0A%23%0A%0Alocal_events%20%3D%20yes%0Awrite_logs%20%3D%20yes%0Alog_file%20%3D%20/var/log/audit/audit.log%0Alog_group%20%3D%20root%0Alog_format%20%3D%20ENRICHED%0Aflush%20%3D%20%7B%7B.var_auditd_flush%7D%7D%0Afreq%20%3D%2050%0Amax_log_file%20%3D%20%7B%7B.var_auditd_max_log_file%7D%7D%0Anum_logs%20%3D%20%7B%7B.var_auditd_num_logs%7D%7D%0Apriority_boost%20%3D%204%0Aname_format%20%3D%20hostname%0A%23%23name%20%3D%20mydomain%0Amax_log_file_action%20%3D%20%7B%7B.var_auditd_max_log_file_action%7D%7D%0Aspace_left%20%3D%20%7B%7B.var_auditd_space_left%7D%7D%0Aspace_left_action%20%3D%20%7B%7B.var_auditd_space_left_action%7D%7D%0Averify_email%20%3D%20yes%0Aaction_mail_acct%20%3D%20%7B%7B.var_auditd_action_mail_acct%7D%7D%0Aadmin_space_left%20%3D%2050%0Aadmin_space_left_action%20%3D%20syslog%0Adisk_full_action%20%3D%20%7B%7B.var_auditd_disk_full_action%7D%7D%0Adisk_error_action%20%3D%20%7B%7B.var_auditd_disk_error_action%7D%7D%0Ause_libwrap%20%3D%20yes%0A%23%23tcp_listen_port%20%3D%2060%0Atcp_listen_queue%20%3D%205%0Atcp_max_per_addr%20%3D%201%0A%23%23tcp_client_ports%20%3D%201024-65535%0Atcp_client_max_idle%20%3D%200%0Atransport%20%3D%20TCP%0Akrb5_principal%20%3D%20auditd%0A%23%23krb5_key_file%20%3D%20/etc/audit/audit.key%0Adistribute_network%20%3D%20no%0Aq_depth%20%3D%20400%0Aoverflow_action%20%3D%20syslog%0Amax_restarts%20%3D%2010%0Aplugin_dir%20%3D%20/etc/audit/plugins.d }} + mode: 0640 + path: /etc/audit/auditd.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +if [ -e "/etc/audit/auditd.conf" ] ; then + + LC_ALL=C sed -i "/^\s*local_events\s*=\s*/Id" "/etc/audit/auditd.conf" +else + touch "/etc/audit/auditd.conf" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/audit/auditd.conf" + +cp "/etc/audit/auditd.conf" "/etc/audit/auditd.conf.bak" +# Insert at the end of the file +printf '%s\n' "local_events = yes" >> "/etc/audit/auditd.conf" +# Clean up after ourselves. +rm "/etc/audit/auditd.conf.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Resolve information before writing to audit logs + To configure Audit daemon to resolve all uid, gid, syscall, +architecture, and socket address information before writing the +events to disk, set log_format to ENRICHED +in /etc/audit/auditd.conf. + CCI-000366 + CM-6 + AU-3 + FAU_GEN.1.2 + SRG-OS-000255-GPOS-00096 + SRG-OS-000480-GPOS-00227 + RHEL-08-030063 + SV-230395r627750_rule + If option log_format isn't set to ENRICHED, the +audit records will be stored in a format exactly as the kernel sends them. + CCE-82201-5 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-82201-5 + - DISA-STIG-RHEL-08-030063 + - NIST-800-53-AU-3 + - NIST-800-53-CM-6 + - auditd_log_format + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Resolve information before writing to audit logs + block: + + - name: Check for duplicate values + lineinfile: + path: /etc/audit/auditd.conf + create: false + regexp: (?i)^\s*log_format\s*=\s* + state: absent + check_mode: true + changed_when: false + register: dupes + + - name: Deduplicate values from /etc/audit/auditd.conf + lineinfile: + path: /etc/audit/auditd.conf + create: false + regexp: (?i)^\s*log_format\s*=\s* + state: absent + when: dupes.found is defined and dupes.found > 1 + + - name: Insert correct line to /etc/audit/auditd.conf + lineinfile: + path: /etc/audit/auditd.conf + create: true + regexp: (?i)^\s*log_format\s*=\s* + line: log_format = ENRICHED + state: present + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82201-5 + - DISA-STIG-RHEL-08-030063 + - NIST-800-53-AU-3 + - NIST-800-53-CM-6 + - auditd_log_format + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,{{ %23%0A%23%20This%20file%20controls%20the%20configuration%20of%20the%20audit%20daemon%0A%23%0A%0Alocal_events%20%3D%20yes%0Awrite_logs%20%3D%20yes%0Alog_file%20%3D%20/var/log/audit/audit.log%0Alog_group%20%3D%20root%0Alog_format%20%3D%20ENRICHED%0Aflush%20%3D%20%7B%7B.var_auditd_flush%7D%7D%0Afreq%20%3D%2050%0Amax_log_file%20%3D%20%7B%7B.var_auditd_max_log_file%7D%7D%0Anum_logs%20%3D%20%7B%7B.var_auditd_num_logs%7D%7D%0Apriority_boost%20%3D%204%0Aname_format%20%3D%20hostname%0A%23%23name%20%3D%20mydomain%0Amax_log_file_action%20%3D%20%7B%7B.var_auditd_max_log_file_action%7D%7D%0Aspace_left%20%3D%20%7B%7B.var_auditd_space_left%7D%7D%0Aspace_left_action%20%3D%20%7B%7B.var_auditd_space_left_action%7D%7D%0Averify_email%20%3D%20yes%0Aaction_mail_acct%20%3D%20%7B%7B.var_auditd_action_mail_acct%7D%7D%0Aadmin_space_left%20%3D%2050%0Aadmin_space_left_action%20%3D%20syslog%0Adisk_full_action%20%3D%20%7B%7B.var_auditd_disk_full_action%7D%7D%0Adisk_error_action%20%3D%20%7B%7B.var_auditd_disk_error_action%7D%7D%0Ause_libwrap%20%3D%20yes%0A%23%23tcp_listen_port%20%3D%2060%0Atcp_listen_queue%20%3D%205%0Atcp_max_per_addr%20%3D%201%0A%23%23tcp_client_ports%20%3D%201024-65535%0Atcp_client_max_idle%20%3D%200%0Atransport%20%3D%20TCP%0Akrb5_principal%20%3D%20auditd%0A%23%23krb5_key_file%20%3D%20/etc/audit/audit.key%0Adistribute_network%20%3D%20no%0Aq_depth%20%3D%20400%0Aoverflow_action%20%3D%20syslog%0Amax_restarts%20%3D%2010%0Aplugin_dir%20%3D%20/etc/audit/plugins.d }} + mode: 0640 + path: /etc/audit/auditd.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +if [ -e "/etc/audit/auditd.conf" ] ; then + + LC_ALL=C sed -i "/^\s*log_format\s*=\s*/Id" "/etc/audit/auditd.conf" +else + touch "/etc/audit/auditd.conf" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/audit/auditd.conf" + +cp "/etc/audit/auditd.conf" "/etc/audit/auditd.conf.bak" +# Insert at the end of the file +printf '%s\n' "log_format = ENRICHED" >> "/etc/audit/auditd.conf" +# Clean up after ourselves. +rm "/etc/audit/auditd.conf.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Set hostname as computer node name in audit logs + To configure Audit daemon to use value returned by gethostname +syscall as computer node name in the audit events, +set name_format to hostname +in /etc/audit/auditd.conf. + CCI-001851 + CM-6 + AU-3 + FAU_GEN.1.2 + SRG-OS-000039-GPOS-00017 + SRG-OS-000342-GPOS-00133 + SRG-OS-000479-GPOS-00224 + RHEL-08-030062 + SV-230394r627750_rule + If option name_format is left at its default value of +none, audit events from different computers may be hard +to distinguish. + CCE-82897-0 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-82897-0 + - DISA-STIG-RHEL-08-030062 + - NIST-800-53-AU-3 + - NIST-800-53-CM-6 + - auditd_name_format + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Set hostname as computer node name in audit logs + block: + + - name: Check for duplicate values + lineinfile: + path: /etc/audit/auditd.conf + create: false + regexp: (?i)^\s*name_format\s*=\s* + state: absent + check_mode: true + changed_when: false + register: dupes + + - name: Deduplicate values from /etc/audit/auditd.conf + lineinfile: + path: /etc/audit/auditd.conf + create: false + regexp: (?i)^\s*name_format\s*=\s* + state: absent + when: dupes.found is defined and dupes.found > 1 + + - name: Insert correct line to /etc/audit/auditd.conf + lineinfile: + path: /etc/audit/auditd.conf + create: true + regexp: (?i)^\s*name_format\s*=\s* + line: name_format = hostname + state: present + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82897-0 + - DISA-STIG-RHEL-08-030062 + - NIST-800-53-AU-3 + - NIST-800-53-CM-6 + - auditd_name_format + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,{{ %23%0A%23%20This%20file%20controls%20the%20configuration%20of%20the%20audit%20daemon%0A%23%0A%0Alocal_events%20%3D%20yes%0Awrite_logs%20%3D%20yes%0Alog_file%20%3D%20/var/log/audit/audit.log%0Alog_group%20%3D%20root%0Alog_format%20%3D%20ENRICHED%0Aflush%20%3D%20%7B%7B.var_auditd_flush%7D%7D%0Afreq%20%3D%2050%0Amax_log_file%20%3D%20%7B%7B.var_auditd_max_log_file%7D%7D%0Anum_logs%20%3D%20%7B%7B.var_auditd_num_logs%7D%7D%0Apriority_boost%20%3D%204%0Aname_format%20%3D%20hostname%0A%23%23name%20%3D%20mydomain%0Amax_log_file_action%20%3D%20%7B%7B.var_auditd_max_log_file_action%7D%7D%0Aspace_left%20%3D%20%7B%7B.var_auditd_space_left%7D%7D%0Aspace_left_action%20%3D%20%7B%7B.var_auditd_space_left_action%7D%7D%0Averify_email%20%3D%20yes%0Aaction_mail_acct%20%3D%20%7B%7B.var_auditd_action_mail_acct%7D%7D%0Aadmin_space_left%20%3D%2050%0Aadmin_space_left_action%20%3D%20syslog%0Adisk_full_action%20%3D%20%7B%7B.var_auditd_disk_full_action%7D%7D%0Adisk_error_action%20%3D%20%7B%7B.var_auditd_disk_error_action%7D%7D%0Ause_libwrap%20%3D%20yes%0A%23%23tcp_listen_port%20%3D%2060%0Atcp_listen_queue%20%3D%205%0Atcp_max_per_addr%20%3D%201%0A%23%23tcp_client_ports%20%3D%201024-65535%0Atcp_client_max_idle%20%3D%200%0Atransport%20%3D%20TCP%0Akrb5_principal%20%3D%20auditd%0A%23%23krb5_key_file%20%3D%20/etc/audit/audit.key%0Adistribute_network%20%3D%20no%0Aq_depth%20%3D%20400%0Aoverflow_action%20%3D%20syslog%0Amax_restarts%20%3D%2010%0Aplugin_dir%20%3D%20/etc/audit/plugins.d }} + mode: 0640 + path: /etc/audit/auditd.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +if [ -e "/etc/audit/auditd.conf" ] ; then + + LC_ALL=C sed -i "/^\s*name_format\s*=\s*/Id" "/etc/audit/auditd.conf" +else + touch "/etc/audit/auditd.conf" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/audit/auditd.conf" + +cp "/etc/audit/auditd.conf" "/etc/audit/auditd.conf.bak" +# Insert at the end of the file +printf '%s\n' "name_format = hostname" >> "/etc/audit/auditd.conf" +# Clean up after ourselves. +rm "/etc/audit/auditd.conf.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Appropriate Action Must be Setup When the Internal Audit Event Queue is Full + The audit system should have an action setup in the event the internal event queue becomes full. +To setup an overflow action edit /etc/audit/auditd.conf. Set overflow_action +to one of the following values: syslog, single, halt. + CCI-001851 + AU-4(1) + SRG-OS-000342-GPOS-00133 + SRG-OS-000479-GPOS-00224 + RHEL-08-030700 + SV-230480r627750_rule + The audit system should have an action setup in the event the internal event queue becomes full +so that no data is lost. + CCE-85889-4 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-85889-4 + - DISA-STIG-RHEL-08-030700 + - NIST-800-53-AU-4(1) + - auditd_overflow_action + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Appropriate Action Must be Setup When the Internal Audit Event Queue is Full + block: + + - name: Check for duplicate values + lineinfile: + path: /etc/audit/auditd.conf + create: false + regexp: (?i)^\s*overflow_action\s*=\s* + state: absent + check_mode: true + changed_when: false + register: dupes + + - name: Deduplicate values from /etc/audit/auditd.conf + lineinfile: + path: /etc/audit/auditd.conf + create: false + regexp: (?i)^\s*overflow_action\s*=\s* + state: absent + when: dupes.found is defined and dupes.found > 1 + + - name: Insert correct line to /etc/audit/auditd.conf + lineinfile: + path: /etc/audit/auditd.conf + create: true + regexp: (?i)^\s*overflow_action\s*=\s* + line: overflow_action = syslog + state: present + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-85889-4 + - DISA-STIG-RHEL-08-030700 + - NIST-800-53-AU-4(1) + - auditd_overflow_action + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +if [ -e "/etc/audit/auditd.conf" ] ; then + + LC_ALL=C sed -i "/^\s*overflow_action\s*=\s*/Id" "/etc/audit/auditd.conf" +else + touch "/etc/audit/auditd.conf" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/audit/auditd.conf" + +cp "/etc/audit/auditd.conf" "/etc/audit/auditd.conf.bak" +# Insert at the end of the file +printf '%s\n' "overflow_action = syslog" >> "/etc/audit/auditd.conf" +# Clean up after ourselves. +rm "/etc/audit/auditd.conf.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Write Audit Logs to the Disk + To configure Audit daemon to write Audit logs to the disk, set +write_logs to yes in /etc/audit/auditd.conf. +This is the default setting. + CM-6 + FAU_STG.1 + SRG-OS-000480-GPOS-00227 + If write_logs isn't set to yes, the Audit logs will +not be written to the disk. + CCE-82366-6 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-82366-6 + - NIST-800-53-CM-6 + - auditd_write_logs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Write Audit Logs to the Disk + block: + + - name: Check for duplicate values + lineinfile: + path: /etc/audit/auditd.conf + create: false + regexp: (?i)^\s*write_logs\s*=\s* + state: absent + check_mode: true + changed_when: false + register: dupes + + - name: Deduplicate values from /etc/audit/auditd.conf + lineinfile: + path: /etc/audit/auditd.conf + create: false + regexp: (?i)^\s*write_logs\s*=\s* + state: absent + when: dupes.found is defined and dupes.found > 1 + + - name: Insert correct line to /etc/audit/auditd.conf + lineinfile: + path: /etc/audit/auditd.conf + create: true + regexp: (?i)^\s*write_logs\s*=\s* + line: write_logs = yes + state: present + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82366-6 + - NIST-800-53-CM-6 + - auditd_write_logs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,{{ %23%0A%23%20This%20file%20controls%20the%20configuration%20of%20the%20audit%20daemon%0A%23%0A%0Alocal_events%20%3D%20yes%0Awrite_logs%20%3D%20yes%0Alog_file%20%3D%20/var/log/audit/audit.log%0Alog_group%20%3D%20root%0Alog_format%20%3D%20ENRICHED%0Aflush%20%3D%20%7B%7B.var_auditd_flush%7D%7D%0Afreq%20%3D%2050%0Amax_log_file%20%3D%20%7B%7B.var_auditd_max_log_file%7D%7D%0Anum_logs%20%3D%20%7B%7B.var_auditd_num_logs%7D%7D%0Apriority_boost%20%3D%204%0Aname_format%20%3D%20hostname%0A%23%23name%20%3D%20mydomain%0Amax_log_file_action%20%3D%20%7B%7B.var_auditd_max_log_file_action%7D%7D%0Aspace_left%20%3D%20%7B%7B.var_auditd_space_left%7D%7D%0Aspace_left_action%20%3D%20%7B%7B.var_auditd_space_left_action%7D%7D%0Averify_email%20%3D%20yes%0Aaction_mail_acct%20%3D%20%7B%7B.var_auditd_action_mail_acct%7D%7D%0Aadmin_space_left%20%3D%2050%0Aadmin_space_left_action%20%3D%20syslog%0Adisk_full_action%20%3D%20%7B%7B.var_auditd_disk_full_action%7D%7D%0Adisk_error_action%20%3D%20%7B%7B.var_auditd_disk_error_action%7D%7D%0Ause_libwrap%20%3D%20yes%0A%23%23tcp_listen_port%20%3D%2060%0Atcp_listen_queue%20%3D%205%0Atcp_max_per_addr%20%3D%201%0A%23%23tcp_client_ports%20%3D%201024-65535%0Atcp_client_max_idle%20%3D%200%0Atransport%20%3D%20TCP%0Akrb5_principal%20%3D%20auditd%0A%23%23krb5_key_file%20%3D%20/etc/audit/audit.key%0Adistribute_network%20%3D%20no%0Aq_depth%20%3D%20400%0Aoverflow_action%20%3D%20syslog%0Amax_restarts%20%3D%2010%0Aplugin_dir%20%3D%20/etc/audit/plugins.d }} + mode: 0640 + path: /etc/audit/auditd.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +if [ -e "/etc/audit/auditd.conf" ] ; then + + LC_ALL=C sed -i "/^\s*write_logs\s*=\s*/Id" "/etc/audit/auditd.conf" +else + touch "/etc/audit/auditd.conf" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/audit/auditd.conf" + +cp "/etc/audit/auditd.conf" "/etc/audit/auditd.conf.bak" +# Insert at the end of the file +printf '%s\n' "write_logs = yes" >> "/etc/audit/auditd.conf" +# Clean up after ourselves. +rm "/etc/audit/auditd.conf.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + System Accounting with auditd + The auditd program can perform comprehensive +monitoring of system activity. This section makes use of recommended +configuration settings for specific policies or use cases. +The rules in this section make use of rules defined in /usr/share/doc/audit-VERSION/rules. + + + Configure auditing of unsuccessful file accesses + Ensure that unsuccessful attempts to access a file are audited. + +The following rules configure audit as described above: +## Unsuccessful file access (any other opens) This has to go last. +-a always,exit -F arch=b32 -S open,openat,openat2,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-access +-a always,exit -F arch=b64 -S open,openat,openat2,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-access +-a always,exit -F arch=b32 -S open,openat,openat2,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-access +-a always,exit -F arch=b64 -S open,openat,openat2,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-access + +Load new Audit rules into kernel by running: +augenrules --load + +Note: This rule uses a special set of Audit rules to comply with OSPP 4.2.1. You may reuse this rule in different profiles. If you decide to do so, it is recommended that you inspect contents of the file closely and make sure that they are alligned with your needs. + 0582 + 0584 + 05885 + 0586 + 0846 + 0957 + AU-2(a) + FAU_GEN.1.1.c + SRG-OS-000458-GPOS-00203 + SRG-OS-000474-GPOS-00219 + SRG-OS-000475-GPOS-00220 + SRG-OS-000463-GPOS-00207 + SRG-OS-000465-GPOS-00209 + SRG-OS-000461-GPOS-00205 + Unsuccessful attempts to access a file might be signs of malicious activity happening within the system. Auditing of such activities helps in their monitoring and investigation. + CCE-82833-5 + - name: Put contents into /etc/audit/rules.d/30-ospp-v42-3-access-failed.rules according + to policy + copy: + dest: /etc/audit/rules.d/30-ospp-v42-3-access-failed.rules + content: | + ## Unsuccessful file access (any other opens) This has to go last. + -a always,exit -F arch=b32 -S open,openat,openat2,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-access + -a always,exit -F arch=b64 -S open,openat,openat2,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-access + -a always,exit -F arch=b32 -S open,openat,openat2,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-access + -a always,exit -F arch=b64 -S open,openat,openat2,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-access + force: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82833-5 + - NIST-800-53-AU-2(a) + - audit_access_failed + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Remove any permissions from other group + file: + path: /etc/audit/rules.d/30-ospp-v42-3-access-failed.rules + mode: o-rwx + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82833-5 + - NIST-800-53-AU-2(a) + - audit_access_failed + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,%23%23%20Unsuccessful%20file%20access%20%28any%20other%20opens%29%20This%20has%20to%20go%20last.%0A-a%20always%2Cexit%20-F%20arch%3Db32%20-S%20open%2Copenat%2Copen_by_handle_at%20-F%20exit%3D-EACCES%20-F%20auid%26gt%3B%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-access%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20open%2Copenat%2Copen_by_handle_at%20-F%20exit%3D-EACCES%20-F%20auid%26gt%3B%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-access%0A-a%20always%2Cexit%20-F%20arch%3Db32%20-S%20open%2Copenat%2Copen_by_handle_at%20-F%20exit%3D-EPERM%20-F%20auid%26gt%3B%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-access%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20open%2Copenat%2Copen_by_handle_at%20-F%20exit%3D-EPERM%20-F%20auid%26gt%3B%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-access + mode: 0600 + path: /etc/audit/rules.d/30-ospp-v42-3-access-failed.rules + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +cat << 'EOF' > /etc/audit/rules.d/30-ospp-v42-3-access-failed.rules +## Unsuccessful file access (any other opens) This has to go last. +-a always,exit -F arch=b32 -S open,openat,openat2,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-access +-a always,exit -F arch=b64 -S open,openat,openat2,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-access +-a always,exit -F arch=b32 -S open,openat,openat2,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-access +-a always,exit -F arch=b64 -S open,openat,openat2,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-access +EOF + +chmod o-rwx /etc/audit/rules.d/30-ospp-v42-3-access-failed.rules + +augenrules --load + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure auditing of successful file accesses + Ensure that successful attempts to access a file are audited. + +The following rules configure audit as described above: +## Successful file access (any other opens) This has to go last. +## These next two are likely to result in a whole lot of events +-a always,exit -F arch=b32 -S open,openat,openat2,open_by_handle_at -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-access +-a always,exit -F arch=b64 -S open,openat,openat2,open_by_handle_at -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-access + +Load new Audit rules into kernel by running: +augenrules --load + +Note: This rule uses a special set of Audit rules to comply with OSPP 4.2.1. You may reuse this rule in different profiles. If you decide to do so, it is recommended that you inspect contents of the file closely and make sure that they are alligned with your needs. + 0582 + 0584 + 05885 + 0586 + 0846 + 0957 + AU-2(a) + FAU_GEN.1.1.c + SRG-OS-000458-GPOS-00203 + SRG-OS-000474-GPOS-00219 + SRG-OS-000475-GPOS-00220 + SRG-OS-000463-GPOS-00207 + SRG-OS-000465-GPOS-00209 + SRG-OS-000461-GPOS-00205 + Auditing of successful attempts to access a file helps in investigation of activities performed on the system. + CCE-82834-3 + - name: Put contents into /etc/audit/rules.d/30-ospp-v42-3-access-success.rules according + to policy + copy: + dest: /etc/audit/rules.d/30-ospp-v42-3-access-success.rules + content: | + ## Successful file access (any other opens) This has to go last. + ## These next two are likely to result in a whole lot of events + -a always,exit -F arch=b32 -S open,openat,openat2,open_by_handle_at -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-access + -a always,exit -F arch=b64 -S open,openat,openat2,open_by_handle_at -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-access + force: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82834-3 + - NIST-800-53-AU-2(a) + - audit_access_success + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Remove any permissions from other group + file: + path: /etc/audit/rules.d/30-ospp-v42-3-access-success.rules + mode: o-rwx + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82834-3 + - NIST-800-53-AU-2(a) + - audit_access_success + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,%23%23%20Successful%20file%20access%20%28any%20other%20opens%29%20This%20has%20to%20go%20last.%0A%23%23%20These%20next%20two%20are%20likely%20to%20result%20in%20a%20whole%20lot%20of%20events%0A-a%20always%2Cexit%20-F%20arch%3Db32%20-S%20open%2Copenat%2Copen_by_handle_at%20-F%20success%3D1%20-F%20auid%26gt%3B%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dsuccessful-access%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20open%2Copenat%2Copen_by_handle_at%20-F%20success%3D1%20-F%20auid%26gt%3B%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dsuccessful-access + mode: 0600 + path: /etc/audit/rules.d/30-ospp-v42-3-access-success.rules + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +cat << 'EOF' > /etc/audit/rules.d/30-ospp-v42-3-access-success.rules +## Successful file access (any other opens) This has to go last. +## These next two are likely to result in a whole lot of events +-a always,exit -F arch=b32 -S open,openat,openat2,open_by_handle_at -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-access +-a always,exit -F arch=b64 -S open,openat,openat2,open_by_handle_at -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-access +EOF + +chmod o-rwx /etc/audit/rules.d/30-ospp-v42-3-access-success.rules + +augenrules --load + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure basic parameters of Audit system + Perform basic configuration of Audit system. +Make sure that any previously defined rules are cleared, the auditing system is configured to handle sudden bursts of events, and in cases of failure, messages are configured to be directed to system log. + +The following rules configure audit as described above: +## First rule - delete all +-D + +## Increase the buffers to survive stress events. +## Make this bigger for busy systems +-b 8192 + +## This determine how long to wait in burst of events +--backlog_wait_time 60000 + +## Set failure mode to syslog +-f 1 + +Load new Audit rules into kernel by running: +augenrules --load + It might happen that Audit buffer configured by this rule is not large enough for certain use cases. If that is the case, the buffer size can be overridden by placing -b larger_buffer_size into a file within /etc/audit/rules.d directory, replacing larger_file_size with the desired value. The file name should start with a number higher than 10 and lower than 99. + AU-2(a) + FAU_GEN.1 + SRG-OS-000365-GPOS-00152 + SRG-OS-000475-GPOS-00220 + Without basic configurations, audit may not perform as expected. It may not be able to correctly handle events under stressful conditions, or log events in case of failure. + CCE-82827-7 + - name: Put contents into /etc/audit/rules.d/10-base-config.rules according to policy + copy: + dest: /etc/audit/rules.d/10-base-config.rules + content: |+ + ## First rule - delete all + -D + + ## Increase the buffers to survive stress events. + ## Make this bigger for busy systems + -b 8192 + + ## This determine how long to wait in burst of events + --backlog_wait_time 60000 + + ## Set failure mode to syslog + -f 1 + + force: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82827-7 + - NIST-800-53-AU-2(a) + - audit_basic_configuration + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Remove any permissions from other group + file: + path: /etc/audit/rules.d/10-base-config.rules + mode: o-rwx + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82827-7 + - NIST-800-53-AU-2(a) + - audit_basic_configuration + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,%23%23%20First%20rule%20-%20delete%20all%0A-D%0A%0A%23%23%20Increase%20the%20buffers%20to%20survive%20stress%20events.%0A%23%23%20Make%20this%20bigger%20for%20busy%20systems%0A-b%208192%0A%0A%23%23%20This%20determine%20how%20long%20to%20wait%20in%20burst%20of%20events%0A--backlog_wait_time%2060000%0A%0A%23%23%20Set%20failure%20mode%20to%20syslog%0A-f%201%0A + mode: 0600 + path: /etc/audit/rules.d/10-base-config.rules + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +cat << 'EOF' > /etc/audit/rules.d/10-base-config.rules +## First rule - delete all +-D + +## Increase the buffers to survive stress events. +## Make this bigger for busy systems +-b 8192 + +## This determine how long to wait in burst of events +--backlog_wait_time 60000 + +## Set failure mode to syslog +-f 1 + +EOF + +chmod o-rwx /etc/audit/rules.d/10-base-config.rules + +augenrules --load + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure auditing of unsuccessful file creations + Ensure that unsuccessful attempts to create a file are audited. + +The following rules configure audit as described above: +## Unsuccessful file creation (open with O_CREAT) +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b32 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b64 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b32 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b64 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create + +Load new Audit rules into kernel by running: +augenrules --load + +Note: This rule uses a special set of Audit rules to comply with OSPP 4.2.1. You may reuse this rule in different profiles. If you decide to do so, it is recommended that you inspect contents of the file closely and make sure that they are alligned with your needs. + AU-2(a) + FAU_GEN.1.1.c + SRG-OS-000458-GPOS-00203 + SRG-OS-000474-GPOS-00219 + SRG-OS-000475-GPOS-00220 + SRG-OS-000463-GPOS-00207 + SRG-OS-000465-GPOS-00209 + SRG-OS-000461-GPOS-00205 + Unsuccessful file creations might be a sign of a malicious action being performed on the system. Keeping log of such events helps in monitoring and investigation of such actions. + CCE-82374-0 + - name: Put contents into /etc/audit/rules.d/30-ospp-v42-1-create-failed.rules according + to policy + copy: + dest: /etc/audit/rules.d/30-ospp-v42-1-create-failed.rules + content: | + ## Unsuccessful file creation (open with O_CREAT) + -a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create + -a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create + -a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create + -a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create + -a always,exit -F arch=b32 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create + -a always,exit -F arch=b64 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create + -a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create + -a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create + -a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create + -a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create + -a always,exit -F arch=b32 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create + -a always,exit -F arch=b64 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create + force: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82374-0 + - NIST-800-53-AU-2(a) + - audit_create_failed + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Remove any permissions from other group + file: + path: /etc/audit/rules.d/30-ospp-v42-1-create-failed.rules + mode: o-rwx + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82374-0 + - NIST-800-53-AU-2(a) + - audit_create_failed + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,%23%23%20Unsuccessful%20file%20creation%20%28open%20with%20O_CREAT%29%0A%23%23%20/etc/audit/rules.d/30-ospp-v42-1-create-failed.rules%0A-a%20always%2Cexit%20-F%20arch%3Db32%20-S%20openat%2Copen_by_handle_at%20-F%20a2%26amp%3B0100%20-F%20exit%3D-EACCES%20-F%20auid%26gt%3B%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-create%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20openat%2Copen_by_handle_at%20-F%20a2%26amp%3B0100%20-F%20exit%3D-EACCES%20-F%20auid%26gt%3B%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-create%0A-a%20always%2Cexit%20-F%20arch%3Db32%20-S%20open%20-F%20a1%26amp%3B0100%20-F%20exit%3D-EACCES%20-F%20auid%26gt%3B%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-create%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20open%20-F%20a1%26amp%3B0100%20-F%20exit%3D-EACCES%20-F%20auid%26gt%3B%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-create%0A-a%20always%2Cexit%20-F%20arch%3Db32%20-S%20creat%20-F%20exit%3D-EACCES%20-F%20auid%26gt%3B%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-create%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20creat%20-F%20exit%3D-EACCES%20-F%20auid%26gt%3B%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-create%0A-a%20always%2Cexit%20-F%20arch%3Db32%20-S%20openat%2Copen_by_handle_at%20-F%20a2%26amp%3B0100%20-F%20exit%3D-EPERM%20-F%20auid%26gt%3B%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-create%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20openat%2Copen_by_handle_at%20-F%20a2%26amp%3B0100%20-F%20exit%3D-EPERM%20-F%20auid%26gt%3B%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-create%0A-a%20always%2Cexit%20-F%20arch%3Db32%20-S%20open%20-F%20a1%26amp%3B0100%20-F%20exit%3D-EPERM%20-F%20auid%26gt%3B%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-create%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20open%20-F%20a1%26amp%3B0100%20-F%20exit%3D-EPERM%20-F%20auid%26gt%3B%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-create%0A-a%20always%2Cexit%20-F%20arch%3Db32%20-S%20creat%20-F%20exit%3D-EPERM%20-F%20auid%26gt%3B%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-create%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20creat%20-F%20exit%3D-EPERM%20-F%20auid%26gt%3B%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-create%0A + mode: 0600 + path: /etc/audit/rules.d/30-ospp-v42-1-create-failed.rules + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +cat << 'EOF' > /etc/audit/rules.d/30-ospp-v42-1-create-failed.rules +## Unsuccessful file creation (open with O_CREAT) +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b32 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b64 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b32 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b64 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +EOF + +chmod o-rwx /etc/audit/rules.d/30-ospp-v42-1-create-failed.rules + +augenrules --load + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure auditing of successful file creations + Ensure that successful attempts to create a file are audited. + +The following rules configure audit as described above: +## Successful file creation (open with O_CREAT) +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create +-a always,exit -F arch=b32 -S open -F a1&0100 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create +-a always,exit -F arch=b64 -S open -F a1&0100 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create +-a always,exit -F arch=b32 -S creat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create +-a always,exit -F arch=b64 -S creat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create + +Load new Audit rules into kernel by running: +augenrules --load + +Note: This rule uses a special set of Audit rules to comply with OSPP 4.2.1. You may reuse this rule in different profiles. If you decide to do so, it is recommended that you inspect contents of the file closely and make sure that they are alligned with your needs. + AU-2(a) + FAU_GEN.1.1.c + SRG-OS-000458-GPOS-00203 + SRG-OS-000474-GPOS-00219 + SRG-OS-000475-GPOS-00220 + SRG-OS-000463-GPOS-00207 + SRG-OS-000465-GPOS-00209 + SRG-OS-000461-GPOS-00205 + Auditing of successful attempts to create a file helps in investigation of actions which happened on the system. + CCE-82829-3 + - name: Put contents into /etc/audit/rules.d/30-ospp-v42-1-create-success.rules according + to policy + copy: + dest: /etc/audit/rules.d/30-ospp-v42-1-create-success.rules + content: | + ## Successful file creation (open with O_CREAT) + -a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create + -a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create + -a always,exit -F arch=b32 -S open -F a1&0100 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create + -a always,exit -F arch=b64 -S open -F a1&0100 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create + -a always,exit -F arch=b32 -S creat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create + -a always,exit -F arch=b64 -S creat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create + force: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82829-3 + - NIST-800-53-AU-2(a) + - audit_create_success + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Remove any permissions from other group + file: + path: /etc/audit/rules.d/30-ospp-v42-1-create-success.rules + mode: o-rwx + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82829-3 + - NIST-800-53-AU-2(a) + - audit_create_success + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +cat << 'EOF' > /etc/audit/rules.d/30-ospp-v42-1-create-success.rules +## Successful file creation (open with O_CREAT) +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create +-a always,exit -F arch=b32 -S open -F a1&0100 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create +-a always,exit -F arch=b64 -S open -F a1&0100 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create +-a always,exit -F arch=b32 -S creat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create +-a always,exit -F arch=b64 -S creat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create +EOF + +chmod o-rwx /etc/audit/rules.d/30-ospp-v42-1-create-success.rules + +augenrules --load + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure auditing of unsuccessful file deletions + Ensure that unsuccessful attempts to delete a file are audited. + +The following rules configure audit as described above: +## Unsuccessful file delete +-a always,exit -F arch=b32 -S unlink,unlinkat,rename,renameat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete +-a always,exit -F arch=b64 -S unlink,unlinkat,rename,renameat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete +-a always,exit -F arch=b32 -S unlink,unlinkat,rename,renameat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete +-a always,exit -F arch=b64 -S unlink,unlinkat,rename,renameat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete + +Load new Audit rules into kernel by running: +augenrules --load + +Note: This rule uses a special set of Audit rules to comply with OSPP 4.2.1. You may reuse this rule in different profiles. If you decide to do so, it is recommended that you inspect contents of the file closely and make sure that they are alligned with your needs. + AU-2(a) + FAU_GEN.1.1.c + SRG-OS-000458-GPOS-00203 + SRG-OS-000474-GPOS-00219 + SRG-OS-000475-GPOS-00220 + SRG-OS-000463-GPOS-00207 + SRG-OS-000465-GPOS-00209 + SRG-OS-000461-GPOS-00205 + SRG-OS-000468-GPOS-00212 + Unsuccessful attempts to delete a file might be signs of malicious activities. Auditing of such events help in monitoring and investigating of such activities. + CCE-82835-0 + - name: Put contents into /etc/audit/rules.d/30-ospp-v42-4-delete-failed.rules according + to policy + copy: + dest: /etc/audit/rules.d/30-ospp-v42-4-delete-failed.rules + content: | + ## Unsuccessful file delete + -a always,exit -F arch=b32 -S unlink,unlinkat,rename,renameat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete + -a always,exit -F arch=b64 -S unlink,unlinkat,rename,renameat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete + -a always,exit -F arch=b32 -S unlink,unlinkat,rename,renameat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete + -a always,exit -F arch=b64 -S unlink,unlinkat,rename,renameat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete + force: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82835-0 + - NIST-800-53-AU-2(a) + - audit_delete_failed + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Remove any permissions from other group + file: + path: /etc/audit/rules.d/30-ospp-v42-4-delete-failed.rules + mode: o-rwx + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82835-0 + - NIST-800-53-AU-2(a) + - audit_delete_failed + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,%23%23%20Unsuccessful%20file%20delete%0A-a%20always%2Cexit%20-F%20arch%3Db32%20-S%20unlink%2Cunlinkat%2Crename%2Crenameat%20-F%20exit%3D-EACCES%20-F%20auid%26gt%3B%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-delete%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20unlink%2Cunlinkat%2Crename%2Crenameat%20-F%20exit%3D-EACCES%20-F%20auid%26gt%3B%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-delete%0A-a%20always%2Cexit%20-F%20arch%3Db32%20-S%20unlink%2Cunlinkat%2Crename%2Crenameat%20-F%20exit%3D-EPERM%20-F%20auid%26gt%3B%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-delete%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20unlink%2Cunlinkat%2Crename%2Crenameat%20-F%20exit%3D-EPERM%20-F%20auid%26gt%3B%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-delete + mode: 0600 + path: /etc/audit/rules.d/30-ospp-v42-4-delete-failed.rules + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +cat << 'EOF' > /etc/audit/rules.d/30-ospp-v42-4-delete-failed.rules +## Unsuccessful file delete +-a always,exit -F arch=b32 -S unlink,unlinkat,rename,renameat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete +-a always,exit -F arch=b64 -S unlink,unlinkat,rename,renameat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete +-a always,exit -F arch=b32 -S unlink,unlinkat,rename,renameat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete +-a always,exit -F arch=b64 -S unlink,unlinkat,rename,renameat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete +EOF + +chmod o-rwx /etc/audit/rules.d/30-ospp-v42-4-delete-failed.rules + +augenrules --load + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure auditing of successful file deletions + Ensure that successful attempts to delete a file are audited. + +The following rules configure audit as described above: +## Successful file delete +-a always,exit -F arch=b32 -S unlink,unlinkat,rename,renameat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-delete +-a always,exit -F arch=b64 -S unlink,unlinkat,rename,renameat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-delete + +Load new Audit rules into kernel by running: +augenrules --load + +Note: This rule uses a special set of Audit rules to comply with OSPP 4.2.1. You may reuse this rule in different profiles. If you decide to do so, it is recommended that you inspect contents of the file closely and make sure that they are alligned with your needs. + AU-2(a) + FAU_GEN.1.1.c + SRG-OS-000458-GPOS-00203 + SRG-OS-000474-GPOS-00219 + SRG-OS-000475-GPOS-00220 + SRG-OS-000463-GPOS-00207 + SRG-OS-000465-GPOS-00209 + SRG-OS-000461-GPOS-00205 + SRG-OS-000468-GPOS-00212 + Auditing of successful attempts to delete a file may help in monitoring and investigation of activities performed on the system. + CCE-82836-8 + - name: Put contents into /etc/audit/rules.d/30-ospp-v42-4-delete-success.rules according + to policy + copy: + dest: /etc/audit/rules.d/30-ospp-v42-4-delete-success.rules + content: | + ## Successful file delete + -a always,exit -F arch=b32 -S unlink,unlinkat,rename,renameat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-delete + -a always,exit -F arch=b64 -S unlink,unlinkat,rename,renameat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-delete + force: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82836-8 + - NIST-800-53-AU-2(a) + - audit_delete_success + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Remove any permissions from other group + file: + path: /etc/audit/rules.d/30-ospp-v42-4-delete-success.rules + mode: o-rwx + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82836-8 + - NIST-800-53-AU-2(a) + - audit_delete_success + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + --- + +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,{{ %23%23%20Successful%20file%20delete%0A-a%20always%2Cexit%20-F%20arch%3Db32%20-S%20unlink%2Cunlinkat%2Crename%2Crenameat%20-F%20success%3D1%20-F%20auid%26gt%3B%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dsuccessful-delete%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20unlink%2Cunlinkat%2Crename%2Crenameat%20-F%20success%3D1%20-F%20auid%26gt%3B%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dsuccessful-delete }} + mode: 0600 + path: /etc/audit/rules.d/30-ospp-v42-4-delete-success.rules + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +cat << 'EOF' > /etc/audit/rules.d/30-ospp-v42-4-delete-success.rules +## Successful file delete +-a always,exit -F arch=b32 -S unlink,unlinkat,rename,renameat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-delete +-a always,exit -F arch=b64 -S unlink,unlinkat,rename,renameat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-delete +EOF + +chmod o-rwx /etc/audit/rules.d/30-ospp-v42-4-delete-success.rules + +augenrules --load + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure immutable Audit login UIDs + Configure kernel to prevent modification of login UIDs once they are set. +Changing login UIDs while this configuration is enforced requires special capabilities which +are not available to unprivileged users. + +The following rules configure audit as described above: +## Make the loginuid immutable. This prevents tampering with the auid. +--loginuid-immutable + +Load new Audit rules into kernel by running: +augenrules --load + CCI-000162 + CCI-000163 + CCI-000164 + AU-2(a) + FAU_GEN.1.2 + SRG-OS-000462-GPOS-00206 + SRG-OS-000475-GPOS-00220 + SRG-OS-000057-GPOS-00027 + SRG-OS-000058-GPOS-00028 + SRG-OS-000059-GPOS-00029 + RHEL-08-030122 + SV-230403r627750_rule + If modification of login UIDs is not prevented, they can be changed by unprivileged users and +make auditing complicated or impossible. + CCE-82828-5 + - name: Put contents into /etc/audit/rules.d/11-loginuid.rules according to policy + copy: + dest: /etc/audit/rules.d/11-loginuid.rules + content: |+ + ## Make the loginuid immutable. This prevents tampering with the auid. + --loginuid-immutable + + force: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82828-5 + - DISA-STIG-RHEL-08-030122 + - NIST-800-53-AU-2(a) + - audit_immutable_login_uids + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Remove any permissions from other group + file: + path: /etc/audit/rules.d/11-loginuid.rules + mode: o-rwx + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82828-5 + - DISA-STIG-RHEL-08-030122 + - NIST-800-53-AU-2(a) + - audit_immutable_login_uids + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,%23%23%20Make%20the%20loginuid%20immutable.%20This%20prevents%20tampering%20with%20the%20auid.%0A--loginuid-immutable + mode: 0600 + path: /etc/audit/rules.d/11-loginuid.rules + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +cat << 'EOF' > /etc/audit/rules.d/11-loginuid.rules +## Make the loginuid immutable. This prevents tampering with the auid. +--loginuid-immutable + +EOF + +chmod o-rwx /etc/audit/rules.d/11-loginuid.rules + +augenrules --load + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure auditing of unsuccessful file modifications + Ensure that unsuccessful attempts to modify a file are audited. + +The following rules configure audit as described above: +## Unsuccessful file modifications (open for write or truncate) +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b32 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b32 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification + +Load new Audit rules into kernel by running: +augenrules --load + +Note: This rule uses a special set of Audit rules to comply with OSPP 4.2.1. You may reuse this rule in different profiles. If you decide to do so, it is recommended that you inspect contents of the file closely and make sure that they are alligned with your needs. + AU-2(a) + FAU_GEN.1.1.c + SRG-OS-000458-GPOS-00203 + SRG-OS-000474-GPOS-00219 + SRG-OS-000475-GPOS-00220 + SRG-OS-000463-GPOS-00207 + SRG-OS-000465-GPOS-00209 + SRG-OS-000461-GPOS-00205 + Unsuccessful file modifications might be a sign of a malicious action being performed on the system. Auditing of such events helps in detection and investigation of such actions. + CCE-82830-1 + - name: Put contents into /etc/audit/rules.d/30-ospp-v42-2-modify-failed.rules according + to policy + copy: + dest: /etc/audit/rules.d/30-ospp-v42-2-modify-failed.rules + content: | + ## Unsuccessful file modifications (open for write or truncate) + -a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification + -a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification + -a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification + -a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification + -a always,exit -F arch=b32 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification + -a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification + -a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification + -a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification + -a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification + -a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification + -a always,exit -F arch=b32 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification + -a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification + force: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82830-1 + - NIST-800-53-AU-2(a) + - audit_modify_failed + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Remove any permissions from other group + file: + path: /etc/audit/rules.d/30-ospp-v42-2-modify-failed.rules + mode: o-rwx + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82830-1 + - NIST-800-53-AU-2(a) + - audit_modify_failed + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,%23%23%20Unsuccessful%20file%20modifications%20%28open%20for%20write%20or%20truncate%29%0A-a%20always%2Cexit%20-F%20arch%3Db32%20-S%20openat%2Copen_by_handle_at%20-F%20a2%2601003%20-F%20exit%3D-EACCES%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-modification%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20openat%2Copen_by_handle_at%20-F%20a2%2601003%20-F%20exit%3D-EACCES%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-modification%0A-a%20always%2Cexit%20-F%20arch%3Db32%20-S%20open%20-F%20a1%2601003%20-F%20exit%3D-EACCES%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-modification%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20open%20-F%20a1%2601003%20-F%20exit%3D-EACCES%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-modification%0A-a%20always%2Cexit%20-F%20arch%3Db32%20-S%20truncate%2Cftruncate%20-F%20exit%3D-EACCES%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-modification%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20truncate%2Cftruncate%20-F%20exit%3D-EACCES%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-modification%0A-a%20always%2Cexit%20-F%20arch%3Db32%20-S%20openat%2Copen_by_handle_at%20-F%20a2%2601003%20-F%20exit%3D-EPERM%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-modification%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20openat%2Copen_by_handle_at%20-F%20a2%2601003%20-F%20exit%3D-EPERM%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-modification%0A-a%20always%2Cexit%20-F%20arch%3Db32%20-S%20open%20-F%20a1%2601003%20-F%20exit%3D-EPERM%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-modification%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20open%20-F%20a1%2601003%20-F%20exit%3D-EPERM%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-modification%0A-a%20always%2Cexit%20-F%20arch%3Db32%20-S%20truncate%2Cftruncate%20-F%20exit%3D-EPERM%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-modification%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20truncate%2Cftruncate%20-F%20exit%3D-EPERM%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-modification%0A + mode: 0600 + path: /etc/audit/rules.d/30-ospp-v42-2-modify-failed.rules + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +cat << 'EOF' > /etc/audit/rules.d/30-ospp-v42-2-modify-failed.rules +## Unsuccessful file modifications (open for write or truncate) +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b32 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b32 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +EOF + +chmod o-rwx /etc/audit/rules.d/30-ospp-v42-2-modify-failed.rules + +augenrules --load + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure auditing of successful file modifications + Ensure that successful attempts to modify a file are audited. + +The following rules configure audit as described above: +## Successful file modifications (open for write or truncate) +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification +-a always,exit -F arch=b32 -S open -F a1&01003 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification +-a always,exit -F arch=b64 -S open -F a1&01003 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification +-a always,exit -F arch=b32 -S truncate,ftruncate -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification +-a always,exit -F arch=b64 -S truncate,ftruncate -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification + +Load new Audit rules into kernel by running: +augenrules --load + +Note: This rule uses a special set of Audit rules to comply with OSPP 4.2.1. You may reuse this rule in different profiles. If you decide to do so, it is recommended that you inspect contents of the file closely and make sure that they are alligned with your needs. + AU-2(a) + FAU_GEN.1.1.c + SRG-OS-000458-GPOS-00203 + SRG-OS-000474-GPOS-00219 + SRG-OS-000475-GPOS-00220 + SRG-OS-000463-GPOS-00207 + SRG-OS-000465-GPOS-00209 + SRG-OS-000461-GPOS-00205 + Auditing of successful attempts to modify a file helps in investigation of actions which happened on the system. + CCE-82832-7 + - name: Put contents into /etc/audit/rules.d/30-ospp-v42-2-modify-success.rules according + to policy + copy: + dest: /etc/audit/rules.d/30-ospp-v42-2-modify-success.rules + content: | + ## Successful file modifications (open for write or truncate) + -a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification + -a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification + -a always,exit -F arch=b32 -S open -F a1&01003 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification + -a always,exit -F arch=b64 -S open -F a1&01003 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification + -a always,exit -F arch=b32 -S truncate,ftruncate -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification + -a always,exit -F arch=b64 -S truncate,ftruncate -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification + force: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82832-7 + - NIST-800-53-AU-2(a) + - audit_modify_success + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Remove any permissions from other group + file: + path: /etc/audit/rules.d/30-ospp-v42-2-modify-success.rules + mode: o-rwx + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82832-7 + - NIST-800-53-AU-2(a) + - audit_modify_success + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,%23%23%20Successful%20file%20modifications%20%28open%20for%20write%20or%20truncate%29%0A-a%20always%2Cexit%20-F%20arch%3Db32%20-S%20openat%2Copen_by_handle_at%20-F%20a2%26amp%3B01003%20-F%20success%3D1%20-F%20auid%26gt%3B%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dsuccessful-modification%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20openat%2Copen_by_handle_at%20-F%20a2%26amp%3B01003%20-F%20success%3D1%20-F%20auid%26gt%3B%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dsuccessful-modification%0A-a%20always%2Cexit%20-F%20arch%3Db32%20-S%20open%20-F%20a1%26amp%3B01003%20-F%20success%3D1%20-F%20auid%26gt%3B%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dsuccessful-modification%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20open%20-F%20a1%26amp%3B01003%20-F%20success%3D1%20-F%20auid%26gt%3B%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dsuccessful-modification%0A-a%20always%2Cexit%20-F%20arch%3Db32%20-S%20truncate%2Cftruncate%20-F%20success%3D1%20-F%20auid%26gt%3B%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dsuccessful-modification%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20truncate%2Cftruncate%20-F%20success%3D1%20-F%20auid%26gt%3B%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dsuccessful-modification%0A + mode: 0600 + path: /etc/audit/rules.d/30-ospp-v42-2-modify-success.rules + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +cat << 'EOF' > /etc/audit/rules.d/30-ospp-v42-2-modify-success.rules +## Successful file modifications (open for write or truncate) +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification +-a always,exit -F arch=b32 -S open -F a1&01003 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification +-a always,exit -F arch=b64 -S open -F a1&01003 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification +-a always,exit -F arch=b32 -S truncate,ftruncate -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification +-a always,exit -F arch=b64 -S truncate,ftruncate -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification +EOF + +chmod o-rwx /etc/audit/rules.d/30-ospp-v42-2-modify-success.rules + +augenrules --load + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure auditing of loading and unloading of kernel modules + Ensure that loading and unloading of kernel modules is audited. + +The following rules configure audit as described above: +## These rules watch for kernel module insertion. By monitoring +## the syscall, we do not need any watches on programs. +-a always,exit -F arch=b32 -S init_module,finit_module -F key=module-load +-a always,exit -F arch=b64 -S init_module,finit_module -F key=module-load +-a always,exit -F arch=b32 -S delete_module -F key=module-unload +-a always,exit -F arch=b64 -S delete_module -F key=module-unload + +Load new Audit rules into kernel by running: +augenrules --load + AU-2(a) + FAU_GEN.1.1.c + SRG-OS-000471-GPOS-00216 + SRG-OS-000477-GPOS-00222 + SRG-OS-000475-GPOS-00220 + Loading of a malicious kernel module introduces a risk to the system, as the module has access to sensitive data and perform actions at the operating system kernel level. Having such events audited helps in monitoring and investigating of malicious activities. + CCE-82838-4 + - name: Put contents into /etc/audit/rules.d/43-module-load.rules according to policy + copy: + dest: /etc/audit/rules.d/43-module-load.rules + content: | + ## These rules watch for kernel module insertion. By monitoring + ## the syscall, we do not need any watches on programs. + -a always,exit -F arch=b32 -S init_module,finit_module -F key=module-load + -a always,exit -F arch=b64 -S init_module,finit_module -F key=module-load + -a always,exit -F arch=b32 -S delete_module -F key=module-unload + -a always,exit -F arch=b64 -S delete_module -F key=module-unload + force: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82838-4 + - NIST-800-53-AU-2(a) + - audit_module_load + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Remove any permissions from other group + file: + path: /etc/audit/rules.d/43-module-load.rules + mode: o-rwx + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82838-4 + - NIST-800-53-AU-2(a) + - audit_module_load + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,%23%23%20These%20rules%20watch%20for%20kernel%20module%20insertion.%20By%20monitoring%0A%23%23%20the%20syscall%2C%20we%20do%20not%20need%20any%20watches%20on%20programs.%0A-a%20always%2Cexit%20-F%20arch%3Db32%20-S%20init_module%2Cfinit_module%20-F%20key%3Dmodule-load%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20init_module%2Cfinit_module%20-F%20key%3Dmodule-load%0A-a%20always%2Cexit%20-F%20arch%3Db32%20-S%20delete_module%20-F%20key%3Dmodule-unload%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20delete_module%20-F%20key%3Dmodule-unload%0A + mode: 0600 + path: /etc/audit/rules.d/43-module-load.rules + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +cat << 'EOF' > /etc/audit/rules.d/43-module-load.rules +## These rules watch for kernel module insertion. By monitoring +## the syscall, we do not need any watches on programs. +-a always,exit -F arch=b32 -S init_module,finit_module -F key=module-load +-a always,exit -F arch=b64 -S init_module,finit_module -F key=module-load +-a always,exit -F arch=b32 -S delete_module -F key=module-unload +-a always,exit -F arch=b64 -S delete_module -F key=module-unload +EOF + +chmod o-rwx /etc/audit/rules.d/43-module-load.rules + +augenrules --load + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Perform general configuration of Audit for OSPP + Configure some basic Audit parameters specific for OSPP profile. +In particular, configure Audit to watch for direct modification of files storing system user and group information, and usage of applications with special rights which can change system configuration. +Further audited events include access to audit log it self, attempts to Alter Process and Session Initiation Information, and attempts to modify MAC controls. + +The following rules configure audit as described above: +## The purpose of these rules is to meet the requirements for Operating +## System Protection Profile (OSPP)v4.2. These rules depends on having +## the following rule files copied to /etc/audit/rules.d: +## +## 10-base-config.rules, 11-loginuid.rules, +## 30-ospp-v42-1-create-failed.rules, 30-ospp-v42-1-create-success.rules, +## 30-ospp-v42-2-modify-failed.rules, 30-ospp-v42-2-modify-success.rules, +## 30-ospp-v42-3-access-failed.rules, 30-ospp-v42-3-access-success.rules, +## 30-ospp-v42-4-delete-failed.rules, 30-ospp-v42-4-delete-success.rules, +## 30-ospp-v42-5-perm-change-failed.rules, +## 30-ospp-v42-5-perm-change-success.rules, +## 30-ospp-v42-6-owner-change-failed.rules, +## 30-ospp-v42-6-owner-change-success.rules +## +## original copies may be found in /usr/share/audit/sample-rules/ + + +## User add delete modify. This is covered by pam. However, someone could +## open a file and directly create or modify a user, so we'll watch passwd and +## shadow for writes +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b32 -S open -F a1&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b64 -S open -F a1&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b32 -S open -F a1&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b64 -S open -F a1&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify + +## User enable and disable. This is entirely handled by pam. + +## Group add delete modify. This is covered by pam. However, someone could +## open a file and directly create or modify a user, so we'll watch group and +## gshadow for writes +-a always,exit -F path=/etc/passwd -F perm=wa -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F path=/etc/shadow -F perm=wa -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F path=/etc/group -F perm=wa -F auid>=1000 -F auid!=unset -F key=group-modify +-a always,exit -F path=/etc/gshadow -F perm=wa -F auid>=1000 -F auid!=unset -F key=group-modify + + +## Use of special rights for config changes. This would be use of setuid +## programs that relate to user accts. This is not all setuid apps because +## requirements are only for ones that affect system configuration. +-a always,exit -F path=/usr/sbin/unix_chkpwd -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/sbin/usernetctl -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/sbin/userhelper -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/sbin/seunshare -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/mount -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/newgrp -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/newuidmap -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/gpasswd -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/newgidmap -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/umount -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/passwd -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/crontab -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/at -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes + +## Privilege escalation via su or sudo. This is entirely handled by pam. + +## Watch for configuration changes to privilege escalation. +-a always,exit -F path=/etc/sudoers -F perm=wa -F key=special-config-changes +-a always,exit -F dir=/etc/sudoers.d/ -F perm=wa -F key=special-config-changes + +## Audit log access +-a always,exit -F dir=/var/log/audit/ -F perm=r -F auid>=1000 -F auid!=unset -F key=access-audit-trail +## Attempts to Alter Process and Session Initiation Information +-a always,exit -F path=/var/run/utmp -F perm=wa -F auid>=1000 -F auid!=unset -F key=session +-a always,exit -F path=/var/log/btmp -F perm=wa -F auid>=1000 -F auid!=unset -F key=session +-a always,exit -F path=/var/log/wtmp -F perm=wa -F auid>=1000 -F auid!=unset -F key=session + +## Attempts to modify MAC controls +-a always,exit -F dir=/etc/selinux/ -F perm=wa -F auid>=1000 -F auid!=unset -F key=MAC-policy + +## Software updates. This is entirely handled by rpm. + +## System start and shutdown. This is entirely handled by systemd + +## Kernel Module loading. This is handled in 43-module-load.rules + +## Application invocation. The requirements list an user requirement +## FPT_SRP_EXT.1 Software Restriction Policies. This event is intended to +## state results from that policy. This would be handled entirely by +## that daemon. + +Load new Audit rules into kernel by running: +augenrules --load + +Note: This rule uses a special set of Audit rules to comply with OSPP 4.2.1. You may reuse this rule in different profiles. If you decide to do so, it is recommended that you inspect contents of the file closely and make sure that they are alligned with your needs. + AU-2(a) + FAU_GEN.1.1.c + SRG-OS-000004-GPOS-00004 + SRG-OS-000241-GPOS-00091 + SRG-OS-000476-GPOS-00221 + SRG-OS-000327-GPOS-00127 + SRG-OS-000475-GPOS-00220 + SRG-OS-000239-GPOS-00089 + SRG-OS-000274-GPOS-00104 + SRG-OS-000275-GPOS-00105 + SRG-OS-000303-GPOS-00120 + SRG-OS-000304-GPOS-00121 + Auditing of events listed in the description provides data for monitoring and investigation of potentially malicious events e.g. tampering with Audit logs, malicious access to files storing information about system users and groups etc. + CCE-82373-2 + - name: Put contents into /etc/audit/rules.d/30-ospp-v42.rules according to policy + copy: + dest: /etc/audit/rules.d/30-ospp-v42.rules + content: |+ + ## The purpose of these rules is to meet the requirements for Operating + ## System Protection Profile (OSPP)v4.2. These rules depends on having + ## the following rule files copied to /etc/audit/rules.d: + ## + ## 10-base-config.rules, 11-loginuid.rules, + ## 30-ospp-v42-1-create-failed.rules, 30-ospp-v42-1-create-success.rules, + ## 30-ospp-v42-2-modify-failed.rules, 30-ospp-v42-2-modify-success.rules, + ## 30-ospp-v42-3-access-failed.rules, 30-ospp-v42-3-access-success.rules, + ## 30-ospp-v42-4-delete-failed.rules, 30-ospp-v42-4-delete-success.rules, + ## 30-ospp-v42-5-perm-change-failed.rules, + ## 30-ospp-v42-5-perm-change-success.rules, + ## 30-ospp-v42-6-owner-change-failed.rules, + ## 30-ospp-v42-6-owner-change-success.rules + ## + ## original copies may be found in /usr/share/audit/sample-rules/ + + + ## User add delete modify. This is covered by pam. However, someone could + ## open a file and directly create or modify a user, so we'll watch passwd and + ## shadow for writes + -a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=user-modify + -a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=user-modify + -a always,exit -F arch=b32 -S open -F a1&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=user-modify + -a always,exit -F arch=b64 -S open -F a1&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=user-modify + -a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify + -a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify + -a always,exit -F arch=b32 -S open -F a1&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify + -a always,exit -F arch=b64 -S open -F a1&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify + + ## User enable and disable. This is entirely handled by pam. + + ## Group add delete modify. This is covered by pam. However, someone could + ## open a file and directly create or modify a user, so we'll watch group and + ## gshadow for writes + -a always,exit -F path=/etc/passwd -F perm=wa -F auid>=1000 -F auid!=unset -F key=user-modify + -a always,exit -F path=/etc/shadow -F perm=wa -F auid>=1000 -F auid!=unset -F key=user-modify + -a always,exit -F path=/etc/group -F perm=wa -F auid>=1000 -F auid!=unset -F key=group-modify + -a always,exit -F path=/etc/gshadow -F perm=wa -F auid>=1000 -F auid!=unset -F key=group-modify + + + ## Use of special rights for config changes. This would be use of setuid + ## programs that relate to user accts. This is not all setuid apps because + ## requirements are only for ones that affect system configuration. + -a always,exit -F path=/usr/sbin/unix_chkpwd -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes + -a always,exit -F path=/usr/sbin/usernetctl -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes + -a always,exit -F path=/usr/sbin/userhelper -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes + -a always,exit -F path=/usr/sbin/seunshare -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes + -a always,exit -F path=/usr/bin/mount -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes + -a always,exit -F path=/usr/bin/newgrp -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes + -a always,exit -F path=/usr/bin/newuidmap -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes + -a always,exit -F path=/usr/bin/gpasswd -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes + -a always,exit -F path=/usr/bin/newgidmap -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes + -a always,exit -F path=/usr/bin/umount -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes + -a always,exit -F path=/usr/bin/passwd -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes + -a always,exit -F path=/usr/bin/crontab -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes + -a always,exit -F path=/usr/bin/at -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes + + ## Privilege escalation via su or sudo. This is entirely handled by pam. + + ## Watch for configuration changes to privilege escalation. + -a always,exit -F path=/etc/sudoers -F perm=wa -F key=special-config-changes + -a always,exit -F dir=/etc/sudoers.d/ -F perm=wa -F key=special-config-changes + + ## Audit log access + -a always,exit -F dir=/var/log/audit/ -F perm=r -F auid>=1000 -F auid!=unset -F key=access-audit-trail + ## Attempts to Alter Process and Session Initiation Information + -a always,exit -F path=/var/run/utmp -F perm=wa -F auid>=1000 -F auid!=unset -F key=session + -a always,exit -F path=/var/log/btmp -F perm=wa -F auid>=1000 -F auid!=unset -F key=session + -a always,exit -F path=/var/log/wtmp -F perm=wa -F auid>=1000 -F auid!=unset -F key=session + + ## Attempts to modify MAC controls + -a always,exit -F dir=/etc/selinux/ -F perm=wa -F auid>=1000 -F auid!=unset -F key=MAC-policy + + ## Software updates. This is entirely handled by rpm. + + ## System start and shutdown. This is entirely handled by systemd + + ## Kernel Module loading. This is handled in 43-module-load.rules + + ## Application invocation. The requirements list an user requirement + ## FPT_SRP_EXT.1 Software Restriction Policies. This event is intended to + ## state results from that policy. This would be handled entirely by + ## that daemon. + + force: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82373-2 + - NIST-800-53-AU-2(a) + - audit_ospp_general + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Remove any permissions from other group + file: + path: /etc/audit/rules.d/30-ospp-v42.rules + mode: o-rwx + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82373-2 + - NIST-800-53-AU-2(a) + - audit_ospp_general + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,%23%23%20The%20purpose%20of%20these%20rules%20is%20to%20meet%20the%20requirements%20for%20Operating%0A%23%23%20System%20Protection%20Profile%20%28OSPP%29v4.2.%20These%20rules%20depends%20on%20having%0A%23%23%20the%20following%20rule%20files%20copied%20to%20/etc/audit/rules.d%3A%0A%23%23%0A%23%23%2010-base-config.rules%2C%2011-loginuid.rules%2C%0A%23%23%2030-ospp-v42-1-create-failed.rules%2C%2030-ospp-v42-1-create-success.rules%2C%0A%23%23%2030-ospp-v42-2-modify-failed.rules%2C%2030-ospp-v42-2-modify-success.rules%2C%0A%23%23%2030-ospp-v42-3-access-failed.rules%2C%2030-ospp-v42-3-access-success.rules%2C%0A%23%23%2030-ospp-v42-4-delete-failed.rules%2C%2030-ospp-v42-4-delete-success.rules%2C%0A%23%23%2030-ospp-v42-5-perm-change-failed.rules%2C%0A%23%23%2030-ospp-v42-5-perm-change-success.rules%2C%0A%23%23%2030-ospp-v42-6-owner-change-failed.rules%2C%0A%23%23%2030-ospp-v42-6-owner-change-success.rules%0A%23%23%0A%23%23%20original%20copies%20may%20be%20found%20in%20/usr/share/audit/sample-rules/%0A%0A%0A%23%23%20User%20add%20delete%20modify.%20This%20is%20covered%20by%20pam.%20However%2C%20someone%20could%0A%23%23%20open%20a%20file%20and%20directly%20create%20or%20modify%20a%20user%2C%20so%20we%27ll%20watch%20passwd%20and%0A%23%23%20shadow%20for%20writes%0A-a%20always%2Cexit%20-F%20arch%3Db32%20-S%20openat%2Copen_by_handle_at%20-F%20a2%2603%20-F%20path%3D/etc/passwd%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Duser-modify%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20openat%2Copen_by_handle_at%20-F%20a2%2603%20-F%20path%3D/etc/passwd%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Duser-modify%0A-a%20always%2Cexit%20-F%20arch%3Db32%20-S%20open%20-F%20a1%2603%20-F%20path%3D/etc/passwd%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Duser-modify%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20open%20-F%20a1%2603%20-F%20path%3D/etc/passwd%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Duser-modify%0A-a%20always%2Cexit%20-F%20arch%3Db32%20-S%20openat%2Copen_by_handle_at%20-F%20a2%2603%20-F%20path%3D/etc/shadow%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Duser-modify%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20openat%2Copen_by_handle_at%20-F%20a2%2603%20-F%20path%3D/etc/shadow%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Duser-modify%0A-a%20always%2Cexit%20-F%20arch%3Db32%20-S%20open%20-F%20a1%2603%20-F%20path%3D/etc/shadow%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Duser-modify%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20open%20-F%20a1%2603%20-F%20path%3D/etc/shadow%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Duser-modify%0A%0A%23%23%20User%20enable%20and%20disable.%20This%20is%20entirely%20handled%20by%20pam.%0A%0A%23%23%20Group%20add%20delete%20modify.%20This%20is%20covered%20by%20pam.%20However%2C%20someone%20could%0A%23%23%20open%20a%20file%20and%20directly%20create%20or%20modify%20a%20user%2C%20so%20we%27ll%20watch%20group%20and%0A%23%23%20gshadow%20for%20writes%0A-a%20always%2Cexit%20-F%20path%3D/etc/passwd%20-F%20perm%3Dwa%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Duser-modify%0A-a%20always%2Cexit%20-F%20path%3D/etc/shadow%20-F%20perm%3Dwa%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Duser-modify%0A-a%20always%2Cexit%20-F%20path%3D/etc/group%20-F%20perm%3Dwa%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dgroup-modify%0A-a%20always%2Cexit%20-F%20path%3D/etc/gshadow%20-F%20perm%3Dwa%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dgroup-modify%0A%0A%0A%23%23%20Use%20of%20special%20rights%20for%20config%20changes.%20This%20would%20be%20use%20of%20setuid%0A%23%23%20programs%20that%20relate%20to%20user%20accts.%20This%20is%20not%20all%20setuid%20apps%20because%0A%23%23%20requirements%20are%20only%20for%20ones%20that%20affect%20system%20configuration.%0A-a%20always%2Cexit%20-F%20path%3D/usr/sbin/unix_chkpwd%20-F%20perm%3Dx%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dspecial-config-changes%0A-a%20always%2Cexit%20-F%20path%3D/usr/sbin/usernetctl%20-F%20perm%3Dx%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dspecial-config-changes%0A-a%20always%2Cexit%20-F%20path%3D/usr/sbin/userhelper%20-F%20perm%3Dx%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dspecial-config-changes%0A-a%20always%2Cexit%20-F%20path%3D/usr/sbin/seunshare%20-F%20perm%3Dx%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dspecial-config-changes%0A-a%20always%2Cexit%20-F%20path%3D/usr/bin/mount%20-F%20perm%3Dx%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dspecial-config-changes%0A-a%20always%2Cexit%20-F%20path%3D/usr/bin/newgrp%20-F%20perm%3Dx%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dspecial-config-changes%0A-a%20always%2Cexit%20-F%20path%3D/usr/bin/newuidmap%20-F%20perm%3Dx%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dspecial-config-changes%0A-a%20always%2Cexit%20-F%20path%3D/usr/bin/gpasswd%20-F%20perm%3Dx%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dspecial-config-changes%0A-a%20always%2Cexit%20-F%20path%3D/usr/bin/newgidmap%20-F%20perm%3Dx%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dspecial-config-changes%0A-a%20always%2Cexit%20-F%20path%3D/usr/bin/umount%20-F%20perm%3Dx%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dspecial-config-changes%0A-a%20always%2Cexit%20-F%20path%3D/usr/bin/passwd%20-F%20perm%3Dx%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dspecial-config-changes%0A-a%20always%2Cexit%20-F%20path%3D/usr/bin/crontab%20-F%20perm%3Dx%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dspecial-config-changes%0A-a%20always%2Cexit%20-F%20path%3D/usr/bin/at%20-F%20perm%3Dx%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dspecial-config-changes%0A%0A%23%23%20Privilege%20escalation%20via%20su%20or%20sudo.%20This%20is%20entirely%20handled%20by%20pam.%0A%0A%23%23%20Audit%20log%20access%0A-a%20always%2Cexit%20-F%20dir%3D/var/log/audit/%20-F%20perm%3Dr%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Daccess-audit-trail%0A%23%23%20Attempts%20to%20Alter%20Process%20and%20Session%20Initiation%20Information%0A-a%20always%2Cexit%20-F%20path%3D/var/run/utmp%20-F%20perm%3Dwa%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dsession%0A-a%20always%2Cexit%20-F%20path%3D/var/log/btmp%20-F%20perm%3Dwa%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dsession%0A-a%20always%2Cexit%20-F%20path%3D/var/log/wtmp%20-F%20perm%3Dwa%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dsession%0A%0A%23%23%20Attempts%20to%20modify%20MAC%20controls%0A-a%20always%2Cexit%20-F%20dir%3D/etc/selinux/%20-F%20perm%3Dwa%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3DMAC-policy%0A%0A%23%23%20Software%20updates.%20This%20is%20entirely%20handled%20by%20rpm.%0A%0A%23%23%20System%20start%20and%20shutdown.%20This%20is%20entirely%20handled%20by%20systemd%0A%0A%23%23%20Kernel%20Module%20loading.%20This%20is%20handled%20in%2043-module-load.rules%0A%0A%23%23%20Application%20invocation.%20The%20requirements%20list%20an%20user%20requirement%0A%23%23%20FPT_SRP_EXT.1%20Software%20Restriction%20Policies.%20This%20event%20is%20intended%20to%0A%23%23%20state%20results%20from%20that%20policy.%20This%20would%20be%20handled%20entirely%20by%0A%23%23%20that%20daemon.%0A + mode: 0600 + path: /etc/audit/rules.d/30-ospp-v42.rules + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +cat << 'EOF' > /etc/audit/rules.d/30-ospp-v42.rules +## The purpose of these rules is to meet the requirements for Operating +## System Protection Profile (OSPP)v4.2. These rules depends on having +## the following rule files copied to /etc/audit/rules.d: +## +## 10-base-config.rules, 11-loginuid.rules, +## 30-ospp-v42-1-create-failed.rules, 30-ospp-v42-1-create-success.rules, +## 30-ospp-v42-2-modify-failed.rules, 30-ospp-v42-2-modify-success.rules, +## 30-ospp-v42-3-access-failed.rules, 30-ospp-v42-3-access-success.rules, +## 30-ospp-v42-4-delete-failed.rules, 30-ospp-v42-4-delete-success.rules, +## 30-ospp-v42-5-perm-change-failed.rules, +## 30-ospp-v42-5-perm-change-success.rules, +## 30-ospp-v42-6-owner-change-failed.rules, +## 30-ospp-v42-6-owner-change-success.rules +## +## original copies may be found in /usr/share/audit/sample-rules/ + + +## User add delete modify. This is covered by pam. However, someone could +## open a file and directly create or modify a user, so we'll watch passwd and +## shadow for writes +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b32 -S open -F a1&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b64 -S open -F a1&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b32 -S open -F a1&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b64 -S open -F a1&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify + +## User enable and disable. This is entirely handled by pam. + +## Group add delete modify. This is covered by pam. However, someone could +## open a file and directly create or modify a user, so we'll watch group and +## gshadow for writes +-a always,exit -F path=/etc/passwd -F perm=wa -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F path=/etc/shadow -F perm=wa -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F path=/etc/group -F perm=wa -F auid>=1000 -F auid!=unset -F key=group-modify +-a always,exit -F path=/etc/gshadow -F perm=wa -F auid>=1000 -F auid!=unset -F key=group-modify + + +## Use of special rights for config changes. This would be use of setuid +## programs that relate to user accts. This is not all setuid apps because +## requirements are only for ones that affect system configuration. +-a always,exit -F path=/usr/sbin/unix_chkpwd -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/sbin/usernetctl -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/sbin/userhelper -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/sbin/seunshare -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/mount -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/newgrp -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/newuidmap -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/gpasswd -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/newgidmap -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/umount -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/passwd -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/crontab -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/at -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes + +## Privilege escalation via su or sudo. This is entirely handled by pam. + +## Watch for configuration changes to privilege escalation. +-a always,exit -F path=/etc/sudoers -F perm=wa -F key=special-config-changes +-a always,exit -F dir=/etc/sudoers.d/ -F perm=wa -F key=special-config-changes + +## Audit log access +-a always,exit -F dir=/var/log/audit/ -F perm=r -F auid>=1000 -F auid!=unset -F key=access-audit-trail +## Attempts to Alter Process and Session Initiation Information +-a always,exit -F path=/var/run/utmp -F perm=wa -F auid>=1000 -F auid!=unset -F key=session +-a always,exit -F path=/var/log/btmp -F perm=wa -F auid>=1000 -F auid!=unset -F key=session +-a always,exit -F path=/var/log/wtmp -F perm=wa -F auid>=1000 -F auid!=unset -F key=session + +## Attempts to modify MAC controls +-a always,exit -F dir=/etc/selinux/ -F perm=wa -F auid>=1000 -F auid!=unset -F key=MAC-policy + +## Software updates. This is entirely handled by rpm. + +## System start and shutdown. This is entirely handled by systemd + +## Kernel Module loading. This is handled in 43-module-load.rules + +## Application invocation. The requirements list an user requirement +## FPT_SRP_EXT.1 Software Restriction Policies. This event is intended to +## state results from that policy. This would be handled entirely by +## that daemon. + +EOF + +chmod o-rwx /etc/audit/rules.d/30-ospp-v42.rules + +augenrules --load + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure auditing of unsuccessful ownership changes + Ensure that unsuccessful attempts to change an ownership of files or directories are audited. + +The following rules configure audit as described above: +## Unsuccessful ownership change +-a always,exit -F arch=b32 -S lchown,fchown,chown,fchownat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-owner-change +-a always,exit -F arch=b64 -S lchown,fchown,chown,fchownat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-owner-change +-a always,exit -F arch=b32 -S lchown,fchown,chown,fchownat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-owner-change +-a always,exit -F arch=b64 -S lchown,fchown,chown,fchownat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-owner-change + +Load new Audit rules into kernel by running: +augenrules --load + +Note: This rule uses a special set of Audit rules to comply with OSPP 4.2.1. You may reuse this rule in different profiles. If you decide to do so, it is recommended that you inspect contents of the file closely and make sure that they are alligned with your needs. + AU-2(a) + FAU_GEN.1.1.c + SRG-OS-000462-GPOS-00206 + SRG-OS-000463-GPOS-00207 + SRG-OS-000465-GPOS-00209 + SRG-OS-000474-GPOS-00219 + SRG-OS-000475-GPOS-00220 + SRG-OS-000466-GPOS-00210 + SRG-OS-000064-GPOS-00033 + Unsuccessful attempts to change an ownership of files or directories might be signs of a malicious activity. Having such events audited helps in monitoring and investigation of such activities. + CCE-82384-9 + - name: Put contents into /etc/audit/rules.d/30-ospp-v42-6-owner-change-failed.rules + according to policy + copy: + dest: /etc/audit/rules.d/30-ospp-v42-6-owner-change-failed.rules + content: | + ## Unsuccessful ownership change + -a always,exit -F arch=b32 -S lchown,fchown,chown,fchownat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-owner-change + -a always,exit -F arch=b64 -S lchown,fchown,chown,fchownat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-owner-change + -a always,exit -F arch=b32 -S lchown,fchown,chown,fchownat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-owner-change + -a always,exit -F arch=b64 -S lchown,fchown,chown,fchownat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-owner-change + force: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82384-9 + - NIST-800-53-AU-2(a) + - audit_owner_change_failed + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Remove any permissions from other group + file: + path: /etc/audit/rules.d/30-ospp-v42-6-owner-change-failed.rules + mode: o-rwx + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82384-9 + - NIST-800-53-AU-2(a) + - audit_owner_change_failed + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +cat << 'EOF' > /etc/audit/rules.d/30-ospp-v42-6-owner-change-failed.rules +## Unsuccessful ownership change +-a always,exit -F arch=b32 -S lchown,fchown,chown,fchownat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-owner-change +-a always,exit -F arch=b64 -S lchown,fchown,chown,fchownat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-owner-change +-a always,exit -F arch=b32 -S lchown,fchown,chown,fchownat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-owner-change +-a always,exit -F arch=b64 -S lchown,fchown,chown,fchownat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-owner-change +EOF + +chmod o-rwx /etc/audit/rules.d/30-ospp-v42-6-owner-change-failed.rules + +augenrules --load + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure auditing of successful ownership changes + Ensure that successful attempts to change an ownership of files or directories are audited. + +The following rules configure audit as described above: +## Successful ownership change +-a always,exit -F arch=b32 -S lchown,fchown,chown,fchownat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-owner-change +-a always,exit -F arch=b64 -S lchown,fchown,chown,fchownat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-owner-change + +Load new Audit rules into kernel by running: +augenrules --load + +Note: This rule uses a special set of Audit rules to comply with OSPP 4.2.1. You may reuse this rule in different profiles. If you decide to do so, it is recommended that you inspect contents of the file closely and make sure that they are alligned with your needs. + AU-2(a) + FAU_GEN.1.1.c + SRG-OS-000462-GPOS-00206 + SRG-OS-000463-GPOS-00207 + SRG-OS-000465-GPOS-00209 + SRG-OS-000474-GPOS-00219 + SRG-OS-000475-GPOS-00220 + SRG-OS-000466-GPOS-00210 + SRG-OS-000064-GPOS-00033 + Auditing of successful ownership changes of files or directories helps in monitoring or investingating of activities performed on the system. + CCE-82385-6 + - name: Put contents into /etc/audit/rules.d/30-ospp-v42-6-owner-change-success.rules + according to policy + copy: + dest: /etc/audit/rules.d/30-ospp-v42-6-owner-change-success.rules + content: | + ## Successful ownership change + -a always,exit -F arch=b32 -S lchown,fchown,chown,fchownat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-owner-change + -a always,exit -F arch=b64 -S lchown,fchown,chown,fchownat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-owner-change + force: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82385-6 + - NIST-800-53-AU-2(a) + - audit_owner_change_success + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Remove any permissions from other group + file: + path: /etc/audit/rules.d/30-ospp-v42-6-owner-change-success.rules + mode: o-rwx + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82385-6 + - NIST-800-53-AU-2(a) + - audit_owner_change_success + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +cat << 'EOF' > /etc/audit/rules.d/30-ospp-v42-6-owner-change-success.rules +## Successful ownership change +-a always,exit -F arch=b32 -S lchown,fchown,chown,fchownat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-owner-change +-a always,exit -F arch=b64 -S lchown,fchown,chown,fchownat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-owner-change +EOF + +chmod o-rwx /etc/audit/rules.d/30-ospp-v42-6-owner-change-success.rules + +augenrules --load + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure auditing of unsuccessful permission changes + Ensure that unsuccessful attempts to change file or directory permissions are audited. + +The following rules configure audit as described above: +## Unsuccessful permission change +-a always,exit -F arch=b32 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-perm-change +-a always,exit -F arch=b64 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-perm-change +-a always,exit -F arch=b32 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-perm-change +-a always,exit -F arch=b64 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-perm-change + +Load new Audit rules into kernel by running: +augenrules --load + +Note: This rule uses a special set of Audit rules to comply with OSPP 4.2.1. You may reuse this rule in different profiles. If you decide to do so, it is recommended that you inspect contents of the file closely and make sure that they are alligned with your needs. + AU-2(a) + FAU_GEN.1.1.c + SRG-OS-000462-GPOS-00206 + SRG-OS-000463-GPOS-00207 + SRG-OS-000465-GPOS-00209 + SRG-OS-000474-GPOS-00219 + SRG-OS-000475-GPOS-00220 + SRG-OS-000466-GPOS-00210 + SRG-OS-000064-GPOS-00033 + Unsuccessful attempts to change permissions of files or directories might be signs of malicious activity. Having such events audited helps in monitoring and investigation of such activities. + CCE-82837-6 + - name: Put contents into /etc/audit/rules.d/30-ospp-v42-5-perm-change-failed.rules + according to policy + copy: + dest: /etc/audit/rules.d/30-ospp-v42-5-perm-change-failed.rules + content: | + ## Unsuccessful permission change + -a always,exit -F arch=b32 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-perm-change + -a always,exit -F arch=b64 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-perm-change + -a always,exit -F arch=b32 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-perm-change + -a always,exit -F arch=b64 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-perm-change + force: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82837-6 + - NIST-800-53-AU-2(a) + - audit_perm_change_failed + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Remove any permissions from other group + file: + path: /etc/audit/rules.d/30-ospp-v42-5-perm-change-failed.rules + mode: o-rwx + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82837-6 + - NIST-800-53-AU-2(a) + - audit_perm_change_failed + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +cat << 'EOF' > /etc/audit/rules.d/30-ospp-v42-5-perm-change-failed.rules +## Unsuccessful permission change +-a always,exit -F arch=b32 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-perm-change +-a always,exit -F arch=b64 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-perm-change +-a always,exit -F arch=b32 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-perm-change +-a always,exit -F arch=b64 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-perm-change +EOF + +chmod o-rwx /etc/audit/rules.d/30-ospp-v42-5-perm-change-failed.rules + +augenrules --load + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure auditing of successful permission changes + Ensure that successful attempts to modify permissions of files or directories are audited. + +The following rules configure audit as described above: +## Successful permission change +-a always,exit -F arch=b32 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change +-a always,exit -F arch=b64 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + +Load new Audit rules into kernel by running: +augenrules --load + +Note: This rule uses a special set of Audit rules to comply with OSPP 4.2.1. You may reuse this rule in different profiles. If you decide to do so, it is recommended that you inspect contents of the file closely and make sure that they are alligned with your needs. + AU-2(a) + FAU_GEN.1.1.c + SRG-OS-000462-GPOS-00206 + SRG-OS-000463-GPOS-00207 + SRG-OS-000465-GPOS-00209 + SRG-OS-000474-GPOS-00219 + SRG-OS-000475-GPOS-00220 + SRG-OS-000466-GPOS-00210 + SRG-OS-000064-GPOS-00033 + Auditing successful file or directory permission changes helps in monitoring and investigating of activities performed on the system. + CCE-82383-1 + - name: Put contents into /etc/audit/rules.d/30-ospp-v42-5-perm-change-success.rules + according to policy + copy: + dest: /etc/audit/rules.d/30-ospp-v42-5-perm-change-success.rules + content: | + ## Successful permission change + -a always,exit -F arch=b32 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + -a always,exit -F arch=b64 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + force: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82383-1 + - NIST-800-53-AU-2(a) + - audit_perm_change_success + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Remove any permissions from other group + file: + path: /etc/audit/rules.d/30-ospp-v42-5-perm-change-success.rules + mode: o-rwx + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82383-1 + - NIST-800-53-AU-2(a) + - audit_perm_change_success + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +cat << 'EOF' > /etc/audit/rules.d/30-ospp-v42-5-perm-change-success.rules +## Successful permission change +-a always,exit -F arch=b32 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change +-a always,exit -F arch=b64 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change +EOF + +chmod o-rwx /etc/audit/rules.d/30-ospp-v42-5-perm-change-success.rules + +augenrules --load + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure audit according to OSPP requirements + Configure audit to meet requirements for Operating System Protection Profile (OSPP) v4.2.1. + +Audit defines groups of rules in /usr/share/doc/audit/rules to satisfy specific policies. + +To fulfill requirements for compliance with OSPP v4.2.1, the following files are necessary: +/usr/share/doc/audit/rules/10-base-config.rules/usr/share/doc/audit/rules/11-loginuid.rules/usr/share/doc/audit/rules/30-ospp-v42.rules/usr/share/doc/audit/rules/43-module-load.rules + +Copy the files from /usr/share/doc/audit/rules to /etc/audit/rules.d: + +cp /usr/share/doc/audit*/rules/{10-base-config,11-loginuid,30-ospp-v42,43-module-load}.rules /etc/audit/rules.d/ + + It might happen that Audit buffer configured by this rule is not large enough for certain use cases. If that is the case, the buffer size can be overridden by placing -b larger_buffer_size into a file within /etc/audit/rules.d directory, replacing larger_file_size with the desired value. The file name should start with a number higher than 10 and lower than 99. + NONE + FAU_GEN.1.1.c + SRG-OS-000004-GPOS-00004 + SRG-OS-000240-GPOS-00090 + SRG-OS-000241-GPOS-00091 + SRG-OS-000303-GPOS-00120 + SRG-OS-000476-GPOS-00221 + SRG-OS-000327-GPOS-00127 + SRG-OS-000064-GPOS-00033 + SRG-OS-000365-GPOS-00152 + SRG-OS-000458-GPOS-00203 + SRG-OS-000461-GPOS-00205 + SRG-OS-000462-GPOS-00206 + SRG-OS-000463-GPOS-00207 + SRG-OS-000465-GPOS-00209 + SRG-OS-000466-GPOS-00210 + SRG-OS-000468-GPOS-00212 + SRG-OS-000470-GPOS-00214 + SRG-OS-000471-GPOS-00215 + SRG-OS-000471-GPOS-00216 + SRG-OS-000472-GPOS-00217 + SRG-OS-000474-GPOS-00219 + SRG-OS-000475-GPOS-00220 + SRG-OS-000477-GPOS-00222 + The audit rules defined in /usr/share/doc/audit/rules are the recommended way to meet compliance with OSPP v4.2.1. + CCE-82309-6 + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +cp /usr/share/doc/audit*/rules/10-base-config.rules /etc/audit/rules.d +cp /usr/share/doc/audit*/rules/11-loginuid.rules /etc/audit/rules.d +cp /usr/share/doc/audit*/rules/30-ospp-v42.rules /etc/audit/rules.d +cp /usr/share/doc/audit*/rules/43-module-load.rules /etc/audit/rules.d + +augenrules --load + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + + AppArmor + Many security vulnerabilities result from bugs in trusted programs. A trusted +program runs with privileges that attackers want to possess. The program fails +to keep that trust if there is a bug in the program that allows the attacker to +acquire said privilege. + +AppArmor® is an application security solution designed specifically to apply +privilege confinement to suspect programs. AppArmor allows the administrator to +specify the domain of activities the program can perform by developing a +security profile. A security profile is a listing of files that the program may +access and the operations the program may perform. AppArmor secures +applications by enforcing good application behavior without relying on attack +signatures, so it can prevent attacks even if previously unknown +vulnerabilities are being exploited. + + + GRUB2 bootloader configuration + During the boot process, the boot loader is +responsible for starting the execution of the kernel and passing +options to it. The boot loader allows for the selection of +different kernels - possibly on different partitions or media. +The default Red Hat Enterprise Linux 8 boot loader for x86 systems is called GRUB2. +Options it can pass to the kernel include single-user mode, which +provides root access without any authentication, and the ability to +disable SELinux. To prevent local users from modifying the boot +parameters and endangering security, protect the boot loader configuration +with a password and ensure its configuration file's permissions +are set properly. + + + L1TF vulnerability mitigation + Defines the L1TF vulneratility mitigations to employ. + flush + full + full,force + flush + flush,nosmt + flush,nowarn + + + MDS vulnerability mitigation + Defines the MDS vulneratility mitigation to employ. + full + full + full,nosmt + + + Confidence level on Hardware Random Number Generator + Defines the level of trust on the hardware random number generators available in the +system and the percentage of entropy to credit. + 500 + 500 + 512 + 1000 + + + Spec Store Bypass Mitigation + This controls how the Speculative Store Bypass (SSB) vulnerability is mitigated. + prctl + on + auto + prctl + seccomp + + + Disable Recovery Booting + Red Hat Enterprise Linux 8 systems support an "recovery boot" option that can be used +to prevent services from being started. The GRUB_DISABLE_RECOVERY +configuration option in /etc/default/grub should be set to +true to disable the generation of recovery mode menu entries. It is +also required to change the runtime configuration, run: +$ sudo grubby --update-kernel=ALL --env=/boot/grub2/grubenv + FIA_UAU.1 + Using recovery boot, the console user could disable auditing, firewalls, +or other services, weakening system security. + + CCE-86006-4 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-86006-4 + - grub2_disable_recovery + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Verify GRUB_DISABLE_RECOVERY=true + lineinfile: + path: /etc/default/grub + regexp: ^GRUB_DISABLE_RECOVERY=.* + line: GRUB_DISABLE_RECOVERY=true + state: present + when: '"grub2-common" in ansible_facts.packages' + tags: + - CCE-86006-4 + - grub2_disable_recovery + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Update grub defaults and the bootloader menu + command: /sbin/grubby --update-kernel=ALL + when: '"grub2-common" in ansible_facts.packages' + tags: + - CCE-86006-4 + - grub2_disable_recovery + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q grub2-common; then + +if grep -q '^GRUB_DISABLE_RECOVERY=.*' '/etc/default/grub' ; then + sed -i 's/GRUB_DISABLE_RECOVERY=.*/GRUB_DISABLE_RECOVERY=true/' "/etc/default/grub" +else + echo "GRUB_DISABLE_RECOVERY=true" >> '/etc/default/grub' +fi + +grubby --update-kernel=ALL --env=/boot/grub2/grubenv + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + IOMMU configuration directive + On x86 architecture supporting VT-d, the IOMMU manages the access control policy between the hardware devices and some + of the system critical units such as the memory. +To ensure that iommu=force is added as a kernel command line +argument to newly installed kernels, add iommu=force to the +default Grub2 command line for Linux operating systems. Modify the line within +/etc/default/grub as shown below: +GRUB_CMDLINE_LINUX="... iommu=force ..." +Run the following command to update command line for already installed kernels:# grubby --update-kernel=ALL --args="iommu=force" + Depending on the hardware, devices and operating system used, enabling IOMMU can cause hardware instabilities. Proper function and stability should be assessed before applying remediation to production systems. + BP28(R11) + On x86 architectures, activating the I/OMMU prevents the system from arbitrary accesses potentially made by + hardware devices. + + CCE-83920-9 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83920-9 + - grub2_enable_iommu_force + - low_disruption + - medium_complexity + - reboot_required + - restrict_strategy + - unknown_severity + +- name: Update grub defaults and the bootloader menu + command: /sbin/grubby --update-kernel=ALL --args="iommu=force" + when: + - '"grub2-common" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83920-9 + - grub2_enable_iommu_force + - low_disruption + - medium_complexity + - reboot_required + - restrict_strategy + - unknown_severity + + [customizations.kernel] +append = "iommu=force" + + # Remediation is applicable only in certain platforms +if rpm --quiet -q grub2-common && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +grubby --update-kernel=ALL --args=iommu=force --env=/boot/grub2/grubenv + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure kernel to trust the CPU random number generator + There exist two ways how to ensure that the Linux kernel trusts the CPU +hardware random number generator. If the option is configured during kernel +compilation, e.g. the option CONFIG_RANDOM_TRUST_CPU is set to +Y, make sure that it is not overridden with the boot parameter. +There must not exist the boot parameter random.trust_cpu=off. If +the option is not compiled in, make sure that random.trust_cpu=on +is configured as a boot parameter. +To ensure that random.trust_cpu=on is added as a kernel command line +argument to newly installed kernels, add random.trust_cpu=on to the +default Grub2 command line for Linux operating systems. Modify the line within +/etc/default/grub as shown below: +GRUB_CMDLINE_LINUX="... random.trust_cpu=on ..." +Run the following command to update command line for already installed kernels:# grubby --update-kernel=ALL --args="random.trust_cpu=on" + FCS_RBG_EXT.1.1 + SRG-OS-000480-GPOS-00227 + The Linux kernel offers an option which signifies if the kernel should trust +data provided by CPU hardware random number generator. Hardware random +number generators can provide random data very quickly and are used to generate random cryptographic keys. They can +be useful during boot time when other means of getting random data can be +slow because there is not yet enough entropy in the system. + + CCE-83314-5 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83314-5 + - grub2_kernel_trust_cpu_rng + - low_disruption + - medium_complexity + - medium_severity + - reboot_required + - restrict_strategy + +- name: Update grub defaults and the bootloader menu + command: /sbin/grubby --update-kernel=ALL --args="random.trust_cpu=on" + when: + - '"grub2-common" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83314-5 + - grub2_kernel_trust_cpu_rng + - low_disruption + - medium_complexity + - medium_severity + - reboot_required + - restrict_strategy + + [customizations.kernel] +append = "random.trust_cpu=on" + + # Remediation is applicable only in certain platforms +if rpm --quiet -q grub2-common && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +grubby --update-kernel=ALL --args=random.trust_cpu=on --env=/boot/grub2/grubenv + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure L1 Terminal Fault mitigations + L1 Terminal Fault (L1TF) is a hardware vulnerability which allows unprivileged +speculative access to data which is available in the Level 1 Data Cache when +the page table entry isn't present. + +Select the appropriate mitigation by adding the argument +l1tf= to the default +GRUB 2 command line for the Linux operating system. +To ensure that l1tf= is added as a kernel command line +argument to newly installed kernels, add l1tf= to the +default Grub2 command line for Linux operating systems. Modify the line within +/etc/default/grub as shown below: +GRUB_CMDLINE_LINUX="... l1tf= ..." +Run the following command to update command line for already installed kernels:# grubby --update-kernel=ALL --args="l1tf=" + +Since Linux Kernel 4.19 you can check the L1TF vulnerability state with the +following command: +cat /sys/devices/system/cpu/vulnerabilities/l1tf + Enabling L1TF mitigations may impact performance of the system. + The L1TF vulnerability allows an attacker to bypass memory access security controls imposed +by the system or hypervisor. The L1TF vulnerability allows read access to any physical memory +location that is cached in the L1 Data Cache. + + CCE-88123-5 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-88123-5 + - grub2_l1tf_argument + - high_severity + - low_disruption + - medium_complexity + - reboot_required + - restrict_strategy +- name: XCCDF Value var_l1tf_options # promote to variable + set_fact: + var_l1tf_options: !!str + tags: + - always + +- name: Update grub defaults and the bootloader menu + command: /sbin/grubby --update-kernel=ALL --args="l1tf={{ var_l1tf_options }}" + when: + - '"grub2-common" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-88123-5 + - grub2_l1tf_argument + - high_severity + - low_disruption + - medium_complexity + - reboot_required + - restrict_strategy + + [customizations.kernel] +append = "l1tf=" + + # Remediation is applicable only in certain platforms +if rpm --quiet -q grub2-common && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +var_l1tf_options='' + + + +grubby --update-kernel=ALL --args=l1tf=$var_l1tf_options --env=/boot/grub2/grubenv + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Force kernel panic on uncorrected MCEs + A Machine Check Exception is an error generated by the CPU itdetects an error +in itself, memory or I/O devices. +These errors may be corrected and generate a check log entry, if an error +cannot be corrected the kernel may panic or SIGBUS. + +To force the kernel to panic on any uncorrected error reported by Machine Check +set the MCE tolerance to zero by adding mce=0 +to the default GRUB 2 command line for the Linux operating system. +To ensure that mce=0 is added as a kernel command line +argument to newly installed kernels, add mce=0 to the +default Grub2 command line for Linux operating systems. Modify the line within +/etc/default/grub as shown below: +GRUB_CMDLINE_LINUX="... mce=0 ..." +Run the following command to update command line for already installed kernels:# grubby --update-kernel=ALL --args="mce=0" + Allowing uncorrected errors to result on a SIGBUS may allow an attacker to continue +trying to exploit a vulnerability such as Rowhammer. + + CCE-87098-0 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-87098-0 + - grub2_mce_argument + - low_disruption + - medium_complexity + - medium_severity + - reboot_required + - restrict_strategy + +- name: Update grub defaults and the bootloader menu + command: /sbin/grubby --update-kernel=ALL --args="mce=0" + when: + - '"grub2-common" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-87098-0 + - grub2_mce_argument + - low_disruption + - medium_complexity + - medium_severity + - reboot_required + - restrict_strategy + + [customizations.kernel] +append = "mce=0" + + # Remediation is applicable only in certain platforms +if rpm --quiet -q grub2-common && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +grubby --update-kernel=ALL --args=mce=0 --env=/boot/grub2/grubenv + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure SMAP is not disabled during boot + The SMAP is used to prevent the supervisor mode from unintentionally reading/writing into +memory pages in the user space, it is enabled by default since Linux kernel 3.7. +But it could be disabled through kernel boot parameters. + +Ensure that Supervisor Mode Access Prevention (SMAP) is not disabled by +the nosmap boot paramenter option. + +Check that the line GRUB_CMDLINE_LINUX="..." within /etc/default/grub +doesn't contain the argument nosmap. +Run the following command to update command line for already installed kernels: +# grubby --update-kernel=ALL --remove-args="nosmap" + Disabling SMAP can facilitate exploitation of vulnerabilities caused by unintended access and +manipulation of data in the user space. + + CCE-87345-5 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-87345-5 + - grub2_nosmap_argument_absent + - low_disruption + - medium_complexity + - medium_severity + - reboot_required + - restrict_strategy + +- name: Update grub defaults and the bootloader menu + command: /sbin/grubby --update-kernel=ALL --remove-args="nosmap" + when: + - '"grub2-common" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-87345-5 + - grub2_nosmap_argument_absent + - low_disruption + - medium_complexity + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q grub2-common && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +grubby --update-kernel=ALL --remove-args=nosmap --env=/boot/grub2/grubenv + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure SMEP is not disabled during boot + The SMEP is used to prevent the supervisor mode from executing user space code, +it is enabled by default since Linux kernel 3.0. But it could be disabled through +kernel boot parameters. + +Ensure that Supervisor Mode Execution Prevention (SMEP) is not disabled by +the nosmep boot paramenter option. + +Check that the line GRUB_CMDLINE_LINUX="..." within /etc/default/grub +doesn't contain the argument nosmep. +Run the following command to update command line for already installed kernels: +# grubby --update-kernel=ALL --remove-args="nosmep" + Disabling SMEP can facilitate exploitation of certain vulnerabilities because it allows +the kernel to unintentionally execute code in less privileged memory space. + + CCE-85989-2 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-85989-2 + - grub2_nosmep_argument_absent + - low_disruption + - medium_complexity + - medium_severity + - reboot_required + - restrict_strategy + +- name: Update grub defaults and the bootloader menu + command: /sbin/grubby --update-kernel=ALL --remove-args="nosmep" + when: + - '"grub2-common" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-85989-2 + - grub2_nosmep_argument_absent + - low_disruption + - medium_complexity + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q grub2-common && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +grubby --update-kernel=ALL --remove-args=nosmep --env=/boot/grub2/grubenv + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Enable Kernel Page-Table Isolation (KPTI) + To enable Kernel page-table isolation, +add the argument pti=on to the default +GRUB 2 command line for the Linux operating system. +To ensure that pti=on is added as a kernel command line +argument to newly installed kernels, add pti=on to the +default Grub2 command line for Linux operating systems. Modify the line within +/etc/default/grub as shown below: +GRUB_CMDLINE_LINUX="... pti=on ..." +Run the following command to update command line for already installed kernels:# grubby --update-kernel=ALL --args="pti=on" + CCI-000381 + SI-16 + SRG-OS-000433-GPOS-00193 + SRG-OS-000095-GPOS-00049 + RHEL-08-040004 + SV-230491r818842_rule + Kernel page-table isolation is a kernel feature that mitigates +the Meltdown security vulnerability and hardens the kernel +against attempts to bypass kernel address space layout +randomization (KASLR). + + CCE-82194-2 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-82194-2 + - DISA-STIG-RHEL-08-040004 + - NIST-800-53-SI-16 + - grub2_pti_argument + - low_disruption + - low_severity + - medium_complexity + - reboot_required + - restrict_strategy + +- name: Update grub defaults and the bootloader menu + command: /sbin/grubby --update-kernel=ALL --args="pti=on" + when: + - '"grub2-common" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82194-2 + - DISA-STIG-RHEL-08-040004 + - NIST-800-53-SI-16 + - grub2_pti_argument + - low_disruption + - low_severity + - medium_complexity + - reboot_required + - restrict_strategy + + [customizations.kernel] +append = "pti=on" + + # Remediation is applicable only in certain platforms +if rpm --quiet -q grub2-common && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +grubby --update-kernel=ALL --args=pti=on --env=/boot/grub2/grubenv + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure the confidence in TPM for entropy + The TPM security chip that is available in most modern systems has a hardware RNG. +It is also used to feed the entropy pool, but generally not credited entropy. + +Use rng_core.default_quality in the kernel command line to set the trust +level on the hardware generators. The trust level defines the amount of entropy to credit. +A value of 0 tells the system not to trust the hardware random number generators +available, and doesn't credit any entropy to the pool. +A value of 1000 assigns full confidence in the generators, and credits all the +entropy it provides to the pool. + +Note that the value of rng_core.default_quality is global, affecting the trust +on all hardware random number generators. + +Select the appropriate confidence by adding the argument +rng_core.default_quality= to the default +GRUB 2 command line for the Linux operating system. +To ensure that rng_core.default_quality= is added as a kernel command line +argument to newly installed kernels, add rng_core.default_quality= to the +default Grub2 command line for Linux operating systems. Modify the line within +/etc/default/grub as shown below: +GRUB_CMDLINE_LINUX="... rng_core.default_quality= ..." +Run the following command to update command line for already installed kernels:# grubby --update-kernel=ALL --args="rng_core.default_quality=" + A system may struggle to initialize its entropy pool and end up starving. Crediting entropy +from the hardware number generators available in the system helps fill up the entropy pool. + + CCE-89567-2 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-89567-2 + - grub2_rng_core_default_quality_argument + - low_disruption + - low_severity + - medium_complexity + - reboot_required + - restrict_strategy +- name: XCCDF Value var_rng_core_default_quality # promote to variable + set_fact: + var_rng_core_default_quality: !!str + tags: + - always + +- name: Update grub defaults and the bootloader menu + command: /sbin/grubby --update-kernel=ALL --args="rng_core.default_quality={{ var_rng_core_default_quality + }}" + when: + - '"grub2-common" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-89567-2 + - grub2_rng_core_default_quality_argument + - low_disruption + - low_severity + - medium_complexity + - reboot_required + - restrict_strategy + + [customizations.kernel] +append = "rng_core.default_quality=" + + # Remediation is applicable only in certain platforms +if rpm --quiet -q grub2-common && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +var_rng_core_default_quality='' + + + +grubby --update-kernel=ALL --args=rng_core.default_quality=$var_rng_core_default_quality --env=/boot/grub2/grubenv + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable merging of slabs with similar size + The kernel may merge similar slabs together to reduce overhead and increase +cache hotness of objects. +Disabling merging of slabs keeps the slabs separate and reduces the risk of +kernel heap overflows overwriting objects in merged caches. + +To disable merging of slabs in the Kernel add the argument slab_nomerge=yes +to the default GRUB 2 command line for the Linux operating system. +To ensure that slab_nomerge=yes is added as a kernel command line +argument to newly installed kernels, add slab_nomerge=yes to the +default Grub2 command line for Linux operating systems. Modify the line within +/etc/default/grub as shown below: +GRUB_CMDLINE_LINUX="... slab_nomerge=yes ..." +Run the following command to update command line for already installed kernels:# grubby --update-kernel=ALL --args="slab_nomerge=yes" + Disabling merge of slabs will slightly increase kernel memory utilization. + Disabling the merge of slabs of similar sizes prevents the kernel from +merging a seemingly useless but vulnerable slab with a useful and valuable slab. +This increase the risk that a heap overflow could overwrite objects from merged caches, +with unmerged caches the heap overflow would only affect the objects in the same cache. +Overall, this reduces the kernel attack surface area by isolating slabs from each other. + + CCE-86777-0 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-86777-0 + - grub2_slab_nomerge_argument + - low_disruption + - medium_complexity + - medium_severity + - reboot_required + - restrict_strategy + +- name: Update grub defaults and the bootloader menu + command: /sbin/grubby --update-kernel=ALL --args="slab_nomerge=yes" + when: + - '"grub2-common" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86777-0 + - grub2_slab_nomerge_argument + - low_disruption + - medium_complexity + - medium_severity + - reboot_required + - restrict_strategy + + [customizations.kernel] +append = "slab_nomerge=yes" + + # Remediation is applicable only in certain platforms +if rpm --quiet -q grub2-common && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +grubby --update-kernel=ALL --args=slab_nomerge=yes --env=/boot/grub2/grubenv + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure Speculative Store Bypass Mitigation + Certain CPUs are vulnerable to an exploit against a common wide industry wide performance +optimization known as Speculative Store Bypass (SSB). + +In such cases, recent stores to the same memory location cannot always be observed by later +loads during speculative execution. However, such stores are unlikely and thus they can be +detected prior to instruction retirement at the end of a particular speculation execution +window. + +Since Linux Kernel 4.17 you can check the SSB mitigation state with the following command: +cat /sys/devices/system/cpu/vulnerabilities/spec_store_bypass + +Select the appropriate SSB state by adding the argument +spec_store_bypass_disable= to the default +GRUB 2 command line for the Linux operating system. +To ensure that spec_store_bypass_disable= is added as a kernel command line +argument to newly installed kernels, add spec_store_bypass_disable= to the +default Grub2 command line for Linux operating systems. Modify the line within +/etc/default/grub as shown below: +GRUB_CMDLINE_LINUX="... spec_store_bypass_disable= ..." +Run the following command to update command line for already installed kernels:# grubby --update-kernel=ALL --args="spec_store_bypass_disable=" + Disabling Speculative Store Bypass may impact performance of the system. + In vulnerable processsors, the speculatively forwarded store can be used in a cache side channel +attack. An example of this is reading memory to which the attacker does not directly have access, +for example inside the sandboxed code. + + CCE-89234-9 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-89234-9 + - grub2_spec_store_bypass_disable_argument + - low_disruption + - medium_complexity + - medium_severity + - reboot_required + - restrict_strategy +- name: XCCDF Value var_spec_store_bypass_disable_options # promote to variable + set_fact: + var_spec_store_bypass_disable_options: !!str + tags: + - always + +- name: Update grub defaults and the bootloader menu + command: /sbin/grubby --update-kernel=ALL --args="spec_store_bypass_disable={{ var_spec_store_bypass_disable_options + }}" + when: + - '"grub2-common" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-89234-9 + - grub2_spec_store_bypass_disable_argument + - low_disruption + - medium_complexity + - medium_severity + - reboot_required + - restrict_strategy + + [customizations.kernel] +append = "spec_store_bypass_disable=" + + # Remediation is applicable only in certain platforms +if rpm --quiet -q grub2-common && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +var_spec_store_bypass_disable_options='' + + + +grubby --update-kernel=ALL --args=spec_store_bypass_disable=$var_spec_store_bypass_disable_options --env=/boot/grub2/grubenv + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enforce Spectre v2 mitigation + Spectre V2 is an indirect branch poisoning attack that can lead to data leakage. +An exploit for Spectre V2 tricks the indirect branch predictor into executing +code from a future indirect branch chosen by the attacker, even if the privilege +level is different. + +Since Linux Kernel 4.15 you can check the Spectre V2 mitigation state with the following command: +cat /sys/devices/system/cpu/vulnerabilities/spectre_v2 + +Enforce the Spectre V2 mitigation by adding the argument +spectre_v2=on to the default +GRUB 2 command line for the Linux operating system. +To ensure that spectre_v2=on) is added as a kernel command line +argument to newly installed kernels, add spectre_v2=on) to the +default Grub2 command line for Linux operating systems. Modify the line within +/etc/default/grub as shown below: +GRUB_CMDLINE_LINUX="... spectre_v2=on) ..." +Run the following command to update command line for already installed kernels:# grubby --update-kernel=ALL --args="spectre_v2=on)" + The Spectre V2 vulnerability allows an attacker to read memory that he should not have +access to. + + CCE-89345-3 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-89345-3 + - grub2_spectre_v2_argument + - high_severity + - low_disruption + - medium_complexity + - reboot_required + - restrict_strategy + +- name: Update grub defaults and the bootloader menu + command: /sbin/grubby --update-kernel=ALL --args="spectre_v2=on" + when: + - '"grub2-common" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-89345-3 + - grub2_spectre_v2_argument + - high_severity + - low_disruption + - medium_complexity + - reboot_required + - restrict_strategy + + [customizations.kernel] +append = "spectre_v2=on" + + # Remediation is applicable only in certain platforms +if rpm --quiet -q grub2-common && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +grubby --update-kernel=ALL --args=spectre_v2=on --env=/boot/grub2/grubenv + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure debug-shell service is not enabled during boot + systemd's debug-shell service is intended to +diagnose systemd related boot issues with various systemctl +commands. Once enabled and following a system reboot, the root shell +will be available on tty9 which is access by pressing +CTRL-ALT-F9. The debug-shell service should only be used +for systemd related issues and should otherwise be disabled. + +By default, the debug-shell systemd service is already disabled. + +Ensure the debug-shell is not enabled by the systemd.debug-shel=1 +boot paramenter option. + +Check that the line GRUB_CMDLINE_LINUX="..." within /etc/default/grub +doesn't contain the argument systemd.debug-shell=1. +Run the following command to update command line for already installed kernels: +# grubby --update-kernel=ALL --remove-args="systemd.debug-shell" + FIA_UAU.1 + This prevents attackers with physical access from trivially bypassing security +on the machine through valid troubleshooting configurations and gaining root +access when the system is rebooted. + + - name: Gather the package facts + package_facts: + manager: auto + tags: + - grub2_systemd_debug-shell_argument_absent + - low_disruption + - medium_complexity + - medium_severity + - reboot_required + - restrict_strategy + +- name: Update grub defaults and the bootloader menu + command: /sbin/grubby --update-kernel=ALL --remove-args="systemd.debug-shell" + when: + - '"grub2-common" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - grub2_systemd_debug-shell_argument_absent + - low_disruption + - medium_complexity + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q grub2-common && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +grubby --update-kernel=ALL --remove-args=systemd.debug-shell --env=/boot/grub2/grubenv + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable vsyscalls + To disable use of virtual syscalls, +add the argument vsyscall=none to the default +GRUB 2 command line for the Linux operating system. +To ensure that vsyscall=none is added as a kernel command line +argument to newly installed kernels, add vsyscall=none to the +default Grub2 command line for Linux operating systems. Modify the line within +/etc/default/grub as shown below: +GRUB_CMDLINE_LINUX="... vsyscall=none ..." +Run the following command to update command line for already installed kernels:# grubby --update-kernel=ALL --args="vsyscall=none" + CCI-001084 + CM-7(a) + FPT_ASLR_EXT.1 + SRG-OS-000480-GPOS-00227 + SRG-OS-000134-GPOS-00068 + RHEL-08-010422 + SV-230278r792886_rule + Virtual Syscalls provide an opportunity of attack for a user who has control +of the return instruction pointer. + + CCE-80946-7 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80946-7 + - DISA-STIG-RHEL-08-010422 + - NIST-800-53-CM-7(a) + - grub2_vsyscall_argument + - low_disruption + - medium_complexity + - medium_severity + - reboot_required + - restrict_strategy + +- name: Update grub defaults and the bootloader menu + command: /sbin/grubby --update-kernel=ALL --args="vsyscall=none" + when: + - '"grub2-common" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80946-7 + - DISA-STIG-RHEL-08-010422 + - NIST-800-53-CM-7(a) + - grub2_vsyscall_argument + - low_disruption + - medium_complexity + - medium_severity + - reboot_required + - restrict_strategy + + [customizations.kernel] +append = "vsyscall=none" + + # Remediation is applicable only in certain platforms +if rpm --quiet -q grub2-common && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +grubby --update-kernel=ALL --args=vsyscall=none --env=/boot/grub2/grubenv + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Non-UEFI GRUB2 bootloader configuration + Non-UEFI GRUB2 bootloader configuration + + + Verify /boot/grub2/grub.cfg Group Ownership + The file /boot/grub2/grub.cfg should +be group-owned by the root group to prevent +destruction or modification of the file. + +To properly set the group owner of /boot/grub2/grub.cfg, run the command: +$ sudo chgrp root /boot/grub2/grub.cfg + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + 5.5.2.2 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + 3.4.5 + CCI-000225 + 164.308(a)(1)(ii)(B) + 164.308(a)(7)(i) + 164.308(a)(7)(ii)(A) + 164.310(a)(1) + 164.310(a)(2)(i) + 164.310(a)(2)(ii) + 164.310(a)(2)(iii) + 164.310(b) + 164.310(c) + 164.310(d)(1) + 164.310(d)(2)(iii) + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + Req-7.1 + SRG-OS-000480-GPOS-00227 + 1.4.2 + The root group is a highly-privileged group. Furthermore, the group-owner of this +file should not have any access privileges anyway. + + CCE-80800-6 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80800-6 + - CJIS-5.5.2.2 + - NIST-800-171-3.4.5 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-7.1 + - configure_strategy + - file_groupowner_grub2_cfg + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Test for existence /boot/grub2/grub.cfg + stat: + path: /boot/grub2/grub.cfg + register: file_exists + when: + - '"/boot/efi" not in ansible_mounts | map(attribute="mount") | list' + - '"grub2-common" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80800-6 + - CJIS-5.5.2.2 + - NIST-800-171-3.4.5 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-7.1 + - configure_strategy + - file_groupowner_grub2_cfg + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure group owner 0 on /boot/grub2/grub.cfg + file: + path: /boot/grub2/grub.cfg + group: '0' + when: + - '"/boot/efi" not in ansible_mounts | map(attribute="mount") | list' + - '"grub2-common" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-80800-6 + - CJIS-5.5.2.2 + - NIST-800-171-3.4.5 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-7.1 + - configure_strategy + - file_groupowner_grub2_cfg + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /sys/firmware/efi ] && rpm --quiet -q grub2-common && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +chgrp 0 /boot/grub2/grub.cfg + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Verify /boot/grub2/grub.cfg User Ownership + The file /boot/grub2/grub.cfg should +be owned by the root user to prevent destruction +or modification of the file. + +To properly set the owner of /boot/grub2/grub.cfg, run the command: +$ sudo chown root /boot/grub2/grub.cfg + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + 5.5.2.2 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + 3.4.5 + CCI-000225 + 164.308(a)(1)(ii)(B) + 164.308(a)(7)(i) + 164.308(a)(7)(ii)(A) + 164.310(a)(1) + 164.310(a)(2)(i) + 164.310(a)(2)(ii) + 164.310(a)(2)(iii) + 164.310(b) + 164.310(c) + 164.310(d)(1) + 164.310(d)(2)(iii) + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + Req-7.1 + 1.4.2 + Only root should be able to modify important boot parameters. + + CCE-80805-5 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80805-5 + - CJIS-5.5.2.2 + - NIST-800-171-3.4.5 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-7.1 + - configure_strategy + - file_owner_grub2_cfg + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Test for existence /boot/grub2/grub.cfg + stat: + path: /boot/grub2/grub.cfg + register: file_exists + when: + - '"/boot/efi" not in ansible_mounts | map(attribute="mount") | list' + - '"grub2-common" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80805-5 + - CJIS-5.5.2.2 + - NIST-800-171-3.4.5 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-7.1 + - configure_strategy + - file_owner_grub2_cfg + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure owner 0 on /boot/grub2/grub.cfg + file: + path: /boot/grub2/grub.cfg + owner: '0' + when: + - '"/boot/efi" not in ansible_mounts | map(attribute="mount") | list' + - '"grub2-common" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-80805-5 + - CJIS-5.5.2.2 + - NIST-800-171-3.4.5 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-7.1 + - configure_strategy + - file_owner_grub2_cfg + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /sys/firmware/efi ] && rpm --quiet -q grub2-common && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +chown 0 /boot/grub2/grub.cfg + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Verify /boot/grub2/grub.cfg Permissions + File permissions for /boot/grub2/grub.cfg should be set to 600. + +To properly set the permissions of /boot/grub2/grub.cfg, run the command: +$ sudo chmod 600 /boot/grub2/grub.cfg + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + 3.4.5 + CCI-000225 + 164.308(a)(1)(ii)(B) + 164.308(a)(7)(i) + 164.308(a)(7)(ii)(A) + 164.310(a)(1) + 164.310(a)(2)(i) + 164.310(a)(2)(ii) + 164.310(a)(2)(iii) + 164.310(b) + 164.310(c) + 164.310(d)(1) + 164.310(d)(2)(iii) + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + 1.4.2 + Proper permissions ensure that only the root user can modify important boot +parameters. + + CCE-80814-7 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80814-7 + - NIST-800-171-3.4.5 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_permissions_grub2_cfg + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Test for existence /boot/grub2/grub.cfg + stat: + path: /boot/grub2/grub.cfg + register: file_exists + when: + - '"/boot/efi" not in ansible_mounts | map(attribute="mount") | list' + - '"grub2-common" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80814-7 + - NIST-800-171-3.4.5 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_permissions_grub2_cfg + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure permission u-xs,g-xwrs,o-xwrt on /boot/grub2/grub.cfg + file: + path: /boot/grub2/grub.cfg + mode: u-xs,g-xwrs,o-xwrt + when: + - '"/boot/efi" not in ansible_mounts | map(attribute="mount") | list' + - '"grub2-common" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-80814-7 + - NIST-800-171-3.4.5 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_permissions_grub2_cfg + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /sys/firmware/efi ] && rpm --quiet -q grub2-common && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +chmod u-xs,g-xwrs,o-xwrt /boot/grub2/grub.cfg + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Set the Boot Loader Admin Username to a Non-Default Value + The grub2 boot loader should have a superuser account and password +protection enabled to protect boot-time settings. + +To maximize the protection, select a password-protected superuser account with unique name, and modify the +/etc/grub.d/01_users configuration file to reflect the account name change. + +Do not to use common administrator account names like root, +admin, or administrator for the grub2 superuser account. + +Change the superuser to a different username (The default is 'root'). +$ sed -i 's/\(set superuser=\).*/\1"<unique user ID>"/g' /etc/grub.d/01_users + +Once the superuser account has been added, +update the +grub.cfg file by running: +grubby --update-kernel=ALL --env=/boot/grub2/grubenv + To prevent hard-coded admin usernames, automatic remediation of this control is not available. Remediation +must be automated as a component of machine provisioning, or followed manually as outlined above. + +Also, do NOT manually add the superuser account and password to the +grub.cfg file as the grub2-mkconfig command overwrites this file. + BP28(R17) + 1 + 11 + 12 + 14 + 15 + 16 + 18 + 3 + 5 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.03 + DSS06.06 + DSS06.10 + 3.4.5 + CCI-000213 + 164.308(a)(1)(ii)(B) + 164.308(a)(7)(i) + 164.308(a)(7)(ii)(A) + 164.310(a)(1) + 164.310(a)(2)(i) + 164.310(a)(2)(ii) + 164.310(a)(2)(iii) + 164.310(b) + 164.310(c) + 164.310(d)(1) + 164.310(d)(2)(iii) + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + A.18.1.4 + A.6.1.2 + A.7.1.1 + A.9.1.2 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.1 + A.9.4.2 + A.9.4.3 + A.9.4.4 + A.9.4.5 + CM-6(a) + PR.AC-1 + PR.AC-4 + PR.AC-6 + PR.AC-7 + PR.PT-3 + FIA_UAU.1 + SRG-OS-000080-GPOS-00048 + RHEL-08-010149 + SV-244522r792984_rule + Having a non-default grub superuser username makes password-guessing attacks less effective. + + CCE-83561-1 + + + + + + + + + Boot Loader Is Not Installed On Removeable Media + The system must not allow removable media to be used as the boot loader. +Remove alternate methods of booting the system from removable media. +usb0, cd, fd0, etc. are some examples of removeable +media which should not exist in the line: +set root='hd0,msdos1' + CCI-001813 + CCI-001814 + SRG-OS-000364-GPOS-00151 + Malicious users with removable boot media can gain access to a system +configured to use removable media as the boot loader. + + + + + + + + + + Set Boot Loader Password in grub2 + The grub2 boot loader should have a superuser account and password +protection enabled to protect boot-time settings. + +Since plaintext passwords are a security risk, generate a hash for the password +by running the following command: + +# grub2-setpassword + +When prompted, enter the password that was selected. + + To prevent hard-coded passwords, automatic remediation of this control is not available. Remediation +must be automated as a component of machine provisioning, or followed manually as outlined above. + +Also, do NOT manually add the superuser account and password to the +grub.cfg file as the grub2-mkconfig command overwrites this file. + BP28(R17) + 1 + 11 + 12 + 14 + 15 + 16 + 18 + 3 + 5 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.03 + DSS06.06 + DSS06.10 + 3.4.5 + CCI-000213 + 164.308(a)(1)(ii)(B) + 164.308(a)(7)(i) + 164.308(a)(7)(ii)(A) + 164.310(a)(1) + 164.310(a)(2)(i) + 164.310(a)(2)(ii) + 164.310(a)(2)(iii) + 164.310(b) + 164.310(c) + 164.310(d)(1) + 164.310(d)(2)(iii) + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + A.18.1.4 + A.6.1.2 + A.7.1.1 + A.9.1.2 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.1 + A.9.4.2 + A.9.4.3 + A.9.4.4 + A.9.4.5 + CM-6(a) + PR.AC-1 + PR.AC-4 + PR.AC-6 + PR.AC-7 + PR.PT-3 + FIA_UAU.1 + SRG-OS-000080-GPOS-00048 + RHEL-08-010150 + 1.4.1 + SV-230235r743925_rule + Password protection on the boot loader configuration ensures +users with physical access cannot trivially alter +important bootloader settings. These include which kernel to use, +and whether to enter single-user mode. + + CCE-80828-7 + + + + + + + + + + UEFI GRUB2 bootloader configuration + UEFI GRUB2 bootloader configuration + + + Verify the UEFI Boot Loader grub.cfg Group Ownership + The file /boot/efi/EFI/redhat/grub.cfg should +be group-owned by the root group to prevent +destruction or modification of the file. + +To properly set the group owner of /boot/efi/EFI/redhat/grub.cfg, run the command: +$ sudo chgrp root /boot/efi/EFI/redhat/grub.cfg + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + 5.5.2.2 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + 3.4.5 + CCI-000225 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + Req-7.1 + 1.4.2 + The root group is a highly-privileged group. Furthermore, the group-owner of this +file should not have any access privileges anyway. + + CCE-85915-7 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-85915-7 + - CJIS-5.5.2.2 + - NIST-800-171-3.4.5 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-7.1 + - configure_strategy + - file_groupowner_efi_grub2_cfg + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Test for existence /boot/efi/EFI/redhat/grub.cfg + stat: + path: /boot/efi/EFI/redhat/grub.cfg + register: file_exists + when: + - '"/boot/efi" in ansible_mounts | map(attribute="mount") | list' + - '"grub2-common" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-85915-7 + - CJIS-5.5.2.2 + - NIST-800-171-3.4.5 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-7.1 + - configure_strategy + - file_groupowner_efi_grub2_cfg + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure group owner 0 on /boot/efi/EFI/redhat/grub.cfg + file: + path: /boot/efi/EFI/redhat/grub.cfg + group: '0' + when: + - '"/boot/efi" in ansible_mounts | map(attribute="mount") | list' + - '"grub2-common" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-85915-7 + - CJIS-5.5.2.2 + - NIST-800-171-3.4.5 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-7.1 + - configure_strategy + - file_groupowner_efi_grub2_cfg + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ -f /sys/firmware/efi ] && rpm --quiet -q grub2-common && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +chgrp 0 /boot/efi/EFI/redhat/grub.cfg + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Verify the UEFI Boot Loader grub.cfg User Ownership + The file /boot/efi/EFI/redhat/grub.cfg should +be owned by the root user to prevent destruction +or modification of the file. + +To properly set the owner of /boot/efi/EFI/redhat/grub.cfg, run the command: +$ sudo chown root /boot/efi/EFI/redhat/grub.cfg + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + 5.5.2.2 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + 3.4.5 + CCI-000225 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + Req-7.1 + 1.4.2 + Only root should be able to modify important boot parameters. + + CCE-85913-2 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-85913-2 + - CJIS-5.5.2.2 + - NIST-800-171-3.4.5 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-7.1 + - configure_strategy + - file_owner_efi_grub2_cfg + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Test for existence /boot/efi/EFI/redhat/grub.cfg + stat: + path: /boot/efi/EFI/redhat/grub.cfg + register: file_exists + when: + - '"/boot/efi" in ansible_mounts | map(attribute="mount") | list' + - '"grub2-common" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-85913-2 + - CJIS-5.5.2.2 + - NIST-800-171-3.4.5 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-7.1 + - configure_strategy + - file_owner_efi_grub2_cfg + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure owner 0 on /boot/efi/EFI/redhat/grub.cfg + file: + path: /boot/efi/EFI/redhat/grub.cfg + owner: '0' + when: + - '"/boot/efi" in ansible_mounts | map(attribute="mount") | list' + - '"grub2-common" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-85913-2 + - CJIS-5.5.2.2 + - NIST-800-171-3.4.5 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-7.1 + - configure_strategy + - file_owner_efi_grub2_cfg + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ -f /sys/firmware/efi ] && rpm --quiet -q grub2-common && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +chown 0 /boot/efi/EFI/redhat/grub.cfg + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Verify the UEFI Boot Loader grub.cfg Permissions + File permissions for /boot/efi/EFI/redhat/grub.cfg should be set to 700. + +To properly set the permissions of /boot/efi/EFI/redhat/grub.cfg, run the command: +$ sudo chmod 700 /boot/efi/EFI/redhat/grub.cfg + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + 3.4.5 + CCI-000225 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + 1.4.2 + Proper permissions ensure that only the root user can modify important boot +parameters. + + CCE-85912-4 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-85912-4 + - NIST-800-171-3.4.5 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_permissions_efi_grub2_cfg + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Test for existence /boot/efi/EFI/redhat/grub.cfg + stat: + path: /boot/efi/EFI/redhat/grub.cfg + register: file_exists + when: + - '"/boot/efi" in ansible_mounts | map(attribute="mount") | list' + - '"grub2-common" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-85912-4 + - NIST-800-171-3.4.5 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_permissions_efi_grub2_cfg + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure permission u-s,g-xwrs,o-xwrt on /boot/efi/EFI/redhat/grub.cfg + file: + path: /boot/efi/EFI/redhat/grub.cfg + mode: u-s,g-xwrs,o-xwrt + when: + - '"/boot/efi" in ansible_mounts | map(attribute="mount") | list' + - '"grub2-common" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-85912-4 + - NIST-800-171-3.4.5 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_permissions_efi_grub2_cfg + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ -f /sys/firmware/efi ] && rpm --quiet -q grub2-common && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +chmod u-s,g-xwrs,o-xwrt /boot/efi/EFI/redhat/grub.cfg + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Set the UEFI Boot Loader Admin Username to a Non-Default Value + The grub2 boot loader should have a superuser account and password +protection enabled to protect boot-time settings. + +To maximize the protection, select a password-protected superuser account with unique name, and modify the +/etc/grub.d/01_users configuration file to reflect the account name change. + +It is highly suggested not to use common administrator account names like root, +admin, or administrator for the grub2 superuser account. + +Change the superuser to a different username (The default is 'root'). +$ sed -i 's/\(set superusers=\).*/\1"<unique user ID>"/g' /etc/grub.d/01_users + +Once the superuser account has been added, +update the +grub.cfg file by running: +grubby --update-kernel=ALL --env=/boot/grub2/grubenv + To prevent hard-coded admin usernames, automatic remediation of this control is not available. Remediation +must be automated as a component of machine provisioning, or followed manually as outlined above. + +Also, do NOT manually add the superuser account and password to the +grub.cfg file as the grub2-mkconfig command overwrites this file. + BP28(R17) + 11 + 12 + 14 + 15 + 16 + 18 + 3 + 5 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + DSS06.03 + DSS06.06 + 3.4.5 + CCI-000213 + 164.308(a)(1)(ii)(B) + 164.308(a)(7)(i) + 164.308(a)(7)(ii)(A) + 164.310(a)(1) + 164.310(a)(2)(i) + 164.310(a)(2)(ii) + 164.310(a)(2)(iii) + 164.310(b) + 164.310(c) + 164.310(d)(1) + 164.310(d)(2)(iii) + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + A.6.1.2 + A.7.1.1 + A.9.1.2 + A.9.2.1 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-6(a) + PR.AC-4 + PR.AC-6 + PR.PT-3 + FIA_UAU.1 + SRG-OS-000080-GPOS-00048 + RHEL-08-010141 + SV-244521r792982_rule + Having a non-default grub superuser username makes password-guessing attacks less effective. + + CCE-83542-1 + + + + + + + + + Set the UEFI Boot Loader Password + The grub2 boot loader should have a superuser account and password +protection enabled to protect boot-time settings. + +Since plaintext passwords are a security risk, generate a hash for the password +by running the following command: + +# grub2-setpassword + +When prompted, enter the password that was selected. + + To prevent hard-coded passwords, automatic remediation of this control is not available. Remediation +must be automated as a component of machine provisioning, or followed manually as outlined above. + +Also, do NOT manually add the superuser account and password to the +grub.cfg file as the grub2-mkconfig command overwrites this file. + BP28(R17) + 11 + 12 + 14 + 15 + 16 + 18 + 3 + 5 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + DSS06.03 + DSS06.06 + 3.4.5 + CCI-000213 + 164.308(a)(1)(ii)(B) + 164.308(a)(7)(i) + 164.308(a)(7)(ii)(A) + 164.310(a)(1) + 164.310(a)(2)(i) + 164.310(a)(2)(ii) + 164.310(a)(2)(iii) + 164.310(b) + 164.310(c) + 164.310(d)(1) + 164.310(d)(2)(iii) + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + A.6.1.2 + A.7.1.1 + A.9.1.2 + A.9.2.1 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-6(a) + PR.AC-4 + PR.AC-6 + PR.PT-3 + FIA_UAU.1 + SRG-OS-000080-GPOS-00048 + RHEL-08-010140 + 1.4.1 + SV-230234r743922_rule + Password protection on the boot loader configuration ensures +users with physical access cannot trivially alter +important bootloader settings. These include which kernel to use, +and whether to enter single-user mode. + + CCE-80829-5 + + + + + + + + + UEFI Boot Loader Is Not Installed On Removeable Media + The system must not allow removable media to be used as the boot loader. +Remove alternate methods of booting the system from removable media. +usb0, cd, fd0, etc. are some examples of removeable +media which should not exist in the line: +set root='hd0,msdos1' + CCI-001813 + CCI-001814 + SRG-OS-000364-GPOS-00151 + Malicious users with removable boot media can gain access to a system +configured to use removable media as the boot loader. + + + + + + + + + + + + zIPL bootloader configuration + During the boot process, the bootloader is +responsible for starting the execution of the kernel and passing +options to it. +The default Red Hat Enterprise Linux 8 boot loader for s390x systems is called zIPL. + + + Enable Auditing to Start Prior to the Audit Daemon in zIPL + To ensure all processes can be audited, even those which start prior to the audit daemon, +check that all boot entries in /boot/loader/entries/*.conf have audit=1 +included in its options. + +To ensure that new kernels and boot entries continue to enable audit, +add audit=1 to /etc/kernel/cmdline. + FAU_GEN.1 + Each process on the system carries an "auditable" flag which indicates whether +its activities can be audited. Although auditd takes care of enabling +this for all processes which launch after it does, adding the kernel argument +ensures it is set for every process during boot. + + CCE-83321-0 + - name: Ensure BLS boot entries options contain audit=1 + block: + + - name: 'Check how many boot entries exist ' + find: + paths: /boot/loader/entries/ + patterns: '*.conf' + register: n_entries + + - name: Check how many boot entries set audit=1 + find: + paths: /boot/loader/entries/ + contains: ^options .*audit=1.*$ + patterns: '*.conf' + register: n_entries_options + + - name: Update boot entries options + command: grubby --update-kernel=ALL --args="audit=1" + when: n_entries is defined and n_entries_options is defined and n_entries.matched + != n_entries_options.matched + + - name: Check if /etc/kernel/cmdline exists + stat: + path: /etc/kernel/cmdline + register: cmdline_stat + + - name: Check if /etc/kernel/cmdline contains audit=1 + find: + paths: /etc/kernel/ + patterns: cmdline + contains: ^.*audit=1.*$ + register: cmdline_find + + - name: Add /etc/kernel/cmdline contains audit=1 + lineinfile: + create: true + path: /etc/kernel/cmdline + line: audit=1 + when: cmdline_stat is defined and not cmdline_stat.stat.exists + + - name: Append /etc/kernel/cmdline contains audit=1 + lineinfile: + path: /etc/kernel/cmdline + backrefs: true + regexp: ^(.*)$ + line: \1 audit=1 + when: cmdline_stat is defined and cmdline_stat.stat.exists and cmdline_find is + defined and cmdline_find.matched == 0 + when: + - ansible_architecture == "s390x" + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83321-0 + - configure_strategy + - low_disruption + - medium_complexity + - medium_severity + - reboot_required + - zipl_audit_argument + + # Remediation is applicable only in certain platforms +if grep -q s390x /proc/sys/kernel/osrelease && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +# Correct BLS option using grubby, which is a thin wrapper around BLS operations +grubby --update-kernel=ALL --args="audit=1" + +# Ensure new kernels and boot entries retain the boot option +if [ ! -f /etc/kernel/cmdline ]; then + echo "audit=1" > /etc/kernel/cmdline +elif ! grep -q '^(.*\s)?audit=1(\s.*)?$' /etc/kernel/cmdline; then + + sed -Ei 's/^(.*)$/\1 audit=1/' /etc/kernel/cmdline +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Extend Audit Backlog Limit for the Audit Daemon in zIPL + To improve the kernel capacity to queue all log events, even those which start prior to the audit daemon, +check that all boot entries in /boot/loader/entries/*.conf have audit_backlog_limit=8192 +included in its options. +To ensure that new kernels and boot entries continue to extend the audit log events queue, +add audit_backlog_limit=8192 to /etc/kernel/cmdline. + FAU_STG.1 + FAU_STG.3 + audit_backlog_limit sets the queue length for audit events awaiting transfer +to the audit daemon. Until the audit daemon is up and running, all log messages +are stored in this queue. If the queue is overrun during boot process, the action +defined by audit failure flag is taken. + + CCE-83341-8 + - name: Ensure BLS boot entries options contain audit_backlog_limit=8192 + block: + + - name: 'Check how many boot entries exist ' + find: + paths: /boot/loader/entries/ + patterns: '*.conf' + register: n_entries + + - name: Check how many boot entries set audit_backlog_limit=8192 + find: + paths: /boot/loader/entries/ + contains: ^options .*audit_backlog_limit=8192.*$ + patterns: '*.conf' + register: n_entries_options + + - name: Update boot entries options + command: grubby --update-kernel=ALL --args="audit_backlog_limit=8192" + when: n_entries is defined and n_entries_options is defined and n_entries.matched + != n_entries_options.matched + + - name: Check if /etc/kernel/cmdline exists + stat: + path: /etc/kernel/cmdline + register: cmdline_stat + + - name: Check if /etc/kernel/cmdline contains audit_backlog_limit=8192 + find: + paths: /etc/kernel/ + patterns: cmdline + contains: ^.*audit_backlog_limit=8192.*$ + register: cmdline_find + + - name: Add /etc/kernel/cmdline contains audit_backlog_limit=8192 + lineinfile: + create: true + path: /etc/kernel/cmdline + line: audit_backlog_limit=8192 + when: cmdline_stat is defined and not cmdline_stat.stat.exists + + - name: Append /etc/kernel/cmdline contains audit_backlog_limit=8192 + lineinfile: + path: /etc/kernel/cmdline + backrefs: true + regexp: ^(.*)$ + line: \1 audit_backlog_limit=8192 + when: cmdline_stat is defined and cmdline_stat.stat.exists and cmdline_find is + defined and cmdline_find.matched == 0 + when: + - ansible_architecture == "s390x" + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83341-8 + - configure_strategy + - low_disruption + - medium_complexity + - medium_severity + - reboot_required + - zipl_audit_backlog_limit_argument + + # Remediation is applicable only in certain platforms +if grep -q s390x /proc/sys/kernel/osrelease && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +# Correct BLS option using grubby, which is a thin wrapper around BLS operations +grubby --update-kernel=ALL --args="audit_backlog_limit=8192" + +# Ensure new kernels and boot entries retain the boot option +if [ ! -f /etc/kernel/cmdline ]; then + echo "audit_backlog_limit=8192" > /etc/kernel/cmdline +elif ! grep -q '^(.*\s)?audit_backlog_limit=8192(\s.*)?$' /etc/kernel/cmdline; then + + sed -Ei 's/^(.*)$/\1 audit_backlog_limit=8192/' /etc/kernel/cmdline +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure all zIPL boot entries are BLS compliant + Ensure that zIPL boot entries fully adheres to Boot Loader Specification (BLS) +by checking that /etc/zipl.conf doesn't contain image = . + To prevent breakage or removal of all boot entries oconfigured in /etc/zipl.conf +automated remediation for this rule is not available. + Red Hat Enterprise Linux 8 adheres to Boot Loader Specification (BLS) and is the prefered method of +configuration. + + CCE-83485-3 + + + + + + + + + Ensure zIPL bootmap is up to date + Make sure that /boot/bootmap is up to date. +Every time a boot entry or zIPL configuration is changed /boot/bootmap needs to +be updated to reflect the changes. +Run zipl command to generate an updated /boot/bootmap. + The file /boot/bootmap contains all boot data, keeping it up to date is crucial to +boot correct kernel and options. + + CCE-83486-1 + - name: Ensure zIPL bootmap is up to date + block: + + - name: Obtain stats of /boot/bootmap + stat: + path: /boot/bootmap + register: boot_bootmap + + - name: Obtain stats of /etc/zipl.conf + stat: + path: /etc/zipl.conf + register: zipl_conf + + - name: Update zIPL bootmap + command: /usr/sbin/zipl + changed_when: true + when: boot_bootmap.stat.mtime is defined and zipl_conf.stat.mtime is defined and boot_bootmap.stat.mtime + < zipl_conf.stat.mtime + when: + - ansible_architecture == "s390x" + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83486-1 + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - zipl_bootmap_is_up_to_date + + # Remediation is applicable only in certain platforms +if grep -q s390x /proc/sys/kernel/osrelease && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +/usr/sbin/zipl + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure SELinux Not Disabled in zIPL + To ensure SELinux is not disabled at boot time, +check that no boot entry in /boot/loader/entries/*.conf has selinux=0 +included in its options. + Disabling a major host protection feature, such as SELinux, at boot time prevents +it from confining system services at boot time. Further, it increases +the chances that it will remain off during system operation. + + + + + + + Enable page allocator poisoning in zIPL + To enable poisoning of free pages, +check that all boot entries in /boot/loader/entries/*.conf have page_poison=1 +included in its options. +To ensure that new kernels and boot entries continue to enable page poisoning, +add page_poison=1 to /etc/kernel/cmdline. + Poisoning writes an arbitrary value to freed pages, so any modification or +reference to that page after being freed or before being initialized will be +detected and prevented. +This prevents many types of use-after-free vulnerabilities at little performance cost. +Also prevents leak of data and detection of corrupted memory. + + CCE-83351-7 + - name: Ensure BLS boot entries options contain page_poison=1 + block: + + - name: 'Check how many boot entries exist ' + find: + paths: /boot/loader/entries/ + patterns: '*.conf' + register: n_entries + + - name: Check how many boot entries set page_poison=1 + find: + paths: /boot/loader/entries/ + contains: ^options .*page_poison=1.*$ + patterns: '*.conf' + register: n_entries_options + + - name: Update boot entries options + command: grubby --update-kernel=ALL --args="page_poison=1" + when: n_entries is defined and n_entries_options is defined and n_entries.matched + != n_entries_options.matched + + - name: Check if /etc/kernel/cmdline exists + stat: + path: /etc/kernel/cmdline + register: cmdline_stat + + - name: Check if /etc/kernel/cmdline contains page_poison=1 + find: + paths: /etc/kernel/ + patterns: cmdline + contains: ^.*page_poison=1.*$ + register: cmdline_find + + - name: Add /etc/kernel/cmdline contains page_poison=1 + lineinfile: + create: true + path: /etc/kernel/cmdline + line: page_poison=1 + when: cmdline_stat is defined and not cmdline_stat.stat.exists + + - name: Append /etc/kernel/cmdline contains page_poison=1 + lineinfile: + path: /etc/kernel/cmdline + backrefs: true + regexp: ^(.*)$ + line: \1 page_poison=1 + when: cmdline_stat is defined and cmdline_stat.stat.exists and cmdline_find is + defined and cmdline_find.matched == 0 + when: + - ansible_architecture == "s390x" + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83351-7 + - configure_strategy + - low_disruption + - medium_complexity + - medium_severity + - reboot_required + - zipl_page_poison_argument + + # Remediation is applicable only in certain platforms +if grep -q s390x /proc/sys/kernel/osrelease && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +# Correct BLS option using grubby, which is a thin wrapper around BLS operations +grubby --update-kernel=ALL --args="page_poison=1" + +# Ensure new kernels and boot entries retain the boot option +if [ ! -f /etc/kernel/cmdline ]; then + echo "page_poison=1" > /etc/kernel/cmdline +elif ! grep -q '^(.*\s)?page_poison=1(\s.*)?$' /etc/kernel/cmdline; then + + sed -Ei 's/^(.*)$/\1 page_poison=1/' /etc/kernel/cmdline +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Enable SLUB/SLAB allocator poisoning in zIPL + To enable poisoning of SLUB/SLAB objects, +check that all boot entries in /boot/loader/entries/*.conf have slub_debug=P +included in its options. +To ensure that new kernels and boot entries continue to enable poisoning of SLUB/SLAB objects, +add slub_debug=P to /etc/kernel/cmdline. + Poisoning writes an arbitrary value to freed objects, so any modification or +reference to that object after being freed or before being initialized will be +detected and prevented. +This prevents many types of use-after-free vulnerabilities at little performance cost. +Also prevents leak of data and detection of corrupted memory. + + CCE-83371-5 + - name: Ensure BLS boot entries options contain slub_debug=P + block: + + - name: 'Check how many boot entries exist ' + find: + paths: /boot/loader/entries/ + patterns: '*.conf' + register: n_entries + + - name: Check how many boot entries set slub_debug=P + find: + paths: /boot/loader/entries/ + contains: ^options .*slub_debug=P.*$ + patterns: '*.conf' + register: n_entries_options + + - name: Update boot entries options + command: grubby --update-kernel=ALL --args="slub_debug=P" + when: n_entries is defined and n_entries_options is defined and n_entries.matched + != n_entries_options.matched + + - name: Check if /etc/kernel/cmdline exists + stat: + path: /etc/kernel/cmdline + register: cmdline_stat + + - name: Check if /etc/kernel/cmdline contains slub_debug=P + find: + paths: /etc/kernel/ + patterns: cmdline + contains: ^.*slub_debug=P.*$ + register: cmdline_find + + - name: Add /etc/kernel/cmdline contains slub_debug=P + lineinfile: + create: true + path: /etc/kernel/cmdline + line: slub_debug=P + when: cmdline_stat is defined and not cmdline_stat.stat.exists + + - name: Append /etc/kernel/cmdline contains slub_debug=P + lineinfile: + path: /etc/kernel/cmdline + backrefs: true + regexp: ^(.*)$ + line: \1 slub_debug=P + when: cmdline_stat is defined and cmdline_stat.stat.exists and cmdline_find is + defined and cmdline_find.matched == 0 + when: + - ansible_architecture == "s390x" + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83371-5 + - configure_strategy + - low_disruption + - medium_complexity + - medium_severity + - reboot_required + - zipl_slub_debug_argument + + # Remediation is applicable only in certain platforms +if grep -q s390x /proc/sys/kernel/osrelease && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +# Correct BLS option using grubby, which is a thin wrapper around BLS operations +grubby --update-kernel=ALL --args="slub_debug=P" + +# Ensure new kernels and boot entries retain the boot option +if [ ! -f /etc/kernel/cmdline ]; then + echo "slub_debug=P" > /etc/kernel/cmdline +elif ! grep -q '^(.*\s)?slub_debug=P(\s.*)?$' /etc/kernel/cmdline; then + + sed -Ei 's/^(.*)$/\1 slub_debug=P/' /etc/kernel/cmdline +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure debug-shell service is not enabled in zIPL + systemd's debug-shell service is intended to +diagnose systemd related boot issues with various systemctl +commands. Once enabled and following a system reboot, the root shell +will be available on tty9 which is access by pressing +CTRL-ALT-F9. The debug-shell service should only be used +for systemd related issues and should otherwise be disabled. + +By default, the debug-shell systemd service is already disabled. + +Ensure the debug-shell is not enabled by the systemd.debug-shel=1 +boot paramenter option. + +Check that not boot entries in /boot/loader/entries/*.conf have +systemd.debug-shell=1 included in its options. +To ensure that new kernels and boot entries don't enable the debug-shell, check +that systemd.debug-shell=1 is not present in /etc/kernel/cmdline. + FIA_UAU.1 + This prevents attackers with physical access from trivially bypassing security +on the machine through valid troubleshooting configurations and gaining root +access when the system is rebooted. + + - name: Ensure BLS boot entries options contain systemd.debug-shell + block: + + - name: Check how many boot entries set systemd.debug-shell + find: + paths: /boot/loader/entries/ + contains: ^options .*systemd\.debug-shell.*$ + patterns: '*.conf' + register: n_entries + + - name: Remove systemd.debug-shell from boot entries + command: grubby --update-kernel=ALL --remove-args="systemd.debug-shell" + when: n_entries is defined and n_entries.matched >= 1 + + - name: Check if /etc/kernel/cmdline exists + stat: + path: /etc/kernel/cmdline + register: cmdline_stat + + - name: Check if /etc/kernel/cmdline contains systemd.debug-shell + find: + paths: /etc/kernel/ + patterns: cmdline + contains: ^.*systemd\.debug-shell.*$ + register: cmdline_find + + - name: Remove systemd.debug-shell from /etc/kernel/cmdline + lineinfile: + path: /etc/kernel/cmdline + backrefs: true + regexp: ^(.*)\s*systemd.debug-shell\b\S*(.*)$ + line: \1\2 + when: cmdline_stat is defined and cmdline_stat.stat.exists and cmdline_find is + defined and cmdline_find.matched >= 1 + when: + - ansible_architecture == "s390x" + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - configure_strategy + - low_disruption + - medium_complexity + - medium_severity + - reboot_required + - zipl_systemd_debug-shell_argument_absent + + # Remediation is applicable only in certain platforms +if grep -q s390x /proc/sys/kernel/osrelease && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +# Correct BLS option using grubby, which is a thin wrapper around BLS operations +grubby --update-kernel=ALL --remove-args="systemd.debug-shell" + +# Ensure new kernels and boot entries retain the boot option +if grep -q '\bsystemd.debug-shell\b' /etc/kernel/cmdline; then + sed -Ei 's/^(.*)\s*systemd.debug-shell\b\S*(.*)/\1\2/' /etc/kernel/cmdline +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable vsyscalls in zIPL + To disable use of virtual syscalls, +check that all boot entries in /boot/loader/entries/*.conf have vsyscall=none +included in its options. +To ensure that new kernels and boot entries continue to disable virtual syscalls, +add vsyscall=none to /etc/kernel/cmdline. + FPT_ASLR_EXT.1 + Virtual Syscalls provide an opportunity of attack for a user who has control +of the return instruction pointer. + + CCE-83381-4 + - name: Ensure BLS boot entries options contain vsyscall=none + block: + + - name: 'Check how many boot entries exist ' + find: + paths: /boot/loader/entries/ + patterns: '*.conf' + register: n_entries + + - name: Check how many boot entries set vsyscall=none + find: + paths: /boot/loader/entries/ + contains: ^options .*vsyscall=none.*$ + patterns: '*.conf' + register: n_entries_options + + - name: Update boot entries options + command: grubby --update-kernel=ALL --args="vsyscall=none" + when: n_entries is defined and n_entries_options is defined and n_entries.matched + != n_entries_options.matched + + - name: Check if /etc/kernel/cmdline exists + stat: + path: /etc/kernel/cmdline + register: cmdline_stat + + - name: Check if /etc/kernel/cmdline contains vsyscall=none + find: + paths: /etc/kernel/ + patterns: cmdline + contains: ^.*vsyscall=none.*$ + register: cmdline_find + + - name: Add /etc/kernel/cmdline contains vsyscall=none + lineinfile: + create: true + path: /etc/kernel/cmdline + line: vsyscall=none + when: cmdline_stat is defined and not cmdline_stat.stat.exists + + - name: Append /etc/kernel/cmdline contains vsyscall=none + lineinfile: + path: /etc/kernel/cmdline + backrefs: true + regexp: ^(.*)$ + line: \1 vsyscall=none + when: cmdline_stat is defined and cmdline_stat.stat.exists and cmdline_find is + defined and cmdline_find.matched == 0 + when: + - ansible_architecture == "s390x" + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83381-4 + - configure_strategy + - low_disruption + - medium_complexity + - medium_severity + - reboot_required + - zipl_vsyscall_argument + + # Remediation is applicable only in certain platforms +if grep -q s390x /proc/sys/kernel/osrelease && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +# Correct BLS option using grubby, which is a thin wrapper around BLS operations +grubby --update-kernel=ALL --args="vsyscall=none" + +# Ensure new kernels and boot entries retain the boot option +if [ ! -f /etc/kernel/cmdline ]; then + echo "vsyscall=none" > /etc/kernel/cmdline +elif ! grep -q '^(.*\s)?vsyscall=none(\s.*)?$' /etc/kernel/cmdline; then + + sed -Ei 's/^(.*)$/\1 vsyscall=none/' /etc/kernel/cmdline +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Protect Random-Number Entropy Pool + The I/O operations of the Linux kernel block layer due to their inherently +unpredictable execution times have been traditionally considered as a reliable +source to contribute to random-number entropy pool of the Linux kernel. This +has changed with introduction of solid-state storage devices (SSDs) though. + + + Ensure Solid State Drives Do Not Contribute To Random-Number Entropy Pool + For each solid-state drive on the system, run: + # echo 0 > /sys/block/DRIVE/queue/add_random + In contrast to traditional electromechanical magnetic disks, containing +spinning disks and / or movable read / write heads, the solid-state storage +devices (SSDs) do not contain moving / mechanical components. Therefore the +I/O operation completion times are much more predictable for them. + + + + Kernel Configuration + Contains rules that check the kernel configuration that was used to build it. + + + Hash function for kernel module signing + The hash function to use when signing modules during kernel build process. + sha512 + sha1 + sha224 + sha256 + sha384 + sha512 + + + Key and certificate for kernel module signing + The private key and certificate to use when signing modules during kernel build process. +On systems where the OpenSSL ENGINE_pkcs11 is functional — a PKCS#11 URI as defined by RFC7512 +In the latter case, the PKCS#11 URI should reference both a certificate and a private key. + certs/signing_key.pem + certs/signing_key.pem + + + Kernel panic timeout + The time, in seconds, to wait until a reboot occurs. +If the value is 0 the system never reboots. +If the value is less than 0 the system reboots immediately. + 0 + 0 + 300 + 60 + -1 + + + Do not allow ACPI methods to be inserted/replaced at run time + This debug facility allows ACPI AML methods to be inserted and/or replaced without rebooting +the system. +This configuration is available from kernel 3.0. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_ACPI_CUSTOM_METHOD, run the following command: + grep CONFIG_ACPI_CUSTOM_METHOD /boot/config-* + + Configs with value 'n' are not explicitly set in the file, so either commented lines or no + lines should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + Enabling this feature allows arbitrary kernel memory to be written to by root (uid=0) users, +allowing them to bypass certain security measures + CCE-86778-8 + + + + + + + + + Emulate Privileged Access Never (PAN) + Enabling this option prevents the kernel from accessing user-space memory directly by pointing +TTBR0_EL1 to a reserved zeroed area and reserved ASID. +The user access routines restore the valid TTBR0_EL1 temporarily. +This configuration is available from kernel 4.10, but may be available if backported +by distros. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_ARM64_SW_TTBR0_PAN, run the following command: + grep CONFIG_ARM64_SW_TTBR0_PAN /boot/config-* + + For each kernel installed, a line with value "y" should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + The Privileged Access Never (PAN) is the ARM equivalent of the x86 Supervisor Mode Access +Prevention (SMAP), and it prevents privileged acccess to user data unless explicitly enabled. + + CCE-89059-0 + + + + + + + + + Disable kernel support for MISC binaries + Enabling CONFIG_BINFMT_MISC makes it possible to plug wrapper-driven binary formats +into the kernel. This is specially useful for programs that need an interpreter to run like +Java, Python and DOS emulators. Once you have registered such a binary class with the kernel, +you can start one of those programs simply by typing in its name at a shell prompt. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_BINFMT_MISC, run the following command: + grep CONFIG_BINFMT_MISC /boot/config-* + + Configs with value 'n' are not explicitly set in the file, so either commented lines or no + lines should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + This disables arbitrary binary format support and helps reduce attack surface. + CCE-87766-2 + + + + + + + + + Enable support for BUG() + Disabling this option eliminates support for BUG and WARN, reducing the size of your kernel +image and potentially quietly ignoring numerous fatal conditions. You should only consider +disabling this option for embedded systems with no facilities for reporting errors. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_BUG, run the following command: + grep CONFIG_BUG /boot/config-* + + For each kernel installed, a line with value "y" should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + Not setting this variable may hide a number of critical errors. + CCE-86095-7 + + + + + + + + + Trigger a kernel BUG when data corruption is detected + This option makes the kernel BUG when it encounters data corruption in kernel memory structures +when they get checked for validity. +This configuration is available from kernel 4.10. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_BUG_ON_DATA_CORRUPTION, run the following command: + grep CONFIG_BUG_ON_DATA_CORRUPTION /boot/config-* + + For each kernel installed, a line with value "y" should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + This helps detect data corruptions early and stop with a BUG() error message. + CCE-87304-2 + + + + + + + + + Disable compatibility with brk() + Enabling compatiliby with brk() allows legacy binaries to run (i.e. those linked +against libc5). But this compatibility comes at the cost of not being able to randomize +the heap placement (ASLR). + +Unless legacy binaries need to run on the system, set CONFIG_COMPAT_BRK to "n". + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_COMPAT_BRK, run the following command: + grep CONFIG_COMPAT_BRK /boot/config-* + + Configs with value 'n' are not explicitly set in the file, so either commented lines or no + lines should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + Enabling compatibility with brk() disables support for ASLR. + CCE-88962-6 + + + + + + + + + Disable the 32-bit vDSO + Certain buggy versions of glibc (2.3.3) will crash if they are presented with a 32-bit vDSO +that is not mapped at the address indicated in its segment table. +Setting CONFIG_COMPAT_VDSO to y turns off the 32-bit VDSO and works +aroud the glibc bug. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_COMPAT_VDSO, run the following command: + grep CONFIG_COMPAT_VDSO /boot/config-* + + Configs with value 'n' are not explicitly set in the file, so either commented lines or no + lines should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + Enabling VDSO compatibility hurts performance and disables ASLR. + CCE-87256-4 + + + + + + + + + Enable checks on credential management + Enable this to turn on some debug checking for credential management. The additional code keeps +track of the number of pointers from task_structs to any given cred struct, and checks to see +that this number never exceeds the usage count of the cred struct. + +Furthermore, if SELinux is enabled, this also checks that the security pointer in the cred +struct is never seen to be invalid. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_DEBUG_CREDENTIALS, run the following command: + grep CONFIG_DEBUG_CREDENTIALS /boot/config-* + + For each kernel installed, a line with value "y" should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + This adds sanity checks and validations to credential data structures. + CCE-86656-6 + + + + + + + + + Disable kernel debugfs + debugfs is a virtual file system that kernel developers use to put debugging files +into. Enable this option to be able to read and write to these files. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_DEBUG_FS, run the following command: + grep CONFIG_DEBUG_FS /boot/config-* + + Configs with value 'n' are not explicitly set in the file, so either commented lines or no + lines should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + To reduce the attack surface, this file system should be disabled if not in use. + CCE-88033-6 + + + + + + + + + Enable checks on linked list manipulation + Enable this to turn on extended checks in the linked-list walking routines. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_DEBUG_LIST, run the following command: + grep CONFIG_DEBUG_LIST /boot/config-* + + For each kernel installed, a line with value "y" should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + This add sanity checks to manipulation of linked lists structures in the kernel and may +prevent exploits such as CVE-2017-1661, where a race condition and simultaneos operations +caused a list to corrupt. + CCE-86986-7 + + + + + + + + + Enable checks on notifier call chains + Enable this to turn on sanity checking for notifier call chains. This is most useful for kernel +developers to make sure that modules properly unregister themselves from notifier chains. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_DEBUG_NOTIFIERS, run the following command: + grep CONFIG_DEBUG_NOTIFIERS /boot/config-* + + For each kernel installed, a line with value "y" should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + This provides validation of notifier chains, it checks whether the notifiers are from the +kernel or a module that is still loaded prior to being invoked. + CCE-86814-1 + + + + + + + + + Enable checks on scatter-gather (SG) table operations + Scatter-gather tables are mechanism used for high performance I/O on DMA devices. +Enable this to turn on checks on scatter-gather tables. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_DEBUG_SG, run the following command: + grep CONFIG_DEBUG_SG /boot/config-* + + For each kernel installed, a line with value "y" should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + This can help find problems with drivers that do not properly initialize their SG tables. + CCE-87148-3 + + + + + + + + + Warn on W+X mappings found at boot + Generate a warning if any W+X mappings are found at boot. +This configuration is available from kernel 5.8. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_DEBUG_WX, run the following command: + grep CONFIG_DEBUG_WX /boot/config-* + + For each kernel installed, a line with value "y" should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + This is useful for discovering cases where the kernel is leaving W+X mappings after applying NX, +as such mappings are a security risk. +Note that even if the check fails, your kernel is possibly still fine, as W+X mappings are not +a security hole in themselves, what they do is that they make the exploitation of other unfixed +kernel bugs easier. + CCE-87032-9 + + + + + + + + + Configure low address space to protect from user allocation + This is the portion of low virtual memory which should be protected from userspace allocation. +This configuration is available from kernel 3.14, but may be available if backported +by distros. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_DEFAULT_MMAP_MIN_ADDR, run the following command: + grep CONFIG_DEFAULT_MMAP_MIN_ADDR /boot/config-* + + For each kernel installed, a line with value "65536" should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + Keeping a user from writing to low pages can help reduce the impact of kernel NULL pointer bugs. + CCE-88160-7 + + + + + + + + + Disable /dev/kmem virtual device support + Disable support for the /dev/kmem device. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_DEVKMEM, run the following command: + grep CONFIG_DEVKMEM /boot/config-* + + Configs with value 'n' are not explicitly set in the file, so either commented lines or no + lines should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + The /dev/kmem device is rarely used, but can be used for certain kind of kernel debugging +operations. + CCE-86947-9 + + + + + + + + + Harden common str/mem functions against buffer overflows + Detect overflows of buffers in common string and memory functions where the compiler can +determine and validate the buffer sizes. +This configuration is available from kernel 4.13, but may be available if backported by distros. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_FORTIFY_SOURCE, run the following command: + grep CONFIG_FORTIFY_SOURCE /boot/config-* + + For each kernel installed, a line with value "y" should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + This features helps reduce likelihood of memory corruption of kernel structures. + CCE-86545-1 + + + + + + + + + Harden memory copies between kernel and userspace + This option checks for obviously wrong memory regions when copying memory to/from the kernel +(via copy_to_user() and copy_from_user() functions) by rejecting memory ranges that are larger +than the specified heap object, span multiple separately allocated pages, are not on the +process stack, or are part of the kernel text. +This configuration is available from kernel 4.8, and may be available if backported by distros. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_HARDENED_USERCOPY, run the following command: + grep CONFIG_HARDENED_USERCOPY /boot/config-* + + For each kernel installed, a line with value "y" should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + This config prevents entire classes of heap overflow exploits and similar kernel memory exposures. + + CCE-88299-3 + + + + + + + + + Do not allow usercopy whitelist violations to fallback to object size + This is a temporary option that allows missing usercopy whitelists to be discovered via a WARN() +to the kernel log, instead of rejecting the copy, falling back to non-whitelisted hardened +usercopy that checks the slab allocation size instead of the whitelist size. +This configuration is available from kernel 4.16. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_HARDENED_USERCOPY_FALLBACK, run the following command: + grep CONFIG_HARDENED_USERCOPY_FALLBACK /boot/config-* + + Configs with value 'n' are not explicitly set in the file, so either commented lines or no + lines should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + This config prevents entire classes of heap overflow exploits and similar kernel memory exposures. + CCE-86091-6 + + + + + + + + + Disable hibernation + Enable the suspend to disk (STD) functionality, which is usually called "hibernation" in user +interfaces. STD checkpoints the system and powers it off; and restores that checkpoint on +reboot. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_HIBERNATION, run the following command: + grep CONFIG_HIBERNATION /boot/config-* + + Configs with value 'n' are not explicitly set in the file, so either commented lines or no + lines should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + Suspending to disk allows one to replace the running kernel. + CCE-87608-6 + + + + + + + + + Disable IA32 emulation + Disables support for legacy 32-bit programs under a 64-bit kernel. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_IA32_EMULATION, run the following command: + grep CONFIG_IA32_EMULATION /boot/config-* + + Configs with value 'n' are not explicitly set in the file, so either commented lines or no + lines should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + Only disable support for 32-bit programs if you are sure you don't need any 32-bit program. + Disabling 32-bit backwards compatibility helps reduce the attack surface. + CCE-88746-3 + + + + + + + + + Disable the IPv6 protocol + Disable support for IP version 6 (IPv6). + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_IPV6, run the following command: + grep CONFIG_IPV6 /boot/config-* + + Configs with value 'n' are not explicitly set in the file, so either commented lines or no + lines should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + Any unnecessary network stacks, including IPv6, should be disabled to reduce +the vulnerability to exploitation. + CCE-87225-9 + + + + + + + + + Disable kexec system call + kexec is a system call that implements the ability to shutdown your current kernel, +and to start another kernel. It is like a reboot but it is independent of the system firmware. +And like a reboot you can start any kernel with it, not just Linux. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_KEXEC, run the following command: + grep CONFIG_KEXEC /boot/config-* + + Configs with value 'n' are not explicitly set in the file, so either commented lines or no + lines should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + Prohibits the execution of a new kernel image after reboot. + CCE-87488-3 + + + + + + + + + Disable legacy (BSD) PTY support + Disable the Linux traditional BSD-like terminal names /dev/ptyxx for masters and /dev/ttyxx for +slaves of pseudo terminals, and use only the modern ptys (devpts) interface. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_LEGACY_PTYS, run the following command: + grep CONFIG_LEGACY_PTYS /boot/config-* + + Configs with value 'n' are not explicitly set in the file, so either commented lines or no + lines should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + The legacy scheme has a number of security problems. + CCE-87925-4 + + + + + + + + + Disable vsyscall emulation + The kernel traps and emulates calls into the fixed vsyscall address mapping. +This configuration is available from kernel 5.3, but may be available if backported by distros. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_LEGACY_VSYSCALL_EMULATE, run the following command: + grep CONFIG_LEGACY_VSYSCALL_EMULATE /boot/config-* + + Configs with value 'n' are not explicitly set in the file, so either commented lines or no + lines should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + The mapping is non-executable, but it still contains known contents, which could be +used in certain rare security vulnerability exploits. + CCE-87649-0 + + + + + + + + + Disable vsyscall mapping + This config disables the vsyscall mapping at all. Attempts to use the vsyscalls will be reported to +dmesg, so that either old or malicious userspace programs can be identified. +This configuration is available from kernel 4.4. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_LEGACY_VSYSCALL_NONE, run the following command: + grep CONFIG_LEGACY_VSYSCALL_NONE /boot/config-* + + For each kernel installed, a line with value "y" should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + This will eliminate any risk of ASLR bypass due to the vsyscall fixed address mapping. + CCE-87573-2 + + + + + + + + + Disable the LDT (local descriptor table) + Linux can allow user programs to install a per-process x86 Local Descriptor Table (LDT) using +the modify_ldt(2) system call. This is required to run 16-bit or segmented code such as DOSEMU +or some Wine programs. It is also used by some very old threading libraries. +This configuration is available from kernel 4.3, but may be available if backported +by distros. + +Disable LDT if 16-bit program emulation is not necessary. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_MODIFY_LDT_SYSCALL, run the following command: + grep CONFIG_MODIFY_LDT_SYSCALL /boot/config-* + + Configs with value 'n' are not explicitly set in the file, so either commented lines or no + lines should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + Disabling support for unnecessary code reduces attack surface. + CCE-88827-1 + + + + + + + + + Enable module signature verification + Check modules for valid signatures upon load. +Note that this option adds the OpenSSL development packages as a kernel build dependency so +that the signing tool can use its crypto library. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_MODULE_SIG, run the following command: + grep CONFIG_MODULE_SIG /boot/config-* + + For each kernel installed, a line with value "y" should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + Loaded modules must be signed. + CCE-89378-4 + + + + + + + + + Enable automatic signing of all modules + Sign all modules during make modules_install. Without this option, modules must be signed +manually, using the scripts/sign-file tool. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_MODULE_SIG_ALL, run the following command: + grep CONFIG_MODULE_SIG_ALL /boot/config-* + + For each kernel installed, a line with value "y" should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + This ensures the modules are signed during install process. + CCE-89615-9 + + + + + + + + + Require modules to be validly signed + Reject unsigned modules or signed modules with an unknown key. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_MODULE_SIG_FORCE, run the following command: + grep CONFIG_MODULE_SIG_FORCE /boot/config-* + + For each kernel installed, a line with value "y" should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + Prevent loading modules that are unsigned or signed with an unknown key. + CCE-89459-2 + + + + + + + + + Specify the hash to use when signing modules + This configures the kernel to build and sign modules using + as the hash function. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_MODULE_SIG_HASH, run the following command: + grep CONFIG_MODULE_SIG_HASH /boot/config-* + + For each kernel installed, a line with value "" should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + Use of strong hash function is important to secure the module against counterfeit signatures. + CCE-89843-7 + + + + + + + + + + Specify module signing key to use + Setting this option to something other than its default of certs/signing_key.pem will +disable the autogeneration of signing keys and allow the kernel modules to be signed with a key +of your choosing. + +The string provided should identify a file containing both a private key and +its corresponding X.509 certificate in PEM form, or — on systems where the OpenSSL ENGINE_pkcs11 +is functional — a PKCS#11 URI as defined by RFC7512. In the latter case, the PKCS#11 URI should +reference both a certificate and a private key. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_MODULE_SIG_KEY, run the following command: + grep CONFIG_MODULE_SIG_KEY /boot/config-* + + For each kernel installed, a line with value "" should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + A key and certificate is required to sign the built modules. + CCE-90000-1 + + + + + + + + + + Sign kernel modules with SHA-512 + This configures the kernel to build and sign modules using SHA512 as the hash function. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_MODULE_SIG_SHA512, run the following command: + grep CONFIG_MODULE_SIG_SHA512 /boot/config-* + + For each kernel installed, a line with value "y" should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + Use of strong hash function is important to secure the module against counterfeit signatures. + CCE-89692-8 + + + + + + + + + Enable poison of pages after freeing + Fill the pages with poison patterns after free_pages() and verify the patterns before +alloc_pages. This does have a potential performance impact if enabled with the "page_poison=1" +kernel boot option. +This configuration is available from kernel 4.6. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_PAGE_POISONING, run the following command: + grep CONFIG_PAGE_POISONING /boot/config-* + + For each kernel installed, a line with value "y" should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + The filling of the memory helps reduce the risk of information leaks from freed data. + CCE-88426-2 + + + + + + + + + Enable poison without sanity check + Skip the sanity checking on alloc, only fill the pages with poison on free. This reduces some +of the overhead of the poisoning feature. +This configuration is available from kernel 4.6. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_PAGE_POISONING_NO_SANITY, run the following command: + grep CONFIG_PAGE_POISONING_NO_SANITY /boot/config-* + + For each kernel installed, a line with value "y" should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + This configuration helps alleviates the performance impact of poisonining. + CCE-88574-9 + + + + + + + + + Use zero for poisoning instead of debugging value + Instead of using the existing poison value, fill the pages with zeros. This makes it harder to +detect when errors are occurring due to sanitization but the zeroing at free means that it is +no longer necessary to write zeros when GFP_ZERO is used on allocation. +This configuration is available from kernel 4.19. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_PAGE_POISONING_ZERO, run the following command: + grep CONFIG_PAGE_POISONING_ZERO /boot/config-* + + For each kernel installed, a line with value "y" should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + This configuration helps alleviates the performance impact of poisonining. + CCE-88808-1 + + + + + + + + + Remove the kernel mapping in user mode + This feature reduces the number of hardware side channels by ensuring that the majority of +kernel addresses are not mapped into userspace. +This configuration is available from kernel 4.15, but may be available if backported +by distros. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_PAGE_TABLE_ISOLATION, run the following command: + grep CONFIG_PAGE_TABLE_ISOLATION /boot/config-* + + For each kernel installed, a line with value "y" should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + This is a countermeasure to the Meltdown attack. + CCE-88591-3 + + + + + + + + + Kernel panic oops + Enable the kernel to panic when it oopses. +This has the same effect as setting oops=panic on the kernel command line. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_PANIC_ON_OOPS, run the following command: + grep CONFIG_PANIC_ON_OOPS /boot/config-* + + For each kernel installed, a line with value "y" should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + This feature ensures that the kernel does not do anything erroneous after an oops which +could result in data corruption or other issues. + CCE-86176-5 + + + + + + + + + Kernel panic timeout + Set the timeout value (in seconds) until a reboot occurs when the kernel panics. +A timeout of 0 configures the system to wait forever. With a timeout value greater than 0, +the system will wait the specified amount of seconds before rebooting. While a timeout value +less than 0 makes the system reboot immediately. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_PANIC_TIMEOUT, run the following command: + grep CONFIG_PANIC_TIMEOUT /boot/config-* + + For each kernel installed, a line with value "" should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + This is required to enable protection against Spectre v2. + CCE-86349-8 + + + + + + + + + + Disable support for /proc/kkcore + Provides a virtual ELF core file of the live kernel. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_PROC_KCORE, run the following command: + grep CONFIG_PROC_KCORE /boot/config-* + + Configs with value 'n' are not explicitly set in the file, so either commented lines or no + lines should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + This feature exposes the memory to the userspace and can assist an attacker in discovering +attack vectors. + CCE-87105-3 + + + + + + + + + Randomize the address of the kernel image (KASLR) + In support of Kernel Address Space Layout Randomization (KASLR), this randomizes the physical +address at which the kernel image is decompressed and the virtual address where the kernel +image is mapped. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_RANDOMIZE_BASE, run the following command: + grep CONFIG_RANDOMIZE_BASE /boot/config-* + + For each kernel installed, a line with value "y" should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + An unpredictable kernel address makes it more difficult to succeed with exploits that rely on +knowledge of the location of kernel code internals. + CCE-88318-1 + + + + + + + + + Randomize the kernel memory sections + Randomizes the base virtual address of kernel memory sections (physical memory mapping, +vmalloc & vmemmap). +This configuration is available from kernel 4.8, but may be available if backported +by distros. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_RANDOMIZE_MEMORY, run the following command: + grep CONFIG_RANDOMIZE_MEMORY /boot/config-* + + For each kernel installed, a line with value "y" should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + This security feature makes exploits relying on predictable memory locations less reliable. + CCE-88440-3 + + + + + + + + + Perform full reference count validation + Enabling this switches the refcounting infrastructure from a fast unchecked atomic_t +implementation to a fully state checked implementation, which can have a slight +impact in performance. +This configuration is available from kernel 4.13, but may be available if backported +by distros. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_REFCOUNT_FULL, run the following command: + grep CONFIG_REFCOUNT_FULL /boot/config-* + + For each kernel installed, a line with value "y" should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + Refcounting provides protections against various use-after-free conditions that can be +used in security flaw exploits. + CCE-86422-3 + + + + + + + + + Avoid speculative indirect branches in kernel + Compile kernel with the retpoline compiler options to guard against kernel-to-user data leaks +by avoiding speculative indirect branches. +Requires a compiler with -mindirect-branch=thunk-extern support for full protection. +The kernel may run slower. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_RETPOLINE, run the following command: + grep CONFIG_RETPOLINE /boot/config-* + + For each kernel installed, a line with value "y" should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + This is required to enable protection against Spectre v2. + CCE-87494-1 + + + + + + + + + Detect stack corruption on calls to schedule() + This option checks for a stack overrun on calls to schedule(). If the stack end location is +found to be overwritten always panic as the content of the corrupted region can no longer +be trusted. +This configuration is available from kernel 3.18. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_SCHED_STACK_END_CHECK, run the following command: + grep CONFIG_SCHED_STACK_END_CHECK /boot/config-* + + For each kernel installed, a line with value "y" should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + This ensures no erroneous behaviour occurs which could result in data corruption or a +sporadic crash at a later stage once the region is examined. + CCE-88041-9 + + + + + + + + + Enable seccomp to safely compute untrusted bytecode + This kernel feature is useful for number crunching applications that may need to compute +untrusted bytecode during their execution. By using pipes or other transports made available +to the process as file descriptors supporting the read/write syscalls, it's possible to isolate +those applications in their own address space using seccomp. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_SECCOMP, run the following command: + grep CONFIG_SECCOMP /boot/config-* + + For each kernel installed, a line with value "y" should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + seccomp enables the ability to filter system calls made by an application, effectively +isolating the system's resources from it. + CCE-86450-4 + + + + + + + + + Enable use of Berkeley Packet Filter with seccomp + Enable tasks to build secure computing environments defined in terms of Berkeley Packet Filter +programs which implement task-defined system call filtering polices. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_SECCOMP_FILTER, run the following command: + grep CONFIG_SECCOMP_FILTER /boot/config-* + + For each kernel installed, a line with value "y" should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + Use of BPF filters allows for expressive filtering of system calls using a filter program +language with a long history of being exposed to userland. + CCE-86490-0 + + + + + + + + + Enable different security models + This allows you to choose different security modules to be configured into your kernel. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_SECURITY, run the following command: + grep CONFIG_SECURITY /boot/config-* + + For each kernel installed, a line with value "y" should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + This is enables kernel security primitives required by the LSM framework. + CCE-86572-5 + + + + + + + + + Restrict unprivileged access to the kernel syslog + Enforce restrictions on unprivileged users reading the kernel syslog via dmesg(8). + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_SECURITY_DMESG_RESTRICT, run the following command: + grep CONFIG_SECURITY_DMESG_RESTRICT /boot/config-* + + Configs with value 'n' are not explicitly set in the file, so either commented lines or no + lines should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + Prevents unprivileged users from retrieving kernel addresses with dmesg. + CCE-87339-8 + + + + + + + + + Disable mutable hooks + Ensure kernel structures associated with LSMs are always mapped as read-only after system boot. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_SECURITY_WRITABLE_HOOKS, run the following command: + grep CONFIG_SECURITY_WRITABLE_HOOKS /boot/config-* + + For each kernel installed, a line with value "y" should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + If CONFIG_SECURITY_WRITABLE_HOOKS is enabled, then hooks can be loaded at runtime and +being able to manipulate hooks is a way to bypass all LSMs. + CCE-86884-4 + + + + + + + + + Enable Yama support + This enables support for LSM module Yama, which extends DAC support with additional system-wide +security settings beyond regular Linux discretionary access controls. The module will limit the +use of the system call ptrace(). + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_SECURITY_YAMA, run the following command: + grep CONFIG_SECURITY_YAMA /boot/config-* + + For each kernel installed, a line with value "y" should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + Unrestricted usage of ptrace allows compromised binaries to run ptrace +on another processes of the user. + CCE-86716-8 + + + + + + + + + Harden slab freelist metadata + This feature protects integrity of the allocator's metadata. +This configuration is available from kernel 4.14. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_SLAB_FREELIST_HARDENED, run the following command: + grep CONFIG_SLAB_FREELIST_HARDENED /boot/config-* + + For each kernel installed, a line with value "y" should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + Many kernel heap attacks try to target slab cache metadata and other infrastructure. +This options makes minor performance sacrifices to harden the kernel slab allocator against +common freelist exploit methods. + CCE-87962-7 + + + + + + + + + Randomize slab freelist + Randomizes the freelist order used on creating new pages. +This configuration is available from kernel 5.9, but may be available if backported by distros. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_SLAB_FRELIST_RANDOM, run the following command: + grep CONFIG_SLAB_FRELIST_RANDOM /boot/config-* + + For each kernel installed, a line with value "y" should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + This security feature reduces the predictability of the kernel slab allocator against heap overflows. + CCE-87725-8 + + + + + + + + + Disallow merge of slab caches + For reduced kernel memory fragmentation, slab caches can be merged when they share the same +size and other characteristics. This carries a risk of kernel heap overflows being able to +overwrite objects from merged caches (and more easily control cache layout), which makes such +heap attacks easier to exploit by attackers. +This configuration is available from kernel 4.13. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_SLAB_MERGE_DEFAULT, run the following command: + grep CONFIG_SLAB_MERGE_DEFAULT /boot/config-* + + Configs with value 'n' are not explicitly set in the file, so either commented lines or no + lines should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + Disabling the merge of slabs of similar sizes prevents the kernel from +merging a seemingly useless but vulnerable slab with a useful and valuable slab. +This increase the risk that a heap overflow could overwrite objects from merged caches, +with unmerged caches the heap overflow would only affect the objects in the same cache. +Overall, this reduces the kernel attack surface area by isolating slabs from each other. + CCE-88122-7 + + + + + + + + + Enable SLUB debugging support + SLUB has extensive debug support features and this allows the allocator validation checking to +be enabled. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_SLUB_DEBUG, run the following command: + grep CONFIG_SLUB_DEBUG /boot/config-* + + For each kernel installed, a line with value "y" should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + This activates the checking of the memory allocator structures and resets to zero the zones +allocated when they are released. + CCE-88275-3 + + + + + + + + + Stack Protector buffer overlow detection + This feature puts, at the beginning of functions, a canary value on the stack just before the +return address, and validates the value just before actually returning. +This configuration is available from kernel 4.18. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_STACKPROTECTOR, run the following command: + grep CONFIG_STACKPROTECTOR /boot/config-* + + For each kernel installed, a line with value "y" should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + This halts the program when a stack overflow is detected, potentially reducing the impact of +exploits. + CCE-88055-9 + + + + + + + + + Strong Stack Protector + This features adds canary logic protection to more kinds of vulnerable functions than +CONFIG_STACKPROTECTOR, but not to all functions so that performance is not severily impacted. +This configuration is available from kernel 4.18. +This config requires gcc version 4.9 or above, or a distribution gcc with the feature +backported ("-fstack-protector-strong"). + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_STACKPROTECTOR_STRONG, run the following command: + grep CONFIG_STACKPROTECTOR_STRONG /boot/config-* + + For each kernel installed, a line with value "y" should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + This provides a mechanism that protects more vulnerable functions than CONFIG_STACKPROTECTOR, +balancing between security and performance. + CCE-88036-9 + + + + + + + + + Make the kernel text and rodata read-only + When set, kernel text and rodata memory will be made read-only, and non-text memory will be made non-executable. +This configuration is available from kernel 4.11. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_STRICT_KERNEL_RWX, run the following command: + grep CONFIG_STRICT_KERNEL_RWX /boot/config-* + + For each kernel installed, a line with value "y" should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + This provides protection against certain security exploits (e.g. executing the heap or modifying text) + CCE-85993-4 + + + + + + + + + Make the module text and rodata read-only + When set, module text and rodata memory will be made read-only, and non-text memory will be made non-executable. +This configuration is available from kernel 4.11. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_STRICT_MODULE_RWX, run the following command: + grep CONFIG_STRICT_MODULE_RWX /boot/config-* + + For each kernel installed, a line with value "y" should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + This provides protection against certain security exploits (e.g. executing the heap or modifying text) + CCE-89227-3 + + + + + + + + + Enable TCP/IP syncookie support + Normal TCP/IP networking is open to an attack known as SYN flooding. +It is denial-of-service attack that prevents legitimate remote users from being able to connect +to your computer during an ongoing attack. + +When enabled the TCP/IP stack will use a cryptographic challenge protocol known as SYN cookies +to enable legitimate users to continue to connect, even when your machine is under attack. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_SYN_COOKIES, run the following command: + grep CONFIG_SYN_COOKIES /boot/config-* + + For each kernel installed, a line with value "y" should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + SYN cookies provide protection against SYN flooding attacks. + CCE-87330-7 + + + + + + + + + Unmap kernel when running in userspace (aka KAISER) + Speculation attacks against some high-performance processors can be used to bypass MMU +permission checks and leak kernel data to userspace. This can be defended against by unmapping +the kernel when running in userspace, mapping it back in on exception entry via a trampoline +page in the vector table. +This configuration is available from kernel 4.16, but may be available if backported +by distros. +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_UNMAP_KERNEL_AT_EL0, run the following command: + grep CONFIG_UNMAP_KERNEL_AT_EL0 /boot/config-* + + For each kernel installed, a line with value "y" should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + This is a countermeasure to the Meltdown attack. + + CCE-89179-6 + + + + + + + + + User a virtually-mapped stack + Enable this to use virtually-mapped kernel stacks with guard pages. +This configuration is available from kernel 4.9. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_VMAP_STACK, run the following command: + grep CONFIG_VMAP_STACK /boot/config-* + + For each kernel installed, a line with value "y" should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + This causes kernel stack overflows to be caught immediately rather than causing difficult-to-diagnose corruption. + CCE-86251-6 + + + + + + + + + Disable x86 vsyscall emulation + Disabling it is roughly equivalent to booting with vsyscall=none, except that it will also +disable the helpful warning if a program tries to use a vsyscall. With this option set to N, +offending programs will just segfault, citing addresses of the form 0xffffffffff600?00. +This configuration is available from kernel 3.19. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_X86_VSYSCALL_EMULATION, run the following command: + grep CONFIG_X86_VSYSCALL_EMULATION /boot/config-* + + Configs with value 'n' are not explicitly set in the file, so either commented lines or no + lines should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + The vsyscall table is no longer required and is a potential source of ROP gadgets. + CCE-87883-5 + + + + + + + + + Kernel GCC plugin configuration + Contains rules that check the configuration of GCC plugins used by the compiler + + + Generate some entropy during boot and runtime + Instrument some kernel code to extract some entropy from both original and artificially created +program state. This will help especially embedded systems where there is little 'natural' source +of entropy normally. + +This configuration is available from kernel 4.9, but may be available if backported +by distros. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_GCC_PLUGIN_LATENT_ENTROPY, run the following command: + grep CONFIG_GCC_PLUGIN_LATENT_ENTROPY /boot/config-* + + For each kernel installed, a line with value "y" should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + Note that entropy extracted this way is not cryptographically secure! + There is a performance cost during the boot process (about 0.5%) and fork and irq processing. + This helps generate entropy during startup and is particularly relevant for devices with +inappropriate entropy sources. + CCE-87034-5 + + + + + + + + + Force initialization of variables containing userspace addresses + While the kernel is built with warnings enabled for any missed stack variable initializations, +this warning is silenced for anything passed by reference to another function, under the +occasionally misguided assumption that the function will do the initialization. As this +regularly leads to exploitable flaws, this plugin is available to identify and zero-initialize +such variables, depending on the chosen level of coverage. +This configuration is available from kernel 4.11, but may be available if backported +by distros. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_GCC_PLUGIN_STRUCTLEAK, run the following command: + grep CONFIG_GCC_PLUGIN_STRUCTLEAK /boot/config-* + + For each kernel installed, a line with value "y" should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + Initializing structures from userspace can prevent some classes of information exposure. + CCE-87046-9 + + + + + + + + + + + Configure Syslog + The syslog service has been the default Unix logging mechanism for +many years. It has a number of downsides, including inconsistent log format, +lack of authentication for received messages, and lack of authentication, +encryption, or reliable transport for messages sent over a network. However, +due to its long history, syslog is a de facto standard which is supported by +almost all Unix applications. + + +In Red Hat Enterprise Linux 8, rsyslog has replaced ksyslogd as the +syslog daemon of choice, and it includes some additional security features +such as reliable, connection-oriented (i.e. TCP) transmission of logs, the +option to log to database formats, and the encryption of log data en route to +a central logging server. +This section discusses how to configure rsyslog for +best effect, and how to use tools provided with the system to maintain and +monitor logs. + + + Ensure rsyslog-gnutls is installed + TLS protocol support for rsyslog is installed. +The rsyslog-gnutls package can be installed with the following command: + +$ sudo yum install rsyslog-gnutls + BP28(R43) + CCI-000366 + FTP_ITC_EXT.1.1 + SRG-OS-000480-GPOS-00227 + SRG-OS-000120-GPOS-00061 + RHEL-08-030680 + SV-230478r744011_rule + The rsyslog-gnutls package provides Transport Layer Security (TLS) support +for the rsyslog daemon, which enables secure remote logging. + CCE-82859-0 + +package --add=rsyslog-gnutls + + include install_rsyslog-gnutls + +class install_rsyslog-gnutls { + package { 'rsyslog-gnutls': + ensure => 'installed', + } +} + + - name: Ensure rsyslog-gnutls is installed + package: + name: rsyslog-gnutls + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82859-0 + - DISA-STIG-RHEL-08-030680 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_rsyslog-gnutls_installed + + +[[packages]] +name = "rsyslog-gnutls" +version = "*" + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if ! rpm -q --quiet "rsyslog-gnutls" ; then + yum install -y "rsyslog-gnutls" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure rsyslog is Installed + Rsyslog is installed by default. The rsyslog package can be installed with the following command: $ sudo yum install rsyslog + BP28(R5) + NT28(R46) + 1 + 14 + 15 + 16 + 3 + 5 + 6 + APO11.04 + BAI03.05 + DSS05.04 + DSS05.07 + MEA02.01 + CCI-001311 + CCI-001312 + CCI-000366 + 164.312(a)(2)(ii) + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + CM-6(a) + PR.PT-1 + FTP_ITC_EXT.1.1 + SRG-OS-000479-GPOS-00224 + SRG-OS-000051-GPOS-00024 + SRG-OS-000480-GPOS-00227 + RHEL-08-030670 + 4.2.1.1 + SV-230477r627750_rule + The rsyslog package provides the rsyslog daemon, which provides +system logging services. + CCE-80847-7 + +package --add=rsyslog + + include install_rsyslog + +class install_rsyslog { + package { 'rsyslog': + ensure => 'installed', + } +} + + - name: Ensure rsyslog is installed + package: + name: rsyslog + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80847-7 + - DISA-STIG-RHEL-08-030670 + - NIST-800-53-CM-6(a) + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_rsyslog_installed + + +[[packages]] +name = "rsyslog" +version = "*" + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if ! rpm -q --quiet "rsyslog" ; then + yum install -y "rsyslog" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Enable rsyslog Service + The rsyslog service provides syslog-style logging by default on Red Hat Enterprise Linux 8. + +The rsyslog service can be enabled with the following command: +$ sudo systemctl enable rsyslog.service + BP28(R5) + NT28(R46) + 1 + 12 + 13 + 14 + 15 + 16 + 2 + 3 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO13.01 + BAI03.05 + BAI04.04 + DSS01.03 + DSS03.05 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + CCI-001311 + CCI-001312 + CCI-001557 + CCI-001851 + CCI-000366 + 164.312(a)(2)(ii) + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.2 + A.12.1.3 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.17.2.1 + CM-6(a) + AU-4(1) + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.DS-4 + PR.PT-1 + SRG-OS-000480-GPOS-00227 + RHEL-08-010561 + 4.2.1.2 + SV-230298r627750_rule + The rsyslog service must be running in order to provide +logging services, which are essential to system administration. + CCE-80886-5 + include enable_rsyslog + +class enable_rsyslog { + service {'rsyslog': + enable => true, + ensure => 'running', + } +} + + - name: Enable service rsyslog + block: + + - name: Gather the package facts + package_facts: + manager: auto + + - name: Enable service rsyslog + service: + name: rsyslog + enabled: 'yes' + state: started + masked: 'no' + when: + - '"rsyslog" in ansible_facts.packages' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80886-5 + - DISA-STIG-RHEL-08-010561 + - NIST-800-53-AU-4(1) + - NIST-800-53-CM-6(a) + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_rsyslog_enabled + + +[customizations.services] +enabled = ["rsyslog"] + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" unmask 'rsyslog.service' +"$SYSTEMCTL_EXEC" start 'rsyslog.service' +"$SYSTEMCTL_EXEC" enable 'rsyslog.service' + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable Logwatch on Clients if a Logserver Exists + Does your site have a central logserver which has been configured to report +on logs received from all systems? If so: +$ sudo rm /etc/cron.daily/0logwatch +If no logserver exists, it will be necessary for each system to run +Logwatch individually. Using a central logserver provides the security and +reliability benefits discussed earlier, and also makes monitoring logs +easier and less time-intensive for administrators. + + + + Configure Logwatch on the Central Log Server + Is this system the central log server? If so, edit the file /etc/logwatch/conf/logwatch.conf as shown below. + + Configure Logwatch HostLimit Line + On a central logserver, you want Logwatch to summarize all syslog entries, +including those which did not originate on the logserver itself. The +HostLimit setting tells Logwatch to report on all hosts, not just +the one on which it is running. + HostLimit = no + + + + + + + Configure Logwatch SplitHosts Line + If SplitHosts is set, Logwatch will separate entries by hostname. +This makes the report longer but significantly more usable. If it is not +set, then Logwatch will not report which host generated a given log entry, +and that information is almost always necessary + SplitHosts = yes + + + + + + + + Ensure Proper Configuration of Log Files + The file /etc/rsyslog.conf controls where log message are written. +These are controlled by lines called rules, which consist of a +selector and an action. +These rules are often customized depending on the role of the system, the +requirements of the environment, and whatever may enable +the administrator to most effectively make use of log data. +The default rules in Red Hat Enterprise Linux 8 are: +*.info;mail.none;authpriv.none;cron.none /var/log/messages +authpriv.* /var/log/secure +mail.* -/var/log/maillog +cron.* /var/log/cron +*.emerg * +uucp,news.crit /var/log/spooler +local7.* /var/log/boot.log +See the man page rsyslog.conf(5) for more information. +Note that the rsyslog daemon can be configured to use a timestamp format that +some log processing programs may not understand. If this occurs, +edit the file /etc/rsyslog.conf and add or edit the following line: +$ ActionFileDefaultTemplate RSYSLOG_TraditionalFileFormat + + group who owns log files + Specify group owner of all logfiles specified in +/etc/rsyslog.conf. + root + adm + root + + + User who owns log files + Specify user owner of all logfiles specified in +/etc/rsyslog.conf. + root + adm + root + syslog + + + Ensure cron Is Logging To Rsyslog + Cron logging must be implemented to spot intrusions or trace +cron job status. If cron is not logging to rsyslog, it +can be implemented by adding the following to the RULES section of +/etc/rsyslog.conf: +cron.* /var/log/cron + 1 + 14 + 15 + 16 + 3 + 5 + 6 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + BAI03.05 + DSS05.04 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + CCI-000366 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 6.1 + 0988 + 1405 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.15.2.1 + A.15.2.2 + CM-6(a) + ID.SC-4 + PR.PT-1 + FAU_GEN.1.1.c + SRG-OS-000480-GPOS-00227 + RHEL-08-030010 + SV-230387r743996_rule + Cron logging can be used to trace the successful or unsuccessful execution +of cron jobs. It can also be used to spot intrusions into the use of the cron +facility by unauthorized and malicious users. + CCE-80859-2 + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if ! grep -s "^\s*cron\.\*\s*/var/log/cron$" /etc/rsyslog.conf /etc/rsyslog.d/*.conf; then + mkdir -p /etc/rsyslog.d + echo "cron.* /var/log/cron" >> /etc/rsyslog.d/cron.conf +fi + +systemctl restart rsyslog.service + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure Rsyslog Authenticates Off-Loaded Audit Records + Rsyslogd is a system utility providing support for message logging. Support +for both internet and UNIX domain sockets enables this utility to support both local +and remote logging. Couple this utility with gnutls (which is a secure communications +library implementing the SSL, TLS and DTLS protocols), and you have a method to securely +encrypt and off-load auditing. + +When using rsyslogd to off-load logs the remote system must be authenticated. + CCI-001851 + AU-4(1) + SRG-OS-000342-GPOS-00133 + SRG-OS-000479-GPOS-00224 + RHEL-08-030720 + SV-230482r627750_rule + The audit records generated by Rsyslog contain valuable information regarding system +configuration, user authentication, and other such information. Audit records should be +protected from unauthorized access. + CCE-86339-9 + - name: Ensure Rsyslog Authenticates Off-Loaded Audit Records + block: + + - name: Deduplicate values from /etc/rsyslog.conf + lineinfile: + path: /etc/rsyslog.conf + create: false + regexp: ^\s*{{ "$ActionSendStreamDriverAuthMode"| regex_escape }}\s + state: absent + + - name: Check if /etc/rsyslog.d exists + stat: + path: /etc/rsyslog.d + register: _etc_rsyslog_d_exists + + - name: Check if the parameter $ActionSendStreamDriverAuthMode is present in /etc/rsyslog.d + find: + paths: /etc/rsyslog.d + recurse: 'yes' + follow: 'no' + contains: ^\s*{{ "$ActionSendStreamDriverAuthMode"| regex_escape }}\s + register: _etc_rsyslog_d_has_parameter + when: _etc_rsyslog_d_exists.stat.isdir is defined and _etc_rsyslog_d_exists.stat.isdir + + - name: Remove parameter from files in /etc/rsyslog.d + lineinfile: + path: '{{ item.path }}' + create: false + regexp: ^\s*{{ "$ActionSendStreamDriverAuthMode"| regex_escape }}\s + state: absent + with_items: '{{ _etc_rsyslog_d_has_parameter.files }}' + when: _etc_rsyslog_d_has_parameter.matched + + - name: Insert correct line to /etc/rsyslog.conf + lineinfile: + path: /etc/rsyslog.conf + create: true + regexp: ^\s*{{ "$ActionSendStreamDriverAuthMode"| regex_escape }}\s + line: $ActionSendStreamDriverAuthMode x509/name + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86339-9 + - DISA-STIG-RHEL-08-030720 + - NIST-800-53-AU-4(1) + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - rsyslog_encrypt_offload_actionsendstreamdriverauthmode + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +sed -i '/^.*\$ActionSendStreamDriverAuthMode.*/d' /etc/rsyslog.conf /etc/rsyslog.d/*.conf 2> /dev/null + +if [ -e "/etc/rsyslog.d/stream_driver_auth.conf" ] ; then + + LC_ALL=C sed -i "/^\s*\$ActionSendStreamDriverAuthMode /Id" "/etc/rsyslog.d/stream_driver_auth.conf" +else + touch "/etc/rsyslog.d/stream_driver_auth.conf" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/rsyslog.d/stream_driver_auth.conf" + +cp "/etc/rsyslog.d/stream_driver_auth.conf" "/etc/rsyslog.d/stream_driver_auth.conf.bak" +# Insert at the end of the file +printf '%s\n' "\$ActionSendStreamDriverAuthMode x509/name" >> "/etc/rsyslog.d/stream_driver_auth.conf" +# Clean up after ourselves. +rm "/etc/rsyslog.d/stream_driver_auth.conf.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure Rsyslog Encrypts Off-Loaded Audit Records + Rsyslogd is a system utility providing support for message logging. Support +for both internet and UNIX domain sockets enables this utility to support both local +and remote logging. Couple this utility with gnutls (which is a secure communications +library implementing the SSL, TLS and DTLS protocols), and you have a method to securely +encrypt and off-load auditing. + +When using rsyslogd to off-load logs off a encrpytion system must be used. + CCI-001851 + AU-4(1) + SRG-OS-000342-GPOS-00133 + SRG-OS-000479-GPOS-00224 + RHEL-08-030710 + SV-230481r818840_rule + The audit records generated by Rsyslog contain valuable information regarding system +configuration, user authentication, and other such information. Audit records should be +protected from unauthorized access. + CCE-86098-1 + - name: Ensure Rsyslog Encrypts Off-Loaded Audit Records + block: + + - name: Deduplicate values from /etc/rsyslog.conf + lineinfile: + path: /etc/rsyslog.conf + create: false + regexp: '^\s*{{ "$ActionSendStreamDriverMode"| regex_escape }} ' + state: absent + + - name: Check if /etc/rsyslog.d exists + stat: + path: /etc/rsyslog.d + register: _etc_rsyslog_d_exists + + - name: Check if the parameter $ActionSendStreamDriverMode is present in /etc/rsyslog.d + find: + paths: /etc/rsyslog.d + recurse: 'yes' + follow: 'no' + contains: '^\s*{{ "$ActionSendStreamDriverMode"| regex_escape }} ' + register: _etc_rsyslog_d_has_parameter + when: _etc_rsyslog_d_exists.stat.isdir is defined and _etc_rsyslog_d_exists.stat.isdir + + - name: Remove parameter from files in /etc/rsyslog.d + lineinfile: + path: '{{ item.path }}' + create: false + regexp: '^\s*{{ "$ActionSendStreamDriverMode"| regex_escape }} ' + state: absent + with_items: '{{ _etc_rsyslog_d_has_parameter.files }}' + when: _etc_rsyslog_d_has_parameter.matched + + - name: Insert correct line to /etc/rsyslog.conf + lineinfile: + path: /etc/rsyslog.conf + create: true + regexp: '^\s*{{ "$ActionSendStreamDriverMode"| regex_escape }} ' + line: $ActionSendStreamDriverMode 1 + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86098-1 + - DISA-STIG-RHEL-08-030710 + - NIST-800-53-AU-4(1) + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - rsyslog_encrypt_offload_actionsendstreamdrivermode + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if [ -e "/etc/rsyslog.d/encrypt.conf" ] ; then + + LC_ALL=C sed -i "/^\s*\$ActionSendStreamDriverMode /Id" "/etc/rsyslog.d/encrypt.conf" +else + touch "/etc/rsyslog.d/encrypt.conf" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/rsyslog.d/encrypt.conf" + +cp "/etc/rsyslog.d/encrypt.conf" "/etc/rsyslog.d/encrypt.conf.bak" +# Insert at the end of the file +printf '%s\n' "\$ActionSendStreamDriverMode 1" >> "/etc/rsyslog.d/encrypt.conf" +# Clean up after ourselves. +rm "/etc/rsyslog.d/encrypt.conf.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure Rsyslog Encrypts Off-Loaded Audit Records + Rsyslogd is a system utility providing support for message logging. Support +for both internet and UNIX domain sockets enables this utility to support both local +and remote logging. Couple this utility with gnutls (which is a secure communications +library implementing the SSL, TLS and DTLS protocols), and you have a method to securely +encrypt and off-load auditing. + +When using rsyslogd to off-load logs off an encryption system must be used. + CCI-001851 + AU-4(1) + SRG-OS-000342-GPOS-00133 + SRG-OS-000479-GPOS-00224 + RHEL-08-030710 + SV-230481r818840_rule + The audit records generated by Rsyslog contain valuable information regarding system +configuration, user authentication, and other such information. Audit records should be +protected from unauthorized access. + CCE-85992-6 + - name: Ensure Rsyslog Encrypts Off-Loaded Audit Records + block: + + - name: Deduplicate values from /etc/rsyslog.conf + lineinfile: + path: /etc/rsyslog.conf + create: false + regexp: '^\s*{{ "$DefaultNetstreamDriver"| regex_escape }} ' + state: absent + + - name: Check if /etc/rsyslog.d exists + stat: + path: /etc/rsyslog.d + register: _etc_rsyslog_d_exists + + - name: Check if the parameter $DefaultNetstreamDriver is present in /etc/rsyslog.d + find: + paths: /etc/rsyslog.d + recurse: 'yes' + follow: 'no' + contains: '^\s*{{ "$DefaultNetstreamDriver"| regex_escape }} ' + register: _etc_rsyslog_d_has_parameter + when: _etc_rsyslog_d_exists.stat.isdir is defined and _etc_rsyslog_d_exists.stat.isdir + + - name: Remove parameter from files in /etc/rsyslog.d + lineinfile: + path: '{{ item.path }}' + create: false + regexp: '^\s*{{ "$DefaultNetstreamDriver"| regex_escape }} ' + state: absent + with_items: '{{ _etc_rsyslog_d_has_parameter.files }}' + when: _etc_rsyslog_d_has_parameter.matched + + - name: Insert correct line to /etc/rsyslog.conf + lineinfile: + path: /etc/rsyslog.conf + create: true + regexp: '^\s*{{ "$DefaultNetstreamDriver"| regex_escape }} ' + line: $DefaultNetstreamDriver gtls + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-85992-6 + - DISA-STIG-RHEL-08-030710 + - NIST-800-53-AU-4(1) + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - rsyslog_encrypt_offload_defaultnetstreamdriver + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if [ -e "/etc/rsyslog.d/encrypt.conf" ] ; then + + LC_ALL=C sed -i "/^\s*\$DefaultNetstreamDriver /Id" "/etc/rsyslog.d/encrypt.conf" +else + touch "/etc/rsyslog.d/encrypt.conf" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/rsyslog.d/encrypt.conf" + +cp "/etc/rsyslog.d/encrypt.conf" "/etc/rsyslog.d/encrypt.conf.bak" +# Insert at the end of the file +printf '%s\n' "\$DefaultNetstreamDriver gtls" >> "/etc/rsyslog.d/encrypt.conf" +# Clean up after ourselves. +rm "/etc/rsyslog.d/encrypt.conf.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure Log Files Are Owned By Appropriate Group + The group-owner of all log files written by +rsyslog should be . +These log files are determined by the second part of each Rule line in +/etc/rsyslog.conf and typically all appear in /var/log. +For each log file LOGFILE referenced in /etc/rsyslog.conf, +run the following command to inspect the file's group owner: +$ ls -l LOGFILE +If the owner is not , run the following command to +correct this: +$ sudo chgrp LOGFILE + BP28(R46) + BP28(R5) + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + CCI-001314 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + 0988 + 1405 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + Req-10.5.1 + Req-10.5.2 + The log files generated by rsyslog contain valuable information regarding system +configuration, user authentication, and other such information. Log files should be +protected from unauthorized access. + CCE-80860-0 + + + + + + + + + Ensure Log Files Are Owned By Appropriate User + The owner of all log files written by +rsyslog should be . +These log files are determined by the second part of each Rule line in +/etc/rsyslog.conf and typically all appear in /var/log. +For each log file LOGFILE referenced in /etc/rsyslog.conf, +run the following command to inspect the file's owner: +$ ls -l LOGFILE +If the owner is not , run the following command to +correct this: +$ sudo chown LOGFILE + BP28(R46) + BP28(R5) + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + CCI-001314 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + 0988 + 1405 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + Req-10.5.1 + Req-10.5.2 + The log files generated by rsyslog contain valuable information regarding system +configuration, user authentication, and other such information. Log files should be +protected from unauthorized access. + CCE-80861-8 + + + + + + + + + Ensure System Log Files Have Correct Permissions + The file permissions for all log files written by rsyslog should +be set to 600, or more restrictive. These log files are determined by the +second part of each Rule line in /etc/rsyslog.conf and typically +all appear in /var/log. For each log file LOGFILE +referenced in /etc/rsyslog.conf, run the following command to +inspect the file's permissions: +$ ls -l LOGFILE +If the permissions are not 600 or more restrictive, run the following +command to correct this: +$ sudo chmod 0600 LOGFILE" + BP28(R36) + CCI-001314 + 0988 + 1405 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-6(a) + AC-6(1) + Req-10.5.1 + Req-10.5.2 + 4.2.3 + Log files can contain valuable information regarding system +configuration. If the system log files are not protected unauthorized +users could change the logged data, eliminating their forensic value. + CCE-80862-6 + - name: Set rsyslog logfile configuration facts + set_fact: + rsyslog_etc_config: /etc/rsyslog.conf + desired_perm_mode: '600' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80862-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.1 + - PCI-DSS-Req-10.5.2 + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - rsyslog_files_permissions + +- name: Get IncludeConfig directive + shell: | + set -o pipefail + grep -e '$IncludeConfig' {{ rsyslog_etc_config }} | cut -d ' ' -f 2 || true + register: rsyslog_old_inc + changed_when: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80862-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.1 + - PCI-DSS-Req-10.5.2 + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - rsyslog_files_permissions + +- name: Get include files directives + shell: | + set -o pipefail + grep -oP '^\s*include\s*\(\s*file.*' {{ rsyslog_etc_config }} |cut -d"\"" -f 2 || true + register: rsyslog_new_inc + changed_when: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80862-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.1 + - PCI-DSS-Req-10.5.2 + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - rsyslog_files_permissions + +- name: Expand glob expressions + shell: | + set -o pipefail + eval printf '%s\\n' {{ item }} + register: include_config_output + loop: '{{ rsyslog_old_inc.stdout_lines + rsyslog_new_inc.stdout_lines }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80862-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.1 + - PCI-DSS-Req-10.5.2 + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - rsyslog_files_permissions + +- name: List all config files + shell: find {{ item }} -not -path "*/.*" -type f + loop: '{{ include_config_output.results|map(attribute=''stdout_lines'')|list|flatten + }}' + register: rsyslog_config_files + failed_when: false + changed_when: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80862-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.1 + - PCI-DSS-Req-10.5.2 + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - rsyslog_files_permissions + +- name: Extract log files + shell: | + set -o pipefail + grep -oP '^[^(\s|#|\$)]+[\s]+.*[\s]+-?(/+[^:;\s]+);*\.*$' {{ item }} |awk '{print $NF}'|sed -e 's/^-//' || true + loop: '{{ rsyslog_config_files.results|map(attribute=''stdout_lines'')|list|flatten|unique + + [ rsyslog_etc_config ] }}' + register: log_files + changed_when: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80862-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.1 + - PCI-DSS-Req-10.5.2 + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - rsyslog_files_permissions + +- name: Setup log files permissions + ignore_errors: true + file: + path: '{{ item }}' + mode: '{{ desired_perm_mode }}' + loop: '{{ log_files.results|map(attribute=''stdout_lines'')|list|flatten|unique + }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80862-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.1 + - PCI-DSS-Req-10.5.2 + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - rsyslog_files_permissions + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# List of log file paths to be inspected for correct permissions +# * Primarily inspect log file paths listed in /etc/rsyslog.conf +RSYSLOG_ETC_CONFIG="/etc/rsyslog.conf" +# * And also the log file paths listed after rsyslog's $IncludeConfig directive +# (store the result into array for the case there's shell glob used as value of IncludeConfig) +readarray -t OLD_INC < <(grep -e "\$IncludeConfig[[:space:]]\+[^[:space:];]\+" /etc/rsyslog.conf | cut -d ' ' -f 2) +readarray -t RSYSLOG_INCLUDE_CONFIG < <(for INCPATH in "${OLD_INC[@]}"; do eval printf '%s\\n' "${INCPATH}"; done) +readarray -t NEW_INC < <(awk '/)/{f=0} /include\(/{f=1} f{nf=gensub("^(include\\(|\\s*)file=\"(\\S+)\".*","\\2",1); if($0!=nf){print nf}}' /etc/rsyslog.conf) +readarray -t RSYSLOG_INCLUDE < <(for INCPATH in "${NEW_INC[@]}"; do eval printf '%s\\n' "${INCPATH}"; done) + +# Declare an array to hold the final list of different log file paths +declare -a LOG_FILE_PATHS + +# Array to hold all rsyslog config entries +RSYSLOG_CONFIGS=() +RSYSLOG_CONFIGS=("${RSYSLOG_ETC_CONFIG}" "${RSYSLOG_INCLUDE_CONFIG[@]}" "${RSYSLOG_INCLUDE[@]}") + +# Get full list of files to be checked +# RSYSLOG_CONFIGS may contain globs such as +# /etc/rsyslog.d/*.conf /etc/rsyslog.d/*.frule +# So, loop over the entries in RSYSLOG_CONFIGS and use find to get the list of included files. +RSYSLOG_CONFIG_FILES=() +for ENTRY in "${RSYSLOG_CONFIGS[@]}" +do + # If directory, rsyslog will search for config files in recursively. + # However, files in hidden sub-directories or hidden files will be ignored. + if [ -d "${ENTRY}" ] + then + readarray -t FINDOUT < <(find "${ENTRY}" -not -path '*/.*' -type f) + RSYSLOG_CONFIG_FILES+=("${FINDOUT[@]}") + elif [ -f "${ENTRY}" ] + then + RSYSLOG_CONFIG_FILES+=("${ENTRY}") + else + echo "Invalid include object: ${ENTRY}" + fi +done + +# Browse each file selected above as containing paths of log files +# ('/etc/rsyslog.conf' and '/etc/rsyslog.d/*.conf' in the default configuration) +for LOG_FILE in "${RSYSLOG_CONFIG_FILES[@]}" +do + # From each of these files extract just particular log file path(s), thus: + # * Ignore lines starting with space (' '), comment ('#"), or variable syntax ('$') characters, + # * Ignore empty lines, + # * Strip quotes and closing brackets from paths. + # * Ignore paths that match /dev|/etc.*\.conf, as those are paths, but likely not log files + # * From the remaining valid rows select only fields constituting a log file path + # Text file column is understood to represent a log file path if and only if all of the following are met: + # * it contains at least one slash '/' character, + # * it is preceded by space + # * it doesn't contain space (' '), colon (':'), and semicolon (';') characters + # Search log file for path(s) only in case it exists! + if [[ -f "${LOG_FILE}" ]] + then + NORMALIZED_CONFIG_FILE_LINES=$(sed -e "/^[#|$]/d" "${LOG_FILE}") + LINES_WITH_PATHS=$(grep '[^/]*\s\+\S*/\S\+$' <<< "${NORMALIZED_CONFIG_FILE_LINES}") + FILTERED_PATHS=$(awk '{if(NF>=2&&($2~/^\//||$2~/^-\//)){sub(/^-\//,"/",$2);print $2}}' <<< "${LINES_WITH_PATHS}") + CLEANED_PATHS=$(sed -e "s/[\"')]//g; /\\/etc.*\.conf/d; /\\/dev\\//d" <<< "${FILTERED_PATHS}") + MATCHED_ITEMS=$(sed -e "/^$/d" <<< "${CLEANED_PATHS}") + # Since above sed command might return more than one item (delimited by newline), split the particular + # matches entries into new array specific for this log file + readarray -t ARRAY_FOR_LOG_FILE <<< "$MATCHED_ITEMS" + # Concatenate the two arrays - previous content of $LOG_FILE_PATHS array with + # items from newly created array for this log file + LOG_FILE_PATHS+=("${ARRAY_FOR_LOG_FILE[@]}") + # Delete the temporary array + unset ARRAY_FOR_LOG_FILE + fi +done + +DESIRED_PERM_MOD=600 + +# Correct the form o +for LOG_FILE_PATH in "${LOG_FILE_PATHS[@]}" +do + # Sanity check - if particular $LOG_FILE_PATH is empty string, skip it from further processing + if [ -z "$LOG_FILE_PATH" ] + then + continue + fi + + # Also for each log file check if its permissions differ from 600. If so, correct them + if [ -f "$LOG_FILE_PATH" ] && [ "$(/usr/bin/stat -c %a "$LOG_FILE_PATH")" -ne $DESIRED_PERM_MOD ] + then + /bin/chmod $DESIRED_PERM_MOD "$LOG_FILE_PATH" + fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure remote access methods are monitored in Rsyslog + Logging of remote access methods must be implemented to help identify cyber +attacks and ensure ongoing compliance with remote access policies are being +audited and upheld. An examples of a remote access method is the use of the +Remote Desktop Protocol (RDP) from an external, non-organization controlled +network. The /etc/rsyslog.conf or +/etc/rsyslog.d/*.conf file should contain a match for the following +selectors: auth.*, authpriv.*, and daemon.*. If +not, use the following as an example configuration: +auth.*;authpriv.*;daemon.* /var/log/secure + CCI-000067 + AC-17(1) + SRG-OS-000032-GPOS-00013 + RHEL-08-010070 + SV-230228r627750_rule + Logging remote access methods can be used to trace the decrease the risks +associated with remote user access management. It can also be used to spot +cyber attacks and ensure ongoing compliance with organizational policies +surrounding the use of remote access methods. + CCE-83426-7 + - name: 'Ensure remote access methods are monitored in Rsyslog: Set facts' + set_fact: + conf_files: + - /etc/rsyslog.conf + remote_methods: + - selector: auth.* + regexp: ^.*auth\.\*.*$ + - selector: authpriv.* + regexp: ^.*authpriv\.\*.*$ + - selector: daemon.* + regexp: ^.*daemon\.\*.*$ + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83426-7 + - DISA-STIG-RHEL-08-010070 + - NIST-800-53-AC-17(1) + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - rsyslog_remote_access_monitoring + +- name: 'Ensure remote access methods are monitored in Rsyslog: Ensure rsyslog.conf + exists' + file: + path: '{{ conf_files.0 }}' + state: touch + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83426-7 + - DISA-STIG-RHEL-08-010070 + - NIST-800-53-AC-17(1) + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - rsyslog_remote_access_monitoring + +- name: 'Ensure remote access methods are monitored in Rsyslog: Gather conf.d files' + find: + patterns: + - '*.conf' + paths: + - /etc/rsyslog.d + register: rsyslogd + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83426-7 + - DISA-STIG-RHEL-08-010070 + - NIST-800-53-AC-17(1) + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - rsyslog_remote_access_monitoring + +- name: 'Ensure remote access methods are monitored in Rsyslog: Set conf file(s)' + set_fact: + conf_files: '{{ conf_files + [item.path] }}' + loop: '{{ rsyslogd.files }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - rsyslogd.matched > 0 + tags: + - CCE-83426-7 + - DISA-STIG-RHEL-08-010070 + - NIST-800-53-AC-17(1) + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - rsyslog_remote_access_monitoring + +- name: 'Ensure remote access methods are monitored in Rsyslog: Check for existing + values' + lineinfile: + path: '{{ item.1 }}' + regexp: '{{ item.0.regexp }}' + state: absent + check_mode: true + changed_when: false + register: remote_method_values + loop: '{{ remote_methods|product(conf_files)|list }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83426-7 + - DISA-STIG-RHEL-08-010070 + - NIST-800-53-AC-17(1) + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - rsyslog_remote_access_monitoring + +- name: 'Ensure remote access methods are monitored in Rsyslog: Configure' + lineinfile: + path: /etc/rsyslog.conf + line: '{{ item.item.0.selector }} /var/log/secure' + insertafter: ^.*\/var\/log\/secure.*$ + create: true + loop: '{{ remote_method_values.results }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - item.found == 0 + tags: + - CCE-83426-7 + - DISA-STIG-RHEL-08-010070 + - NIST-800-53-AC-17(1) + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - rsyslog_remote_access_monitoring + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +declare -A REMOTE_METHODS=( ['auth.*']='^.*auth\.\*.*$' ['authpriv.*']='^.*authpriv\.\*.*$' ['daemon.*']='^.*daemon\.\*.*$' ) + +if [[ ! -f /etc/rsyslog.conf ]]; then + # Something is not right, create the file + touch /etc/rsyslog.conf +fi + +APPEND_LINE=$(sed -rn '/^\S+\s+\/var\/log\/secure$/p' /etc/rsyslog.conf) + +# Loop through the remote methods associative array +for K in "${!REMOTE_METHODS[@]}" +do + # Check to see if selector/value exists + if ! grep -rq "${REMOTE_METHODS[$K]}" /etc/rsyslog.*; then + # Make sure we have a line to insert after, otherwise append to end + if [[ ! -z ${APPEND_LINE} ]]; then + # Add selector to file + sed -r -i "0,/^(\S+\s+\/var\/log\/secure$)/s//\1\n${K} \/var\/log\/secure/" /etc/rsyslog.conf + else + echo "${K} /var/log/secure" >> /etc/rsyslog.conf + fi + fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + systemd-journald + systemd-journald is a system service that collects and stores +logging data. It creates and maintains structured, indexed +journals based on logging information that is received from a +variety of sources. + +For more information on systemd-journald and additional systemd-journald configuration options, see +https://systemd.io/. + + Enable systemd-journald Service + The systemd-journald service is an essential component of +systemd. + +The systemd-journald service can be enabled with the following command: +$ sudo systemctl enable systemd-journald.service + CCI-001665 + SC-24 + SRG-OS-000269-GPOS-00103 + 4.2.2.2 + In the event of a system failure, Red Hat Enterprise Linux 8 must preserve any information necessary to determine cause of failure and any information necessary to return to operations with least disruption to system processes. + + CCE-85921-5 + include enable_systemd-journald + +class enable_systemd-journald { + service {'systemd-journald': + enable => true, + ensure => 'running', + } +} + + - name: Enable service systemd-journald + block: + + - name: Gather the package facts + package_facts: + manager: auto + + - name: Enable service systemd-journald + service: + name: systemd-journald + enabled: 'yes' + state: started + masked: 'no' + when: + - '"systemd" in ansible_facts.packages' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-85921-5 + - NIST-800-53-SC-24 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_systemd-journald_enabled + + +[customizations.services] +enabled = ["systemd-journald"] + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" unmask 'systemd-journald.service' +"$SYSTEMCTL_EXEC" start 'systemd-journald.service' +"$SYSTEMCTL_EXEC" enable 'systemd-journald.service' + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure journald is configured to compress large log files + The journald system can compress large log files to avoid fill the system disk. + 4.2.2.3 + Log files that are not properly compressed run the risk of growing so large that they fill up the log partition. Valuable logging information could be lost if the log partition becomes full. + CCE-85930-6 + - name: Setting shell-quoted shell-style assignment of 'Compress' to 'yes' in '/etc/systemd/journald.conf' + block: + + - name: Check for duplicate values + lineinfile: + path: /etc/systemd/journald.conf + create: false + regexp: ^\s*Compress= + state: absent + check_mode: true + changed_when: false + register: dupes + + - name: Deduplicate values from /etc/systemd/journald.conf + lineinfile: + path: /etc/systemd/journald.conf + create: false + regexp: ^\s*Compress= + state: absent + when: dupes.found is defined and dupes.found > 1 + + - name: Insert correct line to /etc/systemd/journald.conf + lineinfile: + path: /etc/systemd/journald.conf + create: true + regexp: ^\s*Compress= + line: Compress="yes" + state: present + insertbefore: ^# Compress + validate: /usr/bin/bash -n %s + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-85930-6 + - journald_compress + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if [ -e "/etc/systemd/journald.conf" ] ; then + + LC_ALL=C sed -i "/^\s*Compress=/d" "/etc/systemd/journald.conf" +else + touch "/etc/systemd/journald.conf" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/systemd/journald.conf" + +cp "/etc/systemd/journald.conf" "/etc/systemd/journald.conf.bak" +# Insert before the line matching the regex '^#\s*Compress'. +line_number="$(LC_ALL=C grep -n "^#\s*Compress" "/etc/systemd/journald.conf.bak" | LC_ALL=C sed 's/:.*//g')" +if [ -z "$line_number" ]; then + # There was no match of '^#\s*Compress', insert at + # the end of the file. + printf '%s\n' "Compress='yes'" >> "/etc/systemd/journald.conf" +else + head -n "$(( line_number - 1 ))" "/etc/systemd/journald.conf.bak" > "/etc/systemd/journald.conf" + printf '%s\n' "Compress='yes'" >> "/etc/systemd/journald.conf" + tail -n "+$(( line_number ))" "/etc/systemd/journald.conf.bak" >> "/etc/systemd/journald.conf" +fi +# Clean up after ourselves. +rm "/etc/systemd/journald.conf.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure journald is configured to send logs to rsyslog + Data from journald may be stored in volatile memory or persisted locally. +Utilities exist to accept remote export of journald logs. + 4.2.1.3 + Storing log data on a remote host protects log integrity from local attacks. If an attacker gains root access on the local system, they could tamper with or remove log data that is stored on the local system. + CCE-85995-9 + - name: Setting shell-quoted shell-style assignment of 'ForwardToSyslog' to 'yes' + in '/etc/systemd/journald.conf' + block: + + - name: Check for duplicate values + lineinfile: + path: /etc/systemd/journald.conf + create: false + regexp: ^\s*ForwardToSyslog= + state: absent + check_mode: true + changed_when: false + register: dupes + + - name: Deduplicate values from /etc/systemd/journald.conf + lineinfile: + path: /etc/systemd/journald.conf + create: false + regexp: ^\s*ForwardToSyslog= + state: absent + when: dupes.found is defined and dupes.found > 1 + + - name: Insert correct line to /etc/systemd/journald.conf + lineinfile: + path: /etc/systemd/journald.conf + create: true + regexp: ^\s*ForwardToSyslog= + line: ForwardToSyslog="yes" + state: present + insertbefore: ^# ForwardToSyslog + validate: /usr/bin/bash -n %s + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-85995-9 + - journald_forward_to_syslog + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if [ -e "/etc/systemd/journald.conf" ] ; then + + LC_ALL=C sed -i "/^\s*ForwardToSyslog=/d" "/etc/systemd/journald.conf" +else + touch "/etc/systemd/journald.conf" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/systemd/journald.conf" + +cp "/etc/systemd/journald.conf" "/etc/systemd/journald.conf.bak" +# Insert before the line matching the regex '^#\s*ForwardToSyslog'. +line_number="$(LC_ALL=C grep -n "^#\s*ForwardToSyslog" "/etc/systemd/journald.conf.bak" | LC_ALL=C sed 's/:.*//g')" +if [ -z "$line_number" ]; then + # There was no match of '^#\s*ForwardToSyslog', insert at + # the end of the file. + printf '%s\n' "ForwardToSyslog='yes'" >> "/etc/systemd/journald.conf" +else + head -n "$(( line_number - 1 ))" "/etc/systemd/journald.conf.bak" > "/etc/systemd/journald.conf" + printf '%s\n' "ForwardToSyslog='yes'" >> "/etc/systemd/journald.conf" + tail -n "+$(( line_number ))" "/etc/systemd/journald.conf.bak" >> "/etc/systemd/journald.conf" +fi +# Clean up after ourselves. +rm "/etc/systemd/journald.conf.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure journald is configured to write log files to persistent disk + The journald system may store log files in volatile memory or locally on disk. +If the logs are only stored in volatile memory they will we lost upon reboot. + 4.2.2.4 + Log files contain valuable data and need to be persistent to aid in possible investigations. + CCE-86045-2 + - name: Setting shell-quoted shell-style assignment of 'Storage' to 'persistent' in + '/etc/systemd/journald.conf' + block: + + - name: Check for duplicate values + lineinfile: + path: /etc/systemd/journald.conf + create: false + regexp: ^\s*Storage= + state: absent + check_mode: true + changed_when: false + register: dupes + + - name: Deduplicate values from /etc/systemd/journald.conf + lineinfile: + path: /etc/systemd/journald.conf + create: false + regexp: ^\s*Storage= + state: absent + when: dupes.found is defined and dupes.found > 1 + + - name: Insert correct line to /etc/systemd/journald.conf + lineinfile: + path: /etc/systemd/journald.conf + create: true + regexp: ^\s*Storage= + line: Storage="persistent" + state: present + insertbefore: ^# Storage + validate: /usr/bin/bash -n %s + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86045-2 + - journald_storage + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if [ -e "/etc/systemd/journald.conf" ] ; then + + LC_ALL=C sed -i "/^\s*Storage=/d" "/etc/systemd/journald.conf" +else + touch "/etc/systemd/journald.conf" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/systemd/journald.conf" + +cp "/etc/systemd/journald.conf" "/etc/systemd/journald.conf.bak" +# Insert before the line matching the regex '^#\s*Storage'. +line_number="$(LC_ALL=C grep -n "^#\s*Storage" "/etc/systemd/journald.conf.bak" | LC_ALL=C sed 's/:.*//g')" +if [ -z "$line_number" ]; then + # There was no match of '^#\s*Storage', insert at + # the end of the file. + printf '%s\n' "Storage='persistent'" >> "/etc/systemd/journald.conf" +else + head -n "$(( line_number - 1 ))" "/etc/systemd/journald.conf.bak" > "/etc/systemd/journald.conf" + printf '%s\n' "Storage='persistent'" >> "/etc/systemd/journald.conf" + tail -n "+$(( line_number ))" "/etc/systemd/journald.conf.bak" >> "/etc/systemd/journald.conf" +fi +# Clean up after ourselves. +rm "/etc/systemd/journald.conf.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Ensure All Logs are Rotated by logrotate + +Edit the file /etc/logrotate.d/syslog. Find the first + +line, which should look like this (wrapped for clarity): +/var/log/messages /var/log/secure /var/log/maillog /var/log/spooler \ + /var/log/boot.log /var/log/cron { +Edit this line so that it contains a one-space-separated +listing of each log file referenced in /etc/rsyslog.conf. + +All logs in use on a system must be rotated regularly, or the +log files will consume disk space over time, eventually interfering +with system operation. The file /etc/logrotate.d/syslog is the +configuration file used by the logrotate program to maintain all +log files written by syslog. By default, it rotates logs weekly and +stores four archival copies of each log. These settings can be +modified by editing /etc/logrotate.conf, but the defaults are +sufficient for purposes of this guide. + +Note that logrotate is run nightly by the cron job +/etc/cron.daily/logrotate. If particularly active logs need to be +rotated more often than once a day, some other mechanism must be +used. + + Ensure Logrotate Runs Periodically + The logrotate utility allows for the automatic rotation of +log files. The frequency of rotation is specified in /etc/logrotate.conf, +which triggers a cron task. To configure logrotate to run daily, add or correct +the following line in /etc/logrotate.conf: +# rotate log files frequency +daily + BP28(R43) + NT12(R18) + 1 + 14 + 15 + 16 + 3 + 5 + 6 + APO11.04 + BAI03.05 + DSS05.04 + DSS05.07 + MEA02.01 + CCI-000366 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + CM-6(a) + PR.PT-1 + Req-10.7 + 4.3 + Log files that are not properly rotated run the risk of growing so large +that they fill up the /var/log partition. Valuable logging information could be lost +if the /var/log partition becomes full. + CCE-80794-1 + - name: Configure daily log rotation in /etc/logrotate.conf + lineinfile: + create: true + dest: /etc/logrotate.conf + regexp: ^daily$ + line: daily + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80794-1 + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.7 + - configure_strategy + - ensure_logrotate_activated + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Make sure daily log rotation setting is not overriden in /etc/logrotate.conf + lineinfile: + create: false + dest: /etc/logrotate.conf + regexp: ^[\s]*(weekly|monthly|yearly)$ + state: absent + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80794-1 + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.7 + - configure_strategy + - ensure_logrotate_activated + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Configure cron.daily if not already + block: + + - name: Add shebang + lineinfile: + path: /etc/cron.daily/logrotate + line: '#!/bin/sh' + insertbefore: BOF + create: true + + - name: Add logrotate call + lineinfile: + path: /etc/cron.daily/logrotate + line: /usr/sbin/logrotate /etc/logrotate.conf + regexp: ^[\s]*/usr/sbin/logrotate[\s\S]*/etc/logrotate.conf$ + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80794-1 + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.7 + - configure_strategy + - ensure_logrotate_activated + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,{{ %23%20see%20%22man%20logrotate%22%20for%20details%0A%23%20rotate%20log%20files%20daily%0Adaily%0A%0A%23%20keep%204%20weeks%20worth%20of%20backlogs%0Arotate%2030%0A%0A%23%20create%20new%20%28empty%29%20log%20files%20after%20rotating%20old%20ones%0Acreate%0A%0A%23%20use%20date%20as%20a%20suffix%20of%20the%20rotated%20file%0Adateext%0A%0A%23%20uncomment%20this%20if%20you%20want%20your%20log%20files%20compressed%0A%23compress%0A%0A%23%20RPM%20packages%20drop%20log%20rotation%20information%20into%20this%20directory%0Ainclude%20/etc/logrotate.d%0A%0A%23%20system-specific%20logs%20may%20be%20also%20be%20configured%20here. }} + mode: 0644 + path: /etc/logrotate.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +LOGROTATE_CONF_FILE="/etc/logrotate.conf" +CRON_DAILY_LOGROTATE_FILE="/etc/cron.daily/logrotate" + +# daily rotation is configured +grep -q "^daily$" $LOGROTATE_CONF_FILE|| echo "daily" >> $LOGROTATE_CONF_FILE + +# remove any line configuring weekly, monthly or yearly rotation +sed -i '/^\s*\(weekly\|monthly\|yearly\).*$/d' $LOGROTATE_CONF_FILE + +# configure cron.daily if not already +if ! grep -q "^[[:space:]]*/usr/sbin/logrotate[[:alnum:][:blank:][:punct:]]*$LOGROTATE_CONF_FILE$" $CRON_DAILY_LOGROTATE_FILE; then + echo "#!/bin/sh" > $CRON_DAILY_LOGROTATE_FILE + echo "/usr/sbin/logrotate $LOGROTATE_CONF_FILE" >> $CRON_DAILY_LOGROTATE_FILE +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Configure rsyslogd to Accept Remote Messages If Acting as a Log Server + By default, rsyslog does not listen over the network +for log messages. If needed, modules can be enabled to allow +the rsyslog daemon to receive messages from other systems and for the system +thus to act as a log server. +If the system is not a log server, then lines concerning these modules +should remain commented out. + + + Ensure syslog-ng is Installed + syslog-ng can be installed in replacement of rsyslog. +The syslog-ng-core package can be installed with the following command: + +$ sudo yum install syslog-ng-core + BP28(R46) + BP28(R5) + 1 + 14 + 15 + 16 + 3 + 5 + 6 + APO11.04 + BAI03.05 + DSS05.04 + DSS05.07 + MEA02.01 + CCI-001311 + CCI-001312 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + CM-6(a) + PR.PT-1 + The syslog-ng-core package provides the syslog-ng daemon, which provides +system logging services. + +package --add=syslog-ng + + include install_syslog-ng + +class install_syslog-ng { + package { 'syslog-ng': + ensure => 'installed', + } +} + + - name: Ensure syslog-ng is installed + package: + name: syslog-ng + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-CM-6(a) + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_syslogng_installed + + +[[packages]] +name = "syslog-ng" +version = "*" + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if ! rpm -q --quiet "syslog-ng" ; then + yum install -y "syslog-ng" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Enable syslog-ng Service + The syslog-ng service (in replacement of rsyslog) provides syslog-style logging by default on Debian. + +The syslog-ng service can be enabled with the following command: +$ sudo systemctl enable syslog-ng.service + BP28(R46) + BP28(R5) + 1 + 12 + 13 + 14 + 15 + 16 + 2 + 3 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO13.01 + BAI03.05 + BAI04.04 + DSS01.03 + DSS03.05 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + CCI-001311 + CCI-001312 + CCI-001557 + CCI-001851 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.2 + A.12.1.3 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.17.2.1 + CM-6(a) + AU-4(1) + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.DS-4 + PR.PT-1 + The syslog-ng service must be running in order to provide +logging services, which are essential to system administration. + include enable_syslog-ng + +class enable_syslog-ng { + service {'syslog-ng': + enable => true, + ensure => 'running', + } +} + + - name: Enable service syslog-ng + block: + + - name: Gather the package facts + package_facts: + manager: auto + + - name: Enable service syslog-ng + service: + name: syslog-ng + enabled: 'yes' + state: started + masked: 'no' + when: + - '"syslog-ng" in ansible_facts.packages' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AU-4(1) + - NIST-800-53-CM-6(a) + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_syslogng_enabled + + +[customizations.services] +enabled = ["syslog-ng"] + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" unmask 'syslog-ng.service' +"$SYSTEMCTL_EXEC" start 'syslog-ng.service' +"$SYSTEMCTL_EXEC" enable 'syslog-ng.service' + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Enable rsyslog to Accept Messages via TCP, if Acting As Log Server + The rsyslog daemon should not accept remote messages +unless the system acts as a log server. +If the system needs to act as a central log server, add the following lines to +/etc/rsyslog.conf to enable reception of messages over TCP: +$ModLoad imtcp +$InputTCPServerRun 514 + 1 + 14 + 15 + 16 + 3 + 5 + 6 + APO11.04 + BAI03.05 + DSS05.04 + DSS05.07 + MEA02.01 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + CIP-004-6 R2.2.2 + CIP-004-6 R3.3 + CIP-007-3 R.1.3 + CIP-007-3 R5 + CIP-007-3 R5.1.1 + CIP-007-3 R6.5 + CM-6(a) + AU-6(3) + AU-6(4) + PR.PT-1 + If the system needs to act as a log server, this ensures that it can receive +messages over a reliable TCP connection. + + + Enable rsyslog to Accept Messages via UDP, if Acting As Log Server + The rsyslog daemon should not accept remote messages +unless the system acts as a log server. +If the system needs to act as a central log server, add the following lines to +/etc/rsyslog.conf to enable reception of messages over UDP: +$ModLoad imudp +$UDPServerRun 514 + 1 + 14 + 15 + 16 + 3 + 5 + 6 + APO11.04 + BAI03.05 + DSS05.04 + DSS05.07 + MEA02.01 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + CIP-004-6 R2.2.2 + CIP-004-6 R3.3 + CIP-007-3 R.1.3 + CIP-007-3 R5 + CIP-007-3 R5.1.1 + CIP-007-3 R6.5 + CM-6(a) + AU-6(3) + AU-6(4) + PR.PT-1 + Many devices, such as switches, routers, and other Unix-like systems, may only support +the traditional syslog transmission over UDP. If the system must act as a log server, +this enables it to receive their messages as well. + + + Ensure rsyslog Does Not Accept Remote Messages Unless Acting As Log Server + The rsyslog daemon should not accept remote messages +unless the system acts as a log server. +To ensure that it is not listening on the network, ensure the following lines are +not found in /etc/rsyslog.conf: +$ModLoad imtcp +$InputTCPServerRun port +$ModLoad imudp +$UDPServerRun port +$ModLoad imrelp +$InputRELPServerRun port + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 4 + 5 + 6 + 8 + 9 + APO01.06 + APO11.04 + APO13.01 + BAI03.05 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.05 + DSS03.01 + DSS05.02 + DSS05.04 + DSS05.07 + DSS06.02 + MEA02.01 + CCI-000318 + CCI-000366 + CCI-000368 + CCI-001812 + CCI-001813 + CCI-001814 + 4.2.3.4 + 4.3.3.3.9 + 4.3.3.4 + 4.3.3.5.8 + 4.3.4.3.2 + 4.3.4.3.3 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + 4.4.3.3 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + 0988 + 1405 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.12.1.1 + A.12.1.2 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.5.1 + A.12.6.2 + A.12.7.1 + A.13.1.1 + A.13.1.2 + A.13.1.3 + A.13.2.1 + A.13.2.2 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-7(a) + CM-7(b) + CM-6(a) + DE.AE-1 + ID.AM-3 + PR.AC-5 + PR.DS-5 + PR.IP-1 + PR.PT-1 + PR.PT-4 + SRG-OS-000480-GPOS-00227 + Any process which receives messages from the network incurs some risk +of receiving malicious messages. This risk can be eliminated for +rsyslog by configuring it not to listen on the network. + CCE-84275-7 + + + + + + + + + + Rsyslog Logs Sent To Remote Host + If system logs are to be useful in detecting malicious +activities, it is necessary to send logs to a remote server. An +intruder who has compromised the root account on a system may +delete the log entries which indicate that the system was attacked +before they are seen by an administrator. + +However, it is recommended that logs be stored on the local +host in addition to being sent to the loghost, especially if +rsyslog has been configured to use the UDP protocol to send +messages over a network. UDP does not guarantee reliable delivery, +and moderately busy sites will lose log messages occasionally, +especially in periods of high traffic which may be the result of an +attack. In addition, remote rsyslog messages are not +authenticated in any way by default, so it is easy for an attacker to +introduce spurious messages to the central log server. Also, some +problems cause loss of network connectivity, which will prevent the +sending of messages to the central server. For all of these reasons, it is +better to store log messages both centrally and on each host, so +that they can be correlated if necessary. + + Remote Log Server + Specify an URI or IP address of a remote host where the log messages will be sent and stored. + logcollector + + + Ensure Logs Sent To Remote Host + To configure rsyslog to send logs to a remote log server, +open /etc/rsyslog.conf and read and understand the last section of the file, +which describes the multiple directives necessary to activate remote +logging. +Along with these other directives, the system can be configured +to forward its logs to a particular log server by +adding or correcting one of the following lines, +substituting appropriately. +The choice of protocol depends on the environment of the system; +although TCP and RELP provide more reliable message delivery, +they may not be supported in all environments. + +To use UDP for log message delivery: +*.* @ + +To use TCP for log message delivery: +*.* @@ + +To use RELP for log message delivery: +*.* :omrelp: + +There must be a resolvable DNS CNAME or Alias record set to "" for logs to be sent correctly to the centralized logging utility. + It is important to configure queues in case the client is sending log +messages to a remote server. If queues are not configured, +the system will stop functioning when the connection +to the remote server is not available. Please consult Rsyslog +documentation for more information about configuration of queues. The +example configuration which should go into /etc/rsyslog.conf +can look like the following lines: + +$ActionQueueType LinkedList +$ActionQueueFileName queuefilename +$ActionQueueMaxDiskSpace 1g +$ActionQueueSaveOnShutdown on +$ActionResumeRetryCount -1 + + BP28(R7) + NT28(R43) + NT12(R5) + 1 + 13 + 14 + 15 + 16 + 2 + 3 + 5 + 6 + APO11.04 + APO13.01 + BAI03.05 + BAI04.04 + DSS05.04 + DSS05.07 + MEA02.01 + CCI-000366 + CCI-001348 + CCI-000136 + CCI-001851 + 164.308(a)(1)(ii)(D) + 164.308(a)(5)(ii)(B) + 164.308(a)(5)(ii)(C) + 164.308(a)(6)(ii) + 164.308(a)(8) + 164.310(d)(2)(iii) + 164.312(b) + 164.314(a)(2)(i)(C) + 164.314(a)(2)(iii) + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 7.1 + SR 7.2 + 0988 + 1405 + A.12.1.3 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.17.2.1 + CIP-003-8 R5.2 + CIP-004-6 R3.3 + CM-6(a) + AU-4(1) + AU-9(2) + PR.DS-4 + PR.PT-1 + FAU_GEN.1.1.c + SRG-OS-000479-GPOS-00224 + SRG-OS-000480-GPOS-00227 + SRG-OS-000342-GPOS-00133 + SRG-OS-000032-VMM-000130 + RHEL-08-030690 + 4.2.1.6 + SV-230479r627750_rule + A log server (loghost) receives syslog messages from one or more +systems. This data can be used as an additional log source in the event a +system is compromised and its local logs are suspect. Forwarding log messages +to a remote loghost also provides system administrators with a centralized +place to view the status of multiple hosts within the enterprise. + CCE-80863-4 + - name: XCCDF Value rsyslog_remote_loghost_address # promote to variable + set_fact: + rsyslog_remote_loghost_address: !!str + tags: + - always + +- name: Set rsyslog remote loghost + lineinfile: + dest: /etc/rsyslog.conf + regexp: ^\*\.\* + line: '*.* @@{{ rsyslog_remote_loghost_address }}' + create: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80863-4 + - DISA-STIG-RHEL-08-030690 + - NIST-800-53-AU-4(1) + - NIST-800-53-AU-9(2) + - NIST-800-53-CM-6(a) + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - rsyslog_remote_loghost + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +rsyslog_remote_loghost_address='' + + +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/rsyslog.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^\*\.\*") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s %s" "$stripped_key" "@@$rsyslog_remote_loghost_address" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^\*\.\*\\>" "/etc/rsyslog.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^\*\.\*\\>.*/$escaped_formatted_output/gi" "/etc/rsyslog.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-80863-4" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/rsyslog.conf" >> "/etc/rsyslog.conf" + printf '%s\n' "$formatted_output" >> "/etc/rsyslog.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure TLS for rsyslog remote logging + Configure rsyslog to use Transport Layer +Security (TLS) support for logging to remote server +for the Forwarding Output Module in /etc/rsyslog.conf +using action. You can use the following command: +echo 'action(type="omfwd" protocol="tcp" Target="<remote system>" port="6514" + StreamDriver="gtls" StreamDriverMode="1" StreamDriverAuthMode="x509/name" streamdriver.CheckExtendedKeyPurpose="on")' >> /etc/rsyslog.conf + +Replace the <remote system> in the above command with an IP address or a host name of the remote logging server. + BP28(R43) + 0988 + 1405 + AU-9(3) + CM-6(a) + FCS_TLSC_EXT.1 + FTP_ITC_EXT.1.1 + FIA_X509_EXT.1.1 + FMT_SMF_EXT.1.1 + SRG-OS-000480-GPOS-00227 + SRG-OS-000120-GPOS-00061 + For protection of data being logged, the connection to the +remote logging server needs to be authenticated and encrypted. + CCE-82457-3 + + + + + + + + + Configure CA certificate for rsyslog remote logging + Configure CA certificate for rsyslog logging +to remote server using Transport Layer Security (TLS) +using correct path for the DefaultNetstreamDriverCAFile +global option in /etc/rsyslog.conf, for example with the following command: +echo 'global(DefaultNetstreamDriverCAFile="/etc/pki/tls/cert.pem")' >> /etc/rsyslog.conf +Replace the /etc/pki/tls/cert.pem in the above command with the path to the file with CA certificate generated for the purpose of remote logging. + BP28(R43) + 0988 + 1405 + FCS_TLSC_EXT.1 + SRG-OS-000480-GPOS-00227 + The CA certificate needs to be set or rsyslog.service +fails to start with +error: ca certificate is not set, cannot continue + CCE-82458-1 + + + + + + + + + + + Network Configuration and Firewalls + Most systems must be connected to a network of some +sort, and this brings with it the substantial risk of network +attack. This section discusses the security impact of decisions +about networking which must be made when configuring a system. + +This section also discusses firewalls, network access +controls, and other network security frameworks, which allow +system-level rules to be written that can limit an attackers' ability +to connect to your system. These rules can specify that network +traffic should be allowed or denied from certain IP addresses, +hosts, and networks. The rules can also specify which of the +system's network services are available to particular hosts or +networks. + + Configure Multiple DNS Servers in /etc/resolv.conf + +Determine whether the system is using local or DNS name resolution with the +following command: +$ sudo grep hosts /etc/nsswitch.conf +hosts: files dns +If the DNS entry is missing from the host's line in the "/etc/nsswitch.conf" +file, the "/etc/resolv.conf" file must be empty. +Verify the "/etc/resolv.conf" file is empty with the following command: +$ sudo ls -al /etc/resolv.conf +-rw-r--r-- 1 root root 0 Aug 19 08:31 resolv.conf +If the DNS entry is found on the host's line of the "/etc/nsswitch.conf" file, +then verify the following: + +Multiple Domain Name System (DNS) Servers should be configured +in /etc/resolv.conf. This provides redundant name resolution services +in the event that a domain server crashes. To configure the system to contain +as least 2 DNS servers, add a corresponding nameserver +ip_address entry in /etc/resolv.conf for each DNS +server where ip_address is the IP address of a valid DNS server. +For example: +search example.com +nameserver 192.168.0.1 +nameserver 192.168.0.2 + 12 + 15 + 8 + APO13.01 + DSS05.02 + CCI-000366 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + A.13.1.1 + A.13.2.1 + A.14.1.3 + SC-20(a) + CM-6(a) + PR.PT-4 + SRG-OS-000480-GPOS-00227 + RHEL-08-010680 + SV-230316r627750_rule + To provide availability for name resolution services, multiple redundant +name servers are mandated. A failure in name resolution could lead to the +failure of security functions requiring name resolution, which may include +time synchronization, centralized authentication, and remote system logging. + CCE-84049-6 + + + + + + + + + Disable Client Dynamic DNS Updates + Dynamic DNS allows clients to dynamically update their own DNS records. +The updates are transmitted by unencrypted means which can reveal information +to a potential malicious user. If the system does not require Dynamic DNS, +remove all DHCP_HOSTNAME references from the +/etc/sysconfig/network-scripts/ifcfg-interface scripts. If +dhclient is used, remove all send host-name hostname +references from the /etc/dhclient.conf configuration file and/or any +reference from the /etc/dhcp directory. + 11 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + CCI-000366 + 4.3.4.3.2 + 4.3.4.3.3 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + SRG-OS-000480-GPOS-00227 + Dynamic DNS updates transmit unencrypted information about a system +including its name and address and should not be used unless needed. + + + + + + + + + Disable Zeroconf Networking + Zeroconf networking allows the system to assign itself an IP +address and engage in IP communication without a statically-assigned address or +even a DHCP server. Automatic address assignment via Zeroconf (or DHCP) is not +recommended. To disable Zeroconf automatic route assignment in the 169.254.0.0 +subnet, add or correct the following line in /etc/sysconfig/network: +NOZEROCONF=yes + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + PR.PT-3 + Zeroconf addresses are in the network 169.254.0.0. The networking +scripts add entries to the system's routing table for these addresses. Zeroconf +address assignment commonly occurs when the system is configured to use DHCP +but fails to receive an address assignment from the DHCP server. + echo "NOZEROCONF=yes" >> /etc/sysconfig/network + + + + + + + Prevent non-Privileged Users from Modifying Network Interfaces using nmcli + By default, non-privileged users are given permissions to modify networking +interfaces and configurations using the nmcli command. Non-privileged +users should not be making configuration changes to network configurations. To +ensure that non-privileged users do not have permissions to make changes to the +network configuration using nmcli, create the following configuration in +/etc/polkit-1/localauthority/20-org.d/10-nm-harden-access.pkla: + +[Disable General User Access to NetworkManager] +Identity=default +Action=org.freedesktop.NetworkManager.* +ResultAny=no +ResultInactive=no +ResultActive=auth_admin + + 3.1.16 + 0418 + 1055 + 1402 + AC-18(4) + CM-6(a) + Allowing non-privileged users to make changes to network settings can allow +untrusted access, prevent system availability, and/or can lead to a compromise or +attack. + + CCE-82179-3 + - name: Ensure non-privileged users do not have access to nmcli + ini_file: + path: /etc/polkit-1/localauthority/20-org.d/10-nm-harden-access.pkla + section: Disable General User Access to NetworkManager + option: '{{ item.option }}' + value: '{{ item.value }}' + no_extra_spaces: true + create: true + loop: + - option: Identity + value: default + - option: Action + value: org.freedesktop.NetworkManager.* + - option: ResultAny + value: 'no' + - option: ResultInactive + value: 'no' + - option: ResultActive + value: auth_admin + tags: + - CCE-82179-3 + - NIST-800-171-3.1.16 + - NIST-800-53-AC-18(4) + - NIST-800-53-CM-6(a) + - low_complexity + - low_disruption + - medium_severity + - network_nmcli_permissions + - no_reboot_needed + - restrict_strategy + + +printf "[Disable General User Access to NetworkManager]\nIdentity=default\nAction=org.freedesktop.NetworkManager.*\nResultAny=no\nResultInactive=no\nResultActive=auth_admin\n" > /etc/polkit-1/localauthority/20-org.d/10-nm-harden-access.pkla + + + + + + + + + + Ensure System is Not Acting as a Network Sniffer + The system should not be acting as a network sniffer, which can +capture all traffic on the network to which it is connected. Run the following +to determine if any interface is running in promiscuous mode: +$ ip link | grep PROMISC +Promiscuous mode of an interface can be disabled with the following command: +$ sudo ip link set dev device_name multicast off promisc off + 1 + 11 + 14 + 3 + 9 + APO11.06 + APO12.06 + BAI03.10 + BAI09.01 + BAI09.02 + BAI09.03 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.05 + DSS04.05 + DSS05.02 + DSS05.05 + DSS06.06 + CCI-000366 + 4.2.3.4 + 4.3.3.3.7 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + 4.4.3.4 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + SR 7.8 + A.11.1.2 + A.11.2.4 + A.11.2.5 + A.11.2.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.16.1.6 + A.8.1.1 + A.8.1.2 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + CM-7(2) + MA-3 + DE.DP-5 + ID.AM-1 + PR.IP-1 + PR.MA-1 + PR.PT-3 + SRG-OS-000480-GPOS-00227 + RHEL-08-040330 + SV-230554r627750_rule + Network interfaces in promiscuous mode allow for the capture of all network traffic +visible to the system. If unauthorized individuals can access these applications, it +may allow them to collect information such as logon IDs, passwords, and key exchanges +between systems. + +If the system is being used to perform a network troubleshooting function, the use of these +tools must be documented with the Information Systems Security Manager (ISSM) and restricted +to only authorized personnel. + + CCE-82283-3 + + + + + + + + + firewalld + The dynamic firewall daemon firewalld provides a +dynamically managed firewall with support for network “zones” to assign +a level of trust to a network and its associated connections and interfaces. +It has support for IPv4 and IPv6 firewall settings. It supports Ethernet +bridges and has a separation of runtime and permanent configuration options. +It also has an interface for services or applications to add firewall rules +directly. + +A graphical configuration tool, firewall-config, is used to configure +firewalld, which in turn uses iptables tool to communicate +with Netfilter in the kernel which implements packet filtering. + +The firewall service provided by firewalld is dynamic rather than +static because changes to the configuration can be made at anytime and are +immediately implemented. There is no need to save or apply the changes. No +unintended disruption of existing network connections occurs as no part of +the firewall has to be reloaded. + + + Configure Firewalld to Use the Nftables Backend + Firewalld can be configured with many backends, such as nftables. + CCI-002385 + SC-5 + SRG-OS-000420-GPOS-00186 + RHEL-08-040150 + SV-230525r744029_rule + Nftables is modern kernel module for controling network connections coming into a system. +Utilizing the limit statement in "nftables" can help to mitigate DoS attacks. + CCE-86506-3 + - name: Setting shell-quoted shell-style assignment of 'FirewallBackend' to 'nftables' + in '/etc/firewalld/firewalld.conf' + block: + + - name: Check for duplicate values + lineinfile: + path: /etc/firewalld/firewalld.conf + create: false + regexp: ^\s*FirewallBackend= + state: absent + check_mode: true + changed_when: false + register: dupes + + - name: Deduplicate values from /etc/firewalld/firewalld.conf + lineinfile: + path: /etc/firewalld/firewalld.conf + create: false + regexp: ^\s*FirewallBackend= + state: absent + when: dupes.found is defined and dupes.found > 1 + + - name: Insert correct line to /etc/firewalld/firewalld.conf + lineinfile: + path: /etc/firewalld/firewalld.conf + create: true + regexp: ^\s*FirewallBackend= + line: FirewallBackend="nftables" + state: present + insertbefore: ^# FirewallBackend + validate: /usr/bin/bash -n %s + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86506-3 + - DISA-STIG-RHEL-08-040150 + - NIST-800-53-SC-5 + - firewalld-backend + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if [ -e "/etc/firewalld/firewalld.conf" ] ; then + + LC_ALL=C sed -i "/^\s*FirewallBackend=/d" "/etc/firewalld/firewalld.conf" +else + touch "/etc/firewalld/firewalld.conf" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/firewalld/firewalld.conf" + +cp "/etc/firewalld/firewalld.conf" "/etc/firewalld/firewalld.conf.bak" +# Insert before the line matching the regex '^#\s*FirewallBackend'. +line_number="$(LC_ALL=C grep -n "^#\s*FirewallBackend" "/etc/firewalld/firewalld.conf.bak" | LC_ALL=C sed 's/:.*//g')" +if [ -z "$line_number" ]; then + # There was no match of '^#\s*FirewallBackend', insert at + # the end of the file. + printf '%s\n' "FirewallBackend='nftables'" >> "/etc/firewalld/firewalld.conf" +else + head -n "$(( line_number - 1 ))" "/etc/firewalld/firewalld.conf.bak" > "/etc/firewalld/firewalld.conf" + printf '%s\n' "FirewallBackend='nftables'" >> "/etc/firewalld/firewalld.conf" + tail -n "+$(( line_number ))" "/etc/firewalld/firewalld.conf.bak" >> "/etc/firewalld/firewalld.conf" +fi +# Clean up after ourselves. +rm "/etc/firewalld/firewalld.conf.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Inspect and Activate Default firewalld Rules + Firewalls can be used to separate networks into different zones +based on the level of trust the user has decided to place on the devices and +traffic within that network. NetworkManager informs firewalld to which +zone an interface belongs. An interface's assigned zone can be changed by +NetworkManager or via the firewall-config tool. + +The zone settings in /etc/firewalld/ are a range of preset settings +which can be quickly applied to a network interface. These are the zones +provided by firewalld sorted according to the default trust level of the +zones from untrusted to trusted: +dropAny incoming network packets are dropped, there is no +reply. Only outgoing network connections are possible.blockAny incoming network connections are rejected with an +icmp-host-prohibited message for IPv4 and icmp6-adm-prohibited +for IPv6. Only network connections initiated from within the system are +possible.publicFor use in public areas. You do not trust the other +computers on the network to not harm your computer. Only selected incoming +connections are accepted.externalFor use on external networks with masquerading enabled +especially for routers. You do not trust the other computers on the network to +not harm your computer. Only selected incoming connections are accepted.dmzFor computers in your demilitarized zone that are +publicly-accessible with limited access to your internal network. Only selected +incoming connections are accepted.workFor use in work areas. You mostly trust the other computers +on networks to not harm your computer. Only selected incoming connections are +accepted.homeFor use in home areas. You mostly trust the other computers +on networks to not harm your computer. Only selected incoming connections are +accepted.internalFor use on internal networks. You mostly trust the +other computers on the networks to not harm your computer. Only selected +incoming connections are accepted.trustedAll network connections are accepted. + +It is possible to designate one of these zones to be the default zone. When +interface connections are added to NetworkManager, they are assigned +to the default zone. On installation, the default zone in firewalld is set to +be the public zone. + +To find out all the settings of a zone, for example the public zone, +enter the following command as root: +# firewall-cmd --zone=public --list-all +Example output of this command might look like the following: + +# firewall-cmd --zone=public --list-all +public + interfaces: + services: mdns dhcpv6-client ssh + ports: + forward-ports: + icmp-blocks: source-quench + +To view the network zones currently active, enter the following command as root: +# firewall-cmd --get-service +The following listing displays the result of this command +on common Red Hat Enterprise Linux 8 system: + +# firewall-cmd --get-service +amanda-client bacula bacula-client dhcp dhcpv6 dhcpv6-client dns ftp +high-availability http https imaps ipp ipp-client ipsec kerberos kpasswd +ldap ldaps libvirt libvirt-tls mdns mountd ms-wbt mysql nfs ntp openvpn +pmcd pmproxy pmwebapi pmwebapis pop3s postgresql proxy-dhcp radius rpc-bind +samba samba-client smtp ssh telnet tftp tftp-client transmission-client +vnc-server wbem-https + +Finally to view the network zones that will be active after the next firewalld +service reload, enter the following command as root: +# firewall-cmd --get-service --permanent + + Install firewalld Package + The firewalld package can be installed with the following command: + +$ sudo yum install firewalld + CCI-002314 + CM-6(a) + FMT_SMF_EXT.1 + SRG-OS-000096-GPOS-00050 + SRG-OS-000297-GPOS-00115 + SRG-OS-000298-GPOS-00116 + SRG-OS-000480-GPOS-00227 + SRG-OS-000480-GPOS-00232 + RHEL-08-040100 + 3.4.1.1 + SV-230505r744020_rule + "Firewalld" provides an easy and effective way to block/limit remote access to the system via ports, services, and protocols. + +Remote access services, such as those providing remote access to network devices and information systems, which lack automated control capabilities, increase risk and make remote user access management difficult at best. + +Remote access is access to DoD nonpublic information systems by an authorized user (or an information system) communicating through an external, non-organization-controlled network. Remote access methods include, for example, dial-up, broadband, and wireless. + +Red Hat Enterprise Linux 8 functionality (e.g., SSH) must be capable of taking enforcement action if the audit reveals unauthorized activity. +Automated control of remote access sessions allows organizations to ensure ongoing compliance with remote access policies by enforcing connection rules of remote access applications on a variety of information system components (e.g., servers, workstations, notebook computers, smartphones, and tablets)." + CCE-82998-6 + +package --add=firewalld + + include install_firewalld + +class install_firewalld { + package { 'firewalld': + ensure => 'installed', + } +} + + - name: Ensure firewalld is installed + package: + name: firewalld + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82998-6 + - DISA-STIG-RHEL-08-040100 + - NIST-800-53-CM-6(a) + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_firewalld_installed + + +[[packages]] +name = "firewalld" +version = "*" + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if ! rpm -q --quiet "firewalld" ; then + yum install -y "firewalld" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Verify firewalld Enabled + +The firewalld service can be enabled with the following command: +$ sudo systemctl enable firewalld.service + 11 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + 3.1.3 + 3.4.7 + CCI-000366 + CCI-000382 + CCI-002314 + 4.3.4.3.2 + 4.3.4.3.3 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + CIP-003-8 R4 + CIP-003-8 R5 + CIP-004-6 R3 + AC-4 + CM-7(b) + CA-3(5) + SC-7(21) + CM-6(a) + PR.IP-1 + FMT_SMF_EXT.1 + SRG-OS-000096-GPOS-00050 + SRG-OS-000297-GPOS-00115 + SRG-OS-000480-GPOS-00227 + SRG-OS-000480-GPOS-00231 + SRG-OS-000480-GPOS-00232 + RHEL-08-040101 + 3.4.1.4 + SV-244544r743881_rule + Access control methods provide the ability to enhance system security posture +by restricting services and known good IP addresses and address ranges. This +prevents connections from unknown hosts and protocols. + CCE-80877-4 + include enable_firewalld + +class enable_firewalld { + service {'firewalld': + enable => true, + ensure => 'running', + } +} + + - name: Enable service firewalld + block: + + - name: Gather the package facts + package_facts: + manager: auto + + - name: Enable service firewalld + service: + name: firewalld + enabled: 'yes' + state: started + masked: 'no' + when: + - '"firewalld" in ansible_facts.packages' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80877-4 + - DISA-STIG-RHEL-08-040101 + - NIST-800-171-3.1.3 + - NIST-800-171-3.4.7 + - NIST-800-53-AC-4 + - NIST-800-53-CA-3(5) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-7(21) + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_firewalld_enabled + + +[customizations.services] +enabled = ["firewalld"] + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" unmask 'firewalld.service' +"$SYSTEMCTL_EXEC" start 'firewalld.service' +"$SYSTEMCTL_EXEC" enable 'firewalld.service' + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Strengthen the Default Ruleset + The default rules can be strengthened. The system +scripts that activate the firewall rules expect them to be defined +in configuration files under the /etc/firewalld/services +and /etc/firewalld/zones directories. + +The following recommendations describe how to strengthen the +default ruleset configuration file. An alternative to editing this +configuration file is to create a shell script that makes calls to +the firewall-cmd program to load in rules under the /etc/firewalld/services +and /etc/firewalld/zones directories. + +Instructions apply to both unless otherwise noted. Language and address +conventions for regular firewalld rules are used throughout this section. + The program firewall-config +allows additional services to penetrate the default firewall rules +and automatically adjusts the firewalld ruleset(s). + + Configure the Firewalld Ports + Configure the firewalld ports to allow approved services to have access to the system. +To configure firewalld to open ports, run the following command: +firewall-cmd --permanent --add-port=port_number/tcp +To configure firewalld to allow access for pre-defined services, run the following +command: +firewall-cmd --permanent --add-service=service_name + 11 + 12 + 14 + 15 + 3 + 8 + 9 + APO13.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.04 + DSS05.02 + DSS05.03 + DSS05.05 + DSS06.06 + CCI-000382 + CCI-002314 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + 1416 + A.11.2.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.6.2.1 + A.6.2.2 + A.9.1.2 + AC-4 + CM-7(b) + CA-3(5) + SC-7(21) + CM-6(a) + PR.AC-3 + PR.IP-1 + PR.PT-3 + PR.PT-4 + SRG-OS-000096-GPOS-00050 + SRG-OS-000297-GPOS-00115 + SRG-OS-000096-VMM-000490 + SRG-OS-000480-VMM-002000 + RHEL-08-040030 + SV-230500r627750_rule + In order to prevent unauthorized connection of devices, unauthorized transfer of information, +or unauthorized tunneling (i.e., embedding of data types within data types), organizations must +disable or restrict unused or unnecessary physical and logical ports/protocols on information +systems. + +Operating systems are capable of providing a wide variety of functions and services. +Some of the functions and services provided by default may not be necessary to support +essential organizational operations. +Additionally, it is sometimes convenient to provide multiple services from a single component +(e.g., VPN and IPS); however, doing so increases risk over limiting the services provided by +one component. + +To support the requirements and principles of least functionality, the operating system must +support the organizational requirements, providing only essential capabilities and limiting the +use of ports, protocols, and/or services to only those required, authorized, and approved to +conduct official business. + CCE-84300-3 + + + + + + Firewalld Must Employ a Deny-all, Allow-by-exception Policy for Allowing Connections to Other Systems + Red Hat Enterprise Linux 8 incorporates the "firewalld" daemon, which allows for many different configurations. One of these configurations is zones. +Zones can be utilized to a deny-all, allow-by-exception approach. +The default "drop" zone will drop all incoming network packets unless it is explicitly allowed by the configuration file or is related to an outgoing network connection. + CCI-002314 + AC-17 (1) + SRG-OS-000297-GPOS-00115 + RHEL-08-040090 + SV-230504r809321_rule + Failure to restrict network connectivity only to authorized systems permits inbound connections from malicious systems. +It also permits outbound connections that may facilitate exfiltration of data. + CCE-86266-4 + + + + + + Set Default firewalld Zone for Incoming Packets + To set the default zone to drop for +the built-in default zone which processes incoming IPv4 and IPv6 packets, +modify the following line in +/etc/firewalld/firewalld.conf to be: +DefaultZone=drop + To prevent denying any access to the system, automatic remediation +of this control is not available. Remediation must be automated as +a component of machine provisioning, or followed manually as outlined +above. + 11 + 14 + 3 + 9 + 5.10.1 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + 3.1.3 + 3.4.7 + 3.13.6 + CCI-000366 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + 1416 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CA-3(5) + CM-7(b) + SC-7(23) + CM-6(a) + PR.IP-1 + PR.PT-3 + FMT_MOF_EXT.1 + SRG-OS-000480-GPOS-00227 + SRG-OS-000480-VMM-002000 + 3.4.1.5 + In firewalld the default zone is applied only after all +the applicable rules in the table are examined for a match. Setting the +default zone to drop implements proper design for a firewall, i.e. +any packets which are not explicitly permitted should not be +accepted. + CCE-80890-7 + + + + + + + + + + + IPSec Support + Support for Internet Protocol Security (IPsec) +is provided with Libreswan. + + Install libreswan Package + The Libreswan package provides an implementation of IPsec +and IKE, which permits the creation of secure tunnels over +untrusted networks. The libreswan package can be installed with the following command: + +$ sudo yum install libreswan + 12 + 15 + 3 + 5 + 8 + APO13.01 + DSS01.04 + DSS05.02 + DSS05.03 + DSS05.04 + CCI-001130 + CCI-001131 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + SR 1.13 + SR 2.6 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + A.11.2.4 + A.11.2.6 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.15.1.1 + A.15.2.1 + A.6.2.1 + A.6.2.2 + CM-6(a) + PR.AC-3 + PR.MA-2 + PR.PT-4 + Req-4.1 + SRG-OS-000480-GPOS-00227 + SRG-OS-000120-GPOS-00061 + Providing the ability for remote users or systems +to initiate a secure VPN connection protects information when it is +transmitted over a wide area network. + CCE-80845-1 + +package --add=libreswan + + include install_libreswan + +class install_libreswan { + package { 'libreswan': + ensure => 'installed', + } +} + + - name: Ensure libreswan is installed + package: + name: libreswan + state: present + tags: + - CCE-80845-1 + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-4.1 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_libreswan_installed + + +[[packages]] +name = "libreswan" +version = "*" + + +if ! rpm -q --quiet "libreswan" ; then + yum install -y "libreswan" +fi + + + + + + + + + + Verify Any Configured IPSec Tunnel Connections + Libreswan provides an implementation of IPsec +and IKE, which permits the creation of secure tunnels over +untrusted networks. As such, IPsec can be used to circumvent certain +network requirements such as filtering. Verify that if any IPsec connection +(conn) configured in /etc/ipsec.conf and /etc/ipsec.d +exists is an approved organizational connection. + Automatic remediation of this control is not available due to the unique +requirements of each system. + 1 + 12 + 13 + 14 + 15 + 16 + 18 + 4 + 6 + 8 + 9 + APO01.06 + APO13.01 + DSS01.05 + DSS03.01 + DSS05.02 + DSS05.04 + DSS05.07 + DSS06.02 + CCI-000336 + 164.308(a)(4)(i) + 164.308(b)(1) + 164.308(b)(3) + 164.310(b) + 164.312(e)(1) + 164.312(e)(2)(ii) + 4.2.3.4 + 4.3.3.4 + 4.4.3.3 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.12.1.1 + A.12.1.2 + A.13.1.1 + A.13.1.2 + A.13.1.3 + A.13.2.1 + A.13.2.2 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + AC-17(a) + MA-4(6) + CM-6(a) + AC-4 + SC-8 + DE.AE-1 + ID.AM-3 + PR.AC-5 + PR.DS-5 + PR.PT-4 + SRG-OS-000480-GPOS-00227 + IP tunneling mechanisms can be used to bypass network filtering. + CCE-80836-0 + + + + + + + iptables and ip6tables + A host-based firewall called netfilter is included as +part of the Linux kernel distributed with the system. It is +activated by default. This firewall is controlled by the program +iptables, and the entire capability is frequently referred to by +this name. An analogous program called ip6tables handles filtering +for IPv6. + +Unlike TCP Wrappers, which depends on the network server +program to support and respect the rules written, netfilter +filtering occurs at the kernel level, before a program can even +process the data from the network packet. As such, any program on +the system is affected by the rules written. + +This section provides basic information about strengthening +the iptables and ip6tables configurations included with the system. +For more complete information that may allow the construction of a +sophisticated ruleset tailored to your environment, please consult +the references at the end of this section. + + Install iptables-services Package + The iptables-services package can be installed with the following command: + +$ sudo yum install iptables-services + CM-6(a) + SRG-OS-000480-GPOS-00227 + 3.4.3.1.1 + iptables-services provides the services iptables and ip6tables that have been split +out of the base package since they are not active by default anymore. +These services load the iptables rules during the system startup and also allow one to reload +the iptables rules during runtime. + CCE-85982-7 + +package --add=iptables-services + + include install_iptables-services + +class install_iptables-services { + package { 'iptables-services': + ensure => 'installed', + } +} + + - name: Ensure iptables-services is installed + package: + name: iptables-services + state: present + tags: + - CCE-85982-7 + - NIST-800-53-CM-6(a) + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_iptables-services_installed + + +[[packages]] +name = "iptables-services" +version = "*" + + +if ! rpm -q --quiet "iptables-services" ; then + yum install -y "iptables-services" +fi + + + + + + + + + + Install iptables Package + The iptables package can be installed with the following command: + +$ sudo yum install iptables + CM-6(a) + SRG-OS-000480-GPOS-00227 + 3.4.3.1.1 + iptables controls the Linux kernel network packet filtering +code. iptables allows system operators to set up firewalls and IP +masquerading, etc. + CCE-82982-0 + +package --add=iptables + + include install_iptables + +class install_iptables { + package { 'iptables': + ensure => 'installed', + } +} + + - name: Ensure iptables is installed + package: + name: iptables + state: present + tags: + - CCE-82982-0 + - NIST-800-53-CM-6(a) + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_iptables_installed + + +[[packages]] +name = "iptables" +version = "*" + + +if ! rpm -q --quiet "iptables" ; then + yum install -y "iptables" +fi + + + + + + + + + + Inspect and Activate Default Rules + View the currently-enforced iptables rules by running +the command: +$ sudo iptables -nL --line-numbers +The command is analogous for ip6tables. + +If the firewall does not appear to be active (i.e., no rules +appear), activate it and ensure that it starts at boot by issuing +the following commands (and analogously for ip6tables): +$ sudo service iptables restart +The default iptables rules are: +Chain INPUT (policy ACCEPT) +num target prot opt source destination +1 ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 state RELATED,ESTABLISHED +2 ACCEPT icmp -- 0.0.0.0/0 0.0.0.0/0 +3 ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 +4 ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 state NEW tcp dpt:22 +5 REJECT all -- 0.0.0.0/0 0.0.0.0/0 reject-with icmp-host-prohibited + +Chain FORWARD (policy ACCEPT) +num target prot opt source destination +1 REJECT all -- 0.0.0.0/0 0.0.0.0/0 reject-with icmp-host-prohibited + +Chain OUTPUT (policy ACCEPT) +num target prot opt source destination +The ip6tables default rules are essentially the same. + + Verify ip6tables Enabled if Using IPv6 + +The ip6tables service can be enabled with the following command: +$ sudo systemctl enable ip6tables.service + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 4 + 6 + 8 + 9 + APO01.06 + APO13.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.05 + DSS03.01 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + DSS06.02 + DSS06.06 + 4.2.3.4 + 4.3.3.4 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + 4.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.12.1.1 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.13.1.1 + A.13.1.2 + A.13.1.3 + A.13.2.1 + A.13.2.2 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R4 + CIP-003-8 R5 + CIP-004-6 R3 + AC-4 + CM-7(b) + CA-3(5) + SC-7(21) + CM-6(a) + DE.AE-1 + ID.AM-3 + PR.AC-5 + PR.DS-5 + PR.IP-1 + PR.PT-3 + PR.PT-4 + 3.4.3.3.6 + The ip6tables service provides the system's host-based firewalling +capability for IPv6 and ICMPv6. + + CCE-85955-3 + include enable_ip6tables + +class enable_ip6tables { + service {'ip6tables': + enable => true, + ensure => 'running', + } +} + + - name: Enable service ip6tables + block: + + - name: Gather the package facts + package_facts: + manager: auto + + - name: Enable service ip6tables + service: + name: ip6tables + enabled: 'yes' + state: started + masked: 'no' + when: + - '"iptables-ipv6" in ansible_facts.packages' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-85955-3 + - NIST-800-53-AC-4 + - NIST-800-53-CA-3(5) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-7(21) + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_ip6tables_enabled + + +[customizations.services] +enabled = ["ip6tables"] + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" unmask 'ip6tables.service' +"$SYSTEMCTL_EXEC" start 'ip6tables.service' +"$SYSTEMCTL_EXEC" enable 'ip6tables.service' + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Verify iptables Enabled + +The iptables service can be enabled with the following command: +$ sudo systemctl enable iptables.service + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 4 + 6 + 8 + 9 + APO01.06 + APO13.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.05 + DSS03.01 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + DSS06.02 + DSS06.06 + 4.2.3.4 + 4.3.3.4 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + 4.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.12.1.1 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.13.1.1 + A.13.1.2 + A.13.1.3 + A.13.2.1 + A.13.2.2 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R4 + CIP-003-8 R5 + CIP-004-6 R3 + AC-4 + CM-7(b) + CA-3(5) + SC-7(21) + CM-6(a) + DE.AE-1 + ID.AM-3 + PR.AC-5 + PR.DS-5 + PR.IP-1 + PR.PT-3 + PR.PT-4 + 3.4.3.2.6 + The iptables service provides the system's host-based firewalling +capability for IPv4 and ICMP. + + CCE-85961-1 + include enable_iptables + +class enable_iptables { + service {'iptables': + enable => true, + ensure => 'running', + } +} + + - name: Enable service iptables + block: + + - name: Gather the package facts + package_facts: + manager: auto + + - name: Enable service iptables + service: + name: iptables + enabled: 'yes' + state: started + masked: 'no' + when: + - '"iptables" in ansible_facts.packages' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-85961-1 + - NIST-800-53-AC-4 + - NIST-800-53-CA-3(5) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-7(21) + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_iptables_enabled + + +[customizations.services] +enabled = ["iptables"] + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" unmask 'iptables.service' +"$SYSTEMCTL_EXEC" start 'iptables.service' +"$SYSTEMCTL_EXEC" enable 'iptables.service' + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Set Default ip6tables Policy for Incoming Packets + To set the default policy to DROP (instead of ACCEPT) for +the built-in INPUT chain which processes incoming packets, +add or correct the following line in +/etc/sysconfig/ip6tables: +:INPUT DROP [0:0] +If changes were required, reload the ip6tables rules: +$ sudo service ip6tables reload + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CIP-003-8 R4 + CIP-003-8 R5 + CIP-004-6 R3 + AC-4 + CM-7(b) + CA-3(5) + SC-7(21) + CM-6(a) + PR.IP-1 + PR.PT-3 + 3.4.3.3.4 + In ip6tables, the default policy is applied only after all +the applicable rules in the table are examined for a match. Setting the +default policy to DROP implements proper design for a firewall, i.e. +any packets which are not explicitly permitted should not be +accepted. + CCE-85965-2 + sed -i 's/^:INPUT ACCEPT.*/:INPUT DROP [0:0]/g' /etc/sysconfig/ip6tables + + + + + + + + Strengthen the Default Ruleset + The default rules can be strengthened. The system +scripts that activate the firewall rules expect them to be defined +in the configuration files iptables and ip6tables in the directory +/etc/sysconfig. Many of the lines in these files are similar +to the command line arguments that would be provided to the programs +/sbin/iptables or /sbin/ip6tables - but some are quite +different. + +The following recommendations describe how to strengthen the +default ruleset configuration file. An alternative to editing this +configuration file is to create a shell script that makes calls to +the iptables program to load in rules, and then invokes service +iptables save to write those loaded rules to +/etc/sysconfig/iptables. + +The following alterations can be made directly to +/etc/sysconfig/iptables and /etc/sysconfig/ip6tables. +Instructions apply to both unless otherwise noted. Language and address +conventions for regular iptables are used throughout this section; +configuration for ip6tables will be either analogous or explicitly +covered. + The program system-config-securitylevel +allows additional services to penetrate the default firewall rules +and automatically adjusts /etc/sysconfig/iptables. This program +is only useful if the default ruleset meets your security +requirements. Otherwise, this program should not be used to make +changes to the firewall configuration because it re-writes the +saved configuration file. + + Set Default iptables Policy for Incoming Packets + To set the default policy to DROP (instead of ACCEPT) for +the built-in INPUT chain which processes incoming packets, +add or correct the following line in +/etc/sysconfig/iptables: +:INPUT DROP [0:0] + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CA-3(5) + CM-7(b) + SC-7(23) + CM-6(a) + PR.IP-1 + PR.PT-3 + 3.4.3.2.4 + In iptables the default policy is applied only after all +the applicable rules in the table are examined for a match. Setting the +default policy to DROP implements proper design for a firewall, i.e. +any packets which are not explicitly permitted should not be +accepted. + CCE-85968-6 + sed -i 's/^:INPUT ACCEPT.*/:INPUT DROP [0:0]/g' /etc/sysconfig/iptables + + + + + + + Set Default iptables Policy for Forwarded Packets + To set the default policy to DROP (instead of ACCEPT) for +the built-in FORWARD chain which processes packets that will be forwarded from +one interface to another, +add or correct the following line in +/etc/sysconfig/iptables: +:FORWARD DROP [0:0] + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CA-3(5) + CM-7(b) + SC-7(23) + CM-6(a) + PR.IP-1 + PR.PT-3 + In iptables, the default policy is applied only after all +the applicable rules in the table are examined for a match. Setting the +default policy to DROP implements proper design for a firewall, i.e. +any packets which are not explicitly permitted should not be +accepted. + sed -i 's/^:FORWARD ACCEPT.*/:FORWARD DROP [0:0]/g' /etc/sysconfig/iptables + + + + + + + Restrict ICMP Message Types + In /etc/sysconfig/iptables, the accepted ICMP messages +types can be restricted. To accept only ICMP echo reply, destination +unreachable, and time exceeded messages, remove the line: +-A INPUT -p icmp --icmp-type any -j ACCEPT +and insert the lines: +-A INPUT -p icmp --icmp-type echo-reply -j ACCEPT +-A INPUT -p icmp --icmp-type destination-unreachable -j ACCEPT +-A INPUT -p icmp --icmp-type time-exceeded -j ACCEPT +To allow the system to respond to pings, also insert the following line: +-A INPUT -p icmp --icmp-type echo-request -j ACCEPT +Ping responses can also be limited to certain networks or hosts by using the -s +option in the previous rule. Because IPv6 depends so heavily on ICMPv6, it is +preferable to deny the ICMPv6 packets you know you don't need (e.g. ping +requests) in /etc/sysconfig/ip6tables, while letting everything else +through: +-A INPUT -p icmpv6 --icmpv6-type echo-request -j DROP +If you are going to statically configure the system's address, it should +ignore Router Advertisements which could add another IPv6 address to the +interface or alter important network settings: +-A INPUT -p icmpv6 --icmpv6-type router-advertisement -j DROP +Restricting ICMPv6 message types in /etc/sysconfig/ip6tables is not +recommended because the operation of IPv6 depends heavily on ICMPv6. Thus, great +care must be taken if any other ICMPv6 types are blocked. + + + Log and Drop Packets with Suspicious Source Addresses + Packets with non-routable source addresses should be rejected, as they may indicate spoofing. Because the +modified policy will reject non-matching packets, you only need to add these rules if you are interested in also +logging these spoofing or suspicious attempts before they are dropped. If you do choose to log various suspicious +traffic, add identical rules with a target of DROP after each LOG. +To log and then drop these IPv4 packets, insert the following rules in /etc/sysconfig/iptables (excepting +any that are intentionally used): +-A INPUT -s 10.0.0.0/8 -j LOG --log-prefix "IP DROP SPOOF A: " +-A INPUT -s 172.16.0.0/12 -j LOG --log-prefix "IP DROP SPOOF B: " +-A INPUT -s 192.168.0.0/16 -j LOG --log-prefix "IP DROP SPOOF C: " +-A INPUT -s 224.0.0.0/4 -j LOG --log-prefix "IP DROP MULTICAST D: " +-A INPUT -s 240.0.0.0/5 -j LOG --log-prefix "IP DROP SPOOF E: " +-A INPUT -d 127.0.0.0/8 -j LOG --log-prefix "IP DROP LOOPBACK: " +Similarly, you might wish to log packets containing some IPv6 reserved addresses if they are not expected +on your network: +-A INPUT -i eth0 -s ::1 -j LOG --log-prefix "IPv6 DROP LOOPBACK: " +-A INPUT -s 2002:E000::/20 -j LOG --log-prefix "IPv6 6to4 TRAFFIC: " +-A INPUT -s 2002:7F00::/24 -j LOG --log-prefix "IPv6 6to4 TRAFFIC: " +-A INPUT -s 2002:0000::/24 -j LOG --log-prefix "IPv6 6to4 TRAFFIC: " +-A INPUT -s 2002:FF00::/24 -j LOG --log-prefix "IPv6 6to4 TRAFFIC: " +-A INPUT -s 2002:0A00::/24 -j LOG --log-prefix "IPv6 6to4 TRAFFIC: " +-A INPUT -s 2002:AC10::/28 -j LOG --log-prefix "IPv6 6to4 TRAFFIC: " +-A INPUT -s 2002:C0A8::/32 -j LOG --log-prefix "IPv6 6to4 TRAFFIC: " +If you are not expecting to see site-local multicast or auto-tunneled traffic, you can log those: +-A INPUT -s FF05::/16 -j LOG --log-prefix "IPv6 SITE-LOCAL MULTICAST: " +-A INPUT -s ::0.0.0.0/96 -j LOG --log-prefix "IPv4 COMPATIBLE IPv6 ADDR: " +If you wish to block multicasts to all link-local nodes (e.g. if you are not using router auto-configuration and +do not plan to have any services that multicast to the entire local network), you can block the link-local +all-nodes multicast address (before accepting incoming ICMPv6): +-A INPUT -d FF02::1 -j LOG --log-prefix "Link-local All-Nodes Multicast: " +However, if you're going to allow IPv4 compatible IPv6 addresses (of the form ::0.0.0.0/96), you should +then consider logging the non-routable IPv4-compatible addresses: +-A INPUT -s ::0.0.0.0/104 -j LOG --log-prefix "IP NON-ROUTABLE ADDR: " +-A INPUT -s ::127.0.0.0/104 -j LOG --log-prefix "IP DROP LOOPBACK: " +-A INPUT -s ::224.0.0.0.0/100 -j LOG --log-prefix "IP DROP MULTICAST D: " +-A INPUT -s ::255.0.0.0/104 -j LOG --log-prefix "IP BROADCAST: " +If you are not expecting to see any IPv4 (or IPv4-compatible) traffic on your network, consider logging it before it gets dropped: +-A INPUT -s ::FFFF:0.0.0.0/96 -j LOG --log-prefix "IPv4 MAPPED IPv6 ADDR: " +-A INPUT -s 2002::/16 -j LOG --log-prefix "IPv6 6to4 ADDR: " +The following rule will log all traffic originating from a site-local address, which is deprecated address space: +-A INPUT -s FEC0::/10 -j LOG --log-prefix "SITE-LOCAL ADDRESS TRAFFIC: " + + + + + IPv6 + The system includes support for Internet Protocol +version 6. A major and often-mentioned improvement over IPv4 is its +enormous increase in the number of available addresses. Another +important feature is its support for automatic configuration of +many network settings. + + Disable Support for IPv6 Unless Needed + Despite configuration that suggests support for IPv6 has +been disabled, link-local IPv6 address auto-configuration occurs +even when only an IPv4 address is assigned. The only way to +effectively prevent execution of the IPv6 networking stack is to +instruct the system not to activate the IPv6 kernel module. + + Ensure IPv6 is disabled through kernel boot parameter + To disable IPv6 protocol support in the Linux kernel, +add the argument ipv6.disable=1 to the default +GRUB2 command line for the Linux operating system. +To ensure that ipv6.disable=1 is added as a kernel command line +argument to newly installed kernels, add ipv6.disable=1 to the +default Grub2 command line for Linux operating systems. Modify the line within +/etc/default/grub as shown below: +GRUB_CMDLINE_LINUX="... ipv6.disable=1 ..." +Run the following command to update command line for already installed kernels:# grubby --update-kernel=ALL --args="ipv6.disable=1" + 3.6 + Any unnecessary network stacks, including IPv6, should be disabled to reduce +the vulnerability to exploitation. + + CCE-82887-1 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-82887-1 + - grub2_ipv6_disable_argument + - low_disruption + - low_severity + - medium_complexity + - reboot_required + - restrict_strategy + +- name: Update grub defaults and the bootloader menu + command: /sbin/grubby --update-kernel=ALL --args="ipv6.disable=1" + when: '"grub2-common" in ansible_facts.packages' + tags: + - CCE-82887-1 + - grub2_ipv6_disable_argument + - low_disruption + - low_severity + - medium_complexity + - reboot_required + - restrict_strategy + + [customizations.kernel] +append = "ipv6.disable=1" + + # Remediation is applicable only in certain platforms +if rpm --quiet -q grub2-common; then + +grubby --update-kernel=ALL --args=ipv6.disable=1 --env=/boot/grub2/grubenv + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable IPv6 Networking Support Automatic Loading + To prevent the IPv6 kernel module (ipv6) from binding to the +IPv6 networking stack, add the following line to +/etc/modprobe.d/disabled.conf (or another file in +/etc/modprobe.d): +options ipv6 disable=1 +This permits the IPv6 module to be loaded (and thus satisfy other modules that +depend on it), while disabling support for the IPv6 protocol. + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + PR.PT-3 + 3.6 + Any unnecessary network stacks - including IPv6 - should be disabled, to reduce +the vulnerability to exploitation. + + CCE-82872-3 + - name: Disable IPv6 Networking kernel module + lineinfile: + create: true + dest: /etc/modprobe.d/ipv6.conf + regexp: ^options\s+ipv6\s+disable=\d + line: options ipv6 disable=1 + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82872-3 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - kernel_module_ipv6_option_disabled + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + +- name: Ensure disable_ipv6 (all and default) is set to 1 + sysctl: + name: '{{ item }}' + value: '1' + state: present + reload: true + with_items: + - net.ipv6.conf.all.disable_ipv6 + - net.ipv6.conf.default.disable_ipv6 + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82872-3 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - kernel_module_ipv6_option_disabled + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Prevent the IPv6 kernel module (ipv6) from loading the IPv6 networking stack +echo "options ipv6 disable=1" > /etc/modprobe.d/ipv6.conf + +# Since according to: https://access.redhat.com/solutions/72733 +# "ipv6 disable=1" options doesn't always disable the IPv6 networking stack from +# loading, instruct also sysctl configuration to disable IPv6 according to: +# https://access.redhat.com/solutions/8709#rhel6disable + +declare -a IPV6_SETTINGS=("net.ipv6.conf.all.disable_ipv6" "net.ipv6.conf.default.disable_ipv6") + +for setting in "${IPV6_SETTINGS[@]}" +do + # Set runtime =1 for setting + /sbin/sysctl -q -n -w "$setting=1" + + # If setting is present in /etc/sysctl.conf, change value to "1" + # else, add "$setting = 1" to /etc/sysctl.conf + if grep -q ^"$setting" /etc/sysctl.conf ; then + sed -i "s/^$setting.*/$setting = 1/g" /etc/sysctl.conf + else + echo "" >> /etc/sysctl.conf + echo "# Set $setting = 1 per security requirements" >> /etc/sysctl.conf + echo "$setting = 1" >> /etc/sysctl.conf + fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable Interface Usage of IPv6 + To disable interface usage of IPv6, add or correct the following lines in /etc/sysconfig/network: +NETWORKING_IPV6=no +IPV6INIT=no + + + + Disable Support for RPC IPv6 + RPC services for NFSv4 try to load transport modules for +udp6 and tcp6 by default, even if IPv6 has been disabled in +/etc/modprobe.d. To prevent RPC services such as rpc.mountd +from attempting to start IPv6 network listeners, remove or comment out the +following two lines in /etc/netconfig: +udp6 tpi_clts v inet6 udp - - +tcp6 tpi_cots_ord v inet6 tcp - - + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + 3.1.20 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + PR.PT-3 + + +# Drop 'tcp6' and 'udp6' entries from /etc/netconfig to prevent RPC +# services for NFSv4 from attempting to start IPv6 network listeners +declare -a IPV6_RPC_ENTRIES=("tcp6" "udp6") + +for rpc_entry in "${IPV6_RPC_ENTRIES[@]}" +do + sed -i "/^${rpc_entry}[[:space:]]\\+tpi\\_.*inet6.*/d" /etc/netconfig +done + + + + + + + Disable IPv6 Addressing on All IPv6 Interfaces + To disable support for (ipv6) addressing on all interface add the following line to +/etc/sysctl.d/ipv6.conf (or another file in /etc/sysctl.d): +net.ipv6.conf.all.disable_ipv6 = 1 +This disables IPv6 on all network interfaces as other services and system +functionality require the IPv6 stack loaded to work. + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + 3.1.20 + CCI-001551 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + PR.PT-3 + Any unnecessary network stacks - including IPv6 - should be disabled, to reduce +the vulnerability to exploitation. + + CCE-85904-1 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.ipv6.conf.all.disable_ipv6.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-85904-1 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv6_conf_all_disable_ipv6 + +- name: Comment out any occurrences of net.ipv6.conf.all.disable_ipv6 from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.ipv6.conf.all.disable_ipv6 + replace: '#net.ipv6.conf.all.disable_ipv6' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-85904-1 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv6_conf_all_disable_ipv6 + +- name: Ensure sysctl net.ipv6.conf.all.disable_ipv6 is set to 1 + sysctl: + name: net.ipv6.conf.all.disable_ipv6 + value: '1' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-85904-1 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv6_conf_all_disable_ipv6 + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.ipv6.conf.all.disable_ipv6 from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.ipv6.conf.all.disable_ipv6.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.ipv6.conf.all.disable_ipv6" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done + +# +# Set runtime for net.ipv6.conf.all.disable_ipv6 +# +/sbin/sysctl -q -n -w net.ipv6.conf.all.disable_ipv6="1" + +# +# If net.ipv6.conf.all.disable_ipv6 present in /etc/sysctl.conf, change value to "1" +# else, add "net.ipv6.conf.all.disable_ipv6 = 1" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv6.conf.all.disable_ipv6") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "1" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.ipv6.conf.all.disable_ipv6\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.ipv6.conf.all.disable_ipv6\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-85904-1" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable IPv6 Addressing on IPv6 Interfaces by Default + To disable support for (ipv6) addressing on interfaces by default add the following line to +/etc/sysctl.d/ipv6.conf (or another file in /etc/sysctl.d): +net.ipv6.conf.default.disable_ipv6 = 1 +This disables IPv6 on network interfaces by default as other services and system +functionality require the IPv6 stack loaded to work. + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + 3.1.20 + CCI-001551 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + PR.PT-3 + Any unnecessary network stacks - including IPv6 - should be disabled, to reduce +the vulnerability to exploitation. + + CCE-86004-9 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.ipv6.conf.default.disable_ipv6.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86004-9 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv6_conf_default_disable_ipv6 + +- name: Comment out any occurrences of net.ipv6.conf.default.disable_ipv6 from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.ipv6.conf.default.disable_ipv6 + replace: '#net.ipv6.conf.default.disable_ipv6' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86004-9 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv6_conf_default_disable_ipv6 + +- name: Ensure sysctl net.ipv6.conf.default.disable_ipv6 is set to 1 + sysctl: + name: net.ipv6.conf.default.disable_ipv6 + value: '1' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86004-9 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv6_conf_default_disable_ipv6 + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.ipv6.conf.default.disable_ipv6 from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.ipv6.conf.default.disable_ipv6.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.ipv6.conf.default.disable_ipv6" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done + +# +# Set runtime for net.ipv6.conf.default.disable_ipv6 +# +/sbin/sysctl -q -n -w net.ipv6.conf.default.disable_ipv6="1" + +# +# If net.ipv6.conf.default.disable_ipv6 present in /etc/sysctl.conf, change value to "1" +# else, add "net.ipv6.conf.default.disable_ipv6 = 1" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv6.conf.default.disable_ipv6") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "1" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.ipv6.conf.default.disable_ipv6\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.ipv6.conf.default.disable_ipv6\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-86004-9" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Configure IPv6 Settings if Necessary + A major feature of IPv6 is the extent to which systems +implementing it can automatically configure their networking +devices using information from the network. From a security +perspective, manually configuring important configuration +information is preferable to accepting it from the network +in an unauthenticated fashion. + + IPV6_AUTOCONF + Toggle global IPv6 auto-configuration (only, if global +forwarding is disabled) + no + no + yes + + + net.ipv6.conf.all.accept_ra_defrtr + Accept default router in router advertisements? + 0 + 0 + 1 + + + net.ipv6.conf.all.accept_ra_pinfo + Accept prefix information in router advertisements? + 0 + 0 + 1 + + + net.ipv6.conf.all.accept_ra_rtr_pref + Accept router preference in router advertisements? + 0 + 0 + 1 + + + net.ipv6.conf.all.accept_ra + Accept all router advertisements? + 0 + 0 + 1 + + + net.ipv6.conf.all.accept_redirects + Toggle ICMP Redirect Acceptance + 0 + 0 + 1 + + + net.ipv6.conf.all.accept_source_route + Trackers could be using source-routed packets to +generate traffic that seems to be intra-net, but actually was +created outside and has been redirected. + 0 + 0 + 1 + + + net.ipv6.conf.all.autoconf + Enable auto configuration on IPv6 interfaces + 0 + 0 + 1 + + + net.ipv6.conf.all.forwarding + Toggle IPv6 Forwarding + 0 + 0 + 1 + + + net.ipv6.conf.all.max_addresses + Maximum number of autoconfigured IPv6 addresses + 1 + + + net.ipv6.conf.all.router_solicitations + Accept all router solicitations? + 0 + 0 + 1 + + + net.ipv6.conf.default.accept_ra_defrtr + Accept default router in router advertisements? + 0 + 0 + 1 + + + net.ipv6.conf.default.accept_ra_pinfo + Accept prefix information in router advertisements? + 0 + 0 + 1 + + + net.ipv6.conf.default.accept_ra_rtr_pref + Accept router preference in router advertisements? + 0 + 0 + 1 + + + net.ipv6.conf.default.accept_ra + Accept default router advertisements by default? + 0 + 0 + 1 + + + net.ipv6.conf.default.accept_redirects + Toggle ICMP Redirect Acceptance By Default + 0 + 0 + 1 + + + net.ipv6.conf.default.accept_source_route + Trackers could be using source-routed packets to +generate traffic that seems to be intra-net, but actually was +created outside and has been redirected. + 0 + 0 + 1 + + + net.ipv6.conf.default.autoconf + Enable auto configuration on IPv6 interfaces + 0 + 0 + 1 + + + net.ipv6.conf.default.forwarding + Toggle IPv6 default Forwarding + 0 + 0 + 1 + + + net.ipv6.conf.default.max_addresses + Maximum number of autoconfigured IPv6 addresses + 1 + + + net.ipv6.conf.default.router_solicitations + Accept all router solicitations by default? + 0 + 0 + 1 + + + Manually Assign IPv6 Router Address + Edit the file +/etc/sysconfig/network-scripts/ifcfg-interface, and add or correct +the following line (substituting your gateway IP as appropriate): +IPV6_DEFAULTGW=2001:0DB8::0001 +Router addresses should be manually set and not accepted via any +auto-configuration or router advertisement. + CCI-000366 + + + + + + + Use Privacy Extensions for Address + To introduce randomness into the automatic generation of IPv6 +addresses, add or correct the following line in +/etc/sysconfig/network-scripts/ifcfg-interface: +IPV6_PRIVACY=rfc3041 +Automatically-generated IPv6 addresses are based on the underlying hardware +(e.g. Ethernet) address, and so it becomes possible to track a piece of +hardware over its lifetime using its traffic. If it is important for a system's +IP address to not trivially reveal its hardware address, this setting should be +applied. + 3.1.20 + CCI-000366 + + +# enable randomness in ipv6 address generation +for interface in /etc/sysconfig/network-scripts/ifcfg-* +do + echo "IPV6_PRIVACY=rfc3041" >> $interface +done + + + + + + + Manually Assign Global IPv6 Address + To manually assign an IP address for an interface, edit the +file /etc/sysconfig/network-scripts/ifcfg-interface. Add or correct the +following line (substituting the correct IPv6 address): +IPV6ADDR=2001:0DB8::ABCD/64 +Manually assigning an IP address is preferable to accepting one from routers or +from the network otherwise. The example address here is an IPv6 address +reserved for documentation purposes, as defined by RFC3849. + CCI-000366 + 1315 + 1319 + + CCE-84298-9 + + + + + + Configure Accepting Router Advertisements on All IPv6 Interfaces + To set the runtime status of the net.ipv6.conf.all.accept_ra kernel parameter, run the following command: $ sudo sysctl -w net.ipv6.conf.all.accept_ra=0 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: net.ipv6.conf.all.accept_ra = 0 + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + 3.1.20 + CCI-000366 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + PR.PT-3 + SRG-OS-000480-GPOS-00227 + RHEL-08-040261 + 3.3.9 + SV-230541r833351_rule + An illicit router advertisement message could result in a man-in-the-middle attack. + + CCE-81006-9 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.ipv6.conf.all.accept_ra.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-81006-9 + - DISA-STIG-RHEL-08-040261 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv6_conf_all_accept_ra + +- name: Comment out any occurrences of net.ipv6.conf.all.accept_ra from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.ipv6.conf.all.accept_ra + replace: '#net.ipv6.conf.all.accept_ra' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-81006-9 + - DISA-STIG-RHEL-08-040261 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv6_conf_all_accept_ra +- name: XCCDF Value sysctl_net_ipv6_conf_all_accept_ra_value # promote to variable + set_fact: + sysctl_net_ipv6_conf_all_accept_ra_value: !!str + tags: + - always + +- name: Ensure sysctl net.ipv6.conf.all.accept_ra is set + sysctl: + name: net.ipv6.conf.all.accept_ra + value: '{{ sysctl_net_ipv6_conf_all_accept_ra_value }}' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-81006-9 + - DISA-STIG-RHEL-08-040261 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv6_conf_all_accept_ra + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,net.ipv6.conf.all.accept_ra%3D0%0A + mode: 0644 + path: /etc/sysctl.d/75-sysctl_net_ipv6_conf_all_accept_ra.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.ipv6.conf.all.accept_ra from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.ipv6.conf.all.accept_ra.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.ipv6.conf.all.accept_ra" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done +sysctl_net_ipv6_conf_all_accept_ra_value='' + + +# +# Set runtime for net.ipv6.conf.all.accept_ra +# +/sbin/sysctl -q -n -w net.ipv6.conf.all.accept_ra="$sysctl_net_ipv6_conf_all_accept_ra_value" + +# +# If net.ipv6.conf.all.accept_ra present in /etc/sysctl.conf, change value to appropriate value +# else, add "net.ipv6.conf.all.accept_ra = value" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv6.conf.all.accept_ra") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv6_conf_all_accept_ra_value" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.ipv6.conf.all.accept_ra\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.ipv6.conf.all.accept_ra\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-81006-9" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Configure Accepting Default Router in Router Advertisements on All IPv6 Interfaces + To set the runtime status of the net.ipv6.conf.all.accept_ra_defrtr kernel parameter, run the following command: $ sudo sysctl -w net.ipv6.conf.all.accept_ra_defrtr=0 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: net.ipv6.conf.all.accept_ra_defrtr = 0 + BP28(R22) + An illicit router advertisement message could result in a man-in-the-middle attack. + + CCE-84272-4 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.ipv6.conf.all.accept_ra_defrtr.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84272-4 + - disable_strategy + - low_complexity + - medium_disruption + - reboot_required + - sysctl_net_ipv6_conf_all_accept_ra_defrtr + - unknown_severity + +- name: Comment out any occurrences of net.ipv6.conf.all.accept_ra_defrtr from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.ipv6.conf.all.accept_ra_defrtr + replace: '#net.ipv6.conf.all.accept_ra_defrtr' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84272-4 + - disable_strategy + - low_complexity + - medium_disruption + - reboot_required + - sysctl_net_ipv6_conf_all_accept_ra_defrtr + - unknown_severity +- name: XCCDF Value sysctl_net_ipv6_conf_all_accept_ra_defrtr_value # promote to variable + set_fact: + sysctl_net_ipv6_conf_all_accept_ra_defrtr_value: !!str + tags: + - always + +- name: Ensure sysctl net.ipv6.conf.all.accept_ra_defrtr is set + sysctl: + name: net.ipv6.conf.all.accept_ra_defrtr + value: '{{ sysctl_net_ipv6_conf_all_accept_ra_defrtr_value }}' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84272-4 + - disable_strategy + - low_complexity + - medium_disruption + - reboot_required + - sysctl_net_ipv6_conf_all_accept_ra_defrtr + - unknown_severity + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.ipv6.conf.all.accept_ra_defrtr from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.ipv6.conf.all.accept_ra_defrtr.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.ipv6.conf.all.accept_ra_defrtr" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done +sysctl_net_ipv6_conf_all_accept_ra_defrtr_value='' + + +# +# Set runtime for net.ipv6.conf.all.accept_ra_defrtr +# +/sbin/sysctl -q -n -w net.ipv6.conf.all.accept_ra_defrtr="$sysctl_net_ipv6_conf_all_accept_ra_defrtr_value" + +# +# If net.ipv6.conf.all.accept_ra_defrtr present in /etc/sysctl.conf, change value to appropriate value +# else, add "net.ipv6.conf.all.accept_ra_defrtr = value" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv6.conf.all.accept_ra_defrtr") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv6_conf_all_accept_ra_defrtr_value" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.ipv6.conf.all.accept_ra_defrtr\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.ipv6.conf.all.accept_ra_defrtr\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-84272-4" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Configure Accepting Prefix Information in Router Advertisements on All IPv6 Interfaces + To set the runtime status of the net.ipv6.conf.all.accept_ra_pinfo kernel parameter, run the following command: $ sudo sysctl -w net.ipv6.conf.all.accept_ra_pinfo=0 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: net.ipv6.conf.all.accept_ra_pinfo = 0 + BP28(R22) + An illicit router advertisement message could result in a man-in-the-middle attack. + + CCE-84280-7 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.ipv6.conf.all.accept_ra_pinfo.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84280-7 + - disable_strategy + - low_complexity + - medium_disruption + - reboot_required + - sysctl_net_ipv6_conf_all_accept_ra_pinfo + - unknown_severity + +- name: Comment out any occurrences of net.ipv6.conf.all.accept_ra_pinfo from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.ipv6.conf.all.accept_ra_pinfo + replace: '#net.ipv6.conf.all.accept_ra_pinfo' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84280-7 + - disable_strategy + - low_complexity + - medium_disruption + - reboot_required + - sysctl_net_ipv6_conf_all_accept_ra_pinfo + - unknown_severity +- name: XCCDF Value sysctl_net_ipv6_conf_all_accept_ra_pinfo_value # promote to variable + set_fact: + sysctl_net_ipv6_conf_all_accept_ra_pinfo_value: !!str + tags: + - always + +- name: Ensure sysctl net.ipv6.conf.all.accept_ra_pinfo is set + sysctl: + name: net.ipv6.conf.all.accept_ra_pinfo + value: '{{ sysctl_net_ipv6_conf_all_accept_ra_pinfo_value }}' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84280-7 + - disable_strategy + - low_complexity + - medium_disruption + - reboot_required + - sysctl_net_ipv6_conf_all_accept_ra_pinfo + - unknown_severity + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.ipv6.conf.all.accept_ra_pinfo from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.ipv6.conf.all.accept_ra_pinfo.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.ipv6.conf.all.accept_ra_pinfo" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done +sysctl_net_ipv6_conf_all_accept_ra_pinfo_value='' + + +# +# Set runtime for net.ipv6.conf.all.accept_ra_pinfo +# +/sbin/sysctl -q -n -w net.ipv6.conf.all.accept_ra_pinfo="$sysctl_net_ipv6_conf_all_accept_ra_pinfo_value" + +# +# If net.ipv6.conf.all.accept_ra_pinfo present in /etc/sysctl.conf, change value to appropriate value +# else, add "net.ipv6.conf.all.accept_ra_pinfo = value" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv6.conf.all.accept_ra_pinfo") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv6_conf_all_accept_ra_pinfo_value" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.ipv6.conf.all.accept_ra_pinfo\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.ipv6.conf.all.accept_ra_pinfo\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-84280-7" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Configure Accepting Router Preference in Router Advertisements on All IPv6 Interfaces + To set the runtime status of the net.ipv6.conf.all.accept_ra_rtr_pref kernel parameter, run the following command: $ sudo sysctl -w net.ipv6.conf.all.accept_ra_rtr_pref=0 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: net.ipv6.conf.all.accept_ra_rtr_pref = 0 + BP28(R22) + An illicit router advertisement message could result in a man-in-the-middle attack. + + CCE-84288-0 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.ipv6.conf.all.accept_ra_rtr_pref.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84288-0 + - disable_strategy + - low_complexity + - medium_disruption + - reboot_required + - sysctl_net_ipv6_conf_all_accept_ra_rtr_pref + - unknown_severity + +- name: Comment out any occurrences of net.ipv6.conf.all.accept_ra_rtr_pref from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.ipv6.conf.all.accept_ra_rtr_pref + replace: '#net.ipv6.conf.all.accept_ra_rtr_pref' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84288-0 + - disable_strategy + - low_complexity + - medium_disruption + - reboot_required + - sysctl_net_ipv6_conf_all_accept_ra_rtr_pref + - unknown_severity +- name: XCCDF Value sysctl_net_ipv6_conf_all_accept_ra_rtr_pref_value # promote to variable + set_fact: + sysctl_net_ipv6_conf_all_accept_ra_rtr_pref_value: !!str + tags: + - always + +- name: Ensure sysctl net.ipv6.conf.all.accept_ra_rtr_pref is set + sysctl: + name: net.ipv6.conf.all.accept_ra_rtr_pref + value: '{{ sysctl_net_ipv6_conf_all_accept_ra_rtr_pref_value }}' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84288-0 + - disable_strategy + - low_complexity + - medium_disruption + - reboot_required + - sysctl_net_ipv6_conf_all_accept_ra_rtr_pref + - unknown_severity + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.ipv6.conf.all.accept_ra_rtr_pref from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.ipv6.conf.all.accept_ra_rtr_pref.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.ipv6.conf.all.accept_ra_rtr_pref" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done +sysctl_net_ipv6_conf_all_accept_ra_rtr_pref_value='' + + +# +# Set runtime for net.ipv6.conf.all.accept_ra_rtr_pref +# +/sbin/sysctl -q -n -w net.ipv6.conf.all.accept_ra_rtr_pref="$sysctl_net_ipv6_conf_all_accept_ra_rtr_pref_value" + +# +# If net.ipv6.conf.all.accept_ra_rtr_pref present in /etc/sysctl.conf, change value to appropriate value +# else, add "net.ipv6.conf.all.accept_ra_rtr_pref = value" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv6.conf.all.accept_ra_rtr_pref") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv6_conf_all_accept_ra_rtr_pref_value" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.ipv6.conf.all.accept_ra_rtr_pref\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.ipv6.conf.all.accept_ra_rtr_pref\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-84288-0" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable Accepting ICMP Redirects for All IPv6 Interfaces + To set the runtime status of the net.ipv6.conf.all.accept_redirects kernel parameter, run the following command: $ sudo sysctl -w net.ipv6.conf.all.accept_redirects=0 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: net.ipv6.conf.all.accept_redirects = 0 + BP28(R22) + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + 3.1.20 + CCI-000366 + CCI-001551 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + CM-6(b) + CM-6.1(iv) + PR.IP-1 + PR.PT-3 + SRG-OS-000480-GPOS-00227 + RHEL-08-040280 + 3.3.2 + SV-230544r833357_rule + An illicit ICMP redirect message could result in a man-in-the-middle attack. + + CCE-81009-3 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.ipv6.conf.all.accept_redirects.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-81009-3 + - DISA-STIG-RHEL-08-040280 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-6(b) + - NIST-800-53-CM-6.1(iv) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv6_conf_all_accept_redirects + +- name: Comment out any occurrences of net.ipv6.conf.all.accept_redirects from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.ipv6.conf.all.accept_redirects + replace: '#net.ipv6.conf.all.accept_redirects' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-81009-3 + - DISA-STIG-RHEL-08-040280 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-6(b) + - NIST-800-53-CM-6.1(iv) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv6_conf_all_accept_redirects +- name: XCCDF Value sysctl_net_ipv6_conf_all_accept_redirects_value # promote to variable + set_fact: + sysctl_net_ipv6_conf_all_accept_redirects_value: !!str + tags: + - always + +- name: Ensure sysctl net.ipv6.conf.all.accept_redirects is set + sysctl: + name: net.ipv6.conf.all.accept_redirects + value: '{{ sysctl_net_ipv6_conf_all_accept_redirects_value }}' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-81009-3 + - DISA-STIG-RHEL-08-040280 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-6(b) + - NIST-800-53-CM-6.1(iv) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv6_conf_all_accept_redirects + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,net.ipv6.conf.all.accept_redirects%3D0%0A + mode: 0644 + path: /etc/sysctl.d/75-sysctl_net_ipv6_conf_all_accept_redirects.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.ipv6.conf.all.accept_redirects from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.ipv6.conf.all.accept_redirects.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.ipv6.conf.all.accept_redirects" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done +sysctl_net_ipv6_conf_all_accept_redirects_value='' + + +# +# Set runtime for net.ipv6.conf.all.accept_redirects +# +/sbin/sysctl -q -n -w net.ipv6.conf.all.accept_redirects="$sysctl_net_ipv6_conf_all_accept_redirects_value" + +# +# If net.ipv6.conf.all.accept_redirects present in /etc/sysctl.conf, change value to appropriate value +# else, add "net.ipv6.conf.all.accept_redirects = value" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv6.conf.all.accept_redirects") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv6_conf_all_accept_redirects_value" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.ipv6.conf.all.accept_redirects\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.ipv6.conf.all.accept_redirects\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-81009-3" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable Kernel Parameter for Accepting Source-Routed Packets on all IPv6 Interfaces + To set the runtime status of the net.ipv6.conf.all.accept_source_route kernel parameter, run the following command: $ sudo sysctl -w net.ipv6.conf.all.accept_source_route=0 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: net.ipv6.conf.all.accept_source_route = 0 + BP28(R22) + 1 + 12 + 13 + 14 + 15 + 16 + 18 + 4 + 6 + 8 + 9 + APO01.06 + APO13.01 + DSS01.05 + DSS03.01 + DSS05.02 + DSS05.04 + DSS05.07 + DSS06.02 + 3.1.20 + CCI-000366 + 4.2.3.4 + 4.3.3.4 + 4.4.3.3 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.12.1.1 + A.12.1.2 + A.13.1.1 + A.13.1.2 + A.13.1.3 + A.13.2.1 + A.13.2.2 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-7(a) + CM-7(b) + CM-6(a) + DE.AE-1 + ID.AM-3 + PR.AC-5 + PR.DS-5 + PR.PT-4 + SRG-OS-000480-GPOS-00227 + RHEL-08-040240 + 3.3.1 + SV-230538r833346_rule + Source-routed packets allow the source of the packet to suggest routers +forward the packet along a different path than configured on the router, which can +be used to bypass network security measures. This requirement applies only to the +forwarding of source-routerd traffic, such as when IPv6 forwarding is enabled and +the system is functioning as a router. + +Accepting source-routed packets in the IPv6 protocol has few legitimate +uses. It should be disabled unless it is absolutely required. + + CCE-81013-5 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.ipv6.conf.all.accept_source_route.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-81013-5 + - DISA-STIG-RHEL-08-040240 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv6_conf_all_accept_source_route + +- name: Comment out any occurrences of net.ipv6.conf.all.accept_source_route from + /etc/sysctl.d/*.conf files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.ipv6.conf.all.accept_source_route + replace: '#net.ipv6.conf.all.accept_source_route' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-81013-5 + - DISA-STIG-RHEL-08-040240 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv6_conf_all_accept_source_route +- name: XCCDF Value sysctl_net_ipv6_conf_all_accept_source_route_value # promote to variable + set_fact: + sysctl_net_ipv6_conf_all_accept_source_route_value: !!str + tags: + - always + +- name: Ensure sysctl net.ipv6.conf.all.accept_source_route is set + sysctl: + name: net.ipv6.conf.all.accept_source_route + value: '{{ sysctl_net_ipv6_conf_all_accept_source_route_value }}' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-81013-5 + - DISA-STIG-RHEL-08-040240 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv6_conf_all_accept_source_route + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,net.ipv6.conf.all.accept_source_route%3D0%0A + mode: 0644 + path: /etc/sysctl.d/75-sysctl_net_ipv6_conf_all_accept_source_route.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.ipv6.conf.all.accept_source_route from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.ipv6.conf.all.accept_source_route.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.ipv6.conf.all.accept_source_route" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done +sysctl_net_ipv6_conf_all_accept_source_route_value='' + + +# +# Set runtime for net.ipv6.conf.all.accept_source_route +# +/sbin/sysctl -q -n -w net.ipv6.conf.all.accept_source_route="$sysctl_net_ipv6_conf_all_accept_source_route_value" + +# +# If net.ipv6.conf.all.accept_source_route present in /etc/sysctl.conf, change value to appropriate value +# else, add "net.ipv6.conf.all.accept_source_route = value" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv6.conf.all.accept_source_route") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv6_conf_all_accept_source_route_value" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.ipv6.conf.all.accept_source_route\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.ipv6.conf.all.accept_source_route\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-81013-5" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Configure Auto Configuration on All IPv6 Interfaces + To set the runtime status of the net.ipv6.conf.all.autoconf kernel parameter, run the following command: $ sudo sysctl -w net.ipv6.conf.all.autoconf=0 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: net.ipv6.conf.all.autoconf = 0 + BP28(R22) + An illicit router advertisement message could result in a man-in-the-middle attack. + + CCE-84266-6 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.ipv6.conf.all.autoconf.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84266-6 + - disable_strategy + - low_complexity + - medium_disruption + - reboot_required + - sysctl_net_ipv6_conf_all_autoconf + - unknown_severity + +- name: Comment out any occurrences of net.ipv6.conf.all.autoconf from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.ipv6.conf.all.autoconf + replace: '#net.ipv6.conf.all.autoconf' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84266-6 + - disable_strategy + - low_complexity + - medium_disruption + - reboot_required + - sysctl_net_ipv6_conf_all_autoconf + - unknown_severity +- name: XCCDF Value sysctl_net_ipv6_conf_all_autoconf_value # promote to variable + set_fact: + sysctl_net_ipv6_conf_all_autoconf_value: !!str + tags: + - always + +- name: Ensure sysctl net.ipv6.conf.all.autoconf is set + sysctl: + name: net.ipv6.conf.all.autoconf + value: '{{ sysctl_net_ipv6_conf_all_autoconf_value }}' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84266-6 + - disable_strategy + - low_complexity + - medium_disruption + - reboot_required + - sysctl_net_ipv6_conf_all_autoconf + - unknown_severity + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.ipv6.conf.all.autoconf from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.ipv6.conf.all.autoconf.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.ipv6.conf.all.autoconf" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done +sysctl_net_ipv6_conf_all_autoconf_value='' + + +# +# Set runtime for net.ipv6.conf.all.autoconf +# +/sbin/sysctl -q -n -w net.ipv6.conf.all.autoconf="$sysctl_net_ipv6_conf_all_autoconf_value" + +# +# If net.ipv6.conf.all.autoconf present in /etc/sysctl.conf, change value to appropriate value +# else, add "net.ipv6.conf.all.autoconf = value" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv6.conf.all.autoconf") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv6_conf_all_autoconf_value" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.ipv6.conf.all.autoconf\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.ipv6.conf.all.autoconf\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-84266-6" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable Kernel Parameter for IPv6 Forwarding + To set the runtime status of the net.ipv6.conf.all.forwarding kernel parameter, run the following command: $ sudo sysctl -w net.ipv6.conf.all.forwarding=0 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: net.ipv6.conf.all.forwarding = 0 + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 2 + 3 + 7 + 8 + 9 + APO13.01 + BAI04.04 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.03 + DSS03.05 + DSS05.02 + DSS05.05 + DSS05.07 + DSS06.06 + CCI-000366 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 6.2 + SR 7.1 + SR 7.2 + SR 7.6 + A.12.1.2 + A.12.1.3 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.17.2.1 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + CM-6(b) + CM-6.1(iv) + DE.CM-1 + PR.DS-4 + PR.IP-1 + PR.PT-3 + SRG-OS-000480-GPOS-00227 + RHEL-08-040260 + 3.2.1 + SV-230540r833349_rule + IP forwarding permits the kernel to forward packets from one network +interface to another. The ability to forward packets between two networks is +only appropriate for systems acting as routers. + + CCE-82863-2 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.ipv6.conf.all.forwarding.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82863-2 + - DISA-STIG-RHEL-08-040260 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-6(b) + - NIST-800-53-CM-6.1(iv) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv6_conf_all_forwarding + +- name: Comment out any occurrences of net.ipv6.conf.all.forwarding from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.ipv6.conf.all.forwarding + replace: '#net.ipv6.conf.all.forwarding' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82863-2 + - DISA-STIG-RHEL-08-040260 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-6(b) + - NIST-800-53-CM-6.1(iv) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv6_conf_all_forwarding +- name: XCCDF Value sysctl_net_ipv6_conf_all_forwarding_value # promote to variable + set_fact: + sysctl_net_ipv6_conf_all_forwarding_value: !!str + tags: + - always + +- name: Ensure sysctl net.ipv6.conf.all.forwarding is set + sysctl: + name: net.ipv6.conf.all.forwarding + value: '{{ sysctl_net_ipv6_conf_all_forwarding_value }}' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82863-2 + - DISA-STIG-RHEL-08-040260 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-6(b) + - NIST-800-53-CM-6.1(iv) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv6_conf_all_forwarding + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.ipv6.conf.all.forwarding from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.ipv6.conf.all.forwarding.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.ipv6.conf.all.forwarding" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done +sysctl_net_ipv6_conf_all_forwarding_value='' + + +# +# Set runtime for net.ipv6.conf.all.forwarding +# +/sbin/sysctl -q -n -w net.ipv6.conf.all.forwarding="$sysctl_net_ipv6_conf_all_forwarding_value" + +# +# If net.ipv6.conf.all.forwarding present in /etc/sysctl.conf, change value to appropriate value +# else, add "net.ipv6.conf.all.forwarding = value" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv6.conf.all.forwarding") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv6_conf_all_forwarding_value" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.ipv6.conf.all.forwarding\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.ipv6.conf.all.forwarding\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-82863-2" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Configure Maximum Number of Autoconfigured Addresses on All IPv6 Interfaces + To set the runtime status of the net.ipv6.conf.all.max_addresses kernel parameter, run the following command: $ sudo sysctl -w net.ipv6.conf.all.max_addresses=1 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: net.ipv6.conf.all.max_addresses = 1 + BP28(R22) + The number of global unicast IPv6 addresses for each interface should be limited exactly to the number of statically configured addresses. + + CCE-84259-1 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.ipv6.conf.all.max_addresses.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84259-1 + - disable_strategy + - low_complexity + - medium_disruption + - reboot_required + - sysctl_net_ipv6_conf_all_max_addresses + - unknown_severity + +- name: Comment out any occurrences of net.ipv6.conf.all.max_addresses from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.ipv6.conf.all.max_addresses + replace: '#net.ipv6.conf.all.max_addresses' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84259-1 + - disable_strategy + - low_complexity + - medium_disruption + - reboot_required + - sysctl_net_ipv6_conf_all_max_addresses + - unknown_severity +- name: XCCDF Value sysctl_net_ipv6_conf_all_max_addresses_value # promote to variable + set_fact: + sysctl_net_ipv6_conf_all_max_addresses_value: !!str + tags: + - always + +- name: Ensure sysctl net.ipv6.conf.all.max_addresses is set + sysctl: + name: net.ipv6.conf.all.max_addresses + value: '{{ sysctl_net_ipv6_conf_all_max_addresses_value }}' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84259-1 + - disable_strategy + - low_complexity + - medium_disruption + - reboot_required + - sysctl_net_ipv6_conf_all_max_addresses + - unknown_severity + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.ipv6.conf.all.max_addresses from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.ipv6.conf.all.max_addresses.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.ipv6.conf.all.max_addresses" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done +sysctl_net_ipv6_conf_all_max_addresses_value='' + + +# +# Set runtime for net.ipv6.conf.all.max_addresses +# +/sbin/sysctl -q -n -w net.ipv6.conf.all.max_addresses="$sysctl_net_ipv6_conf_all_max_addresses_value" + +# +# If net.ipv6.conf.all.max_addresses present in /etc/sysctl.conf, change value to appropriate value +# else, add "net.ipv6.conf.all.max_addresses = value" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv6.conf.all.max_addresses") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv6_conf_all_max_addresses_value" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.ipv6.conf.all.max_addresses\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.ipv6.conf.all.max_addresses\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-84259-1" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Configure Denying Router Solicitations on All IPv6 Interfaces + To set the runtime status of the net.ipv6.conf.all.router_solicitations kernel parameter, run the following command: $ sudo sysctl -w net.ipv6.conf.all.router_solicitations=0 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: net.ipv6.conf.all.router_solicitations = 0 + BP28(R22) + To prevent discovery of the system by other systems, router solicitation requests should be denied. + + CCE-84109-8 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.ipv6.conf.all.router_solicitations.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84109-8 + - disable_strategy + - low_complexity + - medium_disruption + - reboot_required + - sysctl_net_ipv6_conf_all_router_solicitations + - unknown_severity + +- name: Comment out any occurrences of net.ipv6.conf.all.router_solicitations from + /etc/sysctl.d/*.conf files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.ipv6.conf.all.router_solicitations + replace: '#net.ipv6.conf.all.router_solicitations' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84109-8 + - disable_strategy + - low_complexity + - medium_disruption + - reboot_required + - sysctl_net_ipv6_conf_all_router_solicitations + - unknown_severity +- name: XCCDF Value sysctl_net_ipv6_conf_all_router_solicitations_value # promote to variable + set_fact: + sysctl_net_ipv6_conf_all_router_solicitations_value: !!str + tags: + - always + +- name: Ensure sysctl net.ipv6.conf.all.router_solicitations is set + sysctl: + name: net.ipv6.conf.all.router_solicitations + value: '{{ sysctl_net_ipv6_conf_all_router_solicitations_value }}' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84109-8 + - disable_strategy + - low_complexity + - medium_disruption + - reboot_required + - sysctl_net_ipv6_conf_all_router_solicitations + - unknown_severity + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.ipv6.conf.all.router_solicitations from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.ipv6.conf.all.router_solicitations.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.ipv6.conf.all.router_solicitations" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done +sysctl_net_ipv6_conf_all_router_solicitations_value='' + + +# +# Set runtime for net.ipv6.conf.all.router_solicitations +# +/sbin/sysctl -q -n -w net.ipv6.conf.all.router_solicitations="$sysctl_net_ipv6_conf_all_router_solicitations_value" + +# +# If net.ipv6.conf.all.router_solicitations present in /etc/sysctl.conf, change value to appropriate value +# else, add "net.ipv6.conf.all.router_solicitations = value" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv6.conf.all.router_solicitations") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv6_conf_all_router_solicitations_value" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.ipv6.conf.all.router_solicitations\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.ipv6.conf.all.router_solicitations\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-84109-8" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable Accepting Router Advertisements on all IPv6 Interfaces by Default + To set the runtime status of the net.ipv6.conf.default.accept_ra kernel parameter, run the following command: $ sudo sysctl -w net.ipv6.conf.default.accept_ra=0 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: net.ipv6.conf.default.accept_ra = 0 + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + 3.1.20 + CCI-000366 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + PR.PT-3 + SRG-OS-000480-GPOS-00227 + RHEL-08-040262 + 3.3.9 + SV-230542r833353_rule + An illicit router advertisement message could result in a man-in-the-middle attack. + + CCE-81007-7 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.ipv6.conf.default.accept_ra.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-81007-7 + - DISA-STIG-RHEL-08-040262 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv6_conf_default_accept_ra + +- name: Comment out any occurrences of net.ipv6.conf.default.accept_ra from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.ipv6.conf.default.accept_ra + replace: '#net.ipv6.conf.default.accept_ra' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-81007-7 + - DISA-STIG-RHEL-08-040262 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv6_conf_default_accept_ra +- name: XCCDF Value sysctl_net_ipv6_conf_default_accept_ra_value # promote to variable + set_fact: + sysctl_net_ipv6_conf_default_accept_ra_value: !!str + tags: + - always + +- name: Ensure sysctl net.ipv6.conf.default.accept_ra is set + sysctl: + name: net.ipv6.conf.default.accept_ra + value: '{{ sysctl_net_ipv6_conf_default_accept_ra_value }}' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-81007-7 + - DISA-STIG-RHEL-08-040262 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv6_conf_default_accept_ra + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,net.ipv6.conf.default.accept_ra%3D0%0A + mode: 0644 + path: /etc/sysctl.d/75-sysctl_net_ipv6_conf_default_accept_ra.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.ipv6.conf.default.accept_ra from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.ipv6.conf.default.accept_ra.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.ipv6.conf.default.accept_ra" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done +sysctl_net_ipv6_conf_default_accept_ra_value='' + + +# +# Set runtime for net.ipv6.conf.default.accept_ra +# +/sbin/sysctl -q -n -w net.ipv6.conf.default.accept_ra="$sysctl_net_ipv6_conf_default_accept_ra_value" + +# +# If net.ipv6.conf.default.accept_ra present in /etc/sysctl.conf, change value to appropriate value +# else, add "net.ipv6.conf.default.accept_ra = value" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv6.conf.default.accept_ra") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv6_conf_default_accept_ra_value" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.ipv6.conf.default.accept_ra\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.ipv6.conf.default.accept_ra\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-81007-7" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Configure Accepting Default Router in Router Advertisements on All IPv6 Interfaces By Default + To set the runtime status of the net.ipv6.conf.default.accept_ra_defrtr kernel parameter, run the following command: $ sudo sysctl -w net.ipv6.conf.default.accept_ra_defrtr=0 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: net.ipv6.conf.default.accept_ra_defrtr = 0 + BP28(R22) + An illicit router advertisement message could result in a man-in-the-middle attack. + + CCE-84268-2 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.ipv6.conf.default.accept_ra_defrtr.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84268-2 + - disable_strategy + - low_complexity + - medium_disruption + - reboot_required + - sysctl_net_ipv6_conf_default_accept_ra_defrtr + - unknown_severity + +- name: Comment out any occurrences of net.ipv6.conf.default.accept_ra_defrtr from + /etc/sysctl.d/*.conf files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.ipv6.conf.default.accept_ra_defrtr + replace: '#net.ipv6.conf.default.accept_ra_defrtr' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84268-2 + - disable_strategy + - low_complexity + - medium_disruption + - reboot_required + - sysctl_net_ipv6_conf_default_accept_ra_defrtr + - unknown_severity +- name: XCCDF Value sysctl_net_ipv6_conf_default_accept_ra_defrtr_value # promote to variable + set_fact: + sysctl_net_ipv6_conf_default_accept_ra_defrtr_value: !!str + tags: + - always + +- name: Ensure sysctl net.ipv6.conf.default.accept_ra_defrtr is set + sysctl: + name: net.ipv6.conf.default.accept_ra_defrtr + value: '{{ sysctl_net_ipv6_conf_default_accept_ra_defrtr_value }}' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84268-2 + - disable_strategy + - low_complexity + - medium_disruption + - reboot_required + - sysctl_net_ipv6_conf_default_accept_ra_defrtr + - unknown_severity + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.ipv6.conf.default.accept_ra_defrtr from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.ipv6.conf.default.accept_ra_defrtr.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.ipv6.conf.default.accept_ra_defrtr" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done +sysctl_net_ipv6_conf_default_accept_ra_defrtr_value='' + + +# +# Set runtime for net.ipv6.conf.default.accept_ra_defrtr +# +/sbin/sysctl -q -n -w net.ipv6.conf.default.accept_ra_defrtr="$sysctl_net_ipv6_conf_default_accept_ra_defrtr_value" + +# +# If net.ipv6.conf.default.accept_ra_defrtr present in /etc/sysctl.conf, change value to appropriate value +# else, add "net.ipv6.conf.default.accept_ra_defrtr = value" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv6.conf.default.accept_ra_defrtr") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv6_conf_default_accept_ra_defrtr_value" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.ipv6.conf.default.accept_ra_defrtr\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.ipv6.conf.default.accept_ra_defrtr\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-84268-2" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Configure Accepting Prefix Information in Router Advertisements on All IPv6 Interfaces By Default + To set the runtime status of the net.ipv6.conf.default.accept_ra_pinfo kernel parameter, run the following command: $ sudo sysctl -w net.ipv6.conf.default.accept_ra_pinfo=0 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: net.ipv6.conf.default.accept_ra_pinfo = 0 + BP28(R22) + An illicit router advertisement message could result in a man-in-the-middle attack. + + CCE-84051-2 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.ipv6.conf.default.accept_ra_pinfo.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84051-2 + - disable_strategy + - low_complexity + - medium_disruption + - reboot_required + - sysctl_net_ipv6_conf_default_accept_ra_pinfo + - unknown_severity + +- name: Comment out any occurrences of net.ipv6.conf.default.accept_ra_pinfo from + /etc/sysctl.d/*.conf files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.ipv6.conf.default.accept_ra_pinfo + replace: '#net.ipv6.conf.default.accept_ra_pinfo' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84051-2 + - disable_strategy + - low_complexity + - medium_disruption + - reboot_required + - sysctl_net_ipv6_conf_default_accept_ra_pinfo + - unknown_severity +- name: XCCDF Value sysctl_net_ipv6_conf_default_accept_ra_pinfo_value # promote to variable + set_fact: + sysctl_net_ipv6_conf_default_accept_ra_pinfo_value: !!str + tags: + - always + +- name: Ensure sysctl net.ipv6.conf.default.accept_ra_pinfo is set + sysctl: + name: net.ipv6.conf.default.accept_ra_pinfo + value: '{{ sysctl_net_ipv6_conf_default_accept_ra_pinfo_value }}' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84051-2 + - disable_strategy + - low_complexity + - medium_disruption + - reboot_required + - sysctl_net_ipv6_conf_default_accept_ra_pinfo + - unknown_severity + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.ipv6.conf.default.accept_ra_pinfo from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.ipv6.conf.default.accept_ra_pinfo.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.ipv6.conf.default.accept_ra_pinfo" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done +sysctl_net_ipv6_conf_default_accept_ra_pinfo_value='' + + +# +# Set runtime for net.ipv6.conf.default.accept_ra_pinfo +# +/sbin/sysctl -q -n -w net.ipv6.conf.default.accept_ra_pinfo="$sysctl_net_ipv6_conf_default_accept_ra_pinfo_value" + +# +# If net.ipv6.conf.default.accept_ra_pinfo present in /etc/sysctl.conf, change value to appropriate value +# else, add "net.ipv6.conf.default.accept_ra_pinfo = value" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv6.conf.default.accept_ra_pinfo") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv6_conf_default_accept_ra_pinfo_value" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.ipv6.conf.default.accept_ra_pinfo\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.ipv6.conf.default.accept_ra_pinfo\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-84051-2" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Configure Accepting Router Preference in Router Advertisements on All IPv6 Interfaces By Default + To set the runtime status of the net.ipv6.conf.default.accept_ra_rtr_pref kernel parameter, run the following command: $ sudo sysctl -w net.ipv6.conf.default.accept_ra_rtr_pref=0 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: net.ipv6.conf.default.accept_ra_rtr_pref = 0 + BP28(R22) + An illicit router advertisement message could result in a man-in-the-middle attack. + + CCE-84291-4 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.ipv6.conf.default.accept_ra_rtr_pref.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84291-4 + - disable_strategy + - low_complexity + - medium_disruption + - reboot_required + - sysctl_net_ipv6_conf_default_accept_ra_rtr_pref + - unknown_severity + +- name: Comment out any occurrences of net.ipv6.conf.default.accept_ra_rtr_pref from + /etc/sysctl.d/*.conf files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.ipv6.conf.default.accept_ra_rtr_pref + replace: '#net.ipv6.conf.default.accept_ra_rtr_pref' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84291-4 + - disable_strategy + - low_complexity + - medium_disruption + - reboot_required + - sysctl_net_ipv6_conf_default_accept_ra_rtr_pref + - unknown_severity +- name: XCCDF Value sysctl_net_ipv6_conf_default_accept_ra_rtr_pref_value # promote to variable + set_fact: + sysctl_net_ipv6_conf_default_accept_ra_rtr_pref_value: !!str + tags: + - always + +- name: Ensure sysctl net.ipv6.conf.default.accept_ra_rtr_pref is set + sysctl: + name: net.ipv6.conf.default.accept_ra_rtr_pref + value: '{{ sysctl_net_ipv6_conf_default_accept_ra_rtr_pref_value }}' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84291-4 + - disable_strategy + - low_complexity + - medium_disruption + - reboot_required + - sysctl_net_ipv6_conf_default_accept_ra_rtr_pref + - unknown_severity + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.ipv6.conf.default.accept_ra_rtr_pref from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.ipv6.conf.default.accept_ra_rtr_pref.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.ipv6.conf.default.accept_ra_rtr_pref" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done +sysctl_net_ipv6_conf_default_accept_ra_rtr_pref_value='' + + +# +# Set runtime for net.ipv6.conf.default.accept_ra_rtr_pref +# +/sbin/sysctl -q -n -w net.ipv6.conf.default.accept_ra_rtr_pref="$sysctl_net_ipv6_conf_default_accept_ra_rtr_pref_value" + +# +# If net.ipv6.conf.default.accept_ra_rtr_pref present in /etc/sysctl.conf, change value to appropriate value +# else, add "net.ipv6.conf.default.accept_ra_rtr_pref = value" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv6.conf.default.accept_ra_rtr_pref") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv6_conf_default_accept_ra_rtr_pref_value" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.ipv6.conf.default.accept_ra_rtr_pref\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.ipv6.conf.default.accept_ra_rtr_pref\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-84291-4" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable Kernel Parameter for Accepting ICMP Redirects by Default on IPv6 Interfaces + To set the runtime status of the net.ipv6.conf.default.accept_redirects kernel parameter, run the following command: $ sudo sysctl -w net.ipv6.conf.default.accept_redirects=0 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: net.ipv6.conf.default.accept_redirects = 0 + BP28(R22) + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + 3.1.20 + CCI-000366 + CCI-001551 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + PR.PT-3 + SRG-OS-000480-GPOS-00227 + RHEL-08-040210 + 3.3.2 + SV-230535r833340_rule + An illicit ICMP redirect message could result in a man-in-the-middle attack. + + CCE-81010-1 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.ipv6.conf.default.accept_redirects.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-81010-1 + - DISA-STIG-RHEL-08-040210 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv6_conf_default_accept_redirects + +- name: Comment out any occurrences of net.ipv6.conf.default.accept_redirects from + /etc/sysctl.d/*.conf files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.ipv6.conf.default.accept_redirects + replace: '#net.ipv6.conf.default.accept_redirects' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-81010-1 + - DISA-STIG-RHEL-08-040210 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv6_conf_default_accept_redirects +- name: XCCDF Value sysctl_net_ipv6_conf_default_accept_redirects_value # promote to variable + set_fact: + sysctl_net_ipv6_conf_default_accept_redirects_value: !!str + tags: + - always + +- name: Ensure sysctl net.ipv6.conf.default.accept_redirects is set + sysctl: + name: net.ipv6.conf.default.accept_redirects + value: '{{ sysctl_net_ipv6_conf_default_accept_redirects_value }}' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-81010-1 + - DISA-STIG-RHEL-08-040210 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv6_conf_default_accept_redirects + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,net.ipv6.conf.default.accept_redirects%20%3D%200%0A + mode: 0644 + path: /etc/sysctl.d/75-sysctl_net_ipv6_conf_default_accept_redirects.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.ipv6.conf.default.accept_redirects from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.ipv6.conf.default.accept_redirects.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.ipv6.conf.default.accept_redirects" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done +sysctl_net_ipv6_conf_default_accept_redirects_value='' + + +# +# Set runtime for net.ipv6.conf.default.accept_redirects +# +/sbin/sysctl -q -n -w net.ipv6.conf.default.accept_redirects="$sysctl_net_ipv6_conf_default_accept_redirects_value" + +# +# If net.ipv6.conf.default.accept_redirects present in /etc/sysctl.conf, change value to appropriate value +# else, add "net.ipv6.conf.default.accept_redirects = value" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv6.conf.default.accept_redirects") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv6_conf_default_accept_redirects_value" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.ipv6.conf.default.accept_redirects\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.ipv6.conf.default.accept_redirects\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-81010-1" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable Kernel Parameter for Accepting Source-Routed Packets on IPv6 Interfaces by Default + To set the runtime status of the net.ipv6.conf.default.accept_source_route kernel parameter, run the following command: $ sudo sysctl -w net.ipv6.conf.default.accept_source_route=0 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: net.ipv6.conf.default.accept_source_route = 0 + BP28(R22) + 1 + 12 + 13 + 14 + 15 + 16 + 18 + 4 + 6 + 8 + 9 + APO01.06 + APO13.01 + DSS01.05 + DSS03.01 + DSS05.02 + DSS05.04 + DSS05.07 + DSS06.02 + 3.1.20 + CCI-000366 + 4.2.3.4 + 4.3.3.4 + 4.4.3.3 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.12.1.1 + A.12.1.2 + A.13.1.1 + A.13.1.2 + A.13.1.3 + A.13.2.1 + A.13.2.2 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-7(a) + CM-7(b) + CM-6(a) + CM-6(b) + CM-6.1(iv) + DE.AE-1 + ID.AM-3 + PR.AC-5 + PR.DS-5 + PR.PT-4 + SRG-OS-000480-GPOS-00227 + RHEL-08-040250 + 3.3.1 + SV-230539r838722_rule + Source-routed packets allow the source of the packet to suggest routers +forward the packet along a different path than configured on the router, which can +be used to bypass network security measures. This requirement applies only to the +forwarding of source-routerd traffic, such as when IPv6 forwarding is enabled and +the system is functioning as a router. + +Accepting source-routed packets in the IPv6 protocol has few legitimate +uses. It should be disabled unless it is absolutely required. + + CCE-81015-0 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.ipv6.conf.default.accept_source_route.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-81015-0 + - DISA-STIG-RHEL-08-040250 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-6(b) + - NIST-800-53-CM-6.1(iv) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv6_conf_default_accept_source_route + +- name: Comment out any occurrences of net.ipv6.conf.default.accept_source_route from + /etc/sysctl.d/*.conf files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.ipv6.conf.default.accept_source_route + replace: '#net.ipv6.conf.default.accept_source_route' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-81015-0 + - DISA-STIG-RHEL-08-040250 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-6(b) + - NIST-800-53-CM-6.1(iv) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv6_conf_default_accept_source_route +- name: XCCDF Value sysctl_net_ipv6_conf_default_accept_source_route_value # promote to variable + set_fact: + sysctl_net_ipv6_conf_default_accept_source_route_value: !!str + tags: + - always + +- name: Ensure sysctl net.ipv6.conf.default.accept_source_route is set + sysctl: + name: net.ipv6.conf.default.accept_source_route + value: '{{ sysctl_net_ipv6_conf_default_accept_source_route_value }}' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-81015-0 + - DISA-STIG-RHEL-08-040250 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-6(b) + - NIST-800-53-CM-6.1(iv) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv6_conf_default_accept_source_route + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,net.ipv6.conf.default.accept_source_route%3D0%0A + mode: 0644 + path: /etc/sysctl.d/75-sysctl_net_ipv6_conf_default_accept_source_route.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.ipv6.conf.default.accept_source_route from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.ipv6.conf.default.accept_source_route.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.ipv6.conf.default.accept_source_route" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done +sysctl_net_ipv6_conf_default_accept_source_route_value='' + + +# +# Set runtime for net.ipv6.conf.default.accept_source_route +# +/sbin/sysctl -q -n -w net.ipv6.conf.default.accept_source_route="$sysctl_net_ipv6_conf_default_accept_source_route_value" + +# +# If net.ipv6.conf.default.accept_source_route present in /etc/sysctl.conf, change value to appropriate value +# else, add "net.ipv6.conf.default.accept_source_route = value" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv6.conf.default.accept_source_route") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv6_conf_default_accept_source_route_value" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.ipv6.conf.default.accept_source_route\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.ipv6.conf.default.accept_source_route\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-81015-0" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Configure Auto Configuration on All IPv6 Interfaces By Default + To set the runtime status of the net.ipv6.conf.default.autoconf kernel parameter, run the following command: $ sudo sysctl -w net.ipv6.conf.default.autoconf=0 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: net.ipv6.conf.default.autoconf = 0 + BP28(R22) + An illicit router advertisement message could result in a man-in-the-middle attack. + + CCE-84264-1 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.ipv6.conf.default.autoconf.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84264-1 + - disable_strategy + - low_complexity + - medium_disruption + - reboot_required + - sysctl_net_ipv6_conf_default_autoconf + - unknown_severity + +- name: Comment out any occurrences of net.ipv6.conf.default.autoconf from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.ipv6.conf.default.autoconf + replace: '#net.ipv6.conf.default.autoconf' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84264-1 + - disable_strategy + - low_complexity + - medium_disruption + - reboot_required + - sysctl_net_ipv6_conf_default_autoconf + - unknown_severity +- name: XCCDF Value sysctl_net_ipv6_conf_default_autoconf_value # promote to variable + set_fact: + sysctl_net_ipv6_conf_default_autoconf_value: !!str + tags: + - always + +- name: Ensure sysctl net.ipv6.conf.default.autoconf is set + sysctl: + name: net.ipv6.conf.default.autoconf + value: '{{ sysctl_net_ipv6_conf_default_autoconf_value }}' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84264-1 + - disable_strategy + - low_complexity + - medium_disruption + - reboot_required + - sysctl_net_ipv6_conf_default_autoconf + - unknown_severity + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.ipv6.conf.default.autoconf from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.ipv6.conf.default.autoconf.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.ipv6.conf.default.autoconf" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done +sysctl_net_ipv6_conf_default_autoconf_value='' + + +# +# Set runtime for net.ipv6.conf.default.autoconf +# +/sbin/sysctl -q -n -w net.ipv6.conf.default.autoconf="$sysctl_net_ipv6_conf_default_autoconf_value" + +# +# If net.ipv6.conf.default.autoconf present in /etc/sysctl.conf, change value to appropriate value +# else, add "net.ipv6.conf.default.autoconf = value" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv6.conf.default.autoconf") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv6_conf_default_autoconf_value" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.ipv6.conf.default.autoconf\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.ipv6.conf.default.autoconf\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-84264-1" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Configure Maximum Number of Autoconfigured Addresses on All IPv6 Interfaces By Default + To set the runtime status of the net.ipv6.conf.default.max_addresses kernel parameter, run the following command: $ sudo sysctl -w net.ipv6.conf.default.max_addresses=1 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: net.ipv6.conf.default.max_addresses = 1 + BP28(R22) + The number of global unicast IPv6 addresses for each interface should be limited exactly to the number of statically configured addresses. + + CCE-84257-5 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.ipv6.conf.default.max_addresses.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84257-5 + - disable_strategy + - low_complexity + - medium_disruption + - reboot_required + - sysctl_net_ipv6_conf_default_max_addresses + - unknown_severity + +- name: Comment out any occurrences of net.ipv6.conf.default.max_addresses from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.ipv6.conf.default.max_addresses + replace: '#net.ipv6.conf.default.max_addresses' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84257-5 + - disable_strategy + - low_complexity + - medium_disruption + - reboot_required + - sysctl_net_ipv6_conf_default_max_addresses + - unknown_severity +- name: XCCDF Value sysctl_net_ipv6_conf_default_max_addresses_value # promote to variable + set_fact: + sysctl_net_ipv6_conf_default_max_addresses_value: !!str + tags: + - always + +- name: Ensure sysctl net.ipv6.conf.default.max_addresses is set + sysctl: + name: net.ipv6.conf.default.max_addresses + value: '{{ sysctl_net_ipv6_conf_default_max_addresses_value }}' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84257-5 + - disable_strategy + - low_complexity + - medium_disruption + - reboot_required + - sysctl_net_ipv6_conf_default_max_addresses + - unknown_severity + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.ipv6.conf.default.max_addresses from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.ipv6.conf.default.max_addresses.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.ipv6.conf.default.max_addresses" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done +sysctl_net_ipv6_conf_default_max_addresses_value='' + + +# +# Set runtime for net.ipv6.conf.default.max_addresses +# +/sbin/sysctl -q -n -w net.ipv6.conf.default.max_addresses="$sysctl_net_ipv6_conf_default_max_addresses_value" + +# +# If net.ipv6.conf.default.max_addresses present in /etc/sysctl.conf, change value to appropriate value +# else, add "net.ipv6.conf.default.max_addresses = value" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv6.conf.default.max_addresses") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv6_conf_default_max_addresses_value" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.ipv6.conf.default.max_addresses\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.ipv6.conf.default.max_addresses\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-84257-5" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Configure Denying Router Solicitations on All IPv6 Interfaces By Default + To set the runtime status of the net.ipv6.conf.default.router_solicitations kernel parameter, run the following command: $ sudo sysctl -w net.ipv6.conf.default.router_solicitations=0 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: net.ipv6.conf.default.router_solicitations = 0 + BP28(R22) + To prevent discovery of the system by other systems, router solicitation requests should be denied. + + CCE-83477-0 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.ipv6.conf.default.router_solicitations.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83477-0 + - disable_strategy + - low_complexity + - medium_disruption + - reboot_required + - sysctl_net_ipv6_conf_default_router_solicitations + - unknown_severity + +- name: Comment out any occurrences of net.ipv6.conf.default.router_solicitations + from /etc/sysctl.d/*.conf files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.ipv6.conf.default.router_solicitations + replace: '#net.ipv6.conf.default.router_solicitations' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83477-0 + - disable_strategy + - low_complexity + - medium_disruption + - reboot_required + - sysctl_net_ipv6_conf_default_router_solicitations + - unknown_severity +- name: XCCDF Value sysctl_net_ipv6_conf_default_router_solicitations_value # promote to variable + set_fact: + sysctl_net_ipv6_conf_default_router_solicitations_value: !!str + tags: + - always + +- name: Ensure sysctl net.ipv6.conf.default.router_solicitations is set + sysctl: + name: net.ipv6.conf.default.router_solicitations + value: '{{ sysctl_net_ipv6_conf_default_router_solicitations_value }}' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83477-0 + - disable_strategy + - low_complexity + - medium_disruption + - reboot_required + - sysctl_net_ipv6_conf_default_router_solicitations + - unknown_severity + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.ipv6.conf.default.router_solicitations from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.ipv6.conf.default.router_solicitations.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.ipv6.conf.default.router_solicitations" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done +sysctl_net_ipv6_conf_default_router_solicitations_value='' + + +# +# Set runtime for net.ipv6.conf.default.router_solicitations +# +/sbin/sysctl -q -n -w net.ipv6.conf.default.router_solicitations="$sysctl_net_ipv6_conf_default_router_solicitations_value" + +# +# If net.ipv6.conf.default.router_solicitations present in /etc/sysctl.conf, change value to appropriate value +# else, add "net.ipv6.conf.default.router_solicitations = value" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv6.conf.default.router_solicitations") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv6_conf_default_router_solicitations_value" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.ipv6.conf.default.router_solicitations\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.ipv6.conf.default.router_solicitations\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-83477-0" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Limit Network-Transmitted Configuration if Using Static IPv6 Addresses + To limit the configuration information requested from other +systems and accepted from the network on a system that uses +statically-configured IPv6 addresses, add the following lines to +/etc/sysctl.conf: +net.ipv6.conf.default.router_solicitations = 0 +net.ipv6.conf.default.accept_ra_rtr_pref = 0 +net.ipv6.conf.default.accept_ra_pinfo = 0 +net.ipv6.conf.default.accept_ra_defrtr = 0 +net.ipv6.conf.default.autoconf = 0 +net.ipv6.conf.default.dad_transmits = 0 +net.ipv6.conf.default.max_addresses = 1 +The router_solicitations setting determines how many router +solicitations are sent when bringing up the interface. If addresses are +statically assigned, there is no need to send any solicitations. + +The accept_ra_pinfo setting controls whether the system will accept +prefix info from the router. + +The accept_ra_defrtr setting controls whether the system will accept +Hop Limit settings from a router advertisement. Setting it to 0 prevents a +router from changing your default IPv6 Hop Limit for outgoing packets. + +The autoconf setting controls whether router advertisements can cause +the system to assign a global unicast address to an interface. + +The dad_transmits setting determines how many neighbor solicitations +to send out per address (global and link-local) when bringing up an interface +to ensure the desired address is unique on the network. + +The max_addresses setting determines how many global unicast IPv6 +addresses can be assigned to each interface. The default is 16, but it should +be set to exactly the number of statically configured global addresses +required. + + + + + Kernel Parameters Which Affect Networking + The sysctl utility is used to set +parameters which affect the operation of the Linux kernel. Kernel parameters +which affect networking and have security implications are described here. + + Network Related Kernel Runtime Parameters for Hosts and Routers + Certain kernel parameters should be set for systems which are +acting as either hosts or routers to improve the system's ability defend +against certain types of IPv4 protocol attacks. + + + net.ipv4.conf.all.accept_redirects + Disable ICMP Redirect Acceptance + 0 + 0 + 1 + + + net.ipv4.conf.all.accept_source_route + Trackers could be using source-routed packets to +generate traffic that seems to be intra-net, but actually was +created outside and has been redirected. + 0 + 0 + 1 + + + net.ipv4.conf.default.arp_filter + Controls whether the ARP filter is enabled or not. + +1 - Allows you to have multiple network interfaces on the same subnet, and have the ARPs for each +interface be answered based on whether or not the kernel would route a packet from the ARP’d IP out that interface. +In other words it allows control of which cards (usually 1) will respond to an ARP request. + +0 - (default) The kernel can respond to arp requests with addresses from other interfaces. +This may seem wrong but it usually makes sense, because it increases the chance of successful communication. +IP addresses are owned by the complete host on Linux, not by particular interfaces. + 0 + 0 + 1 + + + net.ipv4.conf.default.arp_ignore + Control the response modes for ARP queries that resolve local target IP addresses: + +0 - (default): reply for any local target IP address, configured on any interface +1 - reply only if the target IP address is local address configured on the incoming interface +2 - reply only if the target IP address is local address configured on the incoming interface and both with the sender’s IP address are part from same subnet on this interface +3 - do not reply for local addresses configured with scope host, only resolutions for global and link addresses are replied +4-7 - reserved +8 - do not reply for all local addresses + 0 + 0 + 1 + 2 + 3 + 8 + + + net.ipv4.conf.all.forwarding + Toggle IPv4 Forwarding + 0 + 0 + 1 + + + net.ipv4.conf.all.log_martians + Disable so you don't Log Spoofed Packets, Source +Routed Packets, Redirect Packets + 1 + 0 + 1 + + + net.ipv4.conf.all.rp_filter + Enable to enforce sanity checking, also called ingress +filtering or egress filtering. The point is to drop a packet if the +source and destination IP addresses in the IP header do not make +sense when considered in light of the physical interface on which +it arrived. + 1 + 1 + 2 + + + net.ipv4.conf.all.secure_redirects + Enable to prevent hijacking of routing path by only +allowing redirects from gateways known in routing +table. Disable to refuse acceptance of secure ICMP redirected packets on all interfaces. + 0 + 0 + 1 + + + net.ipv4.conf.all.shared_media + Controls whether the system can send (router) or accept (host) RFC1620 shared media redirects. +shared_media for the interface will be enabled if at least one of conf/{all,interface}/shared_media +is set to TRUE, it will be disabled otherwise. + 0 + 0 + 1 + + + net.ipv4.conf.default.accept_redirects + Disable ICMP Redirect Acceptance? + 0 + 0 + 1 + + + net.ipv4.conf.default.accept_source_route + Disable IP source routing? + 0 + 0 + 1 + + + net.ipv4.conf.default.log_martians + Disable so you don't Log Spoofed Packets, Source +Routed Packets, Redirect Packets + 1 + 0 + 1 + + + net.ipv4.conf.default.rp_filter + Enables source route verification + 1 + 0 + 1 + + + net.ipv4.conf.default.secure_redirects + Enable to prevent hijacking of routing path by only +allowing redirects from gateways known in routing +table. Disable to refuse acceptance of secure ICMP redirected packages by default. + 0 + 0 + 1 + + + net.ipv4.conf.default.shared_media + Controls whether the system can send(router) or accept(host) RFC1620 shared media redirects. +shared_media for the interface will be enabled if at least one of conf/{all,interface}/shared_media +is set to TRUE, it will be disabled otherwise. + 0 + 0 + 1 + + + net.ipv4.icmp_echo_ignore_broadcasts + Ignore all ICMP ECHO and TIMESTAMP requests sent to it +via broadcast/multicast + 1 + 0 + 1 + + + net.ipv4.icmp_ignore_bogus_error_responses + Enable to prevent unnecessary logging + 1 + 0 + 1 + + + net.ipv4.tcp_invalid_ratelimit + Configure the maximal rate for sending duplicate acknowledgments in +response to incoming invalid TCP packets. + 500 + 1000 + 500 + 250 + 100 + + + net.ipv4.tcp_rfc1337 + Enable to enable TCP behavior conformant with RFC 1337 + 1 + 0 + 1 + + + net.ipv4.tcp_syncookies + Enable to turn on TCP SYN Cookie +Protection + 1 + 0 + 1 + + + Disable Accepting Packets Routed Between Local Interfaces + To set the runtime status of the net.ipv4.conf.all.accept_local kernel parameter, run the following command: $ sudo sysctl -w net.ipv4.conf.all.accept_local=0 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: net.ipv4.conf.all.accept_local = 0 + Configure net.ipv4.conf.all.accept_local=0 to consider as invalid the packets +received from outside whose source is the 127.0.0.0/8 address block. +In combination with suitable routing, this can be used to direct packets between two +local interfaces over the wire and have them accepted properly. + CCE-88789-3 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.ipv4.conf.all.accept_local.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-88789-3 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_all_accept_local + +- name: Comment out any occurrences of net.ipv4.conf.all.accept_local from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.ipv4.conf.all.accept_local + replace: '#net.ipv4.conf.all.accept_local' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-88789-3 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_all_accept_local + +- name: Ensure sysctl net.ipv4.conf.all.accept_local is set to 0 + sysctl: + name: net.ipv4.conf.all.accept_local + value: '0' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-88789-3 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_all_accept_local + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.ipv4.conf.all.accept_local from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.ipv4.conf.all.accept_local.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.ipv4.conf.all.accept_local" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done + +# +# Set runtime for net.ipv4.conf.all.accept_local +# +/sbin/sysctl -q -n -w net.ipv4.conf.all.accept_local="0" + +# +# If net.ipv4.conf.all.accept_local present in /etc/sysctl.conf, change value to "0" +# else, add "net.ipv4.conf.all.accept_local = 0" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv4.conf.all.accept_local") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "0" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.ipv4.conf.all.accept_local\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.ipv4.conf.all.accept_local\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-88789-3" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable Accepting ICMP Redirects for All IPv4 Interfaces + To set the runtime status of the net.ipv4.conf.all.accept_redirects kernel parameter, run the following command: $ sudo sysctl -w net.ipv4.conf.all.accept_redirects=0 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: net.ipv4.conf.all.accept_redirects = 0 + BP28(R22) + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 2 + 3 + 7 + 8 + 9 + 5.10.1.1 + APO13.01 + BAI04.04 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.03 + DSS03.05 + DSS05.02 + DSS05.05 + DSS05.07 + DSS06.06 + 3.1.20 + CCI-000366 + CCI-001503 + CCI-001551 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 6.2 + SR 7.1 + SR 7.2 + SR 7.6 + A.12.1.2 + A.12.1.3 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.17.2.1 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + SC-7(a) + DE.CM-1 + PR.DS-4 + PR.IP-1 + PR.PT-3 + SRG-OS-000480-GPOS-00227 + RHEL-08-040279 + 3.3.2 + SV-244553r833379_rule + ICMP redirect messages are used by routers to inform hosts that a more +direct route exists for a particular destination. These messages modify the +host's route table and are unauthenticated. An illicit ICMP redirect +message could result in a man-in-the-middle attack. + +This feature of the IPv4 protocol has few legitimate uses. It should be +disabled unless absolutely required." + CCE-80917-8 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.ipv4.conf.all.accept_redirects.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80917-8 + - CJIS-5.10.1.1 + - DISA-STIG-RHEL-08-040279 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-7(a) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_all_accept_redirects + +- name: Comment out any occurrences of net.ipv4.conf.all.accept_redirects from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.ipv4.conf.all.accept_redirects + replace: '#net.ipv4.conf.all.accept_redirects' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80917-8 + - CJIS-5.10.1.1 + - DISA-STIG-RHEL-08-040279 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-7(a) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_all_accept_redirects +- name: XCCDF Value sysctl_net_ipv4_conf_all_accept_redirects_value # promote to variable + set_fact: + sysctl_net_ipv4_conf_all_accept_redirects_value: !!str + tags: + - always + +- name: Ensure sysctl net.ipv4.conf.all.accept_redirects is set + sysctl: + name: net.ipv4.conf.all.accept_redirects + value: '{{ sysctl_net_ipv4_conf_all_accept_redirects_value }}' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80917-8 + - CJIS-5.10.1.1 + - DISA-STIG-RHEL-08-040279 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-7(a) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_all_accept_redirects + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,net.ipv4.conf.all.accept_redirects%3D0%0A + mode: 0644 + path: /etc/sysctl.d/75-sysctl_net_ipv4_conf_all_accept_redirects.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.ipv4.conf.all.accept_redirects from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.ipv4.conf.all.accept_redirects.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.ipv4.conf.all.accept_redirects" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done +sysctl_net_ipv4_conf_all_accept_redirects_value='' + + +# +# Set runtime for net.ipv4.conf.all.accept_redirects +# +/sbin/sysctl -q -n -w net.ipv4.conf.all.accept_redirects="$sysctl_net_ipv4_conf_all_accept_redirects_value" + +# +# If net.ipv4.conf.all.accept_redirects present in /etc/sysctl.conf, change value to appropriate value +# else, add "net.ipv4.conf.all.accept_redirects = value" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv4.conf.all.accept_redirects") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv4_conf_all_accept_redirects_value" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.ipv4.conf.all.accept_redirects\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.ipv4.conf.all.accept_redirects\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-80917-8" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable Kernel Parameter for Accepting Source-Routed Packets on all IPv4 Interfaces + To set the runtime status of the net.ipv4.conf.all.accept_source_route kernel parameter, run the following command: $ sudo sysctl -w net.ipv4.conf.all.accept_source_route=0 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: net.ipv4.conf.all.accept_source_route = 0 + BP28(R22) + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 18 + 2 + 3 + 4 + 6 + 7 + 8 + 9 + APO01.06 + APO13.01 + BAI04.04 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.03 + DSS01.05 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + DSS06.02 + DSS06.06 + 3.1.20 + CCI-000366 + 4.2.3.4 + 4.3.3.4 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + 4.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.2 + SR 7.1 + SR 7.2 + SR 7.6 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.12.1.1 + A.12.1.2 + A.12.1.3 + A.12.5.1 + A.12.6.2 + A.13.1.1 + A.13.1.2 + A.13.1.3 + A.13.2.1 + A.13.2.2 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.17.2.1 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-007-3 R4 + CIP-007-3 R4.1 + CIP-007-3 R4.2 + CIP-007-3 R5.1 + CM-7(a) + CM-7(b) + SC-5 + CM-6(a) + SC-7(a) + DE.AE-1 + DE.CM-1 + ID.AM-3 + PR.AC-5 + PR.DS-4 + PR.DS-5 + PR.IP-1 + PR.PT-3 + PR.PT-4 + SRG-OS-000480-GPOS-00227 + RHEL-08-040239 + 3.3.1 + SV-244551r833375_rule + Source-routed packets allow the source of the packet to suggest routers +forward the packet along a different path than configured on the router, +which can be used to bypass network security measures. This requirement +applies only to the forwarding of source-routerd traffic, such as when IPv4 +forwarding is enabled and the system is functioning as a router. + +Accepting source-routed packets in the IPv4 protocol has few legitimate +uses. It should be disabled unless it is absolutely required. + CCE-81011-9 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.ipv4.conf.all.accept_source_route.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-81011-9 + - DISA-STIG-RHEL-08-040239 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-5 + - NIST-800-53-SC-7(a) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_all_accept_source_route + +- name: Comment out any occurrences of net.ipv4.conf.all.accept_source_route from + /etc/sysctl.d/*.conf files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.ipv4.conf.all.accept_source_route + replace: '#net.ipv4.conf.all.accept_source_route' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-81011-9 + - DISA-STIG-RHEL-08-040239 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-5 + - NIST-800-53-SC-7(a) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_all_accept_source_route +- name: XCCDF Value sysctl_net_ipv4_conf_all_accept_source_route_value # promote to variable + set_fact: + sysctl_net_ipv4_conf_all_accept_source_route_value: !!str + tags: + - always + +- name: Ensure sysctl net.ipv4.conf.all.accept_source_route is set + sysctl: + name: net.ipv4.conf.all.accept_source_route + value: '{{ sysctl_net_ipv4_conf_all_accept_source_route_value }}' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-81011-9 + - DISA-STIG-RHEL-08-040239 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-5 + - NIST-800-53-SC-7(a) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_all_accept_source_route + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,net.ipv4.conf.all.accept_source_route%3D0%0A + mode: 0644 + path: /etc/sysctl.d/75-sysctl_net_ipv4_conf_all_accept_source_route.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.ipv4.conf.all.accept_source_route from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.ipv4.conf.all.accept_source_route.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.ipv4.conf.all.accept_source_route" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done +sysctl_net_ipv4_conf_all_accept_source_route_value='' + + +# +# Set runtime for net.ipv4.conf.all.accept_source_route +# +/sbin/sysctl -q -n -w net.ipv4.conf.all.accept_source_route="$sysctl_net_ipv4_conf_all_accept_source_route_value" + +# +# If net.ipv4.conf.all.accept_source_route present in /etc/sysctl.conf, change value to appropriate value +# else, add "net.ipv4.conf.all.accept_source_route = value" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv4.conf.all.accept_source_route") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv4_conf_all_accept_source_route_value" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.ipv4.conf.all.accept_source_route\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.ipv4.conf.all.accept_source_route\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-81011-9" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Configure ARP filtering for All IPv4 Interfaces + To set the runtime status of the net.ipv4.conf.all.arp_filter kernel parameter, run the following command: $ sudo sysctl -w net.ipv4.conf.all.arp_filter= +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: net.ipv4.conf.all.arp_filter = + This behaviour may cause problems to system on a high availability or load balancing configuration. + Prevents the Linux Kernel from handling the ARP table globally. +By default, the kernel may respond to an ARP request from a certain interface with information +from another interface. + CCE-88555-8 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.ipv4.conf.all.arp_filter.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-88555-8 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_all_arp_filter + +- name: Comment out any occurrences of net.ipv4.conf.all.arp_filter from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.ipv4.conf.all.arp_filter + replace: '#net.ipv4.conf.all.arp_filter' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-88555-8 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_all_arp_filter +- name: XCCDF Value sysctl_net_ipv4_conf_all_arp_filter_value # promote to variable + set_fact: + sysctl_net_ipv4_conf_all_arp_filter_value: !!str + tags: + - always + +- name: Ensure sysctl net.ipv4.conf.all.arp_filter is set + sysctl: + name: net.ipv4.conf.all.arp_filter + value: '{{ sysctl_net_ipv4_conf_all_arp_filter_value }}' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-88555-8 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_all_arp_filter + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.ipv4.conf.all.arp_filter from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.ipv4.conf.all.arp_filter.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.ipv4.conf.all.arp_filter" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done +sysctl_net_ipv4_conf_all_arp_filter_value='' + + +# +# Set runtime for net.ipv4.conf.all.arp_filter +# +/sbin/sysctl -q -n -w net.ipv4.conf.all.arp_filter="$sysctl_net_ipv4_conf_all_arp_filter_value" + +# +# If net.ipv4.conf.all.arp_filter present in /etc/sysctl.conf, change value to appropriate value +# else, add "net.ipv4.conf.all.arp_filter = value" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv4.conf.all.arp_filter") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv4_conf_all_arp_filter_value" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.ipv4.conf.all.arp_filter\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.ipv4.conf.all.arp_filter\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-88555-8" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Configure Response Mode of ARP Requests for All IPv4 Interfaces + To set the runtime status of the net.ipv4.conf.all.arp_ignore kernel parameter, run the following command: $ sudo sysctl -w net.ipv4.conf.all.arp_ignore= +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: net.ipv4.conf.all.arp_ignore = + The ARP response mode may impact behaviour of workloads and firewalls on the system. + Avoids ARP Flux on system that have more than one interface on the same subnet. + CCE-88889-1 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.ipv4.conf.all.arp_ignore.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-88889-1 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_all_arp_ignore + +- name: Comment out any occurrences of net.ipv4.conf.all.arp_ignore from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.ipv4.conf.all.arp_ignore + replace: '#net.ipv4.conf.all.arp_ignore' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-88889-1 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_all_arp_ignore +- name: XCCDF Value sysctl_net_ipv4_conf_all_arp_ignore_value # promote to variable + set_fact: + sysctl_net_ipv4_conf_all_arp_ignore_value: !!str + tags: + - always + +- name: Ensure sysctl net.ipv4.conf.all.arp_ignore is set + sysctl: + name: net.ipv4.conf.all.arp_ignore + value: '{{ sysctl_net_ipv4_conf_all_arp_ignore_value }}' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-88889-1 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_all_arp_ignore + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.ipv4.conf.all.arp_ignore from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.ipv4.conf.all.arp_ignore.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.ipv4.conf.all.arp_ignore" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done +sysctl_net_ipv4_conf_all_arp_ignore_value='' + + +# +# Set runtime for net.ipv4.conf.all.arp_ignore +# +/sbin/sysctl -q -n -w net.ipv4.conf.all.arp_ignore="$sysctl_net_ipv4_conf_all_arp_ignore_value" + +# +# If net.ipv4.conf.all.arp_ignore present in /etc/sysctl.conf, change value to appropriate value +# else, add "net.ipv4.conf.all.arp_ignore = value" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv4.conf.all.arp_ignore") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv4_conf_all_arp_ignore_value" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.ipv4.conf.all.arp_ignore\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.ipv4.conf.all.arp_ignore\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-88889-1" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Drop Gratuitious ARP frames on All IPv4 Interfaces + To set the runtime status of the net.ipv4.conf.all.drop_gratuitous_arp kernel parameter, run the following command: $ sudo sysctl -w net.ipv4.conf.all.drop_gratuitous_arp=1 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: net.ipv4.conf.all.drop_gratuitous_arp = 1 + This can cause problems if ARP proxies are used in the network. + Drop Gratuitous ARP frames to prevent ARP poisoning. + CCE-88001-3 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.ipv4.conf.all.drop_gratuitous_arp.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-88001-3 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_all_drop_gratuitous_arp + +- name: Comment out any occurrences of net.ipv4.conf.all.drop_gratuitous_arp from + /etc/sysctl.d/*.conf files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.ipv4.conf.all.drop_gratuitous_arp + replace: '#net.ipv4.conf.all.drop_gratuitous_arp' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-88001-3 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_all_drop_gratuitous_arp + +- name: Ensure sysctl net.ipv4.conf.all.drop_gratuitous_arp is set to 1 + sysctl: + name: net.ipv4.conf.all.drop_gratuitous_arp + value: '1' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-88001-3 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_all_drop_gratuitous_arp + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.ipv4.conf.all.drop_gratuitous_arp from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.ipv4.conf.all.drop_gratuitous_arp.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.ipv4.conf.all.drop_gratuitous_arp" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done + +# +# Set runtime for net.ipv4.conf.all.drop_gratuitous_arp +# +/sbin/sysctl -q -n -w net.ipv4.conf.all.drop_gratuitous_arp="1" + +# +# If net.ipv4.conf.all.drop_gratuitous_arp present in /etc/sysctl.conf, change value to "1" +# else, add "net.ipv4.conf.all.drop_gratuitous_arp = 1" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv4.conf.all.drop_gratuitous_arp") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "1" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.ipv4.conf.all.drop_gratuitous_arp\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.ipv4.conf.all.drop_gratuitous_arp\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-88001-3" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable Kernel Parameter for IPv4 Forwarding on all IPv4 Interfaces + To set the runtime status of the net.ipv4.conf.all.forwarding kernel parameter, run the following command: $ sudo sysctl -w net.ipv4.conf.all.forwarding=0 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: net.ipv4.conf.all.forwarding = 0 + There might be cases when certain applications can systematically override this option. +One such case is Libvirt; a toolkit for managing of virtualization platforms. +By default, Libvirt requires IP forwarding to be enabled to facilitate +network communication between the virtualization host and guest +machines. It enables IP forwarding after every reboot. + CCI-000366 + CM-6(b) + SRG-OS-000480-GPOS-00227 + RHEL-08-040259 + SV-250317r833383_rule + IP forwarding permits the kernel to forward packets from one network +interface to another. The ability to forward packets between two networks is +only appropriate for systems acting as routers. + + CCE-86220-1 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.ipv4.conf.all.forwarding.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86220-1 + - DISA-STIG-RHEL-08-040259 + - NIST-800-53-CM-6(b) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_all_forwarding + +- name: Comment out any occurrences of net.ipv4.conf.all.forwarding from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.ipv4.conf.all.forwarding + replace: '#net.ipv4.conf.all.forwarding' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86220-1 + - DISA-STIG-RHEL-08-040259 + - NIST-800-53-CM-6(b) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_all_forwarding +- name: XCCDF Value sysctl_net_ipv4_conf_all_forwarding_value # promote to variable + set_fact: + sysctl_net_ipv4_conf_all_forwarding_value: !!str + tags: + - always + +- name: Ensure sysctl net.ipv4.conf.all.forwarding is set + sysctl: + name: net.ipv4.conf.all.forwarding + value: '{{ sysctl_net_ipv4_conf_all_forwarding_value }}' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86220-1 + - DISA-STIG-RHEL-08-040259 + - NIST-800-53-CM-6(b) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_all_forwarding + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.ipv4.conf.all.forwarding from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.ipv4.conf.all.forwarding.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.ipv4.conf.all.forwarding" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done +sysctl_net_ipv4_conf_all_forwarding_value='' + + +# +# Set runtime for net.ipv4.conf.all.forwarding +# +/sbin/sysctl -q -n -w net.ipv4.conf.all.forwarding="$sysctl_net_ipv4_conf_all_forwarding_value" + +# +# If net.ipv4.conf.all.forwarding present in /etc/sysctl.conf, change value to appropriate value +# else, add "net.ipv4.conf.all.forwarding = value" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv4.conf.all.forwarding") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv4_conf_all_forwarding_value" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.ipv4.conf.all.forwarding\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.ipv4.conf.all.forwarding\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-86220-1" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable Kernel Parameter to Log Martian Packets on all IPv4 Interfaces + To set the runtime status of the net.ipv4.conf.all.log_martians kernel parameter, run the following command: $ sudo sysctl -w net.ipv4.conf.all.log_martians=1 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: net.ipv4.conf.all.log_martians = 1 + BP28(R22) + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 2 + 3 + 7 + 8 + 9 + APO13.01 + BAI04.04 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.03 + DSS01.04 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.05 + DSS05.07 + DSS06.06 + 3.1.20 + CCI-000126 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.2 + SR 7.1 + SR 7.2 + SR 7.6 + A.11.2.6 + A.12.1.2 + A.12.1.3 + A.12.5.1 + A.12.6.2 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.17.2.1 + A.6.2.1 + A.6.2.2 + A.9.1.2 + CM-7(a) + CM-7(b) + SC-5(3)(a) + DE.CM-1 + PR.AC-3 + PR.DS-4 + PR.IP-1 + PR.PT-3 + PR.PT-4 + SRG-OS-000480-GPOS-00227 + 3.3.4 + The presence of "martian" packets (which have impossible addresses) +as well as spoofed packets, source-routed packets, and redirects could be a +sign of nefarious network activity. Logging these packets enables this activity +to be detected. + CCE-81018-4 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.ipv4.conf.all.log_martians.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-81018-4 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-5(3)(a) + - disable_strategy + - low_complexity + - medium_disruption + - reboot_required + - sysctl_net_ipv4_conf_all_log_martians + - unknown_severity + +- name: Comment out any occurrences of net.ipv4.conf.all.log_martians from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.ipv4.conf.all.log_martians + replace: '#net.ipv4.conf.all.log_martians' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-81018-4 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-5(3)(a) + - disable_strategy + - low_complexity + - medium_disruption + - reboot_required + - sysctl_net_ipv4_conf_all_log_martians + - unknown_severity +- name: XCCDF Value sysctl_net_ipv4_conf_all_log_martians_value # promote to variable + set_fact: + sysctl_net_ipv4_conf_all_log_martians_value: !!str + tags: + - always + +- name: Ensure sysctl net.ipv4.conf.all.log_martians is set + sysctl: + name: net.ipv4.conf.all.log_martians + value: '{{ sysctl_net_ipv4_conf_all_log_martians_value }}' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-81018-4 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-5(3)(a) + - disable_strategy + - low_complexity + - medium_disruption + - reboot_required + - sysctl_net_ipv4_conf_all_log_martians + - unknown_severity + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,net.ipv4.conf.all.log_martians%3D1%0A + mode: 0644 + path: /etc/sysctl.d/75-sysctl_net_ipv4_conf_all_log_martians.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.ipv4.conf.all.log_martians from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.ipv4.conf.all.log_martians.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.ipv4.conf.all.log_martians" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done +sysctl_net_ipv4_conf_all_log_martians_value='' + + +# +# Set runtime for net.ipv4.conf.all.log_martians +# +/sbin/sysctl -q -n -w net.ipv4.conf.all.log_martians="$sysctl_net_ipv4_conf_all_log_martians_value" + +# +# If net.ipv4.conf.all.log_martians present in /etc/sysctl.conf, change value to appropriate value +# else, add "net.ipv4.conf.all.log_martians = value" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv4.conf.all.log_martians") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv4_conf_all_log_martians_value" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.ipv4.conf.all.log_martians\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.ipv4.conf.all.log_martians\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-81018-4" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Prevent Routing External Traffic to Local Loopback on All IPv4 Interfaces + To set the runtime status of the net.ipv4.conf.all.route_localnet kernel parameter, run the following command: $ sudo sysctl -w net.ipv4.conf.all.route_localnet=0 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: net.ipv4.conf.all.route_localnet = 0 + Refuse the routing of packets whose source or destination address is the local loopback. +This prohibits the use of network 127/8 for local routing purposes. +Enabling route_localnet can expose applications listening on localhost to external traffic. + CCE-88023-7 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.ipv4.conf.all.route_localnet.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-88023-7 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_all_route_localnet + +- name: Comment out any occurrences of net.ipv4.conf.all.route_localnet from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.ipv4.conf.all.route_localnet + replace: '#net.ipv4.conf.all.route_localnet' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-88023-7 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_all_route_localnet + +- name: Ensure sysctl net.ipv4.conf.all.route_localnet is set to 0 + sysctl: + name: net.ipv4.conf.all.route_localnet + value: '0' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-88023-7 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_all_route_localnet + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.ipv4.conf.all.route_localnet from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.ipv4.conf.all.route_localnet.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.ipv4.conf.all.route_localnet" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done + +# +# Set runtime for net.ipv4.conf.all.route_localnet +# +/sbin/sysctl -q -n -w net.ipv4.conf.all.route_localnet="0" + +# +# If net.ipv4.conf.all.route_localnet present in /etc/sysctl.conf, change value to "0" +# else, add "net.ipv4.conf.all.route_localnet = 0" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv4.conf.all.route_localnet") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "0" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.ipv4.conf.all.route_localnet\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.ipv4.conf.all.route_localnet\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-88023-7" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Enable Kernel Parameter to Use Reverse Path Filtering on all IPv4 Interfaces + To set the runtime status of the net.ipv4.conf.all.rp_filter kernel parameter, run the following command: $ sudo sysctl -w net.ipv4.conf.all.rp_filter=1 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: net.ipv4.conf.all.rp_filter = 1 + BP28(R22) + 1 + 12 + 13 + 14 + 15 + 16 + 18 + 2 + 4 + 6 + 7 + 8 + 9 + APO01.06 + APO13.01 + BAI04.04 + DSS01.03 + DSS01.05 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.04 + DSS05.07 + DSS06.02 + 3.1.20 + CCI-000366 + CCI-001551 + 4.2.3.4 + 4.3.3.4 + 4.4.3.3 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.2 + SR 7.1 + SR 7.2 + SR 7.6 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.12.1.1 + A.12.1.2 + A.12.1.3 + A.13.1.1 + A.13.1.2 + A.13.1.3 + A.13.2.1 + A.13.2.2 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.17.2.1 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-7(a) + CM-7(b) + CM-6(a) + SC-7(a) + DE.AE-1 + DE.CM-1 + ID.AM-3 + PR.AC-5 + PR.DS-4 + PR.DS-5 + PR.PT-4 + SRG-OS-000480-GPOS-00227 + RHEL-08-040285 + 3.3.7 + SV-230549r833367_rule + Enabling reverse path filtering drops packets with source addresses +that should not have been able to be received on the interface they were +received on. It should not be used on systems which are routers for +complicated networks, but is helpful for end hosts and routers serving small +networks. + CCE-81021-8 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.ipv4.conf.all.rp_filter.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-81021-8 + - DISA-STIG-RHEL-08-040285 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-7(a) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_all_rp_filter + +- name: Comment out any occurrences of net.ipv4.conf.all.rp_filter from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.ipv4.conf.all.rp_filter + replace: '#net.ipv4.conf.all.rp_filter' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-81021-8 + - DISA-STIG-RHEL-08-040285 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-7(a) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_all_rp_filter +- name: XCCDF Value sysctl_net_ipv4_conf_all_rp_filter_value # promote to variable + set_fact: + sysctl_net_ipv4_conf_all_rp_filter_value: !!str + tags: + - always + +- name: Ensure sysctl net.ipv4.conf.all.rp_filter is set + sysctl: + name: net.ipv4.conf.all.rp_filter + value: '{{ sysctl_net_ipv4_conf_all_rp_filter_value }}' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-81021-8 + - DISA-STIG-RHEL-08-040285 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-7(a) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_all_rp_filter + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,net.ipv4.conf.all.rp_filter%3D1%0A + mode: 0644 + path: /etc/sysctl.d/75-sysctl_net_ipv4_conf_all_rp_filter.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.ipv4.conf.all.rp_filter from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.ipv4.conf.all.rp_filter.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.ipv4.conf.all.rp_filter" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done +sysctl_net_ipv4_conf_all_rp_filter_value='' + + +# +# Set runtime for net.ipv4.conf.all.rp_filter +# +/sbin/sysctl -q -n -w net.ipv4.conf.all.rp_filter="$sysctl_net_ipv4_conf_all_rp_filter_value" + +# +# If net.ipv4.conf.all.rp_filter present in /etc/sysctl.conf, change value to appropriate value +# else, add "net.ipv4.conf.all.rp_filter = value" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv4.conf.all.rp_filter") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv4_conf_all_rp_filter_value" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.ipv4.conf.all.rp_filter\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.ipv4.conf.all.rp_filter\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-81021-8" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable Kernel Parameter for Accepting Secure ICMP Redirects on all IPv4 Interfaces + To set the runtime status of the net.ipv4.conf.all.secure_redirects kernel parameter, run the following command: $ sudo sysctl -w net.ipv4.conf.all.secure_redirects=0 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: net.ipv4.conf.all.secure_redirects = 0 + BP28(R22) + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 18 + 2 + 3 + 4 + 6 + 7 + 8 + 9 + APO01.06 + APO13.01 + BAI04.04 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.03 + DSS01.05 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + DSS06.02 + DSS06.06 + 3.1.20 + CCI-001503 + CCI-001551 + 4.2.3.4 + 4.3.3.4 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + 4.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.2 + SR 7.1 + SR 7.2 + SR 7.6 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.12.1.1 + A.12.1.2 + A.12.1.3 + A.12.5.1 + A.12.6.2 + A.13.1.1 + A.13.1.2 + A.13.1.3 + A.13.2.1 + A.13.2.2 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.17.2.1 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-7(a) + CM-7(b) + CM-6(a) + SC-7(a) + DE.AE-1 + DE.CM-1 + ID.AM-3 + PR.AC-5 + PR.DS-4 + PR.DS-5 + PR.IP-1 + PR.PT-3 + PR.PT-4 + SRG-OS-000480-GPOS-00227 + 3.3.3 + Accepting "secure" ICMP redirects (from those gateways listed as +default gateways) has few legitimate uses. It should be disabled unless it is +absolutely required. + CCE-81016-8 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.ipv4.conf.all.secure_redirects.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-81016-8 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-7(a) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_all_secure_redirects + +- name: Comment out any occurrences of net.ipv4.conf.all.secure_redirects from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.ipv4.conf.all.secure_redirects + replace: '#net.ipv4.conf.all.secure_redirects' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-81016-8 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-7(a) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_all_secure_redirects +- name: XCCDF Value sysctl_net_ipv4_conf_all_secure_redirects_value # promote to variable + set_fact: + sysctl_net_ipv4_conf_all_secure_redirects_value: !!str + tags: + - always + +- name: Ensure sysctl net.ipv4.conf.all.secure_redirects is set + sysctl: + name: net.ipv4.conf.all.secure_redirects + value: '{{ sysctl_net_ipv4_conf_all_secure_redirects_value }}' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-81016-8 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-7(a) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_all_secure_redirects + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,net.ipv4.conf.all.secure_redirects%3D0%0A + mode: 0644 + path: /etc/sysctl.d/75-sysctl_net_ipv4_conf_all_secure_redirects.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.ipv4.conf.all.secure_redirects from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.ipv4.conf.all.secure_redirects.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.ipv4.conf.all.secure_redirects" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done +sysctl_net_ipv4_conf_all_secure_redirects_value='' + + +# +# Set runtime for net.ipv4.conf.all.secure_redirects +# +/sbin/sysctl -q -n -w net.ipv4.conf.all.secure_redirects="$sysctl_net_ipv4_conf_all_secure_redirects_value" + +# +# If net.ipv4.conf.all.secure_redirects present in /etc/sysctl.conf, change value to appropriate value +# else, add "net.ipv4.conf.all.secure_redirects = value" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv4.conf.all.secure_redirects") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv4_conf_all_secure_redirects_value" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.ipv4.conf.all.secure_redirects\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.ipv4.conf.all.secure_redirects\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-81016-8" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Configure Sending and Accepting Shared Media Redirects for All IPv4 Interfaces + To set the runtime status of the net.ipv4.conf.all.shared_media kernel parameter, run the following command: $ sudo sysctl -w net.ipv4.conf.all.shared_media= +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: net.ipv4.conf.all.shared_media = + This setting should be aligned with net.ipv4.conf.all.secure_redirects because it overrides it. +If shared_media is enabled for an interface secure_redirects will be enabled too. + CCE-88333-0 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.ipv4.conf.all.shared_media.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-88333-0 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_all_shared_media + +- name: Comment out any occurrences of net.ipv4.conf.all.shared_media from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.ipv4.conf.all.shared_media + replace: '#net.ipv4.conf.all.shared_media' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-88333-0 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_all_shared_media +- name: XCCDF Value sysctl_net_ipv4_conf_all_shared_media_value # promote to variable + set_fact: + sysctl_net_ipv4_conf_all_shared_media_value: !!str + tags: + - always + +- name: Ensure sysctl net.ipv4.conf.all.shared_media is set + sysctl: + name: net.ipv4.conf.all.shared_media + value: '{{ sysctl_net_ipv4_conf_all_shared_media_value }}' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-88333-0 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_all_shared_media + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.ipv4.conf.all.shared_media from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.ipv4.conf.all.shared_media.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.ipv4.conf.all.shared_media" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done +sysctl_net_ipv4_conf_all_shared_media_value='' + + +# +# Set runtime for net.ipv4.conf.all.shared_media +# +/sbin/sysctl -q -n -w net.ipv4.conf.all.shared_media="$sysctl_net_ipv4_conf_all_shared_media_value" + +# +# If net.ipv4.conf.all.shared_media present in /etc/sysctl.conf, change value to appropriate value +# else, add "net.ipv4.conf.all.shared_media = value" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv4.conf.all.shared_media") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv4_conf_all_shared_media_value" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.ipv4.conf.all.shared_media\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.ipv4.conf.all.shared_media\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-88333-0" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable Kernel Parameter for Accepting ICMP Redirects by Default on IPv4 Interfaces + To set the runtime status of the net.ipv4.conf.default.accept_redirects kernel parameter, run the following command: $ sudo sysctl -w net.ipv4.conf.default.accept_redirects=0 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: net.ipv4.conf.default.accept_redirects = 0 + BP28(R22) + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 18 + 2 + 3 + 4 + 6 + 7 + 8 + 9 + 5.10.1.1 + APO01.06 + APO13.01 + BAI04.04 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.03 + DSS01.05 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + DSS06.02 + DSS06.06 + 3.1.20 + CCI-000366 + CCI-001551 + 4.2.3.4 + 4.3.3.4 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + 4.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.2 + SR 7.1 + SR 7.2 + SR 7.6 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.12.1.1 + A.12.1.2 + A.12.1.3 + A.12.5.1 + A.12.6.2 + A.13.1.1 + A.13.1.2 + A.13.1.3 + A.13.2.1 + A.13.2.2 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.17.2.1 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-7(a) + CM-7(b) + CM-6(a) + SC-7(a) + DE.AE-1 + DE.CM-1 + ID.AM-3 + PR.AC-5 + PR.DS-4 + PR.DS-5 + PR.IP-1 + PR.PT-3 + PR.PT-4 + SRG-OS-000480-GPOS-00227 + RHEL-08-040209 + 3.3.2 + SV-244550r833373_rule + ICMP redirect messages are used by routers to inform hosts that a more +direct route exists for a particular destination. These messages modify the +host's route table and are unauthenticated. An illicit ICMP redirect +message could result in a man-in-the-middle attack. +This feature of the IPv4 protocol has few legitimate uses. It should +be disabled unless absolutely required. + CCE-80919-4 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.ipv4.conf.default.accept_redirects.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80919-4 + - CJIS-5.10.1.1 + - DISA-STIG-RHEL-08-040209 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-7(a) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_default_accept_redirects + +- name: Comment out any occurrences of net.ipv4.conf.default.accept_redirects from + /etc/sysctl.d/*.conf files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.ipv4.conf.default.accept_redirects + replace: '#net.ipv4.conf.default.accept_redirects' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80919-4 + - CJIS-5.10.1.1 + - DISA-STIG-RHEL-08-040209 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-7(a) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_default_accept_redirects +- name: XCCDF Value sysctl_net_ipv4_conf_default_accept_redirects_value # promote to variable + set_fact: + sysctl_net_ipv4_conf_default_accept_redirects_value: !!str + tags: + - always + +- name: Ensure sysctl net.ipv4.conf.default.accept_redirects is set + sysctl: + name: net.ipv4.conf.default.accept_redirects + value: '{{ sysctl_net_ipv4_conf_default_accept_redirects_value }}' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80919-4 + - CJIS-5.10.1.1 + - DISA-STIG-RHEL-08-040209 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-7(a) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_default_accept_redirects + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,net.ipv4.conf.default.accept_redirects%3D0%0A + mode: 0644 + path: /etc/sysctl.d/75-sysctl_net_ipv4_conf_default_accept_redirects.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.ipv4.conf.default.accept_redirects from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.ipv4.conf.default.accept_redirects.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.ipv4.conf.default.accept_redirects" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done +sysctl_net_ipv4_conf_default_accept_redirects_value='' + + +# +# Set runtime for net.ipv4.conf.default.accept_redirects +# +/sbin/sysctl -q -n -w net.ipv4.conf.default.accept_redirects="$sysctl_net_ipv4_conf_default_accept_redirects_value" + +# +# If net.ipv4.conf.default.accept_redirects present in /etc/sysctl.conf, change value to appropriate value +# else, add "net.ipv4.conf.default.accept_redirects = value" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv4.conf.default.accept_redirects") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv4_conf_default_accept_redirects_value" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.ipv4.conf.default.accept_redirects\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.ipv4.conf.default.accept_redirects\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-80919-4" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable Kernel Parameter for Accepting Source-Routed Packets on IPv4 Interfaces by Default + To set the runtime status of the net.ipv4.conf.default.accept_source_route kernel parameter, run the following command: $ sudo sysctl -w net.ipv4.conf.default.accept_source_route=0 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: net.ipv4.conf.default.accept_source_route = 0 + BP28(R22) + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 18 + 2 + 3 + 4 + 6 + 7 + 8 + 9 + 5.10.1.1 + APO01.06 + APO13.01 + BAI04.04 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.03 + DSS01.05 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + DSS06.02 + DSS06.06 + 3.1.20 + CCI-000366 + CCI-001551 + 4.2.3.4 + 4.3.3.4 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + 4.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.2 + SR 7.1 + SR 7.2 + SR 7.6 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.12.1.1 + A.12.1.2 + A.12.1.3 + A.12.5.1 + A.12.6.2 + A.13.1.1 + A.13.1.2 + A.13.1.3 + A.13.2.1 + A.13.2.2 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.17.2.1 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-007-3 R4 + CIP-007-3 R4.1 + CIP-007-3 R4.2 + CIP-007-3 R5.1 + CM-7(a) + CM-7(b) + SC-5 + SC-7(a) + DE.AE-1 + DE.CM-1 + ID.AM-3 + PR.AC-5 + PR.DS-4 + PR.DS-5 + PR.IP-1 + PR.PT-3 + PR.PT-4 + SRG-OS-000480-GPOS-00227 + RHEL-08-040249 + 3.3.1 + SV-244552r833377_rule + Source-routed packets allow the source of the packet to suggest routers +forward the packet along a different path than configured on the router, +which can be used to bypass network security measures. + +Accepting source-routed packets in the IPv4 protocol has few legitimate +uses. It should be disabled unless it is absolutely required, such as when +IPv4 forwarding is enabled and the system is legitimately functioning as a +router. + CCE-80920-2 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.ipv4.conf.default.accept_source_route.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80920-2 + - CJIS-5.10.1.1 + - DISA-STIG-RHEL-08-040249 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-5 + - NIST-800-53-SC-7(a) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_default_accept_source_route + +- name: Comment out any occurrences of net.ipv4.conf.default.accept_source_route from + /etc/sysctl.d/*.conf files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.ipv4.conf.default.accept_source_route + replace: '#net.ipv4.conf.default.accept_source_route' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80920-2 + - CJIS-5.10.1.1 + - DISA-STIG-RHEL-08-040249 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-5 + - NIST-800-53-SC-7(a) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_default_accept_source_route +- name: XCCDF Value sysctl_net_ipv4_conf_default_accept_source_route_value # promote to variable + set_fact: + sysctl_net_ipv4_conf_default_accept_source_route_value: !!str + tags: + - always + +- name: Ensure sysctl net.ipv4.conf.default.accept_source_route is set + sysctl: + name: net.ipv4.conf.default.accept_source_route + value: '{{ sysctl_net_ipv4_conf_default_accept_source_route_value }}' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80920-2 + - CJIS-5.10.1.1 + - DISA-STIG-RHEL-08-040249 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-5 + - NIST-800-53-SC-7(a) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_default_accept_source_route + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,net.ipv4.conf.default.accept_source_route%3D0%0A + mode: 0644 + path: /etc/sysctl.d/75-sysctl_net_ipv4_conf_default_accept_source_route.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.ipv4.conf.default.accept_source_route from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.ipv4.conf.default.accept_source_route.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.ipv4.conf.default.accept_source_route" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done +sysctl_net_ipv4_conf_default_accept_source_route_value='' + + +# +# Set runtime for net.ipv4.conf.default.accept_source_route +# +/sbin/sysctl -q -n -w net.ipv4.conf.default.accept_source_route="$sysctl_net_ipv4_conf_default_accept_source_route_value" + +# +# If net.ipv4.conf.default.accept_source_route present in /etc/sysctl.conf, change value to appropriate value +# else, add "net.ipv4.conf.default.accept_source_route = value" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv4.conf.default.accept_source_route") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv4_conf_default_accept_source_route_value" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.ipv4.conf.default.accept_source_route\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.ipv4.conf.default.accept_source_route\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-80920-2" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable Kernel Paremeter to Log Martian Packets on all IPv4 Interfaces by Default + To set the runtime status of the net.ipv4.conf.default.log_martians kernel parameter, run the following command: $ sudo sysctl -w net.ipv4.conf.default.log_martians=1 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: net.ipv4.conf.default.log_martians = 1 + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 2 + 3 + 7 + 8 + 9 + APO13.01 + BAI04.04 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.03 + DSS01.04 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.05 + DSS05.07 + DSS06.06 + 3.1.20 + CCI-000126 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.2 + SR 7.1 + SR 7.2 + SR 7.6 + A.11.2.6 + A.12.1.2 + A.12.1.3 + A.12.5.1 + A.12.6.2 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.17.2.1 + A.6.2.1 + A.6.2.2 + A.9.1.2 + CM-7(a) + CM-7(b) + SC-5(3)(a) + DE.CM-1 + PR.AC-3 + PR.DS-4 + PR.IP-1 + PR.PT-3 + PR.PT-4 + SRG-OS-000480-GPOS-00227 + 3.3.4 + The presence of "martian" packets (which have impossible addresses) +as well as spoofed packets, source-routed packets, and redirects could be a +sign of nefarious network activity. Logging these packets enables this activity +to be detected. + CCE-81020-0 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.ipv4.conf.default.log_martians.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-81020-0 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-5(3)(a) + - disable_strategy + - low_complexity + - medium_disruption + - reboot_required + - sysctl_net_ipv4_conf_default_log_martians + - unknown_severity + +- name: Comment out any occurrences of net.ipv4.conf.default.log_martians from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.ipv4.conf.default.log_martians + replace: '#net.ipv4.conf.default.log_martians' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-81020-0 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-5(3)(a) + - disable_strategy + - low_complexity + - medium_disruption + - reboot_required + - sysctl_net_ipv4_conf_default_log_martians + - unknown_severity +- name: XCCDF Value sysctl_net_ipv4_conf_default_log_martians_value # promote to variable + set_fact: + sysctl_net_ipv4_conf_default_log_martians_value: !!str + tags: + - always + +- name: Ensure sysctl net.ipv4.conf.default.log_martians is set + sysctl: + name: net.ipv4.conf.default.log_martians + value: '{{ sysctl_net_ipv4_conf_default_log_martians_value }}' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-81020-0 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-5(3)(a) + - disable_strategy + - low_complexity + - medium_disruption + - reboot_required + - sysctl_net_ipv4_conf_default_log_martians + - unknown_severity + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,net.ipv4.conf.default.log_martians%3D1%0A + mode: 0644 + path: /etc/sysctl.d/75-sysctl_net_ipv4_conf_default_log_martians.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.ipv4.conf.default.log_martians from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.ipv4.conf.default.log_martians.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.ipv4.conf.default.log_martians" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done +sysctl_net_ipv4_conf_default_log_martians_value='' + + +# +# Set runtime for net.ipv4.conf.default.log_martians +# +/sbin/sysctl -q -n -w net.ipv4.conf.default.log_martians="$sysctl_net_ipv4_conf_default_log_martians_value" + +# +# If net.ipv4.conf.default.log_martians present in /etc/sysctl.conf, change value to appropriate value +# else, add "net.ipv4.conf.default.log_martians = value" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv4.conf.default.log_martians") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv4_conf_default_log_martians_value" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.ipv4.conf.default.log_martians\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.ipv4.conf.default.log_martians\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-81020-0" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable Kernel Parameter to Use Reverse Path Filtering on all IPv4 Interfaces by Default + To set the runtime status of the net.ipv4.conf.default.rp_filter kernel parameter, run the following command: $ sudo sysctl -w net.ipv4.conf.default.rp_filter=1 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: net.ipv4.conf.default.rp_filter = 1 + BP28(R22) + 1 + 12 + 13 + 14 + 15 + 16 + 18 + 2 + 4 + 6 + 7 + 8 + 9 + APO01.06 + APO13.01 + BAI04.04 + DSS01.03 + DSS01.05 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.04 + DSS05.07 + DSS06.02 + 3.1.20 + CCI-000366 + 4.2.3.4 + 4.3.3.4 + 4.4.3.3 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.2 + SR 7.1 + SR 7.2 + SR 7.6 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.12.1.1 + A.12.1.2 + A.12.1.3 + A.13.1.1 + A.13.1.2 + A.13.1.3 + A.13.2.1 + A.13.2.2 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.17.2.1 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-7(a) + CM-7(b) + CM-6(a) + SC-7(a) + DE.AE-1 + DE.CM-1 + ID.AM-3 + PR.AC-5 + PR.DS-4 + PR.DS-5 + PR.PT-4 + SRG-OS-000480-GPOS-00227 + 3.3.7 + Enabling reverse path filtering drops packets with source addresses +that should not have been able to be received on the interface they were +received on. It should not be used on systems which are routers for +complicated networks, but is helpful for end hosts and routers serving small +networks. + CCE-81022-6 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.ipv4.conf.default.rp_filter.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-81022-6 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-7(a) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_default_rp_filter + +- name: Comment out any occurrences of net.ipv4.conf.default.rp_filter from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.ipv4.conf.default.rp_filter + replace: '#net.ipv4.conf.default.rp_filter' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-81022-6 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-7(a) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_default_rp_filter +- name: XCCDF Value sysctl_net_ipv4_conf_default_rp_filter_value # promote to variable + set_fact: + sysctl_net_ipv4_conf_default_rp_filter_value: !!str + tags: + - always + +- name: Ensure sysctl net.ipv4.conf.default.rp_filter is set + sysctl: + name: net.ipv4.conf.default.rp_filter + value: '{{ sysctl_net_ipv4_conf_default_rp_filter_value }}' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-81022-6 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-7(a) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_default_rp_filter + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,net.ipv4.conf.default.rp_filter%3D1%0A + mode: 0644 + path: /etc/sysctl.d/75-sysctl_net_ipv4_conf_default_rp_filter.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.ipv4.conf.default.rp_filter from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.ipv4.conf.default.rp_filter.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.ipv4.conf.default.rp_filter" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done +sysctl_net_ipv4_conf_default_rp_filter_value='' + + +# +# Set runtime for net.ipv4.conf.default.rp_filter +# +/sbin/sysctl -q -n -w net.ipv4.conf.default.rp_filter="$sysctl_net_ipv4_conf_default_rp_filter_value" + +# +# If net.ipv4.conf.default.rp_filter present in /etc/sysctl.conf, change value to appropriate value +# else, add "net.ipv4.conf.default.rp_filter = value" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv4.conf.default.rp_filter") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv4_conf_default_rp_filter_value" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.ipv4.conf.default.rp_filter\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.ipv4.conf.default.rp_filter\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-81022-6" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Configure Kernel Parameter for Accepting Secure Redirects By Default + To set the runtime status of the net.ipv4.conf.default.secure_redirects kernel parameter, run the following command: $ sudo sysctl -w net.ipv4.conf.default.secure_redirects=0 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: net.ipv4.conf.default.secure_redirects = 0 + BP28(R22) + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 18 + 2 + 3 + 4 + 6 + 7 + 8 + 9 + APO01.06 + APO13.01 + BAI04.04 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.03 + DSS01.05 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + DSS06.02 + DSS06.06 + 3.1.20 + CCI-001551 + 4.2.3.4 + 4.3.3.4 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + 4.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.2 + SR 7.1 + SR 7.2 + SR 7.6 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.12.1.1 + A.12.1.2 + A.12.1.3 + A.12.5.1 + A.12.6.2 + A.13.1.1 + A.13.1.2 + A.13.1.3 + A.13.2.1 + A.13.2.2 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.17.2.1 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-007-3 R4 + CIP-007-3 R4.1 + CIP-007-3 R4.2 + CIP-007-3 R5.1 + CM-7(a) + CM-7(b) + SC-5 + SC-7(a) + DE.AE-1 + DE.CM-1 + ID.AM-3 + PR.AC-5 + PR.DS-4 + PR.DS-5 + PR.IP-1 + PR.PT-3 + PR.PT-4 + SRG-OS-000480-GPOS-00227 + 3.3.3 + Accepting "secure" ICMP redirects (from those gateways listed as +default gateways) has few legitimate uses. It should be disabled unless it is +absolutely required. + CCE-81017-6 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.ipv4.conf.default.secure_redirects.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-81017-6 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-5 + - NIST-800-53-SC-7(a) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_default_secure_redirects + +- name: Comment out any occurrences of net.ipv4.conf.default.secure_redirects from + /etc/sysctl.d/*.conf files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.ipv4.conf.default.secure_redirects + replace: '#net.ipv4.conf.default.secure_redirects' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-81017-6 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-5 + - NIST-800-53-SC-7(a) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_default_secure_redirects +- name: XCCDF Value sysctl_net_ipv4_conf_default_secure_redirects_value # promote to variable + set_fact: + sysctl_net_ipv4_conf_default_secure_redirects_value: !!str + tags: + - always + +- name: Ensure sysctl net.ipv4.conf.default.secure_redirects is set + sysctl: + name: net.ipv4.conf.default.secure_redirects + value: '{{ sysctl_net_ipv4_conf_default_secure_redirects_value }}' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-81017-6 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-5 + - NIST-800-53-SC-7(a) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_default_secure_redirects + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,net.ipv4.conf.default.secure_redirects%3D0%0A + mode: 0644 + path: /etc/sysctl.d/75-sysctl_net_ipv4_conf_default_secure_redirects.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.ipv4.conf.default.secure_redirects from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.ipv4.conf.default.secure_redirects.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.ipv4.conf.default.secure_redirects" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done +sysctl_net_ipv4_conf_default_secure_redirects_value='' + + +# +# Set runtime for net.ipv4.conf.default.secure_redirects +# +/sbin/sysctl -q -n -w net.ipv4.conf.default.secure_redirects="$sysctl_net_ipv4_conf_default_secure_redirects_value" + +# +# If net.ipv4.conf.default.secure_redirects present in /etc/sysctl.conf, change value to appropriate value +# else, add "net.ipv4.conf.default.secure_redirects = value" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv4.conf.default.secure_redirects") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv4_conf_default_secure_redirects_value" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.ipv4.conf.default.secure_redirects\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.ipv4.conf.default.secure_redirects\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-81017-6" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Configure Sending and Accepting Shared Media Redirects by Default + To set the runtime status of the net.ipv4.conf.default.shared_media kernel parameter, run the following command: $ sudo sysctl -w net.ipv4.conf.default.shared_media= +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: net.ipv4.conf.default.shared_media = + This setting should be aligned with net.ipv4.conf.default.secure_redirects because it overrides it. +If shared_media is enabled for an interface secure_redirects will be enabled too. + CCE-88444-5 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.ipv4.conf.default.shared_media.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-88444-5 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_default_shared_media + +- name: Comment out any occurrences of net.ipv4.conf.default.shared_media from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.ipv4.conf.default.shared_media + replace: '#net.ipv4.conf.default.shared_media' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-88444-5 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_default_shared_media +- name: XCCDF Value sysctl_net_ipv4_conf_default_shared_media_value # promote to variable + set_fact: + sysctl_net_ipv4_conf_default_shared_media_value: !!str + tags: + - always + +- name: Ensure sysctl net.ipv4.conf.default.shared_media is set + sysctl: + name: net.ipv4.conf.default.shared_media + value: '{{ sysctl_net_ipv4_conf_default_shared_media_value }}' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-88444-5 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_default_shared_media + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.ipv4.conf.default.shared_media from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.ipv4.conf.default.shared_media.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.ipv4.conf.default.shared_media" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done +sysctl_net_ipv4_conf_default_shared_media_value='' + + +# +# Set runtime for net.ipv4.conf.default.shared_media +# +/sbin/sysctl -q -n -w net.ipv4.conf.default.shared_media="$sysctl_net_ipv4_conf_default_shared_media_value" + +# +# If net.ipv4.conf.default.shared_media present in /etc/sysctl.conf, change value to appropriate value +# else, add "net.ipv4.conf.default.shared_media = value" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv4.conf.default.shared_media") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv4_conf_default_shared_media_value" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.ipv4.conf.default.shared_media\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.ipv4.conf.default.shared_media\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-88444-5" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable Kernel Parameter to Ignore ICMP Broadcast Echo Requests on IPv4 Interfaces + To set the runtime status of the net.ipv4.icmp_echo_ignore_broadcasts kernel parameter, run the following command: $ sudo sysctl -w net.ipv4.icmp_echo_ignore_broadcasts=1 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: net.ipv4.icmp_echo_ignore_broadcasts = 1 + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 18 + 2 + 3 + 4 + 6 + 7 + 8 + 9 + 5.10.1.1 + APO01.06 + APO13.01 + BAI04.04 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.03 + DSS01.05 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + DSS06.02 + DSS06.06 + 3.1.20 + CCI-000366 + 4.2.3.4 + 4.3.3.4 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + 4.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.2 + SR 7.1 + SR 7.2 + SR 7.6 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.12.1.1 + A.12.1.2 + A.12.1.3 + A.12.5.1 + A.12.6.2 + A.13.1.1 + A.13.1.2 + A.13.1.3 + A.13.2.1 + A.13.2.2 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.17.2.1 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-007-3 R4 + CIP-007-3 R4.1 + CIP-007-3 R4.2 + CIP-007-3 R5.1 + CM-7(a) + CM-7(b) + SC-5 + DE.AE-1 + DE.CM-1 + ID.AM-3 + PR.AC-5 + PR.DS-4 + PR.DS-5 + PR.IP-1 + PR.PT-3 + PR.PT-4 + SRG-OS-000480-GPOS-00227 + RHEL-08-040230 + 3.3.5 + SV-230537r833344_rule + Responding to broadcast (ICMP) echoes facilitates network mapping +and provides a vector for amplification attacks. + +Ignoring ICMP echo requests (pings) sent to broadcast or multicast +addresses makes the system slightly more difficult to enumerate on the network. + CCE-80922-8 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.ipv4.icmp_echo_ignore_broadcasts.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80922-8 + - CJIS-5.10.1.1 + - DISA-STIG-RHEL-08-040230 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-5 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_icmp_echo_ignore_broadcasts + +- name: Comment out any occurrences of net.ipv4.icmp_echo_ignore_broadcasts from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.ipv4.icmp_echo_ignore_broadcasts + replace: '#net.ipv4.icmp_echo_ignore_broadcasts' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80922-8 + - CJIS-5.10.1.1 + - DISA-STIG-RHEL-08-040230 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-5 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_icmp_echo_ignore_broadcasts +- name: XCCDF Value sysctl_net_ipv4_icmp_echo_ignore_broadcasts_value # promote to variable + set_fact: + sysctl_net_ipv4_icmp_echo_ignore_broadcasts_value: !!str + tags: + - always + +- name: Ensure sysctl net.ipv4.icmp_echo_ignore_broadcasts is set + sysctl: + name: net.ipv4.icmp_echo_ignore_broadcasts + value: '{{ sysctl_net_ipv4_icmp_echo_ignore_broadcasts_value }}' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80922-8 + - CJIS-5.10.1.1 + - DISA-STIG-RHEL-08-040230 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-5 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_icmp_echo_ignore_broadcasts + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,net.ipv4.icmp_echo_ignore_broadcasts%3D1%0A + mode: 0644 + path: /etc/sysctl.d/75-sysctl_net_ipv4_icmp_echo_ignore_broadcasts.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.ipv4.icmp_echo_ignore_broadcasts from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.ipv4.icmp_echo_ignore_broadcasts.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.ipv4.icmp_echo_ignore_broadcasts" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done +sysctl_net_ipv4_icmp_echo_ignore_broadcasts_value='' + + +# +# Set runtime for net.ipv4.icmp_echo_ignore_broadcasts +# +/sbin/sysctl -q -n -w net.ipv4.icmp_echo_ignore_broadcasts="$sysctl_net_ipv4_icmp_echo_ignore_broadcasts_value" + +# +# If net.ipv4.icmp_echo_ignore_broadcasts present in /etc/sysctl.conf, change value to appropriate value +# else, add "net.ipv4.icmp_echo_ignore_broadcasts = value" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv4.icmp_echo_ignore_broadcasts") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv4_icmp_echo_ignore_broadcasts_value" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.ipv4.icmp_echo_ignore_broadcasts\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.ipv4.icmp_echo_ignore_broadcasts\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-80922-8" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable Kernel Parameter to Ignore Bogus ICMP Error Responses on IPv4 Interfaces + To set the runtime status of the net.ipv4.icmp_ignore_bogus_error_responses kernel parameter, run the following command: $ sudo sysctl -w net.ipv4.icmp_ignore_bogus_error_responses=1 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: net.ipv4.icmp_ignore_bogus_error_responses = 1 + BP28(R22) + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 2 + 3 + 7 + 8 + 9 + APO13.01 + BAI04.04 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.03 + DSS03.05 + DSS05.02 + DSS05.05 + DSS05.07 + DSS06.06 + 3.1.20 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 6.2 + SR 7.1 + SR 7.2 + SR 7.6 + A.12.1.2 + A.12.1.3 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.17.2.1 + A.9.1.2 + CIP-007-3 R4 + CIP-007-3 R4.1 + CIP-007-3 R4.2 + CIP-007-3 R5.1 + CM-7(a) + CM-7(b) + SC-5 + DE.CM-1 + PR.DS-4 + PR.IP-1 + PR.PT-3 + SRG-OS-000480-GPOS-00227 + 3.3.6 + Ignoring bogus ICMP error responses reduces +log size, although some activity would not be logged. + CCE-81023-4 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.ipv4.icmp_ignore_bogus_error_responses.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-81023-4 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-5 + - disable_strategy + - low_complexity + - medium_disruption + - reboot_required + - sysctl_net_ipv4_icmp_ignore_bogus_error_responses + - unknown_severity + +- name: Comment out any occurrences of net.ipv4.icmp_ignore_bogus_error_responses + from /etc/sysctl.d/*.conf files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.ipv4.icmp_ignore_bogus_error_responses + replace: '#net.ipv4.icmp_ignore_bogus_error_responses' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-81023-4 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-5 + - disable_strategy + - low_complexity + - medium_disruption + - reboot_required + - sysctl_net_ipv4_icmp_ignore_bogus_error_responses + - unknown_severity +- name: XCCDF Value sysctl_net_ipv4_icmp_ignore_bogus_error_responses_value # promote to variable + set_fact: + sysctl_net_ipv4_icmp_ignore_bogus_error_responses_value: !!str + tags: + - always + +- name: Ensure sysctl net.ipv4.icmp_ignore_bogus_error_responses is set + sysctl: + name: net.ipv4.icmp_ignore_bogus_error_responses + value: '{{ sysctl_net_ipv4_icmp_ignore_bogus_error_responses_value }}' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-81023-4 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-5 + - disable_strategy + - low_complexity + - medium_disruption + - reboot_required + - sysctl_net_ipv4_icmp_ignore_bogus_error_responses + - unknown_severity + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,net.ipv4.icmp_ignore_bogus_error_responses%3D1%0A + mode: 0644 + path: /etc/sysctl.d/75-sysctl_net_ipv4_icmp_ignore_bogus_error_responses.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.ipv4.icmp_ignore_bogus_error_responses from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.ipv4.icmp_ignore_bogus_error_responses.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.ipv4.icmp_ignore_bogus_error_responses" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done +sysctl_net_ipv4_icmp_ignore_bogus_error_responses_value='' + + +# +# Set runtime for net.ipv4.icmp_ignore_bogus_error_responses +# +/sbin/sysctl -q -n -w net.ipv4.icmp_ignore_bogus_error_responses="$sysctl_net_ipv4_icmp_ignore_bogus_error_responses_value" + +# +# If net.ipv4.icmp_ignore_bogus_error_responses present in /etc/sysctl.conf, change value to appropriate value +# else, add "net.ipv4.icmp_ignore_bogus_error_responses = value" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv4.icmp_ignore_bogus_error_responses") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv4_icmp_ignore_bogus_error_responses_value" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.ipv4.icmp_ignore_bogus_error_responses\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.ipv4.icmp_ignore_bogus_error_responses\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-81023-4" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Set Kernel Parameter to Increase Local Port Range + To set the runtime status of the net.ipv4.ip_local_port_range kernel parameter, run the following command: $ sudo sysctl -w net.ipv4.ip_local_port_range=32768 65535 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: net.ipv4.ip_local_port_range = 32768 65535 + BP28(R22) + This setting defines the local port range that is used by TCP and UDP to +choose the local port. The first number is the first, the second the last +local port number. + CCE-84277-3 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.ipv4.ip_local_port_range.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84277-3 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_ip_local_port_range + +- name: Comment out any occurrences of net.ipv4.ip_local_port_range from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.ipv4.ip_local_port_range + replace: '#net.ipv4.ip_local_port_range' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84277-3 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_ip_local_port_range + +- name: Ensure sysctl net.ipv4.ip_local_port_range is set to 32768 65535 + sysctl: + name: net.ipv4.ip_local_port_range + value: 32768 65535 + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84277-3 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_ip_local_port_range + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.ipv4.ip_local_port_range from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.ipv4.ip_local_port_range.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.ipv4.ip_local_port_range" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done + +# +# Set runtime for net.ipv4.ip_local_port_range +# +/sbin/sysctl -q -n -w net.ipv4.ip_local_port_range="32768 65535" + +# +# If net.ipv4.ip_local_port_range present in /etc/sysctl.conf, change value to "32768 65535" +# else, add "net.ipv4.ip_local_port_range = 32768 65535" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv4.ip_local_port_range") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "32768 65535" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.ipv4.ip_local_port_range\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.ipv4.ip_local_port_range\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-84277-3" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure Kernel to Rate Limit Sending of Duplicate TCP Acknowledgments + Make sure that the system is configured to limit the maximal rate for sending +duplicate acknowledgments in response to incoming TCP packets that are for +an existing connection but that are invalid due to any of these reasons: + +(a) out-of-window sequence number, (b) out-of-window acknowledgment number, +or (c) PAWS (Protection Against Wrapped Sequence numbers) check failure +This measure protects against or limits effects of DoS attacks against the system. +Set the system to implement rate-limiting measures by adding the following line to +/etc/sysctl.conf or a configuration file in the /etc/sysctl.d/ directory +(or modify the line to have the required value): +net.ipv4.tcp_invalid_ratelimit = +Issue the following command to make the changes take effect: +# sysctl --system + CCI-002385 + CIP-007-3 R4 + CIP-007-3 R4.1 + CIP-007-3 R4.2 + CIP-007-3 R5.1 + SC-5 + SRG-OS-000420-GPOS-00186 + SRG-OS-000420-VMM-001690 + Denial of Service (DoS) is a condition when a resource is not available for legitimate users. When +this occurs, the organization either cannot accomplish its mission or must +operate at degraded capacity. + +This can help mitigate simple “ack loop” DoS attacks, wherein a buggy or +malicious middlebox or man-in-the-middle can rewrite TCP header fields in +manner that causes each endpoint to think that the other is sending invalid +TCP segments, thus causing each side to send an unterminating stream of +duplicate acknowledgments for invalid segments. + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.ipv4.tcp_invalid_ratelimit.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-SC-5 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_tcp_invalid_ratelimit + +- name: Comment out any occurrences of net.ipv4.tcp_invalid_ratelimit from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.ipv4.tcp_invalid_ratelimit + replace: '#net.ipv4.tcp_invalid_ratelimit' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-SC-5 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_tcp_invalid_ratelimit +- name: XCCDF Value sysctl_net_ipv4_tcp_invalid_ratelimit_value # promote to variable + set_fact: + sysctl_net_ipv4_tcp_invalid_ratelimit_value: !!str + tags: + - always + +- name: Ensure sysctl net.ipv4.tcp_invalid_ratelimit is set + sysctl: + name: net.ipv4.tcp_invalid_ratelimit + value: '{{ sysctl_net_ipv4_tcp_invalid_ratelimit_value }}' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-SC-5 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_tcp_invalid_ratelimit + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.ipv4.tcp_invalid_ratelimit from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.ipv4.tcp_invalid_ratelimit.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.ipv4.tcp_invalid_ratelimit" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done +sysctl_net_ipv4_tcp_invalid_ratelimit_value='' + + +# +# Set runtime for net.ipv4.tcp_invalid_ratelimit +# +/sbin/sysctl -q -n -w net.ipv4.tcp_invalid_ratelimit="$sysctl_net_ipv4_tcp_invalid_ratelimit_value" + +# +# If net.ipv4.tcp_invalid_ratelimit present in /etc/sysctl.conf, change value to appropriate value +# else, add "net.ipv4.tcp_invalid_ratelimit = value" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv4.tcp_invalid_ratelimit") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv4_tcp_invalid_ratelimit_value" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.ipv4.tcp_invalid_ratelimit\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.ipv4.tcp_invalid_ratelimit\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable Kernel Parameter to Use TCP RFC 1337 on IPv4 Interfaces + To set the runtime status of the net.ipv4.tcp_rfc1337 kernel parameter, run the following command: $ sudo sysctl -w net.ipv4.tcp_rfc1337=1 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: net.ipv4.tcp_rfc1337 = 1 + BP28(R22) + Enable TCP behavior conformant with RFC 1337. When disabled, if a RST is +received in TIME_WAIT state, we close the socket immediately without waiting +for the end of the TIME_WAIT period. + CCE-84270-8 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.ipv4.tcp_rfc1337.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84270-8 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_tcp_rfc1337 + +- name: Comment out any occurrences of net.ipv4.tcp_rfc1337 from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.ipv4.tcp_rfc1337 + replace: '#net.ipv4.tcp_rfc1337' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84270-8 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_tcp_rfc1337 +- name: XCCDF Value sysctl_net_ipv4_tcp_rfc1337_value # promote to variable + set_fact: + sysctl_net_ipv4_tcp_rfc1337_value: !!str + tags: + - always + +- name: Ensure sysctl net.ipv4.tcp_rfc1337 is set + sysctl: + name: net.ipv4.tcp_rfc1337 + value: '{{ sysctl_net_ipv4_tcp_rfc1337_value }}' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84270-8 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_tcp_rfc1337 + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.ipv4.tcp_rfc1337 from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.ipv4.tcp_rfc1337.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.ipv4.tcp_rfc1337" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done +sysctl_net_ipv4_tcp_rfc1337_value='' + + +# +# Set runtime for net.ipv4.tcp_rfc1337 +# +/sbin/sysctl -q -n -w net.ipv4.tcp_rfc1337="$sysctl_net_ipv4_tcp_rfc1337_value" + +# +# If net.ipv4.tcp_rfc1337 present in /etc/sysctl.conf, change value to appropriate value +# else, add "net.ipv4.tcp_rfc1337 = value" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv4.tcp_rfc1337") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv4_tcp_rfc1337_value" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.ipv4.tcp_rfc1337\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.ipv4.tcp_rfc1337\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-84270-8" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable Kernel Parameter to Use TCP Syncookies on Network Interfaces + To set the runtime status of the net.ipv4.tcp_syncookies kernel parameter, run the following command: $ sudo sysctl -w net.ipv4.tcp_syncookies=1 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: net.ipv4.tcp_syncookies = 1 + BP28(R22) + 1 + 12 + 13 + 14 + 15 + 16 + 18 + 2 + 4 + 6 + 7 + 8 + 9 + 5.10.1.1 + APO01.06 + APO13.01 + BAI04.04 + DSS01.03 + DSS01.05 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.04 + DSS05.07 + DSS06.02 + 3.1.20 + CCI-000366 + CCI-001095 + 4.2.3.4 + 4.3.3.4 + 4.4.3.3 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.2 + SR 7.1 + SR 7.2 + SR 7.6 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.12.1.1 + A.12.1.2 + A.12.1.3 + A.13.1.1 + A.13.1.2 + A.13.1.3 + A.13.2.1 + A.13.2.2 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.17.2.1 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-7(a) + CM-7(b) + SC-5(1) + SC-5(2) + SC-5(3)(a) + CM-6(a) + DE.AE-1 + DE.CM-1 + ID.AM-3 + PR.AC-5 + PR.DS-4 + PR.DS-5 + PR.PT-4 + SRG-OS-000480-GPOS-00227 + SRG-OS-000420-GPOS-00186 + SRG-OS-000142-GPOS-00071 + 3.3.8 + A TCP SYN flood attack can cause a denial of service by filling a +system's TCP connection table with connections in the SYN_RCVD state. +Syncookies can be used to track a connection when a subsequent ACK is received, +verifying the initiator is attempting a valid connection and is not a flood +source. This feature is activated when a flood condition is detected, and +enables the system to continue servicing valid connection requests. + CCE-80923-6 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.ipv4.tcp_syncookies.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80923-6 + - CJIS-5.10.1.1 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-5(1) + - NIST-800-53-SC-5(2) + - NIST-800-53-SC-5(3)(a) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_tcp_syncookies + +- name: Comment out any occurrences of net.ipv4.tcp_syncookies from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.ipv4.tcp_syncookies + replace: '#net.ipv4.tcp_syncookies' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80923-6 + - CJIS-5.10.1.1 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-5(1) + - NIST-800-53-SC-5(2) + - NIST-800-53-SC-5(3)(a) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_tcp_syncookies +- name: XCCDF Value sysctl_net_ipv4_tcp_syncookies_value # promote to variable + set_fact: + sysctl_net_ipv4_tcp_syncookies_value: !!str + tags: + - always + +- name: Ensure sysctl net.ipv4.tcp_syncookies is set + sysctl: + name: net.ipv4.tcp_syncookies + value: '{{ sysctl_net_ipv4_tcp_syncookies_value }}' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80923-6 + - CJIS-5.10.1.1 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-5(1) + - NIST-800-53-SC-5(2) + - NIST-800-53-SC-5(3)(a) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_tcp_syncookies + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,net.ipv4.tcp_syncookies%3D1%0A + mode: 0644 + path: /etc/sysctl.d/75-sysctl_net_ipv4_tcp_syncookies.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.ipv4.tcp_syncookies from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.ipv4.tcp_syncookies.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.ipv4.tcp_syncookies" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done +sysctl_net_ipv4_tcp_syncookies_value='' + + +# +# Set runtime for net.ipv4.tcp_syncookies +# +/sbin/sysctl -q -n -w net.ipv4.tcp_syncookies="$sysctl_net_ipv4_tcp_syncookies_value" + +# +# If net.ipv4.tcp_syncookies present in /etc/sysctl.conf, change value to appropriate value +# else, add "net.ipv4.tcp_syncookies = value" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv4.tcp_syncookies") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv4_tcp_syncookies_value" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.ipv4.tcp_syncookies\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.ipv4.tcp_syncookies\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-80923-6" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + + Network Parameters for Hosts Only + If the system is not going to be used as a router, then setting certain +kernel parameters ensure that the host will not perform routing +of network traffic. + + + Disable Kernel Parameter for Sending ICMP Redirects on all IPv4 Interfaces + To set the runtime status of the net.ipv4.conf.all.send_redirects kernel parameter, run the following command: $ sudo sysctl -w net.ipv4.conf.all.send_redirects=0 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: net.ipv4.conf.all.send_redirects = 0 + BP28(R22) + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 18 + 2 + 3 + 4 + 6 + 7 + 8 + 9 + 5.10.1.1 + APO01.06 + APO13.01 + BAI04.04 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.03 + DSS01.05 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + DSS06.02 + DSS06.06 + 3.1.20 + CCI-000366 + 4.2.3.4 + 4.3.3.4 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + 4.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.2 + SR 7.1 + SR 7.2 + SR 7.6 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.12.1.1 + A.12.1.2 + A.12.1.3 + A.12.5.1 + A.12.6.2 + A.13.1.1 + A.13.1.2 + A.13.1.3 + A.13.2.1 + A.13.2.2 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.17.2.1 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-007-3 R4 + CIP-007-3 R4.1 + CIP-007-3 R4.2 + CIP-007-3 R5.1 + CM-7(a) + CM-7(b) + SC-5 + CM-6(a) + SC-7(a) + DE.AE-1 + DE.CM-1 + ID.AM-3 + PR.AC-5 + PR.DS-4 + PR.DS-5 + PR.IP-1 + PR.PT-3 + PR.PT-4 + SRG-OS-000480-GPOS-00227 + RHEL-08-040220 + 3.2.2 + SV-230536r833342_rule + ICMP redirect messages are used by routers to inform hosts that a more +direct route exists for a particular destination. These messages contain information +from the system's route table possibly revealing portions of the network topology. + +The ability to send ICMP redirects is only appropriate for systems acting as routers. + CCE-80918-6 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.ipv4.conf.all.send_redirects.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80918-6 + - CJIS-5.10.1.1 + - DISA-STIG-RHEL-08-040220 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-5 + - NIST-800-53-SC-7(a) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_all_send_redirects + +- name: Comment out any occurrences of net.ipv4.conf.all.send_redirects from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.ipv4.conf.all.send_redirects + replace: '#net.ipv4.conf.all.send_redirects' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80918-6 + - CJIS-5.10.1.1 + - DISA-STIG-RHEL-08-040220 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-5 + - NIST-800-53-SC-7(a) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_all_send_redirects + +- name: Ensure sysctl net.ipv4.conf.all.send_redirects is set to 0 + sysctl: + name: net.ipv4.conf.all.send_redirects + value: '0' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80918-6 + - CJIS-5.10.1.1 + - DISA-STIG-RHEL-08-040220 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-5 + - NIST-800-53-SC-7(a) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_all_send_redirects + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,net.ipv4.conf.all.send_redirects%3D0%0A + mode: 0644 + path: /etc/sysctl.d/75-sysctl_net_ipv4_conf_all_send_redirects.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.ipv4.conf.all.send_redirects from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.ipv4.conf.all.send_redirects.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.ipv4.conf.all.send_redirects" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done + +# +# Set runtime for net.ipv4.conf.all.send_redirects +# +/sbin/sysctl -q -n -w net.ipv4.conf.all.send_redirects="0" + +# +# If net.ipv4.conf.all.send_redirects present in /etc/sysctl.conf, change value to "0" +# else, add "net.ipv4.conf.all.send_redirects = 0" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv4.conf.all.send_redirects") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "0" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.ipv4.conf.all.send_redirects\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.ipv4.conf.all.send_redirects\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-80918-6" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable Kernel Parameter for Sending ICMP Redirects on all IPv4 Interfaces by Default + To set the runtime status of the net.ipv4.conf.default.send_redirects kernel parameter, run the following command: $ sudo sysctl -w net.ipv4.conf.default.send_redirects=0 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: net.ipv4.conf.default.send_redirects = 0 + BP28(R22) + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 18 + 2 + 3 + 4 + 6 + 7 + 8 + 9 + 5.10.1.1 + APO01.06 + APO13.01 + BAI04.04 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.03 + DSS01.05 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + DSS06.02 + DSS06.06 + 3.1.20 + CCI-000366 + 4.2.3.4 + 4.3.3.4 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + 4.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.2 + SR 7.1 + SR 7.2 + SR 7.6 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.12.1.1 + A.12.1.2 + A.12.1.3 + A.12.5.1 + A.12.6.2 + A.13.1.1 + A.13.1.2 + A.13.1.3 + A.13.2.1 + A.13.2.2 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.17.2.1 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-007-3 R4 + CIP-007-3 R4.1 + CIP-007-3 R4.2 + CIP-007-3 R5.1 + CM-7(a) + CM-7(b) + SC-5 + CM-6(a) + SC-7(a) + DE.AE-1 + DE.CM-1 + ID.AM-3 + PR.AC-5 + PR.DS-4 + PR.DS-5 + PR.IP-1 + PR.PT-3 + PR.PT-4 + SRG-OS-000480-GPOS-00227 + RHEL-08-040270 + 3.2.2 + SV-230543r833355_rule + ICMP redirect messages are used by routers to inform hosts that a more +direct route exists for a particular destination. These messages contain information +from the system's route table possibly revealing portions of the network topology. + +The ability to send ICMP redirects is only appropriate for systems acting as routers. + CCE-80921-0 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.ipv4.conf.default.send_redirects.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80921-0 + - CJIS-5.10.1.1 + - DISA-STIG-RHEL-08-040270 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-5 + - NIST-800-53-SC-7(a) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_default_send_redirects + +- name: Comment out any occurrences of net.ipv4.conf.default.send_redirects from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.ipv4.conf.default.send_redirects + replace: '#net.ipv4.conf.default.send_redirects' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80921-0 + - CJIS-5.10.1.1 + - DISA-STIG-RHEL-08-040270 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-5 + - NIST-800-53-SC-7(a) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_default_send_redirects + +- name: Ensure sysctl net.ipv4.conf.default.send_redirects is set to 0 + sysctl: + name: net.ipv4.conf.default.send_redirects + value: '0' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80921-0 + - CJIS-5.10.1.1 + - DISA-STIG-RHEL-08-040270 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-5 + - NIST-800-53-SC-7(a) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_default_send_redirects + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,net.ipv4.conf.default.send_redirects%3D0%0A + mode: 0644 + path: /etc/sysctl.d/75-sysctl_net_ipv4_conf_default_send_redirects.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.ipv4.conf.default.send_redirects from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.ipv4.conf.default.send_redirects.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.ipv4.conf.default.send_redirects" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done + +# +# Set runtime for net.ipv4.conf.default.send_redirects +# +/sbin/sysctl -q -n -w net.ipv4.conf.default.send_redirects="0" + +# +# If net.ipv4.conf.default.send_redirects present in /etc/sysctl.conf, change value to "0" +# else, add "net.ipv4.conf.default.send_redirects = 0" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv4.conf.default.send_redirects") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "0" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.ipv4.conf.default.send_redirects\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.ipv4.conf.default.send_redirects\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-80921-0" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable Kernel Parameter for IP Forwarding on IPv4 Interfaces + To set the runtime status of the net.ipv4.ip_forward kernel parameter, run the following command: $ sudo sysctl -w net.ipv4.ip_forward=0 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: net.ipv4.ip_forward = 0 + Certain technologies such as virtual machines, containers, etc. rely on IPv4 forwarding to enable and use networking. +Disabling IPv4 forwarding would cause those technologies to stop working. Therefore, this rule should not be used in +profiles or benchmarks that target usage of IPv4 forwarding. + This rule is disabled on Red Hat Virtualization Hosts and Managers, it will report not applicable. +RHV host requires IPv4 forwarding for the Hosted Engine bootstrap VM to reach network outside of the initial host. + BP28(R22) + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 2 + 3 + 7 + 8 + 9 + APO13.01 + BAI04.04 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.03 + DSS03.05 + DSS05.02 + DSS05.05 + DSS05.07 + DSS06.06 + 3.1.20 + CCI-000366 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.2 + SR 7.1 + SR 7.2 + SR 7.6 + A.12.1.2 + A.12.1.3 + A.12.5.1 + A.12.6.2 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.17.2.1 + A.9.1.2 + CIP-007-3 R4 + CIP-007-3 R4.1 + CIP-007-3 R4.2 + CIP-007-3 R5.1 + CM-7(a) + CM-7(b) + SC-5 + CM-6(a) + SC-7(a) + DE.CM-1 + PR.DS-4 + PR.IP-1 + PR.PT-3 + PR.PT-4 + SRG-OS-000480-GPOS-00227 + 3.2.1 + Routing protocol daemons are typically used on routers to exchange +network topology information with other routers. If this capability is used when +not required, system network information may be unnecessarily transmitted across +the network. + + CCE-81024-2 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.ipv4.ip_forward.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-81024-2 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-5 + - NIST-800-53-SC-7(a) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_ip_forward + +- name: Comment out any occurrences of net.ipv4.ip_forward from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.ipv4.ip_forward + replace: '#net.ipv4.ip_forward' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-81024-2 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-5 + - NIST-800-53-SC-7(a) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_ip_forward + +- name: Ensure sysctl net.ipv4.ip_forward is set to 0 + sysctl: + name: net.ipv4.ip_forward + value: '0' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-81024-2 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-5 + - NIST-800-53-SC-7(a) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_ip_forward + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.ipv4.ip_forward from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.ipv4.ip_forward.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.ipv4.ip_forward" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done + +# +# Set runtime for net.ipv4.ip_forward +# +/sbin/sysctl -q -n -w net.ipv4.ip_forward="0" + +# +# If net.ipv4.ip_forward present in /etc/sysctl.conf, change value to "0" +# else, add "net.ipv4.ip_forward = 0" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv4.ip_forward") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "0" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.ipv4.ip_forward\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.ipv4.ip_forward\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-81024-2" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + + SuSEfirewall2 + The SuSEfirewall2 provides a managed firewall. + + + Uncomplicated Firewall (ufw) + The Linux kernel in Ubuntu provides a packet filtering system called +netfilter, and the traditional interface for manipulating netfilter are +the iptables suite of commands. iptables provide a complete firewall +solution that is both highly configurable and highly flexible. + +Becoming proficient in iptables takes time, and getting started with +netfilter firewalling using only iptables can be a daunting task. As a +result, many frontends for iptables have been created over the years, +each trying to achieve a different result and targeting a different +audience. + +The Uncomplicated Firewall (ufw) is a frontend for iptables and is +particularly well-suited for host-based firewalls. ufw provides a +framework for managing netfilter, as well as a command-line interface +for manipulating the firewall. ufw aims to provide an easy to use +interface for people unfamiliar with firewall concepts, while at the +same time simplifies complicated iptables commands to help an +administrator who knows what he or she is doing. ufw is an upstream +for other distributions and graphical frontends. + + + Verify ufw Enabled + +The ufw service can be enabled with the following command: +$ sudo systemctl enable ufw.service + CCI-002314 + SRG-OS-000297-GPOS-00115 + The ufw service must be enabled and running in order for ufw to protect the system + + include enable_ufw + +class enable_ufw { + service {'ufw': + enable => true, + ensure => 'running', + } +} + + - name: Enable service ufw + block: + + - name: Gather the package facts + package_facts: + manager: auto + + - name: Enable service ufw + service: + name: ufw + enabled: 'yes' + state: started + masked: 'no' + when: + - '"ufw" in ansible_facts.packages' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_ufw_enabled + + +[customizations.services] +enabled = ["ufw"] + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" unmask 'ufw.service' +"$SYSTEMCTL_EXEC" start 'ufw.service' +"$SYSTEMCTL_EXEC" enable 'ufw.service' + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Uncommon Network Protocols + The system includes support for several network protocols which are not commonly used. +Although security vulnerabilities in kernel networking code are not frequently discovered, +the consequences can be dramatic. Ensuring uncommon network protocols are disabled +reduces the system's risk to attacks targeted at its implementation of those protocols. + Although these protocols are not commonly used, avoid disruption +in your network environment by ensuring they are not needed +prior to disabling them. + + + Disable ATM Support + The Asynchronous Transfer Mode (ATM) is a protocol operating on +network, data link, and physical layers, based on virtual circuits +and virtual paths. + +To configure the system to prevent the atm +kernel module from being loaded, add the following line to the file /etc/modprobe.d/atm.conf: +install atm /bin/true + +To configure the system to prevent the atm from being used, +add the following line to file /etc/modprobe.d/atm.conf: +blacklist atm + CCI-000381 + CCI-000366 + AC-18 + FMT_SMF_EXT.1 + SRG-OS-000095-GPOS-00049 + SRG-OS-000480-GPOS-00227 + RHEL-08-040021 + SV-230494r792911_rule + Disabling ATM protects the system against exploitation of any +flaws in its implementation. + CCE-82028-2 + - name: Ensure kernel module 'atm' is disabled + lineinfile: + create: true + dest: /etc/modprobe.d/atm.conf + regexp: atm + line: install atm /bin/true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82028-2 + - DISA-STIG-RHEL-08-040021 + - NIST-800-53-AC-18 + - disable_strategy + - kernel_module_atm_disabled + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + +- name: Ensure kernel module 'atm' is blacklisted + lineinfile: + create: true + dest: /etc/modprobe.d/atm.conf + regexp: ^blacklist atm$ + line: blacklist atm + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82028-2 + - DISA-STIG-RHEL-08-040021 + - NIST-800-53-AC-18 + - disable_strategy + - kernel_module_atm_disabled + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,install%20atm%20/bin/true%0Ablacklist%20atm%0A + mode: 0644 + path: /etc/modprobe.d/atm.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if LC_ALL=C grep -q -m 1 "^install atm" /etc/modprobe.d/atm.conf ; then + + sed -i 's#^install atm.*#install atm /bin/true#g' /etc/modprobe.d/atm.conf +else + echo -e "\n# Disable per security requirements" >> /etc/modprobe.d/atm.conf + echo "install atm /bin/true" >> /etc/modprobe.d/atm.conf +fi + +if ! LC_ALL=C grep -q -m 1 "^blacklist atm$" /etc/modprobe.d/atm.conf ; then + echo "blacklist atm" >> /etc/modprobe.d/atm.conf +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable CAN Support + The Controller Area Network (CAN) is a serial communications +protocol which was initially developed for automotive and +is now also used in marine, industrial, and medical applications. + +To configure the system to prevent the can +kernel module from being loaded, add the following line to the file /etc/modprobe.d/can.conf: +install can /bin/true + +To configure the system to prevent the can from being used, +add the following line to file /etc/modprobe.d/can.conf: +blacklist can + CCI-000381 + CCI-000366 + AC-18 + FMT_SMF_EXT.1 + SRG-OS-000095-GPOS-00049 + SRG-OS-000480-GPOS-00227 + RHEL-08-040022 + SV-230495r792914_rule + Disabling CAN protects the system against exploitation of any +flaws in its implementation. + CCE-82059-7 + - name: Ensure kernel module 'can' is disabled + lineinfile: + create: true + dest: /etc/modprobe.d/can.conf + regexp: can + line: install can /bin/true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82059-7 + - DISA-STIG-RHEL-08-040022 + - NIST-800-53-AC-18 + - disable_strategy + - kernel_module_can_disabled + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + +- name: Ensure kernel module 'can' is blacklisted + lineinfile: + create: true + dest: /etc/modprobe.d/can.conf + regexp: ^blacklist can$ + line: blacklist can + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82059-7 + - DISA-STIG-RHEL-08-040022 + - NIST-800-53-AC-18 + - disable_strategy + - kernel_module_can_disabled + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,install%20can%20/bin/true%0Ablacklist%20can%0A + mode: 0644 + path: /etc/modprobe.d/can.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if LC_ALL=C grep -q -m 1 "^install can" /etc/modprobe.d/can.conf ; then + + sed -i 's#^install can.*#install can /bin/true#g' /etc/modprobe.d/can.conf +else + echo -e "\n# Disable per security requirements" >> /etc/modprobe.d/can.conf + echo "install can /bin/true" >> /etc/modprobe.d/can.conf +fi + +if ! LC_ALL=C grep -q -m 1 "^blacklist can$" /etc/modprobe.d/can.conf ; then + echo "blacklist can" >> /etc/modprobe.d/can.conf +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable DCCP Support + The Datagram Congestion Control Protocol (DCCP) is a +relatively new transport layer protocol, designed to support +streaming media and telephony. + +To configure the system to prevent the dccp +kernel module from being loaded, add the following line to the file /etc/modprobe.d/dccp.conf: +install dccp /bin/true + +To configure the system to prevent the dccp from being used, +add the following line to file /etc/modprobe.d/dccp.conf: +blacklist dccp + 11 + 14 + 3 + 9 + 5.10.1 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + 3.4.6 + CCI-001958 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + PR.PT-3 + SRG-OS-000096-GPOS-00050 + SRG-OS-000378-GPOS-00163 + 3.1.3 + Disabling DCCP protects +the system against exploitation of any flaws in its implementation. + CCE-80833-7 + - name: Ensure kernel module 'dccp' is disabled + lineinfile: + create: true + dest: /etc/modprobe.d/dccp.conf + regexp: dccp + line: install dccp /bin/true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80833-7 + - CJIS-5.10.1 + - NIST-800-171-3.4.6 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - kernel_module_dccp_disabled + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + +- name: Ensure kernel module 'dccp' is blacklisted + lineinfile: + create: true + dest: /etc/modprobe.d/dccp.conf + regexp: ^blacklist dccp$ + line: blacklist dccp + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80833-7 + - CJIS-5.10.1 + - NIST-800-171-3.4.6 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - kernel_module_dccp_disabled + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,install%20dccp%20/bin/true%0Ablacklist%20dccp%0A + mode: 0644 + path: /etc/modprobe.d/dccp.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if LC_ALL=C grep -q -m 1 "^install dccp" /etc/modprobe.d/dccp.conf ; then + + sed -i 's#^install dccp.*#install dccp /bin/true#g' /etc/modprobe.d/dccp.conf +else + echo -e "\n# Disable per security requirements" >> /etc/modprobe.d/dccp.conf + echo "install dccp /bin/true" >> /etc/modprobe.d/dccp.conf +fi + +if ! LC_ALL=C grep -q -m 1 "^blacklist dccp$" /etc/modprobe.d/dccp.conf ; then + echo "blacklist dccp" >> /etc/modprobe.d/dccp.conf +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable IEEE 1394 (FireWire) Support + The IEEE 1394 (FireWire) is a serial bus standard for +high-speed real-time communication. + +To configure the system to prevent the firewire-core +kernel module from being loaded, add the following line to the file /etc/modprobe.d/firewire-core.conf: +install firewire-core /bin/true + +To configure the system to prevent the firewire-core from being used, +add the following line to file /etc/modprobe.d/firewire-core.conf: +blacklist firewire-core + CCI-000381 + AC-18 + FMT_SMF_EXT.1 + SRG-OS-000095-GPOS-00049 + RHEL-08-040026 + SV-230499r792924_rule + Disabling FireWire protects the system against exploitation of any +flaws in its implementation. + CCE-82005-0 + - name: Ensure kernel module 'firewire-core' is disabled + lineinfile: + create: true + dest: /etc/modprobe.d/firewire-core.conf + regexp: firewire-core + line: install firewire-core /bin/true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82005-0 + - DISA-STIG-RHEL-08-040026 + - NIST-800-53-AC-18 + - disable_strategy + - kernel_module_firewire-core_disabled + - low_complexity + - low_severity + - medium_disruption + - reboot_required + +- name: Ensure kernel module 'firewire-core' is blacklisted + lineinfile: + create: true + dest: /etc/modprobe.d/firewire-core.conf + regexp: ^blacklist firewire-core$ + line: blacklist firewire-core + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82005-0 + - DISA-STIG-RHEL-08-040026 + - NIST-800-53-AC-18 + - disable_strategy + - kernel_module_firewire-core_disabled + - low_complexity + - low_severity + - medium_disruption + - reboot_required + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,install%20firewire-core%20/bin/true%0Ablacklist%20firewire-core%0A + mode: 0644 + path: /etc/modprobe.d/firewire-core.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if LC_ALL=C grep -q -m 1 "^install firewire-core" /etc/modprobe.d/firewire-core.conf ; then + + sed -i 's#^install firewire-core.*#install firewire-core /bin/true#g' /etc/modprobe.d/firewire-core.conf +else + echo -e "\n# Disable per security requirements" >> /etc/modprobe.d/firewire-core.conf + echo "install firewire-core /bin/true" >> /etc/modprobe.d/firewire-core.conf +fi + +if ! LC_ALL=C grep -q -m 1 "^blacklist firewire-core$" /etc/modprobe.d/firewire-core.conf ; then + echo "blacklist firewire-core" >> /etc/modprobe.d/firewire-core.conf +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable RDS Support + The Reliable Datagram Sockets (RDS) protocol is a transport +layer protocol designed to provide reliable high-bandwidth, +low-latency communications between nodes in a cluster. + +To configure the system to prevent the rds +kernel module from being loaded, add the following line to the file /etc/modprobe.d/rds.conf: +install rds /bin/true + +To configure the system to prevent the rds from being used, +add the following line to file /etc/modprobe.d/rds.conf: +blacklist rds + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + PR.PT-3 + Disabling RDS protects +the system against exploitation of any flaws in its implementation. + CCE-82870-7 + - name: Ensure kernel module 'rds' is disabled + lineinfile: + create: true + dest: /etc/modprobe.d/rds.conf + regexp: rds + line: install rds /bin/true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82870-7 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - kernel_module_rds_disabled + - low_complexity + - low_severity + - medium_disruption + - reboot_required + +- name: Ensure kernel module 'rds' is blacklisted + lineinfile: + create: true + dest: /etc/modprobe.d/rds.conf + regexp: ^blacklist rds$ + line: blacklist rds + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82870-7 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - kernel_module_rds_disabled + - low_complexity + - low_severity + - medium_disruption + - reboot_required + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,install%20rds%20/bin/true%0Ablacklist%20rds%0A + mode: 0644 + path: /etc/modprobe.d/rds.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if LC_ALL=C grep -q -m 1 "^install rds" /etc/modprobe.d/rds.conf ; then + + sed -i 's#^install rds.*#install rds /bin/true#g' /etc/modprobe.d/rds.conf +else + echo -e "\n# Disable per security requirements" >> /etc/modprobe.d/rds.conf + echo "install rds /bin/true" >> /etc/modprobe.d/rds.conf +fi + +if ! LC_ALL=C grep -q -m 1 "^blacklist rds$" /etc/modprobe.d/rds.conf ; then + echo "blacklist rds" >> /etc/modprobe.d/rds.conf +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable SCTP Support + The Stream Control Transmission Protocol (SCTP) is a +transport layer protocol, designed to support the idea of +message-oriented communication, with several streams of messages +within one connection. + +To configure the system to prevent the sctp +kernel module from being loaded, add the following line to the file /etc/modprobe.d/sctp.conf: +install sctp /bin/true + +To configure the system to prevent the sctp from being used, +add the following line to file /etc/modprobe.d/sctp.conf: +blacklist sctp + 11 + 14 + 3 + 9 + 5.10.1 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + 3.4.6 + CCI-000381 + CCI-000366 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + PR.PT-3 + SRG-OS-000095-GPOS-00049 + SRG-OS-000480-GPOS-00227 + RHEL-08-040023 + 3.1.2 + SV-230496r792917_rule + Disabling SCTP protects +the system against exploitation of any flaws in its implementation. + CCE-80834-5 + - name: Ensure kernel module 'sctp' is disabled + lineinfile: + create: true + dest: /etc/modprobe.d/sctp.conf + regexp: sctp + line: install sctp /bin/true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80834-5 + - CJIS-5.10.1 + - DISA-STIG-RHEL-08-040023 + - NIST-800-171-3.4.6 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - kernel_module_sctp_disabled + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + +- name: Ensure kernel module 'sctp' is blacklisted + lineinfile: + create: true + dest: /etc/modprobe.d/sctp.conf + regexp: ^blacklist sctp$ + line: blacklist sctp + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80834-5 + - CJIS-5.10.1 + - DISA-STIG-RHEL-08-040023 + - NIST-800-171-3.4.6 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - kernel_module_sctp_disabled + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,install%20sctp%20/bin/true%0Ablacklist%20sctp%0A + mode: 0644 + path: /etc/modprobe.d/sctp.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if LC_ALL=C grep -q -m 1 "^install sctp" /etc/modprobe.d/sctp.conf ; then + + sed -i 's#^install sctp.*#install sctp /bin/true#g' /etc/modprobe.d/sctp.conf +else + echo -e "\n# Disable per security requirements" >> /etc/modprobe.d/sctp.conf + echo "install sctp /bin/true" >> /etc/modprobe.d/sctp.conf +fi + +if ! LC_ALL=C grep -q -m 1 "^blacklist sctp$" /etc/modprobe.d/sctp.conf ; then + echo "blacklist sctp" >> /etc/modprobe.d/sctp.conf +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable TIPC Support + The Transparent Inter-Process Communication (TIPC) protocol +is designed to provide communications between nodes in a +cluster. + +To configure the system to prevent the tipc +kernel module from being loaded, add the following line to the file /etc/modprobe.d/tipc.conf: +install tipc /bin/true + +To configure the system to prevent the tipc from being used, +add the following line to file /etc/modprobe.d/tipc.conf: +blacklist tipc + This configuration baseline was created to deploy the base operating system for general purpose +workloads. When the operating system is configured for certain purposes, such as +a node in High Performance Computing cluster, it is expected that +the tipc kernel module will be loaded. + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + CCI-000381 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + PR.PT-3 + FMT_SMF_EXT.1 + SRG-OS-000095-GPOS-00049 + RHEL-08-040024 + SV-230497r792920_rule + Disabling TIPC protects +the system against exploitation of any flaws in its implementation. + CCE-82297-3 + - name: Ensure kernel module 'tipc' is disabled + lineinfile: + create: true + dest: /etc/modprobe.d/tipc.conf + regexp: tipc + line: install tipc /bin/true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82297-3 + - DISA-STIG-RHEL-08-040024 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - kernel_module_tipc_disabled + - low_complexity + - low_severity + - medium_disruption + - reboot_required + +- name: Ensure kernel module 'tipc' is blacklisted + lineinfile: + create: true + dest: /etc/modprobe.d/tipc.conf + regexp: ^blacklist tipc$ + line: blacklist tipc + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82297-3 + - DISA-STIG-RHEL-08-040024 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - kernel_module_tipc_disabled + - low_complexity + - low_severity + - medium_disruption + - reboot_required + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,install%20tipc%20/bin/true%0Ablacklist%20tipc%0A + mode: 0644 + path: /etc/modprobe.d/tipc.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if LC_ALL=C grep -q -m 1 "^install tipc" /etc/modprobe.d/tipc.conf ; then + + sed -i 's#^install tipc.*#install tipc /bin/true#g' /etc/modprobe.d/tipc.conf +else + echo -e "\n# Disable per security requirements" >> /etc/modprobe.d/tipc.conf + echo "install tipc /bin/true" >> /etc/modprobe.d/tipc.conf +fi + +if ! LC_ALL=C grep -q -m 1 "^blacklist tipc$" /etc/modprobe.d/tipc.conf ; then + echo "blacklist tipc" >> /etc/modprobe.d/tipc.conf +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Wireless Networking + Wireless networking, such as 802.11 +(WiFi) and Bluetooth, can present a security risk to sensitive or +classified systems and networks. Wireless networking hardware is +much more likely to be included in laptop or portable systems than +in desktops or servers. + +Removal of hardware provides the greatest assurance that the wireless +capability remains disabled. Acquisition policies often include provisions to +prevent the purchase of equipment that will be used in sensitive spaces and +includes wireless capabilities. If it is impractical to remove the wireless +hardware, and policy permits the device to enter sensitive spaces as long +as wireless is disabled, efforts should instead focus on disabling wireless capability +via software. + + Disable Wireless Through Software Configuration + If it is impossible to remove the wireless hardware +from the device in question, disable as much of it as possible +through software. The following methods can disable software +support for wireless networking, but note that these methods do not +prevent malicious software or careless users from re-activating the +devices. + + Disable Bluetooth Service + +The bluetooth service can be disabled with the following command: +$ sudo systemctl mask --now bluetooth.service +$ sudo service bluetooth stop + 11 + 12 + 14 + 15 + 3 + 8 + 9 + APO13.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.04 + DSS05.02 + DSS05.03 + DSS05.05 + DSS06.06 + 3.1.16 + CCI-000085 + CCI-001551 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.6.2.1 + A.6.2.2 + A.9.1.2 + AC-18(a) + AC-18(3) + CM-7(a) + CM-7(b) + CM-6(a) + MP-7 + PR.AC-3 + PR.IP-1 + PR.PT-3 + PR.PT-4 + Disabling the bluetooth service prevents the system from attempting +connections to Bluetooth devices, which entails some security risk. +Nevertheless, variation in this risk decision may be expected due to the +utility of Bluetooth connectivity and its limited range. + + include disable_bluetooth + +class disable_bluetooth { + service {'bluetooth': + enable => false, + ensure => 'stopped', + } +} + + - name: Disable service bluetooth + block: + + - name: Disable service bluetooth + systemd: + name: bluetooth.service + enabled: 'no' + state: stopped + masked: 'yes' + ignore_errors: 'yes' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-171-3.1.16 + - NIST-800-53-AC-18(3) + - NIST-800-53-AC-18(a) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_bluetooth_disabled + +- name: Unit Socket Exists - bluetooth.socket + command: systemctl list-unit-files bluetooth.socket + args: + warn: false + register: socket_file_exists + changed_when: false + ignore_errors: true + check_mode: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-171-3.1.16 + - NIST-800-53-AC-18(3) + - NIST-800-53-AC-18(a) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_bluetooth_disabled + +- name: Disable socket bluetooth + systemd: + name: bluetooth.socket + enabled: 'no' + state: stopped + masked: 'yes' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"bluetooth.socket" in socket_file_exists.stdout_lines[1]' + tags: + - NIST-800-171-3.1.16 + - NIST-800-53-AC-18(3) + - NIST-800-53-AC-18(a) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_bluetooth_disabled + + +[customizations.services] +disabled = ["bluetooth"] + + apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + systemd: + units: + - name: bluetooth.service + enabled: false + mask: true + - name: bluetooth.socket + enabled: false + mask: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" stop 'bluetooth.service' +"$SYSTEMCTL_EXEC" disable 'bluetooth.service' +"$SYSTEMCTL_EXEC" mask 'bluetooth.service' +# Disable socket activation if we have a unit file for it +if "$SYSTEMCTL_EXEC" list-unit-files | grep -q '^bluetooth.socket'; then + "$SYSTEMCTL_EXEC" stop 'bluetooth.socket' + "$SYSTEMCTL_EXEC" mask 'bluetooth.socket' +fi +# The service may not be running because it has been started and failed, +# so let's reset the state so OVAL checks pass. +# Service should be 'inactive', not 'failed' after reboot though. +"$SYSTEMCTL_EXEC" reset-failed 'bluetooth.service' || true + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable Bluetooth Kernel Module + The kernel's module loading system can be configured to prevent +loading of the Bluetooth module. Add the following to +the appropriate /etc/modprobe.d configuration file +to prevent the loading of the Bluetooth module: +install bluetooth /bin/true + 11 + 12 + 14 + 15 + 3 + 8 + 9 + 5.13.1.3 + APO13.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.04 + DSS05.02 + DSS05.03 + DSS05.05 + DSS06.06 + 3.1.16 + CCI-000085 + CCI-001443 + CCI-001444 + CCI-001551 + CCI-002418 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.6.2.1 + A.6.2.2 + A.9.1.2 + AC-18(a) + AC-18(3) + CM-7(a) + CM-7(b) + CM-6(a) + MP-7 + PR.AC-3 + PR.IP-1 + PR.PT-3 + PR.PT-4 + SRG-OS-000095-GPOS-00049 + SRG-OS-000300-GPOS-00118 + RHEL-08-040111 + SV-230507r833336_rule + If Bluetooth functionality must be disabled, preventing the kernel +from loading the kernel module provides an additional safeguard against its +activation. + + CCE-80832-9 + - name: Ensure kernel module 'bluetooth' is disabled + lineinfile: + create: true + dest: /etc/modprobe.d/bluetooth.conf + regexp: bluetooth + line: install bluetooth /bin/true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80832-9 + - CJIS-5.13.1.3 + - DISA-STIG-RHEL-08-040111 + - NIST-800-171-3.1.16 + - NIST-800-53-AC-18(3) + - NIST-800-53-AC-18(a) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - disable_strategy + - kernel_module_bluetooth_disabled + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + +- name: Ensure kernel module 'bluetooth' is blacklisted + lineinfile: + create: true + dest: /etc/modprobe.d/bluetooth.conf + regexp: ^blacklist bluetooth$ + line: blacklist bluetooth + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80832-9 + - CJIS-5.13.1.3 + - DISA-STIG-RHEL-08-040111 + - NIST-800-171-3.1.16 + - NIST-800-53-AC-18(3) + - NIST-800-53-AC-18(a) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - disable_strategy + - kernel_module_bluetooth_disabled + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,install%20bluetooth%20/bin/true%0Ablacklist%20bluetooth%0A + mode: 0644 + path: /etc/modprobe.d/bluetooth.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if LC_ALL=C grep -q -m 1 "^install bluetooth" /etc/modprobe.d/bluetooth.conf ; then + + sed -i 's#^install bluetooth.*#install bluetooth /bin/true#g' /etc/modprobe.d/bluetooth.conf +else + echo -e "\n# Disable per security requirements" >> /etc/modprobe.d/bluetooth.conf + echo "install bluetooth /bin/true" >> /etc/modprobe.d/bluetooth.conf +fi + +if ! LC_ALL=C grep -q -m 1 "^blacklist bluetooth$" /etc/modprobe.d/bluetooth.conf ; then + echo "blacklist bluetooth" >> /etc/modprobe.d/bluetooth.conf +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable Kernel cfg80211 Module + +To configure the system to prevent the cfg80211 +kernel module from being loaded, add the following line to the file /etc/modprobe.d/cfg80211.conf: +install cfg80211 /bin/true + +To configure the system to prevent the cfg80211 from being used, +add the following line to file /etc/modprobe.d/cfg80211.conf: +blacklist cfg80211 + AC-18(a) + AC-18(3) + CM-7(a) + CM-7(b) + CM-6(a) + MP-7 + AC-18(4) + If Wireless functionality must be disabled, preventing the kernel +from loading the kernel module provides an additional safeguard against its +activation. + + - name: Ensure kernel module 'cfg80211' is disabled + lineinfile: + create: true + dest: /etc/modprobe.d/cfg80211.conf + regexp: cfg80211 + line: install cfg80211 /bin/true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AC-18(3) + - NIST-800-53-AC-18(4) + - NIST-800-53-AC-18(a) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - disable_strategy + - kernel_module_cfg80211_disabled + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + +- name: Ensure kernel module 'cfg80211' is blacklisted + lineinfile: + create: true + dest: /etc/modprobe.d/cfg80211.conf + regexp: ^blacklist cfg80211$ + line: blacklist cfg80211 + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AC-18(3) + - NIST-800-53-AC-18(4) + - NIST-800-53-AC-18(a) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - disable_strategy + - kernel_module_cfg80211_disabled + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,install%20cfg80211%20/bin/true%0Ablacklist%20cfg80211%0A + mode: 0644 + path: /etc/modprobe.d/cfg80211.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if LC_ALL=C grep -q -m 1 "^install cfg80211" /etc/modprobe.d/cfg80211.conf ; then + + sed -i 's#^install cfg80211.*#install cfg80211 /bin/true#g' /etc/modprobe.d/cfg80211.conf +else + echo -e "\n# Disable per security requirements" >> /etc/modprobe.d/cfg80211.conf + echo "install cfg80211 /bin/true" >> /etc/modprobe.d/cfg80211.conf +fi + +if ! LC_ALL=C grep -q -m 1 "^blacklist cfg80211$" /etc/modprobe.d/cfg80211.conf ; then + echo "blacklist cfg80211" >> /etc/modprobe.d/cfg80211.conf +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable Kernel iwlmvm Module + +To configure the system to prevent the iwlmvm +kernel module from being loaded, add the following line to the file /etc/modprobe.d/iwlmvm.conf: +install iwlmvm /bin/true + +To configure the system to prevent the iwlmvm from being used, +add the following line to file /etc/modprobe.d/iwlmvm.conf: +blacklist iwlmvm + AC-18(a) + AC-18(3) + CM-7(a) + CM-7(b) + CM-6(a) + MP-7 + AC-18(4) + If Wireless functionality must be disabled, preventing the kernel +from loading the kernel module provides an additional safeguard against its +activation. + + - name: Ensure kernel module 'iwlmvm' is disabled + lineinfile: + create: true + dest: /etc/modprobe.d/iwlmvm.conf + regexp: iwlmvm + line: install iwlmvm /bin/true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AC-18(3) + - NIST-800-53-AC-18(4) + - NIST-800-53-AC-18(a) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - disable_strategy + - kernel_module_iwlmvm_disabled + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + +- name: Ensure kernel module 'iwlmvm' is blacklisted + lineinfile: + create: true + dest: /etc/modprobe.d/iwlmvm.conf + regexp: ^blacklist iwlmvm$ + line: blacklist iwlmvm + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AC-18(3) + - NIST-800-53-AC-18(4) + - NIST-800-53-AC-18(a) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - disable_strategy + - kernel_module_iwlmvm_disabled + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,install%20iwlmvm%20/bin/true%0Ablacklist%20iwlmvm%0A + mode: 0644 + path: /etc/modprobe.d/iwlmvm.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if LC_ALL=C grep -q -m 1 "^install iwlmvm" /etc/modprobe.d/iwlmvm.conf ; then + + sed -i 's#^install iwlmvm.*#install iwlmvm /bin/true#g' /etc/modprobe.d/iwlmvm.conf +else + echo -e "\n# Disable per security requirements" >> /etc/modprobe.d/iwlmvm.conf + echo "install iwlmvm /bin/true" >> /etc/modprobe.d/iwlmvm.conf +fi + +if ! LC_ALL=C grep -q -m 1 "^blacklist iwlmvm$" /etc/modprobe.d/iwlmvm.conf ; then + echo "blacklist iwlmvm" >> /etc/modprobe.d/iwlmvm.conf +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable Kernel iwlwifi Module + +To configure the system to prevent the iwlwifi +kernel module from being loaded, add the following line to the file /etc/modprobe.d/iwlwifi.conf: +install iwlwifi /bin/true + +To configure the system to prevent the iwlwifi from being used, +add the following line to file /etc/modprobe.d/iwlwifi.conf: +blacklist iwlwifi + AC-18(a) + AC-18(3) + CM-7(a) + CM-7(b) + CM-6(a) + MP-7 + AC-18(4) + If Wireless functionality must be disabled, preventing the kernel +from loading the kernel module provides an additional safeguard against its +activation. + + - name: Ensure kernel module 'iwlwifi' is disabled + lineinfile: + create: true + dest: /etc/modprobe.d/iwlwifi.conf + regexp: iwlwifi + line: install iwlwifi /bin/true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AC-18(3) + - NIST-800-53-AC-18(4) + - NIST-800-53-AC-18(a) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - disable_strategy + - kernel_module_iwlwifi_disabled + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + +- name: Ensure kernel module 'iwlwifi' is blacklisted + lineinfile: + create: true + dest: /etc/modprobe.d/iwlwifi.conf + regexp: ^blacklist iwlwifi$ + line: blacklist iwlwifi + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AC-18(3) + - NIST-800-53-AC-18(4) + - NIST-800-53-AC-18(a) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - disable_strategy + - kernel_module_iwlwifi_disabled + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,install%20iwlwifi%20/bin/true%0Ablacklist%20iwlwifi%0A + mode: 0644 + path: /etc/modprobe.d/iwlwifi.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if LC_ALL=C grep -q -m 1 "^install iwlwifi" /etc/modprobe.d/iwlwifi.conf ; then + + sed -i 's#^install iwlwifi.*#install iwlwifi /bin/true#g' /etc/modprobe.d/iwlwifi.conf +else + echo -e "\n# Disable per security requirements" >> /etc/modprobe.d/iwlwifi.conf + echo "install iwlwifi /bin/true" >> /etc/modprobe.d/iwlwifi.conf +fi + +if ! LC_ALL=C grep -q -m 1 "^blacklist iwlwifi$" /etc/modprobe.d/iwlwifi.conf ; then + echo "blacklist iwlwifi" >> /etc/modprobe.d/iwlwifi.conf +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable Kernel mac80211 Module + +To configure the system to prevent the mac80211 +kernel module from being loaded, add the following line to the file /etc/modprobe.d/mac80211.conf: +install mac80211 /bin/true + +To configure the system to prevent the mac80211 from being used, +add the following line to file /etc/modprobe.d/mac80211.conf: +blacklist mac80211 + AC-18(a) + AC-18(3) + CM-7(a) + CM-7(b) + CM-6(a) + MP-7 + AC-18(4) + If Wireless functionality must be disabled, preventing the kernel +from loading the kernel module provides an additional safeguard against its +activation. + + - name: Ensure kernel module 'mac80211' is disabled + lineinfile: + create: true + dest: /etc/modprobe.d/mac80211.conf + regexp: mac80211 + line: install mac80211 /bin/true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AC-18(3) + - NIST-800-53-AC-18(4) + - NIST-800-53-AC-18(a) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - disable_strategy + - kernel_module_mac80211_disabled + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + +- name: Ensure kernel module 'mac80211' is blacklisted + lineinfile: + create: true + dest: /etc/modprobe.d/mac80211.conf + regexp: ^blacklist mac80211$ + line: blacklist mac80211 + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AC-18(3) + - NIST-800-53-AC-18(4) + - NIST-800-53-AC-18(a) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - disable_strategy + - kernel_module_mac80211_disabled + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,install%20mac80211%20/bin/true%0Ablacklist%20mac80211%0A + mode: 0644 + path: /etc/modprobe.d/mac80211.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if LC_ALL=C grep -q -m 1 "^install mac80211" /etc/modprobe.d/mac80211.conf ; then + + sed -i 's#^install mac80211.*#install mac80211 /bin/true#g' /etc/modprobe.d/mac80211.conf +else + echo -e "\n# Disable per security requirements" >> /etc/modprobe.d/mac80211.conf + echo "install mac80211 /bin/true" >> /etc/modprobe.d/mac80211.conf +fi + +if ! LC_ALL=C grep -q -m 1 "^blacklist mac80211$" /etc/modprobe.d/mac80211.conf ; then + echo "blacklist mac80211" >> /etc/modprobe.d/mac80211.conf +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable WiFi or Bluetooth in BIOS + Some machines that include built-in wireless support offer the +ability to disable the device through the BIOS. This is hardware-specific; +consult your hardware manual or explore the BIOS setup during +boot. + 11 + 12 + 14 + 15 + 3 + 8 + 9 + APO13.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.04 + DSS05.02 + DSS05.03 + DSS05.05 + DSS06.06 + CCI-000085 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.6.2.1 + A.6.2.2 + A.9.1.2 + AC-18(a) + AC-18(3) + CM-7(a) + CM-7(b) + CM-6(a) + MP-7 + PR.AC-3 + PR.IP-1 + PR.PT-3 + PR.PT-4 + Disabling wireless support in the BIOS prevents easy +activation of the wireless interface, generally requiring administrators +to reboot the system first. + + + + Deactivate Wireless Network Interfaces + Deactivating wireless network interfaces should prevent normal usage of the wireless +capability. + + +Configure the system to disable all wireless network interfaces with the following command: +$ sudo nmcli radio all off + 11 + 12 + 14 + 15 + 3 + 8 + 9 + APO13.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.04 + DSS05.02 + DSS05.03 + DSS05.05 + DSS06.06 + 3.1.16 + CCI-000085 + CCI-002418 + CCI-002421 + CCI-001443 + CCI-001444 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + 1315 + 1319 + A.11.2.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.6.2.1 + A.6.2.2 + A.9.1.2 + AC-18(a) + AC-18(3) + CM-7(a) + CM-7(b) + CM-6(a) + MP-7 + PR.AC-3 + PR.IP-1 + PR.PT-3 + PR.PT-4 + SRG-OS-000299-GPOS-00117 + SRG-OS-000300-GPOS-00118 + SRG-OS-000424-GPOS-00188 + SRG-OS-000481-GPOS-000481 + RHEL-08-040110 + 3.1.4 + SV-230506r627750_rule + The use of wireless networking can introduce many different attack vectors into +the organization's network. Common attack vectors such as malicious association +and ad hoc networks will allow an attacker to spoof a wireless access point +(AP), allowing validated systems to connect to the malicious AP and enabling the +attacker to monitor and record network traffic. These malicious APs can also +serve to create a man-in-the-middle attack or be used to create a denial of +service to valid network resources. + + CCE-83501-7 + - name: Deactivate Wireless Network Interfaces + command: nmcli radio wifi off + tags: + - CCE-83501-7 + - DISA-STIG-RHEL-08-040110 + - NIST-800-171-3.1.16 + - NIST-800-53-AC-18(3) + - NIST-800-53-AC-18(a) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + - wireless_disable_interfaces + + +nmcli radio all off + + + + + + + + + + + + Disable Unused Interfaces + Network interfaces expand the attack surface of the +system. Unused interfaces are not monitored or controlled, and +should be disabled. + +If the system does not require network communications but still +needs to use the loopback interface, remove all files of the form +ifcfg-interface except for ifcfg-lo from +/etc/sysconfig/network-scripts: +$ sudo rm /etc/sysconfig/network-scripts/ifcfg-interface +If the system is a standalone machine with no need for network access or even +communication over the loopback device, then disable this service. + +The network service can be disabled with the following command: +$ sudo systemctl mask --now network.service + + + Transport Layer Security Support + Support for Transport Layer Security (TLS), and its predecessor, the Secure +Sockets Layer (SSL), is included in Red Hat Enterprise Linux in the OpenSSL software (RPM package +openssl). TLS provides encrypted and authenticated network +communications, and many network services include support for it. TLS or SSL +can be leveraged to avoid any plaintext transmission of sensitive data. + +For information on how to use OpenSSL, see +http://www.openssl.org/docs/. Information on FIPS validation +of OpenSSL is available at http://www.openssl.org/docs/fips.html +and http://csrc.nist.gov/groups/STM/cmvp/documents/140-1/140val-all.htm. + + + + File Permissions and Masks + Traditional Unix security relies heavily on file and +directory permissions to prevent unauthorized users from reading or +modifying files to which they should not have access. + +Several of the commands in this section search filesystems +for files or directories with certain characteristics, and are +intended to be run on every local partition on a given system. +When the variable PART appears in one of the commands below, +it means that the command is intended to be run repeatedly, with the +name of each local partition substituted for PART in turn. + +The following command prints a list of all xfs partitions on the local +system, which is the default filesystem for Red Hat Enterprise Linux 8 +installations: +$ mount -t xfs | awk '{print $3}' +For any systems that use a different +local filesystem type, modify this command as appropriate. + + Verify Permissions on Important Files and +Directories + Permissions for many files on a system must be set +restrictively to ensure sensitive information is properly protected. +This section discusses important +permission restrictions which can be verified +to ensure that no harmful discrepancies have +arisen. + + Ensure All World-Writable Directories Are Owned by root user + All directories in local partitions which are world-writable should be owned +by root. If any world-writable directories are not owned by root, this +should be investigated. Following this, the files should be deleted or +assigned to root user. + BP28(R40) + CCI-000366 + SRG-OS-000480-GPOS-00227 + SRG-OS-000138-GPOS-00069 + RHEL-08-010700 + SV-230318r743960_rule + Allowing a user account to own a world-writable directory is +undesirable because it allows the owner of that directory to remove +or replace any files that may be placed in the directory by other +users. + CCE-83375-6 + - name: Configure excluded (non local) file systems + set_fact: + excluded_fstypes: + - afs + - ceph + - cifs + - smb3 + - smbfs + - sshfs + - ncpfs + - ncp + - nfs + - nfs4 + - gfs + - gfs2 + - glusterfs + - gpfs + - pvfs2 + - ocfs2 + - lustre + - davfs + - fuse.sshfs + tags: + - CCE-83375-6 + - DISA-STIG-RHEL-08-010700 + - dir_perms_world_writable_root_owned + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Create empty list of excluded paths + set_fact: + excluded_paths: [] + tags: + - CCE-83375-6 + - DISA-STIG-RHEL-08-010700 + - dir_perms_world_writable_root_owned + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Detect nonlocal file systems and add them to excluded paths + set_fact: + excluded_paths: '{{ excluded_paths | union([item.mount]) }}' + loop: '{{ ansible_mounts }}' + when: item.fstype in excluded_fstypes + tags: + - CCE-83375-6 + - DISA-STIG-RHEL-08-010700 + - dir_perms_world_writable_root_owned + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Find all directories excluding non-local partitions + find: + paths: / + excludes: excluded_paths + file_type: directory + hidden: true + recurse: true + register: found_dirs + tags: + - CCE-83375-6 + - DISA-STIG-RHEL-08-010700 + - dir_perms_world_writable_root_owned + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Create list of world writable directories + set_fact: + world_writable_dirs: '{{ found_dirs.files | selectattr(''woth'') | list }}' + tags: + - CCE-83375-6 + - DISA-STIG-RHEL-08-010700 + - dir_perms_world_writable_root_owned + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Change owner to root on directories which are world writable + file: + path: '{{ item.path }}' + owner: root + loop: '{{ world_writable_dirs }}' + ignore_errors: true + tags: + - CCE-83375-6 + - DISA-STIG-RHEL-08-010700 + - dir_perms_world_writable_root_owned + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + +find / -not -fstype afs -not -fstype ceph -not -fstype cifs -not -fstype smb3 -not -fstype smbfs -not -fstype sshfs -not -fstype ncpfs -not -fstype ncp -not -fstype nfs -not -fstype nfs4 -not -fstype gfs -not -fstype gfs2 -not -fstype glusterfs -not -fstype gpfs -not -fstype pvfs2 -not -fstype ocfs2 -not -fstype lustre -not -fstype davfs -not -fstype fuse.sshfs -type d -perm -0002 -uid +0 -exec chown root {} \; + + + + + + + + + + Verify that All World-Writable Directories Have Sticky Bits Set + When the so-called 'sticky bit' is set on a directory, +only the owner of a given file may remove that file from the +directory. Without the sticky bit, any user with write access to a +directory may remove any file in the directory. Setting the sticky +bit prevents users from removing each other's files. In cases where +there is no reason for a directory to be world-writable, a better +solution is to remove that permission rather than to set the sticky +bit. However, if a directory is used by a particular application, +consult that application's documentation instead of blindly +changing modes. + +To set the sticky bit on a world-writable directory DIR, run the +following command: +$ sudo chmod +t DIR + BP28(R40) + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + CCI-001090 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + SRG-OS-000138-GPOS-00069 + RHEL-08-010190 + 6.1.2 + SV-230243r792857_rule + Failing to set the sticky bit on public directories allows unauthorized +users to delete files in the directory structure. + +The only authorized public directories are those temporary directories +supplied with the system, or those designed to be temporary file +repositories. The setting is normally reserved for directories used by the +system, by users for temporary file storage (such as /tmp), and +for directories requiring global read/write access. + CCE-80783-4 + - name: Get all world-writable directories with no sticky bits set + shell: | + set -o pipefail + df --local -P | awk '{if (NR!=1) print $6}' | xargs -I '{}' find '{}' -xdev -type d \( -perm -0002 -a ! -perm -1000 \) 2>/dev/null + register: dir_output + tags: + - CCE-80783-4 + - DISA-STIG-RHEL-08-010190 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - dir_perms_world_writable_sticky_bits + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: ensure sticky bit is set + file: + path: '{{ item }}' + mode: a+t + with_items: + - '{{ dir_output.stdout_lines }}' + tags: + - CCE-80783-4 + - DISA-STIG-RHEL-08-010190 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - dir_perms_world_writable_sticky_bits + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + df --local -P | awk '{if (NR!=1) print $6}' \ +| xargs -I '{}' find '{}' -xdev -type d \ +\( -perm -0002 -a ! -perm -1000 \) 2>/dev/null \ +| xargs chmod a+t + + + + + + + + + + Ensure All World-Writable Directories Are Owned by a System Account + All directories in local partitions which are +world-writable should be owned by root or another +system account. If any world-writable directories are not +owned by a system account, this should be investigated. +Following this, the files should be deleted or assigned to an +appropriate owner. + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + CCI-000366 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + SRG-OS-000480-GPOS-00227 + Allowing a user account to own a world-writable directory is +undesirable because it allows the owner of that directory to remove +or replace any files that may be placed in the directory by other +users. + + + + + + + + + Ensure All World-Writable Directories Are Group Owned by a System Account + All directories in local partitions which are +world-writable should be group owned by root or another +system account. If any world-writable directories are not +group owned by a system account, this should be investigated. +Following this, the files should be deleted or assigned to an +appropriate group. + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + CCI-000366 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + SRG-OS-000480-GPOS-00227 + RHEL-08-010710 + SV-230319r743961_rule + Allowing a user account to group own a world-writable directory is +undesirable because it allows the owner of that directory to remove +or replace any files that may be placed in the directory by other +users. + CCE-85886-0 + + + + + + + + + Verify Permissions on /etc/audit/auditd.conf + +To properly set the permissions of /etc/audit/auditd.conf, run the command: +$ sudo chmod 0640 /etc/audit/auditd.conf + CCI-000171 + AU-12(b) + SRG-OS-000063-GPOS-00032 + RHEL-08-030610 + SV-230471r627750_rule + Without the capability to restrict the roles and individuals that can select which events +are audited, unauthorized personnel may be able to prevent the auditing of critical +events. Misconfigured audits may degrade the system's performance by overwhelming +the audit log. Misconfigured audits may also make it more difficult to establish, +correlate, and investigate the events relating to an incident or identify +those responsible for one. + CCE-85871-2 + - name: Test for existence /etc/audit/auditd.conf + stat: + path: /etc/audit/auditd.conf + register: file_exists + tags: + - CCE-85871-2 + - DISA-STIG-RHEL-08-030610 + - NIST-800-53-AU-12(b) + - configure_strategy + - file_permissions_etc_audit_auditd + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure permission u-xs,g-xws,o-xwrt on /etc/audit/auditd.conf + file: + path: /etc/audit/auditd.conf + mode: u-xs,g-xws,o-xwrt + when: file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-85871-2 + - DISA-STIG-RHEL-08-030610 + - NIST-800-53-AU-12(b) + - configure_strategy + - file_permissions_etc_audit_auditd + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + + + + +chmod u-xs,g-xws,o-xwrt /etc/audit/auditd.conf + + + + + + + + + + Verify Permissions on /etc/audit/rules.d/*.rules + +To properly set the permissions of /etc/audit/rules.d/*.rules, run the command: +$ sudo chmod 0640 /etc/audit/rules.d/*.rules + CCI-000171 + AU-12(b) + SRG-OS-000063-GPOS-00032 + RHEL-08-030610 + SV-230471r627750_rule + Without the capability to restrict the roles and individuals that can select which events +are audited, unauthorized personnel may be able to prevent the auditing of critical +events. Misconfigured audits may degrade the system's performance by overwhelming +the audit log. Misconfigured audits may also make it more difficult to establish, +correlate, and investigate the events relating to an incident or identify +those responsible for one. + CCE-85875-3 + - name: Find /etc/audit/rules.d/ file(s) + command: find -H /etc/audit/rules.d/ -maxdepth 1 -perm /u+xs,g+xws,o+xwrt -type + f -regex "^.*rules$" + register: files_found + changed_when: false + failed_when: false + check_mode: false + tags: + - CCE-85875-3 + - DISA-STIG-RHEL-08-030610 + - NIST-800-53-AU-12(b) + - configure_strategy + - file_permissions_etc_audit_rulesd + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Set permissions for /etc/audit/rules.d/ file(s) + file: + path: '{{ item }}' + mode: u-xs,g-xws,o-xwrt + state: file + with_items: + - '{{ files_found.stdout_lines }}' + tags: + - CCE-85875-3 + - DISA-STIG-RHEL-08-030610 + - NIST-800-53-AU-12(b) + - configure_strategy + - file_permissions_etc_audit_rulesd + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + + + + +find -H /etc/audit/rules.d/ -maxdepth 1 -perm /u+xs,g+xws,o+xwrt -type f -regex '^.*rules$' -exec chmod u-xs,g-xws,o-xwrt {} \; + + + + + + + + + + Verify that local System.map file (if exists) is readable only by root + Files containing sensitive informations should be protected by restrictive + permissions. Most of the time, there is no need that these files need to be read by any non-root user + +To properly set the permissions of /boot/System.map-*, run the command: +$ sudo chmod 0600 /boot/System.map-* + BP28(R13) + The System.map file contains information about kernel symbols and + can give some hints to generate local exploitation. + CCE-82892-1 + + + + + + + + + Ensure All SGID Executables Are Authorized + The SGID (set group id) bit should be set only on files that were +installed via authorized means. A straightforward means of identifying +unauthorized SGID files is determine if any were not installed as part of an +RPM package, which is cryptographically verified. Investigate the origin +of any unpackaged SGID files. +This configuration check considers authorized SGID files which were installed via RPM. +It is assumed that when an individual has sudo access to install an RPM +and all packages are signed with an organizationally-recognized GPG key, +the software should be considered an approved package on the system. +Any SGID file not deployed through an RPM will be flagged for further review. + BP28(R37) + BP28(R38) + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + 6.1.15 + Executable files with the SGID permission run with the privileges of +the owner of the file. SGID files of uncertain provenance could allow for +unprivileged users to elevate privileges. The presence of these files should be +strictly controlled on the system. + CCE-80816-2 + + + + + + + + + Ensure All SUID Executables Are Authorized + The SUID (set user id) bit should be set only on files that were +installed via authorized means. A straightforward means of identifying +unauthorized SUID files is determine if any were not installed as part of an +RPM package, which is cryptographically verified. Investigate the origin +of any unpackaged SUID files. +This configuration check considers authorized SUID files which were installed via RPM. +It is assumed that when an individual has sudo access to install an RPM +and all packages are signed with an organizationally-recognized GPG key, +the software should be considered an approved package on the system. +Any SUID file not deployed through an RPM will be flagged for further review. + BP28(R37) + BP28(R38) + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + 6.1.14 + Executable files with the SUID permission run with the privileges of +the owner of the file. SUID files of uncertain provenance could allow for +unprivileged users to elevate privileges. The presence of these files should be +strictly controlled on the system. + CCE-80817-0 + + + + + + + + + Ensure No World-Writable Files Exist + It is generally a good idea to remove global (other) write +access to a file when it is discovered. However, check with +documentation for specific applications before making changes. +Also, monitor for recurring world-writable files, as these may be +symptoms of a misconfigured application or user account. Finally, +this applies to real files and not virtual files that are a part of +pseudo file systems such as sysfs or procfs. + BP28(R40) + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + 6.1.11 + Data in world-writable files can be modified by any +user on the system. In almost all circumstances, files can be +configured using a combination of user and group permissions to +support whatever legitimate access is needed without the risk +caused by world-writable files. + CCE-80818-8 + +find / -xdev -type f -perm -002 -exec chmod o-w {} \; + + + + + + + + + + Ensure All Files Are Owned by a Group + If any files are not owned by a group, then the +cause of their lack of group-ownership should be investigated. +Following this, the files should be deleted or assigned to an +appropriate group. The following command will discover and print +any files on local partitions which do not belong to a valid group: +$ df --local -P | awk '{if (NR!=1) print $6}' | sudo xargs -I '{}' find '{}' -xdev -nogroup +To search all filesystems on a system including network mounted +filesystems the following command can be run manually for each partition: +$ sudo find PARTITION -xdev -nogroup + This rule only considers local groups. +If you have your groups defined outside /etc/group, the rule won't consider those. + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.02 + DSS06.03 + DSS06.06 + DSS06.10 + CCI-000366 + CCI-002165 + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.18.1.4 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.1 + A.9.4.2 + A.9.4.3 + A.9.4.4 + A.9.4.5 + CM-6(a) + AC-6(1) + PR.AC-1 + PR.AC-4 + PR.AC-6 + PR.AC-7 + PR.DS-5 + PR.PT-3 + SRG-OS-000480-GPOS-00227 + RHEL-08-010790 + 6.1.13 + SV-230327r627750_rule + Unowned files do not directly imply a security problem, but they are generally +a sign that something is amiss. They may +be caused by an intruder, by incorrect software installation or +draft software removal, or by failure to remove all files belonging +to a deleted account. The files should be repaired so they +will not cause problems when accounts are created in the future, +and the cause should be discovered and addressed. + CCE-83497-8 + + + + + + + + + Ensure All Files Are Owned by a User + If any files are not owned by a user, then the +cause of their lack of ownership should be investigated. +Following this, the files should be deleted or assigned to an +appropriate user. The following command will discover and print +any files on local partitions which do not belong to a valid user: +$ df --local -P | awk {'if (NR!=1) print $6'} | sudo xargs -I '{}' find '{}' -xdev -nouser +To search all filesystems on a system including network mounted +filesystems the following command can be run manually for each partition: +$ sudo find PARTITION -xdev -nouser + For this rule to evaluate centralized user accounts, getent must be working properly +so that running the command getent passwd returns a list of all users in your organization. +If using the System Security Services Daemon (SSSD), enumerate = true must be configured +in your organization's domain to return a complete list of users + Enabling this rule will result in slower scan times depending on the size of your organization +and number of centralized users. + 11 + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + 9 + APO01.06 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + DSS06.02 + DSS06.03 + DSS06.06 + CCI-000366 + CCI-002165 + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 5.2 + SR 7.6 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.1 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.AC-6 + PR.DS-5 + PR.IP-1 + PR.PT-3 + SRG-OS-000480-GPOS-00227 + RHEL-08-010780 + 6.1.12 + SV-230326r627750_rule + Unowned files do not directly imply a security problem, but they are generally +a sign that something is amiss. They may +be caused by an intruder, by incorrect software installation or +draft software removal, or by failure to remove all files belonging +to a deleted account. The files should be repaired so they +will not cause problems when accounts are created in the future, +and the cause should be discovered and addressed. + + CCE-83499-4 + + + + + + + + + Enable Kernel Parameter to Enforce DAC on Hardlinks + To set the runtime status of the fs.protected_hardlinks kernel parameter, run the following command: $ sudo sysctl -w fs.protected_hardlinks=1 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: fs.protected_hardlinks = 1 + BP28(R23) + CCI-002165 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-6(a) + AC-6(1) + SRG-OS-000312-GPOS-00122 + SRG-OS-000312-GPOS-00123 + SRG-OS-000324-GPOS-00125 + RHEL-08-010374 + SV-230268r833294_rule + By enabling this kernel parameter, users can no longer create soft or hard links to +files which they do not own. Disallowing such hardlinks mitigate vulnerabilities +based on insecure file system accessed by privileged programs, avoiding an +exploitation vector exploiting unsafe use of open() or creat(). + + CCE-81027-5 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*fs.protected_hardlinks.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-81027-5 + - DISA-STIG-RHEL-08-010374 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_fs_protected_hardlinks + +- name: Comment out any occurrences of fs.protected_hardlinks from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*fs.protected_hardlinks + replace: '#fs.protected_hardlinks' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-81027-5 + - DISA-STIG-RHEL-08-010374 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_fs_protected_hardlinks + +- name: Ensure sysctl fs.protected_hardlinks is set to 1 + sysctl: + name: fs.protected_hardlinks + value: '1' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-81027-5 + - DISA-STIG-RHEL-08-010374 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_fs_protected_hardlinks + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,fs.protected_hardlinks%3D1%0A + mode: 0644 + path: /etc/sysctl.d/75-sysctl_fs_protected_hardlinks.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of fs.protected_hardlinks from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*fs.protected_hardlinks.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "fs.protected_hardlinks" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done + +# +# Set runtime for fs.protected_hardlinks +# +/sbin/sysctl -q -n -w fs.protected_hardlinks="1" + +# +# If fs.protected_hardlinks present in /etc/sysctl.conf, change value to "1" +# else, add "fs.protected_hardlinks = 1" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^fs.protected_hardlinks") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "1" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^fs.protected_hardlinks\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^fs.protected_hardlinks\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-81027-5" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Enable Kernel Parameter to Enforce DAC on Symlinks + To set the runtime status of the fs.protected_symlinks kernel parameter, run the following command: $ sudo sysctl -w fs.protected_symlinks=1 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: fs.protected_symlinks = 1 + BP28(R23) + CCI-002165 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-6(a) + AC-6(1) + SRG-OS-000312-GPOS-00122 + SRG-OS-000312-GPOS-00123 + SRG-OS-000324-GPOS-00125 + RHEL-08-010373 + SV-230267r833292_rule + By enabling this kernel parameter, symbolic links are permitted to be followed +only when outside a sticky world-writable directory, or when the UID of the +link and follower match, or when the directory owner matches the symlink's owner. +Disallowing such symlinks helps mitigate vulnerabilities based on insecure file system +accessed by privileged programs, avoiding an exploitation vector exploiting unsafe use of +open() or creat(). + + CCE-81030-9 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*fs.protected_symlinks.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-81030-9 + - DISA-STIG-RHEL-08-010373 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_fs_protected_symlinks + +- name: Comment out any occurrences of fs.protected_symlinks from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*fs.protected_symlinks + replace: '#fs.protected_symlinks' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-81030-9 + - DISA-STIG-RHEL-08-010373 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_fs_protected_symlinks + +- name: Ensure sysctl fs.protected_symlinks is set to 1 + sysctl: + name: fs.protected_symlinks + value: '1' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-81030-9 + - DISA-STIG-RHEL-08-010373 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_fs_protected_symlinks + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,fs.protected_symlinks%3D1%0A + mode: 0644 + path: /etc/sysctl.d/75-sysctl_fs_protected_symlinks.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of fs.protected_symlinks from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*fs.protected_symlinks.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "fs.protected_symlinks" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done + +# +# Set runtime for fs.protected_symlinks +# +/sbin/sysctl -q -n -w fs.protected_symlinks="1" + +# +# If fs.protected_symlinks present in /etc/sysctl.conf, change value to "1" +# else, add "fs.protected_symlinks = 1" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^fs.protected_symlinks") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "1" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^fs.protected_symlinks\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^fs.protected_symlinks\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-81030-9" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Verify Permissions on Files with Local Account Information and Credentials + The default restrictive permissions for files which act as +important security databases such as passwd, shadow, +group, and gshadow files must be maintained. Many utilities +need read access to the passwd file in order to function properly, but +read access to the shadow file allows malicious attacks against system +passwords, and should never be enabled. + + Verify Group Who Owns Backup group File + To properly set the group owner of /etc/group-, run the command: $ sudo chgrp root /etc/group- + CCI-002223 + AC-6 (1) + SRG-OS-000480-GPOS-00227 + 6.1.9 + The /etc/group- file is a backup file of /etc/group, and as such, +it contains information regarding groups that are configured on the system. +Protection of this file is important for system security. + CCE-83475-4 + - name: Test for existence /etc/group- + stat: + path: /etc/group- + register: file_exists + tags: + - CCE-83475-4 + - NIST-800-53-AC-6 (1) + - configure_strategy + - file_groupowner_backup_etc_group + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure group owner 0 on /etc/group- + file: + path: /etc/group- + group: '0' + when: file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-83475-4 + - NIST-800-53-AC-6 (1) + - configure_strategy + - file_groupowner_backup_etc_group + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + + + +chgrp 0 /etc/group- + + + + + + + + + + Verify Group Who Owns Backup gshadow File + To properly set the group owner of /etc/gshadow-, run the command: $ sudo chgrp root /etc/gshadow- + CCI-002223 + AC-6 (1) + SRG-OS-000480-GPOS-00227 + 6.1.10 + The /etc/gshadow- file is a backup of /etc/gshadow, and as such, +it contains group password hashes. Protection of this file is critical for system security. + CCE-83535-5 + - name: Test for existence /etc/gshadow- + stat: + path: /etc/gshadow- + register: file_exists + tags: + - CCE-83535-5 + - NIST-800-53-AC-6 (1) + - configure_strategy + - file_groupowner_backup_etc_gshadow + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure group owner 0 on /etc/gshadow- + file: + path: /etc/gshadow- + group: '0' + when: file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-83535-5 + - NIST-800-53-AC-6 (1) + - configure_strategy + - file_groupowner_backup_etc_gshadow + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + + + +chgrp 0 /etc/gshadow- + + + + + + + + + + Verify Group Who Owns Backup passwd File + To properly set the group owner of /etc/passwd-, run the command: $ sudo chgrp root /etc/passwd- + CCI-002223 + AC-6 (1) + SRG-OS-000480-GPOS-00227 + 6.1.7 + The /etc/passwd- file is a backup file of /etc/passwd, and as such, +it contains information about the users that are configured on the system. +Protection of this file is critical for system security. + CCE-83324-4 + - name: Test for existence /etc/passwd- + stat: + path: /etc/passwd- + register: file_exists + tags: + - CCE-83324-4 + - NIST-800-53-AC-6 (1) + - configure_strategy + - file_groupowner_backup_etc_passwd + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure group owner 0 on /etc/passwd- + file: + path: /etc/passwd- + group: '0' + when: file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-83324-4 + - NIST-800-53-AC-6 (1) + - configure_strategy + - file_groupowner_backup_etc_passwd + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + + + +chgrp 0 /etc/passwd- + + + + + + + + + + Verify User Who Owns Backup shadow File + To properly set the group owner of /etc/shadow-, run the command: $ sudo chgrp root /etc/shadow- + SRG-OS-000480-GPOS-00227 + 6.1.8 + The /etc/shadow- file is a backup file of /etc/shadow, and as such, +it contains the list of local system accounts and password hashes. +Protection of this file is critical for system security. + CCE-83415-0 + - name: Test for existence /etc/shadow- + stat: + path: /etc/shadow- + register: file_exists + tags: + - CCE-83415-0 + - configure_strategy + - file_groupowner_backup_etc_shadow + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure group owner 0 on /etc/shadow- + file: + path: /etc/shadow- + group: '0' + when: file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-83415-0 + - configure_strategy + - file_groupowner_backup_etc_shadow + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + + + +chgrp 0 /etc/shadow- + + + + + + + + + + Verify Group Who Owns group File + To properly set the group owner of /etc/group, run the command: $ sudo chgrp root /etc/group + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + 5.5.2.2 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + Req-8.7.c + SRG-OS-000480-GPOS-00227 + 6.1.5 + The /etc/group file contains information regarding groups that are configured +on the system. Protection of this file is important for system security. + CCE-80796-6 + - name: Test for existence /etc/group + stat: + path: /etc/group + register: file_exists + tags: + - CCE-80796-6 + - CJIS-5.5.2.2 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.7.c + - configure_strategy + - file_groupowner_etc_group + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure group owner 0 on /etc/group + file: + path: /etc/group + group: '0' + when: file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-80796-6 + - CJIS-5.5.2.2 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.7.c + - configure_strategy + - file_groupowner_etc_group + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + + + +chgrp 0 /etc/group + + + + + + + + + + Verify Group Who Owns gshadow File + To properly set the group owner of /etc/gshadow, run the command: $ sudo chgrp root /etc/gshadow + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + SRG-OS-000480-GPOS-00227 + 6.1.6 + The /etc/gshadow file contains group password hashes. Protection of this file +is critical for system security. + CCE-80797-4 + - name: Test for existence /etc/gshadow + stat: + path: /etc/gshadow + register: file_exists + tags: + - CCE-80797-4 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_groupowner_etc_gshadow + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure group owner 0 on /etc/gshadow + file: + path: /etc/gshadow + group: '0' + when: file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-80797-4 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_groupowner_etc_gshadow + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + + + +chgrp 0 /etc/gshadow + + + + + + + + + + Verify Group Who Owns passwd File + To properly set the group owner of /etc/passwd, run the command: $ sudo chgrp root /etc/passwd + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + 5.5.2.2 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + Req-8.7.c + SRG-OS-000480-GPOS-00227 + 6.1.3 + The /etc/passwd file contains information about the users that are configured on +the system. Protection of this file is critical for system security. + CCE-80798-2 + - name: Test for existence /etc/passwd + stat: + path: /etc/passwd + register: file_exists + tags: + - CCE-80798-2 + - CJIS-5.5.2.2 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.7.c + - configure_strategy + - file_groupowner_etc_passwd + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure group owner 0 on /etc/passwd + file: + path: /etc/passwd + group: '0' + when: file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-80798-2 + - CJIS-5.5.2.2 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.7.c + - configure_strategy + - file_groupowner_etc_passwd + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + + + +chgrp 0 /etc/passwd + + + + + + + + + + Verify Group Who Owns shadow File + To properly set the group owner of /etc/shadow, run the command: $ sudo chgrp root /etc/shadow + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + 5.5.2.2 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + Req-8.7.c + SRG-OS-000480-GPOS-00227 + 6.1.4 + The /etc/shadow file stores password hashes. Protection of this file is +critical for system security. + CCE-80799-0 + - name: Test for existence /etc/shadow + stat: + path: /etc/shadow + register: file_exists + tags: + - CCE-80799-0 + - CJIS-5.5.2.2 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.7.c + - configure_strategy + - file_groupowner_etc_shadow + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure group owner 0 on /etc/shadow + file: + path: /etc/shadow + group: '0' + when: file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-80799-0 + - CJIS-5.5.2.2 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.7.c + - configure_strategy + - file_groupowner_etc_shadow + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + + + +chgrp 0 /etc/shadow + + + + + + + + + + Verify User Who Owns Backup group File + To properly set the owner of /etc/group-, run the command: $ sudo chown root /etc/group- + CCI-002223 + AC-6 (1) + SRG-OS-000480-GPOS-00227 + 6.1.9 + The /etc/group- file is a backup file of /etc/group, and as such, +it contains information regarding groups that are configured on the system. +Protection of this file is important for system security. + CCE-83473-9 + - name: Test for existence /etc/group- + stat: + path: /etc/group- + register: file_exists + tags: + - CCE-83473-9 + - NIST-800-53-AC-6 (1) + - configure_strategy + - file_owner_backup_etc_group + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure owner 0 on /etc/group- + file: + path: /etc/group- + owner: '0' + when: file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-83473-9 + - NIST-800-53-AC-6 (1) + - configure_strategy + - file_owner_backup_etc_group + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + + + +chown 0 /etc/group- + + + + + + + + + + Verify User Who Owns Backup gshadow File + To properly set the owner of /etc/gshadow-, run the command: $ sudo chown root /etc/gshadow- + CCI-002223 + AC-6 (1) + SRG-OS-000480-GPOS-00227 + 6.1.10 + The /etc/gshadow- file is a backup of /etc/gshadow, and as such, +it contains group password hashes. Protection of this file is critical for system security. + CCE-83533-0 + - name: Test for existence /etc/gshadow- + stat: + path: /etc/gshadow- + register: file_exists + tags: + - CCE-83533-0 + - NIST-800-53-AC-6 (1) + - configure_strategy + - file_owner_backup_etc_gshadow + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure owner 0 on /etc/gshadow- + file: + path: /etc/gshadow- + owner: '0' + when: file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-83533-0 + - NIST-800-53-AC-6 (1) + - configure_strategy + - file_owner_backup_etc_gshadow + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + + + +chown 0 /etc/gshadow- + + + + + + + + + + Verify User Who Owns Backup passwd File + To properly set the owner of /etc/passwd-, run the command: $ sudo chown root /etc/passwd- + CCI-002223 + AC-6 (1) + SRG-OS-000480-GPOS-00227 + 6.1.7 + The /etc/passwd- file is a backup file of /etc/passwd, and as such, +it contains information about the users that are configured on the system. +Protection of this file is critical for system security. + CCE-83326-9 + - name: Test for existence /etc/passwd- + stat: + path: /etc/passwd- + register: file_exists + tags: + - CCE-83326-9 + - NIST-800-53-AC-6 (1) + - configure_strategy + - file_owner_backup_etc_passwd + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure owner 0 on /etc/passwd- + file: + path: /etc/passwd- + owner: '0' + when: file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-83326-9 + - NIST-800-53-AC-6 (1) + - configure_strategy + - file_owner_backup_etc_passwd + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + + + +chown 0 /etc/passwd- + + + + + + + + + + Verify Group Who Owns Backup shadow File + To properly set the owner of /etc/shadow-, run the command: $ sudo chown root /etc/shadow- + CCI-002223 + AC-6 (1) + SRG-OS-000480-GPOS-00227 + 6.1.8 + The /etc/shadow- file is a backup file of /etc/shadow, and as such, +it contains the list of local system accounts and password hashes. +Protection of this file is critical for system security. + CCE-83413-5 + - name: Test for existence /etc/shadow- + stat: + path: /etc/shadow- + register: file_exists + tags: + - CCE-83413-5 + - NIST-800-53-AC-6 (1) + - configure_strategy + - file_owner_backup_etc_shadow + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure owner 0 on /etc/shadow- + file: + path: /etc/shadow- + owner: '0' + when: file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-83413-5 + - NIST-800-53-AC-6 (1) + - configure_strategy + - file_owner_backup_etc_shadow + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + + + +chown 0 /etc/shadow- + + + + + + + + + + Verify User Who Owns group File + To properly set the owner of /etc/group, run the command: $ sudo chown root /etc/group + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + 5.5.2.2 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + CCI-002223 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + Req-8.7.c + SRG-OS-000480-GPOS-00227 + 6.1.5 + The /etc/group file contains information regarding groups that are configured +on the system. Protection of this file is important for system security. + CCE-80801-4 + - name: Test for existence /etc/group + stat: + path: /etc/group + register: file_exists + tags: + - CCE-80801-4 + - CJIS-5.5.2.2 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.7.c + - configure_strategy + - file_owner_etc_group + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure owner 0 on /etc/group + file: + path: /etc/group + owner: '0' + when: file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-80801-4 + - CJIS-5.5.2.2 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.7.c + - configure_strategy + - file_owner_etc_group + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + + + +chown 0 /etc/group + + + + + + + + + + Verify User Who Owns gshadow File + To properly set the owner of /etc/gshadow, run the command: $ sudo chown root /etc/gshadow + BP28(R36) + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + CCI-002223 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + SRG-OS-000480-GPOS-00227 + 6.1.6 + The /etc/gshadow file contains group password hashes. Protection of this file +is critical for system security. + CCE-80802-2 + - name: Test for existence /etc/gshadow + stat: + path: /etc/gshadow + register: file_exists + tags: + - CCE-80802-2 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_owner_etc_gshadow + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure owner 0 on /etc/gshadow + file: + path: /etc/gshadow + owner: '0' + when: file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-80802-2 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_owner_etc_gshadow + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + + + +chown 0 /etc/gshadow + + + + + + + + + + Verify User Who Owns passwd File + To properly set the owner of /etc/passwd, run the command: $ sudo chown root /etc/passwd + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + 5.5.2.2 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + CCI-002223 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + Req-8.7.c + SRG-OS-000480-GPOS-00227 + 6.1.3 + The /etc/passwd file contains information about the users that are configured on +the system. Protection of this file is critical for system security. + CCE-80803-0 + - name: Test for existence /etc/passwd + stat: + path: /etc/passwd + register: file_exists + tags: + - CCE-80803-0 + - CJIS-5.5.2.2 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.7.c + - configure_strategy + - file_owner_etc_passwd + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure owner 0 on /etc/passwd + file: + path: /etc/passwd + owner: '0' + when: file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-80803-0 + - CJIS-5.5.2.2 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.7.c + - configure_strategy + - file_owner_etc_passwd + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + + + +chown 0 /etc/passwd + + + + + + + + + + Verify User Who Owns shadow File + To properly set the owner of /etc/shadow, run the command: $ sudo chown root /etc/shadow + BP28(R36) + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + 5.5.2.2 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + CCI-002223 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + Req-8.7.c + SRG-OS-000480-GPOS-00227 + 6.1.4 + The /etc/shadow file contains the list of local +system accounts and stores password hashes. Protection of this file is +critical for system security. Failure to give ownership of this file +to root provides the designated owner with access to sensitive information +which could weaken the system security posture. + CCE-80804-8 + - name: Test for existence /etc/shadow + stat: + path: /etc/shadow + register: file_exists + tags: + - CCE-80804-8 + - CJIS-5.5.2.2 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.7.c + - configure_strategy + - file_owner_etc_shadow + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure owner 0 on /etc/shadow + file: + path: /etc/shadow + owner: '0' + when: file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-80804-8 + - CJIS-5.5.2.2 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.7.c + - configure_strategy + - file_owner_etc_shadow + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + + + +chown 0 /etc/shadow + + + + + + + + + + Verify Permissions on Backup group File + +To properly set the permissions of /etc/group-, run the command: +$ sudo chmod 0644 /etc/group- + CCI-002223 + AC-6 (1) + SRG-OS-000480-GPOS-00227 + 6.1.9 + The /etc/group- file is a backup file of /etc/group, and as such, +it contains information regarding groups that are configured on the system. +Protection of this file is important for system security. + CCE-83483-8 + - name: Test for existence /etc/group- + stat: + path: /etc/group- + register: file_exists + tags: + - CCE-83483-8 + - NIST-800-53-AC-6 (1) + - configure_strategy + - file_permissions_backup_etc_group + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure permission u-xs,g-xws,o-xwt on /etc/group- + file: + path: /etc/group- + mode: u-xs,g-xws,o-xwt + when: file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-83483-8 + - NIST-800-53-AC-6 (1) + - configure_strategy + - file_permissions_backup_etc_group + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + + + + +chmod u-xs,g-xws,o-xwt /etc/group- + + + + + + + + + + Verify Permissions on Backup gshadow File + +To properly set the permissions of /etc/gshadow-, run the command: +$ sudo chmod 0000 /etc/gshadow- + CCI-002223 + AC-6 (1) + SRG-OS-000480-GPOS-00227 + 6.1.10 + The /etc/gshadow- file is a backup of /etc/gshadow, and as such, +it contains group password hashes. Protection of this file is critical for system security. + CCE-83573-6 + - name: Test for existence /etc/gshadow- + stat: + path: /etc/gshadow- + register: file_exists + tags: + - CCE-83573-6 + - NIST-800-53-AC-6 (1) + - configure_strategy + - file_permissions_backup_etc_gshadow + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure permission u-xwrs,g-xwrs,o-xwrt on /etc/gshadow- + file: + path: /etc/gshadow- + mode: u-xwrs,g-xwrs,o-xwrt + when: file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-83573-6 + - NIST-800-53-AC-6 (1) + - configure_strategy + - file_permissions_backup_etc_gshadow + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + + + + +chmod u-xwrs,g-xwrs,o-xwrt /etc/gshadow- + + + + + + + + + + Verify Permissions on Backup passwd File + +To properly set the permissions of /etc/passwd-, run the command: +$ sudo chmod 0644 /etc/passwd- + CCI-002223 + AC-6 (1) + SRG-OS-000480-GPOS-00227 + 6.1.7 + The /etc/passwd- file is a backup file of /etc/passwd, and as such, +it contains information about the users that are configured on the system. +Protection of this file is critical for system security. + CCE-83332-7 + - name: Test for existence /etc/passwd- + stat: + path: /etc/passwd- + register: file_exists + tags: + - CCE-83332-7 + - NIST-800-53-AC-6 (1) + - configure_strategy + - file_permissions_backup_etc_passwd + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure permission u-xs,g-xws,o-xwt on /etc/passwd- + file: + path: /etc/passwd- + mode: u-xs,g-xws,o-xwt + when: file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-83332-7 + - NIST-800-53-AC-6 (1) + - configure_strategy + - file_permissions_backup_etc_passwd + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + + + + +chmod u-xs,g-xws,o-xwt /etc/passwd- + + + + + + + + + + Verify Permissions on Backup shadow File + +To properly set the permissions of /etc/shadow-, run the command: +$ sudo chmod 0000 /etc/shadow- + CCI-002223 + AC-6 (1) + SRG-OS-000480-GPOS-00227 + 6.1.8 + The /etc/shadow- file is a backup file of /etc/shadow, and as such, +it contains the list of local system accounts and password hashes. +Protection of this file is critical for system security. + CCE-83417-6 + - name: Test for existence /etc/shadow- + stat: + path: /etc/shadow- + register: file_exists + tags: + - CCE-83417-6 + - NIST-800-53-AC-6 (1) + - configure_strategy + - file_permissions_backup_etc_shadow + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure permission u-xwrs,g-xwrs,o-xwrt on /etc/shadow- + file: + path: /etc/shadow- + mode: u-xwrs,g-xwrs,o-xwrt + when: file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-83417-6 + - NIST-800-53-AC-6 (1) + - configure_strategy + - file_permissions_backup_etc_shadow + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + + + + +chmod u-xwrs,g-xwrs,o-xwrt /etc/shadow- + + + + + + + + + + Verify Permissions on group File + +To properly set the permissions of /etc/passwd, run the command: +$ sudo chmod 0644 /etc/passwd + BP28(R36) + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + 5.5.2.2 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + CCI-002223 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + Req-8.7.c + SRG-OS-000480-GPOS-00227 + 6.1.5 + The /etc/group file contains information regarding groups that are configured +on the system. Protection of this file is important for system security. + CCE-80810-5 + - name: Test for existence /etc/group + stat: + path: /etc/group + register: file_exists + tags: + - CCE-80810-5 + - CJIS-5.5.2.2 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.7.c + - configure_strategy + - file_permissions_etc_group + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure permission u-xs,g-xws,o-xwt on /etc/group + file: + path: /etc/group + mode: u-xs,g-xws,o-xwt + when: file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-80810-5 + - CJIS-5.5.2.2 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.7.c + - configure_strategy + - file_permissions_etc_group + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + + + + +chmod u-xs,g-xws,o-xwt /etc/group + + + + + + + + + + Verify Permissions on gshadow File + +To properly set the permissions of /etc/gshadow, run the command: +$ sudo chmod 0000 /etc/gshadow + BP28(R36) + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + CCI-002223 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + SRG-OS-000480-GPOS-00227 + 6.1.6 + The /etc/gshadow file contains group password hashes. Protection of this file +is critical for system security. + CCE-80811-3 + - name: Test for existence /etc/gshadow + stat: + path: /etc/gshadow + register: file_exists + tags: + - CCE-80811-3 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_permissions_etc_gshadow + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure permission u-xwrs,g-xwrs,o-xwrt on /etc/gshadow + file: + path: /etc/gshadow + mode: u-xwrs,g-xwrs,o-xwrt + when: file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-80811-3 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_permissions_etc_gshadow + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + + + + +chmod u-xwrs,g-xwrs,o-xwrt /etc/gshadow + + + + + + + + + + Verify Permissions on passwd File + +To properly set the permissions of /etc/passwd, run the command: +$ sudo chmod 0644 /etc/passwd + BP28(R36) + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + 5.5.2.2 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + CCI-002223 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + Req-8.7.c + SRG-OS-000480-GPOS-00227 + 6.1.3 + If the /etc/passwd file is writable by a group-owner or the +world the risk of its compromise is increased. The file contains the list of +accounts on the system and associated information, and protection of this file +is critical for system security. + CCE-80812-1 + - name: Test for existence /etc/passwd + stat: + path: /etc/passwd + register: file_exists + tags: + - CCE-80812-1 + - CJIS-5.5.2.2 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.7.c + - configure_strategy + - file_permissions_etc_passwd + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure permission u-xs,g-xws,o-xwt on /etc/passwd + file: + path: /etc/passwd + mode: u-xs,g-xws,o-xwt + when: file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-80812-1 + - CJIS-5.5.2.2 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.7.c + - configure_strategy + - file_permissions_etc_passwd + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + + + + +chmod u-xs,g-xws,o-xwt /etc/passwd + + + + + + + + + + Verify Permissions on shadow File + +To properly set the permissions of /etc/shadow, run the command: +$ sudo chmod 0000 /etc/shadow + BP28(R36) + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + 5.5.2.2 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + CCI-002223 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + Req-8.7.c + SRG-OS-000480-GPOS-00227 + 6.1.4 + The /etc/shadow file contains the list of local +system accounts and stores password hashes. Protection of this file is +critical for system security. Failure to give ownership of this file +to root provides the designated owner with access to sensitive information +which could weaken the system security posture. + CCE-80813-9 + - name: Test for existence /etc/shadow + stat: + path: /etc/shadow + register: file_exists + tags: + - CCE-80813-9 + - CJIS-5.5.2.2 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.7.c + - configure_strategy + - file_permissions_etc_shadow + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure permission u-xwrs,g-xwrs,o-xwrt on /etc/shadow + file: + path: /etc/shadow + mode: u-xwrs,g-xwrs,o-xwrt + when: file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-80813-9 + - CJIS-5.5.2.2 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.7.c + - configure_strategy + - file_permissions_etc_shadow + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + + + + +chmod u-xwrs,g-xwrs,o-xwrt /etc/shadow + + + + + + + + + + + Verify Permissions on Files within /var/log Directory + The /var/log directory contains files with logs of error +messages in the system and should only be accessed by authorized +personnel. + + Verify Group Who Owns /var/log Directory + To properly set the group owner of /var/log, run the command: $ sudo chgrp root /var/log + CCI-001314 + SRG-OS-000206-GPOS-00084 + RHEL-08-010260 + SV-230250r627750_rule + The /var/log directory contains files with logs of error +messages in the system and should only be accessed by authorized +personnel. + CCE-83659-3 + - name: Ensure group owner on /var/log/ + file: + path: /var/log/ + state: directory + group: '0' + tags: + - CCE-83659-3 + - DISA-STIG-RHEL-08-010260 + - configure_strategy + - file_groupowner_var_log + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + + + +find -H /var/log/ -maxdepth 1 -type d -exec chgrp 0 {} \; + + + + + + + + + + Verify Group Who Owns /var/log/messages File + To properly set the group owner of /var/log/messages, run the command: $ sudo chgrp root /var/log/messages + CCI-001314 + SRG-OS-000206-GPOS-00084 + RHEL-08-010230 + SV-230247r627750_rule + The /var/log/messages file contains logs of error messages in +the system and should only be accessed by authorized personnel. + CCE-83660-1 + - name: Test for existence /var/log/messages + stat: + path: /var/log/messages + register: file_exists + tags: + - CCE-83660-1 + - DISA-STIG-RHEL-08-010230 + - configure_strategy + - file_groupowner_var_log_messages + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure group owner 0 on /var/log/messages + file: + path: /var/log/messages + group: '0' + when: file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-83660-1 + - DISA-STIG-RHEL-08-010230 + - configure_strategy + - file_groupowner_var_log_messages + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + + + +chgrp 0 /var/log/messages + + + + + + + + + + Verify Group Who Owns /var/log/syslog File + To properly set the group owner of /var/log/syslog, run the command: $ sudo chgrp adm /var/log/syslog + CCI-001314 + SRG-OS-000206-GPOS-00084 + The /var/log/syslog file contains logs of error messages in +the system and should only be accessed by authorized personnel. + - name: Test for existence /var/log/syslog + stat: + path: /var/log/syslog + register: file_exists + tags: + - configure_strategy + - file_groupowner_var_log_syslog + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure group owner 4 on /var/log/syslog + file: + path: /var/log/syslog + group: '4' + when: file_exists.stat is defined and file_exists.stat.exists + tags: + - configure_strategy + - file_groupowner_var_log_syslog + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + + + +chgrp 4 /var/log/syslog + + + + + + + + + + Verify User Who Owns /var/log Directory + To properly set the owner of /var/log, run the command: $ sudo chown root /var/log + CCI-001314 + SRG-OS-000206-GPOS-00084 + RHEL-08-010250 + SV-230249r627750_rule + The /var/log directory contains files with logs of error +messages in the system and should only be accessed by authorized +personnel. + CCE-83661-9 + - name: Ensure owner on directory /var/log/ + file: + path: /var/log/ + state: directory + owner: '0' + tags: + - CCE-83661-9 + - DISA-STIG-RHEL-08-010250 + - configure_strategy + - file_owner_var_log + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + + + +find -H /var/log/ -maxdepth 1 -type d -exec chown 0 {} \; + + + + + + + + + + Verify User Who Owns /var/log/messages File + To properly set the owner of /var/log/messages, run the command: $ sudo chown root /var/log/messages + CCI-001314 + SRG-OS-000206-GPOS-00084 + RHEL-08-010220 + SV-230246r627750_rule + The /var/log/messages file contains logs of error messages in +the system and should only be accessed by authorized personnel. + CCE-83662-7 + - name: Test for existence /var/log/messages + stat: + path: /var/log/messages + register: file_exists + tags: + - CCE-83662-7 + - DISA-STIG-RHEL-08-010220 + - configure_strategy + - file_owner_var_log_messages + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure owner 0 on /var/log/messages + file: + path: /var/log/messages + owner: '0' + when: file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-83662-7 + - DISA-STIG-RHEL-08-010220 + - configure_strategy + - file_owner_var_log_messages + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + + + +chown 0 /var/log/messages + + + + + + + + + + Verify User Who Owns /var/log/syslog File + To properly set the owner of /var/log/syslog, run the command: $ sudo chown syslog /var/log/syslog + CCI-001314 + SRG-OS-000206-GPOS-00084 + The /var/log/syslog file contains logs of error messages in +the system and should only be accessed by authorized personnel. + - name: Test for existence /var/log/syslog + stat: + path: /var/log/syslog + register: file_exists + tags: + - configure_strategy + - file_owner_var_log_syslog + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure owner 104 on /var/log/syslog + file: + path: /var/log/syslog + owner: '104' + when: file_exists.stat is defined and file_exists.stat.exists + tags: + - configure_strategy + - file_owner_var_log_syslog + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + + + +chown 104 /var/log/syslog + + + + + + + + + + Verify Permissions on /var/log Directory + +To properly set the permissions of /var/log, run the command: +$ sudo chmod 0755 /var/log + CCI-001314 + SRG-OS-000206-GPOS-00084 + RHEL-08-010240 + SV-230248r627750_rule + The /var/log directory contains files with logs of error +messages in the system and should only be accessed by authorized +personnel. + CCE-83663-5 + - name: Set permissions for /var/log/ + file: + path: /var/log/ + state: directory + mode: u-s,g-ws,o-wt + tags: + - CCE-83663-5 + - DISA-STIG-RHEL-08-010240 + - configure_strategy + - file_permissions_var_log + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + + + + +find -H /var/log/ -maxdepth 1 -perm /u+s,g+ws,o+wt -type d -exec chmod u-s,g-ws,o-wt {} \; + + + + + + + + + + Verify Permissions on /var/log/messages File + +To properly set the permissions of /var/log/messages, run the command: +$ sudo chmod 0640 /var/log/messages + CCI-001314 + SRG-OS-000206-GPOS-00084 + RHEL-08-010210 + SV-230245r627750_rule + The /var/log/messages file contains logs of error messages in +the system and should only be accessed by authorized personnel. + CCE-83665-0 + - name: Test for existence /var/log/messages + stat: + path: /var/log/messages + register: file_exists + tags: + - CCE-83665-0 + - DISA-STIG-RHEL-08-010210 + - configure_strategy + - file_permissions_var_log_messages + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure permission u-xs,g-xws,o-xwrt on /var/log/messages + file: + path: /var/log/messages + mode: u-xs,g-xws,o-xwrt + when: file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-83665-0 + - DISA-STIG-RHEL-08-010210 + - configure_strategy + - file_permissions_var_log_messages + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + + + + +chmod u-xs,g-xws,o-xwrt /var/log/messages + + + + + + + + + + Verify Permissions on /var/log/syslog File + +To properly set the permissions of /var/log/syslog, run the command: +$ sudo chmod 0640 /var/log/syslog + CCI-001314 + SRG-OS-000206-GPOS-00084 + The /var/log/syslog file contains logs of error messages in +the system and should only be accessed by authorized personnel. + - name: Test for existence /var/log/syslog + stat: + path: /var/log/syslog + register: file_exists + tags: + - configure_strategy + - file_permissions_var_log_syslog + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure permission u-xs,g-xws,o-xwrt on /var/log/syslog + file: + path: /var/log/syslog + mode: u-xs,g-xws,o-xwrt + when: file_exists.stat is defined and file_exists.stat.exists + tags: + - configure_strategy + - file_permissions_var_log_syslog + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + + + + +chmod u-xs,g-xws,o-xwrt /var/log/syslog + + + + + + + + + + + Verify File Permissions Within Some Important Directories + Some directories contain files whose confidentiality or integrity +is notably important and may also be susceptible to misconfiguration over time, particularly if +unpackaged software is installed. As such, +an argument exists to verify that files' permissions within these directories remain +configured correctly and restrictively. + + Verify that Shared Library Directories Have Root Group Ownership + System-wide shared library files, which are linked to executables +during process load time or run time, are stored in the following directories +by default: +/lib +/lib64 +/usr/lib +/usr/lib64 + +Kernel modules, which can be added to the kernel during runtime, are also +stored in /lib/modules. All files in these directories should be +group-owned by the root user. If the directories, is found to be owned +by a user other than root correct its +ownership with the following command: +$ sudo chgrp root DIR + CCI-001499 + CM-5(6) + CM-5(6).1 + SRG-OS-000259-GPOS-00100 + RHEL-08-010351 + SV-251709r810014_rule + Files from shared library directories are loaded into the address +space of processes (including privileged ones) or of the kernel itself at +runtime. Proper ownership of library directories is necessary to protect +the integrity of the system. + CCE-85894-4 + - name: Ensure group owner on /lib/ recursively + file: + path: /lib/ + state: directory + recurse: true + group: '0' + tags: + - CCE-85894-4 + - DISA-STIG-RHEL-08-010351 + - NIST-800-53-CM-5(6) + - NIST-800-53-CM-5(6).1 + - configure_strategy + - dir_group_ownership_library_dirs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure group owner on /lib64/ recursively + file: + path: /lib64/ + state: directory + recurse: true + group: '0' + tags: + - CCE-85894-4 + - DISA-STIG-RHEL-08-010351 + - NIST-800-53-CM-5(6) + - NIST-800-53-CM-5(6).1 + - configure_strategy + - dir_group_ownership_library_dirs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure group owner on /usr/lib/ recursively + file: + path: /usr/lib/ + state: directory + recurse: true + group: '0' + tags: + - CCE-85894-4 + - DISA-STIG-RHEL-08-010351 + - NIST-800-53-CM-5(6) + - NIST-800-53-CM-5(6).1 + - configure_strategy + - dir_group_ownership_library_dirs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure group owner on /usr/lib64/ recursively + file: + path: /usr/lib64/ + state: directory + recurse: true + group: '0' + tags: + - CCE-85894-4 + - DISA-STIG-RHEL-08-010351 + - NIST-800-53-CM-5(6) + - NIST-800-53-CM-5(6).1 + - configure_strategy + - dir_group_ownership_library_dirs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + + + +find -H /lib/ -type d -exec chgrp 0 {} \; + +find -H /lib64/ -type d -exec chgrp 0 {} \; + +find -H /usr/lib/ -type d -exec chgrp 0 {} \; + +find -H /usr/lib64/ -type d -exec chgrp 0 {} \; + + + + + + + + + + Verify that System Executable Have Root Ownership + /bin +/sbin +/usr/bin +/usr/sbin +/usr/local/bin +/usr/local/sbin +All these directories should be owned by the root user. +If any directory DIR in these directories is found +to be owned by a user other than root, correct its ownership with the +following command: +$ sudo chown root DIR + CCI-001495 + SRG-OS-000258-GPOS-00099 + System binaries are executed by privileged users as well as system services, +and restrictive permissions are necessary to ensure that their +execution of these programs cannot be co-opted. + - name: Ensure owner on directory /bin/ recursively + file: + path: /bin/ + state: directory + recurse: true + owner: '0' + tags: + - configure_strategy + - dir_ownership_binary_dirs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure owner on directory /sbin/ recursively + file: + path: /sbin/ + state: directory + recurse: true + owner: '0' + tags: + - configure_strategy + - dir_ownership_binary_dirs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure owner on directory /usr/bin/ recursively + file: + path: /usr/bin/ + state: directory + recurse: true + owner: '0' + tags: + - configure_strategy + - dir_ownership_binary_dirs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure owner on directory /usr/sbin/ recursively + file: + path: /usr/sbin/ + state: directory + recurse: true + owner: '0' + tags: + - configure_strategy + - dir_ownership_binary_dirs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure owner on directory /usr/local/bin/ recursively + file: + path: /usr/local/bin/ + state: directory + recurse: true + owner: '0' + tags: + - configure_strategy + - dir_ownership_binary_dirs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure owner on directory /usr/local/sbin/ recursively + file: + path: /usr/local/sbin/ + state: directory + recurse: true + owner: '0' + tags: + - configure_strategy + - dir_ownership_binary_dirs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + + + +find -H /bin/ -type d -exec chown 0 {} \; + +find -H /sbin/ -type d -exec chown 0 {} \; + +find -H /usr/bin/ -type d -exec chown 0 {} \; + +find -H /usr/sbin/ -type d -exec chown 0 {} \; + +find -H /usr/local/bin/ -type d -exec chown 0 {} \; + +find -H /usr/local/sbin/ -type d -exec chown 0 {} \; + + + + + + + + + + Verify that Shared Library Directories Have Root Ownership + System-wide shared library files, which are linked to executables +during process load time or run time, are stored in the following directories +by default: +/lib +/lib64 +/usr/lib +/usr/lib64 + +Kernel modules, which can be added to the kernel during runtime, are also +stored in /lib/modules. All files in these directories should be +owned by the root user. If the directories, is found to be owned +by a user other than root correct its +ownership with the following command: +$ sudo chown root DIR + CCI-001499 + CM-5(6) + CM-5(6).1 + SRG-OS-000259-GPOS-00100 + RHEL-08-010341 + SV-251708r810012_rule + Files from shared library directories are loaded into the address +space of processes (including privileged ones) or of the kernel itself at +runtime. Proper ownership of library directories is necessary to protect +the integrity of the system. + CCE-89021-0 + - name: Ensure owner on directory /lib/ recursively + file: + path: /lib/ + state: directory + recurse: true + owner: '0' + tags: + - CCE-89021-0 + - DISA-STIG-RHEL-08-010341 + - NIST-800-53-CM-5(6) + - NIST-800-53-CM-5(6).1 + - configure_strategy + - dir_ownership_library_dirs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure owner on directory /lib64/ recursively + file: + path: /lib64/ + state: directory + recurse: true + owner: '0' + tags: + - CCE-89021-0 + - DISA-STIG-RHEL-08-010341 + - NIST-800-53-CM-5(6) + - NIST-800-53-CM-5(6).1 + - configure_strategy + - dir_ownership_library_dirs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure owner on directory /usr/lib/ recursively + file: + path: /usr/lib/ + state: directory + recurse: true + owner: '0' + tags: + - CCE-89021-0 + - DISA-STIG-RHEL-08-010341 + - NIST-800-53-CM-5(6) + - NIST-800-53-CM-5(6).1 + - configure_strategy + - dir_ownership_library_dirs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure owner on directory /usr/lib64/ recursively + file: + path: /usr/lib64/ + state: directory + recurse: true + owner: '0' + tags: + - CCE-89021-0 + - DISA-STIG-RHEL-08-010341 + - NIST-800-53-CM-5(6) + - NIST-800-53-CM-5(6).1 + - configure_strategy + - dir_ownership_library_dirs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + + + +find -H /lib/ -type d -exec chown 0 {} \; + +find -H /lib64/ -type d -exec chown 0 {} \; + +find -H /usr/lib/ -type d -exec chown 0 {} \; + +find -H /usr/lib64/ -type d -exec chown 0 {} \; + + + + + + + + + + Verify that System Executable Directories Have Restrictive Permissions + System executables are stored in the following directories by default: +/bin +/sbin +/usr/bin +/usr/sbin +/usr/local/bin +/usr/local/sbin +These directories should not be group-writable or world-writable. +If any directory DIR in these directories is found to be +group-writable or world-writable, correct its permission with the +following command: +$ sudo chmod go-w DIR + CCI-001495 + SRG-OS-000258-GPOS-00099 + System binaries are executed by privileged users, as well as system services, +and restrictive permissions are necessary to ensure execution of these programs +cannot be co-opted. + - name: Set permissions for /bin/ recursively + file: + path: /bin/ + state: directory + recurse: true + mode: u-s,g-ws,o-wt + tags: + - configure_strategy + - dir_permissions_binary_dirs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Set permissions for /sbin/ recursively + file: + path: /sbin/ + state: directory + recurse: true + mode: u-s,g-ws,o-wt + tags: + - configure_strategy + - dir_permissions_binary_dirs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Set permissions for /usr/bin/ recursively + file: + path: /usr/bin/ + state: directory + recurse: true + mode: u-s,g-ws,o-wt + tags: + - configure_strategy + - dir_permissions_binary_dirs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Set permissions for /usr/sbin/ recursively + file: + path: /usr/sbin/ + state: directory + recurse: true + mode: u-s,g-ws,o-wt + tags: + - configure_strategy + - dir_permissions_binary_dirs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Set permissions for /usr/local/bin/ recursively + file: + path: /usr/local/bin/ + state: directory + recurse: true + mode: u-s,g-ws,o-wt + tags: + - configure_strategy + - dir_permissions_binary_dirs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Set permissions for /usr/local/sbin/ recursively + file: + path: /usr/local/sbin/ + state: directory + recurse: true + mode: u-s,g-ws,o-wt + tags: + - configure_strategy + - dir_permissions_binary_dirs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + + + + +find -H /bin/ -perm /u+s,g+ws,o+wt -type d -exec chmod u-s,g-ws,o-wt {} \; + +find -H /sbin/ -perm /u+s,g+ws,o+wt -type d -exec chmod u-s,g-ws,o-wt {} \; + +find -H /usr/bin/ -perm /u+s,g+ws,o+wt -type d -exec chmod u-s,g-ws,o-wt {} \; + +find -H /usr/sbin/ -perm /u+s,g+ws,o+wt -type d -exec chmod u-s,g-ws,o-wt {} \; + +find -H /usr/local/bin/ -perm /u+s,g+ws,o+wt -type d -exec chmod u-s,g-ws,o-wt {} \; + +find -H /usr/local/sbin/ -perm /u+s,g+ws,o+wt -type d -exec chmod u-s,g-ws,o-wt {} \; + + + + + + + + + + Verify that Shared Library Directories Have Restrictive Permissions + System-wide shared library directories, which contain are linked to executables +during process load time or run time, are stored in the following directories +by default: +/lib +/lib64 +/usr/lib +/usr/lib64 + +Kernel modules, which can be added to the kernel during runtime, are +stored in /lib/modules. All sub-directories in these directories +should not be group-writable or world-writable. If any file in these +directories is found to be group-writable or world-writable, correct +its permission with the following command: +$ sudo chmod go-w DIR + CCI-001499 + CIP-003-8 R6 + CM-5 + CM-5(6) + CM-5(6).1 + SRG-OS-000259-GPOS-00100 + RHEL-08-010331 + SV-251707r809345_rule + If the operating system were to allow any user to make changes to software libraries, +then those changes might be implemented without undergoing the appropriate testing +and approvals that are part of a robust change management process. + +This requirement applies to operating systems with software libraries that are accessible +and configurable, as in the case of interpreted languages. Software libraries also include +privileged programs which execute with escalated privileges. Only qualified and authorized +individuals must be allowed to obtain access to information system components for purposes +of initiating changes, including upgrades and modifications. + CCE-88692-9 + - name: Set permissions for /lib/ recursively + file: + path: /lib/ + state: directory + recurse: true + mode: g-w,o-w + tags: + - CCE-88692-9 + - DISA-STIG-RHEL-08-010331 + - NIST-800-53-CM-5 + - NIST-800-53-CM-5(6) + - NIST-800-53-CM-5(6).1 + - configure_strategy + - dir_permissions_library_dirs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Set permissions for /lib64/ recursively + file: + path: /lib64/ + state: directory + recurse: true + mode: g-w,o-w + tags: + - CCE-88692-9 + - DISA-STIG-RHEL-08-010331 + - NIST-800-53-CM-5 + - NIST-800-53-CM-5(6) + - NIST-800-53-CM-5(6).1 + - configure_strategy + - dir_permissions_library_dirs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Set permissions for /usr/lib/ recursively + file: + path: /usr/lib/ + state: directory + recurse: true + mode: g-w,o-w + tags: + - CCE-88692-9 + - DISA-STIG-RHEL-08-010331 + - NIST-800-53-CM-5 + - NIST-800-53-CM-5(6) + - NIST-800-53-CM-5(6).1 + - configure_strategy + - dir_permissions_library_dirs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Set permissions for /usr/lib64/ recursively + file: + path: /usr/lib64/ + state: directory + recurse: true + mode: g-w,o-w + tags: + - CCE-88692-9 + - DISA-STIG-RHEL-08-010331 + - NIST-800-53-CM-5 + - NIST-800-53-CM-5(6) + - NIST-800-53-CM-5(6).1 + - configure_strategy + - dir_permissions_library_dirs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + + + + +find -H /lib/ -perm /g+w,o+w -type d -exec chmod g-w,o-w {} \; + +find -H /lib64/ -perm /g+w,o+w -type d -exec chmod g-w,o-w {} \; + +find -H /usr/lib/ -perm /g+w,o+w -type d -exec chmod g-w,o-w {} \; + +find -H /usr/lib64/ -perm /g+w,o+w -type d -exec chmod g-w,o-w {} \; + + + + + + + + + + Verify that system commands files are group owned by root or a system account + System commands files are stored in the following directories by default: +/bin +/sbin +/usr/bin +/usr/sbin +/usr/local/bin +/usr/local/sbin + +All files in these directories should be owned by the root group, +or a system account. +If the directory, or any file in these directories, is found to be owned +by a group other than root or a a system account correct its ownership +with the following command: +$ sudo chgrp root FILE + CCI-001499 + CM-5(6) + CM-5(6).1 + SRG-OS-000259-GPOS-00100 + RHEL-08-010320 + SV-230259r792864_rule + If the operating system allows any user to make changes to software +libraries, then those changes might be implemented without undergoing the +appropriate testing and approvals that are part of a robust change management +process. +This requirement applies to operating systems with software libraries +that are accessible and configurable, as in the case of interpreted languages. +Software libraries also include privileged programs which execute with +escalated privileges. Only qualified and authorized individuals must be +allowed to obtain access to information system components for purposes +of initiating changes, including upgrades and modifications. + CCE-86519-6 + - name: Retrieve the system command files and set their group ownership to root + command: find -L {{ item }} ! -group root -type f -exec chgrp root '{}' \; + with_items: + - /bin + - /sbin + - /usr/bin + - /usr/sbin + - /usr/local/bin + - /usr/local/sbin + changed_when: false + failed_when: false + check_mode: false + tags: + - CCE-86519-6 + - DISA-STIG-RHEL-08-010320 + - NIST-800-53-CM-5(6) + - NIST-800-53-CM-5(6).1 + - file_groupownership_system_commands_dirs + - medium_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + +for SYSCMDFILES in /bin /sbin /usr/bin /usr/sbin /usr/local/bin /usr/local/sbin +do + find -L $SYSCMDFILES \! -group root -type f -exec chgrp root '{}' \; +done + + + + + + + + + + Verify that System Executables Have Root Ownership + System executables are stored in the following directories by default: +/bin +/sbin +/usr/bin +/usr/libexec +/usr/local/bin +/usr/local/sbin +/usr/sbin +All files in these directories should be owned by the root user. +If any file FILE in these directories is found +to be owned by a user other than root, correct its ownership with the +following command: +$ sudo chown root FILE + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + CCI-001499 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-5(6) + CM-5(6).1 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + SRG-OS-000259-GPOS-00100 + RHEL-08-010310 + SV-230258r627750_rule + System binaries are executed by privileged users as well as system services, +and restrictive permissions are necessary to ensure that their +execution of these programs cannot be co-opted. + CCE-80806-3 + - name: Read list of system executables without root ownership + command: find /bin/ /usr/bin/ /usr/local/bin/ /sbin/ /usr/sbin/ /usr/local/sbin/ + /usr/libexec \! -user root + register: no_root_system_executables + changed_when: false + failed_when: false + check_mode: false + tags: + - CCE-80806-3 + - DISA-STIG-RHEL-08-010310 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-5(6) + - NIST-800-53-CM-5(6).1 + - NIST-800-53-CM-6(a) + - file_ownership_binary_dirs + - medium_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Set ownership to root of system executables + file: + path: '{{ item }}' + owner: root + with_items: '{{ no_root_system_executables.stdout_lines }}' + when: no_root_system_executables.stdout_lines | length > 0 + tags: + - CCE-80806-3 + - DISA-STIG-RHEL-08-010310 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-5(6) + - NIST-800-53-CM-5(6).1 + - NIST-800-53-CM-6(a) + - file_ownership_binary_dirs + - medium_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + find /bin/ \ +/usr/bin/ \ +/usr/local/bin/ \ +/sbin/ \ +/usr/sbin/ \ +/usr/local/sbin/ \ +/usr/libexec \ +\! -user root -execdir chown root {} \; + + + + + + + + + + Verify that Shared Library Files Have Root Ownership + System-wide shared library files, which are linked to executables +during process load time or run time, are stored in the following directories +by default: +/lib +/lib64 +/usr/lib +/usr/lib64 + +Kernel modules, which can be added to the kernel during runtime, are also +stored in /lib/modules. All files in these directories should be +owned by the root user. If the directory, or any file in these +directories, is found to be owned by a user other than root correct its +ownership with the following command: +$ sudo chown root FILE + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + CCI-001499 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-5(6) + CM-5(6).1 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + SRG-OS-000259-GPOS-00100 + RHEL-08-010340 + SV-230261r627750_rule + Files from shared library directories are loaded into the address +space of processes (including privileged ones) or of the kernel itself at +runtime. Proper ownership is necessary to protect the integrity of the system. + CCE-80807-1 + - name: Find /lib/ file(s) matching ^.*$ recursively + command: find -H /lib/ -type f ! -uid 0 -regex "^.*$" + register: files_found + changed_when: false + failed_when: false + check_mode: false + tags: + - CCE-80807-1 + - DISA-STIG-RHEL-08-010340 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-5(6) + - NIST-800-53-CM-5(6).1 + - NIST-800-53-CM-6(a) + - configure_strategy + - file_ownership_library_dirs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure owner on /lib/ file(s) matching ^.*$ + file: + path: '{{ item }}' + owner: '0' + state: file + with_items: + - '{{ files_found.stdout_lines }}' + tags: + - CCE-80807-1 + - DISA-STIG-RHEL-08-010340 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-5(6) + - NIST-800-53-CM-5(6).1 + - NIST-800-53-CM-6(a) + - configure_strategy + - file_ownership_library_dirs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Find /lib64/ file(s) matching ^.*$ recursively + command: find -H /lib64/ -type f ! -uid 0 -regex "^.*$" + register: files_found + changed_when: false + failed_when: false + check_mode: false + tags: + - CCE-80807-1 + - DISA-STIG-RHEL-08-010340 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-5(6) + - NIST-800-53-CM-5(6).1 + - NIST-800-53-CM-6(a) + - configure_strategy + - file_ownership_library_dirs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure owner on /lib64/ file(s) matching ^.*$ + file: + path: '{{ item }}' + owner: '0' + state: file + with_items: + - '{{ files_found.stdout_lines }}' + tags: + - CCE-80807-1 + - DISA-STIG-RHEL-08-010340 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-5(6) + - NIST-800-53-CM-5(6).1 + - NIST-800-53-CM-6(a) + - configure_strategy + - file_ownership_library_dirs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Find /usr/lib/ file(s) matching ^.*$ recursively + command: find -H /usr/lib/ -type f ! -uid 0 -regex "^.*$" + register: files_found + changed_when: false + failed_when: false + check_mode: false + tags: + - CCE-80807-1 + - DISA-STIG-RHEL-08-010340 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-5(6) + - NIST-800-53-CM-5(6).1 + - NIST-800-53-CM-6(a) + - configure_strategy + - file_ownership_library_dirs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure owner on /usr/lib/ file(s) matching ^.*$ + file: + path: '{{ item }}' + owner: '0' + state: file + with_items: + - '{{ files_found.stdout_lines }}' + tags: + - CCE-80807-1 + - DISA-STIG-RHEL-08-010340 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-5(6) + - NIST-800-53-CM-5(6).1 + - NIST-800-53-CM-6(a) + - configure_strategy + - file_ownership_library_dirs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Find /usr/lib64/ file(s) matching ^.*$ recursively + command: find -H /usr/lib64/ -type f ! -uid 0 -regex "^.*$" + register: files_found + changed_when: false + failed_when: false + check_mode: false + tags: + - CCE-80807-1 + - DISA-STIG-RHEL-08-010340 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-5(6) + - NIST-800-53-CM-5(6).1 + - NIST-800-53-CM-6(a) + - configure_strategy + - file_ownership_library_dirs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure owner on /usr/lib64/ file(s) matching ^.*$ + file: + path: '{{ item }}' + owner: '0' + state: file + with_items: + - '{{ files_found.stdout_lines }}' + tags: + - CCE-80807-1 + - DISA-STIG-RHEL-08-010340 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-5(6) + - NIST-800-53-CM-5(6).1 + - NIST-800-53-CM-6(a) + - configure_strategy + - file_ownership_library_dirs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + + + +find /lib/ -type f ! -uid 0 -regex '^.*$' -exec chown 0 {} \; + +find /lib64/ -type f ! -uid 0 -regex '^.*$' -exec chown 0 {} \; + +find /usr/lib/ -type f ! -uid 0 -regex '^.*$' -exec chown 0 {} \; + +find /usr/lib64/ -type f ! -uid 0 -regex '^.*$' -exec chown 0 {} \; + + + + + + + + + + Verify that System Executables Have Restrictive Permissions + System executables are stored in the following directories by default: +/bin +/sbin +/usr/bin +/usr/libexec +/usr/local/bin +/usr/local/sbin +/usr/sbin +All files in these directories should not be group-writable or world-writable. +If any file FILE in these directories is found +to be group-writable or world-writable, correct its permission with the +following command: +$ sudo chmod go-w FILE + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + CCI-001499 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-5(6) + CM-5(6).1 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + SRG-OS-000259-GPOS-00100 + RHEL-08-010300 + SV-230257r792862_rule + System binaries are executed by privileged users, as well as system services, +and restrictive permissions are necessary to ensure execution of these programs +cannot be co-opted. + CCE-80809-7 + - name: Read list of world and group writable system executables + command: find /bin /usr/bin /usr/local/bin /sbin /usr/sbin /usr/local/sbin /usr/libexec + -perm /022 -type f + register: world_writable_library_files + changed_when: false + failed_when: false + check_mode: false + tags: + - CCE-80809-7 + - DISA-STIG-RHEL-08-010300 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-5(6) + - NIST-800-53-CM-5(6).1 + - NIST-800-53-CM-6(a) + - file_permissions_binary_dirs + - medium_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Remove world/group writability of system executables + file: + path: '{{ item }}' + mode: go-w + with_items: '{{ world_writable_library_files.stdout_lines }}' + when: world_writable_library_files.stdout_lines | length > 0 + tags: + - CCE-80809-7 + - DISA-STIG-RHEL-08-010300 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-5(6) + - NIST-800-53-CM-5(6).1 + - NIST-800-53-CM-6(a) + - file_permissions_binary_dirs + - medium_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + DIRS="/bin /usr/bin /usr/local/bin /sbin /usr/sbin /usr/local/sbin /usr/libexec" +for dirPath in $DIRS; do + find "$dirPath" -perm /022 -exec chmod go-w '{}' \; +done + + + + + + + + + + Verify that Shared Library Files Have Restrictive Permissions + System-wide shared library files, which are linked to executables +during process load time or run time, are stored in the following directories +by default: +/lib +/lib64 +/usr/lib +/usr/lib64 + +Kernel modules, which can be added to the kernel during runtime, are +stored in /lib/modules. All files in these directories +should not be group-writable or world-writable. If any file in these +directories is found to be group-writable or world-writable, correct +its permission with the following command: +$ sudo chmod go-w FILE + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + CCI-001499 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-6(a) + CM-5(6) + CM-5(6).1 + AC-6(1) + PR.AC-4 + PR.DS-5 + SRG-OS-000259-GPOS-00100 + RHEL-08-010330 + SV-230260r792867_rule + Files from shared library directories are loaded into the address +space of processes (including privileged ones) or of the kernel itself at +runtime. Restrictive permissions are necessary to protect the integrity of the system. + CCE-80815-4 + - name: Find /lib/ file(s) recursively + command: find -H /lib/ -perm /g+w,o+w -type f -regex "^.*$" + register: files_found + changed_when: false + failed_when: false + check_mode: false + tags: + - CCE-80815-4 + - DISA-STIG-RHEL-08-010330 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-5(6) + - NIST-800-53-CM-5(6).1 + - NIST-800-53-CM-6(a) + - configure_strategy + - file_permissions_library_dirs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Set permissions for /lib/ file(s) + file: + path: '{{ item }}' + mode: g-w,o-w + state: file + with_items: + - '{{ files_found.stdout_lines }}' + tags: + - CCE-80815-4 + - DISA-STIG-RHEL-08-010330 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-5(6) + - NIST-800-53-CM-5(6).1 + - NIST-800-53-CM-6(a) + - configure_strategy + - file_permissions_library_dirs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Find /lib64/ file(s) recursively + command: find -H /lib64/ -perm /g+w,o+w -type f -regex "^.*$" + register: files_found + changed_when: false + failed_when: false + check_mode: false + tags: + - CCE-80815-4 + - DISA-STIG-RHEL-08-010330 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-5(6) + - NIST-800-53-CM-5(6).1 + - NIST-800-53-CM-6(a) + - configure_strategy + - file_permissions_library_dirs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Set permissions for /lib64/ file(s) + file: + path: '{{ item }}' + mode: g-w,o-w + state: file + with_items: + - '{{ files_found.stdout_lines }}' + tags: + - CCE-80815-4 + - DISA-STIG-RHEL-08-010330 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-5(6) + - NIST-800-53-CM-5(6).1 + - NIST-800-53-CM-6(a) + - configure_strategy + - file_permissions_library_dirs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Find /usr/lib/ file(s) recursively + command: find -H /usr/lib/ -perm /g+w,o+w -type f -regex "^.*$" + register: files_found + changed_when: false + failed_when: false + check_mode: false + tags: + - CCE-80815-4 + - DISA-STIG-RHEL-08-010330 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-5(6) + - NIST-800-53-CM-5(6).1 + - NIST-800-53-CM-6(a) + - configure_strategy + - file_permissions_library_dirs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Set permissions for /usr/lib/ file(s) + file: + path: '{{ item }}' + mode: g-w,o-w + state: file + with_items: + - '{{ files_found.stdout_lines }}' + tags: + - CCE-80815-4 + - DISA-STIG-RHEL-08-010330 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-5(6) + - NIST-800-53-CM-5(6).1 + - NIST-800-53-CM-6(a) + - configure_strategy + - file_permissions_library_dirs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Find /usr/lib64/ file(s) recursively + command: find -H /usr/lib64/ -perm /g+w,o+w -type f -regex "^.*$" + register: files_found + changed_when: false + failed_when: false + check_mode: false + tags: + - CCE-80815-4 + - DISA-STIG-RHEL-08-010330 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-5(6) + - NIST-800-53-CM-5(6).1 + - NIST-800-53-CM-6(a) + - configure_strategy + - file_permissions_library_dirs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Set permissions for /usr/lib64/ file(s) + file: + path: '{{ item }}' + mode: g-w,o-w + state: file + with_items: + - '{{ files_found.stdout_lines }}' + tags: + - CCE-80815-4 + - DISA-STIG-RHEL-08-010330 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-5(6) + - NIST-800-53-CM-5(6).1 + - NIST-800-53-CM-6(a) + - configure_strategy + - file_permissions_library_dirs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + + + + +find -H /lib/ -perm /g+w,o+w -type f -regex '^.*$' -exec chmod g-w,o-w {} \; + +find -H /lib64/ -perm /g+w,o+w -type f -regex '^.*$' -exec chmod g-w,o-w {} \; + +find -H /usr/lib/ -perm /g+w,o+w -type f -regex '^.*$' -exec chmod g-w,o-w {} \; + +find -H /usr/lib64/ -perm /g+w,o+w -type f -regex '^.*$' -exec chmod g-w,o-w {} \; + + + + + + + + + + Verify the system-wide library files in directories +"/lib", "/lib64", "/usr/lib/" and "/usr/lib64" are group-owned by root. + System-wide library files are stored in the following directories +by default: +/lib +/lib64 +/usr/lib +/usr/lib64 + +All system-wide shared library files should be protected from unauthorised +access. If any of these files is not group-owned by root, correct its group-owner with +the following command: +$ sudo chgrp root FILE + CCI-001499 + CM-5(6) + CM-5(6).1 + SRG-OS-000259-GPOS-00100 + RHEL-08-010350 + SV-230262r627750_rule + If the operating system were to allow any user to make changes to software libraries, +then those changes might be implemented without undergoing the appropriate testing and +approvals that are part of a robust change management process. + +This requirement applies to operating systems with software libraries that are +accessible and configurable, as in the case of interpreted languages. Software libraries +also include privileged programs which execute with escalated privileges. Only qualified +and authorized individuals must be allowed to obtain access to information system components +for purposes of initiating changes, including upgrades and modifications. + CCE-86523-8 + - name: Ensure group owner on /lib/ recursively + file: + path: /lib/ + state: directory + recurse: true + group: '0' + tags: + - CCE-86523-8 + - DISA-STIG-RHEL-08-010350 + - NIST-800-53-CM-5(6) + - NIST-800-53-CM-5(6).1 + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - root_permissions_syslibrary_files + +- name: Ensure group owner on /lib64/ recursively + file: + path: /lib64/ + state: directory + recurse: true + group: '0' + tags: + - CCE-86523-8 + - DISA-STIG-RHEL-08-010350 + - NIST-800-53-CM-5(6) + - NIST-800-53-CM-5(6).1 + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - root_permissions_syslibrary_files + +- name: Ensure group owner on /usr/lib/ recursively + file: + path: /usr/lib/ + state: directory + recurse: true + group: '0' + tags: + - CCE-86523-8 + - DISA-STIG-RHEL-08-010350 + - NIST-800-53-CM-5(6) + - NIST-800-53-CM-5(6).1 + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - root_permissions_syslibrary_files + +- name: Ensure group owner on /usr/lib64/ recursively + file: + path: /usr/lib64/ + state: directory + recurse: true + group: '0' + tags: + - CCE-86523-8 + - DISA-STIG-RHEL-08-010350 + - NIST-800-53-CM-5(6) + - NIST-800-53-CM-5(6).1 + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - root_permissions_syslibrary_files + + + + +find -H /lib/ -type d -exec chgrp 0 {} \; + +find -H /lib64/ -type d -exec chgrp 0 {} \; + +find -H /usr/lib/ -type d -exec chgrp 0 {} \; + +find -H /usr/lib64/ -type d -exec chgrp 0 {} \; + + + + + + + + + + + + Restrict Dynamic Mounting and Unmounting of +Filesystems + Linux includes a number of facilities for the automated addition +and removal of filesystems on a running system. These facilities may be +necessary in many environments, but this capability also carries some risk -- whether direct +risk from allowing users to introduce arbitrary filesystems, +or risk that software flaws in the automated mount facility itself could +allow an attacker to compromise the system. + +This command can be used to list the types of filesystems that are +available to the currently executing kernel: +$ find /lib/modules/`uname -r`/kernel/fs -type f -name '*.ko' +If these filesystems are not required then they can be explicitly disabled +in a configuratio file in /etc/modprobe.d. + + Disable the Automounter + The autofs daemon mounts and unmounts filesystems, such as user +home directories shared via NFS, on demand. In addition, autofs can be used to handle +removable media, and the default configuration provides the cdrom device as /misc/cd. +However, this method of providing access to removable media is not common, so autofs +can almost always be disabled if NFS is not in use. Even if NFS is required, it may be +possible to configure filesystem mounts statically by editing /etc/fstab +rather than relying on the automounter. + + +The autofs service can be disabled with the following command: +$ sudo systemctl mask --now autofs.service + 1 + 12 + 15 + 16 + 5 + APO13.01 + DSS01.04 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.03 + DSS06.10 + 3.4.6 + CCI-000366 + CCI-000778 + CCI-001958 + 164.308(a)(3)(i) + 164.308(a)(3)(ii)(A) + 164.310(d)(1) + 164.310(d)(2) + 164.312(a)(1) + 164.312(a)(2)(iv) + 164.312(b) + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.2 + 4.3.3.7.4 + SR 1.1 + SR 1.10 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.6 + A.11.2.6 + A.13.1.1 + A.13.2.1 + A.18.1.4 + A.6.2.1 + A.6.2.2 + A.7.1.1 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.2 + A.9.4.3 + CM-7(a) + CM-7(b) + CM-6(a) + MP-7 + PR.AC-1 + PR.AC-3 + PR.AC-6 + PR.AC-7 + SRG-OS-000114-GPOS-00059 + SRG-OS-000378-GPOS-00163 + SRG-OS-000480-GPOS-00227 + RHEL-08-040070 + 1.1.9 + SV-230502r627750_rule + Disabling the automounter permits the administrator to +statically control filesystem mounting through /etc/fstab. + +Additionally, automatically mounting filesystems permits easy introduction of +unknown devices, thereby facilitating malicious activity. + + CCE-80873-3 + include disable_autofs + +class disable_autofs { + service {'autofs': + enable => false, + ensure => 'stopped', + } +} + + - name: Disable service autofs + block: + + - name: Disable service autofs + systemd: + name: autofs.service + enabled: 'no' + state: stopped + masked: 'yes' + ignore_errors: 'yes' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80873-3 + - DISA-STIG-RHEL-08-040070 + - NIST-800-171-3.4.6 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_autofs_disabled + +- name: Unit Socket Exists - autofs.socket + command: systemctl list-unit-files autofs.socket + args: + warn: false + register: socket_file_exists + changed_when: false + ignore_errors: true + check_mode: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80873-3 + - DISA-STIG-RHEL-08-040070 + - NIST-800-171-3.4.6 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_autofs_disabled + +- name: Disable socket autofs + systemd: + name: autofs.socket + enabled: 'no' + state: stopped + masked: 'yes' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"autofs.socket" in socket_file_exists.stdout_lines[1]' + tags: + - CCE-80873-3 + - DISA-STIG-RHEL-08-040070 + - NIST-800-171-3.4.6 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_autofs_disabled + + +[customizations.services] +disabled = ["autofs"] + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + systemd: + units: + - enabled: false + name: autofs.service + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" stop 'autofs.service' +"$SYSTEMCTL_EXEC" disable 'autofs.service' +"$SYSTEMCTL_EXEC" mask 'autofs.service' +# Disable socket activation if we have a unit file for it +if "$SYSTEMCTL_EXEC" list-unit-files | grep -q '^autofs.socket'; then + "$SYSTEMCTL_EXEC" stop 'autofs.socket' + "$SYSTEMCTL_EXEC" mask 'autofs.socket' +fi +# The service may not be running because it has been started and failed, +# so let's reset the state so OVAL checks pass. +# Service should be 'inactive', not 'failed' after reboot though. +"$SYSTEMCTL_EXEC" reset-failed 'autofs.service' || true + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Assign Password to Prevent Changes to Boot Firmware Configuration + Assign a password to the system boot firmware (historically called BIOS on PC +systems) to require a password for any configuration changes. + Assigning a password to the system boot firmware prevents anyone +with physical access from configuring the system to boot +from local media and circumvent the operating system's access controls. +For systems in physically secure locations, such as +a data center or Sensitive Compartmented Information Facility (SCIF), this risk must be weighed +against the risk of administrative personnel being unable to conduct recovery operations in +a timely fashion. + + + + Disable Booting from USB Devices in Boot Firmware + Configure the system boot firmware (historically called BIOS on PC +systems) to disallow booting from USB drives. + 12 + 16 + APO13.01 + DSS01.04 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + DSS06.03 + CCI-001250 + 4.3.3.2.2 + 4.3.3.5.2 + 4.3.3.6.6 + 4.3.3.7.2 + 4.3.3.7.4 + SR 1.1 + SR 1.13 + SR 1.2 + SR 1.4 + SR 1.5 + SR 1.9 + SR 2.1 + SR 2.6 + A.11.2.6 + A.13.1.1 + A.13.2.1 + A.6.2.1 + A.6.2.2 + A.7.1.1 + A.9.2.1 + MP-7 + CM-7(b) + CM-6(a) + PR.AC-3 + PR.AC-6 + Booting a system from a USB device would allow an attacker to +circumvent any security measures provided by the operating system. Attackers +could mount partitions and modify the configuration of the OS. + + + + Disable Kernel Support for USB via Bootloader Configuration + All USB support can be disabled by adding the nousb +argument to the kernel's boot loader configuration. To do so, +append "nousb" to the kernel line in /etc/default/grub as shown: +kernel /vmlinuz-VERSION ro vga=ext root=/dev/VolGroup00/LogVol00 rhgb quiet nousb + Disabling all kernel support for USB will cause problems for systems +with USB-based keyboards, mice, or printers. This configuration is +infeasible for systems which require USB devices, which is common. + 12 + 16 + APO13.01 + DSS01.04 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + DSS06.03 + CCI-001250 + 164.308(a)(3)(i) + 164.308(a)(3)(ii)(A) + 164.310(d)(1) + 164.310(d)(2) + 164.312(a)(1) + 164.312(a)(2)(iv) + 164.312(b) + 4.3.3.2.2 + 4.3.3.5.2 + 4.3.3.6.6 + 4.3.3.7.2 + 4.3.3.7.4 + SR 1.1 + SR 1.13 + SR 1.2 + SR 1.4 + SR 1.5 + SR 1.9 + SR 2.1 + SR 2.6 + A.11.2.6 + A.13.1.1 + A.13.2.1 + A.6.2.1 + A.6.2.2 + A.7.1.1 + A.9.2.1 + MP-7 + CM-6(a) + PR.AC-3 + PR.AC-6 + Disabling the USB subsystem within the Linux kernel at system boot will +protect against potentially malicious USB devices, although it is only practical +in specialized systems. + + + + + + + Disable Mounting of cramfs + +To configure the system to prevent the cramfs +kernel module from being loaded, add the following line to the file /etc/modprobe.d/cramfs.conf: +install cramfs /bin/true + +To configure the system to prevent the cramfs from being used, +add the following line to file /etc/modprobe.d/cramfs.conf: +blacklist cramfs + +This effectively prevents usage of this uncommon filesystem. + +The cramfs filesystem type is a compressed read-only +Linux filesystem embedded in small footprint systems. A +cramfs image can be used without having to first +decompress the image. + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + 3.4.6 + CCI-000381 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + PR.PT-3 + SRG-OS-000095-GPOS-00049 + RHEL-08-040025 + 1.1.1.1 + SV-230498r792922_rule + Removing support for unneeded filesystem types reduces the local attack surface +of the server. + + CCE-81031-7 + - name: Ensure kernel module 'cramfs' is disabled + lineinfile: + create: true + dest: /etc/modprobe.d/cramfs.conf + regexp: cramfs + line: install cramfs /bin/true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-81031-7 + - DISA-STIG-RHEL-08-040025 + - NIST-800-171-3.4.6 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - kernel_module_cramfs_disabled + - low_complexity + - low_severity + - medium_disruption + - reboot_required + +- name: Ensure kernel module 'cramfs' is blacklisted + lineinfile: + create: true + dest: /etc/modprobe.d/cramfs.conf + regexp: ^blacklist cramfs$ + line: blacklist cramfs + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-81031-7 + - DISA-STIG-RHEL-08-040025 + - NIST-800-171-3.4.6 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - kernel_module_cramfs_disabled + - low_complexity + - low_severity + - medium_disruption + - reboot_required + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,install%20cramfs%20/bin/true%0Ablacklist%20cramfs%0A + mode: 0644 + path: /etc/modprobe.d/cramfs.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if LC_ALL=C grep -q -m 1 "^install cramfs" /etc/modprobe.d/cramfs.conf ; then + + sed -i 's#^install cramfs.*#install cramfs /bin/true#g' /etc/modprobe.d/cramfs.conf +else + echo -e "\n# Disable per security requirements" >> /etc/modprobe.d/cramfs.conf + echo "install cramfs /bin/true" >> /etc/modprobe.d/cramfs.conf +fi + +if ! LC_ALL=C grep -q -m 1 "^blacklist cramfs$" /etc/modprobe.d/cramfs.conf ; then + echo "blacklist cramfs" >> /etc/modprobe.d/cramfs.conf +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable Mounting of freevxfs + +To configure the system to prevent the freevxfs +kernel module from being loaded, add the following line to the file /etc/modprobe.d/freevxfs.conf: +install freevxfs /bin/true + +To configure the system to prevent the freevxfs from being used, +add the following line to file /etc/modprobe.d/freevxfs.conf: +blacklist freevxfs + +This effectively prevents usage of this uncommon filesystem. + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + 3.4.6 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + PR.PT-3 + Linux kernel modules which implement filesystems that are not needed by the +local system should be disabled. + + - name: Ensure kernel module 'freevxfs' is disabled + lineinfile: + create: true + dest: /etc/modprobe.d/freevxfs.conf + regexp: freevxfs + line: install freevxfs /bin/true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-171-3.4.6 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - kernel_module_freevxfs_disabled + - low_complexity + - low_severity + - medium_disruption + - reboot_required + +- name: Ensure kernel module 'freevxfs' is blacklisted + lineinfile: + create: true + dest: /etc/modprobe.d/freevxfs.conf + regexp: ^blacklist freevxfs$ + line: blacklist freevxfs + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-171-3.4.6 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - kernel_module_freevxfs_disabled + - low_complexity + - low_severity + - medium_disruption + - reboot_required + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,install%20freevxfs%20/bin/true%0Ablacklist%20freevxfs%0A + mode: 0644 + path: /etc/modprobe.d/freevxfs.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if LC_ALL=C grep -q -m 1 "^install freevxfs" /etc/modprobe.d/freevxfs.conf ; then + + sed -i 's#^install freevxfs.*#install freevxfs /bin/true#g' /etc/modprobe.d/freevxfs.conf +else + echo -e "\n# Disable per security requirements" >> /etc/modprobe.d/freevxfs.conf + echo "install freevxfs /bin/true" >> /etc/modprobe.d/freevxfs.conf +fi + +if ! LC_ALL=C grep -q -m 1 "^blacklist freevxfs$" /etc/modprobe.d/freevxfs.conf ; then + echo "blacklist freevxfs" >> /etc/modprobe.d/freevxfs.conf +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + Disable Mounting of hfs + +To configure the system to prevent the hfs +kernel module from being loaded, add the following line to the file /etc/modprobe.d/hfs.conf: +install hfs /bin/true + +To configure the system to prevent the hfs from being used, +add the following line to file /etc/modprobe.d/hfs.conf: +blacklist hfs + +This effectively prevents usage of this uncommon filesystem. + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + 3.4.6 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + PR.PT-3 + Linux kernel modules which implement filesystems that are not needed by the +local system should be disabled. + + - name: Ensure kernel module 'hfs' is disabled + lineinfile: + create: true + dest: /etc/modprobe.d/hfs.conf + regexp: hfs + line: install hfs /bin/true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-171-3.4.6 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - kernel_module_hfs_disabled + - low_complexity + - low_severity + - medium_disruption + - reboot_required + +- name: Ensure kernel module 'hfs' is blacklisted + lineinfile: + create: true + dest: /etc/modprobe.d/hfs.conf + regexp: ^blacklist hfs$ + line: blacklist hfs + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-171-3.4.6 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - kernel_module_hfs_disabled + - low_complexity + - low_severity + - medium_disruption + - reboot_required + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,install%20hfs%20/bin/true%0Ablacklist%20hfs%0A + mode: 0644 + path: /etc/modprobe.d/hfs.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if LC_ALL=C grep -q -m 1 "^install hfs" /etc/modprobe.d/hfs.conf ; then + + sed -i 's#^install hfs.*#install hfs /bin/true#g' /etc/modprobe.d/hfs.conf +else + echo -e "\n# Disable per security requirements" >> /etc/modprobe.d/hfs.conf + echo "install hfs /bin/true" >> /etc/modprobe.d/hfs.conf +fi + +if ! LC_ALL=C grep -q -m 1 "^blacklist hfs$" /etc/modprobe.d/hfs.conf ; then + echo "blacklist hfs" >> /etc/modprobe.d/hfs.conf +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + Disable Mounting of hfsplus + +To configure the system to prevent the hfsplus +kernel module from being loaded, add the following line to the file /etc/modprobe.d/hfsplus.conf: +install hfsplus /bin/true + +To configure the system to prevent the hfsplus from being used, +add the following line to file /etc/modprobe.d/hfsplus.conf: +blacklist hfsplus + +This effectively prevents usage of this uncommon filesystem. + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + 3.4.6 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + PR.PT-3 + Linux kernel modules which implement filesystems that are not needed by the +local system should be disabled. + + - name: Ensure kernel module 'hfsplus' is disabled + lineinfile: + create: true + dest: /etc/modprobe.d/hfsplus.conf + regexp: hfsplus + line: install hfsplus /bin/true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-171-3.4.6 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - kernel_module_hfsplus_disabled + - low_complexity + - low_severity + - medium_disruption + - reboot_required + +- name: Ensure kernel module 'hfsplus' is blacklisted + lineinfile: + create: true + dest: /etc/modprobe.d/hfsplus.conf + regexp: ^blacklist hfsplus$ + line: blacklist hfsplus + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-171-3.4.6 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - kernel_module_hfsplus_disabled + - low_complexity + - low_severity + - medium_disruption + - reboot_required + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,install%20hfsplus%20/bin/true%0Ablacklist%20hfsplus%0A + mode: 0644 + path: /etc/modprobe.d/hfsplus.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if LC_ALL=C grep -q -m 1 "^install hfsplus" /etc/modprobe.d/hfsplus.conf ; then + + sed -i 's#^install hfsplus.*#install hfsplus /bin/true#g' /etc/modprobe.d/hfsplus.conf +else + echo -e "\n# Disable per security requirements" >> /etc/modprobe.d/hfsplus.conf + echo "install hfsplus /bin/true" >> /etc/modprobe.d/hfsplus.conf +fi + +if ! LC_ALL=C grep -q -m 1 "^blacklist hfsplus$" /etc/modprobe.d/hfsplus.conf ; then + echo "blacklist hfsplus" >> /etc/modprobe.d/hfsplus.conf +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + Disable Mounting of jffs2 + +To configure the system to prevent the jffs2 +kernel module from being loaded, add the following line to the file /etc/modprobe.d/jffs2.conf: +install jffs2 /bin/true + +To configure the system to prevent the jffs2 from being used, +add the following line to file /etc/modprobe.d/jffs2.conf: +blacklist jffs2 + +This effectively prevents usage of this uncommon filesystem. + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + 3.4.6 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + PR.PT-3 + Linux kernel modules which implement filesystems that are not needed by the +local system should be disabled. + + - name: Ensure kernel module 'jffs2' is disabled + lineinfile: + create: true + dest: /etc/modprobe.d/jffs2.conf + regexp: jffs2 + line: install jffs2 /bin/true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-171-3.4.6 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - kernel_module_jffs2_disabled + - low_complexity + - low_severity + - medium_disruption + - reboot_required + +- name: Ensure kernel module 'jffs2' is blacklisted + lineinfile: + create: true + dest: /etc/modprobe.d/jffs2.conf + regexp: ^blacklist jffs2$ + line: blacklist jffs2 + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-171-3.4.6 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - kernel_module_jffs2_disabled + - low_complexity + - low_severity + - medium_disruption + - reboot_required + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,install%20jffs2%20/bin/true%0Ablacklist%20jffs2%0A + mode: 0644 + path: /etc/modprobe.d/jffs2.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if LC_ALL=C grep -q -m 1 "^install jffs2" /etc/modprobe.d/jffs2.conf ; then + + sed -i 's#^install jffs2.*#install jffs2 /bin/true#g' /etc/modprobe.d/jffs2.conf +else + echo -e "\n# Disable per security requirements" >> /etc/modprobe.d/jffs2.conf + echo "install jffs2 /bin/true" >> /etc/modprobe.d/jffs2.conf +fi + +if ! LC_ALL=C grep -q -m 1 "^blacklist jffs2$" /etc/modprobe.d/jffs2.conf ; then + echo "blacklist jffs2" >> /etc/modprobe.d/jffs2.conf +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + Disable Mounting of squashfs + +To configure the system to prevent the squashfs +kernel module from being loaded, add the following line to the file /etc/modprobe.d/squashfs.conf: +install squashfs /bin/true + +To configure the system to prevent the squashfs from being used, +add the following line to file /etc/modprobe.d/squashfs.conf: +blacklist squashfs + +This effectively prevents usage of this uncommon filesystem. + +The squashfs filesystem type is a compressed read-only Linux +filesystem embedded in small footprint systems (similar to +cramfs). A squashfs image can be used without having +to first decompress the image. + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + 3.4.6 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + PR.PT-3 + 1.1.1.2 + Removing support for unneeded filesystem types reduces the local attack +surface of the system. + + CCE-83498-6 + - name: Ensure kernel module 'squashfs' is disabled + lineinfile: + create: true + dest: /etc/modprobe.d/squashfs.conf + regexp: squashfs + line: install squashfs /bin/true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83498-6 + - NIST-800-171-3.4.6 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - kernel_module_squashfs_disabled + - low_complexity + - low_severity + - medium_disruption + - reboot_required + +- name: Ensure kernel module 'squashfs' is blacklisted + lineinfile: + create: true + dest: /etc/modprobe.d/squashfs.conf + regexp: ^blacklist squashfs$ + line: blacklist squashfs + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83498-6 + - NIST-800-171-3.4.6 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - kernel_module_squashfs_disabled + - low_complexity + - low_severity + - medium_disruption + - reboot_required + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,install%20squashfs%20/bin/true%0Ablacklist%20squashfs%0A + mode: 0644 + path: /etc/modprobe.d/squashfs.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if LC_ALL=C grep -q -m 1 "^install squashfs" /etc/modprobe.d/squashfs.conf ; then + + sed -i 's#^install squashfs.*#install squashfs /bin/true#g' /etc/modprobe.d/squashfs.conf +else + echo -e "\n# Disable per security requirements" >> /etc/modprobe.d/squashfs.conf + echo "install squashfs /bin/true" >> /etc/modprobe.d/squashfs.conf +fi + +if ! LC_ALL=C grep -q -m 1 "^blacklist squashfs$" /etc/modprobe.d/squashfs.conf ; then + echo "blacklist squashfs" >> /etc/modprobe.d/squashfs.conf +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + Disable Mounting of udf + +To configure the system to prevent the udf +kernel module from being loaded, add the following line to the file /etc/modprobe.d/udf.conf: +install udf /bin/true + +To configure the system to prevent the udf from being used, +add the following line to file /etc/modprobe.d/udf.conf: +blacklist udf + +This effectively prevents usage of this uncommon filesystem. + +The udf filesystem type is the universal disk format +used to implement the ISO/IEC 13346 and ECMA-167 specifications. +This is an open vendor filesystem type for data storage on a broad +range of media. This filesystem type is neccessary to support +writing DVDs and newer optical disc formats. + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + 3.4.6 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + PR.PT-3 + 1.1.1.3 + Removing support for unneeded filesystem types reduces the local +attack surface of the system. + + CCE-82729-5 + - name: Ensure kernel module 'udf' is disabled + lineinfile: + create: true + dest: /etc/modprobe.d/udf.conf + regexp: udf + line: install udf /bin/true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82729-5 + - NIST-800-171-3.4.6 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - kernel_module_udf_disabled + - low_complexity + - low_severity + - medium_disruption + - reboot_required + +- name: Ensure kernel module 'udf' is blacklisted + lineinfile: + create: true + dest: /etc/modprobe.d/udf.conf + regexp: ^blacklist udf$ + line: blacklist udf + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82729-5 + - NIST-800-171-3.4.6 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - kernel_module_udf_disabled + - low_complexity + - low_severity + - medium_disruption + - reboot_required + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,install%20udf%20/bin/true%0Ablacklist%20udf%0A + mode: 0644 + path: /etc/modprobe.d/udf.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if LC_ALL=C grep -q -m 1 "^install udf" /etc/modprobe.d/udf.conf ; then + + sed -i 's#^install udf.*#install udf /bin/true#g' /etc/modprobe.d/udf.conf +else + echo -e "\n# Disable per security requirements" >> /etc/modprobe.d/udf.conf + echo "install udf /bin/true" >> /etc/modprobe.d/udf.conf +fi + +if ! LC_ALL=C grep -q -m 1 "^blacklist udf$" /etc/modprobe.d/udf.conf ; then + echo "blacklist udf" >> /etc/modprobe.d/udf.conf +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + Disable Modprobe Loading of USB Storage Driver + To prevent USB storage devices from being used, configure the kernel module loading system +to prevent automatic loading of the USB storage driver. + +To configure the system to prevent the usb-storage +kernel module from being loaded, add the following line to the file /etc/modprobe.d/usb-storage.conf: +install usb-storage /bin/true + +To configure the system to prevent the usb-storage from being used, +add the following line to file /etc/modprobe.d/usb-storage.conf: +blacklist usb-storage + +This will prevent the modprobe program from loading the usb-storage +module, but will not prevent an administrator (or another program) from using the +insmod program to load the module manually. + 1 + 12 + 15 + 16 + 5 + APO13.01 + DSS01.04 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.03 + DSS06.10 + 3.1.21 + CCI-000366 + CCI-000778 + CCI-001958 + 164.308(a)(3)(i) + 164.308(a)(3)(ii)(A) + 164.310(d)(1) + 164.310(d)(2) + 164.312(a)(1) + 164.312(a)(2)(iv) + 164.312(b) + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.2 + 4.3.3.7.4 + SR 1.1 + SR 1.10 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.6 + A.11.2.6 + A.13.1.1 + A.13.2.1 + A.18.1.4 + A.6.2.1 + A.6.2.2 + A.7.1.1 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.2 + A.9.4.3 + CM-7(a) + CM-7(b) + CM-6(a) + MP-7 + PR.AC-1 + PR.AC-3 + PR.AC-6 + PR.AC-7 + SRG-OS-000114-GPOS-00059 + SRG-OS-000378-GPOS-00163 + SRG-OS-000480-GPOS-00227 + RHEL-08-040080 + 1.1.10 + SV-230503r809319_rule + USB storage devices such as thumb drives can be used to introduce +malicious software. + + CCE-80835-2 + - name: Ensure kernel module 'usb-storage' is disabled + lineinfile: + create: true + dest: /etc/modprobe.d/usb-storage.conf + regexp: usb-storage + line: install usb-storage /bin/true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80835-2 + - DISA-STIG-RHEL-08-040080 + - NIST-800-171-3.1.21 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - disable_strategy + - kernel_module_usb-storage_disabled + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + +- name: Ensure kernel module 'usb-storage' is blacklisted + lineinfile: + create: true + dest: /etc/modprobe.d/usb-storage.conf + regexp: ^blacklist usb-storage$ + line: blacklist usb-storage + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80835-2 + - DISA-STIG-RHEL-08-040080 + - NIST-800-171-3.1.21 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - disable_strategy + - kernel_module_usb-storage_disabled + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,install%20usb-storage%20/bin/true%0Ablacklist%20usb-storage%0A + mode: 0644 + path: /etc/modprobe.d/usb-storage.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if LC_ALL=C grep -q -m 1 "^install usb-storage" /etc/modprobe.d/usb-storage.conf ; then + + sed -i 's#^install usb-storage.*#install usb-storage /bin/true#g' /etc/modprobe.d/usb-storage.conf +else + echo -e "\n# Disable per security requirements" >> /etc/modprobe.d/usb-storage.conf + echo "install usb-storage /bin/true" >> /etc/modprobe.d/usb-storage.conf +fi + +if ! LC_ALL=C grep -q -m 1 "^blacklist usb-storage$" /etc/modprobe.d/usb-storage.conf ; then + echo "blacklist usb-storage" >> /etc/modprobe.d/usb-storage.conf +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable Mounting of vFAT filesystems + +To configure the system to prevent the vfat +kernel module from being loaded, add the following line to the file /etc/modprobe.d/vfat.conf: +install vfat /bin/true + +To configure the system to prevent the vfat from being used, +add the following line to file /etc/modprobe.d/vfat.conf: +blacklist vfat + +This effectively prevents usage of this uncommon filesystem. + +The vFAT filesystem format is primarily used on older +windows systems and portable USB drives or flash modules. It comes +in three types FAT12, FAT16, and FAT32 +all of which are supported by the vfat kernel module. + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + 3.4.6 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + PR.PT-3 + 1.1.1.2 + Removing support for unneeded filesystems reduces the local attack +surface of the system. + + CCE-82170-2 + - name: Ensure kernel module 'vfat' is disabled + lineinfile: + create: true + dest: /etc/modprobe.d/vfat.conf + regexp: vfat + line: install vfat /bin/true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82170-2 + - NIST-800-171-3.4.6 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - kernel_module_vfat_disabled + - low_complexity + - low_severity + - medium_disruption + - reboot_required + +- name: Ensure kernel module 'vfat' is blacklisted + lineinfile: + create: true + dest: /etc/modprobe.d/vfat.conf + regexp: ^blacklist vfat$ + line: blacklist vfat + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82170-2 + - NIST-800-171-3.4.6 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - kernel_module_vfat_disabled + - low_complexity + - low_severity + - medium_disruption + - reboot_required + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,install%20vfat%20/bin/true%0Ablacklist%20vfat%0A + mode: 0644 + path: /etc/modprobe.d/vfat.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if LC_ALL=C grep -q -m 1 "^install vfat" /etc/modprobe.d/vfat.conf ; then + + sed -i 's#^install vfat.*#install vfat /bin/true#g' /etc/modprobe.d/vfat.conf +else + echo -e "\n# Disable per security requirements" >> /etc/modprobe.d/vfat.conf + echo "install vfat /bin/true" >> /etc/modprobe.d/vfat.conf +fi + +if ! LC_ALL=C grep -q -m 1 "^blacklist vfat$" /etc/modprobe.d/vfat.conf ; then + echo "blacklist vfat" >> /etc/modprobe.d/vfat.conf +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + Restrict Partition Mount Options + System partitions can be mounted with certain options +that limit what files on those partitions can do. These options +are set in the /etc/fstab configuration file, and can be +used to make certain types of malicious behavior more difficult. + + Value for hidepid option + The hidepid mount option is applicable to /proc and is used to control who can access +the information in /proc/[pid] directories. The option can have one of the following +values: +0: Everybody may access all /proc/[pid] directories. +1: Users may not access files and subdirectories inside any /proc/[pid] directories + but their own. The /proc/[pid] directories themselves remain visible. +2: Same as for mode 1, but in addition the /proc/[pid] directories belonging to other + users become invisible. + 0 + 1 + 2 + 2 + + + Removable Partition + This value is used by the checks mount_option_nodev_removable_partitions, mount_option_nodev_removable_partitions, +and mount_option_nodev_removable_partitions to ensure that the correct mount options are set on partitions mounted from +removable media such as CD-ROMs, USB keys, and floppy drives. This value should be modified to reflect any removable +partitions that are required on the local system. + /dev/cdrom + + + Add nosuid Option to /boot/efi + The nosuid mount option can be used to prevent +execution of setuid programs in /boot/efi. The SUID and SGID permissions +should not be required on the boot partition. +Add the nosuid option to the fourth column of +/etc/fstab for the line which controls mounting of +/boot/efi. + CCI-000366 + CM-6(b) + CM-6.1(iv) + SRG-OS-000480-GPOS-00227 + RHEL-08-010572 + SV-244530r809336_rule + The presence of SUID and SGID executables should be tightly controlled. Users +should not be able to execute SUID or SGID binaries from boot partitions. + + CCE-86038-7 + - name: 'Add nosuid Option to /boot/efi: Check information associated to mountpoint' + command: findmnt '/boot/efi' + register: device_name + failed_when: device_name.rc > 1 + changed_when: false + when: ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", + "container"] and "/boot/efi" in ansible_mounts | map(attribute="mount") | list + ) + tags: + - CCE-86038-7 + - DISA-STIG-RHEL-08-010572 + - NIST-800-53-CM-6(b) + - NIST-800-53-CM-6.1(iv) + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_boot_efi_nosuid + - no_reboot_needed + +- name: 'Add nosuid Option to /boot/efi: Create mount_info dictionary variable' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - '{{ device_name.stdout_lines[0].split() | list | lower }}' + - '{{ device_name.stdout_lines[1].split() | list }}' + when: + - ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + and "/boot/efi" in ansible_mounts | map(attribute="mount") | list ) + - device_name.stdout is defined and device_name.stdout_lines is defined + - (device_name.stdout | length > 0) + tags: + - CCE-86038-7 + - DISA-STIG-RHEL-08-010572 + - NIST-800-53-CM-6(b) + - NIST-800-53-CM-6.1(iv) + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_boot_efi_nosuid + - no_reboot_needed + +- name: 'Add nosuid Option to /boot/efi: If /boot/efi not mounted, craft mount_info + manually' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - - target + - source + - fstype + - options + - - /boot/efi + - '' + - '' + - defaults + when: + - ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + and "/boot/efi" in ansible_mounts | map(attribute="mount") | list ) + - ("" | length == 0) + - (device_name.stdout | length == 0) + tags: + - CCE-86038-7 + - DISA-STIG-RHEL-08-010572 + - NIST-800-53-CM-6(b) + - NIST-800-53-CM-6.1(iv) + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_boot_efi_nosuid + - no_reboot_needed + +- name: 'Add nosuid Option to /boot/efi: Make sure nosuid option is part of the to + /boot/efi options' + set_fact: + mount_info: '{{ mount_info | combine( {''options'':''''~mount_info.options~'',nosuid'' + }) }}' + when: + - ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + and "/boot/efi" in ansible_mounts | map(attribute="mount") | list ) + - mount_info is defined and "nosuid" not in mount_info.options + tags: + - CCE-86038-7 + - DISA-STIG-RHEL-08-010572 + - NIST-800-53-CM-6(b) + - NIST-800-53-CM-6.1(iv) + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_boot_efi_nosuid + - no_reboot_needed + +- name: 'Add nosuid Option to /boot/efi: Ensure /boot/efi is mounted with nosuid option' + mount: + path: /boot/efi + src: '{{ mount_info.source }}' + opts: '{{ mount_info.options }}' + state: mounted + fstype: '{{ mount_info.fstype }}' + when: + - ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + and "/boot/efi" in ansible_mounts | map(attribute="mount") | list ) + - (device_name.stdout is defined and (device_name.stdout | length > 0)) or ("" | + length == 0) + tags: + - CCE-86038-7 + - DISA-STIG-RHEL-08-010572 + - NIST-800-53-CM-6(b) + - NIST-800-53-CM-6.1(iv) + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_boot_efi_nosuid + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if ( [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && [ -f /sys/firmware/efi ] ); then + +function perform_remediation { + + + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" /boot/efi)" + + # If the mount point is not in /etc/fstab, get previous mount options from /etc/mtab + if [ "$(grep -c "$mount_point_match_regexp" /etc/fstab)" -eq 0 ]; then + # runtime opts without some automatic kernel/userspace-added defaults + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/mtab | head -1 | awk '{print $4}' \ + | sed -E "s/(rw|defaults|seclabel|nosuid)(,|$)//g;s/,$//") + [ "$previous_mount_opts" ] && previous_mount_opts+="," + echo " /boot/efi defaults,${previous_mount_opts}nosuid 0 0" >> /etc/fstab + # If the mount_opt option is not already in the mount point's /etc/fstab entry, add it + elif [ "$(grep "$mount_point_match_regexp" /etc/fstab | grep -c "nosuid")" -eq 0 ]; then + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/fstab | awk '{print $4}') + sed -i "s|\(${mount_point_match_regexp}.*${previous_mount_opts}\)|\1,nosuid|" /etc/fstab + fi + + + if mkdir -p "/boot/efi"; then + if mountpoint -q "/boot/efi"; then + mount -o remount --target "/boot/efi" + else + mount --target "/boot/efi" + fi + fi +} + +perform_remediation + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Add noauto Option to /boot + The noauto mount option is used to prevent automatic mounting of th +/boot partition. +Add the noauto option to the fourth column of +/etc/fstab for the line which controls mounting of +/boot. + Although contents of the /boot partition should not be needed +during normal system operation, they might need to be accessible during +system maintenance and upgrades. Make sure that applying this rule will +not break upgrade or maintenance processes affecting the system. + BP28(R12) + The /boot partition contains the kernel and the bootloader. Access +to the partition after the boot process finishes should not be needed. Files +contained within this partition can be analysed and gained information can +be used for exploit creation. + + CCE-83345-9 + +part /boot --mountoptions="noauto" + + - name: 'Add noauto Option to /boot: Check information associated to mountpoint' + command: findmnt --fstab '/boot' + register: device_name + failed_when: device_name.rc > 1 + changed_when: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83345-9 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_boot_noauto + - no_reboot_needed + +- name: 'Add noauto Option to /boot: Create mount_info dictionary variable' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - '{{ device_name.stdout_lines[0].split() | list | lower }}' + - '{{ device_name.stdout_lines[1].split() | list }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - device_name.stdout is defined and device_name.stdout_lines is defined + - (device_name.stdout | length > 0) + tags: + - CCE-83345-9 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_boot_noauto + - no_reboot_needed + +- name: 'Add noauto Option to /boot: If /boot not mounted, craft mount_info manually' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - - target + - source + - fstype + - options + - - /boot + - '' + - '' + - defaults + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ("--fstab" | length == 0) + - (device_name.stdout | length == 0) + tags: + - CCE-83345-9 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_boot_noauto + - no_reboot_needed + +- name: 'Add noauto Option to /boot: Make sure noauto option is part of the to /boot + options' + set_fact: + mount_info: '{{ mount_info | combine( {''options'':''''~mount_info.options~'',noauto'' + }) }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - mount_info is defined and "noauto" not in mount_info.options + tags: + - CCE-83345-9 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_boot_noauto + - no_reboot_needed + +- name: 'Add noauto Option to /boot: Ensure /boot is mounted with noauto option' + mount: + path: /boot + src: '{{ mount_info.source }}' + opts: '{{ mount_info.options }}' + state: mounted + fstype: '{{ mount_info.fstype }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - (device_name.stdout is defined and (device_name.stdout | length > 0)) or ("--fstab" + | length == 0) + tags: + - CCE-83345-9 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_boot_noauto + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +function perform_remediation { + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" "/boot")" + + grep "$mount_point_match_regexp" -q /etc/fstab \ + || { echo "The mount point '/boot' is not even in /etc/fstab, so we can't set up mount options" >&2; + echo "Not remediating, because there is no record of /boot in /etc/fstab" >&2; return 1; } + + + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" /boot)" + + # If the mount point is not in /etc/fstab, get previous mount options from /etc/mtab + if [ "$(grep -c "$mount_point_match_regexp" /etc/fstab)" -eq 0 ]; then + # runtime opts without some automatic kernel/userspace-added defaults + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/mtab | head -1 | awk '{print $4}' \ + | sed -E "s/(rw|defaults|seclabel|noauto)(,|$)//g;s/,$//") + [ "$previous_mount_opts" ] && previous_mount_opts+="," + echo " /boot defaults,${previous_mount_opts}noauto 0 0" >> /etc/fstab + # If the mount_opt option is not already in the mount point's /etc/fstab entry, add it + elif [ "$(grep "$mount_point_match_regexp" /etc/fstab | grep -c "noauto")" -eq 0 ]; then + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/fstab | awk '{print $4}') + sed -i "s|\(${mount_point_match_regexp}.*${previous_mount_opts}\)|\1,noauto|" /etc/fstab + fi + + + if mkdir -p "/boot"; then + if mountpoint -q "/boot"; then + mount -o remount --target "/boot" + else + mount --target "/boot" + fi + fi +} + +perform_remediation + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Add nodev Option to /boot + The nodev mount option can be used to prevent device files from +being created in /boot. +Legitimate character and block devices should exist only in +the /dev directory on the root partition or within chroot +jails built for system services. +Add the nodev option to the fourth column of +/etc/fstab for the line which controls mounting of +/boot. + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + AC-6 + AC-6(1) + MP-7 + PR.IP-1 + PR.PT-2 + PR.PT-3 + SRG-OS-000368-GPOS-00154 + The only legitimate location for device files is the /dev directory +located on the root partition. The only exception to this is chroot jails. + + CCE-82941-6 + +part /boot --mountoptions="nodev" + + - name: 'Add nodev Option to /boot: Check information associated to mountpoint' + command: findmnt --fstab '/boot' + register: device_name + failed_when: device_name.rc > 1 + changed_when: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82941-6 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_boot_nodev + - no_reboot_needed + +- name: 'Add nodev Option to /boot: Create mount_info dictionary variable' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - '{{ device_name.stdout_lines[0].split() | list | lower }}' + - '{{ device_name.stdout_lines[1].split() | list }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - device_name.stdout is defined and device_name.stdout_lines is defined + - (device_name.stdout | length > 0) + tags: + - CCE-82941-6 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_boot_nodev + - no_reboot_needed + +- name: 'Add nodev Option to /boot: If /boot not mounted, craft mount_info manually' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - - target + - source + - fstype + - options + - - /boot + - '' + - '' + - defaults + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ("--fstab" | length == 0) + - (device_name.stdout | length == 0) + tags: + - CCE-82941-6 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_boot_nodev + - no_reboot_needed + +- name: 'Add nodev Option to /boot: Make sure nodev option is part of the to /boot + options' + set_fact: + mount_info: '{{ mount_info | combine( {''options'':''''~mount_info.options~'',nodev'' + }) }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - mount_info is defined and "nodev" not in mount_info.options + tags: + - CCE-82941-6 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_boot_nodev + - no_reboot_needed + +- name: 'Add nodev Option to /boot: Ensure /boot is mounted with nodev option' + mount: + path: /boot + src: '{{ mount_info.source }}' + opts: '{{ mount_info.options }}' + state: mounted + fstype: '{{ mount_info.fstype }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - (device_name.stdout is defined and (device_name.stdout | length > 0)) or ("--fstab" + | length == 0) + tags: + - CCE-82941-6 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_boot_nodev + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +function perform_remediation { + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" "/boot")" + + grep "$mount_point_match_regexp" -q /etc/fstab \ + || { echo "The mount point '/boot' is not even in /etc/fstab, so we can't set up mount options" >&2; + echo "Not remediating, because there is no record of /boot in /etc/fstab" >&2; return 1; } + + + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" /boot)" + + # If the mount point is not in /etc/fstab, get previous mount options from /etc/mtab + if [ "$(grep -c "$mount_point_match_regexp" /etc/fstab)" -eq 0 ]; then + # runtime opts without some automatic kernel/userspace-added defaults + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/mtab | head -1 | awk '{print $4}' \ + | sed -E "s/(rw|defaults|seclabel|nodev)(,|$)//g;s/,$//") + [ "$previous_mount_opts" ] && previous_mount_opts+="," + echo " /boot defaults,${previous_mount_opts}nodev 0 0" >> /etc/fstab + # If the mount_opt option is not already in the mount point's /etc/fstab entry, add it + elif [ "$(grep "$mount_point_match_regexp" /etc/fstab | grep -c "nodev")" -eq 0 ]; then + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/fstab | awk '{print $4}') + sed -i "s|\(${mount_point_match_regexp}.*${previous_mount_opts}\)|\1,nodev|" /etc/fstab + fi + + + if mkdir -p "/boot"; then + if mountpoint -q "/boot"; then + mount -o remount --target "/boot" + else + mount --target "/boot" + fi + fi +} + +perform_remediation + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Add noexec Option to /boot + The noexec mount option can be used to prevent binaries from being +executed out of /boot. +Add the noexec option to the fourth column of +/etc/fstab for the line which controls mounting of +/boot. + BP28(R12) + The /boot partition contains the kernel and the bootloader. No +binaries should be executed from this partition after the booting process +finishes. + + CCE-83316-0 + +part /boot --mountoptions="noexec" + + - name: 'Add noexec Option to /boot: Check information associated to mountpoint' + command: findmnt --fstab '/boot' + register: device_name + failed_when: device_name.rc > 1 + changed_when: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83316-0 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_boot_noexec + - no_reboot_needed + +- name: 'Add noexec Option to /boot: Create mount_info dictionary variable' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - '{{ device_name.stdout_lines[0].split() | list | lower }}' + - '{{ device_name.stdout_lines[1].split() | list }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - device_name.stdout is defined and device_name.stdout_lines is defined + - (device_name.stdout | length > 0) + tags: + - CCE-83316-0 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_boot_noexec + - no_reboot_needed + +- name: 'Add noexec Option to /boot: If /boot not mounted, craft mount_info manually' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - - target + - source + - fstype + - options + - - /boot + - '' + - '' + - defaults + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ("--fstab" | length == 0) + - (device_name.stdout | length == 0) + tags: + - CCE-83316-0 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_boot_noexec + - no_reboot_needed + +- name: 'Add noexec Option to /boot: Make sure noexec option is part of the to /boot + options' + set_fact: + mount_info: '{{ mount_info | combine( {''options'':''''~mount_info.options~'',noexec'' + }) }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - mount_info is defined and "noexec" not in mount_info.options + tags: + - CCE-83316-0 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_boot_noexec + - no_reboot_needed + +- name: 'Add noexec Option to /boot: Ensure /boot is mounted with noexec option' + mount: + path: /boot + src: '{{ mount_info.source }}' + opts: '{{ mount_info.options }}' + state: mounted + fstype: '{{ mount_info.fstype }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - (device_name.stdout is defined and (device_name.stdout | length > 0)) or ("--fstab" + | length == 0) + tags: + - CCE-83316-0 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_boot_noexec + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +function perform_remediation { + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" "/boot")" + + grep "$mount_point_match_regexp" -q /etc/fstab \ + || { echo "The mount point '/boot' is not even in /etc/fstab, so we can't set up mount options" >&2; + echo "Not remediating, because there is no record of /boot in /etc/fstab" >&2; return 1; } + + + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" /boot)" + + # If the mount point is not in /etc/fstab, get previous mount options from /etc/mtab + if [ "$(grep -c "$mount_point_match_regexp" /etc/fstab)" -eq 0 ]; then + # runtime opts without some automatic kernel/userspace-added defaults + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/mtab | head -1 | awk '{print $4}' \ + | sed -E "s/(rw|defaults|seclabel|noexec)(,|$)//g;s/,$//") + [ "$previous_mount_opts" ] && previous_mount_opts+="," + echo " /boot defaults,${previous_mount_opts}noexec 0 0" >> /etc/fstab + # If the mount_opt option is not already in the mount point's /etc/fstab entry, add it + elif [ "$(grep "$mount_point_match_regexp" /etc/fstab | grep -c "noexec")" -eq 0 ]; then + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/fstab | awk '{print $4}') + sed -i "s|\(${mount_point_match_regexp}.*${previous_mount_opts}\)|\1,noexec|" /etc/fstab + fi + + + if mkdir -p "/boot"; then + if mountpoint -q "/boot"; then + mount -o remount --target "/boot" + else + mount --target "/boot" + fi + fi +} + +perform_remediation + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Add nosuid Option to /boot + The nosuid mount option can be used to prevent +execution of setuid programs in /boot. The SUID and SGID permissions +should not be required on the boot partition. +Add the nosuid option to the fourth column of +/etc/fstab for the line which controls mounting of +/boot. + BP28(R12) + CCI-000366 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + AC-6 + AC-6(1) + MP-7 + PR.IP-1 + PR.PT-2 + PR.PT-3 + SRG-OS-000368-GPOS-00154 + SRG-OS-000480-GPOS-00227 + RHEL-08-010571 + SV-230300r743959_rule + The presence of SUID and SGID executables should be tightly controlled. Users +should not be able to execute SUID or SGID binaries from boot partitions. + + CCE-81033-3 + +part /boot --mountoptions="nosuid" + + - name: 'Add nosuid Option to /boot: Check information associated to mountpoint' + command: findmnt --fstab '/boot' + register: device_name + failed_when: device_name.rc > 1 + changed_when: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-81033-3 + - DISA-STIG-RHEL-08-010571 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_boot_nosuid + - no_reboot_needed + +- name: 'Add nosuid Option to /boot: Create mount_info dictionary variable' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - '{{ device_name.stdout_lines[0].split() | list | lower }}' + - '{{ device_name.stdout_lines[1].split() | list }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - device_name.stdout is defined and device_name.stdout_lines is defined + - (device_name.stdout | length > 0) + tags: + - CCE-81033-3 + - DISA-STIG-RHEL-08-010571 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_boot_nosuid + - no_reboot_needed + +- name: 'Add nosuid Option to /boot: If /boot not mounted, craft mount_info manually' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - - target + - source + - fstype + - options + - - /boot + - '' + - '' + - defaults + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ("--fstab" | length == 0) + - (device_name.stdout | length == 0) + tags: + - CCE-81033-3 + - DISA-STIG-RHEL-08-010571 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_boot_nosuid + - no_reboot_needed + +- name: 'Add nosuid Option to /boot: Make sure nosuid option is part of the to /boot + options' + set_fact: + mount_info: '{{ mount_info | combine( {''options'':''''~mount_info.options~'',nosuid'' + }) }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - mount_info is defined and "nosuid" not in mount_info.options + tags: + - CCE-81033-3 + - DISA-STIG-RHEL-08-010571 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_boot_nosuid + - no_reboot_needed + +- name: 'Add nosuid Option to /boot: Ensure /boot is mounted with nosuid option' + mount: + path: /boot + src: '{{ mount_info.source }}' + opts: '{{ mount_info.options }}' + state: mounted + fstype: '{{ mount_info.fstype }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - (device_name.stdout is defined and (device_name.stdout | length > 0)) or ("--fstab" + | length == 0) + tags: + - CCE-81033-3 + - DISA-STIG-RHEL-08-010571 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_boot_nosuid + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +function perform_remediation { + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" "/boot")" + + grep "$mount_point_match_regexp" -q /etc/fstab \ + || { echo "The mount point '/boot' is not even in /etc/fstab, so we can't set up mount options" >&2; + echo "Not remediating, because there is no record of /boot in /etc/fstab" >&2; return 1; } + + + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" /boot)" + + # If the mount point is not in /etc/fstab, get previous mount options from /etc/mtab + if [ "$(grep -c "$mount_point_match_regexp" /etc/fstab)" -eq 0 ]; then + # runtime opts without some automatic kernel/userspace-added defaults + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/mtab | head -1 | awk '{print $4}' \ + | sed -E "s/(rw|defaults|seclabel|nosuid)(,|$)//g;s/,$//") + [ "$previous_mount_opts" ] && previous_mount_opts+="," + echo " /boot defaults,${previous_mount_opts}nosuid 0 0" >> /etc/fstab + # If the mount_opt option is not already in the mount point's /etc/fstab entry, add it + elif [ "$(grep "$mount_point_match_regexp" /etc/fstab | grep -c "nosuid")" -eq 0 ]; then + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/fstab | awk '{print $4}') + sed -i "s|\(${mount_point_match_regexp}.*${previous_mount_opts}\)|\1,nosuid|" /etc/fstab + fi + + + if mkdir -p "/boot"; then + if mountpoint -q "/boot"; then + mount -o remount --target "/boot" + else + mount --target "/boot" + fi + fi +} + +perform_remediation + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Add nodev Option to /dev/shm + The nodev mount option can be used to prevent creation of device +files in /dev/shm. Legitimate character and block devices should +not exist within temporary directories like /dev/shm. +Add the nodev option to the fourth column of +/etc/fstab for the line which controls mounting of +/dev/shm. + 11 + 13 + 14 + 3 + 8 + 9 + APO13.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS05.06 + DSS06.06 + CCI-001764 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.11.2.9 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.8.2.1 + A.8.2.2 + A.8.2.3 + A.8.3.1 + A.8.3.3 + A.9.1.2 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + AC-6 + AC-6(1) + MP-7 + PR.IP-1 + PR.PT-2 + PR.PT-3 + SRG-OS-000368-GPOS-00154 + RHEL-08-040120 + 1.1.8.1 + SV-230508r627750_rule + The only legitimate location for device files is the /dev directory +located on the root partition. The only exception to this is chroot jails. + + CCE-80837-8 + - name: 'Add nodev Option to /dev/shm: Check information associated to mountpoint' + command: findmnt '/dev/shm' + register: device_name + failed_when: device_name.rc > 1 + changed_when: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80837-8 + - DISA-STIG-RHEL-08-040120 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_dev_shm_nodev + - no_reboot_needed + +- name: 'Add nodev Option to /dev/shm: Create mount_info dictionary variable' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - '{{ device_name.stdout_lines[0].split() | list | lower }}' + - '{{ device_name.stdout_lines[1].split() | list }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - device_name.stdout is defined and device_name.stdout_lines is defined + - (device_name.stdout | length > 0) + tags: + - CCE-80837-8 + - DISA-STIG-RHEL-08-040120 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_dev_shm_nodev + - no_reboot_needed + +- name: 'Add nodev Option to /dev/shm: If /dev/shm not mounted, craft mount_info manually' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - - target + - source + - fstype + - options + - - /dev/shm + - tmpfs + - tmpfs + - defaults + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ("" | length == 0) + - (device_name.stdout | length == 0) + tags: + - CCE-80837-8 + - DISA-STIG-RHEL-08-040120 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_dev_shm_nodev + - no_reboot_needed + +- name: 'Add nodev Option to /dev/shm: Make sure nodev option is part of the to /dev/shm + options' + set_fact: + mount_info: '{{ mount_info | combine( {''options'':''''~mount_info.options~'',nodev'' + }) }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - mount_info is defined and "nodev" not in mount_info.options + tags: + - CCE-80837-8 + - DISA-STIG-RHEL-08-040120 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_dev_shm_nodev + - no_reboot_needed + +- name: 'Add nodev Option to /dev/shm: Ensure /dev/shm is mounted with nodev option' + mount: + path: /dev/shm + src: '{{ mount_info.source }}' + opts: '{{ mount_info.options }}' + state: mounted + fstype: '{{ mount_info.fstype }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - (device_name.stdout is defined and (device_name.stdout | length > 0)) or ("" | + length == 0) + tags: + - CCE-80837-8 + - DISA-STIG-RHEL-08-040120 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_dev_shm_nodev + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +function perform_remediation { + + + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" /dev/shm)" + + # If the mount point is not in /etc/fstab, get previous mount options from /etc/mtab + if [ "$(grep -c "$mount_point_match_regexp" /etc/fstab)" -eq 0 ]; then + # runtime opts without some automatic kernel/userspace-added defaults + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/mtab | head -1 | awk '{print $4}' \ + | sed -E "s/(rw|defaults|seclabel|nodev)(,|$)//g;s/,$//") + [ "$previous_mount_opts" ] && previous_mount_opts+="," + echo "tmpfs /dev/shm tmpfs defaults,${previous_mount_opts}nodev 0 0" >> /etc/fstab + # If the mount_opt option is not already in the mount point's /etc/fstab entry, add it + elif [ "$(grep "$mount_point_match_regexp" /etc/fstab | grep -c "nodev")" -eq 0 ]; then + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/fstab | awk '{print $4}') + sed -i "s|\(${mount_point_match_regexp}.*${previous_mount_opts}\)|\1,nodev|" /etc/fstab + fi + + + if mkdir -p "/dev/shm"; then + if mountpoint -q "/dev/shm"; then + mount -o remount --target "/dev/shm" + else + mount --target "/dev/shm" + fi + fi +} + +perform_remediation + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Add noexec Option to /dev/shm + The noexec mount option can be used to prevent binaries +from being executed out of /dev/shm. +It can be dangerous to allow the execution of binaries +from world-writable temporary storage directories such as /dev/shm. +Add the noexec option to the fourth column of +/etc/fstab for the line which controls mounting of +/dev/shm. + 11 + 13 + 14 + 3 + 8 + 9 + APO13.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS05.06 + DSS06.06 + CCI-001764 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.11.2.9 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.8.2.1 + A.8.2.2 + A.8.2.3 + A.8.3.1 + A.8.3.3 + A.9.1.2 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + AC-6 + AC-6(1) + MP-7 + PR.IP-1 + PR.PT-2 + PR.PT-3 + SRG-OS-000368-GPOS-00154 + RHEL-08-040122 + 1.1.8.2 + SV-230510r627750_rule + Allowing users to execute binaries from world-writable directories +such as /dev/shm can expose the system to potential compromise. + + CCE-80838-6 + - name: 'Add noexec Option to /dev/shm: Check information associated to mountpoint' + command: findmnt '/dev/shm' + register: device_name + failed_when: device_name.rc > 1 + changed_when: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80838-6 + - DISA-STIG-RHEL-08-040122 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_dev_shm_noexec + - no_reboot_needed + +- name: 'Add noexec Option to /dev/shm: Create mount_info dictionary variable' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - '{{ device_name.stdout_lines[0].split() | list | lower }}' + - '{{ device_name.stdout_lines[1].split() | list }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - device_name.stdout is defined and device_name.stdout_lines is defined + - (device_name.stdout | length > 0) + tags: + - CCE-80838-6 + - DISA-STIG-RHEL-08-040122 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_dev_shm_noexec + - no_reboot_needed + +- name: 'Add noexec Option to /dev/shm: If /dev/shm not mounted, craft mount_info + manually' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - - target + - source + - fstype + - options + - - /dev/shm + - tmpfs + - tmpfs + - defaults + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ("" | length == 0) + - (device_name.stdout | length == 0) + tags: + - CCE-80838-6 + - DISA-STIG-RHEL-08-040122 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_dev_shm_noexec + - no_reboot_needed + +- name: 'Add noexec Option to /dev/shm: Make sure noexec option is part of the to + /dev/shm options' + set_fact: + mount_info: '{{ mount_info | combine( {''options'':''''~mount_info.options~'',noexec'' + }) }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - mount_info is defined and "noexec" not in mount_info.options + tags: + - CCE-80838-6 + - DISA-STIG-RHEL-08-040122 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_dev_shm_noexec + - no_reboot_needed + +- name: 'Add noexec Option to /dev/shm: Ensure /dev/shm is mounted with noexec option' + mount: + path: /dev/shm + src: '{{ mount_info.source }}' + opts: '{{ mount_info.options }}' + state: mounted + fstype: '{{ mount_info.fstype }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - (device_name.stdout is defined and (device_name.stdout | length > 0)) or ("" | + length == 0) + tags: + - CCE-80838-6 + - DISA-STIG-RHEL-08-040122 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_dev_shm_noexec + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +function perform_remediation { + + + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" /dev/shm)" + + # If the mount point is not in /etc/fstab, get previous mount options from /etc/mtab + if [ "$(grep -c "$mount_point_match_regexp" /etc/fstab)" -eq 0 ]; then + # runtime opts without some automatic kernel/userspace-added defaults + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/mtab | head -1 | awk '{print $4}' \ + | sed -E "s/(rw|defaults|seclabel|noexec)(,|$)//g;s/,$//") + [ "$previous_mount_opts" ] && previous_mount_opts+="," + echo "tmpfs /dev/shm tmpfs defaults,${previous_mount_opts}noexec 0 0" >> /etc/fstab + # If the mount_opt option is not already in the mount point's /etc/fstab entry, add it + elif [ "$(grep "$mount_point_match_regexp" /etc/fstab | grep -c "noexec")" -eq 0 ]; then + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/fstab | awk '{print $4}') + sed -i "s|\(${mount_point_match_regexp}.*${previous_mount_opts}\)|\1,noexec|" /etc/fstab + fi + + + if mkdir -p "/dev/shm"; then + if mountpoint -q "/dev/shm"; then + mount -o remount --target "/dev/shm" + else + mount --target "/dev/shm" + fi + fi +} + +perform_remediation + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Add nosuid Option to /dev/shm + The nosuid mount option can be used to prevent execution +of setuid programs in /dev/shm. The SUID and SGID permissions should not +be required in these world-writable directories. +Add the nosuid option to the fourth column of +/etc/fstab for the line which controls mounting of +/dev/shm. + 11 + 13 + 14 + 3 + 8 + 9 + APO13.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS05.06 + DSS06.06 + CCI-001764 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.11.2.9 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.8.2.1 + A.8.2.2 + A.8.2.3 + A.8.3.1 + A.8.3.3 + A.9.1.2 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + AC-6 + AC-6(1) + MP-7 + PR.IP-1 + PR.PT-2 + PR.PT-3 + SRG-OS-000368-GPOS-00154 + RHEL-08-040121 + 1.1.8.3 + SV-230509r627750_rule + The presence of SUID and SGID executables should be tightly controlled. Users +should not be able to execute SUID or SGID binaries from temporary storage partitions. + + CCE-80839-4 + - name: 'Add nosuid Option to /dev/shm: Check information associated to mountpoint' + command: findmnt '/dev/shm' + register: device_name + failed_when: device_name.rc > 1 + changed_when: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80839-4 + - DISA-STIG-RHEL-08-040121 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_dev_shm_nosuid + - no_reboot_needed + +- name: 'Add nosuid Option to /dev/shm: Create mount_info dictionary variable' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - '{{ device_name.stdout_lines[0].split() | list | lower }}' + - '{{ device_name.stdout_lines[1].split() | list }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - device_name.stdout is defined and device_name.stdout_lines is defined + - (device_name.stdout | length > 0) + tags: + - CCE-80839-4 + - DISA-STIG-RHEL-08-040121 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_dev_shm_nosuid + - no_reboot_needed + +- name: 'Add nosuid Option to /dev/shm: If /dev/shm not mounted, craft mount_info + manually' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - - target + - source + - fstype + - options + - - /dev/shm + - tmpfs + - tmpfs + - defaults + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ("" | length == 0) + - (device_name.stdout | length == 0) + tags: + - CCE-80839-4 + - DISA-STIG-RHEL-08-040121 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_dev_shm_nosuid + - no_reboot_needed + +- name: 'Add nosuid Option to /dev/shm: Make sure nosuid option is part of the to + /dev/shm options' + set_fact: + mount_info: '{{ mount_info | combine( {''options'':''''~mount_info.options~'',nosuid'' + }) }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - mount_info is defined and "nosuid" not in mount_info.options + tags: + - CCE-80839-4 + - DISA-STIG-RHEL-08-040121 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_dev_shm_nosuid + - no_reboot_needed + +- name: 'Add nosuid Option to /dev/shm: Ensure /dev/shm is mounted with nosuid option' + mount: + path: /dev/shm + src: '{{ mount_info.source }}' + opts: '{{ mount_info.options }}' + state: mounted + fstype: '{{ mount_info.fstype }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - (device_name.stdout is defined and (device_name.stdout | length > 0)) or ("" | + length == 0) + tags: + - CCE-80839-4 + - DISA-STIG-RHEL-08-040121 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_dev_shm_nosuid + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +function perform_remediation { + + + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" /dev/shm)" + + # If the mount point is not in /etc/fstab, get previous mount options from /etc/mtab + if [ "$(grep -c "$mount_point_match_regexp" /etc/fstab)" -eq 0 ]; then + # runtime opts without some automatic kernel/userspace-added defaults + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/mtab | head -1 | awk '{print $4}' \ + | sed -E "s/(rw|defaults|seclabel|nosuid)(,|$)//g;s/,$//") + [ "$previous_mount_opts" ] && previous_mount_opts+="," + echo "tmpfs /dev/shm tmpfs defaults,${previous_mount_opts}nosuid 0 0" >> /etc/fstab + # If the mount_opt option is not already in the mount point's /etc/fstab entry, add it + elif [ "$(grep "$mount_point_match_regexp" /etc/fstab | grep -c "nosuid")" -eq 0 ]; then + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/fstab | awk '{print $4}') + sed -i "s|\(${mount_point_match_regexp}.*${previous_mount_opts}\)|\1,nosuid|" /etc/fstab + fi + + + if mkdir -p "/dev/shm"; then + if mountpoint -q "/dev/shm"; then + mount -o remount --target "/dev/shm" + else + mount --target "/dev/shm" + fi + fi +} + +perform_remediation + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Add nodev Option to /home + The nodev mount option can be used to prevent device files from +being created in /home. +Legitimate character and block devices should exist only in +the /dev directory on the root partition or within chroot +jails built for system services. +Add the nodev option to the fourth column of +/etc/fstab for the line which controls mounting of +/home. + BP28(R12) + SRG-OS-000368-GPOS-00154 + 1.1.7.2 + The only legitimate location for device files is the /dev directory +located on the root partition. The only exception to this is chroot jails. + + CCE-81048-1 + +part /home --mountoptions="nodev" + + - name: 'Add nodev Option to /home: Check information associated to mountpoint' + command: findmnt --fstab '/home' + register: device_name + failed_when: device_name.rc > 1 + changed_when: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-81048-1 + - configure_strategy + - high_disruption + - low_complexity + - mount_option_home_nodev + - no_reboot_needed + - unknown_severity + +- name: 'Add nodev Option to /home: Create mount_info dictionary variable' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - '{{ device_name.stdout_lines[0].split() | list | lower }}' + - '{{ device_name.stdout_lines[1].split() | list }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - device_name.stdout is defined and device_name.stdout_lines is defined + - (device_name.stdout | length > 0) + tags: + - CCE-81048-1 + - configure_strategy + - high_disruption + - low_complexity + - mount_option_home_nodev + - no_reboot_needed + - unknown_severity + +- name: 'Add nodev Option to /home: If /home not mounted, craft mount_info manually' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - - target + - source + - fstype + - options + - - /home + - '' + - '' + - defaults + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ("--fstab" | length == 0) + - (device_name.stdout | length == 0) + tags: + - CCE-81048-1 + - configure_strategy + - high_disruption + - low_complexity + - mount_option_home_nodev + - no_reboot_needed + - unknown_severity + +- name: 'Add nodev Option to /home: Make sure nodev option is part of the to /home + options' + set_fact: + mount_info: '{{ mount_info | combine( {''options'':''''~mount_info.options~'',nodev'' + }) }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - mount_info is defined and "nodev" not in mount_info.options + tags: + - CCE-81048-1 + - configure_strategy + - high_disruption + - low_complexity + - mount_option_home_nodev + - no_reboot_needed + - unknown_severity + +- name: 'Add nodev Option to /home: Ensure /home is mounted with nodev option' + mount: + path: /home + src: '{{ mount_info.source }}' + opts: '{{ mount_info.options }}' + state: mounted + fstype: '{{ mount_info.fstype }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - (device_name.stdout is defined and (device_name.stdout | length > 0)) or ("--fstab" + | length == 0) + tags: + - CCE-81048-1 + - configure_strategy + - high_disruption + - low_complexity + - mount_option_home_nodev + - no_reboot_needed + - unknown_severity + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +function perform_remediation { + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" "/home")" + + grep "$mount_point_match_regexp" -q /etc/fstab \ + || { echo "The mount point '/home' is not even in /etc/fstab, so we can't set up mount options" >&2; + echo "Not remediating, because there is no record of /home in /etc/fstab" >&2; return 1; } + + + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" /home)" + + # If the mount point is not in /etc/fstab, get previous mount options from /etc/mtab + if [ "$(grep -c "$mount_point_match_regexp" /etc/fstab)" -eq 0 ]; then + # runtime opts without some automatic kernel/userspace-added defaults + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/mtab | head -1 | awk '{print $4}' \ + | sed -E "s/(rw|defaults|seclabel|nodev)(,|$)//g;s/,$//") + [ "$previous_mount_opts" ] && previous_mount_opts+="," + echo " /home defaults,${previous_mount_opts}nodev 0 0" >> /etc/fstab + # If the mount_opt option is not already in the mount point's /etc/fstab entry, add it + elif [ "$(grep "$mount_point_match_regexp" /etc/fstab | grep -c "nodev")" -eq 0 ]; then + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/fstab | awk '{print $4}') + sed -i "s|\(${mount_point_match_regexp}.*${previous_mount_opts}\)|\1,nodev|" /etc/fstab + fi + + + if mkdir -p "/home"; then + if mountpoint -q "/home"; then + mount -o remount --target "/home" + else + mount --target "/home" + fi + fi +} + +perform_remediation + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Add noexec Option to /home + The noexec mount option can be used to prevent binaries from being +executed out of /home. +Add the noexec option to the fourth column of +/etc/fstab for the line which controls mounting of +/home. + BP28(R12) + CCI-000366 + CM-6(b) + SRG-OS-000480-GPOS-00227 + RHEL-08-010590 + SV-230302r627750_rule + The /home directory contains data of individual users. Binaries in +this directory should not be considered as trusted and users should not be +able to execute them. + + CCE-83328-5 + +part /home --mountoptions="noexec" + + - name: 'Add noexec Option to /home: Check information associated to mountpoint' + command: findmnt --fstab '/home' + register: device_name + failed_when: device_name.rc > 1 + changed_when: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83328-5 + - DISA-STIG-RHEL-08-010590 + - NIST-800-53-CM-6(b) + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_home_noexec + - no_reboot_needed + +- name: 'Add noexec Option to /home: Create mount_info dictionary variable' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - '{{ device_name.stdout_lines[0].split() | list | lower }}' + - '{{ device_name.stdout_lines[1].split() | list }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - device_name.stdout is defined and device_name.stdout_lines is defined + - (device_name.stdout | length > 0) + tags: + - CCE-83328-5 + - DISA-STIG-RHEL-08-010590 + - NIST-800-53-CM-6(b) + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_home_noexec + - no_reboot_needed + +- name: 'Add noexec Option to /home: If /home not mounted, craft mount_info manually' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - - target + - source + - fstype + - options + - - /home + - '' + - '' + - defaults + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ("--fstab" | length == 0) + - (device_name.stdout | length == 0) + tags: + - CCE-83328-5 + - DISA-STIG-RHEL-08-010590 + - NIST-800-53-CM-6(b) + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_home_noexec + - no_reboot_needed + +- name: 'Add noexec Option to /home: Make sure noexec option is part of the to /home + options' + set_fact: + mount_info: '{{ mount_info | combine( {''options'':''''~mount_info.options~'',noexec'' + }) }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - mount_info is defined and "noexec" not in mount_info.options + tags: + - CCE-83328-5 + - DISA-STIG-RHEL-08-010590 + - NIST-800-53-CM-6(b) + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_home_noexec + - no_reboot_needed + +- name: 'Add noexec Option to /home: Ensure /home is mounted with noexec option' + mount: + path: /home + src: '{{ mount_info.source }}' + opts: '{{ mount_info.options }}' + state: mounted + fstype: '{{ mount_info.fstype }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - (device_name.stdout is defined and (device_name.stdout | length > 0)) or ("--fstab" + | length == 0) + tags: + - CCE-83328-5 + - DISA-STIG-RHEL-08-010590 + - NIST-800-53-CM-6(b) + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_home_noexec + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +function perform_remediation { + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" "/home")" + + grep "$mount_point_match_regexp" -q /etc/fstab \ + || { echo "The mount point '/home' is not even in /etc/fstab, so we can't set up mount options" >&2; + echo "Not remediating, because there is no record of /home in /etc/fstab" >&2; return 1; } + + + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" /home)" + + # If the mount point is not in /etc/fstab, get previous mount options from /etc/mtab + if [ "$(grep -c "$mount_point_match_regexp" /etc/fstab)" -eq 0 ]; then + # runtime opts without some automatic kernel/userspace-added defaults + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/mtab | head -1 | awk '{print $4}' \ + | sed -E "s/(rw|defaults|seclabel|noexec)(,|$)//g;s/,$//") + [ "$previous_mount_opts" ] && previous_mount_opts+="," + echo " /home defaults,${previous_mount_opts}noexec 0 0" >> /etc/fstab + # If the mount_opt option is not already in the mount point's /etc/fstab entry, add it + elif [ "$(grep "$mount_point_match_regexp" /etc/fstab | grep -c "noexec")" -eq 0 ]; then + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/fstab | awk '{print $4}') + sed -i "s|\(${mount_point_match_regexp}.*${previous_mount_opts}\)|\1,noexec|" /etc/fstab + fi + + + if mkdir -p "/home"; then + if mountpoint -q "/home"; then + mount -o remount --target "/home" + else + mount --target "/home" + fi + fi +} + +perform_remediation + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Add nosuid Option to /home + The nosuid mount option can be used to prevent +execution of setuid programs in /home. The SUID and SGID permissions +should not be required in these user data directories. +Add the nosuid option to the fourth column of +/etc/fstab for the line which controls mounting of +/home. + BP28(R12) + 11 + 13 + 14 + 3 + 8 + 9 + APO13.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS05.06 + DSS06.06 + CCI-000366 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.11.2.9 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.8.2.1 + A.8.2.2 + A.8.2.3 + A.8.3.1 + A.8.3.3 + A.9.1.2 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + AC-6 + AC-6(1) + MP-7 + PR.IP-1 + PR.PT-2 + PR.PT-3 + SRG-OS-000368-GPOS-00154 + SRG-OS-000480-GPOS-00227 + RHEL-08-010570 + 1.1.7.3 + SV-230299r627750_rule + The presence of SUID and SGID executables should be tightly controlled. Users +should not be able to execute SUID or SGID binaries from user home directory partitions. + + CCE-81050-7 + +part /home --mountoptions="nosuid" + + - name: 'Add nosuid Option to /home: Check information associated to mountpoint' + command: findmnt --fstab '/home' + register: device_name + failed_when: device_name.rc > 1 + changed_when: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-81050-7 + - DISA-STIG-RHEL-08-010570 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_home_nosuid + - no_reboot_needed + +- name: 'Add nosuid Option to /home: Create mount_info dictionary variable' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - '{{ device_name.stdout_lines[0].split() | list | lower }}' + - '{{ device_name.stdout_lines[1].split() | list }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - device_name.stdout is defined and device_name.stdout_lines is defined + - (device_name.stdout | length > 0) + tags: + - CCE-81050-7 + - DISA-STIG-RHEL-08-010570 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_home_nosuid + - no_reboot_needed + +- name: 'Add nosuid Option to /home: If /home not mounted, craft mount_info manually' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - - target + - source + - fstype + - options + - - /home + - '' + - '' + - defaults + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ("--fstab" | length == 0) + - (device_name.stdout | length == 0) + tags: + - CCE-81050-7 + - DISA-STIG-RHEL-08-010570 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_home_nosuid + - no_reboot_needed + +- name: 'Add nosuid Option to /home: Make sure nosuid option is part of the to /home + options' + set_fact: + mount_info: '{{ mount_info | combine( {''options'':''''~mount_info.options~'',nosuid'' + }) }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - mount_info is defined and "nosuid" not in mount_info.options + tags: + - CCE-81050-7 + - DISA-STIG-RHEL-08-010570 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_home_nosuid + - no_reboot_needed + +- name: 'Add nosuid Option to /home: Ensure /home is mounted with nosuid option' + mount: + path: /home + src: '{{ mount_info.source }}' + opts: '{{ mount_info.options }}' + state: mounted + fstype: '{{ mount_info.fstype }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - (device_name.stdout is defined and (device_name.stdout | length > 0)) or ("--fstab" + | length == 0) + tags: + - CCE-81050-7 + - DISA-STIG-RHEL-08-010570 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_home_nosuid + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +function perform_remediation { + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" "/home")" + + grep "$mount_point_match_regexp" -q /etc/fstab \ + || { echo "The mount point '/home' is not even in /etc/fstab, so we can't set up mount options" >&2; + echo "Not remediating, because there is no record of /home in /etc/fstab" >&2; return 1; } + + + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" /home)" + + # If the mount point is not in /etc/fstab, get previous mount options from /etc/mtab + if [ "$(grep -c "$mount_point_match_regexp" /etc/fstab)" -eq 0 ]; then + # runtime opts without some automatic kernel/userspace-added defaults + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/mtab | head -1 | awk '{print $4}' \ + | sed -E "s/(rw|defaults|seclabel|nosuid)(,|$)//g;s/,$//") + [ "$previous_mount_opts" ] && previous_mount_opts+="," + echo " /home defaults,${previous_mount_opts}nosuid 0 0" >> /etc/fstab + # If the mount_opt option is not already in the mount point's /etc/fstab entry, add it + elif [ "$(grep "$mount_point_match_regexp" /etc/fstab | grep -c "nosuid")" -eq 0 ]; then + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/fstab | awk '{print $4}') + sed -i "s|\(${mount_point_match_regexp}.*${previous_mount_opts}\)|\1,nosuid|" /etc/fstab + fi + + + if mkdir -p "/home"; then + if mountpoint -q "/home"; then + mount -o remount --target "/home" + else + mount --target "/home" + fi + fi +} + +perform_remediation + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Add nodev Option to Non-Root Local Partitions + The nodev mount option prevents files from being interpreted as +character or block devices. Legitimate character and block devices should +exist only in the /dev directory on the root partition or within +chroot jails built for system services. +Add the nodev option to the fourth column of +/etc/fstab for the line which controls mounting of + + any non-root local partitions. + BP28(R12) + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + CCI-000366 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + AC-6 + AC-6(1) + MP-7 + PR.IP-1 + PR.PT-3 + SRG-OS-000368-GPOS-00154 + SRG-OS-000480-GPOS-00227 + RHEL-08-010580 + SV-230301r627750_rule + The nodev mount option prevents files from being +interpreted as character or block devices. The only legitimate location +for device files is the /dev directory located on the root partition. +The only exception to this is chroot jails, for which it is not advised +to set nodev on these filesystems. + + CCE-82069-6 + - name: Ensure non-root local partitions are mounted with nodev option + mount: + path: '{{ item.mount }}' + src: '{{ item.device }}' + opts: '{{ item.options }},nodev' + state: mounted + fstype: '{{ item.fstype }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - item.mount is match('/\w') + - item.options is not search('nodev') + with_items: + - '{{ ansible_facts.mounts }}' + tags: + - CCE-82069-6 + - DISA-STIG-RHEL-08-010580 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_nodev_nonroot_local_partitions + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +MOUNT_OPTION="nodev" +# Create array of local non-root partitions +readarray -t partitions_records < <(findmnt --mtab --raw --evaluate | grep "^/\w" | grep "\s/dev/\w") + +for partition_record in "${partitions_records[@]}"; do + # Get all important information for fstab + mount_point="$(echo ${partition_record} | cut -d " " -f1)" + device="$(echo ${partition_record} | cut -d " " -f2)" + device_type="$(echo ${partition_record} | cut -d " " -f3)" + # device and device_type will be used only in case when the device doesn't have fstab record + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" $mount_point)" + +# If the mount point is not in /etc/fstab, get previous mount options from /etc/mtab +if [ "$(grep -c "$mount_point_match_regexp" /etc/fstab)" -eq 0 ]; then + # runtime opts without some automatic kernel/userspace-added defaults + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/mtab | head -1 | awk '{print $4}' \ + | sed -E "s/(rw|defaults|seclabel|$MOUNT_OPTION)(,|$)//g;s/,$//") + [ "$previous_mount_opts" ] && previous_mount_opts+="," + echo "$device $mount_point $device_type defaults,${previous_mount_opts}$MOUNT_OPTION 0 0" >> /etc/fstab +# If the mount_opt option is not already in the mount point's /etc/fstab entry, add it +elif [ "$(grep "$mount_point_match_regexp" /etc/fstab | grep -c "$MOUNT_OPTION")" -eq 0 ]; then + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/fstab | awk '{print $4}') + sed -i "s|\(${mount_point_match_regexp}.*${previous_mount_opts}\)|\1,$MOUNT_OPTION|" /etc/fstab +fi + if mkdir -p "$mount_point"; then + if mountpoint -q "$mount_point"; then + mount -o remount --target "$mount_point" + else + mount --target "$mount_point" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Add nodev Option to Removable Media Partitions + The nodev mount option prevents files from being +interpreted as character or block devices. +Legitimate character and block devices should exist only in +the /dev directory on the root partition or within chroot +jails built for system services. +Add the nodev option to the fourth column of +/etc/fstab for the line which controls mounting of + + any removable media partitions. + 11 + 12 + 13 + 14 + 16 + 3 + 8 + 9 + APO13.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.04 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.06 + DSS05.07 + DSS06.03 + DSS06.06 + CCI-000366 + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.11.2.6 + A.11.2.9 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.13.1.1 + A.13.2.1 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.6.2.1 + A.6.2.2 + A.7.1.1 + A.8.2.1 + A.8.2.2 + A.8.2.3 + A.8.3.1 + A.8.3.3 + A.9.1.2 + A.9.2.1 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + AC-6 + AC-6(1) + MP-7 + PR.AC-3 + PR.AC-6 + PR.IP-1 + PR.PT-2 + PR.PT-3 + SRG-OS-000480-GPOS-00227 + RHEL-08-010600 + 1.1.18 + SV-230303r627750_rule + The only legitimate location for device files is the /dev directory +located on the root partition. An exception to this is chroot jails, and it is +not advised to set nodev on partitions which contain their root +filesystems. + + CCE-82742-8 + - name: XCCDF Value var_removable_partition # promote to variable + set_fact: + var_removable_partition: !!str + tags: + - always + +- name: Ensure permission nodev are set on var_removable_partition + lineinfile: + path: /etc/fstab + regexp: ^\s*({{ var_removable_partition }})\s+([^\s]*)\s+([^\s]*)\s+([^\s]*)(.*)$ + backrefs: true + line: \1 \2 \3 \4,nodev \5 + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82742-8 + - DISA-STIG-RHEL-08-010600 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_nodev_removable_partitions + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_removable_partition='' + + +device_regex="^\s*$var_removable_partition\s\+" +mount_option="nodev" + +if grep -q $device_regex /etc/fstab ; then + previous_opts=$(grep $device_regex /etc/fstab | awk '{print $4}') + sed -i "s|\($device_regex.*$previous_opts\)|\1,$mount_option|" /etc/fstab +else + echo "Not remediating, because there is no record of $var_removable_partition in /etc/fstab" >&2 + return 1 +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Add noexec Option to Removable Media Partitions + The noexec mount option prevents the direct execution of binaries +on the mounted filesystem. Preventing the direct execution of binaries from +removable media (such as a USB key) provides a defense against malicious +software that may be present on such untrusted media. +Add the noexec option to the fourth column of +/etc/fstab for the line which controls mounting of + + any removable media partitions. + 11 + 12 + 13 + 14 + 16 + 3 + 8 + 9 + APO13.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.04 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.06 + DSS05.07 + DSS06.03 + DSS06.06 + CCI-000087 + CCI-000366 + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.11.2.6 + A.11.2.9 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.13.1.1 + A.13.2.1 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.6.2.1 + A.6.2.2 + A.7.1.1 + A.8.2.1 + A.8.2.2 + A.8.2.3 + A.8.3.1 + A.8.3.3 + A.9.1.2 + A.9.2.1 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + AC-6 + AC-6(1) + MP-7 + PR.AC-3 + PR.AC-6 + PR.IP-1 + PR.PT-2 + PR.PT-3 + SRG-OS-000480-GPOS-00227 + RHEL-08-010610 + 1.1.20 + SV-230304r627750_rule + Allowing users to execute binaries from removable media such as USB keys exposes +the system to potential compromise. + + CCE-82746-9 + - name: XCCDF Value var_removable_partition # promote to variable + set_fact: + var_removable_partition: !!str + tags: + - always + +- name: Ensure permission noexec are set on var_removable_partition + lineinfile: + path: /etc/fstab + regexp: ^\s*({{ var_removable_partition }})\s+([^\s]*)\s+([^\s]*)\s+([^\s]*)(.*)$ + backrefs: true + line: \1 \2 \3 \4,noexec \5 + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82746-9 + - DISA-STIG-RHEL-08-010610 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_noexec_removable_partitions + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_removable_partition='' + + +device_regex="^\s*$var_removable_partition\s\+" +mount_option="noexec" + +if grep -q $device_regex /etc/fstab ; then + previous_opts=$(grep $device_regex /etc/fstab | awk '{print $4}') + sed -i "s|\($device_regex.*$previous_opts\)|\1,$mount_option|" /etc/fstab +else + echo "Not remediating, because there is no record of $var_removable_partition in /etc/fstab" >&2 + return 1 +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Add nosuid Option to Removable Media Partitions + The nosuid mount option prevents set-user-identifier (SUID) +and set-group-identifier (SGID) permissions from taking effect. These permissions +allow users to execute binaries with the same permissions as the owner and group +of the file respectively. Users should not be allowed to introduce SUID and SGID +files into the system via partitions mounted from removeable media. +Add the nosuid option to the fourth column of +/etc/fstab for the line which controls mounting of + + any removable media partitions. + 11 + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + 8 + 9 + APO01.06 + APO13.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.04 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.06 + DSS05.07 + DSS06.02 + DSS06.03 + DSS06.06 + CCI-000366 + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 5.2 + SR 7.6 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.11.2.6 + A.11.2.9 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.6.1.2 + A.6.2.1 + A.6.2.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.1 + A.8.2.2 + A.8.2.3 + A.8.3.1 + A.8.3.3 + A.9.1.1 + A.9.1.2 + A.9.2.1 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + AC-6 + AC-6(1) + MP-7 + PR.AC-3 + PR.AC-4 + PR.AC-6 + PR.DS-5 + PR.IP-1 + PR.PT-2 + PR.PT-3 + SRG-OS-000480-GPOS-00227 + RHEL-08-010620 + 1.1.19 + SV-230305r627750_rule + The presence of SUID and SGID executables should be tightly controlled. Allowing +users to introduce SUID or SGID binaries from partitions mounted off of +removable media would allow them to introduce their own highly-privileged programs. + + CCE-82744-4 + - name: XCCDF Value var_removable_partition # promote to variable + set_fact: + var_removable_partition: !!str + tags: + - always + +- name: Ensure permission nosuid are set on var_removable_partition + lineinfile: + path: /etc/fstab + regexp: ^\s*({{ var_removable_partition }})\s+([^\s]*)\s+([^\s]*)\s+([^\s]*)(.*)$ + backrefs: true + line: \1 \2 \3 \4,nosuid \5 + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82744-4 + - DISA-STIG-RHEL-08-010620 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_nosuid_removable_partitions + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_removable_partition='' + + +device_regex="^\s*$var_removable_partition\s\+" +mount_option="nosuid" + +if grep -q $device_regex /etc/fstab ; then + previous_opts=$(grep $device_regex /etc/fstab | awk '{print $4}') + sed -i "s|\($device_regex.*$previous_opts\)|\1,$mount_option|" /etc/fstab +else + echo "Not remediating, because there is no record of $var_removable_partition in /etc/fstab" >&2 + return 1 +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Add nosuid Option to /opt + The nosuid mount option can be used to prevent +execution of setuid programs in /opt. The SUID and SGID permissions +should not be required in this directory. +Add the nosuid option to the fourth column of +/etc/fstab for the line which controls mounting of +/opt. + BP28(R12) + The presence of SUID and SGID executables should be tightly controlled. The +/opt directory contains additional software packages. Users should +not be able to execute SUID or SGID binaries from this directory. + + CCE-83319-4 + +part /opt --mountoptions="nosuid" + + - name: 'Add nosuid Option to /opt: Check information associated to mountpoint' + command: findmnt --fstab '/opt' + register: device_name + failed_when: device_name.rc > 1 + changed_when: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83319-4 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_opt_nosuid + - no_reboot_needed + +- name: 'Add nosuid Option to /opt: Create mount_info dictionary variable' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - '{{ device_name.stdout_lines[0].split() | list | lower }}' + - '{{ device_name.stdout_lines[1].split() | list }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - device_name.stdout is defined and device_name.stdout_lines is defined + - (device_name.stdout | length > 0) + tags: + - CCE-83319-4 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_opt_nosuid + - no_reboot_needed + +- name: 'Add nosuid Option to /opt: If /opt not mounted, craft mount_info manually' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - - target + - source + - fstype + - options + - - /opt + - '' + - '' + - defaults + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ("--fstab" | length == 0) + - (device_name.stdout | length == 0) + tags: + - CCE-83319-4 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_opt_nosuid + - no_reboot_needed + +- name: 'Add nosuid Option to /opt: Make sure nosuid option is part of the to /opt + options' + set_fact: + mount_info: '{{ mount_info | combine( {''options'':''''~mount_info.options~'',nosuid'' + }) }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - mount_info is defined and "nosuid" not in mount_info.options + tags: + - CCE-83319-4 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_opt_nosuid + - no_reboot_needed + +- name: 'Add nosuid Option to /opt: Ensure /opt is mounted with nosuid option' + mount: + path: /opt + src: '{{ mount_info.source }}' + opts: '{{ mount_info.options }}' + state: mounted + fstype: '{{ mount_info.fstype }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - (device_name.stdout is defined and (device_name.stdout | length > 0)) or ("--fstab" + | length == 0) + tags: + - CCE-83319-4 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_opt_nosuid + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +function perform_remediation { + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" "/opt")" + + grep "$mount_point_match_regexp" -q /etc/fstab \ + || { echo "The mount point '/opt' is not even in /etc/fstab, so we can't set up mount options" >&2; + echo "Not remediating, because there is no record of /opt in /etc/fstab" >&2; return 1; } + + + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" /opt)" + + # If the mount point is not in /etc/fstab, get previous mount options from /etc/mtab + if [ "$(grep -c "$mount_point_match_regexp" /etc/fstab)" -eq 0 ]; then + # runtime opts without some automatic kernel/userspace-added defaults + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/mtab | head -1 | awk '{print $4}' \ + | sed -E "s/(rw|defaults|seclabel|nosuid)(,|$)//g;s/,$//") + [ "$previous_mount_opts" ] && previous_mount_opts+="," + echo " /opt defaults,${previous_mount_opts}nosuid 0 0" >> /etc/fstab + # If the mount_opt option is not already in the mount point's /etc/fstab entry, add it + elif [ "$(grep "$mount_point_match_regexp" /etc/fstab | grep -c "nosuid")" -eq 0 ]; then + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/fstab | awk '{print $4}') + sed -i "s|\(${mount_point_match_regexp}.*${previous_mount_opts}\)|\1,nosuid|" /etc/fstab + fi + + + if mkdir -p "/opt"; then + if mountpoint -q "/opt"; then + mount -o remount --target "/opt" + else + mount --target "/opt" + fi + fi +} + +perform_remediation + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Add hidepid Option to /proc + The hidepid mount option is applicable to /proc and is used to +control who can access the information in /proc/[pid] directories. +The option can have one of the following values: + +0: Everybody may access all /proc/[pid] directories. +1: Users may not access files and subdirectories inside any /proc/[pid] directories + but their own. The /proc/[pid] directories themselves remain visible. +2: Same as for mode 1, but in addition the /proc/[pid] directories belonging to other + users become invisible. + +For example, if you choose the value 2: +Add the hidepid=2 option to the fourth column of +/etc/fstab for the line which controls mounting of +/proc. + Hiding the pid of processes may lead to problems with PolicyKit and D-Bus, +it may also convey a false sense of security. + +Proceed to +https://access.redhat.com/solutions/6704531 for more details. + BP28(R12) + Users should not be able to see and access directories within /proc, which are not +related to their own processes in a system. Otherwise, sensitive information from +other users could be seem. + + CCE-85882-9 + - name: XCCDF Value var_mount_option_proc_hidepid # promote to variable + set_fact: + var_mount_option_proc_hidepid: !!str + tags: + - always + +- name: 'Add hidepid Option to /proc: Check information associated to mountpoint' + command: findmnt '/proc' + register: device_name + failed_when: device_name.rc > 1 + changed_when: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-85882-9 + - configure_strategy + - high_disruption + - low_complexity + - low_severity + - mount_option_proc_hidepid + - no_reboot_needed + +- name: 'Add hidepid Option to /proc: Create mount_info dictionary variable' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - '{{ device_name.stdout_lines[0].split() | list | lower }}' + - '{{ device_name.stdout_lines[1].split() | list }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - device_name.stdout is defined and device_name.stdout_lines is defined + - (device_name.stdout | length > 0) + tags: + - CCE-85882-9 + - configure_strategy + - high_disruption + - low_complexity + - low_severity + - mount_option_proc_hidepid + - no_reboot_needed + +- name: 'Add hidepid Option to /proc: If /proc not mounted, craft mount_info manually' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - - target + - source + - fstype + - options + - - /proc + - proc + - proc + - defaults + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ("" | length == 0) + - (device_name.stdout | length == 0) + tags: + - CCE-85882-9 + - configure_strategy + - high_disruption + - low_complexity + - low_severity + - mount_option_proc_hidepid + - no_reboot_needed + +- name: 'Add hidepid Option to /proc: Make sure hidepid option is part of the to /proc + options' + set_fact: + mount_info: '{{ mount_info | combine( {''options'':''''~mount_info.options~'',hidepid=''~var_mount_option_proc_hidepid~'''' + }) }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - mount_info is defined and "hidepid" not in mount_info.options + tags: + - CCE-85882-9 + - configure_strategy + - high_disruption + - low_complexity + - low_severity + - mount_option_proc_hidepid + - no_reboot_needed + +- name: 'Add hidepid Option to /proc: Ensure /proc is mounted with hidepid option' + mount: + path: /proc + src: '{{ mount_info.source }}' + opts: '{{ mount_info.options }}' + state: mounted + fstype: '{{ mount_info.fstype }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - (device_name.stdout is defined and (device_name.stdout | length > 0)) or ("" | + length == 0) + tags: + - CCE-85882-9 + - configure_strategy + - high_disruption + - low_complexity + - low_severity + - mount_option_proc_hidepid + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +function perform_remediation { + + + + var_mount_option_proc_hidepid='' + + mountoption="hidepid=$var_mount_option_proc_hidepid" + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" /proc)" + + # If the mount point is not in /etc/fstab, get previous mount options from /etc/mtab + if [ "$(grep -c "$mount_point_match_regexp" /etc/fstab)" -eq 0 ]; then + # runtime opts without some automatic kernel/userspace-added defaults + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/mtab | head -1 | awk '{print $4}' \ + | sed -E "s/(rw|defaults|seclabel|$mountoption)(,|$)//g;s/,$//") + [ "$previous_mount_opts" ] && previous_mount_opts+="," + echo "proc /proc proc defaults,${previous_mount_opts}$mountoption 0 0" >> /etc/fstab + # If the mount_opt option is not already in the mount point's /etc/fstab entry, add it + elif [ "$(grep "$mount_point_match_regexp" /etc/fstab | grep -c "$mountoption")" -eq 0 ]; then + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/fstab | awk '{print $4}') + sed -i "s|\(${mount_point_match_regexp}.*${previous_mount_opts}\)|\1,$mountoption|" /etc/fstab + fi + + + if mkdir -p "/proc"; then + if mountpoint -q "/proc"; then + mount -o remount --target "/proc" + else + mount --target "/proc" + fi + fi +} + +perform_remediation + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Add nosuid Option to /srv + The nosuid mount option can be used to prevent +execution of setuid programs in /srv. The SUID and SGID permissions +should not be required in this directory. +Add the nosuid option to the fourth column of +/etc/fstab for the line which controls mounting of +/srv. + BP28(R12) + The presence of SUID and SGID executables should be tightly controlled. The +/srv directory contains files served by various network services such as FTP. Users should +not be able to execute SUID or SGID binaries from this directory. + + CCE-83322-8 + +part /srv --mountoptions="nosuid" + + - name: 'Add nosuid Option to /srv: Check information associated to mountpoint' + command: findmnt --fstab '/srv' + register: device_name + failed_when: device_name.rc > 1 + changed_when: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83322-8 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_srv_nosuid + - no_reboot_needed + +- name: 'Add nosuid Option to /srv: Create mount_info dictionary variable' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - '{{ device_name.stdout_lines[0].split() | list | lower }}' + - '{{ device_name.stdout_lines[1].split() | list }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - device_name.stdout is defined and device_name.stdout_lines is defined + - (device_name.stdout | length > 0) + tags: + - CCE-83322-8 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_srv_nosuid + - no_reboot_needed + +- name: 'Add nosuid Option to /srv: If /srv not mounted, craft mount_info manually' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - - target + - source + - fstype + - options + - - /srv + - '' + - '' + - defaults + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ("--fstab" | length == 0) + - (device_name.stdout | length == 0) + tags: + - CCE-83322-8 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_srv_nosuid + - no_reboot_needed + +- name: 'Add nosuid Option to /srv: Make sure nosuid option is part of the to /srv + options' + set_fact: + mount_info: '{{ mount_info | combine( {''options'':''''~mount_info.options~'',nosuid'' + }) }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - mount_info is defined and "nosuid" not in mount_info.options + tags: + - CCE-83322-8 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_srv_nosuid + - no_reboot_needed + +- name: 'Add nosuid Option to /srv: Ensure /srv is mounted with nosuid option' + mount: + path: /srv + src: '{{ mount_info.source }}' + opts: '{{ mount_info.options }}' + state: mounted + fstype: '{{ mount_info.fstype }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - (device_name.stdout is defined and (device_name.stdout | length > 0)) or ("--fstab" + | length == 0) + tags: + - CCE-83322-8 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_srv_nosuid + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +function perform_remediation { + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" "/srv")" + + grep "$mount_point_match_regexp" -q /etc/fstab \ + || { echo "The mount point '/srv' is not even in /etc/fstab, so we can't set up mount options" >&2; + echo "Not remediating, because there is no record of /srv in /etc/fstab" >&2; return 1; } + + + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" /srv)" + + # If the mount point is not in /etc/fstab, get previous mount options from /etc/mtab + if [ "$(grep -c "$mount_point_match_regexp" /etc/fstab)" -eq 0 ]; then + # runtime opts without some automatic kernel/userspace-added defaults + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/mtab | head -1 | awk '{print $4}' \ + | sed -E "s/(rw|defaults|seclabel|nosuid)(,|$)//g;s/,$//") + [ "$previous_mount_opts" ] && previous_mount_opts+="," + echo " /srv defaults,${previous_mount_opts}nosuid 0 0" >> /etc/fstab + # If the mount_opt option is not already in the mount point's /etc/fstab entry, add it + elif [ "$(grep "$mount_point_match_regexp" /etc/fstab | grep -c "nosuid")" -eq 0 ]; then + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/fstab | awk '{print $4}') + sed -i "s|\(${mount_point_match_regexp}.*${previous_mount_opts}\)|\1,nosuid|" /etc/fstab + fi + + + if mkdir -p "/srv"; then + if mountpoint -q "/srv"; then + mount -o remount --target "/srv" + else + mount --target "/srv" + fi + fi +} + +perform_remediation + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Add nodev Option to /tmp + The nodev mount option can be used to prevent device files from +being created in /tmp. Legitimate character and block devices +should not exist within temporary directories like /tmp. +Add the nodev option to the fourth column of +/etc/fstab for the line which controls mounting of +/tmp. + BP28(R12) + 11 + 13 + 14 + 3 + 8 + 9 + APO13.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS05.06 + DSS06.06 + CCI-001764 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.11.2.9 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.8.2.1 + A.8.2.2 + A.8.2.3 + A.8.3.1 + A.8.3.3 + A.9.1.2 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + AC-6 + AC-6(1) + MP-7 + PR.IP-1 + PR.PT-2 + PR.PT-3 + SRG-OS-000368-GPOS-00154 + RHEL-08-040123 + 1.1.2.2 + SV-230511r627750_rule + The only legitimate location for device files is the /dev directory +located on the root partition. The only exception to this is chroot jails. + + CCE-82623-0 + +part /tmp --mountoptions="nodev" + + - name: 'Add nodev Option to /tmp: Check information associated to mountpoint' + command: findmnt --fstab '/tmp' + register: device_name + failed_when: device_name.rc > 1 + changed_when: false + when: ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", + "container"] and "/tmp" in ansible_mounts | map(attribute="mount") | list ) + tags: + - CCE-82623-0 + - DISA-STIG-RHEL-08-040123 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_tmp_nodev + - no_reboot_needed + +- name: 'Add nodev Option to /tmp: Create mount_info dictionary variable' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - '{{ device_name.stdout_lines[0].split() | list | lower }}' + - '{{ device_name.stdout_lines[1].split() | list }}' + when: + - ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + and "/tmp" in ansible_mounts | map(attribute="mount") | list ) + - device_name.stdout is defined and device_name.stdout_lines is defined + - (device_name.stdout | length > 0) + tags: + - CCE-82623-0 + - DISA-STIG-RHEL-08-040123 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_tmp_nodev + - no_reboot_needed + +- name: 'Add nodev Option to /tmp: If /tmp not mounted, craft mount_info manually' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - - target + - source + - fstype + - options + - - /tmp + - '' + - '' + - defaults + when: + - ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + and "/tmp" in ansible_mounts | map(attribute="mount") | list ) + - ("--fstab" | length == 0) + - (device_name.stdout | length == 0) + tags: + - CCE-82623-0 + - DISA-STIG-RHEL-08-040123 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_tmp_nodev + - no_reboot_needed + +- name: 'Add nodev Option to /tmp: Make sure nodev option is part of the to /tmp options' + set_fact: + mount_info: '{{ mount_info | combine( {''options'':''''~mount_info.options~'',nodev'' + }) }}' + when: + - ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + and "/tmp" in ansible_mounts | map(attribute="mount") | list ) + - mount_info is defined and "nodev" not in mount_info.options + tags: + - CCE-82623-0 + - DISA-STIG-RHEL-08-040123 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_tmp_nodev + - no_reboot_needed + +- name: 'Add nodev Option to /tmp: Ensure /tmp is mounted with nodev option' + mount: + path: /tmp + src: '{{ mount_info.source }}' + opts: '{{ mount_info.options }}' + state: mounted + fstype: '{{ mount_info.fstype }}' + when: + - ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + and "/tmp" in ansible_mounts | map(attribute="mount") | list ) + - (device_name.stdout is defined and (device_name.stdout | length > 0)) or ("--fstab" + | length == 0) + tags: + - CCE-82623-0 + - DISA-STIG-RHEL-08-040123 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_tmp_nodev + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if ( [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && findmnt --kernel "/tmp" > /dev/null ); then + +function perform_remediation { + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" "/tmp")" + + grep "$mount_point_match_regexp" -q /etc/fstab \ + || { echo "The mount point '/tmp' is not even in /etc/fstab, so we can't set up mount options" >&2; + echo "Not remediating, because there is no record of /tmp in /etc/fstab" >&2; return 1; } + + + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" /tmp)" + + # If the mount point is not in /etc/fstab, get previous mount options from /etc/mtab + if [ "$(grep -c "$mount_point_match_regexp" /etc/fstab)" -eq 0 ]; then + # runtime opts without some automatic kernel/userspace-added defaults + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/mtab | head -1 | awk '{print $4}' \ + | sed -E "s/(rw|defaults|seclabel|nodev)(,|$)//g;s/,$//") + [ "$previous_mount_opts" ] && previous_mount_opts+="," + echo " /tmp defaults,${previous_mount_opts}nodev 0 0" >> /etc/fstab + # If the mount_opt option is not already in the mount point's /etc/fstab entry, add it + elif [ "$(grep "$mount_point_match_regexp" /etc/fstab | grep -c "nodev")" -eq 0 ]; then + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/fstab | awk '{print $4}') + sed -i "s|\(${mount_point_match_regexp}.*${previous_mount_opts}\)|\1,nodev|" /etc/fstab + fi + + + if mkdir -p "/tmp"; then + if mountpoint -q "/tmp"; then + mount -o remount --target "/tmp" + else + mount --target "/tmp" + fi + fi +} + +perform_remediation + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Add noexec Option to /tmp + The noexec mount option can be used to prevent binaries +from being executed out of /tmp. +Add the noexec option to the fourth column of +/etc/fstab for the line which controls mounting of +/tmp. + BP28(R12) + 11 + 13 + 14 + 3 + 8 + 9 + APO13.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS05.06 + DSS06.06 + CCI-001764 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.11.2.9 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.8.2.1 + A.8.2.2 + A.8.2.3 + A.8.3.1 + A.8.3.3 + A.9.1.2 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + AC-6 + AC-6(1) + MP-7 + PR.IP-1 + PR.PT-2 + PR.PT-3 + SRG-OS-000368-GPOS-00154 + RHEL-08-040125 + 1.1.2.3 + SV-230513r627750_rule + Allowing users to execute binaries from world-writable directories +such as /tmp should never be necessary in normal operation and +can expose the system to potential compromise. + + CCE-82139-7 + +part /tmp --mountoptions="noexec" + + - name: 'Add noexec Option to /tmp: Check information associated to mountpoint' + command: findmnt --fstab '/tmp' + register: device_name + failed_when: device_name.rc > 1 + changed_when: false + when: ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", + "container"] and "/tmp" in ansible_mounts | map(attribute="mount") | list ) + tags: + - CCE-82139-7 + - DISA-STIG-RHEL-08-040125 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_tmp_noexec + - no_reboot_needed + +- name: 'Add noexec Option to /tmp: Create mount_info dictionary variable' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - '{{ device_name.stdout_lines[0].split() | list | lower }}' + - '{{ device_name.stdout_lines[1].split() | list }}' + when: + - ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + and "/tmp" in ansible_mounts | map(attribute="mount") | list ) + - device_name.stdout is defined and device_name.stdout_lines is defined + - (device_name.stdout | length > 0) + tags: + - CCE-82139-7 + - DISA-STIG-RHEL-08-040125 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_tmp_noexec + - no_reboot_needed + +- name: 'Add noexec Option to /tmp: If /tmp not mounted, craft mount_info manually' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - - target + - source + - fstype + - options + - - /tmp + - '' + - '' + - defaults + when: + - ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + and "/tmp" in ansible_mounts | map(attribute="mount") | list ) + - ("--fstab" | length == 0) + - (device_name.stdout | length == 0) + tags: + - CCE-82139-7 + - DISA-STIG-RHEL-08-040125 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_tmp_noexec + - no_reboot_needed + +- name: 'Add noexec Option to /tmp: Make sure noexec option is part of the to /tmp + options' + set_fact: + mount_info: '{{ mount_info | combine( {''options'':''''~mount_info.options~'',noexec'' + }) }}' + when: + - ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + and "/tmp" in ansible_mounts | map(attribute="mount") | list ) + - mount_info is defined and "noexec" not in mount_info.options + tags: + - CCE-82139-7 + - DISA-STIG-RHEL-08-040125 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_tmp_noexec + - no_reboot_needed + +- name: 'Add noexec Option to /tmp: Ensure /tmp is mounted with noexec option' + mount: + path: /tmp + src: '{{ mount_info.source }}' + opts: '{{ mount_info.options }}' + state: mounted + fstype: '{{ mount_info.fstype }}' + when: + - ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + and "/tmp" in ansible_mounts | map(attribute="mount") | list ) + - (device_name.stdout is defined and (device_name.stdout | length > 0)) or ("--fstab" + | length == 0) + tags: + - CCE-82139-7 + - DISA-STIG-RHEL-08-040125 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_tmp_noexec + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if ( [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && findmnt --kernel "/tmp" > /dev/null ); then + +function perform_remediation { + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" "/tmp")" + + grep "$mount_point_match_regexp" -q /etc/fstab \ + || { echo "The mount point '/tmp' is not even in /etc/fstab, so we can't set up mount options" >&2; + echo "Not remediating, because there is no record of /tmp in /etc/fstab" >&2; return 1; } + + + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" /tmp)" + + # If the mount point is not in /etc/fstab, get previous mount options from /etc/mtab + if [ "$(grep -c "$mount_point_match_regexp" /etc/fstab)" -eq 0 ]; then + # runtime opts without some automatic kernel/userspace-added defaults + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/mtab | head -1 | awk '{print $4}' \ + | sed -E "s/(rw|defaults|seclabel|noexec)(,|$)//g;s/,$//") + [ "$previous_mount_opts" ] && previous_mount_opts+="," + echo " /tmp defaults,${previous_mount_opts}noexec 0 0" >> /etc/fstab + # If the mount_opt option is not already in the mount point's /etc/fstab entry, add it + elif [ "$(grep "$mount_point_match_regexp" /etc/fstab | grep -c "noexec")" -eq 0 ]; then + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/fstab | awk '{print $4}') + sed -i "s|\(${mount_point_match_regexp}.*${previous_mount_opts}\)|\1,noexec|" /etc/fstab + fi + + + if mkdir -p "/tmp"; then + if mountpoint -q "/tmp"; then + mount -o remount --target "/tmp" + else + mount --target "/tmp" + fi + fi +} + +perform_remediation + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Add nosuid Option to /tmp + The nosuid mount option can be used to prevent +execution of setuid programs in /tmp. The SUID and SGID permissions +should not be required in these world-writable directories. +Add the nosuid option to the fourth column of +/etc/fstab for the line which controls mounting of +/tmp. + BP28(R12) + 11 + 13 + 14 + 3 + 8 + 9 + APO13.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS05.06 + DSS06.06 + CCI-001764 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.11.2.9 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.8.2.1 + A.8.2.2 + A.8.2.3 + A.8.3.1 + A.8.3.3 + A.9.1.2 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + AC-6 + AC-6(1) + MP-7 + PR.IP-1 + PR.PT-2 + PR.PT-3 + SRG-OS-000368-GPOS-00154 + RHEL-08-040124 + 1.1.2.4 + SV-230512r627750_rule + The presence of SUID and SGID executables should be tightly controlled. Users +should not be able to execute SUID or SGID binaries from temporary storage partitions. + + CCE-82140-5 + +part /tmp --mountoptions="nosuid" + + - name: 'Add nosuid Option to /tmp: Check information associated to mountpoint' + command: findmnt --fstab '/tmp' + register: device_name + failed_when: device_name.rc > 1 + changed_when: false + when: ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", + "container"] and "/tmp" in ansible_mounts | map(attribute="mount") | list ) + tags: + - CCE-82140-5 + - DISA-STIG-RHEL-08-040124 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_tmp_nosuid + - no_reboot_needed + +- name: 'Add nosuid Option to /tmp: Create mount_info dictionary variable' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - '{{ device_name.stdout_lines[0].split() | list | lower }}' + - '{{ device_name.stdout_lines[1].split() | list }}' + when: + - ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + and "/tmp" in ansible_mounts | map(attribute="mount") | list ) + - device_name.stdout is defined and device_name.stdout_lines is defined + - (device_name.stdout | length > 0) + tags: + - CCE-82140-5 + - DISA-STIG-RHEL-08-040124 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_tmp_nosuid + - no_reboot_needed + +- name: 'Add nosuid Option to /tmp: If /tmp not mounted, craft mount_info manually' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - - target + - source + - fstype + - options + - - /tmp + - '' + - '' + - defaults + when: + - ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + and "/tmp" in ansible_mounts | map(attribute="mount") | list ) + - ("--fstab" | length == 0) + - (device_name.stdout | length == 0) + tags: + - CCE-82140-5 + - DISA-STIG-RHEL-08-040124 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_tmp_nosuid + - no_reboot_needed + +- name: 'Add nosuid Option to /tmp: Make sure nosuid option is part of the to /tmp + options' + set_fact: + mount_info: '{{ mount_info | combine( {''options'':''''~mount_info.options~'',nosuid'' + }) }}' + when: + - ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + and "/tmp" in ansible_mounts | map(attribute="mount") | list ) + - mount_info is defined and "nosuid" not in mount_info.options + tags: + - CCE-82140-5 + - DISA-STIG-RHEL-08-040124 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_tmp_nosuid + - no_reboot_needed + +- name: 'Add nosuid Option to /tmp: Ensure /tmp is mounted with nosuid option' + mount: + path: /tmp + src: '{{ mount_info.source }}' + opts: '{{ mount_info.options }}' + state: mounted + fstype: '{{ mount_info.fstype }}' + when: + - ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + and "/tmp" in ansible_mounts | map(attribute="mount") | list ) + - (device_name.stdout is defined and (device_name.stdout | length > 0)) or ("--fstab" + | length == 0) + tags: + - CCE-82140-5 + - DISA-STIG-RHEL-08-040124 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_tmp_nosuid + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if ( [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && findmnt --kernel "/tmp" > /dev/null ); then + +function perform_remediation { + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" "/tmp")" + + grep "$mount_point_match_regexp" -q /etc/fstab \ + || { echo "The mount point '/tmp' is not even in /etc/fstab, so we can't set up mount options" >&2; + echo "Not remediating, because there is no record of /tmp in /etc/fstab" >&2; return 1; } + + + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" /tmp)" + + # If the mount point is not in /etc/fstab, get previous mount options from /etc/mtab + if [ "$(grep -c "$mount_point_match_regexp" /etc/fstab)" -eq 0 ]; then + # runtime opts without some automatic kernel/userspace-added defaults + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/mtab | head -1 | awk '{print $4}' \ + | sed -E "s/(rw|defaults|seclabel|nosuid)(,|$)//g;s/,$//") + [ "$previous_mount_opts" ] && previous_mount_opts+="," + echo " /tmp defaults,${previous_mount_opts}nosuid 0 0" >> /etc/fstab + # If the mount_opt option is not already in the mount point's /etc/fstab entry, add it + elif [ "$(grep "$mount_point_match_regexp" /etc/fstab | grep -c "nosuid")" -eq 0 ]; then + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/fstab | awk '{print $4}') + sed -i "s|\(${mount_point_match_regexp}.*${previous_mount_opts}\)|\1,nosuid|" /etc/fstab + fi + + + if mkdir -p "/tmp"; then + if mountpoint -q "/tmp"; then + mount -o remount --target "/tmp" + else + mount --target "/tmp" + fi + fi +} + +perform_remediation + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Add nodev Option to /var/log/audit + The nodev mount option can be used to prevent device files from +being created in /var/log/audit. +Legitimate character and block devices should exist only in +the /dev directory on the root partition or within chroot +jails built for system services. +Add the nodev option to the fourth column of +/etc/fstab for the line which controls mounting of +/var/log/audit. + CCI-001764 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + AC-6 + AC-6(1) + MP-7 + PR.IP-1 + PR.PT-2 + PR.PT-3 + SRG-OS-000368-GPOS-00154 + RHEL-08-040129 + 1.1.6.3 + SV-230517r627750_rule + The only legitimate location for device files is the /dev directory +located on the root partition. The only exception to this is chroot jails. + + CCE-82080-3 + +part /var/log/audit --mountoptions="nodev" + + - name: 'Add nodev Option to /var/log/audit: Check information associated to mountpoint' + command: findmnt --fstab '/var/log/audit' + register: device_name + failed_when: device_name.rc > 1 + changed_when: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82080-3 + - DISA-STIG-RHEL-08-040129 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_log_audit_nodev + - no_reboot_needed + +- name: 'Add nodev Option to /var/log/audit: Create mount_info dictionary variable' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - '{{ device_name.stdout_lines[0].split() | list | lower }}' + - '{{ device_name.stdout_lines[1].split() | list }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - device_name.stdout is defined and device_name.stdout_lines is defined + - (device_name.stdout | length > 0) + tags: + - CCE-82080-3 + - DISA-STIG-RHEL-08-040129 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_log_audit_nodev + - no_reboot_needed + +- name: 'Add nodev Option to /var/log/audit: If /var/log/audit not mounted, craft + mount_info manually' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - - target + - source + - fstype + - options + - - /var/log/audit + - '' + - '' + - defaults + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ("--fstab" | length == 0) + - (device_name.stdout | length == 0) + tags: + - CCE-82080-3 + - DISA-STIG-RHEL-08-040129 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_log_audit_nodev + - no_reboot_needed + +- name: 'Add nodev Option to /var/log/audit: Make sure nodev option is part of the + to /var/log/audit options' + set_fact: + mount_info: '{{ mount_info | combine( {''options'':''''~mount_info.options~'',nodev'' + }) }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - mount_info is defined and "nodev" not in mount_info.options + tags: + - CCE-82080-3 + - DISA-STIG-RHEL-08-040129 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_log_audit_nodev + - no_reboot_needed + +- name: 'Add nodev Option to /var/log/audit: Ensure /var/log/audit is mounted with + nodev option' + mount: + path: /var/log/audit + src: '{{ mount_info.source }}' + opts: '{{ mount_info.options }}' + state: mounted + fstype: '{{ mount_info.fstype }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - (device_name.stdout is defined and (device_name.stdout | length > 0)) or ("--fstab" + | length == 0) + tags: + - CCE-82080-3 + - DISA-STIG-RHEL-08-040129 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_log_audit_nodev + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +function perform_remediation { + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" "/var/log/audit")" + + grep "$mount_point_match_regexp" -q /etc/fstab \ + || { echo "The mount point '/var/log/audit' is not even in /etc/fstab, so we can't set up mount options" >&2; + echo "Not remediating, because there is no record of /var/log/audit in /etc/fstab" >&2; return 1; } + + + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" /var/log/audit)" + + # If the mount point is not in /etc/fstab, get previous mount options from /etc/mtab + if [ "$(grep -c "$mount_point_match_regexp" /etc/fstab)" -eq 0 ]; then + # runtime opts without some automatic kernel/userspace-added defaults + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/mtab | head -1 | awk '{print $4}' \ + | sed -E "s/(rw|defaults|seclabel|nodev)(,|$)//g;s/,$//") + [ "$previous_mount_opts" ] && previous_mount_opts+="," + echo " /var/log/audit defaults,${previous_mount_opts}nodev 0 0" >> /etc/fstab + # If the mount_opt option is not already in the mount point's /etc/fstab entry, add it + elif [ "$(grep "$mount_point_match_regexp" /etc/fstab | grep -c "nodev")" -eq 0 ]; then + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/fstab | awk '{print $4}') + sed -i "s|\(${mount_point_match_regexp}.*${previous_mount_opts}\)|\1,nodev|" /etc/fstab + fi + + + if mkdir -p "/var/log/audit"; then + if mountpoint -q "/var/log/audit"; then + mount -o remount --target "/var/log/audit" + else + mount --target "/var/log/audit" + fi + fi +} + +perform_remediation + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Add noexec Option to /var/log/audit + The noexec mount option can be used to prevent binaries +from being executed out of /var/log/audit. +Add the noexec option to the fourth column of +/etc/fstab for the line which controls mounting of +/var/log/audit. + CCI-001764 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + AC-6 + AC-6(1) + MP-7 + PR.IP-1 + PR.PT-2 + PR.PT-3 + SRG-OS-000368-GPOS-00154 + RHEL-08-040131 + 1.1.6.2 + SV-230519r627750_rule + Allowing users to execute binaries from directories containing audit log files +such as /var/log/audit should never be necessary in normal operation and +can expose the system to potential compromise. + + CCE-82975-4 + +part /var/log/audit --mountoptions="noexec" + + - name: 'Add noexec Option to /var/log/audit: Check information associated to mountpoint' + command: findmnt --fstab '/var/log/audit' + register: device_name + failed_when: device_name.rc > 1 + changed_when: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82975-4 + - DISA-STIG-RHEL-08-040131 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_log_audit_noexec + - no_reboot_needed + +- name: 'Add noexec Option to /var/log/audit: Create mount_info dictionary variable' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - '{{ device_name.stdout_lines[0].split() | list | lower }}' + - '{{ device_name.stdout_lines[1].split() | list }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - device_name.stdout is defined and device_name.stdout_lines is defined + - (device_name.stdout | length > 0) + tags: + - CCE-82975-4 + - DISA-STIG-RHEL-08-040131 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_log_audit_noexec + - no_reboot_needed + +- name: 'Add noexec Option to /var/log/audit: If /var/log/audit not mounted, craft + mount_info manually' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - - target + - source + - fstype + - options + - - /var/log/audit + - '' + - '' + - defaults + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ("--fstab" | length == 0) + - (device_name.stdout | length == 0) + tags: + - CCE-82975-4 + - DISA-STIG-RHEL-08-040131 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_log_audit_noexec + - no_reboot_needed + +- name: 'Add noexec Option to /var/log/audit: Make sure noexec option is part of the + to /var/log/audit options' + set_fact: + mount_info: '{{ mount_info | combine( {''options'':''''~mount_info.options~'',noexec'' + }) }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - mount_info is defined and "noexec" not in mount_info.options + tags: + - CCE-82975-4 + - DISA-STIG-RHEL-08-040131 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_log_audit_noexec + - no_reboot_needed + +- name: 'Add noexec Option to /var/log/audit: Ensure /var/log/audit is mounted with + noexec option' + mount: + path: /var/log/audit + src: '{{ mount_info.source }}' + opts: '{{ mount_info.options }}' + state: mounted + fstype: '{{ mount_info.fstype }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - (device_name.stdout is defined and (device_name.stdout | length > 0)) or ("--fstab" + | length == 0) + tags: + - CCE-82975-4 + - DISA-STIG-RHEL-08-040131 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_log_audit_noexec + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +function perform_remediation { + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" "/var/log/audit")" + + grep "$mount_point_match_regexp" -q /etc/fstab \ + || { echo "The mount point '/var/log/audit' is not even in /etc/fstab, so we can't set up mount options" >&2; + echo "Not remediating, because there is no record of /var/log/audit in /etc/fstab" >&2; return 1; } + + + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" /var/log/audit)" + + # If the mount point is not in /etc/fstab, get previous mount options from /etc/mtab + if [ "$(grep -c "$mount_point_match_regexp" /etc/fstab)" -eq 0 ]; then + # runtime opts without some automatic kernel/userspace-added defaults + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/mtab | head -1 | awk '{print $4}' \ + | sed -E "s/(rw|defaults|seclabel|noexec)(,|$)//g;s/,$//") + [ "$previous_mount_opts" ] && previous_mount_opts+="," + echo " /var/log/audit defaults,${previous_mount_opts}noexec 0 0" >> /etc/fstab + # If the mount_opt option is not already in the mount point's /etc/fstab entry, add it + elif [ "$(grep "$mount_point_match_regexp" /etc/fstab | grep -c "noexec")" -eq 0 ]; then + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/fstab | awk '{print $4}') + sed -i "s|\(${mount_point_match_regexp}.*${previous_mount_opts}\)|\1,noexec|" /etc/fstab + fi + + + if mkdir -p "/var/log/audit"; then + if mountpoint -q "/var/log/audit"; then + mount -o remount --target "/var/log/audit" + else + mount --target "/var/log/audit" + fi + fi +} + +perform_remediation + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Add nosuid Option to /var/log/audit + The nosuid mount option can be used to prevent +execution of setuid programs in /var/log/audit. The SUID and SGID permissions +should not be required in directories containing audit log files. +Add the nosuid option to the fourth column of +/etc/fstab for the line which controls mounting of +/var/log/audit. + CCI-001764 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + AC-6 + AC-6(1) + MP-7 + PR.IP-1 + PR.PT-2 + PR.PT-3 + SRG-OS-000368-GPOS-00154 + RHEL-08-040130 + 1.1.6.4 + SV-230518r627750_rule + The presence of SUID and SGID executables should be tightly controlled. Users +should not be able to execute SUID or SGID binaries from partitions +designated for audit log files. + + CCE-82921-8 + +part /var/log/audit --mountoptions="nosuid" + + - name: 'Add nosuid Option to /var/log/audit: Check information associated to mountpoint' + command: findmnt --fstab '/var/log/audit' + register: device_name + failed_when: device_name.rc > 1 + changed_when: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82921-8 + - DISA-STIG-RHEL-08-040130 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_log_audit_nosuid + - no_reboot_needed + +- name: 'Add nosuid Option to /var/log/audit: Create mount_info dictionary variable' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - '{{ device_name.stdout_lines[0].split() | list | lower }}' + - '{{ device_name.stdout_lines[1].split() | list }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - device_name.stdout is defined and device_name.stdout_lines is defined + - (device_name.stdout | length > 0) + tags: + - CCE-82921-8 + - DISA-STIG-RHEL-08-040130 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_log_audit_nosuid + - no_reboot_needed + +- name: 'Add nosuid Option to /var/log/audit: If /var/log/audit not mounted, craft + mount_info manually' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - - target + - source + - fstype + - options + - - /var/log/audit + - '' + - '' + - defaults + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ("--fstab" | length == 0) + - (device_name.stdout | length == 0) + tags: + - CCE-82921-8 + - DISA-STIG-RHEL-08-040130 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_log_audit_nosuid + - no_reboot_needed + +- name: 'Add nosuid Option to /var/log/audit: Make sure nosuid option is part of the + to /var/log/audit options' + set_fact: + mount_info: '{{ mount_info | combine( {''options'':''''~mount_info.options~'',nosuid'' + }) }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - mount_info is defined and "nosuid" not in mount_info.options + tags: + - CCE-82921-8 + - DISA-STIG-RHEL-08-040130 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_log_audit_nosuid + - no_reboot_needed + +- name: 'Add nosuid Option to /var/log/audit: Ensure /var/log/audit is mounted with + nosuid option' + mount: + path: /var/log/audit + src: '{{ mount_info.source }}' + opts: '{{ mount_info.options }}' + state: mounted + fstype: '{{ mount_info.fstype }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - (device_name.stdout is defined and (device_name.stdout | length > 0)) or ("--fstab" + | length == 0) + tags: + - CCE-82921-8 + - DISA-STIG-RHEL-08-040130 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_log_audit_nosuid + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +function perform_remediation { + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" "/var/log/audit")" + + grep "$mount_point_match_regexp" -q /etc/fstab \ + || { echo "The mount point '/var/log/audit' is not even in /etc/fstab, so we can't set up mount options" >&2; + echo "Not remediating, because there is no record of /var/log/audit in /etc/fstab" >&2; return 1; } + + + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" /var/log/audit)" + + # If the mount point is not in /etc/fstab, get previous mount options from /etc/mtab + if [ "$(grep -c "$mount_point_match_regexp" /etc/fstab)" -eq 0 ]; then + # runtime opts without some automatic kernel/userspace-added defaults + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/mtab | head -1 | awk '{print $4}' \ + | sed -E "s/(rw|defaults|seclabel|nosuid)(,|$)//g;s/,$//") + [ "$previous_mount_opts" ] && previous_mount_opts+="," + echo " /var/log/audit defaults,${previous_mount_opts}nosuid 0 0" >> /etc/fstab + # If the mount_opt option is not already in the mount point's /etc/fstab entry, add it + elif [ "$(grep "$mount_point_match_regexp" /etc/fstab | grep -c "nosuid")" -eq 0 ]; then + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/fstab | awk '{print $4}') + sed -i "s|\(${mount_point_match_regexp}.*${previous_mount_opts}\)|\1,nosuid|" /etc/fstab + fi + + + if mkdir -p "/var/log/audit"; then + if mountpoint -q "/var/log/audit"; then + mount -o remount --target "/var/log/audit" + else + mount --target "/var/log/audit" + fi + fi +} + +perform_remediation + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Add nodev Option to /var/log + The nodev mount option can be used to prevent device files from +being created in /var/log. +Legitimate character and block devices should exist only in +the /dev directory on the root partition or within chroot +jails built for system services. +Add the nodev option to the fourth column of +/etc/fstab for the line which controls mounting of +/var/log. + CCI-001764 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + AC-6 + AC-6(1) + MP-7 + PR.IP-1 + PR.PT-2 + PR.PT-3 + SRG-OS-000368-GPOS-00154 + RHEL-08-040126 + 1.1.5.2 + SV-230514r627750_rule + The only legitimate location for device files is the /dev directory +located on the root partition. The only exception to this is chroot jails. + + CCE-82077-9 + +part /var/log --mountoptions="nodev" + + - name: 'Add nodev Option to /var/log: Check information associated to mountpoint' + command: findmnt --fstab '/var/log' + register: device_name + failed_when: device_name.rc > 1 + changed_when: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82077-9 + - DISA-STIG-RHEL-08-040126 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_log_nodev + - no_reboot_needed + +- name: 'Add nodev Option to /var/log: Create mount_info dictionary variable' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - '{{ device_name.stdout_lines[0].split() | list | lower }}' + - '{{ device_name.stdout_lines[1].split() | list }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - device_name.stdout is defined and device_name.stdout_lines is defined + - (device_name.stdout | length > 0) + tags: + - CCE-82077-9 + - DISA-STIG-RHEL-08-040126 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_log_nodev + - no_reboot_needed + +- name: 'Add nodev Option to /var/log: If /var/log not mounted, craft mount_info manually' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - - target + - source + - fstype + - options + - - /var/log + - '' + - '' + - defaults + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ("--fstab" | length == 0) + - (device_name.stdout | length == 0) + tags: + - CCE-82077-9 + - DISA-STIG-RHEL-08-040126 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_log_nodev + - no_reboot_needed + +- name: 'Add nodev Option to /var/log: Make sure nodev option is part of the to /var/log + options' + set_fact: + mount_info: '{{ mount_info | combine( {''options'':''''~mount_info.options~'',nodev'' + }) }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - mount_info is defined and "nodev" not in mount_info.options + tags: + - CCE-82077-9 + - DISA-STIG-RHEL-08-040126 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_log_nodev + - no_reboot_needed + +- name: 'Add nodev Option to /var/log: Ensure /var/log is mounted with nodev option' + mount: + path: /var/log + src: '{{ mount_info.source }}' + opts: '{{ mount_info.options }}' + state: mounted + fstype: '{{ mount_info.fstype }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - (device_name.stdout is defined and (device_name.stdout | length > 0)) or ("--fstab" + | length == 0) + tags: + - CCE-82077-9 + - DISA-STIG-RHEL-08-040126 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_log_nodev + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +function perform_remediation { + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" "/var/log")" + + grep "$mount_point_match_regexp" -q /etc/fstab \ + || { echo "The mount point '/var/log' is not even in /etc/fstab, so we can't set up mount options" >&2; + echo "Not remediating, because there is no record of /var/log in /etc/fstab" >&2; return 1; } + + + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" /var/log)" + + # If the mount point is not in /etc/fstab, get previous mount options from /etc/mtab + if [ "$(grep -c "$mount_point_match_regexp" /etc/fstab)" -eq 0 ]; then + # runtime opts without some automatic kernel/userspace-added defaults + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/mtab | head -1 | awk '{print $4}' \ + | sed -E "s/(rw|defaults|seclabel|nodev)(,|$)//g;s/,$//") + [ "$previous_mount_opts" ] && previous_mount_opts+="," + echo " /var/log defaults,${previous_mount_opts}nodev 0 0" >> /etc/fstab + # If the mount_opt option is not already in the mount point's /etc/fstab entry, add it + elif [ "$(grep "$mount_point_match_regexp" /etc/fstab | grep -c "nodev")" -eq 0 ]; then + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/fstab | awk '{print $4}') + sed -i "s|\(${mount_point_match_regexp}.*${previous_mount_opts}\)|\1,nodev|" /etc/fstab + fi + + + if mkdir -p "/var/log"; then + if mountpoint -q "/var/log"; then + mount -o remount --target "/var/log" + else + mount --target "/var/log" + fi + fi +} + +perform_remediation + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Add noexec Option to /var/log + The noexec mount option can be used to prevent binaries +from being executed out of /var/log. +Add the noexec option to the fourth column of +/etc/fstab for the line which controls mounting of +/var/log. + BP28(R12) + CCI-001764 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + AC-6 + AC-6(1) + MP-7 + PR.IP-1 + PR.PT-2 + PR.PT-3 + SRG-OS-000368-GPOS-00154 + RHEL-08-040128 + 1.1.5.3 + SV-230516r627750_rule + Allowing users to execute binaries from directories containing log files +such as /var/log should never be necessary in normal operation and +can expose the system to potential compromise. + + CCE-82008-4 + +part /var/log --mountoptions="noexec" + + - name: 'Add noexec Option to /var/log: Check information associated to mountpoint' + command: findmnt --fstab '/var/log' + register: device_name + failed_when: device_name.rc > 1 + changed_when: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82008-4 + - DISA-STIG-RHEL-08-040128 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_log_noexec + - no_reboot_needed + +- name: 'Add noexec Option to /var/log: Create mount_info dictionary variable' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - '{{ device_name.stdout_lines[0].split() | list | lower }}' + - '{{ device_name.stdout_lines[1].split() | list }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - device_name.stdout is defined and device_name.stdout_lines is defined + - (device_name.stdout | length > 0) + tags: + - CCE-82008-4 + - DISA-STIG-RHEL-08-040128 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_log_noexec + - no_reboot_needed + +- name: 'Add noexec Option to /var/log: If /var/log not mounted, craft mount_info + manually' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - - target + - source + - fstype + - options + - - /var/log + - '' + - '' + - defaults + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ("--fstab" | length == 0) + - (device_name.stdout | length == 0) + tags: + - CCE-82008-4 + - DISA-STIG-RHEL-08-040128 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_log_noexec + - no_reboot_needed + +- name: 'Add noexec Option to /var/log: Make sure noexec option is part of the to + /var/log options' + set_fact: + mount_info: '{{ mount_info | combine( {''options'':''''~mount_info.options~'',noexec'' + }) }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - mount_info is defined and "noexec" not in mount_info.options + tags: + - CCE-82008-4 + - DISA-STIG-RHEL-08-040128 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_log_noexec + - no_reboot_needed + +- name: 'Add noexec Option to /var/log: Ensure /var/log is mounted with noexec option' + mount: + path: /var/log + src: '{{ mount_info.source }}' + opts: '{{ mount_info.options }}' + state: mounted + fstype: '{{ mount_info.fstype }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - (device_name.stdout is defined and (device_name.stdout | length > 0)) or ("--fstab" + | length == 0) + tags: + - CCE-82008-4 + - DISA-STIG-RHEL-08-040128 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_log_noexec + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +function perform_remediation { + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" "/var/log")" + + grep "$mount_point_match_regexp" -q /etc/fstab \ + || { echo "The mount point '/var/log' is not even in /etc/fstab, so we can't set up mount options" >&2; + echo "Not remediating, because there is no record of /var/log in /etc/fstab" >&2; return 1; } + + + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" /var/log)" + + # If the mount point is not in /etc/fstab, get previous mount options from /etc/mtab + if [ "$(grep -c "$mount_point_match_regexp" /etc/fstab)" -eq 0 ]; then + # runtime opts without some automatic kernel/userspace-added defaults + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/mtab | head -1 | awk '{print $4}' \ + | sed -E "s/(rw|defaults|seclabel|noexec)(,|$)//g;s/,$//") + [ "$previous_mount_opts" ] && previous_mount_opts+="," + echo " /var/log defaults,${previous_mount_opts}noexec 0 0" >> /etc/fstab + # If the mount_opt option is not already in the mount point's /etc/fstab entry, add it + elif [ "$(grep "$mount_point_match_regexp" /etc/fstab | grep -c "noexec")" -eq 0 ]; then + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/fstab | awk '{print $4}') + sed -i "s|\(${mount_point_match_regexp}.*${previous_mount_opts}\)|\1,noexec|" /etc/fstab + fi + + + if mkdir -p "/var/log"; then + if mountpoint -q "/var/log"; then + mount -o remount --target "/var/log" + else + mount --target "/var/log" + fi + fi +} + +perform_remediation + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Add nosuid Option to /var/log + The nosuid mount option can be used to prevent +execution of setuid programs in /var/log. The SUID and SGID permissions +should not be required in directories containing log files. +Add the nosuid option to the fourth column of +/etc/fstab for the line which controls mounting of +/var/log. + BP28(R12) + CCI-001764 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + AC-6 + AC-6(1) + MP-7 + PR.IP-1 + PR.PT-2 + PR.PT-3 + SRG-OS-000368-GPOS-00154 + RHEL-08-040127 + 1.1.5.4 + SV-230515r627750_rule + The presence of SUID and SGID executables should be tightly controlled. Users +should not be able to execute SUID or SGID binaries from partitions +designated for log files. + + CCE-82065-4 + +part /var/log --mountoptions="nosuid" + + - name: 'Add nosuid Option to /var/log: Check information associated to mountpoint' + command: findmnt --fstab '/var/log' + register: device_name + failed_when: device_name.rc > 1 + changed_when: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82065-4 + - DISA-STIG-RHEL-08-040127 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_log_nosuid + - no_reboot_needed + +- name: 'Add nosuid Option to /var/log: Create mount_info dictionary variable' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - '{{ device_name.stdout_lines[0].split() | list | lower }}' + - '{{ device_name.stdout_lines[1].split() | list }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - device_name.stdout is defined and device_name.stdout_lines is defined + - (device_name.stdout | length > 0) + tags: + - CCE-82065-4 + - DISA-STIG-RHEL-08-040127 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_log_nosuid + - no_reboot_needed + +- name: 'Add nosuid Option to /var/log: If /var/log not mounted, craft mount_info + manually' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - - target + - source + - fstype + - options + - - /var/log + - '' + - '' + - defaults + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ("--fstab" | length == 0) + - (device_name.stdout | length == 0) + tags: + - CCE-82065-4 + - DISA-STIG-RHEL-08-040127 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_log_nosuid + - no_reboot_needed + +- name: 'Add nosuid Option to /var/log: Make sure nosuid option is part of the to + /var/log options' + set_fact: + mount_info: '{{ mount_info | combine( {''options'':''''~mount_info.options~'',nosuid'' + }) }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - mount_info is defined and "nosuid" not in mount_info.options + tags: + - CCE-82065-4 + - DISA-STIG-RHEL-08-040127 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_log_nosuid + - no_reboot_needed + +- name: 'Add nosuid Option to /var/log: Ensure /var/log is mounted with nosuid option' + mount: + path: /var/log + src: '{{ mount_info.source }}' + opts: '{{ mount_info.options }}' + state: mounted + fstype: '{{ mount_info.fstype }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - (device_name.stdout is defined and (device_name.stdout | length > 0)) or ("--fstab" + | length == 0) + tags: + - CCE-82065-4 + - DISA-STIG-RHEL-08-040127 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_log_nosuid + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +function perform_remediation { + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" "/var/log")" + + grep "$mount_point_match_regexp" -q /etc/fstab \ + || { echo "The mount point '/var/log' is not even in /etc/fstab, so we can't set up mount options" >&2; + echo "Not remediating, because there is no record of /var/log in /etc/fstab" >&2; return 1; } + + + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" /var/log)" + + # If the mount point is not in /etc/fstab, get previous mount options from /etc/mtab + if [ "$(grep -c "$mount_point_match_regexp" /etc/fstab)" -eq 0 ]; then + # runtime opts without some automatic kernel/userspace-added defaults + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/mtab | head -1 | awk '{print $4}' \ + | sed -E "s/(rw|defaults|seclabel|nosuid)(,|$)//g;s/,$//") + [ "$previous_mount_opts" ] && previous_mount_opts+="," + echo " /var/log defaults,${previous_mount_opts}nosuid 0 0" >> /etc/fstab + # If the mount_opt option is not already in the mount point's /etc/fstab entry, add it + elif [ "$(grep "$mount_point_match_regexp" /etc/fstab | grep -c "nosuid")" -eq 0 ]; then + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/fstab | awk '{print $4}') + sed -i "s|\(${mount_point_match_regexp}.*${previous_mount_opts}\)|\1,nosuid|" /etc/fstab + fi + + + if mkdir -p "/var/log"; then + if mountpoint -q "/var/log"; then + mount -o remount --target "/var/log" + else + mount --target "/var/log" + fi + fi +} + +perform_remediation + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Add nodev Option to /var + The nodev mount option can be used to prevent device files from +being created in /var. +Legitimate character and block devices should exist only in +the /dev directory on the root partition or within chroot +jails built for system services. +Add the nodev option to the fourth column of +/etc/fstab for the line which controls mounting of +/var. + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + AC-6 + AC-6(1) + MP-7 + PR.IP-1 + PR.PT-2 + PR.PT-3 + SRG-OS-000368-GPOS-00154 + 1.1.3.2 + The only legitimate location for device files is the /dev directory +located on the root partition. The only exception to this is chroot jails. + + CCE-82062-1 + +part /var --mountoptions="nodev" + + - name: 'Add nodev Option to /var: Check information associated to mountpoint' + command: findmnt --fstab '/var' + register: device_name + failed_when: device_name.rc > 1 + changed_when: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82062-1 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_nodev + - no_reboot_needed + +- name: 'Add nodev Option to /var: Create mount_info dictionary variable' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - '{{ device_name.stdout_lines[0].split() | list | lower }}' + - '{{ device_name.stdout_lines[1].split() | list }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - device_name.stdout is defined and device_name.stdout_lines is defined + - (device_name.stdout | length > 0) + tags: + - CCE-82062-1 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_nodev + - no_reboot_needed + +- name: 'Add nodev Option to /var: If /var not mounted, craft mount_info manually' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - - target + - source + - fstype + - options + - - /var + - '' + - '' + - defaults + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ("--fstab" | length == 0) + - (device_name.stdout | length == 0) + tags: + - CCE-82062-1 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_nodev + - no_reboot_needed + +- name: 'Add nodev Option to /var: Make sure nodev option is part of the to /var options' + set_fact: + mount_info: '{{ mount_info | combine( {''options'':''''~mount_info.options~'',nodev'' + }) }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - mount_info is defined and "nodev" not in mount_info.options + tags: + - CCE-82062-1 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_nodev + - no_reboot_needed + +- name: 'Add nodev Option to /var: Ensure /var is mounted with nodev option' + mount: + path: /var + src: '{{ mount_info.source }}' + opts: '{{ mount_info.options }}' + state: mounted + fstype: '{{ mount_info.fstype }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - (device_name.stdout is defined and (device_name.stdout | length > 0)) or ("--fstab" + | length == 0) + tags: + - CCE-82062-1 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_nodev + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +function perform_remediation { + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" "/var")" + + grep "$mount_point_match_regexp" -q /etc/fstab \ + || { echo "The mount point '/var' is not even in /etc/fstab, so we can't set up mount options" >&2; + echo "Not remediating, because there is no record of /var in /etc/fstab" >&2; return 1; } + + + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" /var)" + + # If the mount point is not in /etc/fstab, get previous mount options from /etc/mtab + if [ "$(grep -c "$mount_point_match_regexp" /etc/fstab)" -eq 0 ]; then + # runtime opts without some automatic kernel/userspace-added defaults + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/mtab | head -1 | awk '{print $4}' \ + | sed -E "s/(rw|defaults|seclabel|nodev)(,|$)//g;s/,$//") + [ "$previous_mount_opts" ] && previous_mount_opts+="," + echo " /var defaults,${previous_mount_opts}nodev 0 0" >> /etc/fstab + # If the mount_opt option is not already in the mount point's /etc/fstab entry, add it + elif [ "$(grep "$mount_point_match_regexp" /etc/fstab | grep -c "nodev")" -eq 0 ]; then + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/fstab | awk '{print $4}') + sed -i "s|\(${mount_point_match_regexp}.*${previous_mount_opts}\)|\1,nodev|" /etc/fstab + fi + + + if mkdir -p "/var"; then + if mountpoint -q "/var"; then + mount -o remount --target "/var" + else + mount --target "/var" + fi + fi +} + +perform_remediation + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Add noexec Option to /var + The noexec mount option can be used to prevent binaries from being +executed out of /var. +Add the noexec option to the fourth column of +/etc/fstab for the line which controls mounting of +/var. + BP28(R12) + 1.1.3.3 + The /var directory contains variable system data such as logs, +mails and caches. No binaries should be executed from this directory. + + CCE-83330-1 + +part /var --mountoptions="noexec" + + - name: 'Add noexec Option to /var: Check information associated to mountpoint' + command: findmnt --fstab '/var' + register: device_name + failed_when: device_name.rc > 1 + changed_when: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83330-1 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_noexec + - no_reboot_needed + +- name: 'Add noexec Option to /var: Create mount_info dictionary variable' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - '{{ device_name.stdout_lines[0].split() | list | lower }}' + - '{{ device_name.stdout_lines[1].split() | list }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - device_name.stdout is defined and device_name.stdout_lines is defined + - (device_name.stdout | length > 0) + tags: + - CCE-83330-1 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_noexec + - no_reboot_needed + +- name: 'Add noexec Option to /var: If /var not mounted, craft mount_info manually' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - - target + - source + - fstype + - options + - - /var + - '' + - '' + - defaults + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ("--fstab" | length == 0) + - (device_name.stdout | length == 0) + tags: + - CCE-83330-1 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_noexec + - no_reboot_needed + +- name: 'Add noexec Option to /var: Make sure noexec option is part of the to /var + options' + set_fact: + mount_info: '{{ mount_info | combine( {''options'':''''~mount_info.options~'',noexec'' + }) }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - mount_info is defined and "noexec" not in mount_info.options + tags: + - CCE-83330-1 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_noexec + - no_reboot_needed + +- name: 'Add noexec Option to /var: Ensure /var is mounted with noexec option' + mount: + path: /var + src: '{{ mount_info.source }}' + opts: '{{ mount_info.options }}' + state: mounted + fstype: '{{ mount_info.fstype }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - (device_name.stdout is defined and (device_name.stdout | length > 0)) or ("--fstab" + | length == 0) + tags: + - CCE-83330-1 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_noexec + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +function perform_remediation { + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" "/var")" + + grep "$mount_point_match_regexp" -q /etc/fstab \ + || { echo "The mount point '/var' is not even in /etc/fstab, so we can't set up mount options" >&2; + echo "Not remediating, because there is no record of /var in /etc/fstab" >&2; return 1; } + + + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" /var)" + + # If the mount point is not in /etc/fstab, get previous mount options from /etc/mtab + if [ "$(grep -c "$mount_point_match_regexp" /etc/fstab)" -eq 0 ]; then + # runtime opts without some automatic kernel/userspace-added defaults + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/mtab | head -1 | awk '{print $4}' \ + | sed -E "s/(rw|defaults|seclabel|noexec)(,|$)//g;s/,$//") + [ "$previous_mount_opts" ] && previous_mount_opts+="," + echo " /var defaults,${previous_mount_opts}noexec 0 0" >> /etc/fstab + # If the mount_opt option is not already in the mount point's /etc/fstab entry, add it + elif [ "$(grep "$mount_point_match_regexp" /etc/fstab | grep -c "noexec")" -eq 0 ]; then + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/fstab | awk '{print $4}') + sed -i "s|\(${mount_point_match_regexp}.*${previous_mount_opts}\)|\1,noexec|" /etc/fstab + fi + + + if mkdir -p "/var"; then + if mountpoint -q "/var"; then + mount -o remount --target "/var" + else + mount --target "/var" + fi + fi +} + +perform_remediation + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Add nosuid Option to /var + The nosuid mount option can be used to prevent +execution of setuid programs in /var. The SUID and SGID permissions +should not be required for this directory. +Add the nosuid option to the fourth column of +/etc/fstab for the line which controls mounting of +/var. + BP28(R12) + 1.1.3.4 + The presence of SUID and SGID executables should be tightly controlled. + + CCE-83383-0 + +part /var --mountoptions="nosuid" + + - name: 'Add nosuid Option to /var: Check information associated to mountpoint' + command: findmnt --fstab '/var' + register: device_name + failed_when: device_name.rc > 1 + changed_when: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83383-0 + - configure_strategy + - high_disruption + - low_complexity + - mount_option_var_nosuid + - no_reboot_needed + - unknown_severity + +- name: 'Add nosuid Option to /var: Create mount_info dictionary variable' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - '{{ device_name.stdout_lines[0].split() | list | lower }}' + - '{{ device_name.stdout_lines[1].split() | list }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - device_name.stdout is defined and device_name.stdout_lines is defined + - (device_name.stdout | length > 0) + tags: + - CCE-83383-0 + - configure_strategy + - high_disruption + - low_complexity + - mount_option_var_nosuid + - no_reboot_needed + - unknown_severity + +- name: 'Add nosuid Option to /var: If /var not mounted, craft mount_info manually' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - - target + - source + - fstype + - options + - - /var + - '' + - '' + - defaults + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ("--fstab" | length == 0) + - (device_name.stdout | length == 0) + tags: + - CCE-83383-0 + - configure_strategy + - high_disruption + - low_complexity + - mount_option_var_nosuid + - no_reboot_needed + - unknown_severity + +- name: 'Add nosuid Option to /var: Make sure nosuid option is part of the to /var + options' + set_fact: + mount_info: '{{ mount_info | combine( {''options'':''''~mount_info.options~'',nosuid'' + }) }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - mount_info is defined and "nosuid" not in mount_info.options + tags: + - CCE-83383-0 + - configure_strategy + - high_disruption + - low_complexity + - mount_option_var_nosuid + - no_reboot_needed + - unknown_severity + +- name: 'Add nosuid Option to /var: Ensure /var is mounted with nosuid option' + mount: + path: /var + src: '{{ mount_info.source }}' + opts: '{{ mount_info.options }}' + state: mounted + fstype: '{{ mount_info.fstype }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - (device_name.stdout is defined and (device_name.stdout | length > 0)) or ("--fstab" + | length == 0) + tags: + - CCE-83383-0 + - configure_strategy + - high_disruption + - low_complexity + - mount_option_var_nosuid + - no_reboot_needed + - unknown_severity + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +function perform_remediation { + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" "/var")" + + grep "$mount_point_match_regexp" -q /etc/fstab \ + || { echo "The mount point '/var' is not even in /etc/fstab, so we can't set up mount options" >&2; + echo "Not remediating, because there is no record of /var in /etc/fstab" >&2; return 1; } + + + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" /var)" + + # If the mount point is not in /etc/fstab, get previous mount options from /etc/mtab + if [ "$(grep -c "$mount_point_match_regexp" /etc/fstab)" -eq 0 ]; then + # runtime opts without some automatic kernel/userspace-added defaults + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/mtab | head -1 | awk '{print $4}' \ + | sed -E "s/(rw|defaults|seclabel|nosuid)(,|$)//g;s/,$//") + [ "$previous_mount_opts" ] && previous_mount_opts+="," + echo " /var defaults,${previous_mount_opts}nosuid 0 0" >> /etc/fstab + # If the mount_opt option is not already in the mount point's /etc/fstab entry, add it + elif [ "$(grep "$mount_point_match_regexp" /etc/fstab | grep -c "nosuid")" -eq 0 ]; then + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/fstab | awk '{print $4}') + sed -i "s|\(${mount_point_match_regexp}.*${previous_mount_opts}\)|\1,nosuid|" /etc/fstab + fi + + + if mkdir -p "/var"; then + if mountpoint -q "/var"; then + mount -o remount --target "/var" + else + mount --target "/var" + fi + fi +} + +perform_remediation + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Bind Mount /var/tmp To /tmp + The /var/tmp directory is a world-writable directory. Bind-mount +it to /tmp in order to consolidate temporary storage into one +location protected by the same techniques as /tmp. To do so, edit +/etc/fstab and add the following line: +/tmp /var/tmp none rw,nodev,noexec,nosuid,bind 0 0 +See the mount(8) man page for further explanation of bind mounting. + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + AC-6 + AC-6(1) + MP-7 + PR.IP-1 + PR.PT-3 + Having multiple locations for temporary storage is not required. Unless absolutely +necessary to meet requirements, the storage location /var/tmp should be bind mounted to +/tmp and thus share the same protections. + + +part /var/tmp --mountoptions="bind" + + - name: 'Bind Mount /var/tmp To /tmp: Check information associated to mountpoint' + command: findmnt --fstab '/var/tmp' + register: device_name + failed_when: device_name.rc > 1 + changed_when: false + when: ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", + "container"] and "/var/tmp" in ansible_mounts | map(attribute="mount") | list + ) + tags: + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - mount_option_var_tmp_bind + - no_reboot_needed + - unknown_severity + +- name: 'Bind Mount /var/tmp To /tmp: Create mount_info dictionary variable' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - '{{ device_name.stdout_lines[0].split() | list | lower }}' + - '{{ device_name.stdout_lines[1].split() | list }}' + when: + - ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + and "/var/tmp" in ansible_mounts | map(attribute="mount") | list ) + - device_name.stdout is defined and device_name.stdout_lines is defined + - (device_name.stdout | length > 0) + tags: + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - mount_option_var_tmp_bind + - no_reboot_needed + - unknown_severity + +- name: 'Bind Mount /var/tmp To /tmp: If /var/tmp not mounted, craft mount_info manually' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - - target + - source + - fstype + - options + - - /var/tmp + - '' + - '' + - defaults + when: + - ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + and "/var/tmp" in ansible_mounts | map(attribute="mount") | list ) + - ("--fstab" | length == 0) + - (device_name.stdout | length == 0) + tags: + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - mount_option_var_tmp_bind + - no_reboot_needed + - unknown_severity + +- name: 'Bind Mount /var/tmp To /tmp: Make sure bind option is part of the to /var/tmp + options' + set_fact: + mount_info: '{{ mount_info | combine( {''options'':''''~mount_info.options~'',bind'' + }) }}' + when: + - ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + and "/var/tmp" in ansible_mounts | map(attribute="mount") | list ) + - mount_info is defined and "bind" not in mount_info.options + tags: + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - mount_option_var_tmp_bind + - no_reboot_needed + - unknown_severity + +- name: 'Bind Mount /var/tmp To /tmp: Ensure /var/tmp is mounted with bind option' + mount: + path: /var/tmp + src: '{{ mount_info.source }}' + opts: '{{ mount_info.options }}' + state: mounted + fstype: '{{ mount_info.fstype }}' + when: + - ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + and "/var/tmp" in ansible_mounts | map(attribute="mount") | list ) + - (device_name.stdout is defined and (device_name.stdout | length > 0)) or ("--fstab" + | length == 0) + tags: + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - mount_option_var_tmp_bind + - no_reboot_needed + - unknown_severity + + # Remediation is applicable only in certain platforms +if ( [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && findmnt --kernel "/var/tmp" > /dev/null ); then + +# Delete particular /etc/fstab's row if /var/tmp is already configured to +# represent a mount point (for some device or filesystem other than /tmp) +if grep -q -P '.*\/var\/tmp.*' /etc/fstab +then + sed -i '/.*\/var\/tmp.*/d' /etc/fstab +fi +umount /var/tmp + +# Bind-mount /var/tmp to /tmp via /etc/fstab (preserving the /etc/fstab form) +printf "%-24s%-24s%-8s%-32s%-3s\n" "/tmp" "/var/tmp" "none" "rw,nodev,noexec,nosuid,bind" "0 0" >> /etc/fstab + +mkdir -p /var/tmp +mount -B /tmp /var/tmp + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + Add nodev Option to /var/tmp + The nodev mount option can be used to prevent device files from +being created in /var/tmp. Legitimate character and block devices +should not exist within temporary directories like /var/tmp. +Add the nodev option to the fourth column of +/etc/fstab for the line which controls mounting of +/var/tmp. + BP28(R12) + CCI-001764 + SRG-OS-000368-GPOS-00154 + RHEL-08-040132 + 1.1.4.4 + SV-230520r792927_rule + The only legitimate location for device files is the /dev directory +located on the root partition. The only exception to this is chroot jails. + + CCE-82068-8 + +part /var/tmp --mountoptions="nodev" + + - name: 'Add nodev Option to /var/tmp: Check information associated to mountpoint' + command: findmnt --fstab '/var/tmp' + register: device_name + failed_when: device_name.rc > 1 + changed_when: false + when: ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", + "container"] and "/var/tmp" in ansible_mounts | map(attribute="mount") | list + ) + tags: + - CCE-82068-8 + - DISA-STIG-RHEL-08-040132 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_tmp_nodev + - no_reboot_needed + +- name: 'Add nodev Option to /var/tmp: Create mount_info dictionary variable' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - '{{ device_name.stdout_lines[0].split() | list | lower }}' + - '{{ device_name.stdout_lines[1].split() | list }}' + when: + - ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + and "/var/tmp" in ansible_mounts | map(attribute="mount") | list ) + - device_name.stdout is defined and device_name.stdout_lines is defined + - (device_name.stdout | length > 0) + tags: + - CCE-82068-8 + - DISA-STIG-RHEL-08-040132 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_tmp_nodev + - no_reboot_needed + +- name: 'Add nodev Option to /var/tmp: If /var/tmp not mounted, craft mount_info manually' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - - target + - source + - fstype + - options + - - /var/tmp + - '' + - '' + - defaults + when: + - ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + and "/var/tmp" in ansible_mounts | map(attribute="mount") | list ) + - ("--fstab" | length == 0) + - (device_name.stdout | length == 0) + tags: + - CCE-82068-8 + - DISA-STIG-RHEL-08-040132 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_tmp_nodev + - no_reboot_needed + +- name: 'Add nodev Option to /var/tmp: Make sure nodev option is part of the to /var/tmp + options' + set_fact: + mount_info: '{{ mount_info | combine( {''options'':''''~mount_info.options~'',nodev'' + }) }}' + when: + - ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + and "/var/tmp" in ansible_mounts | map(attribute="mount") | list ) + - mount_info is defined and "nodev" not in mount_info.options + tags: + - CCE-82068-8 + - DISA-STIG-RHEL-08-040132 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_tmp_nodev + - no_reboot_needed + +- name: 'Add nodev Option to /var/tmp: Ensure /var/tmp is mounted with nodev option' + mount: + path: /var/tmp + src: '{{ mount_info.source }}' + opts: '{{ mount_info.options }}' + state: mounted + fstype: '{{ mount_info.fstype }}' + when: + - ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + and "/var/tmp" in ansible_mounts | map(attribute="mount") | list ) + - (device_name.stdout is defined and (device_name.stdout | length > 0)) or ("--fstab" + | length == 0) + tags: + - CCE-82068-8 + - DISA-STIG-RHEL-08-040132 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_tmp_nodev + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if ( [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && findmnt --kernel "/var/tmp" > /dev/null ); then + +function perform_remediation { + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" "/var/tmp")" + + grep "$mount_point_match_regexp" -q /etc/fstab \ + || { echo "The mount point '/var/tmp' is not even in /etc/fstab, so we can't set up mount options" >&2; + echo "Not remediating, because there is no record of /var/tmp in /etc/fstab" >&2; return 1; } + + + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" /var/tmp)" + + # If the mount point is not in /etc/fstab, get previous mount options from /etc/mtab + if [ "$(grep -c "$mount_point_match_regexp" /etc/fstab)" -eq 0 ]; then + # runtime opts without some automatic kernel/userspace-added defaults + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/mtab | head -1 | awk '{print $4}' \ + | sed -E "s/(rw|defaults|seclabel|nodev)(,|$)//g;s/,$//") + [ "$previous_mount_opts" ] && previous_mount_opts+="," + echo " /var/tmp defaults,${previous_mount_opts}nodev 0 0" >> /etc/fstab + # If the mount_opt option is not already in the mount point's /etc/fstab entry, add it + elif [ "$(grep "$mount_point_match_regexp" /etc/fstab | grep -c "nodev")" -eq 0 ]; then + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/fstab | awk '{print $4}') + sed -i "s|\(${mount_point_match_regexp}.*${previous_mount_opts}\)|\1,nodev|" /etc/fstab + fi + + + if mkdir -p "/var/tmp"; then + if mountpoint -q "/var/tmp"; then + mount -o remount --target "/var/tmp" + else + mount --target "/var/tmp" + fi + fi +} + +perform_remediation + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Add noexec Option to /var/tmp + The noexec mount option can be used to prevent binaries +from being executed out of /var/tmp. +Add the noexec option to the fourth column of +/etc/fstab for the line which controls mounting of +/var/tmp. + BP28(R12) + CCI-001764 + SRG-OS-000368-GPOS-00154 + RHEL-08-040134 + 1.1.4.2 + SV-230522r792933_rule + Allowing users to execute binaries from world-writable directories +such as /var/tmp should never be necessary in normal operation and +can expose the system to potential compromise. + + CCE-82151-2 + +part /var/tmp --mountoptions="noexec" + + - name: 'Add noexec Option to /var/tmp: Check information associated to mountpoint' + command: findmnt --fstab '/var/tmp' + register: device_name + failed_when: device_name.rc > 1 + changed_when: false + when: ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", + "container"] and "/var/tmp" in ansible_mounts | map(attribute="mount") | list + ) + tags: + - CCE-82151-2 + - DISA-STIG-RHEL-08-040134 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_tmp_noexec + - no_reboot_needed + +- name: 'Add noexec Option to /var/tmp: Create mount_info dictionary variable' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - '{{ device_name.stdout_lines[0].split() | list | lower }}' + - '{{ device_name.stdout_lines[1].split() | list }}' + when: + - ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + and "/var/tmp" in ansible_mounts | map(attribute="mount") | list ) + - device_name.stdout is defined and device_name.stdout_lines is defined + - (device_name.stdout | length > 0) + tags: + - CCE-82151-2 + - DISA-STIG-RHEL-08-040134 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_tmp_noexec + - no_reboot_needed + +- name: 'Add noexec Option to /var/tmp: If /var/tmp not mounted, craft mount_info + manually' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - - target + - source + - fstype + - options + - - /var/tmp + - '' + - '' + - defaults + when: + - ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + and "/var/tmp" in ansible_mounts | map(attribute="mount") | list ) + - ("--fstab" | length == 0) + - (device_name.stdout | length == 0) + tags: + - CCE-82151-2 + - DISA-STIG-RHEL-08-040134 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_tmp_noexec + - no_reboot_needed + +- name: 'Add noexec Option to /var/tmp: Make sure noexec option is part of the to + /var/tmp options' + set_fact: + mount_info: '{{ mount_info | combine( {''options'':''''~mount_info.options~'',noexec'' + }) }}' + when: + - ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + and "/var/tmp" in ansible_mounts | map(attribute="mount") | list ) + - mount_info is defined and "noexec" not in mount_info.options + tags: + - CCE-82151-2 + - DISA-STIG-RHEL-08-040134 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_tmp_noexec + - no_reboot_needed + +- name: 'Add noexec Option to /var/tmp: Ensure /var/tmp is mounted with noexec option' + mount: + path: /var/tmp + src: '{{ mount_info.source }}' + opts: '{{ mount_info.options }}' + state: mounted + fstype: '{{ mount_info.fstype }}' + when: + - ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + and "/var/tmp" in ansible_mounts | map(attribute="mount") | list ) + - (device_name.stdout is defined and (device_name.stdout | length > 0)) or ("--fstab" + | length == 0) + tags: + - CCE-82151-2 + - DISA-STIG-RHEL-08-040134 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_tmp_noexec + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if ( [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && findmnt --kernel "/var/tmp" > /dev/null ); then + +function perform_remediation { + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" "/var/tmp")" + + grep "$mount_point_match_regexp" -q /etc/fstab \ + || { echo "The mount point '/var/tmp' is not even in /etc/fstab, so we can't set up mount options" >&2; + echo "Not remediating, because there is no record of /var/tmp in /etc/fstab" >&2; return 1; } + + + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" /var/tmp)" + + # If the mount point is not in /etc/fstab, get previous mount options from /etc/mtab + if [ "$(grep -c "$mount_point_match_regexp" /etc/fstab)" -eq 0 ]; then + # runtime opts without some automatic kernel/userspace-added defaults + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/mtab | head -1 | awk '{print $4}' \ + | sed -E "s/(rw|defaults|seclabel|noexec)(,|$)//g;s/,$//") + [ "$previous_mount_opts" ] && previous_mount_opts+="," + echo " /var/tmp defaults,${previous_mount_opts}noexec 0 0" >> /etc/fstab + # If the mount_opt option is not already in the mount point's /etc/fstab entry, add it + elif [ "$(grep "$mount_point_match_regexp" /etc/fstab | grep -c "noexec")" -eq 0 ]; then + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/fstab | awk '{print $4}') + sed -i "s|\(${mount_point_match_regexp}.*${previous_mount_opts}\)|\1,noexec|" /etc/fstab + fi + + + if mkdir -p "/var/tmp"; then + if mountpoint -q "/var/tmp"; then + mount -o remount --target "/var/tmp" + else + mount --target "/var/tmp" + fi + fi +} + +perform_remediation + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Add nosuid Option to /var/tmp + The nosuid mount option can be used to prevent +execution of setuid programs in /var/tmp. The SUID and SGID permissions +should not be required in these world-writable directories. +Add the nosuid option to the fourth column of +/etc/fstab for the line which controls mounting of +/var/tmp. + BP28(R12) + CCI-001764 + SRG-OS-000368-GPOS-00154 + RHEL-08-040133 + 1.1.4.3 + SV-230521r792930_rule + The presence of SUID and SGID executables should be tightly controlled. Users +should not be able to execute SUID or SGID binaries from temporary storage partitions. + + CCE-82154-6 + +part /var/tmp --mountoptions="nosuid" + + - name: 'Add nosuid Option to /var/tmp: Check information associated to mountpoint' + command: findmnt --fstab '/var/tmp' + register: device_name + failed_when: device_name.rc > 1 + changed_when: false + when: ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", + "container"] and "/var/tmp" in ansible_mounts | map(attribute="mount") | list + ) + tags: + - CCE-82154-6 + - DISA-STIG-RHEL-08-040133 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_tmp_nosuid + - no_reboot_needed + +- name: 'Add nosuid Option to /var/tmp: Create mount_info dictionary variable' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - '{{ device_name.stdout_lines[0].split() | list | lower }}' + - '{{ device_name.stdout_lines[1].split() | list }}' + when: + - ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + and "/var/tmp" in ansible_mounts | map(attribute="mount") | list ) + - device_name.stdout is defined and device_name.stdout_lines is defined + - (device_name.stdout | length > 0) + tags: + - CCE-82154-6 + - DISA-STIG-RHEL-08-040133 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_tmp_nosuid + - no_reboot_needed + +- name: 'Add nosuid Option to /var/tmp: If /var/tmp not mounted, craft mount_info + manually' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - - target + - source + - fstype + - options + - - /var/tmp + - '' + - '' + - defaults + when: + - ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + and "/var/tmp" in ansible_mounts | map(attribute="mount") | list ) + - ("--fstab" | length == 0) + - (device_name.stdout | length == 0) + tags: + - CCE-82154-6 + - DISA-STIG-RHEL-08-040133 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_tmp_nosuid + - no_reboot_needed + +- name: 'Add nosuid Option to /var/tmp: Make sure nosuid option is part of the to + /var/tmp options' + set_fact: + mount_info: '{{ mount_info | combine( {''options'':''''~mount_info.options~'',nosuid'' + }) }}' + when: + - ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + and "/var/tmp" in ansible_mounts | map(attribute="mount") | list ) + - mount_info is defined and "nosuid" not in mount_info.options + tags: + - CCE-82154-6 + - DISA-STIG-RHEL-08-040133 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_tmp_nosuid + - no_reboot_needed + +- name: 'Add nosuid Option to /var/tmp: Ensure /var/tmp is mounted with nosuid option' + mount: + path: /var/tmp + src: '{{ mount_info.source }}' + opts: '{{ mount_info.options }}' + state: mounted + fstype: '{{ mount_info.fstype }}' + when: + - ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + and "/var/tmp" in ansible_mounts | map(attribute="mount") | list ) + - (device_name.stdout is defined and (device_name.stdout | length > 0)) or ("--fstab" + | length == 0) + tags: + - CCE-82154-6 + - DISA-STIG-RHEL-08-040133 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_tmp_nosuid + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if ( [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && findmnt --kernel "/var/tmp" > /dev/null ); then + +function perform_remediation { + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" "/var/tmp")" + + grep "$mount_point_match_regexp" -q /etc/fstab \ + || { echo "The mount point '/var/tmp' is not even in /etc/fstab, so we can't set up mount options" >&2; + echo "Not remediating, because there is no record of /var/tmp in /etc/fstab" >&2; return 1; } + + + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" /var/tmp)" + + # If the mount point is not in /etc/fstab, get previous mount options from /etc/mtab + if [ "$(grep -c "$mount_point_match_regexp" /etc/fstab)" -eq 0 ]; then + # runtime opts without some automatic kernel/userspace-added defaults + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/mtab | head -1 | awk '{print $4}' \ + | sed -E "s/(rw|defaults|seclabel|nosuid)(,|$)//g;s/,$//") + [ "$previous_mount_opts" ] && previous_mount_opts+="," + echo " /var/tmp defaults,${previous_mount_opts}nosuid 0 0" >> /etc/fstab + # If the mount_opt option is not already in the mount point's /etc/fstab entry, add it + elif [ "$(grep "$mount_point_match_regexp" /etc/fstab | grep -c "nosuid")" -eq 0 ]; then + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/fstab | awk '{print $4}') + sed -i "s|\(${mount_point_match_regexp}.*${previous_mount_opts}\)|\1,nosuid|" /etc/fstab + fi + + + if mkdir -p "/var/tmp"; then + if mountpoint -q "/var/tmp"; then + mount -o remount --target "/var/tmp" + else + mount --target "/var/tmp" + fi + fi +} + +perform_remediation + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Verify Permissions on Important Files and +Directories Are Configured in /etc/permissions.local + Permissions for many files on a system must be set +restrictively to ensure sensitive information is properly protected. +This section discusses the /etc/permissions.local file, where +expected permissions can be configured to be checked and fixed through +usage of the chkstat command. + + + Restrict Programs from Dangerous Execution Patterns + The recommendations in this section are designed to +ensure that the system's features to protect against potentially +dangerous program execution are activated. +These protections are applied at the system initialization or +kernel level, and defend against certain types of badly-configured +or compromised programs. + + kernel.unprivileged_bpf_disabled + Prevent unprivileged processes from using the bpf() syscall. + 2 + 1 + 2 + + + Disable the uvcvideo module + If the device contains a camera it should be covered or disabled when not in use. + CCI-000381 + CM-7 (a) + CM-7 (5) (b) + SRG-OS-000095-GPOS-00049 + SRG-OS-000370-GPOS-00155 + RHEL-08-040020 + SV-230493r809316_rule + Failing to disconnect from collaborative computing devices (i.e., cameras) can result in subsequent compromises of organizational information. +Providing easy methods to physically disconnect from such devices after a collaborative computing session helps to ensure participants actually carry out the disconnect activity without having to go through complex and tedious procedures. + + CCE-86960-2 + - name: Ensure kernel module 'uvcvideo' is disabled + lineinfile: + create: true + dest: /etc/modprobe.d/uvcvideo.conf + regexp: uvcvideo + line: install uvcvideo /bin/true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86960-2 + - DISA-STIG-RHEL-08-040020 + - NIST-800-53-CM-7 (5) (b) + - NIST-800-53-CM-7 (a) + - disable_strategy + - kernel_module_uvcvideo_disabled + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + +- name: Ensure kernel module 'uvcvideo' is blacklisted + lineinfile: + create: true + dest: /etc/modprobe.d/uvcvideo.conf + regexp: ^blacklist uvcvideo$ + line: blacklist uvcvideo + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86960-2 + - DISA-STIG-RHEL-08-040020 + - NIST-800-53-CM-7 (5) (b) + - NIST-800-53-CM-7 (a) + - disable_strategy + - kernel_module_uvcvideo_disabled + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,install%20uvcvideo%20/bin/true%0Ablacklist%20uvcvideo%0A + mode: 0644 + path: /etc/modprobe.d/uvcvideo.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if LC_ALL=C grep -q -m 1 "^install uvcvideo" /etc/modprobe.d/uvcvideo.conf ; then + + sed -i 's#^install uvcvideo.*#install uvcvideo /bin/true#g' /etc/modprobe.d/uvcvideo.conf +else + echo -e "\n# Disable per security requirements" >> /etc/modprobe.d/uvcvideo.conf + echo "install uvcvideo /bin/true" >> /etc/modprobe.d/uvcvideo.conf +fi + +if ! LC_ALL=C grep -q -m 1 "^blacklist uvcvideo$" /etc/modprobe.d/uvcvideo.conf ; then + echo "blacklist uvcvideo" >> /etc/modprobe.d/uvcvideo.conf +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable storing core dumps + To set the runtime status of the kernel.core_pattern kernel parameter, run the following command: $ sudo sysctl -w kernel.core_pattern=|/bin/false +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: kernel.core_pattern = |/bin/false + CCI-000366 + SC-7(10) + FMT_SMF_EXT.1 + SRG-OS-000480-GPOS-00227 + RHEL-08-010671 + SV-230311r833305_rule + A core dump includes a memory image taken at the time the operating system +terminates an application. The memory image could contain sensitive data and is generally useful +only for developers trying to debug problems. + + CCE-82215-5 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*kernel.core_pattern.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82215-5 + - DISA-STIG-RHEL-08-010671 + - NIST-800-53-SC-7(10) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_kernel_core_pattern + +- name: Comment out any occurrences of kernel.core_pattern from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*kernel.core_pattern + replace: '#kernel.core_pattern' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82215-5 + - DISA-STIG-RHEL-08-010671 + - NIST-800-53-SC-7(10) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_kernel_core_pattern + +- name: Ensure sysctl kernel.core_pattern is set to |/bin/false + sysctl: + name: kernel.core_pattern + value: '|/bin/false' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82215-5 + - DISA-STIG-RHEL-08-010671 + - NIST-800-53-SC-7(10) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_kernel_core_pattern + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,kernel.core_pattern%20%3D%20%7C/bin/false%0A + mode: 0644 + path: /etc/sysctl.d/75-sysctl_kernel_core_pattern.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of kernel.core_pattern from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*kernel.core_pattern.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "kernel.core_pattern" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done + +# +# Set runtime for kernel.core_pattern +# +/sbin/sysctl -q -n -w kernel.core_pattern="|/bin/false" + +# +# If kernel.core_pattern present in /etc/sysctl.conf, change value to "|/bin/false" +# else, add "kernel.core_pattern = |/bin/false" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^kernel.core_pattern") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "|/bin/false" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^kernel.core_pattern\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^kernel.core_pattern\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-82215-5" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure file name of core dumps + To set the runtime status of the kernel.core_uses_pid kernel parameter, run the following command: $ sudo sysctl -w kernel.core_uses_pid=0 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: kernel.core_uses_pid = 0 + FMT_SMF_EXT.1 + The default coredump filename is core. By setting +core_uses_pid to 1, the coredump filename becomes +core.PID. If core_pattern does not include +%p (default does not) and core_uses_pid is set, then +.PID will be appended to the filename. +When combined with kernel.core_pattern = "" configuration, it +is ensured that no core dumps are generated and also no confusing error +messages are printed by a shell. + + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*kernel.core_uses_pid.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_kernel_core_uses_pid + +- name: Comment out any occurrences of kernel.core_uses_pid from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*kernel.core_uses_pid + replace: '#kernel.core_uses_pid' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_kernel_core_uses_pid + +- name: Ensure sysctl kernel.core_uses_pid is set to 0 + sysctl: + name: kernel.core_uses_pid + value: '0' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_kernel_core_uses_pid + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of kernel.core_uses_pid from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*kernel.core_uses_pid.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "kernel.core_uses_pid" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done + +# +# Set runtime for kernel.core_uses_pid +# +/sbin/sysctl -q -n -w kernel.core_uses_pid="0" + +# +# If kernel.core_uses_pid present in /etc/sysctl.conf, change value to "0" +# else, add "kernel.core_uses_pid = 0" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^kernel.core_uses_pid") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "0" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^kernel.core_uses_pid\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^kernel.core_uses_pid\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Restrict Access to Kernel Message Buffer + To set the runtime status of the kernel.dmesg_restrict kernel parameter, run the following command: $ sudo sysctl -w kernel.dmesg_restrict=1 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: kernel.dmesg_restrict = 1 + BP28(R23) + 3.1.5 + CCI-001090 + CCI-001314 + 164.308(a)(1)(ii)(D) + 164.308(a)(3) + 164.308(a)(4) + 164.310(b) + 164.310(c) + 164.312(a) + 164.312(e) + SI-11(a) + SI-11(b) + SRG-OS-000132-GPOS-00067 + SRG-OS-000138-GPOS-00069 + RHEL-08-010375 + SV-230269r833296_rule + Unprivileged access to the kernel syslog can expose sensitive kernel +address information. + + CCE-80913-7 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*kernel.dmesg_restrict.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80913-7 + - DISA-STIG-RHEL-08-010375 + - NIST-800-171-3.1.5 + - NIST-800-53-SI-11(a) + - NIST-800-53-SI-11(b) + - disable_strategy + - low_complexity + - low_severity + - medium_disruption + - reboot_required + - sysctl_kernel_dmesg_restrict + +- name: Comment out any occurrences of kernel.dmesg_restrict from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*kernel.dmesg_restrict + replace: '#kernel.dmesg_restrict' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80913-7 + - DISA-STIG-RHEL-08-010375 + - NIST-800-171-3.1.5 + - NIST-800-53-SI-11(a) + - NIST-800-53-SI-11(b) + - disable_strategy + - low_complexity + - low_severity + - medium_disruption + - reboot_required + - sysctl_kernel_dmesg_restrict + +- name: Ensure sysctl kernel.dmesg_restrict is set to 1 + sysctl: + name: kernel.dmesg_restrict + value: '1' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80913-7 + - DISA-STIG-RHEL-08-010375 + - NIST-800-171-3.1.5 + - NIST-800-53-SI-11(a) + - NIST-800-53-SI-11(b) + - disable_strategy + - low_complexity + - low_severity + - medium_disruption + - reboot_required + - sysctl_kernel_dmesg_restrict + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,kernel.dmesg_restrict%3D1%0A + mode: 0644 + path: /etc/sysctl.d/75-sysctl_kernel_dmesg_restrict.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of kernel.dmesg_restrict from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*kernel.dmesg_restrict.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "kernel.dmesg_restrict" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done + +# +# Set runtime for kernel.dmesg_restrict +# +/sbin/sysctl -q -n -w kernel.dmesg_restrict="1" + +# +# If kernel.dmesg_restrict present in /etc/sysctl.conf, change value to "1" +# else, add "kernel.dmesg_restrict = 1" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^kernel.dmesg_restrict") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "1" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^kernel.dmesg_restrict\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^kernel.dmesg_restrict\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-80913-7" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable Kernel Image Loading + To set the runtime status of the kernel.kexec_load_disabled kernel parameter, run the following command: $ sudo sysctl -w kernel.kexec_load_disabled=1 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: kernel.kexec_load_disabled = 1 + CCI-001749 + CM-6 + SRG-OS-000480-GPOS-00227 + SRG-OS-000366-GPOS-00153 + RHEL-08-010372 + SV-230266r833290_rule + Disabling kexec_load allows greater control of the kernel memory. +It makes it impossible to load another kernel image after it has been disabled. + + + CCE-80952-5 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*kernel.kexec_load_disabled.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80952-5 + - DISA-STIG-RHEL-08-010372 + - NIST-800-53-CM-6 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_kernel_kexec_load_disabled + +- name: Comment out any occurrences of kernel.kexec_load_disabled from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*kernel.kexec_load_disabled + replace: '#kernel.kexec_load_disabled' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80952-5 + - DISA-STIG-RHEL-08-010372 + - NIST-800-53-CM-6 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_kernel_kexec_load_disabled + +- name: Ensure sysctl kernel.kexec_load_disabled is set to 1 + sysctl: + name: kernel.kexec_load_disabled + value: '1' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80952-5 + - DISA-STIG-RHEL-08-010372 + - NIST-800-53-CM-6 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_kernel_kexec_load_disabled + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,kernel.kexec_load_disabled%3D1%0A + mode: 0644 + path: /etc/sysctl.d/75-sysctl_kernel_kexec_load_disabled.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of kernel.kexec_load_disabled from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*kernel.kexec_load_disabled.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "kernel.kexec_load_disabled" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done + +# +# Set runtime for kernel.kexec_load_disabled +# +/sbin/sysctl -q -n -w kernel.kexec_load_disabled="1" + +# +# If kernel.kexec_load_disabled present in /etc/sysctl.conf, change value to "1" +# else, add "kernel.kexec_load_disabled = 1" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^kernel.kexec_load_disabled") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "1" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^kernel.kexec_load_disabled\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^kernel.kexec_load_disabled\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-80952-5" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable loading and unloading of kernel modules + To set the runtime status of the kernel.modules_disabled kernel parameter, run the following command: $ sudo sysctl -w kernel.modules_disabled=1 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: kernel.modules_disabled = 1 + This rule doesn't come with Bash remediation. Remediating this rule during the installation process disrupts the install and boot process. + BP28(R24) + Malicious kernel modules can have a significant impact on system security and +availability. Disabling loading of kernel modules prevents this threat. Note +that once this option has been set, it cannot be reverted without doing a +system reboot. Make sure that all needed kernel modules are loaded before +setting this option. + + CCE-83397-0 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*kernel.modules_disabled.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83397-0 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_kernel_modules_disabled + +- name: Comment out any occurrences of kernel.modules_disabled from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*kernel.modules_disabled + replace: '#kernel.modules_disabled' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83397-0 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_kernel_modules_disabled + +- name: Ensure sysctl kernel.modules_disabled is set to 1 + sysctl: + name: kernel.modules_disabled + value: '1' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83397-0 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_kernel_modules_disabled + + + + + + + + + + Kernel panic on oops + To set the runtime status of the kernel.panic_on_oops kernel parameter, run the following command: $ sudo sysctl -w kernel.panic_on_oops=1 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: kernel.panic_on_oops = 1 + The system may start to panic when it normally wouldn't. A non-catastrophic error that +would have allowed the system to continue operating will now result in a panic. + An attacker trying to exploit the kernel may trigger kernel OOPSes, +panicking the system will impede them from continuing. + + CCE-87666-4 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*kernel.panic_on_oops.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-87666-4 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_kernel_panic_on_oops + +- name: Comment out any occurrences of kernel.panic_on_oops from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*kernel.panic_on_oops + replace: '#kernel.panic_on_oops' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-87666-4 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_kernel_panic_on_oops + +- name: Ensure sysctl kernel.panic_on_oops is set to 1 + sysctl: + name: kernel.panic_on_oops + value: '1' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-87666-4 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_kernel_panic_on_oops + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of kernel.panic_on_oops from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*kernel.panic_on_oops.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "kernel.panic_on_oops" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done + +# +# Set runtime for kernel.panic_on_oops +# +/sbin/sysctl -q -n -w kernel.panic_on_oops="1" + +# +# If kernel.panic_on_oops present in /etc/sysctl.conf, change value to "1" +# else, add "kernel.panic_on_oops = 1" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^kernel.panic_on_oops") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "1" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^kernel.panic_on_oops\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^kernel.panic_on_oops\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-87666-4" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Limit CPU consumption of the Perf system + To set the runtime status of the kernel.perf_cpu_time_max_percent kernel parameter, run the following command: $ sudo sysctl -w kernel.perf_cpu_time_max_percent=1 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: kernel.perf_cpu_time_max_percent = 1 + BP28(R23) + The kernel.perf_cpu_time_max_percent configures a treshold of +maximum percentile of CPU that can be used by Perf system. Restricting usage +of Perf system decreases risk of potential availability problems. + + CCE-83373-1 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*kernel.perf_cpu_time_max_percent.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83373-1 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_kernel_perf_cpu_time_max_percent + +- name: Comment out any occurrences of kernel.perf_cpu_time_max_percent from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*kernel.perf_cpu_time_max_percent + replace: '#kernel.perf_cpu_time_max_percent' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83373-1 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_kernel_perf_cpu_time_max_percent + +- name: Ensure sysctl kernel.perf_cpu_time_max_percent is set to 1 + sysctl: + name: kernel.perf_cpu_time_max_percent + value: '1' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83373-1 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_kernel_perf_cpu_time_max_percent + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of kernel.perf_cpu_time_max_percent from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*kernel.perf_cpu_time_max_percent.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "kernel.perf_cpu_time_max_percent" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done + +# +# Set runtime for kernel.perf_cpu_time_max_percent +# +/sbin/sysctl -q -n -w kernel.perf_cpu_time_max_percent="1" + +# +# If kernel.perf_cpu_time_max_percent present in /etc/sysctl.conf, change value to "1" +# else, add "kernel.perf_cpu_time_max_percent = 1" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^kernel.perf_cpu_time_max_percent") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "1" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^kernel.perf_cpu_time_max_percent\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^kernel.perf_cpu_time_max_percent\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-83373-1" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Limit sampling frequency of the Perf system + To set the runtime status of the kernel.perf_event_max_sample_rate kernel parameter, run the following command: $ sudo sysctl -w kernel.perf_event_max_sample_rate=1 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: kernel.perf_event_max_sample_rate = 1 + BP28(R23) + The kernel.perf_event_max_sample_rate parameter configures maximum +frequency of collecting of samples for the Perf system. It is expressed in +samples per second. Restricting usage of Perf system decreases risk +of potential availability problems. + + CCE-83368-1 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*kernel.perf_event_max_sample_rate.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83368-1 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_kernel_perf_event_max_sample_rate + +- name: Comment out any occurrences of kernel.perf_event_max_sample_rate from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*kernel.perf_event_max_sample_rate + replace: '#kernel.perf_event_max_sample_rate' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83368-1 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_kernel_perf_event_max_sample_rate + +- name: Ensure sysctl kernel.perf_event_max_sample_rate is set to 1 + sysctl: + name: kernel.perf_event_max_sample_rate + value: '1' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83368-1 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_kernel_perf_event_max_sample_rate + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of kernel.perf_event_max_sample_rate from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*kernel.perf_event_max_sample_rate.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "kernel.perf_event_max_sample_rate" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done + +# +# Set runtime for kernel.perf_event_max_sample_rate +# +/sbin/sysctl -q -n -w kernel.perf_event_max_sample_rate="1" + +# +# If kernel.perf_event_max_sample_rate present in /etc/sysctl.conf, change value to "1" +# else, add "kernel.perf_event_max_sample_rate = 1" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^kernel.perf_event_max_sample_rate") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "1" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^kernel.perf_event_max_sample_rate\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^kernel.perf_event_max_sample_rate\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-83368-1" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disallow kernel profiling by unprivileged users + To set the runtime status of the kernel.perf_event_paranoid kernel parameter, run the following command: $ sudo sysctl -w kernel.perf_event_paranoid=2 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: kernel.perf_event_paranoid = 2 + BP28(R23) + CCI-001090 + AC-6 + FMT_SMF_EXT.1 + SRG-OS-000132-GPOS-00067 + SRG-OS-000138-GPOS-00069 + RHEL-08-010376 + SV-230270r833298_rule + Kernel profiling can reveal sensitive information about kernel behaviour. + + CCE-81054-9 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*kernel.perf_event_paranoid.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-81054-9 + - DISA-STIG-RHEL-08-010376 + - NIST-800-53-AC-6 + - disable_strategy + - low_complexity + - low_severity + - medium_disruption + - reboot_required + - sysctl_kernel_perf_event_paranoid + +- name: Comment out any occurrences of kernel.perf_event_paranoid from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*kernel.perf_event_paranoid + replace: '#kernel.perf_event_paranoid' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-81054-9 + - DISA-STIG-RHEL-08-010376 + - NIST-800-53-AC-6 + - disable_strategy + - low_complexity + - low_severity + - medium_disruption + - reboot_required + - sysctl_kernel_perf_event_paranoid + +- name: Ensure sysctl kernel.perf_event_paranoid is set to 2 + sysctl: + name: kernel.perf_event_paranoid + value: '2' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-81054-9 + - DISA-STIG-RHEL-08-010376 + - NIST-800-53-AC-6 + - disable_strategy + - low_complexity + - low_severity + - medium_disruption + - reboot_required + - sysctl_kernel_perf_event_paranoid + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,kernel.perf_event_paranoid%3D2%0A + mode: 0644 + path: /etc/sysctl.d/75-sysctl_kernel_perf_event_paranoid.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of kernel.perf_event_paranoid from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*kernel.perf_event_paranoid.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "kernel.perf_event_paranoid" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done + +# +# Set runtime for kernel.perf_event_paranoid +# +/sbin/sysctl -q -n -w kernel.perf_event_paranoid="2" + +# +# If kernel.perf_event_paranoid present in /etc/sysctl.conf, change value to "2" +# else, add "kernel.perf_event_paranoid = 2" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^kernel.perf_event_paranoid") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "2" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^kernel.perf_event_paranoid\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^kernel.perf_event_paranoid\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-81054-9" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure maximum number of process identifiers + To set the runtime status of the kernel.pid_max kernel parameter, run the following command: $ sudo sysctl -w kernel.pid_max=65536 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: kernel.pid_max = 65536 + BP28(R23) + The kernel.pid_max parameter configures upper limit on process +identifiers (PID). If this number is not high enough, it might happen that +forking of new processes is not possible, because all available PIDs are +exhausted. Increasing this number enhances availability. + + CCE-83366-5 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*kernel.pid_max.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83366-5 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_kernel_pid_max + +- name: Comment out any occurrences of kernel.pid_max from /etc/sysctl.d/*.conf files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*kernel.pid_max + replace: '#kernel.pid_max' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83366-5 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_kernel_pid_max + +- name: Ensure sysctl kernel.pid_max is set to 65536 + sysctl: + name: kernel.pid_max + value: '65536' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83366-5 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_kernel_pid_max + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of kernel.pid_max from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*kernel.pid_max.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "kernel.pid_max" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done + +# +# Set runtime for kernel.pid_max +# +/sbin/sysctl -q -n -w kernel.pid_max="65536" + +# +# If kernel.pid_max present in /etc/sysctl.conf, change value to "65536" +# else, add "kernel.pid_max = 65536" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^kernel.pid_max") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "65536" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^kernel.pid_max\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^kernel.pid_max\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-83366-5" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disallow magic SysRq key + To set the runtime status of the kernel.sysrq kernel parameter, run the following command: $ sudo sysctl -w kernel.sysrq=0 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: kernel.sysrq = 0 + BP28(R23) + The Magic SysRq key allows sending certain commands directly to the running +kernel. It can dump various system and process information, potentially +revealing sensitive information. It can also reboot or shutdown the machine, +disturbing its availability. + + CCE-83355-8 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*kernel.sysrq.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83355-8 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_kernel_sysrq + +- name: Comment out any occurrences of kernel.sysrq from /etc/sysctl.d/*.conf files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*kernel.sysrq + replace: '#kernel.sysrq' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83355-8 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_kernel_sysrq + +- name: Ensure sysctl kernel.sysrq is set to 0 + sysctl: + name: kernel.sysrq + value: '0' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83355-8 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_kernel_sysrq + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of kernel.sysrq from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*kernel.sysrq.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "kernel.sysrq" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done + +# +# Set runtime for kernel.sysrq +# +/sbin/sysctl -q -n -w kernel.sysrq="0" + +# +# If kernel.sysrq present in /etc/sysctl.conf, change value to "0" +# else, add "kernel.sysrq = 0" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^kernel.sysrq") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "0" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^kernel.sysrq\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^kernel.sysrq\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-83355-8" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable Access to Network bpf() Syscall From Unprivileged Processes + To set the runtime status of the kernel.unprivileged_bpf_disabled kernel parameter, run the following command: $ sudo sysctl -w kernel.unprivileged_bpf_disabled=1 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: kernel.unprivileged_bpf_disabled = 1 + CCI-000366 + AC-6 + SC-7(10) + FMT_SMF_EXT.1 + SRG-OS-000132-GPOS-00067 + SRG-OS-000480-GPOS-00227 + RHEL-08-040281 + SV-230545r833359_rule + Loading and accessing the packet filters programs and maps using the bpf() +syscall has the potential of revealing sensitive information about the kernel state. + + CCE-82974-7 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*kernel.unprivileged_bpf_disabled.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82974-7 + - DISA-STIG-RHEL-08-040281 + - NIST-800-53-AC-6 + - NIST-800-53-SC-7(10) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_kernel_unprivileged_bpf_disabled + +- name: Comment out any occurrences of kernel.unprivileged_bpf_disabled from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*kernel.unprivileged_bpf_disabled + replace: '#kernel.unprivileged_bpf_disabled' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82974-7 + - DISA-STIG-RHEL-08-040281 + - NIST-800-53-AC-6 + - NIST-800-53-SC-7(10) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_kernel_unprivileged_bpf_disabled + +- name: Ensure sysctl kernel.unprivileged_bpf_disabled is set to 1 + sysctl: + name: kernel.unprivileged_bpf_disabled + value: '1' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82974-7 + - DISA-STIG-RHEL-08-040281 + - NIST-800-53-AC-6 + - NIST-800-53-SC-7(10) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_kernel_unprivileged_bpf_disabled + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,kernel.unprivileged_bpf_disabled%3D1%0A + mode: 0644 + path: /etc/sysctl.d/75-sysctl_kernel_unprivileged_bpf_disabled.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of kernel.unprivileged_bpf_disabled from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*kernel.unprivileged_bpf_disabled.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "kernel.unprivileged_bpf_disabled" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done + +# +# Set runtime for kernel.unprivileged_bpf_disabled +# +/sbin/sysctl -q -n -w kernel.unprivileged_bpf_disabled="1" + +# +# If kernel.unprivileged_bpf_disabled present in /etc/sysctl.conf, change value to "1" +# else, add "kernel.unprivileged_bpf_disabled = 1" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^kernel.unprivileged_bpf_disabled") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "1" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^kernel.unprivileged_bpf_disabled\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^kernel.unprivileged_bpf_disabled\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-82974-7" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Restrict usage of ptrace to descendant processes + To set the runtime status of the kernel.yama.ptrace_scope kernel parameter, run the following command: $ sudo sysctl -w kernel.yama.ptrace_scope=1 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: kernel.yama.ptrace_scope = 1 + BP28(R25) + CCI-000366 + SC-7(10) + SRG-OS-000132-GPOS-00067 + SRG-OS-000480-GPOS-00227 + RHEL-08-040282 + SV-230546r833361_rule + Unrestricted usage of ptrace allows compromised binaries to run ptrace +on another processes of the user. Like this, the attacker can steal +sensitive information from the target processes (e.g. SSH sessions, web browser, ...) +without any additional assistance from the user (i.e. without resorting to phishing). + + + CCE-80953-3 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*kernel.yama.ptrace_scope.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80953-3 + - DISA-STIG-RHEL-08-040282 + - NIST-800-53-SC-7(10) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_kernel_yama_ptrace_scope + +- name: Comment out any occurrences of kernel.yama.ptrace_scope from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*kernel.yama.ptrace_scope + replace: '#kernel.yama.ptrace_scope' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80953-3 + - DISA-STIG-RHEL-08-040282 + - NIST-800-53-SC-7(10) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_kernel_yama_ptrace_scope + +- name: Ensure sysctl kernel.yama.ptrace_scope is set to 1 + sysctl: + name: kernel.yama.ptrace_scope + value: '1' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80953-3 + - DISA-STIG-RHEL-08-040282 + - NIST-800-53-SC-7(10) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_kernel_yama_ptrace_scope + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,kernel.yama.ptrace_scope%3D1%0A + mode: 0644 + path: /etc/sysctl.d/75-sysctl_kernel_yama_ptrace_scope.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of kernel.yama.ptrace_scope from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*kernel.yama.ptrace_scope.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "kernel.yama.ptrace_scope" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done + +# +# Set runtime for kernel.yama.ptrace_scope +# +/sbin/sysctl -q -n -w kernel.yama.ptrace_scope="1" + +# +# If kernel.yama.ptrace_scope present in /etc/sysctl.conf, change value to "1" +# else, add "kernel.yama.ptrace_scope = 1" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^kernel.yama.ptrace_scope") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "1" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^kernel.yama.ptrace_scope\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^kernel.yama.ptrace_scope\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-80953-3" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Harden the operation of the BPF just-in-time compiler + To set the runtime status of the net.core.bpf_jit_harden kernel parameter, run the following command: $ sudo sysctl -w net.core.bpf_jit_harden=2 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: net.core.bpf_jit_harden = 2 + CCI-000366 + CM-6 + SC-7(10) + FMT_SMF_EXT.1 + SRG-OS-000480-GPOS-00227 + RHEL-08-040286 + SV-244554r833381_rule + When hardened, the extended Berkeley Packet Filter just-in-time compiler +will randomize any kernel addresses in the BPF programs and maps, +and will not expose the JIT addresses in /proc/kallsyms. + + CCE-82934-1 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.core.bpf_jit_harden.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82934-1 + - DISA-STIG-RHEL-08-040286 + - NIST-800-53-CM-6 + - NIST-800-53-SC-7(10) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_core_bpf_jit_harden + +- name: Comment out any occurrences of net.core.bpf_jit_harden from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.core.bpf_jit_harden + replace: '#net.core.bpf_jit_harden' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82934-1 + - DISA-STIG-RHEL-08-040286 + - NIST-800-53-CM-6 + - NIST-800-53-SC-7(10) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_core_bpf_jit_harden + +- name: Ensure sysctl net.core.bpf_jit_harden is set to 2 + sysctl: + name: net.core.bpf_jit_harden + value: '2' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82934-1 + - DISA-STIG-RHEL-08-040286 + - NIST-800-53-CM-6 + - NIST-800-53-SC-7(10) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_core_bpf_jit_harden + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,net.core.bpf_jit_harden%3D2%0A + mode: 0644 + path: /etc/sysctl.d/75-sysctl_net_core_bpf_jit_harden.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.core.bpf_jit_harden from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.core.bpf_jit_harden.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.core.bpf_jit_harden" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done + +# +# Set runtime for net.core.bpf_jit_harden +# +/sbin/sysctl -q -n -w net.core.bpf_jit_harden="2" + +# +# If net.core.bpf_jit_harden present in /etc/sysctl.conf, change value to "2" +# else, add "net.core.bpf_jit_harden = 2" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.core.bpf_jit_harden") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "2" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.core.bpf_jit_harden\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.core.bpf_jit_harden\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-82934-1" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable the use of user namespaces + To set the runtime status of the user.max_user_namespaces kernel parameter, +run the following command: +$ sudo sysctl -w user.max_user_namespaces=0 + +To make sure that the setting is persistent, +add the following line to a file in the directory /etc/sysctl.d: +user.max_user_namespaces = 0 +When containers are deployed on the machine, the value should be set +to large non-zero value. + This configuration baseline was created to deploy the base operating system for general purpose +workloads. When the operating system is configured for certain purposes, such as to host Linux Containers, +it is expected that user.max_user_namespaces will be enabled. + CCI-000366 + SC-39 + CM-6(a) + FMT_SMF_EXT.1 + SRG-OS-000480-GPOS-00227 + RHEL-08-040284 + SV-230548r833365_rule + It is detrimental for operating systems to provide, or install by default, functionality exceeding requirements or system objectives. +These unnecessary capabilities or services are often overlooked and therefore may remain unsecured. +They increase the risk to the platform by providing additional attack vectors. +User namespaces are used primarily for Linux containers. The value 0 +disallows the use of user namespaces. + + CCE-82211-4 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*user.max_user_namespaces.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82211-4 + - DISA-STIG-RHEL-08-040284 + - NIST-800-53-CM-6(a) + - NIST-800-53-SC-39 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_user_max_user_namespaces + +- name: Comment out any occurrences of user.max_user_namespaces from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*user.max_user_namespaces + replace: '#user.max_user_namespaces' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82211-4 + - DISA-STIG-RHEL-08-040284 + - NIST-800-53-CM-6(a) + - NIST-800-53-SC-39 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_user_max_user_namespaces + +- name: Ensure sysctl user.max_user_namespaces is set to 0 + sysctl: + name: user.max_user_namespaces + value: '0' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82211-4 + - DISA-STIG-RHEL-08-040284 + - NIST-800-53-CM-6(a) + - NIST-800-53-SC-39 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_user_max_user_namespaces + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,user.max_user_namespaces%20%3D%200%0A + mode: 0644 + path: /etc/sysctl.d/75-sysctl_user_max_user_namespaces.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of user.max_user_namespaces from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*user.max_user_namespaces.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "user.max_user_namespaces" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done + +# +# Set runtime for user.max_user_namespaces +# +/sbin/sysctl -q -n -w user.max_user_namespaces="0" + +# +# If user.max_user_namespaces present in /etc/sysctl.conf, change value to "0" +# else, add "user.max_user_namespaces = 0" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^user.max_user_namespaces") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "0" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^user.max_user_namespaces\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^user.max_user_namespaces\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-82211-4" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Prevent applications from mapping low portion of virtual memory + To set the runtime status of the vm.mmap_min_addr kernel parameter, run the following command: $ sudo sysctl -w vm.mmap_min_addr=65536 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: vm.mmap_min_addr = 65536 + BP28(R23) + The vm.mmap_min_addr parameter specifies the minimum virtual +address that a process is allowed to mmap. Allowing a process to mmap low +portion of virtual memory can have security implications such as such as +heightened risk of kernel null pointer dereference defects. + + CCE-83363-2 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*vm.mmap_min_addr.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83363-2 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_vm_mmap_min_addr + +- name: Comment out any occurrences of vm.mmap_min_addr from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*vm.mmap_min_addr + replace: '#vm.mmap_min_addr' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83363-2 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_vm_mmap_min_addr + +- name: Ensure sysctl vm.mmap_min_addr is set to 65536 + sysctl: + name: vm.mmap_min_addr + value: '65536' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83363-2 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_vm_mmap_min_addr + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of vm.mmap_min_addr from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*vm.mmap_min_addr.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "vm.mmap_min_addr" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done + +# +# Set runtime for vm.mmap_min_addr +# +/sbin/sysctl -q -n -w vm.mmap_min_addr="65536" + +# +# If vm.mmap_min_addr present in /etc/sysctl.conf, change value to "65536" +# else, add "vm.mmap_min_addr = 65536" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^vm.mmap_min_addr") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "65536" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^vm.mmap_min_addr\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^vm.mmap_min_addr\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-83363-2" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable Core Dumps + A core dump file is the memory image of an executable +program when it was terminated by the operating system due to +errant behavior. In most cases, only software developers +legitimately need to access these files. The core dump files may +also contain sensitive information, or unnecessarily occupy large +amounts of disk space. + +Once a hard limit is set in /etc/security/limits.conf, or +to a file within the /etc/security/limits.d/ directory, a +user cannot increase that limit within his or her own session. If access +to core dumps is required, consider restricting them to only +certain users or groups. See the limits.conf man page for more +information. + +The core dumps of setuid programs are further protected. The +sysctl variable fs.suid_dumpable controls whether +the kernel allows core dumps from these programs at all. The default +value of 0 is recommended. + + Disable acquiring, saving, and processing core dumps + The systemd-coredump.socket unit is a socket activation of +the systemd-coredump@.service which processes core dumps. +By masking the unit, core dump processing is disabled. + CCI-000366 + SC-7(10) + FMT_SMF_EXT.1 + SRG-OS-000480-GPOS-00227 + RHEL-08-010672 + SV-230312r833308_rule + A core dump includes a memory image taken at the time the operating system +terminates an application. The memory image could contain sensitive data +and is generally useful only for developers trying to debug problems. + + CCE-82881-4 + include disable_systemd-coredump + +class disable_systemd-coredump { + service {'systemd-coredump': + enable => false, + ensure => 'stopped', + } +} + + - name: Disable service systemd-coredump + block: + + - name: Disable service systemd-coredump + systemd: + name: systemd-coredump.service + enabled: 'no' + state: stopped + masked: 'yes' + ignore_errors: 'yes' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82881-4 + - DISA-STIG-RHEL-08-010672 + - NIST-800-53-SC-7(10) + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_systemd-coredump_disabled + +- name: Unit Socket Exists - systemd-coredump.socket + command: systemctl list-unit-files systemd-coredump.socket + args: + warn: false + register: socket_file_exists + changed_when: false + ignore_errors: true + check_mode: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82881-4 + - DISA-STIG-RHEL-08-010672 + - NIST-800-53-SC-7(10) + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_systemd-coredump_disabled + +- name: Disable socket systemd-coredump + systemd: + name: systemd-coredump.socket + enabled: 'no' + state: stopped + masked: 'yes' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"systemd-coredump.socket" in socket_file_exists.stdout_lines[1]' + tags: + - CCE-82881-4 + - DISA-STIG-RHEL-08-010672 + - NIST-800-53-SC-7(10) + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_systemd-coredump_disabled + + +[customizations.services] +disabled = ["systemd-coredump"] + + apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + systemd: + units: + - name: systemd-coredump.service + enabled: false + mask: true + - name: systemd-coredump.socket + enabled: false + mask: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" stop 'systemd-coredump.service' +"$SYSTEMCTL_EXEC" disable 'systemd-coredump.service' +"$SYSTEMCTL_EXEC" mask 'systemd-coredump.service' +# Disable socket activation if we have a unit file for it +if "$SYSTEMCTL_EXEC" list-unit-files | grep -q '^systemd-coredump.socket'; then + "$SYSTEMCTL_EXEC" stop 'systemd-coredump.socket' + "$SYSTEMCTL_EXEC" mask 'systemd-coredump.socket' +fi +# The service may not be running because it has been started and failed, +# so let's reset the state so OVAL checks pass. +# Service should be 'inactive', not 'failed' after reboot though. +"$SYSTEMCTL_EXEC" reset-failed 'systemd-coredump.service' || true + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable core dump backtraces + The ProcessSizeMax option in [Coredump] section +of /etc/systemd/coredump.conf +specifies the maximum size in bytes of a core which will be processed. +Core dumps exceeding this size may be stored, but the backtrace will not +be generated. + If the /etc/systemd/coredump.conf file +does not already contain the [Coredump] section, +the value will not be configured correctly. + CCI-000366 + CM-6 + FMT_SMF_EXT.1 + SRG-OS-000480-GPOS-00227 + RHEL-08-010675 + 1.5.2 + SV-230315r627750_rule + A core dump includes a memory image taken at the time the operating system +terminates an application. The memory image could contain sensitive data +and is generally useful only for developers or system operators trying to +debug problems. + +Enabling core dumps on production systems is not recommended, +however there may be overriding operational requirements to enable advanced +debuging. Permitting temporary enablement of core dumps during such situations +should be reviewed through local needs and policy. + CCE-82251-0 + - name: Disable core dump backtraces + block: + + - name: Check for duplicate values + lineinfile: + path: /etc/systemd/coredump.conf + create: false + regexp: ^\s*ProcessSizeMax\s*=\s* + state: absent + check_mode: true + changed_when: false + register: dupes + + - name: Deduplicate values from /etc/systemd/coredump.conf + lineinfile: + path: /etc/systemd/coredump.conf + create: false + regexp: ^\s*ProcessSizeMax\s*=\s* + state: absent + when: dupes.found is defined and dupes.found > 1 + + - name: Insert correct line to /etc/systemd/coredump.conf + lineinfile: + path: /etc/systemd/coredump.conf + create: false + regexp: ^\s*ProcessSizeMax\s*=\s* + line: ProcessSizeMax=0 + state: present + tags: + - CCE-82251-0 + - DISA-STIG-RHEL-08-010675 + - NIST-800-53-CM-6 + - coredump_disable_backtraces + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,%23%20%20This%20file%20is%20part%20of%20systemd.%0A%23%0A%23%20%20systemd%20is%20free%20software%3B%20you%20can%20redistribute%20it%20and/or%20modify%20it%0A%23%20%20under%20the%20terms%20of%20the%20GNU%20Lesser%20General%20Public%20License%20as%20published%20by%0A%23%20%20the%20Free%20Software%20Foundation%3B%20either%20version%202.1%20of%20the%20License%2C%20or%0A%23%20%20%28at%20your%20option%29%20any%20later%20version.%0A%23%0A%23%20Entries%20in%20this%20file%20show%20the%20compile%20time%20defaults.%0A%23%20You%20can%20change%20settings%20by%20editing%20this%20file.%0A%23%20Defaults%20can%20be%20restored%20by%20simply%20deleting%20this%20file.%0A%23%0A%23%20See%20coredump.conf%285%29%20for%20details.%0A%0A%5BCoredump%5D%0A%23Storage%3Dexternal%0A%23Compress%3Dyes%0A%23ProcessSizeMax%3D2G%0A%23ExternalSizeMax%3D2G%0A%23JournalSizeMax%3D767M%0A%23MaxUse%3D%0A%23KeepFree%3D%0AStorage%3Dnone%0AProcessSizeMax%3D0%0A + mode: 0644 + path: /etc/systemd/coredump.conf + overwrite: true + + if [ -e "/etc/systemd/coredump.conf" ] ; then + + LC_ALL=C sed -i "/^\s*ProcessSizeMax\s*=\s*/Id" "/etc/systemd/coredump.conf" +else + touch "/etc/systemd/coredump.conf" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/systemd/coredump.conf" + +cp "/etc/systemd/coredump.conf" "/etc/systemd/coredump.conf.bak" +# Insert at the end of the file +printf '%s\n' "ProcessSizeMax=0" >> "/etc/systemd/coredump.conf" +# Clean up after ourselves. +rm "/etc/systemd/coredump.conf.bak" + + + + + + + + + + Disable storing core dump + The Storage option in [Coredump] section +of /etc/systemd/coredump.conf +can be set to none to disable storing core dumps permanently. + If the /etc/systemd/coredump.conf file +does not already contain the [Coredump] section, +the value will not be configured correctly. + CCI-000366 + CM-6 + FMT_SMF_EXT.1 + SRG-OS-000480-GPOS-00227 + RHEL-08-010674 + 1.5.1 + SV-230314r627750_rule + A core dump includes a memory image taken at the time the operating system +terminates an application. The memory image could contain sensitive data +and is generally useful only for developers or system operators trying to +debug problems. Enabling core dumps on production systems is not recommended, +however there may be overriding operational requirements to enable advanced +debuging. Permitting temporary enablement of core dumps during such situations +should be reviewed through local needs and policy. + CCE-82252-8 + - name: Disable storing core dump + block: + + - name: Check for duplicate values + lineinfile: + path: /etc/systemd/coredump.conf + create: false + regexp: ^\s*Storage\s*=\s* + state: absent + check_mode: true + changed_when: false + register: dupes + + - name: Deduplicate values from /etc/systemd/coredump.conf + lineinfile: + path: /etc/systemd/coredump.conf + create: false + regexp: ^\s*Storage\s*=\s* + state: absent + when: dupes.found is defined and dupes.found > 1 + + - name: Insert correct line to /etc/systemd/coredump.conf + lineinfile: + path: /etc/systemd/coredump.conf + create: false + regexp: ^\s*Storage\s*=\s* + line: Storage=none + state: present + tags: + - CCE-82252-8 + - DISA-STIG-RHEL-08-010674 + - NIST-800-53-CM-6 + - coredump_disable_storage + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,%23%20%20This%20file%20is%20part%20of%20systemd.%0A%23%0A%23%20%20systemd%20is%20free%20software%3B%20you%20can%20redistribute%20it%20and/or%20modify%20it%0A%23%20%20under%20the%20terms%20of%20the%20GNU%20Lesser%20General%20Public%20License%20as%20published%20by%0A%23%20%20the%20Free%20Software%20Foundation%3B%20either%20version%202.1%20of%20the%20License%2C%20or%0A%23%20%20%28at%20your%20option%29%20any%20later%20version.%0A%23%0A%23%20Entries%20in%20this%20file%20show%20the%20compile%20time%20defaults.%0A%23%20You%20can%20change%20settings%20by%20editing%20this%20file.%0A%23%20Defaults%20can%20be%20restored%20by%20simply%20deleting%20this%20file.%0A%23%0A%23%20See%20coredump.conf%285%29%20for%20details.%0A%0A%5BCoredump%5D%0A%23Storage%3Dexternal%0A%23Compress%3Dyes%0A%23ProcessSizeMax%3D2G%0A%23ExternalSizeMax%3D2G%0A%23JournalSizeMax%3D767M%0A%23MaxUse%3D%0A%23KeepFree%3D%0AStorage%3Dnone%0AProcessSizeMax%3D0%0A + mode: 0644 + path: /etc/systemd/coredump.conf + overwrite: true + + if [ -e "/etc/systemd/coredump.conf" ] ; then + + LC_ALL=C sed -i "/^\s*Storage\s*=\s*/Id" "/etc/systemd/coredump.conf" +else + touch "/etc/systemd/coredump.conf" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/systemd/coredump.conf" + +cp "/etc/systemd/coredump.conf" "/etc/systemd/coredump.conf.bak" +# Insert at the end of the file +printf '%s\n' "Storage=none" >> "/etc/systemd/coredump.conf" +# Clean up after ourselves. +rm "/etc/systemd/coredump.conf.bak" + + + + + + + + + + Disable Core Dumps for All Users + To disable core dumps for all users, add the following line to +/etc/security/limits.conf, or to a file within the +/etc/security/limits.d/ directory: +* hard core 0 + 1 + 12 + 13 + 15 + 16 + 2 + 7 + 8 + APO13.01 + BAI04.04 + DSS01.03 + DSS03.05 + DSS05.07 + CCI-000366 + SR 6.2 + SR 7.1 + SR 7.2 + A.12.1.3 + A.17.2.1 + CM-6 + SC-7(10) + DE.CM-1 + PR.DS-4 + SRG-OS-000480-GPOS-00227 + RHEL-08-010673 + 1.6.1 + SV-230313r627750_rule + A core dump includes a memory image taken at the time the operating system +terminates an application. The memory image could contain sensitive data and is generally useful +only for developers trying to debug problems. + + CCE-81038-2 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-81038-2 + - DISA-STIG-RHEL-08-010673 + - NIST-800-53-CM-6 + - NIST-800-53-SC-7(10) + - disable_users_coredumps + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: disable core dumps with limits + lineinfile: + dest: /etc/security/limits.conf + regexp: ^[^#].*core + line: '* hard core 0' + create: true + when: '"pam" in ansible_facts.packages' + tags: + - CCE-81038-2 + - DISA-STIG-RHEL-08-010673 + - NIST-800-53-CM-6 + - NIST-800-53-SC-7(10) + - disable_users_coredumps + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,%2A%20%20%20%20%20hard%20%20%20core%20%20%20%200 + mode: 0644 + path: /etc/security/limits.d/75-disable_users_coredumps.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if rpm --quiet -q pam; then + +SECURITY_LIMITS_FILE="/etc/security/limits.conf" + +if grep -qE '^\s*\*\s+hard\s+core' $SECURITY_LIMITS_FILE; then + sed -ri 's/(hard\s+core\s+)[[:digit:]]+/\1 0/' $SECURITY_LIMITS_FILE +else + echo "* hard core 0" >> $SECURITY_LIMITS_FILE +fi + +if ls /etc/security/limits.d/*.conf > /dev/null; then + sed -ri '/^\s*\*\s+hard\s+core/d' /etc/security/limits.d/*.conf +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable Core Dumps for SUID programs + To set the runtime status of the fs.suid_dumpable kernel parameter, run the following command: $ sudo sysctl -w fs.suid_dumpable=0 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: fs.suid_dumpable = 0 + BP28(R23) + 164.308(a)(1)(ii)(D) + 164.308(a)(3) + 164.308(a)(4) + 164.310(b) + 164.310(c) + 164.312(a) + 164.312(e) + SI-11(a) + SI-11(b) + 1.6.1 + The core dump of a setuid program is more likely to contain +sensitive data, as the program itself runs with greater privileges than the +user who initiated execution of the program. Disabling the ability for any +setuid program to write a core file decreases the risk of unauthorized access +of such data. + + CCE-80912-9 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*fs.suid_dumpable.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80912-9 + - NIST-800-53-SI-11(a) + - NIST-800-53-SI-11(b) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_fs_suid_dumpable + +- name: Comment out any occurrences of fs.suid_dumpable from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*fs.suid_dumpable + replace: '#fs.suid_dumpable' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80912-9 + - NIST-800-53-SI-11(a) + - NIST-800-53-SI-11(b) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_fs_suid_dumpable + +- name: Ensure sysctl fs.suid_dumpable is set to 0 + sysctl: + name: fs.suid_dumpable + value: '0' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80912-9 + - NIST-800-53-SI-11(a) + - NIST-800-53-SI-11(b) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_fs_suid_dumpable + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of fs.suid_dumpable from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*fs.suid_dumpable.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "fs.suid_dumpable" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done + +# +# Set runtime for fs.suid_dumpable +# +/sbin/sysctl -q -n -w fs.suid_dumpable="0" + +# +# If fs.suid_dumpable present in /etc/sysctl.conf, change value to "0" +# else, add "fs.suid_dumpable = 0" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^fs.suid_dumpable") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "0" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^fs.suid_dumpable\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^fs.suid_dumpable\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-80912-9" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Daemon Umask + The umask is a per-process setting which limits +the default permissions for creation of new files and directories. +The system includes initialization scripts which set the default umask +for system daemons. + + daemon umask + Enter umask for daemons + 022 + 027 + 022 + + + Set Daemon Umask + The file /etc/init.d/functions includes initialization +parameters for most or all daemons started at boot time. Many daemons +on the system already individually restrict themselves to +a umask of 077 in their own init scripts. By default, the umask of +022 is set which prevents creation of group- or world-writable files. +To set the umask for daemons expected by the profile, edit the following line: +umask + Setting the umask to too restrictive a setting can cause serious errors at +runtime. + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + The umask influences the permissions assigned to files created by a +process at run time. An unnecessarily permissive umask could result in files +being created with insecure permissions. + +var_umask_for_daemons='' + + +grep -q ^umask /etc/init.d/functions && \ + sed -i "s/umask.*/umask $var_umask_for_daemons/g" /etc/init.d/functions +if ! [ $? -eq 0 ]; then + echo "umask $var_umask_for_daemons" >> /etc/init.d/functions +fi + + + + + + + + + + + + Enable ExecShield + ExecShield describes kernel features that provide +protection against exploitation of memory corruption errors such as buffer +overflows. These features include random placement of the stack and other +memory regions, prevention of execution in memory that should only hold data, +and special handling of text buffers. These protections are enabled by default +on 32-bit systems and controlled through sysctl variables +kernel.exec-shield and kernel.randomize_va_space. On the latest +64-bit systems, kernel.exec-shield cannot be enabled or disabled with +sysctl. + + kernel.kptr_restrict + Configure exposition of kernel pointer addresses + 1 + 1 + 2 + + + Enable ExecShield via sysctl + By default on Red Hat Enterprise Linux 8 64-bit systems, ExecShield is +enabled and can only be disabled if the hardware does not support +ExecShield or is disabled in /etc/default/grub. + BP28(R9) + 12 + 15 + 8 + APO13.01 + DSS05.02 + 3.1.7 + CCI-002530 + 164.308(a)(1)(ii)(D) + 164.308(a)(3) + 164.308(a)(4) + 164.310(b) + 164.310(c) + 164.312(a) + 164.312(e) + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + A.13.1.1 + A.13.2.1 + A.14.1.3 + SC-39 + CM-6(a) + PR.PT-4 + SRG-OS-000433-GPOS-00192 + ExecShield uses the segmentation feature on all x86 systems to prevent +execution in memory higher than a certain address. It writes an address as +a limit in the code segment descriptor, to control where code can be +executed, on a per-process basis. When the kernel places a process's memory +regions such as the stack and heap higher than this address, the hardware +prevents execution in that address range. This is enabled by default on the +latest Red Hat and Fedora systems if supported by the hardware. + + CCE-80914-5 + - name: Update grub defaults and the bootloader menu + command: /sbin/grubby --update-kernel=ALL --remove-args="noexec" + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80914-5 + - NIST-800-171-3.1.7 + - NIST-800-53-CM-6(a) + - NIST-800-53-SC-39 + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + - sysctl_kernel_exec_shield + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +grubby --update-kernel=ALL --remove-args=noexec --env=/boot/grub2/grubenv + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Restrict Exposed Kernel Pointer Addresses Access + To set the runtime status of the kernel.kptr_restrict kernel parameter, run the following command: $ sudo sysctl -w kernel.kptr_restrict= +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: kernel.kptr_restrict = + BP28(R23) + CCI-002824 + CCI-000366 + CIP-002-5 R1.1 + CIP-002-5 R1.2 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 4.1 + CIP-004-6 4.2 + CIP-004-6 R2.2.3 + CIP-004-6 R2.2.4 + CIP-004-6 R2.3 + CIP-004-6 R4 + CIP-005-6 R1 + CIP-005-6 R1.1 + CIP-005-6 R1.2 + CIP-007-3 R3 + CIP-007-3 R3.1 + CIP-007-3 R5.1 + CIP-007-3 R5.1.2 + CIP-007-3 R5.1.3 + CIP-007-3 R5.2.1 + CIP-007-3 R5.2.3 + CIP-007-3 R8.4 + CIP-009-6 R.1.1 + CIP-009-6 R4 + SC-30 + SC-30(2) + SC-30(5) + CM-6(a) + SRG-OS-000132-GPOS-00067 + SRG-OS-000433-GPOS-00192 + SRG-OS-000480-GPOS-00227 + RHEL-08-040283 + SV-230547r833363_rule + Exposing kernel pointers (through procfs or seq_printf()) exposes kernel +writeable structures which may contain functions pointers. If a write vulnerability +occurs in the kernel, allowing write access to any of this structure, the kernel can +be compromised. This option disallow any program without the CAP_SYSLOG capability +to get the addresses of kernel pointers by replacing them with 0. + + CCE-80915-2 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*kernel.kptr_restrict.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80915-2 + - DISA-STIG-RHEL-08-040283 + - NIST-800-53-CM-6(a) + - NIST-800-53-SC-30 + - NIST-800-53-SC-30(2) + - NIST-800-53-SC-30(5) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_kernel_kptr_restrict + +- name: Comment out any occurrences of kernel.kptr_restrict from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*kernel.kptr_restrict + replace: '#kernel.kptr_restrict' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80915-2 + - DISA-STIG-RHEL-08-040283 + - NIST-800-53-CM-6(a) + - NIST-800-53-SC-30 + - NIST-800-53-SC-30(2) + - NIST-800-53-SC-30(5) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_kernel_kptr_restrict +- name: XCCDF Value sysctl_kernel_kptr_restrict_value # promote to variable + set_fact: + sysctl_kernel_kptr_restrict_value: !!str + tags: + - always + +- name: Ensure sysctl kernel.kptr_restrict is set + sysctl: + name: kernel.kptr_restrict + value: '{{ sysctl_kernel_kptr_restrict_value }}' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80915-2 + - DISA-STIG-RHEL-08-040283 + - NIST-800-53-CM-6(a) + - NIST-800-53-SC-30 + - NIST-800-53-SC-30(2) + - NIST-800-53-SC-30(5) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_kernel_kptr_restrict + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,kernel.kptr_restrict%3D1%0A + mode: 0644 + path: /etc/sysctl.d/75-sysctl_kernel_kptr_restrict.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of kernel.kptr_restrict from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*kernel.kptr_restrict.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "kernel.kptr_restrict" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done +sysctl_kernel_kptr_restrict_value='' + + +# +# Set runtime for kernel.kptr_restrict +# +/sbin/sysctl -q -n -w kernel.kptr_restrict="$sysctl_kernel_kptr_restrict_value" + +# +# If kernel.kptr_restrict present in /etc/sysctl.conf, change value to appropriate value +# else, add "kernel.kptr_restrict = value" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^kernel.kptr_restrict") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_kernel_kptr_restrict_value" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^kernel.kptr_restrict\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^kernel.kptr_restrict\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-80915-2" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Enable Randomized Layout of Virtual Address Space + To set the runtime status of the kernel.randomize_va_space kernel parameter, run the following command: $ sudo sysctl -w kernel.randomize_va_space=2 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: kernel.randomize_va_space = 2 + BP28(R23) + 3.1.7 + CCI-000366 + CCI-002824 + 164.308(a)(1)(ii)(D) + 164.308(a)(3) + 164.308(a)(4) + 164.310(b) + 164.310(c) + 164.312(a) + 164.312(e) + CIP-002-5 R1.1 + CIP-002-5 R1.2 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 4.1 + CIP-004-6 4.2 + CIP-004-6 R2.2.3 + CIP-004-6 R2.2.4 + CIP-004-6 R2.3 + CIP-004-6 R4 + CIP-005-6 R1 + CIP-005-6 R1.1 + CIP-005-6 R1.2 + CIP-007-3 R3 + CIP-007-3 R3.1 + CIP-007-3 R5.1 + CIP-007-3 R5.1.2 + CIP-007-3 R5.1.3 + CIP-007-3 R5.2.1 + CIP-007-3 R5.2.3 + CIP-007-3 R8.4 + CIP-009-6 R.1.1 + CIP-009-6 R4 + SC-30 + SC-30(2) + CM-6(a) + SRG-OS-000433-GPOS-00193 + SRG-OS-000480-GPOS-00227 + RHEL-08-010430 + 1.5.3 + SV-230280r833303_rule + Address space layout randomization (ASLR) makes it more difficult for an +attacker to predict the location of attack code they have introduced into a +process's address space during an attempt at exploitation. Additionally, +ASLR makes it more difficult for an attacker to know the location of +existing code in order to re-purpose it using return oriented programming +(ROP) techniques. + + CCE-80916-0 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*kernel.randomize_va_space.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80916-0 + - DISA-STIG-RHEL-08-010430 + - NIST-800-171-3.1.7 + - NIST-800-53-CM-6(a) + - NIST-800-53-SC-30 + - NIST-800-53-SC-30(2) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_kernel_randomize_va_space + +- name: Comment out any occurrences of kernel.randomize_va_space from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*kernel.randomize_va_space + replace: '#kernel.randomize_va_space' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80916-0 + - DISA-STIG-RHEL-08-010430 + - NIST-800-171-3.1.7 + - NIST-800-53-CM-6(a) + - NIST-800-53-SC-30 + - NIST-800-53-SC-30(2) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_kernel_randomize_va_space + +- name: Ensure sysctl kernel.randomize_va_space is set to 2 + sysctl: + name: kernel.randomize_va_space + value: '2' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80916-0 + - DISA-STIG-RHEL-08-010430 + - NIST-800-171-3.1.7 + - NIST-800-53-CM-6(a) + - NIST-800-53-SC-30 + - NIST-800-53-SC-30(2) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_kernel_randomize_va_space + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,kernel.randomize_va_space%3D2%0A + mode: 0644 + path: /etc/sysctl.d/75-sysctl_kernel_randomize_va_space.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of kernel.randomize_va_space from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*kernel.randomize_va_space.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "kernel.randomize_va_space" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done + +# +# Set runtime for kernel.randomize_va_space +# +/sbin/sysctl -q -n -w kernel.randomize_va_space="2" + +# +# If kernel.randomize_va_space present in /etc/sysctl.conf, change value to "2" +# else, add "kernel.randomize_va_space = 2" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^kernel.randomize_va_space") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "2" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^kernel.randomize_va_space\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^kernel.randomize_va_space\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-80916-0" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable Execute Disable (XD) or No Execute (NX) Support on +x86 Systems + Recent processors in the x86 family support the +ability to prevent code execution on a per memory page basis. +Generically and on AMD processors, this ability is called No +Execute (NX), while on Intel processors it is called Execute +Disable (XD). This ability can help prevent exploitation of buffer +overflow vulnerabilities and should be activated whenever possible. +Extra steps must be taken to ensure that this protection is +enabled, particularly on 32-bit x86 systems. Other processors, such +as Itanium and POWER, have included such support since inception +and the standard kernel for those platforms supports the +feature. This is enabled by default on the latest Red Hat and +Fedora systems if supported by the hardware. + + Enable NX or XD Support in the BIOS + Reboot the system and enter the BIOS or Setup configuration menu. +Navigate the BIOS configuration menu and make sure that the option is enabled. The setting may be located +under a Security section. Look for Execute Disable (XD) on Intel-based systems and No Execute (NX) +on AMD-based systems. + BP28(R9) + 11 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + 3.1.7 + CCI-002824 + 4.3.4.3.2 + 4.3.4.3.3 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + SC-39 + CM-6(a) + PR.IP-1 + SRG-OS-000433-GPOS-00192 + RHEL-08-010420 + SV-230276r627750_rule + Computers with the ability to prevent this type of code execution frequently put an option in the BIOS that will +allow users to turn the feature on or off at will. + + CCE-83918-3 + + + + + + + + + Install PAE Kernel on Supported 32-bit x86 Systems + Systems that are using the 64-bit x86 kernel package +do not need to install the kernel-PAE package because the 64-bit +x86 kernel already includes this support. However, if the system is +32-bit and also supports the PAE and NX features as +determined in the previous section, the kernel-PAE package should +be installed to enable XD or NX support. +The kernel-PAE package can be installed with the following command: + +$ sudo yum install kernel-PAE +The installation process should also have configured the +bootloader to load the new kernel at boot. Verify this after reboot +and modify /etc/default/grub if necessary. + The kernel-PAE package should not be +installed on older systems that do not support the XD or NX bit, as +8this may prevent them from booting.8 + BP28(R9) + 11 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + 3.1.7 + 4.3.4.3.2 + 4.3.4.3.3 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + CM-6(a) + PR.IP-1 + On 32-bit systems that support the XD or NX bit, the vendor-supplied +PAE kernel is required to enable either Execute Disable (XD) or No Execute (NX) support. + + CCE-83919-1 + + + + + + + Memory Poisoning + Memory Poisoning consists of writing a special value to uninitialized or freed memory. +Poisoning can be used as a mechanism to prevent leak of information and detection of +corrupted memory. + + + slub_debug - debug options + Defines the debug options to use in slub_debug kernel command line argument. + P + F + Z + P + FZ + FZP + + + Enable page allocator poisoning + To enable poisoning of free pages, +add the argument page_poison=1 to the default +GRUB 2 command line for the Linux operating system. +To ensure that page_poison=1 is added as a kernel command line +argument to newly installed kernels, add page_poison=1 to the +default Grub2 command line for Linux operating systems. Modify the line within +/etc/default/grub as shown below: +GRUB_CMDLINE_LINUX="... page_poison=1 ..." +Run the following command to update command line for already installed kernels:# grubby --update-kernel=ALL --args="page_poison=1" + CCI-001084 + CM-6(a) + SRG-OS-000480-GPOS-00227 + SRG-OS-000134-GPOS-00068 + RHEL-08-010421 + SV-230277r792884_rule + Poisoning writes an arbitrary value to freed pages, so any modification or +reference to that page after being freed or before being initialized will be +detected and prevented. +This prevents many types of use-after-free vulnerabilities at little performance cost. +Also prevents leak of data and detection of corrupted memory. + + CCE-80944-2 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80944-2 + - DISA-STIG-RHEL-08-010421 + - NIST-800-53-CM-6(a) + - grub2_page_poison_argument + - low_disruption + - medium_complexity + - medium_severity + - reboot_required + - restrict_strategy + +- name: Update grub defaults and the bootloader menu + command: /sbin/grubby --update-kernel=ALL --args="page_poison=1" + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"grub2-common" in ansible_facts.packages' + tags: + - CCE-80944-2 + - DISA-STIG-RHEL-08-010421 + - NIST-800-53-CM-6(a) + - grub2_page_poison_argument + - low_disruption + - medium_complexity + - medium_severity + - reboot_required + - restrict_strategy + + [customizations.kernel] +append = "page_poison=1" + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && { rpm --quiet -q grub2-common; }; then + +grubby --update-kernel=ALL --args=page_poison=1 --env=/boot/grub2/grubenv + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Enable SLUB/SLAB allocator poisoning + To enable poisoning of SLUB/SLAB objects, +add the argument slub_debug= to the default +GRUB 2 command line for the Linux operating system. +To ensure that slub_debug= is added as a kernel command line +argument to newly installed kernels, add slub_debug= to the +default Grub2 command line for Linux operating systems. Modify the line within +/etc/default/grub as shown below: +GRUB_CMDLINE_LINUX="... slub_debug= ..." +Run the following command to update command line for already installed kernels:# grubby --update-kernel=ALL --args="slub_debug=" + CCI-001084 + CM-6(a) + SRG-OS-000433-GPOS-00192 + SRG-OS-000134-GPOS-00068 + RHEL-08-010423 + SV-230279r792888_rule + Poisoning writes an arbitrary value to freed objects, so any modification or +reference to that object after being freed or before being initialized will be +detected and prevented. +This prevents many types of use-after-free vulnerabilities at little performance cost. +Also prevents leak of data and detection of corrupted memory. + + CCE-80945-9 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80945-9 + - DISA-STIG-RHEL-08-010423 + - NIST-800-53-CM-6(a) + - grub2_slub_debug_argument + - low_disruption + - medium_complexity + - medium_severity + - reboot_required + - restrict_strategy +- name: XCCDF Value var_slub_debug_options # promote to variable + set_fact: + var_slub_debug_options: !!str + tags: + - always + +- name: Update grub defaults and the bootloader menu + command: /sbin/grubby --update-kernel=ALL --args="slub_debug={{ var_slub_debug_options + }}" + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"grub2-common" in ansible_facts.packages' + tags: + - CCE-80945-9 + - DISA-STIG-RHEL-08-010423 + - NIST-800-53-CM-6(a) + - grub2_slub_debug_argument + - low_disruption + - medium_complexity + - medium_severity + - reboot_required + - restrict_strategy + + [customizations.kernel] +append = "slub_debug=" + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && { rpm --quiet -q grub2-common; }; then + +var_slub_debug_options='' + + + +grubby --update-kernel=ALL --args=slub_debug=$var_slub_debug_options --env=/boot/grub2/grubenv + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + + + + SELinux + SELinux is a feature of the Linux kernel which can be +used to guard against misconfigured or compromised programs. +SELinux enforces the idea that programs should be limited in what +files they can access and what actions they can take. + +The default SELinux policy, as configured on Red Hat Enterprise Linux 8, has been +sufficiently developed and debugged that it should be usable on +almost any system with minimal configuration and a small +amount of system administrator training. This policy prevents +system services - including most of the common network-visible +services such as mail servers, FTP servers, and DNS servers - from +accessing files which those services have no valid reason to +access. This action alone prevents a huge amount of possible damage +from network attacks against services, from trojaned software, and +so forth. + +This guide recommends that SELinux be enabled using the +default (targeted) policy on every Red Hat Enterprise Linux 8 system, unless that +system has unusual requirements which make a stronger policy +appropriate. + + +For more information on SELinux, see https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/8/html/using_selinux. + + + SELinux policy + Type of policy in use. Possible values are: +targeted - Only targeted network daemons are protected. +strict - Full SELinux protection. +mls - Multiple levels of security + targeted + mls + targeted + + + SELinux state + enforcing - SELinux security policy is enforced. +permissive - SELinux prints warnings instead of enforcing. +disabled - SELinux is fully disabled. + enforcing + disabled + enforcing + permissive + + + Install libselinux Package + The libselinux package can be installed with the following command: + +$ sudo yum install libselinux + 1.6.1.1 + Security-enhanced Linux is a feature of the Linux kernel and a number of utilities +with enhanced security functionality designed to add rhisam access controls to Linux. + +The libselinux package contains the core library of the Security-enhanced Linux system. + CCE-82877-2 + +package --add=libselinux + + include install_libselinux + +class install_libselinux { + package { 'libselinux': + ensure => 'installed', + } +} + + - name: Ensure libselinux is installed + package: + name: libselinux + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82877-2 + - enable_strategy + - high_severity + - low_complexity + - low_disruption + - no_reboot_needed + - package_libselinux_installed + + +[[packages]] +name = "libselinux" +version = "*" + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if ! rpm -q --quiet "libselinux" ; then + yum install -y "libselinux" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Install policycoreutils-python-utils package + The policycoreutils-python-utils package can be installed with the following command: + +$ sudo yum install policycoreutils-python-utils + SRG-OS-000480-GPOS-00227 + This package is required to operate and manage an SELinux environment and its policies. +It provides utilities such as semanage, audit2allow, audit2why, chcat and sandbox. + CCE-82724-6 + +package --add=policycoreutils-python-utils + + include install_policycoreutils-python-utils + +class install_policycoreutils-python-utils { + package { 'policycoreutils-python-utils': + ensure => 'installed', + } +} + + - name: Ensure policycoreutils-python-utils is installed + package: + name: policycoreutils-python-utils + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82724-6 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_policycoreutils-python-utils_installed + + +[[packages]] +name = "policycoreutils-python-utils" +version = "*" + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if ! rpm -q --quiet "policycoreutils-python-utils" ; then + yum install -y "policycoreutils-python-utils" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Install policycoreutils Package + The policycoreutils package can be installed with the following command: + +$ sudo yum install policycoreutils + CCI-001084 + SRG-OS-000480-GPOS-00227 + SRG-OS-000134-GPOS-00068 + RHEL-08-010171 + SV-230241r627750_rule + Security-enhanced Linux is a feature of the Linux kernel and a number of utilities +with enhanced security functionality designed to add rhisam access controls to Linux. +The Security-enhanced Linux kernel contains new architectural components originally +developed to improve security of the Flask operating system. These architectural components +provide general support for the enforcement of many kinds of rhisam access control +policies, including those based on the concepts of Type Enforcement, Role-based Access +Control, and Multi-level Security. + +policycoreutils contains the policy core utilities that are required for +basic operation of an SELinux-enabled system. These utilities include load_policy +to load SELinux policies, setfiles to label filesystems, newrole to +switch roles, and so on. + CCE-82976-2 + +package --add=policycoreutils + + include install_policycoreutils + +class install_policycoreutils { + package { 'policycoreutils': + ensure => 'installed', + } +} + + - name: Ensure policycoreutils is installed + package: + name: policycoreutils + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82976-2 + - DISA-STIG-RHEL-08-010171 + - enable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - package_policycoreutils_installed + + +[[packages]] +name = "policycoreutils" +version = "*" + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if ! rpm -q --quiet "policycoreutils" ; then + yum install -y "policycoreutils" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Uninstall mcstrans Package + The mcstransd daemon provides category label information +to client processes requesting information. The label translations are defined +in /etc/selinux/targeted/setrans.conf. +The mcstrans package can be removed with the following command: + +$ sudo yum erase mcstrans + 1.6.1.8 + Since this service is not used very often, disable it to reduce the +amount of potentially vulnerable code running on the system. + CCE-82756-8 + +package --remove=mcstrans + + include remove_mcstrans + +class remove_mcstrans { + package { 'mcstrans': + ensure => 'purged', + } +} + + - name: Ensure mcstrans is removed + package: + name: mcstrans + state: absent + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82756-8 + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - package_mcstrans_removed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# CAUTION: This remediation script will remove mcstrans +# from the system, and may remove any packages +# that depend on mcstrans. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "mcstrans" ; then + + yum remove -y "mcstrans" + +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + Uninstall setroubleshoot-plugins Package + The SETroubleshoot plugins are used to analyze SELinux AVC data. The service provides information around configuration errors, +unauthorized intrusions, and other potential errors. +The setroubleshoot-plugins package can be removed with the following command: + +$ sudo yum erase setroubleshoot-plugins + BP28(R68) + The SETroubleshoot service is an unnecessary daemon to +have running on a server. + CCE-84250-0 + +package --remove=setroubleshoot-plugins + + include remove_setroubleshoot-plugins + +class remove_setroubleshoot-plugins { + package { 'setroubleshoot-plugins': + ensure => 'purged', + } +} + + - name: Ensure setroubleshoot-plugins is removed + package: + name: setroubleshoot-plugins + state: absent + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84250-0 + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - package_setroubleshoot-plugins_removed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# CAUTION: This remediation script will remove setroubleshoot-plugins +# from the system, and may remove any packages +# that depend on setroubleshoot-plugins. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "setroubleshoot-plugins" ; then + + yum remove -y "setroubleshoot-plugins" + +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Uninstall setroubleshoot-server Package + The SETroubleshoot service notifies desktop users of SELinux +denials. The service provides information around configuration errors, +unauthorized intrusions, and other potential errors. +The setroubleshoot-server package can be removed with the following command: + +$ sudo yum erase setroubleshoot-server + BP28(R68) + The SETroubleshoot service is an unnecessary daemon to have +running on a server. + CCE-83490-3 + +package --remove=setroubleshoot-server + + include remove_setroubleshoot-server + +class remove_setroubleshoot-server { + package { 'setroubleshoot-server': + ensure => 'purged', + } +} + + - name: Ensure setroubleshoot-server is removed + package: + name: setroubleshoot-server + state: absent + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83490-3 + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - package_setroubleshoot-server_removed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# CAUTION: This remediation script will remove setroubleshoot-server +# from the system, and may remove any packages +# that depend on setroubleshoot-server. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "setroubleshoot-server" ; then + + yum remove -y "setroubleshoot-server" + +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Uninstall setroubleshoot Package + The SETroubleshoot service notifies desktop users of SELinux +denials. The service provides information around configuration errors, +unauthorized intrusions, and other potential errors. +The setroubleshoot package can be removed with the following command: + +$ sudo yum erase setroubleshoot + BP28(R68) + 1.6.1.7 + The SETroubleshoot service is an unnecessary daemon to +have running on a server, especially if +X Windows is removed or disabled. + CCE-82755-0 + +package --remove=setroubleshoot + + include remove_setroubleshoot + +class remove_setroubleshoot { + package { 'setroubleshoot': + ensure => 'purged', + } +} + + - name: Ensure setroubleshoot is removed + package: + name: setroubleshoot + state: absent + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82755-0 + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - package_setroubleshoot_removed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# CAUTION: This remediation script will remove setroubleshoot +# from the system, and may remove any packages +# that depend on setroubleshoot. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "setroubleshoot" ; then + + yum remove -y "setroubleshoot" + +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + Ensure SELinux Not Disabled in the kernel arguments + SELinux can be disabled at boot time by disabling it via a kernel argument. +Remove any instances of selinux=0 from the kernel arguments in that +file to prevent SELinux from being disabled at boot. + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 4 + 5 + 6 + 8 + 9 + APO01.06 + APO11.04 + APO13.01 + BAI03.05 + DSS01.05 + DSS03.01 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + DSS06.02 + DSS06.03 + DSS06.06 + MEA02.01 + 3.1.2 + 3.7.2 + CCI-000022 + CCI-000032 + 164.308(a)(1)(ii)(D) + 164.308(a)(3) + 164.308(a)(4) + 164.310(b) + 164.310(c) + 164.312(a) + 164.312(e) + 4.2.3.4 + 4.3.3.2.2 + 4.3.3.3.9 + 4.3.3.4 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + 4.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.12.1.1 + A.12.1.2 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.1.2 + A.13.1.3 + A.13.2.1 + A.13.2.2 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.1 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.2.3 + CIP-004-6 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.2 + CIP-007-3 R5.2 + CIP-007-3 R5.3.1 + CIP-007-3 R5.3.2 + CIP-007-3 R5.3.3 + AC-3 + AC-3(3)(a) + DE.AE-1 + ID.AM-3 + PR.AC-4 + PR.AC-5 + PR.AC-6 + PR.DS-5 + PR.PT-1 + PR.PT-3 + PR.PT-4 + SRG-OS-000445-VMM-001780 + Disabling a major host protection feature, such as SELinux, at boot time prevents +it from confining system services at boot time. Further, it increases +the chances that it will remain off during system operation. + + + + + + + + + Ensure SELinux Not Disabled in /etc/default/grub + SELinux can be disabled at boot time by an argument in +/etc/default/grub. +Remove any instances of selinux=0 from the kernel arguments in that +file to prevent SELinux from being disabled at boot. + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 4 + 5 + 6 + 8 + 9 + APO01.06 + APO11.04 + APO13.01 + BAI03.05 + DSS01.05 + DSS03.01 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + DSS06.02 + DSS06.03 + DSS06.06 + MEA02.01 + 3.1.2 + 3.7.2 + CCI-000022 + CCI-000032 + 164.308(a)(1)(ii)(D) + 164.308(a)(3) + 164.308(a)(4) + 164.310(b) + 164.310(c) + 164.312(a) + 164.312(e) + 4.2.3.4 + 4.3.3.2.2 + 4.3.3.3.9 + 4.3.3.4 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + 4.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.12.1.1 + A.12.1.2 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.1.2 + A.13.1.3 + A.13.2.1 + A.13.2.2 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.1 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.2.3 + CIP-004-6 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.2 + CIP-007-3 R5.2 + CIP-007-3 R5.3.1 + CIP-007-3 R5.3.2 + CIP-007-3 R5.3.3 + AC-3 + AC-3(3)(a) + DE.AE-1 + ID.AM-3 + PR.AC-4 + PR.AC-5 + PR.AC-6 + PR.DS-5 + PR.PT-1 + PR.PT-3 + PR.PT-4 + SRG-OS-000445-VMM-001780 + 1.6.1.2 + Disabling a major host protection feature, such as SELinux, at boot time prevents +it from confining system services at boot time. Further, it increases +the chances that it will remain off during system operation. + CCE-80827-9 + - name: Find /etc/grub.d/ files + find: + paths: + - /etc/grub.d/ + follow: true + register: grub + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80827-9 + - NIST-800-171-3.1.2 + - NIST-800-171-3.7.2 + - NIST-800-53-AC-3 + - NIST-800-53-AC-3(3)(a) + - grub2_enable_selinux + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Ensure SELinux Not Disabled in grub files + replace: + dest: '{{ item.path }}' + regexp: (selinux|enforcing)=0 + with_items: + - '{{ grub.files }}' + - path: /etc/grub2.cfg + - path: /etc/default/grub + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80827-9 + - NIST-800-171-3.1.2 + - NIST-800-171-3.7.2 + - NIST-800-53-AC-3 + - NIST-800-53-AC-3(3)(a) + - grub2_enable_selinux + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +sed -i --follow-symlinks "s/selinux=0//gI" /etc/default/grub /etc/grub2.cfg /etc/grub.d/* +sed -i --follow-symlinks "s/enforcing=0//gI" /etc/default/grub /etc/grub2.cfg /etc/grub.d/* + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure No Device Files are Unlabeled by SELinux + Device files, which are used for communication with important system +resources, should be labeled with proper SELinux types. If any device files +carry the SELinux type device_t or unlabeled_t, report the +bug so that policy can be corrected. Supply information about what the +device is and what programs use it. + +To check for incorrectly labeled device files, run following commands: +$ sudo find /dev -context *:device_t:* \( -type c -o -type b \) -printf "%p %Z\n" +$ sudo find /dev -context *:unlabeled_t:* \( -type c -o -type b \) -printf "%p %Z\n" +It should produce no output in a well-configured system. + Automatic remediation of this control is not available. The remediation +can be achieved by amending SELinux policy. + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 18 + 2 + 3 + 5 + 6 + 7 + 8 + 9 + APO01.06 + APO11.04 + BAI01.06 + BAI03.05 + BAI06.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.03 + DSS03.05 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + DSS06.02 + DSS06.06 + MEA02.01 + 3.1.2 + 3.1.5 + 3.7.2 + CCI-000022 + CCI-000032 + CCI-000318 + CCI-000366 + CCI-000368 + CCI-001812 + CCI-001813 + CCI-001814 + 4.3.3.3.9 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 2.8 + SR 2.9 + SR 5.2 + SR 6.2 + SR 7.6 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.12.1.2 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.5.1 + A.12.6.2 + A.12.7.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.14.2.7 + A.15.2.1 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-7(a) + CM-7(b) + CM-6(a) + AC-3(3)(a) + AC-6 + DE.CM-1 + DE.CM-7 + PR.AC-4 + PR.DS-5 + PR.IP-1 + PR.IP-3 + PR.PT-1 + PR.PT-3 + SRG-OS-000480-GPOS-00227 + If a device file carries the SELinux type device_t or +unlabeled_t, then SELinux cannot properly restrict access to the +device file. + CCE-80866-7 + + + + + + + + + Ensure No Daemons are Unconfined by SELinux + Daemons for which the SELinux policy does not contain rules will inherit the +context of the parent process. Because daemons are launched during +startup and descend from the init process, they inherit the unconfined_service_t context. + + +To check for unconfined daemons, run the following command: +$ sudo ps -eZ | grep "unconfined_service_t" +It should produce no output in a well-configured system. + Automatic remediation of this control is not available. Remediation +can be achieved by amending SELinux policy or stopping the unconfined +daemons as outlined above. + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + 6 + 9 + APO01.06 + APO11.04 + BAI03.05 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + DSS06.02 + DSS06.06 + MEA02.01 + 3.1.2 + 3.1.5 + 3.7.2 + 164.308(a)(1)(ii)(D) + 164.308(a)(3) + 164.308(a)(4) + 164.310(b) + 164.310(c) + 164.312(a) + 164.312(e) + 4.3.3.3.9 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 2.8 + SR 2.9 + SR 5.2 + SR 7.6 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.12.1.2 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.5.1 + A.12.6.2 + A.12.7.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + AC-3(3)(a) + AC-6 + PR.AC-4 + PR.DS-5 + PR.IP-1 + PR.PT-1 + PR.PT-3 + 1.6.1.6 + Daemons which run with the unconfined_service_t context may cause AVC denials, +or allow privileges that the daemon does not require. + CCE-80867-5 + + + + + + + + + Configure SELinux Policy + The SELinux targeted policy is appropriate for +general-purpose desktops and servers, as well as systems in many other roles. +To configure the system to use this policy, add or correct the following line +in /etc/selinux/config: +SELINUXTYPE= +Other policies, such as mls, provide additional security labeling +and greater confinement but are not compatible with many general-purpose +use cases. + BP28(R66) + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 4 + 5 + 6 + 8 + 9 + APO01.06 + APO11.04 + APO13.01 + BAI03.05 + DSS01.05 + DSS03.01 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + DSS06.02 + DSS06.03 + DSS06.06 + MEA02.01 + 3.1.2 + 3.7.2 + CCI-002165 + CCI-002696 + 164.308(a)(1)(ii)(D) + 164.308(a)(3) + 164.308(a)(4) + 164.310(b) + 164.310(c) + 164.312(a) + 164.312(e) + 4.2.3.4 + 4.3.3.2.2 + 4.3.3.3.9 + 4.3.3.4 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + 4.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.12.1.1 + A.12.1.2 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.1.2 + A.13.1.3 + A.13.2.1 + A.13.2.2 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.1 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.2 + CIP-003-8 R5.3 + CIP-004-6 R2.2.3 + CIP-004-6 R2.3 + CIP-004-6 R3.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.2 + CIP-007-3 R5.2 + CIP-007-3 R5.3.1 + CIP-007-3 R5.3.2 + CIP-007-3 R5.3.3 + CIP-007-3 R6.5 + AC-3 + AC-3(3)(a) + AU-9 + SC-7(21) + DE.AE-1 + ID.AM-3 + PR.AC-4 + PR.AC-5 + PR.AC-6 + PR.DS-5 + PR.PT-1 + PR.PT-3 + PR.PT-4 + SRG-OS-000445-GPOS-00199 + SRG-OS-000445-VMM-001780 + RHEL-08-010450 + 1.6.1.3 + SV-230282r627750_rule + Setting the SELinux policy to targeted or a more specialized policy +ensures the system will confine processes that are likely to be +targeted for exploitation, such as network or system services. + +Note: During the development or debugging of SELinux modules, it is common to +temporarily place non-production systems in permissive mode. In such +temporary cases, SELinux policies should be developed, and once work +is completed, the system should be reconfigured to +. + CCE-80868-3 + - name: XCCDF Value var_selinux_policy_name # promote to variable + set_fact: + var_selinux_policy_name: !!str + tags: + - always + +- name: Configure SELinux Policy + block: + + - name: Check for duplicate values + lineinfile: + path: /etc/selinux/config + create: false + regexp: ^SELINUXTYPE= + state: absent + check_mode: true + changed_when: false + register: dupes + + - name: Deduplicate values from /etc/selinux/config + lineinfile: + path: /etc/selinux/config + create: false + regexp: ^SELINUXTYPE= + state: absent + when: dupes.found is defined and dupes.found > 1 + + - name: Insert correct line to /etc/selinux/config + lineinfile: + path: /etc/selinux/config + create: true + regexp: ^SELINUXTYPE= + line: SELINUXTYPE={{ var_selinux_policy_name }} + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80868-3 + - DISA-STIG-RHEL-08-010450 + - NIST-800-171-3.1.2 + - NIST-800-171-3.7.2 + - NIST-800-53-AC-3 + - NIST-800-53-AC-3(3)(a) + - NIST-800-53-AU-9 + - NIST-800-53-SC-7(21) + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + - selinux_policytype + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_selinux_policy_name='' + + +if [ -e "/etc/selinux/config" ] ; then + + LC_ALL=C sed -i "/^SELINUXTYPE=/Id" "/etc/selinux/config" +else + touch "/etc/selinux/config" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/selinux/config" + +cp "/etc/selinux/config" "/etc/selinux/config.bak" +# Insert at the end of the file +printf '%s\n' "SELINUXTYPE=$var_selinux_policy_name" >> "/etc/selinux/config" +# Clean up after ourselves. +rm "/etc/selinux/config.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Ensure SELinux State is Enforcing + The SELinux state should be set to at +system boot time. In the file /etc/selinux/config, add or correct the +following line to configure the system to boot into enforcing mode: +SELINUX= + BP28(R4) + BP28(R66) + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 4 + 5 + 6 + 8 + 9 + APO01.06 + APO11.04 + APO13.01 + BAI03.05 + DSS01.05 + DSS03.01 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + DSS06.02 + DSS06.03 + DSS06.06 + MEA02.01 + 3.1.2 + 3.7.2 + CCI-001084 + CCI-002165 + CCI-002696 + 164.308(a)(1)(ii)(D) + 164.308(a)(3) + 164.308(a)(4) + 164.310(b) + 164.310(c) + 164.312(a) + 164.312(e) + 4.2.3.4 + 4.3.3.2.2 + 4.3.3.3.9 + 4.3.3.4 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + 4.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.12.1.1 + A.12.1.2 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.1.2 + A.13.1.3 + A.13.2.1 + A.13.2.2 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.1 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.2 + CIP-003-8 R5.3 + CIP-004-6 R2.2.3 + CIP-004-6 R2.3 + CIP-004-6 R3.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.2 + CIP-007-3 R5.2 + CIP-007-3 R5.3.1 + CIP-007-3 R5.3.2 + CIP-007-3 R5.3.3 + CIP-007-3 R6.5 + AC-3 + AC-3(3)(a) + AU-9 + SC-7(21) + DE.AE-1 + ID.AM-3 + PR.AC-4 + PR.AC-5 + PR.AC-6 + PR.DS-5 + PR.PT-1 + PR.PT-3 + PR.PT-4 + SRG-OS-000445-GPOS-00199 + SRG-OS-000134-GPOS-00068 + SRG-OS-000445-VMM-001780 + RHEL-08-010170 + 1.7.1.4 + SV-230240r627750_rule + Setting the SELinux state to enforcing ensures SELinux is able to confine +potentially compromised processes to the security policy, which is designed to +prevent them from causing damage to the system or further elevating their +privileges. + CCE-80869-1 + - name: XCCDF Value var_selinux_state # promote to variable + set_fact: + var_selinux_state: !!str + tags: + - always + +- name: Ensure SELinux State is Enforcing + block: + + - name: Check for duplicate values + lineinfile: + path: /etc/selinux/config + create: false + regexp: ^SELINUX= + state: absent + check_mode: true + changed_when: false + register: dupes + + - name: Deduplicate values from /etc/selinux/config + lineinfile: + path: /etc/selinux/config + create: false + regexp: ^SELINUX= + state: absent + when: dupes.found is defined and dupes.found > 1 + + - name: Insert correct line to /etc/selinux/config + lineinfile: + path: /etc/selinux/config + create: true + regexp: ^SELINUX= + line: SELINUX={{ var_selinux_state }} + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80869-1 + - DISA-STIG-RHEL-08-010170 + - NIST-800-171-3.1.2 + - NIST-800-171-3.7.2 + - NIST-800-53-AC-3 + - NIST-800-53-AC-3(3)(a) + - NIST-800-53-AU-9 + - NIST-800-53-SC-7(21) + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - selinux_state + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_selinux_state='' + + +if [ -e "/etc/selinux/config" ] ; then + + LC_ALL=C sed -i "/^SELINUX=/Id" "/etc/selinux/config" +else + touch "/etc/selinux/config" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/selinux/config" + +cp "/etc/selinux/config" "/etc/selinux/config.bak" +# Insert at the end of the file +printf '%s\n' "SELINUX=$var_selinux_state" >> "/etc/selinux/config" +# Clean up after ourselves. +rm "/etc/selinux/config.bak" + +fixfiles onboot +fixfiles -f relabel + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Map System Users To The Appropriate SELinux Role + Configure the operating system to prevent non-privileged users from executing +privileged functions to include disabling, circumventing, or altering +implemented security safeguards/countermeasures. All administrators must be +mapped to the sysadm_u or staff_u users with the +appropriate domains (sysadm_t and staff_t). +$ sudo semanage login -m -s sysadm_u USER or +$ sudo semanage login -m -s staff_u USER + +All authorized non-administrative +users must be mapped to the user_u role or the appropriate domain +(user_t). +$ sudo semanage login -m -s user_u USER + CCI-002165 + CCI-002235 + SRG-OS-000324-GPOS-00125 + Preventing non-privileged users from executing privileged functions mitigates +the risk that unauthorized individuals or processes may gain unnecessary access +to information or privileges. + +Privileged functions include, for example, +establishing accounts, performing system integrity checks, or administering +cryptographic key management activities. Non-privileged users are individuals +who do not possess appropriate authorizations. Circumventing intrusion detection +and prevention mechanisms or malicious code protection mechanisms are examples +of privileged functions that require protection from non-privileged users. + + + + + + SELinux - Booleans + Enable or Disable runtime customization of SELinux system policies +without having to reload or recompile the SELinux policy. + + abrt_anon_write SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + abrt_handle_event SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + abrt_upload_watch_anon_write SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + antivirus_can_scan_system SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + antivirus_use_jit SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + auditadm_exec_content SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + authlogin_nsswitch_use_ldap SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + authlogin_radius SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + authlogin_yubikey SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + awstats_purge_apache_log_files SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + boinc_execmem SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + cdrecord_read_content SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + cluster_can_network_connect SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + cluster_manage_all_files SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + cluster_use_execmem SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + cobbler_anon_write SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + cobbler_can_network_connect SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + cobbler_use_cifs SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + cobbler_use_nfs SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + collectd_tcp_network_connect SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + condor_tcp_network_connect SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + conman_can_network SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + container_connect_any SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + cron_can_relabel SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + cron_system_cronjob_use_shares SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + cron_userdomain_transition SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + cups_execmem SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + cvs_read_shadow SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + daemons_dump_core SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + daemons_enable_cluster_mode SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + daemons_use_tcp_wrapper SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + daemons_use_tty SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + dbadm_exec_content SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + dbadm_manage_user_files SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + dbadm_read_user_files SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + deny_execmem SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + deny_ptrace SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + dhcpc_exec_iptables SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + dhcpd_use_ldap SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + domain_fd_use SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + domain_kernel_load_modules SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + entropyd_use_audio SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + exim_can_connect_db SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + exim_manage_user_files SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + exim_read_user_files SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + fcron_crond SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + fenced_can_network_connect SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + fenced_can_ssh SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + fips_mode SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + ftpd_anon_write SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + ftpd_connect_all_unreserved SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + ftpd_connect_db SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + ftpd_full_access SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + ftpd_use_cifs SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + ftpd_use_fusefs SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + ftpd_use_nfs SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + ftpd_use_passive_mode SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + git_cgi_enable_homedirs SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + git_cgi_use_cifs SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + git_cgi_use_nfs SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + git_session_bind_all_unreserved_ports SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + git_session_users SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + git_system_enable_homedirs SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + git_system_use_cifs SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + git_system_use_nfs SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + gitosis_can_sendmail SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + glance_api_can_network SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + glance_use_execmem SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + glance_use_fusefs SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + global_ssp SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + gluster_anon_write SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + gluster_export_all_ro SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + gluster_export_all_rw SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + gpg_web_anon_write SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + gssd_read_tmp SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + guest_exec_content SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + haproxy_connect_any SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + httpd_anon_write SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + httpd_builtin_scripting SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + httpd_can_check_spam SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + httpd_can_connect_ftp SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + httpd_can_connect_ldap SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + httpd_can_connect_mythtv SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + httpd_can_connect_zabbix SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + httpd_can_network_connect SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + httpd_can_network_connect_cobbler SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + httpd_can_network_connect_db SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + httpd_can_network_memcache SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + httpd_can_network_relay SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + httpd_can_sendmail SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + httpd_dbus_avahi SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + httpd_dbus_sssd SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + httpd_dontaudit_search_dirs SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + httpd_enable_cgi SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + httpd_enable_ftp_server SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + httpd_enable_homedirs SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + httpd_execmem SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + httpd_graceful_shutdown SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + httpd_manage_ipa SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + httpd_mod_auth_ntlm_winbind SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + httpd_mod_auth_pam SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + httpd_read_user_content SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + httpd_run_ipa SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + httpd_run_preupgrade SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + httpd_run_stickshift SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + httpd_serve_cobbler_files SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + httpd_setrlimit SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + httpd_ssi_exec SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + httpd_sys_script_anon_write SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + httpd_tmp_exec SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + httpd_tty_comm SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + httpd_unified SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + httpd_use_cifs SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + httpd_use_fusefs SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + httpd_use_gpg SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + httpd_use_nfs SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + httpd_use_openstack SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + httpd_use_sasl SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + httpd_verify_dns SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + icecast_use_any_tcp_ports SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + irc_use_any_tcp_ports SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + irssi_use_full_network SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + kdumpgui_run_bootloader SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + kerberos_enabled SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + ksmtuned_use_cifs SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + ksmtuned_use_nfs SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + logadm_exec_content SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + logging_syslogd_can_sendmail SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + logging_syslogd_run_nagios_plugins SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + logging_syslogd_use_tty SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + login_console_enabled SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + logrotate_use_nfs SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + logwatch_can_network_connect_mail SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + lsmd_plugin_connect_any SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + mailman_use_fusefs SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + mcelog_client SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + mcelog_exec_scripts SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + mcelog_foreground SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + mcelog_server SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + minidlna_read_generic_user_content SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + mmap_low_allowed SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + mock_enable_homedirs SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + mount_anyfile SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + mozilla_plugin_bind_unreserved_ports SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + mozilla_plugin_can_network_connect SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + mozilla_plugin_use_bluejeans SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + mozilla_plugin_use_gps SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + mozilla_plugin_use_spice SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + mozilla_read_content SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + mpd_enable_homedirs SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + mpd_use_cifs SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + mpd_use_nfs SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + mplayer_execstack SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + mysql_connect_any SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + nagios_run_pnp4nagios SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + nagios_run_sudo SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + named_tcp_bind_http_port SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + named_write_master_zones SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + neutron_can_network SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + nfs_export_all_ro SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + nfs_export_all_rw SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + nfsd_anon_write SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + nis_enabled SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + nscd_use_shm SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + openshift_use_nfs SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + openvpn_can_network_connect SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + openvpn_enable_homedirs SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + openvpn_run_unconfined SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + pcp_bind_all_unreserved_ports SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + pcp_read_generic_logs SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + piranha_lvs_can_network_connect SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + polipo_connect_all_unreserved SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + polipo_session_bind_all_unreserved_ports SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + polipo_session_users SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + polipo_use_cifs SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + polipo_use_nfs SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + polyinstantiation_enabled SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + postfix_local_write_mail_spool SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + postgresql_can_rsync SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + postgresql_selinux_transmit_client_label SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + postgresql_selinux_unconfined_dbadm SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + postgresql_selinux_users_ddl SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + pppd_can_insmod SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + pppd_for_user SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + privoxy_connect_any SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + prosody_bind_http_port SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + puppetagent_manage_all_files SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + puppetmaster_use_db SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + racoon_read_shadow SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + rsync_anon_write SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + rsync_client SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + rsync_export_all_ro SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + rsync_full_access SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + samba_create_home_dirs SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + samba_domain_controller SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + samba_enable_home_dirs SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + samba_export_all_ro SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + samba_export_all_rw SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + samba_load_libgfapi SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + samba_portmapper SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + samba_run_unconfined SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + samba_share_fusefs SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + samba_share_nfs SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + sanlock_use_fusefs SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + sanlock_use_nfs SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + sanlock_use_samba SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + saslauthd_read_shadow SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + secadm_exec_content SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + secure_mode SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + secure_mode_insmod SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + secure_mode_policyload SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + selinuxuser_direct_dri_enabled SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + selinuxuser_execheap SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + selinuxuser_execmod SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + selinuxuser_execstack SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + selinuxuser_mysql_connect_enabled SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + selinuxuser_ping SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + selinuxuser_postgresql_connect_enabled SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + selinuxuser_rw_noexattrfile SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + selinuxuser_share_music SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + selinuxuser_tcp_server SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + selinuxuser_udp_server SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + selinuxuser_use_ssh_chroot SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + sge_domain_can_network_connect SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + sge_use_nfs SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + smartmon_3ware SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + smbd_anon_write SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + spamassassin_can_network SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + spamd_enable_home_dirs SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + squid_connect_any SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + squid_use_tproxy SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + ssh_chroot_rw_homedirs SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + ssh_keysign SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + ssh_sysadm_login SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + staff_exec_content SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + staff_use_svirt SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + swift_can_network SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + sysadm_exec_content SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + telepathy_connect_all_ports SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + telepathy_tcp_connect_generic_network_ports SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + tftp_anon_write SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + tftp_home_dir SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + tmpreaper_use_nfs SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + tmpreaper_use_samba SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + tor_bind_all_unreserved_ports SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + tor_can_network_relay SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + unconfined_chrome_sandbox_transition SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + unconfined_login SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + unconfined_mozilla_plugin_transition SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + unprivuser_use_svirt SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + use_ecryptfs_home_dirs SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + use_fusefs_home_dirs SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + use_lpd_server SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + use_nfs_home_dirs SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + use_samba_home_dirs SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + user_exec_content SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + varnishd_connect_any SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + virt_read_qemu_ga_data SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + virt_rw_qemu_ga_data SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + virt_sandbox_use_all_caps SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + virt_sandbox_use_audit SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + virt_sandbox_use_mknod SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + virt_sandbox_use_netlink SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + virt_sandbox_use_sys_admin SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + virt_transition_userdomain SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + virt_use_comm SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + virt_use_execmem SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + virt_use_fusefs SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + virt_use_nfs SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + virt_use_rawip SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + virt_use_samba SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + virt_use_sanlock SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + virt_use_usb SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + virt_use_xserver SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + webadm_manage_user_files SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + webadm_read_user_files SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + wine_mmap_zero_ignore SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + xdm_bind_vnc_tcp_port SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + xdm_exec_bootloader SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + xdm_sysadm_login SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + xdm_write_home SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + xen_use_nfs SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + xend_run_blktap SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + xend_run_qemu SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + xguest_connect_network SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + xguest_exec_content SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + xguest_mount_media SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + xguest_use_bluetooth SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + xserver_clients_write_xshm SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + xserver_execmem SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + xserver_object_manager SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + zabbix_can_network SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + zarafa_setrlimit SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + zebra_write_config SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + zoneminder_anon_write SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + zoneminder_run_sudo SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + Disable the abrt_anon_write SELinux Boolean + By default, the SELinux boolean abrt_anon_write is disabled. +If this setting is enabled, it should be disabled. + +To disable the abrt_anon_write SELinux boolean, run the following command: +$ sudo setsebool -P abrt_anon_write off + 3.7.2 + + - name: XCCDF Value var_abrt_anon_write # promote to variable + set_fact: + var_abrt_anon_write: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-171-3.7.2 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_abrt_anon_write + +- name: Set SELinux boolean abrt_anon_write accordingly + seboolean: + name: abrt_anon_write + state: '{{ var_abrt_anon_write }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-171-3.7.2 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_abrt_anon_write + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_abrt_anon_write='' + + +setsebool -P abrt_anon_write $var_abrt_anon_write + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the abrt_handle_event SELinux Boolean + By default, the SELinux boolean abrt_handle_event is disabled. +If this setting is enabled, it should be disabled. + +To disable the abrt_handle_event SELinux boolean, run the following command: +$ sudo setsebool -P abrt_handle_event off + 3.7.2 + + - name: XCCDF Value var_abrt_handle_event # promote to variable + set_fact: + var_abrt_handle_event: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-171-3.7.2 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_abrt_handle_event + +- name: Set SELinux boolean abrt_handle_event accordingly + seboolean: + name: abrt_handle_event + state: '{{ var_abrt_handle_event }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-171-3.7.2 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_abrt_handle_event + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_abrt_handle_event='' + + +setsebool -P abrt_handle_event $var_abrt_handle_event + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the abrt_upload_watch_anon_write SELinux Boolean + By default, the SELinux boolean abrt_upload_watch_anon_write is enabled. +This setting should be disabled as it allows the Automatic Bug Report Tool (ABRT) +to modify public files used for public file transfer services. + +To disable the abrt_upload_watch_anon_write SELinux boolean, run the following command: +$ sudo setsebool -P abrt_upload_watch_anon_write off + 3.7.2 + + - name: XCCDF Value var_abrt_upload_watch_anon_write # promote to variable + set_fact: + var_abrt_upload_watch_anon_write: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-171-3.7.2 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_abrt_upload_watch_anon_write + +- name: Set SELinux boolean abrt_upload_watch_anon_write accordingly + seboolean: + name: abrt_upload_watch_anon_write + state: '{{ var_abrt_upload_watch_anon_write }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-171-3.7.2 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_abrt_upload_watch_anon_write + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_abrt_upload_watch_anon_write='' + + +setsebool -P abrt_upload_watch_anon_write $var_abrt_upload_watch_anon_write + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable the antivirus_can_scan_system SELinux Boolean + By default, the SELinux boolean antivirus_can_scan_system is disabled. +This setting should be enabled as it allows antivirus programs to read non-security +files on a system. + +To enable the antivirus_can_scan_system SELinux boolean, run the following command: +$ sudo setsebool -P antivirus_can_scan_system on + 3.7.2 + + - name: XCCDF Value var_antivirus_can_scan_system # promote to variable + set_fact: + var_antivirus_can_scan_system: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-171-3.7.2 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_antivirus_can_scan_system + +- name: Set SELinux boolean antivirus_can_scan_system accordingly + seboolean: + name: antivirus_can_scan_system + state: '{{ var_antivirus_can_scan_system }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-171-3.7.2 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_antivirus_can_scan_system + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_antivirus_can_scan_system='' + + +setsebool -P antivirus_can_scan_system $var_antivirus_can_scan_system + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the antivirus_use_jit SELinux Boolean + By default, the SELinux boolean antivirus_use_jit is disabled. +If this setting is enabled, it should be disabled. + +To disable the antivirus_use_jit SELinux boolean, run the following command: +$ sudo setsebool -P antivirus_use_jit off + 3.7.2 + + - name: XCCDF Value var_antivirus_use_jit # promote to variable + set_fact: + var_antivirus_use_jit: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-171-3.7.2 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_antivirus_use_jit + +- name: Set SELinux boolean antivirus_use_jit accordingly + seboolean: + name: antivirus_use_jit + state: '{{ var_antivirus_use_jit }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-171-3.7.2 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_antivirus_use_jit + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_antivirus_use_jit='' + + +setsebool -P antivirus_use_jit $var_antivirus_use_jit + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable the auditadm_exec_content SELinux Boolean + By default, the SELinux boolean auditadm_exec_content is enabled. +If this setting is disabled, it should be enabled. + +To enable the auditadm_exec_content SELinux boolean, run the following command: +$ sudo setsebool -P auditadm_exec_content on + 80424-5 + 0582 + 0584 + 05885 + 0586 + 0846 + 0957 + + CCE-84297-1 + - name: XCCDF Value var_auditadm_exec_content # promote to variable + set_fact: + var_auditadm_exec_content: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84297-1 + - NIST-800-171-80424-5 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_auditadm_exec_content + +- name: Set SELinux boolean auditadm_exec_content accordingly + seboolean: + name: auditadm_exec_content + state: '{{ var_auditadm_exec_content }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84297-1 + - NIST-800-171-80424-5 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_auditadm_exec_content + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_auditadm_exec_content='' + + +setsebool -P auditadm_exec_content $var_auditadm_exec_content + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the authlogin_nsswitch_use_ldap SELinux Boolean + By default, the SELinux boolean authlogin_nsswitch_use_ldap is disabled. +If this setting is enabled, it should be disabled. + +To disable the authlogin_nsswitch_use_ldap SELinux boolean, run the following command: +$ sudo setsebool -P authlogin_nsswitch_use_ldap off + 3.7.2 + 0421 + 0422 + 0431 + 0974 + 1173 + 1401 + 1504 + 1505 + 1546 + 1557 + 1558 + 1559 + 1560 + 1561 + + CCE-84296-3 + - name: XCCDF Value var_authlogin_nsswitch_use_ldap # promote to variable + set_fact: + var_authlogin_nsswitch_use_ldap: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84296-3 + - NIST-800-171-3.7.2 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_authlogin_nsswitch_use_ldap + +- name: Set SELinux boolean authlogin_nsswitch_use_ldap accordingly + seboolean: + name: authlogin_nsswitch_use_ldap + state: '{{ var_authlogin_nsswitch_use_ldap }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84296-3 + - NIST-800-171-3.7.2 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_authlogin_nsswitch_use_ldap + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_authlogin_nsswitch_use_ldap='' + + +setsebool -P authlogin_nsswitch_use_ldap $var_authlogin_nsswitch_use_ldap + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the authlogin_radius SELinux Boolean + By default, the SELinux boolean authlogin_radius is disabled. +If this setting is enabled, it should be disabled. + +To disable the authlogin_radius SELinux boolean, run the following command: +$ sudo setsebool -P authlogin_radius off + 3.7.2 + 0421 + 0422 + 0431 + 0974 + 1173 + 1401 + 1504 + 1505 + 1546 + 1557 + 1558 + 1559 + 1560 + 1561 + + CCE-84294-8 + - name: XCCDF Value var_authlogin_radius # promote to variable + set_fact: + var_authlogin_radius: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84294-8 + - NIST-800-171-3.7.2 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_authlogin_radius + +- name: Set SELinux boolean authlogin_radius accordingly + seboolean: + name: authlogin_radius + state: '{{ var_authlogin_radius }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84294-8 + - NIST-800-171-3.7.2 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_authlogin_radius + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_authlogin_radius='' + + +setsebool -P authlogin_radius $var_authlogin_radius + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the authlogin_yubikey SELinux Boolean + By default, the SELinux boolean authlogin_yubikey is disabled. +If this setting is enabled, it should be disabled. + +To disable the authlogin_yubikey SELinux boolean, run the following command: +$ sudo setsebool -P authlogin_yubikey off + 3.7.2 + + - name: XCCDF Value var_authlogin_yubikey # promote to variable + set_fact: + var_authlogin_yubikey: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-171-3.7.2 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_authlogin_yubikey + +- name: Set SELinux boolean authlogin_yubikey accordingly + seboolean: + name: authlogin_yubikey + state: '{{ var_authlogin_yubikey }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-171-3.7.2 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_authlogin_yubikey + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_authlogin_yubikey='' + + +setsebool -P authlogin_yubikey $var_authlogin_yubikey + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the awstats_purge_apache_log_files SELinux Boolean + By default, the SELinux boolean awstats_purge_apache_log_files is disabled. +If this setting is enabled, it should be disabled. + +To disable the awstats_purge_apache_log_files SELinux boolean, run the following command: +$ sudo setsebool -P awstats_purge_apache_log_files off + 3.7.2 + + - name: XCCDF Value var_awstats_purge_apache_log_files # promote to variable + set_fact: + var_awstats_purge_apache_log_files: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-171-3.7.2 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_awstats_purge_apache_log_files + +- name: Set SELinux boolean awstats_purge_apache_log_files accordingly + seboolean: + name: awstats_purge_apache_log_files + state: '{{ var_awstats_purge_apache_log_files }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-171-3.7.2 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_awstats_purge_apache_log_files + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_awstats_purge_apache_log_files='' + + +setsebool -P awstats_purge_apache_log_files $var_awstats_purge_apache_log_files + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the boinc_execmem SELinux Boolean + By default, the SELinux boolean boinc_execmem is enabled. +This setting should be disabled. + +To disable the boinc_execmem SELinux boolean, run the following command: +$ sudo setsebool -P boinc_execmem off + BP28(R67) + 3.7.2 + + CCE-83304-6 + - name: XCCDF Value var_boinc_execmem # promote to variable + set_fact: + var_boinc_execmem: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83304-6 + - NIST-800-171-3.7.2 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_boinc_execmem + +- name: Set SELinux boolean boinc_execmem accordingly + seboolean: + name: boinc_execmem + state: '{{ var_boinc_execmem }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83304-6 + - NIST-800-171-3.7.2 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_boinc_execmem + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_boinc_execmem='' + + +setsebool -P boinc_execmem $var_boinc_execmem + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the cdrecord_read_content SELinux Boolean + By default, the SELinux boolean cdrecord_read_content is disabled. +If this setting is enabled, it should be disabled. + +To disable the cdrecord_read_content SELinux boolean, run the following command: +$ sudo setsebool -P cdrecord_read_content off + + - name: XCCDF Value var_cdrecord_read_content # promote to variable + set_fact: + var_cdrecord_read_content: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_cdrecord_read_content + +- name: Set SELinux boolean cdrecord_read_content accordingly + seboolean: + name: cdrecord_read_content + state: '{{ var_cdrecord_read_content }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_cdrecord_read_content + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_cdrecord_read_content='' + + +setsebool -P cdrecord_read_content $var_cdrecord_read_content + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the cluster_can_network_connect SELinux Boolean + By default, the SELinux boolean cluster_can_network_connect is disabled. +If this setting is enabled, it should be disabled. + +To disable the cluster_can_network_connect SELinux boolean, run the following command: +$ sudo setsebool -P cluster_can_network_connect off + + - name: XCCDF Value var_cluster_can_network_connect # promote to variable + set_fact: + var_cluster_can_network_connect: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_cluster_can_network_connect + +- name: Set SELinux boolean cluster_can_network_connect accordingly + seboolean: + name: cluster_can_network_connect + state: '{{ var_cluster_can_network_connect }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_cluster_can_network_connect + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_cluster_can_network_connect='' + + +setsebool -P cluster_can_network_connect $var_cluster_can_network_connect + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the cluster_manage_all_files SELinux Boolean + By default, the SELinux boolean cluster_manage_all_files is disabled. +If this setting is enabled, it should be disabled. + +To disable the cluster_manage_all_files SELinux boolean, run the following command: +$ sudo setsebool -P cluster_manage_all_files off + + - name: XCCDF Value var_cluster_manage_all_files # promote to variable + set_fact: + var_cluster_manage_all_files: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_cluster_manage_all_files + +- name: Set SELinux boolean cluster_manage_all_files accordingly + seboolean: + name: cluster_manage_all_files + state: '{{ var_cluster_manage_all_files }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_cluster_manage_all_files + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_cluster_manage_all_files='' + + +setsebool -P cluster_manage_all_files $var_cluster_manage_all_files + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the cluster_use_execmem SELinux Boolean + By default, the SELinux boolean cluster_use_execmem is disabled. +If this setting is enabled, it should be disabled. + +To disable the cluster_use_execmem SELinux boolean, run the following command: +$ sudo setsebool -P cluster_use_execmem off + BP28(R67) + + CCE-83305-3 + - name: XCCDF Value var_cluster_use_execmem # promote to variable + set_fact: + var_cluster_use_execmem: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83305-3 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_cluster_use_execmem + +- name: Set SELinux boolean cluster_use_execmem accordingly + seboolean: + name: cluster_use_execmem + state: '{{ var_cluster_use_execmem }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83305-3 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_cluster_use_execmem + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_cluster_use_execmem='' + + +setsebool -P cluster_use_execmem $var_cluster_use_execmem + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the cobbler_anon_write SELinux Boolean + By default, the SELinux boolean cobbler_anon_write is disabled. +If this setting is enabled, it should be disabled. + +To disable the cobbler_anon_write SELinux boolean, run the following command: +$ sudo setsebool -P cobbler_anon_write off + + - name: XCCDF Value var_cobbler_anon_write # promote to variable + set_fact: + var_cobbler_anon_write: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_cobbler_anon_write + +- name: Set SELinux boolean cobbler_anon_write accordingly + seboolean: + name: cobbler_anon_write + state: '{{ var_cobbler_anon_write }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_cobbler_anon_write + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_cobbler_anon_write='' + + +setsebool -P cobbler_anon_write $var_cobbler_anon_write + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the cobbler_can_network_connect SELinux Boolean + By default, the SELinux boolean cobbler_can_network_connect is disabled. +If this setting is enabled, it should be disabled. + +To disable the cobbler_can_network_connect SELinux boolean, run the following command: +$ sudo setsebool -P cobbler_can_network_connect off + + - name: XCCDF Value var_cobbler_can_network_connect # promote to variable + set_fact: + var_cobbler_can_network_connect: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_cobbler_can_network_connect + +- name: Set SELinux boolean cobbler_can_network_connect accordingly + seboolean: + name: cobbler_can_network_connect + state: '{{ var_cobbler_can_network_connect }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_cobbler_can_network_connect + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_cobbler_can_network_connect='' + + +setsebool -P cobbler_can_network_connect $var_cobbler_can_network_connect + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the cobbler_use_cifs SELinux Boolean + By default, the SELinux boolean cobbler_use_cifs is disabled. +If this setting is enabled, it should be disabled. + +To disable the cobbler_use_cifs SELinux boolean, run the following command: +$ sudo setsebool -P cobbler_use_cifs off + + - name: XCCDF Value var_cobbler_use_cifs # promote to variable + set_fact: + var_cobbler_use_cifs: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_cobbler_use_cifs + +- name: Set SELinux boolean cobbler_use_cifs accordingly + seboolean: + name: cobbler_use_cifs + state: '{{ var_cobbler_use_cifs }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_cobbler_use_cifs + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_cobbler_use_cifs='' + + +setsebool -P cobbler_use_cifs $var_cobbler_use_cifs + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the cobbler_use_nfs SELinux Boolean + By default, the SELinux boolean cobbler_use_nfs is disabled. +If this setting is enabled, it should be disabled. + +To disable the cobbler_use_nfs SELinux boolean, run the following command: +$ sudo setsebool -P cobbler_use_nfs off + + - name: XCCDF Value var_cobbler_use_nfs # promote to variable + set_fact: + var_cobbler_use_nfs: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_cobbler_use_nfs + +- name: Set SELinux boolean cobbler_use_nfs accordingly + seboolean: + name: cobbler_use_nfs + state: '{{ var_cobbler_use_nfs }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_cobbler_use_nfs + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_cobbler_use_nfs='' + + +setsebool -P cobbler_use_nfs $var_cobbler_use_nfs + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the collectd_tcp_network_connect SELinux Boolean + By default, the SELinux boolean collectd_tcp_network_connect is disabled. +If this setting is enabled, it should be disabled. + +To disable the collectd_tcp_network_connect SELinux boolean, run the following command: +$ sudo setsebool -P collectd_tcp_network_connect off + + - name: XCCDF Value var_collectd_tcp_network_connect # promote to variable + set_fact: + var_collectd_tcp_network_connect: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_collectd_tcp_network_connect + +- name: Set SELinux boolean collectd_tcp_network_connect accordingly + seboolean: + name: collectd_tcp_network_connect + state: '{{ var_collectd_tcp_network_connect }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_collectd_tcp_network_connect + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_collectd_tcp_network_connect='' + + +setsebool -P collectd_tcp_network_connect $var_collectd_tcp_network_connect + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the condor_tcp_network_connect SELinux Boolean + By default, the SELinux boolean condor_tcp_network_connect is disabled. +If this setting is enabled, it should be disabled. + +To disable the condor_tcp_network_connect SELinux boolean, run the following command: +$ sudo setsebool -P condor_tcp_network_connect off + + - name: XCCDF Value var_condor_tcp_network_connect # promote to variable + set_fact: + var_condor_tcp_network_connect: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_condor_tcp_network_connect + +- name: Set SELinux boolean condor_tcp_network_connect accordingly + seboolean: + name: condor_tcp_network_connect + state: '{{ var_condor_tcp_network_connect }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_condor_tcp_network_connect + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_condor_tcp_network_connect='' + + +setsebool -P condor_tcp_network_connect $var_condor_tcp_network_connect + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the conman_can_network SELinux Boolean + By default, the SELinux boolean conman_can_network is disabled. +If this setting is enabled, it should be disabled. + +To disable the conman_can_network SELinux boolean, run the following command: +$ sudo setsebool -P conman_can_network off + + - name: XCCDF Value var_conman_can_network # promote to variable + set_fact: + var_conman_can_network: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_conman_can_network + +- name: Set SELinux boolean conman_can_network accordingly + seboolean: + name: conman_can_network + state: '{{ var_conman_can_network }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_conman_can_network + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_conman_can_network='' + + +setsebool -P conman_can_network $var_conman_can_network + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the container_connect_any SELinux Boolean + By default, the SELinux boolean container_connect_any is disabled. +If this setting is enabled, it should be disabled. + +To disable the container_connect_any SELinux boolean, run the following command: +$ sudo setsebool -P container_connect_any off + + - name: XCCDF Value var_container_connect_any # promote to variable + set_fact: + var_container_connect_any: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_container_connect_any + +- name: Set SELinux boolean container_connect_any accordingly + seboolean: + name: container_connect_any + state: '{{ var_container_connect_any }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_container_connect_any + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_container_connect_any='' + + +setsebool -P container_connect_any $var_container_connect_any + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the cron_can_relabel SELinux Boolean + By default, the SELinux boolean cron_can_relabel is disabled. +If this setting is enabled, it should be disabled. + +To disable the cron_can_relabel SELinux boolean, run the following command: +$ sudo setsebool -P cron_can_relabel off + + - name: XCCDF Value var_cron_can_relabel # promote to variable + set_fact: + var_cron_can_relabel: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_cron_can_relabel + +- name: Set SELinux boolean cron_can_relabel accordingly + seboolean: + name: cron_can_relabel + state: '{{ var_cron_can_relabel }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_cron_can_relabel + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_cron_can_relabel='' + + +setsebool -P cron_can_relabel $var_cron_can_relabel + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the cron_system_cronjob_use_shares SELinux Boolean + By default, the SELinux boolean cron_system_cronjob_use_shares is disabled. +If this setting is enabled, it should be disabled. + +To disable the cron_system_cronjob_use_shares SELinux boolean, run the following command: +$ sudo setsebool -P cron_system_cronjob_use_shares off + + - name: XCCDF Value var_cron_system_cronjob_use_shares # promote to variable + set_fact: + var_cron_system_cronjob_use_shares: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_cron_system_cronjob_use_shares + +- name: Set SELinux boolean cron_system_cronjob_use_shares accordingly + seboolean: + name: cron_system_cronjob_use_shares + state: '{{ var_cron_system_cronjob_use_shares }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_cron_system_cronjob_use_shares + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_cron_system_cronjob_use_shares='' + + +setsebool -P cron_system_cronjob_use_shares $var_cron_system_cronjob_use_shares + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable the cron_userdomain_transition SELinux Boolean + By default, the SELinux boolean cron_userdomain_transition is enabled. +This setting should be enabled as end user cron jobs run in their default +associated user domain(s) instead of the general cronjob domain. + +To enable the cron_userdomain_transition SELinux boolean, run the following command: +$ sudo setsebool -P cron_userdomain_transition on + + - name: XCCDF Value var_cron_userdomain_transition # promote to variable + set_fact: + var_cron_userdomain_transition: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_cron_userdomain_transition + +- name: Set SELinux boolean cron_userdomain_transition accordingly + seboolean: + name: cron_userdomain_transition + state: '{{ var_cron_userdomain_transition }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_cron_userdomain_transition + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_cron_userdomain_transition='' + + +setsebool -P cron_userdomain_transition $var_cron_userdomain_transition + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the cups_execmem SELinux Boolean + By default, the SELinux boolean cups_execmem is disabled. +If this setting is enabled, it should be disabled. + +To disable the cups_execmem SELinux boolean, run the following command: +$ sudo setsebool -P cups_execmem off + BP28(R67) + + CCE-83306-1 + - name: XCCDF Value var_cups_execmem # promote to variable + set_fact: + var_cups_execmem: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83306-1 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_cups_execmem + +- name: Set SELinux boolean cups_execmem accordingly + seboolean: + name: cups_execmem + state: '{{ var_cups_execmem }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83306-1 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_cups_execmem + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_cups_execmem='' + + +setsebool -P cups_execmem $var_cups_execmem + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the cvs_read_shadow SELinux Boolean + By default, the SELinux boolean cvs_read_shadow is disabled. +If this setting is enabled, it should be disabled. + +To disable the cvs_read_shadow SELinux boolean, run the following command: +$ sudo setsebool -P cvs_read_shadow off + + - name: XCCDF Value var_cvs_read_shadow # promote to variable + set_fact: + var_cvs_read_shadow: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_cvs_read_shadow + +- name: Set SELinux boolean cvs_read_shadow accordingly + seboolean: + name: cvs_read_shadow + state: '{{ var_cvs_read_shadow }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_cvs_read_shadow + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_cvs_read_shadow='' + + +setsebool -P cvs_read_shadow $var_cvs_read_shadow + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the daemons_dump_core SELinux Boolean + By default, the SELinux boolean daemons_dump_core is disabled. +If this setting is enabled, it should be disabled. + +To disable the daemons_dump_core SELinux boolean, run the following command: +$ sudo setsebool -P daemons_dump_core off + + - name: XCCDF Value var_daemons_dump_core # promote to variable + set_fact: + var_daemons_dump_core: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_daemons_dump_core + +- name: Set SELinux boolean daemons_dump_core accordingly + seboolean: + name: daemons_dump_core + state: '{{ var_daemons_dump_core }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_daemons_dump_core + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_daemons_dump_core='' + + +setsebool -P daemons_dump_core $var_daemons_dump_core + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the daemons_enable_cluster_mode SELinux Boolean + By default, the SELinux boolean daemons_enable_cluster_mode is disabled. +If this setting is enabled, it should be disabled. + +To disable the daemons_enable_cluster_mode SELinux boolean, run the following command: +$ sudo setsebool -P daemons_enable_cluster_mode off + + - name: XCCDF Value var_daemons_enable_cluster_mode # promote to variable + set_fact: + var_daemons_enable_cluster_mode: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_daemons_enable_cluster_mode + +- name: Set SELinux boolean daemons_enable_cluster_mode accordingly + seboolean: + name: daemons_enable_cluster_mode + state: '{{ var_daemons_enable_cluster_mode }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_daemons_enable_cluster_mode + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_daemons_enable_cluster_mode='' + + +setsebool -P daemons_enable_cluster_mode $var_daemons_enable_cluster_mode + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the daemons_use_tcp_wrapper SELinux Boolean + By default, the SELinux boolean daemons_use_tcp_wrapper is disabled. +If this setting is enabled, it should be disabled. + +To disable the daemons_use_tcp_wrapper SELinux boolean, run the following command: +$ sudo setsebool -P daemons_use_tcp_wrapper off + + - name: XCCDF Value var_daemons_use_tcp_wrapper # promote to variable + set_fact: + var_daemons_use_tcp_wrapper: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_daemons_use_tcp_wrapper + +- name: Set SELinux boolean daemons_use_tcp_wrapper accordingly + seboolean: + name: daemons_use_tcp_wrapper + state: '{{ var_daemons_use_tcp_wrapper }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_daemons_use_tcp_wrapper + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_daemons_use_tcp_wrapper='' + + +setsebool -P daemons_use_tcp_wrapper $var_daemons_use_tcp_wrapper + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the daemons_use_tty SELinux Boolean + By default, the SELinux boolean daemons_use_tty is disabled. +If this setting is enabled, it should be disabled. + +To disable the daemons_use_tty SELinux boolean, run the following command: +$ sudo setsebool -P daemons_use_tty off + + - name: XCCDF Value var_daemons_use_tty # promote to variable + set_fact: + var_daemons_use_tty: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_daemons_use_tty + +- name: Set SELinux boolean daemons_use_tty accordingly + seboolean: + name: daemons_use_tty + state: '{{ var_daemons_use_tty }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_daemons_use_tty + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_daemons_use_tty='' + + +setsebool -P daemons_use_tty $var_daemons_use_tty + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable the dbadm_exec_content SELinux Boolean + By default, the SELinux boolean dbadm_exec_content is enabled. +If this setting is disabled, it should be enabled. + +To enable the dbadm_exec_content SELinux boolean, run the following command: +$ sudo setsebool -P dbadm_exec_content on + + - name: XCCDF Value var_dbadm_exec_content # promote to variable + set_fact: + var_dbadm_exec_content: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_dbadm_exec_content + +- name: Set SELinux boolean dbadm_exec_content accordingly + seboolean: + name: dbadm_exec_content + state: '{{ var_dbadm_exec_content }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_dbadm_exec_content + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_dbadm_exec_content='' + + +setsebool -P dbadm_exec_content $var_dbadm_exec_content + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the dbadm_manage_user_files SELinux Boolean + By default, the SELinux boolean dbadm_manage_user_files is disabled. +If this setting is enabled, it should be disabled. + +To disable the dbadm_manage_user_files SELinux boolean, run the following command: +$ sudo setsebool -P dbadm_manage_user_files off + + - name: XCCDF Value var_dbadm_manage_user_files # promote to variable + set_fact: + var_dbadm_manage_user_files: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_dbadm_manage_user_files + +- name: Set SELinux boolean dbadm_manage_user_files accordingly + seboolean: + name: dbadm_manage_user_files + state: '{{ var_dbadm_manage_user_files }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_dbadm_manage_user_files + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_dbadm_manage_user_files='' + + +setsebool -P dbadm_manage_user_files $var_dbadm_manage_user_files + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the dbadm_read_user_files SELinux Boolean + By default, the SELinux boolean dbadm_read_user_files is disabled. +If this setting is enabled, it should be disabled. + +To disable the dbadm_read_user_files SELinux boolean, run the following command: +$ sudo setsebool -P dbadm_read_user_files off + + - name: XCCDF Value var_dbadm_read_user_files # promote to variable + set_fact: + var_dbadm_read_user_files: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_dbadm_read_user_files + +- name: Set SELinux boolean dbadm_read_user_files accordingly + seboolean: + name: dbadm_read_user_files + state: '{{ var_dbadm_read_user_files }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_dbadm_read_user_files + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_dbadm_read_user_files='' + + +setsebool -P dbadm_read_user_files $var_dbadm_read_user_files + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Configure the deny_execmem SELinux Boolean + By default, the SELinux boolean deny_execmem is disabled. +This setting should be configured to . + +To set the deny_execmem SELinux boolean, run the following command: +$ sudo setsebool -P deny_execmem + This rule doesn't come with a remediation, as enabling this SELinux boolean can cause +applications to malfunction, for example Graphical login managers and Firefox. + Proper function and stability should be assessed before applying enabling the SELinux +boolean in production systems. + BP28(R67) + Allowing user domain applications to map a memory region as both writable and +executable makes them more susceptible to data execution attacks. + CCE-83307-9 + + + + + + + + + + Disable the deny_ptrace SELinux Boolean + By default, the SELinux boolean deny_ptrace is disabled. +If this setting is enabled, it should be disabled. + +To disable the deny_ptrace SELinux boolean, run the following command: +$ sudo setsebool -P deny_ptrace off + + - name: XCCDF Value var_deny_ptrace # promote to variable + set_fact: + var_deny_ptrace: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_deny_ptrace + +- name: Set SELinux boolean deny_ptrace accordingly + seboolean: + name: deny_ptrace + state: '{{ var_deny_ptrace }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_deny_ptrace + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_deny_ptrace='' + + +setsebool -P deny_ptrace $var_deny_ptrace + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the dhcpc_exec_iptables SELinux Boolean + By default, the SELinux boolean dhcpc_exec_iptables is disabled. +If this setting is enabled, it should be disabled. + +To disable the dhcpc_exec_iptables SELinux boolean, run the following command: +$ sudo setsebool -P dhcpc_exec_iptables off + + - name: XCCDF Value var_dhcpc_exec_iptables # promote to variable + set_fact: + var_dhcpc_exec_iptables: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_dhcpc_exec_iptables + +- name: Set SELinux boolean dhcpc_exec_iptables accordingly + seboolean: + name: dhcpc_exec_iptables + state: '{{ var_dhcpc_exec_iptables }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_dhcpc_exec_iptables + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_dhcpc_exec_iptables='' + + +setsebool -P dhcpc_exec_iptables $var_dhcpc_exec_iptables + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the dhcpd_use_ldap SELinux Boolean + By default, the SELinux boolean dhcpd_use_ldap is disabled. +If this setting is enabled, it should be disabled. + +To disable the dhcpd_use_ldap SELinux boolean, run the following command: +$ sudo setsebool -P dhcpd_use_ldap off + + - name: XCCDF Value var_dhcpd_use_ldap # promote to variable + set_fact: + var_dhcpd_use_ldap: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_dhcpd_use_ldap + +- name: Set SELinux boolean dhcpd_use_ldap accordingly + seboolean: + name: dhcpd_use_ldap + state: '{{ var_dhcpd_use_ldap }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_dhcpd_use_ldap + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_dhcpd_use_ldap='' + + +setsebool -P dhcpd_use_ldap $var_dhcpd_use_ldap + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable the domain_fd_use SELinux Boolean + By default, the SELinux boolean domain_fd_use is enabled. +If this setting is disabled, it should be enabled. + +To enable the domain_fd_use SELinux boolean, run the following command: +$ sudo setsebool -P domain_fd_use on + + - name: XCCDF Value var_domain_fd_use # promote to variable + set_fact: + var_domain_fd_use: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_domain_fd_use + +- name: Set SELinux boolean domain_fd_use accordingly + seboolean: + name: domain_fd_use + state: '{{ var_domain_fd_use }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_domain_fd_use + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_domain_fd_use='' + + +setsebool -P domain_fd_use $var_domain_fd_use + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the domain_kernel_load_modules SELinux Boolean + By default, the SELinux boolean domain_kernel_load_modules is disabled. +If this setting is enabled, it should be disabled. + +To disable the domain_kernel_load_modules SELinux boolean, run the following command: +$ sudo setsebool -P domain_kernel_load_modules off + + - name: XCCDF Value var_domain_kernel_load_modules # promote to variable + set_fact: + var_domain_kernel_load_modules: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_domain_kernel_load_modules + +- name: Set SELinux boolean domain_kernel_load_modules accordingly + seboolean: + name: domain_kernel_load_modules + state: '{{ var_domain_kernel_load_modules }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_domain_kernel_load_modules + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_domain_kernel_load_modules='' + + +setsebool -P domain_kernel_load_modules $var_domain_kernel_load_modules + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the entropyd_use_audio SELinux Boolean + By default, the SELinux boolean entropyd_use_audio is enabled. +This setting should be disabled as it uses audit input to generate entropy. + +To disable the entropyd_use_audio SELinux boolean, run the following command: +$ sudo setsebool -P entropyd_use_audio off + + - name: XCCDF Value var_entropyd_use_audio # promote to variable + set_fact: + var_entropyd_use_audio: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_entropyd_use_audio + +- name: Set SELinux boolean entropyd_use_audio accordingly + seboolean: + name: entropyd_use_audio + state: '{{ var_entropyd_use_audio }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_entropyd_use_audio + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_entropyd_use_audio='' + + +setsebool -P entropyd_use_audio $var_entropyd_use_audio + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the exim_can_connect_db SELinux Boolean + By default, the SELinux boolean exim_can_connect_db is disabled. +If this setting is enabled, it should be disabled. + +To disable the exim_can_connect_db SELinux boolean, run the following command: +$ sudo setsebool -P exim_can_connect_db off + + - name: XCCDF Value var_exim_can_connect_db # promote to variable + set_fact: + var_exim_can_connect_db: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_exim_can_connect_db + +- name: Set SELinux boolean exim_can_connect_db accordingly + seboolean: + name: exim_can_connect_db + state: '{{ var_exim_can_connect_db }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_exim_can_connect_db + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_exim_can_connect_db='' + + +setsebool -P exim_can_connect_db $var_exim_can_connect_db + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the exim_manage_user_files SELinux Boolean + By default, the SELinux boolean exim_manage_user_files is disabled. +If this setting is enabled, it should be disabled. + +To disable the exim_manage_user_files SELinux boolean, run the following command: +$ sudo setsebool -P exim_manage_user_files off + + - name: XCCDF Value var_exim_manage_user_files # promote to variable + set_fact: + var_exim_manage_user_files: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_exim_manage_user_files + +- name: Set SELinux boolean exim_manage_user_files accordingly + seboolean: + name: exim_manage_user_files + state: '{{ var_exim_manage_user_files }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_exim_manage_user_files + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_exim_manage_user_files='' + + +setsebool -P exim_manage_user_files $var_exim_manage_user_files + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the exim_read_user_files SELinux Boolean + By default, the SELinux boolean exim_read_user_files is disabled. +If this setting is enabled, it should be disabled. + +To disable the exim_read_user_files SELinux boolean, run the following command: +$ sudo setsebool -P exim_read_user_files off + + - name: XCCDF Value var_exim_read_user_files # promote to variable + set_fact: + var_exim_read_user_files: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_exim_read_user_files + +- name: Set SELinux boolean exim_read_user_files accordingly + seboolean: + name: exim_read_user_files + state: '{{ var_exim_read_user_files }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_exim_read_user_files + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_exim_read_user_files='' + + +setsebool -P exim_read_user_files $var_exim_read_user_files + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the fcron_crond SELinux Boolean + By default, the SELinux boolean fcron_crond is disabled. +If this setting is enabled, it should be disabled. + +To disable the fcron_crond SELinux boolean, run the following command: +$ sudo setsebool -P fcron_crond off + + - name: XCCDF Value var_fcron_crond # promote to variable + set_fact: + var_fcron_crond: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_fcron_crond + +- name: Set SELinux boolean fcron_crond accordingly + seboolean: + name: fcron_crond + state: '{{ var_fcron_crond }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_fcron_crond + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_fcron_crond='' + + +setsebool -P fcron_crond $var_fcron_crond + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the fenced_can_network_connect SELinux Boolean + By default, the SELinux boolean fenced_can_network_connect is disabled. +If this setting is enabled, it should be disabled. + +To disable the fenced_can_network_connect SELinux boolean, run the following command: +$ sudo setsebool -P fenced_can_network_connect off + + - name: XCCDF Value var_fenced_can_network_connect # promote to variable + set_fact: + var_fenced_can_network_connect: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_fenced_can_network_connect + +- name: Set SELinux boolean fenced_can_network_connect accordingly + seboolean: + name: fenced_can_network_connect + state: '{{ var_fenced_can_network_connect }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_fenced_can_network_connect + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_fenced_can_network_connect='' + + +setsebool -P fenced_can_network_connect $var_fenced_can_network_connect + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the fenced_can_ssh SELinux Boolean + By default, the SELinux boolean fenced_can_ssh is disabled. +If this setting is enabled, it should be disabled. + +To disable the fenced_can_ssh SELinux boolean, run the following command: +$ sudo setsebool -P fenced_can_ssh off + + - name: XCCDF Value var_fenced_can_ssh # promote to variable + set_fact: + var_fenced_can_ssh: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_fenced_can_ssh + +- name: Set SELinux boolean fenced_can_ssh accordingly + seboolean: + name: fenced_can_ssh + state: '{{ var_fenced_can_ssh }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_fenced_can_ssh + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_fenced_can_ssh='' + + +setsebool -P fenced_can_ssh $var_fenced_can_ssh + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable the fips_mode SELinux Boolean + By default, the SELinux boolean fips_mode is enabled. +This allows all SELinux domains to execute in fips_mode. +If this setting is disabled, it should be enabled. + +To enable the fips_mode SELinux boolean, run the following command: +$ sudo setsebool -P fips_mode on + 13 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + 3.13.11 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + SC-12(2) + SC-12(3) + IA-7 + SC-13 + CM-6(a) + SC-12 + PR.DS-5 + + - name: XCCDF Value var_fips_mode # promote to variable + set_fact: + var_fips_mode: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-171-3.13.11 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-7 + - NIST-800-53-SC-12 + - NIST-800-53-SC-12(2) + - NIST-800-53-SC-12(3) + - NIST-800-53-SC-13 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_fips_mode + +- name: Set SELinux boolean fips_mode accordingly + seboolean: + name: fips_mode + state: '{{ var_fips_mode }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-171-3.13.11 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-7 + - NIST-800-53-SC-12 + - NIST-800-53-SC-12(2) + - NIST-800-53-SC-12(3) + - NIST-800-53-SC-13 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_fips_mode + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_fips_mode='' + + +setsebool -P fips_mode $var_fips_mode + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the ftpd_anon_write SELinux Boolean + By default, the SELinux boolean ftpd_anon_write is disabled. +If this setting is enabled, it should be disabled. + +To disable the ftpd_anon_write SELinux boolean, run the following command: +$ sudo setsebool -P ftpd_anon_write off + + - name: XCCDF Value var_ftpd_anon_write # promote to variable + set_fact: + var_ftpd_anon_write: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_ftpd_anon_write + +- name: Set SELinux boolean ftpd_anon_write accordingly + seboolean: + name: ftpd_anon_write + state: '{{ var_ftpd_anon_write }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_ftpd_anon_write + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_ftpd_anon_write='' + + +setsebool -P ftpd_anon_write $var_ftpd_anon_write + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the ftpd_connect_all_unreserved SELinux Boolean + By default, the SELinux boolean ftpd_connect_all_unreserved is disabled. +If this setting is enabled, it should be disabled. + +To disable the ftpd_connect_all_unreserved SELinux boolean, run the following command: +$ sudo setsebool -P ftpd_connect_all_unreserved off + + - name: XCCDF Value var_ftpd_connect_all_unreserved # promote to variable + set_fact: + var_ftpd_connect_all_unreserved: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_ftpd_connect_all_unreserved + +- name: Set SELinux boolean ftpd_connect_all_unreserved accordingly + seboolean: + name: ftpd_connect_all_unreserved + state: '{{ var_ftpd_connect_all_unreserved }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_ftpd_connect_all_unreserved + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_ftpd_connect_all_unreserved='' + + +setsebool -P ftpd_connect_all_unreserved $var_ftpd_connect_all_unreserved + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the ftpd_connect_db SELinux Boolean + By default, the SELinux boolean ftpd_connect_db is disabled. +If this setting is enabled, it should be disabled. + +To disable the ftpd_connect_db SELinux boolean, run the following command: +$ sudo setsebool -P ftpd_connect_db off + + - name: XCCDF Value var_ftpd_connect_db # promote to variable + set_fact: + var_ftpd_connect_db: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_ftpd_connect_db + +- name: Set SELinux boolean ftpd_connect_db accordingly + seboolean: + name: ftpd_connect_db + state: '{{ var_ftpd_connect_db }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_ftpd_connect_db + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_ftpd_connect_db='' + + +setsebool -P ftpd_connect_db $var_ftpd_connect_db + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the ftpd_full_access SELinux Boolean + By default, the SELinux boolean ftpd_full_access is disabled. +If this setting is enabled, it should be disabled. + +To disable the ftpd_full_access SELinux boolean, run the following command: +$ sudo setsebool -P ftpd_full_access off + + - name: XCCDF Value var_ftpd_full_access # promote to variable + set_fact: + var_ftpd_full_access: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_ftpd_full_access + +- name: Set SELinux boolean ftpd_full_access accordingly + seboolean: + name: ftpd_full_access + state: '{{ var_ftpd_full_access }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_ftpd_full_access + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_ftpd_full_access='' + + +setsebool -P ftpd_full_access $var_ftpd_full_access + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the ftpd_use_cifs SELinux Boolean + By default, the SELinux boolean ftpd_use_cifs is disabled. +If this setting is enabled, it should be disabled. + +To disable the ftpd_use_cifs SELinux boolean, run the following command: +$ sudo setsebool -P ftpd_use_cifs off + + - name: XCCDF Value var_ftpd_use_cifs # promote to variable + set_fact: + var_ftpd_use_cifs: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_ftpd_use_cifs + +- name: Set SELinux boolean ftpd_use_cifs accordingly + seboolean: + name: ftpd_use_cifs + state: '{{ var_ftpd_use_cifs }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_ftpd_use_cifs + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_ftpd_use_cifs='' + + +setsebool -P ftpd_use_cifs $var_ftpd_use_cifs + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the ftpd_use_fusefs SELinux Boolean + By default, the SELinux boolean ftpd_use_fusefs is disabled. +If this setting is enabled, it should be disabled. + +To disable the ftpd_use_fusefs SELinux boolean, run the following command: +$ sudo setsebool -P ftpd_use_fusefs off + + - name: XCCDF Value var_ftpd_use_fusefs # promote to variable + set_fact: + var_ftpd_use_fusefs: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_ftpd_use_fusefs + +- name: Set SELinux boolean ftpd_use_fusefs accordingly + seboolean: + name: ftpd_use_fusefs + state: '{{ var_ftpd_use_fusefs }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_ftpd_use_fusefs + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_ftpd_use_fusefs='' + + +setsebool -P ftpd_use_fusefs $var_ftpd_use_fusefs + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the ftpd_use_nfs SELinux Boolean + By default, the SELinux boolean ftpd_use_nfs is disabled. +If this setting is enabled, it should be disabled. + +To disable the ftpd_use_nfs SELinux boolean, run the following command: +$ sudo setsebool -P ftpd_use_nfs off + + - name: XCCDF Value var_ftpd_use_nfs # promote to variable + set_fact: + var_ftpd_use_nfs: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_ftpd_use_nfs + +- name: Set SELinux boolean ftpd_use_nfs accordingly + seboolean: + name: ftpd_use_nfs + state: '{{ var_ftpd_use_nfs }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_ftpd_use_nfs + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_ftpd_use_nfs='' + + +setsebool -P ftpd_use_nfs $var_ftpd_use_nfs + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the ftpd_use_passive_mode SELinux Boolean + By default, the SELinux boolean ftpd_use_passive_mode is disabled. +If this setting is enabled, it should be disabled. + +To disable the ftpd_use_passive_mode SELinux boolean, run the following command: +$ sudo setsebool -P ftpd_use_passive_mode off + + - name: XCCDF Value var_ftpd_use_passive_mode # promote to variable + set_fact: + var_ftpd_use_passive_mode: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_ftpd_use_passive_mode + +- name: Set SELinux boolean ftpd_use_passive_mode accordingly + seboolean: + name: ftpd_use_passive_mode + state: '{{ var_ftpd_use_passive_mode }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_ftpd_use_passive_mode + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_ftpd_use_passive_mode='' + + +setsebool -P ftpd_use_passive_mode $var_ftpd_use_passive_mode + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the git_cgi_enable_homedirs SELinux Boolean + By default, the SELinux boolean git_cgi_enable_homedirs is disabled. +If this setting is enabled, it should be disabled. + +To disable the git_cgi_enable_homedirs SELinux boolean, run the following command: +$ sudo setsebool -P git_cgi_enable_homedirs off + + - name: XCCDF Value var_git_cgi_enable_homedirs # promote to variable + set_fact: + var_git_cgi_enable_homedirs: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_git_cgi_enable_homedirs + +- name: Set SELinux boolean git_cgi_enable_homedirs accordingly + seboolean: + name: git_cgi_enable_homedirs + state: '{{ var_git_cgi_enable_homedirs }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_git_cgi_enable_homedirs + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_git_cgi_enable_homedirs='' + + +setsebool -P git_cgi_enable_homedirs $var_git_cgi_enable_homedirs + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the git_cgi_use_cifs SELinux Boolean + By default, the SELinux boolean git_cgi_use_cifs is disabled. +If this setting is enabled, it should be disabled. + +To disable the git_cgi_use_cifs SELinux boolean, run the following command: +$ sudo setsebool -P git_cgi_use_cifs off + + - name: XCCDF Value var_git_cgi_use_cifs # promote to variable + set_fact: + var_git_cgi_use_cifs: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_git_cgi_use_cifs + +- name: Set SELinux boolean git_cgi_use_cifs accordingly + seboolean: + name: git_cgi_use_cifs + state: '{{ var_git_cgi_use_cifs }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_git_cgi_use_cifs + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_git_cgi_use_cifs='' + + +setsebool -P git_cgi_use_cifs $var_git_cgi_use_cifs + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the git_cgi_use_nfs SELinux Boolean + By default, the SELinux boolean git_cgi_use_nfs is disabled. +If this setting is enabled, it should be disabled. + +To disable the git_cgi_use_nfs SELinux boolean, run the following command: +$ sudo setsebool -P git_cgi_use_nfs off + + - name: XCCDF Value var_git_cgi_use_nfs # promote to variable + set_fact: + var_git_cgi_use_nfs: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_git_cgi_use_nfs + +- name: Set SELinux boolean git_cgi_use_nfs accordingly + seboolean: + name: git_cgi_use_nfs + state: '{{ var_git_cgi_use_nfs }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_git_cgi_use_nfs + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_git_cgi_use_nfs='' + + +setsebool -P git_cgi_use_nfs $var_git_cgi_use_nfs + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the git_session_bind_all_unreserved_ports SELinux Boolean + By default, the SELinux boolean git_session_bind_all_unreserved_ports is disabled. +If this setting is enabled, it should be disabled. + +To disable the git_session_bind_all_unreserved_ports SELinux boolean, run the following command: +$ sudo setsebool -P git_session_bind_all_unreserved_ports off + + - name: XCCDF Value var_git_session_bind_all_unreserved_ports # promote to variable + set_fact: + var_git_session_bind_all_unreserved_ports: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_git_session_bind_all_unreserved_ports + +- name: Set SELinux boolean git_session_bind_all_unreserved_ports accordingly + seboolean: + name: git_session_bind_all_unreserved_ports + state: '{{ var_git_session_bind_all_unreserved_ports }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_git_session_bind_all_unreserved_ports + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_git_session_bind_all_unreserved_ports='' + + +setsebool -P git_session_bind_all_unreserved_ports $var_git_session_bind_all_unreserved_ports + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the git_session_users SELinux Boolean + By default, the SELinux boolean git_session_users is disabled. +If this setting is enabled, it should be disabled. + +To disable the git_session_users SELinux boolean, run the following command: +$ sudo setsebool -P git_session_users off + + - name: XCCDF Value var_git_session_users # promote to variable + set_fact: + var_git_session_users: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_git_session_users + +- name: Set SELinux boolean git_session_users accordingly + seboolean: + name: git_session_users + state: '{{ var_git_session_users }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_git_session_users + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_git_session_users='' + + +setsebool -P git_session_users $var_git_session_users + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the git_system_enable_homedirs SELinux Boolean + By default, the SELinux boolean git_system_enable_homedirs is disabled. +If this setting is enabled, it should be disabled. + +To disable the git_system_enable_homedirs SELinux boolean, run the following command: +$ sudo setsebool -P git_system_enable_homedirs off + + - name: XCCDF Value var_git_system_enable_homedirs # promote to variable + set_fact: + var_git_system_enable_homedirs: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_git_system_enable_homedirs + +- name: Set SELinux boolean git_system_enable_homedirs accordingly + seboolean: + name: git_system_enable_homedirs + state: '{{ var_git_system_enable_homedirs }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_git_system_enable_homedirs + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_git_system_enable_homedirs='' + + +setsebool -P git_system_enable_homedirs $var_git_system_enable_homedirs + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the git_system_use_cifs SELinux Boolean + By default, the SELinux boolean git_system_use_cifs is disabled. +If this setting is enabled, it should be disabled. + +To disable the git_system_use_cifs SELinux boolean, run the following command: +$ sudo setsebool -P git_system_use_cifs off + + - name: XCCDF Value var_git_system_use_cifs # promote to variable + set_fact: + var_git_system_use_cifs: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_git_system_use_cifs + +- name: Set SELinux boolean git_system_use_cifs accordingly + seboolean: + name: git_system_use_cifs + state: '{{ var_git_system_use_cifs }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_git_system_use_cifs + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_git_system_use_cifs='' + + +setsebool -P git_system_use_cifs $var_git_system_use_cifs + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the git_system_use_nfs SELinux Boolean + By default, the SELinux boolean git_system_use_nfs is disabled. +If this setting is enabled, it should be disabled. + +To disable the git_system_use_nfs SELinux boolean, run the following command: +$ sudo setsebool -P git_system_use_nfs off + + - name: XCCDF Value var_git_system_use_nfs # promote to variable + set_fact: + var_git_system_use_nfs: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_git_system_use_nfs + +- name: Set SELinux boolean git_system_use_nfs accordingly + seboolean: + name: git_system_use_nfs + state: '{{ var_git_system_use_nfs }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_git_system_use_nfs + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_git_system_use_nfs='' + + +setsebool -P git_system_use_nfs $var_git_system_use_nfs + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the gitosis_can_sendmail SELinux Boolean + By default, the SELinux boolean gitosis_can_sendmail is disabled. +If this setting is enabled, it should be disabled. + +To disable the gitosis_can_sendmail SELinux boolean, run the following command: +$ sudo setsebool -P gitosis_can_sendmail off + + - name: XCCDF Value var_gitosis_can_sendmail # promote to variable + set_fact: + var_gitosis_can_sendmail: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_gitosis_can_sendmail + +- name: Set SELinux boolean gitosis_can_sendmail accordingly + seboolean: + name: gitosis_can_sendmail + state: '{{ var_gitosis_can_sendmail }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_gitosis_can_sendmail + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_gitosis_can_sendmail='' + + +setsebool -P gitosis_can_sendmail $var_gitosis_can_sendmail + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the glance_api_can_network SELinux Boolean + By default, the SELinux boolean glance_api_can_network is disabled. +If this setting is enabled, it should be disabled. + +To disable the glance_api_can_network SELinux boolean, run the following command: +$ sudo setsebool -P glance_api_can_network off + + - name: XCCDF Value var_glance_api_can_network # promote to variable + set_fact: + var_glance_api_can_network: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_glance_api_can_network + +- name: Set SELinux boolean glance_api_can_network accordingly + seboolean: + name: glance_api_can_network + state: '{{ var_glance_api_can_network }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_glance_api_can_network + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_glance_api_can_network='' + + +setsebool -P glance_api_can_network $var_glance_api_can_network + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the glance_use_execmem SELinux Boolean + By default, the SELinux boolean glance_use_execmem is disabled. +If this setting is enabled, it should be disabled. + +To disable the glance_use_execmem SELinux boolean, run the following command: +$ sudo setsebool -P glance_use_execmem off + BP28(R67) + + CCE-83308-7 + - name: XCCDF Value var_glance_use_execmem # promote to variable + set_fact: + var_glance_use_execmem: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83308-7 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_glance_use_execmem + +- name: Set SELinux boolean glance_use_execmem accordingly + seboolean: + name: glance_use_execmem + state: '{{ var_glance_use_execmem }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83308-7 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_glance_use_execmem + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_glance_use_execmem='' + + +setsebool -P glance_use_execmem $var_glance_use_execmem + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the glance_use_fusefs SELinux Boolean + By default, the SELinux boolean glance_use_fusefs is disabled. +If this setting is enabled, it should be disabled. + +To disable the glance_use_fusefs SELinux boolean, run the following command: +$ sudo setsebool -P glance_use_fusefs off + + - name: XCCDF Value var_glance_use_fusefs # promote to variable + set_fact: + var_glance_use_fusefs: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_glance_use_fusefs + +- name: Set SELinux boolean glance_use_fusefs accordingly + seboolean: + name: glance_use_fusefs + state: '{{ var_glance_use_fusefs }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_glance_use_fusefs + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_glance_use_fusefs='' + + +setsebool -P glance_use_fusefs $var_glance_use_fusefs + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the global_ssp SELinux Boolean + By default, the SELinux boolean global_ssp is disabled. +If this setting is enabled, it should be disabled. + +To disable the global_ssp SELinux boolean, run the following command: +$ sudo setsebool -P global_ssp off + + - name: XCCDF Value var_global_ssp # promote to variable + set_fact: + var_global_ssp: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_global_ssp + +- name: Set SELinux boolean global_ssp accordingly + seboolean: + name: global_ssp + state: '{{ var_global_ssp }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_global_ssp + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_global_ssp='' + + +setsebool -P global_ssp $var_global_ssp + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the gluster_anon_write SELinux Boolean + By default, the SELinux boolean gluster_anon_write is disabled. +If this setting is enabled, it should be disabled. + +To disable the gluster_anon_write SELinux boolean, run the following command: +$ sudo setsebool -P gluster_anon_write off + + - name: XCCDF Value var_gluster_anon_write # promote to variable + set_fact: + var_gluster_anon_write: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_gluster_anon_write + +- name: Set SELinux boolean gluster_anon_write accordingly + seboolean: + name: gluster_anon_write + state: '{{ var_gluster_anon_write }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_gluster_anon_write + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_gluster_anon_write='' + + +setsebool -P gluster_anon_write $var_gluster_anon_write + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the gluster_export_all_ro SELinux Boolean + By default, the SELinux boolean gluster_export_all_ro is disabled. +If this setting is enabled, it should be disabled. + +To disable the gluster_export_all_ro SELinux boolean, run the following command: +$ sudo setsebool -P gluster_export_all_ro off + + - name: XCCDF Value var_gluster_export_all_ro # promote to variable + set_fact: + var_gluster_export_all_ro: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_gluster_export_all_ro + +- name: Set SELinux boolean gluster_export_all_ro accordingly + seboolean: + name: gluster_export_all_ro + state: '{{ var_gluster_export_all_ro }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_gluster_export_all_ro + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_gluster_export_all_ro='' + + +setsebool -P gluster_export_all_ro $var_gluster_export_all_ro + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Configure the gluster_export_all_rw SELinux Boolean + By default, the SELinux boolean gluster_export_all_rw is enabled. +If GlusterFS is in use, this setting should be enabled. Otherwise, +disable it. + +To disable the gluster_export_all_rw SELinux boolean, run the following command: +$ sudo setsebool -P gluster_export_all_rw off + + - name: XCCDF Value var_gluster_export_all_rw # promote to variable + set_fact: + var_gluster_export_all_rw: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_gluster_export_all_rw + +- name: Set SELinux boolean gluster_export_all_rw accordingly + seboolean: + name: gluster_export_all_rw + state: '{{ var_gluster_export_all_rw }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_gluster_export_all_rw + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_gluster_export_all_rw='' + + +setsebool -P gluster_export_all_rw $var_gluster_export_all_rw + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the gpg_web_anon_write SELinux Boolean + By default, the SELinux boolean gpg_web_anon_write is disabled. +If this setting is enabled, it should be disabled. + +To disable the gpg_web_anon_write SELinux boolean, run the following command: +$ sudo setsebool -P gpg_web_anon_write off + + - name: XCCDF Value var_gpg_web_anon_write # promote to variable + set_fact: + var_gpg_web_anon_write: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_gpg_web_anon_write + +- name: Set SELinux boolean gpg_web_anon_write accordingly + seboolean: + name: gpg_web_anon_write + state: '{{ var_gpg_web_anon_write }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_gpg_web_anon_write + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_gpg_web_anon_write='' + + +setsebool -P gpg_web_anon_write $var_gpg_web_anon_write + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable the gssd_read_tmp SELinux Boolean + By default, the SELinux boolean gssd_read_tmp is enabled. +This setting allows gssd processes to access Kerberos to read +TGTs in the temp directory. If this setting is disabled, it should +be enabled. + +To enable the gssd_read_tmp SELinux boolean, run the following command: +$ sudo setsebool -P gssd_read_tmp on + + - name: XCCDF Value var_gssd_read_tmp # promote to variable + set_fact: + var_gssd_read_tmp: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_gssd_read_tmp + +- name: Set SELinux boolean gssd_read_tmp accordingly + seboolean: + name: gssd_read_tmp + state: '{{ var_gssd_read_tmp }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_gssd_read_tmp + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_gssd_read_tmp='' + + +setsebool -P gssd_read_tmp $var_gssd_read_tmp + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the guest_exec_content SELinux Boolean + By default, the SELinux boolean guest_exec_content is enabled. +This setting should be disabled as no guest accounts should be used. + +To disable the guest_exec_content SELinux boolean, run the following command: +$ sudo setsebool -P guest_exec_content off + + - name: XCCDF Value var_guest_exec_content # promote to variable + set_fact: + var_guest_exec_content: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_guest_exec_content + +- name: Set SELinux boolean guest_exec_content accordingly + seboolean: + name: guest_exec_content + state: '{{ var_guest_exec_content }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_guest_exec_content + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_guest_exec_content='' + + +setsebool -P guest_exec_content $var_guest_exec_content + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the haproxy_connect_any SELinux Boolean + By default, the SELinux boolean haproxy_connect_any is disabled. +If this setting is enabled, it should be disabled. + +To disable the haproxy_connect_any SELinux boolean, run the following command: +$ sudo setsebool -P haproxy_connect_any off + + - name: XCCDF Value var_haproxy_connect_any # promote to variable + set_fact: + var_haproxy_connect_any: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_haproxy_connect_any + +- name: Set SELinux boolean haproxy_connect_any accordingly + seboolean: + name: haproxy_connect_any + state: '{{ var_haproxy_connect_any }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_haproxy_connect_any + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_haproxy_connect_any='' + + +setsebool -P haproxy_connect_any $var_haproxy_connect_any + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the httpd_anon_write SELinux Boolean + By default, the SELinux boolean httpd_anon_write is disabled. +If this setting is enabled, it should be disabled. + +To disable the httpd_anon_write SELinux boolean, run the following command: +$ sudo setsebool -P httpd_anon_write off + + - name: XCCDF Value var_httpd_anon_write # promote to variable + set_fact: + var_httpd_anon_write: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_anon_write + +- name: Set SELinux boolean httpd_anon_write accordingly + seboolean: + name: httpd_anon_write + state: '{{ var_httpd_anon_write }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_anon_write + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_httpd_anon_write='' + + +setsebool -P httpd_anon_write $var_httpd_anon_write + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Configure the httpd_builtin_scripting SELinux Boolean + By default, the SELinux boolean httpd_builtin_scripting is enabled. +This setting should be disabled if httpd is not running php +or some similary scripting language. + +To disable the httpd_builtin_scripting SELinux boolean, run the following command: +$ sudo setsebool -P httpd_builtin_scripting off + + - name: XCCDF Value var_httpd_builtin_scripting # promote to variable + set_fact: + var_httpd_builtin_scripting: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_builtin_scripting + +- name: Set SELinux boolean httpd_builtin_scripting accordingly + seboolean: + name: httpd_builtin_scripting + state: '{{ var_httpd_builtin_scripting }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_builtin_scripting + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_httpd_builtin_scripting='' + + +setsebool -P httpd_builtin_scripting $var_httpd_builtin_scripting + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the httpd_can_check_spam SELinux Boolean + By default, the SELinux boolean httpd_can_check_spam is disabled. +If this setting is enabled, it should be disabled. + +To disable the httpd_can_check_spam SELinux boolean, run the following command: +$ sudo setsebool -P httpd_can_check_spam off + + - name: XCCDF Value var_httpd_can_check_spam # promote to variable + set_fact: + var_httpd_can_check_spam: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_can_check_spam + +- name: Set SELinux boolean httpd_can_check_spam accordingly + seboolean: + name: httpd_can_check_spam + state: '{{ var_httpd_can_check_spam }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_can_check_spam + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_httpd_can_check_spam='' + + +setsebool -P httpd_can_check_spam $var_httpd_can_check_spam + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the httpd_can_connect_ftp SELinux Boolean + By default, the SELinux boolean httpd_can_connect_ftp is disabled. +If this setting is enabled, it should be disabled. + +To disable the httpd_can_connect_ftp SELinux boolean, run the following command: +$ sudo setsebool -P httpd_can_connect_ftp off + + - name: XCCDF Value var_httpd_can_connect_ftp # promote to variable + set_fact: + var_httpd_can_connect_ftp: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_can_connect_ftp + +- name: Set SELinux boolean httpd_can_connect_ftp accordingly + seboolean: + name: httpd_can_connect_ftp + state: '{{ var_httpd_can_connect_ftp }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_can_connect_ftp + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_httpd_can_connect_ftp='' + + +setsebool -P httpd_can_connect_ftp $var_httpd_can_connect_ftp + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the httpd_can_connect_ldap SELinux Boolean + By default, the SELinux boolean httpd_can_connect_ldap is disabled. +If this setting is enabled, it should be disabled. + +To disable the httpd_can_connect_ldap SELinux boolean, run the following command: +$ sudo setsebool -P httpd_can_connect_ldap off + + - name: XCCDF Value var_httpd_can_connect_ldap # promote to variable + set_fact: + var_httpd_can_connect_ldap: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_can_connect_ldap + +- name: Set SELinux boolean httpd_can_connect_ldap accordingly + seboolean: + name: httpd_can_connect_ldap + state: '{{ var_httpd_can_connect_ldap }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_can_connect_ldap + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_httpd_can_connect_ldap='' + + +setsebool -P httpd_can_connect_ldap $var_httpd_can_connect_ldap + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the httpd_can_connect_mythtv SELinux Boolean + By default, the SELinux boolean httpd_can_connect_mythtv is disabled. +If this setting is enabled, it should be disabled. + +To disable the httpd_can_connect_mythtv SELinux boolean, run the following command: +$ sudo setsebool -P httpd_can_connect_mythtv off + + - name: XCCDF Value var_httpd_can_connect_mythtv # promote to variable + set_fact: + var_httpd_can_connect_mythtv: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_can_connect_mythtv + +- name: Set SELinux boolean httpd_can_connect_mythtv accordingly + seboolean: + name: httpd_can_connect_mythtv + state: '{{ var_httpd_can_connect_mythtv }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_can_connect_mythtv + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_httpd_can_connect_mythtv='' + + +setsebool -P httpd_can_connect_mythtv $var_httpd_can_connect_mythtv + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the httpd_can_connect_zabbix SELinux Boolean + By default, the SELinux boolean httpd_can_connect_zabbix is disabled. +If this setting is enabled, it should be disabled. + +To disable the httpd_can_connect_zabbix SELinux boolean, run the following command: +$ sudo setsebool -P httpd_can_connect_zabbix off + + - name: XCCDF Value var_httpd_can_connect_zabbix # promote to variable + set_fact: + var_httpd_can_connect_zabbix: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_can_connect_zabbix + +- name: Set SELinux boolean httpd_can_connect_zabbix accordingly + seboolean: + name: httpd_can_connect_zabbix + state: '{{ var_httpd_can_connect_zabbix }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_can_connect_zabbix + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_httpd_can_connect_zabbix='' + + +setsebool -P httpd_can_connect_zabbix $var_httpd_can_connect_zabbix + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the httpd_can_network_connect SELinux Boolean + By default, the SELinux boolean httpd_can_network_connect is disabled. +If this setting is enabled, it should be disabled. + +To disable the httpd_can_network_connect SELinux boolean, run the following command: +$ sudo setsebool -P httpd_can_network_connect off + + - name: XCCDF Value var_httpd_can_network_connect # promote to variable + set_fact: + var_httpd_can_network_connect: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_can_network_connect + +- name: Set SELinux boolean httpd_can_network_connect accordingly + seboolean: + name: httpd_can_network_connect + state: '{{ var_httpd_can_network_connect }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_can_network_connect + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_httpd_can_network_connect='' + + +setsebool -P httpd_can_network_connect $var_httpd_can_network_connect + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the httpd_can_network_connect_cobbler SELinux Boolean + By default, the SELinux boolean httpd_can_network_connect_cobbler is disabled. +If this setting is enabled, it should be disabled. + +To disable the httpd_can_network_connect_cobbler SELinux boolean, run the following command: +$ sudo setsebool -P httpd_can_network_connect_cobbler off + + - name: XCCDF Value var_httpd_can_network_connect_cobbler # promote to variable + set_fact: + var_httpd_can_network_connect_cobbler: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_can_network_connect_cobbler + +- name: Set SELinux boolean httpd_can_network_connect_cobbler accordingly + seboolean: + name: httpd_can_network_connect_cobbler + state: '{{ var_httpd_can_network_connect_cobbler }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_can_network_connect_cobbler + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_httpd_can_network_connect_cobbler='' + + +setsebool -P httpd_can_network_connect_cobbler $var_httpd_can_network_connect_cobbler + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the httpd_can_network_connect_db SELinux Boolean + By default, the SELinux boolean httpd_can_network_connect_db is disabled. +If this setting is enabled, it should be disabled. + +To disable the httpd_can_network_connect_db SELinux boolean, run the following command: +$ sudo setsebool -P httpd_can_network_connect_db off + + - name: XCCDF Value var_httpd_can_network_connect_db # promote to variable + set_fact: + var_httpd_can_network_connect_db: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_can_network_connect_db + +- name: Set SELinux boolean httpd_can_network_connect_db accordingly + seboolean: + name: httpd_can_network_connect_db + state: '{{ var_httpd_can_network_connect_db }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_can_network_connect_db + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_httpd_can_network_connect_db='' + + +setsebool -P httpd_can_network_connect_db $var_httpd_can_network_connect_db + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the httpd_can_network_memcache SELinux Boolean + By default, the SELinux boolean httpd_can_network_memcache is disabled. +If this setting is enabled, it should be disabled. + +To disable the httpd_can_network_memcache SELinux boolean, run the following command: +$ sudo setsebool -P httpd_can_network_memcache off + + - name: XCCDF Value var_httpd_can_network_memcache # promote to variable + set_fact: + var_httpd_can_network_memcache: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_can_network_memcache + +- name: Set SELinux boolean httpd_can_network_memcache accordingly + seboolean: + name: httpd_can_network_memcache + state: '{{ var_httpd_can_network_memcache }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_can_network_memcache + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_httpd_can_network_memcache='' + + +setsebool -P httpd_can_network_memcache $var_httpd_can_network_memcache + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the httpd_can_network_relay SELinux Boolean + By default, the SELinux boolean httpd_can_network_relay is disabled. +If this setting is enabled, it should be disabled. + +To disable the httpd_can_network_relay SELinux boolean, run the following command: +$ sudo setsebool -P httpd_can_network_relay off + + - name: XCCDF Value var_httpd_can_network_relay # promote to variable + set_fact: + var_httpd_can_network_relay: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_can_network_relay + +- name: Set SELinux boolean httpd_can_network_relay accordingly + seboolean: + name: httpd_can_network_relay + state: '{{ var_httpd_can_network_relay }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_can_network_relay + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_httpd_can_network_relay='' + + +setsebool -P httpd_can_network_relay $var_httpd_can_network_relay + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the httpd_can_sendmail SELinux Boolean + By default, the SELinux boolean httpd_can_sendmail is disabled. +If this setting is enabled, it should be disabled. + +To disable the httpd_can_sendmail SELinux boolean, run the following command: +$ sudo setsebool -P httpd_can_sendmail off + + - name: XCCDF Value var_httpd_can_sendmail # promote to variable + set_fact: + var_httpd_can_sendmail: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_can_sendmail + +- name: Set SELinux boolean httpd_can_sendmail accordingly + seboolean: + name: httpd_can_sendmail + state: '{{ var_httpd_can_sendmail }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_can_sendmail + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_httpd_can_sendmail='' + + +setsebool -P httpd_can_sendmail $var_httpd_can_sendmail + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the httpd_dbus_avahi SELinux Boolean + By default, the SELinux boolean httpd_dbus_avahi is disabled. +If this setting is enabled, it should be disabled. + +To disable the httpd_dbus_avahi SELinux boolean, run the following command: +$ sudo setsebool -P httpd_dbus_avahi off + + - name: XCCDF Value var_httpd_dbus_avahi # promote to variable + set_fact: + var_httpd_dbus_avahi: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_dbus_avahi + +- name: Set SELinux boolean httpd_dbus_avahi accordingly + seboolean: + name: httpd_dbus_avahi + state: '{{ var_httpd_dbus_avahi }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_dbus_avahi + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_httpd_dbus_avahi='' + + +setsebool -P httpd_dbus_avahi $var_httpd_dbus_avahi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the httpd_dbus_sssd SELinux Boolean + By default, the SELinux boolean httpd_dbus_sssd is disabled. +If this setting is enabled, it should be disabled. + +To disable the httpd_dbus_sssd SELinux boolean, run the following command: +$ sudo setsebool -P httpd_dbus_sssd off + + - name: XCCDF Value var_httpd_dbus_sssd # promote to variable + set_fact: + var_httpd_dbus_sssd: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_dbus_sssd + +- name: Set SELinux boolean httpd_dbus_sssd accordingly + seboolean: + name: httpd_dbus_sssd + state: '{{ var_httpd_dbus_sssd }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_dbus_sssd + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_httpd_dbus_sssd='' + + +setsebool -P httpd_dbus_sssd $var_httpd_dbus_sssd + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the httpd_dontaudit_search_dirs SELinux Boolean + By default, the SELinux boolean httpd_dontaudit_search_dirs is disabled. +If this setting is enabled, it should be disabled. + +To disable the httpd_dontaudit_search_dirs SELinux boolean, run the following command: +$ sudo setsebool -P httpd_dontaudit_search_dirs off + + - name: XCCDF Value var_httpd_dontaudit_search_dirs # promote to variable + set_fact: + var_httpd_dontaudit_search_dirs: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_dontaudit_search_dirs + +- name: Set SELinux boolean httpd_dontaudit_search_dirs accordingly + seboolean: + name: httpd_dontaudit_search_dirs + state: '{{ var_httpd_dontaudit_search_dirs }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_dontaudit_search_dirs + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_httpd_dontaudit_search_dirs='' + + +setsebool -P httpd_dontaudit_search_dirs $var_httpd_dontaudit_search_dirs + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Configure the httpd_enable_cgi SELinux Boolean + By default, the SELinux boolean httpd_enable_cgi is enabled. +This setting should be disabled unless httpd is used with CGI +scripting. + +To disable the httpd_enable_cgi SELinux boolean, run the following command: +$ sudo setsebool -P httpd_enable_cgi off + + - name: XCCDF Value var_httpd_enable_cgi # promote to variable + set_fact: + var_httpd_enable_cgi: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_enable_cgi + +- name: Set SELinux boolean httpd_enable_cgi accordingly + seboolean: + name: httpd_enable_cgi + state: '{{ var_httpd_enable_cgi }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_enable_cgi + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_httpd_enable_cgi='' + + +setsebool -P httpd_enable_cgi $var_httpd_enable_cgi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the httpd_enable_ftp_server SELinux Boolean + By default, the SELinux boolean httpd_enable_ftp_server is disabled. +If this setting is enabled, it should be disabled. + +To disable the httpd_enable_ftp_server SELinux boolean, run the following command: +$ sudo setsebool -P httpd_enable_ftp_server off + + - name: XCCDF Value var_httpd_enable_ftp_server # promote to variable + set_fact: + var_httpd_enable_ftp_server: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_enable_ftp_server + +- name: Set SELinux boolean httpd_enable_ftp_server accordingly + seboolean: + name: httpd_enable_ftp_server + state: '{{ var_httpd_enable_ftp_server }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_enable_ftp_server + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_httpd_enable_ftp_server='' + + +setsebool -P httpd_enable_ftp_server $var_httpd_enable_ftp_server + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the httpd_enable_homedirs SELinux Boolean + By default, the SELinux boolean httpd_enable_homedirs is disabled. +If this setting is enabled, it should be disabled. + +To disable the httpd_enable_homedirs SELinux boolean, run the following command: +$ sudo setsebool -P httpd_enable_homedirs off + + - name: XCCDF Value var_httpd_enable_homedirs # promote to variable + set_fact: + var_httpd_enable_homedirs: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_enable_homedirs + +- name: Set SELinux boolean httpd_enable_homedirs accordingly + seboolean: + name: httpd_enable_homedirs + state: '{{ var_httpd_enable_homedirs }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_enable_homedirs + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_httpd_enable_homedirs='' + + +setsebool -P httpd_enable_homedirs $var_httpd_enable_homedirs + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the httpd_execmem SELinux Boolean + By default, the SELinux boolean httpd_execmem is disabled. +If this setting is enabled, it should be disabled. + +To disable the httpd_execmem SELinux boolean, run the following command: +$ sudo setsebool -P httpd_execmem off + BP28(R67) + + CCE-83309-5 + - name: XCCDF Value var_httpd_execmem # promote to variable + set_fact: + var_httpd_execmem: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83309-5 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_execmem + +- name: Set SELinux boolean httpd_execmem accordingly + seboolean: + name: httpd_execmem + state: '{{ var_httpd_execmem }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83309-5 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_execmem + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_httpd_execmem='' + + +setsebool -P httpd_execmem $var_httpd_execmem + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable the httpd_graceful_shutdown SELinux Boolean + By default, the SELinux boolean httpd_graceful_shutdown is enabled. +If this setting is disabled, it should be enabled. + +To enable the httpd_graceful_shutdown SELinux boolean, run the following command: +$ sudo setsebool -P httpd_graceful_shutdown on + + - name: XCCDF Value var_httpd_graceful_shutdown # promote to variable + set_fact: + var_httpd_graceful_shutdown: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_graceful_shutdown + +- name: Set SELinux boolean httpd_graceful_shutdown accordingly + seboolean: + name: httpd_graceful_shutdown + state: '{{ var_httpd_graceful_shutdown }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_graceful_shutdown + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_httpd_graceful_shutdown='' + + +setsebool -P httpd_graceful_shutdown $var_httpd_graceful_shutdown + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the httpd_manage_ipa SELinux Boolean + By default, the SELinux boolean httpd_manage_ipa is disabled. +If this setting is enabled, it should be disabled. + +To disable the httpd_manage_ipa SELinux boolean, run the following command: +$ sudo setsebool -P httpd_manage_ipa off + + - name: XCCDF Value var_httpd_manage_ipa # promote to variable + set_fact: + var_httpd_manage_ipa: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_manage_ipa + +- name: Set SELinux boolean httpd_manage_ipa accordingly + seboolean: + name: httpd_manage_ipa + state: '{{ var_httpd_manage_ipa }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_manage_ipa + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_httpd_manage_ipa='' + + +setsebool -P httpd_manage_ipa $var_httpd_manage_ipa + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the httpd_mod_auth_ntlm_winbind SELinux Boolean + By default, the SELinux boolean httpd_mod_auth_ntlm_winbind is disabled. +If this setting is enabled, it should be disabled. + +To disable the httpd_mod_auth_ntlm_winbind SELinux boolean, run the following command: +$ sudo setsebool -P httpd_mod_auth_ntlm_winbind off + + - name: XCCDF Value var_httpd_mod_auth_ntlm_winbind # promote to variable + set_fact: + var_httpd_mod_auth_ntlm_winbind: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_mod_auth_ntlm_winbind + +- name: Set SELinux boolean httpd_mod_auth_ntlm_winbind accordingly + seboolean: + name: httpd_mod_auth_ntlm_winbind + state: '{{ var_httpd_mod_auth_ntlm_winbind }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_mod_auth_ntlm_winbind + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_httpd_mod_auth_ntlm_winbind='' + + +setsebool -P httpd_mod_auth_ntlm_winbind $var_httpd_mod_auth_ntlm_winbind + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the httpd_mod_auth_pam SELinux Boolean + By default, the SELinux boolean httpd_mod_auth_pam is disabled. +If this setting is enabled, it should be disabled. + +To disable the httpd_mod_auth_pam SELinux boolean, run the following command: +$ sudo setsebool -P httpd_mod_auth_pam off + + - name: XCCDF Value var_httpd_mod_auth_pam # promote to variable + set_fact: + var_httpd_mod_auth_pam: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_mod_auth_pam + +- name: Set SELinux boolean httpd_mod_auth_pam accordingly + seboolean: + name: httpd_mod_auth_pam + state: '{{ var_httpd_mod_auth_pam }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_mod_auth_pam + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_httpd_mod_auth_pam='' + + +setsebool -P httpd_mod_auth_pam $var_httpd_mod_auth_pam + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the httpd_read_user_content SELinux Boolean + By default, the SELinux boolean httpd_read_user_content is disabled. +If this setting is enabled, it should be disabled. + +To disable the httpd_read_user_content SELinux boolean, run the following command: +$ sudo setsebool -P httpd_read_user_content off + + - name: XCCDF Value var_httpd_read_user_content # promote to variable + set_fact: + var_httpd_read_user_content: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_read_user_content + +- name: Set SELinux boolean httpd_read_user_content accordingly + seboolean: + name: httpd_read_user_content + state: '{{ var_httpd_read_user_content }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_read_user_content + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_httpd_read_user_content='' + + +setsebool -P httpd_read_user_content $var_httpd_read_user_content + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the httpd_run_ipa SELinux Boolean + By default, the SELinux boolean httpd_run_ipa is disabled. +If this setting is enabled, it should be disabled. + +To disable the httpd_run_ipa SELinux boolean, run the following command: +$ sudo setsebool -P httpd_run_ipa off + + - name: XCCDF Value var_httpd_run_ipa # promote to variable + set_fact: + var_httpd_run_ipa: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_run_ipa + +- name: Set SELinux boolean httpd_run_ipa accordingly + seboolean: + name: httpd_run_ipa + state: '{{ var_httpd_run_ipa }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_run_ipa + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_httpd_run_ipa='' + + +setsebool -P httpd_run_ipa $var_httpd_run_ipa + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the httpd_run_preupgrade SELinux Boolean + By default, the SELinux boolean httpd_run_preupgrade is disabled. +If this setting is enabled, it should be disabled. + +To disable the httpd_run_preupgrade SELinux boolean, run the following command: +$ sudo setsebool -P httpd_run_preupgrade off + + - name: XCCDF Value var_httpd_run_preupgrade # promote to variable + set_fact: + var_httpd_run_preupgrade: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_run_preupgrade + +- name: Set SELinux boolean httpd_run_preupgrade accordingly + seboolean: + name: httpd_run_preupgrade + state: '{{ var_httpd_run_preupgrade }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_run_preupgrade + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_httpd_run_preupgrade='' + + +setsebool -P httpd_run_preupgrade $var_httpd_run_preupgrade + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the httpd_run_stickshift SELinux Boolean + By default, the SELinux boolean httpd_run_stickshift is disabled. +If this setting is enabled, it should be disabled. + +To disable the httpd_run_stickshift SELinux boolean, run the following command: +$ sudo setsebool -P httpd_run_stickshift off + + - name: XCCDF Value var_httpd_run_stickshift # promote to variable + set_fact: + var_httpd_run_stickshift: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_run_stickshift + +- name: Set SELinux boolean httpd_run_stickshift accordingly + seboolean: + name: httpd_run_stickshift + state: '{{ var_httpd_run_stickshift }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_run_stickshift + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_httpd_run_stickshift='' + + +setsebool -P httpd_run_stickshift $var_httpd_run_stickshift + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the httpd_serve_cobbler_files SELinux Boolean + By default, the SELinux boolean httpd_serve_cobbler_files is disabled. +If this setting is enabled, it should be disabled. + +To disable the httpd_serve_cobbler_files SELinux boolean, run the following command: +$ sudo setsebool -P httpd_serve_cobbler_files off + + - name: XCCDF Value var_httpd_serve_cobbler_files # promote to variable + set_fact: + var_httpd_serve_cobbler_files: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_serve_cobbler_files + +- name: Set SELinux boolean httpd_serve_cobbler_files accordingly + seboolean: + name: httpd_serve_cobbler_files + state: '{{ var_httpd_serve_cobbler_files }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_serve_cobbler_files + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_httpd_serve_cobbler_files='' + + +setsebool -P httpd_serve_cobbler_files $var_httpd_serve_cobbler_files + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the httpd_setrlimit SELinux Boolean + By default, the SELinux boolean httpd_setrlimit is disabled. +If this setting is enabled, it should be disabled. + +To disable the httpd_setrlimit SELinux boolean, run the following command: +$ sudo setsebool -P httpd_setrlimit off + + - name: XCCDF Value var_httpd_setrlimit # promote to variable + set_fact: + var_httpd_setrlimit: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_setrlimit + +- name: Set SELinux boolean httpd_setrlimit accordingly + seboolean: + name: httpd_setrlimit + state: '{{ var_httpd_setrlimit }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_setrlimit + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_httpd_setrlimit='' + + +setsebool -P httpd_setrlimit $var_httpd_setrlimit + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the httpd_ssi_exec SELinux Boolean + By default, the SELinux boolean httpd_ssi_exec is disabled. +If this setting is enabled, it should be disabled. + +To disable the httpd_ssi_exec SELinux boolean, run the following command: +$ sudo setsebool -P httpd_ssi_exec off + + - name: XCCDF Value var_httpd_ssi_exec # promote to variable + set_fact: + var_httpd_ssi_exec: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_ssi_exec + +- name: Set SELinux boolean httpd_ssi_exec accordingly + seboolean: + name: httpd_ssi_exec + state: '{{ var_httpd_ssi_exec }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_ssi_exec + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_httpd_ssi_exec='' + + +setsebool -P httpd_ssi_exec $var_httpd_ssi_exec + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the httpd_sys_script_anon_write SELinux Boolean + By default, the SELinux boolean httpd_sys_script_anon_write is disabled. +If this setting is enabled, it should be disabled. + +To disable the httpd_sys_script_anon_write SELinux boolean, run the following command: +$ sudo setsebool -P httpd_sys_script_anon_write off + + - name: XCCDF Value var_httpd_sys_script_anon_write # promote to variable + set_fact: + var_httpd_sys_script_anon_write: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_sys_script_anon_write + +- name: Set SELinux boolean httpd_sys_script_anon_write accordingly + seboolean: + name: httpd_sys_script_anon_write + state: '{{ var_httpd_sys_script_anon_write }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_sys_script_anon_write + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_httpd_sys_script_anon_write='' + + +setsebool -P httpd_sys_script_anon_write $var_httpd_sys_script_anon_write + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the httpd_tmp_exec SELinux Boolean + By default, the SELinux boolean httpd_tmp_exec is disabled. +If this setting is enabled, it should be disabled. + +To disable the httpd_tmp_exec SELinux boolean, run the following command: +$ sudo setsebool -P httpd_tmp_exec off + + - name: XCCDF Value var_httpd_tmp_exec # promote to variable + set_fact: + var_httpd_tmp_exec: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_tmp_exec + +- name: Set SELinux boolean httpd_tmp_exec accordingly + seboolean: + name: httpd_tmp_exec + state: '{{ var_httpd_tmp_exec }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_tmp_exec + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_httpd_tmp_exec='' + + +setsebool -P httpd_tmp_exec $var_httpd_tmp_exec + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the httpd_tty_comm SELinux Boolean + By default, the SELinux boolean httpd_tty_comm is disabled. +If this setting is enabled, it should be disabled. + +To disable the httpd_tty_comm SELinux boolean, run the following command: +$ sudo setsebool -P httpd_tty_comm off + + - name: XCCDF Value var_httpd_tty_comm # promote to variable + set_fact: + var_httpd_tty_comm: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_tty_comm + +- name: Set SELinux boolean httpd_tty_comm accordingly + seboolean: + name: httpd_tty_comm + state: '{{ var_httpd_tty_comm }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_tty_comm + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_httpd_tty_comm='' + + +setsebool -P httpd_tty_comm $var_httpd_tty_comm + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the httpd_unified SELinux Boolean + By default, the SELinux boolean httpd_unified is disabled. +If this setting is enabled, it should be disabled. + +To disable the httpd_unified SELinux boolean, run the following command: +$ sudo setsebool -P httpd_unified off + + - name: XCCDF Value var_httpd_unified # promote to variable + set_fact: + var_httpd_unified: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_unified + +- name: Set SELinux boolean httpd_unified accordingly + seboolean: + name: httpd_unified + state: '{{ var_httpd_unified }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_unified + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_httpd_unified='' + + +setsebool -P httpd_unified $var_httpd_unified + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the httpd_use_cifs SELinux Boolean + By default, the SELinux boolean httpd_use_cifs is disabled. +If this setting is enabled, it should be disabled. + +To disable the httpd_use_cifs SELinux boolean, run the following command: +$ sudo setsebool -P httpd_use_cifs off + + - name: XCCDF Value var_httpd_use_cifs # promote to variable + set_fact: + var_httpd_use_cifs: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_use_cifs + +- name: Set SELinux boolean httpd_use_cifs accordingly + seboolean: + name: httpd_use_cifs + state: '{{ var_httpd_use_cifs }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_use_cifs + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_httpd_use_cifs='' + + +setsebool -P httpd_use_cifs $var_httpd_use_cifs + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the httpd_use_fusefs SELinux Boolean + By default, the SELinux boolean httpd_use_fusefs is disabled. +If this setting is enabled, it should be disabled. + +To disable the httpd_use_fusefs SELinux boolean, run the following command: +$ sudo setsebool -P httpd_use_fusefs off + + - name: XCCDF Value var_httpd_use_fusefs # promote to variable + set_fact: + var_httpd_use_fusefs: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_use_fusefs + +- name: Set SELinux boolean httpd_use_fusefs accordingly + seboolean: + name: httpd_use_fusefs + state: '{{ var_httpd_use_fusefs }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_use_fusefs + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_httpd_use_fusefs='' + + +setsebool -P httpd_use_fusefs $var_httpd_use_fusefs + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the httpd_use_gpg SELinux Boolean + By default, the SELinux boolean httpd_use_gpg is disabled. +If this setting is enabled, it should be disabled. + +To disable the httpd_use_gpg SELinux boolean, run the following command: +$ sudo setsebool -P httpd_use_gpg off + + - name: XCCDF Value var_httpd_use_gpg # promote to variable + set_fact: + var_httpd_use_gpg: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_use_gpg + +- name: Set SELinux boolean httpd_use_gpg accordingly + seboolean: + name: httpd_use_gpg + state: '{{ var_httpd_use_gpg }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_use_gpg + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_httpd_use_gpg='' + + +setsebool -P httpd_use_gpg $var_httpd_use_gpg + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the httpd_use_nfs SELinux Boolean + By default, the SELinux boolean httpd_use_nfs is disabled. +If this setting is enabled, it should be disabled. + +To disable the httpd_use_nfs SELinux boolean, run the following command: +$ sudo setsebool -P httpd_use_nfs off + + - name: XCCDF Value var_httpd_use_nfs # promote to variable + set_fact: + var_httpd_use_nfs: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_use_nfs + +- name: Set SELinux boolean httpd_use_nfs accordingly + seboolean: + name: httpd_use_nfs + state: '{{ var_httpd_use_nfs }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_use_nfs + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_httpd_use_nfs='' + + +setsebool -P httpd_use_nfs $var_httpd_use_nfs + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the httpd_use_openstack SELinux Boolean + By default, the SELinux boolean httpd_use_openstack is disabled. +If this setting is enabled, it should be disabled. + +To disable the httpd_use_openstack SELinux boolean, run the following command: +$ sudo setsebool -P httpd_use_openstack off + + - name: XCCDF Value var_httpd_use_openstack # promote to variable + set_fact: + var_httpd_use_openstack: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_use_openstack + +- name: Set SELinux boolean httpd_use_openstack accordingly + seboolean: + name: httpd_use_openstack + state: '{{ var_httpd_use_openstack }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_use_openstack + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_httpd_use_openstack='' + + +setsebool -P httpd_use_openstack $var_httpd_use_openstack + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the httpd_use_sasl SELinux Boolean + By default, the SELinux boolean httpd_use_sasl is disabled. +If this setting is enabled, it should be disabled. + +To disable the httpd_use_sasl SELinux boolean, run the following command: +$ sudo setsebool -P httpd_use_sasl off + + - name: XCCDF Value var_httpd_use_sasl # promote to variable + set_fact: + var_httpd_use_sasl: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_use_sasl + +- name: Set SELinux boolean httpd_use_sasl accordingly + seboolean: + name: httpd_use_sasl + state: '{{ var_httpd_use_sasl }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_use_sasl + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_httpd_use_sasl='' + + +setsebool -P httpd_use_sasl $var_httpd_use_sasl + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the httpd_verify_dns SELinux Boolean + By default, the SELinux boolean httpd_verify_dns is disabled. +If this setting is enabled, it should be disabled. + +To disable the httpd_verify_dns SELinux boolean, run the following command: +$ sudo setsebool -P httpd_verify_dns off + + - name: XCCDF Value var_httpd_verify_dns # promote to variable + set_fact: + var_httpd_verify_dns: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_verify_dns + +- name: Set SELinux boolean httpd_verify_dns accordingly + seboolean: + name: httpd_verify_dns + state: '{{ var_httpd_verify_dns }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_verify_dns + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_httpd_verify_dns='' + + +setsebool -P httpd_verify_dns $var_httpd_verify_dns + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the icecast_use_any_tcp_ports SELinux Boolean + By default, the SELinux boolean icecast_use_any_tcp_ports is disabled. +If this setting is enabled, it should be disabled. + +To disable the icecast_use_any_tcp_ports SELinux boolean, run the following command: +$ sudo setsebool -P icecast_use_any_tcp_ports off + + - name: XCCDF Value var_icecast_use_any_tcp_ports # promote to variable + set_fact: + var_icecast_use_any_tcp_ports: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_icecast_use_any_tcp_ports + +- name: Set SELinux boolean icecast_use_any_tcp_ports accordingly + seboolean: + name: icecast_use_any_tcp_ports + state: '{{ var_icecast_use_any_tcp_ports }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_icecast_use_any_tcp_ports + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_icecast_use_any_tcp_ports='' + + +setsebool -P icecast_use_any_tcp_ports $var_icecast_use_any_tcp_ports + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the irc_use_any_tcp_ports SELinux Boolean + By default, the SELinux boolean irc_use_any_tcp_ports is disabled. +If this setting is enabled, it should be disabled. + +To disable the irc_use_any_tcp_ports SELinux boolean, run the following command: +$ sudo setsebool -P irc_use_any_tcp_ports off + + - name: XCCDF Value var_irc_use_any_tcp_ports # promote to variable + set_fact: + var_irc_use_any_tcp_ports: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_irc_use_any_tcp_ports + +- name: Set SELinux boolean irc_use_any_tcp_ports accordingly + seboolean: + name: irc_use_any_tcp_ports + state: '{{ var_irc_use_any_tcp_ports }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_irc_use_any_tcp_ports + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_irc_use_any_tcp_ports='' + + +setsebool -P irc_use_any_tcp_ports $var_irc_use_any_tcp_ports + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the irssi_use_full_network SELinux Boolean + By default, the SELinux boolean irssi_use_full_network is disabled. +If this setting is enabled, it should be disabled. + +To disable the irssi_use_full_network SELinux boolean, run the following command: +$ sudo setsebool -P irssi_use_full_network off + + - name: XCCDF Value var_irssi_use_full_network # promote to variable + set_fact: + var_irssi_use_full_network: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_irssi_use_full_network + +- name: Set SELinux boolean irssi_use_full_network accordingly + seboolean: + name: irssi_use_full_network + state: '{{ var_irssi_use_full_network }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_irssi_use_full_network + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_irssi_use_full_network='' + + +setsebool -P irssi_use_full_network $var_irssi_use_full_network + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the kdumpgui_run_bootloader SELinux Boolean + By default, the SELinux boolean kdumpgui_run_bootloader is disabled. +If this setting is enabled, it should be disabled. + +To disable the kdumpgui_run_bootloader SELinux boolean, run the following command: +$ sudo setsebool -P kdumpgui_run_bootloader off + + - name: XCCDF Value var_kdumpgui_run_bootloader # promote to variable + set_fact: + var_kdumpgui_run_bootloader: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_kdumpgui_run_bootloader + +- name: Set SELinux boolean kdumpgui_run_bootloader accordingly + seboolean: + name: kdumpgui_run_bootloader + state: '{{ var_kdumpgui_run_bootloader }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_kdumpgui_run_bootloader + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_kdumpgui_run_bootloader='' + + +setsebool -P kdumpgui_run_bootloader $var_kdumpgui_run_bootloader + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable the kerberos_enabled SELinux Boolean + By default, the SELinux boolean kerberos_enabled is enabled. +If this setting is disabled, it should be enabled to allow confined +applications to run with Kerberos. + +To enable the kerberos_enabled SELinux boolean, run the following command: +$ sudo setsebool -P kerberos_enabled on + 0418 + 1055 + 1402 + + CCE-84293-0 + - name: XCCDF Value var_kerberos_enabled # promote to variable + set_fact: + var_kerberos_enabled: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84293-0 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_kerberos_enabled + +- name: Set SELinux boolean kerberos_enabled accordingly + seboolean: + name: kerberos_enabled + state: '{{ var_kerberos_enabled }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84293-0 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_kerberos_enabled + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_kerberos_enabled='' + + +setsebool -P kerberos_enabled $var_kerberos_enabled + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the ksmtuned_use_cifs SELinux Boolean + By default, the SELinux boolean ksmtuned_use_cifs is disabled. +If this setting is enabled, it should be disabled. + +To disable the ksmtuned_use_cifs SELinux boolean, run the following command: +$ sudo setsebool -P ksmtuned_use_cifs off + + - name: XCCDF Value var_ksmtuned_use_cifs # promote to variable + set_fact: + var_ksmtuned_use_cifs: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_ksmtuned_use_cifs + +- name: Set SELinux boolean ksmtuned_use_cifs accordingly + seboolean: + name: ksmtuned_use_cifs + state: '{{ var_ksmtuned_use_cifs }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_ksmtuned_use_cifs + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_ksmtuned_use_cifs='' + + +setsebool -P ksmtuned_use_cifs $var_ksmtuned_use_cifs + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the ksmtuned_use_nfs SELinux Boolean + By default, the SELinux boolean ksmtuned_use_nfs is disabled. +If this setting is enabled, it should be disabled. + +To disable the ksmtuned_use_nfs SELinux boolean, run the following command: +$ sudo setsebool -P ksmtuned_use_nfs off + + - name: XCCDF Value var_ksmtuned_use_nfs # promote to variable + set_fact: + var_ksmtuned_use_nfs: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_ksmtuned_use_nfs + +- name: Set SELinux boolean ksmtuned_use_nfs accordingly + seboolean: + name: ksmtuned_use_nfs + state: '{{ var_ksmtuned_use_nfs }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_ksmtuned_use_nfs + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_ksmtuned_use_nfs='' + + +setsebool -P ksmtuned_use_nfs $var_ksmtuned_use_nfs + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable the logadm_exec_content SELinux Boolean + By default, the SELinux boolean logadm_exec_content is enabled. +If this setting is disabled, it should be enabled. + +To enable the logadm_exec_content SELinux boolean, run the following command: +$ sudo setsebool -P logadm_exec_content on + + - name: XCCDF Value var_logadm_exec_content # promote to variable + set_fact: + var_logadm_exec_content: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_logadm_exec_content + +- name: Set SELinux boolean logadm_exec_content accordingly + seboolean: + name: logadm_exec_content + state: '{{ var_logadm_exec_content }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_logadm_exec_content + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_logadm_exec_content='' + + +setsebool -P logadm_exec_content $var_logadm_exec_content + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the logging_syslogd_can_sendmail SELinux Boolean + By default, the SELinux boolean logging_syslogd_can_sendmail is disabled. +If this setting is enabled, it should be disabled. + +To disable the logging_syslogd_can_sendmail SELinux boolean, run the following command: +$ sudo setsebool -P logging_syslogd_can_sendmail off + + - name: XCCDF Value var_logging_syslogd_can_sendmail # promote to variable + set_fact: + var_logging_syslogd_can_sendmail: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_logging_syslogd_can_sendmail + +- name: Set SELinux boolean logging_syslogd_can_sendmail accordingly + seboolean: + name: logging_syslogd_can_sendmail + state: '{{ var_logging_syslogd_can_sendmail }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_logging_syslogd_can_sendmail + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_logging_syslogd_can_sendmail='' + + +setsebool -P logging_syslogd_can_sendmail $var_logging_syslogd_can_sendmail + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the logging_syslogd_run_nagios_plugins SELinux Boolean + By default, the SELinux boolean logging_syslogd_run_nagios_plugins is disabled. +If this setting is enabled, it should be disabled. + +To disable the logging_syslogd_run_nagios_plugins SELinux boolean, run the following command: +$ sudo setsebool -P logging_syslogd_run_nagios_plugins off + + - name: XCCDF Value var_logging_syslogd_run_nagios_plugins # promote to variable + set_fact: + var_logging_syslogd_run_nagios_plugins: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_logging_syslogd_run_nagios_plugins + +- name: Set SELinux boolean logging_syslogd_run_nagios_plugins accordingly + seboolean: + name: logging_syslogd_run_nagios_plugins + state: '{{ var_logging_syslogd_run_nagios_plugins }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_logging_syslogd_run_nagios_plugins + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_logging_syslogd_run_nagios_plugins='' + + +setsebool -P logging_syslogd_run_nagios_plugins $var_logging_syslogd_run_nagios_plugins + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable the logging_syslogd_use_tty SELinux Boolean + By default, the SELinux boolean logging_syslogd_use_tty is enabled. +If this setting is disabled, it should be enabled as it allows syslog +the ability to read/write to terminal. + +To enable the logging_syslogd_use_tty SELinux boolean, run the following command: +$ sudo setsebool -P logging_syslogd_use_tty on + + - name: XCCDF Value var_logging_syslogd_use_tty # promote to variable + set_fact: + var_logging_syslogd_use_tty: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_logging_syslogd_use_tty + +- name: Set SELinux boolean logging_syslogd_use_tty accordingly + seboolean: + name: logging_syslogd_use_tty + state: '{{ var_logging_syslogd_use_tty }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_logging_syslogd_use_tty + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_logging_syslogd_use_tty='' + + +setsebool -P logging_syslogd_use_tty $var_logging_syslogd_use_tty + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable the login_console_enabled SELinux Boolean + By default, the SELinux boolean login_console_enabled is enabled. +If this setting is disabled, it should be enabled as it allows login from +/dev/console to a console session. + +To enable the login_console_enabled SELinux boolean, run the following command: +$ sudo setsebool -P login_console_enabled on + + - name: XCCDF Value var_login_console_enabled # promote to variable + set_fact: + var_login_console_enabled: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_login_console_enabled + +- name: Set SELinux boolean login_console_enabled accordingly + seboolean: + name: login_console_enabled + state: '{{ var_login_console_enabled }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_login_console_enabled + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_login_console_enabled='' + + +setsebool -P login_console_enabled $var_login_console_enabled + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the logrotate_use_nfs SELinux Boolean + By default, the SELinux boolean logrotate_use_nfs is disabled. +If this setting is enabled, it should be disabled. + +To disable the logrotate_use_nfs SELinux boolean, run the following command: +$ sudo setsebool -P logrotate_use_nfs off + + - name: XCCDF Value var_logrotate_use_nfs # promote to variable + set_fact: + var_logrotate_use_nfs: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_logrotate_use_nfs + +- name: Set SELinux boolean logrotate_use_nfs accordingly + seboolean: + name: logrotate_use_nfs + state: '{{ var_logrotate_use_nfs }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_logrotate_use_nfs + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_logrotate_use_nfs='' + + +setsebool -P logrotate_use_nfs $var_logrotate_use_nfs + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the logwatch_can_network_connect_mail SELinux Boolean + By default, the SELinux boolean logwatch_can_network_connect_mail is disabled. +If this setting is enabled, it should be disabled. + +To disable the logwatch_can_network_connect_mail SELinux boolean, run the following command: +$ sudo setsebool -P logwatch_can_network_connect_mail off + + - name: XCCDF Value var_logwatch_can_network_connect_mail # promote to variable + set_fact: + var_logwatch_can_network_connect_mail: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_logwatch_can_network_connect_mail + +- name: Set SELinux boolean logwatch_can_network_connect_mail accordingly + seboolean: + name: logwatch_can_network_connect_mail + state: '{{ var_logwatch_can_network_connect_mail }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_logwatch_can_network_connect_mail + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_logwatch_can_network_connect_mail='' + + +setsebool -P logwatch_can_network_connect_mail $var_logwatch_can_network_connect_mail + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the lsmd_plugin_connect_any SELinux Boolean + By default, the SELinux boolean lsmd_plugin_connect_any is disabled. +If this setting is enabled, it should be disabled. + +To disable the lsmd_plugin_connect_any SELinux boolean, run the following command: +$ sudo setsebool -P lsmd_plugin_connect_any off + + - name: XCCDF Value var_lsmd_plugin_connect_any # promote to variable + set_fact: + var_lsmd_plugin_connect_any: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_lsmd_plugin_connect_any + +- name: Set SELinux boolean lsmd_plugin_connect_any accordingly + seboolean: + name: lsmd_plugin_connect_any + state: '{{ var_lsmd_plugin_connect_any }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_lsmd_plugin_connect_any + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_lsmd_plugin_connect_any='' + + +setsebool -P lsmd_plugin_connect_any $var_lsmd_plugin_connect_any + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the mailman_use_fusefs SELinux Boolean + By default, the SELinux boolean mailman_use_fusefs is disabled. +If this setting is enabled, it should be disabled. + +To disable the mailman_use_fusefs SELinux boolean, run the following command: +$ sudo setsebool -P mailman_use_fusefs off + + - name: XCCDF Value var_mailman_use_fusefs # promote to variable + set_fact: + var_mailman_use_fusefs: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_mailman_use_fusefs + +- name: Set SELinux boolean mailman_use_fusefs accordingly + seboolean: + name: mailman_use_fusefs + state: '{{ var_mailman_use_fusefs }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_mailman_use_fusefs + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_mailman_use_fusefs='' + + +setsebool -P mailman_use_fusefs $var_mailman_use_fusefs + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the mcelog_client SELinux Boolean + By default, the SELinux boolean mcelog_client is disabled. +If this setting is enabled, it should be disabled. + +To disable the mcelog_client SELinux boolean, run the following command: +$ sudo setsebool -P mcelog_client off + + - name: XCCDF Value var_mcelog_client # promote to variable + set_fact: + var_mcelog_client: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_mcelog_client + +- name: Set SELinux boolean mcelog_client accordingly + seboolean: + name: mcelog_client + state: '{{ var_mcelog_client }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_mcelog_client + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_mcelog_client='' + + +setsebool -P mcelog_client $var_mcelog_client + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable the mcelog_exec_scripts SELinux Boolean + By default, the SELinux boolean mcelog_exec_scripts is enabled. +If this setting is disabled, it should be enabled. + +To enable the mcelog_exec_scripts SELinux boolean, run the following command: +$ sudo setsebool -P mcelog_exec_scripts on + + - name: XCCDF Value var_mcelog_exec_scripts # promote to variable + set_fact: + var_mcelog_exec_scripts: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_mcelog_exec_scripts + +- name: Set SELinux boolean mcelog_exec_scripts accordingly + seboolean: + name: mcelog_exec_scripts + state: '{{ var_mcelog_exec_scripts }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_mcelog_exec_scripts + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_mcelog_exec_scripts='' + + +setsebool -P mcelog_exec_scripts $var_mcelog_exec_scripts + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the mcelog_foreground SELinux Boolean + By default, the SELinux boolean mcelog_foreground is disabled. +If this setting is enabled, it should be disabled. + +To disable the mcelog_foreground SELinux boolean, run the following command: +$ sudo setsebool -P mcelog_foreground off + + - name: XCCDF Value var_mcelog_foreground # promote to variable + set_fact: + var_mcelog_foreground: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_mcelog_foreground + +- name: Set SELinux boolean mcelog_foreground accordingly + seboolean: + name: mcelog_foreground + state: '{{ var_mcelog_foreground }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_mcelog_foreground + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_mcelog_foreground='' + + +setsebool -P mcelog_foreground $var_mcelog_foreground + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the mcelog_server SELinux Boolean + By default, the SELinux boolean mcelog_server is disabled. +If this setting is enabled, it should be disabled. + +To disable the mcelog_server SELinux boolean, run the following command: +$ sudo setsebool -P mcelog_server off + + - name: XCCDF Value var_mcelog_server # promote to variable + set_fact: + var_mcelog_server: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_mcelog_server + +- name: Set SELinux boolean mcelog_server accordingly + seboolean: + name: mcelog_server + state: '{{ var_mcelog_server }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_mcelog_server + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_mcelog_server='' + + +setsebool -P mcelog_server $var_mcelog_server + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the minidlna_read_generic_user_content SELinux Boolean + By default, the SELinux boolean minidlna_read_generic_user_content is disabled. +If this setting is enabled, it should be disabled. + +To disable the minidlna_read_generic_user_content SELinux boolean, run the following command: +$ sudo setsebool -P minidlna_read_generic_user_content off + + - name: XCCDF Value var_minidlna_read_generic_user_content # promote to variable + set_fact: + var_minidlna_read_generic_user_content: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_minidlna_read_generic_user_content + +- name: Set SELinux boolean minidlna_read_generic_user_content accordingly + seboolean: + name: minidlna_read_generic_user_content + state: '{{ var_minidlna_read_generic_user_content }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_minidlna_read_generic_user_content + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_minidlna_read_generic_user_content='' + + +setsebool -P minidlna_read_generic_user_content $var_minidlna_read_generic_user_content + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the mmap_low_allowed SELinux Boolean + By default, the SELinux boolean mmap_low_allowed is disabled. +If this setting is enabled, it should be disabled. + +To disable the mmap_low_allowed SELinux boolean, run the following command: +$ sudo setsebool -P mmap_low_allowed off + + - name: XCCDF Value var_mmap_low_allowed # promote to variable + set_fact: + var_mmap_low_allowed: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_mmap_low_allowed + +- name: Set SELinux boolean mmap_low_allowed accordingly + seboolean: + name: mmap_low_allowed + state: '{{ var_mmap_low_allowed }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_mmap_low_allowed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_mmap_low_allowed='' + + +setsebool -P mmap_low_allowed $var_mmap_low_allowed + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the mock_enable_homedirs SELinux Boolean + By default, the SELinux boolean mock_enable_homedirs is disabled. +If this setting is enabled, it should be disabled. + +To disable the mock_enable_homedirs SELinux boolean, run the following command: +$ sudo setsebool -P mock_enable_homedirs off + + - name: XCCDF Value var_mock_enable_homedirs # promote to variable + set_fact: + var_mock_enable_homedirs: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_mock_enable_homedirs + +- name: Set SELinux boolean mock_enable_homedirs accordingly + seboolean: + name: mock_enable_homedirs + state: '{{ var_mock_enable_homedirs }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_mock_enable_homedirs + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_mock_enable_homedirs='' + + +setsebool -P mock_enable_homedirs $var_mock_enable_homedirs + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable the mount_anyfile SELinux Boolean + By default, the SELinux boolean mount_anyfile is enabled. +If this setting is disabled, it should be enabled to allow any file +or directory to be mounted. + +To enable the mount_anyfile SELinux boolean, run the following command: +$ sudo setsebool -P mount_anyfile on + + - name: XCCDF Value var_mount_anyfile # promote to variable + set_fact: + var_mount_anyfile: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_mount_anyfile + +- name: Set SELinux boolean mount_anyfile accordingly + seboolean: + name: mount_anyfile + state: '{{ var_mount_anyfile }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_mount_anyfile + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_mount_anyfile='' + + +setsebool -P mount_anyfile $var_mount_anyfile + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the mozilla_plugin_bind_unreserved_ports SELinux Boolean + By default, the SELinux boolean mozilla_plugin_bind_unreserved_ports is disabled. +If this setting is enabled, it should be disabled. + +To disable the mozilla_plugin_bind_unreserved_ports SELinux boolean, run the following command: +$ sudo setsebool -P mozilla_plugin_bind_unreserved_ports off + + - name: XCCDF Value var_mozilla_plugin_bind_unreserved_ports # promote to variable + set_fact: + var_mozilla_plugin_bind_unreserved_ports: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_mozilla_plugin_bind_unreserved_ports + +- name: Set SELinux boolean mozilla_plugin_bind_unreserved_ports accordingly + seboolean: + name: mozilla_plugin_bind_unreserved_ports + state: '{{ var_mozilla_plugin_bind_unreserved_ports }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_mozilla_plugin_bind_unreserved_ports + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_mozilla_plugin_bind_unreserved_ports='' + + +setsebool -P mozilla_plugin_bind_unreserved_ports $var_mozilla_plugin_bind_unreserved_ports + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the mozilla_plugin_can_network_connect SELinux Boolean + By default, the SELinux boolean mozilla_plugin_can_network_connect is disabled. +If this setting is enabled, it should be disabled. + +To disable the mozilla_plugin_can_network_connect SELinux boolean, run the following command: +$ sudo setsebool -P mozilla_plugin_can_network_connect off + + - name: XCCDF Value var_mozilla_plugin_can_network_connect # promote to variable + set_fact: + var_mozilla_plugin_can_network_connect: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_mozilla_plugin_can_network_connect + +- name: Set SELinux boolean mozilla_plugin_can_network_connect accordingly + seboolean: + name: mozilla_plugin_can_network_connect + state: '{{ var_mozilla_plugin_can_network_connect }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_mozilla_plugin_can_network_connect + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_mozilla_plugin_can_network_connect='' + + +setsebool -P mozilla_plugin_can_network_connect $var_mozilla_plugin_can_network_connect + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the mozilla_plugin_use_bluejeans SELinux Boolean + By default, the SELinux boolean mozilla_plugin_use_bluejeans is disabled. +If this setting is enabled, it should be disabled. + +To disable the mozilla_plugin_use_bluejeans SELinux boolean, run the following command: +$ sudo setsebool -P mozilla_plugin_use_bluejeans off + + - name: XCCDF Value var_mozilla_plugin_use_bluejeans # promote to variable + set_fact: + var_mozilla_plugin_use_bluejeans: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_mozilla_plugin_use_bluejeans + +- name: Set SELinux boolean mozilla_plugin_use_bluejeans accordingly + seboolean: + name: mozilla_plugin_use_bluejeans + state: '{{ var_mozilla_plugin_use_bluejeans }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_mozilla_plugin_use_bluejeans + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_mozilla_plugin_use_bluejeans='' + + +setsebool -P mozilla_plugin_use_bluejeans $var_mozilla_plugin_use_bluejeans + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the mozilla_plugin_use_gps SELinux Boolean + By default, the SELinux boolean mozilla_plugin_use_gps is disabled. +If this setting is enabled, it should be disabled. + +To disable the mozilla_plugin_use_gps SELinux boolean, run the following command: +$ sudo setsebool -P mozilla_plugin_use_gps off + + - name: XCCDF Value var_mozilla_plugin_use_gps # promote to variable + set_fact: + var_mozilla_plugin_use_gps: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_mozilla_plugin_use_gps + +- name: Set SELinux boolean mozilla_plugin_use_gps accordingly + seboolean: + name: mozilla_plugin_use_gps + state: '{{ var_mozilla_plugin_use_gps }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_mozilla_plugin_use_gps + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_mozilla_plugin_use_gps='' + + +setsebool -P mozilla_plugin_use_gps $var_mozilla_plugin_use_gps + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the mozilla_plugin_use_spice SELinux Boolean + By default, the SELinux boolean mozilla_plugin_use_spice is disabled. +If this setting is enabled, it should be disabled. + +To disable the mozilla_plugin_use_spice SELinux boolean, run the following command: +$ sudo setsebool -P mozilla_plugin_use_spice off + + - name: XCCDF Value var_mozilla_plugin_use_spice # promote to variable + set_fact: + var_mozilla_plugin_use_spice: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_mozilla_plugin_use_spice + +- name: Set SELinux boolean mozilla_plugin_use_spice accordingly + seboolean: + name: mozilla_plugin_use_spice + state: '{{ var_mozilla_plugin_use_spice }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_mozilla_plugin_use_spice + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_mozilla_plugin_use_spice='' + + +setsebool -P mozilla_plugin_use_spice $var_mozilla_plugin_use_spice + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the mozilla_read_content SELinux Boolean + By default, the SELinux boolean mozilla_read_content is disabled. +If this setting is enabled, it should be disabled. + +To disable the mozilla_read_content SELinux boolean, run the following command: +$ sudo setsebool -P mozilla_read_content off + + - name: XCCDF Value var_mozilla_read_content # promote to variable + set_fact: + var_mozilla_read_content: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_mozilla_read_content + +- name: Set SELinux boolean mozilla_read_content accordingly + seboolean: + name: mozilla_read_content + state: '{{ var_mozilla_read_content }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_mozilla_read_content + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_mozilla_read_content='' + + +setsebool -P mozilla_read_content $var_mozilla_read_content + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the mpd_enable_homedirs SELinux Boolean + By default, the SELinux boolean mpd_enable_homedirs is disabled. +If this setting is enabled, it should be disabled. + +To disable the mpd_enable_homedirs SELinux boolean, run the following command: +$ sudo setsebool -P mpd_enable_homedirs off + + - name: XCCDF Value var_mpd_enable_homedirs # promote to variable + set_fact: + var_mpd_enable_homedirs: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_mpd_enable_homedirs + +- name: Set SELinux boolean mpd_enable_homedirs accordingly + seboolean: + name: mpd_enable_homedirs + state: '{{ var_mpd_enable_homedirs }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_mpd_enable_homedirs + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_mpd_enable_homedirs='' + + +setsebool -P mpd_enable_homedirs $var_mpd_enable_homedirs + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the mpd_use_cifs SELinux Boolean + By default, the SELinux boolean mpd_use_cifs is disabled. +If this setting is enabled, it should be disabled. + +To disable the mpd_use_cifs SELinux boolean, run the following command: +$ sudo setsebool -P mpd_use_cifs off + + - name: XCCDF Value var_mpd_use_cifs # promote to variable + set_fact: + var_mpd_use_cifs: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_mpd_use_cifs + +- name: Set SELinux boolean mpd_use_cifs accordingly + seboolean: + name: mpd_use_cifs + state: '{{ var_mpd_use_cifs }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_mpd_use_cifs + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_mpd_use_cifs='' + + +setsebool -P mpd_use_cifs $var_mpd_use_cifs + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the mpd_use_nfs SELinux Boolean + By default, the SELinux boolean mpd_use_nfs is disabled. +If this setting is enabled, it should be disabled. + +To disable the mpd_use_nfs SELinux boolean, run the following command: +$ sudo setsebool -P mpd_use_nfs off + + - name: XCCDF Value var_mpd_use_nfs # promote to variable + set_fact: + var_mpd_use_nfs: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_mpd_use_nfs + +- name: Set SELinux boolean mpd_use_nfs accordingly + seboolean: + name: mpd_use_nfs + state: '{{ var_mpd_use_nfs }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_mpd_use_nfs + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_mpd_use_nfs='' + + +setsebool -P mpd_use_nfs $var_mpd_use_nfs + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the mplayer_execstack SELinux Boolean + By default, the SELinux boolean mplayer_execstack is disabled. +If this setting is enabled, it should be disabled. + +To disable the mplayer_execstack SELinux boolean, run the following command: +$ sudo setsebool -P mplayer_execstack off + + - name: XCCDF Value var_mplayer_execstack # promote to variable + set_fact: + var_mplayer_execstack: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_mplayer_execstack + +- name: Set SELinux boolean mplayer_execstack accordingly + seboolean: + name: mplayer_execstack + state: '{{ var_mplayer_execstack }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_mplayer_execstack + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_mplayer_execstack='' + + +setsebool -P mplayer_execstack $var_mplayer_execstack + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the mysql_connect_any SELinux Boolean + By default, the SELinux boolean mysql_connect_any is disabled. +If this setting is enabled, it should be disabled. + +To disable the mysql_connect_any SELinux boolean, run the following command: +$ sudo setsebool -P mysql_connect_any off + + - name: XCCDF Value var_mysql_connect_any # promote to variable + set_fact: + var_mysql_connect_any: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_mysql_connect_any + +- name: Set SELinux boolean mysql_connect_any accordingly + seboolean: + name: mysql_connect_any + state: '{{ var_mysql_connect_any }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_mysql_connect_any + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_mysql_connect_any='' + + +setsebool -P mysql_connect_any $var_mysql_connect_any + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the nagios_run_pnp4nagios SELinux Boolean + By default, the SELinux boolean nagios_run_pnp4nagios is disabled. +If this setting is enabled, it should be disabled. + +To disable the nagios_run_pnp4nagios SELinux boolean, run the following command: +$ sudo setsebool -P nagios_run_pnp4nagios off + + - name: XCCDF Value var_nagios_run_pnp4nagios # promote to variable + set_fact: + var_nagios_run_pnp4nagios: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_nagios_run_pnp4nagios + +- name: Set SELinux boolean nagios_run_pnp4nagios accordingly + seboolean: + name: nagios_run_pnp4nagios + state: '{{ var_nagios_run_pnp4nagios }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_nagios_run_pnp4nagios + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_nagios_run_pnp4nagios='' + + +setsebool -P nagios_run_pnp4nagios $var_nagios_run_pnp4nagios + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the nagios_run_sudo SELinux Boolean + By default, the SELinux boolean nagios_run_sudo is disabled. +If this setting is enabled, it should be disabled. + +To disable the nagios_run_sudo SELinux boolean, run the following command: +$ sudo setsebool -P nagios_run_sudo off + + - name: XCCDF Value var_nagios_run_sudo # promote to variable + set_fact: + var_nagios_run_sudo: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_nagios_run_sudo + +- name: Set SELinux boolean nagios_run_sudo accordingly + seboolean: + name: nagios_run_sudo + state: '{{ var_nagios_run_sudo }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_nagios_run_sudo + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_nagios_run_sudo='' + + +setsebool -P nagios_run_sudo $var_nagios_run_sudo + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the named_tcp_bind_http_port SELinux Boolean + By default, the SELinux boolean named_tcp_bind_http_port is disabled. +If this setting is enabled, it should be disabled. + +To disable the named_tcp_bind_http_port SELinux boolean, run the following command: +$ sudo setsebool -P named_tcp_bind_http_port off + + - name: XCCDF Value var_named_tcp_bind_http_port # promote to variable + set_fact: + var_named_tcp_bind_http_port: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_named_tcp_bind_http_port + +- name: Set SELinux boolean named_tcp_bind_http_port accordingly + seboolean: + name: named_tcp_bind_http_port + state: '{{ var_named_tcp_bind_http_port }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_named_tcp_bind_http_port + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_named_tcp_bind_http_port='' + + +setsebool -P named_tcp_bind_http_port $var_named_tcp_bind_http_port + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the named_write_master_zones SELinux Boolean + By default, the SELinux boolean named_write_master_zones is disabled. +If this setting is enabled, it should be disabled. + +To disable the named_write_master_zones SELinux boolean, run the following command: +$ sudo setsebool -P named_write_master_zones off + + - name: XCCDF Value var_named_write_master_zones # promote to variable + set_fact: + var_named_write_master_zones: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_named_write_master_zones + +- name: Set SELinux boolean named_write_master_zones accordingly + seboolean: + name: named_write_master_zones + state: '{{ var_named_write_master_zones }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_named_write_master_zones + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_named_write_master_zones='' + + +setsebool -P named_write_master_zones $var_named_write_master_zones + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the neutron_can_network SELinux Boolean + By default, the SELinux boolean neutron_can_network is disabled. +If this setting is enabled, it should be disabled. + +To disable the neutron_can_network SELinux boolean, run the following command: +$ sudo setsebool -P neutron_can_network off + + - name: XCCDF Value var_neutron_can_network # promote to variable + set_fact: + var_neutron_can_network: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_neutron_can_network + +- name: Set SELinux boolean neutron_can_network accordingly + seboolean: + name: neutron_can_network + state: '{{ var_neutron_can_network }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_neutron_can_network + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_neutron_can_network='' + + +setsebool -P neutron_can_network $var_neutron_can_network + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable the nfs_export_all_ro SELinux Boolean + By default, the SELinux boolean nfs_export_all_ro is enabled. +If this setting is disabled, it should be enabled as it allows NFS to +export read-only mounts. + +To enable the nfs_export_all_ro SELinux boolean, run the following command: +$ sudo setsebool -P nfs_export_all_ro on + + - name: XCCDF Value var_nfs_export_all_ro # promote to variable + set_fact: + var_nfs_export_all_ro: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_nfs_export_all_ro + +- name: Set SELinux boolean nfs_export_all_ro accordingly + seboolean: + name: nfs_export_all_ro + state: '{{ var_nfs_export_all_ro }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_nfs_export_all_ro + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_nfs_export_all_ro='' + + +setsebool -P nfs_export_all_ro $var_nfs_export_all_ro + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable the nfs_export_all_rw SELinux Boolean + By default, the SELinux boolean nfs_export_all_rw is enabled. +If this setting is disabled, it should be enabled as it allows NFS to +export read/write mounts. + +To enable the nfs_export_all_rw SELinux boolean, run the following command: +$ sudo setsebool -P nfs_export_all_rw on + + - name: XCCDF Value var_nfs_export_all_rw # promote to variable + set_fact: + var_nfs_export_all_rw: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_nfs_export_all_rw + +- name: Set SELinux boolean nfs_export_all_rw accordingly + seboolean: + name: nfs_export_all_rw + state: '{{ var_nfs_export_all_rw }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_nfs_export_all_rw + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_nfs_export_all_rw='' + + +setsebool -P nfs_export_all_rw $var_nfs_export_all_rw + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the nfsd_anon_write SELinux Boolean + By default, the SELinux boolean nfsd_anon_write is disabled. +If this setting is enabled, it should be disabled. + +To disable the nfsd_anon_write SELinux boolean, run the following command: +$ sudo setsebool -P nfsd_anon_write off + + - name: XCCDF Value var_nfsd_anon_write # promote to variable + set_fact: + var_nfsd_anon_write: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_nfsd_anon_write + +- name: Set SELinux boolean nfsd_anon_write accordingly + seboolean: + name: nfsd_anon_write + state: '{{ var_nfsd_anon_write }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_nfsd_anon_write + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_nfsd_anon_write='' + + +setsebool -P nfsd_anon_write $var_nfsd_anon_write + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the nis_enabled SELinux Boolean + By default, the SELinux boolean nis_enabled is disabled. +If this setting is enabled, it should be disabled. + +To disable the nis_enabled SELinux boolean, run the following command: +$ sudo setsebool -P nis_enabled off + + - name: XCCDF Value var_nis_enabled # promote to variable + set_fact: + var_nis_enabled: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_nis_enabled + +- name: Set SELinux boolean nis_enabled accordingly + seboolean: + name: nis_enabled + state: '{{ var_nis_enabled }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_nis_enabled + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_nis_enabled='' + + +setsebool -P nis_enabled $var_nis_enabled + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable the nscd_use_shm SELinux Boolean + By default, the SELinux boolean nscd_use_shm is enabled. +If this setting is disabled, it should be enabled to allow nscd +to use shared memory. + +To enable the nscd_use_shm SELinux boolean, run the following command: +$ sudo setsebool -P nscd_use_shm on + + - name: XCCDF Value var_nscd_use_shm # promote to variable + set_fact: + var_nscd_use_shm: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_nscd_use_shm + +- name: Set SELinux boolean nscd_use_shm accordingly + seboolean: + name: nscd_use_shm + state: '{{ var_nscd_use_shm }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_nscd_use_shm + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_nscd_use_shm='' + + +setsebool -P nscd_use_shm $var_nscd_use_shm + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the openshift_use_nfs SELinux Boolean + By default, the SELinux boolean openshift_use_nfs is disabled. +If this setting is enabled, it should be disabled. + +To disable the openshift_use_nfs SELinux boolean, run the following command: +$ sudo setsebool -P openshift_use_nfs off + + - name: XCCDF Value var_openshift_use_nfs # promote to variable + set_fact: + var_openshift_use_nfs: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_openshift_use_nfs + +- name: Set SELinux boolean openshift_use_nfs accordingly + seboolean: + name: openshift_use_nfs + state: '{{ var_openshift_use_nfs }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_openshift_use_nfs + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_openshift_use_nfs='' + + +setsebool -P openshift_use_nfs $var_openshift_use_nfs + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the openvpn_can_network_connect SELinux Boolean + By default, the SELinux boolean openvpn_can_network_connect is enabled. +This setting should be disabled. + +To disable the openvpn_can_network_connect SELinux boolean, run the following command: +$ sudo setsebool -P openvpn_can_network_connect off + + - name: XCCDF Value var_openvpn_can_network_connect # promote to variable + set_fact: + var_openvpn_can_network_connect: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_openvpn_can_network_connect + +- name: Set SELinux boolean openvpn_can_network_connect accordingly + seboolean: + name: openvpn_can_network_connect + state: '{{ var_openvpn_can_network_connect }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_openvpn_can_network_connect + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_openvpn_can_network_connect='' + + +setsebool -P openvpn_can_network_connect $var_openvpn_can_network_connect + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the openvpn_enable_homedirs SELinux Boolean + By default, the SELinux boolean openvpn_enable_homedirs is enabled. +This setting should be disabled. + +To disable the openvpn_enable_homedirs SELinux boolean, run the following command: +$ sudo setsebool -P openvpn_enable_homedirs off + + - name: XCCDF Value var_openvpn_enable_homedirs # promote to variable + set_fact: + var_openvpn_enable_homedirs: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_openvpn_enable_homedirs + +- name: Set SELinux boolean openvpn_enable_homedirs accordingly + seboolean: + name: openvpn_enable_homedirs + state: '{{ var_openvpn_enable_homedirs }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_openvpn_enable_homedirs + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_openvpn_enable_homedirs='' + + +setsebool -P openvpn_enable_homedirs $var_openvpn_enable_homedirs + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the openvpn_run_unconfined SELinux Boolean + By default, the SELinux boolean openvpn_run_unconfined is disabled. +If this setting is enabled, it should be disabled. + +To disable the openvpn_run_unconfined SELinux boolean, run the following command: +$ sudo setsebool -P openvpn_run_unconfined off + + - name: XCCDF Value var_openvpn_run_unconfined # promote to variable + set_fact: + var_openvpn_run_unconfined: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_openvpn_run_unconfined + +- name: Set SELinux boolean openvpn_run_unconfined accordingly + seboolean: + name: openvpn_run_unconfined + state: '{{ var_openvpn_run_unconfined }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_openvpn_run_unconfined + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_openvpn_run_unconfined='' + + +setsebool -P openvpn_run_unconfined $var_openvpn_run_unconfined + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the pcp_bind_all_unreserved_ports SELinux Boolean + By default, the SELinux boolean pcp_bind_all_unreserved_ports is disabled. +If this setting is enabled, it should be disabled. + +To disable the pcp_bind_all_unreserved_ports SELinux boolean, run the following command: +$ sudo setsebool -P pcp_bind_all_unreserved_ports off + + - name: XCCDF Value var_pcp_bind_all_unreserved_ports # promote to variable + set_fact: + var_pcp_bind_all_unreserved_ports: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_pcp_bind_all_unreserved_ports + +- name: Set SELinux boolean pcp_bind_all_unreserved_ports accordingly + seboolean: + name: pcp_bind_all_unreserved_ports + state: '{{ var_pcp_bind_all_unreserved_ports }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_pcp_bind_all_unreserved_ports + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_pcp_bind_all_unreserved_ports='' + + +setsebool -P pcp_bind_all_unreserved_ports $var_pcp_bind_all_unreserved_ports + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the pcp_read_generic_logs SELinux Boolean + By default, the SELinux boolean pcp_read_generic_logs is disabled. +If this setting is enabled, it should be disabled. + +To disable the pcp_read_generic_logs SELinux boolean, run the following command: +$ sudo setsebool -P pcp_read_generic_logs off + + - name: XCCDF Value var_pcp_read_generic_logs # promote to variable + set_fact: + var_pcp_read_generic_logs: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_pcp_read_generic_logs + +- name: Set SELinux boolean pcp_read_generic_logs accordingly + seboolean: + name: pcp_read_generic_logs + state: '{{ var_pcp_read_generic_logs }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_pcp_read_generic_logs + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_pcp_read_generic_logs='' + + +setsebool -P pcp_read_generic_logs $var_pcp_read_generic_logs + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the piranha_lvs_can_network_connect SELinux Boolean + By default, the SELinux boolean piranha_lvs_can_network_connect is disabled. +If this setting is enabled, it should be disabled. + +To disable the piranha_lvs_can_network_connect SELinux boolean, run the following command: +$ sudo setsebool -P piranha_lvs_can_network_connect off + + - name: XCCDF Value var_piranha_lvs_can_network_connect # promote to variable + set_fact: + var_piranha_lvs_can_network_connect: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_piranha_lvs_can_network_connect + +- name: Set SELinux boolean piranha_lvs_can_network_connect accordingly + seboolean: + name: piranha_lvs_can_network_connect + state: '{{ var_piranha_lvs_can_network_connect }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_piranha_lvs_can_network_connect + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_piranha_lvs_can_network_connect='' + + +setsebool -P piranha_lvs_can_network_connect $var_piranha_lvs_can_network_connect + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the polipo_connect_all_unreserved SELinux Boolean + By default, the SELinux boolean polipo_connect_all_unreserved is disabled. +If this setting is enabled, it should be disabled. + +To disable the polipo_connect_all_unreserved SELinux boolean, run the following command: +$ sudo setsebool -P polipo_connect_all_unreserved off + + - name: XCCDF Value var_polipo_connect_all_unreserved # promote to variable + set_fact: + var_polipo_connect_all_unreserved: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_polipo_connect_all_unreserved + +- name: Set SELinux boolean polipo_connect_all_unreserved accordingly + seboolean: + name: polipo_connect_all_unreserved + state: '{{ var_polipo_connect_all_unreserved }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_polipo_connect_all_unreserved + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_polipo_connect_all_unreserved='' + + +setsebool -P polipo_connect_all_unreserved $var_polipo_connect_all_unreserved + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the polipo_session_bind_all_unreserved_ports SELinux Boolean + By default, the SELinux boolean polipo_session_bind_all_unreserved_ports is disabled. +If this setting is enabled, it should be disabled. + +To disable the polipo_session_bind_all_unreserved_ports SELinux boolean, run the following command: +$ sudo setsebool -P polipo_session_bind_all_unreserved_ports off + + - name: XCCDF Value var_polipo_session_bind_all_unreserved_ports # promote to variable + set_fact: + var_polipo_session_bind_all_unreserved_ports: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_polipo_session_bind_all_unreserved_ports + +- name: Set SELinux boolean polipo_session_bind_all_unreserved_ports accordingly + seboolean: + name: polipo_session_bind_all_unreserved_ports + state: '{{ var_polipo_session_bind_all_unreserved_ports }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_polipo_session_bind_all_unreserved_ports + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_polipo_session_bind_all_unreserved_ports='' + + +setsebool -P polipo_session_bind_all_unreserved_ports $var_polipo_session_bind_all_unreserved_ports + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the polipo_session_users SELinux Boolean + By default, the SELinux boolean polipo_session_users is disabled. +If this setting is enabled, it should be disabled. + +To disable the polipo_session_users SELinux boolean, run the following command: +$ sudo setsebool -P polipo_session_users off + + - name: XCCDF Value var_polipo_session_users # promote to variable + set_fact: + var_polipo_session_users: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_polipo_session_users + +- name: Set SELinux boolean polipo_session_users accordingly + seboolean: + name: polipo_session_users + state: '{{ var_polipo_session_users }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_polipo_session_users + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_polipo_session_users='' + + +setsebool -P polipo_session_users $var_polipo_session_users + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the polipo_use_cifs SELinux Boolean + By default, the SELinux boolean polipo_use_cifs is disabled. +If this setting is enabled, it should be disabled. + +To disable the polipo_use_cifs SELinux boolean, run the following command: +$ sudo setsebool -P polipo_use_cifs off + + - name: XCCDF Value var_polipo_use_cifs # promote to variable + set_fact: + var_polipo_use_cifs: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_polipo_use_cifs + +- name: Set SELinux boolean polipo_use_cifs accordingly + seboolean: + name: polipo_use_cifs + state: '{{ var_polipo_use_cifs }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_polipo_use_cifs + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_polipo_use_cifs='' + + +setsebool -P polipo_use_cifs $var_polipo_use_cifs + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the polipo_use_nfs SELinux Boolean + By default, the SELinux boolean polipo_use_nfs is disabled. +If this setting is enabled, it should be disabled. + +To disable the polipo_use_nfs SELinux boolean, run the following command: +$ sudo setsebool -P polipo_use_nfs off + + - name: XCCDF Value var_polipo_use_nfs # promote to variable + set_fact: + var_polipo_use_nfs: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_polipo_use_nfs + +- name: Set SELinux boolean polipo_use_nfs accordingly + seboolean: + name: polipo_use_nfs + state: '{{ var_polipo_use_nfs }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_polipo_use_nfs + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_polipo_use_nfs='' + + +setsebool -P polipo_use_nfs $var_polipo_use_nfs + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Configure the polyinstantiation_enabled SELinux Boolean + By default, the SELinux boolean polyinstantiation_enabled is disabled. +This setting should be configured to . + +To set the polyinstantiation_enabled SELinux boolean, run the following command: +$ sudo setsebool -P polyinstantiation_enabled + BP28(R39) + + CCE-84230-2 + - name: XCCDF Value var_polyinstantiation_enabled # promote to variable + set_fact: + var_polyinstantiation_enabled: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84230-2 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_polyinstantiation_enabled + +- name: Set SELinux boolean polyinstantiation_enabled accordingly + seboolean: + name: polyinstantiation_enabled + state: '{{ var_polyinstantiation_enabled }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84230-2 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_polyinstantiation_enabled + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_polyinstantiation_enabled='' + + +setsebool -P polyinstantiation_enabled $var_polyinstantiation_enabled + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable the postfix_local_write_mail_spool SELinux Boolean + By default, the SELinux boolean postfix_local_write_mail_spool is enabled. +If this setting is disabled, it should be enabled as it allows Postfix to write +to the mail spool directories. + +To enable the postfix_local_write_mail_spool SELinux boolean, run the following command: +$ sudo setsebool -P postfix_local_write_mail_spool on + + - name: XCCDF Value var_postfix_local_write_mail_spool # promote to variable + set_fact: + var_postfix_local_write_mail_spool: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_postfix_local_write_mail_spool + +- name: Set SELinux boolean postfix_local_write_mail_spool accordingly + seboolean: + name: postfix_local_write_mail_spool + state: '{{ var_postfix_local_write_mail_spool }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_postfix_local_write_mail_spool + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_postfix_local_write_mail_spool='' + + +setsebool -P postfix_local_write_mail_spool $var_postfix_local_write_mail_spool + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the postgresql_can_rsync SELinux Boolean + By default, the SELinux boolean postgresql_can_rsync is disabled. +If this setting is enabled, it should be disabled. + +To disable the postgresql_can_rsync SELinux boolean, run the following command: +$ sudo setsebool -P postgresql_can_rsync off + + - name: XCCDF Value var_postgresql_can_rsync # promote to variable + set_fact: + var_postgresql_can_rsync: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_postgresql_can_rsync + +- name: Set SELinux boolean postgresql_can_rsync accordingly + seboolean: + name: postgresql_can_rsync + state: '{{ var_postgresql_can_rsync }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_postgresql_can_rsync + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_postgresql_can_rsync='' + + +setsebool -P postgresql_can_rsync $var_postgresql_can_rsync + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the postgresql_selinux_transmit_client_label SELinux Boolean + By default, the SELinux boolean postgresql_selinux_transmit_client_label is disabled. +If this setting is enabled, it should be disabled. + +To disable the postgresql_selinux_transmit_client_label SELinux boolean, run the following command: +$ sudo setsebool -P postgresql_selinux_transmit_client_label off + + - name: XCCDF Value var_postgresql_selinux_transmit_client_label # promote to variable + set_fact: + var_postgresql_selinux_transmit_client_label: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_postgresql_selinux_transmit_client_label + +- name: Set SELinux boolean postgresql_selinux_transmit_client_label accordingly + seboolean: + name: postgresql_selinux_transmit_client_label + state: '{{ var_postgresql_selinux_transmit_client_label }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_postgresql_selinux_transmit_client_label + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_postgresql_selinux_transmit_client_label='' + + +setsebool -P postgresql_selinux_transmit_client_label $var_postgresql_selinux_transmit_client_label + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable the postgresql_selinux_unconfined_dbadm SELinux Boolean + By default, the SELinux boolean postgresql_selinux_unconfined_dbadm is enabled. +If this setting is disabled, it should be enabled as it allows Database Administrators to +execute Data Manipulation Language (DML) statements. + +To enable the postgresql_selinux_unconfined_dbadm SELinux boolean, run the following command: +$ sudo setsebool -P postgresql_selinux_unconfined_dbadm on + + - name: XCCDF Value var_postgresql_selinux_unconfined_dbadm # promote to variable + set_fact: + var_postgresql_selinux_unconfined_dbadm: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_postgresql_selinux_unconfined_dbadm + +- name: Set SELinux boolean postgresql_selinux_unconfined_dbadm accordingly + seboolean: + name: postgresql_selinux_unconfined_dbadm + state: '{{ var_postgresql_selinux_unconfined_dbadm }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_postgresql_selinux_unconfined_dbadm + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_postgresql_selinux_unconfined_dbadm='' + + +setsebool -P postgresql_selinux_unconfined_dbadm $var_postgresql_selinux_unconfined_dbadm + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable the postgresql_selinux_users_ddl SELinux Boolean + By default, the SELinux boolean postgresql_selinux_users_ddl is enabled. +If this setting is disabled, it should be enabled as it allows Database Administrators to +execute Data Definition Language (DDL) statements. + +To enable the postgresql_selinux_users_ddl SELinux boolean, run the following command: +$ sudo setsebool -P postgresql_selinux_users_ddl on + + - name: XCCDF Value var_postgresql_selinux_users_ddl # promote to variable + set_fact: + var_postgresql_selinux_users_ddl: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_postgresql_selinux_users_ddl + +- name: Set SELinux boolean postgresql_selinux_users_ddl accordingly + seboolean: + name: postgresql_selinux_users_ddl + state: '{{ var_postgresql_selinux_users_ddl }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_postgresql_selinux_users_ddl + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_postgresql_selinux_users_ddl='' + + +setsebool -P postgresql_selinux_users_ddl $var_postgresql_selinux_users_ddl + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the pppd_can_insmod SELinux Boolean + By default, the SELinux boolean pppd_can_insmod is disabled. +If this setting is enabled, it should be disabled. + +To disable the pppd_can_insmod SELinux boolean, run the following command: +$ sudo setsebool -P pppd_can_insmod off + + - name: XCCDF Value var_pppd_can_insmod # promote to variable + set_fact: + var_pppd_can_insmod: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_pppd_can_insmod + +- name: Set SELinux boolean pppd_can_insmod accordingly + seboolean: + name: pppd_can_insmod + state: '{{ var_pppd_can_insmod }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_pppd_can_insmod + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_pppd_can_insmod='' + + +setsebool -P pppd_can_insmod $var_pppd_can_insmod + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the pppd_for_user SELinux Boolean + By default, the SELinux boolean pppd_for_user is disabled. +If this setting is enabled, it should be disabled. + +To disable the pppd_for_user SELinux boolean, run the following command: +$ sudo setsebool -P pppd_for_user off + + - name: XCCDF Value var_pppd_for_user # promote to variable + set_fact: + var_pppd_for_user: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_pppd_for_user + +- name: Set SELinux boolean pppd_for_user accordingly + seboolean: + name: pppd_for_user + state: '{{ var_pppd_for_user }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_pppd_for_user + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_pppd_for_user='' + + +setsebool -P pppd_for_user $var_pppd_for_user + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the privoxy_connect_any SELinux Boolean + By default, the SELinux boolean privoxy_connect_any is enabled. +This setting should be disabled. + +To disable the privoxy_connect_any SELinux boolean, run the following command: +$ sudo setsebool -P privoxy_connect_any off + + - name: XCCDF Value var_privoxy_connect_any # promote to variable + set_fact: + var_privoxy_connect_any: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_privoxy_connect_any + +- name: Set SELinux boolean privoxy_connect_any accordingly + seboolean: + name: privoxy_connect_any + state: '{{ var_privoxy_connect_any }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_privoxy_connect_any + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_privoxy_connect_any='' + + +setsebool -P privoxy_connect_any $var_privoxy_connect_any + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the prosody_bind_http_port SELinux Boolean + By default, the SELinux boolean prosody_bind_http_port is disabled. +If this setting is enabled, it should be disabled. + +To disable the prosody_bind_http_port SELinux boolean, run the following command: +$ sudo setsebool -P prosody_bind_http_port off + + - name: XCCDF Value var_prosody_bind_http_port # promote to variable + set_fact: + var_prosody_bind_http_port: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_prosody_bind_http_port + +- name: Set SELinux boolean prosody_bind_http_port accordingly + seboolean: + name: prosody_bind_http_port + state: '{{ var_prosody_bind_http_port }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_prosody_bind_http_port + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_prosody_bind_http_port='' + + +setsebool -P prosody_bind_http_port $var_prosody_bind_http_port + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the puppetagent_manage_all_files SELinux Boolean + By default, the SELinux boolean puppetagent_manage_all_files is disabled. +If this setting is enabled, it should be disabled. + +To disable the puppetagent_manage_all_files SELinux boolean, run the following command: +$ sudo setsebool -P puppetagent_manage_all_files off + + - name: XCCDF Value var_puppetagent_manage_all_files # promote to variable + set_fact: + var_puppetagent_manage_all_files: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_puppetagent_manage_all_files + +- name: Set SELinux boolean puppetagent_manage_all_files accordingly + seboolean: + name: puppetagent_manage_all_files + state: '{{ var_puppetagent_manage_all_files }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_puppetagent_manage_all_files + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_puppetagent_manage_all_files='' + + +setsebool -P puppetagent_manage_all_files $var_puppetagent_manage_all_files + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the puppetmaster_use_db SELinux Boolean + By default, the SELinux boolean puppetmaster_use_db is disabled. +If this setting is enabled, it should be disabled. + +To disable the puppetmaster_use_db SELinux boolean, run the following command: +$ sudo setsebool -P puppetmaster_use_db off + + - name: XCCDF Value var_puppetmaster_use_db # promote to variable + set_fact: + var_puppetmaster_use_db: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_puppetmaster_use_db + +- name: Set SELinux boolean puppetmaster_use_db accordingly + seboolean: + name: puppetmaster_use_db + state: '{{ var_puppetmaster_use_db }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_puppetmaster_use_db + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_puppetmaster_use_db='' + + +setsebool -P puppetmaster_use_db $var_puppetmaster_use_db + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the racoon_read_shadow SELinux Boolean + By default, the SELinux boolean racoon_read_shadow is disabled. +If this setting is enabled, it should be disabled. + +To disable the racoon_read_shadow SELinux boolean, run the following command: +$ sudo setsebool -P racoon_read_shadow off + + - name: XCCDF Value var_racoon_read_shadow # promote to variable + set_fact: + var_racoon_read_shadow: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_racoon_read_shadow + +- name: Set SELinux boolean racoon_read_shadow accordingly + seboolean: + name: racoon_read_shadow + state: '{{ var_racoon_read_shadow }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_racoon_read_shadow + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_racoon_read_shadow='' + + +setsebool -P racoon_read_shadow $var_racoon_read_shadow + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the rsync_anon_write SELinux Boolean + By default, the SELinux boolean rsync_anon_write is disabled. +If this setting is enabled, it should be disabled. + +To disable the rsync_anon_write SELinux boolean, run the following command: +$ sudo setsebool -P rsync_anon_write off + + - name: XCCDF Value var_rsync_anon_write # promote to variable + set_fact: + var_rsync_anon_write: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_rsync_anon_write + +- name: Set SELinux boolean rsync_anon_write accordingly + seboolean: + name: rsync_anon_write + state: '{{ var_rsync_anon_write }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_rsync_anon_write + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_rsync_anon_write='' + + +setsebool -P rsync_anon_write $var_rsync_anon_write + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the rsync_client SELinux Boolean + By default, the SELinux boolean rsync_client is disabled. +If this setting is enabled, it should be disabled. + +To disable the rsync_client SELinux boolean, run the following command: +$ sudo setsebool -P rsync_client off + + - name: XCCDF Value var_rsync_client # promote to variable + set_fact: + var_rsync_client: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_rsync_client + +- name: Set SELinux boolean rsync_client accordingly + seboolean: + name: rsync_client + state: '{{ var_rsync_client }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_rsync_client + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_rsync_client='' + + +setsebool -P rsync_client $var_rsync_client + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the rsync_export_all_ro SELinux Boolean + By default, the SELinux boolean rsync_export_all_ro is disabled. +If this setting is enabled, it should be disabled. + +To disable the rsync_export_all_ro SELinux boolean, run the following command: +$ sudo setsebool -P rsync_export_all_ro off + + - name: XCCDF Value var_rsync_export_all_ro # promote to variable + set_fact: + var_rsync_export_all_ro: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_rsync_export_all_ro + +- name: Set SELinux boolean rsync_export_all_ro accordingly + seboolean: + name: rsync_export_all_ro + state: '{{ var_rsync_export_all_ro }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_rsync_export_all_ro + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_rsync_export_all_ro='' + + +setsebool -P rsync_export_all_ro $var_rsync_export_all_ro + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the rsync_full_access SELinux Boolean + By default, the SELinux boolean rsync_full_access is disabled. +If this setting is enabled, it should be disabled. + +To disable the rsync_full_access SELinux boolean, run the following command: +$ sudo setsebool -P rsync_full_access off + + - name: XCCDF Value var_rsync_full_access # promote to variable + set_fact: + var_rsync_full_access: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_rsync_full_access + +- name: Set SELinux boolean rsync_full_access accordingly + seboolean: + name: rsync_full_access + state: '{{ var_rsync_full_access }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_rsync_full_access + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_rsync_full_access='' + + +setsebool -P rsync_full_access $var_rsync_full_access + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the samba_create_home_dirs SELinux Boolean + By default, the SELinux boolean samba_create_home_dirs is disabled. +If this setting is enabled, it should be disabled. + +To disable the samba_create_home_dirs SELinux boolean, run the following command: +$ sudo setsebool -P samba_create_home_dirs off + + - name: XCCDF Value var_samba_create_home_dirs # promote to variable + set_fact: + var_samba_create_home_dirs: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_samba_create_home_dirs + +- name: Set SELinux boolean samba_create_home_dirs accordingly + seboolean: + name: samba_create_home_dirs + state: '{{ var_samba_create_home_dirs }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_samba_create_home_dirs + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_samba_create_home_dirs='' + + +setsebool -P samba_create_home_dirs $var_samba_create_home_dirs + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the samba_domain_controller SELinux Boolean + By default, the SELinux boolean samba_domain_controller is disabled. +If this setting is enabled, it should be disabled. + +To disable the samba_domain_controller SELinux boolean, run the following command: +$ sudo setsebool -P samba_domain_controller off + + - name: XCCDF Value var_samba_domain_controller # promote to variable + set_fact: + var_samba_domain_controller: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_samba_domain_controller + +- name: Set SELinux boolean samba_domain_controller accordingly + seboolean: + name: samba_domain_controller + state: '{{ var_samba_domain_controller }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_samba_domain_controller + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_samba_domain_controller='' + + +setsebool -P samba_domain_controller $var_samba_domain_controller + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the samba_enable_home_dirs SELinux Boolean + By default, the SELinux boolean samba_enable_home_dirs is disabled. +If this setting is enabled, it should be disabled. + +To disable the samba_enable_home_dirs SELinux boolean, run the following command: +$ sudo setsebool -P samba_enable_home_dirs off + + - name: XCCDF Value var_samba_enable_home_dirs # promote to variable + set_fact: + var_samba_enable_home_dirs: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_samba_enable_home_dirs + +- name: Set SELinux boolean samba_enable_home_dirs accordingly + seboolean: + name: samba_enable_home_dirs + state: '{{ var_samba_enable_home_dirs }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_samba_enable_home_dirs + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_samba_enable_home_dirs='' + + +setsebool -P samba_enable_home_dirs $var_samba_enable_home_dirs + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the samba_export_all_ro SELinux Boolean + By default, the SELinux boolean samba_export_all_ro is disabled. +If this setting is enabled, it should be disabled. + +To disable the samba_export_all_ro SELinux boolean, run the following command: +$ sudo setsebool -P samba_export_all_ro off + + - name: XCCDF Value var_samba_export_all_ro # promote to variable + set_fact: + var_samba_export_all_ro: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_samba_export_all_ro + +- name: Set SELinux boolean samba_export_all_ro accordingly + seboolean: + name: samba_export_all_ro + state: '{{ var_samba_export_all_ro }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_samba_export_all_ro + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_samba_export_all_ro='' + + +setsebool -P samba_export_all_ro $var_samba_export_all_ro + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the samba_export_all_rw SELinux Boolean + By default, the SELinux boolean samba_export_all_rw is disabled. +If this setting is enabled, it should be disabled. + +To disable the samba_export_all_rw SELinux boolean, run the following command: +$ sudo setsebool -P samba_export_all_rw off + + - name: XCCDF Value var_samba_export_all_rw # promote to variable + set_fact: + var_samba_export_all_rw: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_samba_export_all_rw + +- name: Set SELinux boolean samba_export_all_rw accordingly + seboolean: + name: samba_export_all_rw + state: '{{ var_samba_export_all_rw }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_samba_export_all_rw + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_samba_export_all_rw='' + + +setsebool -P samba_export_all_rw $var_samba_export_all_rw + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the samba_load_libgfapi SELinux Boolean + By default, the SELinux boolean samba_load_libgfapi is disabled. +If this setting is enabled, it should be disabled. + +To disable the samba_load_libgfapi SELinux boolean, run the following command: +$ sudo setsebool -P samba_load_libgfapi off + + - name: XCCDF Value var_samba_load_libgfapi # promote to variable + set_fact: + var_samba_load_libgfapi: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_samba_load_libgfapi + +- name: Set SELinux boolean samba_load_libgfapi accordingly + seboolean: + name: samba_load_libgfapi + state: '{{ var_samba_load_libgfapi }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_samba_load_libgfapi + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_samba_load_libgfapi='' + + +setsebool -P samba_load_libgfapi $var_samba_load_libgfapi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the samba_portmapper SELinux Boolean + By default, the SELinux boolean samba_portmapper is disabled. +If this setting is enabled, it should be disabled. + +To disable the samba_portmapper SELinux boolean, run the following command: +$ sudo setsebool -P samba_portmapper off + + - name: XCCDF Value var_samba_portmapper # promote to variable + set_fact: + var_samba_portmapper: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_samba_portmapper + +- name: Set SELinux boolean samba_portmapper accordingly + seboolean: + name: samba_portmapper + state: '{{ var_samba_portmapper }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_samba_portmapper + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_samba_portmapper='' + + +setsebool -P samba_portmapper $var_samba_portmapper + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the samba_run_unconfined SELinux Boolean + By default, the SELinux boolean samba_run_unconfined is disabled. +If this setting is enabled, it should be disabled. + +To disable the samba_run_unconfined SELinux boolean, run the following command: +$ sudo setsebool -P samba_run_unconfined off + + - name: XCCDF Value var_samba_run_unconfined # promote to variable + set_fact: + var_samba_run_unconfined: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_samba_run_unconfined + +- name: Set SELinux boolean samba_run_unconfined accordingly + seboolean: + name: samba_run_unconfined + state: '{{ var_samba_run_unconfined }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_samba_run_unconfined + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_samba_run_unconfined='' + + +setsebool -P samba_run_unconfined $var_samba_run_unconfined + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the samba_share_fusefs SELinux Boolean + By default, the SELinux boolean samba_share_fusefs is disabled. +If this setting is enabled, it should be disabled. + +To disable the samba_share_fusefs SELinux boolean, run the following command: +$ sudo setsebool -P samba_share_fusefs off + + - name: XCCDF Value var_samba_share_fusefs # promote to variable + set_fact: + var_samba_share_fusefs: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_samba_share_fusefs + +- name: Set SELinux boolean samba_share_fusefs accordingly + seboolean: + name: samba_share_fusefs + state: '{{ var_samba_share_fusefs }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_samba_share_fusefs + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_samba_share_fusefs='' + + +setsebool -P samba_share_fusefs $var_samba_share_fusefs + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the samba_share_nfs SELinux Boolean + By default, the SELinux boolean samba_share_nfs is disabled. +If this setting is enabled, it should be disabled. + +To disable the samba_share_nfs SELinux boolean, run the following command: +$ sudo setsebool -P samba_share_nfs off + + - name: XCCDF Value var_samba_share_nfs # promote to variable + set_fact: + var_samba_share_nfs: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_samba_share_nfs + +- name: Set SELinux boolean samba_share_nfs accordingly + seboolean: + name: samba_share_nfs + state: '{{ var_samba_share_nfs }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_samba_share_nfs + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_samba_share_nfs='' + + +setsebool -P samba_share_nfs $var_samba_share_nfs + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the sanlock_use_fusefs SELinux Boolean + By default, the SELinux boolean sanlock_use_fusefs is disabled. +If this setting is enabled, it should be disabled. + +To disable the sanlock_use_fusefs SELinux boolean, run the following command: +$ sudo setsebool -P sanlock_use_fusefs off + + - name: XCCDF Value var_sanlock_use_fusefs # promote to variable + set_fact: + var_sanlock_use_fusefs: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_sanlock_use_fusefs + +- name: Set SELinux boolean sanlock_use_fusefs accordingly + seboolean: + name: sanlock_use_fusefs + state: '{{ var_sanlock_use_fusefs }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_sanlock_use_fusefs + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_sanlock_use_fusefs='' + + +setsebool -P sanlock_use_fusefs $var_sanlock_use_fusefs + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the sanlock_use_nfs SELinux Boolean + By default, the SELinux boolean sanlock_use_nfs is disabled. +If this setting is enabled, it should be disabled. + +To disable the sanlock_use_nfs SELinux boolean, run the following command: +$ sudo setsebool -P sanlock_use_nfs off + + - name: XCCDF Value var_sanlock_use_nfs # promote to variable + set_fact: + var_sanlock_use_nfs: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_sanlock_use_nfs + +- name: Set SELinux boolean sanlock_use_nfs accordingly + seboolean: + name: sanlock_use_nfs + state: '{{ var_sanlock_use_nfs }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_sanlock_use_nfs + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_sanlock_use_nfs='' + + +setsebool -P sanlock_use_nfs $var_sanlock_use_nfs + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the sanlock_use_samba SELinux Boolean + By default, the SELinux boolean sanlock_use_samba is disabled. +If this setting is enabled, it should be disabled. + +To disable the sanlock_use_samba SELinux boolean, run the following command: +$ sudo setsebool -P sanlock_use_samba off + + - name: XCCDF Value var_sanlock_use_samba # promote to variable + set_fact: + var_sanlock_use_samba: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_sanlock_use_samba + +- name: Set SELinux boolean sanlock_use_samba accordingly + seboolean: + name: sanlock_use_samba + state: '{{ var_sanlock_use_samba }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_sanlock_use_samba + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_sanlock_use_samba='' + + +setsebool -P sanlock_use_samba $var_sanlock_use_samba + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the saslauthd_read_shadow SELinux Boolean + By default, the SELinux boolean saslauthd_read_shadow is disabled. +If this setting is enabled, it should be disabled. + +To disable the saslauthd_read_shadow SELinux boolean, run the following command: +$ sudo setsebool -P saslauthd_read_shadow off + + - name: XCCDF Value var_saslauthd_read_shadow # promote to variable + set_fact: + var_saslauthd_read_shadow: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_saslauthd_read_shadow + +- name: Set SELinux boolean saslauthd_read_shadow accordingly + seboolean: + name: saslauthd_read_shadow + state: '{{ var_saslauthd_read_shadow }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_saslauthd_read_shadow + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_saslauthd_read_shadow='' + + +setsebool -P saslauthd_read_shadow $var_saslauthd_read_shadow + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable the secadm_exec_content SELinux Boolean + By default, the SELinux boolean secadm_exec_content is enabled. +If this setting is disabled, it should be enabled. + +To enable the secadm_exec_content SELinux boolean, run the following command: +$ sudo setsebool -P secadm_exec_content on + + - name: XCCDF Value var_secadm_exec_content # promote to variable + set_fact: + var_secadm_exec_content: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_secadm_exec_content + +- name: Set SELinux boolean secadm_exec_content accordingly + seboolean: + name: secadm_exec_content + state: '{{ var_secadm_exec_content }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_secadm_exec_content + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_secadm_exec_content='' + + +setsebool -P secadm_exec_content $var_secadm_exec_content + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the secure_mode SELinux Boolean + By default, the SELinux boolean secure_mode is disabled. +If this setting is enabled, it should be disabled. + +To disable the secure_mode SELinux boolean, run the following command: +$ sudo setsebool -P secure_mode off + + - name: XCCDF Value var_secure_mode # promote to variable + set_fact: + var_secure_mode: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_secure_mode + +- name: Set SELinux boolean secure_mode accordingly + seboolean: + name: secure_mode + state: '{{ var_secure_mode }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_secure_mode + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_secure_mode='' + + +setsebool -P secure_mode $var_secure_mode + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Configure the secure_mode_insmod SELinux Boolean + By default, the SELinux boolean secure_mode_insmod is disabled. +This setting should be configured to . + +To set the secure_mode_insmod SELinux boolean, run the following command: +$ sudo setsebool -P secure_mode_insmod + BP28(R67) + + CCE-83310-3 + - name: XCCDF Value var_secure_mode_insmod # promote to variable + set_fact: + var_secure_mode_insmod: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83310-3 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_secure_mode_insmod + +- name: Set SELinux boolean secure_mode_insmod accordingly + seboolean: + name: secure_mode_insmod + state: '{{ var_secure_mode_insmod }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83310-3 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_secure_mode_insmod + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_secure_mode_insmod='' + + +setsebool -P secure_mode_insmod $var_secure_mode_insmod + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the secure_mode_policyload SELinux Boolean + By default, the SELinux boolean secure_mode_policyload is disabled. +If this setting is enabled, it should be disabled. + +To disable the secure_mode_policyload SELinux boolean, run the following command: +$ sudo setsebool -P secure_mode_policyload off + + - name: XCCDF Value var_secure_mode_policyload # promote to variable + set_fact: + var_secure_mode_policyload: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_secure_mode_policyload + +- name: Set SELinux boolean secure_mode_policyload accordingly + seboolean: + name: secure_mode_policyload + state: '{{ var_secure_mode_policyload }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_secure_mode_policyload + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_secure_mode_policyload='' + + +setsebool -P secure_mode_policyload $var_secure_mode_policyload + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Configure the selinuxuser_direct_dri_enabled SELinux Boolean + By default, the SELinux boolean selinuxuser_direct_dri_enabled is enabled. +If XWindows is not installed or used on the system, this setting should be disabled. +Otherwise, enable it. + +To disable the selinuxuser_direct_dri_enabled SELinux boolean, run the following command: +$ sudo setsebool -P selinuxuser_direct_dri_enabled off + + - name: XCCDF Value var_selinuxuser_direct_dri_enabled # promote to variable + set_fact: + var_selinuxuser_direct_dri_enabled: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_selinuxuser_direct_dri_enabled + +- name: Set SELinux boolean selinuxuser_direct_dri_enabled accordingly + seboolean: + name: selinuxuser_direct_dri_enabled + state: '{{ var_selinuxuser_direct_dri_enabled }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_selinuxuser_direct_dri_enabled + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_selinuxuser_direct_dri_enabled='' + + +setsebool -P selinuxuser_direct_dri_enabled $var_selinuxuser_direct_dri_enabled + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the selinuxuser_execheap SELinux Boolean + By default, the SELinux boolean selinuxuser_execheap is disabled. +When enabled this boolean is enabled it allows selinuxusers to execute code from the heap. +If this setting is enabled, it should be disabled. + +To disable the selinuxuser_execheap SELinux boolean, run the following command: +$ sudo setsebool -P selinuxuser_execheap off + BP28(R67) + 164.308(a)(1)(ii)(D) + 164.308(a)(3) + 164.308(a)(4) + 164.310(b) + 164.310(c) + 164.312(a) + 164.312(e) + Disabling code execution from the heap blocks buffer overflow attacks. + CCE-80949-1 + - name: XCCDF Value var_selinuxuser_execheap # promote to variable + set_fact: + var_selinuxuser_execheap: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80949-1 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_selinuxuser_execheap + +- name: Set SELinux boolean selinuxuser_execheap accordingly + seboolean: + name: selinuxuser_execheap + state: '{{ var_selinuxuser_execheap }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80949-1 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_selinuxuser_execheap + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_selinuxuser_execheap='' + + +setsebool -P selinuxuser_execheap $var_selinuxuser_execheap + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable the selinuxuser_execmod SELinux Boolean + By default, the SELinux boolean selinuxuser_execmod is enabled. +If this setting is disabled, it should be enabled. + +To enable the selinuxuser_execmod SELinux boolean, run the following command: +$ sudo setsebool -P selinuxuser_execmod on + 164.308(a)(1)(ii)(D) + 164.308(a)(3) + 164.308(a)(4) + 164.310(b) + 164.310(c) + 164.312(a) + 164.312(e) + + CCE-80950-9 + - name: XCCDF Value var_selinuxuser_execmod # promote to variable + set_fact: + var_selinuxuser_execmod: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80950-9 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_selinuxuser_execmod + +- name: Set SELinux boolean selinuxuser_execmod accordingly + seboolean: + name: selinuxuser_execmod + state: '{{ var_selinuxuser_execmod }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80950-9 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_selinuxuser_execmod + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_selinuxuser_execmod='' + + +setsebool -P selinuxuser_execmod $var_selinuxuser_execmod + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + disable the selinuxuser_execstack SELinux Boolean + By default, the SELinux boolean selinuxuser_execstack is enabled. +This setting should be disabled as unconfined executables should not be able +to make their stack executable. + +To disable the selinuxuser_execstack SELinux boolean, run the following command: +$ sudo setsebool -P selinuxuser_execstack off + BP28(R67) + 164.308(a)(1)(ii)(D) + 164.308(a)(3) + 164.308(a)(4) + 164.310(b) + 164.310(c) + 164.312(a) + 164.312(e) + Disabling code execution from the stack blocks buffer overflow attacks. + CCE-80951-7 + - name: XCCDF Value var_selinuxuser_execstack # promote to variable + set_fact: + var_selinuxuser_execstack: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80951-7 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_selinuxuser_execstack + +- name: Set SELinux boolean selinuxuser_execstack accordingly + seboolean: + name: selinuxuser_execstack + state: '{{ var_selinuxuser_execstack }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80951-7 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_selinuxuser_execstack + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_selinuxuser_execstack='' + + +setsebool -P selinuxuser_execstack $var_selinuxuser_execstack + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the selinuxuser_mysql_connect_enabled SELinux Boolean + By default, the SELinux boolean selinuxuser_mysql_connect_enabled is disabled. +If this setting is enabled, it should be disabled. + +To disable the selinuxuser_mysql_connect_enabled SELinux boolean, run the following command: +$ sudo setsebool -P selinuxuser_mysql_connect_enabled off + + - name: XCCDF Value var_selinuxuser_mysql_connect_enabled # promote to variable + set_fact: + var_selinuxuser_mysql_connect_enabled: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_selinuxuser_mysql_connect_enabled + +- name: Set SELinux boolean selinuxuser_mysql_connect_enabled accordingly + seboolean: + name: selinuxuser_mysql_connect_enabled + state: '{{ var_selinuxuser_mysql_connect_enabled }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_selinuxuser_mysql_connect_enabled + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_selinuxuser_mysql_connect_enabled='' + + +setsebool -P selinuxuser_mysql_connect_enabled $var_selinuxuser_mysql_connect_enabled + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable the selinuxuser_ping SELinux Boolean + By default, the SELinux boolean selinuxuser_ping is enabled. +If this setting is disabled, it should be enabled as it allows confined users +to use ping and traceroute which is helpful for network troubleshooting. + +To enable the selinuxuser_ping SELinux boolean, run the following command: +$ sudo setsebool -P selinuxuser_ping on + + - name: XCCDF Value var_selinuxuser_ping # promote to variable + set_fact: + var_selinuxuser_ping: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_selinuxuser_ping + +- name: Set SELinux boolean selinuxuser_ping accordingly + seboolean: + name: selinuxuser_ping + state: '{{ var_selinuxuser_ping }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_selinuxuser_ping + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_selinuxuser_ping='' + + +setsebool -P selinuxuser_ping $var_selinuxuser_ping + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the selinuxuser_postgresql_connect_enabled SELinux Boolean + By default, the SELinux boolean selinuxuser_postgresql_connect_enabled is disabled. +If this setting is enabled, it should be disabled. + +To disable the selinuxuser_postgresql_connect_enabled SELinux boolean, run the following command: +$ sudo setsebool -P selinuxuser_postgresql_connect_enabled off + + - name: XCCDF Value var_selinuxuser_postgresql_connect_enabled # promote to variable + set_fact: + var_selinuxuser_postgresql_connect_enabled: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_selinuxuser_postgresql_connect_enabled + +- name: Set SELinux boolean selinuxuser_postgresql_connect_enabled accordingly + seboolean: + name: selinuxuser_postgresql_connect_enabled + state: '{{ var_selinuxuser_postgresql_connect_enabled }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_selinuxuser_postgresql_connect_enabled + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_selinuxuser_postgresql_connect_enabled='' + + +setsebool -P selinuxuser_postgresql_connect_enabled $var_selinuxuser_postgresql_connect_enabled + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the selinuxuser_rw_noexattrfile SELinux Boolean + By default, the SELinux boolean selinuxuser_rw_noexattrfile is enabled. +This setting should be disabled as users should not be able to read/write files +on filesystems that do not have extended attributes e.g. FAT, CDROM, FLOPPY, etc. + +To disable the selinuxuser_rw_noexattrfile SELinux boolean, run the following command: +$ sudo setsebool -P selinuxuser_rw_noexattrfile off + + - name: XCCDF Value var_selinuxuser_rw_noexattrfile # promote to variable + set_fact: + var_selinuxuser_rw_noexattrfile: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_selinuxuser_rw_noexattrfile + +- name: Set SELinux boolean selinuxuser_rw_noexattrfile accordingly + seboolean: + name: selinuxuser_rw_noexattrfile + state: '{{ var_selinuxuser_rw_noexattrfile }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_selinuxuser_rw_noexattrfile + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_selinuxuser_rw_noexattrfile='' + + +setsebool -P selinuxuser_rw_noexattrfile $var_selinuxuser_rw_noexattrfile + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the selinuxuser_share_music SELinux Boolean + By default, the SELinux boolean selinuxuser_share_music is disabled. +If this setting is enabled, it should be disabled. + +To disable the selinuxuser_share_music SELinux boolean, run the following command: +$ sudo setsebool -P selinuxuser_share_music off + + - name: XCCDF Value var_selinuxuser_share_music # promote to variable + set_fact: + var_selinuxuser_share_music: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_selinuxuser_share_music + +- name: Set SELinux boolean selinuxuser_share_music accordingly + seboolean: + name: selinuxuser_share_music + state: '{{ var_selinuxuser_share_music }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_selinuxuser_share_music + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_selinuxuser_share_music='' + + +setsebool -P selinuxuser_share_music $var_selinuxuser_share_music + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the selinuxuser_tcp_server SELinux Boolean + By default, the SELinux boolean selinuxuser_tcp_server is disabled. +If this setting is enabled, it should be disabled. + +To disable the selinuxuser_tcp_server SELinux boolean, run the following command: +$ sudo setsebool -P selinuxuser_tcp_server off + + - name: XCCDF Value var_selinuxuser_tcp_server # promote to variable + set_fact: + var_selinuxuser_tcp_server: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_selinuxuser_tcp_server + +- name: Set SELinux boolean selinuxuser_tcp_server accordingly + seboolean: + name: selinuxuser_tcp_server + state: '{{ var_selinuxuser_tcp_server }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_selinuxuser_tcp_server + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_selinuxuser_tcp_server='' + + +setsebool -P selinuxuser_tcp_server $var_selinuxuser_tcp_server + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the selinuxuser_udp_server SELinux Boolean + By default, the SELinux boolean selinuxuser_udp_server is disabled. +If this setting is enabled, it should be disabled. + +To disable the selinuxuser_udp_server SELinux boolean, run the following command: +$ sudo setsebool -P selinuxuser_udp_server off + + - name: XCCDF Value var_selinuxuser_udp_server # promote to variable + set_fact: + var_selinuxuser_udp_server: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_selinuxuser_udp_server + +- name: Set SELinux boolean selinuxuser_udp_server accordingly + seboolean: + name: selinuxuser_udp_server + state: '{{ var_selinuxuser_udp_server }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_selinuxuser_udp_server + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_selinuxuser_udp_server='' + + +setsebool -P selinuxuser_udp_server $var_selinuxuser_udp_server + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the selinuxuser_use_ssh_chroot SELinux Boolean + By default, the SELinux boolean selinuxuser_use_ssh_chroot is disabled. +If this setting is enabled, it should be disabled. + +To disable the selinuxuser_use_ssh_chroot SELinux boolean, run the following command: +$ sudo setsebool -P selinuxuser_use_ssh_chroot off + + - name: XCCDF Value var_selinuxuser_use_ssh_chroot # promote to variable + set_fact: + var_selinuxuser_use_ssh_chroot: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_selinuxuser_use_ssh_chroot + +- name: Set SELinux boolean selinuxuser_use_ssh_chroot accordingly + seboolean: + name: selinuxuser_use_ssh_chroot + state: '{{ var_selinuxuser_use_ssh_chroot }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_selinuxuser_use_ssh_chroot + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_selinuxuser_use_ssh_chroot='' + + +setsebool -P selinuxuser_use_ssh_chroot $var_selinuxuser_use_ssh_chroot + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the sge_domain_can_network_connect SELinux Boolean + By default, the SELinux boolean sge_domain_can_network_connect is disabled. +If this setting is enabled, it should be disabled. + +To disable the sge_domain_can_network_connect SELinux boolean, run the following command: +$ sudo setsebool -P sge_domain_can_network_connect off + + - name: XCCDF Value var_sge_domain_can_network_connect # promote to variable + set_fact: + var_sge_domain_can_network_connect: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_sge_domain_can_network_connect + +- name: Set SELinux boolean sge_domain_can_network_connect accordingly + seboolean: + name: sge_domain_can_network_connect + state: '{{ var_sge_domain_can_network_connect }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_sge_domain_can_network_connect + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_sge_domain_can_network_connect='' + + +setsebool -P sge_domain_can_network_connect $var_sge_domain_can_network_connect + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the sge_use_nfs SELinux Boolean + By default, the SELinux boolean sge_use_nfs is disabled. +If this setting is enabled, it should be disabled. + +To disable the sge_use_nfs SELinux boolean, run the following command: +$ sudo setsebool -P sge_use_nfs off + + - name: XCCDF Value var_sge_use_nfs # promote to variable + set_fact: + var_sge_use_nfs: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_sge_use_nfs + +- name: Set SELinux boolean sge_use_nfs accordingly + seboolean: + name: sge_use_nfs + state: '{{ var_sge_use_nfs }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_sge_use_nfs + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_sge_use_nfs='' + + +setsebool -P sge_use_nfs $var_sge_use_nfs + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the smartmon_3ware SELinux Boolean + By default, the SELinux boolean smartmon_3ware is disabled. +If this setting is enabled, it should be disabled. + +To disable the smartmon_3ware SELinux boolean, run the following command: +$ sudo setsebool -P smartmon_3ware off + + - name: XCCDF Value var_smartmon_3ware # promote to variable + set_fact: + var_smartmon_3ware: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_smartmon_3ware + +- name: Set SELinux boolean smartmon_3ware accordingly + seboolean: + name: smartmon_3ware + state: '{{ var_smartmon_3ware }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_smartmon_3ware + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_smartmon_3ware='' + + +setsebool -P smartmon_3ware $var_smartmon_3ware + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the smbd_anon_write SELinux Boolean + By default, the SELinux boolean smbd_anon_write is disabled. +If this setting is enabled, it should be disabled. + +To disable the smbd_anon_write SELinux boolean, run the following command: +$ sudo setsebool -P smbd_anon_write off + + - name: XCCDF Value var_smbd_anon_write # promote to variable + set_fact: + var_smbd_anon_write: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_smbd_anon_write + +- name: Set SELinux boolean smbd_anon_write accordingly + seboolean: + name: smbd_anon_write + state: '{{ var_smbd_anon_write }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_smbd_anon_write + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_smbd_anon_write='' + + +setsebool -P smbd_anon_write $var_smbd_anon_write + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the spamassassin_can_network SELinux Boolean + By default, the SELinux boolean spamassassin_can_network is disabled. +If this setting is enabled, it should be disabled. + +To disable the spamassassin_can_network SELinux boolean, run the following command: +$ sudo setsebool -P spamassassin_can_network off + + - name: XCCDF Value var_spamassassin_can_network # promote to variable + set_fact: + var_spamassassin_can_network: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_spamassassin_can_network + +- name: Set SELinux boolean spamassassin_can_network accordingly + seboolean: + name: spamassassin_can_network + state: '{{ var_spamassassin_can_network }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_spamassassin_can_network + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_spamassassin_can_network='' + + +setsebool -P spamassassin_can_network $var_spamassassin_can_network + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable the spamd_enable_home_dirs SELinux Boolean + By default, the SELinux boolean spamd_enable_home_dirs is enabled. +If this setting is disabled, it should be enabled. + +To enable the spamd_enable_home_dirs SELinux boolean, run the following command: +$ sudo setsebool -P spamd_enable_home_dirs on + + - name: XCCDF Value var_spamd_enable_home_dirs # promote to variable + set_fact: + var_spamd_enable_home_dirs: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_spamd_enable_home_dirs + +- name: Set SELinux boolean spamd_enable_home_dirs accordingly + seboolean: + name: spamd_enable_home_dirs + state: '{{ var_spamd_enable_home_dirs }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_spamd_enable_home_dirs + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_spamd_enable_home_dirs='' + + +setsebool -P spamd_enable_home_dirs $var_spamd_enable_home_dirs + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the squid_connect_any SELinux Boolean + By default, the SELinux boolean squid_connect_any is enabled. +This setting should be disabled as squid should only connect on specified +ports. + +To disable the squid_connect_any SELinux boolean, run the following command: +$ sudo setsebool -P squid_connect_any off + + - name: XCCDF Value var_squid_connect_any # promote to variable + set_fact: + var_squid_connect_any: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_squid_connect_any + +- name: Set SELinux boolean squid_connect_any accordingly + seboolean: + name: squid_connect_any + state: '{{ var_squid_connect_any }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_squid_connect_any + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_squid_connect_any='' + + +setsebool -P squid_connect_any $var_squid_connect_any + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the squid_use_tproxy SELinux Boolean + By default, the SELinux boolean squid_use_tproxy is disabled. +If this setting is enabled, it should be disabled. + +To disable the squid_use_tproxy SELinux boolean, run the following command: +$ sudo setsebool -P squid_use_tproxy off + + - name: XCCDF Value var_squid_use_tproxy # promote to variable + set_fact: + var_squid_use_tproxy: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_squid_use_tproxy + +- name: Set SELinux boolean squid_use_tproxy accordingly + seboolean: + name: squid_use_tproxy + state: '{{ var_squid_use_tproxy }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_squid_use_tproxy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_squid_use_tproxy='' + + +setsebool -P squid_use_tproxy $var_squid_use_tproxy + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the ssh_chroot_rw_homedirs SELinux Boolean + By default, the SELinux boolean ssh_chroot_rw_homedirs is disabled. +If this setting is enabled, it should be disabled. + +To disable the ssh_chroot_rw_homedirs SELinux boolean, run the following command: +$ sudo setsebool -P ssh_chroot_rw_homedirs off + + - name: XCCDF Value var_ssh_chroot_rw_homedirs # promote to variable + set_fact: + var_ssh_chroot_rw_homedirs: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_ssh_chroot_rw_homedirs + +- name: Set SELinux boolean ssh_chroot_rw_homedirs accordingly + seboolean: + name: ssh_chroot_rw_homedirs + state: '{{ var_ssh_chroot_rw_homedirs }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_ssh_chroot_rw_homedirs + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_ssh_chroot_rw_homedirs='' + + +setsebool -P ssh_chroot_rw_homedirs $var_ssh_chroot_rw_homedirs + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the ssh_keysign SELinux Boolean + By default, the SELinux boolean ssh_keysign is disabled. +If this setting is enabled, it should be disabled. + +To disable the ssh_keysign SELinux boolean, run the following command: +$ sudo setsebool -P ssh_keysign off + + - name: XCCDF Value var_ssh_keysign # promote to variable + set_fact: + var_ssh_keysign: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_ssh_keysign + +- name: Set SELinux boolean ssh_keysign accordingly + seboolean: + name: ssh_keysign + state: '{{ var_ssh_keysign }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_ssh_keysign + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_ssh_keysign='' + + +setsebool -P ssh_keysign $var_ssh_keysign + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the ssh_sysadm_login SELinux Boolean + By default, the SELinux boolean ssh_sysadm_login is disabled. +If this setting is enabled, it should be disabled. + +To disable the ssh_sysadm_login SELinux boolean, run the following command: +$ sudo setsebool -P ssh_sysadm_login off + BP28(R67) + CCI-002165 + CCI-002235 + SRG-OS-000324-GPOS-00125 + Preventing non-privileged users from executing privileged functions mitigates +the risk that unauthorized individuals or processes may gain unnecessary access +to information or privileges. + +Privileged functions include, for example, establishing accounts, performing +system integrity checks, or administering cryptographic key management +activities. Non-privileged users are individuals who do not possess appropriate +authorizations. Circumventing intrusion detection and prevention mechanisms or +malicious code protection mechanisms are examples of privileged functions that +require protection from non-privileged users. + CCE-83311-1 + - name: XCCDF Value var_ssh_sysadm_login # promote to variable + set_fact: + var_ssh_sysadm_login: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83311-1 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_ssh_sysadm_login + +- name: Set SELinux boolean ssh_sysadm_login accordingly + seboolean: + name: ssh_sysadm_login + state: '{{ var_ssh_sysadm_login }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83311-1 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_ssh_sysadm_login + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_ssh_sysadm_login='' + + +setsebool -P ssh_sysadm_login $var_ssh_sysadm_login + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable the staff_exec_content SELinux Boolean + By default, the SELinux boolean staff_exec_content is enabled. +If this setting is disabled, it should be enabled. + +To enable the staff_exec_content SELinux boolean, run the following command: +$ sudo setsebool -P staff_exec_content on + + - name: XCCDF Value var_staff_exec_content # promote to variable + set_fact: + var_staff_exec_content: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_staff_exec_content + +- name: Set SELinux boolean staff_exec_content accordingly + seboolean: + name: staff_exec_content + state: '{{ var_staff_exec_content }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_staff_exec_content + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_staff_exec_content='' + + +setsebool -P staff_exec_content $var_staff_exec_content + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the staff_use_svirt SELinux Boolean + By default, the SELinux boolean staff_use_svirt is disabled. +If this setting is enabled, it should be disabled. + +To disable the staff_use_svirt SELinux boolean, run the following command: +$ sudo setsebool -P staff_use_svirt off + + - name: XCCDF Value var_staff_use_svirt # promote to variable + set_fact: + var_staff_use_svirt: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_staff_use_svirt + +- name: Set SELinux boolean staff_use_svirt accordingly + seboolean: + name: staff_use_svirt + state: '{{ var_staff_use_svirt }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_staff_use_svirt + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_staff_use_svirt='' + + +setsebool -P staff_use_svirt $var_staff_use_svirt + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the swift_can_network SELinux Boolean + By default, the SELinux boolean swift_can_network is disabled. +If this setting is enabled, it should be disabled. + +To disable the swift_can_network SELinux boolean, run the following command: +$ sudo setsebool -P swift_can_network off + + - name: XCCDF Value var_swift_can_network # promote to variable + set_fact: + var_swift_can_network: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_swift_can_network + +- name: Set SELinux boolean swift_can_network accordingly + seboolean: + name: swift_can_network + state: '{{ var_swift_can_network }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_swift_can_network + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_swift_can_network='' + + +setsebool -P swift_can_network $var_swift_can_network + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable the sysadm_exec_content SELinux Boolean + By default, the SELinux boolean sysadm_exec_content is enabled. +If this setting is disabled, it should be enabled. + +To enable the sysadm_exec_content SELinux boolean, run the following command: +$ sudo setsebool -P sysadm_exec_content on + + - name: XCCDF Value var_sysadm_exec_content # promote to variable + set_fact: + var_sysadm_exec_content: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_sysadm_exec_content + +- name: Set SELinux boolean sysadm_exec_content accordingly + seboolean: + name: sysadm_exec_content + state: '{{ var_sysadm_exec_content }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_sysadm_exec_content + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_sysadm_exec_content='' + + +setsebool -P sysadm_exec_content $var_sysadm_exec_content + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the telepathy_connect_all_ports SELinux Boolean + By default, the SELinux boolean telepathy_connect_all_ports is disabled. +If this setting is enabled, it should be disabled. + +To disable the telepathy_connect_all_ports SELinux boolean, run the following command: +$ sudo setsebool -P telepathy_connect_all_ports off + + - name: XCCDF Value var_telepathy_connect_all_ports # promote to variable + set_fact: + var_telepathy_connect_all_ports: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_telepathy_connect_all_ports + +- name: Set SELinux boolean telepathy_connect_all_ports accordingly + seboolean: + name: telepathy_connect_all_ports + state: '{{ var_telepathy_connect_all_ports }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_telepathy_connect_all_ports + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_telepathy_connect_all_ports='' + + +setsebool -P telepathy_connect_all_ports $var_telepathy_connect_all_ports + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the telepathy_tcp_connect_generic_network_ports SELinux Boolean + By default, the SELinux boolean telepathy_tcp_connect_generic_network_ports is enabled. +This setting should be disabled as telepathy should not connect to any generic network +ports. + +To disable the telepathy_tcp_connect_generic_network_ports SELinux boolean, run the following command: +$ sudo setsebool -P telepathy_tcp_connect_generic_network_ports off + + - name: XCCDF Value var_telepathy_tcp_connect_generic_network_ports # promote to variable + set_fact: + var_telepathy_tcp_connect_generic_network_ports: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_telepathy_tcp_connect_generic_network_ports + +- name: Set SELinux boolean telepathy_tcp_connect_generic_network_ports accordingly + seboolean: + name: telepathy_tcp_connect_generic_network_ports + state: '{{ var_telepathy_tcp_connect_generic_network_ports }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_telepathy_tcp_connect_generic_network_ports + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_telepathy_tcp_connect_generic_network_ports='' + + +setsebool -P telepathy_tcp_connect_generic_network_ports $var_telepathy_tcp_connect_generic_network_ports + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the tftp_anon_write SELinux Boolean + By default, the SELinux boolean tftp_anon_write is disabled. +If this setting is enabled, it should be disabled. + +To disable the tftp_anon_write SELinux boolean, run the following command: +$ sudo setsebool -P tftp_anon_write off + + - name: XCCDF Value var_tftp_anon_write # promote to variable + set_fact: + var_tftp_anon_write: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_tftp_anon_write + +- name: Set SELinux boolean tftp_anon_write accordingly + seboolean: + name: tftp_anon_write + state: '{{ var_tftp_anon_write }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_tftp_anon_write + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_tftp_anon_write='' + + +setsebool -P tftp_anon_write $var_tftp_anon_write + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the tftp_home_dir SELinux Boolean + By default, the SELinux boolean tftp_home_dir is disabled. +If this setting is enabled, it should be disabled. + +To disable the tftp_home_dir SELinux boolean, run the following command: +$ sudo setsebool -P tftp_home_dir off + + - name: XCCDF Value var_tftp_home_dir # promote to variable + set_fact: + var_tftp_home_dir: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_tftp_home_dir + +- name: Set SELinux boolean tftp_home_dir accordingly + seboolean: + name: tftp_home_dir + state: '{{ var_tftp_home_dir }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_tftp_home_dir + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_tftp_home_dir='' + + +setsebool -P tftp_home_dir $var_tftp_home_dir + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the tmpreaper_use_nfs SELinux Boolean + By default, the SELinux boolean tmpreaper_use_nfs is disabled. +If this setting is enabled, it should be disabled. + +To disable the tmpreaper_use_nfs SELinux boolean, run the following command: +$ sudo setsebool -P tmpreaper_use_nfs off + + - name: XCCDF Value var_tmpreaper_use_nfs # promote to variable + set_fact: + var_tmpreaper_use_nfs: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_tmpreaper_use_nfs + +- name: Set SELinux boolean tmpreaper_use_nfs accordingly + seboolean: + name: tmpreaper_use_nfs + state: '{{ var_tmpreaper_use_nfs }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_tmpreaper_use_nfs + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_tmpreaper_use_nfs='' + + +setsebool -P tmpreaper_use_nfs $var_tmpreaper_use_nfs + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the tmpreaper_use_samba SELinux Boolean + By default, the SELinux boolean tmpreaper_use_samba is disabled. +If this setting is enabled, it should be disabled. + +To disable the tmpreaper_use_samba SELinux boolean, run the following command: +$ sudo setsebool -P tmpreaper_use_samba off + + - name: XCCDF Value var_tmpreaper_use_samba # promote to variable + set_fact: + var_tmpreaper_use_samba: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_tmpreaper_use_samba + +- name: Set SELinux boolean tmpreaper_use_samba accordingly + seboolean: + name: tmpreaper_use_samba + state: '{{ var_tmpreaper_use_samba }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_tmpreaper_use_samba + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_tmpreaper_use_samba='' + + +setsebool -P tmpreaper_use_samba $var_tmpreaper_use_samba + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the tor_bind_all_unreserved_ports SELinux Boolean + By default, the SELinux boolean tor_bind_all_unreserved_ports is disabled. +If this setting is enabled, it should be disabled. + +To disable the tor_bind_all_unreserved_ports SELinux boolean, run the following command: +$ sudo setsebool -P tor_bind_all_unreserved_ports off + + - name: XCCDF Value var_tor_bind_all_unreserved_ports # promote to variable + set_fact: + var_tor_bind_all_unreserved_ports: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_tor_bind_all_unreserved_ports + +- name: Set SELinux boolean tor_bind_all_unreserved_ports accordingly + seboolean: + name: tor_bind_all_unreserved_ports + state: '{{ var_tor_bind_all_unreserved_ports }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_tor_bind_all_unreserved_ports + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_tor_bind_all_unreserved_ports='' + + +setsebool -P tor_bind_all_unreserved_ports $var_tor_bind_all_unreserved_ports + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the tor_can_network_relay SELinux Boolean + By default, the SELinux boolean tor_can_network_relay is disabled. +If this setting is enabled, it should be disabled. + +To disable the tor_can_network_relay SELinux boolean, run the following command: +$ sudo setsebool -P tor_can_network_relay off + + - name: XCCDF Value var_tor_can_network_relay # promote to variable + set_fact: + var_tor_can_network_relay: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_tor_can_network_relay + +- name: Set SELinux boolean tor_can_network_relay accordingly + seboolean: + name: tor_can_network_relay + state: '{{ var_tor_can_network_relay }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_tor_can_network_relay + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_tor_can_network_relay='' + + +setsebool -P tor_can_network_relay $var_tor_can_network_relay + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable the unconfined_chrome_sandbox_transition SELinux Boolean + By default, the SELinux boolean unconfined_chrome_sandbox_transition is enabled. +If this setting is disabled, it should be enabled. + +To enable the unconfined_chrome_sandbox_transition SELinux boolean, run the following command: +$ sudo setsebool -P unconfined_chrome_sandbox_transition on + + - name: XCCDF Value var_unconfined_chrome_sandbox_transition # promote to variable + set_fact: + var_unconfined_chrome_sandbox_transition: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_unconfined_chrome_sandbox_transition + +- name: Set SELinux boolean unconfined_chrome_sandbox_transition accordingly + seboolean: + name: unconfined_chrome_sandbox_transition + state: '{{ var_unconfined_chrome_sandbox_transition }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_unconfined_chrome_sandbox_transition + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_unconfined_chrome_sandbox_transition='' + + +setsebool -P unconfined_chrome_sandbox_transition $var_unconfined_chrome_sandbox_transition + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable the unconfined_login SELinux Boolean + By default, the SELinux boolean unconfined_login is enabled. +If this setting is disabled, it should be enabled. + +To enable the unconfined_login SELinux boolean, run the following command: +$ sudo setsebool -P unconfined_login on + + - name: XCCDF Value var_unconfined_login # promote to variable + set_fact: + var_unconfined_login: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_unconfined_login + +- name: Set SELinux boolean unconfined_login accordingly + seboolean: + name: unconfined_login + state: '{{ var_unconfined_login }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_unconfined_login + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_unconfined_login='' + + +setsebool -P unconfined_login $var_unconfined_login + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable the unconfined_mozilla_plugin_transition SELinux Boolean + By default, the SELinux boolean unconfined_mozilla_plugin_transition is enabled. +If this setting is disabled, it should be enabled. + +To enable the unconfined_mozilla_plugin_transition SELinux boolean, run the following command: +$ sudo setsebool -P unconfined_mozilla_plugin_transition on + + - name: XCCDF Value var_unconfined_mozilla_plugin_transition # promote to variable + set_fact: + var_unconfined_mozilla_plugin_transition: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_unconfined_mozilla_plugin_transition + +- name: Set SELinux boolean unconfined_mozilla_plugin_transition accordingly + seboolean: + name: unconfined_mozilla_plugin_transition + state: '{{ var_unconfined_mozilla_plugin_transition }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_unconfined_mozilla_plugin_transition + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_unconfined_mozilla_plugin_transition='' + + +setsebool -P unconfined_mozilla_plugin_transition $var_unconfined_mozilla_plugin_transition + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the unprivuser_use_svirt SELinux Boolean + By default, the SELinux boolean unprivuser_use_svirt is disabled. +If this setting is enabled, it should be disabled. + +To disable the unprivuser_use_svirt SELinux boolean, run the following command: +$ sudo setsebool -P unprivuser_use_svirt off + + - name: XCCDF Value var_unprivuser_use_svirt # promote to variable + set_fact: + var_unprivuser_use_svirt: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_unprivuser_use_svirt + +- name: Set SELinux boolean unprivuser_use_svirt accordingly + seboolean: + name: unprivuser_use_svirt + state: '{{ var_unprivuser_use_svirt }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_unprivuser_use_svirt + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_unprivuser_use_svirt='' + + +setsebool -P unprivuser_use_svirt $var_unprivuser_use_svirt + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the use_ecryptfs_home_dirs SELinux Boolean + By default, the SELinux boolean use_ecryptfs_home_dirs is disabled. +If this setting is enabled, it should be disabled. + +To disable the use_ecryptfs_home_dirs SELinux boolean, run the following command: +$ sudo setsebool -P use_ecryptfs_home_dirs off + + - name: XCCDF Value var_use_ecryptfs_home_dirs # promote to variable + set_fact: + var_use_ecryptfs_home_dirs: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_use_ecryptfs_home_dirs + +- name: Set SELinux boolean use_ecryptfs_home_dirs accordingly + seboolean: + name: use_ecryptfs_home_dirs + state: '{{ var_use_ecryptfs_home_dirs }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_use_ecryptfs_home_dirs + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_use_ecryptfs_home_dirs='' + + +setsebool -P use_ecryptfs_home_dirs $var_use_ecryptfs_home_dirs + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the use_fusefs_home_dirs SELinux Boolean + By default, the SELinux boolean use_fusefs_home_dirs is disabled. +If this setting is enabled, it should be disabled. + +To disable the use_fusefs_home_dirs SELinux boolean, run the following command: +$ sudo setsebool -P use_fusefs_home_dirs off + + - name: XCCDF Value var_use_fusefs_home_dirs # promote to variable + set_fact: + var_use_fusefs_home_dirs: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_use_fusefs_home_dirs + +- name: Set SELinux boolean use_fusefs_home_dirs accordingly + seboolean: + name: use_fusefs_home_dirs + state: '{{ var_use_fusefs_home_dirs }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_use_fusefs_home_dirs + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_use_fusefs_home_dirs='' + + +setsebool -P use_fusefs_home_dirs $var_use_fusefs_home_dirs + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the use_lpd_server SELinux Boolean + By default, the SELinux boolean use_lpd_server is disabled. +If this setting is enabled, it should be disabled. + +To disable the use_lpd_server SELinux boolean, run the following command: +$ sudo setsebool -P use_lpd_server off + + - name: XCCDF Value var_use_lpd_server # promote to variable + set_fact: + var_use_lpd_server: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_use_lpd_server + +- name: Set SELinux boolean use_lpd_server accordingly + seboolean: + name: use_lpd_server + state: '{{ var_use_lpd_server }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_use_lpd_server + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_use_lpd_server='' + + +setsebool -P use_lpd_server $var_use_lpd_server + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the use_nfs_home_dirs SELinux Boolean + By default, the SELinux boolean use_nfs_home_dirs is disabled. +If this setting is enabled, it should be disabled. + +To disable the use_nfs_home_dirs SELinux boolean, run the following command: +$ sudo setsebool -P use_nfs_home_dirs off + + - name: XCCDF Value var_use_nfs_home_dirs # promote to variable + set_fact: + var_use_nfs_home_dirs: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_use_nfs_home_dirs + +- name: Set SELinux boolean use_nfs_home_dirs accordingly + seboolean: + name: use_nfs_home_dirs + state: '{{ var_use_nfs_home_dirs }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_use_nfs_home_dirs + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_use_nfs_home_dirs='' + + +setsebool -P use_nfs_home_dirs $var_use_nfs_home_dirs + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the use_samba_home_dirs SELinux Boolean + By default, the SELinux boolean use_samba_home_dirs is disabled. +If this setting is enabled, it should be disabled. + +To disable the use_samba_home_dirs SELinux boolean, run the following command: +$ sudo setsebool -P use_samba_home_dirs off + + - name: XCCDF Value var_use_samba_home_dirs # promote to variable + set_fact: + var_use_samba_home_dirs: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_use_samba_home_dirs + +- name: Set SELinux boolean use_samba_home_dirs accordingly + seboolean: + name: use_samba_home_dirs + state: '{{ var_use_samba_home_dirs }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_use_samba_home_dirs + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_use_samba_home_dirs='' + + +setsebool -P use_samba_home_dirs $var_use_samba_home_dirs + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable the user_exec_content SELinux Boolean + By default, the SELinux boolean user_exec_content is enabled. +If this setting is disabled, it should be enabled. + +To enable the user_exec_content SELinux boolean, run the following command: +$ sudo setsebool -P user_exec_content on + + - name: XCCDF Value var_user_exec_content # promote to variable + set_fact: + var_user_exec_content: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_user_exec_content + +- name: Set SELinux boolean user_exec_content accordingly + seboolean: + name: user_exec_content + state: '{{ var_user_exec_content }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_user_exec_content + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_user_exec_content='' + + +setsebool -P user_exec_content $var_user_exec_content + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the varnishd_connect_any SELinux Boolean + By default, the SELinux boolean varnishd_connect_any is disabled. +If this setting is enabled, it should be disabled. + +To disable the varnishd_connect_any SELinux boolean, run the following command: +$ sudo setsebool -P varnishd_connect_any off + + - name: XCCDF Value var_varnishd_connect_any # promote to variable + set_fact: + var_varnishd_connect_any: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_varnishd_connect_any + +- name: Set SELinux boolean varnishd_connect_any accordingly + seboolean: + name: varnishd_connect_any + state: '{{ var_varnishd_connect_any }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_varnishd_connect_any + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_varnishd_connect_any='' + + +setsebool -P varnishd_connect_any $var_varnishd_connect_any + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the virt_read_qemu_ga_data SELinux Boolean + By default, the SELinux boolean virt_read_qemu_ga_data is disabled. +If this setting is enabled, it should be disabled. + +To disable the virt_read_qemu_ga_data SELinux boolean, run the following command: +$ sudo setsebool -P virt_read_qemu_ga_data off + + - name: XCCDF Value var_virt_read_qemu_ga_data # promote to variable + set_fact: + var_virt_read_qemu_ga_data: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_virt_read_qemu_ga_data + +- name: Set SELinux boolean virt_read_qemu_ga_data accordingly + seboolean: + name: virt_read_qemu_ga_data + state: '{{ var_virt_read_qemu_ga_data }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_virt_read_qemu_ga_data + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_virt_read_qemu_ga_data='' + + +setsebool -P virt_read_qemu_ga_data $var_virt_read_qemu_ga_data + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the virt_rw_qemu_ga_data SELinux Boolean + By default, the SELinux boolean virt_rw_qemu_ga_data is disabled. +If this setting is enabled, it should be disabled. + +To disable the virt_rw_qemu_ga_data SELinux boolean, run the following command: +$ sudo setsebool -P virt_rw_qemu_ga_data off + + - name: XCCDF Value var_virt_rw_qemu_ga_data # promote to variable + set_fact: + var_virt_rw_qemu_ga_data: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_virt_rw_qemu_ga_data + +- name: Set SELinux boolean virt_rw_qemu_ga_data accordingly + seboolean: + name: virt_rw_qemu_ga_data + state: '{{ var_virt_rw_qemu_ga_data }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_virt_rw_qemu_ga_data + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_virt_rw_qemu_ga_data='' + + +setsebool -P virt_rw_qemu_ga_data $var_virt_rw_qemu_ga_data + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the virt_sandbox_use_all_caps SELinux Boolean + By default, the SELinux boolean virt_sandbox_use_all_caps is enabled. +This setting is disabled as containers should not run with privileges. + +To disable the virt_sandbox_use_all_caps SELinux boolean, run the following command: +$ sudo setsebool -P virt_sandbox_use_all_caps off + + - name: XCCDF Value var_virt_sandbox_use_all_caps # promote to variable + set_fact: + var_virt_sandbox_use_all_caps: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_virt_sandbox_use_all_caps + +- name: Set SELinux boolean virt_sandbox_use_all_caps accordingly + seboolean: + name: virt_sandbox_use_all_caps + state: '{{ var_virt_sandbox_use_all_caps }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_virt_sandbox_use_all_caps + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_virt_sandbox_use_all_caps='' + + +setsebool -P virt_sandbox_use_all_caps $var_virt_sandbox_use_all_caps + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable the virt_sandbox_use_audit SELinux Boolean + By default, the SELinux boolean virt_sandbox_use_audit is enabled. +If this setting is disabled, it should be enabled to allow sandboxed containers +to send audit messages. + +To enable the virt_sandbox_use_audit SELinux boolean, run the following command: +$ sudo setsebool -P virt_sandbox_use_audit on + + - name: XCCDF Value var_virt_sandbox_use_audit # promote to variable + set_fact: + var_virt_sandbox_use_audit: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_virt_sandbox_use_audit + +- name: Set SELinux boolean virt_sandbox_use_audit accordingly + seboolean: + name: virt_sandbox_use_audit + state: '{{ var_virt_sandbox_use_audit }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_virt_sandbox_use_audit + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_virt_sandbox_use_audit='' + + +setsebool -P virt_sandbox_use_audit $var_virt_sandbox_use_audit + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the virt_sandbox_use_mknod SELinux Boolean + By default, the SELinux boolean virt_sandbox_use_mknod is disabled. +If this setting is enabled, it should be disabled. + +To disable the virt_sandbox_use_mknod SELinux boolean, run the following command: +$ sudo setsebool -P virt_sandbox_use_mknod off + + - name: XCCDF Value var_virt_sandbox_use_mknod # promote to variable + set_fact: + var_virt_sandbox_use_mknod: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_virt_sandbox_use_mknod + +- name: Set SELinux boolean virt_sandbox_use_mknod accordingly + seboolean: + name: virt_sandbox_use_mknod + state: '{{ var_virt_sandbox_use_mknod }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_virt_sandbox_use_mknod + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_virt_sandbox_use_mknod='' + + +setsebool -P virt_sandbox_use_mknod $var_virt_sandbox_use_mknod + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the virt_sandbox_use_netlink SELinux Boolean + By default, the SELinux boolean virt_sandbox_use_netlink is disabled. +If this setting is enabled, it should be disabled. + +To disable the virt_sandbox_use_netlink SELinux boolean, run the following command: +$ sudo setsebool -P virt_sandbox_use_netlink off + + - name: XCCDF Value var_virt_sandbox_use_netlink # promote to variable + set_fact: + var_virt_sandbox_use_netlink: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_virt_sandbox_use_netlink + +- name: Set SELinux boolean virt_sandbox_use_netlink accordingly + seboolean: + name: virt_sandbox_use_netlink + state: '{{ var_virt_sandbox_use_netlink }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_virt_sandbox_use_netlink + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_virt_sandbox_use_netlink='' + + +setsebool -P virt_sandbox_use_netlink $var_virt_sandbox_use_netlink + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the virt_sandbox_use_sys_admin SELinux Boolean + By default, the SELinux boolean virt_sandbox_use_sys_admin is disabled. +If this setting is enabled, it should be disabled. + +To disable the virt_sandbox_use_sys_admin SELinux boolean, run the following command: +$ sudo setsebool -P virt_sandbox_use_sys_admin off + + - name: XCCDF Value var_virt_sandbox_use_sys_admin # promote to variable + set_fact: + var_virt_sandbox_use_sys_admin: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_virt_sandbox_use_sys_admin + +- name: Set SELinux boolean virt_sandbox_use_sys_admin accordingly + seboolean: + name: virt_sandbox_use_sys_admin + state: '{{ var_virt_sandbox_use_sys_admin }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_virt_sandbox_use_sys_admin + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_virt_sandbox_use_sys_admin='' + + +setsebool -P virt_sandbox_use_sys_admin $var_virt_sandbox_use_sys_admin + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the virt_transition_userdomain SELinux Boolean + By default, the SELinux boolean virt_transition_userdomain is disabled. +If this setting is enabled, it should be disabled. + +To disable the virt_transition_userdomain SELinux boolean, run the following command: +$ sudo setsebool -P virt_transition_userdomain off + + - name: XCCDF Value var_virt_transition_userdomain # promote to variable + set_fact: + var_virt_transition_userdomain: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_virt_transition_userdomain + +- name: Set SELinux boolean virt_transition_userdomain accordingly + seboolean: + name: virt_transition_userdomain + state: '{{ var_virt_transition_userdomain }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_virt_transition_userdomain + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_virt_transition_userdomain='' + + +setsebool -P virt_transition_userdomain $var_virt_transition_userdomain + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the virt_use_comm SELinux Boolean + By default, the SELinux boolean virt_use_comm is disabled. +If this setting is enabled, it should be disabled. + +To disable the virt_use_comm SELinux boolean, run the following command: +$ sudo setsebool -P virt_use_comm off + + - name: XCCDF Value var_virt_use_comm # promote to variable + set_fact: + var_virt_use_comm: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_virt_use_comm + +- name: Set SELinux boolean virt_use_comm accordingly + seboolean: + name: virt_use_comm + state: '{{ var_virt_use_comm }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_virt_use_comm + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_virt_use_comm='' + + +setsebool -P virt_use_comm $var_virt_use_comm + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the virt_use_execmem SELinux Boolean + By default, the SELinux boolean virt_use_execmem is disabled. +If this setting is enabled, it should be disabled. + +To disable the virt_use_execmem SELinux boolean, run the following command: +$ sudo setsebool -P virt_use_execmem off + BP28(R67) + + CCE-83312-9 + - name: XCCDF Value var_virt_use_execmem # promote to variable + set_fact: + var_virt_use_execmem: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83312-9 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_virt_use_execmem + +- name: Set SELinux boolean virt_use_execmem accordingly + seboolean: + name: virt_use_execmem + state: '{{ var_virt_use_execmem }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83312-9 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_virt_use_execmem + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_virt_use_execmem='' + + +setsebool -P virt_use_execmem $var_virt_use_execmem + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the virt_use_fusefs SELinux Boolean + By default, the SELinux boolean virt_use_fusefs is disabled. +If this setting is enabled, it should be disabled. + +To disable the virt_use_fusefs SELinux boolean, run the following command: +$ sudo setsebool -P virt_use_fusefs off + + - name: XCCDF Value var_virt_use_fusefs # promote to variable + set_fact: + var_virt_use_fusefs: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_virt_use_fusefs + +- name: Set SELinux boolean virt_use_fusefs accordingly + seboolean: + name: virt_use_fusefs + state: '{{ var_virt_use_fusefs }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_virt_use_fusefs + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_virt_use_fusefs='' + + +setsebool -P virt_use_fusefs $var_virt_use_fusefs + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the virt_use_nfs SELinux Boolean + By default, the SELinux boolean virt_use_nfs is disabled. +If this setting is enabled, it should be disabled. + +To disable the virt_use_nfs SELinux boolean, run the following command: +$ sudo setsebool -P virt_use_nfs off + + - name: XCCDF Value var_virt_use_nfs # promote to variable + set_fact: + var_virt_use_nfs: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_virt_use_nfs + +- name: Set SELinux boolean virt_use_nfs accordingly + seboolean: + name: virt_use_nfs + state: '{{ var_virt_use_nfs }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_virt_use_nfs + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_virt_use_nfs='' + + +setsebool -P virt_use_nfs $var_virt_use_nfs + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the virt_use_rawip SELinux Boolean + By default, the SELinux boolean virt_use_rawip is disabled. +If this setting is enabled, it should be disabled. + +To disable the virt_use_rawip SELinux boolean, run the following command: +$ sudo setsebool -P virt_use_rawip off + + - name: XCCDF Value var_virt_use_rawip # promote to variable + set_fact: + var_virt_use_rawip: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_virt_use_rawip + +- name: Set SELinux boolean virt_use_rawip accordingly + seboolean: + name: virt_use_rawip + state: '{{ var_virt_use_rawip }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_virt_use_rawip + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_virt_use_rawip='' + + +setsebool -P virt_use_rawip $var_virt_use_rawip + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the virt_use_samba SELinux Boolean + By default, the SELinux boolean virt_use_samba is disabled. +If this setting is enabled, it should be disabled. + +To disable the virt_use_samba SELinux boolean, run the following command: +$ sudo setsebool -P virt_use_samba off + + - name: XCCDF Value var_virt_use_samba # promote to variable + set_fact: + var_virt_use_samba: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_virt_use_samba + +- name: Set SELinux boolean virt_use_samba accordingly + seboolean: + name: virt_use_samba + state: '{{ var_virt_use_samba }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_virt_use_samba + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_virt_use_samba='' + + +setsebool -P virt_use_samba $var_virt_use_samba + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the virt_use_sanlock SELinux Boolean + By default, the SELinux boolean virt_use_sanlock is disabled. +If this setting is enabled, it should be disabled. + +To disable the virt_use_sanlock SELinux boolean, run the following command: +$ sudo setsebool -P virt_use_sanlock off + + - name: XCCDF Value var_virt_use_sanlock # promote to variable + set_fact: + var_virt_use_sanlock: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_virt_use_sanlock + +- name: Set SELinux boolean virt_use_sanlock accordingly + seboolean: + name: virt_use_sanlock + state: '{{ var_virt_use_sanlock }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_virt_use_sanlock + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_virt_use_sanlock='' + + +setsebool -P virt_use_sanlock $var_virt_use_sanlock + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the virt_use_usb SELinux Boolean + By default, the SELinux boolean virt_use_usb is enabled. +This setting should be disabled. + +To disable the virt_use_usb SELinux boolean, run the following command: +$ sudo setsebool -P virt_use_usb off + + - name: XCCDF Value var_virt_use_usb # promote to variable + set_fact: + var_virt_use_usb: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_virt_use_usb + +- name: Set SELinux boolean virt_use_usb accordingly + seboolean: + name: virt_use_usb + state: '{{ var_virt_use_usb }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_virt_use_usb + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_virt_use_usb='' + + +setsebool -P virt_use_usb $var_virt_use_usb + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the virt_use_xserver SELinux Boolean + By default, the SELinux boolean virt_use_xserver is disabled. +If this setting is enabled, it should be disabled. + +To disable the virt_use_xserver SELinux boolean, run the following command: +$ sudo setsebool -P virt_use_xserver off + + - name: XCCDF Value var_virt_use_xserver # promote to variable + set_fact: + var_virt_use_xserver: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_virt_use_xserver + +- name: Set SELinux boolean virt_use_xserver accordingly + seboolean: + name: virt_use_xserver + state: '{{ var_virt_use_xserver }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_virt_use_xserver + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_virt_use_xserver='' + + +setsebool -P virt_use_xserver $var_virt_use_xserver + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the webadm_manage_user_files SELinux Boolean + By default, the SELinux boolean webadm_manage_user_files is disabled. +If this setting is enabled, it should be disabled. + +To disable the webadm_manage_user_files SELinux boolean, run the following command: +$ sudo setsebool -P webadm_manage_user_files off + + - name: XCCDF Value var_webadm_manage_user_files # promote to variable + set_fact: + var_webadm_manage_user_files: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_webadm_manage_user_files + +- name: Set SELinux boolean webadm_manage_user_files accordingly + seboolean: + name: webadm_manage_user_files + state: '{{ var_webadm_manage_user_files }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_webadm_manage_user_files + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_webadm_manage_user_files='' + + +setsebool -P webadm_manage_user_files $var_webadm_manage_user_files + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the webadm_read_user_files SELinux Boolean + By default, the SELinux boolean webadm_read_user_files is disabled. +If this setting is enabled, it should be disabled. + +To disable the webadm_read_user_files SELinux boolean, run the following command: +$ sudo setsebool -P webadm_read_user_files off + + - name: XCCDF Value var_webadm_read_user_files # promote to variable + set_fact: + var_webadm_read_user_files: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_webadm_read_user_files + +- name: Set SELinux boolean webadm_read_user_files accordingly + seboolean: + name: webadm_read_user_files + state: '{{ var_webadm_read_user_files }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_webadm_read_user_files + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_webadm_read_user_files='' + + +setsebool -P webadm_read_user_files $var_webadm_read_user_files + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the wine_mmap_zero_ignore SELinux Boolean + By default, the SELinux boolean wine_mmap_zero_ignore is disabled. +If this setting is enabled, it should be disabled. + +To disable the wine_mmap_zero_ignore SELinux boolean, run the following command: +$ sudo setsebool -P wine_mmap_zero_ignore off + + - name: XCCDF Value var_wine_mmap_zero_ignore # promote to variable + set_fact: + var_wine_mmap_zero_ignore: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_wine_mmap_zero_ignore + +- name: Set SELinux boolean wine_mmap_zero_ignore accordingly + seboolean: + name: wine_mmap_zero_ignore + state: '{{ var_wine_mmap_zero_ignore }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_wine_mmap_zero_ignore + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_wine_mmap_zero_ignore='' + + +setsebool -P wine_mmap_zero_ignore $var_wine_mmap_zero_ignore + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the xdm_bind_vnc_tcp_port SELinux Boolean + By default, the SELinux boolean xdm_bind_vnc_tcp_port is disabled. +If this setting is enabled, it should be disabled. + +To disable the xdm_bind_vnc_tcp_port SELinux boolean, run the following command: +$ sudo setsebool -P xdm_bind_vnc_tcp_port off + + - name: XCCDF Value var_xdm_bind_vnc_tcp_port # promote to variable + set_fact: + var_xdm_bind_vnc_tcp_port: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_xdm_bind_vnc_tcp_port + +- name: Set SELinux boolean xdm_bind_vnc_tcp_port accordingly + seboolean: + name: xdm_bind_vnc_tcp_port + state: '{{ var_xdm_bind_vnc_tcp_port }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_xdm_bind_vnc_tcp_port + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_xdm_bind_vnc_tcp_port='' + + +setsebool -P xdm_bind_vnc_tcp_port $var_xdm_bind_vnc_tcp_port + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the xdm_exec_bootloader SELinux Boolean + By default, the SELinux boolean xdm_exec_bootloader is disabled. +If this setting is enabled, it should be disabled. + +To disable the xdm_exec_bootloader SELinux boolean, run the following command: +$ sudo setsebool -P xdm_exec_bootloader off + + - name: XCCDF Value var_xdm_exec_bootloader # promote to variable + set_fact: + var_xdm_exec_bootloader: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_xdm_exec_bootloader + +- name: Set SELinux boolean xdm_exec_bootloader accordingly + seboolean: + name: xdm_exec_bootloader + state: '{{ var_xdm_exec_bootloader }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_xdm_exec_bootloader + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_xdm_exec_bootloader='' + + +setsebool -P xdm_exec_bootloader $var_xdm_exec_bootloader + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the xdm_sysadm_login SELinux Boolean + By default, the SELinux boolean xdm_sysadm_login is disabled. +If this setting is enabled, it should be disabled. + +To disable the xdm_sysadm_login SELinux boolean, run the following command: +$ sudo setsebool -P xdm_sysadm_login off + + - name: XCCDF Value var_xdm_sysadm_login # promote to variable + set_fact: + var_xdm_sysadm_login: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_xdm_sysadm_login + +- name: Set SELinux boolean xdm_sysadm_login accordingly + seboolean: + name: xdm_sysadm_login + state: '{{ var_xdm_sysadm_login }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_xdm_sysadm_login + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_xdm_sysadm_login='' + + +setsebool -P xdm_sysadm_login $var_xdm_sysadm_login + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the xdm_write_home SELinux Boolean + By default, the SELinux boolean xdm_write_home is disabled. +If this setting is enabled, it should be disabled. + +To disable the xdm_write_home SELinux boolean, run the following command: +$ sudo setsebool -P xdm_write_home off + + - name: XCCDF Value var_xdm_write_home # promote to variable + set_fact: + var_xdm_write_home: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_xdm_write_home + +- name: Set SELinux boolean xdm_write_home accordingly + seboolean: + name: xdm_write_home + state: '{{ var_xdm_write_home }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_xdm_write_home + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_xdm_write_home='' + + +setsebool -P xdm_write_home $var_xdm_write_home + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the xen_use_nfs SELinux Boolean + By default, the SELinux boolean xen_use_nfs is disabled. +If this setting is enabled, it should be disabled. + +To disable the xen_use_nfs SELinux boolean, run the following command: +$ sudo setsebool -P xen_use_nfs off + + - name: XCCDF Value var_xen_use_nfs # promote to variable + set_fact: + var_xen_use_nfs: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_xen_use_nfs + +- name: Set SELinux boolean xen_use_nfs accordingly + seboolean: + name: xen_use_nfs + state: '{{ var_xen_use_nfs }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_xen_use_nfs + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_xen_use_nfs='' + + +setsebool -P xen_use_nfs $var_xen_use_nfs + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable the xend_run_blktap SELinux Boolean + By default, the SELinux boolean xend_run_blktap is enabled. +If this setting is disabled, it should be enabled. + +To enable the xend_run_blktap SELinux boolean, run the following command: +$ sudo setsebool -P xend_run_blktap on + + - name: XCCDF Value var_xend_run_blktap # promote to variable + set_fact: + var_xend_run_blktap: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_xend_run_blktap + +- name: Set SELinux boolean xend_run_blktap accordingly + seboolean: + name: xend_run_blktap + state: '{{ var_xend_run_blktap }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_xend_run_blktap + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_xend_run_blktap='' + + +setsebool -P xend_run_blktap $var_xend_run_blktap + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable the xend_run_qemu SELinux Boolean + By default, the SELinux boolean xend_run_qemu is enabled. +If this setting is disabled, it should be enabled. + +To enable the xend_run_qemu SELinux boolean, run the following command: +$ sudo setsebool -P xend_run_qemu on + + - name: XCCDF Value var_xend_run_qemu # promote to variable + set_fact: + var_xend_run_qemu: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_xend_run_qemu + +- name: Set SELinux boolean xend_run_qemu accordingly + seboolean: + name: xend_run_qemu + state: '{{ var_xend_run_qemu }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_xend_run_qemu + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_xend_run_qemu='' + + +setsebool -P xend_run_qemu $var_xend_run_qemu + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the xguest_connect_network SELinux Boolean + By default, the SELinux boolean xguest_connect_network is enabled. +This setting should be disabled as guest users should not be able to configure +NetworkManager. + +To disable the xguest_connect_network SELinux boolean, run the following command: +$ sudo setsebool -P xguest_connect_network off + + - name: XCCDF Value var_xguest_connect_network # promote to variable + set_fact: + var_xguest_connect_network: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_xguest_connect_network + +- name: Set SELinux boolean xguest_connect_network accordingly + seboolean: + name: xguest_connect_network + state: '{{ var_xguest_connect_network }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_xguest_connect_network + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_xguest_connect_network='' + + +setsebool -P xguest_connect_network $var_xguest_connect_network + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the xguest_exec_content SELinux Boolean + By default, the SELinux boolean xguest_exec_content is enabled. +This setting should be disabled as guest users should not be able to run +executables. + +To disable the xguest_exec_content SELinux boolean, run the following command: +$ sudo setsebool -P xguest_exec_content off + + - name: XCCDF Value var_xguest_exec_content # promote to variable + set_fact: + var_xguest_exec_content: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_xguest_exec_content + +- name: Set SELinux boolean xguest_exec_content accordingly + seboolean: + name: xguest_exec_content + state: '{{ var_xguest_exec_content }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_xguest_exec_content + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_xguest_exec_content='' + + +setsebool -P xguest_exec_content $var_xguest_exec_content + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the xguest_mount_media SELinux Boolean + By default, the SELinux boolean xguest_mount_media is enabled. +This setting should be disabled as guest users should not be able to mount +any media. + +To disable the xguest_mount_media SELinux boolean, run the following command: +$ sudo setsebool -P xguest_mount_media off + + - name: XCCDF Value var_xguest_mount_media # promote to variable + set_fact: + var_xguest_mount_media: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_xguest_mount_media + +- name: Set SELinux boolean xguest_mount_media accordingly + seboolean: + name: xguest_mount_media + state: '{{ var_xguest_mount_media }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_xguest_mount_media + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_xguest_mount_media='' + + +setsebool -P xguest_mount_media $var_xguest_mount_media + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the xguest_use_bluetooth SELinux Boolean + By default, the SELinux boolean xguest_use_bluetooth is enabled. +This setting should be disabled as guests users should not be able to access +or use bluetooth. + +To disable the xguest_use_bluetooth SELinux boolean, run the following command: +$ sudo setsebool -P xguest_use_bluetooth off + + - name: XCCDF Value var_xguest_use_bluetooth # promote to variable + set_fact: + var_xguest_use_bluetooth: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_xguest_use_bluetooth + +- name: Set SELinux boolean xguest_use_bluetooth accordingly + seboolean: + name: xguest_use_bluetooth + state: '{{ var_xguest_use_bluetooth }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_xguest_use_bluetooth + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_xguest_use_bluetooth='' + + +setsebool -P xguest_use_bluetooth $var_xguest_use_bluetooth + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the xserver_clients_write_xshm SELinux Boolean + By default, the SELinux boolean xserver_clients_write_xshm is disabled. +If this setting is enabled, it should be disabled. + +To disable the xserver_clients_write_xshm SELinux boolean, run the following command: +$ sudo setsebool -P xserver_clients_write_xshm off + + - name: XCCDF Value var_xserver_clients_write_xshm # promote to variable + set_fact: + var_xserver_clients_write_xshm: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_xserver_clients_write_xshm + +- name: Set SELinux boolean xserver_clients_write_xshm accordingly + seboolean: + name: xserver_clients_write_xshm + state: '{{ var_xserver_clients_write_xshm }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_xserver_clients_write_xshm + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_xserver_clients_write_xshm='' + + +setsebool -P xserver_clients_write_xshm $var_xserver_clients_write_xshm + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the xserver_execmem SELinux Boolean + By default, the SELinux boolean xserver_execmem is disabled. +If this setting is enabled, it should be disabled. + +To disable the xserver_execmem SELinux boolean, run the following command: +$ sudo setsebool -P xserver_execmem off + BP28(R67) + + CCE-83313-7 + - name: XCCDF Value var_xserver_execmem # promote to variable + set_fact: + var_xserver_execmem: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83313-7 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_xserver_execmem + +- name: Set SELinux boolean xserver_execmem accordingly + seboolean: + name: xserver_execmem + state: '{{ var_xserver_execmem }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83313-7 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_xserver_execmem + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_xserver_execmem='' + + +setsebool -P xserver_execmem $var_xserver_execmem + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the xserver_object_manager SELinux Boolean + By default, the SELinux boolean xserver_object_manager is disabled. +If this setting is enabled, it should be disabled. + +To disable the xserver_object_manager SELinux boolean, run the following command: +$ sudo setsebool -P xserver_object_manager off + + - name: XCCDF Value var_xserver_object_manager # promote to variable + set_fact: + var_xserver_object_manager: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_xserver_object_manager + +- name: Set SELinux boolean xserver_object_manager accordingly + seboolean: + name: xserver_object_manager + state: '{{ var_xserver_object_manager }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_xserver_object_manager + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_xserver_object_manager='' + + +setsebool -P xserver_object_manager $var_xserver_object_manager + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the zabbix_can_network SELinux Boolean + By default, the SELinux boolean zabbix_can_network is disabled. +If this setting is enabled, it should be disabled. + +To disable the zabbix_can_network SELinux boolean, run the following command: +$ sudo setsebool -P zabbix_can_network off + + - name: XCCDF Value var_zabbix_can_network # promote to variable + set_fact: + var_zabbix_can_network: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_zabbix_can_network + +- name: Set SELinux boolean zabbix_can_network accordingly + seboolean: + name: zabbix_can_network + state: '{{ var_zabbix_can_network }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_zabbix_can_network + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_zabbix_can_network='' + + +setsebool -P zabbix_can_network $var_zabbix_can_network + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the zarafa_setrlimit SELinux Boolean + By default, the SELinux boolean zarafa_setrlimit is disabled. +If this setting is enabled, it should be disabled. + +To disable the zarafa_setrlimit SELinux boolean, run the following command: +$ sudo setsebool -P zarafa_setrlimit off + + - name: XCCDF Value var_zarafa_setrlimit # promote to variable + set_fact: + var_zarafa_setrlimit: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_zarafa_setrlimit + +- name: Set SELinux boolean zarafa_setrlimit accordingly + seboolean: + name: zarafa_setrlimit + state: '{{ var_zarafa_setrlimit }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_zarafa_setrlimit + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_zarafa_setrlimit='' + + +setsebool -P zarafa_setrlimit $var_zarafa_setrlimit + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the zebra_write_config SELinux Boolean + By default, the SELinux boolean zebra_write_config is disabled. +If this setting is enabled, it should be disabled. + +To disable the zebra_write_config SELinux boolean, run the following command: +$ sudo setsebool -P zebra_write_config off + + - name: XCCDF Value var_zebra_write_config # promote to variable + set_fact: + var_zebra_write_config: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_zebra_write_config + +- name: Set SELinux boolean zebra_write_config accordingly + seboolean: + name: zebra_write_config + state: '{{ var_zebra_write_config }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_zebra_write_config + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_zebra_write_config='' + + +setsebool -P zebra_write_config $var_zebra_write_config + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the zoneminder_anon_write SELinux Boolean + By default, the SELinux boolean zoneminder_anon_write is disabled. +If this setting is enabled, it should be disabled. + +To disable the zoneminder_anon_write SELinux boolean, run the following command: +$ sudo setsebool -P zoneminder_anon_write off + + - name: XCCDF Value var_zoneminder_anon_write # promote to variable + set_fact: + var_zoneminder_anon_write: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_zoneminder_anon_write + +- name: Set SELinux boolean zoneminder_anon_write accordingly + seboolean: + name: zoneminder_anon_write + state: '{{ var_zoneminder_anon_write }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_zoneminder_anon_write + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_zoneminder_anon_write='' + + +setsebool -P zoneminder_anon_write $var_zoneminder_anon_write + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the zoneminder_run_sudo SELinux Boolean + By default, the SELinux boolean zoneminder_run_sudo is disabled. +If this setting is enabled, it should be disabled. + +To disable the zoneminder_run_sudo SELinux boolean, run the following command: +$ sudo setsebool -P zoneminder_run_sudo off + + - name: XCCDF Value var_zoneminder_run_sudo # promote to variable + set_fact: + var_zoneminder_run_sudo: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_zoneminder_run_sudo + +- name: Set SELinux boolean zoneminder_run_sudo accordingly + seboolean: + name: zoneminder_run_sudo + state: '{{ var_zoneminder_run_sudo }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_zoneminder_run_sudo + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_zoneminder_run_sudo='' + + +setsebool -P zoneminder_run_sudo $var_zoneminder_run_sudo + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + + + + Services + The best protection against vulnerable software is running less software. This section describes how to review +the software which Red Hat Enterprise Linux 8 installs on a system and disable software which is not needed. It +then enumerates the software packages installed on a default Red Hat Enterprise Linux 8 system and provides guidance about which +ones can be safely disabled. + +Red Hat Enterprise Linux 8 provides a convenient minimal install option that essentially installs the bare necessities for a functional +system. When building Red Hat Enterprise Linux 8 systems, it is highly recommended to select the minimal packages and then build up +the system from there. + + Apport Service + The Apport service provides debugging and crash reporting +features on Ubuntu distributions. + + + APT service configuration + The apt service manage the package management and update of the whole system. Its configuration need to be properly defined to ensure efficient security updates, packages and repository authentication and proper lifecycle management. + + + Avahi Server + The Avahi daemon implements the DNS Service Discovery +and Multicast DNS protocols, which provide service and host +discovery on a network. It allows a system to automatically +identify resources on the network, such as printers or web servers. +This capability is also known as mDNSresponder and is a major part +of Zeroconf networking. + + Configure Avahi if Necessary + If your system requires the Avahi daemon, its configuration can be restricted +to improve security. The Avahi daemon configuration file is +/etc/avahi/avahi-daemon.conf. The following security recommendations +should be applied to this file: +See the avahi-daemon.conf(5) man page, or documentation at + + http://www.avahi.org, for more detailed information +about the configuration options. + + Check Avahi Responses' TTL Field + To make Avahi ignore packets unless the TTL field is 255, edit +/etc/avahi/avahi-daemon.conf and ensure the following line +appears in the [server] section: +check-response-ttl=yes + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-6(a) + PR.IP-1 + PR.PT-3 + This helps to ensure that only mDNS responses from the local network are +processed, because the TTL field in a packet is decremented from its initial +value of 255 whenever it is routed from one network to another. Although a +properly-configured router or firewall should not allow mDNS packets into +the local network at all, this option provides another check to ensure they +are not permitted. + CCE-82377-3 + + + Disable Avahi Publishing + To prevent Avahi from publishing its records, edit /etc/avahi/avahi-daemon.conf +and ensure the following line appears in the [publish] section: +disable-publishing=yes + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + PR.PT-3 + This helps ensure that no record will be published by Avahi. + CCE-82372-4 + + + Serve Avahi Only via Required Protocol + If you are using only IPv4, edit /etc/avahi/avahi-daemon.conf and ensure +the following line exists in the [server] section: +use-ipv6=no +Similarly, if you are using only IPv6, disable IPv4 sockets with the line: +use-ipv4=no + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-6(a) + PR.IP-1 + PR.PT-3 + + CCE-82378-1 + + + Prevent Other Programs from Using Avahi's Port + To prevent other mDNS stacks from running, edit /etc/avahi/avahi-daemon.conf +and ensure the following line appears in the [server] section: +disallow-other-stacks=yes + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + PR.PT-3 + This helps ensure that only Avahi is responsible for mDNS traffic coming from +that port on the system. + CCE-82376-5 + + + Restrict Information Published by Avahi + If it is necessary to publish some information to the network, it should not be joined +by any extraneous information, or by information supplied by a non-trusted source +on the system. +Prevent user applications from using Avahi to publish services by adding or +correcting the following line in the [publish] section: +disable-user-service-publishing=yes +Implement as many of the following lines as possible, to restrict the information +published by Avahi. +publish-addresses=no +publish-hinfo=no +publish-workstation=no +publish-domain=no +Inspect the files in the directory /etc/avahi/services/. Unless there +is an operational need to publish information about each of these services, +delete the corresponding file. + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + PR.PT-3 + These options prevent publishing attempts from succeeding, +and can be applied even if publishing is disabled entirely via +disable-publishing. Alternatively, these can be used to restrict +the types of published information in the event that some information +must be published. + CCE-82375-7 + + + + Disable Avahi Server if Possible + Because the Avahi daemon service keeps an open network +port, it is subject to network attacks. +Disabling it can reduce the system's vulnerability to such attacks. + + Disable Avahi Server Software + +The avahi-daemon service can be disabled with the following command: +$ sudo systemctl mask --now avahi-daemon.service + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + CCI-000366 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + PR.PT-3 + 2.2.3 + Because the Avahi daemon service keeps an open network +port, it is subject to network attacks. Its functionality +is convenient but is only appropriate if the local network +can be trusted. + + CCE-82188-4 + include disable_avahi-daemon + +class disable_avahi-daemon { + service {'avahi-daemon': + enable => false, + ensure => 'stopped', + } +} + + - name: Disable service avahi-daemon + block: + + - name: Disable service avahi-daemon + systemd: + name: avahi-daemon.service + enabled: 'no' + state: stopped + masked: 'yes' + ignore_errors: 'yes' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82188-4 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_avahi-daemon_disabled + +- name: Unit Socket Exists - avahi-daemon.socket + command: systemctl list-unit-files avahi-daemon.socket + args: + warn: false + register: socket_file_exists + changed_when: false + ignore_errors: true + check_mode: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82188-4 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_avahi-daemon_disabled + +- name: Disable socket avahi-daemon + systemd: + name: avahi-daemon.socket + enabled: 'no' + state: stopped + masked: 'yes' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"avahi-daemon.socket" in socket_file_exists.stdout_lines[1]' + tags: + - CCE-82188-4 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_avahi-daemon_disabled + + +[customizations.services] +disabled = ["avahi-daemon"] + + apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + systemd: + units: + - name: avahi-daemon.service + enabled: false + mask: true + - name: avahi-daemon.socket + enabled: false + mask: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" stop 'avahi-daemon.service' +"$SYSTEMCTL_EXEC" disable 'avahi-daemon.service' +"$SYSTEMCTL_EXEC" mask 'avahi-daemon.service' +# Disable socket activation if we have a unit file for it +if "$SYSTEMCTL_EXEC" list-unit-files | grep -q '^avahi-daemon.socket'; then + "$SYSTEMCTL_EXEC" stop 'avahi-daemon.socket' + "$SYSTEMCTL_EXEC" mask 'avahi-daemon.socket' +fi +# The service may not be running because it has been started and failed, +# so let's reset the state so OVAL checks pass. +# Service should be 'inactive', not 'failed' after reboot though. +"$SYSTEMCTL_EXEC" reset-failed 'avahi-daemon.service' || true + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + + Base Services + This section addresses the base services that are installed on a +Red Hat Enterprise Linux 8 default installation which are not covered in other +sections. Some of these services listen on the network and +should be treated with particular discretion. Other services are local +system utilities that may or may not be extraneous. In general, system services +should be disabled if not required. + + Install the psacct package + The process accounting service, psacct, works with programs +including acct and ac to allow system administrators to view +user activity, such as commands issued by users of the system. +The psacct package can be installed with the following command: + +$ sudo yum install psacct + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 2 + 3 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + BAI03.05 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.03 + DSS03.05 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + DSS06.06 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 2.8 + SR 2.9 + SR 6.1 + SR 6.2 + SR 7.6 + A.12.1.2 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.5.1 + A.12.6.2 + A.12.7.1 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.9.1.2 + AU-12(a) + CM-6(a) + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.IP-1 + PR.PT-1 + PR.PT-3 + The psacct service can provide administrators a convenient +view into some user activities. However, it should be noted that the auditing +system and its audit records provide more authoritative and comprehensive +records. + CCE-82404-5 + +package --add=psacct + + include install_psacct + +class install_psacct { + package { 'psacct': + ensure => 'installed', + } +} + + - name: Ensure psacct is installed + package: + name: psacct + state: present + tags: + - CCE-82404-5 + - NIST-800-53-AU-12(a) + - NIST-800-53-CM-6(a) + - enable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - package_psacct_installed + + +[[packages]] +name = "psacct" +version = "*" + + +if ! rpm -q --quiet "psacct" ; then + yum install -y "psacct" +fi + + + + + + + + + + Uninstall Automatic Bug Reporting Tool (abrt) + The Automatic Bug Reporting Tool (abrt) collects +and reports crash data when an application crash is detected. Using a variety +of plugins, abrt can email crash reports to system administrators, log crash +reports to files, or forward crash reports to a centralized issue tracking +system such as RHTSupport. +The abrt package can be removed with the following command: + +$ sudo yum erase abrt + CCI-000381 + SRG-OS-000095-GPOS-00049 + RHEL-08-040001 + SV-230488r627750_rule + Mishandling crash data could expose sensitive information about +vulnerabilities in software executing on the system, as well as sensitive +information from within a process's address space or registers. + CCE-80948-3 + +package --remove=abrt + + include remove_abrt + +class remove_abrt { + package { 'abrt': + ensure => 'purged', + } +} + + - name: Ensure abrt is removed + package: + name: abrt + state: absent + tags: + - CCE-80948-3 + - DISA-STIG-RHEL-08-040001 + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_abrt_removed + + +# CAUTION: This remediation script will remove abrt +# from the system, and may remove any packages +# that depend on abrt. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "abrt" ; then + + yum remove -y "abrt" + +fi + + + + + + + + + + Enable Process Accounting (psacct) + The process accounting service, psacct, works with programs +including acct and ac to allow system administrators to view +user activity, such as commands issued by users of the system. + +The psacct service can be enabled with the following command: +$ sudo systemctl enable psacct.service + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 2 + 3 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + BAI03.05 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.03 + DSS03.05 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + DSS06.06 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 2.8 + SR 2.9 + SR 6.1 + SR 6.2 + SR 7.6 + A.12.1.2 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.5.1 + A.12.6.2 + A.12.7.1 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.9.1.2 + AU-12(a) + CM-6(a) + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.IP-1 + PR.PT-1 + PR.PT-3 + The psacct service can provide administrators a convenient +view into some user activities. However, it should be noted that the auditing +system and its audit records provide more authoritative and comprehensive +records. + + CCE-82401-1 + include enable_psacct + +class enable_psacct { + service {'psacct': + enable => true, + ensure => 'running', + } +} + + - name: Enable service psacct + block: + + - name: Gather the package facts + package_facts: + manager: auto + + - name: Enable service psacct + service: + name: psacct + enabled: 'yes' + state: started + masked: 'no' + when: + - '"psacct" in ansible_facts.packages' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82401-1 + - NIST-800-53-AU-12(a) + - NIST-800-53-CM-6(a) + - enable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - service_psacct_enabled + + +[customizations.services] +enabled = ["psacct"] + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" unmask 'psacct.service' +"$SYSTEMCTL_EXEC" start 'psacct.service' +"$SYSTEMCTL_EXEC" enable 'psacct.service' + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable Automatic Bug Reporting Tool (abrtd) + The Automatic Bug Reporting Tool (abrtd) daemon collects +and reports crash data when an application crash is detected. Using a variety +of plugins, abrtd can email crash reports to system administrators, log crash +reports to files, or forward crash reports to a centralized issue tracking +system such as RHTSupport. + +The abrtd service can be disabled with the following command: +$ sudo systemctl mask --now abrtd.service + 11 + 12 + 14 + 15 + 3 + 8 + 9 + APO13.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.04 + DSS05.02 + DSS05.03 + DSS05.05 + DSS06.06 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.6.2.1 + A.6.2.2 + A.9.1.2 + CM-7(a) + CM-6(a) + PR.AC-3 + PR.IP-1 + PR.PT-3 + PR.PT-4 + Mishandling crash data could expose sensitive information about +vulnerabilities in software executing on the system, as well as sensitive +information from within a process's address space or registers. + + CCE-80870-9 + include disable_abrtd + +class disable_abrtd { + service {'abrtd': + enable => false, + ensure => 'stopped', + } +} + + - name: Disable service abrtd + block: + + - name: Disable service abrtd + systemd: + name: abrtd.service + enabled: 'no' + state: stopped + masked: 'yes' + ignore_errors: 'yes' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80870-9 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_abrtd_disabled + +- name: Unit Socket Exists - abrtd.socket + command: systemctl list-unit-files abrtd.socket + args: + warn: false + register: socket_file_exists + changed_when: false + ignore_errors: true + check_mode: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80870-9 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_abrtd_disabled + +- name: Disable socket abrtd + systemd: + name: abrtd.socket + enabled: 'no' + state: stopped + masked: 'yes' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"abrtd.socket" in socket_file_exists.stdout_lines[1]' + tags: + - CCE-80870-9 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_abrtd_disabled + + +[customizations.services] +disabled = ["abrtd"] + + apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + systemd: + units: + - name: abrtd.service + enabled: false + mask: true + - name: abrtd.socket + enabled: false + mask: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" stop 'abrtd.service' +"$SYSTEMCTL_EXEC" disable 'abrtd.service' +"$SYSTEMCTL_EXEC" mask 'abrtd.service' +# Disable socket activation if we have a unit file for it +if "$SYSTEMCTL_EXEC" list-unit-files | grep -q '^abrtd.socket'; then + "$SYSTEMCTL_EXEC" stop 'abrtd.socket' + "$SYSTEMCTL_EXEC" mask 'abrtd.socket' +fi +# The service may not be running because it has been started and failed, +# so let's reset the state so OVAL checks pass. +# Service should be 'inactive', not 'failed' after reboot though. +"$SYSTEMCTL_EXEC" reset-failed 'abrtd.service' || true + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable Advanced Configuration and Power Interface (acpid) + The Advanced Configuration and Power Interface Daemon (acpid) +dispatches ACPI events (such as power/reset button depressed) to userspace +programs. + +The acpid service can be disabled with the following command: +$ sudo systemctl mask --now acpid.service + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + PR.PT-3 + ACPI support is highly desirable for systems in some network roles, +such as laptops or desktops. For other systems, such as servers, it may permit +accidental or trivially achievable denial of service situations and disabling +it is appropriate. + + CCE-82407-8 + include disable_acpid + +class disable_acpid { + service {'acpid': + enable => false, + ensure => 'stopped', + } +} + + - name: Disable service acpid + block: + + - name: Disable service acpid + systemd: + name: acpid.service + enabled: 'no' + state: stopped + masked: 'yes' + ignore_errors: 'yes' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82407-8 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_acpid_disabled + +- name: Unit Socket Exists - acpid.socket + command: systemctl list-unit-files acpid.socket + args: + warn: false + register: socket_file_exists + changed_when: false + ignore_errors: true + check_mode: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82407-8 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_acpid_disabled + +- name: Disable socket acpid + systemd: + name: acpid.socket + enabled: 'no' + state: stopped + masked: 'yes' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"acpid.socket" in socket_file_exists.stdout_lines[1]' + tags: + - CCE-82407-8 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_acpid_disabled + + +[customizations.services] +disabled = ["acpid"] + + apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + systemd: + units: + - name: acpid.service + enabled: false + mask: true + - name: acpid.socket + enabled: false + mask: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" stop 'acpid.service' +"$SYSTEMCTL_EXEC" disable 'acpid.service' +"$SYSTEMCTL_EXEC" mask 'acpid.service' +# Disable socket activation if we have a unit file for it +if "$SYSTEMCTL_EXEC" list-unit-files | grep -q '^acpid.socket'; then + "$SYSTEMCTL_EXEC" stop 'acpid.socket' + "$SYSTEMCTL_EXEC" mask 'acpid.socket' +fi +# The service may not be running because it has been started and failed, +# so let's reset the state so OVAL checks pass. +# Service should be 'inactive', not 'failed' after reboot though. +"$SYSTEMCTL_EXEC" reset-failed 'acpid.service' || true + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable Certmonger Service (certmonger) + Certmonger is a D-Bus based service that attempts to simplify interaction +with certifying authorities on networks which use public-key infrastructure. It is often +combined with Red Hat's IPA (Identity Policy Audit) security information management +solution to aid in the management of certificates. + +The certmonger service can be disabled with the following command: +$ sudo systemctl mask --now certmonger.service + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + PR.PT-3 + The services provided by certmonger may be essential for systems +fulfilling some roles a PKI infrastructure, but its functionality is not necessary +for many other use cases. + + CCE-82452-4 + include disable_certmonger + +class disable_certmonger { + service {'certmonger': + enable => false, + ensure => 'stopped', + } +} + + - name: Disable service certmonger + block: + + - name: Disable service certmonger + systemd: + name: certmonger.service + enabled: 'no' + state: stopped + masked: 'yes' + ignore_errors: 'yes' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82452-4 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - service_certmonger_disabled + +- name: Unit Socket Exists - certmonger.socket + command: systemctl list-unit-files certmonger.socket + args: + warn: false + register: socket_file_exists + changed_when: false + ignore_errors: true + check_mode: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82452-4 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - service_certmonger_disabled + +- name: Disable socket certmonger + systemd: + name: certmonger.socket + enabled: 'no' + state: stopped + masked: 'yes' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"certmonger.socket" in socket_file_exists.stdout_lines[1]' + tags: + - CCE-82452-4 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - service_certmonger_disabled + + +[customizations.services] +disabled = ["certmonger"] + + apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + systemd: + units: + - name: certmonger.service + enabled: false + mask: true + - name: certmonger.socket + enabled: false + mask: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" stop 'certmonger.service' +"$SYSTEMCTL_EXEC" disable 'certmonger.service' +"$SYSTEMCTL_EXEC" mask 'certmonger.service' +# Disable socket activation if we have a unit file for it +if "$SYSTEMCTL_EXEC" list-unit-files | grep -q '^certmonger.socket'; then + "$SYSTEMCTL_EXEC" stop 'certmonger.socket' + "$SYSTEMCTL_EXEC" mask 'certmonger.socket' +fi +# The service may not be running because it has been started and failed, +# so let's reset the state so OVAL checks pass. +# Service should be 'inactive', not 'failed' after reboot though. +"$SYSTEMCTL_EXEC" reset-failed 'certmonger.service' || true + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable Cockpit Management Server + The Cockpit Management Server (cockpit) provides a web based +login and management framework. + +The cockpit service can be disabled with the following command: +$ sudo systemctl mask --now cockpit.service + Cockpit provides a form of remote login. + + include disable_cockpit + +class disable_cockpit { + service {'cockpit': + enable => false, + ensure => 'stopped', + } +} + + - name: Disable service cockpit + block: + + - name: Disable service cockpit + systemd: + name: cockpit.service + enabled: 'no' + state: stopped + masked: 'yes' + ignore_errors: 'yes' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_cockpit_disabled + +- name: Unit Socket Exists - cockpit.socket + command: systemctl list-unit-files cockpit.socket + args: + warn: false + register: socket_file_exists + changed_when: false + ignore_errors: true + check_mode: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_cockpit_disabled + +- name: Disable socket cockpit + systemd: + name: cockpit.socket + enabled: 'no' + state: stopped + masked: 'yes' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"cockpit.socket" in socket_file_exists.stdout_lines[1]' + tags: + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_cockpit_disabled + + +[customizations.services] +disabled = ["cockpit"] + + apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + systemd: + units: + - name: cockpit.service + enabled: false + mask: true + - name: cockpit.socket + enabled: false + mask: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" stop 'cockpit.service' +"$SYSTEMCTL_EXEC" disable 'cockpit.service' +"$SYSTEMCTL_EXEC" mask 'cockpit.service' +# Disable socket activation if we have a unit file for it +if "$SYSTEMCTL_EXEC" list-unit-files | grep -q '^cockpit.socket'; then + "$SYSTEMCTL_EXEC" stop 'cockpit.socket' + "$SYSTEMCTL_EXEC" mask 'cockpit.socket' +fi +# The service may not be running because it has been started and failed, +# so let's reset the state so OVAL checks pass. +# Service should be 'inactive', not 'failed' after reboot though. +"$SYSTEMCTL_EXEC" reset-failed 'cockpit.service' || true + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable CPU Speed (cpupower) + The cpupower service can adjust the clock speed of supported CPUs based upon +the current processing load thereby conserving power and reducing heat. + +The cpupower service can be disabled with the following command: +$ sudo systemctl mask --now cpupower.service + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + PR.PT-3 + The cpupower service is only necessary if adjusting the CPU clock speed +provides benefit. Traditionally this has included laptops (to enhance battery life), +but may also apply to server or desktop environments where conserving power is +highly desirable or necessary. + + CCE-82382-3 + include disable_cpupower + +class disable_cpupower { + service {'cpupower': + enable => false, + ensure => 'stopped', + } +} + + - name: Disable service cpupower + block: + + - name: Disable service cpupower + systemd: + name: cpupower.service + enabled: 'no' + state: stopped + masked: 'yes' + ignore_errors: 'yes' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82382-3 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - service_cpupower_disabled + +- name: Unit Socket Exists - cpupower.socket + command: systemctl list-unit-files cpupower.socket + args: + warn: false + register: socket_file_exists + changed_when: false + ignore_errors: true + check_mode: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82382-3 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - service_cpupower_disabled + +- name: Disable socket cpupower + systemd: + name: cpupower.socket + enabled: 'no' + state: stopped + masked: 'yes' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"cpupower.socket" in socket_file_exists.stdout_lines[1]' + tags: + - CCE-82382-3 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - service_cpupower_disabled + + +[customizations.services] +disabled = ["cpupower"] + + apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + systemd: + units: + - name: cpupower.service + enabled: false + mask: true + - name: cpupower.socket + enabled: false + mask: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" stop 'cpupower.service' +"$SYSTEMCTL_EXEC" disable 'cpupower.service' +"$SYSTEMCTL_EXEC" mask 'cpupower.service' +# Disable socket activation if we have a unit file for it +if "$SYSTEMCTL_EXEC" list-unit-files | grep -q '^cpupower.socket'; then + "$SYSTEMCTL_EXEC" stop 'cpupower.socket' + "$SYSTEMCTL_EXEC" mask 'cpupower.socket' +fi +# The service may not be running because it has been started and failed, +# so let's reset the state so OVAL checks pass. +# Service should be 'inactive', not 'failed' after reboot though. +"$SYSTEMCTL_EXEC" reset-failed 'cpupower.service' || true + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable KDump Kernel Crash Analyzer (kdump) + The kdump service provides a kernel crash dump analyzer. It uses the kexec +system call to boot a secondary kernel ("capture" kernel) following a system +crash, which can load information from the crashed kernel for analysis. + +The kdump service can be disabled with the following command: +$ sudo systemctl mask --now kdump.service + 11 + 12 + 14 + 15 + 3 + 8 + 9 + APO13.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.04 + DSS05.02 + DSS05.03 + DSS05.05 + DSS06.06 + CCI-000366 + CCI-001665 + 164.308(a)(1)(ii)(D) + 164.308(a)(3) + 164.308(a)(4) + 164.310(b) + 164.310(c) + 164.312(a) + 164.312(e) + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.6.2.1 + A.6.2.2 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.AC-3 + PR.IP-1 + PR.PT-3 + PR.PT-4 + FMT_SMF_EXT.1.1 + SRG-OS-000269-GPOS-00103 + SRG-OS-000480-GPOS-00227 + RHEL-08-010670 + SV-230310r627750_rule + Kernel core dumps may contain the full contents of system memory at the +time of the crash. Kernel core dumps consume a considerable amount of disk +space and may result in denial of service by exhausting the available space +on the target file system partition. Unless the system is used for kernel +development or testing, there is little need to run the kdump service. + + CCE-80878-2 + +kdump --disable + + include disable_kdump + +class disable_kdump { + service {'kdump': + enable => false, + ensure => 'stopped', + } +} + + - name: Disable service kdump + block: + + - name: Disable service kdump + systemd: + name: kdump.service + enabled: 'no' + state: stopped + masked: 'yes' + ignore_errors: 'yes' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80878-2 + - DISA-STIG-RHEL-08-010670 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_kdump_disabled + +- name: Unit Socket Exists - kdump.socket + command: systemctl list-unit-files kdump.socket + args: + warn: false + register: socket_file_exists + changed_when: false + ignore_errors: true + check_mode: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80878-2 + - DISA-STIG-RHEL-08-010670 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_kdump_disabled + +- name: Disable socket kdump + systemd: + name: kdump.socket + enabled: 'no' + state: stopped + masked: 'yes' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"kdump.socket" in socket_file_exists.stdout_lines[1]' + tags: + - CCE-80878-2 + - DISA-STIG-RHEL-08-010670 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_kdump_disabled + + +[customizations.services] +disabled = ["kdump"] + + apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + systemd: + units: + - name: kdump.service + enabled: false + mask: true + - name: kdump.socket + enabled: false + mask: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" stop 'kdump.service' +"$SYSTEMCTL_EXEC" disable 'kdump.service' +"$SYSTEMCTL_EXEC" mask 'kdump.service' +# Disable socket activation if we have a unit file for it +if "$SYSTEMCTL_EXEC" list-unit-files | grep -q '^kdump.socket'; then + "$SYSTEMCTL_EXEC" stop 'kdump.socket' + "$SYSTEMCTL_EXEC" mask 'kdump.socket' +fi +# The service may not be running because it has been started and failed, +# so let's reset the state so OVAL checks pass. +# Service should be 'inactive', not 'failed' after reboot though. +"$SYSTEMCTL_EXEC" reset-failed 'kdump.service' || true + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable Software RAID Monitor (mdmonitor) + The mdmonitor service is used for monitoring a software RAID array; hardware +RAID setups do not use this service. + +The mdmonitor service can be disabled with the following command: +$ sudo systemctl mask --now mdmonitor.service + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + PR.PT-3 + If software RAID monitoring is not required, +there is no need to run this service. + + CCE-82386-4 + include disable_mdmonitor + +class disable_mdmonitor { + service {'mdmonitor': + enable => false, + ensure => 'stopped', + } +} + + - name: Disable service mdmonitor + block: + + - name: Disable service mdmonitor + systemd: + name: mdmonitor.service + enabled: 'no' + state: stopped + masked: 'yes' + ignore_errors: 'yes' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82386-4 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - service_mdmonitor_disabled + +- name: Unit Socket Exists - mdmonitor.socket + command: systemctl list-unit-files mdmonitor.socket + args: + warn: false + register: socket_file_exists + changed_when: false + ignore_errors: true + check_mode: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82386-4 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - service_mdmonitor_disabled + +- name: Disable socket mdmonitor + systemd: + name: mdmonitor.socket + enabled: 'no' + state: stopped + masked: 'yes' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"mdmonitor.socket" in socket_file_exists.stdout_lines[1]' + tags: + - CCE-82386-4 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - service_mdmonitor_disabled + + +[customizations.services] +disabled = ["mdmonitor"] + + apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + systemd: + units: + - name: mdmonitor.service + enabled: false + mask: true + - name: mdmonitor.socket + enabled: false + mask: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" stop 'mdmonitor.service' +"$SYSTEMCTL_EXEC" disable 'mdmonitor.service' +"$SYSTEMCTL_EXEC" mask 'mdmonitor.service' +# Disable socket activation if we have a unit file for it +if "$SYSTEMCTL_EXEC" list-unit-files | grep -q '^mdmonitor.socket'; then + "$SYSTEMCTL_EXEC" stop 'mdmonitor.socket' + "$SYSTEMCTL_EXEC" mask 'mdmonitor.socket' +fi +# The service may not be running because it has been started and failed, +# so let's reset the state so OVAL checks pass. +# Service should be 'inactive', not 'failed' after reboot though. +"$SYSTEMCTL_EXEC" reset-failed 'mdmonitor.service' || true + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable Network Console (netconsole) + The netconsole service is responsible for loading the +netconsole kernel module, which logs kernel printk messages over UDP to a +syslog server. This allows debugging of problems where disk logging fails and +serial consoles are impractical. + +The netconsole service can be disabled with the following command: +$ sudo systemctl mask --now netconsole.service + 11 + 12 + 14 + 15 + 3 + 8 + 9 + APO13.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.04 + DSS05.02 + DSS05.03 + DSS05.05 + DSS06.06 + CCI-000381 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.6.2.1 + A.6.2.2 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.AC-3 + PR.IP-1 + PR.PT-3 + PR.PT-4 + The netconsole service is not necessary unless there is a need to debug +kernel panics, which is not common. + + CCE-82455-7 + include disable_netconsole + +class disable_netconsole { + service {'netconsole': + enable => false, + ensure => 'stopped', + } +} + + - name: Disable service netconsole + block: + + - name: Disable service netconsole + systemd: + name: netconsole.service + enabled: 'no' + state: stopped + masked: 'yes' + ignore_errors: 'yes' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82455-7 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - service_netconsole_disabled + +- name: Unit Socket Exists - netconsole.socket + command: systemctl list-unit-files netconsole.socket + args: + warn: false + register: socket_file_exists + changed_when: false + ignore_errors: true + check_mode: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82455-7 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - service_netconsole_disabled + +- name: Disable socket netconsole + systemd: + name: netconsole.socket + enabled: 'no' + state: stopped + masked: 'yes' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"netconsole.socket" in socket_file_exists.stdout_lines[1]' + tags: + - CCE-82455-7 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - service_netconsole_disabled + + +[customizations.services] +disabled = ["netconsole"] + + apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + systemd: + units: + - name: netconsole.service + enabled: false + mask: true + - name: netconsole.socket + enabled: false + mask: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" stop 'netconsole.service' +"$SYSTEMCTL_EXEC" disable 'netconsole.service' +"$SYSTEMCTL_EXEC" mask 'netconsole.service' +# Disable socket activation if we have a unit file for it +if "$SYSTEMCTL_EXEC" list-unit-files | grep -q '^netconsole.socket'; then + "$SYSTEMCTL_EXEC" stop 'netconsole.socket' + "$SYSTEMCTL_EXEC" mask 'netconsole.socket' +fi +# The service may not be running because it has been started and failed, +# so let's reset the state so OVAL checks pass. +# Service should be 'inactive', not 'failed' after reboot though. +"$SYSTEMCTL_EXEC" reset-failed 'netconsole.service' || true + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable ntpdate Service (ntpdate) + The ntpdate service sets the local hardware clock by polling NTP servers +when the system boots. It synchronizes to the NTP servers listed in +/etc/ntp/step-tickers or /etc/ntp.conf +and then sets the local hardware clock to the newly synchronized +system time. + +The ntpdate service can be disabled with the following command: +$ sudo systemctl mask --now ntpdate.service + 11 + 12 + 14 + 15 + 3 + 8 + 9 + APO13.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.04 + DSS05.02 + DSS05.03 + DSS05.05 + DSS06.06 + CCI-000382 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.6.2.1 + A.6.2.2 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.AC-3 + PR.IP-1 + PR.PT-3 + PR.PT-4 + The ntpdate service may only be suitable for systems which +are rebooted frequently enough that clock drift does not cause problems between +reboots. In any event, the functionality of the ntpdate service is now +available in the ntpd program and should be considered deprecated. + + CCE-80879-0 + include disable_ntpdate + +class disable_ntpdate { + service {'ntpdate': + enable => false, + ensure => 'stopped', + } +} + + - name: Disable service ntpdate + block: + + - name: Disable service ntpdate + systemd: + name: ntpdate.service + enabled: 'no' + state: stopped + masked: 'yes' + ignore_errors: 'yes' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80879-0 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - service_ntpdate_disabled + +- name: Unit Socket Exists - ntpdate.socket + command: systemctl list-unit-files ntpdate.socket + args: + warn: false + register: socket_file_exists + changed_when: false + ignore_errors: true + check_mode: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80879-0 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - service_ntpdate_disabled + +- name: Disable socket ntpdate + systemd: + name: ntpdate.socket + enabled: 'no' + state: stopped + masked: 'yes' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"ntpdate.socket" in socket_file_exists.stdout_lines[1]' + tags: + - CCE-80879-0 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - service_ntpdate_disabled + + +[customizations.services] +disabled = ["ntpdate"] + + apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + systemd: + units: + - name: ntpdate.service + enabled: false + mask: true + - name: ntpdate.socket + enabled: false + mask: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" stop 'ntpdate.service' +"$SYSTEMCTL_EXEC" disable 'ntpdate.service' +"$SYSTEMCTL_EXEC" mask 'ntpdate.service' +# Disable socket activation if we have a unit file for it +if "$SYSTEMCTL_EXEC" list-unit-files | grep -q '^ntpdate.socket'; then + "$SYSTEMCTL_EXEC" stop 'ntpdate.socket' + "$SYSTEMCTL_EXEC" mask 'ntpdate.socket' +fi +# The service may not be running because it has been started and failed, +# so let's reset the state so OVAL checks pass. +# Service should be 'inactive', not 'failed' after reboot though. +"$SYSTEMCTL_EXEC" reset-failed 'ntpdate.service' || true + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable Odd Job Daemon (oddjobd) + The oddjobd service exists to provide an interface and +access control mechanism through which +specified privileged tasks can run tasks for unprivileged client +applications. Communication with oddjobd through the system message bus. + +The oddjobd service can be disabled with the following command: +$ sudo systemctl mask --now oddjobd.service + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + CCI-000381 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + PR.PT-3 + The oddjobd service may provide necessary functionality in +some environments, and can be disabled if it is not needed. Execution of +tasks by privileged programs, on behalf of unprivileged ones, has traditionally +been a source of privilege escalation security issues. + + CCE-80880-8 + include disable_oddjobd + +class disable_oddjobd { + service {'oddjobd': + enable => false, + ensure => 'stopped', + } +} + + - name: Disable service oddjobd + block: + + - name: Disable service oddjobd + systemd: + name: oddjobd.service + enabled: 'no' + state: stopped + masked: 'yes' + ignore_errors: 'yes' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80880-8 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_oddjobd_disabled + +- name: Unit Socket Exists - oddjobd.socket + command: systemctl list-unit-files oddjobd.socket + args: + warn: false + register: socket_file_exists + changed_when: false + ignore_errors: true + check_mode: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80880-8 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_oddjobd_disabled + +- name: Disable socket oddjobd + systemd: + name: oddjobd.socket + enabled: 'no' + state: stopped + masked: 'yes' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"oddjobd.socket" in socket_file_exists.stdout_lines[1]' + tags: + - CCE-80880-8 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_oddjobd_disabled + + +[customizations.services] +disabled = ["oddjobd"] + + apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + systemd: + units: + - name: oddjobd.service + enabled: false + mask: true + - name: oddjobd.socket + enabled: false + mask: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" stop 'oddjobd.service' +"$SYSTEMCTL_EXEC" disable 'oddjobd.service' +"$SYSTEMCTL_EXEC" mask 'oddjobd.service' +# Disable socket activation if we have a unit file for it +if "$SYSTEMCTL_EXEC" list-unit-files | grep -q '^oddjobd.socket'; then + "$SYSTEMCTL_EXEC" stop 'oddjobd.socket' + "$SYSTEMCTL_EXEC" mask 'oddjobd.socket' +fi +# The service may not be running because it has been started and failed, +# so let's reset the state so OVAL checks pass. +# Service should be 'inactive', not 'failed' after reboot though. +"$SYSTEMCTL_EXEC" reset-failed 'oddjobd.service' || true + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable Portreserve (portreserve) + The portreserve service is a TCP port reservation utility that can +be used to prevent portmap from binding to well known TCP ports that are +required for other services. + +The portreserve service can be disabled with the following command: +$ sudo systemctl mask --now portreserve.service + 11 + 12 + 14 + 15 + 3 + 8 + 9 + APO13.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.04 + DSS05.02 + DSS05.03 + DSS05.05 + DSS06.06 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.6.2.1 + A.6.2.2 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.AC-3 + PR.IP-1 + PR.PT-3 + PR.PT-4 + The portreserve service provides helpful functionality by +preventing conflicting usage of ports in the reserved port range, but it can be +disabled if not needed. + + CCE-82390-6 + include disable_portreserve + +class disable_portreserve { + service {'portreserve': + enable => false, + ensure => 'stopped', + } +} + + - name: Disable service portreserve + block: + + - name: Disable service portreserve + systemd: + name: portreserve.service + enabled: 'no' + state: stopped + masked: 'yes' + ignore_errors: 'yes' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82390-6 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - service_portreserve_disabled + +- name: Unit Socket Exists - portreserve.socket + command: systemctl list-unit-files portreserve.socket + args: + warn: false + register: socket_file_exists + changed_when: false + ignore_errors: true + check_mode: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82390-6 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - service_portreserve_disabled + +- name: Disable socket portreserve + systemd: + name: portreserve.socket + enabled: 'no' + state: stopped + masked: 'yes' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"portreserve.socket" in socket_file_exists.stdout_lines[1]' + tags: + - CCE-82390-6 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - service_portreserve_disabled + + +[customizations.services] +disabled = ["portreserve"] + + apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + systemd: + units: + - name: portreserve.service + enabled: false + mask: true + - name: portreserve.socket + enabled: false + mask: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" stop 'portreserve.service' +"$SYSTEMCTL_EXEC" disable 'portreserve.service' +"$SYSTEMCTL_EXEC" mask 'portreserve.service' +# Disable socket activation if we have a unit file for it +if "$SYSTEMCTL_EXEC" list-unit-files | grep -q '^portreserve.socket'; then + "$SYSTEMCTL_EXEC" stop 'portreserve.socket' + "$SYSTEMCTL_EXEC" mask 'portreserve.socket' +fi +# The service may not be running because it has been started and failed, +# so let's reset the state so OVAL checks pass. +# Service should be 'inactive', not 'failed' after reboot though. +"$SYSTEMCTL_EXEC" reset-failed 'portreserve.service' || true + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable Apache Qpid (qpidd) + The qpidd service provides high speed, secure, +guaranteed delivery services. It is an implementation of the Advanced Message +Queuing Protocol. By default the qpidd service will bind to port 5672 and +listen for connection attempts. + +The qpidd service can be disabled with the following command: +$ sudo systemctl mask --now qpidd.service + 11 + 12 + 14 + 15 + 3 + 8 + 9 + APO13.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.04 + DSS05.02 + DSS05.03 + DSS05.05 + DSS06.06 + CCI-000382 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.6.2.1 + A.6.2.2 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.AC-3 + PR.IP-1 + PR.PT-3 + PR.PT-4 + The qpidd service is automatically installed when the base package +selection is selected during installation. The qpidd service listens for +network connections, which increases the attack surface of the system. If +the system is not intended to receive AMQP traffic, then the qpidd +service is not needed and should be disabled or removed. + + CCE-80882-4 + include disable_qpidd + +class disable_qpidd { + service {'qpidd': + enable => false, + ensure => 'stopped', + } +} + + - name: Disable service qpidd + block: + + - name: Disable service qpidd + systemd: + name: qpidd.service + enabled: 'no' + state: stopped + masked: 'yes' + ignore_errors: 'yes' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80882-4 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - service_qpidd_disabled + +- name: Unit Socket Exists - qpidd.socket + command: systemctl list-unit-files qpidd.socket + args: + warn: false + register: socket_file_exists + changed_when: false + ignore_errors: true + check_mode: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80882-4 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - service_qpidd_disabled + +- name: Disable socket qpidd + systemd: + name: qpidd.socket + enabled: 'no' + state: stopped + masked: 'yes' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"qpidd.socket" in socket_file_exists.stdout_lines[1]' + tags: + - CCE-80882-4 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - service_qpidd_disabled + + +[customizations.services] +disabled = ["qpidd"] + + apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + systemd: + units: + - name: qpidd.service + enabled: false + mask: true + - name: qpidd.socket + enabled: false + mask: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" stop 'qpidd.service' +"$SYSTEMCTL_EXEC" disable 'qpidd.service' +"$SYSTEMCTL_EXEC" mask 'qpidd.service' +# Disable socket activation if we have a unit file for it +if "$SYSTEMCTL_EXEC" list-unit-files | grep -q '^qpidd.socket'; then + "$SYSTEMCTL_EXEC" stop 'qpidd.socket' + "$SYSTEMCTL_EXEC" mask 'qpidd.socket' +fi +# The service may not be running because it has been started and failed, +# so let's reset the state so OVAL checks pass. +# Service should be 'inactive', not 'failed' after reboot though. +"$SYSTEMCTL_EXEC" reset-failed 'qpidd.service' || true + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable Quota Netlink (quota_nld) + The quota_nld service provides notifications to +users of disk space quota violations. It listens to the kernel via a netlink +socket for disk quota violations and notifies the appropriate user of the +violation using D-Bus or by sending a message to the terminal that the user has +last accessed. + +The quota_nld service can be disabled with the following command: +$ sudo systemctl mask --now quota_nld.service + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + PR.PT-3 + If disk quotas are enforced on the local system, then the +quota_nld service likely provides useful functionality and should +remain enabled. However, if disk quotas are not used or user notification of +disk quota violation is not desired then there is no need to run this +service. + + CCE-82406-0 + include disable_quota_nld + +class disable_quota_nld { + service {'quota_nld': + enable => false, + ensure => 'stopped', + } +} + + - name: Disable service quota_nld + block: + + - name: Disable service quota_nld + systemd: + name: quota_nld.service + enabled: 'no' + state: stopped + masked: 'yes' + ignore_errors: 'yes' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82406-0 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - service_quota_nld_disabled + +- name: Unit Socket Exists - quota_nld.socket + command: systemctl list-unit-files quota_nld.socket + args: + warn: false + register: socket_file_exists + changed_when: false + ignore_errors: true + check_mode: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82406-0 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - service_quota_nld_disabled + +- name: Disable socket quota_nld + systemd: + name: quota_nld.socket + enabled: 'no' + state: stopped + masked: 'yes' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"quota_nld.socket" in socket_file_exists.stdout_lines[1]' + tags: + - CCE-82406-0 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - service_quota_nld_disabled + + +[customizations.services] +disabled = ["quota_nld"] + + apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + systemd: + units: + - name: quota_nld.service + enabled: false + mask: true + - name: quota_nld.socket + enabled: false + mask: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" stop 'quota_nld.service' +"$SYSTEMCTL_EXEC" disable 'quota_nld.service' +"$SYSTEMCTL_EXEC" mask 'quota_nld.service' +# Disable socket activation if we have a unit file for it +if "$SYSTEMCTL_EXEC" list-unit-files | grep -q '^quota_nld.socket'; then + "$SYSTEMCTL_EXEC" stop 'quota_nld.socket' + "$SYSTEMCTL_EXEC" mask 'quota_nld.socket' +fi +# The service may not be running because it has been started and failed, +# so let's reset the state so OVAL checks pass. +# Service should be 'inactive', not 'failed' after reboot though. +"$SYSTEMCTL_EXEC" reset-failed 'quota_nld.service' || true + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable Network Router Discovery Daemon (rdisc) + The rdisc service implements the client side of the ICMP +Internet Router Discovery Protocol (IRDP), which allows discovery of routers on +the local subnet. If a router is discovered then the local routing table is +updated with a corresponding default route. By default this daemon is disabled. + +The rdisc service can be disabled with the following command: +$ sudo systemctl mask --now rdisc.service + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 4 + 6 + 8 + 9 + APO01.06 + APO13.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.04 + DSS01.05 + DSS03.01 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + DSS06.02 + DSS06.06 + CCI-000382 + 4.2.3.4 + 4.3.3.4 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + 4.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.11.2.6 + A.12.1.1 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.13.1.1 + A.13.1.2 + A.13.1.3 + A.13.2.1 + A.13.2.2 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.6.1.2 + A.6.2.1 + A.6.2.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + AC-4 + CM-7(a) + CM-7(b) + CM-6(a) + DE.AE-1 + ID.AM-3 + PR.AC-3 + PR.AC-5 + PR.DS-5 + PR.IP-1 + PR.PT-3 + PR.PT-4 + General-purpose systems typically have their network and routing +information configured statically by a system administrator. Workstations or +some special-purpose systems often use DHCP (instead of IRDP) to retrieve +dynamic network configuration information. + + CCE-80883-2 + include disable_rdisc + +class disable_rdisc { + service {'rdisc': + enable => false, + ensure => 'stopped', + } +} + + - name: Disable service rdisc + block: + + - name: Disable service rdisc + systemd: + name: rdisc.service + enabled: 'no' + state: stopped + masked: 'yes' + ignore_errors: 'yes' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80883-2 + - NIST-800-53-AC-4 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_rdisc_disabled + +- name: Unit Socket Exists - rdisc.socket + command: systemctl list-unit-files rdisc.socket + args: + warn: false + register: socket_file_exists + changed_when: false + ignore_errors: true + check_mode: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80883-2 + - NIST-800-53-AC-4 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_rdisc_disabled + +- name: Disable socket rdisc + systemd: + name: rdisc.socket + enabled: 'no' + state: stopped + masked: 'yes' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"rdisc.socket" in socket_file_exists.stdout_lines[1]' + tags: + - CCE-80883-2 + - NIST-800-53-AC-4 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_rdisc_disabled + + +[customizations.services] +disabled = ["rdisc"] + + apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + systemd: + units: + - name: rdisc.service + enabled: false + mask: true + - name: rdisc.socket + enabled: false + mask: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" stop 'rdisc.service' +"$SYSTEMCTL_EXEC" disable 'rdisc.service' +"$SYSTEMCTL_EXEC" mask 'rdisc.service' +# Disable socket activation if we have a unit file for it +if "$SYSTEMCTL_EXEC" list-unit-files | grep -q '^rdisc.socket'; then + "$SYSTEMCTL_EXEC" stop 'rdisc.socket' + "$SYSTEMCTL_EXEC" mask 'rdisc.socket' +fi +# The service may not be running because it has been started and failed, +# so let's reset the state so OVAL checks pass. +# Service should be 'inactive', not 'failed' after reboot though. +"$SYSTEMCTL_EXEC" reset-failed 'rdisc.service' || true + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable Red Hat Network Service (rhnsd) + The Red Hat Network service automatically queries Red Hat Network +servers to determine whether there are any actions that should be executed, +such as package updates. This only occurs if the system was registered to an +RHN server or satellite and managed as such. + +The rhnsd service can be disabled with the following command: +$ sudo systemctl mask --now rhnsd.service + 11 + 12 + 14 + 15 + 3 + 8 + 9 + APO13.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.04 + DSS05.02 + DSS05.03 + DSS05.05 + DSS06.06 + CCI-000382 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.6.2.1 + A.6.2.2 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.AC-3 + PR.IP-1 + PR.PT-3 + PR.PT-4 + 1.2.2 + Although systems management and patching is extremely important to +system security, management by a system outside the enterprise enclave is not +desirable for some environments. However, if the system is being managed by RHN or + RHN Satellite Server the rhnsd daemon can remain on. + + CCE-82405-2 + include disable_rhnsd + +class disable_rhnsd { + service {'rhnsd': + enable => false, + ensure => 'stopped', + } +} + + - name: Disable service rhnsd + block: + + - name: Disable service rhnsd + systemd: + name: rhnsd.service + enabled: 'no' + state: stopped + masked: 'yes' + ignore_errors: 'yes' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82405-2 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - service_rhnsd_disabled + +- name: Unit Socket Exists - rhnsd.socket + command: systemctl list-unit-files rhnsd.socket + args: + warn: false + register: socket_file_exists + changed_when: false + ignore_errors: true + check_mode: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82405-2 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - service_rhnsd_disabled + +- name: Disable socket rhnsd + systemd: + name: rhnsd.socket + enabled: 'no' + state: stopped + masked: 'yes' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"rhnsd.socket" in socket_file_exists.stdout_lines[1]' + tags: + - CCE-82405-2 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - service_rhnsd_disabled + + +[customizations.services] +disabled = ["rhnsd"] + + apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + systemd: + units: + - name: rhnsd.service + enabled: false + mask: true + - name: rhnsd.socket + enabled: false + mask: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" stop 'rhnsd.service' +"$SYSTEMCTL_EXEC" disable 'rhnsd.service' +"$SYSTEMCTL_EXEC" mask 'rhnsd.service' +# Disable socket activation if we have a unit file for it +if "$SYSTEMCTL_EXEC" list-unit-files | grep -q '^rhnsd.socket'; then + "$SYSTEMCTL_EXEC" stop 'rhnsd.socket' + "$SYSTEMCTL_EXEC" mask 'rhnsd.socket' +fi +# The service may not be running because it has been started and failed, +# so let's reset the state so OVAL checks pass. +# Service should be 'inactive', not 'failed' after reboot though. +"$SYSTEMCTL_EXEC" reset-failed 'rhnsd.service' || true + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable Red Hat Subscription Manager Daemon (rhsmcertd) + The Red Hat Subscription Manager (rhsmcertd) periodically checks for +changes in the entitlement certificates for a registered system and updates it +accordingly. + +The rhsmcertd service can be disabled with the following command: +$ sudo systemctl mask --now rhsmcertd.service + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + PR.PT-3 + The rhsmcertd service can provide administrators with some +additional control over which of their systems are entitled to particular +subscriptions. However, for systems that are managed locally or which are not +expected to require remote changes to their subscription status, it is +unnecessary and can be disabled. + + CCE-82387-2 + include disable_rhsmcertd + +class disable_rhsmcertd { + service {'rhsmcertd': + enable => false, + ensure => 'stopped', + } +} + + - name: Disable service rhsmcertd + block: + + - name: Disable service rhsmcertd + systemd: + name: rhsmcertd.service + enabled: 'no' + state: stopped + masked: 'yes' + ignore_errors: 'yes' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82387-2 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - service_rhsmcertd_disabled + +- name: Unit Socket Exists - rhsmcertd.socket + command: systemctl list-unit-files rhsmcertd.socket + args: + warn: false + register: socket_file_exists + changed_when: false + ignore_errors: true + check_mode: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82387-2 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - service_rhsmcertd_disabled + +- name: Disable socket rhsmcertd + systemd: + name: rhsmcertd.socket + enabled: 'no' + state: stopped + masked: 'yes' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"rhsmcertd.socket" in socket_file_exists.stdout_lines[1]' + tags: + - CCE-82387-2 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - service_rhsmcertd_disabled + + +[customizations.services] +disabled = ["rhsmcertd"] + + apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + systemd: + units: + - name: rhsmcertd.service + enabled: false + mask: true + - name: rhsmcertd.socket + enabled: false + mask: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" stop 'rhsmcertd.service' +"$SYSTEMCTL_EXEC" disable 'rhsmcertd.service' +"$SYSTEMCTL_EXEC" mask 'rhsmcertd.service' +# Disable socket activation if we have a unit file for it +if "$SYSTEMCTL_EXEC" list-unit-files | grep -q '^rhsmcertd.socket'; then + "$SYSTEMCTL_EXEC" stop 'rhsmcertd.socket' + "$SYSTEMCTL_EXEC" mask 'rhsmcertd.socket' +fi +# The service may not be running because it has been started and failed, +# so let's reset the state so OVAL checks pass. +# Service should be 'inactive', not 'failed' after reboot though. +"$SYSTEMCTL_EXEC" reset-failed 'rhsmcertd.service' || true + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable Cyrus SASL Authentication Daemon (saslauthd) + The saslauthd service handles plaintext authentication requests on +behalf of the SASL library. The service isolates all code requiring superuser +privileges for SASL authentication into a single process, and can also be used +to provide proxy authentication services to clients that do not understand SASL +based authentication. + +The saslauthd service can be disabled with the following command: +$ sudo systemctl mask --now saslauthd.service + 11 + 12 + 14 + 15 + 3 + 8 + 9 + APO13.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.04 + DSS05.02 + DSS05.03 + DSS05.05 + DSS06.06 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.6.2.1 + A.6.2.2 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.AC-3 + PR.IP-1 + PR.PT-3 + PR.PT-4 + The saslauthd service provides essential functionality for +performing authentication in some directory environments, such as those which +use Kerberos and LDAP. For others, however, in which only local files may be +consulted, it is not necessary and should be disabled. + + CCE-82389-8 + include disable_saslauthd + +class disable_saslauthd { + service {'saslauthd': + enable => false, + ensure => 'stopped', + } +} + + - name: Disable service saslauthd + block: + + - name: Disable service saslauthd + systemd: + name: saslauthd.service + enabled: 'no' + state: stopped + masked: 'yes' + ignore_errors: 'yes' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82389-8 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - service_saslauthd_disabled + +- name: Unit Socket Exists - saslauthd.socket + command: systemctl list-unit-files saslauthd.socket + args: + warn: false + register: socket_file_exists + changed_when: false + ignore_errors: true + check_mode: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82389-8 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - service_saslauthd_disabled + +- name: Disable socket saslauthd + systemd: + name: saslauthd.socket + enabled: 'no' + state: stopped + masked: 'yes' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"saslauthd.socket" in socket_file_exists.stdout_lines[1]' + tags: + - CCE-82389-8 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - service_saslauthd_disabled + + +[customizations.services] +disabled = ["saslauthd"] + + apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + systemd: + units: + - name: saslauthd.service + enabled: false + mask: true + - name: saslauthd.socket + enabled: false + mask: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" stop 'saslauthd.service' +"$SYSTEMCTL_EXEC" disable 'saslauthd.service' +"$SYSTEMCTL_EXEC" mask 'saslauthd.service' +# Disable socket activation if we have a unit file for it +if "$SYSTEMCTL_EXEC" list-unit-files | grep -q '^saslauthd.socket'; then + "$SYSTEMCTL_EXEC" stop 'saslauthd.socket' + "$SYSTEMCTL_EXEC" mask 'saslauthd.socket' +fi +# The service may not be running because it has been started and failed, +# so let's reset the state so OVAL checks pass. +# Service should be 'inactive', not 'failed' after reboot though. +"$SYSTEMCTL_EXEC" reset-failed 'saslauthd.service' || true + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable System Statistics Reset Service (sysstat) + The sysstat service resets various I/O and CPU +performance statistics to zero in order to begin counting from a fresh state +at boot time. + +The sysstat service can be disabled with the following command: +$ sudo systemctl mask --now sysstat.service + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + PR.PT-3 + By default the sysstat service runs a program at +boot to reset performance statistics. This data can be retrieved using programs such as +sar and sadc. While the sysstat service may provide useful +insight into system operation, through the lens of providing only essential system services, +this service should be disabled. + + CCE-82388-0 + include disable_sysstat + +class disable_sysstat { + service {'sysstat': + enable => false, + ensure => 'stopped', + } +} + + - name: Disable service sysstat + block: + + - name: Disable service sysstat + systemd: + name: sysstat.service + enabled: 'no' + state: stopped + masked: 'yes' + ignore_errors: 'yes' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82388-0 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - service_sysstat_disabled + +- name: Unit Socket Exists - sysstat.socket + command: systemctl list-unit-files sysstat.socket + args: + warn: false + register: socket_file_exists + changed_when: false + ignore_errors: true + check_mode: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82388-0 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - service_sysstat_disabled + +- name: Disable socket sysstat + systemd: + name: sysstat.socket + enabled: 'no' + state: stopped + masked: 'yes' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"sysstat.socket" in socket_file_exists.stdout_lines[1]' + tags: + - CCE-82388-0 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - service_sysstat_disabled + + +[customizations.services] +disabled = ["sysstat"] + + apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + systemd: + units: + - name: sysstat.service + enabled: false + mask: true + - name: sysstat.socket + enabled: false + mask: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" stop 'sysstat.service' +"$SYSTEMCTL_EXEC" disable 'sysstat.service' +"$SYSTEMCTL_EXEC" mask 'sysstat.service' +# Disable socket activation if we have a unit file for it +if "$SYSTEMCTL_EXEC" list-unit-files | grep -q '^sysstat.socket'; then + "$SYSTEMCTL_EXEC" stop 'sysstat.socket' + "$SYSTEMCTL_EXEC" mask 'sysstat.socket' +fi +# The service may not be running because it has been started and failed, +# so let's reset the state so OVAL checks pass. +# Service should be 'inactive', not 'failed' after reboot though. +"$SYSTEMCTL_EXEC" reset-failed 'sysstat.service' || true + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Cron and At Daemons + The cron and at services are used to allow commands to +be executed at a later time. The cron service is required by almost +all systems to perform necessary maintenance tasks, while at may or +may not be required on a given system. Both daemons should be +configured defensively. + + + Install the cron service + The Cron service should be installed. + BP28(R50) + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-6(a) + PR.IP-1 + PR.PT-3 + The cron service allow periodic job execution, needed for almost all administrative tasks and services (software update, log rotating, etc.). Access to cron service should be restricted to administrative accounts only. + +package --add=cron + + include install_cron + +class install_cron { + package { 'cron': + ensure => 'installed', + } +} + + - name: Ensure cron is installed + package: + name: cron + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-CM-6(a) + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_cron_installed + + +[[packages]] +name = "cron" +version = "*" + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if ! rpm -q --quiet "cron" ; then + yum install -y "cron" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + Enable cron Service + The crond service is used to execute commands at +preconfigured times. It is required by almost all systems to perform necessary +maintenance tasks, such as notifying root of system activity. + +The cron service can be enabled with the following command: +$ sudo systemctl enable cron.service + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + 164.308(a)(4)(i) + 164.308(b)(1) + 164.308(b)(3) + 164.310(b) + 164.312(e)(1) + 164.312(e)(2)(ii) + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-6(a) + PR.IP-1 + PR.PT-3 + Due to its usage for maintenance and security-supporting tasks, +enabling the cron daemon is essential. + include enable_cron + +class enable_cron { + service {'cron': + enable => true, + ensure => 'running', + } +} + + - name: Enable service cron + block: + + - name: Gather the package facts + package_facts: + manager: auto + + - name: Enable service cron + service: + name: cron + enabled: 'yes' + state: started + masked: 'no' + when: + - '"cron" in ansible_facts.packages' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-CM-6(a) + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_cron_enabled + + +[customizations.services] +enabled = ["cron"] + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" unmask 'cron.service' +"$SYSTEMCTL_EXEC" start 'cron.service' +"$SYSTEMCTL_EXEC" enable 'cron.service' + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Enable cron Service + The crond service is used to execute commands at +preconfigured times. It is required by almost all systems to perform necessary +maintenance tasks, such as notifying root of system activity. + +The crond service can be enabled with the following command: +$ sudo systemctl enable crond.service + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + 164.308(a)(4)(i) + 164.308(b)(1) + 164.308(b)(3) + 164.310(b) + 164.312(e)(1) + 164.312(e)(2)(ii) + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-6(a) + PR.IP-1 + PR.PT-3 + 5.1.1 + Due to its usage for maintenance and security-supporting tasks, +enabling the cron daemon is essential. + CCE-80875-8 + include enable_crond + +class enable_crond { + service {'crond': + enable => true, + ensure => 'running', + } +} + + - name: Enable service crond + block: + + - name: Gather the package facts + package_facts: + manager: auto + + - name: Enable service crond + service: + name: crond + enabled: 'yes' + state: started + masked: 'no' + when: + - '"cronie" in ansible_facts.packages' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80875-8 + - NIST-800-53-CM-6(a) + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_crond_enabled + + +[customizations.services] +enabled = ["crond"] + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" unmask 'crond.service' +"$SYSTEMCTL_EXEC" start 'crond.service' +"$SYSTEMCTL_EXEC" enable 'crond.service' + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable At Service (atd) + The at and batch commands can be used to +schedule tasks that are meant to be executed only once. This allows delayed +execution in a manner similar to cron, except that it is not +recurring. The daemon atd keeps track of tasks scheduled via +at and batch, and executes them at the specified time. + +The atd service can be disabled with the following command: +$ sudo systemctl mask --now atd.service + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + CCI-000381 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + PR.PT-3 + The atd service could be used by an unsophisticated insider to carry +out activities outside of a normal login session, which could complicate +accountability. Furthermore, the need to schedule tasks with at or +batch is not common. + + CCE-80871-7 + include disable_atd + +class disable_atd { + service {'atd': + enable => false, + ensure => 'stopped', + } +} + + - name: Disable service atd + block: + + - name: Disable service atd + systemd: + name: atd.service + enabled: 'no' + state: stopped + masked: 'yes' + ignore_errors: 'yes' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80871-7 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_atd_disabled + +- name: Unit Socket Exists - atd.socket + command: systemctl list-unit-files atd.socket + args: + warn: false + register: socket_file_exists + changed_when: false + ignore_errors: true + check_mode: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80871-7 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_atd_disabled + +- name: Disable socket atd + systemd: + name: atd.socket + enabled: 'no' + state: stopped + masked: 'yes' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"atd.socket" in socket_file_exists.stdout_lines[1]' + tags: + - CCE-80871-7 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_atd_disabled + + +[customizations.services] +disabled = ["atd"] + + apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + systemd: + units: + - name: atd.service + enabled: false + mask: true + - name: atd.socket + enabled: false + mask: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" stop 'atd.service' +"$SYSTEMCTL_EXEC" disable 'atd.service' +"$SYSTEMCTL_EXEC" mask 'atd.service' +# Disable socket activation if we have a unit file for it +if "$SYSTEMCTL_EXEC" list-unit-files | grep -q '^atd.socket'; then + "$SYSTEMCTL_EXEC" stop 'atd.socket' + "$SYSTEMCTL_EXEC" mask 'atd.socket' +fi +# The service may not be running because it has been started and failed, +# so let's reset the state so OVAL checks pass. +# Service should be 'inactive', not 'failed' after reboot though. +"$SYSTEMCTL_EXEC" reset-failed 'atd.service' || true + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable anacron Service + The cronie-anacron package, which provides anacron +functionality, is installed by default. +The cronie-anacron package can be removed with the following command: + +$ sudo yum erase cronie-anacron + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + PR.PT-3 + The anacron service provides cron functionality for systems +such as laptops and workstations that may be shut down during the normal times +that cron jobs are scheduled to run. On systems which do not require this +additional functionality, anacron could needlessly increase the possible +attack surface for an intruder. + + + + + + Verify Group Who Owns cron.d + +To properly set the group owner of /etc/cron.d, run the command: +$ sudo chgrp root /etc/cron.d + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + SRG-OS-000480-GPOS-00227 + 5.1.7 + Service configuration files enable or disable features of their respective services that if configured incorrectly +can lead to insecure and vulnerable configurations. Therefore, service configuration files should be owned by the +correct group to prevent unauthorized changes. + CCE-82268-4 + - name: Ensure group owner on /etc/cron.d/ + file: + path: /etc/cron.d/ + state: directory + group: '0' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82268-4 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_groupowner_cron_d + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +find -H /etc/cron.d/ -maxdepth 1 -type d -exec chgrp 0 {} \; + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Verify Group Who Owns cron.daily + +To properly set the group owner of /etc/cron.daily, run the command: +$ sudo chgrp root /etc/cron.daily + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + SRG-OS-000480-GPOS-00227 + 5.1.4 + Service configuration files enable or disable features of their respective services that if configured incorrectly +can lead to insecure and vulnerable configurations. Therefore, service configuration files should be owned by the +correct group to prevent unauthorized changes. + CCE-82234-6 + - name: Ensure group owner on /etc/cron.daily/ + file: + path: /etc/cron.daily/ + state: directory + group: '0' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82234-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_groupowner_cron_daily + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +find -H /etc/cron.daily/ -maxdepth 1 -type d -exec chgrp 0 {} \; + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Verify Group Who Owns cron.hourly + +To properly set the group owner of /etc/cron.hourly, run the command: +$ sudo chgrp root /etc/cron.hourly + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + SRG-OS-000480-GPOS-00227 + 5.1.3 + Service configuration files enable or disable features of their respective services that if configured incorrectly +can lead to insecure and vulnerable configurations. Therefore, service configuration files should be owned by the +correct group to prevent unauthorized changes. + CCE-82227-0 + - name: Ensure group owner on /etc/cron.hourly/ + file: + path: /etc/cron.hourly/ + state: directory + group: '0' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82227-0 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_groupowner_cron_hourly + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +find -H /etc/cron.hourly/ -maxdepth 1 -type d -exec chgrp 0 {} \; + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Verify Group Who Owns cron.monthly + +To properly set the group owner of /etc/cron.monthly, run the command: +$ sudo chgrp root /etc/cron.monthly + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + SRG-OS-000480-GPOS-00227 + 5.1.6 + Service configuration files enable or disable features of their respective services that if configured incorrectly +can lead to insecure and vulnerable configurations. Therefore, service configuration files should be owned by the +correct group to prevent unauthorized changes. + CCE-82256-9 + - name: Ensure group owner on /etc/cron.monthly/ + file: + path: /etc/cron.monthly/ + state: directory + group: '0' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82256-9 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_groupowner_cron_monthly + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +find -H /etc/cron.monthly/ -maxdepth 1 -type d -exec chgrp 0 {} \; + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Verify Group Who Owns cron.weekly + +To properly set the group owner of /etc/cron.weekly, run the command: +$ sudo chgrp root /etc/cron.weekly + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + SRG-OS-000480-GPOS-00227 + 5.1.5 + Service configuration files enable or disable features of their respective services that if configured incorrectly +can lead to insecure and vulnerable configurations. Therefore, service configuration files should be owned by the +correct group to prevent unauthorized changes. + CCE-82244-5 + - name: Ensure group owner on /etc/cron.weekly/ + file: + path: /etc/cron.weekly/ + state: directory + group: '0' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82244-5 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_groupowner_cron_weekly + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +find -H /etc/cron.weekly/ -maxdepth 1 -type d -exec chgrp 0 {} \; + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Verify Group Who Owns Crontab + +To properly set the group owner of /etc/crontab, run the command: +$ sudo chgrp root /etc/crontab + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + SRG-OS-000480-GPOS-00227 + 5.1.2 + Service configuration files enable or disable features of their respective services that if configured incorrectly +can lead to insecure and vulnerable configurations. Therefore, service configuration files should be owned by the +correct group to prevent unauthorized changes. + CCE-82223-9 + - name: Test for existence /etc/crontab + stat: + path: /etc/crontab + register: file_exists + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82223-9 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_groupowner_crontab + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure group owner 0 on /etc/crontab + file: + path: /etc/crontab + group: '0' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-82223-9 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_groupowner_crontab + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +chgrp 0 /etc/crontab + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Verify Owner on cron.d + +To properly set the owner of /etc/cron.d, run the command: +$ sudo chown root /etc/cron.d + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + SRG-OS-000480-GPOS-00227 + 5.1.7 + Service configuration files enable or disable features of their respective services that if configured incorrectly +can lead to insecure and vulnerable configurations. Therefore, service configuration files should be owned by the +correct user to prevent unauthorized changes. + CCE-82272-6 + - name: Ensure owner on directory /etc/cron.d/ + file: + path: /etc/cron.d/ + state: directory + owner: '0' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82272-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_owner_cron_d + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +find -H /etc/cron.d/ -maxdepth 1 -type d -exec chown 0 {} \; + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Verify Owner on cron.daily + +To properly set the owner of /etc/cron.daily, run the command: +$ sudo chown root /etc/cron.daily + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + SRG-OS-000480-GPOS-00227 + 5.1.4 + Service configuration files enable or disable features of their respective services that if configured incorrectly +can lead to insecure and vulnerable configurations. Therefore, service configuration files should be owned by the +correct user to prevent unauthorized changes. + CCE-82237-9 + - name: Ensure owner on directory /etc/cron.daily/ + file: + path: /etc/cron.daily/ + state: directory + owner: '0' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82237-9 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_owner_cron_daily + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +find -H /etc/cron.daily/ -maxdepth 1 -type d -exec chown 0 {} \; + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Verify Owner on cron.hourly + +To properly set the owner of /etc/cron.hourly, run the command: +$ sudo chown root /etc/cron.hourly + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + SRG-OS-000480-GPOS-00227 + 5.1.3 + Service configuration files enable or disable features of their respective services that if configured incorrectly +can lead to insecure and vulnerable configurations. Therefore, service configuration files should be owned by the +correct user to prevent unauthorized changes. + CCE-82209-8 + - name: Ensure owner on directory /etc/cron.hourly/ + file: + path: /etc/cron.hourly/ + state: directory + owner: '0' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82209-8 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_owner_cron_hourly + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +find -H /etc/cron.hourly/ -maxdepth 1 -type d -exec chown 0 {} \; + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Verify Owner on cron.monthly + +To properly set the owner of /etc/cron.monthly, run the command: +$ sudo chown root /etc/cron.monthly + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + SRG-OS-000480-GPOS-00227 + 5.1.6 + Service configuration files enable or disable features of their respective services that if configured incorrectly +can lead to insecure and vulnerable configurations. Therefore, service configuration files should be owned by the +correct user to prevent unauthorized changes. + CCE-82260-1 + - name: Ensure owner on directory /etc/cron.monthly/ + file: + path: /etc/cron.monthly/ + state: directory + owner: '0' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82260-1 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_owner_cron_monthly + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +find -H /etc/cron.monthly/ -maxdepth 1 -type d -exec chown 0 {} \; + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Verify Owner on cron.weekly + +To properly set the owner of /etc/cron.weekly, run the command: +$ sudo chown root /etc/cron.weekly + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + SRG-OS-000480-GPOS-00227 + 5.1.5 + Service configuration files enable or disable features of their respective services that if configured incorrectly +can lead to insecure and vulnerable configurations. Therefore, service configuration files should be owned by the +correct user to prevent unauthorized changes. + CCE-82247-8 + - name: Ensure owner on directory /etc/cron.weekly/ + file: + path: /etc/cron.weekly/ + state: directory + owner: '0' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82247-8 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_owner_cron_weekly + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +find -H /etc/cron.weekly/ -maxdepth 1 -type d -exec chown 0 {} \; + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Verify Owner on crontab + +To properly set the owner of /etc/crontab, run the command: +$ sudo chown root /etc/crontab + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + SRG-OS-000480-GPOS-00227 + 5.1.2 + Service configuration files enable or disable features of their respective services that if configured incorrectly +can lead to insecure and vulnerable configurations. Therefore, service configuration files should be owned by the +correct user to prevent unauthorized changes. + CCE-82224-7 + - name: Test for existence /etc/crontab + stat: + path: /etc/crontab + register: file_exists + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82224-7 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_owner_crontab + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure owner 0 on /etc/crontab + file: + path: /etc/crontab + owner: '0' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-82224-7 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_owner_crontab + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +chown 0 /etc/crontab + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Verify Permissions on cron.d + +To properly set the permissions of /etc/cron.d, run the command: +$ sudo chmod 0700 /etc/cron.d + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + SRG-OS-000480-GPOS-00227 + 5.1.7 + Service configuration files enable or disable features of their respective services that if configured incorrectly +can lead to insecure and vulnerable configurations. Therefore, service configuration files should have the +correct access rights to prevent unauthorized changes. + CCE-82277-5 + - name: Set permissions for /etc/cron.d/ + file: + path: /etc/cron.d/ + state: directory + mode: u-s,g-xwrs,o-xwrt + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82277-5 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_permissions_cron_d + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +find -H /etc/cron.d/ -maxdepth 1 -perm /u+s,g+xwrs,o+xwrt -type d -exec chmod u-s,g-xwrs,o-xwrt {} \; + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Verify Permissions on cron.daily + +To properly set the permissions of /etc/cron.daily, run the command: +$ sudo chmod 0700 /etc/cron.daily + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + SRG-OS-000480-GPOS-00227 + 5.1.4 + Service configuration files enable or disable features of their respective services that if configured incorrectly +can lead to insecure and vulnerable configurations. Therefore, service configuration files should have the +correct access rights to prevent unauthorized changes. + CCE-82240-3 + - name: Set permissions for /etc/cron.daily/ + file: + path: /etc/cron.daily/ + state: directory + mode: u-s,g-xwrs,o-xwrt + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82240-3 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_permissions_cron_daily + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +find -H /etc/cron.daily/ -maxdepth 1 -perm /u+s,g+xwrs,o+xwrt -type d -exec chmod u-s,g-xwrs,o-xwrt {} \; + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Verify Permissions on cron.hourly + +To properly set the permissions of /etc/cron.hourly, run the command: +$ sudo chmod 0700 /etc/cron.hourly + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + SRG-OS-000480-GPOS-00227 + 5.1.3 + Service configuration files enable or disable features of their respective services that if configured incorrectly +can lead to insecure and vulnerable configurations. Therefore, service configuration files should have the +correct access rights to prevent unauthorized changes. + CCE-82230-4 + - name: Set permissions for /etc/cron.hourly/ + file: + path: /etc/cron.hourly/ + state: directory + mode: u-s,g-xwrs,o-xwrt + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82230-4 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_permissions_cron_hourly + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +find -H /etc/cron.hourly/ -maxdepth 1 -perm /u+s,g+xwrs,o+xwrt -type d -exec chmod u-s,g-xwrs,o-xwrt {} \; + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Verify Permissions on cron.monthly + +To properly set the permissions of /etc/cron.monthly, run the command: +$ sudo chmod 0700 /etc/cron.monthly + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + SRG-OS-000480-GPOS-00227 + 5.1.6 + Service configuration files enable or disable features of their respective services that if configured incorrectly +can lead to insecure and vulnerable configurations. Therefore, service configuration files should have the +correct access rights to prevent unauthorized changes. + CCE-82263-5 + - name: Set permissions for /etc/cron.monthly/ + file: + path: /etc/cron.monthly/ + state: directory + mode: u-s,g-xwrs,o-xwrt + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82263-5 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_permissions_cron_monthly + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +find -H /etc/cron.monthly/ -maxdepth 1 -perm /u+s,g+xwrs,o+xwrt -type d -exec chmod u-s,g-xwrs,o-xwrt {} \; + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Verify Permissions on cron.weekly + +To properly set the permissions of /etc/cron.weekly, run the command: +$ sudo chmod 0700 /etc/cron.weekly + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + SRG-OS-000480-GPOS-00227 + 5.1.5 + Service configuration files enable or disable features of their respective services that if configured incorrectly +can lead to insecure and vulnerable configurations. Therefore, service configuration files should have the +correct access rights to prevent unauthorized changes. + CCE-82253-6 + - name: Set permissions for /etc/cron.weekly/ + file: + path: /etc/cron.weekly/ + state: directory + mode: u-s,g-xwrs,o-xwrt + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82253-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_permissions_cron_weekly + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +find -H /etc/cron.weekly/ -maxdepth 1 -perm /u+s,g+xwrs,o+xwrt -type d -exec chmod u-s,g-xwrs,o-xwrt {} \; + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Verify Permissions on crontab + +To properly set the permissions of /etc/crontab, run the command: +$ sudo chmod 0600 /etc/crontab + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + SRG-OS-000480-GPOS-00227 + 5.1.2 + Service configuration files enable or disable features of their respective services that if configured incorrectly +can lead to insecure and vulnerable configurations. Therefore, service configuration files should have the +correct access rights to prevent unauthorized changes. + CCE-82206-4 + - name: Test for existence /etc/crontab + stat: + path: /etc/crontab + register: file_exists + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82206-4 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_permissions_crontab + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure permission u-xs,g-xwrs,o-xwrt on /etc/crontab + file: + path: /etc/crontab + mode: u-xs,g-xwrs,o-xwrt + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-82206-4 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_permissions_crontab + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +chmod u-xs,g-xwrs,o-xwrt /etc/crontab + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Restrict at and cron to Authorized Users if Necessary + The /etc/cron.allow and /etc/at.allow files contain lists of +users who are allowed to use cron and at to delay execution of +processes. If these files exist and if the corresponding files +/etc/cron.deny and /etc/at.deny do not exist, then only users +listed in the relevant allow files can run the crontab and at commands +to submit jobs to be run at scheduled intervals. On many systems, only the +system administrator needs the ability to schedule jobs. Note that even if a +given user is not listed in cron.allow, cron jobs can still be run as +that user. The cron.allow file controls only administrative access +to the crontab command for scheduling and modifying cron jobs. + + +To restrict at and cron to only authorized users: +Remove the cron.deny file:$ sudo rm /etc/cron.denyEdit /etc/cron.allow, adding one line for each user allowed to use +the crontab command to create cron jobs.Remove the at.deny file:$ sudo rm /etc/at.denyEdit /etc/at.allow, adding one line for each user allowed to use +the at command to create at jobs. + + Ensure that /etc/at.deny does not exist + The file /etc/at.deny should not exist. +Use /etc/at.allow instead. + 5.1.9 + Access to at should be restricted. +It is easier to manage an allow list than a deny list. + CCE-86945-3 + - name: Remove /etc/at.deny + file: + path: /etc/at.deny + state: absent + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86945-3 + - disable_strategy + - file_at_deny_not_exist + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +#!/bin/bash + + + + if [[ -f /etc/at.deny ]]; then + rm /etc/at.deny + fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure that /etc/cron.deny does not exist + The file /etc/cron.deny should not exist. +Use /etc/cron.allow instead. + 5.1.8 + Access to cron should be restricted. +It is easier to manage an allow list than a deny list. + CCE-86849-7 + - name: Remove /etc/cron.deny + file: + path: /etc/cron.deny + state: absent + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86849-7 + - disable_strategy + - file_cron_deny_not_exist + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +#!/bin/bash + + + + if [[ -f /etc/cron.deny ]]; then + rm /etc/cron.deny + fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Verify Group Who Owns /etc/at.allow file + If /etc/at.allow exists, it must be group-owned by root. + +To properly set the group owner of /etc/at.allow, run the command: +$ sudo chgrp root /etc/at.allow + 5.1.9 + If the owner of the at.allow file is not set to root, the possibility exists for an +unauthorized user to view or edit sensitive information. + CCE-87102-0 + - name: Test for existence /etc/at.allow + stat: + path: /etc/at.allow + register: file_exists + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-87102-0 + - configure_strategy + - file_groupowner_at_allow + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure group owner 0 on /etc/at.allow + file: + path: /etc/at.allow + group: '0' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-87102-0 + - configure_strategy + - file_groupowner_at_allow + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +chgrp 0 /etc/at.allow + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Verify Group Who Owns /etc/cron.allow file + If /etc/cron.allow exists, it must be group-owned by root. + +To properly set the group owner of /etc/cron.allow, run the command: +$ sudo chgrp root /etc/cron.allow + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + CCI-000366 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + SRG-OS-000480-GPOS-00227 + 5.1.8 + If the owner of the cron.allow file is not set to root, the possibility exists for an +unauthorized user to view or edit sensitive information. + CCE-86829-9 + - name: Test for existence /etc/cron.allow + stat: + path: /etc/cron.allow + register: file_exists + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86829-9 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_groupowner_cron_allow + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure group owner 0 on /etc/cron.allow + file: + path: /etc/cron.allow + group: '0' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-86829-9 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_groupowner_cron_allow + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +chgrp 0 /etc/cron.allow + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Verify User Who Owns /etc/cron.allow file + If /etc/cron.allow exists, it must be owned by root. + +To properly set the owner of /etc/cron.allow, run the command: +$ sudo chown root /etc/cron.allow + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + CCI-000366 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + SRG-OS-000480-GPOS-00227 + 5.1.8 + If the owner of the cron.allow file is not set to root, the possibility exists for an +unauthorized user to view or edit sensitive information. + CCE-86843-0 + - name: Test for existence /etc/cron.allow + stat: + path: /etc/cron.allow + register: file_exists + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86843-0 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_owner_cron_allow + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure owner 0 on /etc/cron.allow + file: + path: /etc/cron.allow + owner: '0' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-86843-0 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_owner_cron_allow + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +chown 0 /etc/cron.allow + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Verify Permissions on /etc/at.allow file + If /etc/at.allow exists, it must have permissions 0600 +or more restrictive. + + +To properly set the permissions of /etc/at.allow, run the command: +$ sudo chmod 0600 /etc/at.allow + 5.1.9 + If the permissions of the at.allow file are not set to 0600 or more restrictive, +the possibility exists for an unauthorized user to view or edit sensitive information. + CCE-86903-2 + - name: Test for existence /etc/at.allow + stat: + path: /etc/at.allow + register: file_exists + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86903-2 + - configure_strategy + - file_permissions_at_allow + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure permission u-xs,g-xwrs,o-xwrt on /etc/at.allow + file: + path: /etc/at.allow + mode: u-xs,g-xwrs,o-xwrt + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-86903-2 + - configure_strategy + - file_permissions_at_allow + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +chmod u-xs,g-xwrs,o-xwrt /etc/at.allow + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Verify Permissions on /etc/cron.allow file + If /etc/cron.allow exists, it must have permissions 0600 +or more restrictive. + + +To properly set the permissions of /etc/cron.allow, run the command: +$ sudo chmod 0600 /etc/cron.allow + SRG-OS-000480-GPOS-00227 + 5.1.8 + If the permissions of the cron.allow file are not set to 0600 or more restrictive, +the possibility exists for an unauthorized user to view or edit sensitive information. + CCE-86876-0 + - name: Test for existence /etc/cron.allow + stat: + path: /etc/cron.allow + register: file_exists + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86876-0 + - configure_strategy + - file_permissions_cron_allow + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure permission u-xs,g-xwrs,o-xwrt on /etc/cron.allow + file: + path: /etc/cron.allow + mode: u-xs,g-xwrs,o-xwrt + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-86876-0 + - configure_strategy + - file_permissions_cron_allow + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +chmod u-xs,g-xwrs,o-xwrt /etc/cron.allow + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + + Deprecated services + Some deprecated software services impact the overall system security due to their behavior (leak of +confidentiality in network exchange, usage as uncontrolled communication channel, risk associated with the service due to its old age, etc. + + Uninstall the inet-based telnet server + The inet-based telnet daemon should be uninstalled. + NT007(R03) + 11 + 12 + 14 + 15 + 3 + 8 + 9 + APO13.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.04 + DSS05.02 + DSS05.03 + DSS05.05 + DSS06.06 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.6.2.1 + A.6.2.2 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.AC-3 + PR.IP-1 + PR.PT-3 + PR.PT-4 + telnet allows clear text communications, and does not protect any +data transmission between client and server. Any confidential data can be +listened and no integrity checking is made. + +package --remove=inetutils-telnetd + + include remove_inetutils-telnetd + +class remove_inetutils-telnetd { + package { 'inetutils-telnetd': + ensure => 'purged', + } +} + + - name: Ensure inetutils-telnetd is removed + package: + name: inetutils-telnetd + state: absent + tags: + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - high_severity + - low_complexity + - low_disruption + - no_reboot_needed + - package_inetutils-telnetd_removed + + +# CAUTION: This remediation script will remove inetutils-telnetd +# from the system, and may remove any packages +# that depend on inetutils-telnetd. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "inetutils-telnetd" ; then + + yum remove -y "inetutils-telnetd" + +fi + + + + + + + Uninstall the nis package + The support for Yellowpages should not be installed unless it is required. + NIS is the historical SUN service for central account management, more and more replaced by LDAP. +NIS does not support efficiently security constraints, ACL, etc. and should not be used. + +package --remove=nis + + include remove_nis + +class remove_nis { + package { 'nis': + ensure => 'purged', + } +} + + - name: Ensure nis is removed + package: + name: nis + state: absent + tags: + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - package_nis_removed + + +# CAUTION: This remediation script will remove nis +# from the system, and may remove any packages +# that depend on nis. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "nis" ; then + + yum remove -y "nis" + +fi + + + + + + + Uninstall the ntpdate package + ntpdate is a historical ntp synchronization client for unixes. It sould be uninstalled. + ntpdate is an old not security-compliant ntp client. It should be replaced by modern ntp clients such as ntpd, able to use cryptographic mechanisms integrated in NTP. + +package --remove=ntpdate + + include remove_ntpdate + +class remove_ntpdate { + package { 'ntpdate': + ensure => 'purged', + } +} + + - name: Ensure ntpdate is removed + package: + name: ntpdate + state: absent + tags: + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - package_ntpdate_removed + + +# CAUTION: This remediation script will remove ntpdate +# from the system, and may remove any packages +# that depend on ntpdate. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "ntpdate" ; then + + yum remove -y "ntpdate" + +fi + + + + + + + Uninstall the ssl compliant telnet server + The telnet daemon, even with ssl support, should be uninstalled. + NT007(R02) + 11 + 12 + 14 + 15 + 3 + 8 + 9 + APO13.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.04 + DSS05.02 + DSS05.03 + DSS05.05 + DSS06.06 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.6.2.1 + A.6.2.2 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.AC-3 + PR.IP-1 + PR.PT-3 + PR.PT-4 + telnet, even with ssl support, should not be installed. +When remote shell is required, up-to-date ssh daemon can be used. + +package --remove=telnetd-ssl + + include remove_telnetd-ssl + +class remove_telnetd-ssl { + package { 'telnetd-ssl': + ensure => 'purged', + } +} + + - name: Ensure telnetd-ssl is removed + package: + name: telnetd-ssl + state: absent + tags: + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - high_severity + - low_complexity + - low_disruption + - no_reboot_needed + - package_telnetd-ssl_removed + + +# CAUTION: This remediation script will remove telnetd-ssl +# from the system, and may remove any packages +# that depend on telnetd-ssl. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "telnetd-ssl" ; then + + yum remove -y "telnetd-ssl" + +fi + + + + + + + Uninstall the telnet server + The telnet daemon should be uninstalled. + BP28(R1) + NT007(R03) + 11 + 12 + 14 + 15 + 3 + 8 + 9 + APO13.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.04 + DSS05.02 + DSS05.03 + DSS05.05 + DSS06.06 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.6.2.1 + A.6.2.2 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.AC-3 + PR.IP-1 + PR.PT-3 + PR.PT-4 + telnet allows clear text communications, and does not protect +any data transmission between client and server. Any confidential data +can be listened and no integrity checking is made.' + CCE-83302-0 + +package --remove=telnetd + + include remove_telnetd + +class remove_telnetd { + package { 'telnetd': + ensure => 'purged', + } +} + + - name: Ensure telnetd is removed + package: + name: telnetd + state: absent + tags: + - CCE-83302-0 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - high_severity + - low_complexity + - low_disruption + - no_reboot_needed + - package_telnetd_removed + + +# CAUTION: This remediation script will remove telnetd +# from the system, and may remove any packages +# that depend on telnetd. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "telnetd" ; then + + yum remove -y "telnetd" + +fi + + + + + + + + DHCP + The Dynamic Host Configuration Protocol (DHCP) allows +systems to request and obtain an IP address and other configuration +parameters from a server. + +This guide recommends configuring networking on clients by manually editing +the appropriate files under /etc/sysconfig. Use of DHCP can make client +systems vulnerable to compromise by rogue DHCP servers, and should be avoided +unless necessary. If using DHCP is necessary, however, there are best practices +that should be followed to minimize security risk. + + Configure DHCP Client if Necessary + If DHCP must be used, then certain configuration changes can +minimize the amount of information it receives and applies from the network, +and thus the amount of incorrect information a rogue DHCP server could +successfully distribute. For more information on configuring dhclient, see the +dhclient(8) and dhclient.conf(5) man pages. + + Minimize the DHCP-Configured Options + Create the file /etc/dhcp/dhclient.conf, and add an +appropriate setting for each of the ten configuration settings which can be +obtained via DHCP. For each setting, do one of the following: + +If the setting should not be configured remotely by the DHCP server, +select an appropriate static value, and add the line: +supersede setting value; +If the setting should be configured remotely by the DHCP server, add the lines: +request setting; +require setting; +For example, suppose the DHCP server should provide only the IP address itself +and the subnet mask. Then the entire file should look like: +supersede domain-name "example.com"; +supersede domain-name-servers 192.168.1.2; +supersede nis-domain ""; +supersede nis-servers ""; +supersede ntp-servers "ntp.example.com "; +supersede routers 192.168.1.1; +supersede time-offset -18000; +request subnet-mask; +require subnet-mask; + In this example, the options nis-servers and +nis-domain are set to empty strings, on the assumption that the deprecated NIS +protocol is not in use. It is necessary to supersede settings for unused +services so that they cannot be set by a hostile DHCP server. If an option is +set to an empty string, dhclient will typically not attempt to configure the +service. + By default, the DHCP client program, dhclient, requests and applies +ten configuration options (in addition to the IP address) from the DHCP server. +subnet-mask, broadcast-address, time-offset, routers, domain-name, +domain-name-servers, host-name, nis-domain, nis-servers, and ntp-servers. Many +of the options requested and applied by dhclient may be the same for every +system on a network. It is recommended that almost all configuration options be +assigned statically, and only options which must vary on a host-by-host basis +be assigned via DHCP. This limits the damage which can be done by a rogue DHCP +server. If appropriate for your site, it is also possible to supersede the +host-name directive in /etc/dhcp/dhclient.conf, establishing a static +hostname for the system. However, dhclient does not use the host name option +provided by the DHCP server (instead using the value provided by a reverse DNS +lookup). + + + + Configure DHCP Server + If the system must act as a DHCP server, the configuration +information it serves should be minimized. Also, support for other protocols +and DNS-updating schemes should be explicitly disabled unless needed. The +configuration file for dhcpd is called /etc/dhcp/dhcpd.conf. The file +begins with a number of global configuration options. The remainder of the file +is divided into sections, one for each block of addresses offered by dhcpd, +each of which contains configuration options specific to that address +block. + + Configure Logging + Ensure that the following line exists in +/etc/rsyslog.conf: +daemon.* /var/log/daemon.log +Configure logwatch or other log monitoring tools to summarize error conditions +reported by the dhcpd process. + 1 + 12 + 13 + 14 + 15 + 16 + 2 + 3 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + BAI03.05 + DSS01.03 + DSS03.05 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 6.1 + SR 6.2 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.14.2.7 + A.15.2.1 + A.15.2.2 + AU-12(a) + AU-12(c) + CM-6(a) + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.PT-1 + By default, dhcpd logs notices to the daemon facility. Sending all +daemon messages to a dedicated log file is part of the syslog configuration +outlined in the Logging and Auditing section + + + Deny BOOTP Queries + Unless your network needs to support older BOOTP clients, disable +support for the bootp protocol by adding or correcting the global option: +deny bootp; + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + PR.PT-3 + The bootp option tells dhcpd to respond to BOOTP queries. If support +for this simpler protocol is not needed, it should be disabled to remove attack +vectors against the DHCP server. + + + Deny Decline Messages + Edit /etc/dhcp/dhcpd.conf and add or correct the following +global option to prevent the DHCP server from responding the DHCPDECLINE +messages, if possible: deny declines; + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + PR.PT-3 + The DHCPDECLINE message can be sent by a DHCP client to indicate +that it does not consider the lease offered by the server to be valid. By +issuing many DHCPDECLINE messages, a malicious client can exhaust the DHCP +server's pool of IP addresses, causing the DHCP server to forget old address +allocations. + + + Do Not Use Dynamic DNS + To prevent the DHCP server from receiving DNS information from +clients, edit /etc/dhcp/dhcpd.conf, and add or correct the following global +option: ddns-update-style none; + The ddns-update-style option controls only whether +the DHCP server will attempt to act as a Dynamic DNS client. As long as the DNS +server itself is correctly configured to reject DDNS attempts, an incorrect +ddns-update-style setting on the client is harmless (but should be fixed as a +best practice). + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + PR.PT-3 + The Dynamic DNS protocol is used to remotely update the data served +by a DNS server. DHCP servers can use Dynamic DNS to publish information about +their clients. This setup carries security risks, and its use is not +recommended. If Dynamic DNS must be used despite the risks it poses, it is +critical that Dynamic DNS transactions be protected using TSIG or some other +cryptographic authentication mechanism. See dhcpd.conf(5) for more information +about protecting the DHCP server from passing along malicious DNS data from its +clients. + + + Minimize Served Information + Edit /etc/dhcp/dhcpd.conf. Examine each address range section within +the file, and ensure that the following options are not defined unless there is +an operational need to provide this information via DHCP: +option domain-name +option domain-name-servers +option nis-domain +option nis-servers +option ntp-servers +option routers +option time-offset + By default, the Red Hat Enterprise Linux client installation uses DHCP +to request much of the above information from the DHCP server. In particular, +domain-name, domain-name-servers, and routers are configured via DHCP. These +settings are typically necessary for proper network functionality, but are also +usually static across systems at a given site. + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + PR.PT-3 + Because the configuration information provided by the DHCP server +could be maliciously provided to clients by a rogue DHCP server, the amount of +information provided via DHCP should be minimized. Remove these definitions +from the DHCP server configuration to ensure that legitimate clients do not +unnecessarily rely on DHCP for this information. + + + + Disable DHCP Client + DHCP is the default network configuration method provided by the system +installer, and common on many networks. Nevertheless, manual management +of IP addresses for systems implies a greater degree of management and +accountability for network activity. + + Disable DHCP Client in ifcfg + For each interface on the system (e.g. eth0), edit +/etc/sysconfig/network-scripts/ifcfg-interface and make the +following changes: + Correct the BOOTPROTO line to read: +BOOTPROTO=none + Add or correct the following lines, substituting the appropriate +values based on your site's addressing scheme: +NETMASK=255.255.255.0 +IPADDR=192.168.1.2 +GATEWAY=192.168.1.1 + + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + CCI-000366 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-6(a) + PR.IP-1 + PR.PT-3 + DHCP relies on trusting the local network. If the local network is not trusted, +then it should not be used. However, the automatic configuration provided by +DHCP is commonly used and the alternative, manual configuration, presents an +unacceptable burden in many circumstances. + +for config_file in /etc/sysconfig/network-scripts/ifcfg-*; do + if grep -q ^BOOTPROTO= $config_file; then + sed -i 's/^BOOTPROTO=.*/BOOTPROTO=none/' $config_file + else + echo BOOTPROTO=none >>$config_file + fi +done + + + + + + + + + + + Disable DHCP Server + The DHCP server dhcpd is not installed or activated by +default. If the software was installed and activated, but the +system does not need to act as a DHCP server, it should be disabled +and removed. + + Uninstall DHCP Server Package + If the system does not need to act as a DHCP server, +the dhcp package can be uninstalled. + +The dhcp-server package can be removed with the following command: + +$ sudo yum erase dhcp-server + BP28(R1) + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + CCI-000366 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + PR.PT-3 + Removing the DHCP server ensures that it cannot be easily or +accidentally reactivated and disrupt network operation. + CCE-83385-5 + +package --remove=dhcp-server + + include remove_dhcp-server + +class remove_dhcp-server { + package { 'dhcp-server': + ensure => 'purged', + } +} + + - name: Ensure dhcp-server is removed + package: + name: dhcp-server + state: absent + tags: + - CCE-83385-5 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_dhcp_removed + + +# CAUTION: This remediation script will remove dhcp-server +# from the system, and may remove any packages +# that depend on dhcp-server. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "dhcp-server" ; then + + yum remove -y "dhcp-server" + +fi + + + + + + + + + + Disable DHCP Service + The dhcpd service should be disabled on +any system that does not need to act as a DHCP server. + +The dhcpd service can be disabled with the following command: +$ sudo systemctl mask --now dhcpd.service + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + CCI-000366 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + PR.PT-3 + 2.2.5 + Unmanaged or unintentionally activated DHCP servers may provide faulty information +to clients, interfering with the operation of a legitimate site +DHCP server if there is one. + + CCE-82864-0 + include disable_dhcpd + +class disable_dhcpd { + service {'dhcpd': + enable => false, + ensure => 'stopped', + } +} + + - name: Disable service dhcpd + block: + + - name: Disable service dhcpd + systemd: + name: dhcpd.service + enabled: 'no' + state: stopped + masked: 'yes' + ignore_errors: 'yes' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82864-0 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_dhcpd_disabled + +- name: Unit Socket Exists - dhcpd.socket + command: systemctl list-unit-files dhcpd.socket + args: + warn: false + register: socket_file_exists + changed_when: false + ignore_errors: true + check_mode: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82864-0 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_dhcpd_disabled + +- name: Disable socket dhcpd + systemd: + name: dhcpd.socket + enabled: 'no' + state: stopped + masked: 'yes' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"dhcpd.socket" in socket_file_exists.stdout_lines[1]' + tags: + - CCE-82864-0 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_dhcpd_disabled + + +[customizations.services] +disabled = ["dhcpd"] + + apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + systemd: + units: + - name: dhcpd.service + enabled: false + mask: true + - name: dhcpd.socket + enabled: false + mask: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" stop 'dhcpd.service' +"$SYSTEMCTL_EXEC" disable 'dhcpd.service' +"$SYSTEMCTL_EXEC" mask 'dhcpd.service' +# Disable socket activation if we have a unit file for it +if "$SYSTEMCTL_EXEC" list-unit-files | grep -q '^dhcpd.socket'; then + "$SYSTEMCTL_EXEC" stop 'dhcpd.socket' + "$SYSTEMCTL_EXEC" mask 'dhcpd.socket' +fi +# The service may not be running because it has been started and failed, +# so let's reset the state so OVAL checks pass. +# Service should be 'inactive', not 'failed' after reboot though. +"$SYSTEMCTL_EXEC" reset-failed 'dhcpd.service' || true + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + + DNS Server + Most organizations have an operational need to run at +least one nameserver. However, there are many common attacks +involving DNS server software, and this server software should +be disabled on any system +on which it is not needed. + + Disable DNS Server + DNS software should be disabled on any systems which does not +need to be a nameserver. Note that the BIND DNS server software is +not installed on Red Hat Enterprise Linux 8 by default. The remainder of this section +discusses secure configuration of systems which must be +nameservers. + + Uninstall bind Package + The named service is provided by the bind package. +The bind package can be removed with the following command: + +$ sudo yum erase bind + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + CCI-000366 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + PR.PT-3 + If there is no need to make DNS server software available, +removing it provides a safeguard against its activation. + CCE-82408-6 + +package --remove=bind + + include remove_bind + +class remove_bind { + package { 'bind': + ensure => 'purged', + } +} + + - name: Ensure bind is removed + package: + name: bind + state: absent + tags: + - CCE-82408-6 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - package_bind_removed + + +# CAUTION: This remediation script will remove bind +# from the system, and may remove any packages +# that depend on bind. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "bind" ; then + + yum remove -y "bind" + +fi + + + + + + + + + + Disable named Service + +The named service can be disabled with the following command: +$ sudo systemctl mask --now named.service + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + CCI-000366 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + PR.PT-3 + 2.2.6 + All network services involve some risk of compromise due to +implementation flaws and should be disabled if possible. + + CCE-82409-4 + include disable_named + +class disable_named { + service {'named': + enable => false, + ensure => 'stopped', + } +} + + - name: Disable service named + block: + + - name: Disable service named + systemd: + name: named.service + enabled: 'no' + state: stopped + masked: 'yes' + ignore_errors: 'yes' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82409-4 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_named_disabled + +- name: Unit Socket Exists - named.socket + command: systemctl list-unit-files named.socket + args: + warn: false + register: socket_file_exists + changed_when: false + ignore_errors: true + check_mode: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82409-4 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_named_disabled + +- name: Disable socket named + systemd: + name: named.socket + enabled: 'no' + state: stopped + masked: 'yes' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"named.socket" in socket_file_exists.stdout_lines[1]' + tags: + - CCE-82409-4 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_named_disabled + + +[customizations.services] +disabled = ["named"] + + apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + systemd: + units: + - name: named.service + enabled: false + mask: true + - name: named.socket + enabled: false + mask: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" stop 'named.service' +"$SYSTEMCTL_EXEC" disable 'named.service' +"$SYSTEMCTL_EXEC" mask 'named.service' +# Disable socket activation if we have a unit file for it +if "$SYSTEMCTL_EXEC" list-unit-files | grep -q '^named.socket'; then + "$SYSTEMCTL_EXEC" stop 'named.socket' + "$SYSTEMCTL_EXEC" mask 'named.socket' +fi +# The service may not be running because it has been started and failed, +# so let's reset the state so OVAL checks pass. +# Service should be 'inactive', not 'failed' after reboot though. +"$SYSTEMCTL_EXEC" reset-failed 'named.service' || true + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Isolate DNS from Other Services + This section discusses mechanisms for preventing the DNS server +from interfering with other services. This is done both to protect the +remainder of the network should a nameserver be compromised, and to make direct +attacks on nameservers more difficult. + + Run DNS Software in a chroot Jail + Install the bind-chroot package: +$ sudo yum install bind-chroot +Place a valid named.conf file inside the chroot jail: +$ sudo cp /etc/named.conf /var/named/chroot/etc/named.conf +$ sudo chown root:root /var/named/chroot/etc/named.conf +$ sudo chmod 644 /var/named/chroot/etc/named.conf +Create and populate an appropriate zone directory within the jail, based on the +options directive. If your named.conf includes: +options { +directory "/path/to/DIRNAME "; +... +} +then copy that directory and its contents from the original zone directory: +$ sudo cp -r /path/to/DIRNAME /var/named/chroot/DIRNAME +Add or correct the following line within /etc/sysconfig/named: +ROOTDIR=/var/named/chroot + If you are running BIND in a chroot jail, then you +should use the jailed named.conf as the primary nameserver +configuration file. That is, when this guide recommends editing +/etc/named.conf, you should instead edit +/var/named/chroot/etc/named.conf. + + + Run DNS Software on Dedicated Servers + Since DNS is +a high-risk service which must frequently be made available to the entire +Internet, it is strongly recommended that no other services be offered by +systems which act as organizational DNS servers. + + + + Protect DNS Data from Tampering or Attack + This section discusses DNS configuration options which make it +more difficult for attackers to gain access to private DNS data or to modify +DNS data. + + Authenticate Zone Transfers + If it is necessary for a secondary nameserver to receive zone data +via zone transfer from the primary server, follow the instructions here. Use +dnssec-keygen to create a symmetric key file in the current directory: +$ cd /tmp +$ sudo dnssec-keygen -a HMAC-MD5 -b 128 -n HOST dns.example.com +Kdns.example.com .+aaa +iiiii +This output is the name of a file containing the new key. Read the file to find +the base64-encoded key string: +$ sudo cat Kdns.example.com .+NNN +MMMMM .key +dns.example.com IN KEY 512 3 157 base64-key-string +Add the directives to /etc/named.conf on the primary server: +key zone-transfer-key { + algorithm hmac-md5; + secret "base64-key-string "; +}; +zone "example.com " IN { + type master; + allow-transfer { key zone-transfer-key; }; + ... +}; +Add the directives below to /etc/named.conf on the secondary nameserver: +key zone-transfer-key { + algorithm hmac-md5; + secret "base64-key-string "; +}; + +server IP-OF-MASTER { + keys { zone-transfer-key; }; +}; + +zone "example.com " IN { + type slave; + masters { IP-OF-MASTER ; }; + ... +}; + The purpose of the dnssec-keygen command is to +create the shared secret string base64-key-string. Once this secret has been +obtained and inserted into named.conf on the primary and secondary servers, the +key files Kdns.example.com .+NNN +MMMMM .key and Kdns.example.com .+NNN +MMMMM +.private are no longer needed, and may safely be deleted. + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + PR.PT-3 + The BIND transaction signature (TSIG) functionality allows primary +and secondary nameservers to use a shared secret to verify authorization to +perform zone transfers. This method is more secure than using IP-based limiting +to restrict nameserver access, since IP addresses can be easily spoofed. +However, if you cannot configure TSIG between your servers because, for +instance, the secondary nameserver is not under your control and its +administrators are unwilling to configure TSIG, you can configure an +allow-transfer directive with numerical IP addresses or ACLs as a last resort. + CCE-82410-2 + + + Disable Dynamic Updates + Is there a mission-critical reason to enable the risky dynamic +update functionality? If not, edit /etc/named.conf. For each zone +specification, correct the following directive if necessary: +zone "example.com " IN { + allow-update { none; }; + ... +}; + Dynamic updates allow remote servers to add, delete, or modify any +entries in your zone file. Therefore, they should be considered highly risky, +and disabled unless there is a very good reason for their use. If dynamic +updates must be allowed, IP-based ACLs are insufficient protection, since they +are easily spoofed. Instead, use TSIG keys (see the previous section for an +example), and consider using the update-policy directive to restrict changes to +only the precise type of change needed. + + + Disable Zone Transfers from the Nameserver + Is it necessary for a secondary nameserver to receive zone data +via zone transfer from the primary server? If not, follow the instructions in +this section. If so, see the next section for instructions on protecting zone +transfers. +Add or correct the following directive within /etc/named.conf: +options { + allow-transfer { none; }; + ... +} + If both the primary and secondary nameserver are under your control, +or if you have only one nameserver, it may be possible to use an external +configuration management mechanism to distribute zone updates. In that case, it +is not necessary to allow zone transfers within BIND itself, so they should be +disabled to avoid the potential for abuse. + + + Use Views to Partition External and Internal Information + If it is not possible to run external and internal nameservers on +separate physical systems, run BIND9 and simulate this feature using views. +Edit /etc/named.conf. Add or correct the following directives (where +SUBNET is the numerical IP representation of your organization in the form +xxx.xxx.xxx.xxx/xx): +acl internal { + SUBNET ; + localhost; +}; +view "internal-view" { + match-clients { internal; }; + zone "." IN { + type hint; + file "db.cache"; + }; + zone "internal.example.com " IN { + ... + }; +}; + +view "external-view" { + match-clients { any; }; + recursion no; + zone "example.com " IN { + ... + }; +}; + As shown in the example, database files which are +required for recursion, such as the root hints file, must be available to any +clients which are allowed to make recursive queries. Under typical +circumstances, this includes only the internal clients which are allowed to use +this server as a general-purpose nameserver. + + + Run Separate DNS Servers for External and Internal Queries + Is it possible to run external and internal nameservers on +separate systems? If so, follow the configuration guidance in this section. On +the external nameserver, edit /etc/named.conf to add or correct the +following directives: +options { + allow-query { any; }; + recursion no; + ... +}; +zone "example.com " IN { + ... +}; +On the internal nameserver, edit /etc/named.conf. Add or correct the +following directives, where SUBNET is the numerical IP representation of your +organization in the form xxx.xxx.xxx.xxx/xx: +acl internal { + SUBNET ; + localhost; +}; +options { + allow-query { internal; }; + ... +}; +zone "internal.example.com " IN { + ... +}; + + + + + Docker Service + The docker service is necessary to create containers, which are + self-sufficient and self-contained applications using the resource + isolation features of the kernel. + + + Application Whitelisting Daemon + Fapolicyd (File Access Policy Daemon) implements application whitelisting +to decide file access rights. Applications that are known via a reputation +source are allowed access while unknown applications are not. The daemon +makes use of the kernel's fanotify interface to determine file access rights. + + + Install fapolicyd Package + The fapolicyd package can be installed with the following command: + +$ sudo yum install fapolicyd + CCI-001764 + CCI-001774 + CM-6(a) + SI-4(22) + SRG-OS-000370-GPOS-00155 + SRG-OS-000368-GPOS-00154 + SRG-OS-000480-GPOS-00230 + RHEL-08-040135 + SV-230523r744023_rule + fapolicyd (File Access Policy Daemon) +implements application whitelisting to decide file access rights. + CCE-82191-8 + +package --add=fapolicyd + + include install_fapolicyd + +class install_fapolicyd { + package { 'fapolicyd': + ensure => 'installed', + } +} + + - name: Ensure fapolicyd is installed + package: + name: fapolicyd + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82191-8 + - DISA-STIG-RHEL-08-040135 + - NIST-800-53-CM-6(a) + - NIST-800-53-SI-4(22) + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_fapolicyd_installed + + +[[packages]] +name = "fapolicyd" +version = "*" + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if ! rpm -q --quiet "fapolicyd" ; then + yum install -y "fapolicyd" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Enable the File Access Policy Service + The File Access Policy service should be enabled. + +The fapolicyd service can be enabled with the following command: +$ sudo systemctl enable fapolicyd.service + CCI-001764 + CCI-001774 + CM-6(a) + SI-4(22) + FMT_SMF_EXT.1 + SRG-OS-000370-GPOS-00155 + SRG-OS-000368-GPOS-00154 + SRG-OS-000480-GPOS-00230 + RHEL-08-040136 + SV-244545r743884_rule + The fapolicyd service (File Access Policy Daemon) +implements application whitelisting to decide file access rights. + CCE-82249-4 + include enable_fapolicyd + +class enable_fapolicyd { + service {'fapolicyd': + enable => true, + ensure => 'running', + } +} + + - name: Enable service fapolicyd + block: + + - name: Gather the package facts + package_facts: + manager: auto + + - name: Enable service fapolicyd + service: + name: fapolicyd + enabled: 'yes' + state: started + masked: 'no' + when: + - '"fapolicyd" in ansible_facts.packages' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82249-4 + - DISA-STIG-RHEL-08-040136 + - NIST-800-53-CM-6(a) + - NIST-800-53-SI-4(22) + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_fapolicyd_enabled + + +[customizations.services] +enabled = ["fapolicyd"] + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" unmask 'fapolicyd.service' +"$SYSTEMCTL_EXEC" start 'fapolicyd.service' +"$SYSTEMCTL_EXEC" enable 'fapolicyd.service' + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure Fapolicy Module to Employ a Deny-all, Permit-by-exception Policy to Allow the Execution of Authorized Software Programs. + The Fapolicy module must be configured to employ a deny-all, permit-by-exception policy to allow the execution of authorized software programs and to prevent unauthorized software from running. + CCI-001764 + CM-7 (2) + CM-7 (5) (b) + CM-6 b + SRG-OS-000368-GPOS-00154 + SRG-OS-000370-GPOS-00155 + SRG-OS-000480-GPOS-00232 + RHEL-08-040137 + SV-244546r809339_rule + Utilizing a whitelist provides a configuration management method for allowing the execution of only authorized software. +Using only authorized software decreases risk by limiting the number of potential vulnerabilities. Verification of whitelisted software occurs prior to execution or at system startup. + +Proceed with caution with enforcing the use of this daemon. +Improper configuration may render the system non-functional. +The "fapolicyd" API is not namespace aware and can cause issues when launching or running containers. + CCE-86478-5 + + + + + + + + + fapolicyd Must be Configured to Limit Access to Users Home Folders + fapolicyd needs be configured so that users cannot give access to their home folders to other users. + CCI-000366 + CM-6 b + SRG-OS-000480-GPOS-00230 + Users' home directories/folders may contain information of a sensitive nature. +Non-privileged users should coordinate any sharing of information with a System Administrator (SA) through shared resources. +fapolicyd can confine users to their home directory, not allowing them to make any changes outside of their own home directories. +Confining users to their home directory will minimize the risk of sharing information. + + + + + + + FTP Server + FTP is a common method for allowing remote access to +files. Like telnet, the FTP protocol is unencrypted, which means +that passwords and other data transmitted during the session can be +captured and that the session is vulnerable to hijacking. +Therefore, running the FTP server software is not recommended. + +However, there are some FTP server configurations which may +be appropriate for some environments, particularly those which +allow only read-only anonymous access as a means of downloading +data available to the public. + + Disable vsftpd if Possible + To minimize attack surface, disable vsftpd if at all +possible. + + Uninstall vsftpd Package + The vsftpd package can be removed with the following command: $ sudo yum erase vsftpd + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + CCI-000197 + CCI-000366 + CCI-000381 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + IA-5(1)(c) + IA-5(1).1(v) + CM-7 + CM-7.1(ii) + PR.IP-1 + PR.PT-3 + SRG-OS-000074-GPOS-00042 + SRG-OS-000095-GPOS-00049 + SRG-OS-000480-GPOS-00227 + RHEL-08-040360 + 2.2.8 + SV-230558r627750_rule + Removing the vsftpd package decreases the risk of its +accidental activation. + CCE-82414-4 + +package --remove=vsftpd + + include remove_vsftpd + +class remove_vsftpd { + package { 'vsftpd': + ensure => 'purged', + } +} + + - name: Ensure vsftpd is removed + package: + name: vsftpd + state: absent + tags: + - CCE-82414-4 + - DISA-STIG-RHEL-08-040360 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7 + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-CM-7.1(ii) + - NIST-800-53-IA-5(1)(c) + - NIST-800-53-IA-5(1).1(v) + - disable_strategy + - high_severity + - low_complexity + - low_disruption + - no_reboot_needed + - package_vsftpd_removed + + +# CAUTION: This remediation script will remove vsftpd +# from the system, and may remove any packages +# that depend on vsftpd. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "vsftpd" ; then + + yum remove -y "vsftpd" + +fi + + + + + + + + + + Disable vsftpd Service + +The vsftpd service can be disabled with the following command: +$ sudo systemctl mask --now vsftpd.service + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + CCI-001436 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + PR.PT-3 + 2.2.7 + Running FTP server software provides a network-based avenue +of attack, and should be disabled if not needed. +Furthermore, the FTP protocol is unencrypted and creates +a risk of compromising sensitive information. + + CCE-82413-6 + include disable_vsftpd + +class disable_vsftpd { + service {'vsftpd': + enable => false, + ensure => 'stopped', + } +} + + - name: Disable service vsftpd + block: + + - name: Disable service vsftpd + systemd: + name: vsftpd.service + enabled: 'no' + state: stopped + masked: 'yes' + ignore_errors: 'yes' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82413-6 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_vsftpd_disabled + +- name: Unit Socket Exists - vsftpd.socket + command: systemctl list-unit-files vsftpd.socket + args: + warn: false + register: socket_file_exists + changed_when: false + ignore_errors: true + check_mode: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82413-6 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_vsftpd_disabled + +- name: Disable socket vsftpd + systemd: + name: vsftpd.socket + enabled: 'no' + state: stopped + masked: 'yes' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"vsftpd.socket" in socket_file_exists.stdout_lines[1]' + tags: + - CCE-82413-6 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_vsftpd_disabled + + +[customizations.services] +disabled = ["vsftpd"] + + apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + systemd: + units: + - name: vsftpd.service + enabled: false + mask: true + - name: vsftpd.socket + enabled: false + mask: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" stop 'vsftpd.service' +"$SYSTEMCTL_EXEC" disable 'vsftpd.service' +"$SYSTEMCTL_EXEC" mask 'vsftpd.service' +# Disable socket activation if we have a unit file for it +if "$SYSTEMCTL_EXEC" list-unit-files | grep -q '^vsftpd.socket'; then + "$SYSTEMCTL_EXEC" stop 'vsftpd.socket' + "$SYSTEMCTL_EXEC" mask 'vsftpd.socket' +fi +# The service may not be running because it has been started and failed, +# so let's reset the state so OVAL checks pass. +# Service should be 'inactive', not 'failed' after reboot though. +"$SYSTEMCTL_EXEC" reset-failed 'vsftpd.service' || true + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Configure vsftpd to Provide FTP Service if Necessary + The primary vsftpd configuration file is +/etc/vsftpd.conf, if that file exists, or +/etc/vsftpd/vsftpd.conf if it does not. + + Configure Firewalls to Protect the FTP Server + +By default, iptables +blocks access to the ports used by the web server. + +To configure iptables to allow port 21 traffic, one must edit +/etc/sysconfig/iptables and +/etc/sysconfig/ip6tables (if IPv6 is in use). +Add the following line, ensuring that it appears before the final LOG and DROP lines for the INPUT chain: +-A INPUT -m state --state NEW -p tcp --dport 21 -j ACCEPT +Edit the file /etc/sysconfig/iptables-config. Ensure that the space-separated list of modules contains +the FTP connection tracking module: +IPTABLES_MODULES="ip_conntrack_ftp" + These settings configure the firewall to allow connections to an FTP server. + + +The first line allows initial connections to the FTP server port. +FTP is an older protocol which is not very compatible with firewalls. During the initial FTP dialogue, the client +and server negotiate an arbitrary port to be used for data transfer. The ip_conntrack_ftp module is used by +iptables to listen to that dialogue and allow connections to the data ports which FTP negotiates. This allows an +FTP server to operate on a system which is running a firewall. + + + Disable FTP Uploads if Possible + Is there a mission-critical reason for users to upload files via FTP? If not, +edit the vsftpd configuration file to add or correct the following configuration options: +write_enable=NO +If FTP uploads are necessary, follow the guidance in the remainder of this section to secure these transactions +as much as possible. + Anonymous FTP can be a convenient way to make files available for universal download. However, it is less +common to have a need to allow unauthenticated users to place files on the FTP server. If this must be done, it +is necessary to ensure that files cannot be uploaded and downloaded from the same directory. + + + Place the FTP Home Directory on its Own Partition + By default, the anonymous FTP root is the home directory of the FTP user account. The df command can +be used to verify that this directory is on its own partition. + If there is a mission-critical reason for anonymous users to upload files, precautions must be taken to prevent +these users from filling a disk used by other services. + + + Enable Logging of All FTP Transactions + Add or correct the following configuration options within the vsftpd +configuration file, located at /etc/vsftpd/vsftpd.conf: +xferlog_enable=YES +xferlog_std_format=NO +log_ftp_protocol=YES + If verbose logging to vsftpd.log is done, sparse logging of +downloads to /var/log/xferlog will not also occur. However, +the information about what files were downloaded is included in the +information logged to vsftpd.log. + To trace malicious activity facilitated by the FTP service, it must be configured to ensure that all commands sent to +the FTP server are logged using the verbose vsftpd log +format. The default vsftpd log file is /var/log/vsftpd.log. + + + + + + + + + Create Warning Banners for All FTP Users + +Edit the vsftpd configuration file, which resides at /etc/vsftpd/vsftpd.conf + +by default. Add or correct the following configuration options: +banner_file=/etc/issue + CCI-000048 + This setting will cause the system greeting banner to be used for FTP connections as well. + + + + + + + + + Restrict the Set of Users Allowed to Access FTP + This section describes how to disable non-anonymous (password-based) FTP logins, or, if it is not possible to +do this entirely due to legacy applications, how to restrict insecure FTP login to only those users who have an +identified need for this access. + + Limit Users Allowed FTP Access if Necessary + If there is a mission-critical reason for users to access their accounts via the insecure FTP protocol, limit the set of users who are allowed this access. Edit the vsftpd configuration file. Add or correct the following configuration options: +userlist_enable=YES +userlist_file=/etc/vsftp.ftpusers +userlist_deny=NO +Edit the file /etc/vsftp.ftpusers. For each user USERNAME who should be allowed to access the system via FTP, add a line containing that user's name: +USERNAME +If anonymous access is also required, add the anonymous usernames to /etc/vsftp.ftpusers as well. +anonymous +ftp + Historically, the file /etc/ftpusers contained a list of users who were not allowed to access the system via FTP. It was used to prevent system users such as the root user from logging in via the insecure FTP protocol. However, when the configuration option userlist deny=NO is set, vsftpd interprets ftpusers as the set of users who are allowed to login via FTP. Since it should be possible for most users to access their accounts via secure protocols, it is recommended that this setting be used, so that non-anonymous FTP access can be limited to legacy users who have been explicitly identified. + + + Restrict Access to Anonymous Users if Possible + Is there a mission-critical reason for users to transfer files to/from their own accounts +using FTP, rather than using a secure protocol like SCP/SFTP? If not, edit the vsftpd +configuration file. Add or correct the following configuration option: + +local_enable=NO + +If non-anonymous FTP logins are necessary, follow the guidance in the remainder of +this section to secure these logins as much as possible. + 11 + 12 + 14 + 15 + 16 + 18 + 3 + 5 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + DSS06.03 + DSS06.06 + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.6.1.2 + A.7.1.1 + A.9.1.2 + A.9.2.1 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-7(a) + CM-7(b) + CM-6(a) + AC-3 + AC-17(a) + PR.AC-4 + PR.AC-6 + PR.IP-1 + PR.PT-3 + The use of non-anonymous FTP logins is strongly discouraged. Since SSH clients +and servers are widely available, and since SSH provides support for a transfer +mode which resembles FTP in user interface, there is no good reason to allow +password-based FTP access.' + CCE-82412-8 + + + + + Use vsftpd to Provide FTP Service if Necessary + If your use-case requires FTP service, install and +set-up vsftpd to provide it. + + Install vsftpd Package + If this system must operate as an FTP server, install the vsftpd package via the standard channels. +The vsftpd package can be installed with the following command: + +$ sudo yum install vsftpd + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-6(a) + PR.IP-1 + PR.PT-3 + After Red Hat Enterprise Linux 2.1, Red Hat switched from distributing wu-ftpd with +Red Hat Enterprise Linux to distributing vsftpd. For security +and for consistency with future Red Hat releases, the use of vsftpd is recommended. + CCE-82411-0 + +package --add=vsftpd + + include install_vsftpd + +class install_vsftpd { + package { 'vsftpd': + ensure => 'installed', + } +} + + - name: Ensure vsftpd is installed + package: + name: vsftpd + state: present + tags: + - CCE-82411-0 + - NIST-800-53-CM-6(a) + - enable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - package_vsftpd_installed + + +[[packages]] +name = "vsftpd" +version = "*" + + +if ! rpm -q --quiet "vsftpd" ; then + yum install -y "vsftpd" +fi + + + + + + + + + Web Server + The web server is responsible for providing access to +content via the HTTP protocol. Web servers represent a significant +security risk because: + +The HTTP port is commonly probed by malicious sourcesWeb server software is very complex, and includes a long +history of vulnerabilitiesThe HTTP protocol is unencrypted and vulnerable to passive +monitoring + +The system's default web server software is Apache 2 and is +provided in the RPM package httpd. + + Disable Apache if Possible + If Apache was installed and activated, but the system +does not need to act as a web server, then it should be disabled +and removed from the system. + + Uninstall httpd Package + +The httpd package can be removed with the following command: + +$ sudo yum erase httpd + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + PR.PT-3 + 2.2.10 + If there is no need to make the web server software available, +removing it provides a safeguard against its activation. + CCE-85970-2 + +package --remove=httpd + + include remove_httpd + +class remove_httpd { + package { 'httpd': + ensure => 'purged', + } +} + + - name: Ensure httpd is removed + package: + name: httpd + state: absent + tags: + - CCE-85970-2 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - no_reboot_needed + - package_httpd_removed + - unknown_severity + + +# CAUTION: This remediation script will remove httpd +# from the system, and may remove any packages +# that depend on httpd. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "httpd" ; then + + yum remove -y "httpd" + +fi + + + + + + + + + + Disable httpd Service + +The httpd service can be disabled with the following command: +$ sudo systemctl mask --now httpd.service + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + PR.PT-3 + Running web server software provides a network-based avenue +of attack, and should be disabled if not needed. + + CCE-82761-8 + include disable_httpd + +class disable_httpd { + service {'httpd': + enable => false, + ensure => 'stopped', + } +} + + - name: Disable service httpd + block: + + - name: Disable service httpd + systemd: + name: httpd.service + enabled: 'no' + state: stopped + masked: 'yes' + ignore_errors: 'yes' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82761-8 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - no_reboot_needed + - service_httpd_disabled + - unknown_severity + +- name: Unit Socket Exists - httpd.socket + command: systemctl list-unit-files httpd.socket + args: + warn: false + register: socket_file_exists + changed_when: false + ignore_errors: true + check_mode: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82761-8 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - no_reboot_needed + - service_httpd_disabled + - unknown_severity + +- name: Disable socket httpd + systemd: + name: httpd.socket + enabled: 'no' + state: stopped + masked: 'yes' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"httpd.socket" in socket_file_exists.stdout_lines[1]' + tags: + - CCE-82761-8 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - no_reboot_needed + - service_httpd_disabled + - unknown_severity + + +[customizations.services] +disabled = ["httpd"] + + apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + systemd: + units: + - name: httpd.service + enabled: false + mask: true + - name: httpd.socket + enabled: false + mask: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" stop 'httpd.service' +"$SYSTEMCTL_EXEC" disable 'httpd.service' +"$SYSTEMCTL_EXEC" mask 'httpd.service' +# Disable socket activation if we have a unit file for it +if "$SYSTEMCTL_EXEC" list-unit-files | grep -q '^httpd.socket'; then + "$SYSTEMCTL_EXEC" stop 'httpd.socket' + "$SYSTEMCTL_EXEC" mask 'httpd.socket' +fi +# The service may not be running because it has been started and failed, +# so let's reset the state so OVAL checks pass. +# Service should be 'inactive', not 'failed' after reboot though. +"$SYSTEMCTL_EXEC" reset-failed 'httpd.service' || true + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Install Apache if Necessary + If httpd was not installed and activated, but the system +needs to act as a web server, then it should be installed on the system. Follow these +guidelines to install it defensively. The httpd package can be installed with +the following command: +$ sudo yum install httpd +This method of installation is recommended over installing the "Web Server" +package group during the system installation process. The Web Server package +group includes many packages which are likely extraneous, while the +command-line method installs only the required httpd package itself. + + Confirm Minimal Built-in Modules Installed + The default httpd installation minimizes the number of +modules that are compiled directly into the binary (core prefork http_core +mod_so). This minimizes risk by limiting the capabilities allowed by the +web server. + +Query the set of compiled-in modules using the following command: +$ httpd -l +If the number of compiled-in modules is significantly larger than the +aforementioned set, this guide recommends re-installing httpd with a +reduced configuration. Minimizing the number of modules that are compiled into +the httpd binary, reduces risk by limiting the capabilities allowed by +the webserver. + + + + Secure Apache Configuration + The httpd configuration file is +/etc/httpd/conf/httpd.conf. Apply the recommendations in the remainder +of this section to this file. + + HTTPD Log Level + The setting for LogLevel in /etc/httpd/conf/httpd.conf + alert + crit + warn + emerg + error + warn + + + Maximum KeepAlive Requests for HTTPD + The setting for MaxKeepAliveRequests in httpd.conf + 100 + 1000 + 10000 + 100000 + 500 + 100 + + + Configure Error Log Format + LogFormat should be enabled and set to the following in +/etc/httpd/conf/httpd.conf: +LogFormat "a %A %h %H %l %m %s %t %u %U \"%{Referer}i\" \"%{User-Agent}i\"" combined + WA00612 + The server error logs are invaluable because they can also be used to identify +potential problems and enable proactive remediation. Log data can reveal +anomalous behavior such as "not found" or "unauthorized" errors that may +be an evidence of attack attempts. Failure to enable error logging can +significantly reduce the ability of Web Administrators to detect or remediate +problems. The LogFormat directive defines the format and information to be +included in the access log entries. + + + + + + Configure The Number of Allowed Simultaneous Requests + The MaxKeepAliveRequests directive should be set and configured to + or greater by setting the following +in /etc/httpd/conf/httpd.conf: +MaxKeepAliveRequests + WG110 + Resource exhaustion can occur when an unlimited number of concurrent requests +are allowed on a web site, facilitating a denial of service attack. Mitigating +this kind of attack will include limiting the number of concurrent HTTP/HTTPS +requests per IP address and may include, where feasible, limiting parameter +values associated with keepalive, (i.e., a parameter used to limit the amount of +time a connection may be inactive). + + + + + + MIME types for csh or sh shell programs must be disabled + Users must not be allowed to access the shell programs. + WG370 + Shell programs might execute shell escapes and could then perform +unauthorized activities that could damage the security posture of the web +server. A shell is a program that serves as the basic interface between the +user and the operating system. In this regard, there are shells that are +security risks in the context of a web server and shells that are +unauthorized. + + + + + + Enable HTTPD Error Logging + ErrorLog should be enabled and set to the following in +/etc/httpd/conf/httpd.conf: +ErrorLog "logs/error_log" + WA00605 + The server error logs are invaluable because they can also be used to identify +potential problems and enable proactive remediation. Log data can reveal +anomalous behavior such as "not found" or "unauthorized" errors that may +be an evidence of attack attempts. Failure to enable error logging can +significantly reduce the ability of Web Administrators to detect or remediate +problems. + + + + + + Enable HTTPD LogLevel + LogLevel should be enabled and set to . +Add or edit the following in /etc/httpd/conf/httpd.conf: +LogLevel + WA00620 + The server error logs are invaluable because they can also be used to identify +potential problems and enable proactive remediation. Log data can reveal +anomalous behavior such as "not found" or "unauthorized" errors that may +be an evidence of attack attempts. Failure to enable error logging can +significantly reduce the ability of Web Administrators to detect or remediate +problems. While the ErrorLog directive configures the error log file name, the +LogLevel directive is used to configure the severity level for the error logs. +The log level values are the standard syslog levels: emerg, alert, crit, error, +warn, notice, info and debug. + + + + + + Enable HTTPD System Logging + CustomLog should be enabled and set to the following in +/etc/httpd/conf/httpd.conf: +CustomLog "logs/access_log" combined + WA00615 + The server error logs are invaluable because they can also be used to identify +potential problems and enable proactive remediation. Log data can reveal +anomalous behavior such as "not found" or "unauthorized" errors that may +be an evidence of attack attempts. Failure to enable error logging can +significantly reduce the ability of Web Administrators to detect or remediate +problems. The CustomLog directive specifies the log file, syslog facility, or +piped logging utility. + + + + + + The web server password(s) must be entrusted to the SA or Web Manager + Normally, a service account is established for the web server. This is +because a privileged account is not desirable and the server is designed to +run for long uninterrupted periods of time. The SA or Web Manager will need +password access to the web server to restart the service in the event or an +emergency as the web server is not to restart automatically after an +unscheduled interruption. + WG050 + If the password is not entrusted to an SA or web manager the ability to +ensure the availability of the web server is compromised. + + + + + + A public web server, if hosted on the NIPRNet, must be isolated in an accredited DoD DMZ extension + To minimize exposure of private assets to unnecesarry risk by attackers, +public web servers must be isolated from internal systems. + +Logically relocate public web servers to be isolated from internal +systems. In addition, ensure the public web server does not have +trusted connections with assets outside the confines of the +demilitarizez done (DMZ) other than application and/or database servers +that are a part of the same system as the web server. + WA060 + Public web servers are by nature more vulnerabile to attack from publically +based sources, such as the public Internet. Once compromised, a public +server might be used as a base for further attack on private resources, +unless additional layers of protection are implemented. Public web servers +must be located in a DoD DMZ Extension, if hosted on the NIPRNet, with +carefully controlled access. Failure to isolate resources in this way +increase risk that private assets are exposed to attacks from public +sources. An improperly located public web server is a potential +threat to the entire network. + + + + + + Installation of a compiler on production web server is prohibited + The presence of a compiler on a production server facilitates the malicious +user's task of creating custom versions of programs and installing Trojan +Horses or viruses. + WG080 + An attacker's code could be uploaded and compiled on the server +under attack. + + + + + + A private web server must be located on a separate controlled access subnet + Private web servers, which host sites that serve controlled access data, +must be protected from outside threats in addition to insider threats. + +Isolate the private web server from the public DMZ and separate it from the +internal general population LAN. + WA070 + Insider threat may be accidental or intentional but, in either case, can +cause a disruption in service of the web server. To protect the private +web server from these threats, it must be located on a separate controlled +access subnet and must not be part of the public DMZ that houses the public +web servers. it also cannot be located inside the enclave as part of the +local general population LAN. + + + + + + Public web server resources must not be shared with private assets + It is important to segregate public web server resources from private +resources located behind the DoD DMZ in order to protect private +assets. + WG040 + When folders, drives, or other resources are directly shared between the +public web server and private servers the intent of data and resource +segregation can be compromised. + +In addition to the requirements of the DoD Internet-NIPRNet DMZ STIG that +isolates inbound traffic from external network to the internal network, +resources such as printers, files, and folders/directories will not be +shared between public web servers and assets located within the internal +network. + + + + + + Backup interactive scripts on the production web server are prohibited + Copies of backup files will not execute on the server, but they can be +read by the anonymous user if special precautions are not taken. + WG420 + Such backup copies contain the same sensitive information as the actual +scripts being executed and, as such, are useful to malicious users. +Techniques and systems exist today that search web servers for such files +and are able to exploit the information contained in them. + +Backup copies of files are automatically created by some text editors such +such as emacs and VIM. Editors may write a backup file with an extension +~ added to the name of the original file. The edit plus editor will +create a .bak file. Of course, this would imply the presence and use of +development tools on the web server, which is a finding under WG130. Having +backup scripts on the web server provides one more opportunity for +malicious persons to view these scripts and use the information found in +them. + + + + + + Configure Operating System to Protect Web Server + The following configuration steps should be taken on the system which hosts the +web server, in order to provide as safe an environment as possible for the web server. + + Scan All Uploaded Content for Malicious Software + Install anti-virus software on the system and set it to automatically scan new +files that are introduced to the web server. + WG237 + Remote web authors should not be able to upload files to the Document Root +directory structure without virus checking and checking for malicious or mobile +code. A remote web user, whose agency has a Memorandum of Agreement (MOA) with +the hosting agency and has submitted a DoD form 2875 (System Authorization +Access Request (SAAR)) or an equivalent document, will be allowed to post files +to a temporary location on the server. All posted files to this temporary +location will be scanned for viruses and content checked for malicious or mobile +code. Only files free of viruses and malicious or mobile code will be posted to +the appropriate DocumentRoot directory. + + + + + + Configure firewall to Allow Access to the Web Server + +By default, iptables +blocks access to the ports used by the web server. +To configure iptables to allow port 80 traffic, one must edit +/etc/sysconfig/iptables and +/etc/sysconfig/ip6tables (if IPv6 is in use). +Add the following line, ensuring that it appears before the final LOG and DROP lines for the INPUT chain: +-A INPUT -m state --state NEW -p tcp --dport 80 -j ACCEPT +To configure iptables to allow port 443 traffic, one must edit +/etc/sysconfig/iptables and +/etc/sysconfig/ip6tables (if IPv6 is in use). +Add the following line, ensuring that it appears before the final LOG and DROP lines for the INPUT chain: +-A INPUT -m state --state NEW -p tcp --dport 443 -j ACCEPT + WG610 + Failure to comply with DoD ports, protocols, and services (PPS) requirements +can result in compromise of enclave boundary protections and/or functionality +of the AIS. + + + + + + Ensure Remote Administrative Access Is Encrypted + Ensure that the SSH server service is enabled. + +The sshd service can be enabled with the following command: +$ sudo systemctl enable sshd.service + WG230 + Logging into a web server remotely using an unencrypted protocol or service +when performing updates and maintenance is a major risk. Data, such as user +account, is transmitted in plaintext and can easily be compromised. When +performing remote administrative tasks, a protocol or service that encrypts the +communication channel must be used. + +An alternative to remote administration of +the web server is to perform web server administration locally at the console. +Local administration at the console implies physical access to the server. + + + + + + Run httpd in a chroot Jail if Practical + Running httpd inside a chroot jail is designed to isolate the +web server process to a small section of the filesystem, limiting the damage if +it is compromised. Versions of Apache greater than 2.2.10 (such as the one +included with Red Hat Enterprise Linux 8) provide the ChrootDir directive. To run Apache +inside a chroot jail in /chroot/apache, add the following line to +/etc/httpd/conf/httpd.conf: ChrootDir /chroot/apache This +necessitates placing all files required by httpd inside +/chroot/apache , including httpd's binaries, modules, +configuration files, and served web pages. The details of this configuration +are beyond the scope of this guide. This may also require additional SELinux +configuration. + + + Restrict File and Directory Access + Minimize access to critical httpd files and directories. + + Set Permissions on the /etc/httpd/conf/ Directory + To properly set the permissions of /etc/http/conf, run the command: $ sudo chmod 0750 /etc/http/conf + Access to the web server's configuration files may allow an unauthorized user or attacker +to access information about the web server or alter the server's configuration files. + + + + + + + + + Set Permissions on the /var/log/httpd/ Directory + Ensure that the permissions on the web server log directory is set to 700: +$ sudo chmod 700 /var/log/httpd/ +This is its default setting. + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + AC-6(1) + PR.IP-1 + PR.PT-3 + A major tool in exploring the web site use, attempted use, unusual conditions, +and problems are the access and error logs. In the event of a security incident, +these logs can provide the SA and the web manager with valuable information. To +ensure the integrity of the log files and protect the SA and the web manager +from a conflict of interest related to the maintenance of these files, only the +members of the Auditors group will be granted permissions to move, copy, and +delete these files in the course of their duties related to the archiving of +these files. + + + + + + + + + Set Permissions on All Configuration Files Inside /etc/httpd/conf.d/ + To properly set the permissions of /etc/http/conf.d/*, run the command: $ sudo chmod 0640 /etc/http/conf.d/* + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + AC-6(1) + PR.IP-1 + PR.PT-3 + Access to the web server's configuration files may allow an unauthorized user or attacker +to access information about the web server or to alter the server's configuration files. + - name: Find /etc/httpd/conf.d/ file(s) + command: find -H /etc/httpd/conf.d/ -maxdepth 1 -perm /u+xs,g+xws,o+xwrt -type f + -regex "^.*$" + register: files_found + changed_when: false + failed_when: false + check_mode: false + tags: + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - configure_strategy + - file_permissions_httpd_server_conf_d_files + - low_complexity + - low_disruption + - no_reboot_needed + - unknown_severity + +- name: Set permissions for /etc/httpd/conf.d/ file(s) + file: + path: '{{ item }}' + mode: u-xs,g-xws,o-xwrt + state: file + with_items: + - '{{ files_found.stdout_lines }}' + tags: + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - configure_strategy + - file_permissions_httpd_server_conf_d_files + - low_complexity + - low_disruption + - no_reboot_needed + - unknown_severity + + + + + +find -H /etc/httpd/conf.d/ -maxdepth 1 -perm /u+xs,g+xws,o+xwrt -type f -regex '^.*$' -exec chmod u-xs,g-xws,o-xwrt {} \; + + + + + + + + + + Set Permissions on All Configuration Files Inside /etc/httpd/conf/ + To properly set the permissions of /etc/http/conf/*, run the command: $ sudo chmod 0640 /etc/http/conf/* + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + AC-6(1) + PR.IP-1 + PR.PT-3 + Access to the web server's configuration files may allow an unauthorized user or attacker +to access information about the web server or to alter the server's configuration files. + - name: Find /etc/httpd/conf/ file(s) + command: find -H /etc/httpd/conf/ -maxdepth 1 -perm /u+xs,g+xws,o+xwrt -type f -regex + "^.*$" + register: files_found + changed_when: false + failed_when: false + check_mode: false + tags: + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - configure_strategy + - file_permissions_httpd_server_conf_files + - low_complexity + - low_disruption + - no_reboot_needed + - unknown_severity + +- name: Set permissions for /etc/httpd/conf/ file(s) + file: + path: '{{ item }}' + mode: u-xs,g-xws,o-xwrt + state: file + with_items: + - '{{ files_found.stdout_lines }}' + tags: + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - configure_strategy + - file_permissions_httpd_server_conf_files + - low_complexity + - low_disruption + - no_reboot_needed + - unknown_severity + + + + + +find -H /etc/httpd/conf/ -maxdepth 1 -perm /u+xs,g+xws,o+xwrt -type f -regex '^.*$' -exec chmod u-xs,g-xws,o-xwrt {} \; + + + + + + + + + + Set Permissions on All Configuration Files Inside /etc/httpd/conf.modules.d/ + To properly set the permissions of /etc/http/conf.modules.d/*, run the command: $ sudo chmod 0640 /etc/http/conf.modules.d/* + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + AC-6(1) + PR.IP-1 + PR.PT-3 + Access to the web server's configuration files may allow an unauthorized user or attacker +to access information about the web server or to alter the server's configuration files. + + + + + + + + + HTTPD Log Files Must Be Owned By Root + All httpd logs must be owned by root user and group. By default, +the path for httpd logs is /var/log/httpd/ + +To properly set the owner of /var/log/httpd, run the command: +$ sudo chown root /var/log/httpd + +To properly set the owner of /var/log/httpd/*, run the command: +$ sudo chown root /var/log/httpd/* + WG255 + A major tool in exploring the web site use, attempted use, unusual conditions, +and problems are the access and error logs. In the event of a security incident, +these logs can provide the SA and the web administrator with valuable +information. Because of the information that is captured in the logs, it is +critical that only authorized individuals have access to the logs. + + + + + + + + Configure PERL Securely + PERL (Practical Extraction and Report Language) is an interpreted language +optimized for scanning arbitrary text files, extracting information from those +text files, and printing reports based on that information. The language is +often used in shell scripting and is intended to be practical, easy to use, and +efficient means of generating interactive web pages for the user. + + Configure HTTP PERL Scripts To Use TAINT Option + If the mod_perl module is installed, enable Perl Taint checking in +/etc/httpd/conf/httpd.conf. To enable Perl Taint +checking, add or uncomment the following to /etc/httpd/conf.d/perl.conf: +PerlSwitches -T + WG460 + PERL (Practical Extraction and Report Language) is an interpreted language +optimized for scanning arbitrary text files, extracting information from those +text files, and printing reports based on that information. The language is +often used in shell scripting and is intended to be practical, easy to use, and +efficient means of generating interactive web pages for the user. Unfortunately, +many widely available freeware PERL programs (scripts) are extremely insecure. +This is most readily accomplished by a malicious user substituting input to a +PERL script during a POST or a GET operation. + +Consequently, the founders of +PERL have developed a mechanism named TAINT that protects the system from +malicious input sent from outside the program. When the data is tainted, it +cannot be used in programs or functions such as eval(), system(), exec(), pipes, +or popen(). The script will exit with a warning message. + + + + + + + Configure PHP Securely + PHP is a widely-used and often misconfigured server-side scripting language. It should +be used with caution, but configured appropriately when needed. + +Review /etc/php.ini and make the following changes if possible: +# Do not expose PHP error messages to external users +display_errors = Off + +# Enable safe mode +safe_mode = On + +# Only allow access to executables in isolated directory +safe_mode_exec_dir = php-required-executables-path + +# Limit external access to PHP environment +safe_mode_allowed_env_vars = PHP_ + +# Restrict PHP information leakage +expose_php = Off + +# Log all errors +log_errors = On + +# Do not register globals for input data +register_globals = Off + +# Minimize allowable PHP post size +post_max_size = 1K + +# Ensure PHP redirects appropriately +cgi.force_redirect = 0 + +# Disallow uploading unless necessary +file_uploads = Off + +# Disallow treatment of file requests as fopen calls +allow_url_fopen = Off + +# Enable SQL safe mode +sql.safe_mode = On + + + + Directory Restrictions + The Directory tags in the web server configuration file allow finer grained access +control for a specified directory. All web directories should be configured on a +case-by-case basis, allowing access only where needed. + + Web Content Directories Must Not Be Shared Anonymously + Web content directories should not be shared anonymously over remote filesystems +such as nfs and smb. Remove the shares from the applicable +directories. + WG210 + Sharing web content is a security risk when a web server is involved. Users +accessing the share anonymously could experience privileged access to the +content of such directories. Network sharable directories expose those +directories and their contents to unnecessary access. Any unnecessary exposure +increases the risk that someone could exploit that access and either compromises +the web content or cause web server performance problems. + + + + + + Remove Write Permissions From Filesystem Paths And Server Scripts + Configure permissions for each instance of Alias, +ScriptAlias, and ScriptAliasMatch that exist. +$ sudo find DIR -type d -exec chmod 755 {} \; +$ sudo find DIR -type f -exec chmod 555 {} \; +Where DIR matches the paths from Alias, +ScriptAlias, and ScriptAliasMatch. + WG290 + Excessive permissions for the anonymous web user account are one of the most +common faults contributing to the compromise of a web server. If this user is +able to upload and execute files on the web server, the organization or owner of +the server will no longer have control of the asset. + + + + + + Disable Anonymous FTP Access + If any directories that contain dynamic scripts can be accessed via FTP by +any group or user that does not require access, remove permissions to such +directories that allow anonymous access. Also, ensure that any such +access employs an encrypted connection. + WG430 + The directories containing the CGI scripts, such as PERL, must not be +accessible to anonymous users via FTP. This applies to all directories that +contain scripts that can dynamically produce web pages in an interactive manner +(i.e., scripts based upon user-provided input). Such scripts contain information +that could be used to compromise a web service, access system resources, or +deface a web site. + + + + + + Ignore HTTPD .htaccess Files + Set AllowOverride to none for each instant of +<Directory>. + WG400 + CGI scripts represents one of the most common and exploitable means of +compromising a web server. By definition, CGI are executable by the operating +system of the host server. While access control is provided via the web service, +the execution of CGI programs is not otherwise limited unless the SA or Web +Manager takes specific measures. CGI programs can access and alter data files, +launch other programs and use the network. CGI programs can be written in any +available programming language. C, PERL, PHP, Javascript, VBScript and shell +(sh, ksh, bash) are popular choices. + + + + + + Limit Available Methods + Web server methods are defined in section 9 of RFC 2616 ( + http://www.ietf.org/rfc/rfc2616.txt). +If a web server does not require the implementation of all available methods, +they should be disabled. + +Note: GET and POST are the most common methods. A majority of the others +are limited to the WebDAV protocol. +<Directory /var/www/html> +# ... + # Only allow specific methods (this command is case-sensitive!) + <LimitExcept GET POST> + Order allow,deny + </LimitExcept> +# ... +</Directory> + Minimizing the number of available methods to the web client reduces risk +by limiting the capabilities allowed by the web server. + + + Restrict Other Critical Directories + All accessible web directories should be configured with similarly restrictive settings. +The Options directive should be limited to necessary functionality and the AllowOverride +directive should be used only if needed. The Order and Deny access control tags +should be used to deny access by default, allowing access only where necessary. + Directories accessible from a web client should be configured with the least amount of +access possible in order to avoid unauthorized access to restricted content or server information. + + + Restrict Root Directory + The httpd root directory should always have the most restrictive configuration enabled. +<Directory / > + Options None + AllowOverride None + Order allow,deny +</Directory> + The Web Server's root directory content should be protected from unauthorized access +by web clients. + + + Restrict Web Directory + The default configuration for the web (/var/www/html) Directory allows directory +indexing (Indexes) and the following of symbolic links (FollowSymLinks). +Neither of these is recommended. + +The /var/www/html directory hierarchy should not be viewable via the web, and +symlinks should only be followed if the owner of the symlink also owns the linked file. + +Ensure that this policy is adhered to by altering the related section of the configuration: +<Directory "/var/www/html"> +# ... + Options SymLinksIfOwnerMatch +# ... +</Directory> + Access to the web server's directory hierarchy could allow access to unauthorized files +by web clients. Following symbolic links could also allow such access. + + + + Minimize Web Server Loadable Modules + A default installation of httpd includes a plethora of dynamically shared objects (DSO) +that are loaded at run-time. Unlike the aforementioned compiled-in modules, a DSO can be +disabled in the configuration file by removing the corresponding LoadModule directive. + +Note: A DSO only provides additional functionality if associated directives are included +in the httpd configuration file. It should also be noted that removing a DSO will produce +errors on httpd startup if the configuration file contains directives that apply to that +module. Refer to http://httpd.apache.org/docs/ for details on which directives +are associated with each DSO. + +Following each DSO removal, the configuration can be tested with the following command +to check if everything still works: +$ sudo service httpd configtest +The purpose of each of the modules loaded by default will now be addressed one at a time. +If none of a module's directives are being used, remove it. + + httpd Core Modules + These modules comprise a basic subset of modules that are likely needed for base httpd +functionality; ensure they are not commented out in /etc/httpd/conf/httpd.conf: +LoadModule auth_basic_module modules/mod_auth_basic.so +LoadModule authn_default_module modules/mod_authn_default.so +LoadModule authz_host_module modules/mod_authz_host.so +LoadModule authz_user_module modules/mod_authz_user.so +LoadModule authz_groupfile_module modules/mod_authz_groupfile.so +LoadModule authz_default_module modules/mod_authz_default.so +LoadModule log_config_module modules/mod_log_config.so +LoadModule logio_module modules/mod_logio.so +LoadModule setenvif_module modules/mod_setenvif.so +LoadModule mime_module modules/mod_mome.so +LoadModule autoindex_module modules/mod_autoindex.so +LoadModule negotiation_module modules/mod_negotiation.so +LoadModule dir_module modules/mod_dir.so +LoadModule alias_module modules/mod_alias.so +Minimizing the number of loadable modules available to the web server reduces risk +by limiting the capabilities allowed by the web server. + + Disable Cache Support + The cache module allows httpd to cache data, optimizing access to +frequently accessed content. However, it introduces potential security flaws +such as the possibility of circumventing Allow and +Deny directives. + If this functionality is +unnecessary, comment out the module: +#LoadModule cache_module modules/mod_cache.so +If caching is required, it should not be enabled for any limited-access content. + Minimizing the number of loadable modules available to the web server reduces risk +by limiting the capabilities allowed by the web server. + + + Disable CGI Support + The cgi module allows HTML to interact with the CGI web programming language. + +If this functionality is unnecessary, comment out the module: +#LoadModule cgi_module modules/mod_cgi.so + +If the web server requires the use of CGI, enable mod_cgi. + Minimizing the number of loadable modules available to the web server reduces risk +by limiting the capabilities allowed by the web server. + + + Disable HTTP Digest Authentication + The auth_digest module provides encrypted authentication sessions. +If this functionality is unnecessary, comment out the related module: +#LoadModule auth_digest_module modules/mod_auth_digest.so + Minimizing the number of loadable modules available to the web server reduces risk +by limiting the capabilities allowed by the web server. + + + Enable log_config_module For HTTPD Logging + The log_config_module should exist and be configured in +the /etc/httpd/conf/httpd.conf file by adding the following module to +configure logging: +log_config_module + WG240 + A major tool in exploring the web site use, attempted use, unusual conditions, +and problems are reported in the access and error logs. In the event of a +security incident, these logs can provide the SA and the web manager with +valuable information. Without these log files, SAs and web managers are +seriously hindered in their efforts to respond appropriately to suspicious or +criminal actions targeted at the web site. + + + + + + Disable LDAP Support + The ldap module provides HTTP authentication via an LDAP directory. +If its functionality is unnecessary, comment out the related modules: +#LoadModule ldap_module modules/mod_ldap.so +#LoadModule authnz_ldap_module modules/mod_authnz_ldap.so +If LDAP is to be used, SSL encryption should be used as well. + Minimizing the number of loadable modules available to the web server reduces risk +by limiting the capabilities allowed by the web server. + + + Disable MIME Magic + The mime_magic module provides a second layer of MIME support that in most configurations +is likely extraneous. If its functionality is unnecessary, comment out the related module: +#LoadModule mime_magic_module modules/mod_mime_magic.so + Minimizing the number of loadable modules available to the web server reduces risk +by limiting the capabilities allowed by the web server. + + + Disable HTTP mod_rewrite + The mod_rewrite module is very powerful and can protect against +certain classes of web attacks. However, it is also very complex and has a +significant history of vulnerabilities itself. If its functionality is +unnecessary, comment out the related module: +#LoadModule rewrite_module modules/mod_rewrite.so + Minimizing the number of loadable modules available to the web server reduces risk +by limiting the capabilities allowed by the web server. + + + Disable Proxy Support + The proxy module provides proxying support, allowing httpd to forward requests and +serve as a gateway for other servers. If its functionality is unnecessary, comment out the module: +#LoadModule proxy_module modules/mod_proxy.so + +If proxy support is needed, load mod_proxy and the appropriate proxy protocol handler +module (one of mod_proxy_http, mod_proxy_ftp, or mod_proxy_connect). Additionally, +make certain that a server is secure before enabling proxying, as open proxy servers +are a security risk. mod_proxy_balancer enables load balancing, but requires that +mod status be enabled. + Minimizing the number of loadable modules available to the web server reduces risk +by limiting the capabilities allowed by the web server. + + + Disable Server Activity Status + The status module provides real-time access to statistics on the internal operation of +the web server. This may constitute an unnecessary information leak and should be disabled +unless necessary. To do so, comment out the related module: +#LoadModule status_module modules/mod_status.so +If there is a critical need for this module, ensure that access to the status +page is properly restricted to a limited set of hosts in the status handler +configuration. + Minimizing the number of loadable modules available to the web server reduces risk +by limiting the capabilities allowed by the web server. + + + Disable Web Server Configuration Display + The info module creates a web page illustrating the configuration of the web server. This +can create an unnecessary security leak and should be disabled. +If its functionality is unnecessary, comment out the module: +#LoadModule info_module modules/mod_info.so +If there is a critical need for this module, use the Location directive to provide +an access control list to restrict access to the information. + Minimizing the number of loadable modules available to the web server reduces risk +by limiting the capabilities allowed by the web server. + + + Disable Server Side Includes + Server Side Includes provide a method of dynamically generating web pages through the +insertion of server-side code. However, the technology is also deprecated and +introduces significant security concerns. +If this functionality is unnecessary, comment out the related module: +#LoadModule include_module modules/mod_include.so +If there is a critical need for Server Side Includes, they should be enabled with the +option IncludesNoExec to prevent arbitrary code execution. Additionally, user +supplied data should be encoded to prevent cross-site scripting vulnerabilities. + Minimizing the number of loadable modules available to the web server reduces risk +by limiting the capabilities allowed by the web server. + + + Disable URL Correction on Misspelled Entries + The speling module attempts to find a document match by allowing one misspelling in an +otherwise failed request. If this functionality is unnecessary, comment out the module: +#LoadModule speling_module modules/mod_speling.so +This functionality weakens server security by making site enumeration easier. + Minimizing the number of loadable modules available to the web server reduces risk +by limiting the capabilities allowed by the web server. + + + Disable WebDAV (Distributed Authoring and Versioning) + WebDAV is an extension of the HTTP protocol that provides distributed and +collaborative access to web content. If its functionality is unnecessary, +comment out the related modules: +#LoadModule dav_module modules/mod_dav.so +#LoadModule dav_fs_module modules/mod_dav_fs.so +If there is a critical need for WebDAV, extra care should be taken in its configuration. +Since DAV access allows remote clients to manipulate server files, any location on the +server that is DAV enabled should be protected by access controls. + Minimizing the number of loadable modules available to the web server, reduces risk +by limiting the capabilities allowed by the web server. + + + Minimize Modules for HTTP Basic Authentication + The following modules are necessary if this web server will provide content that will +be restricted by a password. + +Authentication can be performed using local plain text password files (authn_file), +local DBM password files (authn_dbm) or an LDAP directory. The only module required by +the web server depends on your choice of authentication. Comment out the modules you don't +need from the following: +LoadModule authn_file_module modules/mod_authn_file.so +LoadModule authn_dbm_module modules/mod_authn_dbm.so +authn_alias allows for authentication based on aliases. authn_anon +allows anonymous authentication similar to that of anonymous ftp sites. authz_owner +allows authorization based on file ownership. authz_dbm allows for authorization +based on group membership if the web server is using DBM authentication. + +If the above functionality is unnecessary, comment out the related module: +#LoadModule authn_alias_module modules/mod_authn_alias.so +#LoadModule authn_anon_module modules/mod_authn_anon.so +#LoadModule authz_owner_module modules/mod_authz_owner.so +#LoadModule authz_dbm_module modules/mod_authz_dbm.so + + + Minimize Configuration Files Included + The Include directive directs httpd to load supplementary configuration files +from a provided path. The default configuration loads all files that end in .conf +from the /etc/httpd/conf.d directory. + +To restrict excess configuration, the following line should be commented out and +replaced with Include directives that only reference required configuration files: +#Include conf.d/*.conf +If the above change was made, ensure that the SSL encryption remains loaded by +explicitly including the corresponding configuration file: +Include conf.d/ssl.conf +If PHP is necessary, a similar alteration must be made: +Include conf.d/php.conf + +Explicitly listing the configuration files to be loaded during web server start-up avoids +the possibility of unwanted or malicious configuration files to be automatically included as +part of the server's running configuration. + + + Minimize Various Optional Components + The following modules perform very specific tasks, sometimes providing access to +just a few additional directives. If such functionality is not required (or if you +are not using these directives), comment out the associated module: +External filtering (response passed through external program prior to client delivery) +#LoadModule ext_filter_module modules/mod_ext_filter.soUser-specified Cache Control and Expiration +#LoadModule expires_module modules/mod_expires.soCompression Output Filter (provides content compression prior to client delivery) +#LoadModule deflate_module modules/mod_deflate.soHTTP Response/Request Header Customization +#LoadModule headers_module modules/mod_headers.soUser activity monitoring via cookies +#LoadModule usertrack_module modules/mod_usertrack.soDynamically configured mass virtual hosting +#LoadModule vhost_alias_module modules/mod_vhost_alias.so +Minimizing the number of loadable modules available to the web server reduces risk +by limiting the capabilities allowed by the web server. + + + + + Use Appropriate Modules to Improve httpd's Security + Among the modules available for httpd are several whose use may improve the +security of the web server installation. This section recommends and discusses +the deployment of security-relevant modules. + + Deploy mod_security + The security module provides an application level firewall for httpd. +Following its installation with the base ruleset, specific configuration advice can be found at + + http://www.modsecurity.org/ to design a policy that best matches the security needs of +the web applications. Usage of mod_security is highly recommended for some environments, +but it should be noted this module does not ship with Red Hat Enterprise Linux itself, +and instead is provided via Extra Packages for Enterprise Linux (EPEL). +For more information on EPEL please refer to + http://fedoraproject.org/wiki/EPEL. + + Install mod_security + Install the security module: +The mod_security package can be installed with the following command: + +$ sudo yum install mod_security + mod_security provides an additional level of protection for the web server by +enabling the administrator to implement content access policies and filters at the +application layer. + + + + Deploy mod_ssl + Because HTTP is a plain text protocol, all traffic is susceptible to passive +monitoring. If there is a need for confidentiality, SSL should be configured +and enabled to encrypt content. + +Note: mod_nss is a FIPS 140-2 certified alternative to mod_ssl. +The modules share a considerable amount of code and should be nearly identical +in functionality. If FIPS 140-2 validation is required, then mod_nss should +be used. If it provides some feature or its greater compatibility is required, +then mod_ssl should be used. + + Enable Transport Layer Security (TLS) Encryption + Disable old SSL and TLS version and enable the latest TLS encryption by setting +the following in /etc/httpd/conf.modules.d/ssl.conf: +SSLProtocol all -SSLv2 -SSLv3 -TLSv1 -TLSv1.1 +Make sure to also set SSLEngine to on in +/etc/httpd/conf.modules.d/ssl.conf like the following: +SSLEngine on + WG340 + Transport Layer Security (TLS) encryption is a required security setting for a +private web server. Encryption of private information is essential to ensuring +data confidentiality. If private information is not encrypted, it can be +intercepted and easily read by an unauthorized party. A web server must +use a FIPS 140-2 approved TLS version, and all non-FIPS-approved SSL versions +must be disabled. + + + + + + Configure A Valid Server Certificate + Configure the web site to use a valid organizationally defined certificate. +For DoD, this is a DoD server certificate issued by the DoD CA. + WG350 + This check verifies that DoD is a hosted web site's CA. The certificate is +actually a DoD-issued server certificate used by the organization being +reviewed. This is used to verify the authenticity of the web site to the user. +If the certificate is not for the server (Certificate belongs to), if the +certificate is not issued by DoD (Certificate was issued by), or if the current +date is not included in the valid date (Certificate is valid from), then there +is no assurance that the use of the certificate is valid. The entire purpose of +using a certificate is, therefore, compromised. + + + + + + Install mod_ssl + Install the mod_ssl module: +The mod_ssl package can be installed with the following command: + +$ sudo yum install mod_ssl + mod_ssl provides encryption capabilities for the httpd Web server. Unencrypted +content is transmitted in plain text which could be passively monitored and accessed by +unauthorized parties. + + + Require Client Certificates + SSLVerifyClient should be set and configured to require by +setting the following in /etc/httpd/conf/httpd.conf: +SSLVerifyClient require + WG140 + Web sites requiring authentication within the DoD must utilize PKI as an +authentication mechanism for web users. Information systems residing behind web +servers requiring authorization based on individual identity must use the +identity provided by certificate-based authentication to support access control +decisions. + + + + + + + + Restrict Web Server Information Leakage + The ServerTokens and ServerSignature directives determine how +much information the web server discloses about the configuration of the +system. + + Set httpd ServerSignature Directive to Off + ServerSignature Off restricts httpd from displaying server version number +on error pages. + +Add or correct the following directive in /etc/httpd/conf/httpd.conf: +ServerSignature Off + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + PR.PT-3 + Information disclosed to clients about the configuration of the web server and system could be used +to plan an attack on the given system. This information disclosure should be restricted to a minimum. + + + Set httpd ServerTokens Directive to Prod + ServerTokens Prod restricts information in page headers, returning only the word "Apache." + +Add or correct the following directive in /etc/httpd/conf/httpd.conf: +ServerTokens Prod + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + PR.PT-3 + Information disclosed to clients about the configuration of the web server and system could be used +to plan an attack on the given system. This information disclosure should be restricted to a minimum. + + + + Configure HTTPD-Served Web Content Securely + Running httpd inside a chroot jail is designed to isolate the +web server process to a small section of the filesystem, limiting the damage if +it is compromised. Versions of Apache greater than 2.2.10 (such as the one +included with Red Hat Enterprise Linux 7) provide the ChrootDir directive. To run Apache +inside a chroot jail in /chroot/apache, add the following line to +/etc/httpd/conf/httpd.conf: ChrootDir /chroot/apache This +necessitates placing all files required by httpd inside +/chroot/apache , including httpd's binaries, modules, +configuration files, and served web pages. The details of this configuration +are beyond the scope of this guide. This may also require additional SELinux +configuration. + + Web Login Banner Verbiage + Enter an appropriate login banner for your organization. Please note that new lines must +be expressed by the '\n' character and special characters like parentheses and quotation marks must be escaped with '\\'. + ^(You[\s\n]+are[\s\n]+accessing[\s\n]+a[\s\n]+U\.S\.[\s\n]+Government[\s\n]+\(USG\)[\s\n]+Information[\s\n]+System[\s\n]+\(IS\)[\s\n]+that[\s\n]+is[\s\n]+provided[\s\n]+for[\s\n]+USG\-authorized[\s\n]+use[\s\n]+only\.[\s\n]+By[\s\n]+using[\s\n]+this[\s\n]+IS[\s\n]+\(which[\s\n]+includes[\s\n]+any[\s\n]+device[\s\n]+attached[\s\n]+to[\s\n]+this[\s\n]+IS\)\,[\s\n]+you[\s\n]+consent[\s\n]+to[\s\n]+the[\s\n]+following[\s\n]+conditions\:(?:[\n]+|(?:\\n)+)\-The[\s\n]+USG[\s\n]+routinely[\s\n]+intercepts[\s\n]+and[\s\n]+monitors[\s\n]+communications[\s\n]+on[\s\n]+this[\s\n]+IS[\s\n]+for[\s\n]+purposes[\s\n]+including\,[\s\n]+but[\s\n]+not[\s\n]+limited[\s\n]+to\,[\s\n]+penetration[\s\n]+testing\,[\s\n]+COMSEC[\s\n]+monitoring\,[\s\n]+network[\s\n]+operations[\s\n]+and[\s\n]+defense\,[\s\n]+personnel[\s\n]+misconduct[\s\n]+\(PM\)\,[\s\n]+law[\s\n]+enforcement[\s\n]+\(LE\)\,[\s\n]+and[\s\n]+counterintelligence[\s\n]+\(CI\)[\s\n]+investigations\.(?:[\n]+|(?:\\n)+)\-At[\s\n]+any[\s\n]+time\,[\s\n]+the[\s\n]+USG[\s\n]+may[\s\n]+inspect[\s\n]+and[\s\n]+seize[\s\n]+data[\s\n]+stored[\s\n]+on[\s\n]+this[\s\n]+IS\.(?:[\n]+|(?:\\n)+)\-Communications[\s\n]+using\,[\s\n]+or[\s\n]+data[\s\n]+stored[\s\n]+on\,[\s\n]+this[\s\n]+IS[\s\n]+are[\s\n]+not[\s\n]+private\,[\s\n]+are[\s\n]+subject[\s\n]+to[\s\n]+routine[\s\n]+monitoring\,[\s\n]+interception\,[\s\n]+and[\s\n]+search\,[\s\n]+and[\s\n]+may[\s\n]+be[\s\n]+disclosed[\s\n]+or[\s\n]+used[\s\n]+for[\s\n]+any[\s\n]+USG\-authorized[\s\n]+purpose\.(?:[\n]+|(?:\\n)+)\-This[\s\n]+IS[\s\n]+includes[\s\n]+security[\s\n]+measures[\s\n]+\(e\.g\.\,[\s\n]+authentication[\s\n]+and[\s\n]+access[\s\n]+controls\)[\s\n]+to[\s\n]+protect[\s\n]+USG[\s\n]+interests\-\-not[\s\n]+for[\s\n]+your[\s\n]+personal[\s\n]+benefit[\s\n]+or[\s\n]+privacy\.(?:[\n]+|(?:\\n)+)\-Notwithstanding[\s\n]+the[\s\n]+above\,[\s\n]+using[\s\n]+this[\s\n]+IS[\s\n]+does[\s\n]+not[\s\n]+constitute[\s\n]+consent[\s\n]+to[\s\n]+PM\,[\s\n]+LE[\s\n]+or[\s\n]+CI[\s\n]+investigative[\s\n]+searching[\s\n]+or[\s\n]+monitoring[\s\n]+of[\s\n]+the[\s\n]+content[\s\n]+of[\s\n]+privileged[\s\n]+communications\,[\s\n]+or[\s\n]+work[\s\n]+product\,[\s\n]+related[\s\n]+to[\s\n]+personal[\s\n]+representation[\s\n]+or[\s\n]+services[\s\n]+by[\s\n]+attorneys\,[\s\n]+psychotherapists\,[\s\n]+or[\s\n]+clergy\,[\s\n]+and[\s\n]+their[\s\n]+assistants\.[\s\n]+Such[\s\n]+communications[\s\n]+and[\s\n]+work[\s\n]+product[\s\n]+are[\s\n]+private[\s\n]+and[\s\n]+confidential\.[\s\n]+See[\s\n]+User[\s\n]+Agreement[\s\n]+for[\s\n]+details\.|I've[\s\n]+read[\s\n]+\&[\s\n]+consent[\s\n]+to[\s\n]+terms[\s\n]+in[\s\n]+IS[\s\n]+user[\s\n]+agreem't\.)$ + ^You[\s\n]+are[\s\n]+accessing[\s\n]+a[\s\n]+U\.S\.[\s\n]+Government[\s\n]+\(USG\)[\s\n]+Information[\s\n]+System[\s\n]+\(IS\)[\s\n]+that[\s\n]+is[\s\n]+provided[\s\n]+for[\s\n]+USG\-authorized[\s\n]+use[\s\n]+only\.[\s\n]+By[\s\n]+using[\s\n]+this[\s\n]+IS[\s\n]+\(which[\s\n]+includes[\s\n]+any[\s\n]+device[\s\n]+attached[\s\n]+to[\s\n]+this[\s\n]+IS\)\,[\s\n]+you[\s\n]+consent[\s\n]+to[\s\n]+the[\s\n]+following[\s\n]+conditions\:(?:[\n]+|(?:\\n)+)\-The[\s\n]+USG[\s\n]+routinely[\s\n]+intercepts[\s\n]+and[\s\n]+monitors[\s\n]+communications[\s\n]+on[\s\n]+this[\s\n]+IS[\s\n]+for[\s\n]+purposes[\s\n]+including\,[\s\n]+but[\s\n]+not[\s\n]+limited[\s\n]+to\,[\s\n]+penetration[\s\n]+testing\,[\s\n]+COMSEC[\s\n]+monitoring\,[\s\n]+network[\s\n]+operations[\s\n]+and[\s\n]+defense\,[\s\n]+personnel[\s\n]+misconduct[\s\n]+\(PM\)\,[\s\n]+law[\s\n]+enforcement[\s\n]+\(LE\)\,[\s\n]+and[\s\n]+counterintelligence[\s\n]+\(CI\)[\s\n]+investigations\.(?:[\n]+|(?:\\n)+)\-At[\s\n]+any[\s\n]+time\,[\s\n]+the[\s\n]+USG[\s\n]+may[\s\n]+inspect[\s\n]+and[\s\n]+seize[\s\n]+data[\s\n]+stored[\s\n]+on[\s\n]+this[\s\n]+IS\.(?:[\n]+|(?:\\n)+)\-Communications[\s\n]+using\,[\s\n]+or[\s\n]+data[\s\n]+stored[\s\n]+on\,[\s\n]+this[\s\n]+IS[\s\n]+are[\s\n]+not[\s\n]+private\,[\s\n]+are[\s\n]+subject[\s\n]+to[\s\n]+routine[\s\n]+monitoring\,[\s\n]+interception\,[\s\n]+and[\s\n]+search\,[\s\n]+and[\s\n]+may[\s\n]+be[\s\n]+disclosed[\s\n]+or[\s\n]+used[\s\n]+for[\s\n]+any[\s\n]+USG\-authorized[\s\n]+purpose\.(?:[\n]+|(?:\\n)+)\-This[\s\n]+IS[\s\n]+includes[\s\n]+security[\s\n]+measures[\s\n]+\(e\.g\.\,[\s\n]+authentication[\s\n]+and[\s\n]+access[\s\n]+controls\)[\s\n]+to[\s\n]+protect[\s\n]+USG[\s\n]+interests\-\-not[\s\n]+for[\s\n]+your[\s\n]+personal[\s\n]+benefit[\s\n]+or[\s\n]+privacy\.(?:[\n]+|(?:\\n)+)\-Notwithstanding[\s\n]+the[\s\n]+above\,[\s\n]+using[\s\n]+this[\s\n]+IS[\s\n]+does[\s\n]+not[\s\n]+constitute[\s\n]+consent[\s\n]+to[\s\n]+PM\,[\s\n]+LE[\s\n]+or[\s\n]+CI[\s\n]+investigative[\s\n]+searching[\s\n]+or[\s\n]+monitoring[\s\n]+of[\s\n]+the[\s\n]+content[\s\n]+of[\s\n]+privileged[\s\n]+communications\,[\s\n]+or[\s\n]+work[\s\n]+product\,[\s\n]+related[\s\n]+to[\s\n]+personal[\s\n]+representation[\s\n]+or[\s\n]+services[\s\n]+by[\s\n]+attorneys\,[\s\n]+psychotherapists\,[\s\n]+or[\s\n]+clergy\,[\s\n]+and[\s\n]+their[\s\n]+assistants\.[\s\n]+Such[\s\n]+communications[\s\n]+and[\s\n]+work[\s\n]+product[\s\n]+are[\s\n]+private[\s\n]+and[\s\n]+confidential\.[\s\n]+See[\s\n]+User[\s\n]+Agreement[\s\n]+for[\s\n]+details\.$ + ^I've[\s\n]+read[\s\n]+\&[\s\n]+consent[\s\n]+to[\s\n]+terms[\s\n]+in[\s\n]+IS[\s\n]+user[\s\n]+agreem't\.$ + ^Use[\s\n]+of[\s\n]+this[\s\n]+or[\s\n]+any[\s\n]+other[\s\n]+DoD[\s\n]+interest[\s\n]+computer[\s\n]+system[\s\n]+constitutes[\s\n]+consent[\s\n]+to[\s\n]+monitoring[\s\n]+at[\s\n]+all[\s\n]+times\.[\s\n]+This[\s\n]+is[\s\n]+a[\s\n]+DoD[\s\n]+interest[\s\n]+computer[\s\n]+system\.[\s\n]+All[\s\n]+DoD[\s\n]+interest[\s\n]+computer[\s\n]+systems[\s\n]+and[\s\n]+related[\s\n]+equipment[\s\n]+are[\s\n]+intended[\s\n]+for[\s\n]+the[\s\n]+communication\,[\s\n]+transmission\,[\s\n]+processing\,[\s\n]+and[\s\n]+storage[\s\n]+of[\s\n]+official[\s\n]+U\.S\.[\s\n]+Government[\s\n]+or[\s\n]+other[\s\n]+authorized[\s\n]+information[\s\n]+only\.[\s\n]+All[\s\n]+DoD[\s\n]+interest[\s\n]+computer[\s\n]+systems[\s\n]+are[\s\n]+subject[\s\n]+to[\s\n]+monitoring[\s\n]+at[\s\n]+all[\s\n]+times[\s\n]+to[\s\n]+ensure[\s\n]+proper[\s\n]+functioning[\s\n]+of[\s\n]+equipment[\s\n]+and[\s\n]+systems[\s\n]+including[\s\n]+security[\s\n]+devices[\s\n]+and[\s\n]+systems\,[\s\n]+to[\s\n]+prevent[\s\n]+unauthorized[\s\n]+use[\s\n]+and[\s\n]+violations[\s\n]+of[\s\n]+statutes[\s\n]+and[\s\n]+security[\s\n]+regulations\,[\s\n]+to[\s\n]+deter[\s\n]+criminal[\s\n]+activity\,[\s\n]+and[\s\n]+for[\s\n]+other[\s\n]+similar[\s\n]+purposes\.[\s\n]+Any[\s\n]+user[\s\n]+of[\s\n]+a[\s\n]+DoD[\s\n]+interest[\s\n]+computer[\s\n]+system[\s\n]+should[\s\n]+be[\s\n]+aware[\s\n]+that[\s\n]+any[\s\n]+information[\s\n]+placed[\s\n]+in[\s\n]+the[\s\n]+system[\s\n]+is[\s\n]+subject[\s\n]+to[\s\n]+monitoring[\s\n]+and[\s\n]+is[\s\n]+not[\s\n]+subject[\s\n]+to[\s\n]+any[\s\n]+expectation[\s\n]+of[\s\n]+privacy\.[\s\n]+If[\s\n]+monitoring[\s\n]+of[\s\n]+this[\s\n]+or[\s\n]+any[\s\n]+other[\s\n]+DoD[\s\n]+interest[\s\n]+computer[\s\n]+system[\s\n]+reveals[\s\n]+possible[\s\n]+evidence[\s\n]+of[\s\n]+violation[\s\n]+of[\s\n]+criminal[\s\n]+statutes\,[\s\n]+this[\s\n]+evidence[\s\n]+and[\s\n]+any[\s\n]+other[\s\n]+related[\s\n]+information\,[\s\n]+including[\s\n]+identification[\s\n]+information[\s\n]+about[\s\n]+the[\s\n]+user\,[\s\n]+may[\s\n]+be[\s\n]+provided[\s\n]+to[\s\n]+law[\s\n]+enforcement[\s\n]+officials\.[\s\n]+If[\s\n]+monitoring[\s\n]+of[\s\n]+this[\s\n]+or[\s\n]+any[\s\n]+other[\s\n]+DoD[\s\n]+interest[\s\n]+computer[\s\n]+systems[\s\n]+reveals[\s\n]+violations[\s\n]+of[\s\n]+security[\s\n]+regulations[\s\n]+or[\s\n]+unauthorized[\s\n]+use\,[\s\n]+employees[\s\n]+who[\s\n]+violate[\s\n]+security[\s\n]+regulations[\s\n]+or[\s\n]+make[\s\n]+unauthorized[\s\n]+use[\s\n]+of[\s\n]+DoD[\s\n]+interest[\s\n]+computer[\s\n]+systems[\s\n]+are[\s\n]+subject[\s\n]+to[\s\n]+appropriate[\s\n]+disciplinary[\s\n]+action\.[\s\n]+Use[\s\n]+of[\s\n]+this[\s\n]+or[\s\n]+any[\s\n]+other[\s\n]+DoD[\s\n]+interest[\s\n]+computer[\s\n]+system[\s\n]+constitutes[\s\n]+consent[\s\n]+to[\s\n]+monitoring[\s\n]+at[\s\n]+all[\s\n]+times\.$ + ^\-\-[\s\n]+WARNING[\s\n]+\-\-[\s\n]+This[\s\n]+system[\s\n]+is[\s\n]+for[\s\n]+the[\s\n]+use[\s\n]+of[\s\n]+authorized[\s\n]+users[\s\n]+only\.[\s\n]+Individuals[\s\n]+using[\s\n]+this[\s\n]+computer[\s\n]+system[\s\n]+without[\s\n]+authority[\s\n]+or[\s\n]+in[\s\n]+excess[\s\n]+of[\s\n]+their[\s\n]+authority[\s\n]+are[\s\n]+subject[\s\n]+to[\s\n]+having[\s\n]+all[\s\n]+their[\s\n]+activities[\s\n]+on[\s\n]+this[\s\n]+system[\s\n]+monitored[\s\n]+and[\s\n]+recorded[\s\n]+by[\s\n]+system[\s\n]+personnel\.[\s\n]+Anyone[\s\n]+using[\s\n]+this[\s\n]+system[\s\n]+expressly[\s\n]+consents[\s\n]+to[\s\n]+such[\s\n]+monitoring[\s\n]+and[\s\n]+is[\s\n]+advised[\s\n]+that[\s\n]+if[\s\n]+such[\s\n]+monitoring[\s\n]+reveals[\s\n]+possible[\s\n]+evidence[\s\n]+of[\s\n]+criminal[\s\n]+activity[\s\n]+system[\s\n]+personal[\s\n]+may[\s\n]+provide[\s\n]+the[\s\n]+evidence[\s\n]+of[\s\n]+such[\s\n]+monitoring[\s\n]+to[\s\n]+law[\s\n]+enforcement[\s\n]+officials\.$ + + + Configure A Banner Page For Each Website + Configure a login banner for each website when authentication is required for +user access. + WG265 + A consent banner will be in place to make prospective entrants aware that the +website they are about to enter is a DoD web site and their activity is subject +to monitoring. The document, DoDI 8500.01, establishes the policy on the use of +DoD information systems. It requires the use of a standard Notice and Consent +Banner and standard text to be included in user agreements. The requirement for +the banner is for websites with security and access controls. These are +restricted and not publicly accessible. If the website does not require +authentication/authorization for use, then the banner does not need to be +present. A manual check of the document root directory for a banner page file +(such as banner.html) or navigation to the website via a browser can be used to +confirm the information provided from interviewing the web staff. + + + + + + Each Web Content Directory Must Contain An index.html File + Every DocumentRoot that is configured should have an +index.html file that exists. Add an index.html file to every +configured DocumentRoot. + WG170 + The goal is to completely control the web users experience in navigating any +portion of the web document root directories. Ensuring all web content +directories have at least the equivalent of an index.html file is a significant +factor to accomplish this end. Also, enumeration techniques, such as URL +parameter manipulation, rely upon being able to obtain information about the web +server's directory structure by locating directories with default pages. This +practice helps ensure that the anonymous web user will not obtain directory +browsing information or an error message that reveals the server type and +version. + + + + + + Disable Web Content Symbolic Links + For each <Directory> instance, remove the following: +FollowSymLinks +If symbolic links are allowed, the following can be added for each +<Directory> instance: +Options SymLinksIfOwnerMatchDisable + WG360 + A symbolic link allows a file or a directory to be referenced using a symbolic +name raising a potential hazard if symbolic linkage is made to a sensitive area. +When web scripts are executed and symbolic links are allowed, the web user could +be allowed to access locations on the web server that are outside the scope of +the web document root or home directory. + + + + + + Encrypt All File Uploads + Use only secure encrypted logons and connections for uploading files to the web +site. + WG235 + Logging in to a web server via an unencrypted protocol or service, to upload +documents to the web site, is a risk if proper encryption is not utilized to +protect the data being transmitted. An encrypted protocol or service must be +used for remote access to web administration tasks. + + + + + + Remove .java And .jpp Files + .java and .jpp files should not exist and should be removed +from the web server. + WG490 + From the source code in a .java or a .jpp file, the Java compiler produces a +binary file with an extension of .class. The .java or .jpp file would, +therefore, reveal sensitive information regarding an application's logic and +permissions to resources on the server. By contrast, the .class file, because it +is intended to be machine independent, is referred to as bytecode. Bytecodes are +run by the Java Virtual Machine (JVM), or the Java Runtime Environment (JRE), +via a browser configured to permit Java code. + + + + + + The robots.txt Files Must Not Exist + Remove any robots.txt files that may exist with any web content. +Other methods must be employed if there is information on the web site that +needs protection from search engines and public view. Inspect all instances of +DocumentRoot and Alias and remove any robots.txt file. +$ sudo rm -f path/to/robots.txt + WG310 + Search engines are constantly at work on the Internet. Search engines are +augmented by agents, often referred to as spiders or bots, which endeavor to +capture and catalog web-site content. In turn, these search engines make the +content they obtain and catalog available to any public web user. + +To request +that a well behaved search engine not crawl and catalog a site, the web site may +contain a file called robots.txt. This file contains directories and files that +the web server SA desires not be crawled or cataloged, but this file can also be +used, by an attacker or poorly coded search engine, as a directory and file +index to a site. This information may be used to reduce an attacker's time +searching and traversing the web site to find files that might be relevant. If +information on the web site needs to be protected from search engines and public +view, other methods must be used. + + + + + + Ensure Web Content Located on Separate partition + The DocumentRoot directory is used for storing web content and data. +Ensure that the DocumentRoot directory exists on a separate logical +volume at installation time, or migrate it using LVM. + WG205 + Application partitioning enables an additional security measure by securing +user traffic under one security context, while managing system and application +files under another. Web content is can be to an anonymous web user. For such +an account to have access to system files of any type is a major security risk +that is avoidable and desirable. Failure to partition the system files from the +web site documents increases risk of attack via directory traversal, or impede +web site availability due to drive space exhaustion. + + + + + + + Use Denial-of-Service Protection Modules + Denial-of-service attacks are difficult to detect and prevent while maintaining +acceptable access to authorized users. However, some traffic-shaping +modules can be used to address the problem. Well-known DoS protection modules include: +mod_cband mod_bwshare mod_limitipconn mod_evasive +Denial-of-service prevention should be implemented for a web server if such a threat exists. +However, specific configuration details are very dependent on the environment and often best left +at the discretion of the administrator. + + + + + IMAP and POP3 Server + Dovecot provides IMAP and POP3 services. It is not +installed by default. The project page at + http://www.dovecot.org +contains more detailed information about Dovecot +configuration. + + Configure Dovecot if Necessary + If the system will operate as an IMAP or +POP3 server, the dovecot software should be configured securely by following +the recommendations below. + + Allow IMAP Clients to Access the Server + +The default iptables configuration does not allow inbound access to any services. +This modification will allow remote hosts to initiate connections to the IMAP daemon, +while keeping all other ports on the server in their default protected state. +To configure iptables to allow port 143 traffic, one must edit +/etc/sysconfig/iptables and +/etc/sysconfig/ip6tables (if IPv6 is in use). +Add the following line, ensuring that it appears before the final LOG and DROP lines for the INPUT chain: +-A INPUT -m state --state NEW -p tcp --dport 143 -j ACCEPT + + + Enable SSL Support + SSL should be used to encrypt network traffic between the +Dovecot server and its clients. Users must authenticate to the Dovecot +server in order to read their mail, and passwords should never be +transmitted in clear text. In addition, protecting mail as it is +downloaded is a privacy measure, and clients may use SSL certificates +to authenticate the server, preventing another system from impersonating +the server. + + Configure Dovecot to Use the SSL Certificate file + This option tells Dovecot where to find the mail server's SSL +Certificate. + +Edit /etc/dovecot/conf.d/10-ssl.conf and add or correct the +following line (note: the path below is the default path set by the +Dovecot installation. If you are using a different path, ensure you +reference the appropriate file): +ssl_cert = </etc/pki/dovecot/certs/dovecot.pem" + SSL certificates are used by the client to authenticate the identity of the +server, as well as to encrypt credentials and message traffic. Not using +SSL to encrypt mail server traffic could allow unauthorized access to +credentials and mail messages since they are sent in plain text over the +network. + + + Configure Dovecot to Use the SSL Key file + This option tells Dovecot where to find the mail server's SSL Key. + +Edit /etc/dovecot/conf.d/10-ssl.conf and add or correct the +following line (note: the path below is the default path set by the +Dovecot installation. If you are using a different path, ensure you +reference the appropriate file): +ssl_key = </etc/pki/dovecot/private/dovecot.pem + SSL certificates are used by the client to authenticate the identity of the +server, as well as to encrypt credentials and message traffic. Not using +SSL to encrypt mail server traffic could allow unauthorized access to +credentials and mail messages since they are sent in plain text over the +network. + + + Disable Plaintext Authentication + To prevent Dovecot from attempting plaintext authentication of clients, +edit /etc/dovecot/conf.d/10-auth.conf and add\or correct the +following line: +disable_plaintext_auth = yes + Using plain text authentication to the mail server could allow an attacker +access to credentials by monitoring network traffic. + + + + + + Enable the SSL flag in /etc/dovecot.conf + To allow clients to make encrypted connections the ssl +flag in Dovecot's configuration file needs to be set to yes. + +Edit /etc/dovecot/conf.d/10-ssl.conf and add or correct the following line: +ssl = yes + SSL encrypt network traffic between the Dovecot server and its clients +protecting user credentials, mail as it is downloaded, and clients may use +SSL certificates to authenticate the server, preventing another system from +impersonating the server. + + + + + + + Support Only the Necessary Protocols + Dovecot supports the IMAP and POP3 protocols, as well as +SSL-protected versions of those protocols. Configure the Dovecot server +to support only the protocols needed by your site. Edit /etc/dovecot/dovecot.conf. +Add or correct the following lines, replacing PROTOCOL with +only the subset of protocols (imap, imaps, +pop3, pop3s) required: +protocols = PROTOCOL +If possible, require SSL protection for all transactions. The SSL +protocol variants listen on alternate ports (995 instead of 110 for +pop3s, and 993 instead of 143 for imaps), and require SSL-aware clients. +An alternate approach is to listen on the standard port and require the +client to use the STARTTLS command before authenticating. + + + + Disable Dovecot + If the system does not need to operate as an IMAP or +POP3 server, the dovecot software should be disabled and removed. + + Uninstall dovecot Package + +The dovecot package can be removed with the following command: + +$ sudo yum erase dovecot + 2.2.11 + If there is no need to make the Dovecot software available, +removing it provides a safeguard against its activation. + CCE-85976-9 + +package --remove=dovecot + + include remove_dovecot + +class remove_dovecot { + package { 'dovecot': + ensure => 'purged', + } +} + + - name: Ensure dovecot is removed + package: + name: dovecot + state: absent + tags: + - CCE-85976-9 + - disable_strategy + - low_complexity + - low_disruption + - no_reboot_needed + - package_dovecot_removed + - unknown_severity + + +# CAUTION: This remediation script will remove dovecot +# from the system, and may remove any packages +# that depend on dovecot. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "dovecot" ; then + + yum remove -y "dovecot" + +fi + + + + + + + + + + Disable Dovecot Service + +The dovecot service can be disabled with the following command: +$ sudo systemctl mask --now dovecot.service + Running an IMAP or POP3 server provides a network-based +avenue of attack, and should be disabled if not needed. + + CCE-82760-0 + include disable_dovecot + +class disable_dovecot { + service {'dovecot': + enable => false, + ensure => 'stopped', + } +} + + - name: Disable service dovecot + block: + + - name: Disable service dovecot + systemd: + name: dovecot.service + enabled: 'no' + state: stopped + masked: 'yes' + ignore_errors: 'yes' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82760-0 + - disable_strategy + - low_complexity + - low_disruption + - no_reboot_needed + - service_dovecot_disabled + - unknown_severity + +- name: Unit Socket Exists - dovecot.socket + command: systemctl list-unit-files dovecot.socket + args: + warn: false + register: socket_file_exists + changed_when: false + ignore_errors: true + check_mode: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82760-0 + - disable_strategy + - low_complexity + - low_disruption + - no_reboot_needed + - service_dovecot_disabled + - unknown_severity + +- name: Disable socket dovecot + systemd: + name: dovecot.socket + enabled: 'no' + state: stopped + masked: 'yes' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"dovecot.socket" in socket_file_exists.stdout_lines[1]' + tags: + - CCE-82760-0 + - disable_strategy + - low_complexity + - low_disruption + - no_reboot_needed + - service_dovecot_disabled + - unknown_severity + + +[customizations.services] +disabled = ["dovecot"] + + apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + systemd: + units: + - name: dovecot.service + enabled: false + mask: true + - name: dovecot.socket + enabled: false + mask: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" stop 'dovecot.service' +"$SYSTEMCTL_EXEC" disable 'dovecot.service' +"$SYSTEMCTL_EXEC" mask 'dovecot.service' +# Disable socket activation if we have a unit file for it +if "$SYSTEMCTL_EXEC" list-unit-files | grep -q '^dovecot.socket'; then + "$SYSTEMCTL_EXEC" stop 'dovecot.socket' + "$SYSTEMCTL_EXEC" mask 'dovecot.socket' +fi +# The service may not be running because it has been started and failed, +# so let's reset the state so OVAL checks pass. +# Service should be 'inactive', not 'failed' after reboot though. +"$SYSTEMCTL_EXEC" reset-failed 'dovecot.service' || true + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + + Kerberos + The Kerberos protocol is used for authentication across +non-secure network. Authentication can happen between +various types of principals -- users, service, or hosts. +Their identity and encryption keys can be stored in keytab +files. + + + Remove the Kerberos Server Package + The krb5-server package should be removed if not in use. +Is this system the Kerberos server? If not, remove the package. +The krb5-server package can be removed with the following command: + +$ sudo yum erase krb5-server +The krb5-server RPM is not installed by default on a Red Hat Enterprise Linux 8 +system. It is needed only by the Kerberos servers, not by the +clients which use Kerberos for authentication. If the system is not +intended for use as a Kerberos Server it should be removed. + CCI-000803 + IA-7 + IA-7.1 + SRG-OS-000120-GPOS-00061 + RHEL-08-010163 + SV-237640r646890_rule + Unnecessary packages should not be installed to decrease the attack +surface of the system. While this software is clearly essential on an KDC +server, it is not necessary on typical desktop or workstation systems. + + + CCE-85887-8 + +package --remove=krb5-server + + include remove_krb5-server + +class remove_krb5-server { + package { 'krb5-server': + ensure => 'purged', + } +} + + - name: Ensure krb5-server is removed + package: + name: krb5-server + state: absent + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-85887-8 + - DISA-STIG-RHEL-08-010163 + - NIST-800-53-IA-7 + - NIST-800-53-IA-7.1 + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_krb5-server_removed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# CAUTION: This remediation script will remove krb5-server +# from the system, and may remove any packages +# that depend on krb5-server. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "krb5-server" ; then + + yum remove -y "krb5-server" + +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable Kerberos by removing host keytab + Kerberos is not an approved key distribution method for +Common Criteria. To prevent using Kerberos by system daemons, +remove the Kerberos keytab files, especially +/etc/krb5.keytab. + CCI-000803 + 0418 + 1055 + 1402 + FTP_ITC_EXT.1 + SRG-OS-000120-GPOS-00061 + RHEL-08-010161 + SV-230238r646862_rule + The key derivation function (KDF) in Kerberos is not FIPS compatible. + + + CCE-82175-1 + - name: Find keytab files + find: + paths: /etc/ + patterns: '*.keytab' + register: keytab_files + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82175-1 + - DISA-STIG-RHEL-08-010161 + - disable_strategy + - kerberos_disable_no_keytab + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Remove keytab files + file: + path: '{{ item.path }}' + state: absent + with_items: '{{ keytab_files.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82175-1 + - DISA-STIG-RHEL-08-010161 + - disable_strategy + - kerberos_disable_no_keytab + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +rm -f /etc/*.keytab + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + LDAP + LDAP is a popular directory service, that is, a +standardized way of looking up information from a central database. +Red Hat Enterprise Linux 8 includes software that enables a system to act as both +an LDAP client and server. + + Configure OpenLDAP Clients + This section provides information on which security settings are +important to configure in OpenLDAP clients by manually editing the appropriate +configuration files. Red Hat Enterprise Linux 8 provides an automated configuration tool called +authconfig and a graphical wrapper for authconfig called +system-config-authentication. However, these tools do not provide as +much control over configuration as manual editing of configuration files. The +authconfig tools do not allow you to specify locations of SSL certificate +files, which is useful when trying to use SSL cleanly across several protocols. +Installation and configuration of OpenLDAP on Red Hat Enterprise Linux 8 is available at + Before configuring any system to be an +LDAP client, ensure that a working LDAP server is present on the +network. + + Ensure LDAP client is not installed + The Lightweight Directory Access Protocol (LDAP) is a service that provides +a method for looking up information from a central database. +The openldap-clients package can be removed with the following command: + +$ sudo yum erase openldap-clients + 2.3.5 + If the system does not need to act as an LDAP client, it is recommended that the software is removed to reduce the potential attack surface. + CCE-82885-5 + +package --remove=openldap-clients + + include remove_openldap-clients + +class remove_openldap-clients { + package { 'openldap-clients': + ensure => 'purged', + } +} + + - name: Ensure openldap-clients is removed + package: + name: openldap-clients + state: absent + tags: + - CCE-82885-5 + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - package_openldap-clients_removed + + +# CAUTION: This remediation script will remove openldap-clients +# from the system, and may remove any packages +# that depend on openldap-clients. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "openldap-clients" ; then + + yum remove -y "openldap-clients" + +fi + + + + + + + + + + Enable the LDAP Client For Use in Authconfig + To determine if LDAP is being used for authentication, use the following +command: +$ sudo grep -i useldapauth /etc/sysconfig/authconfig + +If USELDAPAUTH=yes, then LDAP is being used. If not, set USELDAPAUTH +to yes. + 11 + 12 + 14 + 15 + 3 + 8 + 9 + APO13.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.04 + DSS05.02 + DSS05.03 + DSS05.05 + DSS06.06 + CCI-001453 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + 0418 + 1055 + 1402 + A.11.2.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.6.2.1 + A.6.2.2 + A.9.1.2 + AC-17(a) + CM-6(a) + PR.AC-3 + PR.IP-1 + PR.PT-3 + PR.PT-4 + SRG-OS-000250-GPOS-00093 + Without cryptographic integrity protections, information can be +altered by unauthorized users without detection. The ssl directive specifies +whether to use TLS or not. If not specified it will default to no. +It should be set to start_tls rather than doing LDAP over SSL. + CCE-82418-5 + + + + + + + + + Configure LDAP Client to Use TLS For All Transactions + This check verifies cryptography has been implemented +to protect the integrity of remote LDAP authentication sessions. + +To determine if LDAP is being used for authentication, use the following +command: +$ sudo grep -i useldapauth /etc/sysconfig/authconfig + +If USELDAPAUTH=yes, then LDAP is being used. To check if LDAP is +configured to use TLS, use the following command: +$ sudo grep -i ssl /etc/pam_ldap.conf + 11 + 12 + 14 + 15 + 3 + 8 + 9 + APO13.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.04 + DSS05.02 + DSS05.03 + DSS05.05 + DSS06.06 + CCI-001453 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.6.2.1 + A.6.2.2 + A.9.1.2 + AC-17(a) + AC-17(2) + CM-6(a) + SC-12(a) + SC-12(b) + PR.AC-3 + PR.IP-1 + PR.PT-3 + PR.PT-4 + SRG-OS-000250-GPOS-00093 + Without cryptographic integrity protections, information can be altered by +unauthorized users without detection. The ssl directive specifies whether +to use TLS or not. If not specified it will default to no. It should be set +to start_tls rather than doing LDAP over SSL. + + CCE-82416-9 + # Remediation is applicable only in certain platforms +if rpm --quiet -q nss-pam-ldapd; then + +# Use LDAP for authentication +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysconfig/authconfig"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^USELDAPAUTH") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s=%s" "$stripped_key" "yes" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^USELDAPAUTH\\>" "/etc/sysconfig/authconfig"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^USELDAPAUTH\\>.*/$escaped_formatted_output/gi" "/etc/sysconfig/authconfig" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-82416-9" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysconfig/authconfig" >> "/etc/sysconfig/authconfig" + printf '%s\n' "$formatted_output" >> "/etc/sysconfig/authconfig" +fi + +# Configure client to use TLS for all authentications +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/nslcd.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^ssl") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s %s" "$stripped_key" "start_tls" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^ssl\\>" "/etc/nslcd.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^ssl\\>.*/$escaped_formatted_output/gi" "/etc/nslcd.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-82416-9" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/nslcd.conf" >> "/etc/nslcd.conf" + printf '%s\n' "$formatted_output" >> "/etc/nslcd.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure Certificate Directives for LDAP Use of TLS + Ensure a copy of a trusted CA certificate has been placed in the file +/etc/pki/tls/CA/cacert.pem. Configure LDAP to enforce TLS use and +to trust certificates signed by that CA. First, edit the file +/etc/nslcd.conf, and add or correct either of the following lines: +tls_cacertdir /etc/pki/tls/CA or +tls_cacertfile /etc/pki/tls/CA/cacert.pem +Then review the LDAP server and ensure TLS has been configured. + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + CCI-000776 + CCI-000778 + CCI-001453 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-6(a) + PR.IP-1 + PR.PT-3 + The tls_cacertdir or tls_cacertfile directives are required when +tls_checkpeer is configured (which is the default for openldap versions 2.1 and +up). These directives define the path to the trust certificates signed by the +site CA. + CCE-82417-7 + + + + + + + + + + Configure OpenLDAP Server + This section details some security-relevant settings +for an OpenLDAP server. + + Uninstall openldap-servers Package + +The openldap-servers package is not installed by default on a Red Hat Enterprise Linux 8 + +system. It is needed only by the OpenLDAP server, not by the +clients which use LDAP for authentication. If the system is not +intended for use as an LDAP Server it should be removed. + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + CCI-000366 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + PR.PT-3 + Unnecessary packages should not be installed to decrease the attack +surface of the system. While this software is clearly essential on an LDAP +server, it is not necessary on typical desktop or workstation systems. + CCE-82415-1 + +package --remove=openldap-servers + + include remove_openldap-servers + +class remove_openldap-servers { + package { 'openldap-servers': + ensure => 'purged', + } +} + + - name: Ensure openldap-servers is removed + package: + name: openldap-servers + state: absent + tags: + - CCE-82415-1 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - package_openldap-servers_removed + + +# CAUTION: This remediation script will remove openldap-servers +# from the system, and may remove any packages +# that depend on openldap-servers. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "openldap-servers" ; then + + yum remove -y "openldap-servers" + +fi + + + + + + + + + + Disable LDAP Server (slapd) + The Lightweight Directory Access Protocol (LDAP) is a service that provides a method for looking up information from a central database. + If the system will not need to act as an LDAP server, it is recommended that the software be +disabled to reduce the potential attack surface. + + CCE-87262-2 + include disable_slapd + +class disable_slapd { + service {'slapd': + enable => false, + ensure => 'stopped', + } +} + + - name: Disable service slapd + block: + + - name: Disable service slapd + systemd: + name: slapd.service + enabled: 'no' + state: stopped + masked: 'yes' + ignore_errors: 'yes' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-87262-2 + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_slapd_disabled + +- name: Unit Socket Exists - slapd.socket + command: systemctl list-unit-files slapd.socket + args: + warn: false + register: socket_file_exists + changed_when: false + ignore_errors: true + check_mode: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-87262-2 + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_slapd_disabled + +- name: Disable socket slapd + systemd: + name: slapd.socket + enabled: 'no' + state: stopped + masked: 'yes' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"slapd.socket" in socket_file_exists.stdout_lines[1]' + tags: + - CCE-87262-2 + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_slapd_disabled + + +[customizations.services] +disabled = ["slapd"] + + apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + systemd: + units: + - name: slapd.service + enabled: false + mask: true + - name: slapd.socket + enabled: false + mask: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" stop 'slapd.service' +"$SYSTEMCTL_EXEC" disable 'slapd.service' +"$SYSTEMCTL_EXEC" mask 'slapd.service' +# Disable socket activation if we have a unit file for it +if "$SYSTEMCTL_EXEC" list-unit-files | grep -q '^slapd.socket'; then + "$SYSTEMCTL_EXEC" stop 'slapd.socket' + "$SYSTEMCTL_EXEC" mask 'slapd.socket' +fi +# The service may not be running because it has been started and failed, +# so let's reset the state so OVAL checks pass. +# Service should be 'inactive', not 'failed' after reboot though. +"$SYSTEMCTL_EXEC" reset-failed 'slapd.service' || true + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Install and Protect LDAP Certificate Files + Create the PKI directory for LDAP certificates if it does not already exist: +$ sudo mkdir /etc/pki/tls/ldap +$ sudo chown root:root /etc/pki/tls/ldap +$ sudo chmod 755 /etc/pki/tls/ldap +Using removable media or some other secure transmission format, install the certificate files +onto the LDAP server: +/etc/pki/tls/ldap/serverkey.pem: the private key ldapserverkey.pem/etc/pki/tls/ldap/servercert.pem: the certificate file ldapservercert.pem +Verify the ownership and permissions of these files: +$ sudo chown root:ldap /etc/pki/tls/ldap/serverkey.pem +$ sudo chown root:ldap /etc/pki/tls/ldap/servercert.pem +$ sudo chmod 640 /etc/pki/tls/ldap/serverkey.pem +$ sudo chmod 640 /etc/pki/tls/ldap/servercert.pem +Verify that the CA's public certificate file has been installed as +/etc/pki/tls/CA/cacert.pem, and has the correct permissions: +$ sudo mkdir /etc/pki/tls/CA +$ sudo chown root:root /etc/pki/tls/CA/cacert.pem +$ sudo chmod 644 /etc/pki/tls/CA/cacert.pem + +As a result of these steps, the LDAP server will have access to its own private +certificate and the key with which that certificate is encrypted, and to the +public certificate file belonging to the CA. Note that it would be possible for +the key to be protected further, so that processes running as ldap could not +read it. If this were done, the LDAP server process would need to be restarted +manually whenever the server rebooted. + + + + + Mail Server Software + Mail servers are used to send and receive email over the network. +Mail is a very common service, and Mail Transfer Agents (MTAs) are obvious +targets of network attack. +Ensure that systems are not running MTAs unnecessarily, +and configure needed MTAs as defensively as possible. + +Very few systems at any site should be configured to directly receive email over the +network. Users should instead use mail client programs to retrieve email +from a central server that supports protocols such as IMAP or POP3. +However, it is normal for most systems to be independently capable of sending email, +for instance so that cron jobs can report output to an administrator. +Most MTAs, including Postfix, support a submission-only mode in which mail can be sent from +the local system to a central site MTA (or directly delivered to a local account), +but the system still cannot receive mail directly over a network. + +The alternatives program in Red Hat Enterprise Linux 8 permits selection of other mail server software +(such as Sendmail), but Postfix is the default and is preferred. +Postfix was coded with security in mind and can also be more effectively contained by +SELinux as its modular design has resulted in separate processes performing specific actions. +More information is available on its website, + http://www.postfix.org. + + + The Postfix package is installed + A mail server is required for sending emails. +The postfix package can be installed with the following command: + +$ sudo yum install postfix + SRG-OS-000046-GPOS-00022 + RHEL-08-030030 + SV-230389r627750_rule + Emails can be used to notify designated personnel about important +system events such as failures or warnings. + CCE-85983-5 + +package --add=postfix + + include install_postfix + +class install_postfix { + package { 'postfix': + ensure => 'installed', + } +} + + - name: Ensure postfix is installed + package: + name: postfix + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-85983-5 + - DISA-STIG-RHEL-08-030030 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_postfix_installed + + +[[packages]] +name = "postfix" +version = "*" + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if ! rpm -q --quiet "postfix" ; then + yum install -y "postfix" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Uninstall Sendmail Package + Sendmail is not the default mail transfer agent and is +not installed by default. +The sendmail package can be removed with the following command: + +$ sudo yum erase sendmail + BP28(R1) + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + CCI-000381 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + PR.PT-3 + SRG-OS-000480-GPOS-00227 + SRG-OS-000095-GPOS-00049 + RHEL-08-040002 + SV-230489r627750_rule + The sendmail software was not developed with security in mind and +its design prevents it from being effectively contained by SELinux. Postfix +should be used instead. + CCE-81039-0 + +package --remove=sendmail + + include remove_sendmail + +class remove_sendmail { + package { 'sendmail': + ensure => 'purged', + } +} + + - name: Ensure sendmail is removed + package: + name: sendmail + state: absent + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-81039-0 + - DISA-STIG-RHEL-08-040002 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_sendmail_removed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# CAUTION: This remediation script will remove sendmail +# from the system, and may remove any packages +# that depend on sendmail. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "sendmail" ; then + + yum remove -y "sendmail" + +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Enable Postfix Service + The Postfix mail transfer agent is used for local mail delivery +within the system. The default configuration only listens for connections to +the default SMTP port (port 25) on the loopback interface (127.0.0.1). It is +recommended to leave this service enabled for local mail delivery. + +The postfix service can be enabled with the following command: +$ sudo systemctl enable postfix.service + Local mail delivery is essential to some system maintenance and +notification tasks. + include enable_postfix + +class enable_postfix { + service {'postfix': + enable => true, + ensure => 'running', + } +} + + - name: Enable service postfix + block: + + - name: Gather the package facts + package_facts: + manager: auto + + - name: Enable service postfix + service: + name: postfix + enabled: 'yes' + state: started + masked: 'no' + when: + - '"postfix" in ansible_facts.packages' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - no_reboot_needed + - service_postfix_enabled + - unknown_severity + + +[customizations.services] +enabled = ["postfix"] + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" unmask 'postfix.service' +"$SYSTEMCTL_EXEC" start 'postfix.service' +"$SYSTEMCTL_EXEC" enable 'postfix.service' + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure SMTP For Mail Clients + This section discusses settings for Postfix in a submission-only +e-mail configuration. + + Postfix Network Interfaces + The setting for inet_interfaces in /etc/postfix/main.cf + loopback-only + loopback-only + localhost + + + Postfix relayhost + Specify the host all outbound email should be routed into. + smtp.$mydomain + + + Postfix Root Mail Alias + Specify an email address (string) for a root mail alias. + system.administrator@mail.mil + system.administrator@mail.mil + + + Configure System to Forward All Mail For The Root Account + Make sure that mails delivered to root user are forwarded to a monitored +email address. Make sure that the address + is a valid email address +reachable from the system in question. Use the following command to +configure the alias: +$ sudo echo "root: " >> /etc/aliases +$ sudo newaliases + BP28(R49) + CCI-000139 + CCI-000366 + CM-6(a) + SRG-OS-000046-GPOS-00022 + A number of system services utilize email messages sent to the root user to +notify system administrators of active or impending issues. These messages must +be forwarded to at least one monitored email address. + CCE-82381-5 + - name: XCCDF Value var_postfix_root_mail_alias # promote to variable + set_fact: + var_postfix_root_mail_alias: !!str + tags: + - always + +- name: Make sure that "/etc/aliases" has a defined value for root + lineinfile: + path: /etc/aliases + line: 'root: {{ var_postfix_root_mail_alias }}' + regexp: ^(?:[rR][oO][oO][tT]|"[rR][oO][oO][tT]")\s*:\s*(.+)$ + create: true + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82381-5 + - NIST-800-53-CM-6(a) + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - postfix_client_configure_mail_alias + +- name: Check if newaliases command is available + ansible.builtin.stat: + path: /usr/bin/newaliases + register: result_newaliases_present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82381-5 + - NIST-800-53-CM-6(a) + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - postfix_client_configure_mail_alias + +- name: Update postfix aliases + ansible.builtin.command: + cmd: newaliases + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - result_newaliases_present.stat.exists + tags: + - CCE-82381-5 + - NIST-800-53-CM-6(a) + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - postfix_client_configure_mail_alias + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_postfix_root_mail_alias='' + + +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/aliases"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^root") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s: %s" "$stripped_key" "$var_postfix_root_mail_alias" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^root\\>" "/etc/aliases"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^root\\>.*/$escaped_formatted_output/gi" "/etc/aliases" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-82381-5" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/aliases" >> "/etc/aliases" + printf '%s\n' "$formatted_output" >> "/etc/aliases" +fi + +if [ -f /usr/bin/newaliases ]; then + newaliases +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Configure System to Forward All Mail From Postmaster to The Root Account + Verify the administrators are notified in the event of an audit processing failure. +Check that the "/etc/aliases" file has a defined value for "root". +$ sudo grep "postmaster:\s*root$" /etc/aliases + +postmaster: root + CCI-000139 + AU-5(a) + AU-5.1(ii) + SRG-OS-000046-GPOS-00022 + RHEL-08-030030 + SV-230389r627750_rule + It is critical for the appropriate personnel to be aware if a system is at risk of failing to +process audit logs as required. Without this notification, the security personnel may be +unaware of an impending failure of the audit capability, and system operation may be adversely +affected. + +Audit processing failures include software/hardware errors, failures in the audit capturing +mechanisms, and audit storage capacity being reached or exceeded. + CCE-89063-2 + - name: Configure System to Forward All Mail From Postmaster to The Root Account + block: + + - name: Check for duplicate values + lineinfile: + path: /etc/aliases + create: false + regexp: ^\s*postmaster\s*:\s* + state: absent + check_mode: true + changed_when: false + register: dupes + + - name: Deduplicate values from /etc/aliases + lineinfile: + path: /etc/aliases + create: false + regexp: ^\s*postmaster\s*:\s* + state: absent + when: dupes.found is defined and dupes.found > 1 + + - name: Insert correct line to /etc/aliases + lineinfile: + path: /etc/aliases + create: true + regexp: ^\s*postmaster\s*:\s* + line: 'postmaster: root' + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-89063-2 + - DISA-STIG-RHEL-08-030030 + - NIST-800-53-AU-5(a) + - NIST-800-53-AU-5.1(ii) + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - postfix_client_configure_mail_alias_postmaster + +- name: Check if newaliases command is available + ansible.builtin.stat: + path: /usr/bin/newaliases + register: result_newaliases_present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-89063-2 + - DISA-STIG-RHEL-08-030030 + - NIST-800-53-AU-5(a) + - NIST-800-53-AU-5.1(ii) + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - postfix_client_configure_mail_alias_postmaster + +- name: Update postfix aliases + ansible.builtin.command: + cmd: newaliases + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - result_newaliases_present.stat.exists + tags: + - CCE-89063-2 + - DISA-STIG-RHEL-08-030030 + - NIST-800-53-AU-5(a) + - NIST-800-53-AU-5.1(ii) + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - postfix_client_configure_mail_alias_postmaster + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if [ -e "/etc/aliases" ] ; then + + LC_ALL=C sed -i "/^\s*postmaster\s*:\s*/Id" "/etc/aliases" +else + touch "/etc/aliases" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/aliases" + +cp "/etc/aliases" "/etc/aliases.bak" +# Insert at the end of the file +printf '%s\n' "postmaster: root" >> "/etc/aliases" +# Clean up after ourselves. +rm "/etc/aliases.bak" + +if [ -f /usr/bin/newaliases ]; then + newaliases +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure System to Forward All Mail through a specific host + Set up a relay host that will act as a gateway for all outbound email. +Edit the file /etc/postfix/main.cf to ensure that only the following +relayhost line appears: +relayhost = + A central outbound email location ensures messages sent from any network host +can be audited for potential unexpected content. Tooling on the central server +may help prevent spam or viruses from being delivered. + + + + + + + Disable Postfix Network Listening + Edit the file /etc/postfix/main.cf to ensure that only the following +inet_interfaces line appears: +inet_interfaces = + BP28(R48) + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + CCI-000382 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + PR.PT-3 + 2.2.17 + This ensures postfix accepts mail messages +(such as cron job reports) from the local system only, +and not from the network, which protects it from network attack. + + CCE-82174-4 + - name: XCCDF Value var_postfix_inet_interfaces # promote to variable + set_fact: + var_postfix_inet_interfaces: !!str + tags: + - always + +- name: Gather list of packages + package_facts: + manager: auto + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82174-4 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - postfix_network_listening_disabled + - restrict_strategy + +- name: Make changes to Postfix configuration file + lineinfile: + path: /etc/postfix/main.cf + create: false + regexp: ^inet_interfaces\s*=\s.* + line: inet_interfaces = {{ var_postfix_inet_interfaces }} + state: present + insertafter: ^inet_interfaces\s*=\s.* + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"postfix" in ansible_facts.packages' + - '"postfix" in ansible_facts.packages' + tags: + - CCE-82174-4 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - postfix_network_listening_disabled + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && { rpm --quiet -q postfix; }; then + +var_postfix_inet_interfaces='' + + +if [ -e "/etc/postfix/main.cf" ] ; then + + LC_ALL=C sed -i "/^\s*inet_interfaces\s\+=\s\+/Id" "/etc/postfix/main.cf" +else + touch "/etc/postfix/main.cf" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/postfix/main.cf" + +cp "/etc/postfix/main.cf" "/etc/postfix/main.cf.bak" +# Insert at the end of the file +printf '%s\n' "inet_interfaces=$var_postfix_inet_interfaces" >> "/etc/postfix/main.cf" +# Clean up after ourselves. +rm "/etc/postfix/main.cf.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + + Configure Operating System to Protect Mail Server + The guidance in this section is appropriate for any host which is +operating as a site MTA, whether the mail server runs using Sendmail, Postfix, +or some other software. + + + Configure SSL Certificates for Use with SMTP AUTH + If SMTP AUTH is to be used, the use of SSL to protect credentials in transit is strongly recommended. +There are also configurations for which it may be desirable to encrypt all mail in transit from one MTA to another, +though such configurations are beyond the scope of this guide. In either event, the steps for creating and installing +an SSL certificate are independent of the MTA in use, and are described here. + + Ensure Security of Postfix SSL Certificate + Create the PKI directory for mail certificates, if it does not already exist: +$ sudo mkdir /etc/pki/tls/mail +$ sudo chown root:root /etc/pki/tls/mail +$ sudo chmod 755 /etc/pki/tls/mail +Using removable media or some other secure transmission format, install the files generated in the previous +step onto the mail server: +/etc/pki/tls/mail/serverkey.pem: the private key mailserverkey.pem +/etc/pki/tls/mail/servercert.pem: the certificate file mailservercert.pem +Verify the ownership and permissions of these files: +$ sudo chown root:root /etc/pki/tls/mail/serverkey.pem +$ sudo chown root:root /etc/pki/tls/mail/servercert.pem +$ sudo chmod 600 /etc/pki/tls/mail/serverkey.pem +$ sudo chmod 644 /etc/pki/tls/mail/servercert.pem +Verify that the CA's public certificate file has been installed as /etc/pki/tls/CA/cacert.pem, and has the +correct permissions: +$ sudo chown root:root /etc/pki/tls/CA/cacert.pem +$ sudo chmod 644 /etc/pki/tls/CA/cacert.pem + + + + Configure Postfix if Necessary + Postfix stores its configuration files in the directory +/etc/postfix by default. The primary configuration file is +/etc/postfix/main.cf. + + Configure SMTP Greeting Banner + Edit /etc/postfix/main.cf, and add or correct the +following line, substituting some other wording for the banner information if +you prefer: +smtpd_banner = $myhostname ESMTP + 1 + 14 + 15 + 16 + 3 + 5 + 6 + 7 + APO11.04 + BAI03.05 + DSS05.04 + DSS05.07 + MEA02.01 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 6.2 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + AC-8(a) + AC-8(c) + DE.CM-3 + PR.PT-1 + The default greeting banner discloses that the listening mail +process is Postfix. When remote mail senders connect to the MTA on port 25, +they are greeted by an initial banner as part of the SMTP dialogue. This banner +is necessary, but it frequently gives away too much information, including the +MTA software which is in use, and sometimes also its version number. Remote +mail senders do not need this information in order to send mail, so the banner +should be changed to reveal only the hostname (which is already known and may +be useful) and the word ESMTP, to indicate that the modern SMTP protocol +variant is supported. + CCE-82379-9 + + + + + + Configure Postfix Resource Usage to Limit Denial of Service Attacks + Edit /etc/postfix/main.cf. Edit the following lines to +configure the amount of system resources Postfix can consume: +default_process_limit = 100 +smtpd_client_connection_count_limit = 10 +smtpd_client_connection_rate_limit = 30 +queue_minfree = 20971520 +header_size_limit = 51200 +message_size_limit = 10485760 +smtpd_recipient_limit = 100 +The values here are examples. + Note: The values given here are examples, and may +need to be modified for any particular site. By default, the Postfix anvil +process gathers mail receipt statistics. To get information about about what +connection rates are typical at your site, look in /var/log/maillog +for lines with the daemon name postfix/anvil. + + + Control Mail Relaying + Postfix's mail relay controls are implemented with the help of the +smtpd recipient restrictions option, which controls the restrictions placed on +the SMTP dialogue once the sender and recipient envelope addresses are known. +The guidance in the following sections should be applied to all systems. If +there are systems which must be allowed to relay mail, but which cannot be +trusted to relay unconditionally, configure SMTP AUTH with SSL support. + + Prevent Unrestricted Mail Relaying + Modify the /etc/postfix/main.cf file to restrict client connections +to the local network with the following command: +$ sudo postconf -e 'smtpd_client_restrictions = permit_mynetworks,reject' + CCI-000366 + SRG-OS-000480-GPOS-00227 + RHEL-08-040290 + SV-230550r627750_rule + If unrestricted mail relaying is permitted, unauthorized senders could use this +host as a mail relay for the purpose of sending spam or other unauthorized +activity. + CCE-84054-6 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-84054-6 + - DISA-STIG-RHEL-08-040290 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - postfix_prevent_unrestricted_relay + - restrict_strategy + +- name: Prevent Unrestricted Mail Relaying + block: + + - name: Check for duplicate values + lineinfile: + path: /etc/postfix/main.cf + create: false + regexp: ^[ \t]*smtpd_client_restrictions\s*=\s* + state: absent + check_mode: true + changed_when: false + register: dupes + + - name: Deduplicate values from /etc/postfix/main.cf + lineinfile: + path: /etc/postfix/main.cf + create: false + regexp: ^[ \t]*smtpd_client_restrictions\s*=\s* + state: absent + when: dupes.found is defined and dupes.found > 1 + + - name: Insert correct line to /etc/postfix/main.cf + lineinfile: + path: /etc/postfix/main.cf + create: true + regexp: ^[ \t]*smtpd_client_restrictions\s*=\s* + line: smtpd_client_restrictions = permit_mynetworks,reject + state: present + when: + - '"postfix" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84054-6 + - DISA-STIG-RHEL-08-040290 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - postfix_prevent_unrestricted_relay + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q postfix; then + +if ! grep -q ^smtpd_client_restrictions /etc/postfix/main.cf; then + echo "smtpd_client_restrictions = permit_mynetworks,reject" >> /etc/postfix/main.cf +else + sed -i "s/^smtpd_client_restrictions.*/smtpd_client_restrictions = permit_mynetworks,reject/g" /etc/postfix/main.cf +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Enact SMTP Recipient Restrictions + To configure Postfix to restrict addresses to which it +will send mail, see: + + http://www.postfix.org/SMTPD_ACCESS_README.html#danger + +The full contents of smtpd_recipient_restrictions will +vary by site, since this is a common place to put spam restrictions and other +site-specific options. The permit_mynetworks option allows all mail to +be relayed from the systems in mynetworks. Then, the +reject_unauth_destination option denies all mail whose destination +address is not local, preventing any other systems from relaying. These two +options should always appear in this order, and should usually follow one +another immediately unless SMTP AUTH is used. + + + Enact SMTP Relay Restrictions + To configure Postfix to restrict addresses to which it +will send mail, see: + + http://www.postfix.org/SMTPD_ACCESS_README.html#danger + +The full contents of smtpd_recipient_restrictions will +vary by site, since this is a common place to put spam restrictions and other +site-specific options. The permit_mynetworks option allows all mail to +be relayed from the systems in mynetworks. Then, the +reject_unauth_destination option denies all mail whose destination +address is not local, preventing any other systems from relaying. These two +options should always appear in this order, and should usually follow one +another immediately unless SMTP AUTH is used. + + + Use TLS for SMTP AUTH + Postfix provides options to use TLS for certificate-based +authentication and encrypted sessions. An encrypted session protects the +information that is transmitted with SMTP mail or with SASL authentication. +To configure Postfix to protect all SMTP AUTH transactions +using TLS, see + http://www.postfix.org/TLS_README.html. + + + Configure Trusted Networks and Hosts + Edit /etc/postfix/main.cf, and configure the contents of +the mynetworks variable in one of the following ways: +If any system in the subnet containing the MTA may be trusted to relay +messages, add or correct the following line: +mynetworks_style = subnet +This is also the default setting, and is in effect if all +my_networks_style directives are commented.If only the MTA host itself is trusted to relay messages, add or correct +the following line: +mynetworks_style = hostIf the set of systems which can relay is more complicated, manually +specify an entry for each netblock or IP address which is trusted to relay by +setting the mynetworks variable directly: +mynetworks = 10.0.0.0/16, 192.168.1.0/24, 127.0.0.1 + + + Require SMTP AUTH Before Relaying from Untrusted Clients + SMTP authentication allows remote clients to relay mail safely by +requiring them to authenticate before submitting mail. Postfix's SMTP AUTH uses +an authentication library called SASL, which is not part of Postfix itself. To +enable the use of SASL authentication, see + + http://www.postfix.org/SASL_README.html + + + + + + + NFS and RPC + The Network File System is a popular distributed filesystem for +the Unix environment, and is very widely deployed. This section discusses the +circumstances under which it is possible to disable NFS and its dependencies, +and then details steps which should be taken to secure +NFS's configuration. This section is relevant to systems operating as NFS +clients, as well as to those operating as NFS servers. + + Uninstall nfs-utils Package + The nfs-utils package can be removed with the following command: + +$ sudo yum erase nfs-utils + SRG-OS-000095-GPOS-00049 + 2.2.18 + nfs-utils provides a daemon for the kernel NFS server and related tools. This +package also contains the showmount program. showmount queries the mount +daemon on a remote host for information about the Network File System (NFS) server on the +remote host. For example, showmount can display the clients which are mounted on +that host. + CCE-82932-5 + +package --remove=nfs-utils + + include remove_nfs-utils + +class remove_nfs-utils { + package { 'nfs-utils': + ensure => 'purged', + } +} + + - name: Ensure nfs-utils is removed + package: + name: nfs-utils + state: absent + tags: + - CCE-82932-5 + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - package_nfs-utils_removed + + +# CAUTION: This remediation script will remove nfs-utils +# from the system, and may remove any packages +# that depend on nfs-utils. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "nfs-utils" ; then + + yum remove -y "nfs-utils" + +fi + + + + + + + + + + Disable All NFS Services if Possible + If there is not a reason for the system to operate as either an +NFS client or an NFS server, follow all instructions in this section to disable +subsystems required by NFS. + The steps in this section will prevent a system +from operating as either an NFS client or an NFS server. Only perform these +steps on systems which do not need NFS at all. + + + Disable netfs if Possible + To determine if any network filesystems handled by netfs are +currently mounted on the system execute the following command: +$ mount -t nfs,nfs4,smbfs,cifs,ncpfs +If the command did not return any output then disable netfs. + + Disable Network File Systems (netfs) + The netfs script manages the boot-time mounting of several types +of networked filesystems, of which NFS and Samba are the most common. If these +filesystem types are not in use, the script can be disabled, protecting the +system somewhat against accidental or malicious changes to /etc/fstab +and against flaws in the netfs script itself. + +The netfs service can be disabled with the following command: +$ sudo systemctl mask --now netfs.service + + include disable_netfs + +class disable_netfs { + service {'netfs': + enable => false, + ensure => 'stopped', + } +} + + - name: Disable service netfs + block: + + - name: Disable service netfs + systemd: + name: netfs.service + enabled: 'no' + state: stopped + masked: 'yes' + ignore_errors: 'yes' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - disable_strategy + - low_complexity + - low_disruption + - no_reboot_needed + - service_netfs_disabled + - unknown_severity + +- name: Unit Socket Exists - netfs.socket + command: systemctl list-unit-files netfs.socket + args: + warn: false + register: socket_file_exists + changed_when: false + ignore_errors: true + check_mode: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - disable_strategy + - low_complexity + - low_disruption + - no_reboot_needed + - service_netfs_disabled + - unknown_severity + +- name: Disable socket netfs + systemd: + name: netfs.socket + enabled: 'no' + state: stopped + masked: 'yes' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"netfs.socket" in socket_file_exists.stdout_lines[1]' + tags: + - disable_strategy + - low_complexity + - low_disruption + - no_reboot_needed + - service_netfs_disabled + - unknown_severity + + +[customizations.services] +disabled = ["netfs"] + + apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + systemd: + units: + - name: netfs.service + enabled: false + mask: true + - name: netfs.socket + enabled: false + mask: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" stop 'netfs.service' +"$SYSTEMCTL_EXEC" disable 'netfs.service' +"$SYSTEMCTL_EXEC" mask 'netfs.service' +# Disable socket activation if we have a unit file for it +if "$SYSTEMCTL_EXEC" list-unit-files | grep -q '^netfs.socket'; then + "$SYSTEMCTL_EXEC" stop 'netfs.socket' + "$SYSTEMCTL_EXEC" mask 'netfs.socket' +fi +# The service may not be running because it has been started and failed, +# so let's reset the state so OVAL checks pass. +# Service should be 'inactive', not 'failed' after reboot though. +"$SYSTEMCTL_EXEC" reset-failed 'netfs.service' || true + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + Disable Services Used Only by NFS + If NFS is not needed, disable the NFS client daemons nfslock, rpcgssd, and rpcidmapd. + +All of these daemons run with elevated privileges, and many listen for network +connections. If they are not needed, they should be disabled to improve system +security posture. + + + Disable Network File System Lock Service (nfslock) + The Network File System Lock (nfslock) service starts the required +remote procedure call (RPC) processes which allow clients to lock files on the +server. If the local system is not configured to mount NFS filesystems then +this service should be disabled. + +The nfslock service can be disabled with the following command: +$ sudo systemctl mask --now nfslock.service + + include disable_nfslock + +class disable_nfslock { + service {'nfslock': + enable => false, + ensure => 'stopped', + } +} + + - name: Disable service nfslock + block: + + - name: Disable service nfslock + systemd: + name: nfslock.service + enabled: 'no' + state: stopped + masked: 'yes' + ignore_errors: 'yes' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - disable_strategy + - low_complexity + - low_disruption + - no_reboot_needed + - service_nfslock_disabled + - unknown_severity + +- name: Unit Socket Exists - nfslock.socket + command: systemctl list-unit-files nfslock.socket + args: + warn: false + register: socket_file_exists + changed_when: false + ignore_errors: true + check_mode: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - disable_strategy + - low_complexity + - low_disruption + - no_reboot_needed + - service_nfslock_disabled + - unknown_severity + +- name: Disable socket nfslock + systemd: + name: nfslock.socket + enabled: 'no' + state: stopped + masked: 'yes' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"nfslock.socket" in socket_file_exists.stdout_lines[1]' + tags: + - disable_strategy + - low_complexity + - low_disruption + - no_reboot_needed + - service_nfslock_disabled + - unknown_severity + + +[customizations.services] +disabled = ["nfslock"] + + apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + systemd: + units: + - name: nfslock.service + enabled: false + mask: true + - name: nfslock.socket + enabled: false + mask: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" stop 'nfslock.service' +"$SYSTEMCTL_EXEC" disable 'nfslock.service' +"$SYSTEMCTL_EXEC" mask 'nfslock.service' +# Disable socket activation if we have a unit file for it +if "$SYSTEMCTL_EXEC" list-unit-files | grep -q '^nfslock.socket'; then + "$SYSTEMCTL_EXEC" stop 'nfslock.socket' + "$SYSTEMCTL_EXEC" mask 'nfslock.socket' +fi +# The service may not be running because it has been started and failed, +# so let's reset the state so OVAL checks pass. +# Service should be 'inactive', not 'failed' after reboot though. +"$SYSTEMCTL_EXEC" reset-failed 'nfslock.service' || true + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + Disable rpcbind Service + The rpcbind utility maps RPC services to the ports on which they listen. +RPC processes notify rpcbind when they start, registering the ports they +are listening on and the RPC program numbers they expect to serve. The +rpcbind service redirects the client to the proper port number so it can +communicate with the requested service. If the system does not require RPC +(such as for NFS servers) then this service should be disabled. + +The rpcbind service can be disabled with the following command: +$ sudo systemctl mask --now rpcbind.service + If the system does not require rpc based services, it is recommended that +rpcbind be disabled to reduce the attack surface. + CCE-82858-2 + include disable_rpcbind + +class disable_rpcbind { + service {'rpcbind': + enable => false, + ensure => 'stopped', + } +} + + - name: Disable service rpcbind + block: + + - name: Disable service rpcbind + systemd: + name: rpcbind.service + enabled: 'no' + state: stopped + masked: 'yes' + ignore_errors: 'yes' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82858-2 + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - service_rpcbind_disabled + +- name: Unit Socket Exists - rpcbind.socket + command: systemctl list-unit-files rpcbind.socket + args: + warn: false + register: socket_file_exists + changed_when: false + ignore_errors: true + check_mode: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82858-2 + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - service_rpcbind_disabled + +- name: Disable socket rpcbind + systemd: + name: rpcbind.socket + enabled: 'no' + state: stopped + masked: 'yes' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"rpcbind.socket" in socket_file_exists.stdout_lines[1]' + tags: + - CCE-82858-2 + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - service_rpcbind_disabled + + +[customizations.services] +disabled = ["rpcbind"] + + apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + systemd: + units: + - name: rpcbind.service + enabled: false + mask: true + - name: rpcbind.socket + enabled: false + mask: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" stop 'rpcbind.service' +"$SYSTEMCTL_EXEC" disable 'rpcbind.service' +"$SYSTEMCTL_EXEC" mask 'rpcbind.service' +# Disable socket activation if we have a unit file for it +if "$SYSTEMCTL_EXEC" list-unit-files | grep -q '^rpcbind.socket'; then + "$SYSTEMCTL_EXEC" stop 'rpcbind.socket' + "$SYSTEMCTL_EXEC" mask 'rpcbind.socket' +fi +# The service may not be running because it has been started and failed, +# so let's reset the state so OVAL checks pass. +# Service should be 'inactive', not 'failed' after reboot though. +"$SYSTEMCTL_EXEC" reset-failed 'rpcbind.service' || true + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + Disable Secure RPC Client Service (rpcgssd) + The rpcgssd service manages RPCSEC GSS contexts required to secure protocols +that use RPC (most often Kerberos and NFS). The rpcgssd service is the +client-side of RPCSEC GSS. If the system does not require secure RPC then this +service should be disabled. + +The rpcgssd service can be disabled with the following command: +$ sudo systemctl mask --now rpcgssd.service + + include disable_rpcgssd + +class disable_rpcgssd { + service {'rpcgssd': + enable => false, + ensure => 'stopped', + } +} + + - name: Disable service rpcgssd + block: + + - name: Disable service rpcgssd + systemd: + name: rpcgssd.service + enabled: 'no' + state: stopped + masked: 'yes' + ignore_errors: 'yes' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - disable_strategy + - low_complexity + - low_disruption + - no_reboot_needed + - service_rpcgssd_disabled + - unknown_severity + +- name: Unit Socket Exists - rpcgssd.socket + command: systemctl list-unit-files rpcgssd.socket + args: + warn: false + register: socket_file_exists + changed_when: false + ignore_errors: true + check_mode: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - disable_strategy + - low_complexity + - low_disruption + - no_reboot_needed + - service_rpcgssd_disabled + - unknown_severity + +- name: Disable socket rpcgssd + systemd: + name: rpcgssd.socket + enabled: 'no' + state: stopped + masked: 'yes' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"rpcgssd.socket" in socket_file_exists.stdout_lines[1]' + tags: + - disable_strategy + - low_complexity + - low_disruption + - no_reboot_needed + - service_rpcgssd_disabled + - unknown_severity + + +[customizations.services] +disabled = ["rpcgssd"] + + apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + systemd: + units: + - name: rpcgssd.service + enabled: false + mask: true + - name: rpcgssd.socket + enabled: false + mask: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" stop 'rpcgssd.service' +"$SYSTEMCTL_EXEC" disable 'rpcgssd.service' +"$SYSTEMCTL_EXEC" mask 'rpcgssd.service' +# Disable socket activation if we have a unit file for it +if "$SYSTEMCTL_EXEC" list-unit-files | grep -q '^rpcgssd.socket'; then + "$SYSTEMCTL_EXEC" stop 'rpcgssd.socket' + "$SYSTEMCTL_EXEC" mask 'rpcgssd.socket' +fi +# The service may not be running because it has been started and failed, +# so let's reset the state so OVAL checks pass. +# Service should be 'inactive', not 'failed' after reboot though. +"$SYSTEMCTL_EXEC" reset-failed 'rpcgssd.service' || true + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + Disable RPC ID Mapping Service (rpcidmapd) + The rpcidmapd service is used to map user names and groups to UID +and GID numbers on NFSv4 mounts. If NFS is not in use on the local system then +this service should be disabled. + +The rpcidmapd service can be disabled with the following command: +$ sudo systemctl mask --now rpcidmapd.service + + include disable_rpcidmapd + +class disable_rpcidmapd { + service {'rpcidmapd': + enable => false, + ensure => 'stopped', + } +} + + - name: Disable service rpcidmapd + block: + + - name: Disable service rpcidmapd + systemd: + name: rpcidmapd.service + enabled: 'no' + state: stopped + masked: 'yes' + ignore_errors: 'yes' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - disable_strategy + - low_complexity + - low_disruption + - no_reboot_needed + - service_rpcidmapd_disabled + - unknown_severity + +- name: Unit Socket Exists - rpcidmapd.socket + command: systemctl list-unit-files rpcidmapd.socket + args: + warn: false + register: socket_file_exists + changed_when: false + ignore_errors: true + check_mode: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - disable_strategy + - low_complexity + - low_disruption + - no_reboot_needed + - service_rpcidmapd_disabled + - unknown_severity + +- name: Disable socket rpcidmapd + systemd: + name: rpcidmapd.socket + enabled: 'no' + state: stopped + masked: 'yes' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"rpcidmapd.socket" in socket_file_exists.stdout_lines[1]' + tags: + - disable_strategy + - low_complexity + - low_disruption + - no_reboot_needed + - service_rpcidmapd_disabled + - unknown_severity + + +[customizations.services] +disabled = ["rpcidmapd"] + + apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + systemd: + units: + - name: rpcidmapd.service + enabled: false + mask: true + - name: rpcidmapd.socket + enabled: false + mask: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" stop 'rpcidmapd.service' +"$SYSTEMCTL_EXEC" disable 'rpcidmapd.service' +"$SYSTEMCTL_EXEC" mask 'rpcidmapd.service' +# Disable socket activation if we have a unit file for it +if "$SYSTEMCTL_EXEC" list-unit-files | grep -q '^rpcidmapd.socket'; then + "$SYSTEMCTL_EXEC" stop 'rpcidmapd.socket' + "$SYSTEMCTL_EXEC" mask 'rpcidmapd.socket' +fi +# The service may not be running because it has been started and failed, +# so let's reset the state so OVAL checks pass. +# Service should be 'inactive', not 'failed' after reboot though. +"$SYSTEMCTL_EXEC" reset-failed 'rpcidmapd.service' || true + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + Configure All Systems which Use NFS + The steps in this section are appropriate for all systems which +run NFS, whether they operate as clients or as servers. + + Make Each System a Client or a Server, not Both + If NFS must be used, it should be deployed in the simplest +configuration possible to avoid maintainability problems which may lead to +unnecessary security exposure. Due to the reliability and security problems +caused by NFS (specially NFSv3 and NFSv2), it is not a good idea for systems +which act as NFS servers to also mount filesystems via NFS. At the least, +crossed mounts (the situation in which each of two servers mounts a filesystem +from the other) should never be used. + + + Configure NFS Services to Use Fixed Ports (NFSv3 and NFSv2) + Firewalling should be done at each host and at the border +firewalls to protect the NFS daemons from remote access, since NFS servers +should never be accessible from outside the organization. However, by default +for NFSv3 and NFSv2, the RPC Bind service assigns each NFS service to a port +dynamically at service startup time. Dynamic ports cannot be protected by port + +filtering firewalls such as iptables. + + +Therefore, restrict each service to always use a given port, so that +firewalling can be done effectively. Note that, because of the way RPC is +implemented, it is not possible to disable the RPC Bind service even if ports +are assigned statically to all RPC services. + +In NFSv4, the mounting and locking protocols have been incorporated into the +protocol, and the server listens on the the well-known TCP port 2049. As such, +NFSv4 does not need to interact with the rpcbind, lockd, and rpc.statd +daemons, which can and should be disabled in a pure NFSv4 environment. The +rpc.mountd daemon is still required on the NFS server to setup +exports, but is not involved in any over-the-wire operations. + + Configure lockd to use static TCP port + Configure the lockd daemon to use a static TCP port as +opposed to letting the RPC Bind service dynamically assign a port. Edit the +file /etc/sysconfig/nfs. Add or correct the following line: +LOCKD_TCPPORT=lockd-port +Where lockd-port is a port which is not used by any other service on +your network. + Restrict service to always use a given port, so that firewalling can be done +effectively. + + + Configure lockd to use static UDP port + Configure the lockd daemon to use a static UDP port as +opposed to letting the RPC Bind service dynamically assign a port. Edit the +file /etc/sysconfig/nfs. Add or correct the following line: +LOCKD_UDPPORT=lockd-port +Where lockd-port is a port which is not used by any other service on +your network. + Restricting services to always use a given port enables firewalling +to be done more effectively. + + + Configure mountd to use static port + Configure the mountd daemon to use a static port as +opposed to letting the RPC Bind service dynamically assign a port. Edit the +file /etc/sysconfig/nfs. Add or correct the following line: +MOUNTD_PORT=statd-port +Where mountd-port is a port which is not used by any other service on your network. + Restricting services to always use a given port enables firewalling +to be done more effectively. + + + Configure statd to use static port + Configure the statd daemon to use a static port as +opposed to letting the RPC Bind service dynamically assign a port. Edit the +file /etc/sysconfig/nfs. Add or correct the following line: +STATD_PORT=statd-port +Where statd-port is a port which is not used by any other service on your network. + Restricting services to always use a given port enables firewalling +to be done more effectively. + + + + + Configure NFS Clients + The steps in this section are appropriate for systems which operate as NFS clients. + + Disable NFS Server Daemons + There is no need to run the NFS server daemons nfs and +rpcsvcgssd except on a small number of properly secured systems +designated as NFS servers. Ensure that these daemons are turned off on +clients. + + Disable Network File System (nfs) + The Network File System (NFS) service allows remote hosts to mount +and interact with shared filesystems on the local system. If the local system +is not designated as a NFS server then this service should be disabled. + +The nfs-server service can be disabled with the following command: +$ sudo systemctl mask --now nfs-server.service + 11 + 12 + 14 + 15 + 16 + 18 + 3 + 5 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + DSS06.03 + DSS06.06 + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + A.6.1.2 + A.7.1.1 + A.9.1.2 + A.9.2.1 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-7(a) + CM-7(b) + CM-6(a) + PR.AC-4 + PR.AC-6 + PR.PT-3 + 2.2.18 + Unnecessary services should be disabled to decrease the attack surface of the system. + + CCE-82762-6 + include disable_nfs-server + +class disable_nfs-server { + service {'nfs-server': + enable => false, + ensure => 'stopped', + } +} + + - name: Disable service nfs-server + block: + + - name: Disable service nfs-server + systemd: + name: nfs-server.service + enabled: 'no' + state: stopped + masked: 'yes' + ignore_errors: 'yes' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82762-6 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - no_reboot_needed + - service_nfs_disabled + - unknown_severity + +- name: Unit Socket Exists - nfs-server.socket + command: systemctl list-unit-files nfs-server.socket + args: + warn: false + register: socket_file_exists + changed_when: false + ignore_errors: true + check_mode: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82762-6 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - no_reboot_needed + - service_nfs_disabled + - unknown_severity + +- name: Disable socket nfs-server + systemd: + name: nfs-server.socket + enabled: 'no' + state: stopped + masked: 'yes' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"nfs-server.socket" in socket_file_exists.stdout_lines[1]' + tags: + - CCE-82762-6 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - no_reboot_needed + - service_nfs_disabled + - unknown_severity + + +[customizations.services] +disabled = ["nfs-server"] + + apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + systemd: + units: + - name: nfs-server.service + enabled: false + mask: true + - name: nfs-server.socket + enabled: false + mask: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" stop 'nfs-server.service' +"$SYSTEMCTL_EXEC" disable 'nfs-server.service' +"$SYSTEMCTL_EXEC" mask 'nfs-server.service' +# Disable socket activation if we have a unit file for it +if "$SYSTEMCTL_EXEC" list-unit-files | grep -q '^nfs-server.socket'; then + "$SYSTEMCTL_EXEC" stop 'nfs-server.socket' + "$SYSTEMCTL_EXEC" mask 'nfs-server.socket' +fi +# The service may not be running because it has been started and failed, +# so let's reset the state so OVAL checks pass. +# Service should be 'inactive', not 'failed' after reboot though. +"$SYSTEMCTL_EXEC" reset-failed 'nfs-server.service' || true + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable Secure RPC Server Service (rpcsvcgssd) + The rpcsvcgssd service manages RPCSEC GSS contexts required to +secure protocols that use RPC (most often Kerberos and NFS). The rpcsvcgssd +service is the server-side of RPCSEC GSS. If the system does not require secure +RPC then this service should be disabled. + +The rpcsvcgssd service can be disabled with the following command: +$ sudo systemctl mask --now rpcsvcgssd.service + Unnecessary services should be disabled to decrease the attack surface of the system. + + include disable_rpcsvcgssd + +class disable_rpcsvcgssd { + service {'rpcsvcgssd': + enable => false, + ensure => 'stopped', + } +} + + - name: Disable service rpcsvcgssd + block: + + - name: Disable service rpcsvcgssd + systemd: + name: rpcsvcgssd.service + enabled: 'no' + state: stopped + masked: 'yes' + ignore_errors: 'yes' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - disable_strategy + - low_complexity + - low_disruption + - no_reboot_needed + - service_rpcsvcgssd_disabled + - unknown_severity + +- name: Unit Socket Exists - rpcsvcgssd.socket + command: systemctl list-unit-files rpcsvcgssd.socket + args: + warn: false + register: socket_file_exists + changed_when: false + ignore_errors: true + check_mode: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - disable_strategy + - low_complexity + - low_disruption + - no_reboot_needed + - service_rpcsvcgssd_disabled + - unknown_severity + +- name: Disable socket rpcsvcgssd + systemd: + name: rpcsvcgssd.socket + enabled: 'no' + state: stopped + masked: 'yes' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"rpcsvcgssd.socket" in socket_file_exists.stdout_lines[1]' + tags: + - disable_strategy + - low_complexity + - low_disruption + - no_reboot_needed + - service_rpcsvcgssd_disabled + - unknown_severity + + +[customizations.services] +disabled = ["rpcsvcgssd"] + + apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + systemd: + units: + - name: rpcsvcgssd.service + enabled: false + mask: true + - name: rpcsvcgssd.socket + enabled: false + mask: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" stop 'rpcsvcgssd.service' +"$SYSTEMCTL_EXEC" disable 'rpcsvcgssd.service' +"$SYSTEMCTL_EXEC" mask 'rpcsvcgssd.service' +# Disable socket activation if we have a unit file for it +if "$SYSTEMCTL_EXEC" list-unit-files | grep -q '^rpcsvcgssd.socket'; then + "$SYSTEMCTL_EXEC" stop 'rpcsvcgssd.socket' + "$SYSTEMCTL_EXEC" mask 'rpcsvcgssd.socket' +fi +# The service may not be running because it has been started and failed, +# so let's reset the state so OVAL checks pass. +# Service should be 'inactive', not 'failed' after reboot though. +"$SYSTEMCTL_EXEC" reset-failed 'rpcsvcgssd.service' || true + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Specify UID and GID for Anonymous NFS Connections + To specify the UID and GID for remote root users, edit the /etc/exports file and add the following for each export: + +anonuid=value greater than UID_MAX from /etc/login.defs +anongid=value greater than GID_MAX from /etc/login.defs + +Note that a value of "-1" is technically acceptable as this will randomize the anonuid and +anongid values on a Red Hat Enterprise Linux based NFS server. While acceptable from a security perspective, +a value of -1 may cause interoperability issues, particularly with Red Hat Enterprise Linux 7 client systems. +Alternatively, functionally equivalent values of 60001, 65534, 65535 may be used. + Specifying the anonymous UID and GID ensures that the remote root user is mapped +to a local account which has no permissions on the system. + + + + + + + Mount Remote Filesystems with Restrictive Options + Edit the file /etc/fstab. For each filesystem whose type +(column 3) is nfs or nfs4, add the text +,nodev,nosuid to the list of mount options in column 4. If +appropriate, also add ,noexec. + +See the section titled "Restrict Partition Mount Options" for a description of +the effects of these options. In general, execution of files mounted via NFS +should be considered risky because of the possibility that an adversary could +intercept the request and substitute a malicious file. Allowing setuid files to +be executed from remote servers is particularly risky, both for this reason and +because it requires the clients to extend root-level trust to the NFS +server. + + + Mount Remote Filesystems with Kerberos Security + Add the sec=krb5:krb5i:krb5p option to the fourth column of /etc/fstab for the line which controls mounting of +any NFS mounts. + 1 + 12 + 14 + 15 + 16 + 18 + 3 + 5 + DSS05.04 + DSS05.10 + DSS06.10 + CCI-000366 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.3 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + A.18.1.4 + A.6.1.2 + A.9.1.2 + A.9.2.1 + A.9.2.3 + A.9.2.4 + A.9.3.1 + A.9.4.1 + A.9.4.2 + A.9.4.3 + A.9.4.4 + A.9.4.5 + CM-7(a) + CM-7(b) + CM-6(a) + IA-2 + IA-2(8) + IA-2(9) + AC-17(a) + PR.AC-4 + PR.AC-7 + SRG-OS-000480-GPOS-00227 + When an NFS server is configured to use AUTH_SYS a selected userid and groupid are used to handle +requests from the remote user. The userid and groupid could mistakenly or maliciously be set +incorrectly. The AUTH_GSS method of authentication uses certificates on the server and client +systems to more securely authenticate the remote mount request. + - name: Get nfs and nfs4 mount points, that don't have sec=krb5:krb5i:krb5p + command: findmnt --fstab --types nfs,nfs4 -O nosec=krb5:krb5i:krb5p -n + register: points_register + check_mode: false + changed_when: false + failed_when: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AC-17(a) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-IA-2 + - NIST-800-53-IA-2(8) + - NIST-800-53-IA-2(9) + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - mount_option_krb_sec_remote_filesystems + - no_reboot_needed + +- name: Add sec=krb5:krb5i:krb5p to nfs and nfs4 mount points + mount: + path: '{{ item.split()[0] }}' + src: '{{ item.split()[1] }}' + fstype: '{{ item.split()[2] }}' + state: mounted + opts: '{{ item.split()[3] }},sec=krb5:krb5i:krb5p' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - (points_register.stdout | length > 0) + with_items: '{{ points_register.stdout_lines }}' + tags: + - NIST-800-53-AC-17(a) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-IA-2 + - NIST-800-53-IA-2(8) + - NIST-800-53-IA-2(9) + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - mount_option_krb_sec_remote_filesystems + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +vfstype_points=() +readarray -t vfstype_points < <(grep -E "[[:space:]]nfs[4]?[[:space:]]" /etc/fstab | awk '{print $2}') + +for vfstype_point in "${vfstype_points[@]}" +do + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" ${vfstype_point//\\/\\\\})" + + # If the mount point is not in /etc/fstab, get previous mount options from /etc/mtab + if [ "$(grep -c "$mount_point_match_regexp" /etc/fstab)" -eq 0 ]; then + # runtime opts without some automatic kernel/userspace-added defaults + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/mtab | head -1 | awk '{print $4}' \ + | sed -E "s/(rw|defaults|seclabel|sec=krb5:krb5i:krb5p)(,|$)//g;s/,$//") + [ "$previous_mount_opts" ] && previous_mount_opts+="," + echo " ${vfstype_point//\\/\\\\} nfs4 defaults,${previous_mount_opts}sec=krb5:krb5i:krb5p 0 0" >> /etc/fstab + # If the mount_opt option is not already in the mount point's /etc/fstab entry, add it + elif [ "$(grep "$mount_point_match_regexp" /etc/fstab | grep -c "sec=krb5:krb5i:krb5p")" -eq 0 ]; then + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/fstab | awk '{print $4}') + sed -i "s|\(${mount_point_match_regexp}.*${previous_mount_opts}\)|\1,sec=krb5:krb5i:krb5p|" /etc/fstab + fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Mount Remote Filesystems with nodev + Add the nodev option to the fourth column of /etc/fstab for the line which controls mounting of +any NFS mounts. + 11 + 13 + 14 + 3 + 8 + 9 + APO13.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS05.06 + DSS06.06 + CCI-000366 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.11.2.9 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.8.2.1 + A.8.2.2 + A.8.2.3 + A.8.3.1 + A.8.3.3 + A.9.1.2 + CM-6(a) + MP-2 + PR.IP-1 + PR.PT-2 + PR.PT-3 + SRG-OS-000480-GPOS-00227 + RHEL-08-010640 + SV-230307r627750_rule + Legitimate device files should only exist in the /dev directory. NFS mounts +should not present device files to users. + CCE-84052-0 + - name: Get nfs and nfs4 mount points, that don't have nodev + command: findmnt --fstab --types nfs,nfs4 -O nonodev -n + register: points_register + check_mode: false + changed_when: false + failed_when: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84052-0 + - DISA-STIG-RHEL-08-010640 + - NIST-800-53-CM-6(a) + - NIST-800-53-MP-2 + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - mount_option_nodev_remote_filesystems + - no_reboot_needed + +- name: Add nodev to nfs and nfs4 mount points + mount: + path: '{{ item.split()[0] }}' + src: '{{ item.split()[1] }}' + fstype: '{{ item.split()[2] }}' + state: mounted + opts: '{{ item.split()[3] }},nodev' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - (points_register.stdout | length > 0) + with_items: '{{ points_register.stdout_lines }}' + tags: + - CCE-84052-0 + - DISA-STIG-RHEL-08-010640 + - NIST-800-53-CM-6(a) + - NIST-800-53-MP-2 + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - mount_option_nodev_remote_filesystems + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +vfstype_points=() +readarray -t vfstype_points < <(grep -E "[[:space:]]nfs[4]?[[:space:]]" /etc/fstab | awk '{print $2}') + +for vfstype_point in "${vfstype_points[@]}" +do + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" ${vfstype_point//\\/\\\\})" + + # If the mount point is not in /etc/fstab, get previous mount options from /etc/mtab + if [ "$(grep -c "$mount_point_match_regexp" /etc/fstab)" -eq 0 ]; then + # runtime opts without some automatic kernel/userspace-added defaults + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/mtab | head -1 | awk '{print $4}' \ + | sed -E "s/(rw|defaults|seclabel|nodev)(,|$)//g;s/,$//") + [ "$previous_mount_opts" ] && previous_mount_opts+="," + echo " ${vfstype_point//\\/\\\\} nfs4 defaults,${previous_mount_opts}nodev 0 0" >> /etc/fstab + # If the mount_opt option is not already in the mount point's /etc/fstab entry, add it + elif [ "$(grep "$mount_point_match_regexp" /etc/fstab | grep -c "nodev")" -eq 0 ]; then + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/fstab | awk '{print $4}') + sed -i "s|\(${mount_point_match_regexp}.*${previous_mount_opts}\)|\1,nodev|" /etc/fstab + fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Mount Remote Filesystems with noexec + Add the noexec option to the fourth column of /etc/fstab for the line which controls mounting of +any NFS mounts. + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + CCI-000366 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + AC-6 + AC-6(8) + AC-6(10) + CM-6(a) + PR.AC-4 + PR.DS-5 + SRG-OS-000480-GPOS-00227 + RHEL-08-010630 + SV-230306r627750_rule + The noexec mount option causes the system not to execute binary files. This option must be used +for mounting any file system not containing approved binary files as they may be incompatible. Executing +files from untrusted file systems increases the opportunity for unprivileged users to attain unauthorized +administrative access. + CCE-84050-4 + - name: Get nfs and nfs4 mount points, that don't have noexec + command: findmnt --fstab --types nfs,nfs4 -O nonoexec -n + register: points_register + check_mode: false + changed_when: false + failed_when: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84050-4 + - DISA-STIG-RHEL-08-010630 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(10) + - NIST-800-53-AC-6(8) + - NIST-800-53-CM-6(a) + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - mount_option_noexec_remote_filesystems + - no_reboot_needed + +- name: Add noexec to nfs and nfs4 mount points + mount: + path: '{{ item.split()[0] }}' + src: '{{ item.split()[1] }}' + fstype: '{{ item.split()[2] }}' + state: mounted + opts: '{{ item.split()[3] }},noexec' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - (points_register.stdout | length > 0) + with_items: '{{ points_register.stdout_lines }}' + tags: + - CCE-84050-4 + - DISA-STIG-RHEL-08-010630 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(10) + - NIST-800-53-AC-6(8) + - NIST-800-53-CM-6(a) + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - mount_option_noexec_remote_filesystems + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +vfstype_points=() +readarray -t vfstype_points < <(grep -E "[[:space:]]nfs[4]?[[:space:]]" /etc/fstab | awk '{print $2}') + +for vfstype_point in "${vfstype_points[@]}" +do + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" ${vfstype_point//\\/\\\\})" + + # If the mount point is not in /etc/fstab, get previous mount options from /etc/mtab + if [ "$(grep -c "$mount_point_match_regexp" /etc/fstab)" -eq 0 ]; then + # runtime opts without some automatic kernel/userspace-added defaults + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/mtab | head -1 | awk '{print $4}' \ + | sed -E "s/(rw|defaults|seclabel|noexec)(,|$)//g;s/,$//") + [ "$previous_mount_opts" ] && previous_mount_opts+="," + echo " ${vfstype_point//\\/\\\\} nfs4 defaults,${previous_mount_opts}noexec 0 0" >> /etc/fstab + # If the mount_opt option is not already in the mount point's /etc/fstab entry, add it + elif [ "$(grep "$mount_point_match_regexp" /etc/fstab | grep -c "noexec")" -eq 0 ]; then + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/fstab | awk '{print $4}') + sed -i "s|\(${mount_point_match_regexp}.*${previous_mount_opts}\)|\1,noexec|" /etc/fstab + fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Mount Remote Filesystems with nosuid + Add the nosuid option to the fourth column of /etc/fstab for the line which controls mounting of +any NFS mounts. + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + CCI-000366 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + AC-6 + AC-6(1) + CM6(a) + PR.AC-4 + PR.DS-5 + SRG-OS-000480-GPOS-00227 + RHEL-08-010650 + SV-230308r627750_rule + NFS mounts should not present suid binaries to users. Only vendor-supplied suid executables +should be installed to their default location on the local filesystem. + CCE-84053-8 + - name: Get nfs and nfs4 mount points, that don't have nosuid + command: findmnt --fstab --types nfs,nfs4 -O nonosuid -n + register: points_register + check_mode: false + changed_when: false + failed_when: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84053-8 + - DISA-STIG-RHEL-08-010650 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM6(a) + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - mount_option_nosuid_remote_filesystems + - no_reboot_needed + +- name: Add nosuid to nfs and nfs4 mount points + mount: + path: '{{ item.split()[0] }}' + src: '{{ item.split()[1] }}' + fstype: '{{ item.split()[2] }}' + state: mounted + opts: '{{ item.split()[3] }},nosuid' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - (points_register.stdout | length > 0) + with_items: '{{ points_register.stdout_lines }}' + tags: + - CCE-84053-8 + - DISA-STIG-RHEL-08-010650 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM6(a) + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - mount_option_nosuid_remote_filesystems + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +vfstype_points=() +readarray -t vfstype_points < <(grep -E "[[:space:]]nfs[4]?[[:space:]]" /etc/fstab | awk '{print $2}') + +for vfstype_point in "${vfstype_points[@]}" +do + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" ${vfstype_point//\\/\\\\})" + + # If the mount point is not in /etc/fstab, get previous mount options from /etc/mtab + if [ "$(grep -c "$mount_point_match_regexp" /etc/fstab)" -eq 0 ]; then + # runtime opts without some automatic kernel/userspace-added defaults + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/mtab | head -1 | awk '{print $4}' \ + | sed -E "s/(rw|defaults|seclabel|nosuid)(,|$)//g;s/,$//") + [ "$previous_mount_opts" ] && previous_mount_opts+="," + echo " ${vfstype_point//\\/\\\\} nfs4 defaults,${previous_mount_opts}nosuid 0 0" >> /etc/fstab + # If the mount_opt option is not already in the mount point's /etc/fstab entry, add it + elif [ "$(grep "$mount_point_match_regexp" /etc/fstab | grep -c "nosuid")" -eq 0 ]; then + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/fstab | awk '{print $4}') + sed -i "s|\(${mount_point_match_regexp}.*${previous_mount_opts}\)|\1,nosuid|" /etc/fstab + fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + + Configure NFS Servers + The steps in this section are appropriate for systems which operate as NFS servers. + + Ensure All-Squashing Disabled On All Exports + The all_squash maps all uids and gids to an anonymous user. +This should be disabled by removing any instances of the +all_squash option from the file /etc/exports. + The all_squash option maps all client requests to a single anonymous +uid/gid on the NFS server, negating the ability to track file access +by user ID. + + + + + + Ensure Insecure File Locking is Not Allowed + By default the NFS server requires secure file-lock requests, which require +credentials from the client in order to lock a file. Most NFS clients send +credentials with file lock requests, however, there are a few clients that +do not send credentials when requesting a file-lock, allowing the client to +only be able to lock world-readable files. To get around this, the +insecure_locks option can be used so these clients can access the +desired export. This poses a security risk by potentially allowing the +client access to data for which it does not have authorization. Remove any +instances of the insecure_locks option from the file +/etc/exports. + CCI-000764 + Allowing insecure file locking could allow for sensitive data to be +viewed or edited by an unauthorized user. + + + + + + + + + Restrict NFS Clients to Privileged Ports + By default, the server NFS implementation requires that all client requests be made +from ports less than 1024. If your organization has control over systems connected to its +network, and if NFS requests are prohibited at the border firewall, this offers some protection +against malicious requests from unprivileged users. Therefore, the default should not be changed. + +To ensure that the default has not been changed, ensure no line in +/etc/exports contains the option insecure. + 11 + 12 + 14 + 15 + 16 + 18 + 3 + 5 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + DSS06.03 + DSS06.06 + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + A.6.1.2 + A.7.1.1 + A.9.1.2 + A.9.2.1 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-7(a) + CM-7(b) + CM-6(a) + PR.AC-4 + PR.AC-6 + PR.PT-3 + Allowing client requests to be made from ports higher than 1024 could allow a unprivileged +user to initiate an NFS connection. If the unprivileged user account has been compromised, an +attacker could gain access to data on the NFS server. + + + Use Kerberos Security on All Exports + Using Kerberos on all exported mounts prevents a malicious client or user from +impersonating a system user. To cryptography authenticate users to the NFS server, +add sec=krb5:krb5i:krb5p to each export in /etc/exports. + 1 + 12 + 14 + 15 + 16 + 18 + 3 + 5 + DSS05.04 + DSS05.10 + DSS06.10 + CCI-000366 + 164.308(a)(4)(i) + 164.308(b)(1) + 164.308(b)(3) + 164.310(b) + 164.312(e)(1) + 164.312(e)(2)(ii) + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.3 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + A.18.1.4 + A.6.1.2 + A.9.1.2 + A.9.2.1 + A.9.2.3 + A.9.2.4 + A.9.3.1 + A.9.4.1 + A.9.4.2 + A.9.4.3 + A.9.4.4 + A.9.4.5 + CM-7(a) + CM-7(b) + CM-6(a) + IA-2 + IA-2(8) + IA-2(9) + AC-17(a) + PR.AC-4 + PR.AC-7 + SRG-OS-000480-GPOS-00227 + When an NFS server is configured to use AUTH_SYS a selected userid and groupid are used to handle +requests from the remote user. The userid and groupid could mistakenly or maliciously be set +incorrectly. The AUTH_GSS method of authentication uses certificates on the server and client +systems to more securely authenticate the remote mount request. + CCE-80924-4 + - name: Drop any security clause for every export + replace: + path: /etc/exports + regexp: ^(/.*\w+.*\(.*),sec=[^,]*(.*\)\w*$) + replace: \1\2 + ignore_errors: true + tags: + - CCE-80924-4 + - NIST-800-53-AC-17(a) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-IA-2 + - NIST-800-53-IA-2(8) + - NIST-800-53-IA-2(9) + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - use_kerberos_security_all_exports + +- name: Add kerberos security when no security is defined for an export + replace: + path: /etc/exports + regexp: ^(/.*\w+.*\(.*)(\)\w*$) + replace: \1,sec=krb5:krb5i:krb5p\2 + tags: + - CCE-80924-4 + - NIST-800-53-AC-17(a) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-IA-2 + - NIST-800-53-IA-2(8) + - NIST-800-53-IA-2(9) + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - use_kerberos_security_all_exports + + +nfs_exports=() +readarray -t nfs_exports < <(grep -E "^/.*[[:space:]]+ .*\(.*\)[[:space:]]*$" /etc/exports | awk '{print $2}') + +for nfs_export in "${nfs_exports[@]}" +do + correct_export="" + if [ "$(grep -c "sec=" <<<"$nfs_export")" -eq 0 ]; then + correct_export="$(echo $nfs_export|sed -e 's/).*$/,sec=krb5\:krb5i\:krb5p)/')" + else + correct_export="$(echo $nfs_export|sed -e 's/sec=[^\,\)]*/sec=krb5\:krb5i\:krb5p/')" + fi + sed -i "s|$nfs_export|$correct_export|g" /etc/exports +done + + + + + + + + + + Use Root-Squashing on All Exports + If a filesystem is exported using root squashing, requests from root on the client +are considered to be unprivileged (mapped to a user such as nobody). This provides some mild +protection against remote abuse of an NFS server. Root squashing is enabled by default, and +should not be disabled. + +Ensure that no line in /etc/exports contains the option no_root_squash. + If the NFS server allows root access to local file systems from remote hosts, this +access could be used to compromise the system. + + + Configure the Exports File Restrictively + Linux's NFS implementation uses the file /etc/exports to control what filesystems +and directories may be accessed via NFS. (See the exports(5) manpage for more information about the +format of this file.) + +The syntax of the exports file is not necessarily checked fully on reload, and syntax errors +can leave your NFS configuration more open than intended. Therefore, exercise caution when modifying +the file. + +The syntax of each line in /etc/exports is: +/DIR host1(opt1,opt2) host2(opt3) +where /DIR is a directory or filesystem to export, hostN is an IP address, netblock, +hostname, domain, or netgroup to which to export, and optN is an option. + + + Export Filesystems Read-Only if Possible + If a filesystem is being exported so that users can view the files in a convenient +fashion, but there is no need for users to edit those files, exporting the filesystem read-only +removes an attack vector against the server. The default filesystem export mode is ro, +so do not specify rw without a good reason. + + + Use Access Lists to Enforce Authorization Restrictions + When configuring NFS exports, ensure that each export line in /etc/exports contains +a list of hosts which are allowed to access that export. If no hosts are specified on an export line, +then that export is available to any remote host which requests it. All lines of the exports file should +specify the hosts (or subnets, if needed) which are allowed to access the exported directory, so that +unknown or remote hosts will be denied. + +Authorized hosts can be specified in several different formats: +Name or alias that is recognized by the resolverFully qualified domain nameIP addressIP subnets in the format address/netmask or address/CIDR + + + + + Network Time Protocol + The Network Time Protocol is used to manage the system +clock over a network. Computer clocks are not very accurate, so +time will drift unpredictably on unmanaged systems. Central time +protocols can be used both to ensure that time is consistent among +a network of systems, and that their time is consistent with the +outside world. + +If every system on a network reliably reports the same time, then it is much +easier to correlate log messages in case of an attack. In addition, a number of +cryptographic protocols (such as Kerberos) use timestamps to prevent certain +types of attacks. If your network does not have synchronized time, these +protocols may be unreliable or even unusable. + +Depending on the specifics of the network, global time accuracy may be just as +important as local synchronization, or not very important at all. If your +network is connected to the Internet, using a public timeserver (or one +provided by your enterprise) provides globally accurate timestamps which may be +essential in investigating or responding to an attack which originated outside +of your network. + +A typical network setup involves a small number of internal systems operating +as NTP servers, and the remainder obtaining time information from those +internal servers. + +There is a choice between the daemons ntpd and chronyd, which +are available from the repositories in the ntp and chrony +packages respectively. + +The default chronyd daemon can work well when external time references +are only intermittently accesible, can perform well even when the network is +congested for longer periods of time, can usually synchronize the clock faster +and with better time accuracy, and quickly adapts to sudden changes in the rate +of the clock, for example, due to changes in the temperature of the crystal +oscillator. Chronyd should be considered for all systems which are +frequently suspended or otherwise intermittently disconnected and reconnected +to a network. Mobile and virtual systems for example. + +The ntpd NTP daemon fully supports NTP protocol version 4 (RFC 5905), +including broadcast, multicast, manycast clients and servers, and the orphan +mode. It also supports extra authentication schemes based on public-key +cryptography (RFC 5906). The NTP daemon (ntpd) should be considered +for systems which are normally kept permanently on. Systems which are required +to use broadcast or multicast IP, or to perform authentication of packets with +the Autokey protocol, should consider using ntpd. + +Refer to + + + https://docs.fedoraproject.org/en-US/fedora/rawhide/system-administrators-guide/servers/Configuring_NTP_Using_the_chrony_Suite/ + +for more detailed comparison of features of chronyd +and ntpd daemon features respectively, and for further guidance how to +choose between the two NTP daemons. + +The upstream manual pages at + http://chrony.tuxfamily.org/manual.html for +chronyd and + http://www.ntp.org for ntpd provide additional +information on the capabilities and configuration of each of the NTP daemons. + + + Vendor Approved Time Servers + The list of vendor-approved time servers + 0.pool.ntp.org,1.pool.ntp.org,2.pool.ntp.org,3.pool.ntp.org + 0.fedora.pool.ntp.org,1.fedora.pool.ntp.org,2.fedora.pool.ntp.org,3.fedora.pool.ntp.org + 0.rhel.pool.ntp.org,1.rhel.pool.ntp.org,2.rhel.pool.ntp.org,3.rhel.pool.ntp.org + 0.pool.ntp.org,1.pool.ntp.org,2.pool.ntp.org,3.pool.ntp.org + 0.suse.pool.ntp.org,1.suse.pool.ntp.org,2.suse.pool.ntp.org,3.suse.pool.ntp.org + 0.ntp.cloud.aliyuncs.com,1.ntp.aliyun.com,2.ntp1.aliyun.com,3.ntp1.cloud.aliyuncs.com + + + Maximum NTP or Chrony Poll + The maximum NTP or Chrony poll interval number in seconds specified as a power of two. + 17 + 16 + 10 + 10 + + + The Chrony package is installed + System time should be synchronized between all systems in an environment. This is +typically done by establishing an authoritative time server or set of servers and having all +systems synchronize their clocks to them. +The chrony package can be installed with the following command: + +$ sudo yum install chrony + BP28(R43) + 0988 + 1405 + FMT_SMF_EXT.1 + SRG-OS-000355-GPOS-00143 + 2.1.1 + Time synchronization is important to support time sensitive security mechanisms like +Kerberos and also ensures log files have consistent time records across the enterprise, +which aids in forensic investigations. + + CCE-82874-9 + +package --add=chrony + + include install_chrony + +class install_chrony { + package { 'chrony': + ensure => 'installed', + } +} + + - name: Ensure chrony is installed + package: + name: chrony + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82874-9 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_chrony_installed + + +[[packages]] +name = "chrony" +version = "*" + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if ! rpm -q --quiet "chrony" ; then + yum install -y "chrony" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Install the ntp service + The ntpd service should be installed. + NT012(R03) + 1 + 14 + 15 + 16 + 3 + 5 + 6 + APO11.04 + BAI03.05 + DSS05.04 + DSS05.07 + MEA02.01 + CCI-000160 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + CM-6(a) + PR.PT-1 + Req-10.4 + Time synchronization (using NTP) is required by almost all network and administrative tasks (syslog, cryptographic based services (authentication, etc.), etc.). Ntpd is regulary maintained and updated, supporting security features such as RFC 5906. + +package --add=ntp + + include install_ntp + +class install_ntp { + package { 'ntp': + ensure => 'installed', + } +} + + - name: Ensure ntp is installed + package: + name: ntp + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.4 + - enable_strategy + - high_severity + - low_complexity + - low_disruption + - no_reboot_needed + - package_ntp_installed + + +[[packages]] +name = "ntp" +version = "*" + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if ! rpm -q --quiet "ntp" ; then + yum install -y "ntp" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + The Chronyd service is enabled + chrony is a daemon which implements the Network Time Protocol (NTP) is designed to +synchronize system clocks across a variety of systems and use a source that is highly +accurate. More information on chrony can be found at + + http://chrony.tuxfamily.org/. +Chrony can be configured to be a client and/or a server. +To enable Chronyd service, you can run: +# systemctl enable chronyd.service +This recommendation only applies if chrony is in use on the system. + 0988 + 1405 + SRG-OS-000355-GPOS-00143 + 2.2.1.2 + If chrony is in use on the system proper configuration is vital to ensuring time +synchronization is working properly. + + CCE-82875-6 + include enable_chronyd + +class enable_chronyd { + service {'chronyd': + enable => true, + ensure => 'running', + } +} + + - name: Enable service chronyd + block: + + - name: Gather the package facts + package_facts: + manager: auto + + - name: Enable service chronyd + service: + name: chronyd + enabled: 'yes' + state: started + masked: 'no' + when: + - '"chrony" in ansible_facts.packages' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82875-6 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_chronyd_enabled + + +[customizations.services] +enabled = ["chronyd"] + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" unmask 'chronyd.service' +"$SYSTEMCTL_EXEC" start 'chronyd.service' +"$SYSTEMCTL_EXEC" enable 'chronyd.service' + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Enable the NTP Daemon + + +Run the following command to determine the current status of the +chronyd service: +$ sudo systemctl is-active chronyd +If the service is running, it should return the following: active +Note: The chronyd daemon is enabled by default. + + + +Run the following command to determine the current status of the +ntpd service: +$ sudo systemctl is-active ntpd +If the service is running, it should return the following: active +Note: The ntpd daemon is not enabled by default. Though as mentioned +in the previous sections in certain environments the ntpd daemon might +be preferred to be used rather than the chronyd one. Refer to: + + + https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/7/html/system_administrators_guide/ch-configuring_ntp_using_the_chrony_suite + +for guidance which NTP daemon to choose depending on the environment used. + 1 + 14 + 15 + 16 + 3 + 5 + 6 + APO11.04 + BAI03.05 + DSS05.04 + DSS05.07 + MEA02.01 + 3.3.7 + CCI-000160 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + 0988 + 1405 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + CM-6(a) + AU-8(1)(a) + AU-12(1) + PR.PT-1 + Req-10.4.1 + SRG-OS-000356-VMM-001340 + 2.2.1.1 + Enabling some of chronyd or ntpd services ensures +that the NTP daemon will be running and that the system will synchronize its +time to any servers specified. This is important whether the system is +configured to be a client (and synchronize only its own clock) or it is also +acting as an NTP server to other systems. Synchronizing time is essential for +authentication services such as Kerberos, but it is also important for +maintaining accurate logs and auditing possible security breaches. + +The chronyd and ntpd NTP daemons offer all of the +functionality of ntpdate, which is now deprecated. + + CCE-80874-1 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80874-1 + - NIST-800-171-3.3.7 + - NIST-800-53-AU-12(1) + - NIST-800-53-AU-8(1)(a) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.4.1 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_chronyd_or_ntpd_enabled + +- name: Gather the package facts + ansible.builtin.package_facts: + manager: auto + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80874-1 + - NIST-800-171-3.3.7 + - NIST-800-53-AU-12(1) + - NIST-800-53-AU-8(1)(a) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.4.1 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_chronyd_or_ntpd_enabled + +- name: Start ntpd service if ntp installed + service: + name: ntpd + enabled: 'yes' + state: started + masked: 'no' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '''ntp'' in ansible_facts.packages' + ignore_errors: true + tags: + - CCE-80874-1 + - NIST-800-171-3.3.7 + - NIST-800-53-AU-12(1) + - NIST-800-53-AU-8(1)(a) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.4.1 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_chronyd_or_ntpd_enabled + +- name: Start chronyd service if chrony or chronyd installed + service: + name: chronyd + enabled: 'yes' + state: started + masked: 'no' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ('chrony' in ansible_facts.packages) or ('chronyd' in ansible_facts.packages) + ignore_errors: true + tags: + - CCE-80874-1 + - NIST-800-171-3.3.7 + - NIST-800-53-AU-12(1) + - NIST-800-53-AU-8(1)(a) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.4.1 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_chronyd_or_ntpd_enabled + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if rpm --quiet -q "chrony" ; then + if ! /usr/sbin/pidof ntpd ; then + /usr/bin/systemctl enable "chronyd" + /usr/bin/systemctl start "chronyd" + # The service may not be running because it has been started and failed, + # so let's reset the state so OVAL checks pass. + # Service should be 'inactive', not 'failed' after reboot though. + /usr/bin/systemctl reset-failed "chronyd" + fi +elif rpm --quiet -q "ntp" ; then + /usr/bin/systemctl enable "ntpd" + /usr/bin/systemctl start "ntpd" + # The service may not be running because it has been started and failed, + # so let's reset the state so OVAL checks pass. + # Service should be 'inactive', not 'failed' after reboot though. + /usr/bin/systemctl reset-failed "ntpd" +else + if ! rpm -q --quiet "chrony" ; then + yum install -y "chrony" + fi + /usr/bin/systemctl enable "chronyd" + /usr/bin/systemctl start "chronyd" + # The service may not be running because it has been started and failed, + # so let's reset the state so OVAL checks pass. + # Service should be 'inactive', not 'failed' after reboot though. + /usr/bin/systemctl reset-failed "chronyd" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Enable the NTP Daemon + +The ntpd service can be enabled with the following command: +$ sudo systemctl enable ntpd.service + NT012(R03) + 1 + 14 + 15 + 16 + 3 + 5 + 6 + APO11.04 + BAI03.05 + DSS05.04 + DSS05.07 + MEA02.01 + CCI-000160 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + CM-6(a) + AU-8(1)(a) + PR.PT-1 + Req-10.4 + Enabling the ntpd service ensures that the ntpd +service will be running and that the system will synchronize its time to +any servers specified. This is important whether the system is configured to be +a client (and synchronize only its own clock) or it is also acting as an NTP +server to other systems. Synchronizing time is essential for authentication +services such as Kerberos, but it is also important for maintaining accurate +logs and auditing possible security breaches. + +The NTP daemon offers all of the functionality of ntpdate, which is now +deprecated. + include enable_ntp + +class enable_ntp { + service {'ntp': + enable => true, + ensure => 'running', + } +} + + - name: Enable service ntp + block: + + - name: Gather the package facts + package_facts: + manager: auto + + - name: Enable service ntp + service: + name: ntp + enabled: 'yes' + state: started + masked: 'no' + when: + - '"ntp" in ansible_facts.packages' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AU-8(1)(a) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.4 + - enable_strategy + - high_severity + - low_complexity + - low_disruption + - no_reboot_needed + - service_ntp_enabled + + +[customizations.services] +enabled = ["ntp"] + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" unmask 'ntp.service' +"$SYSTEMCTL_EXEC" start 'ntp.service' +"$SYSTEMCTL_EXEC" enable 'ntp.service' + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Enable the NTP Daemon + +The ntpd service can be enabled with the following command: +$ sudo systemctl enable ntpd.service + 1 + 14 + 15 + 16 + 3 + 5 + 6 + APO11.04 + BAI03.05 + DSS05.04 + DSS05.07 + MEA02.01 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + CM-6(a) + AU-8(1)(a) + PR.PT-1 + Req-10.4 + Enabling the ntpd service ensures that the ntpd +service will be running and that the system will synchronize its time to +any servers specified. This is important whether the system is configured to be +a client (and synchronize only its own clock) or it is also acting as an NTP +server to other systems. Synchronizing time is essential for authentication +services such as Kerberos, but it is also important for maintaining accurate +logs and auditing possible security breaches. + +The NTP daemon offers all of the functionality of ntpdate, which is now +deprecated. + + include enable_ntpd + +class enable_ntpd { + service {'ntpd': + enable => true, + ensure => 'running', + } +} + + - name: Gather the package facts + package_facts: + manager: auto + tags: + - NIST-800-53-AU-8(1)(a) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.4 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_ntpd_enabled + +- name: Enable service ntpd + block: + + - name: Gather the package facts + package_facts: + manager: auto + + - name: Enable service ntpd + service: + name: ntpd + enabled: 'yes' + state: started + masked: 'no' + when: + - '"ntp" in ansible_facts.packages' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"ntp" in ansible_facts.packages' + tags: + - NIST-800-53-AU-8(1)(a) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.4 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_ntpd_enabled + + +[customizations.services] +enabled = ["ntpd"] + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && { rpm --quiet -q ntp; }; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" unmask 'ntpd.service' +"$SYSTEMCTL_EXEC" start 'ntpd.service' +"$SYSTEMCTL_EXEC" enable 'ntpd.service' + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable chrony daemon from acting as server + The port option in /etc/chrony.conf can be set to +0 to make chrony daemon to never open any listening port +for server operation and to operate strictly in a client-only mode. + CCI-000381 + AU-8(1) + AU-12(1) + FMT_SMF_EXT.1 + SRG-OS-000096-GPOS-00050 + SRG-OS-000095-GPOS-00049 + RHEL-08-030741 + SV-230485r627750_rule + Minimizing the exposure of the server functionality of the chrony +daemon diminishes the attack surface. + + CCE-82988-7 + - name: Disable chrony daemon from acting as server + block: + + - name: Check for duplicate values + lineinfile: + path: /etc/chrony.conf + create: false + regexp: ^\s*port\s+ + state: absent + check_mode: true + changed_when: false + register: dupes + + - name: Deduplicate values from /etc/chrony.conf + lineinfile: + path: /etc/chrony.conf + create: false + regexp: ^\s*port\s+ + state: absent + when: dupes.found is defined and dupes.found > 1 + + - name: Insert correct line to /etc/chrony.conf + lineinfile: + path: /etc/chrony.conf + create: true + regexp: ^\s*port\s+ + line: port 0 + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82988-7 + - DISA-STIG-RHEL-08-030741 + - NIST-800-53-AU-12(1) + - NIST-800-53-AU-8(1) + - chronyd_client_only + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,{{ %23%20Allow%20for%20extra%20configuration%20files.%20This%20is%20useful%0A%23%20for%20admins%20specifying%20their%20own%20NTP%20servers%0Ainclude%20/etc/chrony.d/%2A.conf%0A%0A%23%20Set%20chronyd%20as%20client-only.%0Aport%200%0A%0A%23%20Disable%20chronyc%20from%20the%20network%0Acmdport%200%0A%0A%23%20Record%20the%20rate%20at%20which%20the%20system%20clock%20gains/losses%20time.%0Adriftfile%20/var/lib/chrony/drift%0A%0A%23%20Allow%20the%20system%20clock%20to%20be%20stepped%20in%20the%20first%20three%20updates%0A%23%20if%20its%20offset%20is%20larger%20than%201%20second.%0Amakestep%201.0%203%0A%0A%23%20Enable%20kernel%20synchronization%20of%20the%20real-time%20clock%20%28RTC%29.%0Artcsync%0A%0A%23%20Enable%20hardware%20timestamping%20on%20all%20interfaces%20that%20support%20it.%0A%23hwtimestamp%20%2A%0A%0A%23%20Increase%20the%20minimum%20number%20of%20selectable%20sources%20required%20to%20adjust%0A%23%20the%20system%20clock.%0A%23minsources%202%0A%0A%23%20Allow%20NTP%20client%20access%20from%20local%20network.%0A%23allow%20192.168.0.0/16%0A%0A%23%20Serve%20time%20even%20if%20not%20synchronized%20to%20a%20time%20source.%0A%23local%20stratum%2010%0A%0A%23%20Require%20authentication%20%28nts%20or%20key%20option%29%20for%20all%20NTP%20sources.%0A%23authselectmode%20require%0A%0A%23%20Specify%20file%20containing%20keys%20for%20NTP%20authentication.%0Akeyfile%20/etc/chrony.keys%0A%0A%23%20Insert/delete%20leap%20seconds%20by%20slewing%20instead%20of%20stepping.%0A%23leapsecmode%20slew%0A%0A%23%20Get%20TAI-UTC%20offset%20and%20leap%20seconds%20from%20the%20system%20tz%20database.%0Aleapsectz%20right/UTC%0A%0A%23%20Specify%20directory%20for%20log%20files.%0Alogdir%20/var/log/chrony%0A%0A%23%20Select%20which%20information%20is%20logged.%0A%23log%20measurements%20statistics%20tracking }} + mode: 420 + overwrite: true + path: /etc/chrony.conf + - contents: + source: data:, + mode: 420 + overwrite: true + path: /etc/chrony.d/.mco-keep + - contents: + source: data:,{{ %23%0A%23%20This%20file%20controls%20the%20configuration%20of%20the%20ntp%20server%0A%23%20%7B%7B.var_multiple_time_servers%7D%7D%20we%20have%20to%20put%20variable%20array%20name%20here%20for%20mutilines%20remediation%20%0A%7B%7B%24var_time_service_set_maxpoll%3A%3D.var_time_service_set_maxpoll%7D%7D%0A%7B%7Brange%20%24element%3A%3D.var_multiple_time_servers%7CtoArrayByComma%7D%7Dserver%20%7B%7B%24element%7D%7D%20minpoll%204%20maxpoll%20%7B%7B%24var_time_service_set_maxpoll%7D%7D%0A%7B%7Bend%7D%7D }} + mode: 420 + overwrite: true + path: /etc/chrony.d/ntp-server.conf + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/chrony.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^port") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s %s" "$stripped_key" "0" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^port\\>" "/etc/chrony.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^port\\>.*/$escaped_formatted_output/gi" "/etc/chrony.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-82988-7" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/chrony.conf" >> "/etc/chrony.conf" + printf '%s\n' "$formatted_output" >> "/etc/chrony.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable network management of chrony daemon + The cmdport option in /etc/chrony.conf can be set to +0 to stop chrony daemon from listening on the UDP port 323 +for management connections made by chronyc. + CCI-000381 + CM-7(1) + FMT_SMF_EXT.1 + SRG-OS-000096-GPOS-00050 + SRG-OS-000095-GPOS-00049 + RHEL-08-030742 + SV-230486r627750_rule + Not exposing the management interface of the chrony daemon on +the network diminishes the attack space. + + CCE-82840-0 + - name: Disable network management of chrony daemon + block: + + - name: Check for duplicate values + lineinfile: + path: /etc/chrony.conf + create: false + regexp: ^\s*cmdport\s+ + state: absent + check_mode: true + changed_when: false + register: dupes + + - name: Deduplicate values from /etc/chrony.conf + lineinfile: + path: /etc/chrony.conf + create: false + regexp: ^\s*cmdport\s+ + state: absent + when: dupes.found is defined and dupes.found > 1 + + - name: Insert correct line to /etc/chrony.conf + lineinfile: + path: /etc/chrony.conf + create: true + regexp: ^\s*cmdport\s+ + line: cmdport 0 + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82840-0 + - DISA-STIG-RHEL-08-030742 + - NIST-800-53-CM-7(1) + - chronyd_no_chronyc_network + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,{{ %23%20Allow%20for%20extra%20configuration%20files.%20This%20is%20useful%0A%23%20for%20admins%20specifying%20their%20own%20NTP%20servers%0Ainclude%20/etc/chrony.d/%2A.conf%0A%0A%23%20Set%20chronyd%20as%20client-only.%0Aport%200%0A%0A%23%20Disable%20chronyc%20from%20the%20network%0Acmdport%200%0A%0A%23%20Record%20the%20rate%20at%20which%20the%20system%20clock%20gains/losses%20time.%0Adriftfile%20/var/lib/chrony/drift%0A%0A%23%20Allow%20the%20system%20clock%20to%20be%20stepped%20in%20the%20first%20three%20updates%0A%23%20if%20its%20offset%20is%20larger%20than%201%20second.%0Amakestep%201.0%203%0A%0A%23%20Enable%20kernel%20synchronization%20of%20the%20real-time%20clock%20%28RTC%29.%0Artcsync%0A%0A%23%20Enable%20hardware%20timestamping%20on%20all%20interfaces%20that%20support%20it.%0A%23hwtimestamp%20%2A%0A%0A%23%20Increase%20the%20minimum%20number%20of%20selectable%20sources%20required%20to%20adjust%0A%23%20the%20system%20clock.%0A%23minsources%202%0A%0A%23%20Allow%20NTP%20client%20access%20from%20local%20network.%0A%23allow%20192.168.0.0/16%0A%0A%23%20Serve%20time%20even%20if%20not%20synchronized%20to%20a%20time%20source.%0A%23local%20stratum%2010%0A%0A%23%20Require%20authentication%20%28nts%20or%20key%20option%29%20for%20all%20NTP%20sources.%0A%23authselectmode%20require%0A%0A%23%20Specify%20file%20containing%20keys%20for%20NTP%20authentication.%0Akeyfile%20/etc/chrony.keys%0A%0A%23%20Insert/delete%20leap%20seconds%20by%20slewing%20instead%20of%20stepping.%0A%23leapsecmode%20slew%0A%0A%23%20Get%20TAI-UTC%20offset%20and%20leap%20seconds%20from%20the%20system%20tz%20database.%0Aleapsectz%20right/UTC%0A%0A%23%20Specify%20directory%20for%20log%20files.%0Alogdir%20/var/log/chrony%0A%0A%23%20Select%20which%20information%20is%20logged.%0A%23log%20measurements%20statistics%20tracking }} + mode: 420 + overwrite: true + path: /etc/chrony.conf + - contents: + source: data:, + mode: 420 + overwrite: true + path: /etc/chrony.d/.mco-keep + - contents: + source: data:,{{ %23%0A%23%20This%20file%20controls%20the%20configuration%20of%20the%20ntp%20server%0A%23%20%7B%7B.var_multiple_time_servers%7D%7D%20we%20have%20to%20put%20variable%20array%20name%20here%20for%20mutilines%20remediation%20%0A%7B%7B%24var_time_service_set_maxpoll%3A%3D.var_time_service_set_maxpoll%7D%7D%0A%7B%7Brange%20%24element%3A%3D.var_multiple_time_servers%7CtoArrayByComma%7D%7Dserver%20%7B%7B%24element%7D%7D%20minpoll%204%20maxpoll%20%7B%7B%24var_time_service_set_maxpoll%7D%7D%0A%7B%7Bend%7D%7D }} + mode: 420 + overwrite: true + path: /etc/chrony.d/ntp-server.conf + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/chrony.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^cmdport") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s %s" "$stripped_key" "0" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^cmdport\\>" "/etc/chrony.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^cmdport\\>.*/$escaped_formatted_output/gi" "/etc/chrony.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-82840-0" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/chrony.conf" >> "/etc/chrony.conf" + printf '%s\n' "$formatted_output" >> "/etc/chrony.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure Time Service Maxpoll Interval + The maxpoll should be configured to + in /etc/ntp.conf or +/etc/chrony.conf to continuously poll time servers. To configure +maxpoll in /etc/ntp.conf or /etc/chrony.conf +add the following after each `server`, `pool` or `peer` entry: +maxpoll +to server directives. If using chrony any pool directives +should be configured too. +If no server or pool directives are configured, the rule evaluates +to pass. + 1 + 14 + 15 + 16 + 3 + 5 + 6 + APO11.04 + BAI03.05 + DSS05.04 + DSS05.07 + MEA02.01 + CCI-001891 + CCI-002046 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + CM-6(a) + AU-8(1)(b) + AU-12(1) + PR.PT-1 + SRG-OS-000355-GPOS-00143 + SRG-OS-000356-GPOS-00144 + SRG-OS-000359-GPOS-00146 + RHEL-08-030740 + SV-230484r627750_rule + Inaccurate time stamps make it more difficult to correlate events and can lead to an inaccurate analysis. Determining the correct time a particular event occurred on a system is critical when conducting forensic analysis and investigating system events. Sources outside the configured acceptable allowance (drift) may be inaccurate. +Synchronizing internal information system clocks provides uniformity of time stamps for information systems with multiple system clocks and systems connected over a network. +Organizations should consider endpoints that may not have regular access to the authoritative time server (e.g., mobile, teleworking, and tactical endpoints). + + CCE-84059-5 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-84059-5 + - DISA-STIG-RHEL-08-030740 + - NIST-800-53-AU-12(1) + - NIST-800-53-AU-8(1)(b) + - NIST-800-53-CM-6(a) + - chronyd_or_ntpd_set_maxpoll + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy +- name: XCCDF Value var_time_service_set_maxpoll # promote to variable + set_fact: + var_time_service_set_maxpoll: !!str + tags: + - always + +- name: Check that /etc/ntp.conf exist + stat: + path: /etc/ntp.conf + register: ntp_conf_exist_result + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ( "chrony" in ansible_facts.packages or "ntp" in ansible_facts.packages ) + tags: + - CCE-84059-5 + - DISA-STIG-RHEL-08-030740 + - NIST-800-53-AU-12(1) + - NIST-800-53-AU-8(1)(b) + - NIST-800-53-CM-6(a) + - chronyd_or_ntpd_set_maxpoll + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Update the maxpoll values in /etc/ntp.conf + replace: + path: /etc/ntp.conf + regexp: ^(server.*maxpoll)[ ]+[0-9]+(.*)$ + replace: \1 {{ var_time_service_set_maxpoll }}\2 + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ( "chrony" in ansible_facts.packages or "ntp" in ansible_facts.packages ) + - ntp_conf_exist_result.stat.exists + tags: + - CCE-84059-5 + - DISA-STIG-RHEL-08-030740 + - NIST-800-53-AU-12(1) + - NIST-800-53-AU-8(1)(b) + - NIST-800-53-CM-6(a) + - chronyd_or_ntpd_set_maxpoll + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Set the maxpoll values in /etc/ntp.conf + replace: + path: /etc/ntp.conf + regexp: (^server\s+((?!maxpoll).)*)$ + replace: \1 maxpoll {{ var_time_service_set_maxpoll }}\n + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ( "chrony" in ansible_facts.packages or "ntp" in ansible_facts.packages ) + - ntp_conf_exist_result.stat.exists + tags: + - CCE-84059-5 + - DISA-STIG-RHEL-08-030740 + - NIST-800-53-AU-12(1) + - NIST-800-53-AU-8(1)(b) + - NIST-800-53-CM-6(a) + - chronyd_or_ntpd_set_maxpoll + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Check that /etc/chrony.conf exist + stat: + path: /etc/chrony.conf + register: chrony_conf_exist_result + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ( "chrony" in ansible_facts.packages or "ntp" in ansible_facts.packages ) + tags: + - CCE-84059-5 + - DISA-STIG-RHEL-08-030740 + - NIST-800-53-AU-12(1) + - NIST-800-53-AU-8(1)(b) + - NIST-800-53-CM-6(a) + - chronyd_or_ntpd_set_maxpoll + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Get get conf files from /etc/chrony.conf + shell: | + set -o pipefail + CHRONY_NAME=/etc/chrony.conf + CHRONY_PATH=${CHRONY_NAME%%.*} + find ${CHRONY_PATH}.* -type f -name '*.conf' + register: update_chrony_files + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ( "chrony" in ansible_facts.packages or "ntp" in ansible_facts.packages ) + - chrony_conf_exist_result.stat.exists + changed_when: false + tags: + - CCE-84059-5 + - DISA-STIG-RHEL-08-030740 + - NIST-800-53-AU-12(1) + - NIST-800-53-AU-8(1)(b) + - NIST-800-53-CM-6(a) + - chronyd_or_ntpd_set_maxpoll + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Update the maxpoll values in /etc/chrony.conf + replace: + path: '{{ item }}' + regexp: ^((?:server|pool|peer).*maxpoll)[ ]+[0-9]+(.*)$ + replace: \1 {{ var_time_service_set_maxpoll }}\2 + loop: '{{ update_chrony_files.stdout_lines|list|flatten|unique }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ( "chrony" in ansible_facts.packages or "ntp" in ansible_facts.packages ) + - chrony_conf_exist_result.stat.exists + tags: + - CCE-84059-5 + - DISA-STIG-RHEL-08-030740 + - NIST-800-53-AU-12(1) + - NIST-800-53-AU-8(1)(b) + - NIST-800-53-CM-6(a) + - chronyd_or_ntpd_set_maxpoll + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Set the maxpoll values in /etc/chrony.conf + replace: + path: '{{ item }}' + regexp: (^(?:server|pool|peer)\s+((?!maxpoll).)*)$ + replace: \1 maxpoll {{ var_time_service_set_maxpoll }}\n + loop: '{{ update_chrony_files.stdout_lines|list|flatten|unique }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ( "chrony" in ansible_facts.packages or "ntp" in ansible_facts.packages ) + - chrony_conf_exist_result.stat.exists + tags: + - CCE-84059-5 + - DISA-STIG-RHEL-08-030740 + - NIST-800-53-AU-12(1) + - NIST-800-53-AU-8(1)(b) + - NIST-800-53-CM-6(a) + - chronyd_or_ntpd_set_maxpoll + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,{{ %23%20Allow%20for%20extra%20configuration%20files.%20This%20is%20useful%0A%23%20for%20admins%20specifying%20their%20own%20NTP%20servers%0Ainclude%20/etc/chrony.d/%2A.conf%0A%0A%23%20Set%20chronyd%20as%20client-only.%0Aport%200%0A%0A%23%20Disable%20chronyc%20from%20the%20network%0Acmdport%200%0A%0A%23%20Record%20the%20rate%20at%20which%20the%20system%20clock%20gains/losses%20time.%0Adriftfile%20/var/lib/chrony/drift%0A%0A%23%20Allow%20the%20system%20clock%20to%20be%20stepped%20in%20the%20first%20three%20updates%0A%23%20if%20its%20offset%20is%20larger%20than%201%20second.%0Amakestep%201.0%203%0A%0A%23%20Enable%20kernel%20synchronization%20of%20the%20real-time%20clock%20%28RTC%29.%0Artcsync%0A%0A%23%20Enable%20hardware%20timestamping%20on%20all%20interfaces%20that%20support%20it.%0A%23hwtimestamp%20%2A%0A%0A%23%20Increase%20the%20minimum%20number%20of%20selectable%20sources%20required%20to%20adjust%0A%23%20the%20system%20clock.%0A%23minsources%202%0A%0A%23%20Allow%20NTP%20client%20access%20from%20local%20network.%0A%23allow%20192.168.0.0/16%0A%0A%23%20Serve%20time%20even%20if%20not%20synchronized%20to%20a%20time%20source.%0A%23local%20stratum%2010%0A%0A%23%20Require%20authentication%20%28nts%20or%20key%20option%29%20for%20all%20NTP%20sources.%0A%23authselectmode%20require%0A%0A%23%20Specify%20file%20containing%20keys%20for%20NTP%20authentication.%0Akeyfile%20/etc/chrony.keys%0A%0A%23%20Insert/delete%20leap%20seconds%20by%20slewing%20instead%20of%20stepping.%0A%23leapsecmode%20slew%0A%0A%23%20Get%20TAI-UTC%20offset%20and%20leap%20seconds%20from%20the%20system%20tz%20database.%0Aleapsectz%20right/UTC%0A%0A%23%20Specify%20directory%20for%20log%20files.%0Alogdir%20/var/log/chrony%0A%0A%23%20Select%20which%20information%20is%20logged.%0A%23log%20measurements%20statistics%20tracking }} + mode: 420 + overwrite: true + path: /etc/chrony.conf + - contents: + source: data:, + mode: 420 + overwrite: true + path: /etc/chrony.d/.mco-keep + - contents: + source: data:,{{ %23%0A%23%20This%20file%20controls%20the%20configuration%20of%20the%20ntp%20server%0A%23%20%7B%7B.var_multiple_time_servers%7D%7D%20we%20have%20to%20put%20variable%20array%20name%20here%20for%20mutilines%20remediation%20%0A%7B%7B%24var_time_service_set_maxpoll%3A%3D.var_time_service_set_maxpoll%7D%7D%0A%7B%7Brange%20%24element%3A%3D.var_multiple_time_servers%7CtoArrayByComma%7D%7Dserver%20%7B%7B%24element%7D%7D%20minpoll%204%20maxpoll%20%7B%7B%24var_time_service_set_maxpoll%7D%7D%0A%7B%7Bend%7D%7D }} + mode: 420 + overwrite: true + path: /etc/chrony.d/ntp-server.conf + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && { ( rpm --quiet -q chrony || rpm --quiet -q ntp ); }; then + +var_time_service_set_maxpoll='' + + + + +pof="/usr/sbin/pidof" + + +CONFIG_FILES="/etc/ntp.conf" +$pof ntpd || { + CHRONY_NAME=/etc/chrony.conf + CHRONY_PATH=${CHRONY_NAME%%.*} + CONFIG_FILES=$(find ${CHRONY_PATH}.* -type f -name '*.conf') +} + +# get list of ntp files + +for config_file in $CONFIG_FILES; do + # Set maxpoll values to var_time_service_set_maxpoll + sed -i "s/^\(\(server\|pool\|peer\).*maxpoll\) [0-9][0-9]*\(.*\)$/\1 $var_time_service_set_maxpoll \3/" "$config_file" +done + + + + +for config_file in $CONFIG_FILES; do + # Add maxpoll to server, pool or peer entries without maxpoll + grep "^\(server\|pool\|peer\)" "$config_file" | grep -v maxpoll | while read -r line ; do + sed -i "s/$line/& maxpoll $var_time_service_set_maxpoll/" "$config_file" + done +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Specify Additional Remote NTP Servers + Depending on specific functional requirements of a concrete +production environment, the Red Hat Enterprise Linux 8 system can be +configured to utilize the services of the chronyd NTP daemon (the +default), or services of the ntpd NTP daemon. Refer to + + + https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/7/html/system_administrators_guide/ch-configuring_ntp_using_the_chrony_suite + +for more detailed comparison of the features of both of the choices, and for +further guidance how to choose between the two NTP daemons. + +Additional NTP servers can be specified for time synchronization. To do so, +perform the following: + if the system is configured to use the chronyd as the NTP daemon +(the default), edit the file /etc/chrony.conf as follows, if the system is configured to use the ntpd as the NTP daemon, +edit the file /etc/ntp.conf as documented below. +Add additional lines of the following form, substituting the IP address or +hostname of a remote NTP server for ntpserver: +server ntpserver + 1 + 14 + 15 + 16 + 3 + 5 + 6 + APO11.04 + BAI03.05 + DSS05.04 + DSS05.07 + MEA02.01 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + 0988 + 1405 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + CM-6(a) + AU-8(1)(a) + AU-8(2) + AU-12(1) + PR.PT-1 + Req-10.4.3 + Specifying additional NTP servers increases the availability of +accurate time data, in the event that one of the specified servers becomes +unavailable. This is typical for a system acting as an NTP server for +other systems. + + CCE-80764-4 + - name: XCCDF Value var_multiple_time_servers # promote to variable + set_fact: + var_multiple_time_servers: !!str + tags: + - always + +- name: Detect if chrony configuration file is present + find: + path: /etc + patterns: chrony.conf + register: chrony_server_config + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80764-4 + - NIST-800-53-AU-12(1) + - NIST-800-53-AU-8(1)(a) + - NIST-800-53-AU-8(2) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.4.3 + - chronyd_or_ntpd_specify_multiple_servers + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Configure multiple time servers in chrony config + lineinfile: + path: /etc/chrony.conf + line: server {{ item }} + state: present + create: true + loop: '{{ var_multiple_time_servers.split(",") }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - chrony_server_config.matched == 1 + tags: + - CCE-80764-4 + - NIST-800-53-AU-12(1) + - NIST-800-53-AU-8(1)(a) + - NIST-800-53-AU-8(2) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.4.3 + - chronyd_or_ntpd_specify_multiple_servers + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Detect if NTP configuration file is present + find: + path: /etc + patterns: ntp.conf + register: ntp_server_config + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80764-4 + - NIST-800-53-AU-12(1) + - NIST-800-53-AU-8(1)(a) + - NIST-800-53-AU-8(2) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.4.3 + - chronyd_or_ntpd_specify_multiple_servers + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Configure multiple time servers in NTP config + lineinfile: + path: /etc/chrony.conf + line: pool {{ item }} + state: present + create: true + loop: '{{ var_multiple_time_servers.split(",") }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ntp_server_config.matched == 1 + tags: + - CCE-80764-4 + - NIST-800-53-AU-12(1) + - NIST-800-53-AU-8(1)(a) + - NIST-800-53-AU-8(2) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.4.3 + - chronyd_or_ntpd_specify_multiple_servers + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,{{ %23%20Allow%20for%20extra%20configuration%20files.%20This%20is%20useful%0A%23%20for%20admins%20specifying%20their%20own%20NTP%20servers%0Ainclude%20/etc/chrony.d/%2A.conf%0A%0A%23%20Set%20chronyd%20as%20client-only.%0Aport%200%0A%0A%23%20Disable%20chronyc%20from%20the%20network%0Acmdport%200%0A%0A%23%20Record%20the%20rate%20at%20which%20the%20system%20clock%20gains/losses%20time.%0Adriftfile%20/var/lib/chrony/drift%0A%0A%23%20Allow%20the%20system%20clock%20to%20be%20stepped%20in%20the%20first%20three%20updates%0A%23%20if%20its%20offset%20is%20larger%20than%201%20second.%0Amakestep%201.0%203%0A%0A%23%20Enable%20kernel%20synchronization%20of%20the%20real-time%20clock%20%28RTC%29.%0Artcsync%0A%0A%23%20Enable%20hardware%20timestamping%20on%20all%20interfaces%20that%20support%20it.%0A%23hwtimestamp%20%2A%0A%0A%23%20Increase%20the%20minimum%20number%20of%20selectable%20sources%20required%20to%20adjust%0A%23%20the%20system%20clock.%0A%23minsources%202%0A%0A%23%20Allow%20NTP%20client%20access%20from%20local%20network.%0A%23allow%20192.168.0.0/16%0A%0A%23%20Serve%20time%20even%20if%20not%20synchronized%20to%20a%20time%20source.%0A%23local%20stratum%2010%0A%0A%23%20Require%20authentication%20%28nts%20or%20key%20option%29%20for%20all%20NTP%20sources.%0A%23authselectmode%20require%0A%0A%23%20Specify%20file%20containing%20keys%20for%20NTP%20authentication.%0Akeyfile%20/etc/chrony.keys%0A%0A%23%20Insert/delete%20leap%20seconds%20by%20slewing%20instead%20of%20stepping.%0A%23leapsecmode%20slew%0A%0A%23%20Get%20TAI-UTC%20offset%20and%20leap%20seconds%20from%20the%20system%20tz%20database.%0Aleapsectz%20right/UTC%0A%0A%23%20Specify%20directory%20for%20log%20files.%0Alogdir%20/var/log/chrony%0A%0A%23%20Select%20which%20information%20is%20logged.%0A%23log%20measurements%20statistics%20tracking }} + mode: 420 + overwrite: true + path: /etc/chrony.conf + - contents: + source: data:, + mode: 420 + overwrite: true + path: /etc/chrony.d/.mco-keep + - contents: + source: data:,{{ %23%0A%23%20This%20file%20controls%20the%20configuration%20of%20the%20ntp%20server%0A%23%20%7B%7B.var_multiple_time_servers%7D%7D%20we%20have%20to%20put%20variable%20array%20name%20here%20for%20mutilines%20remediation%20%0A%7B%7B%24var_time_service_set_maxpoll%3A%3D.var_time_service_set_maxpoll%7D%7D%0A%7B%7Brange%20%24element%3A%3D.var_multiple_time_servers%7CtoArrayByComma%7D%7Dserver%20%7B%7B%24element%7D%7D%20minpoll%204%20maxpoll%20%7B%7B%24var_time_service_set_maxpoll%7D%7D%0A%7B%7Bend%7D%7D }} + mode: 420 + overwrite: true + path: /etc/chrony.d/ntp-server.conf + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_multiple_time_servers='' + + +config_file="/etc/ntp.conf" +/usr/sbin/pidof ntpd || config_file="/etc/chrony.conf" + +if ! [ "$(grep -c '^server' "$config_file")" -gt 1 ] ; then + if ! grep -q '#[[:space:]]*server' "$config_file" ; then + for server in $(echo "$var_multiple_time_servers" | tr ',' '\n') ; do + printf '\nserver %s' "$server" >> "$config_file" + done + else + sed -i 's/#[ \t]*server/server/g' "$config_file" + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + Specify a Remote NTP Server + Depending on specific functional requirements of a concrete +production environment, the Red Hat Enterprise Linux 8 system can be +configured to utilize the services of the chronyd NTP daemon (the +default), or services of the ntpd NTP daemon. Refer to + + + https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/7/html/system_administrators_guide/ch-configuring_ntp_using_the_chrony_suite + +for more detailed comparison of the features of both of the choices, and for +further guidance how to choose between the two NTP daemons. + +To specify a remote NTP server for time synchronization, perform the following: + if the system is configured to use the chronyd as the NTP daemon (the +default), edit the file /etc/chrony.conf as follows, if the system is configured to use the ntpd as the NTP daemon, +edit the file /etc/ntp.conf as documented below. +Add or correct the following lines, substituting the IP or hostname of a remote +NTP server for ntpserver: +server ntpserver +This instructs the NTP software to contact that remote server to obtain time +data. + 1 + 14 + 15 + 16 + 3 + 5 + 6 + APO11.04 + BAI03.05 + DSS05.04 + DSS05.07 + MEA02.01 + 3.3.7 + CCI-000160 + CCI-001891 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + CM-6(a) + AU-8(1)(a) + AU-8(2) + AU-12(1) + PR.PT-1 + Req-10.4.1 + Req-10.4.3 + SRG-OS-000355-VMM-001330 + 2.2.1.2 + Synchronizing with an NTP server makes it possible to collate system +logs from multiple sources or correlate computer events with real time events. + + CCE-80765-1 + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,{{ %23%20Allow%20for%20extra%20configuration%20files.%20This%20is%20useful%0A%23%20for%20admins%20specifying%20their%20own%20NTP%20servers%0Ainclude%20/etc/chrony.d/%2A.conf%0A%0A%23%20Set%20chronyd%20as%20client-only.%0Aport%200%0A%0A%23%20Disable%20chronyc%20from%20the%20network%0Acmdport%200%0A%0A%23%20Record%20the%20rate%20at%20which%20the%20system%20clock%20gains/losses%20time.%0Adriftfile%20/var/lib/chrony/drift%0A%0A%23%20Allow%20the%20system%20clock%20to%20be%20stepped%20in%20the%20first%20three%20updates%0A%23%20if%20its%20offset%20is%20larger%20than%201%20second.%0Amakestep%201.0%203%0A%0A%23%20Enable%20kernel%20synchronization%20of%20the%20real-time%20clock%20%28RTC%29.%0Artcsync%0A%0A%23%20Enable%20hardware%20timestamping%20on%20all%20interfaces%20that%20support%20it.%0A%23hwtimestamp%20%2A%0A%0A%23%20Increase%20the%20minimum%20number%20of%20selectable%20sources%20required%20to%20adjust%0A%23%20the%20system%20clock.%0A%23minsources%202%0A%0A%23%20Allow%20NTP%20client%20access%20from%20local%20network.%0A%23allow%20192.168.0.0/16%0A%0A%23%20Serve%20time%20even%20if%20not%20synchronized%20to%20a%20time%20source.%0A%23local%20stratum%2010%0A%0A%23%20Require%20authentication%20%28nts%20or%20key%20option%29%20for%20all%20NTP%20sources.%0A%23authselectmode%20require%0A%0A%23%20Specify%20file%20containing%20keys%20for%20NTP%20authentication.%0Akeyfile%20/etc/chrony.keys%0A%0A%23%20Insert/delete%20leap%20seconds%20by%20slewing%20instead%20of%20stepping.%0A%23leapsecmode%20slew%0A%0A%23%20Get%20TAI-UTC%20offset%20and%20leap%20seconds%20from%20the%20system%20tz%20database.%0Aleapsectz%20right/UTC%0A%0A%23%20Specify%20directory%20for%20log%20files.%0Alogdir%20/var/log/chrony%0A%0A%23%20Select%20which%20information%20is%20logged.%0A%23log%20measurements%20statistics%20tracking }} + mode: 420 + overwrite: true + path: /etc/chrony.conf + - contents: + source: data:, + mode: 420 + overwrite: true + path: /etc/chrony.d/.mco-keep + - contents: + source: data:,{{ %23%0A%23%20This%20file%20controls%20the%20configuration%20of%20the%20ntp%20server%0A%23%20%7B%7B.var_multiple_time_servers%7D%7D%20we%20have%20to%20put%20variable%20array%20name%20here%20for%20mutilines%20remediation%20%0A%7B%7B%24var_time_service_set_maxpoll%3A%3D.var_time_service_set_maxpoll%7D%7D%0A%7B%7Brange%20%24element%3A%3D.var_multiple_time_servers%7CtoArrayByComma%7D%7Dserver%20%7B%7B%24element%7D%7D%20minpoll%204%20maxpoll%20%7B%7B%24var_time_service_set_maxpoll%7D%7D%0A%7B%7Bend%7D%7D }} + mode: 420 + overwrite: true + path: /etc/chrony.d/ntp-server.conf + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && { ( [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && ( rpm --quiet -q chrony || rpm --quiet -q ntp ) ); }; then + +var_multiple_time_servers='' + + +config_file="/etc/ntp.conf" +/usr/sbin/pidof ntpd || config_file="/etc/chrony.conf" + +if ! grep -q ^server "$config_file" ; then + if ! grep -q '#[[:space:]]*server' "$config_file" ; then + for server in $(echo "$var_multiple_time_servers" | tr ',' '\n') ; do + printf '\nserver %s' "$server" >> "$config_file" + done + else + sed -i 's/#[ \t]*server/server/g' "$config_file" + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure that chronyd is running under chrony user account + chrony is a daemon which implements the Network Time Protocol (NTP). It is designed to +synchronize system clocks across a variety of systems and use a source that is highly +accurate. More information on chrony can be found at + + http://chrony.tuxfamily.org/. +Chrony can be configured to be a client and/or a server. +To ensure that chronyd is running under chrony user account, +remove any -u ... option from OPTIONS other than -u chrony, +as chrony is run under its own user by default. +This recommendation only applies if chrony is in use on the system. + 2.1.2 + If chrony is in use on the system proper configuration is vital to ensuring time synchronization +is working properly. + + CCE-82879-8 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-82879-8 + - chronyd_run_as_chrony_user + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Detect if file /etc/sysconfig/chronyd is not empty or missing + find: + path: /etc/sysconfig/ + patterns: chronyd + contains: ^([\s]*OPTIONS=["]?[^"]*)("?) + register: chronyd_file + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"chrony" in ansible_facts.packages' + tags: + - CCE-82879-8 + - chronyd_run_as_chrony_user + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Remove any previous configuration of user used to run chronyd process + replace: + path: /etc/sysconfig/chronyd + regexp: \s*-u\s*\w+\s* + replace: ' ' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"chrony" in ansible_facts.packages' + - chronyd_file is defined and chronyd_file.matched > 0 + tags: + - CCE-82879-8 + - chronyd_run_as_chrony_user + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && { rpm --quiet -q chrony; }; then + +if grep -q 'OPTIONS=.*' /etc/sysconfig/chronyd; then + # trying to solve cases where the parameter after OPTIONS + #may or may not be enclosed in quotes + sed -i -E -e 's/\s*-u\s*\w+\s*/ /' -e 's/^([\s]*OPTIONS=["]?[^"]*)("?)/\1\2/' /etc/sysconfig/chronyd +fi + +if grep -q 'OPTIONS=.*' /etc/sysconfig/chronyd; then + # trying to solve cases where the parameter after OPTIONS + #may or may not be enclosed in quotes + sed -i -E -e 's/\s*-u\s*\w+\s*/ /' -e 's/^([\s]*OPTIONS=["]?[^"]*)("?)/\1 -u chrony\2/' /etc/sysconfig/chronyd +else + echo 'OPTIONS="-u chrony"' >> /etc/sysconfig/chronyd +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure Chrony is only configured with the server directive + Check that Chrony only has time sources configured with the server directive. + This rule doesn't come with a remediation, the time source needs to be added by the adminstrator. + CCI-001891 + SRG-OS-000355-GPOS-00143 + SRG-OS-000356-GPOS-00144 + SRG-OS-000359-GPOS-00146 + RHEL-08-030740 + SV-230484r627750_rule + Depending on the infrastruture being used the pool directive may not be supported. + + CCE-86077-5 + + + + + + + + + A remote time server for Chrony is configured + Chrony is a daemon which implements the Network Time Protocol (NTP). It is designed to +synchronize system clocks across a variety of systems and use a source that is highly +accurate. More information on chrony can be found at + + http://chrony.tuxfamily.org/. +Chrony can be configured to be a client and/or a server. +Add or edit server or pool lines to /etc/chrony.conf as appropriate: +server <remote-server> +Multiple servers may be configured. + BP28(R43) + CCI-000160 + CCI-001891 + 0988 + 1405 + CM-6(a) + AU-8(1)(a) + Req-10.4.3 + 2.1.2 + If chrony is in use on the system proper configuration is vital to ensuring time +synchronization is working properly. + + CCE-82873-1 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-82873-1 + - NIST-800-53-AU-8(1)(a) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.4.3 + - chronyd_specify_remote_server + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed +- name: XCCDF Value var_multiple_time_servers # promote to variable + set_fact: + var_multiple_time_servers: !!str + tags: + - always + +- name: Detect if chrony is already configured with pools or servers + find: + path: /etc + patterns: chrony.conf + contains: ^[\s]*(?:server|pool)[\s]+[\w]+ + register: chrony_servers + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"chrony" in ansible_facts.packages' + tags: + - CCE-82873-1 + - NIST-800-53-AU-8(1)(a) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.4.3 + - chronyd_specify_remote_server + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Configure remote time servers + lineinfile: + path: /etc/chrony.conf + line: server {{ item }} + state: present + create: true + loop: '{{ var_multiple_time_servers.split(",") }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"chrony" in ansible_facts.packages' + - chrony_servers.matched == 0 + tags: + - CCE-82873-1 + - NIST-800-53-AU-8(1)(a) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.4.3 + - chronyd_specify_remote_server + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && { rpm --quiet -q chrony; }; then + +var_multiple_time_servers='' + + +config_file="/etc/chrony.conf" + +if ! grep -q '^[\s]*(?:server|pool)[\s]+[\w]+' "$config_file" ; then + if ! grep -q '#[[:space:]]*server' "$config_file" ; then + for server in $(echo "$var_multiple_time_servers" | tr ',' '\n') ; do + printf '\nserver %s' "$server" >> "$config_file" + done + else + sed -i 's/#[ \t]*server/server/g' "$config_file" + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Specify Additional Remote NTP Servers + Additional NTP servers can be specified for time synchronization +in the file /etc/ntp.conf. To do so, add additional lines of the +following form, substituting the IP address or hostname of a remote NTP server for +ntpserver: +server ntpserver + 1 + 14 + 15 + 16 + 3 + 5 + 6 + APO11.04 + BAI03.05 + DSS05.04 + DSS05.07 + MEA02.01 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + CM-6(a) + AU-8(1)(a) + AU-8(2) + PR.PT-1 + Req-10.4.3 + Specifying additional NTP servers increases the availability of +accurate time data, in the event that one of the specified servers becomes +unavailable. This is typical for a system acting as an NTP server for +other systems. + + + + + + Specify a Remote NTP Server + To specify a remote NTP server for time synchronization, edit +the file /etc/ntp.conf. Add or correct the following lines, +substituting the IP or hostname of a remote NTP server for ntpserver: +server ntpserver +This instructs the NTP software to contact that remote server to obtain time +data. + 1 + 14 + 15 + 16 + 3 + 5 + 6 + APO11.04 + BAI03.05 + DSS05.04 + DSS05.07 + MEA02.01 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + CM-6(a) + AU-8(1)(a) + PR.PT-1 + Req-10.4.1 + Req-10.4.3 + Synchronizing with an NTP server makes it possible +to collate system logs from multiple sources or correlate computer events with +real time events. + + + + + + + + + + + Obsolete Services + This section discusses a number of network-visible +services which have historically caused problems for system +security, and for which disabling or severely limiting the service +has been the best available guidance for some time. As a result of +this, many of these services are not installed as part of Red Hat Enterprise Linux 8 +by default. + +Organizations which are running these services should +switch to more secure equivalents as soon as possible. +If it remains absolutely necessary to run one of +these services for legacy reasons, care should be taken to restrict +the service as much as possible, for instance by configuring host + +firewall software such as iptables to restrict access to the + +vulnerable service to only those remote hosts which have a known +need to use it. + + Ensure rsyncd service is diabled + +The rsyncd service can be disabled with the following command: +$ sudo systemctl mask --now rsyncd.service + The rsyncd service presents a security risk as it uses unencrypted protocols for +communication. + + CCE-83335-0 + include disable_rsyncd + +class disable_rsyncd { + service {'rsyncd': + enable => false, + ensure => 'stopped', + } +} + + - name: Disable service rsyncd + block: + + - name: Disable service rsyncd + systemd: + name: rsyncd.service + enabled: 'no' + state: stopped + masked: 'yes' + ignore_errors: 'yes' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83335-0 + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_rsyncd_disabled + +- name: Unit Socket Exists - rsyncd.socket + command: systemctl list-unit-files rsyncd.socket + args: + warn: false + register: socket_file_exists + changed_when: false + ignore_errors: true + check_mode: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83335-0 + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_rsyncd_disabled + +- name: Disable socket rsyncd + systemd: + name: rsyncd.socket + enabled: 'no' + state: stopped + masked: 'yes' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"rsyncd.socket" in socket_file_exists.stdout_lines[1]' + tags: + - CCE-83335-0 + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_rsyncd_disabled + + +[customizations.services] +disabled = ["rsyncd"] + + apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + systemd: + units: + - name: rsyncd.service + enabled: false + mask: true + - name: rsyncd.socket + enabled: false + mask: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" stop 'rsyncd.service' +"$SYSTEMCTL_EXEC" disable 'rsyncd.service' +"$SYSTEMCTL_EXEC" mask 'rsyncd.service' +# Disable socket activation if we have a unit file for it +if "$SYSTEMCTL_EXEC" list-unit-files | grep -q '^rsyncd.socket'; then + "$SYSTEMCTL_EXEC" stop 'rsyncd.socket' + "$SYSTEMCTL_EXEC" mask 'rsyncd.socket' +fi +# The service may not be running because it has been started and failed, +# so let's reset the state so OVAL checks pass. +# Service should be 'inactive', not 'failed' after reboot though. +"$SYSTEMCTL_EXEC" reset-failed 'rsyncd.service' || true + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Xinetd + The xinetd service acts as a dedicated listener for some +network services (mostly, obsolete ones) and can be used to provide access +controls and perform some logging. It has been largely obsoleted by other +features, and it is not installed by default. The older Inetd service +is not even available as part of Red Hat Enterprise Linux 8. + + + Uninstall xinetd Package + The xinetd package can be removed with the following command: + +$ sudo yum erase xinetd + BP28(R1) + 11 + 12 + 14 + 15 + 3 + 8 + 9 + APO13.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.04 + DSS05.02 + DSS05.03 + DSS05.05 + DSS06.06 + CCI-000305 + 164.308(a)(4)(i) + 164.308(b)(1) + 164.308(b)(3) + 164.310(b) + 164.312(e)(1) + 164.312(e)(2)(ii) + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.6.2.1 + A.6.2.2 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.AC-3 + PR.IP-1 + PR.PT-3 + PR.PT-4 + 2.1.1 + Removing the xinetd package decreases the risk of the +xinetd service's accidental (or intentional) activation. + CCE-80850-1 + +package --remove=xinetd + + include remove_xinetd + +class remove_xinetd { + package { 'xinetd': + ensure => 'purged', + } +} + + - name: Ensure xinetd is removed + package: + name: xinetd + state: absent + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80850-1 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - package_xinetd_removed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# CAUTION: This remediation script will remove xinetd +# from the system, and may remove any packages +# that depend on xinetd. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "xinetd" ; then + + yum remove -y "xinetd" + +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable xinetd Service + +The xinetd service can be disabled with the following command: +$ sudo systemctl mask --now xinetd.service + 11 + 12 + 14 + 15 + 3 + 8 + 9 + APO13.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.04 + DSS05.02 + DSS05.03 + DSS05.05 + DSS06.06 + 3.4.7 + CCI-000305 + 164.308(a)(4)(i) + 164.308(b)(1) + 164.308(b)(3) + 164.310(b) + 164.312(e)(1) + 164.312(e)(2)(ii) + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.6.2.1 + A.6.2.2 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.AC-3 + PR.IP-1 + PR.PT-3 + PR.PT-4 + 2.1.7 + The xinetd service provides a dedicated listener service for some programs, +which is no longer necessary for commonly-used network services. Disabling +it ensures that these uncommon services are not running, and also prevents +attacks against xinetd itself. + + CCE-80888-1 + include disable_xinetd + +class disable_xinetd { + service {'xinetd': + enable => false, + ensure => 'stopped', + } +} + + - name: Disable service xinetd + block: + + - name: Disable service xinetd + systemd: + name: xinetd.service + enabled: 'no' + state: stopped + masked: 'yes' + ignore_errors: 'yes' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80888-1 + - NIST-800-171-3.4.7 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_xinetd_disabled + +- name: Unit Socket Exists - xinetd.socket + command: systemctl list-unit-files xinetd.socket + args: + warn: false + register: socket_file_exists + changed_when: false + ignore_errors: true + check_mode: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80888-1 + - NIST-800-171-3.4.7 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_xinetd_disabled + +- name: Disable socket xinetd + systemd: + name: xinetd.socket + enabled: 'no' + state: stopped + masked: 'yes' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"xinetd.socket" in socket_file_exists.stdout_lines[1]' + tags: + - CCE-80888-1 + - NIST-800-171-3.4.7 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_xinetd_disabled + + +[customizations.services] +disabled = ["xinetd"] + + apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + systemd: + units: + - name: xinetd.service + enabled: false + mask: true + - name: xinetd.socket + enabled: false + mask: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" stop 'xinetd.service' +"$SYSTEMCTL_EXEC" disable 'xinetd.service' +"$SYSTEMCTL_EXEC" mask 'xinetd.service' +# Disable socket activation if we have a unit file for it +if "$SYSTEMCTL_EXEC" list-unit-files | grep -q '^xinetd.socket'; then + "$SYSTEMCTL_EXEC" stop 'xinetd.socket' + "$SYSTEMCTL_EXEC" mask 'xinetd.socket' +fi +# The service may not be running because it has been started and failed, +# so let's reset the state so OVAL checks pass. +# Service should be 'inactive', not 'failed' after reboot though. +"$SYSTEMCTL_EXEC" reset-failed 'xinetd.service' || true + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + NIS + The Network Information Service (NIS), also known as 'Yellow +Pages' (YP), and its successor NIS+ have been made obsolete by +Kerberos, LDAP, and other modern centralized authentication +services. NIS should not be used because it suffers from security +problems inherent in its design, such as inadequate protection of +important authentication information. + + Remove NIS Client + The Network Information Service (NIS), formerly known as Yellow Pages, +is a client-server directory service protocol used to distribute system configuration +files. The NIS client (ypbind) was used to bind a system to an NIS server +and receive the distributed configuration files. + BP28(R1) + 164.308(a)(4)(i) + 164.308(b)(1) + 164.308(b)(3) + 164.310(b) + 164.312(e)(1) + 164.312(e)(2)(ii) + 2.3.1 + The NIS service is inherently an insecure system that has been vulnerable +to DOS attacks, buffer overflows and has poor authentication for querying +NIS maps. NIS generally has been replaced by such protocols as Lightweight +Directory Access Protocol (LDAP). It is recommended that the service be +removed. + CCE-82181-9 + +package --remove=ypbind + + include remove_ypbind + +class remove_ypbind { + package { 'ypbind': + ensure => 'purged', + } +} + + - name: Ensure ypbind is removed + package: + name: ypbind + state: absent + tags: + - CCE-82181-9 + - disable_strategy + - low_complexity + - low_disruption + - no_reboot_needed + - package_ypbind_removed + - unknown_severity + + +# CAUTION: This remediation script will remove ypbind +# from the system, and may remove any packages +# that depend on ypbind. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "ypbind" ; then + + yum remove -y "ypbind" + +fi + + + + + + + + + + Uninstall ypserv Package + The ypserv package can be removed with the following command: + +$ sudo yum erase ypserv + BP28(R1) + 11 + 12 + 14 + 15 + 3 + 8 + 9 + APO13.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.04 + DSS05.02 + DSS05.03 + DSS05.05 + DSS06.06 + CCI-000381 + 164.308(a)(4)(i) + 164.308(b)(1) + 164.308(b)(3) + 164.310(b) + 164.312(e)(1) + 164.312(e)(2)(ii) + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.6.2.1 + A.6.2.2 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + IA-5(1)(c) + PR.AC-3 + PR.IP-1 + PR.PT-3 + PR.PT-4 + SRG-OS-000095-GPOS-00049 + 2.2.15 + The NIS service provides an unencrypted authentication service which does +not provide for the confidentiality and integrity of user passwords or the +remote session. + +Removing the ypserv package decreases the risk of the accidental +(or intentional) activation of NIS or NIS+ services. + CCE-82432-6 + +package --remove=ypserv + + include remove_ypserv + +class remove_ypserv { + package { 'ypserv': + ensure => 'purged', + } +} + + - name: Ensure ypserv is removed + package: + name: ypserv + state: absent + tags: + - CCE-82432-6 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-IA-5(1)(c) + - disable_strategy + - high_severity + - low_complexity + - low_disruption + - no_reboot_needed + - package_ypserv_removed + + +# CAUTION: This remediation script will remove ypserv +# from the system, and may remove any packages +# that depend on ypserv. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "ypserv" ; then + + yum remove -y "ypserv" + +fi + + + + + + + + + + Disable ypbind Service + The ypbind service, which allows the system to act as a client in +a NIS or NIS+ domain, should be disabled. + +The ypbind service can be disabled with the following command: +$ sudo systemctl mask --now ypbind.service + 11 + 12 + 14 + 15 + 3 + 8 + 9 + APO13.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.04 + DSS05.02 + DSS05.03 + DSS05.05 + DSS06.06 + CCI-000305 + 164.308(a)(4)(i) + 164.308(b)(1) + 164.308(b)(3) + 164.310(b) + 164.312(e)(1) + 164.312(e)(2)(ii) + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.6.2.1 + A.6.2.2 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + IA-5(1)(c) + PR.AC-3 + PR.IP-1 + PR.PT-3 + PR.PT-4 + Disabling the ypbind service ensures the system is not acting +as a client in a NIS or NIS+ domain. This service should be disabled +unless in use. + + CCE-82433-4 + include disable_ypbind + +class disable_ypbind { + service {'ypbind': + enable => false, + ensure => 'stopped', + } +} + + - name: Disable service ypbind + block: + + - name: Disable service ypbind + systemd: + name: ypbind.service + enabled: 'no' + state: stopped + masked: 'yes' + ignore_errors: 'yes' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82433-4 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-IA-5(1)(c) + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_ypbind_disabled + +- name: Unit Socket Exists - ypbind.socket + command: systemctl list-unit-files ypbind.socket + args: + warn: false + register: socket_file_exists + changed_when: false + ignore_errors: true + check_mode: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82433-4 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-IA-5(1)(c) + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_ypbind_disabled + +- name: Disable socket ypbind + systemd: + name: ypbind.socket + enabled: 'no' + state: stopped + masked: 'yes' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"ypbind.socket" in socket_file_exists.stdout_lines[1]' + tags: + - CCE-82433-4 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-IA-5(1)(c) + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_ypbind_disabled + + +[customizations.services] +disabled = ["ypbind"] + + apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + systemd: + units: + - name: ypbind.service + enabled: false + mask: true + - name: ypbind.socket + enabled: false + mask: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" stop 'ypbind.service' +"$SYSTEMCTL_EXEC" disable 'ypbind.service' +"$SYSTEMCTL_EXEC" mask 'ypbind.service' +# Disable socket activation if we have a unit file for it +if "$SYSTEMCTL_EXEC" list-unit-files | grep -q '^ypbind.socket'; then + "$SYSTEMCTL_EXEC" stop 'ypbind.socket' + "$SYSTEMCTL_EXEC" mask 'ypbind.socket' +fi +# The service may not be running because it has been started and failed, +# so let's reset the state so OVAL checks pass. +# Service should be 'inactive', not 'failed' after reboot though. +"$SYSTEMCTL_EXEC" reset-failed 'ypbind.service' || true + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable ypserv Service + The ypserv service, which allows the system to act as a client in +a NIS or NIS+ domain, should be disabled. + +The ypserv service can be disabled with the following command: +$ sudo systemctl mask --now ypserv.service + Disabling the ypserv service ensures the system is not acting +as a client in a NIS or NIS+ domain. This service should be disabled +unless in use. + + CCE-86121-1 + include disable_ypserv + +class disable_ypserv { + service {'ypserv': + enable => false, + ensure => 'stopped', + } +} + + - name: Disable service ypserv + block: + + - name: Disable service ypserv + systemd: + name: ypserv.service + enabled: 'no' + state: stopped + masked: 'yes' + ignore_errors: 'yes' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86121-1 + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_ypserv_disabled + +- name: Unit Socket Exists - ypserv.socket + command: systemctl list-unit-files ypserv.socket + args: + warn: false + register: socket_file_exists + changed_when: false + ignore_errors: true + check_mode: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86121-1 + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_ypserv_disabled + +- name: Disable socket ypserv + systemd: + name: ypserv.socket + enabled: 'no' + state: stopped + masked: 'yes' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"ypserv.socket" in socket_file_exists.stdout_lines[1]' + tags: + - CCE-86121-1 + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_ypserv_disabled + + +[customizations.services] +disabled = ["ypserv"] + + apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + systemd: + units: + - name: ypserv.service + enabled: false + mask: true + - name: ypserv.socket + enabled: false + mask: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" stop 'ypserv.service' +"$SYSTEMCTL_EXEC" disable 'ypserv.service' +"$SYSTEMCTL_EXEC" mask 'ypserv.service' +# Disable socket activation if we have a unit file for it +if "$SYSTEMCTL_EXEC" list-unit-files | grep -q '^ypserv.socket'; then + "$SYSTEMCTL_EXEC" stop 'ypserv.socket' + "$SYSTEMCTL_EXEC" mask 'ypserv.socket' +fi +# The service may not be running because it has been started and failed, +# so let's reset the state so OVAL checks pass. +# Service should be 'inactive', not 'failed' after reboot though. +"$SYSTEMCTL_EXEC" reset-failed 'ypserv.service' || true + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Rlogin, Rsh, and Rexec + The Berkeley r-commands are legacy services which +allow cleartext remote access and have an insecure trust +model. + + Uninstall rsh-server Package + The rsh-server package can be removed with the following command: + +$ sudo yum erase rsh-server + BP28(R1) + 11 + 12 + 14 + 15 + 3 + 8 + 9 + APO13.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.04 + DSS05.02 + DSS05.03 + DSS05.05 + DSS06.06 + CCI-000381 + 164.308(a)(4)(i) + 164.308(b)(1) + 164.308(b)(3) + 164.310(b) + 164.312(e)(1) + 164.312(e)(2)(ii) + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.6.2.1 + A.6.2.2 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + IA-5(1)(c) + PR.AC-3 + PR.IP-1 + PR.PT-3 + PR.PT-4 + SRG-OS-000095-GPOS-00049 + RHEL-08-040010 + SV-230492r627750_rule + The rsh-server service provides unencrypted remote access service which does not +provide for the confidentiality and integrity of user passwords or the remote session and has very weak +authentication. If a privileged user were to login using this service, the privileged user password +could be compromised. The rsh-server package provides several obsolete and insecure +network services. Removing it decreases the risk of those services' accidental (or intentional) +activation. + CCE-82184-3 + +package --remove=rsh-server + + include remove_rsh-server + +class remove_rsh-server { + package { 'rsh-server': + ensure => 'purged', + } +} + + - name: Ensure rsh-server is removed + package: + name: rsh-server + state: absent + tags: + - CCE-82184-3 + - DISA-STIG-RHEL-08-040010 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-IA-5(1)(c) + - disable_strategy + - high_severity + - low_complexity + - low_disruption + - no_reboot_needed + - package_rsh-server_removed + + +# CAUTION: This remediation script will remove rsh-server +# from the system, and may remove any packages +# that depend on rsh-server. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "rsh-server" ; then + + yum remove -y "rsh-server" + +fi + + + + + + + + + + Uninstall rsh Package + +The rsh package contains the client commands + +for the rsh services + BP28(R1) + 3.1.13 + 164.308(a)(4)(i) + 164.308(b)(1) + 164.308(b)(3) + 164.310(b) + 164.312(e)(1) + 164.312(e)(2)(ii) + A.8.2.3 + A.13.1.1 + A.13.2.1 + A.13.2.3 + A.14.1.2 + A.14.1.3 + 2.3.2 + These legacy clients contain numerous security exposures and have +been replaced with the more secure SSH package. Even if the server is removed, +it is best to ensure the clients are also removed to prevent users from +inadvertently attempting to use these commands and therefore exposing + +their credentials. Note that removing the rsh package removes + +the clients for rsh,rcp, and rlogin. + CCE-82183-5 + +package --remove=rsh + + include remove_rsh + +class remove_rsh { + package { 'rsh': + ensure => 'purged', + } +} + + - name: Ensure rsh is removed + package: + name: rsh + state: absent + tags: + - CCE-82183-5 + - NIST-800-171-3.1.13 + - disable_strategy + - low_complexity + - low_disruption + - no_reboot_needed + - package_rsh_removed + - unknown_severity + + +# CAUTION: This remediation script will remove rsh +# from the system, and may remove any packages +# that depend on rsh. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "rsh" ; then + + yum remove -y "rsh" + +fi + + + + + + + + + + Disable rexec Service + The rexec service, which is available with the rsh-server package +and runs as a service through xinetd or separately as a systemd socket, should be disabled. +If using xinetd, set disable to yes in /etc/xinetd.d/rexec. + +The rexec socket can be disabled with the following command: +$ sudo systemctl mask --now rexec.socket + 11 + 12 + 14 + 15 + 3 + 8 + 9 + APO13.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.04 + DSS05.02 + DSS05.03 + DSS05.05 + DSS06.06 + 3.1.13 + 3.4.7 + CCI-000068 + CCI-001436 + 164.308(a)(4)(i) + 164.308(b)(1) + 164.308(b)(3) + 164.310(b) + 164.312(e)(1) + 164.312(e)(2)(ii) + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.6.2.1 + A.6.2.2 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + IA-5(1)(c) + PR.AC-3 + PR.IP-1 + PR.PT-3 + PR.PT-4 + The rexec service uses unencrypted network communications, which +means that data from the login session, including passwords and +all other information transmitted during the session, can be +stolen by eavesdroppers on the network. + + CCE-80884-0 + include disable_rexec + +class disable_rexec { + service {'rexec': + enable => false, + ensure => 'stopped', + } +} + + - name: Disable service rexec + block: + + - name: Disable service rexec + systemd: + name: rexec.service + enabled: 'no' + state: stopped + masked: 'yes' + ignore_errors: 'yes' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80884-0 + - NIST-800-171-3.1.13 + - NIST-800-171-3.4.7 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-IA-5(1)(c) + - disable_strategy + - high_severity + - low_complexity + - low_disruption + - no_reboot_needed + - service_rexec_disabled + +- name: Unit Socket Exists - rexec.socket + command: systemctl list-unit-files rexec.socket + args: + warn: false + register: socket_file_exists + changed_when: false + ignore_errors: true + check_mode: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80884-0 + - NIST-800-171-3.1.13 + - NIST-800-171-3.4.7 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-IA-5(1)(c) + - disable_strategy + - high_severity + - low_complexity + - low_disruption + - no_reboot_needed + - service_rexec_disabled + +- name: Disable socket rexec + systemd: + name: rexec.socket + enabled: 'no' + state: stopped + masked: 'yes' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"rexec.socket" in socket_file_exists.stdout_lines[1]' + tags: + - CCE-80884-0 + - NIST-800-171-3.1.13 + - NIST-800-171-3.4.7 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-IA-5(1)(c) + - disable_strategy + - high_severity + - low_complexity + - low_disruption + - no_reboot_needed + - service_rexec_disabled + + +[customizations.services] +disabled = ["rexec"] + + apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + systemd: + units: + - name: rexec.service + enabled: false + mask: true + - name: rexec.socket + enabled: false + mask: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" stop 'rexec.service' +"$SYSTEMCTL_EXEC" disable 'rexec.service' +"$SYSTEMCTL_EXEC" mask 'rexec.service' +# Disable socket activation if we have a unit file for it +if "$SYSTEMCTL_EXEC" list-unit-files | grep -q '^rexec.socket'; then + "$SYSTEMCTL_EXEC" stop 'rexec.socket' + "$SYSTEMCTL_EXEC" mask 'rexec.socket' +fi +# The service may not be running because it has been started and failed, +# so let's reset the state so OVAL checks pass. +# Service should be 'inactive', not 'failed' after reboot though. +"$SYSTEMCTL_EXEC" reset-failed 'rexec.service' || true + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable rlogin Service + The rlogin service, which is available with +the rsh-server package and runs as a service through xinetd or separately +as a systemd socket, should be disabled. +If using xinetd, set disable to yes in /etc/xinetd.d/rlogin. + +The rlogin socket can be disabled with the following command: +$ sudo systemctl mask --now rlogin.socket + 1 + 11 + 12 + 14 + 15 + 16 + 3 + 5 + 8 + 9 + APO13.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.04 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.03 + DSS06.06 + DSS06.10 + 3.1.13 + 3.4.7 + CCI-001436 + 164.308(a)(4)(i) + 164.308(b)(1) + 164.308(b)(3) + 164.310(b) + 164.312(e)(1) + 164.312(e)(2)(ii) + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.18.1.4 + A.6.2.1 + A.6.2.2 + A.7.1.1 + A.9.1.2 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.2 + A.9.4.3 + CM-7(a) + CM-7(b) + CM-6(a) + IA-5(1)(c) + PR.AC-1 + PR.AC-3 + PR.AC-6 + PR.AC-7 + PR.IP-1 + PR.PT-3 + PR.PT-4 + The rlogin service uses unencrypted network communications, which +means that data from the login session, including passwords and +all other information transmitted during the session, can be +stolen by eavesdroppers on the network. + + CCE-80885-7 + include disable_rlogin + +class disable_rlogin { + service {'rlogin': + enable => false, + ensure => 'stopped', + } +} + + - name: Disable service rlogin + block: + + - name: Disable service rlogin + systemd: + name: rlogin.service + enabled: 'no' + state: stopped + masked: 'yes' + ignore_errors: 'yes' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80885-7 + - NIST-800-171-3.1.13 + - NIST-800-171-3.4.7 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-IA-5(1)(c) + - disable_strategy + - high_severity + - low_complexity + - low_disruption + - no_reboot_needed + - service_rlogin_disabled + +- name: Unit Socket Exists - rlogin.socket + command: systemctl list-unit-files rlogin.socket + args: + warn: false + register: socket_file_exists + changed_when: false + ignore_errors: true + check_mode: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80885-7 + - NIST-800-171-3.1.13 + - NIST-800-171-3.4.7 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-IA-5(1)(c) + - disable_strategy + - high_severity + - low_complexity + - low_disruption + - no_reboot_needed + - service_rlogin_disabled + +- name: Disable socket rlogin + systemd: + name: rlogin.socket + enabled: 'no' + state: stopped + masked: 'yes' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"rlogin.socket" in socket_file_exists.stdout_lines[1]' + tags: + - CCE-80885-7 + - NIST-800-171-3.1.13 + - NIST-800-171-3.4.7 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-IA-5(1)(c) + - disable_strategy + - high_severity + - low_complexity + - low_disruption + - no_reboot_needed + - service_rlogin_disabled + + +[customizations.services] +disabled = ["rlogin"] + + apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + systemd: + units: + - name: rlogin.service + enabled: false + mask: true + - name: rlogin.socket + enabled: false + mask: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" stop 'rlogin.service' +"$SYSTEMCTL_EXEC" disable 'rlogin.service' +"$SYSTEMCTL_EXEC" mask 'rlogin.service' +# Disable socket activation if we have a unit file for it +if "$SYSTEMCTL_EXEC" list-unit-files | grep -q '^rlogin.socket'; then + "$SYSTEMCTL_EXEC" stop 'rlogin.socket' + "$SYSTEMCTL_EXEC" mask 'rlogin.socket' +fi +# The service may not be running because it has been started and failed, +# so let's reset the state so OVAL checks pass. +# Service should be 'inactive', not 'failed' after reboot though. +"$SYSTEMCTL_EXEC" reset-failed 'rlogin.service' || true + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable rsh Service + The rsh service, which is available with +the rsh-server package and runs as a service through xinetd or separately +as a systemd socket, should be disabled. +If using xinetd, set disable to yes in /etc/xinetd.d/rsh. + +The rsh socket can be disabled with the following command: +$ sudo systemctl mask --now rsh.socket + 1 + 11 + 12 + 14 + 15 + 16 + 3 + 5 + 8 + 9 + APO13.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.04 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.03 + DSS06.06 + DSS06.10 + 3.1.13 + 3.4.7 + CCI-000068 + CCI-001436 + 164.308(a)(4)(i) + 164.308(b)(1) + 164.308(b)(3) + 164.310(b) + 164.312(e)(1) + 164.312(e)(2)(ii) + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.18.1.4 + A.6.2.1 + A.6.2.2 + A.7.1.1 + A.9.1.2 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.2 + A.9.4.3 + CM-7(a) + CM-7(b) + CM-6(a) + IA-5(1)(c) + PR.AC-1 + PR.AC-3 + PR.AC-6 + PR.AC-7 + PR.IP-1 + PR.PT-3 + PR.PT-4 + The rsh service uses unencrypted network communications, which +means that data from the login session, including passwords and +all other information transmitted during the session, can be +stolen by eavesdroppers on the network. + + CCE-82431-8 + include disable_rsh + +class disable_rsh { + service {'rsh': + enable => false, + ensure => 'stopped', + } +} + + - name: Disable service rsh + block: + + - name: Disable service rsh + systemd: + name: rsh.service + enabled: 'no' + state: stopped + masked: 'yes' + ignore_errors: 'yes' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82431-8 + - NIST-800-171-3.1.13 + - NIST-800-171-3.4.7 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-IA-5(1)(c) + - disable_strategy + - high_severity + - low_complexity + - low_disruption + - no_reboot_needed + - service_rsh_disabled + +- name: Unit Socket Exists - rsh.socket + command: systemctl list-unit-files rsh.socket + args: + warn: false + register: socket_file_exists + changed_when: false + ignore_errors: true + check_mode: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82431-8 + - NIST-800-171-3.1.13 + - NIST-800-171-3.4.7 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-IA-5(1)(c) + - disable_strategy + - high_severity + - low_complexity + - low_disruption + - no_reboot_needed + - service_rsh_disabled + +- name: Disable socket rsh + systemd: + name: rsh.socket + enabled: 'no' + state: stopped + masked: 'yes' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"rsh.socket" in socket_file_exists.stdout_lines[1]' + tags: + - CCE-82431-8 + - NIST-800-171-3.1.13 + - NIST-800-171-3.4.7 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-IA-5(1)(c) + - disable_strategy + - high_severity + - low_complexity + - low_disruption + - no_reboot_needed + - service_rsh_disabled + + +[customizations.services] +disabled = ["rsh"] + + apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + systemd: + units: + - name: rsh.service + enabled: false + mask: true + - name: rsh.socket + enabled: false + mask: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" stop 'rsh.service' +"$SYSTEMCTL_EXEC" disable 'rsh.service' +"$SYSTEMCTL_EXEC" mask 'rsh.service' +# Disable socket activation if we have a unit file for it +if "$SYSTEMCTL_EXEC" list-unit-files | grep -q '^rsh.socket'; then + "$SYSTEMCTL_EXEC" stop 'rsh.socket' + "$SYSTEMCTL_EXEC" mask 'rsh.socket' +fi +# The service may not be running because it has been started and failed, +# so let's reset the state so OVAL checks pass. +# Service should be 'inactive', not 'failed' after reboot though. +"$SYSTEMCTL_EXEC" reset-failed 'rsh.service' || true + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Remove Host-Based Authentication Files + The shosts.equiv file list remote hosts +and users that are trusted by the local system. +To remove these files, run the following command to delete them from any +location: +$ sudo rm /[path]/[to]/[file]/shosts.equiv + CCI-000366 + SRG-OS-000480-GPOS-00227 + RHEL-08-010460 + SV-230283r627750_rule + The shosts.equiv files are used to configure host-based authentication for the +system via SSH. Host-based authentication is not sufficient for preventing +unauthorized access to the system, as it does not require interactive +identification and authentication of a connection request, or for the use of +two-factor authentication. + CCE-84055-3 + +# Identify local mounts +MOUNT_LIST=$(df --local | awk '{ print $6 }') + +# Find file on each listed mount point +for cur_mount in ${MOUNT_LIST} +do + find ${cur_mount} -xdev -type f -name "shosts.equiv" -exec rm -f {} \; +done + + + + + + + + + + Remove Rsh Trust Files + The files /etc/hosts.equiv and ~/.rhosts (in +each user's home directory) list remote hosts and users that are trusted by the +local system when using the rshd daemon. +To remove these files, run the following command to delete them from any +location: +$ sudo rm /etc/hosts.equiv +$ rm ~/.rhosts + 11 + 12 + 14 + 15 + 3 + 8 + 9 + APO13.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.04 + DSS05.02 + DSS05.03 + DSS05.05 + DSS06.06 + CCI-001436 + 164.308(a)(4)(i) + 164.308(b)(1) + 164.308(b)(3) + 164.310(b) + 164.312(e)(1) + 164.312(e)(2)(ii) + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.6.2.1 + A.6.2.2 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.AC-3 + PR.IP-1 + PR.PT-3 + PR.PT-4 + 6.2.16 + This action is only meaningful if .rhosts support is permitted +through PAM. Trust files are convenient, but when used in conjunction with +the R-services, they can allow unauthenticated access to a system. + CCE-80842-8 + - block: + + - name: Detect .rhosts files in users home directories + find: + paths: + - /root + - /home + recurse: true + patterns: .rhosts + hidden: true + file_type: file + check_mode: false + register: rhosts_locations + + - name: Remove .rhosts files + file: + path: '{{ item }}' + state: absent + with_items: '{{ rhosts_locations.files | map(attribute=''path'') | list }}' + when: rhosts_locations is success + + - name: Remove /etc/hosts.equiv file + file: + path: /etc/hosts.equiv + state: absent + tags: + - CCE-80842-8 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - high_severity + - low_complexity + - low_disruption + - no_reboot_needed + - no_rsh_trust_files + - restrict_strategy + + +find /root -xdev -type f -name ".rhosts" -exec rm -f {} \; +find /home -maxdepth 2 -xdev -type f -name ".rhosts" -exec rm -f {} \; +rm -f /etc/hosts.equiv + + + + + + + + + + Remove User Host-Based Authentication Files + The ~/.shosts (in each user's home directory) files +list remote hosts and users that are trusted by the +local system. To remove these files, run the following command +to delete them from any location: +$ sudo find / -name '.shosts' -type f -delete + CCI-000366 + SRG-OS-000480-GPOS-00227 + RHEL-08-010470 + SV-230284r627750_rule + The .shosts files are used to configure host-based authentication for +individual users or the system via SSH. Host-based authentication is not +sufficient for preventing unauthorized access to the system, as it does not +require interactive identification and authentication of a connection request, +or for the use of two-factor authentication. + CCE-84056-1 + +# Identify local mounts +MOUNT_LIST=$(df --local | awk '{ print $6 }') + +# Find file on each listed mount point +for cur_mount in ${MOUNT_LIST} +do + find ${cur_mount} -xdev -type f -name ".shosts" -exec rm -f {} \; +done + + + + + + + + + + + Chat/Messaging Services + The talk software makes it possible for users to send and receive messages +across systems through a terminal session. + + Uninstall talk-server Package + The talk-server package can be removed with the following command: $ sudo yum erase talk-server + BP28(R1) + 164.308(a)(4)(i) + 164.308(b)(1) + 164.308(b)(3) + 164.310(b) + 164.312(e)(1) + 164.312(e)(2)(ii) + The talk software presents a security risk as it uses unencrypted protocols +for communications. Removing the talk-server package decreases the +risk of the accidental (or intentional) activation of talk services. + CCE-82180-1 + +package --remove=talk-server + + include remove_talk-server + +class remove_talk-server { + package { 'talk-server': + ensure => 'purged', + } +} + + - name: Ensure talk-server is removed + package: + name: talk-server + state: absent + tags: + - CCE-82180-1 + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_talk-server_removed + + +# CAUTION: This remediation script will remove talk-server +# from the system, and may remove any packages +# that depend on talk-server. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "talk-server" ; then + + yum remove -y "talk-server" + +fi + + + + + + + + + + Uninstall talk Package + The talk package contains the client program for the +Internet talk protocol, which allows the user to chat with other users on +different systems. Talk is a communication program which copies lines from one +terminal to the terminal of another user. +The talk package can be removed with the following command: + +$ sudo yum erase talk + BP28(R1) + 164.308(a)(4)(i) + 164.308(b)(1) + 164.308(b)(3) + 164.310(b) + 164.312(e)(1) + 164.312(e)(2)(ii) + 2.3.3 + The talk software presents a security risk as it uses unencrypted protocols +for communications. Removing the talk package decreases the +risk of the accidental (or intentional) activation of talk client program. + CCE-80848-5 + +package --remove=talk + + include remove_talk + +class remove_talk { + package { 'talk': + ensure => 'purged', + } +} + + - name: Ensure talk is removed + package: + name: talk + state: absent + tags: + - CCE-80848-5 + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_talk_removed + + +# CAUTION: This remediation script will remove talk +# from the system, and may remove any packages +# that depend on talk. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "talk" ; then + + yum remove -y "talk" + +fi + + + + + + + + + + + Telnet + The telnet protocol does not provide confidentiality or integrity +for information transmitted on the network. This includes authentication +information such as passwords. Organizations which use telnet should be +actively working to migrate to a more secure protocol. + + Uninstall telnet-server Package + The telnet-server package can be removed with the following command: + +$ sudo yum erase telnet-server + BP28(R1) + 11 + 12 + 14 + 15 + 3 + 8 + 9 + APO13.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.04 + DSS05.02 + DSS05.03 + DSS05.05 + DSS06.06 + CCI-000381 + 164.308(a)(4)(i) + 164.308(b)(1) + 164.308(b)(3) + 164.310(b) + 164.312(e)(1) + 164.312(e)(2)(ii) + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.6.2.1 + A.6.2.2 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.AC-3 + PR.IP-1 + PR.PT-3 + PR.PT-4 + SRG-OS-000095-GPOS-00049 + RHEL-08-040000 + 2.2.16 + SV-230487r627750_rule + It is detrimental for operating systems to provide, or install by default, +functionality exceeding requirements or mission objectives. These +unnecessary capabilities are often overlooked and therefore may remain +unsecure. They increase the risk to the platform by providing additional +attack vectors. + +The telnet service provides an unencrypted remote access service which does +not provide for the confidentiality and integrity of user passwords or the +remote session. If a privileged user were to login using this service, the +privileged user password could be compromised. + +Removing the telnet-server package decreases the risk of the +telnet service's accidental (or intentional) activation. + CCE-82182-7 + +package --remove=telnet-server + + include remove_telnet-server + +class remove_telnet-server { + package { 'telnet-server': + ensure => 'purged', + } +} + + - name: Ensure telnet-server is removed + package: + name: telnet-server + state: absent + tags: + - CCE-82182-7 + - DISA-STIG-RHEL-08-040000 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - high_severity + - low_complexity + - low_disruption + - no_reboot_needed + - package_telnet-server_removed + + +# CAUTION: This remediation script will remove telnet-server +# from the system, and may remove any packages +# that depend on telnet-server. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "telnet-server" ; then + + yum remove -y "telnet-server" + +fi + + + + + + + + + + Remove telnet Clients + The telnet client allows users to start connections to other systems via +the telnet protocol. + BP28(R1) + 3.1.13 + 164.308(a)(4)(i) + 164.308(b)(1) + 164.308(b)(3) + 164.310(b) + 164.312(e)(1) + 164.312(e)(2)(ii) + A.8.2.3 + A.13.1.1 + A.13.2.1 + A.13.2.3 + A.14.1.2 + A.14.1.3 + 2.3.4 + The telnet protocol is insecure and unencrypted. The use +of an unencrypted transmission medium could allow an unauthorized user +to steal credentials. The ssh package provides an +encrypted session and stronger security and is included in Red Hat Enterprise Linux 8. + CCE-80849-3 + +package --remove=telnet + + include remove_telnet + +class remove_telnet { + package { 'telnet': + ensure => 'purged', + } +} + + - name: Ensure telnet is removed + package: + name: telnet + state: absent + tags: + - CCE-80849-3 + - NIST-800-171-3.1.13 + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - package_telnet_removed + + +# CAUTION: This remediation script will remove telnet +# from the system, and may remove any packages +# that depend on telnet. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "telnet" ; then + + yum remove -y "telnet" + +fi + + + + + + + + + + Disable telnet Service + The telnet service configuration file /etc/xinetd.d/telnet +is not created automatically. If it was created manually, check the +/etc/xinetd.d/telnet file and ensure that disable = no +is changed to read disable = yes as follows below: + +# description: The telnet server serves telnet sessions; it uses \\ +# unencrypted username/password pairs for authentication. +service telnet +{ + flags = REUSE + socket_type = stream + + wait = no + user = root + server = /usr/sbin/in.telnetd + log_on_failure += USERID + disable = yes +} + +If the /etc/xinetd.d/telnet file does not exist, make sure that +the activation of the telnet service on system boot is disabled +via the following command: + +The rexec socket can be disabled with the following command: +$ sudo systemctl mask --now rexec.socket + 1 + 11 + 12 + 14 + 15 + 16 + 3 + 5 + 8 + 9 + APO13.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.04 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.03 + DSS06.06 + DSS06.10 + 3.1.13 + 3.4.7 + 164.308(a)(4)(i) + 164.308(b)(1) + 164.308(b)(3) + 164.310(b) + 164.312(e)(1) + 164.312(e)(2)(ii) + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.18.1.4 + A.6.2.1 + A.6.2.2 + A.7.1.1 + A.9.1.2 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.2 + A.9.4.3 + CM-7(a) + CM-7(b) + CM-6(a) + IA-5(1)(c) + PR.AC-1 + PR.AC-3 + PR.AC-6 + PR.AC-7 + PR.IP-1 + PR.PT-3 + PR.PT-4 + The telnet protocol uses unencrypted network communication, which +means that data from the login session, including passwords and +all other information transmitted during the session, can be +stolen by eavesdroppers on the network. The telnet protocol is also +subject to man-in-the-middle attacks. + + CCE-80887-3 + include disable_telnet + +class disable_telnet { + service {'telnet': + enable => false, + ensure => 'stopped', + } +} + + - name: Disable service telnet + block: + + - name: Disable service telnet + systemd: + name: telnet.service + enabled: 'no' + state: stopped + masked: 'yes' + ignore_errors: 'yes' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80887-3 + - NIST-800-171-3.1.13 + - NIST-800-171-3.4.7 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-IA-5(1)(c) + - disable_strategy + - high_severity + - low_complexity + - low_disruption + - no_reboot_needed + - service_telnet_disabled + +- name: Unit Socket Exists - telnet.socket + command: systemctl list-unit-files telnet.socket + args: + warn: false + register: socket_file_exists + changed_when: false + ignore_errors: true + check_mode: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80887-3 + - NIST-800-171-3.1.13 + - NIST-800-171-3.4.7 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-IA-5(1)(c) + - disable_strategy + - high_severity + - low_complexity + - low_disruption + - no_reboot_needed + - service_telnet_disabled + +- name: Disable socket telnet + systemd: + name: telnet.socket + enabled: 'no' + state: stopped + masked: 'yes' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"telnet.socket" in socket_file_exists.stdout_lines[1]' + tags: + - CCE-80887-3 + - NIST-800-171-3.1.13 + - NIST-800-171-3.4.7 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-IA-5(1)(c) + - disable_strategy + - high_severity + - low_complexity + - low_disruption + - no_reboot_needed + - service_telnet_disabled + + +[customizations.services] +disabled = ["telnet"] + + apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + systemd: + units: + - name: telnet.service + enabled: false + mask: true + - name: telnet.socket + enabled: false + mask: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" stop 'telnet.service' +"$SYSTEMCTL_EXEC" disable 'telnet.service' +"$SYSTEMCTL_EXEC" mask 'telnet.service' +# Disable socket activation if we have a unit file for it +if "$SYSTEMCTL_EXEC" list-unit-files | grep -q '^telnet.socket'; then + "$SYSTEMCTL_EXEC" stop 'telnet.socket' + "$SYSTEMCTL_EXEC" mask 'telnet.socket' +fi +# The service may not be running because it has been started and failed, +# so let's reset the state so OVAL checks pass. +# Service should be 'inactive', not 'failed' after reboot though. +"$SYSTEMCTL_EXEC" reset-failed 'telnet.service' || true + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + TFTP Server + TFTP is a lightweight version of the FTP protocol which has +traditionally been used to configure networking equipment. However, +TFTP provides little security, and modern versions of networking +operating systems frequently support configuration via SSH or other +more secure protocols. A TFTP server should be run only if no more +secure method of supporting existing equipment can be +found. + + TFTP server secure directory + Specify the directory which is used by TFTP server as a root directory when running in secure mode. + /var/lib/tftpboot + + + Uninstall tftp-server Package + The tftp-server package can be removed with the following command: $ sudo yum erase tftp-server + BP28(R1) + 11 + 12 + 14 + 15 + 3 + 8 + 9 + APO13.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.04 + DSS05.02 + DSS05.03 + DSS05.05 + DSS06.06 + CCI-000318 + CCI-000366 + CCI-000368 + CCI-001812 + CCI-001813 + CCI-001814 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.6.2.1 + A.6.2.2 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.AC-3 + PR.IP-1 + PR.PT-3 + PR.PT-4 + SRG-OS-000480-GPOS-00227 + RHEL-08-040190 + 2.2.9 + SV-230533r627750_rule + Removing the tftp-server package decreases the risk of the accidental +(or intentional) activation of tftp services. + +If TFTP is required for operational support (such as transmission of router +configurations), its use must be documented with the Information Systems +Securty Manager (ISSM), restricted to only authorized personnel, and have +access control rules established. + CCE-82436-7 + +package --remove=tftp-server + + include remove_tftp-server + +class remove_tftp-server { + package { 'tftp-server': + ensure => 'purged', + } +} + + - name: Ensure tftp-server is removed + package: + name: tftp-server + state: absent + tags: + - CCE-82436-7 + - DISA-STIG-RHEL-08-040190 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - high_severity + - low_complexity + - low_disruption + - no_reboot_needed + - package_tftp-server_removed + + +# CAUTION: This remediation script will remove tftp-server +# from the system, and may remove any packages +# that depend on tftp-server. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "tftp-server" ; then + + yum remove -y "tftp-server" + +fi + + + + + + + + + + Remove tftp Daemon + Trivial File Transfer Protocol (TFTP) is a simple file transfer protocol, +typically used to automatically transfer configuration or boot files between systems. +TFTP does not support authentication and can be easily hacked. The package +tftp is a client program that allows for connections to a tftp server. + BP28(R1) + 2.3.6 + It is recommended that TFTP be removed, unless there is a specific need +for TFTP (such as a boot server). In that case, use extreme caution when configuring +the services. + CCE-83590-0 + +package --remove=tftp + + include remove_tftp + +class remove_tftp { + package { 'tftp': + ensure => 'purged', + } +} + + - name: Ensure tftp is removed + package: + name: tftp + state: absent + tags: + - CCE-83590-0 + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - package_tftp_removed + + +# CAUTION: This remediation script will remove tftp +# from the system, and may remove any packages +# that depend on tftp. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "tftp" ; then + + yum remove -y "tftp" + +fi + + + + + + + + + + Disable tftp Service + The tftp service should be disabled. + +The tftp service can be disabled with the following command: +$ sudo systemctl mask --now tftp.service + 11 + 12 + 14 + 15 + 3 + 8 + 9 + APO13.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.04 + DSS05.02 + DSS05.03 + DSS05.05 + DSS06.06 + CCI-001436 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.6.2.1 + A.6.2.2 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.AC-3 + PR.IP-1 + PR.PT-3 + PR.PT-4 + Disabling the tftp service ensures the system is not acting +as a TFTP server, which does not provide encryption or authentication. + + CCE-82435-9 + include disable_tftp + +class disable_tftp { + service {'tftp': + enable => false, + ensure => 'stopped', + } +} + + - name: Disable service tftp + block: + + - name: Disable service tftp + systemd: + name: tftp.service + enabled: 'no' + state: stopped + masked: 'yes' + ignore_errors: 'yes' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82435-9 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - high_severity + - low_complexity + - low_disruption + - no_reboot_needed + - service_tftp_disabled + +- name: Unit Socket Exists - tftp.socket + command: systemctl list-unit-files tftp.socket + args: + warn: false + register: socket_file_exists + changed_when: false + ignore_errors: true + check_mode: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82435-9 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - high_severity + - low_complexity + - low_disruption + - no_reboot_needed + - service_tftp_disabled + +- name: Disable socket tftp + systemd: + name: tftp.socket + enabled: 'no' + state: stopped + masked: 'yes' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"tftp.socket" in socket_file_exists.stdout_lines[1]' + tags: + - CCE-82435-9 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - high_severity + - low_complexity + - low_disruption + - no_reboot_needed + - service_tftp_disabled + + +[customizations.services] +disabled = ["tftp"] + + apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + systemd: + units: + - name: tftp.service + enabled: false + mask: true + - name: tftp.socket + enabled: false + mask: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" stop 'tftp.service' +"$SYSTEMCTL_EXEC" disable 'tftp.service' +"$SYSTEMCTL_EXEC" mask 'tftp.service' +# Disable socket activation if we have a unit file for it +if "$SYSTEMCTL_EXEC" list-unit-files | grep -q '^tftp.socket'; then + "$SYSTEMCTL_EXEC" stop 'tftp.socket' + "$SYSTEMCTL_EXEC" mask 'tftp.socket' +fi +# The service may not be running because it has been started and failed, +# so let's reset the state so OVAL checks pass. +# Service should be 'inactive', not 'failed' after reboot though. +"$SYSTEMCTL_EXEC" reset-failed 'tftp.service' || true + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure tftp Daemon Uses Secure Mode + If running the tftp service is necessary, it should be configured +to change its root directory at startup. To do so, ensure +/etc/xinetd.d/tftp includes -s as a command line argument, as shown in +the following example: +server_args = -s + 11 + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + 8 + 9 + APO01.06 + APO13.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.04 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + DSS06.02 + DSS06.06 + CCI-000366 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.11.2.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.6.1.2 + A.6.2.1 + A.6.2.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-6(b) + AC-6 + CM-7(a) + PR.AC-3 + PR.AC-4 + PR.DS-5 + PR.IP-1 + PR.PT-3 + PR.PT-4 + SRG-OS-000480-GPOS-00227 + RHEL-08-040350 + SV-230557r627750_rule + Using the -s option causes the TFTP service to only serve files from the +given directory. Serving files from an intentionally-specified directory +reduces the risk of sharing files which should remain private. + + CCE-82434-2 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-82434-2 + - DISA-STIG-RHEL-08-040350 + - NIST-800-53-AC-6 + - NIST-800-53-CM-6(b) + - NIST-800-53-CM-7(a) + - configure_strategy + - high_severity + - low_complexity + - low_disruption + - no_reboot_needed + - tftpd_uses_secure_mode +- name: XCCDF Value var_tftpd_secure_directory # promote to variable + set_fact: + var_tftpd_secure_directory: !!str + tags: + - always + +- name: Find out if the file exists and contains the line configuring server arguments + find: + path: /etc/xinetd.d + patterns: tftp + contains: ^[\s]+server_args.*$ + register: tftpd_secure_config_line + when: '"tftp-server" in ansible_facts.packages' + tags: + - CCE-82434-2 + - DISA-STIG-RHEL-08-040350 + - NIST-800-53-AC-6 + - NIST-800-53-CM-6(b) + - NIST-800-53-CM-7(a) + - configure_strategy + - high_severity + - low_complexity + - low_disruption + - no_reboot_needed + - tftpd_uses_secure_mode + +- name: Ensure that TFTP server is configured to start with secure directory + lineinfile: + path: /etc/xinetd.d/tftp + regexp: ^[\s]*(server_args[\s]+=[\s]+.*?)(-s[\s]+[/\.\w]+)*(.*)$ + line: \1 -s {{ var_tftpd_secure_directory }} \3 + state: present + backrefs: true + when: + - '"tftp-server" in ansible_facts.packages' + - tftpd_secure_config_line is defined and tftpd_secure_config_line.matched > 0 + tags: + - CCE-82434-2 + - DISA-STIG-RHEL-08-040350 + - NIST-800-53-AC-6 + - NIST-800-53-CM-6(b) + - NIST-800-53-CM-7(a) + - configure_strategy + - high_severity + - low_complexity + - low_disruption + - no_reboot_needed + - tftpd_uses_secure_mode + +- name: Insert correct config line to start TFTP server with secure directory + lineinfile: + path: /etc/xinetd.d/tftp + line: server_args = -s {{ var_tftpd_secure_directory }} + state: present + create: true + when: + - '"tftp-server" in ansible_facts.packages' + - tftpd_secure_config_line is defined and tftpd_secure_config_line.matched == 0 + tags: + - CCE-82434-2 + - DISA-STIG-RHEL-08-040350 + - NIST-800-53-AC-6 + - NIST-800-53-CM-6(b) + - NIST-800-53-CM-7(a) + - configure_strategy + - high_severity + - low_complexity + - low_disruption + - no_reboot_needed + - tftpd_uses_secure_mode + + # Remediation is applicable only in certain platforms +if rpm --quiet -q tftp-server; then + +var_tftpd_secure_directory='' + + +if grep -q 'server_args' /etc/xinetd.d/tftp; then + sed -i -E "s;^([[:blank:]]*server_args[[:blank:]]+=[[:blank:]]+.*?)(-s[[:blank:]]+[[:graph:]]+)*(.*)$;\1 -s $var_tftpd_secure_directory \3;" /etc/xinetd.d/tftp +else + echo "server_args = -s $var_tftpd_secure_directory" >> /etc/xinetd.d/tftp +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + + + Print Support + The Common Unix Printing System (CUPS) service provides both local +and network printing support. A system running the CUPS service can accept +print jobs from other systems, process them, and send them to the appropriate +printer. It also provides an interface for remote administration through a web +browser. The CUPS service is installed and activated by default. The project +homepage and more detailed documentation are available at + + http://www.cups.org. + + + Disable the CUPS Service + +The cups service can be disabled with the following command: +$ sudo systemctl mask --now cups.service + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + PR.PT-3 + 2.2.4 + Turn off unneeded services to reduce attack surface. + + CCE-82861-6 + include disable_cups + +class disable_cups { + service {'cups': + enable => false, + ensure => 'stopped', + } +} + + - name: Disable service cups + block: + + - name: Disable service cups + systemd: + name: cups.service + enabled: 'no' + state: stopped + masked: 'yes' + ignore_errors: 'yes' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82861-6 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - no_reboot_needed + - service_cups_disabled + - unknown_severity + +- name: Unit Socket Exists - cups.socket + command: systemctl list-unit-files cups.socket + args: + warn: false + register: socket_file_exists + changed_when: false + ignore_errors: true + check_mode: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82861-6 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - no_reboot_needed + - service_cups_disabled + - unknown_severity + +- name: Disable socket cups + systemd: + name: cups.socket + enabled: 'no' + state: stopped + masked: 'yes' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"cups.socket" in socket_file_exists.stdout_lines[1]' + tags: + - CCE-82861-6 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - no_reboot_needed + - service_cups_disabled + - unknown_severity + + +[customizations.services] +disabled = ["cups"] + + apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + systemd: + units: + - name: cups.service + enabled: false + mask: true + - name: cups.socket + enabled: false + mask: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" stop 'cups.service' +"$SYSTEMCTL_EXEC" disable 'cups.service' +"$SYSTEMCTL_EXEC" mask 'cups.service' +# Disable socket activation if we have a unit file for it +if "$SYSTEMCTL_EXEC" list-unit-files | grep -q '^cups.socket'; then + "$SYSTEMCTL_EXEC" stop 'cups.socket' + "$SYSTEMCTL_EXEC" mask 'cups.socket' +fi +# The service may not be running because it has been started and failed, +# so let's reset the state so OVAL checks pass. +# Service should be 'inactive', not 'failed' after reboot though. +"$SYSTEMCTL_EXEC" reset-failed 'cups.service' || true + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure the CUPS Service if Necessary + CUPS provides the ability to easily share local printers with +other systems over the network. It does this by allowing systems to share +lists of available printers. Additionally, each system that runs the CUPS +service can potentially act as a print server. Whenever possible, the printer +sharing and print server capabilities of CUPS should be limited or disabled. +The following recommendations should demonstrate how to do just that. + + Disable Printer Browsing Entirely if Possible + By default, CUPS listens on the network for printer list +broadcasts on UDP port 631. This functionality is called printer browsing. +To disable printer browsing entirely, edit the CUPS configuration +file, located at /etc/cups/cupsd.conf, to include the following: +Browsing Off +BrowseAllow none + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + PR.PT-3 + The CUPS print service can be configured to broadcast a list of +available printers to the network. Other systems on the network, also running +the CUPS print service, can be configured to listen to these broadcasts and add +and configure these printers for immediate use. By disabling this browsing +capability, the system will no longer generate or receive such broadcasts. + + + + + + + + + Disable Print Server Capabilities + To prevent remote users from potentially connecting to and using +locally configured printers, disable the CUPS print server sharing +capabilities. To do so, limit how the server will listen for print jobs by +removing the more generic port directive from /etc/cups/cupsd.conf: +Port 631 +and replacing it with the Listen directive: +Listen localhost:631 +This will prevent remote users from printing to locally configured printers +while still allowing local users on the system to print normally. + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + PR.PT-3 + By default, locally configured printers will not be shared over the +network, but if this functionality has somehow been enabled, these +recommendations will disable it again. Be sure to disable outgoing printer list +broadcasts, or remote users will still be able to see the locally configured +printers, even if they cannot actually print to them. To limit print serving to +a particular set of users, use the Policy directive. + + + + + + + + Proxy Server + A proxy server is a very desirable target for a +potential adversary because much (or all) sensitive data for a +given infrastructure may flow through it. Therefore, if one is +required, the system acting as a proxy server should be dedicated +to that purpose alone and be stored in a physically secure +location. The system's default proxy server software is Squid, and +provided in an RPM package of the same name. + + Disable Squid if Possible + If Squid was installed and activated, but the system +does not need to act as a proxy server, then it should be disabled +and removed. + + Uninstall squid Package + The squid package can be removed with the following command: $ sudo yum erase squid + 2.2.13 + If there is no need to make the proxy server software available, +removing it provides a safeguard against its activation. + CCE-82189-2 + +package --remove=squid + + include remove_squid + +class remove_squid { + package { 'squid': + ensure => 'purged', + } +} + + - name: Ensure squid is removed + package: + name: squid + state: absent + tags: + - CCE-82189-2 + - disable_strategy + - low_complexity + - low_disruption + - no_reboot_needed + - package_squid_removed + - unknown_severity + + +# CAUTION: This remediation script will remove squid +# from the system, and may remove any packages +# that depend on squid. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "squid" ; then + + yum remove -y "squid" + +fi + + + + + + + + + + Disable Squid + +The squid service can be disabled with the following command: +$ sudo systemctl mask --now squid.service + Running proxy server software provides a network-based avenue +of attack, and should be removed if not needed. + + CCE-82190-0 + include disable_squid + +class disable_squid { + service {'squid': + enable => false, + ensure => 'stopped', + } +} + + - name: Disable service squid + block: + + - name: Disable service squid + systemd: + name: squid.service + enabled: 'no' + state: stopped + masked: 'yes' + ignore_errors: 'yes' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82190-0 + - disable_strategy + - low_complexity + - low_disruption + - no_reboot_needed + - service_squid_disabled + - unknown_severity + +- name: Unit Socket Exists - squid.socket + command: systemctl list-unit-files squid.socket + args: + warn: false + register: socket_file_exists + changed_when: false + ignore_errors: true + check_mode: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82190-0 + - disable_strategy + - low_complexity + - low_disruption + - no_reboot_needed + - service_squid_disabled + - unknown_severity + +- name: Disable socket squid + systemd: + name: squid.socket + enabled: 'no' + state: stopped + masked: 'yes' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"squid.socket" in socket_file_exists.stdout_lines[1]' + tags: + - CCE-82190-0 + - disable_strategy + - low_complexity + - low_disruption + - no_reboot_needed + - service_squid_disabled + - unknown_severity + + +[customizations.services] +disabled = ["squid"] + + apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + systemd: + units: + - name: squid.service + enabled: false + mask: true + - name: squid.socket + enabled: false + mask: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" stop 'squid.service' +"$SYSTEMCTL_EXEC" disable 'squid.service' +"$SYSTEMCTL_EXEC" mask 'squid.service' +# Disable socket activation if we have a unit file for it +if "$SYSTEMCTL_EXEC" list-unit-files | grep -q '^squid.socket'; then + "$SYSTEMCTL_EXEC" stop 'squid.socket' + "$SYSTEMCTL_EXEC" mask 'squid.socket' +fi +# The service may not be running because it has been started and failed, +# so let's reset the state so OVAL checks pass. +# Service should be 'inactive', not 'failed' after reboot though. +"$SYSTEMCTL_EXEC" reset-failed 'squid.service' || true + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + + Remote Authentication Dial-In User Service (RADIUS) + Remote Authentication Dial-In User Service (RADIUS) is a networking +protocol, operating on port 1812 that provides centralized +Authentication, Authorization, and Accounting (AAA or Triple A) +management for users who connect and use a network service. + + Remove the FreeRadius Server Package + The freeradius package should be removed if not in use. +Is this system a RADIUS server? If not, remove the package. +The freeradius package can be removed with the following command: + +$ sudo yum erase freeradius +The freeradius RPM is not installed by default on a Red Hat Enterprise Linux 8 +system. It is needed only by the RADIUS servers, not by the +clients which use RADIUS for authentication. If the system is not +intended for use as a RADIUS Server it should be removed. + Unnecessary packages should not be installed to decrease the attack +surface of the system. While this software is clearly essential on a +RADIUS server, it is not necessary on typical desktop or workstation systems. + CCE-82752-7 + +package --remove=freeradius + + include remove_freeradius + +class remove_freeradius { + package { 'freeradius': + ensure => 'purged', + } +} + + - name: Ensure freeradius is removed + package: + name: freeradius + state: absent + tags: + - CCE-82752-7 + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - package_freeradius_removed + + +# CAUTION: This remediation script will remove freeradius +# from the system, and may remove any packages +# that depend on freeradius. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "freeradius" ; then + + yum remove -y "freeradius" + +fi + + + + + + + + + + + Hardware RNG Entropy Gatherer Daemon + The rngd feeds random data from hardware device to kernel random device. + + + Enable the Hardware RNG Entropy Gatherer Service + The Hardware RNG Entropy Gatherer service should be enabled. + +The rngd service can be enabled with the following command: +$ sudo systemctl enable rngd.service + CCI-000366 + FCS_RBG_EXT.1 + SRG-OS-000480-GPOS-00227 + RHEL-08-010471 + SV-230285r627750_rule + The rngd service +feeds random data from hardware device to kernel random device. + CCE-82831-9 + include enable_rngd + +class enable_rngd { + service {'rngd': + enable => true, + ensure => 'running', + } +} + + - name: Enable service rngd + block: + + - name: Gather the package facts + package_facts: + manager: auto + + - name: Enable service rngd + service: + name: rngd + enabled: 'yes' + state: started + masked: 'no' + when: + - '"rng-tools" in ansible_facts.packages' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82831-9 + - DISA-STIG-RHEL-08-010471 + - enable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - service_rngd_enabled + + +[customizations.services] +enabled = ["rngd"] + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" unmask 'rngd.service' +"$SYSTEMCTL_EXEC" start 'rngd.service' +"$SYSTEMCTL_EXEC" enable 'rngd.service' + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Network Routing + A router is a very desirable target for a +potential adversary because they fulfill a variety of +infrastructure networking roles such as access to network segments, +gateways to other networks, filtering, etc. Therefore, if one is +required, the system acting as a router should be dedicated +to that purpose alone and be stored in a physically secure +location. The system's default routing software is Quagga, and +provided in an RPM package of the same name. + + Disable Quagga if Possible + If Quagga was installed and activated, but the system +does not need to act as a router, then it should be disabled +and removed. + + Uninstall quagga Package + The quagga package can be removed with the following command: $ sudo yum erase quagga + 12 + 15 + 8 + APO13.01 + DSS05.02 + CCI-000366 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + A.13.1.1 + A.13.2.1 + A.14.1.3 + CM-7(a) + CM-7(b) + CM-6(a) + PR.PT-4 + SRG-OS-000480-GPOS-00227 + Routing software is typically used on routers to exchange network topology information +with other routers. If routing software is used when not required, system network +information may be unnecessarily transmitted across the network. + +If there is no need to make the router software available, +removing it provides a safeguard against its activation. + CCE-82187-6 + +package --remove=quagga + + include remove_quagga + +class remove_quagga { + package { 'quagga': + ensure => 'purged', + } +} + + - name: Ensure quagga is removed + package: + name: quagga + state: absent + tags: + - CCE-82187-6 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - package_quagga_removed + + +# CAUTION: This remediation script will remove quagga +# from the system, and may remove any packages +# that depend on quagga. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "quagga" ; then + + yum remove -y "quagga" + +fi + + + + + + + + + + Disable Quagga Service + +The zebra service can be disabled with the following command: +$ sudo systemctl mask --now zebra.service + 12 + 15 + 8 + APO13.01 + DSS05.02 + CCI-000366 + 164.308(a)(4)(i) + 164.308(b)(1) + 164.308(b)(3) + 164.310(b) + 164.312(e)(1) + 164.312(e)(2)(ii) + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + A.13.1.1 + A.13.2.1 + A.14.1.3 + CM-7(a) + CM-7(b) + CM-6(a) + PR.PT-4 + SRG-OS-000480-GPOS-00227 + Routing protocol daemons are typically used on routers to exchange network +topology information with other routers. If routing daemons are used when not +required, system network information may be unnecessarily transmitted across +the network. + + CCE-80889-9 + include disable_zebra + +class disable_zebra { + service {'zebra': + enable => false, + ensure => 'stopped', + } +} + + - name: Disable service zebra + block: + + - name: Disable service zebra + systemd: + name: zebra.service + enabled: 'no' + state: stopped + masked: 'yes' + ignore_errors: 'yes' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80889-9 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_zebra_disabled + +- name: Unit Socket Exists - zebra.socket + command: systemctl list-unit-files zebra.socket + args: + warn: false + register: socket_file_exists + changed_when: false + ignore_errors: true + check_mode: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80889-9 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_zebra_disabled + +- name: Disable socket zebra + systemd: + name: zebra.socket + enabled: 'no' + state: stopped + masked: 'yes' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"zebra.socket" in socket_file_exists.stdout_lines[1]' + tags: + - CCE-80889-9 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_zebra_disabled + + +[customizations.services] +disabled = ["zebra"] + + apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + systemd: + units: + - name: zebra.service + enabled: false + mask: true + - name: zebra.socket + enabled: false + mask: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" stop 'zebra.service' +"$SYSTEMCTL_EXEC" disable 'zebra.service' +"$SYSTEMCTL_EXEC" mask 'zebra.service' +# Disable socket activation if we have a unit file for it +if "$SYSTEMCTL_EXEC" list-unit-files | grep -q '^zebra.socket'; then + "$SYSTEMCTL_EXEC" stop 'zebra.socket' + "$SYSTEMCTL_EXEC" mask 'zebra.socket' +fi +# The service may not be running because it has been started and failed, +# so let's reset the state so OVAL checks pass. +# Service should be 'inactive', not 'failed' after reboot though. +"$SYSTEMCTL_EXEC" reset-failed 'zebra.service' || true + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + + Samba(SMB) Microsoft Windows File Sharing Server + When properly configured, the Samba service allows +Linux systems to provide file and print sharing to Microsoft +Windows systems. There are two software packages that provide +Samba support. The first, samba-client, provides a series of +command line tools that enable a client system to access Samba +shares. The second, simply labeled samba, provides the Samba +service. It is this second package that allows a Linux system to +act as an Active Directory server, a domain controller, or as a +domain member. Only the samba-client package is installed by +default. + + Configure Samba if Necessary + All settings for the Samba daemon can be found in +/etc/samba/smb.conf. Settings are divided between a +[global] configuration section and a series of user +created share definition sections meant to describe file or print +shares on the system. By default, Samba will operate in user mode +and allow client systems to access local home directories and +printers. It is recommended that these settings be changed or that +additional limitations be set in place. + + Install the Samba Common Package + The samba-common package should be installed. +The samba-common package can be installed with the following command: + +$ sudo yum install samba-common + If the samba-common package is not installed, samba cannot be configured. + +package --add=samba-common + + include install_samba-common + +class install_samba-common { + package { 'samba-common': + ensure => 'installed', + } +} + + - name: Ensure samba-common is installed + package: + name: samba-common + state: present + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_samba-common_installed + + +[[packages]] +name = "samba-common" +version = "*" + + +if ! rpm -q --quiet "samba-common" ; then + yum install -y "samba-common" +fi + + + + + + + + + + Require Client SMB Packet Signing, if using mount.cifs + Require packet signing of clients who mount Samba +shares using the mount.cifs program (e.g., those who specify shares +in /etc/fstab). To do so, ensure signing options (either +sec=krb5i or sec=ntlmv2i) are used. + +See the mount.cifs(8) man page for more information. A Samba +client should only communicate with servers who can support SMB +packet signing. + Packet signing can prevent man-in-the-middle +attacks which modify SMB packets in transit. + + + + + + + + + + Require Client SMB Packet Signing, if using smbclient + To require samba clients running smbclient to use +packet signing, add the following to the [global] section +of the Samba configuration file, /etc/samba/smb.conf: +client signing = rhisam +Requiring samba clients such as smbclient to use packet +signing ensures they can +only communicate with servers that support packet signing. + Packet signing can prevent +man-in-the-middle attacks which modify SMB packets in +transit. + - name: Check if /etc/samba/smb.conf exists + stat: + path: /etc/samba/smb.conf + register: st_smb + tags: + - configure_strategy + - low_complexity + - medium_disruption + - no_reboot_needed + - require_smb_client_signing + - unknown_severity + +- name: Require Client SMB Packet Signing, if using smbclient + lineinfile: + dest: /etc/samba/smb.conf + line: client signing = rhisam + state: present + insertafter: + - global + when: st_smb.stat.exists + tags: + - configure_strategy + - low_complexity + - medium_disruption + - no_reboot_needed + - require_smb_client_signing + - unknown_severity + + ###################################################################### +#By Luke "Brisk-OH" Brisk +#luke.brisk@boeing.com or luke.brisk@gmail.com +###################################################################### + +CLIENTSIGNING=$( grep -ic 'client signing' /etc/samba/smb.conf ) + +if [ "$CLIENTSIGNING" -eq 0 ]; then + # Add to global section + sed -i 's/\[global\]/\[global\]\n\n\tclient signing = rhisam/g' /etc/samba/smb.conf +else + sed -i 's/[[:blank:]]*client[[:blank:]]signing[[:blank:]]*=[[:blank:]]*no/ client signing = rhisam/g' /etc/samba/smb.conf +fi + + + + + + + + + + Disable Root Access to SMB Shares + Administrators should not use administrator accounts to access +Samba file and printer shares. Disable the root user and the wheel +administrator group: +[share] + invalid users = root @wheel +If administrator accounts cannot be disabled, ensure that local system +passwords and Samba service passwords do not match. + Typically, administrator access is required when Samba must create user and +system accounts and shares. Domain member servers and standalone servers may +not need administrator access at all. If that is the case, add the invalid +users parameter to [global] instead. + + + Restrict Printer Sharing + By default, Samba utilizes the CUPS printing service to enable +printer sharing with Microsoft Windows workstations. If there are no printers +on the local system, or if printer sharing with Microsoft Windows is not +required, disable the printer sharing capability by commenting out the +following lines, found in /etc/samba/smb.conf: +[global] + load printers = yes + cups options = raw +[printers] + comment = All Printers + path = /usr/spool/samba + browseable = no + guest ok = no + writable = no + printable = yes +There may be other options present, but these are the only options enabled and +uncommented by default. Removing the [printers] share should be enough +for most users. If the Samba printer sharing capability is needed, consider +disabling the Samba network browsing capability or restricting access to a +particular set of users or network addresses. Set the valid users +parameter to a small subset of users or restrict it to a particular group of +users with the shorthand @. Separate each user or group of users with +a space. For example, under the [printers] share: +[printers] + valid users = user @printerusers + + + Restrict SMB File Sharing to Configured Networks + Only users with local user accounts will be able to log in to +Samba shares by default. Shares can be limited to particular users or network +addresses. Use the hosts allow and hosts deny directives +accordingly, and consider setting the valid users directive to a limited subset +of users or to a group of users. Separate each address, user, or user group +with a space as follows for a particular share or global: +[share] + hosts allow = 192.168.1. 127.0.0.1 + valid users = userone usertwo @usergroup +It is also possible to limit read and write access to particular users with the +read list and write list options, though the permissions set by the system +itself will override these settings. Set the read only attribute for each share +to ensure that global settings will not accidentally override the individual +share settings. Then, as with the valid users directive, separate each user or +group of users with a space: +[share] + read only = yes + write list = userone usertwo @usergroup + + + + Disable Samba if Possible + Even after the Samba server package has been installed, it +will remain disabled. Do not enable this service unless it is +absolutely necessary to provide Microsoft Windows file and print +sharing functionality. + + Uninstall Samba Package + The samba package can be removed with the following command: $ sudo yum erase samba + 2.2.12 + If there is no need to make the Samba software available, +removing it provides a safeguard against its activation. + CCE-85978-5 + +package --remove=samba + + include remove_samba + +class remove_samba { + package { 'samba': + ensure => 'purged', + } +} + + - name: Ensure samba is removed + package: + name: samba + state: absent + tags: + - CCE-85978-5 + - disable_strategy + - low_complexity + - low_disruption + - no_reboot_needed + - package_samba_removed + - unknown_severity + + +# CAUTION: This remediation script will remove samba +# from the system, and may remove any packages +# that depend on samba. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "samba" ; then + + yum remove -y "samba" + +fi + + + + + + + + + + Disable Samba + +The smb service can be disabled with the following command: +$ sudo systemctl mask --now smb.service + CCI-001436 + Running a Samba server provides a network-based avenue of attack, and +should be disabled if not needed. + + CCE-82759-2 + include disable_smb + +class disable_smb { + service {'smb': + enable => false, + ensure => 'stopped', + } +} + + - name: Disable service smb + block: + + - name: Disable service smb + systemd: + name: smb.service + enabled: 'no' + state: stopped + masked: 'yes' + ignore_errors: 'yes' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82759-2 + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - service_smb_disabled + +- name: Unit Socket Exists - smb.socket + command: systemctl list-unit-files smb.socket + args: + warn: false + register: socket_file_exists + changed_when: false + ignore_errors: true + check_mode: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82759-2 + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - service_smb_disabled + +- name: Disable socket smb + systemd: + name: smb.socket + enabled: 'no' + state: stopped + masked: 'yes' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"smb.socket" in socket_file_exists.stdout_lines[1]' + tags: + - CCE-82759-2 + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - service_smb_disabled + + +[customizations.services] +disabled = ["smb"] + + apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + systemd: + units: + - name: smb.service + enabled: false + mask: true + - name: smb.socket + enabled: false + mask: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" stop 'smb.service' +"$SYSTEMCTL_EXEC" disable 'smb.service' +"$SYSTEMCTL_EXEC" mask 'smb.service' +# Disable socket activation if we have a unit file for it +if "$SYSTEMCTL_EXEC" list-unit-files | grep -q '^smb.socket'; then + "$SYSTEMCTL_EXEC" stop 'smb.socket' + "$SYSTEMCTL_EXEC" mask 'smb.socket' +fi +# The service may not be running because it has been started and failed, +# so let's reset the state so OVAL checks pass. +# Service should be 'inactive', not 'failed' after reboot though. +"$SYSTEMCTL_EXEC" reset-failed 'smb.service' || true + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + + SNMP Server + The Simple Network Management Protocol allows +administrators to monitor the state of network devices, including +computers. Older versions of SNMP were well-known for weak +security, such as plaintext transmission of the community string +(used for authentication) and usage of easily-guessable +choices for the community string. + + Disable SNMP Server if Possible + The system includes an SNMP daemon that allows for its remote +monitoring, though it not installed by default. If it was installed and +activated but is not needed, the software should be disabled and removed. + + Uninstall net-snmp Package + +The net-snmp package provides the snmpd service. +The net-snmp package can be removed with the following command: + +$ sudo yum erase net-snmp + 2.2.14 + If there is no need to run SNMP server software, +removing the package provides a safeguard against its +activation. + CCE-85980-1 + +package --remove=net-snmp + + include remove_net-snmp + +class remove_net-snmp { + package { 'net-snmp': + ensure => 'purged', + } +} + + - name: Ensure net-snmp is removed + package: + name: net-snmp + state: absent + tags: + - CCE-85980-1 + - disable_strategy + - low_complexity + - low_disruption + - no_reboot_needed + - package_net-snmp_removed + - unknown_severity + + +# CAUTION: This remediation script will remove net-snmp +# from the system, and may remove any packages +# that depend on net-snmp. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "net-snmp" ; then + + yum remove -y "net-snmp" + +fi + + + + + + + + + + Disable snmpd Service + +The snmpd service can be disabled with the following command: +$ sudo systemctl mask --now snmpd.service + 1311 + SRG-OS-000480-VMM-002000 + Running SNMP software provides a network-based avenue of attack, and +should be disabled if not needed. + + CCE-82758-4 + include disable_snmpd + +class disable_snmpd { + service {'snmpd': + enable => false, + ensure => 'stopped', + } +} + + - name: Disable service snmpd + block: + + - name: Disable service snmpd + systemd: + name: snmpd.service + enabled: 'no' + state: stopped + masked: 'yes' + ignore_errors: 'yes' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82758-4 + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - service_snmpd_disabled + +- name: Unit Socket Exists - snmpd.socket + command: systemctl list-unit-files snmpd.socket + args: + warn: false + register: socket_file_exists + changed_when: false + ignore_errors: true + check_mode: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82758-4 + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - service_snmpd_disabled + +- name: Disable socket snmpd + systemd: + name: snmpd.socket + enabled: 'no' + state: stopped + masked: 'yes' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"snmpd.socket" in socket_file_exists.stdout_lines[1]' + tags: + - CCE-82758-4 + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - service_snmpd_disabled + + +[customizations.services] +disabled = ["snmpd"] + + apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + systemd: + units: + - name: snmpd.service + enabled: false + mask: true + - name: snmpd.socket + enabled: false + mask: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" stop 'snmpd.service' +"$SYSTEMCTL_EXEC" disable 'snmpd.service' +"$SYSTEMCTL_EXEC" mask 'snmpd.service' +# Disable socket activation if we have a unit file for it +if "$SYSTEMCTL_EXEC" list-unit-files | grep -q '^snmpd.socket'; then + "$SYSTEMCTL_EXEC" stop 'snmpd.socket' + "$SYSTEMCTL_EXEC" mask 'snmpd.socket' +fi +# The service may not be running because it has been started and failed, +# so let's reset the state so OVAL checks pass. +# Service should be 'inactive', not 'failed' after reboot though. +"$SYSTEMCTL_EXEC" reset-failed 'snmpd.service' || true + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Configure SNMP Server if Necessary + If it is necessary to run the snmpd agent on the system, some best +practices should be followed to minimize the security risk from the +installation. The multiple security models implemented by SNMP cannot be fully +covered here so only the following general configuration advice can be offered: +use only SNMP version 3 security models and enable the use of authentication and encryptionwrite access to the MIB (Management Information Base) should be allowed only if necessaryall access to the MIB should be restricted following a principle of least privilegenetwork access should be limited to the maximum extent possible including restricting to expected network +addresses both in the configuration files and in the system firewall rulesensure SNMP agents send traps only to, and accept SNMP queries only from, authorized management +stationsensure that permissions on the snmpd.conf configuration file (by default, in /etc/snmp) are 640 or more restrictiveensure that any MIB files' permissions are also 640 or more restrictive + + SNMP read-only community string + Specify the SNMP community string used for read-only access. + changemero + + + SNMP read-write community string + Specify the SNMP community string used for read-write access. + changemerw + + + Ensure SNMP Read Write is disabled + Edit /etc/snmp/snmpd.conf, remove any rwuser entries. +Once the read write users have been removed, restart the SNMP service: +$ sudo service snmpd restart + Certain SNMP settings can permit users to execute system behaviors from user +writes to the community strings. +This may permit a compromised account to execute commands on a remote system. + + CCE-82733-7 + # Remediation is applicable only in certain platforms +if rpm --quiet -q net-snmp; then + +if grep -s "rwuser" /etc/snmp/snmpd.conf | grep -qv "^#"; then + sed -i "/^\s*#/b;/rwuser/ s/^/#/" /etc/snmp/snmpd.conf +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + Ensure Default SNMP Password Is Not Used + Edit /etc/snmp/snmpd.conf, remove or change the default community strings of +public and private. +This profile configures new read-only community string to and read-write community string to . +Once the default community strings have been changed, restart the SNMP service: +$ sudo service snmpd restart + 1 + 12 + 15 + 16 + 5 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.03 + DSS06.10 + CCI-000366 + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.2 + 4.3.3.7.4 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + A.18.1.4 + A.7.1.1 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.2 + A.9.4.3 + IA-5(e) + PR.AC-1 + PR.AC-6 + PR.AC-7 + SRG-OS-000480-GPOS-00227 + Whether active or not, default simple network management protocol (SNMP) community +strings must be changed to maintain security. If the service is running with the +default authenticators, then anyone can gather data about the system and the network +and use the information to potentially compromise the integrity of the system and +network(s). + + - name: Gather the package facts + package_facts: + manager: auto + tags: + - NIST-800-53-IA-5(e) + - configure_strategy + - high_severity + - low_complexity + - medium_disruption + - no_reboot_needed + - snmpd_not_default_password +- name: XCCDF Value var_snmpd_ro_string # promote to variable + set_fact: + var_snmpd_ro_string: !!str + tags: + - always +- name: XCCDF Value var_snmpd_rw_string # promote to variable + set_fact: + var_snmpd_rw_string: !!str + tags: + - always + +- name: Check if file /etc/snmp/snmpd.conf exists + stat: + path: /etc/snmp/snmpd.conf + register: snmpd + when: '"net-snmp" in ansible_facts.packages' + tags: + - NIST-800-53-IA-5(e) + - configure_strategy + - high_severity + - low_complexity + - medium_disruption + - no_reboot_needed + - snmpd_not_default_password + +- name: Replace all instances of SNMP RO strings + replace: + path: /etc/snmp/snmpd.conf + regexp: public + replace: '{{ var_snmpd_ro_string }}' + when: + - '"net-snmp" in ansible_facts.packages' + - (snmpd.stat.exists is defined and snmpd.stat.exists) + tags: + - NIST-800-53-IA-5(e) + - configure_strategy + - high_severity + - low_complexity + - medium_disruption + - no_reboot_needed + - snmpd_not_default_password + +- name: Replace all instances of SNMP RW strings + replace: + path: /etc/snmp/snmpd.conf + regexp: private + replace: '{{ var_snmpd_rw_string }}' + when: + - '"net-snmp" in ansible_facts.packages' + - (snmpd.stat.exists is defined and snmpd.stat.exists) + tags: + - NIST-800-53-IA-5(e) + - configure_strategy + - high_severity + - low_complexity + - medium_disruption + - no_reboot_needed + - snmpd_not_default_password + + # Remediation is applicable only in certain platforms +if rpm --quiet -q net-snmp; then + +var_snmpd_ro_string='' +var_snmpd_rw_string='' + + +# remediate read-only community string +if grep -q 'public' /etc/snmp/snmpd.conf; then + sed -i "s/public/$var_snmpd_ro_string/" /etc/snmp/snmpd.conf +fi + +# remediate read-write community string +if grep -q 'private' /etc/snmp/snmpd.conf; then + sed -i "s/private/$var_snmpd_rw_string/" /etc/snmp/snmpd.conf +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure SNMP Service to Use Only SNMPv3 or Newer + Edit /etc/snmp/snmpd.conf, removing any references to rocommunity, rwcommunity, or com2sec. +Upon doing that, restart the SNMP service: +$ sudo service snmpd restart + 1311 + Earlier versions of SNMP are considered insecure, as they potentially allow +unauthorized access to detailed system management information. + + CCE-84292-2 + + + + + + + + + + + SSH Server + The SSH protocol is recommended for remote login and +remote file transfer. SSH provides confidentiality and integrity +for data exchanged between two systems, as well as server +authentication, through the use of public key cryptography. The +implementation included with the system is called OpenSSH, and more +detailed documentation is available from its website, + + https://www.openssh.com. +Its server program is called sshd and provided by the RPM package +openssh-server. + + + SSH enabled firewalld zone + Specify firewalld zone to enable SSH service. This value is used only for remediation purposes. + block + public + dmz + drop + external + home + internal + public + trusted + work + + + SSH Approved ciphers by FIPS + Specify the FIPS approved ciphers that are used for data integrity protection by the SSH server. + aes256-ctr,aes192-ctr,aes128-ctr + aes128-ctr,aes192-ctr,aes256-ctr,aes128-cbc,3des-cbc,aes192-cbc,aes256-cbc,rijndael-cbc@lysator.liu.se + chacha20-poly1305@openssh.com,aes128-ctr,aes192-ctr,aes256-ctr,aes128-gcm@openssh.com,aes256-gcm@openssh.com,aes128-cbc,aes192-cbc,aes256-cbc,blowfish-cbc,cast128-cbc,3des-cbc + chacha20-poly1305@openssh.com,aes128-ctr,aes192-ctr,aes256-ctr,aes128-gcm@openssh.com,aes256-gcm@openssh.com,aes128-cbc,aes192-cbc,aes256-cbc,blowfish-cbc,cast128-cbc,3des-cbc + chacha20-poly1305@openssh.com,aes128-ctr,aes192-ctr,aes256-ctr,aes128-gcm@openssh.com,aes256-gcm@openssh.com,aes128-cbc,aes192-cbc,aes256-cbc,blowfish-cbc,cast128-cbc,3des-cbc + chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr + + + SSH Approved MACs by FIPS + Specify the FIPS approved MACs (message authentication code) algorithms + that are used for data integrity protection by the SSH server. + hmac-sha2-512,hmac-sha2-256 + hmac-sha2-512,hmac-sha2-256,hmac-sha1,hmac-sha1-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com + umac-64-etm@openssh.com,umac-128-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com,hmac-sha1-etm@openssh.com,umac-64@openssh.com,umac-128@openssh.com,hmac-sha2-256,hmac-sha2-512,hmac-sha1,hmac-sha1-etm@openssh.com + umac-64-etm@openssh.com,umac-128-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com,hmac-sha1-etm@openssh.com,umac-64@openssh.com,umac-128@openssh.com,hmac-sha2-256,hmac-sha2-512,hmac-sha1,hmac-sha1-etm@openssh.com + umac-64-etm@openssh.com,umac-128-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com,hmac-sha1-etm@openssh.com,umac-64@openssh.com,umac-128@openssh.com,hmac-sha2-256,hmac-sha2-512,hmac-sha1,hmac-sha1-etm@openssh.com + hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-sha2-512,hmac-sha2-256 + + + SSH session Idle time + Specify duration of allowed idle time. + 600 + 7200 + 840 + 900 + 1800 + 300 + 3600 + 300 + + + SSH Server Listening Port + Specify port the SSH server is listening. + 22 + + + SSH Max authentication attempts + Specify the maximum number of authentication attempts per connection. + 10 + 3 + 4 + 5 + 4 + + + SSH is required to be installed + Specify if the Policy requires SSH to be installed. Used by SSH Rules +to determine if SSH should be uninstalled or configured. +A value of 0 means that the policy doesn't care if OpenSSH server is installed or not. If it is installed, scanner will check for it's configuration, if it's not installed, the check will pass. +A value of 1 indicates that OpenSSH server package is not required by the policy; +A value of 2 indicates that OpenSSH server package is required by the policy. + 0 + 1 + 2 + + + SSH Max Sessions Count + Specify the maximum number of open sessions permitted. + 10 + 4 + 3 + 2 + 1 + 0 + 10 + + + SSH Max Keep Alive Count + Specify the maximum number of idle message counts before session is terminated. + 10 + 3 + 5 + 0 + 1 + 0 + + + Install OpenSSH client software + The openssh-clients package can be installed with the following command: + +$ sudo yum install openssh-clients + FIA_UAU.5 + FTP_ITC_EXT.1 + FCS_SSH_EXT.1 + FCS_SSHC_EXT.1 + SRG-OS-000480-GPOS-00227 + This package includes utilities to make encrypted connections and transfer +files securely to SSH servers. + CCE-82722-0 + +package --add=openssh-clients + + include install_openssh-clients + +class install_openssh-clients { + package { 'openssh-clients': + ensure => 'installed', + } +} + + - name: Ensure openssh-clients is installed + package: + name: openssh-clients + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82722-0 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_openssh-clients_installed + + +[[packages]] +name = "openssh-clients" +version = "*" + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if ! rpm -q --quiet "openssh-clients" ; then + yum install -y "openssh-clients" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Install the OpenSSH Server Package + The openssh-server package should be installed. +The openssh-server package can be installed with the following command: + +$ sudo yum install openssh-server + 13 + 14 + APO01.06 + DSS05.02 + DSS05.04 + DSS05.07 + DSS06.02 + DSS06.06 + CCI-002418 + CCI-002420 + CCI-002421 + CCI-002422 + SR 3.1 + SR 3.8 + SR 4.1 + SR 4.2 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-6(a) + PR.DS-2 + PR.DS-5 + FIA_UAU.5 + FTP_ITC_EXT.1 + FCS_SSH_EXT.1 + FCS_SSHS_EXT.1 + SRG-OS-000423-GPOS-00187 + SRG-OS-000424-GPOS-00188 + SRG-OS-000425-GPOS-00189 + SRG-OS-000426-GPOS-00190 + RHEL-08-040159 + SV-244549r743896_rule + Without protection of the transmitted information, confidentiality, and +integrity may be compromised because unprotected communications can be +intercepted and either read or altered. + CCE-83303-8 + +package --add=openssh-server + + include install_openssh-server + +class install_openssh-server { + package { 'openssh-server': + ensure => 'installed', + } +} + + - name: Ensure openssh-server is installed + package: + name: openssh-server + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83303-8 + - DISA-STIG-RHEL-08-040159 + - NIST-800-53-CM-6(a) + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_openssh-server_installed + + +[[packages]] +name = "openssh-server" +version = "*" + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if ! rpm -q --quiet "openssh-server" ; then + yum install -y "openssh-server" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Remove the OpenSSH Server Package + The openssh-server package should be removed. +The openssh-server package can be removed with the following command: + +$ sudo yum erase openssh-server + Without protection of the transmitted information, confidentiality, and +integrity may be compromised because unprotected communications can be +intercepted and either read or altered. + +package --remove=openssh-server + + include remove_openssh-server + +class remove_openssh-server { + package { 'openssh-server': + ensure => 'purged', + } +} + + - name: Ensure openssh-server is removed + package: + name: openssh-server + state: absent + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_openssh-server_removed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# CAUTION: This remediation script will remove openssh-server +# from the system, and may remove any packages +# that depend on openssh-server. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "openssh-server" ; then + + yum remove -y "openssh-server" + +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Enable the OpenSSH Service + The SSH server service, sshd, is commonly needed. + +The sshd service can be enabled with the following command: +$ sudo systemctl enable sshd.service + 13 + 14 + APO01.06 + DSS05.02 + DSS05.04 + DSS05.07 + DSS06.02 + DSS06.06 + 3.1.13 + 3.5.4 + 3.13.8 + CCI-002418 + CCI-002420 + CCI-002421 + CCI-002422 + SR 3.1 + SR 3.8 + SR 4.1 + SR 4.2 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-6(a) + SC-8 + SC-8(1) + SC-8(2) + SC-8(3) + SC-8(4) + PR.DS-2 + PR.DS-5 + SRG-OS-000423-GPOS-00187 + SRG-OS-000424-GPOS-00188 + SRG-OS-000425-GPOS-00189 + SRG-OS-000426-GPOS-00190 + RHEL-08-040160 + SV-230526r744032_rule + Without protection of the transmitted information, confidentiality, and +integrity may be compromised because unprotected communications can be +intercepted and either read or altered. + +This checklist item applies to both internal and external networks and all types +of information system components from which information can be transmitted (e.g., servers, +mobile devices, notebook computers, printers, copiers, scanners, etc). Communication paths +outside the physical protection of a controlled boundary are exposed to the possibility +of interception and modification. + CCE-82426-8 + include enable_sshd + +class enable_sshd { + service {'sshd': + enable => true, + ensure => 'running', + } +} + + - name: Enable service sshd + block: + + - name: Gather the package facts + package_facts: + manager: auto + + - name: Enable service sshd + service: + name: sshd + enabled: 'yes' + state: started + masked: 'no' + when: + - '"openssh-server" in ansible_facts.packages' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82426-8 + - DISA-STIG-RHEL-08-040160 + - NIST-800-171-3.1.13 + - NIST-800-171-3.13.8 + - NIST-800-171-3.5.4 + - NIST-800-53-CM-6(a) + - NIST-800-53-SC-8 + - NIST-800-53-SC-8(1) + - NIST-800-53-SC-8(2) + - NIST-800-53-SC-8(3) + - NIST-800-53-SC-8(4) + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_sshd_enabled + + +[customizations.services] +enabled = ["sshd"] + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" unmask 'sshd.service' +"$SYSTEMCTL_EXEC" start 'sshd.service' +"$SYSTEMCTL_EXEC" enable 'sshd.service' + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable SSH Server If Possible (Unusual) + The SSH server service, sshd, is commonly needed. +However, if it can be disabled, do so. + + +The sshd service can be disabled with the following command: +$ sudo systemctl mask --now sshd.service + +This is unusual, as SSH is a common method for encrypted and authenticated +remote access. + CM-3(6) + IA-2(4) + + include disable_sshd + +class disable_sshd { + service {'sshd': + enable => false, + ensure => 'stopped', + } +} + + - name: Disable service sshd + block: + + - name: Disable service sshd + systemd: + name: sshd.service + enabled: 'no' + state: stopped + masked: 'yes' + ignore_errors: 'yes' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-CM-3(6) + - NIST-800-53-IA-2(4) + - disable_strategy + - low_complexity + - low_disruption + - no_reboot_needed + - service_sshd_disabled + - unknown_severity + +- name: Unit Socket Exists - sshd.socket + command: systemctl list-unit-files sshd.socket + args: + warn: false + register: socket_file_exists + changed_when: false + ignore_errors: true + check_mode: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-CM-3(6) + - NIST-800-53-IA-2(4) + - disable_strategy + - low_complexity + - low_disruption + - no_reboot_needed + - service_sshd_disabled + - unknown_severity + +- name: Disable socket sshd + systemd: + name: sshd.socket + enabled: 'no' + state: stopped + masked: 'yes' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"sshd.socket" in socket_file_exists.stdout_lines[1]' + tags: + - NIST-800-53-CM-3(6) + - NIST-800-53-IA-2(4) + - disable_strategy + - low_complexity + - low_disruption + - no_reboot_needed + - service_sshd_disabled + - unknown_severity + + +[customizations.services] +disabled = ["sshd"] + + apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + systemd: + units: + - name: sshd.service + enabled: false + mask: true + - name: sshd.socket + enabled: false + mask: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" stop 'sshd.service' +"$SYSTEMCTL_EXEC" disable 'sshd.service' +"$SYSTEMCTL_EXEC" mask 'sshd.service' +# Disable socket activation if we have a unit file for it +if "$SYSTEMCTL_EXEC" list-unit-files | grep -q '^sshd.socket'; then + "$SYSTEMCTL_EXEC" stop 'sshd.socket' + "$SYSTEMCTL_EXEC" mask 'sshd.socket' +fi +# The service may not be running because it has been started and failed, +# so let's reset the state so OVAL checks pass. +# Service should be 'inactive', not 'failed' after reboot though. +"$SYSTEMCTL_EXEC" reset-failed 'sshd.service' || true + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + Verify Group Who Owns SSH Server config file + +To properly set the group owner of /etc/ssh/sshd_config, run the command: +$ sudo chgrp root /etc/ssh/sshd_config + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + AC-17(a) + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + SRG-OS-000480-GPOS-00227 + 5.2.1 + Service configuration files enable or disable features of their respective +services that if configured incorrectly can lead to insecure and vulnerable +configurations. Therefore, service configuration files should be owned by the +correct group to prevent unauthorized changes. + CCE-82901-0 + - name: Test for existence /etc/ssh/sshd_config + stat: + path: /etc/ssh/sshd_config + register: file_exists + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82901-0 + - NIST-800-53-AC-17(a) + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_groupowner_sshd_config + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure group owner 0 on /etc/ssh/sshd_config + file: + path: /etc/ssh/sshd_config + group: '0' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-82901-0 + - NIST-800-53-AC-17(a) + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_groupowner_sshd_config + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +chgrp 0 /etc/ssh/sshd_config + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Verify Owner on SSH Server config file + +To properly set the owner of /etc/ssh/sshd_config, run the command: +$ sudo chown root /etc/ssh/sshd_config + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + AC-17(a) + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + SRG-OS-000480-GPOS-00227 + 5.2.1 + Service configuration files enable or disable features of their respective +services that if configured incorrectly can lead to insecure and vulnerable +configurations. Therefore, service configuration files should be owned by the +correct group to prevent unauthorized changes. + CCE-82898-8 + - name: Test for existence /etc/ssh/sshd_config + stat: + path: /etc/ssh/sshd_config + register: file_exists + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82898-8 + - NIST-800-53-AC-17(a) + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_owner_sshd_config + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure owner 0 on /etc/ssh/sshd_config + file: + path: /etc/ssh/sshd_config + owner: '0' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-82898-8 + - NIST-800-53-AC-17(a) + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_owner_sshd_config + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +chown 0 /etc/ssh/sshd_config + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Verify Permissions on SSH Server config file + +To properly set the permissions of /etc/ssh/sshd_config, run the command: +$ sudo chmod 0600 /etc/ssh/sshd_config + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + AC-17(a) + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + SRG-OS-000480-GPOS-00227 + 5.2.1 + Service configuration files enable or disable features of their respective +services that if configured incorrectly can lead to insecure and vulnerable +configurations. Therefore, service configuration files should be owned by the +correct group to prevent unauthorized changes. + CCE-82894-7 + - name: Test for existence /etc/ssh/sshd_config + stat: + path: /etc/ssh/sshd_config + register: file_exists + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82894-7 + - NIST-800-53-AC-17(a) + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_permissions_sshd_config + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure permission u-xs,g-xwrs,o-xwrt on /etc/ssh/sshd_config + file: + path: /etc/ssh/sshd_config + mode: u-xs,g-xwrs,o-xwrt + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-82894-7 + - NIST-800-53-AC-17(a) + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_permissions_sshd_config + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +chmod u-xs,g-xwrs,o-xwrt /etc/ssh/sshd_config + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Verify Permissions on SSH Server Private *_key Key Files + SSH server private keys - files that match the /etc/ssh/*_key glob, have to have restricted permissions. +If those files are owned by the root user and the root group, they have to have the 0600 permission or stricter. +If they are owned by the root user, but by a dedicated group ssh_keys, they can have the 0640 permission or stricter. + BP28(R36) + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + 3.1.13 + 3.13.10 + CCI-000366 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + AC-17(a) + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + SRG-OS-000480-GPOS-00227 + RHEL-08-010490 + 5.2.3 + SV-230287r743951_rule + If an unauthorized user obtains the private SSH host key file, the host could be +impersonated. + CCE-82424-3 + include ssh_private_key_perms + +class ssh_private_key_perms { + exec { 'sshd_priv_key': + command => "chmod 0640 /etc/ssh/*_key", + path => '/bin:/usr/bin' + } +} + + - name: Find root:root-owned keys + command: find -H /etc/ssh/ -maxdepth 1 -user root -regex ".*_key$" -type f -group + root -perm /u+xs,g+xwrs,o+xwrt + register: root_owned_keys + changed_when: false + failed_when: false + check_mode: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82424-3 + - DISA-STIG-RHEL-08-010490 + - NIST-800-171-3.1.13 + - NIST-800-171-3.13.10 + - NIST-800-53-AC-17(a) + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_permissions_sshd_private_key + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Set permissions for root:root-owned keys + file: + path: '{{ item }}' + mode: u-xs,g-xwrs,o-xwrt + state: file + with_items: + - '{{ root_owned_keys.stdout_lines }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82424-3 + - DISA-STIG-RHEL-08-010490 + - NIST-800-171-3.1.13 + - NIST-800-171-3.13.10 + - NIST-800-53-AC-17(a) + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_permissions_sshd_private_key + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Find root:ssh_keys-owned keys + command: find -H /etc/ssh/ -maxdepth 1 -user root -regex ".*_key$" -type f -group + ssh_keys -perm /u+xs,g+xws,o+xwrt + register: dedicated_group_owned_keys + changed_when: false + failed_when: false + check_mode: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82424-3 + - DISA-STIG-RHEL-08-010490 + - NIST-800-171-3.1.13 + - NIST-800-171-3.13.10 + - NIST-800-53-AC-17(a) + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_permissions_sshd_private_key + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Set permissions for root:ssh_keys-owned keys + file: + path: '{{ item }}' + mode: u-xs,g-xws,o-xwrt + state: file + with_items: + - '{{ dedicated_group_owned_keys.stdout_lines }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82424-3 + - DISA-STIG-RHEL-08-010490 + - NIST-800-171-3.1.13 + - NIST-800-171-3.13.10 + - NIST-800-53-AC-17(a) + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_permissions_sshd_private_key + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +for keyfile in /etc/ssh/*_key; do + test -f "$keyfile" || continue + if test root:root = "$(stat -c "%U:%G" "$keyfile")"; then + chmod u-xs,g-xwrs,o-xwrt "$keyfile" + elif test root:ssh_keys = "$(stat -c "%U:%G" "$keyfile")"; then + chmod u-xs,g-xws,o-xwrt "$keyfile" + else + echo "Key-like file '$keyfile' is owned by an unexpected user:group combination" + fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Verify Permissions on SSH Server Public *.pub Key Files + To properly set the permissions of /etc/ssh/*.pub, run the command: $ sudo chmod 0644 /etc/ssh/*.pub + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + 3.1.13 + 3.13.10 + CCI-000366 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + AC-17(a) + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + SRG-OS-000480-GPOS-00227 + RHEL-08-010480 + 5.2.3 + SV-230286r627750_rule + If a public host key file is modified by an unauthorized user, the SSH service +may be compromised. + CCE-82428-4 + include ssh_public_key_perms + +class ssh_public_key_perms { + exec { 'sshd_pub_key': + command => "chmod 0644 /etc/ssh/*.pub", + path => '/bin:/usr/bin' + } +} + + - name: Find /etc/ssh/ file(s) + command: find -H /etc/ssh/ -maxdepth 1 -perm /u+xs,g+xws,o+xwt -type f -regex "^.*\.pub$" + register: files_found + changed_when: false + failed_when: false + check_mode: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82428-4 + - DISA-STIG-RHEL-08-010480 + - NIST-800-171-3.1.13 + - NIST-800-171-3.13.10 + - NIST-800-53-AC-17(a) + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_permissions_sshd_pub_key + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Set permissions for /etc/ssh/ file(s) + file: + path: '{{ item }}' + mode: u-xs,g-xws,o-xwt + state: file + with_items: + - '{{ files_found.stdout_lines }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82428-4 + - DISA-STIG-RHEL-08-010480 + - NIST-800-171-3.1.13 + - NIST-800-171-3.13.10 + - NIST-800-53-AC-17(a) + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_permissions_sshd_pub_key + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +find -H /etc/ssh/ -maxdepth 1 -perm /u+xs,g+xws,o+xwt -type f -regex '^.*\.pub$' -exec chmod u-xs,g-xws,o-xwt {} \; + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Remove SSH Server firewalld Firewall exception (Unusual) + By default, inbound connections to SSH's port are allowed. If +the SSH server is not being used, this exception should be removed from the +firewall configuration. + + +To configure firewalld to prevent access, run the following command(s): + + +firewall-cmd --permanent --remove-service=ssh + 3.1.12 + If inbound SSH connections are not expected, disallowing access to the SSH port will +avoid possible exploitation of the port by an attacker. + + + + + + Remove SSH Server iptables Firewall exception (Unusual) + By default, inbound connections to SSH's port are allowed. If the SSH +server is not being used, this exception should be removed from the +firewall configuration. + +Edit the files /etc/sysconfig/iptables and +/etc/sysconfig/ip6tables (if IPv6 is in use). In each file, locate +and delete the line: +-A INPUT -m state --state NEW -m tcp -p tcp --dport 22 -j ACCEPT +This is unusual, as SSH is a common method for encrypted and authenticated +remote access. + If inbound SSH connections are not expected, disallowing access to the SSH +port will avoid possible exploitation of the port by an attacker. + + + Configure OpenSSH Client if Necessary + The following configuration changes apply to the SSH client. They can +improve security parameters relwevant to the client user, e.g. increasing +entropy while generating initialization vectors. Note that these changes +influence only the default SSH client configuration. Changes in this group +can be overridden by the client user by modifying files within the +~/.ssh directory or by supplying parameters on the command line. + + Configure session renegotiation for SSH client + The RekeyLimit parameter specifies how often +the session key is renegotiated, both in terms of +amount of data that may be transmitted and the time +elapsed. To decrease the default limits, put line +RekeyLimit to file /etc/ssh/ssh_config.d/02-rekey-limit.conf. +Make sure that there is no other RekeyLimit configuration preceding +the include directive in the main config file +/etc/ssh/ssh_config. Check also other files in +/etc/ssh/ssh_config.d directory. Files are processed according to +lexicographical order of file names. Make sure that there is no file +processed before 02-rekey-limit.conf containing definition of +RekeyLimit. + CCI-000068 + FCS_SSH_EXT.1.8 + SRG-OS-000423-GPOS-00187 + SRG-OS-000033-GPOS-00014 + SRG-OS-000424-GPOS-00188 + By decreasing the limit based on the amount of data and enabling +time-based limit, effects of potential attacks against +encryption keys are limited. + CCE-82880-6 + - name: XCCDF Value var_ssh_client_rekey_limit_size # promote to variable + set_fact: + var_ssh_client_rekey_limit_size: !!str + tags: + - always +- name: XCCDF Value var_ssh_client_rekey_limit_time # promote to variable + set_fact: + var_ssh_client_rekey_limit_time: !!str + tags: + - always + +- name: Ensure RekeyLimit is not configured in /etc/ssh/ssh_config + lineinfile: + path: /etc/ssh/ssh_config + create: false + regexp: ^\s*RekeyLimit.*$ + state: absent + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82880-6 + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - ssh_client_rekey_limit + +- name: Collect all include config files for ssh client which configure RekeyLimit + find: + paths: /etc/ssh/ssh_config.d/ + contains: ^[\s]*RekeyLimit.*$ + patterns: '*.config' + register: ssh_config_include_files + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82880-6 + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - ssh_client_rekey_limit + +- name: Remove all occurences of RekeyLimit configuration from include config files + of ssh client + lineinfile: + path: '{{ item }}' + regexp: ^[\s]*RekeyLimit.*$ + state: absent + loop: '{{ ssh_config_include_files.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82880-6 + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - ssh_client_rekey_limit + +- name: Ensure that rekey limit is set to {{ var_ssh_client_rekey_limit_size }} {{ + var_ssh_client_rekey_limit_time }} in /etc/ssh/ssh_config.d/02-rekey-limit.conf + lineinfile: + path: /etc/ssh/ssh_config.d/02-rekey-limit.conf + create: true + regexp: ^\s*RekeyLimit.*$ + line: RekeyLimit {{ var_ssh_client_rekey_limit_size }} {{ var_ssh_client_rekey_limit_time + }} + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82880-6 + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - ssh_client_rekey_limit + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_ssh_client_rekey_limit_size='' +var_ssh_client_rekey_limit_time='' + + +main_config="/etc/ssh/ssh_config" +include_directory="/etc/ssh/ssh_config.d" + +if grep -q '^[\s]*RekeyLimit.*$' "$main_config"; then + sed -i '/^[\s]*RekeyLimit.*/d' "$main_config" +fi + +for file in "$include_directory"/*.conf; do + if grep -q '^[\s]*RekeyLimit.*$' "$file"; then + sed -i '/^[\s]*RekeyLimit.*/d' "$file" + fi +done + +if [ -e "/etc/ssh/ssh_config.d/02-rekey-limit.conf" ] ; then + + LC_ALL=C sed -i "/^\s*RekeyLimit\s\+/d" "/etc/ssh/ssh_config.d/02-rekey-limit.conf" +else + touch "/etc/ssh/ssh_config.d/02-rekey-limit.conf" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/ssh/ssh_config.d/02-rekey-limit.conf" + +cp "/etc/ssh/ssh_config.d/02-rekey-limit.conf" "/etc/ssh/ssh_config.d/02-rekey-limit.conf.bak" +# Insert at the end of the file +printf '%s\n' "RekeyLimit $var_ssh_client_rekey_limit_size $var_ssh_client_rekey_limit_time" >> "/etc/ssh/ssh_config.d/02-rekey-limit.conf" +# Clean up after ourselves. +rm "/etc/ssh/ssh_config.d/02-rekey-limit.conf.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + + SSH client uses strong entropy to seed (for CSH like shells) + To set up SSH client to use entropy from a high-quality source, make sure +that the appropriate shell environment variable is configured. The +SSH_USE_STRONG_RNG environment variable determines how many bytes +of entropy to use. Make sure that the file +/etc/profile.d/cc-ssh-strong-rng.csh contains line +setenv SSH_USE_STRONG_RNG 32. + FCS_CKM.1.1 + SRG-OS-000480-GPOS-00227 + Some SSH implementations use the openssl library for entropy, which by default, doesn't use high-entropy sources. +Randomness is needed to generate considerably more secure data-encryption keys. Plaintext padding, initialization vectors +in encryption algorithms, and high-quality entropy eliminates the possibility that the output of +the random number generator used by SSH would be known to potential attackers. + CCE-83349-1 + - name: Ensure that correct variable is exported in /etc/profile.d/cc-ssh-strong-rng.csh + lineinfile: + path: /etc/profile.d/cc-ssh-strong-rng.csh + regexp: ^[\s]*setenv[\s]+SSH_USE_STRONG_RNG.*$ + line: setenv SSH_USE_STRONG_RNG 32 + state: present + create: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83349-1 + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - ssh_client_use_strong_rng_csh + +- name: Ensure that the configuration is not overridden in /etc/profile + lineinfile: + path: /etc/profile + regexp: ^[\s]*setenv[\s]+SSH_USE_STRONG_RNG.*$ + state: absent + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83349-1 + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - ssh_client_use_strong_rng_csh + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# put line into the file +echo "setenv SSH_USE_STRONG_RNG 32" > /etc/profile.d/cc-ssh-strong-rng.csh + +# remove eventual override in /etc/profile +sed -i '/^[[:space:]]*setenv[[:space:]]\+SSH_USE_STRONG_RNG.*$/d' /etc/profile + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + SSH client uses strong entropy to seed (Bash-like shells) + To set up SSH client to use entropy from a high-quality source, make sure +that the appropriate shell environment variable is configured. The +SSH_USE_STRONG_RNG environment variable determines how many bytes +of entropy to use. Make sure that the file +/etc/profile.d/cc-ssh-strong-rng.sh contains line +export SSH_USE_STRONG_RNG=32. + FCS_CKM.1.1 + SRG-OS-000480-GPOS-00227 + Some SSH implementations use the openssl library for entropy, which by default, doesn't use high-entropy sources. +Randomness is needed to generate considerably more secure data-encryption keys. Plaintext padding, initialization vectors +in encryption algorithms, and high-quality entropy eliminates the possibility that the output of +the random number generator used by SSH would be known to potential attackers. + CCE-83346-7 + - name: Ensure that correct variable is exported in /etc/profile.d/cc-ssh-strong-rng.sh + lineinfile: + path: /etc/profile.d/cc-ssh-strong-rng.sh + regexp: ^[\s]*export[\s]+SSH_USE_STRONG_RNG=.*$ + line: export SSH_USE_STRONG_RNG=32 + state: present + create: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83346-7 + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - ssh_client_use_strong_rng_sh + +- name: Ensure that the configuration is not overridden in /etc/profile + lineinfile: + path: /etc/profile + regexp: ^[\s]*export[\s]+SSH_USE_STRONG_RNG=.*$ + state: absent + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83346-7 + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - ssh_client_use_strong_rng_sh + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# put line into the file +echo "export SSH_USE_STRONG_RNG=32" > /etc/profile.d/cc-ssh-strong-rng.sh + +# remove eventual override in /etc/profile +sed -i '/^[[:space:]]*export[[:space:]]\+SSH_USE_STRONG_RNG=.*$/d' /etc/profile + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Configure OpenSSH Server if Necessary + If the system needs to act as an SSH server, then +certain changes should be made to the OpenSSH daemon configuration +file /etc/ssh/sshd_config. The following recommendations can be +applied to this file. See the sshd_config(5) man page for more +detailed information. + + SSH RekeyLimit - size + Specify the size component of the rekey limit. + default + 512M + 512M + 1G + + + SSH RekeyLimit - size + Specify the size component of the rekey limit. + none + 1h + 1h + + + SSH Compression Setting + Specify the compression setting for SSH connections. + no + delayed + no + + + SSH Privilege Separation Setting + Specify whether and how sshd separates privileges when handling incoming network connections. + no + yes + sandbox + sandbox + + + SSH LoginGraceTime setting + Configure parameters for how long the servers stays connected before the user has successfully logged in + 60 + 60 + + + SSH MaxStartups setting + Configure parameters for maximum concurrent unauthenticated connections to the SSH daemon. + 10:30:100 + 10:30:60 + + + Set SSH Client Alive Count Max to zero + The SSH server sends at most ClientAliveCountMax messages +during a SSH session and waits for a response from the SSH client. +The option ClientAliveInterval configures timeout after +each ClientAliveCountMax message. If the SSH server does not +receive a response from the client, then the connection is considered idle +and terminated. + +To ensure the SSH idle timeout occurs precisely when the +ClientAliveInterval is set, set the ClientAliveCountMax to +value of 0 in + + +/etc/ssh/sshd_config: + 1 + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + 7 + 8 + 5.5.6 + APO13.01 + BAI03.01 + BAI03.02 + BAI03.03 + DSS01.03 + DSS03.05 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.03 + DSS06.10 + 3.1.11 + CCI-000879 + CCI-001133 + CCI-002361 + 164.308(a)(4)(i) + 164.308(b)(1) + 164.308(b)(3) + 164.310(b) + 164.312(e)(1) + 164.312(e)(2)(ii) + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 6.2 + A.12.4.1 + A.12.4.3 + A.14.1.1 + A.14.2.1 + A.14.2.5 + A.18.1.4 + A.6.1.2 + A.6.1.5 + A.7.1.1 + A.9.1.2 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.1 + A.9.4.2 + A.9.4.3 + A.9.4.4 + A.9.4.5 + CIP-004-6 R2.2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.2 + CIP-007-3 R5.3.1 + CIP-007-3 R5.3.2 + CIP-007-3 R5.3.3 + AC-2(5) + AC-12 + AC-17(a) + SC-10 + CM-6(a) + DE.CM-1 + DE.CM-3 + PR.AC-1 + PR.AC-4 + PR.AC-6 + PR.AC-7 + PR.IP-2 + Req-8.1.8 + SRG-OS-000126-GPOS-00066 + SRG-OS-000163-GPOS-00072 + SRG-OS-000279-GPOS-00109 + SRG-OS-000480-VMM-002000 + RHEL-08-010200 + 5.2.13 + SV-230244r743934_rule + This ensures a user login will be terminated as soon as the ClientAliveInterval +is reached. + + CCE-83405-1 + - name: Set SSH Client Alive Count Max to zero + block: + + - name: Check for duplicate values + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*ClientAliveCountMax\s+ + state: absent + check_mode: true + changed_when: false + register: dupes + + - name: Deduplicate values from /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*ClientAliveCountMax\s+ + state: absent + when: dupes.found is defined and dupes.found > 1 + + - name: Insert correct line to /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: true + regexp: (?i)^\s*ClientAliveCountMax\s+ + line: ClientAliveCountMax 0 + state: present + insertbefore: ^[#\s]*Match + validate: /usr/sbin/sshd -t -f %s + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83405-1 + - CJIS-5.5.6 + - DISA-STIG-RHEL-08-010200 + - NIST-800-171-3.1.11 + - NIST-800-53-AC-12 + - NIST-800-53-AC-17(a) + - NIST-800-53-AC-2(5) + - NIST-800-53-CM-6(a) + - NIST-800-53-SC-10 + - PCI-DSS-Req-8.1.8 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sshd_set_keepalive_0 + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if [ -e "/etc/ssh/sshd_config" ] ; then + + LC_ALL=C sed -i "/^\s*ClientAliveCountMax\s\+/Id" "/etc/ssh/sshd_config" +else + touch "/etc/ssh/sshd_config" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/ssh/sshd_config" + +cp "/etc/ssh/sshd_config" "/etc/ssh/sshd_config.bak" +# Insert before the line matching the regex '^Match'. +line_number="$(LC_ALL=C grep -n "^Match" "/etc/ssh/sshd_config.bak" | LC_ALL=C sed 's/:.*//g')" +if [ -z "$line_number" ]; then + # There was no match of '^Match', insert at + # the end of the file. + printf '%s\n' "ClientAliveCountMax 0" >> "/etc/ssh/sshd_config" +else + head -n "$(( line_number - 1 ))" "/etc/ssh/sshd_config.bak" > "/etc/ssh/sshd_config" + printf '%s\n' "ClientAliveCountMax 0" >> "/etc/ssh/sshd_config" + tail -n "+$(( line_number ))" "/etc/ssh/sshd_config.bak" >> "/etc/ssh/sshd_config" +fi +# Clean up after ourselves. +rm "/etc/ssh/sshd_config.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Set SSH Client Alive Count Max + The SSH server sends at most ClientAliveCountMax messages +during a SSH session and waits for a response from the SSH client. +The option ClientAliveInterval configures timeout after +each ClientAliveCountMax message. If the SSH server does not +receive a response from the client, then the connection is considered idle +and terminated. +For SSH earlier than v8.2, a ClientAliveCountMax value of 0 +causes an idle timeout precisely when the ClientAliveInterval is set. +Starting with v8.2, a value of 0 disables the timeout functionality +completely. If the option is set to a number greater than 0, then +the idle session will be disconnected after +ClientAliveInterval * ClientAliveCountMax seconds. + BP28(R29) + 1 + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + 7 + 8 + 5.5.6 + APO13.01 + BAI03.01 + BAI03.02 + BAI03.03 + DSS01.03 + DSS03.05 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.03 + DSS06.10 + 3.1.11 + CCI-000879 + CCI-001133 + CCI-002361 + 164.308(a)(4)(i) + 164.308(b)(1) + 164.308(b)(3) + 164.310(b) + 164.312(e)(1) + 164.312(e)(2)(ii) + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 6.2 + A.12.4.1 + A.12.4.3 + A.14.1.1 + A.14.2.1 + A.14.2.5 + A.18.1.4 + A.6.1.2 + A.6.1.5 + A.7.1.1 + A.9.1.2 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.1 + A.9.4.2 + A.9.4.3 + A.9.4.4 + A.9.4.5 + CIP-004-6 R2.2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.2 + CIP-007-3 R5.3.1 + CIP-007-3 R5.3.2 + CIP-007-3 R5.3.3 + AC-2(5) + AC-12 + AC-17(a) + SC-10 + CM-6(a) + DE.CM-1 + DE.CM-3 + PR.AC-1 + PR.AC-4 + PR.AC-6 + PR.AC-7 + PR.IP-2 + Req-8.1.8 + SRG-OS-000163-GPOS-00072 + SRG-OS-000279-GPOS-00109 + SRG-OS-000480-VMM-002000 + 5.2.20 + This ensures a user login will be terminated as soon as the ClientAliveInterval +is reached. + + CCE-80907-9 + - name: XCCDF Value var_sshd_set_keepalive # promote to variable + set_fact: + var_sshd_set_keepalive: !!str + tags: + - always + +- name: Set SSH Client Alive Count Max + block: + + - name: Check for duplicate values + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*ClientAliveCountMax\s+ + state: absent + check_mode: true + changed_when: false + register: dupes + + - name: Deduplicate values from /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*ClientAliveCountMax\s+ + state: absent + when: dupes.found is defined and dupes.found > 1 + + - name: Insert correct line to /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: true + regexp: (?i)^\s*ClientAliveCountMax\s+ + line: ClientAliveCountMax {{ var_sshd_set_keepalive }} + state: present + insertbefore: ^[#\s]*Match + validate: /usr/sbin/sshd -t -f %s + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80907-9 + - CJIS-5.5.6 + - NIST-800-171-3.1.11 + - NIST-800-53-AC-12 + - NIST-800-53-AC-17(a) + - NIST-800-53-AC-2(5) + - NIST-800-53-CM-6(a) + - NIST-800-53-SC-10 + - PCI-DSS-Req-8.1.8 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sshd_set_keepalive + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_sshd_set_keepalive='' + + +if [ -e "/etc/ssh/sshd_config" ] ; then + + LC_ALL=C sed -i "/^\s*ClientAliveCountMax\s\+/Id" "/etc/ssh/sshd_config" +else + touch "/etc/ssh/sshd_config" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/ssh/sshd_config" + +cp "/etc/ssh/sshd_config" "/etc/ssh/sshd_config.bak" +# Insert before the line matching the regex '^Match'. +line_number="$(LC_ALL=C grep -n "^Match" "/etc/ssh/sshd_config.bak" | LC_ALL=C sed 's/:.*//g')" +if [ -z "$line_number" ]; then + # There was no match of '^Match', insert at + # the end of the file. + printf '%s\n' "ClientAliveCountMax $var_sshd_set_keepalive" >> "/etc/ssh/sshd_config" +else + head -n "$(( line_number - 1 ))" "/etc/ssh/sshd_config.bak" > "/etc/ssh/sshd_config" + printf '%s\n' "ClientAliveCountMax $var_sshd_set_keepalive" >> "/etc/ssh/sshd_config" + tail -n "+$(( line_number ))" "/etc/ssh/sshd_config.bak" >> "/etc/ssh/sshd_config" +fi +# Clean up after ourselves. +rm "/etc/ssh/sshd_config.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + + Set SSH Idle Timeout Interval + SSH allows administrators to set an idle timeout interval. After this interval +has passed, the idle user will be automatically logged out. + +To set an idle timeout interval, edit the following line in /etc/ssh/sshd_config as +follows: +ClientAliveInterval + +The timeout interval is given in seconds. For example, have a timeout +of 10 minutes, set interval to 600. + +If a shorter timeout has already been set for the login shell, that value will +preempt any SSH setting made in /etc/ssh/sshd_config. Keep in mind that +some processes may stop SSH from correctly detecting that the user is idle. + SSH disconnecting idle clients will not have desired effect without also +configuring ClientAliveCountMax in the SSH service configuration. + Following conditions may prevent the SSH session to time out: +Remote processes on the remote machine generates output. As the output has to be transferred over the network to the client, the timeout is reset every time such transfer happens.Any scp or sftp activity by the same user to the host resets the timeout. + BP28(R29) + 1 + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + 7 + 8 + 5.5.6 + APO13.01 + BAI03.01 + BAI03.02 + BAI03.03 + DSS01.03 + DSS03.05 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.03 + DSS06.10 + 3.1.11 + CCI-000879 + CCI-001133 + CCI-002361 + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 6.2 + A.12.4.1 + A.12.4.3 + A.14.1.1 + A.14.2.1 + A.14.2.5 + A.18.1.4 + A.6.1.2 + A.6.1.5 + A.7.1.1 + A.9.1.2 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.1 + A.9.4.2 + A.9.4.3 + A.9.4.4 + A.9.4.5 + CIP-004-6 R2.2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.2 + CIP-007-3 R5.3.1 + CIP-007-3 R5.3.2 + CIP-007-3 R5.3.3 + CM-6(a) + AC-17(a) + AC-2(5) + AC-12 + AC-17(a) + SC-10 + CM-6(a) + DE.CM-1 + DE.CM-3 + PR.AC-1 + PR.AC-4 + PR.AC-6 + PR.AC-7 + PR.IP-2 + Req-8.1.8 + SRG-OS-000126-GPOS-00066 + SRG-OS-000163-GPOS-00072 + SRG-OS-000279-GPOS-00109 + SRG-OS-000395-GPOS-00175 + SRG-OS-000480-VMM-002000 + RHEL-08-010201 + 5.2.20 + SV-244525r743824_rule + Terminating an idle ssh session within a short time period reduces the window of +opportunity for unauthorized personnel to take control of a management session +enabled on the console or console port that has been let unattended. + + CCE-80906-1 + - name: XCCDF Value sshd_idle_timeout_value # promote to variable + set_fact: + sshd_idle_timeout_value: !!str + tags: + - always + +- name: Set SSH Idle Timeout Interval + block: + + - name: Check for duplicate values + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*ClientAliveInterval\s+ + state: absent + check_mode: true + changed_when: false + register: dupes + + - name: Deduplicate values from /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*ClientAliveInterval\s+ + state: absent + when: dupes.found is defined and dupes.found > 1 + + - name: Insert correct line to /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: true + regexp: (?i)^\s*ClientAliveInterval\s+ + line: ClientAliveInterval {{ sshd_idle_timeout_value }} + state: present + insertbefore: ^[#\s]*Match + validate: /usr/sbin/sshd -t -f %s + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80906-1 + - CJIS-5.5.6 + - DISA-STIG-RHEL-08-010201 + - NIST-800-171-3.1.11 + - NIST-800-53-AC-12 + - NIST-800-53-AC-17(a) + - NIST-800-53-AC-17(a) + - NIST-800-53-AC-2(5) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-6(a) + - NIST-800-53-SC-10 + - PCI-DSS-Req-8.1.8 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sshd_set_idle_timeout + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +sshd_idle_timeout_value='' + + +if [ -e "/etc/ssh/sshd_config" ] ; then + + LC_ALL=C sed -i "/^\s*ClientAliveInterval\s\+/Id" "/etc/ssh/sshd_config" +else + touch "/etc/ssh/sshd_config" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/ssh/sshd_config" + +cp "/etc/ssh/sshd_config" "/etc/ssh/sshd_config.bak" +# Insert before the line matching the regex '^Match'. +line_number="$(LC_ALL=C grep -n "^Match" "/etc/ssh/sshd_config.bak" | LC_ALL=C sed 's/:.*//g')" +if [ -z "$line_number" ]; then + # There was no match of '^Match', insert at + # the end of the file. + printf '%s\n' "ClientAliveInterval $sshd_idle_timeout_value" >> "/etc/ssh/sshd_config" +else + head -n "$(( line_number - 1 ))" "/etc/ssh/sshd_config.bak" > "/etc/ssh/sshd_config" + printf '%s\n' "ClientAliveInterval $sshd_idle_timeout_value" >> "/etc/ssh/sshd_config" + tail -n "+$(( line_number ))" "/etc/ssh/sshd_config.bak" >> "/etc/ssh/sshd_config" +fi +# Clean up after ourselves. +rm "/etc/ssh/sshd_config.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + + Disable Host-Based Authentication + SSH's cryptographic host-based authentication is +more secure than .rhosts authentication. However, it is +not recommended that hosts unilaterally trust one another, even +within an organization. + +The default SSH configuration disables host-based authentication. The appropriate +configuration is used if no value is set for HostbasedAuthentication. + +To explicitly disable host-based authentication, add or correct the +following line in + + +/etc/ssh/sshd_config: + +HostbasedAuthentication no + 11 + 12 + 14 + 15 + 16 + 18 + 3 + 5 + 9 + 5.5.6 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + DSS06.03 + DSS06.06 + 3.1.12 + CCI-000366 + 164.308(a)(4)(i) + 164.308(b)(1) + 164.308(b)(3) + 164.310(b) + 164.312(e)(1) + 164.312(e)(2)(ii) + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + 0421 + 0422 + 0431 + 0974 + 1173 + 1401 + 1504 + 1505 + 1546 + 1557 + 1558 + 1559 + 1560 + 1561 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.6.1.2 + A.7.1.1 + A.9.1.2 + A.9.2.1 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.2.3 + CIP-004-6 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.2 + CIP-007-3 R5.2 + CIP-007-3 R5.3.1 + CIP-007-3 R5.3.2 + CIP-007-3 R5.3.3 + AC-3 + AC-17(a) + CM-7(a) + CM-7(b) + CM-6(a) + PR.AC-4 + PR.AC-6 + PR.IP-1 + PR.PT-3 + FIA_UAU.1 + SRG-OS-000480-GPOS-00229 + SRG-OS-000480-VMM-002000 + 5.2.8 + SSH trust relationships mean a compromise on one host +can allow an attacker to move trivially to other hosts. + CCE-80786-7 + - name: Disable Host-Based Authentication + block: + + - name: Check for duplicate values + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*HostbasedAuthentication\s+ + state: absent + check_mode: true + changed_when: false + register: dupes + + - name: Deduplicate values from /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*HostbasedAuthentication\s+ + state: absent + when: dupes.found is defined and dupes.found > 1 + + - name: Insert correct line to /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: true + regexp: (?i)^\s*HostbasedAuthentication\s+ + line: HostbasedAuthentication no + state: present + insertbefore: ^[#\s]*Match + validate: /usr/sbin/sshd -t -f %s + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80786-7 + - CJIS-5.5.6 + - NIST-800-171-3.1.12 + - NIST-800-53-AC-17(a) + - NIST-800-53-AC-3 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_host_auth + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,%23%09%24OpenBSD%3A%20sshd_config%2Cv%201.103%202018%2F04%2F09%2020%3A41%3A22%20tj%20Exp%20%24%0A%0A%23%20This%20is%20the%20sshd%20server%20system-wide%20configuration%20file.%20%20See%0A%23%20sshd_config%285%29%20for%20more%20information.%0A%0A%23%20This%20sshd%20was%20compiled%20with%20PATH%3D%2Fusr%2Flocal%2Fbin%3A%2Fusr%2Fbin%3A%2Fusr%2Flocal%2Fsbin%3A%2Fusr%2Fsbin%0A%0A%23%20The%20strategy%20used%20for%20options%20in%20the%20default%20sshd_config%20shipped%20with%0A%23%20OpenSSH%20is%20to%20specify%20options%20with%20their%20default%20value%20where%0A%23%20possible%2C%20but%20leave%20them%20commented.%20%20Uncommented%20options%20override%20the%0A%23%20default%20value.%0A%0A%23%20If%20you%20want%20to%20change%20the%20port%20on%20a%20SELinux%20system%2C%20you%20have%20to%20tell%0A%23%20SELinux%20about%20this%20change.%0A%23%20semanage%20port%20-a%20-t%20ssh_port_t%20-p%20tcp%20%23PORTNUMBER%0A%23%0A%23Port%2022%0A%23AddressFamily%20any%0A%23ListenAddress%200.0.0.0%0A%23ListenAddress%20%3A%3A%0A%0AHostKey%20%2Fetc%2Fssh%2Fssh_host_rsa_key%0AHostKey%20%2Fetc%2Fssh%2Fssh_host_ecdsa_key%0AHostKey%20%2Fetc%2Fssh%2Fssh_host_ed25519_key%0A%0A%23%20Ciphers%20and%20keying%0ARekeyLimit%20512M%201h%0A%0A%23%20System-wide%20Crypto%20policy%3A%0A%23%20This%20system%20is%20following%20system-wide%20crypto%20policy.%20The%20changes%20to%0A%23%20Ciphers%2C%20MACs%2C%20KexAlgoritms%20and%20GSSAPIKexAlgorithsm%20will%20not%20have%20any%0A%23%20effect%20here.%20They%20will%20be%20overridden%20by%20command-line%20options%20passed%20on%0A%23%20the%20server%20start%20up.%0A%23%20To%20opt%20out%2C%20uncomment%20a%20line%20with%20redefinition%20of%20%20CRYPTO_POLICY%3D%0A%23%20variable%20in%20%20%2Fetc%2Fsysconfig%2Fsshd%20%20to%20overwrite%20the%20policy.%0A%23%20For%20more%20information%2C%20see%20manual%20page%20for%20update-crypto-policies%288%29.%0A%0A%23%20Logging%0A%23SyslogFacility%20AUTH%0ASyslogFacility%20AUTHPRIV%0A%23LogLevel%20INFO%0A%0A%23%20Authentication%3A%0A%0A%23LoginGraceTime%202m%0APermitRootLogin%20no%0AStrictModes%20yes%0A%23MaxAuthTries%206%0A%23MaxSessions%2010%0A%0APubkeyAuthentication%20yes%0A%0A%23%20The%20default%20is%20to%20check%20both%20.ssh%2Fauthorized_keys%20and%20.ssh%2Fauthorized_keys2%0A%23%20but%20this%20is%20overridden%20so%20installations%20will%20only%20check%20.ssh%2Fauthorized_keys%0AAuthorizedKeysFile%09.ssh%2Fauthorized_keys%0A%0A%23AuthorizedPrincipalsFile%20none%0A%0A%23AuthorizedKeysCommand%20none%0A%23AuthorizedKeysCommandUser%20nobody%0A%0A%23%20For%20this%20to%20work%20you%20will%20also%20need%20host%20keys%20in%20%2Fetc%2Fssh%2Fssh_known_hosts%0AHostbasedAuthentication%20no%0A%23%20Change%20to%20yes%20if%20you%20don%27t%20trust%20~%2F.ssh%2Fknown_hosts%20for%0A%23%20HostbasedAuthentication%0AIgnoreUserKnownHosts%20yes%0A%23%20Don%27t%20read%20the%20user%27s%20~%2F.rhosts%20and%20~%2F.shosts%20files%0AIgnoreRhosts%20yes%0A%0A%23%20To%20disable%20tunneled%20clear%20text%20passwords%2C%20change%20to%20no%20here%21%0A%23PasswordAuthentication%20yes%0APermitEmptyPasswords%20no%0APasswordAuthentication%20no%0A%0A%23%20Change%20to%20no%20to%20disable%20s%2Fkey%20passwords%0A%23ChallengeResponseAuthentication%20yes%0AChallengeResponseAuthentication%20no%0A%0A%23%20Kerberos%20options%0AKerberosAuthentication%20no%0A%23KerberosOrLocalPasswd%20yes%0A%23KerberosTicketCleanup%20yes%0A%23KerberosGetAFSToken%20no%0A%23KerberosUseKuserok%20yes%0A%0A%23%20GSSAPI%20options%0AGSSAPIAuthentication%20no%0AGSSAPICleanupCredentials%20no%0A%23GSSAPIStrictAcceptorCheck%20yes%0A%23GSSAPIKeyExchange%20no%0A%23GSSAPIEnablek5users%20no%0A%0A%23%20Set%20this%20to%20%27yes%27%20to%20enable%20PAM%20authentication%2C%20account%20processing%2C%0A%23%20and%20session%20processing.%20If%20this%20is%20enabled%2C%20PAM%20authentication%20will%0A%23%20be%20allowed%20through%20the%20ChallengeResponseAuthentication%20and%0A%23%20PasswordAuthentication.%20%20Depending%20on%20your%20PAM%20configuration%2C%0A%23%20PAM%20authentication%20via%20ChallengeResponseAuthentication%20may%20bypass%0A%23%20the%20setting%20of%20%22PermitRootLogin%20without-password%22.%0A%23%20If%20you%20just%20want%20the%20PAM%20account%20and%20session%20checks%20to%20run%20without%0A%23%20PAM%20authentication%2C%20then%20enable%20this%20but%20set%20PasswordAuthentication%0A%23%20and%20ChallengeResponseAuthentication%20to%20%27no%27.%0A%23%20WARNING%3A%20%27UsePAM%20no%27%20is%20not%20supported%20in%20Fedora%20and%20may%20cause%20several%0A%23%20problems.%0AUsePAM%20yes%0A%0A%23AllowAgentForwarding%20yes%0A%23AllowTcpForwarding%20yes%0A%23GatewayPorts%20no%0AX11Forwarding%20yes%0A%23X11DisplayOffset%2010%0A%23X11UseLocalhost%20yes%0A%23PermitTTY%20yes%0A%0A%23%20It%20is%20recommended%20to%20use%20pam_motd%20in%20%2Fetc%2Fpam.d%2Fsshd%20instead%20of%20PrintMotd%2C%0A%23%20as%20it%20is%20more%20configurable%20and%20versatile%20than%20the%20built-in%20version.%0APrintMotd%20no%0A%0APrintLastLog%20yes%0A%23TCPKeepAlive%20yes%0APermitUserEnvironment%20no%0ACompression%20no%0AClientAliveInterval%20600%0AClientAliveCountMax%200%0A%23UseDNS%20no%0A%23PidFile%20%2Fvar%2Frun%2Fsshd.pid%0A%23MaxStartups%2010%3A30%3A100%0A%23PermitTunnel%20no%0A%23ChrootDirectory%20none%0A%23VersionAddendum%20none%0A%0A%23%20no%20default%20banner%20path%0ABanner%20%2Fetc%2Fissue%0A%0A%23%20Accept%20locale-related%20environment%20variables%0AAcceptEnv%20LANG%20LC_CTYPE%20LC_NUMERIC%20LC_TIME%20LC_COLLATE%20LC_MONETARY%20LC_MESSAGES%0AAcceptEnv%20LC_PAPER%20LC_NAME%20LC_ADDRESS%20LC_TELEPHONE%20LC_MEASUREMENT%0AAcceptEnv%20LC_IDENTIFICATION%20LC_ALL%20LANGUAGE%0AAcceptEnv%20XMODIFIERS%0A%0A%23%20override%20default%20of%20no%20subsystems%0ASubsystem%09sftp%09%2Fusr%2Flibexec%2Fopenssh%2Fsftp-server%0A%0A%23%20Example%20of%20overriding%20settings%20on%20a%20per-user%20basis%0A%23Match%20User%20anoncvs%0A%23%09X11Forwarding%20no%0A%23%09AllowTcpForwarding%20no%0A%23%09PermitTTY%20no%0A%23%09ForceCommand%20cvs%20server%0A%0AUsePrivilegeSeparation%20sandbox + mode: 0600 + path: /etc/ssh/sshd_config + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if [ -e "/etc/ssh/sshd_config" ] ; then + + LC_ALL=C sed -i "/^\s*HostbasedAuthentication\s\+/Id" "/etc/ssh/sshd_config" +else + touch "/etc/ssh/sshd_config" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/ssh/sshd_config" + +cp "/etc/ssh/sshd_config" "/etc/ssh/sshd_config.bak" +# Insert before the line matching the regex '^Match'. +line_number="$(LC_ALL=C grep -n "^Match" "/etc/ssh/sshd_config.bak" | LC_ALL=C sed 's/:.*//g')" +if [ -z "$line_number" ]; then + # There was no match of '^Match', insert at + # the end of the file. + printf '%s\n' "HostbasedAuthentication no" >> "/etc/ssh/sshd_config" +else + head -n "$(( line_number - 1 ))" "/etc/ssh/sshd_config.bak" > "/etc/ssh/sshd_config" + printf '%s\n' "HostbasedAuthentication no" >> "/etc/ssh/sshd_config" + tail -n "+$(( line_number ))" "/etc/ssh/sshd_config.bak" >> "/etc/ssh/sshd_config" +fi +# Clean up after ourselves. +rm "/etc/ssh/sshd_config.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable SSH Server firewalld Firewall Exception + By default, inbound connections to SSH's port are allowed. If +the SSH server is being used but denied by the firewall, this exception should +be added to the firewall configuration. + + + + +To configure firewalld to allow ssh access, run the following command(s): +firewall-cmd --permanent --add-service=ssh + +Then run the following command to load the newly created rule(s): +firewall-cmd --reload + 3.1.12 + 1416 + AC-17(a) + CM-6(b) + CM-7(a) + CM-7(b) + SRG-OS-000096-GPOS-00050 + If inbound SSH connections are expected, adding a firewall rule exception +will allow remote access through the SSH port. + CCE-80820-4 + - name: Ensure firewalld is installed + package: + name: '{{ item }}' + state: present + with_items: + - firewalld + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80820-4 + - NIST-800-171-3.1.12 + - NIST-800-53-AC-17(a) + - NIST-800-53-CM-6(b) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - configure_strategy + - firewalld_sshd_port_enabled + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed +- name: XCCDF Value sshd_listening_port # promote to variable + set_fact: + sshd_listening_port: !!str + tags: + - always + +- name: Enable SSHD in firewalld (custom port) + firewalld: + port: '{{ sshd_listening_port }}/tcp' + permanent: true + state: enabled + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - sshd_listening_port != 22 + tags: + - CCE-80820-4 + - NIST-800-171-3.1.12 + - NIST-800-53-AC-17(a) + - NIST-800-53-CM-6(b) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - configure_strategy + - firewalld_sshd_port_enabled + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Enable SSHD in firewalld (default port) + firewalld: + service: ssh + permanent: true + state: enabled + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - sshd_listening_port == 22 + tags: + - CCE-80820-4 + - NIST-800-171-3.1.12 + - NIST-800-53-AC-17(a) + - NIST-800-53-CM-6(b) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - configure_strategy + - firewalld_sshd_port_enabled + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if ! rpm -q --quiet "firewalld" ; then + yum install -y "firewalld" +fi + +firewalld_sshd_zone='' + + + + + + +# This assumes that firewalld_sshd_zone is one of the pre-defined zones +if [ ! -f "/etc/firewalld/zones/${firewalld_sshd_zone}.xml" ]; then + cp "/usr/lib/firewalld/zones/${firewalld_sshd_zone}.xml" "/etc/firewalld/zones/${firewalld_sshd_zone}.xml" +fi +if ! grep -q 'service name="ssh"' "/etc/firewalld/zones/${firewalld_sshd_zone}.xml"; then + sed -i '/<\/description>/a \ + <service name="ssh"/>' "/etc/firewalld/zones/${firewalld_sshd_zone}.xml" +fi + +# Check if any eth interface is bounded to the zone with SSH service enabled +nic_bound=false +readarray -t eth_interface_list < <(ip link show up | cut -d ' ' -f2 | cut -d ':' -s -f1 | grep -E '^(en|eth)') +for interface in "${eth_interface_list[@]}"; do + if grep -qi "ZONE=$firewalld_sshd_zone" "/etc/sysconfig/network-scripts/ifcfg-${interface}"; then + nic_bound=true + break; + fi +done + +if [ $nic_bound = false ];then + # Add first NIC to SSH enabled zone + interface="${eth_interface_list[0]}" + + if ! firewall-cmd --state -q; then + + # Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. + # Otherwise, regular sed command will do. + sed_command=('sed' '-i') + if test -L "/etc/sysconfig/network-scripts/ifcfg-${interface}"; then + sed_command+=('--follow-symlinks') + fi + + # Strip any search characters in the key arg so that the key can be replaced without + # adding any search characters to the config file. + stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^ZONE=") + + # shellcheck disable=SC2059 + printf -v formatted_output "%s=%s" "$stripped_key" "$firewalld_sshd_zone" + + # If the key exists, change it. Otherwise, add it to the config_file. + # We search for the key string followed by a word boundary (matched by \>), + # so if we search for 'setting', 'setting2' won't match. + if LC_ALL=C grep -q -m 1 -i -e "^ZONE=\\>" "/etc/sysconfig/network-scripts/ifcfg-${interface}"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^ZONE=\\>.*/$escaped_formatted_output/gi" "/etc/sysconfig/network-scripts/ifcfg-${interface}" + else + # \n is precaution for case where file ends without trailing newline + cce="CCE-80820-4" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysconfig/network-scripts/ifcfg-${interface}" >> "/etc/sysconfig/network-scripts/ifcfg-${interface}" + printf '%s\n' "$formatted_output" >> "/etc/sysconfig/network-scripts/ifcfg-${interface}" + fi + + else + # If firewalld service is running, we need to do this step with firewall-cmd + # Otherwise firewalld will communicate with NetworkManage and will revert assigned zone + # of NetworkManager managed interfaces upon reload + firewall-cmd --permanent --zone="$firewalld_sshd_zone" --add-interface="${eth_interface_list[0]}" + firewall-cmd --reload + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Allow Only SSH Protocol 2 + Only SSH protocol version 2 connections should be +permitted. The default setting in +/etc/ssh/sshd_config is correct, and can be +verified by ensuring that the following +line appears: +Protocol 2 + As of openssh-server version 7.4 and above, the only protocol +supported is version 2, and line Protocol 2 in +/etc/ssh/sshd_config is not necessary. + NT007(R1) + 1 + 12 + 15 + 16 + 5 + 8 + 5.5.6 + APO13.01 + DSS01.04 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.03 + DSS06.10 + 3.1.13 + 3.5.4 + CCI-000197 + CCI-000366 + 164.308(a)(4)(i) + 164.308(b)(1) + 164.308(b)(3) + 164.310(b) + 164.312(e)(1) + 164.312(e)(2)(ii) + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.2 + 4.3.3.7.4 + SR 1.1 + SR 1.10 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.6 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + 0487 + 1449 + 1506 + A.11.2.6 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.18.1.4 + A.6.2.1 + A.6.2.2 + A.7.1.1 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.2 + A.9.4.3 + CIP-003-8 R4.2 + CIP-007-3 R5.1 + CIP-007-3 R7.1 + CM-6(a) + AC-17(a) + AC-17(2) + IA-5(1)(c) + SC-13 + MA-4(6) + PR.AC-1 + PR.AC-3 + PR.AC-6 + PR.AC-7 + PR.PT-4 + SRG-OS-000074-GPOS-00042 + SRG-OS-000480-GPOS-00227 + SRG-OS-000033-VMM-000140 + SSH protocol version 1 is an insecure implementation of the SSH protocol and +has many well-known vulnerability exploits. Exploits of the SSH daemon could provide +immediate root access to the system. + CCE-80894-9 + - name: Allow Only SSH Protocol 2 + block: + + - name: Check for duplicate values + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*Protocol\s+ + state: absent + check_mode: true + changed_when: false + register: dupes + + - name: Deduplicate values from /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*Protocol\s+ + state: absent + when: dupes.found is defined and dupes.found > 1 + + - name: Insert correct line to /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: true + regexp: (?i)^\s*Protocol\s+ + line: Protocol 2 + state: present + insertbefore: ^[#\s]*Match + validate: /usr/sbin/sshd -t -f %s + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80894-9 + - CJIS-5.5.6 + - NIST-800-171-3.1.13 + - NIST-800-171-3.5.4 + - NIST-800-53-AC-17(2) + - NIST-800-53-AC-17(a) + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(c) + - NIST-800-53-MA-4(6) + - NIST-800-53-SC-13 + - high_severity + - low_complexity + - low_disruption + - no_reboot_needed + - restrict_strategy + - sshd_allow_only_protocol2 + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/ssh/sshd_config"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^Protocol") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s %s" "$stripped_key" "2" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^Protocol\\>" "/etc/ssh/sshd_config"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^Protocol\\>.*/$escaped_formatted_output/gi" "/etc/ssh/sshd_config" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-80894-9" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/ssh/sshd_config" >> "/etc/ssh/sshd_config" + printf '%s\n' "$formatted_output" >> "/etc/ssh/sshd_config" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable Compression Or Set Compression to delayed + Compression is useful for slow network connections over long +distances but can cause performance issues on local LANs. If use of compression +is required, it should be enabled only after a user has authenticated; otherwise, +it should be disabled. To disable compression or delay compression until after +a user has successfully authenticated, add or correct the following line in the +/etc/ssh/sshd_config file: +Compression + 11 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + 3.1.12 + CCI-000366 + 164.308(a)(4)(i) + 164.308(b)(1) + 164.308(b)(3) + 164.310(b) + 164.312(e)(1) + 164.312(e)(2)(ii) + 4.3.4.3.2 + 4.3.4.3.3 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + AC-17(a) + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + SRG-OS-000480-GPOS-00227 + SRG-OS-000480-VMM-002000 + RHEL-08-010510 + SV-230289r743954_rule + If compression is allowed in an SSH connection prior to authentication, +vulnerabilities in the compression software could result in compromise of the +system from an unauthenticated connection, potentially with root privileges. + CCE-80895-6 + - name: XCCDF Value var_sshd_disable_compression # promote to variable + set_fact: + var_sshd_disable_compression: !!str + tags: + - always + +- name: Disable Compression Or Set Compression to delayed + block: + + - name: Check for duplicate values + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*Compression\s+ + state: absent + check_mode: true + changed_when: false + register: dupes + + - name: Deduplicate values from /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*Compression\s+ + state: absent + when: dupes.found is defined and dupes.found > 1 + + - name: Insert correct line to /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: true + regexp: (?i)^\s*Compression\s+ + line: Compression {{ var_sshd_disable_compression }} + state: present + insertbefore: ^[#\s]*Match + validate: /usr/sbin/sshd -t -f %s + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80895-6 + - DISA-STIG-RHEL-08-010510 + - NIST-800-171-3.1.12 + - NIST-800-53-AC-17(a) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sshd_disable_compression + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_sshd_disable_compression='' + + +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/ssh/sshd_config"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^Compression") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s %s" "$stripped_key" "$var_sshd_disable_compression" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^Compression\\>" "/etc/ssh/sshd_config"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^Compression\\>.*/$escaped_formatted_output/gi" "/etc/ssh/sshd_config" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-80895-6" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/ssh/sshd_config" >> "/etc/ssh/sshd_config" + printf '%s\n' "$formatted_output" >> "/etc/ssh/sshd_config" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + + Disable SSH Access via Empty Passwords + Disallow SSH login with empty passwords. +The default SSH configuration disables logins with empty passwords. The appropriate +configuration is used if no value is set for PermitEmptyPasswords. + +To explicitly disallow SSH login from accounts with empty passwords, +add or correct the following line in + + +/etc/ssh/sshd_config: + + +PermitEmptyPasswords no +Any accounts with empty passwords should be disabled immediately, and PAM configuration +should prevent users from being able to assign themselves empty passwords. + NT007(R17) + 11 + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + 9 + 5.5.6 + APO01.06 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + DSS06.02 + DSS06.03 + DSS06.06 + 3.1.1 + 3.1.5 + CCI-000366 + CCI-000766 + 164.308(a)(4)(i) + 164.308(b)(1) + 164.308(b)(3) + 164.310(b) + 164.312(e)(1) + 164.312(e)(2)(ii) + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 5.2 + SR 7.6 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.1 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + AC-17(a) + CM-7(a) + CM-7(b) + CM-6(a) + PR.AC-4 + PR.AC-6 + PR.DS-5 + PR.IP-1 + PR.PT-3 + FIA_UAU.1 + SRG-OS-000106-GPOS-00053 + SRG-OS-000480-GPOS-00229 + SRG-OS-000480-GPOS-00227 + SRG-OS-000480-VMM-002000 + RHEL-08-020330 + 5.2.9 + SV-230380r743993_rule + Configuring this setting for the SSH daemon provides additional assurance +that remote login via SSH will require a password, even in the event of +misconfiguration elsewhere. + CCE-80896-4 + - name: Disable SSH Access via Empty Passwords + block: + + - name: Check for duplicate values + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*PermitEmptyPasswords\s+ + state: absent + check_mode: true + changed_when: false + register: dupes + + - name: Deduplicate values from /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*PermitEmptyPasswords\s+ + state: absent + when: dupes.found is defined and dupes.found > 1 + + - name: Insert correct line to /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: true + regexp: (?i)^\s*PermitEmptyPasswords\s+ + line: PermitEmptyPasswords no + state: present + insertbefore: ^[#\s]*Match + validate: /usr/sbin/sshd -t -f %s + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80896-4 + - CJIS-5.5.6 + - DISA-STIG-RHEL-08-020330 + - NIST-800-171-3.1.1 + - NIST-800-171-3.1.5 + - NIST-800-53-AC-17(a) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - high_severity + - low_complexity + - low_disruption + - no_reboot_needed + - restrict_strategy + - sshd_disable_empty_passwords + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if [ -e "/etc/ssh/sshd_config" ] ; then + + LC_ALL=C sed -i "/^\s*PermitEmptyPasswords\s\+/Id" "/etc/ssh/sshd_config" +else + touch "/etc/ssh/sshd_config" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/ssh/sshd_config" + +cp "/etc/ssh/sshd_config" "/etc/ssh/sshd_config.bak" +# Insert before the line matching the regex '^Match'. +line_number="$(LC_ALL=C grep -n "^Match" "/etc/ssh/sshd_config.bak" | LC_ALL=C sed 's/:.*//g')" +if [ -z "$line_number" ]; then + # There was no match of '^Match', insert at + # the end of the file. + printf '%s\n' "PermitEmptyPasswords no" >> "/etc/ssh/sshd_config" +else + head -n "$(( line_number - 1 ))" "/etc/ssh/sshd_config.bak" > "/etc/ssh/sshd_config" + printf '%s\n' "PermitEmptyPasswords no" >> "/etc/ssh/sshd_config" + tail -n "+$(( line_number ))" "/etc/ssh/sshd_config.bak" >> "/etc/ssh/sshd_config" +fi +# Clean up after ourselves. +rm "/etc/ssh/sshd_config.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable GSSAPI Authentication + Unless needed, SSH should not permit extraneous or unnecessary +authentication mechanisms like GSSAPI. + +The default SSH configuration disallows authentications based on GSSAPI. The appropriate +configuration is used if no value is set for GSSAPIAuthentication. + +To explicitly disable GSSAPI authentication, add or correct the following line in + + +/etc/ssh/sshd_config: + +GSSAPIAuthentication no + 11 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + 3.1.12 + CCI-000318 + CCI-000368 + CCI-001812 + CCI-001813 + CCI-001814 + CCI-000366 + 164.308(a)(4)(i) + 164.308(b)(1) + 164.308(b)(3) + 164.310(b) + 164.312(e)(1) + 164.312(e)(2)(ii) + 4.3.4.3.2 + 4.3.4.3.3 + SR 7.6 + 0418 + 1055 + 1402 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + CM-7(a) + CM-7(b) + CM-6(a) + AC-17(a) + PR.IP-1 + FTP_ITC_EXT.1 + FCS_SSH_EXT.1.2 + SRG-OS-000364-GPOS-00151 + SRG-OS-000480-GPOS-00227 + SRG-OS-000480-VMM-002000 + RHEL-08-010522 + SV-244528r743833_rule + GSSAPI authentication is used to provide additional authentication mechanisms to +applications. Allowing GSSAPI authentication through SSH exposes the system's +GSSAPI to remote hosts, increasing the attack surface of the system. + CCE-80897-2 + - name: Disable GSSAPI Authentication + block: + + - name: Check for duplicate values + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*GSSAPIAuthentication\s+ + state: absent + check_mode: true + changed_when: false + register: dupes + + - name: Deduplicate values from /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*GSSAPIAuthentication\s+ + state: absent + when: dupes.found is defined and dupes.found > 1 + + - name: Insert correct line to /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: true + regexp: (?i)^\s*GSSAPIAuthentication\s+ + line: GSSAPIAuthentication no + state: present + insertbefore: ^[#\s]*Match + validate: /usr/sbin/sshd -t -f %s + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80897-2 + - DISA-STIG-RHEL-08-010522 + - NIST-800-171-3.1.12 + - NIST-800-53-AC-17(a) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sshd_disable_gssapi_auth + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if [ -e "/etc/ssh/sshd_config" ] ; then + + LC_ALL=C sed -i "/^\s*GSSAPIAuthentication\s\+/Id" "/etc/ssh/sshd_config" +else + touch "/etc/ssh/sshd_config" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/ssh/sshd_config" + +cp "/etc/ssh/sshd_config" "/etc/ssh/sshd_config.bak" +# Insert before the line matching the regex '^Match'. +line_number="$(LC_ALL=C grep -n "^Match" "/etc/ssh/sshd_config.bak" | LC_ALL=C sed 's/:.*//g')" +if [ -z "$line_number" ]; then + # There was no match of '^Match', insert at + # the end of the file. + printf '%s\n' "GSSAPIAuthentication no" >> "/etc/ssh/sshd_config" +else + head -n "$(( line_number - 1 ))" "/etc/ssh/sshd_config.bak" > "/etc/ssh/sshd_config" + printf '%s\n' "GSSAPIAuthentication no" >> "/etc/ssh/sshd_config" + tail -n "+$(( line_number ))" "/etc/ssh/sshd_config.bak" >> "/etc/ssh/sshd_config" +fi +# Clean up after ourselves. +rm "/etc/ssh/sshd_config.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable Kerberos Authentication + Unless needed, SSH should not permit extraneous or unnecessary +authentication mechanisms like Kerberos. + +The default SSH configuration disallows authentication validation through Kerberos. +The appropriate configuration is used if no value is set for KerberosAuthentication. + +To explicitly disable Kerberos authentication, add or correct the following line in + + +/etc/ssh/sshd_config: + +KerberosAuthentication no + 11 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + 3.1.12 + CCI-000318 + CCI-000368 + CCI-001812 + CCI-001813 + CCI-001814 + CCI-000366 + 164.308(a)(4)(i) + 164.308(b)(1) + 164.308(b)(3) + 164.310(b) + 164.312(e)(1) + 164.312(e)(2)(ii) + 4.3.4.3.2 + 4.3.4.3.3 + SR 7.6 + 0421 + 0422 + 0431 + 0974 + 1173 + 1401 + 1504 + 1505 + 1546 + 1557 + 1558 + 1559 + 1560 + 1561 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + AC-17(a) + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + FTP_ITC_EXT.1 + FCS_SSH_EXT.1.2 + SRG-OS-000364-GPOS-00151 + SRG-OS-000480-GPOS-00227 + SRG-OS-000480-VMM-002000 + RHEL-08-010521 + SV-230291r743957_rule + Kerberos authentication for SSH is often implemented using GSSAPI. If Kerberos +is enabled through SSH, the SSH daemon provides a means of access to the +system's Kerberos implementation. +Configuring these settings for the SSH daemon provides additional assurance that remote logon via SSH will not use unused methods of authentication, even in the event of misconfiguration elsewhere. + CCE-80898-0 + - name: Disable Kerberos Authentication + block: + + - name: Check for duplicate values + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*KerberosAuthentication\s+ + state: absent + check_mode: true + changed_when: false + register: dupes + + - name: Deduplicate values from /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*KerberosAuthentication\s+ + state: absent + when: dupes.found is defined and dupes.found > 1 + + - name: Insert correct line to /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: true + regexp: (?i)^\s*KerberosAuthentication\s+ + line: KerberosAuthentication no + state: present + insertbefore: ^[#\s]*Match + validate: /usr/sbin/sshd -t -f %s + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80898-0 + - DISA-STIG-RHEL-08-010521 + - NIST-800-171-3.1.12 + - NIST-800-53-AC-17(a) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sshd_disable_kerb_auth + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if [ -e "/etc/ssh/sshd_config" ] ; then + + LC_ALL=C sed -i "/^\s*KerberosAuthentication\s\+/Id" "/etc/ssh/sshd_config" +else + touch "/etc/ssh/sshd_config" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/ssh/sshd_config" + +cp "/etc/ssh/sshd_config" "/etc/ssh/sshd_config.bak" +# Insert before the line matching the regex '^Match'. +line_number="$(LC_ALL=C grep -n "^Match" "/etc/ssh/sshd_config.bak" | LC_ALL=C sed 's/:.*//g')" +if [ -z "$line_number" ]; then + # There was no match of '^Match', insert at + # the end of the file. + printf '%s\n' "KerberosAuthentication no" >> "/etc/ssh/sshd_config" +else + head -n "$(( line_number - 1 ))" "/etc/ssh/sshd_config.bak" > "/etc/ssh/sshd_config" + printf '%s\n' "KerberosAuthentication no" >> "/etc/ssh/sshd_config" + tail -n "+$(( line_number ))" "/etc/ssh/sshd_config.bak" >> "/etc/ssh/sshd_config" +fi +# Clean up after ourselves. +rm "/etc/ssh/sshd_config.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable PubkeyAuthentication Authentication + Unless needed, SSH should not permit extraneous or unnecessary +authentication mechanisms. To disable PubkeyAuthentication authentication, add or +correct the following line in + + +/etc/ssh/sshd_config: + +PubkeyAuthentication no + PubkeyAuthentication authentication is used to provide additional authentication mechanisms to +applications. Allowing PubkeyAuthentication authentication through SSH allows users to +generate their own authentication tokens, increasing the attack surface of the system. + CCE-82345-0 + - name: Disable PubkeyAuthentication Authentication + block: + + - name: Check for duplicate values + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*PubkeyAuthentication\s+ + state: absent + check_mode: true + changed_when: false + register: dupes + + - name: Deduplicate values from /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*PubkeyAuthentication\s+ + state: absent + when: dupes.found is defined and dupes.found > 1 + + - name: Insert correct line to /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: true + regexp: (?i)^\s*PubkeyAuthentication\s+ + line: PubkeyAuthentication no + state: present + insertbefore: ^[#\s]*Match + validate: /usr/sbin/sshd -t -f %s + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82345-0 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sshd_disable_pubkey_auth + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if [ -e "/etc/ssh/sshd_config" ] ; then + + LC_ALL=C sed -i "/^\s*PubkeyAuthentication\s\+/Id" "/etc/ssh/sshd_config" +else + touch "/etc/ssh/sshd_config" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/ssh/sshd_config" + +cp "/etc/ssh/sshd_config" "/etc/ssh/sshd_config.bak" +# Insert before the line matching the regex '^Match'. +line_number="$(LC_ALL=C grep -n "^Match" "/etc/ssh/sshd_config.bak" | LC_ALL=C sed 's/:.*//g')" +if [ -z "$line_number" ]; then + # There was no match of '^Match', insert at + # the end of the file. + printf '%s\n' "PubkeyAuthentication no" >> "/etc/ssh/sshd_config" +else + head -n "$(( line_number - 1 ))" "/etc/ssh/sshd_config.bak" > "/etc/ssh/sshd_config" + printf '%s\n' "PubkeyAuthentication no" >> "/etc/ssh/sshd_config" + tail -n "+$(( line_number ))" "/etc/ssh/sshd_config.bak" >> "/etc/ssh/sshd_config" +fi +# Clean up after ourselves. +rm "/etc/ssh/sshd_config.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable SSH Support for .rhosts Files + SSH can emulate the behavior of the obsolete rsh +command in allowing users to enable insecure access to their +accounts via .rhosts files. + +The default SSH configuration disables support for .rhosts. The appropriate +configuration is used if no value is set for IgnoreRhosts. + +To explicitly disable support for .rhosts files, add or correct the following line in + + +/etc/ssh/sshd_config: + +IgnoreRhosts yes + 11 + 12 + 14 + 15 + 16 + 18 + 3 + 5 + 9 + 5.5.6 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + DSS06.03 + DSS06.06 + 3.1.12 + CCI-000366 + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.6.1.2 + A.7.1.1 + A.9.1.2 + A.9.2.1 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + AC-17(a) + CM-7(a) + CM-7(b) + CM-6(a) + PR.AC-4 + PR.AC-6 + PR.IP-1 + PR.PT-3 + FIA_UAU.1 + SRG-OS-000480-GPOS-00227 + SRG-OS-000107-VMM-000530 + 5.2.11 + SSH trust relationships mean a compromise on one host +can allow an attacker to move trivially to other hosts. + CCE-80899-8 + - name: Disable SSH Support for .rhosts Files + block: + + - name: Check for duplicate values + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*IgnoreRhosts\s+ + state: absent + check_mode: true + changed_when: false + register: dupes + + - name: Deduplicate values from /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*IgnoreRhosts\s+ + state: absent + when: dupes.found is defined and dupes.found > 1 + + - name: Insert correct line to /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: true + regexp: (?i)^\s*IgnoreRhosts\s+ + line: IgnoreRhosts yes + state: present + insertbefore: ^[#\s]*Match + validate: /usr/sbin/sshd -t -f %s + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80899-8 + - CJIS-5.5.6 + - NIST-800-171-3.1.12 + - NIST-800-53-AC-17(a) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sshd_disable_rhosts + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if [ -e "/etc/ssh/sshd_config" ] ; then + + LC_ALL=C sed -i "/^\s*IgnoreRhosts\s\+/Id" "/etc/ssh/sshd_config" +else + touch "/etc/ssh/sshd_config" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/ssh/sshd_config" + +cp "/etc/ssh/sshd_config" "/etc/ssh/sshd_config.bak" +# Insert before the line matching the regex '^Match'. +line_number="$(LC_ALL=C grep -n "^Match" "/etc/ssh/sshd_config.bak" | LC_ALL=C sed 's/:.*//g')" +if [ -z "$line_number" ]; then + # There was no match of '^Match', insert at + # the end of the file. + printf '%s\n' "IgnoreRhosts yes" >> "/etc/ssh/sshd_config" +else + head -n "$(( line_number - 1 ))" "/etc/ssh/sshd_config.bak" > "/etc/ssh/sshd_config" + printf '%s\n' "IgnoreRhosts yes" >> "/etc/ssh/sshd_config" + tail -n "+$(( line_number ))" "/etc/ssh/sshd_config.bak" >> "/etc/ssh/sshd_config" +fi +# Clean up after ourselves. +rm "/etc/ssh/sshd_config.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable SSH Support for Rhosts RSA Authentication + SSH can allow authentication through the obsolete rsh +command through the use of the authenticating user's SSH keys. This should be disabled. + +To ensure this behavior is disabled, add or correct the +following line in /etc/ssh/sshd_config: +RhostsRSAAuthentication no + As of openssh-server version 7.4 and above, +the RhostsRSAAuthentication option has been deprecated, and the line +RhostsRSAAuthentication no in /etc/ssh/sshd_config is not +necessary. + 11 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + 3.1.12 + CCI-000366 + 164.308(a)(4)(i) + 164.308(b)(1) + 164.308(b)(3) + 164.310(b) + 164.312(e)(1) + 164.312(e)(2)(ii) + 4.3.4.3.2 + 4.3.4.3.3 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + AC-17(a) + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + FIA_UAU.1 + SRG-OS-000480-GPOS-00227 + Configuring this setting for the SSH daemon provides additional +assurance that remote login via SSH will require a password, even +in the event of misconfiguration elsewhere. + CCE-80900-4 + - name: Disable SSH Support for Rhosts RSA Authentication + block: + + - name: Check for duplicate values + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*RhostsRSAAuthentication\s+ + state: absent + check_mode: true + changed_when: false + register: dupes + + - name: Deduplicate values from /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*RhostsRSAAuthentication\s+ + state: absent + when: dupes.found is defined and dupes.found > 1 + + - name: Insert correct line to /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: true + regexp: (?i)^\s*RhostsRSAAuthentication\s+ + line: RhostsRSAAuthentication no + state: present + insertbefore: ^[#\s]*Match + validate: /usr/sbin/sshd -t -f %s + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80900-4 + - NIST-800-171-3.1.12 + - NIST-800-53-AC-17(a) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sshd_disable_rhosts_rsa + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/ssh/sshd_config"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^RhostsRSAAuthentication") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s %s" "$stripped_key" "no" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^RhostsRSAAuthentication\\>" "/etc/ssh/sshd_config"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^RhostsRSAAuthentication\\>.*/$escaped_formatted_output/gi" "/etc/ssh/sshd_config" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-80900-4" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/ssh/sshd_config" >> "/etc/ssh/sshd_config" + printf '%s\n' "$formatted_output" >> "/etc/ssh/sshd_config" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable SSH Root Login + The root user should never be allowed to login to a +system directly over a network. +To disable root login via SSH, add or correct the following line in + + +/etc/ssh/sshd_config: + +PermitRootLogin no + This rule is disabled on Red Hat Virtualization Hosts and Managers, it will report not applicable. +RHV hosts require root access to be managed by RHV Manager. + BP28(R19) + NT007(R21) + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + 5.5.6 + APO01.06 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.02 + DSS06.03 + DSS06.06 + DSS06.10 + 3.1.1 + 3.1.5 + CCI-000366 + CCI-000770 + 164.308(a)(4)(i) + 164.308(b)(1) + 164.308(b)(3) + 164.310(b) + 164.312(e)(1) + 164.312(e)(2)(ii) + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.18.1.4 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.1 + A.9.4.2 + A.9.4.3 + A.9.4.4 + A.9.4.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.2.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CIP-007-3 R5.2 + CIP-007-3 R5.3.1 + CIP-007-3 R5.3.2 + CIP-007-3 R5.3.3 + AC-6(2) + AC-17(a) + IA-2 + IA-2(5) + CM-7(a) + CM-7(b) + CM-6(a) + PR.AC-1 + PR.AC-4 + PR.AC-6 + PR.AC-7 + PR.DS-5 + PR.PT-3 + FAU_GEN.1 + SRG-OS-000109-GPOS-00056 + SRG-OS-000480-GPOS-00227 + SRG-OS-000480-VMM-002000 + RHEL-08-010550 + 5.2.7 + SV-230296r627750_rule + Even though the communications channel may be encrypted, an additional layer of +security is gained by extending the policy of not logging directly on as root. +In addition, logging in with a user-specific account provides individual +accountability of actions performed on the system and also helps to minimize +direct attack attempts on root's password. + + CCE-80901-2 + - name: Disable SSH Root Login + block: + + - name: Check for duplicate values + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*PermitRootLogin\s+ + state: absent + check_mode: true + changed_when: false + register: dupes + + - name: Deduplicate values from /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*PermitRootLogin\s+ + state: absent + when: dupes.found is defined and dupes.found > 1 + + - name: Insert correct line to /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: true + regexp: (?i)^\s*PermitRootLogin\s+ + line: PermitRootLogin no + state: present + insertbefore: ^[#\s]*Match + validate: /usr/sbin/sshd -t -f %s + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80901-2 + - CJIS-5.5.6 + - DISA-STIG-RHEL-08-010550 + - NIST-800-171-3.1.1 + - NIST-800-171-3.1.5 + - NIST-800-53-AC-17(a) + - NIST-800-53-AC-6(2) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-IA-2 + - NIST-800-53-IA-2(5) + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sshd_disable_root_login + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if [ -e "/etc/ssh/sshd_config" ] ; then + + LC_ALL=C sed -i "/^\s*PermitRootLogin\s\+/Id" "/etc/ssh/sshd_config" +else + touch "/etc/ssh/sshd_config" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/ssh/sshd_config" + +cp "/etc/ssh/sshd_config" "/etc/ssh/sshd_config.bak" +# Insert before the line matching the regex '^Match'. +line_number="$(LC_ALL=C grep -n "^Match" "/etc/ssh/sshd_config.bak" | LC_ALL=C sed 's/:.*//g')" +if [ -z "$line_number" ]; then + # There was no match of '^Match', insert at + # the end of the file. + printf '%s\n' "PermitRootLogin no" >> "/etc/ssh/sshd_config" +else + head -n "$(( line_number - 1 ))" "/etc/ssh/sshd_config.bak" > "/etc/ssh/sshd_config" + printf '%s\n' "PermitRootLogin no" >> "/etc/ssh/sshd_config" + tail -n "+$(( line_number ))" "/etc/ssh/sshd_config.bak" >> "/etc/ssh/sshd_config" +fi +# Clean up after ourselves. +rm "/etc/ssh/sshd_config.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable SSH root Login with a Password (Insecure) + To disable password-based root logins over SSH, add or correct the following line in + + +/etc/ssh/sshd_config: + +PermitRootLogin prohibit-password + While this disables password-based root logins, direct root logins +through other means such as through SSH keys or GSSAPI will still be +permitted. Permitting any sort of root login remotely opens up the +root account to attack. +To fully disable direct root logins over SSH (which is considered a +best practice) and prevent remote attacks against the root account, +see CCE-27100-7, CCE-27445-6, CCE-80901-2, and similar. + Even though the communications channel may be encrypted, an additional +layer of security is gained by preventing use of a password. +This also helps to minimize direct attack attempts on root's password. + - name: Disable SSH root Login with a Password (Insecure) + block: + + - name: Check for duplicate values + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*PermitRootLogin\s+ + state: absent + check_mode: true + changed_when: false + register: dupes + + - name: Deduplicate values from /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*PermitRootLogin\s+ + state: absent + when: dupes.found is defined and dupes.found > 1 + + - name: Insert correct line to /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: true + regexp: (?i)^\s*PermitRootLogin\s+ + line: PermitRootLogin prohibit-password + state: present + insertbefore: ^[#\s]*Match + validate: /usr/sbin/sshd -t -f %s + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sshd_disable_root_password_login + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if [ -e "/etc/ssh/sshd_config" ] ; then + + LC_ALL=C sed -i "/^\s*PermitRootLogin\s\+/Id" "/etc/ssh/sshd_config" +else + touch "/etc/ssh/sshd_config" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/ssh/sshd_config" + +cp "/etc/ssh/sshd_config" "/etc/ssh/sshd_config.bak" +# Insert before the line matching the regex '^Match'. +line_number="$(LC_ALL=C grep -n "^Match" "/etc/ssh/sshd_config.bak" | LC_ALL=C sed 's/:.*//g')" +if [ -z "$line_number" ]; then + # There was no match of '^Match', insert at + # the end of the file. + printf '%s\n' "PermitRootLogin prohibit-password" >> "/etc/ssh/sshd_config" +else + head -n "$(( line_number - 1 ))" "/etc/ssh/sshd_config.bak" > "/etc/ssh/sshd_config" + printf '%s\n' "PermitRootLogin prohibit-password" >> "/etc/ssh/sshd_config" + tail -n "+$(( line_number ))" "/etc/ssh/sshd_config.bak" >> "/etc/ssh/sshd_config" +fi +# Clean up after ourselves. +rm "/etc/ssh/sshd_config.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable SSH TCP Forwarding + The AllowTcpForwarding parameter specifies whether TCP forwarding is permitted. +To disable TCP forwarding, add or correct the following line in + + +/etc/ssh/sshd_config: + +AllowTcpForwarding no + 5.2.13 + Leaving port forwarding enabled can expose the organization to security risks and back-doors. + CCE-83301-2 + - name: Disable SSH TCP Forwarding + block: + + - name: Check for duplicate values + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*AllowTcpForwarding\s+ + state: absent + check_mode: true + changed_when: false + register: dupes + + - name: Deduplicate values from /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*AllowTcpForwarding\s+ + state: absent + when: dupes.found is defined and dupes.found > 1 + + - name: Insert correct line to /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: true + regexp: (?i)^\s*AllowTcpForwarding\s+ + line: AllowTcpForwarding no + state: present + insertbefore: ^[#\s]*Match + validate: /usr/sbin/sshd -t -f %s + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83301-2 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sshd_disable_tcp_forwarding + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if [ -e "/etc/ssh/sshd_config" ] ; then + + LC_ALL=C sed -i "/^\s*AllowTcpForwarding\s\+/Id" "/etc/ssh/sshd_config" +else + touch "/etc/ssh/sshd_config" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/ssh/sshd_config" + +cp "/etc/ssh/sshd_config" "/etc/ssh/sshd_config.bak" +# Insert before the line matching the regex '^Match'. +line_number="$(LC_ALL=C grep -n "^Match" "/etc/ssh/sshd_config.bak" | LC_ALL=C sed 's/:.*//g')" +if [ -z "$line_number" ]; then + # There was no match of '^Match', insert at + # the end of the file. + printf '%s\n' "AllowTcpForwarding no" >> "/etc/ssh/sshd_config" +else + head -n "$(( line_number - 1 ))" "/etc/ssh/sshd_config.bak" > "/etc/ssh/sshd_config" + printf '%s\n' "AllowTcpForwarding no" >> "/etc/ssh/sshd_config" + tail -n "+$(( line_number ))" "/etc/ssh/sshd_config.bak" >> "/etc/ssh/sshd_config" +fi +# Clean up after ourselves. +rm "/etc/ssh/sshd_config.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable SSH Support for User Known Hosts + SSH can allow system users to connect to systems if a cache of the remote +systems public keys is available. This should be disabled. + +To ensure this behavior is disabled, add or correct the following line in + + +/etc/ssh/sshd_config: + +IgnoreUserKnownHosts yes + 11 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + 3.1.12 + CCI-000366 + 164.308(a)(4)(i) + 164.308(b)(1) + 164.308(b)(3) + 164.310(b) + 164.312(e)(1) + 164.312(e)(2)(ii) + 4.3.4.3.2 + 4.3.4.3.3 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + AC-17(a) + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + FIA_UAU.1 + SRG-OS-000480-GPOS-00227 + RHEL-08-010520 + SV-230290r627750_rule + Configuring this setting for the SSH daemon provides additional +assurance that remote login via SSH will require a password, even +in the event of misconfiguration elsewhere. + CCE-80902-0 + - name: Disable SSH Support for User Known Hosts + block: + + - name: Check for duplicate values + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*IgnoreUserKnownHosts\s+ + state: absent + check_mode: true + changed_when: false + register: dupes + + - name: Deduplicate values from /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*IgnoreUserKnownHosts\s+ + state: absent + when: dupes.found is defined and dupes.found > 1 + + - name: Insert correct line to /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: true + regexp: (?i)^\s*IgnoreUserKnownHosts\s+ + line: IgnoreUserKnownHosts yes + state: present + insertbefore: ^[#\s]*Match + validate: /usr/sbin/sshd -t -f %s + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80902-0 + - DISA-STIG-RHEL-08-010520 + - NIST-800-171-3.1.12 + - NIST-800-53-AC-17(a) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sshd_disable_user_known_hosts + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if [ -e "/etc/ssh/sshd_config" ] ; then + + LC_ALL=C sed -i "/^\s*IgnoreUserKnownHosts\s\+/Id" "/etc/ssh/sshd_config" +else + touch "/etc/ssh/sshd_config" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/ssh/sshd_config" + +cp "/etc/ssh/sshd_config" "/etc/ssh/sshd_config.bak" +# Insert before the line matching the regex '^Match'. +line_number="$(LC_ALL=C grep -n "^Match" "/etc/ssh/sshd_config.bak" | LC_ALL=C sed 's/:.*//g')" +if [ -z "$line_number" ]; then + # There was no match of '^Match', insert at + # the end of the file. + printf '%s\n' "IgnoreUserKnownHosts yes" >> "/etc/ssh/sshd_config" +else + head -n "$(( line_number - 1 ))" "/etc/ssh/sshd_config.bak" > "/etc/ssh/sshd_config" + printf '%s\n' "IgnoreUserKnownHosts yes" >> "/etc/ssh/sshd_config" + tail -n "+$(( line_number ))" "/etc/ssh/sshd_config.bak" >> "/etc/ssh/sshd_config" +fi +# Clean up after ourselves. +rm "/etc/ssh/sshd_config.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable X11 Forwarding + The X11Forwarding parameter provides the ability to tunnel X11 traffic +through the connection to enable remote graphic connections. +SSH has the capability to encrypt remote X11 connections when SSH's +X11Forwarding option is enabled. + +The default SSH configuration disables X11Forwarding. The appropriate +configuration is used if no value is set for X11Forwarding. + +To explicitly disable X11 Forwarding, add or correct the following line in + + +/etc/ssh/sshd_config: + +X11Forwarding no + CCI-000366 + CM-6(b) + SRG-OS-000480-GPOS-00227 + RHEL-08-040340 + 5.2.12 + SV-230555r627750_rule + Disable X11 forwarding unless there is an operational requirement to use X11 +applications directly. There is a small risk that the remote X11 servers of +users who are logged in via SSH with X11 forwarding could be compromised by +other users on the X11 server. Note that even if X11 forwarding is disabled, +users can always install their own forwarders. + CCE-83360-8 + - name: Disable X11 Forwarding + block: + + - name: Check for duplicate values + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*X11Forwarding\s+ + state: absent + check_mode: true + changed_when: false + register: dupes + + - name: Deduplicate values from /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*X11Forwarding\s+ + state: absent + when: dupes.found is defined and dupes.found > 1 + + - name: Insert correct line to /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: true + regexp: (?i)^\s*X11Forwarding\s+ + line: X11Forwarding no + state: present + insertbefore: ^[#\s]*Match + validate: /usr/sbin/sshd -t -f %s + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83360-8 + - DISA-STIG-RHEL-08-040340 + - NIST-800-53-CM-6(b) + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sshd_disable_x11_forwarding + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if [ -e "/etc/ssh/sshd_config" ] ; then + + LC_ALL=C sed -i "/^\s*X11Forwarding\s\+/Id" "/etc/ssh/sshd_config" +else + touch "/etc/ssh/sshd_config" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/ssh/sshd_config" + +cp "/etc/ssh/sshd_config" "/etc/ssh/sshd_config.bak" +# Insert before the line matching the regex '^Match'. +line_number="$(LC_ALL=C grep -n "^Match" "/etc/ssh/sshd_config.bak" | LC_ALL=C sed 's/:.*//g')" +if [ -z "$line_number" ]; then + # There was no match of '^Match', insert at + # the end of the file. + printf '%s\n' "X11Forwarding no" >> "/etc/ssh/sshd_config" +else + head -n "$(( line_number - 1 ))" "/etc/ssh/sshd_config.bak" > "/etc/ssh/sshd_config" + printf '%s\n' "X11Forwarding no" >> "/etc/ssh/sshd_config" + tail -n "+$(( line_number ))" "/etc/ssh/sshd_config.bak" >> "/etc/ssh/sshd_config" +fi +# Clean up after ourselves. +rm "/etc/ssh/sshd_config.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Do Not Allow SSH Environment Options + Ensure that users are not able to override environment variables of the SSH daemon. + +The default SSH configuration disables environment processing. The appropriate +configuration is used if no value is set for PermitUserEnvironment. + +To explicitly disable Environment options, add or correct the following + + +/etc/ssh/sshd_config: + +PermitUserEnvironment no + 11 + 3 + 9 + 5.5.6 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + 3.1.12 + CCI-000366 + 164.308(a)(4)(i) + 164.308(b)(1) + 164.308(b)(3) + 164.310(b) + 164.312(e)(1) + 164.312(e)(2)(ii) + 4.3.4.3.2 + 4.3.4.3.3 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + AC-17(a) + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + SRG-OS-000480-GPOS-00229 + SRG-OS-000480-VMM-002000 + RHEL-08-010830 + 5.2.10 + SV-230330r646870_rule + SSH environment options potentially allow users to bypass +access restriction in some configurations. + CCE-80903-8 + - name: Do Not Allow SSH Environment Options + block: + + - name: Check for duplicate values + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*PermitUserEnvironment\s+ + state: absent + check_mode: true + changed_when: false + register: dupes + + - name: Deduplicate values from /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*PermitUserEnvironment\s+ + state: absent + when: dupes.found is defined and dupes.found > 1 + + - name: Insert correct line to /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: true + regexp: (?i)^\s*PermitUserEnvironment\s+ + line: PermitUserEnvironment no + state: present + insertbefore: ^[#\s]*Match + validate: /usr/sbin/sshd -t -f %s + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80903-8 + - CJIS-5.5.6 + - DISA-STIG-RHEL-08-010830 + - NIST-800-171-3.1.12 + - NIST-800-53-AC-17(a) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sshd_do_not_permit_user_env + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if [ -e "/etc/ssh/sshd_config" ] ; then + + LC_ALL=C sed -i "/^\s*PermitUserEnvironment\s\+/Id" "/etc/ssh/sshd_config" +else + touch "/etc/ssh/sshd_config" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/ssh/sshd_config" + +cp "/etc/ssh/sshd_config" "/etc/ssh/sshd_config.bak" +# Insert before the line matching the regex '^Match'. +line_number="$(LC_ALL=C grep -n "^Match" "/etc/ssh/sshd_config.bak" | LC_ALL=C sed 's/:.*//g')" +if [ -z "$line_number" ]; then + # There was no match of '^Match', insert at + # the end of the file. + printf '%s\n' "PermitUserEnvironment no" >> "/etc/ssh/sshd_config" +else + head -n "$(( line_number - 1 ))" "/etc/ssh/sshd_config.bak" > "/etc/ssh/sshd_config" + printf '%s\n' "PermitUserEnvironment no" >> "/etc/ssh/sshd_config" + tail -n "+$(( line_number ))" "/etc/ssh/sshd_config.bak" >> "/etc/ssh/sshd_config" +fi +# Clean up after ourselves. +rm "/etc/ssh/sshd_config.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable GSSAPI Authentication + Sites setup to use Kerberos or other GSSAPI Authenticaion require setting +sshd to accept this authentication. +To enable GSSAPI authentication, add or correct the following line in + + +/etc/ssh/sshd_config: + +GSSAPIAuthentication yes + Kerberos authentication for SSH is often implemented using GSSAPI. If +Kerberos is enabled through SSH, the SSH daemon provides a means of access +to the system's Kerberos implementation. Vulnerabilities in the system's +Kerberos implementations may be subject to exploitation. + +For enterprises, Kerberos is often enabled and used with GSSAPI for +centralized user account management which may necessitate enabling of +GSSAPI functionality in SSH. + - name: Enable GSSAPI Authentication + block: + + - name: Check for duplicate values + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*GSSAPIAuthentication\s+ + state: absent + check_mode: true + changed_when: false + register: dupes + + - name: Deduplicate values from /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*GSSAPIAuthentication\s+ + state: absent + when: dupes.found is defined and dupes.found > 1 + + - name: Insert correct line to /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: true + regexp: (?i)^\s*GSSAPIAuthentication\s+ + line: GSSAPIAuthentication yes + state: present + insertbefore: ^[#\s]*Match + validate: /usr/sbin/sshd -t -f %s + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sshd_enable_gssapi_auth + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if [ -e "/etc/ssh/sshd_config" ] ; then + + LC_ALL=C sed -i "/^\s*GSSAPIAuthentication\s\+/Id" "/etc/ssh/sshd_config" +else + touch "/etc/ssh/sshd_config" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/ssh/sshd_config" + +cp "/etc/ssh/sshd_config" "/etc/ssh/sshd_config.bak" +# Insert before the line matching the regex '^Match'. +line_number="$(LC_ALL=C grep -n "^Match" "/etc/ssh/sshd_config.bak" | LC_ALL=C sed 's/:.*//g')" +if [ -z "$line_number" ]; then + # There was no match of '^Match', insert at + # the end of the file. + printf '%s\n' "GSSAPIAuthentication yes" >> "/etc/ssh/sshd_config" +else + head -n "$(( line_number - 1 ))" "/etc/ssh/sshd_config.bak" > "/etc/ssh/sshd_config" + printf '%s\n' "GSSAPIAuthentication yes" >> "/etc/ssh/sshd_config" + tail -n "+$(( line_number ))" "/etc/ssh/sshd_config.bak" >> "/etc/ssh/sshd_config" +fi +# Clean up after ourselves. +rm "/etc/ssh/sshd_config.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable PAM + UsePAM Enables the Pluggable Authentication Module interface. If set to “yes” this will +enable PAM authentication using ChallengeResponseAuthentication and +PasswordAuthentication in addition to PAM account and session module processing for all +authentication types. + +To enable PAM authentication, add or correct the following line in + + +/etc/ssh/sshd_config: + +UsePAM yes + CCI-000877 + SRG-OS-000125-GPOS-00065 + 5.2.6 + When UsePAM is set to yes, PAM runs through account and session types properly. This is +important if you want to restrict access to services based off of IP, time or other factors of +the account. Additionally, you can make sure users inherit certain environment variables +on login or disallow access to the server. + CCE-86721-8 + - name: Enable PAM + block: + + - name: Check for duplicate values + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*UsePAM\s+ + state: absent + check_mode: true + changed_when: false + register: dupes + + - name: Deduplicate values from /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*UsePAM\s+ + state: absent + when: dupes.found is defined and dupes.found > 1 + + - name: Insert correct line to /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: true + regexp: (?i)^\s*UsePAM\s+ + line: UsePAM yes + state: present + insertbefore: ^[#\s]*Match + validate: /usr/sbin/sshd -t -f %s + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86721-8 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sshd_enable_pam + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if [ -e "/etc/ssh/sshd_config" ] ; then + + LC_ALL=C sed -i "/^\s*UsePAM\s\+/Id" "/etc/ssh/sshd_config" +else + touch "/etc/ssh/sshd_config" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/ssh/sshd_config" + +cp "/etc/ssh/sshd_config" "/etc/ssh/sshd_config.bak" +# Insert before the line matching the regex '^Match'. +line_number="$(LC_ALL=C grep -n "^Match" "/etc/ssh/sshd_config.bak" | LC_ALL=C sed 's/:.*//g')" +if [ -z "$line_number" ]; then + # There was no match of '^Match', insert at + # the end of the file. + printf '%s\n' "UsePAM yes" >> "/etc/ssh/sshd_config" +else + head -n "$(( line_number - 1 ))" "/etc/ssh/sshd_config.bak" > "/etc/ssh/sshd_config" + printf '%s\n' "UsePAM yes" >> "/etc/ssh/sshd_config" + tail -n "+$(( line_number ))" "/etc/ssh/sshd_config.bak" >> "/etc/ssh/sshd_config" +fi +# Clean up after ourselves. +rm "/etc/ssh/sshd_config.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable Public Key Authentication + Enable SSH login with public keys. + +The default SSH configuration enables authentication based on public keys. The appropriate +configuration is used if no value is set for PubkeyAuthentication. + +To explicitly enable Public Key Authentication, add or correct the following + + +/etc/ssh/sshd_config: + +PubkeyAuthentication yes + CCI-000765 + CCI-000766 + CCI-000767 + CCI-000768 + SRG-OS-000105-GPOS-00052 + SRG-OS-000106-GPOS-00053 + SRG-OS-000107-GPOS-00054 + SRG-OS-000108-GPOS-00055 + Without the use of multifactor authentication, the ease of access to +privileged functions is greatly increased. Multifactor authentication +requires using two or more factors to achieve authentication. +A privileged account is defined as an information system account with +authorizations of a privileged user. +The DoD CAC with DoD-approved PKI is an example of multifactor +authentication. + - name: Enable Public Key Authentication + block: + + - name: Check for duplicate values + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*PubkeyAuthentication\s+ + state: absent + check_mode: true + changed_when: false + register: dupes + + - name: Deduplicate values from /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*PubkeyAuthentication\s+ + state: absent + when: dupes.found is defined and dupes.found > 1 + + - name: Insert correct line to /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: true + regexp: (?i)^\s*PubkeyAuthentication\s+ + line: PubkeyAuthentication yes + state: present + insertbefore: ^[#\s]*Match + validate: /usr/sbin/sshd -t -f %s + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sshd_enable_pubkey_auth + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if [ -e "/etc/ssh/sshd_config" ] ; then + + LC_ALL=C sed -i "/^\s*PubkeyAuthentication\s\+/Id" "/etc/ssh/sshd_config" +else + touch "/etc/ssh/sshd_config" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/ssh/sshd_config" + +cp "/etc/ssh/sshd_config" "/etc/ssh/sshd_config.bak" +# Insert before the line matching the regex '^Match'. +line_number="$(LC_ALL=C grep -n "^Match" "/etc/ssh/sshd_config.bak" | LC_ALL=C sed 's/:.*//g')" +if [ -z "$line_number" ]; then + # There was no match of '^Match', insert at + # the end of the file. + printf '%s\n' "PubkeyAuthentication yes" >> "/etc/ssh/sshd_config" +else + head -n "$(( line_number - 1 ))" "/etc/ssh/sshd_config.bak" > "/etc/ssh/sshd_config" + printf '%s\n' "PubkeyAuthentication yes" >> "/etc/ssh/sshd_config" + tail -n "+$(( line_number ))" "/etc/ssh/sshd_config.bak" >> "/etc/ssh/sshd_config" +fi +# Clean up after ourselves. +rm "/etc/ssh/sshd_config.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable Use of Strict Mode Checking + SSHs StrictModes option checks file and ownership permissions in +the user's home directory .ssh folder before accepting login. If world- +writable permissions are found, logon is rejected. + +The default SSH configuration has StrictModes enabled. The appropriate +configuration is used if no value is set for StrictModes. + +To explicitly enable StrictModes in SSH, add or correct the following line in + + +/etc/ssh/sshd_config: + +StrictModes yes + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + 3.1.12 + CCI-000366 + 164.308(a)(4)(i) + 164.308(b)(1) + 164.308(b)(3) + 164.310(b) + 164.312(e)(1) + 164.312(e)(2)(ii) + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + AC-6 + AC-17(a) + CM-6(a) + PR.AC-4 + PR.DS-5 + SRG-OS-000480-GPOS-00227 + SRG-OS-000480-VMM-002000 + RHEL-08-010500 + SV-230288r627750_rule + If other users have access to modify user-specific SSH configuration files, they +may be able to log into the system as another user. + CCE-80904-6 + - name: Enable Use of Strict Mode Checking + block: + + - name: Check for duplicate values + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*StrictModes\s+ + state: absent + check_mode: true + changed_when: false + register: dupes + + - name: Deduplicate values from /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*StrictModes\s+ + state: absent + when: dupes.found is defined and dupes.found > 1 + + - name: Insert correct line to /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: true + regexp: (?i)^\s*StrictModes\s+ + line: StrictModes yes + state: present + insertbefore: ^[#\s]*Match + validate: /usr/sbin/sshd -t -f %s + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80904-6 + - DISA-STIG-RHEL-08-010500 + - NIST-800-171-3.1.12 + - NIST-800-53-AC-17(a) + - NIST-800-53-AC-6 + - NIST-800-53-CM-6(a) + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sshd_enable_strictmodes + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if [ -e "/etc/ssh/sshd_config" ] ; then + + LC_ALL=C sed -i "/^\s*StrictModes\s\+/Id" "/etc/ssh/sshd_config" +else + touch "/etc/ssh/sshd_config" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/ssh/sshd_config" + +cp "/etc/ssh/sshd_config" "/etc/ssh/sshd_config.bak" +# Insert before the line matching the regex '^Match'. +line_number="$(LC_ALL=C grep -n "^Match" "/etc/ssh/sshd_config.bak" | LC_ALL=C sed 's/:.*//g')" +if [ -z "$line_number" ]; then + # There was no match of '^Match', insert at + # the end of the file. + printf '%s\n' "StrictModes yes" >> "/etc/ssh/sshd_config" +else + head -n "$(( line_number - 1 ))" "/etc/ssh/sshd_config.bak" > "/etc/ssh/sshd_config" + printf '%s\n' "StrictModes yes" >> "/etc/ssh/sshd_config" + tail -n "+$(( line_number ))" "/etc/ssh/sshd_config.bak" >> "/etc/ssh/sshd_config" +fi +# Clean up after ourselves. +rm "/etc/ssh/sshd_config.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable SSH Warning Banner + To enable the warning banner and ensure it is consistent +across the system, add or correct the following line in + + +/etc/ssh/sshd_config: + +Banner /etc/issue +Another section contains information on how to create an +appropriate system-wide warning banner. + 1 + 12 + 15 + 16 + 5.5.6 + DSS05.04 + DSS05.10 + DSS06.10 + 3.1.9 + CCI-000048 + CCI-000050 + CCI-001384 + CCI-001385 + CCI-001386 + CCI-001387 + CCI-001388 + 164.308(a)(4)(i) + 164.308(b)(1) + 164.308(b)(3) + 164.310(b) + 164.312(e)(1) + 164.312(e)(2)(ii) + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + A.18.1.4 + A.9.2.1 + A.9.2.4 + A.9.3.1 + A.9.4.2 + A.9.4.3 + AC-8(a) + AC-8(c) + AC-17(a) + CM-6(a) + PR.AC-7 + FTA_TAB.1 + SRG-OS-000023-GPOS-00006 + SRG-OS-000228-GPOS-00088 + SRG-OS-000023-VMM-000060 + SRG-OS-000024-VMM-000070 + RHEL-08-010040 + 5.2.15 + SV-230225r627750_rule + The warning message reinforces policy awareness during the logon process and +facilitates possible legal action against attackers. Alternatively, systems +whose ownership should not be obvious should ensure usage of a banner that does +not provide easy attribution. + CCE-80905-3 + - name: Enable SSH Warning Banner + block: + + - name: Check for duplicate values + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*Banner\s+ + state: absent + check_mode: true + changed_when: false + register: dupes + + - name: Deduplicate values from /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*Banner\s+ + state: absent + when: dupes.found is defined and dupes.found > 1 + + - name: Insert correct line to /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: true + regexp: (?i)^\s*Banner\s+ + line: Banner /etc/issue + state: present + insertbefore: ^[#\s]*Match + validate: /usr/sbin/sshd -t -f %s + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80905-3 + - CJIS-5.5.6 + - DISA-STIG-RHEL-08-010040 + - NIST-800-171-3.1.9 + - NIST-800-53-AC-17(a) + - NIST-800-53-AC-8(a) + - NIST-800-53-AC-8(c) + - NIST-800-53-CM-6(a) + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sshd_enable_warning_banner + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if [ -e "/etc/ssh/sshd_config" ] ; then + + LC_ALL=C sed -i "/^\s*Banner\s\+/Id" "/etc/ssh/sshd_config" +else + touch "/etc/ssh/sshd_config" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/ssh/sshd_config" + +cp "/etc/ssh/sshd_config" "/etc/ssh/sshd_config.bak" +# Insert before the line matching the regex '^Match'. +line_number="$(LC_ALL=C grep -n "^Match" "/etc/ssh/sshd_config.bak" | LC_ALL=C sed 's/:.*//g')" +if [ -z "$line_number" ]; then + # There was no match of '^Match', insert at + # the end of the file. + printf '%s\n' "Banner /etc/issue" >> "/etc/ssh/sshd_config" +else + head -n "$(( line_number - 1 ))" "/etc/ssh/sshd_config.bak" > "/etc/ssh/sshd_config" + printf '%s\n' "Banner /etc/issue" >> "/etc/ssh/sshd_config" + tail -n "+$(( line_number ))" "/etc/ssh/sshd_config.bak" >> "/etc/ssh/sshd_config" +fi +# Clean up after ourselves. +rm "/etc/ssh/sshd_config.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable SSH Warning Banner + To enable the warning banner and ensure it is consistent +across the system, add or correct the following line in + +/etc/ssh/sshd_config: + +Banner /etc/issue.net +Another section contains information on how to create an +appropriate system-wide warning banner. + 5.5.6 + DSS05.04 + DSS05.10 + DSS06.10 + 3.1.9 + CCI-000048 + CCI-000050 + CCI-001384 + CCI-001385 + CCI-001386 + CCI-001387 + CCI-001388 + 164.308(a)(4)(i) + 164.308(b)(1) + 164.308(b)(3) + 164.310(b) + 164.312(e)(1) + 164.312(e)(2)(ii) + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + A.18.1.4 + A.9.2.1 + A.9.2.4 + A.9.3.1 + A.9.4.2 + A.9.4.3 + AC-8(a) + AC-8(c) + AC-17(a) + CM-6(a) + PR.AC-7 + FTA_TAB.1 + SRG-OS-000023-GPOS-00006 + SRG-OS-000228-GPOS-00088 + SRG-OS-000023-VMM-000060 + SRG-OS-000024-VMM-000070 + The warning message reinforces policy awareness during the logon process and +facilitates possible legal action against attackers. Alternatively, systems +whose ownership should not be obvious should ensure usage of a banner that does +not provide easy attribution. + - name: Enable SSH Warning Banner + block: + + - name: Check for duplicate values + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*Banner\s+ + state: absent + check_mode: true + changed_when: false + register: dupes + + - name: Deduplicate values from /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*Banner\s+ + state: absent + when: dupes.found is defined and dupes.found > 1 + + - name: Insert correct line to /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: true + regexp: (?i)^\s*Banner\s+ + line: Banner /etc/issue.net + state: present + insertbefore: ^[#\s]*Match + validate: /usr/sbin/sshd -t -f %s + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CJIS-5.5.6 + - NIST-800-171-3.1.9 + - NIST-800-53-AC-17(a) + - NIST-800-53-AC-8(a) + - NIST-800-53-AC-8(c) + - NIST-800-53-CM-6(a) + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sshd_enable_warning_banner_net + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if [ -e "/etc/ssh/sshd_config" ] ; then + + LC_ALL=C sed -i "/^\s*Banner\s\+/Id" "/etc/ssh/sshd_config" +else + touch "/etc/ssh/sshd_config" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/ssh/sshd_config" + +cp "/etc/ssh/sshd_config" "/etc/ssh/sshd_config.bak" +# Insert before the line matching the regex '^Match'. +line_number="$(LC_ALL=C grep -n "^Match" "/etc/ssh/sshd_config.bak" | LC_ALL=C sed 's/:.*//g')" +if [ -z "$line_number" ]; then + # There was no match of '^Match', insert at + # the end of the file. + printf '%s\n' "Banner /etc/issue.net" >> "/etc/ssh/sshd_config" +else + head -n "$(( line_number - 1 ))" "/etc/ssh/sshd_config.bak" > "/etc/ssh/sshd_config" + printf '%s\n' "Banner /etc/issue.net" >> "/etc/ssh/sshd_config" + tail -n "+$(( line_number ))" "/etc/ssh/sshd_config.bak" >> "/etc/ssh/sshd_config" +fi +# Clean up after ourselves. +rm "/etc/ssh/sshd_config.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable Encrypted X11 Forwarding + By default, remote X11 connections are not encrypted when initiated +by users. SSH has the capability to encrypt remote X11 connections when SSH's +X11Forwarding option is enabled. + +To enable X11 Forwarding, add or correct the following line in + + +/etc/ssh/sshd_config: + +X11Forwarding yes + 1 + 11 + 12 + 13 + 15 + 16 + 18 + 20 + 3 + 4 + 6 + 9 + BAI03.08 + BAI07.04 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS03.01 + 3.1.13 + CCI-000366 + 4.3.4.3.2 + 4.3.4.3.3 + 4.4.3.3 + SR 7.6 + A.12.1.1 + A.12.1.2 + A.12.1.4 + A.12.5.1 + A.12.6.2 + A.13.1.1 + A.13.1.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + CIP-007-3 R7.1 + CM-6(a) + AC-17(a) + AC-17(2) + DE.AE-1 + PR.DS-7 + PR.IP-1 + SRG-OS-000480-GPOS-00227 + Non-encrypted X displays allow an attacker to capture keystrokes and to execute commands +remotely. + CCE-82421-9 + - name: Enable Encrypted X11 Forwarding + block: + + - name: Check for duplicate values + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*X11Forwarding\s+ + state: absent + check_mode: true + changed_when: false + register: dupes + + - name: Deduplicate values from /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*X11Forwarding\s+ + state: absent + when: dupes.found is defined and dupes.found > 1 + + - name: Insert correct line to /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: true + regexp: (?i)^\s*X11Forwarding\s+ + line: X11Forwarding yes + state: present + insertbefore: ^[#\s]*Match + validate: /usr/sbin/sshd -t -f %s + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82421-9 + - NIST-800-171-3.1.13 + - NIST-800-53-AC-17(2) + - NIST-800-53-AC-17(a) + - NIST-800-53-CM-6(a) + - high_severity + - low_complexity + - low_disruption + - no_reboot_needed + - restrict_strategy + - sshd_enable_x11_forwarding + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if [ -e "/etc/ssh/sshd_config" ] ; then + + LC_ALL=C sed -i "/^\s*X11Forwarding\s\+/Id" "/etc/ssh/sshd_config" +else + touch "/etc/ssh/sshd_config" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/ssh/sshd_config" + +cp "/etc/ssh/sshd_config" "/etc/ssh/sshd_config.bak" +# Insert before the line matching the regex '^Match'. +line_number="$(LC_ALL=C grep -n "^Match" "/etc/ssh/sshd_config.bak" | LC_ALL=C sed 's/:.*//g')" +if [ -z "$line_number" ]; then + # There was no match of '^Match', insert at + # the end of the file. + printf '%s\n' "X11Forwarding yes" >> "/etc/ssh/sshd_config" +else + head -n "$(( line_number - 1 ))" "/etc/ssh/sshd_config.bak" > "/etc/ssh/sshd_config" + printf '%s\n' "X11Forwarding yes" >> "/etc/ssh/sshd_config" + tail -n "+$(( line_number ))" "/etc/ssh/sshd_config.bak" >> "/etc/ssh/sshd_config" +fi +# Clean up after ourselves. +rm "/etc/ssh/sshd_config.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Limit Users' SSH Access + By default, the SSH configuration allows any user with an account +to access the system. In order to specify the users that are allowed to login +via SSH and deny all other users, add or correct the following line in the +/etc/ssh/sshd_config file: +AllowUsers USER1 USER2 +Where USER1 and USER2 are valid user names. + 11 + 12 + 14 + 15 + 16 + 18 + 3 + 5 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + DSS06.03 + DSS06.06 + 3.1.12 + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + A.6.1.2 + A.7.1.1 + A.9.1.2 + A.9.2.1 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.2.3 + CIP-004-6 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.2 + CIP-007-3 R5.2 + CIP-007-3 R5.3.1 + CIP-007-3 R5.3.2 + CIP-007-3 R5.3.3 + AC-3 + CM-6(a) + PR.AC-4 + PR.AC-6 + PR.PT-3 + Specifying which accounts are allowed SSH access into the system reduces the +possibility of unauthorized access to the system. + CCE-82422-7 + + + + + + Enable SSH Print Last Log + Ensure that SSH will display the date and time of the last successful account logon. + +The default SSH configuration enables print of the date and time of the last login. +The appropriate configuration is used if no value is set for PrintLastLog. + +To explicitly enable LastLog in SSH, add or correct the following line in + + +/etc/ssh/sshd_config: + +PrintLastLog yes + 1 + 12 + 15 + 16 + DSS05.04 + DSS05.10 + DSS06.10 + CCI-000366 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + A.18.1.4 + A.9.2.1 + A.9.2.4 + A.9.3.1 + A.9.4.2 + A.9.4.3 + AC-9 + AC-17(a) + CM-6(a) + PR.AC-7 + SRG-OS-000480-GPOS-00227 + RHEL-08-020350 + SV-230382r627750_rule + Providing users feedback on when account accesses last occurred facilitates user +recognition and reporting of unauthorized account use. + CCE-82281-7 + - name: Enable SSH Print Last Log + block: + + - name: Check for duplicate values + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*PrintLastLog\s+ + state: absent + check_mode: true + changed_when: false + register: dupes + + - name: Deduplicate values from /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*PrintLastLog\s+ + state: absent + when: dupes.found is defined and dupes.found > 1 + + - name: Insert correct line to /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: true + regexp: (?i)^\s*PrintLastLog\s+ + line: PrintLastLog yes + state: present + insertbefore: ^[#\s]*Match + validate: /usr/sbin/sshd -t -f %s + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82281-7 + - DISA-STIG-RHEL-08-020350 + - NIST-800-53-AC-17(a) + - NIST-800-53-AC-9 + - NIST-800-53-CM-6(a) + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sshd_print_last_log + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if [ -e "/etc/ssh/sshd_config" ] ; then + + LC_ALL=C sed -i "/^\s*PrintLastLog\s\+/Id" "/etc/ssh/sshd_config" +else + touch "/etc/ssh/sshd_config" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/ssh/sshd_config" + +cp "/etc/ssh/sshd_config" "/etc/ssh/sshd_config.bak" +# Insert before the line matching the regex '^Match'. +line_number="$(LC_ALL=C grep -n "^Match" "/etc/ssh/sshd_config.bak" | LC_ALL=C sed 's/:.*//g')" +if [ -z "$line_number" ]; then + # There was no match of '^Match', insert at + # the end of the file. + printf '%s\n' "PrintLastLog yes" >> "/etc/ssh/sshd_config" +else + head -n "$(( line_number - 1 ))" "/etc/ssh/sshd_config.bak" > "/etc/ssh/sshd_config" + printf '%s\n' "PrintLastLog yes" >> "/etc/ssh/sshd_config" + tail -n "+$(( line_number ))" "/etc/ssh/sshd_config.bak" >> "/etc/ssh/sshd_config" +fi +# Clean up after ourselves. +rm "/etc/ssh/sshd_config.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Force frequent session key renegotiation + The RekeyLimit parameter specifies how often +the session key of the is renegotiated, both in terms of +amount of data that may be transmitted and the time +elapsed. +To decrease the default limits, add or correct the following line in + + +/etc/ssh/sshd_config: + +RekeyLimit + CCI-000068 + FCS_SSH_EXT.1.8 + SRG-OS-000480-GPOS-00227 + SRG-OS-000033-GPOS-00014 + RHEL-08-040161 + SV-230527r627750_rule + By decreasing the limit based on the amount of data and enabling +time-based limit, effects of potential attacks against +encryption keys are limited. + CCE-82177-7 + - name: XCCDF Value var_rekey_limit_size # promote to variable + set_fact: + var_rekey_limit_size: !!str + tags: + - always +- name: XCCDF Value var_rekey_limit_time # promote to variable + set_fact: + var_rekey_limit_time: !!str + tags: + - always + +- name: Force frequent session key renegotiation + block: + + - name: Check for duplicate values + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*RekeyLimit\s+ + state: absent + check_mode: true + changed_when: false + register: dupes + + - name: Deduplicate values from /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*RekeyLimit\s+ + state: absent + when: dupes.found is defined and dupes.found > 1 + + - name: Insert correct line to /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: true + regexp: (?i)^\s*RekeyLimit\s+ + line: RekeyLimit {{ var_rekey_limit_size }} {{ var_rekey_limit_time }} + state: present + insertbefore: ^[#\s]*Match + validate: /usr/sbin/sshd -t -f %s + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82177-7 + - DISA-STIG-RHEL-08-040161 + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sshd_rekey_limit + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_rekey_limit_size='' +var_rekey_limit_time='' + + + +if [ -e "/etc/ssh/sshd_config" ] ; then + + LC_ALL=C sed -i "/^\s*RekeyLimit\s\+/Id" "/etc/ssh/sshd_config" +else + touch "/etc/ssh/sshd_config" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/ssh/sshd_config" + +cp "/etc/ssh/sshd_config" "/etc/ssh/sshd_config.bak" +# Insert before the line matching the regex '^Match'. +line_number="$(LC_ALL=C grep -n "^Match" "/etc/ssh/sshd_config.bak" | LC_ALL=C sed 's/:.*//g')" +if [ -z "$line_number" ]; then + # There was no match of '^Match', insert at + # the end of the file. + printf '%s\n' "RekeyLimit $var_rekey_limit_size $var_rekey_limit_time" >> "/etc/ssh/sshd_config" +else + head -n "$(( line_number - 1 ))" "/etc/ssh/sshd_config.bak" > "/etc/ssh/sshd_config" + printf '%s\n' "RekeyLimit $var_rekey_limit_size $var_rekey_limit_time" >> "/etc/ssh/sshd_config" + tail -n "+$(( line_number ))" "/etc/ssh/sshd_config.bak" >> "/etc/ssh/sshd_config" +fi +# Clean up after ourselves. +rm "/etc/ssh/sshd_config.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + + + Ensure SSH LoginGraceTime is configured + The LoginGraceTime parameter to the SSH server specifies the time allowed for successful authentication to +the SSH server. The longer the Grace period is the more open unauthenticated connections +can exist. Like other session controls in this session the Grace Period should be limited to +appropriate limits to ensure the service is available for needed access. + 5.2.19 + Setting the LoginGraceTime parameter to a low number will minimize the risk of successful +brute force attacks to the SSH server. It will also limit the number of concurrent +unauthenticated connections. + CCE-86551-9 + - name: XCCDF Value var_sshd_set_login_grace_time # promote to variable + set_fact: + var_sshd_set_login_grace_time: !!str + tags: + - always + +- name: Ensure SSH LoginGraceTime is configured + block: + + - name: Check for duplicate values + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*LoginGraceTime\s+ + state: absent + check_mode: true + changed_when: false + register: dupes + + - name: Deduplicate values from /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*LoginGraceTime\s+ + state: absent + when: dupes.found is defined and dupes.found > 1 + + - name: Insert correct line to /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: true + regexp: (?i)^\s*LoginGraceTime\s+ + line: LoginGraceTime {{ var_sshd_set_login_grace_time }} + state: present + insertbefore: ^[#\s]*Match + validate: /usr/sbin/sshd -t -f %s + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86551-9 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sshd_set_login_grace_time + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_sshd_set_login_grace_time='' + + +if [ -e "/etc/ssh/sshd_config" ] ; then + + LC_ALL=C sed -i "/^\s*LoginGraceTime\s\+/Id" "/etc/ssh/sshd_config" +else + touch "/etc/ssh/sshd_config" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/ssh/sshd_config" + +cp "/etc/ssh/sshd_config" "/etc/ssh/sshd_config.bak" +# Insert before the line matching the regex '^Match'. +line_number="$(LC_ALL=C grep -n "^Match" "/etc/ssh/sshd_config.bak" | LC_ALL=C sed 's/:.*//g')" +if [ -z "$line_number" ]; then + # There was no match of '^Match', insert at + # the end of the file. + printf '%s\n' "LoginGraceTime $var_sshd_set_login_grace_time" >> "/etc/ssh/sshd_config" +else + head -n "$(( line_number - 1 ))" "/etc/ssh/sshd_config.bak" > "/etc/ssh/sshd_config" + printf '%s\n' "LoginGraceTime $var_sshd_set_login_grace_time" >> "/etc/ssh/sshd_config" + tail -n "+$(( line_number ))" "/etc/ssh/sshd_config.bak" >> "/etc/ssh/sshd_config" +fi +# Clean up after ourselves. +rm "/etc/ssh/sshd_config.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + + Set LogLevel to INFO + The INFO parameter specifices that record login and logout activity will be logged. + +The default SSH configuration sets the log level to INFO. The appropriate +configuration is used if no value is set for LogLevel. + +To explicitly specify the log level in SSH, add or correct the following line in + + +/etc/ssh/sshd_config: + +LogLevel INFO + AC-17(a) + CM-6(a) + 5.2.5 + SSH provides several logging levels with varying amounts of verbosity. DEBUG is specifically +not recommended other than strictly for debugging SSH communications since it provides +so much data that it is difficult to identify important security information. INFO level is the +basic level that only records login activity of SSH users. In many situations, such as Incident +Response, it is important to determine when a particular user was active on a system. The +logout record can eliminate those users who disconnected, which helps narrow the field. + CCE-82282-5 + - name: Set LogLevel to INFO + block: + + - name: Check for duplicate values + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*LogLevel\s+ + state: absent + check_mode: true + changed_when: false + register: dupes + + - name: Deduplicate values from /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*LogLevel\s+ + state: absent + when: dupes.found is defined and dupes.found > 1 + + - name: Insert correct line to /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: true + regexp: (?i)^\s*LogLevel\s+ + line: LogLevel INFO + state: present + insertbefore: ^[#\s]*Match + validate: /usr/sbin/sshd -t -f %s + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82282-5 + - NIST-800-53-AC-17(a) + - NIST-800-53-CM-6(a) + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - restrict_strategy + - sshd_set_loglevel_info + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if [ -e "/etc/ssh/sshd_config" ] ; then + + LC_ALL=C sed -i "/^\s*LogLevel\s\+/Id" "/etc/ssh/sshd_config" +else + touch "/etc/ssh/sshd_config" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/ssh/sshd_config" + +cp "/etc/ssh/sshd_config" "/etc/ssh/sshd_config.bak" +# Insert before the line matching the regex '^Match'. +line_number="$(LC_ALL=C grep -n "^Match" "/etc/ssh/sshd_config.bak" | LC_ALL=C sed 's/:.*//g')" +if [ -z "$line_number" ]; then + # There was no match of '^Match', insert at + # the end of the file. + printf '%s\n' "LogLevel INFO" >> "/etc/ssh/sshd_config" +else + head -n "$(( line_number - 1 ))" "/etc/ssh/sshd_config.bak" > "/etc/ssh/sshd_config" + printf '%s\n' "LogLevel INFO" >> "/etc/ssh/sshd_config" + tail -n "+$(( line_number ))" "/etc/ssh/sshd_config.bak" >> "/etc/ssh/sshd_config" +fi +# Clean up after ourselves. +rm "/etc/ssh/sshd_config.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Set SSH Daemon LogLevel to VERBOSE + The VERBOSE parameter configures the SSH daemon to record login and logout activity. +To specify the log level in +SSH, add or correct the following line in + + +/etc/ssh/sshd_config: + +LogLevel VERBOSE + CCI-000067 + CIP-007-3 R7.1 + AC-17(a) + AC-17(1) + CM-6(a) + SRG-OS-000032-GPOS-00013 + 5.2.5 + SSH provides several logging levels with varying amounts of verbosity. DEBUG is specifically +not recommended other than strictly for debugging SSH communications since it provides +so much data that it is difficult to identify important security information. INFO or +VERBOSE level is the basic level that only records login activity of SSH users. In many +situations, such as Incident Response, it is important to determine when a particular user was active +on a system. The logout record can eliminate those users who disconnected, which helps narrow the +field. + CCE-82420-1 + - name: Set SSH Daemon LogLevel to VERBOSE + block: + + - name: Check for duplicate values + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*LogLevel\s+ + state: absent + check_mode: true + changed_when: false + register: dupes + + - name: Deduplicate values from /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*LogLevel\s+ + state: absent + when: dupes.found is defined and dupes.found > 1 + + - name: Insert correct line to /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: true + regexp: (?i)^\s*LogLevel\s+ + line: LogLevel VERBOSE + state: present + insertbefore: ^[#\s]*Match + validate: /usr/sbin/sshd -t -f %s + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82420-1 + - NIST-800-53-AC-17(1) + - NIST-800-53-AC-17(a) + - NIST-800-53-CM-6(a) + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sshd_set_loglevel_verbose + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if [ -e "/etc/ssh/sshd_config" ] ; then + + LC_ALL=C sed -i "/^\s*LogLevel\s\+/Id" "/etc/ssh/sshd_config" +else + touch "/etc/ssh/sshd_config" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/ssh/sshd_config" + +cp "/etc/ssh/sshd_config" "/etc/ssh/sshd_config.bak" +# Insert before the line matching the regex '^Match'. +line_number="$(LC_ALL=C grep -n "^Match" "/etc/ssh/sshd_config.bak" | LC_ALL=C sed 's/:.*//g')" +if [ -z "$line_number" ]; then + # There was no match of '^Match', insert at + # the end of the file. + printf '%s\n' "LogLevel VERBOSE" >> "/etc/ssh/sshd_config" +else + head -n "$(( line_number - 1 ))" "/etc/ssh/sshd_config.bak" > "/etc/ssh/sshd_config" + printf '%s\n' "LogLevel VERBOSE" >> "/etc/ssh/sshd_config" + tail -n "+$(( line_number ))" "/etc/ssh/sshd_config.bak" >> "/etc/ssh/sshd_config" +fi +# Clean up after ourselves. +rm "/etc/ssh/sshd_config.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Set SSH authentication attempt limit + The MaxAuthTries parameter specifies the maximum number of authentication attempts +permitted per connection. Once the number of failures reaches half this value, additional failures are logged. +to set MaxAUthTries edit /etc/ssh/sshd_config as follows: +MaxAuthTries + 0421 + 0422 + 0431 + 0974 + 1173 + 1401 + 1504 + 1505 + 1546 + 1557 + 1558 + 1559 + 1560 + 1561 + 5.2.16 + Setting the MaxAuthTries parameter to a low number will minimize the risk of successful +brute force attacks to the SSH server. + CCE-83500-9 + - name: XCCDF Value sshd_max_auth_tries_value # promote to variable + set_fact: + sshd_max_auth_tries_value: !!str + tags: + - always + +- name: Set SSH authentication attempt limit + block: + + - name: Check for duplicate values + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*MaxAuthTries\s+ + state: absent + check_mode: true + changed_when: false + register: dupes + + - name: Deduplicate values from /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*MaxAuthTries\s+ + state: absent + when: dupes.found is defined and dupes.found > 1 + + - name: Insert correct line to /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: true + regexp: (?i)^\s*MaxAuthTries\s+ + line: MaxAuthTries {{ sshd_max_auth_tries_value }} + state: present + insertbefore: ^[#\s]*Match + validate: /usr/sbin/sshd -t -f %s + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83500-9 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sshd_set_max_auth_tries + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +sshd_max_auth_tries_value='' + + +if [ -e "/etc/ssh/sshd_config" ] ; then + + LC_ALL=C sed -i "/^\s*MaxAuthTries\s\+/Id" "/etc/ssh/sshd_config" +else + touch "/etc/ssh/sshd_config" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/ssh/sshd_config" + +cp "/etc/ssh/sshd_config" "/etc/ssh/sshd_config.bak" +# Insert before the line matching the regex '^Match'. +line_number="$(LC_ALL=C grep -n "^Match" "/etc/ssh/sshd_config.bak" | LC_ALL=C sed 's/:.*//g')" +if [ -z "$line_number" ]; then + # There was no match of '^Match', insert at + # the end of the file. + printf '%s\n' "MaxAuthTries $sshd_max_auth_tries_value" >> "/etc/ssh/sshd_config" +else + head -n "$(( line_number - 1 ))" "/etc/ssh/sshd_config.bak" > "/etc/ssh/sshd_config" + printf '%s\n' "MaxAuthTries $sshd_max_auth_tries_value" >> "/etc/ssh/sshd_config" + tail -n "+$(( line_number ))" "/etc/ssh/sshd_config.bak" >> "/etc/ssh/sshd_config" +fi +# Clean up after ourselves. +rm "/etc/ssh/sshd_config.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + + Set SSH MaxSessions limit + The MaxSessions parameter specifies the maximum number of open sessions permitted +from a given connection. To set MaxSessions edit +/etc/ssh/sshd_config as follows: MaxSessions + 5.2.18 + To protect a system from denial of service due to a large number of concurrent +sessions, use the rate limiting function of MaxSessions to protect availability +of sshd logins and prevent overwhelming the daemon. + CCE-83357-4 + - name: XCCDF Value var_sshd_max_sessions # promote to variable + set_fact: + var_sshd_max_sessions: !!str + tags: + - always + +- name: Set SSH MaxSessions limit + block: + + - name: Check for duplicate values + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*MaxSessions\s+ + state: absent + check_mode: true + changed_when: false + register: dupes + + - name: Deduplicate values from /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*MaxSessions\s+ + state: absent + when: dupes.found is defined and dupes.found > 1 + + - name: Insert correct line to /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: true + regexp: (?i)^\s*MaxSessions\s+ + line: MaxSessions {{ var_sshd_max_sessions }} + state: present + insertbefore: ^[#\s]*Match + validate: /usr/sbin/sshd -t -f %s + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83357-4 + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sshd_set_max_sessions + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_sshd_max_sessions='' + + +if [ -e "/etc/ssh/sshd_config" ] ; then + + LC_ALL=C sed -i "/^\s*MaxSessions\s\+/Id" "/etc/ssh/sshd_config" +else + touch "/etc/ssh/sshd_config" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/ssh/sshd_config" + +cp "/etc/ssh/sshd_config" "/etc/ssh/sshd_config.bak" +# Insert before the line matching the regex '^Match'. +line_number="$(LC_ALL=C grep -n "^Match" "/etc/ssh/sshd_config.bak" | LC_ALL=C sed 's/:.*//g')" +if [ -z "$line_number" ]; then + # There was no match of '^Match', insert at + # the end of the file. + printf '%s\n' "MaxSessions $var_sshd_max_sessions" >> "/etc/ssh/sshd_config" +else + head -n "$(( line_number - 1 ))" "/etc/ssh/sshd_config.bak" > "/etc/ssh/sshd_config" + printf '%s\n' "MaxSessions $var_sshd_max_sessions" >> "/etc/ssh/sshd_config" + tail -n "+$(( line_number ))" "/etc/ssh/sshd_config.bak" >> "/etc/ssh/sshd_config" +fi +# Clean up after ourselves. +rm "/etc/ssh/sshd_config.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + + Ensure SSH MaxStartups is configured + The MaxStartups parameter specifies the maximum number of concurrent +unauthenticated connections to the SSH daemon. Additional connections will be +dropped until authentication succeeds or the LoginGraceTime expires for a +connection. To confgure MaxStartups, you should add or correct the following +line in the +/etc/ssh/sshd_config file: +MaxStartups +CIS recommends a MaxStartups value of '10:30:60', or more restrictive where +dictated by site policy. + 5.2.17 + To protect a system from denial of service due to a large number of pending +authentication connection attempts, use the rate limiting function of MaxStartups +to protect availability of sshd logins and prevent overwhelming the daemon. + CCE-90718-8 + - name: XCCDF Value var_sshd_set_maxstartups # promote to variable + set_fact: + var_sshd_set_maxstartups: !!str + tags: + - always + +- name: Ensure SSH MaxStartups is configured + block: + + - name: Check for duplicate values + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*MaxStartups\s+ + state: absent + check_mode: true + changed_when: false + register: dupes + + - name: Deduplicate values from /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*MaxStartups\s+ + state: absent + when: dupes.found is defined and dupes.found > 1 + + - name: Insert correct line to /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: true + regexp: (?i)^\s*MaxStartups\s+ + line: MaxStartups {{ var_sshd_set_maxstartups }} + state: present + insertbefore: ^[#\s]*Match + validate: /usr/sbin/sshd -t -f %s + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-90718-8 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sshd_set_maxstartups + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_sshd_set_maxstartups='' + + +if [ -e "/etc/ssh/sshd_config" ] ; then + + LC_ALL=C sed -i "/^\s*MaxStartups\s\+/Id" "/etc/ssh/sshd_config" +else + touch "/etc/ssh/sshd_config" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/ssh/sshd_config" + +cp "/etc/ssh/sshd_config" "/etc/ssh/sshd_config.bak" +# Insert before the line matching the regex '^Match'. +line_number="$(LC_ALL=C grep -n "^Match" "/etc/ssh/sshd_config.bak" | LC_ALL=C sed 's/:.*//g')" +if [ -z "$line_number" ]; then + # There was no match of '^Match', insert at + # the end of the file. + printf '%s\n' "MaxStartups $var_sshd_set_maxstartups" >> "/etc/ssh/sshd_config" +else + head -n "$(( line_number - 1 ))" "/etc/ssh/sshd_config.bak" > "/etc/ssh/sshd_config" + printf '%s\n' "MaxStartups $var_sshd_set_maxstartups" >> "/etc/ssh/sshd_config" + tail -n "+$(( line_number ))" "/etc/ssh/sshd_config.bak" >> "/etc/ssh/sshd_config" +fi +# Clean up after ourselves. +rm "/etc/ssh/sshd_config.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Use Only FIPS 140-2 Validated Ciphers + Limit the ciphers to those algorithms which are FIPS-approved. +Counter (CTR) mode is also preferred over cipher-block chaining (CBC) mode. +The following line in /etc/ssh/sshd_config +demonstrates use of FIPS-approved ciphers: +Ciphers aes128-ctr,aes192-ctr,aes256-ctr,aes128-cbc,3des-cbc,aes192-cbc,aes256-cbc +The man page sshd_config(5) contains a list of supported ciphers. + +The rule is parametrized to use the following ciphers: . + The system needs to be rebooted for these changes to take effect. + System Crypto Modules must be provided by a vendor that undergoes +FIPS-140 certifications. +FIPS-140 is applicable to all Federal agencies that use +cryptographic-based security systems to protect sensitive information +in computer and telecommunication systems (including voice systems) as +defined in Section 5131 of the Information Technology Management Reform +Act of 1996, Public Law 104-106. This standard shall be used in +designing and implementing cryptographic modules that Federal +departments and agencies operate or are operated for them under +contract. See https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.140-2.pdf +To meet this, the system has to have cryptographic software provided by +a vendor that has undergone this certification. This means providing +documentation, test results, design information, and independent third +party review by an accredited lab. While open source software is +capable of meeting this, it does not meet FIPS-140 unless the vendor +submits to this process. + 1 + 11 + 12 + 14 + 15 + 16 + 18 + 3 + 5 + 6 + 8 + 9 + 5.5.6 + APO11.04 + APO13.01 + BAI03.05 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.04 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.03 + DSS06.06 + DSS06.10 + MEA02.01 + 3.1.13 + 3.13.11 + 3.13.8 + CCI-000068 + CCI-000366 + CCI-000803 + CCI-000877 + CCI-002890 + CCI-003123 + 164.308(b)(1) + 164.308(b)(2) + 164.312(e)(1) + 164.312(e)(2)(i) + 164.312(e)(2)(ii) + 164.314(b)(2)(i) + 4.3.3.2.2 + 4.3.3.3.9 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.1.2 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.5.1 + A.12.6.2 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.18.1.4 + A.6.1.2 + A.6.2.1 + A.6.2.2 + A.7.1.1 + A.9.1.2 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.1 + A.9.4.2 + A.9.4.3 + A.9.4.4 + A.9.4.5 + CM-6(a) + AC-17(a) + AC-17(2) + SC-13 + MA-4(6) + IA-5(1)(c) + SC-12(2) + SC-12(3) + PR.AC-1 + PR.AC-3 + PR.AC-4 + PR.AC-6 + PR.AC-7 + PR.IP-1 + PR.PT-1 + PR.PT-3 + PR.PT-4 + SRG-OS-000033-GPOS-00014 + SRG-OS-000120-GPOS-00061 + SRG-OS-000125-GPOS-00065 + SRG-OS-000250-GPOS-00093 + SRG-OS-000393-GPOS-00173 + SRG-OS-000394-GPOS-00174 + SRG-OS-000033-VMM-000140 + SRG-OS-000120-VMM-000600 + SRG-OS-000478-VMM-001980 + SRG-OS-000396-VMM-001590 + Unapproved mechanisms that are used for authentication to the cryptographic module are not verified and therefore +cannot be relied upon to provide confidentiality or integrity, and system data may be compromised. + +Operating systems utilizing encryption are required to use FIPS-compliant mechanisms for authenticating to +cryptographic modules. + +FIPS 140-2 is the current standard for validating that mechanisms used to access cryptographic modules +utilize authentication that meets industry and government requirements. For government systems, this allows +Security Levels 1, 2, 3, or 4 for use on Red Hat Enterprise Linux 8. + CCE-81032-5 + + + + + + + + + + + Use Only FIPS 140-2 Validated MACs + Limit the MACs to those hash algorithms which are FIPS-approved. +The following line in /etc/ssh/sshd_config +demonstrates use of FIPS-approved MACs: + +MACs hmac-sha2-512,hmac-sha2-256,hmac-sha1 + +The man page sshd_config(5) contains a list of supported MACs. + +The rule is parametrized to use the following MACs: . + The system needs to be rebooted for these changes to take effect. + System Crypto Modules must be provided by a vendor that undergoes +FIPS-140 certifications. +FIPS-140 is applicable to all Federal agencies that use +cryptographic-based security systems to protect sensitive information +in computer and telecommunication systems (including voice systems) as +defined in Section 5131 of the Information Technology Management Reform +Act of 1996, Public Law 104-106. This standard shall be used in +designing and implementing cryptographic modules that Federal +departments and agencies operate or are operated for them under +contract. See https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.140-2.pdf +To meet this, the system has to have cryptographic software provided by +a vendor that has undergone this certification. This means providing +documentation, test results, design information, and independent third +party review by an accredited lab. While open source software is +capable of meeting this, it does not meet FIPS-140 unless the vendor +submits to this process. + 1 + 12 + 13 + 15 + 16 + 5 + 8 + APO01.06 + APO13.01 + DSS01.04 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.07 + DSS06.02 + DSS06.03 + 3.1.13 + 3.13.11 + 3.13.8 + CCI-000068 + CCI-000803 + CCI-000877 + CCI-001453 + CCI-003123 + 164.308(b)(1) + 164.308(b)(2) + 164.312(e)(1) + 164.312(e)(2)(i) + 164.312(e)(2)(ii) + 164.314(b)(2)(i) + 4.3.3.5.1 + 4.3.3.6.6 + SR 1.1 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.6 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.11.2.6 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.6.2.1 + A.6.2.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.1 + A.9.4.2 + A.9.4.3 + A.9.4.4 + A.9.4.5 + CM-6(a) + AC-17(a) + AC-17(2) + SC-13 + MA-4(6) + SC-12(2) + SC-12(3) + PR.AC-1 + PR.AC-3 + PR.DS-5 + PR.PT-4 + SRG-OS-000125-GPOS-00065 + SRG-OS-000250-GPOS-00093 + SRG-OS-000394-GPOS-00174 + SRG-OS-000033-VMM-000140 + SRG-OS-000120-VMM-000600 + SRG-OS-000478-VMM-001980 + SRG-OS-000480-VMM-002000 + SRG-OS-000396-VMM-001590 + DoD Information Systems are required to use FIPS-approved cryptographic hash +functions. The only SSHv2 hash algorithms meeting this requirement is SHA2. + CCE-82198-3 + + + + + + + + + + + Enable Use of Privilege Separation + When enabled, SSH will create an unprivileged child process that +has the privilege of the authenticated user. To enable privilege separation in +SSH, add or correct the following line in the /etc/ssh/sshd_config file: +UsePrivilegeSeparation + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + 3.1.12 + CCI-000366 + 164.308(a)(4)(i) + 164.308(b)(1) + 164.308(b)(3) + 164.310(b) + 164.312(e)(1) + 164.312(e)(2)(ii) + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-6(a) + AC-17(a) + AC-6 + PR.AC-4 + PR.DS-5 + SRG-OS-000480-GPOS-00227 + SSH daemon privilege separation causes the SSH process to drop root privileges +when not needed which would decrease the impact of software vulnerabilities in +the unprivileged section. + CCE-80908-7 + - name: XCCDF Value var_sshd_priv_separation # promote to variable + set_fact: + var_sshd_priv_separation: !!str + tags: + - always + +- name: Enable Use of Privilege Separation + block: + + - name: Check for duplicate values + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*UsePrivilegeSeparation\s+ + state: absent + check_mode: true + changed_when: false + register: dupes + + - name: Deduplicate values from /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*UsePrivilegeSeparation\s+ + state: absent + when: dupes.found is defined and dupes.found > 1 + + - name: Insert correct line to /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: true + regexp: (?i)^\s*UsePrivilegeSeparation\s+ + line: UsePrivilegeSeparation {{ var_sshd_priv_separation }} + state: present + insertbefore: ^[#\s]*Match + validate: /usr/sbin/sshd -t -f %s + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80908-7 + - NIST-800-171-3.1.12 + - NIST-800-53-AC-17(a) + - NIST-800-53-AC-6 + - NIST-800-53-CM-6(a) + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sshd_use_priv_separation + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_sshd_priv_separation='' + + +if [ -e "/etc/ssh/sshd_config" ] ; then + + LC_ALL=C sed -i "/^\s*UsePrivilegeSeparation\s\+/Id" "/etc/ssh/sshd_config" +else + touch "/etc/ssh/sshd_config" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/ssh/sshd_config" + +cp "/etc/ssh/sshd_config" "/etc/ssh/sshd_config.bak" +# Insert before the line matching the regex '^Match'. +line_number="$(LC_ALL=C grep -n "^Match" "/etc/ssh/sshd_config.bak" | LC_ALL=C sed 's/:.*//g')" +if [ -z "$line_number" ]; then + # There was no match of '^Match', insert at + # the end of the file. + printf '%s\n' "UsePrivilegeSeparation $var_sshd_priv_separation" >> "/etc/ssh/sshd_config" +else + head -n "$(( line_number - 1 ))" "/etc/ssh/sshd_config.bak" > "/etc/ssh/sshd_config" + printf '%s\n' "UsePrivilegeSeparation $var_sshd_priv_separation" >> "/etc/ssh/sshd_config" + tail -n "+$(( line_number ))" "/etc/ssh/sshd_config.bak" >> "/etc/ssh/sshd_config" +fi +# Clean up after ourselves. +rm "/etc/ssh/sshd_config.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + + SSH server uses strong entropy to seed + To set up SSH server to use entropy from a high-quality source, edit the /etc/sysconfig/sshd file. +The SSH_USE_STRONG_RNG configuration value determines how many bytes of entropy to use, so +make sure that the file contains line +SSH_USE_STRONG_RNG=32 + This setting can cause problems on computers without the hardware random generator, because insufficient entropy causes the connection to be blocked until enough entropy is available. + CCI-000366 + FCS_RBG_EXT.1.2 + SRG-OS-000480-GPOS-00232 + SRG-OS-000480-GPOS-00227 + RHEL-08-010292 + SV-230253r627750_rule + SSH implementation in Red Hat Enterprise Linux 8 uses the openssl library, which doesn't use +high-entropy sources by default. Randomness is needed to generate data-encryption keys, and as +plaintext padding and initialization vectors in encryption algorithms, and high-quality +entropy elliminates the possibility that the output of the random number generator used by SSH +would be known to potential attackers. + CCE-82462-3 + - name: Setting unquoted shell-style assignment of 'SSH_USE_STRONG_RNG' to '32' in + '/etc/sysconfig/sshd' + block: + + - name: Check for duplicate values + lineinfile: + path: /etc/sysconfig/sshd + create: false + regexp: ^\s*SSH_USE_STRONG_RNG= + state: absent + check_mode: true + changed_when: false + register: dupes + + - name: Deduplicate values from /etc/sysconfig/sshd + lineinfile: + path: /etc/sysconfig/sshd + create: false + regexp: ^\s*SSH_USE_STRONG_RNG= + state: absent + when: dupes.found is defined and dupes.found > 1 + + - name: Insert correct line to /etc/sysconfig/sshd + lineinfile: + path: /etc/sysconfig/sshd + create: true + regexp: ^\s*SSH_USE_STRONG_RNG= + line: SSH_USE_STRONG_RNG=32 + state: present + insertbefore: ^# SSH_USE_STRONG_RNG + validate: /usr/bin/bash -n %s + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82462-3 + - DISA-STIG-RHEL-08-010292 + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - restrict_strategy + - sshd_use_strong_rng + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if [ -e "/etc/sysconfig/sshd" ] ; then + + LC_ALL=C sed -i "/^\s*SSH_USE_STRONG_RNG=/d" "/etc/sysconfig/sshd" +else + touch "/etc/sysconfig/sshd" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/sysconfig/sshd" + +cp "/etc/sysconfig/sshd" "/etc/sysconfig/sshd.bak" +# Insert before the line matching the regex '^#\s*SSH_USE_STRONG_RNG'. +line_number="$(LC_ALL=C grep -n "^#\s*SSH_USE_STRONG_RNG" "/etc/sysconfig/sshd.bak" | LC_ALL=C sed 's/:.*//g')" +if [ -z "$line_number" ]; then + # There was no match of '^#\s*SSH_USE_STRONG_RNG', insert at + # the end of the file. + printf '%s\n' "SSH_USE_STRONG_RNG=32" >> "/etc/sysconfig/sshd" +else + head -n "$(( line_number - 1 ))" "/etc/sysconfig/sshd.bak" > "/etc/sysconfig/sshd" + printf '%s\n' "SSH_USE_STRONG_RNG=32" >> "/etc/sysconfig/sshd" + tail -n "+$(( line_number ))" "/etc/sysconfig/sshd.bak" >> "/etc/sysconfig/sshd" +fi +# Clean up after ourselves. +rm "/etc/sysconfig/sshd.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Prevent remote hosts from connecting to the proxy display + The SSH daemon should prevent remote hosts from connecting to the proxy +display. + +The default SSH configuration for X11UseLocalhost is yes, +which prevents remote hosts from connecting to the proxy display. + +To explicitly prevent remote connections to the proxy display, add or correct +the following line in + + +/etc/ssh/sshd_config: + +X11UseLocalhost yes + CCI-000366 + CM-6(b) + SRG-OS-000480-GPOS-00227 + RHEL-08-040341 + SV-230556r627750_rule + When X11 forwarding is enabled, there may be additional exposure to the +server and client displays if the sshd proxy display is configured to listen +on the wildcard address. By default, sshd binds the forwarding server to the +loopback address and sets the hostname part of the DISPLAY +environment variable to localhost. This prevents remote hosts from +connecting to the proxy display. + CCE-84058-7 + - name: Prevent remote hosts from connecting to the proxy display + block: + + - name: Check for duplicate values + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*X11UseLocalhost\s+ + state: absent + check_mode: true + changed_when: false + register: dupes + + - name: Deduplicate values from /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*X11UseLocalhost\s+ + state: absent + when: dupes.found is defined and dupes.found > 1 + + - name: Insert correct line to /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: true + regexp: (?i)^\s*X11UseLocalhost\s+ + line: X11UseLocalhost yes + state: present + insertbefore: ^[#\s]*Match + validate: /usr/sbin/sshd -t -f %s + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84058-7 + - DISA-STIG-RHEL-08-040341 + - NIST-800-53-CM-6(b) + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sshd_x11_use_localhost + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if [ -e "/etc/ssh/sshd_config" ] ; then + + LC_ALL=C sed -i "/^\s*X11UseLocalhost\s\+/Id" "/etc/ssh/sshd_config" +else + touch "/etc/ssh/sshd_config" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/ssh/sshd_config" + +cp "/etc/ssh/sshd_config" "/etc/ssh/sshd_config.bak" +# Insert before the line matching the regex '^Match'. +line_number="$(LC_ALL=C grep -n "^Match" "/etc/ssh/sshd_config.bak" | LC_ALL=C sed 's/:.*//g')" +if [ -z "$line_number" ]; then + # There was no match of '^Match', insert at + # the end of the file. + printf '%s\n' "X11UseLocalhost yes" >> "/etc/ssh/sshd_config" +else + head -n "$(( line_number - 1 ))" "/etc/ssh/sshd_config.bak" > "/etc/ssh/sshd_config" + printf '%s\n' "X11UseLocalhost yes" >> "/etc/ssh/sshd_config" + tail -n "+$(( line_number ))" "/etc/ssh/sshd_config.bak" >> "/etc/ssh/sshd_config" +fi +# Clean up after ourselves. +rm "/etc/ssh/sshd_config.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Strengthen Firewall Configuration if Possible + If the SSH server is expected to only receive connections from +the local network, then strengthen the default firewall rule for the SSH service +to only accept connections from the appropriate network segment(s). + +Determine an appropriate network block, netwk, network mask, mask, and +network protocol, ip_protocol, representing the systems on your network which will +be allowed to access this SSH server. + +Run the following command: +firewall-cmd --permanent --add-rich-rule='rule family="ip_protocol" source address="netwk/mask" service name="ssh" accept' + + + + + System Security Services Daemon + The System Security Services Daemon (SSSD) is a system daemon that provides access +to different identity and authentication providers such as Red Hat's IdM, Microsoft's AD, +openLDAP, MIT Kerberos, etc. It uses a common framework that can provide caching and offline +support to systems utilizing SSSD. SSSD using caching to reduce load on authentication +servers permit offline authentication as well as store extended user data. + +For more information, see + https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/8/html-single/installing_identity_management/index#assembly_installing-an-idm-client_installing-identity-management + + + SSSD certificate_verification option + Value of the certificate_verification option in +the SSSD config. + sha1 + sha256 + sha384 + sha512 + sha1 + + + SSSD memcache_timeout option + Value of the memcache_timeout option in the [nss] section +of SSSD config /etc/sssd/sssd.conf. + 180 + 300 + 600 + 900 + 1800 + 86400 + 300 + + + SSSD ssh_known_hosts_timeout option + Value of the ssh_known_hosts_timeout option in the [ssh] section +of SSSD configuration file /etc/sssd/sssd.conf. + 180 + 300 + 600 + 900 + 1800 + 86400 + 180 + + + Install sssd-ipa Package + The sssd-ipa package can be installed with the following command: + +$ sudo yum install sssd-ipa + SRG-OS-000480-GPOS-00227 + sssd-ipa provides the IPA back end that the SSSD can utilize to +fetch identity data from and authenticate against an IPA server. + CCE-82994-5 + +package --add=sssd-ipa + + include install_sssd-ipa + +class install_sssd-ipa { + package { 'sssd-ipa': + ensure => 'installed', + } +} + + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-82994-5 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_sssd-ipa_installed + +- name: Ensure sssd-ipa is installed + package: + name: sssd-ipa + state: present + when: '"sssd-common" in ansible_facts.packages' + tags: + - CCE-82994-5 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_sssd-ipa_installed + + +[[packages]] +name = "sssd-ipa" +version = "*" + + # Remediation is applicable only in certain platforms +if rpm --quiet -q sssd-common; then + +if ! rpm -q --quiet "sssd-ipa" ; then + yum install -y "sssd-ipa" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Install the SSSD Package + The sssd package should be installed. +The sssd package can be installed with the following command: + +$ sudo yum install sssd + 1 + 12 + 15 + 16 + 5 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.03 + DSS06.10 + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.2 + 4.3.3.7.4 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + A.18.1.4 + A.7.1.1 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.2 + A.9.4.3 + CM-6(a) + PR.AC-1 + PR.AC-6 + PR.AC-7 + + CCE-82444-1 + +package --add=sssd + + include install_sssd + +class install_sssd { + package { 'sssd': + ensure => 'installed', + } +} + + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-82444-1 + - NIST-800-53-CM-6(a) + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_sssd_installed + +- name: Ensure sssd is installed + package: + name: sssd + state: present + when: '"sssd-common" in ansible_facts.packages' + tags: + - CCE-82444-1 + - NIST-800-53-CM-6(a) + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_sssd_installed + + +[[packages]] +name = "sssd" +version = "*" + + # Remediation is applicable only in certain platforms +if rpm --quiet -q sssd-common; then + +if ! rpm -q --quiet "sssd" ; then + yum install -y "sssd" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Enable the SSSD Service + The SSSD service should be enabled. + +The sssd service can be enabled with the following command: +$ sudo systemctl enable sssd.service + 1 + 12 + 15 + 16 + 5 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.03 + DSS06.10 + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.2 + 4.3.3.7.4 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + A.18.1.4 + A.7.1.1 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.2 + A.9.4.3 + CM-6(a) + IA-5(10) + PR.AC-1 + PR.AC-6 + PR.AC-7 + + + CCE-82440-9 + include enable_sssd + +class enable_sssd { + service {'sssd': + enable => true, + ensure => 'running', + } +} + + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-82440-9 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(10) + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_sssd_enabled + +- name: Enable service sssd + block: + + - name: Gather the package facts + package_facts: + manager: auto + + - name: Enable service sssd + service: + name: sssd + enabled: 'yes' + state: started + masked: 'no' + when: + - '"sssd" in ansible_facts.packages' + when: + - '"sssd-common" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82440-9 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(10) + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_sssd_enabled + + +[customizations.services] +enabled = ["sssd"] + + # Remediation is applicable only in certain platforms +if rpm --quiet -q sssd-common && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" unmask 'sssd.service' +"$SYSTEMCTL_EXEC" start 'sssd.service' +"$SYSTEMCTL_EXEC" enable 'sssd.service' + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Certificate status checking in SSSD + Multifactor solutions that require devices separate from information systems gaining access include, +for example, hardware tokens providing time-based or challenge-response authenticators and smart cards. +Configuring certificate_verification to ocsp_dgst= ensures that certificates for +multifactor solutions are checked via Online Certificate Status Protocol (OCSP). + CCI-001948 + CCI-001954 + IA-2(11) + SRG-OS-000375-GPOS-00160 + SRG-OS-000377-GPOS-00162 + RHEL-08-010400 + SV-230274r809281_rule + Ensuring that multifactor solutions certificates are checked via Online Certificate Status Protocol (OCSP) +ensures the security of the system. + CCE-86120-3 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-86120-3 + - DISA-STIG-RHEL-08-010400 + - NIST-800-53-IA-2(11) + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - sssd_certificate_verification +- name: XCCDF Value var_sssd_certificate_verification_digest_function # promote to variable + set_fact: + var_sssd_certificate_verification_digest_function: !!str + tags: + - always + +- name: Ensure that "certificate_verification" is not set in /etc/sssd/sssd.conf + ini_file: + path: /etc/sssd/sssd.conf + section: sssd + option: certificate_verification + state: absent + when: '"sssd-common" in ansible_facts.packages' + tags: + - CCE-86120-3 + - DISA-STIG-RHEL-08-010400 + - NIST-800-53-IA-2(11) + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - sssd_certificate_verification + +- name: Ensure that "certificate_verification" is not set in /etc/sssd/conf.d/*.conf + ini_file: + path: /etc/sssd/conf.d/*.conf + section: sssd + option: certificate_verification + state: absent + when: '"sssd-common" in ansible_facts.packages' + tags: + - CCE-86120-3 + - DISA-STIG-RHEL-08-010400 + - NIST-800-53-IA-2(11) + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - sssd_certificate_verification + +- name: Ensure that "certificate_verification" is set + ini_file: + path: /etc/sssd/conf.d/certificate_verification.conf + section: sssd + option: certificate_verification + value: ocsp_dgst = {{ var_sssd_certificate_verification_digest_function }} + state: present + when: '"sssd-common" in ansible_facts.packages' + tags: + - CCE-86120-3 + - DISA-STIG-RHEL-08-010400 + - NIST-800-53-IA-2(11) + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - sssd_certificate_verification + + # Remediation is applicable only in certain platforms +if rpm --quiet -q sssd-common; then + +var_sssd_certificate_verification_digest_function='' + + +MAIN_CONF="/etc/sssd/conf.d/certificate_verification.conf" + +found=false + +# set value in all files if they contain section or key +for f in $(echo -n "$MAIN_CONF /etc/sssd/sssd.conf /etc/sssd/conf.d/*.conf"); do + if [ ! -e "$f" ]; then + continue + fi + + # find key in section and change value + if grep -qzosP "[[:space:]]*\[sssd\]([^\n\[]*\n+)+?[[:space:]]*certificate_verification" "$f"; then + sed -i "s/certificate_verification[^(\n)]*/certificate_verification = ocsp_dgst = $var_sssd_certificate_verification_digest_function/" "$f" + found=true + + # find section and add key = value to it + elif grep -qs "[[:space:]]*\[sssd\]" "$f"; then + sed -i "/[[:space:]]*\[sssd\]/a certificate_verification = ocsp_dgst = $var_sssd_certificate_verification_digest_function" "$f" + found=true + fi +done + +# if section not in any file, append section with key = value to FIRST file in files parameter +if ! $found ; then + file=$(echo "$MAIN_CONF /etc/sssd/sssd.conf /etc/sssd/conf.d/*.conf" | cut -f1 -d ' ') + mkdir -p "$(dirname "$file")" + echo -e "[sssd]\ncertificate_verification = ocsp_dgst = $var_sssd_certificate_verification_digest_function" >> "$file" +fi + +if [ -e "$MAIN_CONF" ]; then + chown root:root "$MAIN_CONF" + chmod 600 "$MAIN_CONF" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable Certmap in SSSD + SSSD should be configured to verify the certificate of the user or group. To set this up + ensure that section like certmap/testing.test/rule_name is setup in +/etc/sssd/sssd.conf. For example + +[certmap/testing.test/rule_name] +matchrule =<SAN>.*EDIPI@mil +maprule = (userCertificate;binary={cert!bin}) +domains = testing.test + + Automatic remediation of this control is not available, since all of the settings in +in the certmap need to be customized. + CCI-000187 + IA-5 (2) (c) + SRG-OS-000068-GPOS-00036 + RHEL-08-020090 + SV-230355r818836_rule + Without mapping the certificate used to authenticate to the user account, the ability to +determine the identity of the individual user or group will not be available for forensic +analysis. + CCE-86060-1 + + + + + + + + + Configure PAM in SSSD Services + SSSD should be configured to run SSSD pam services. +To configure SSSD to known SSH hosts, add pam +to services under the [sssd] section in +/etc/sssd/sssd.conf. For example: +[sssd] +services = sudo, autofs, pam + + 1 + 12 + 15 + 16 + 5 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.03 + DSS06.10 + CCI-001948 + CCI-001953 + CCI-001954 + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.2 + 4.3.3.7.4 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + A.18.1.4 + A.7.1.1 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.2 + A.9.4.3 + IA-2(1) + CM-6(a) + PR.AC-1 + PR.AC-6 + PR.AC-7 + SRG-OS-000375-GPOS-00160 + SRG-OS-000376-GPOS-00161 + SRG-OS-000377-GPOS-00162 + SRG-OS-000107-VMM-000530 + Using an authentication device, such as a CAC or token that is separate from +the information system, ensures that even if the information system is +compromised, that compromise will not affect credentials stored on the +authentication device. + CCE-82446-6 + # Remediation is applicable only in certain platforms +if rpm --quiet -q sssd-common; then + +SSSD_CONF="/etc/sssd/sssd.conf" +SSSD_CONF_DIR="/etc/sssd/conf.d/*.conf" + +if [ ! -f "$SSSD_CONF" ] && [ ! -f "$SSSD_CONF_DIR" ]; then + mkdir /etc/sssd + touch "$SSSD_CONF" +fi + +# Flag to check if there is already services with pam +service_already_exist=false +for f in $SSSD_CONF $SSSD_CONF_DIR; do + if [ ! -e "$f" ]; then + continue + fi + # finds all services entries under [sssd] configuration category, get a unique list so it doesn't add redundant fix + services_list=$( awk '/^\s*\[/{f=0} /^\s*\[sssd\]/{f=1}f' $f | grep -P '^services[ \t]*=' | uniq ) + if [ -z "$services_list" ]; then + continue + fi + + while IFS= read -r services; do + if [[ ! $services =~ "pam" ]]; then + sed -i "s/$services$/&, pam/" $f + fi + # Either pam service was already there or got added now + service_already_exist=true + done <<< "$services_list" + +done + +# If there was no service in [sssd], add it to first config +if [ "$service_already_exist" = false ]; then + for f in $SSSD_CONF $SSSD_CONF_DIR; do + cat << EOF >> "$f" +[sssd] +services = pam +EOF + break + done +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Enable Smartcards in SSSD + SSSD should be configured to authenticate access to the system using smart cards. +To enable smart cards in SSSD, set pam_cert_auth to True under the +[pam] section in /etc/sssd/sssd.conf. For example: +[pam] +pam_cert_auth = True + + +Add or update "pam_sss.so" line in auth section of "/etc/pam.d/system-auth" file to include +"try_cert_auth" or "require_cert_auth" option, like in the following example: + +/etc/pam.d/system-auth:auth [success=done authinfo_unavail=ignore ignore=ignore default=die] pam_sss.so try_cert_auth + +Also add or update "pam_sss.so" line in auth section of "/etc/pam.d/smartcard-auth" file to +include the "allow_missing_name" option, like in the following example: +/etc/pam.d/smartcard-auth:auth sufficient pam_sss.so allow_missing_name + CCI-001954 + CCI-000765 + CCI-000766 + CCI-000767 + CCI-000768 + 0421 + 0422 + 0431 + 0974 + 1173 + 1401 + 1504 + 1505 + 1546 + 1557 + 1558 + 1559 + 1560 + 1561 + SRG-OS-000375-GPOS-00160 + SRG-OS-000105-GPOS-00052 + SRG-OS-000106-GPOS-00053 + SRG-OS-000107-GPOS-00054 + SRG-OS-000108-GPOS-00055 + SRG-OS-000107-VMM-000530 + RHEL-08-020250 + SV-230372r627750_rule + Using an authentication device, such as a CAC or token that is separate from +the information system, ensures that even if the information system is +compromised, that compromise will not affect credentials stored on the +authentication device. + +Multi-Factor Authentication (MFA) solutions that require devices separate from +information systems gaining access include, for example, hardware tokens +providing time-based or challenge-response authenticators and smart cards such +as the U.S. Government Personal Identity Verification card and the DoD Common +Access Card. + + CCE-80909-5 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80909-5 + - DISA-STIG-RHEL-08-020250 + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - sssd_enable_smartcards + +- name: Test for domain group + command: grep '^\s*\[domain\/[^]]*]' /etc/sssd/sssd.conf + register: test_grep_domain + ignore_errors: true + changed_when: false + check_mode: false + when: + - '"sssd-common" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80909-5 + - DISA-STIG-RHEL-08-020250 + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - sssd_enable_smartcards + +- name: Add default domain group (if no domain there) + ini_file: + path: /etc/sssd/sssd.conf + section: '{{ item.section }}' + option: '{{ item.option }}' + value: '{{ item.value }}' + create: true + mode: 384 + with_items: + - section: sssd + option: domains + value: default + - section: domain/default + option: id_provider + value: files + when: + - '"sssd-common" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - test_grep_domain.stdout is defined + - test_grep_domain.stdout | length < 1 + tags: + - CCE-80909-5 + - DISA-STIG-RHEL-08-020250 + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - sssd_enable_smartcards + +- name: Enable Smartcards in SSSD + ini_file: + dest: /etc/sssd/sssd.conf + section: pam + option: pam_cert_auth + value: 'True' + create: true + mode: 384 + when: + - '"sssd-common" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80909-5 + - DISA-STIG-RHEL-08-020250 + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - sssd_enable_smartcards + +- name: Enable Smartcards in SSSD - Check if system relies on authselect + ansible.builtin.stat: + path: /usr/bin/authselect + register: result_authselect_present + when: + - '"sssd-common" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80909-5 + - DISA-STIG-RHEL-08-020250 + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - sssd_enable_smartcards + +- name: Enable Smartcards in SSSD - Remediate using authselect + block: + + - name: Enable Smartcards in SSSD - Check integrity of authselect current profile + ansible.builtin.command: + cmd: authselect check + register: result_authselect_check_cmd + changed_when: false + ignore_errors: true + + - name: Enable Smartcards in SSSD - Informative message based on the authselect + integrity check result + ansible.builtin.assert: + that: + - result_authselect_check_cmd is success + fail_msg: + - authselect integrity check failed. Remediation aborted! + - This remediation could not be applied because an authselect profile was not + selected or the selected profile is not intact. + - It is not recommended to manually edit the PAM files when authselect tool + is available. + - In cases where the default authselect profile does not cover a specific demand, + a custom authselect profile is recommended. + success_msg: + - authselect integrity check passed + + - name: Enable Smartcards in SSSD - Get authselect current features + ansible.builtin.shell: + cmd: authselect current | tail -n+3 | awk '{ print $2 }' + register: result_authselect_features + changed_when: false + when: + - result_authselect_check_cmd is success + + - name: Enable Smartcards in SSSD - Ensure "with-smartcard" feature is enabled using + authselect tool + ansible.builtin.command: + cmd: authselect enable-feature with-smartcard + register: result_authselect_enable_feature_cmd + when: + - result_authselect_check_cmd is success + - result_authselect_features.stdout is not search("with-smartcard") + + - name: Enable Smartcards in SSSD - Ensure authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: + - result_authselect_enable_feature_cmd is not skipped + - result_authselect_enable_feature_cmd is success + when: + - '"sssd-common" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - result_authselect_present.stat.exists + tags: + - CCE-80909-5 + - DISA-STIG-RHEL-08-020250 + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - sssd_enable_smartcards + +- name: Enable Smartcards in SSSD - Remediate by directly editing PAM files + block: + + - name: Enable Smartcards in SSSD - Check if expected PAM module line is present + in /etc/pam.d/smartcard-auth + ansible.builtin.lineinfile: + path: /etc/pam.d/smartcard-auth + regexp: ^\s*auth\s+sufficient\s+pam_sss.so\s*.* + state: absent + check_mode: true + changed_when: false + register: result_pam_line_present + + - name: Enable Smartcards in SSSD - Include or update the PAM module line in /etc/pam.d/smartcard-auth + block: + + - name: Enable Smartcards in SSSD - Check if required PAM module line is present + in /etc/pam.d/smartcard-auth with different control + ansible.builtin.lineinfile: + path: /etc/pam.d/smartcard-auth + regexp: ^\s*auth\s+.*\s+pam_sss.so\s* + state: absent + check_mode: true + changed_when: false + register: result_pam_line_other_control_present + + - name: Enable Smartcards in SSSD - Ensure the correct control for the required + PAM module line in /etc/pam.d/smartcard-auth + ansible.builtin.replace: + dest: /etc/pam.d/smartcard-auth + regexp: ^(\s*auth\s+).*(\bpam_sss.so.*) + replace: \1sufficient \2 + register: result_pam_module_edit + when: + - result_pam_line_other_control_present.found == 1 + + - name: Enable Smartcards in SSSD - Ensure the required PAM module line is included + in /etc/pam.d/smartcard-auth + ansible.builtin.lineinfile: + dest: /etc/pam.d/smartcard-auth + line: auth sufficient pam_sss.so + register: result_pam_module_add + when: + - result_pam_line_other_control_present.found == 0 or result_pam_line_other_control_present.found + > 1 + + - name: Enable Smartcards in SSSD - Ensure authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: | + result_authselect_present is defined and result_authselect_present.stat.exists and ((result_pam_module_add is defined and result_pam_module_add.changed) or (result_pam_module_edit is defined and result_pam_module_edit.changed)) + when: + - result_pam_line_present.found is defined + - result_pam_line_present.found == 0 + + - name: Enable Smartcards in SSSD - Check if the required PAM module option is present + in /etc/pam.d/smartcard-auth + ansible.builtin.lineinfile: + path: /etc/pam.d/smartcard-auth + regexp: ^\s*auth\s+sufficient\s+pam_sss.so\s*.*\sallow_missing_name\b + state: absent + check_mode: true + changed_when: false + register: result_pam_module_allow_missing_name_option_present + + - name: Enable Smartcards in SSSD - Ensure the "allow_missing_name" PAM option for + "pam_sss.so" is included in /etc/pam.d/smartcard-auth + ansible.builtin.lineinfile: + path: /etc/pam.d/smartcard-auth + backrefs: true + regexp: ^(\s*auth\s+sufficient\s+pam_sss.so.*) + line: \1 allow_missing_name + state: present + register: result_pam_allow_missing_name_add + when: + - result_pam_module_allow_missing_name_option_present.found == 0 + + - name: Enable Smartcards in SSSD - Check if expected PAM module line is present + in /etc/pam.d/system-auth + ansible.builtin.lineinfile: + path: /etc/pam.d/system-auth + regexp: ^\s*auth\s+\[success=done authinfo_unavail=ignore ignore=ignore default=die\]\s+pam_sss.so\s*.* + state: absent + check_mode: true + changed_when: false + register: result_pam_line_present + + - name: Enable Smartcards in SSSD - Include or update the PAM module line in /etc/pam.d/system-auth + block: + + - name: Enable Smartcards in SSSD - Check if required PAM module line is present + in /etc/pam.d/system-auth with different control + ansible.builtin.lineinfile: + path: /etc/pam.d/system-auth + regexp: ^\s*auth\s+.*\s+pam_sss.so\s* + state: absent + check_mode: true + changed_when: false + register: result_pam_line_other_control_present + + - name: Enable Smartcards in SSSD - Ensure the correct control for the required + PAM module line in /etc/pam.d/system-auth + ansible.builtin.replace: + dest: /etc/pam.d/system-auth + regexp: ^(\s*auth\s+).*(\bpam_sss.so.*) + replace: \1\[success=done authinfo_unavail=ignore ignore=ignore default=die\] + \2 + register: result_pam_module_edit + when: + - result_pam_line_other_control_present.found == 1 + + - name: Enable Smartcards in SSSD - Ensure the required PAM module line is included + in /etc/pam.d/system-auth + ansible.builtin.lineinfile: + dest: /etc/pam.d/system-auth + line: auth \[success=done authinfo_unavail=ignore ignore=ignore default=die\] pam_sss.so + register: result_pam_module_add + when: + - result_pam_line_other_control_present.found == 0 or result_pam_line_other_control_present.found + > 1 + + - name: Enable Smartcards in SSSD - Ensure authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: | + result_authselect_present is defined and result_authselect_present.stat.exists and ((result_pam_module_add is defined and result_pam_module_add.changed) or (result_pam_module_edit is defined and result_pam_module_edit.changed)) + when: + - result_pam_line_present.found is defined + - result_pam_line_present.found == 0 + + - name: Enable Smartcards in SSSD - Check if the required PAM module option is present + in /etc/pam.d/system-auth + ansible.builtin.lineinfile: + path: /etc/pam.d/system-auth + regexp: ^\s*auth\s+\[success=done authinfo_unavail=ignore ignore=ignore default=die\]\s+pam_sss.so\s*.*\stry_cert_auth\b + state: absent + check_mode: true + changed_when: false + register: result_pam_module_try_cert_auth_option_present + + - name: Enable Smartcards in SSSD - Ensure the "try_cert_auth" PAM option for "pam_sss.so" + is included in /etc/pam.d/system-auth + ansible.builtin.lineinfile: + path: /etc/pam.d/system-auth + backrefs: true + regexp: ^(\s*auth\s+\[success=done authinfo_unavail=ignore ignore=ignore default=die\]\s+pam_sss.so.*) + line: \1 try_cert_auth + state: present + register: result_pam_try_cert_auth_add + when: + - result_pam_module_try_cert_auth_option_present.found == 0 + when: + - '"sssd-common" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - not result_authselect_present.stat.exists + tags: + - CCE-80909-5 + - DISA-STIG-RHEL-08-020250 + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - sssd_enable_smartcards + + # Remediation is applicable only in certain platforms +if rpm --quiet -q sssd-common && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +found=false + +# set value in all files if they contain section or key +for f in $(echo -n "/etc/sssd/sssd.conf"); do + if [ ! -e "$f" ]; then + continue + fi + + # find key in section and change value + if grep -qzosP "[[:space:]]*\[pam\]([^\n\[]*\n+)+?[[:space:]]*pam_cert_auth" "$f"; then + sed -i "s/pam_cert_auth[^(\n)]*/pam_cert_auth = True/" "$f" + found=true + + # find section and add key = value to it + elif grep -qs "[[:space:]]*\[pam\]" "$f"; then + sed -i "/[[:space:]]*\[pam\]/a pam_cert_auth = True" "$f" + found=true + fi +done + +# if section not in any file, append section with key = value to FIRST file in files parameter +if ! $found ; then + file=$(echo "/etc/sssd/sssd.conf" | cut -f1 -d ' ') + mkdir -p "$(dirname "$file")" + echo -e "[pam]\npam_cert_auth = True" >> "$file" +fi + + +if [ -f /usr/bin/authselect ]; then + if authselect check; then + if ! authselect check; then + echo " + authselect integrity check failed. Remediation aborted! + This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact. + It is not recommended to manually edit the PAM files when authselect tool is available. + In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended." + exit 1 + fi + authselect enable-feature with-smartcard + + authselect apply-changes -b + fi +else + if ! grep -qP '^\s*auth\s+'"sufficient"'\s+pam_sss.so\s*.*' "/etc/pam.d/smartcard-auth"; then + # Line matching group + control + module was not found. Check group + module. + if [ "$(grep -cP '^\s*auth\s+.*\s+pam_sss.so\s*' "/etc/pam.d/smartcard-auth")" -eq 1 ]; then + # The control is updated only if one single line matches. + sed -i -E --follow-symlinks 's/^(\s*auth\s+).*(\bpam_sss.so.*)/\1'"sufficient"' \2/' "/etc/pam.d/smartcard-auth" + else + echo 'auth '"sufficient"' pam_sss.so' >> "/etc/pam.d/smartcard-auth" + fi + fi + # Check the option + if ! grep -qP '^\s*auth\s+'"sufficient"'\s+pam_sss.so\s*.*\sallow_missing_name\b' "/etc/pam.d/smartcard-auth"; then + sed -i -E --follow-symlinks '/\s*auth\s+'"sufficient"'\s+pam_sss.so.*/ s/$/ allow_missing_name/' "/etc/pam.d/smartcard-auth" + fi + if ! grep -qP '^\s*auth\s+'"\[success=done authinfo_unavail=ignore ignore=ignore default=die\]"'\s+pam_sss.so\s*.*' "/etc/pam.d/system-auth"; then + # Line matching group + control + module was not found. Check group + module. + if [ "$(grep -cP '^\s*auth\s+.*\s+pam_sss.so\s*' "/etc/pam.d/system-auth")" -eq 1 ]; then + # The control is updated only if one single line matches. + sed -i -E --follow-symlinks 's/^(\s*auth\s+).*(\bpam_sss.so.*)/\1'"\[success=done authinfo_unavail=ignore ignore=ignore default=die\]"' \2/' "/etc/pam.d/system-auth" + else + echo 'auth '"\[success=done authinfo_unavail=ignore ignore=ignore default=die\]"' pam_sss.so' >> "/etc/pam.d/system-auth" + fi + fi + # Check the option + if ! grep -qP '^\s*auth\s+'"\[success=done authinfo_unavail=ignore ignore=ignore default=die\]"'\s+pam_sss.so\s*.*\stry_cert_auth\b' "/etc/pam.d/system-auth"; then + sed -i -E --follow-symlinks '/\s*auth\s+'"\[success=done authinfo_unavail=ignore ignore=ignore default=die\]"'\s+pam_sss.so.*/ s/$/ try_cert_auth/' "/etc/pam.d/system-auth" + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure SSSD's Memory Cache to Expire + SSSD's memory cache should be configured to set to expire records after + seconds. +To configure SSSD to expire memory cache, set memcache_timeout to + under the +[nss] section in /etc/sssd/sssd.conf. + +For example: +[nss] +memcache_timeout = + + 1 + 12 + 15 + 16 + 5 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.03 + DSS06.10 + CCI-002007 + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.2 + 4.3.3.7.4 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + A.18.1.4 + A.7.1.1 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.2 + A.9.4.3 + CM-6(a) + IA-5(13) + PR.AC-1 + PR.AC-6 + PR.AC-7 + SRG-OS-000383-GPOS-00166 + SRG-OS-000383-VMM-001570 + If cached authentication information is out-of-date, the validity of the +authentication information may be questionable. + + CCE-80910-3 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-80910-3 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(13) + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - sssd_memcache_timeout + - unknown_strategy +- name: XCCDF Value var_sssd_memcache_timeout # promote to variable + set_fact: + var_sssd_memcache_timeout: !!str + tags: + - always + +- name: Test for domain group + command: grep '\s*\[domain\/[^]]*]' /etc/sssd/sssd.conf + register: test_grep_domain + ignore_errors: true + changed_when: false + check_mode: false + when: + - '"sssd-common" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80910-3 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(13) + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - sssd_memcache_timeout + - unknown_strategy + +- name: Add default domain group (if no domain there) + ini_file: + path: /etc/sssd/sssd.conf + section: '{{ item.section }}' + option: '{{ item.option }}' + value: '{{ item.value }}' + create: true + mode: 384 + with_items: + - section: sssd + option: domains + value: default + - section: domain/default + option: id_provider + value: files + when: + - '"sssd-common" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - test_grep_domain.stdout is defined + - test_grep_domain.stdout | length < 1 + tags: + - CCE-80910-3 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(13) + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - sssd_memcache_timeout + - unknown_strategy + +- name: Configure SSSD's Memory Cache to Expire + ini_file: + dest: /etc/sssd/sssd.conf + section: nss + option: memcache_timeout + value: '{{ var_sssd_memcache_timeout }}' + create: true + mode: 384 + when: + - '"sssd-common" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-80910-3 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(13) + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - sssd_memcache_timeout + - unknown_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q sssd-common && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +var_sssd_memcache_timeout='' + + +found=false + +# set value in all files if they contain section or key +for f in $(echo -n "/etc/sssd/sssd.conf"); do + if [ ! -e "$f" ]; then + continue + fi + + # find key in section and change value + if grep -qzosP "[[:space:]]*\[nss\]([^\n\[]*\n+)+?[[:space:]]*memcache_timeout" "$f"; then + sed -i "s/memcache_timeout[^(\n)]*/memcache_timeout = $var_sssd_memcache_timeout/" "$f" + found=true + + # find section and add key = value to it + elif grep -qs "[[:space:]]*\[nss\]" "$f"; then + sed -i "/[[:space:]]*\[nss\]/a memcache_timeout = $var_sssd_memcache_timeout" "$f" + found=true + fi +done + +# if section not in any file, append section with key = value to FIRST file in files parameter +if ! $found ; then + file=$(echo "/etc/sssd/sssd.conf" | cut -f1 -d ' ') + mkdir -p "$(dirname "$file")" + echo -e "[nss]\nmemcache_timeout = $var_sssd_memcache_timeout" >> "$file" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Configure SSSD to Expire Offline Credentials + SSSD should be configured to expire offline credentials after 1 day. + +Check if SSSD allows cached authentications with the following command: + +$ sudo grep cache_credentials /etc/sssd/sssd.conf +cache_credentials = true + +If "cache_credentials" is set to "false" or is missing no further checks are required. + +To configure SSSD to expire offline credentials, set +offline_credentials_expiration to 1 under the [pam] +section in /etc/sssd/sssd.conf. For example: +[pam] +offline_credentials_expiration = 1 + + 1 + 12 + 15 + 16 + 5 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.03 + DSS06.10 + CCI-002007 + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.2 + 4.3.3.7.4 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + A.18.1.4 + A.7.1.1 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.2 + A.9.4.3 + CM-6(a) + IA-5(13) + PR.AC-1 + PR.AC-6 + PR.AC-7 + SRG-OS-000383-GPOS-00166 + SRG-OS-000383-VMM-001570 + RHEL-08-020290 + SV-230376r627750_rule + If cached authentication information is out-of-date, the validity of the +authentication information may be questionable. + + CCE-82460-7 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-82460-7 + - DISA-STIG-RHEL-08-020290 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(13) + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - sssd_offline_cred_expiration + +- name: Test for domain group + command: grep '\s*\[domain\/[^]]*]' /etc/sssd/sssd.conf + register: test_grep_domain + ignore_errors: true + changed_when: false + check_mode: false + when: + - '"sssd-common" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82460-7 + - DISA-STIG-RHEL-08-020290 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(13) + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - sssd_offline_cred_expiration + +- name: Add default domain group (if no domain there) + ini_file: + path: /etc/sssd/sssd.conf + section: '{{ item.section }}' + option: '{{ item.option }}' + value: '{{ item.value }}' + create: true + mode: 384 + with_items: + - section: sssd + option: domains + value: default + - section: domain/default + option: id_provider + value: files + when: + - '"sssd-common" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - test_grep_domain.stdout is defined + - test_grep_domain.stdout | length < 1 + tags: + - CCE-82460-7 + - DISA-STIG-RHEL-08-020290 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(13) + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - sssd_offline_cred_expiration + +- name: Configure SSD to Expire Offline Credentials + ini_file: + dest: /etc/sssd/sssd.conf + section: pam + option: offline_credentials_expiration + value: 1 + create: true + mode: 384 + when: + - '"sssd-common" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82460-7 + - DISA-STIG-RHEL-08-020290 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(13) + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - sssd_offline_cred_expiration + + # Remediation is applicable only in certain platforms +if rpm --quiet -q sssd-common && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +found=false + +# set value in all files if they contain section or key +for f in $(echo -n "/etc/sssd/sssd.conf"); do + if [ ! -e "$f" ]; then + continue + fi + + # find key in section and change value + if grep -qzosP "[[:space:]]*\[pam\]([^\n\[]*\n+)+?[[:space:]]*offline_credentials_expiration" "$f"; then + sed -i "s/offline_credentials_expiration[^(\n)]*/offline_credentials_expiration = 1/" "$f" + found=true + + # find section and add key = value to it + elif grep -qs "[[:space:]]*\[pam\]" "$f"; then + sed -i "/[[:space:]]*\[pam\]/a offline_credentials_expiration = 1" "$f" + found=true + fi +done + +# if section not in any file, append section with key = value to FIRST file in files parameter +if ! $found ; then + file=$(echo "/etc/sssd/sssd.conf" | cut -f1 -d ' ') + mkdir -p "$(dirname "$file")" + echo -e "[pam]\noffline_credentials_expiration = 1" >> "$file" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure SSSD to run as user sssd + SSSD processes should be configured to run as user sssd, not root. + FMT_SMF_EXT.1 + SRG-OS-000480-GPOS-00227 + To minimize privileges of SSSD processes, they are configured to +run as non-root user. + CCE-82072-0 + # Remediation is applicable only in certain platforms +if rpm --quiet -q sssd-common; then + +MAIN_CONF="/etc/sssd/conf.d/ospp.conf" + +found=false + +# set value in all files if they contain section or key +for f in $(echo -n "$MAIN_CONF /etc/sssd/sssd.conf /etc/sssd/conf.d/*.conf"); do + if [ ! -e "$f" ]; then + continue + fi + + # find key in section and change value + if grep -qzosP "[[:space:]]*\[sssd\]([^\n\[]*\n+)+?[[:space:]]*user" "$f"; then + sed -i "s/user[^(\n)]*/user = sssd/" "$f" + found=true + + # find section and add key = value to it + elif grep -qs "[[:space:]]*\[sssd\]" "$f"; then + sed -i "/[[:space:]]*\[sssd\]/a user = sssd" "$f" + found=true + fi +done + +# if section not in any file, append section with key = value to FIRST file in files parameter +if ! $found ; then + file=$(echo "$MAIN_CONF /etc/sssd/sssd.conf /etc/sssd/conf.d/*.conf" | cut -f1 -d ' ') + mkdir -p "$(dirname "$file")" + echo -e "[sssd]\nuser = sssd" >> "$file" +fi + +if [ -e "$MAIN_CONF" ]; then + chown root:root "$MAIN_CONF" + chmod 600 "$MAIN_CONF" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure SSSD to Expire SSH Known Hosts + SSSD should be configured to expire keys from known SSH hosts after + seconds. +To configure SSSD to known SSH hosts, set ssh_known_hosts_timeout +to under the +[ssh] section in /etc/sssd/sssd.conf. For example: +[ssh] +ssh_known_hosts_timeout = + + 1 + 12 + 15 + 16 + 5 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.03 + DSS06.10 + CCI-002007 + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.2 + 4.3.3.7.4 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + A.18.1.4 + A.7.1.1 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.2 + A.9.4.3 + CM-6(a) + IA-5(13) + PR.AC-1 + PR.AC-6 + PR.AC-7 + SRG-OS-000383-GPOS-00166 + If cached authentication information is out-of-date, the validity of the +authentication information may be questionable. + + CCE-82442-5 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-82442-5 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(13) + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - sssd_ssh_known_hosts_timeout + - unknown_strategy +- name: XCCDF Value var_sssd_ssh_known_hosts_timeout # promote to variable + set_fact: + var_sssd_ssh_known_hosts_timeout: !!str + tags: + - always + +- name: Test for domain group + command: grep '\s*\[domain\/[^]]*]' /etc/sssd/sssd.conf + register: test_grep_domain + ignore_errors: true + changed_when: false + check_mode: false + when: + - '"sssd-common" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82442-5 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(13) + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - sssd_ssh_known_hosts_timeout + - unknown_strategy + +- name: Add default domain group (if no domain there) + ini_file: + path: /etc/sssd/sssd.conf + section: '{{ item.section }}' + option: '{{ item.option }}' + value: '{{ item.value }}' + create: true + mode: 384 + with_items: + - section: sssd + option: domains + value: default + - section: domain/default + option: id_provider + value: files + when: + - '"sssd-common" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - test_grep_domain.stdout is defined + - test_grep_domain.stdout | length < 1 + tags: + - CCE-82442-5 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(13) + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - sssd_ssh_known_hosts_timeout + - unknown_strategy + +- name: Configure SSSD to Expire SSH Known Hosts + ini_file: + dest: /etc/sssd/sssd.conf + section: ssh + option: ssh_known_hosts_timeout + value: '{{ var_sssd_ssh_known_hosts_timeout }}' + create: true + mode: 384 + when: + - '"sssd-common" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82442-5 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(13) + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - sssd_ssh_known_hosts_timeout + - unknown_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q sssd-common && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +var_sssd_ssh_known_hosts_timeout='' + + +found=false + +# set value in all files if they contain section or key +for f in $(echo -n "/etc/sssd/sssd.conf"); do + if [ ! -e "$f" ]; then + continue + fi + + # find key in section and change value + if grep -qzosP "[[:space:]]*\[ssh\]([^\n\[]*\n+)+?[[:space:]]*ssh_known_hosts_timeout" "$f"; then + sed -i "s/ssh_known_hosts_timeout[^(\n)]*/ssh_known_hosts_timeout = $var_sssd_ssh_known_hosts_timeout/" "$f" + found=true + + # find section and add key = value to it + elif grep -qs "[[:space:]]*\[ssh\]" "$f"; then + sed -i "/[[:space:]]*\[ssh\]/a ssh_known_hosts_timeout = $var_sssd_ssh_known_hosts_timeout" "$f" + found=true + fi +done + +# if section not in any file, append section with key = value to FIRST file in files parameter +if ! $found ; then + file=$(echo "/etc/sssd/sssd.conf" | cut -f1 -d ' ') + mkdir -p "$(dirname "$file")" + echo -e "[ssh]\nssh_known_hosts_timeout = $var_sssd_ssh_known_hosts_timeout" >> "$file" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + System Security Services Daemon (SSSD) - LDAP + The System Security Services Daemon (SSSD) is a system daemon that provides access +to different identity and authentication providers such as Red Hat's IdM, Microsoft's AD, +openLDAP, MIT Kerberos, etc. It uses a common framework that can provide caching and offline +support to systems utilizing SSSD. SSSD using caching to reduce load on authentication +servers permit offline authentication as well as store extended user data. + +SSSD can support many backends including LDAP. The sssd-ldap backend +allows SSSD to fetch identity information from an LDAP server. + + + SSSD LDAP Backend Client CA Certificate Location + Path of a directory that contains Certificate Authority certificates. + /etc/openldap/cacerts + + + Configure SSSD LDAP Backend Client CA Certificate + Configure SSSD to implement cryptography to protect the +integrity of LDAP remote access sessions. By setting +the ldap_tls_cacert option in /etc/sssd/sssd.conf +to point to the path for the X.509 certificates used for peer authentication. +ldap_tls_cacert /path/to/tls/ca.cert + CCI-001453 + SC-12(3) + CM-6(a) + SRG-OS-000250-GPOS-00093 + Without cryptographic integrity protections, information can be altered by +unauthorized users without detection. + +Cryptographic mechanisms used for +protecting the integrity of information include, for example, signed hash +functions using asymmetric cryptography enabling distribution of the public key +to verify the hash information while maintaining the confidentiality of the key +used to generate the hash. + + CCE-82438-3 + + + + + + Configure SSSD LDAP Backend Client CA Certificate Location + Configure SSSD to implement cryptography to protect the +integrity of LDAP remote access sessions. By setting +the ldap_tls_cacertdir option in /etc/sssd/sssd.conf +to point to the path for the X.509 certificates used for peer authentication. +ldap_tls_cacertdir /path/to/tls/cacert + CCI-001453 + SC-12(3) + CM-6(a) + SRG-OS-000250-GPOS-00093 + Without cryptographic integrity protections, information can be altered by +unauthorized users without detection. + +Cryptographic mechanisms used for +protecting the integrity of information include, for example, signed hash +functions using asymmetric cryptography enabling distribution of the public key +to verify the hash information while maintaining the confidentiality of the key +used to generate the hash. + + CCE-82456-5 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-82456-5 + - NIST-800-53-CM-6(a) + - NIST-800-53-SC-12(3) + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - sssd_ldap_configure_tls_ca_dir + - unknown_strategy +- name: XCCDF Value var_sssd_ldap_tls_ca_dir # promote to variable + set_fact: + var_sssd_ldap_tls_ca_dir: !!str + tags: + - always + +- name: Test for id_provider different than Active Directory (ad) + command: grep -qzosP '[[:space:]]*\[domain\/[^]]*]([^(\n)]*(\n)+)+?[[:space:]]*id_provider[[:space:]]*=[[:space:]]*((?i)ad)[[:space:]]*$' + /etc/sssd/sssd.conf + register: test_id_provider + ignore_errors: true + changed_when: false + check_mode: false + when: + - '"sssd-common" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82456-5 + - NIST-800-53-CM-6(a) + - NIST-800-53-SC-12(3) + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - sssd_ldap_configure_tls_ca_dir + - unknown_strategy + +- name: Test for domain group + command: grep '\s*\[domain\/[^]]*]' /etc/sssd/sssd.conf + register: test_grep_domain + ignore_errors: true + changed_when: false + check_mode: false + when: + - '"sssd-common" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82456-5 + - NIST-800-53-CM-6(a) + - NIST-800-53-SC-12(3) + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - sssd_ldap_configure_tls_ca_dir + - unknown_strategy + +- name: Add default domain group and set ldap_tls_cacertdir in sssd configuration + (if no domain there) + ini_file: + path: /etc/sssd/sssd.conf + section: '{{ item.section }}' + option: '{{ item.option }}' + value: '{{ item.value }}' + with_items: + - section: sssd + option: domains + value: default + - section: domain/default + option: ldap_tls_cacertdir + value: '{{ var_sssd_ldap_tls_ca_dir }}' + when: + - '"sssd-common" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - test_grep_domain.stdout is defined + - test_grep_domain.stdout | length < 1 + - test_id_provider.stdout is defined + - test_id_provider.stdout | length < 1 + tags: + - CCE-82456-5 + - NIST-800-53-CM-6(a) + - NIST-800-53-SC-12(3) + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - sssd_ldap_configure_tls_ca_dir + - unknown_strategy + +- name: Set ldap_tls_cacertdir in sssd configuration + ini_file: + path: /etc/sssd/sssd.conf + section: '{{ test_grep_domain.stdout | regex_replace(''\[(.*)\]'',''\1'') }}' + option: ldap_tls_cacertdir + value: '{{ var_sssd_ldap_tls_ca_dir }}' + when: + - '"sssd-common" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - test_grep_domain.stdout is defined + - test_grep_domain.stdout | length > 0 + - test_id_provider.stdout is defined + - test_id_provider.stdout | length < 1 + tags: + - CCE-82456-5 + - NIST-800-53-CM-6(a) + - NIST-800-53-SC-12(3) + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - sssd_ldap_configure_tls_ca_dir + - unknown_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q sssd-common; then + +var_sssd_ldap_tls_ca_dir='' + + +SSSD_CONF="/etc/sssd/sssd.conf" +LDAP_REGEX='[[:space:]]*\[domain\/[^]]*]([^(\n)]*(\n)+)+?[[:space:]]*ldap_tls_cacertdir' +AD_REGEX='[[:space:]]*\[domain\/[^]]*]([^(\n)]*(\n)+)+?[[:space:]]*id_provider[[:space:]]*=[[:space:]]*((?i)ad)[[:space:]]*$' +DOMAIN_REGEX="[[:space:]]*\[domain\/[^]]*]" + +# Check if id_provider is not set to ad (Active Directory) which makes start_tls not applicable, note the -v option to invert the grep. +# Try to find [domain/..] and ldap_tls_cacertdir in sssd.conf, if it exists, set to '$var_sssd_ldap_tls_ca_dir' +# if ldap_tls_cacertdir isn't here, add it +# if [domain/..] doesn't exist, add it here for default domain +if grep -qvzosP $AD_REGEX $SSSD_CONF; then + if grep -qzosP $LDAP_REGEX $SSSD_CONF; then + + sed -i "s#ldap_tls_cacertdir[^(\n)]*#ldap_tls_cacertdir = $var_sssd_ldap_tls_ca_dir#" $SSSD_CONF + elif grep -qs $DOMAIN_REGEX $SSSD_CONF; then + sed -i "/$DOMAIN_REGEX/a ldap_tls_cacertdir = $var_sssd_ldap_tls_ca_dir" $SSSD_CONF + else + if test -f "$SSSD_CONF"; then + echo -e "[domain/default]\nldap_tls_cacertdir = $var_sssd_ldap_tls_ca_dir" >> $SSSD_CONF + else + echo "Config file '$SSSD_CONF' doesnt exist, not remediating, assuming non-applicability." >&2 + fi + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Configure SSSD LDAP Backend Client to Demand a Valid Certificate from the Server + Configure SSSD to demand a valid certificate from the server to +protect the integrity of LDAP remote access sessions by setting +the ldap_tls_reqcert option in /etc/sssd/sssd.conf +to demand. + CCI-001453 + SC-12(3) + CM-6(a) + SRG-OS-000250-GPOS-00093 + Without a valid certificate presented to the LDAP client backend, the identity of a +server can be forged compromising LDAP remote access sessions. + + CCE-84062-9 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-84062-9 + - NIST-800-53-CM-6(a) + - NIST-800-53-SC-12(3) + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - sssd_ldap_configure_tls_reqcert + - unknown_strategy + +- name: Test for id_provider different than Active Directory (ad) + command: grep -qzosP '[[:space:]]*\[domain\/[^]]*]([^(\n)]*(\n)+)+?[[:space:]]*id_provider[[:space:]]*=[[:space:]]*((?i)ad)[[:space:]]*$' + /etc/sssd/sssd.conf + register: test_id_provider + ignore_errors: true + changed_when: false + check_mode: false + when: + - '"sssd-common" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84062-9 + - NIST-800-53-CM-6(a) + - NIST-800-53-SC-12(3) + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - sssd_ldap_configure_tls_reqcert + - unknown_strategy + +- name: Test for domain group + command: grep '\s*\[domain\/[^]]*]' /etc/sssd/sssd.conf + register: test_grep_domain + ignore_errors: true + changed_when: false + check_mode: false + when: + - '"sssd-common" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84062-9 + - NIST-800-53-CM-6(a) + - NIST-800-53-SC-12(3) + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - sssd_ldap_configure_tls_reqcert + - unknown_strategy + +- name: Add default domain group and set ldap_tls_reqcert in sssd configuration (if + no domain there) + ini_file: + path: /etc/sssd/sssd.conf + section: '{{ item.section }}' + option: '{{ item.option }}' + value: '{{ item.value }}' + with_items: + - section: sssd + option: domains + value: default + - section: domain/default + option: ldap_tls_reqcert + value: demand + when: + - '"sssd-common" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - test_grep_domain.stdout is defined + - test_grep_domain.stdout | length < 1 + - test_id_provider.stdout is defined + - test_id_provider.stdout | length < 1 + tags: + - CCE-84062-9 + - NIST-800-53-CM-6(a) + - NIST-800-53-SC-12(3) + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - sssd_ldap_configure_tls_reqcert + - unknown_strategy + +- name: Set ldap_tls_reqcert in sssd configuration + ini_file: + path: /etc/sssd/sssd.conf + section: '{{ test_grep_domain.stdout | regex_replace(''\[(.*)\]'',''\1'') }}' + option: ldap_tls_reqcert + value: demand + when: + - '"sssd-common" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - test_grep_domain.stdout is defined + - test_grep_domain.stdout | length > 0 + - test_id_provider.stdout is defined + - test_id_provider.stdout | length < 1 + tags: + - CCE-84062-9 + - NIST-800-53-CM-6(a) + - NIST-800-53-SC-12(3) + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - sssd_ldap_configure_tls_reqcert + - unknown_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q sssd-common; then + +SSSD_CONF="/etc/sssd/sssd.conf" +LDAP_REGEX='[[:space:]]*\[domain\/[^]]*]([^(\n)]*(\n)+)+?[[:space:]]*ldap_tls_reqcert' +AD_REGEX='[[:space:]]*\[domain\/[^]]*]([^(\n)]*(\n)+)+?[[:space:]]*id_provider[[:space:]]*=[[:space:]]*((?i)ad)[[:space:]]*$' +DOMAIN_REGEX="[[:space:]]*\[domain\/[^]]*]" + +# Check if id_provider is not set to ad (Active Directory) which makes start_tls not applicable, note the -v option to invert the grep. +# Try to find [domain/..] and ldap_tls_reqcert in sssd.conf, if it exists, set to 'demand' +# if ldap_tls_reqcert isn't here, add it +# if [domain/..] doesn't exist, add it here for default domain +if grep -qvzosP $AD_REGEX $SSSD_CONF; then + if grep -qzosP $LDAP_REGEX $SSSD_CONF; then + + sed -i "s#ldap_tls_reqcert[^(\n)]*#ldap_tls_reqcert = demand#" $SSSD_CONF + elif grep -qs $DOMAIN_REGEX $SSSD_CONF; then + sed -i "/$DOMAIN_REGEX/a ldap_tls_reqcert = demand" $SSSD_CONF + else + if test -f "$SSSD_CONF"; then + echo -e "[domain/default]\nldap_tls_reqcert = demand" >> $SSSD_CONF + else + echo "Config file '$SSSD_CONF' doesnt exist, not remediating, assuming non-applicability." >&2 + fi + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure SSSD LDAP Backend to Use TLS For All Transactions + The LDAP client should be configured to implement TLS for the integrity +of all remote LDAP authentication sessions. If the id_provider is +set to ldap or ipa in /etc/sssd/sssd.conf or any of the +/etc/sssd/sssd.conf.d configuration files, ldap_id_use_start_tls +must be set to true. + +To check if LDAP is configured to use TLS when id_provider is +set to ldap or ipa, use the following command: +$ sudo grep -i ldap_id_use_start_tls /etc/sssd/sssd.conf + 11 + 12 + 14 + 15 + 3 + 8 + 9 + APO13.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.04 + DSS05.02 + DSS05.03 + DSS05.05 + DSS06.06 + CCI-001453 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.6.2.1 + A.6.2.2 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.AC-3 + PR.IP-1 + PR.PT-3 + PR.PT-4 + SRG-OS-000250-GPOS-00093 + Without cryptographic integrity protections, information can be +altered by unauthorized users without detection. The ssl directive specifies +whether to use TLS or not. If not specified it will default to no. +It should be set to start_tls rather than doing LDAP over SSL. + + CCE-82437-5 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-82437-5 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - high_severity + - low_complexity + - medium_disruption + - no_reboot_needed + - sssd_ldap_start_tls + - unknown_strategy + +- name: Test for id_provider different than Active Directory (ad) + command: grep -qzosP '[[:space:]]*\[domain\/[^]]*]([^(\n)]*(\n)+)+?[[:space:]]*id_provider[[:space:]]*=[[:space:]]*((?i)ad)[[:space:]]*$' + /etc/sssd/sssd.conf + register: test_id_provider + ignore_errors: true + changed_when: false + check_mode: false + when: + - '"sssd-common" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82437-5 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - high_severity + - low_complexity + - medium_disruption + - no_reboot_needed + - sssd_ldap_start_tls + - unknown_strategy + +- name: Test for domain group + command: grep '\s*\[domain\/[^]]*]' /etc/sssd/sssd.conf + register: test_grep_domain + ignore_errors: true + changed_when: false + check_mode: false + when: + - '"sssd-common" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82437-5 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - high_severity + - low_complexity + - medium_disruption + - no_reboot_needed + - sssd_ldap_start_tls + - unknown_strategy + +- name: Add default domain group and set ldap_id_use_start_tls in sssd configuration + (if no domain there) + ini_file: + path: /etc/sssd/sssd.conf + section: '{{ item.section }}' + option: '{{ item.option }}' + value: '{{ item.value }}' + with_items: + - section: sssd + option: domains + value: default + - section: domain/default + option: ldap_id_use_start_tls + value: 'true' + when: + - '"sssd-common" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - test_grep_domain.stdout is defined + - test_grep_domain.stdout | length < 1 + - test_id_provider.stdout is defined + - test_id_provider.stdout | length < 1 + tags: + - CCE-82437-5 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - high_severity + - low_complexity + - medium_disruption + - no_reboot_needed + - sssd_ldap_start_tls + - unknown_strategy + +- name: Set ldap_id_use_start_tls in sssd configuration + ini_file: + path: /etc/sssd/sssd.conf + section: '{{ test_grep_domain.stdout | regex_replace(''\[(.*)\]'',''\1'') }}' + option: ldap_id_use_start_tls + value: 'true' + when: + - '"sssd-common" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - test_grep_domain.stdout is defined + - test_grep_domain.stdout | length > 0 + - test_id_provider.stdout is defined + - test_id_provider.stdout | length < 1 + tags: + - CCE-82437-5 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - high_severity + - low_complexity + - medium_disruption + - no_reboot_needed + - sssd_ldap_start_tls + - unknown_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q sssd-common; then + +SSSD_CONF="/etc/sssd/sssd.conf" +LDAP_REGEX='[[:space:]]*\[domain\/[^]]*]([^(\n)]*(\n)+)+?[[:space:]]*ldap_id_use_start_tls' +AD_REGEX='[[:space:]]*\[domain\/[^]]*]([^(\n)]*(\n)+)+?[[:space:]]*id_provider[[:space:]]*=[[:space:]]*((?i)ad)[[:space:]]*$' +DOMAIN_REGEX="[[:space:]]*\[domain\/[^]]*]" + +# Check if id_provider is not set to ad (Active Directory) which makes start_tls not applicable, note the -v option to invert the grep. +# Try to find [domain/..] and ldap_id_use_start_tls in sssd.conf, if it exists, set to 'true' +# if ldap_id_use_start_tls isn't here, add it +# if [domain/..] doesn't exist, add it here for default domain +if grep -qvzosP $AD_REGEX $SSSD_CONF; then + if grep -qzosP $LDAP_REGEX $SSSD_CONF; then + + sed -i "s#ldap_id_use_start_tls[^(\n)]*#ldap_id_use_start_tls = true#" $SSSD_CONF + elif grep -qs $DOMAIN_REGEX $SSSD_CONF; then + sed -i "/$DOMAIN_REGEX/a ldap_id_use_start_tls = true" $SSSD_CONF + else + if test -f "$SSSD_CONF"; then + echo -e "[domain/default]\nldap_id_use_start_tls = true" >> $SSSD_CONF + else + echo "Config file '$SSSD_CONF' doesnt exist, not remediating, assuming non-applicability." >&2 + fi + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + + USBGuard daemon + The USBGuard daemon enforces the USB device authorization policy for all USB devices. + + + Install usbguard Package + +The usbguard package can be installed with the following command: + +$ sudo yum install usbguard + CCI-001958 + 1418 + CM-8(3) + IA-3 + SRG-OS-000378-GPOS-00163 + RHEL-08-040139 + SV-244547r743890_rule + usbguard is a software framework that helps to protect +against rogue USB devices by implementing basic whitelisting/blacklisting +capabilities based on USB device attributes. + CCE-82959-8 + +package --add=usbguard + + include install_usbguard + +class install_usbguard { + package { 'usbguard': + ensure => 'installed', + } +} + + - name: Ensure usbguard is installed + package: + name: usbguard + state: present + when: ansible_architecture != "s390x" + tags: + - CCE-82959-8 + - DISA-STIG-RHEL-08-040139 + - NIST-800-53-CM-8(3) + - NIST-800-53-IA-3 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_usbguard_installed + + +[[packages]] +name = "usbguard" +version = "*" + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + extensions: + - usbguard + + # Remediation is applicable only in certain platforms +if ! grep -q s390x /proc/sys/kernel/osrelease; then + +if ! rpm -q --quiet "usbguard" ; then + yum install -y "usbguard" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Enable the USBGuard Service + The USBGuard service should be enabled. + +The usbguard service can be enabled with the following command: +$ sudo systemctl enable usbguard.service + CCI-000416 + CCI-001958 + 1418 + CM-8(3)(a) + IA-3 + FMT_SMF_EXT.1 + SRG-OS-000378-GPOS-00163 + RHEL-08-040141 + SV-244548r743893_rule + The usbguard service must be running in order to +enforce the USB device authorization policy for all USB devices. + + CCE-82853-3 + include enable_usbguard + +class enable_usbguard { + service {'usbguard': + enable => true, + ensure => 'running', + } +} + + - name: Enable service usbguard + block: + + - name: Gather the package facts + package_facts: + manager: auto + + - name: Enable service usbguard + service: + name: usbguard + enabled: 'yes' + state: started + masked: 'no' + when: + - '"usbguard" in ansible_facts.packages' + when: + - ansible_architecture != "s390x" + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-82853-3 + - DISA-STIG-RHEL-08-040141 + - NIST-800-53-CM-8(3)(a) + - NIST-800-53-IA-3 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_usbguard_enabled + + +[customizations.services] +enabled = ["usbguard"] + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +metadata: + annotations: + complianceascode.io/depends-on: xccdf_org.ssgproject.content_rule_package_usbguard_installed +spec: + config: + ignition: + version: 3.1.0 + systemd: + units: + - name: usbguard.service + enabled: true + + # Remediation is applicable only in certain platforms +if ! grep -q s390x /proc/sys/kernel/osrelease && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" unmask 'usbguard.service' +"$SYSTEMCTL_EXEC" start 'usbguard.service' +"$SYSTEMCTL_EXEC" enable 'usbguard.service' + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Log USBGuard daemon audit events using Linux Audit + To configure USBGuard daemon to log via Linux Audit +(as opposed directly to a file), +AuditBackend option in /etc/usbguard/usbguard-daemon.conf +needs to be set to LinuxAudit. + CCI-000169 + CCI-000172 + AU-2 + CM-8(3) + IA-3 + FMT_SMF_EXT.1 + SRG-OS-000062-GPOS-00031 + SRG-OS-000471-GPOS-00215 + RHEL-08-030603 + SV-230470r744006_rule + Using the Linux Audit logging allows for centralized trace +of events. + + CCE-82168-6 + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +metadata: + annotations: + complianceascode.io/depends-on: xccdf_org.ssgproject.content_rule_package_usbguard_installed + complianceascode.io/ocp-version: '>=4.7.0' +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,{{ %0A%23%0A%23%20Rule%20set%20file%20path.%0A%23%0A%23%20The%20USBGuard%20daemon%20will%20use%20this%20file%20to%20load%20the%20policy%0A%23%20rule%20set%20from%20it%20and%20to%20write%20new%20rules%20received%20via%20the%0A%23%20IPC%20interface.%0A%23%0A%23%20RuleFile%3D/path/to/rules.conf%0A%23%0ARuleFile%3D/etc/usbguard/rules.conf%0A%0A%23%0A%23%20Rule%20set%20folder%20path.%0A%23%0A%23%20The%20USBGuard%20daemon%20will%20use%20this%20folder%20to%20load%20the%20policy%0A%23%20rule%20set%20from%20it%20and%20to%20write%20new%20rules%20received%20via%20the%0A%23%20IPC%20interface.%20Usually%2C%20we%20set%20the%20option%20to%0A%23%20/etc/usbguard/rules.d/.%20The%20USBGuard%20daemon%20is%20supposed%20to%0A%23%20behave%20like%20any%20other%20standard%20Linux%20daemon%20therefore%20it%0A%23%20loads%20rule%20files%20in%20alpha-numeric%20order.%20File%20names%20inside%0A%23%20RuleFolder%20directory%20should%20start%20with%20a%20two-digit%20number%0A%23%20prefix%20indicating%20the%20position%2C%20in%20which%20the%20rules%20are%0A%23%20scanned%20by%20the%20daemon.%0A%23%0A%23%20RuleFolder%3D/path/to/rulesfolder/%0A%23%0ARuleFolder%3D/etc/usbguard/rules.d/%0A%0A%23%0A%23%20Implicit%20policy%20target.%0A%23%0A%23%20How%20to%20treat%20devices%20that%20don%27t%20match%20any%20rule%20in%20the%0A%23%20policy.%20One%20of%3A%0A%23%0A%23%20%2A%20allow%20%20-%20authorize%20the%20device%0A%23%20%2A%20block%20%20-%20block%20the%20device%0A%23%20%2A%20reject%20-%20remove%20the%20device%0A%23%0AImplicitPolicyTarget%3Dblock%0A%0A%23%0A%23%20Present%20device%20policy.%0A%23%0A%23%20How%20to%20treat%20devices%20that%20are%20already%20connected%20when%20the%0A%23%20daemon%20starts.%20One%20of%3A%0A%23%0A%23%20%2A%20allow%20%20%20%20%20%20%20%20-%20authorize%20every%20present%20device%0A%23%20%2A%20block%20%20%20%20%20%20%20%20-%20deauthorize%20every%20present%20device%0A%23%20%2A%20reject%20%20%20%20%20%20%20-%20remove%20every%20present%20device%0A%23%20%2A%20keep%20%20%20%20%20%20%20%20%20-%20just%20sync%20the%20internal%20state%20and%20leave%20it%0A%23%20%2A%20apply-policy%20-%20evaluate%20the%20ruleset%20for%20every%20present%0A%23%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20device%0A%23%0APresentDevicePolicy%3Dapply-policy%0A%0A%23%0A%23%20Present%20controller%20policy.%0A%23%0A%23%20How%20to%20treat%20USB%20controllers%20that%20are%20already%20connected%0A%23%20when%20the%20daemon%20starts.%20One%20of%3A%0A%23%0A%23%20%2A%20allow%20%20%20%20%20%20%20%20-%20authorize%20every%20present%20device%0A%23%20%2A%20block%20%20%20%20%20%20%20%20-%20deauthorize%20every%20present%20device%0A%23%20%2A%20reject%20%20%20%20%20%20%20-%20remove%20every%20present%20device%0A%23%20%2A%20keep%20%20%20%20%20%20%20%20%20-%20just%20sync%20the%20internal%20state%20and%20leave%20it%0A%23%20%2A%20apply-policy%20-%20evaluate%20the%20ruleset%20for%20every%20present%0A%23%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20device%0A%23%0APresentControllerPolicy%3Dkeep%0A%0A%23%0A%23%20Inserted%20device%20policy.%0A%23%0A%23%20How%20to%20treat%20USB%20devices%20that%20are%20already%20connected%0A%23%20%2Aafter%2A%20the%20daemon%20starts.%20One%20of%3A%0A%23%0A%23%20%2A%20block%20%20%20%20%20%20%20%20-%20deauthorize%20every%20present%20device%0A%23%20%2A%20reject%20%20%20%20%20%20%20-%20remove%20every%20present%20device%0A%23%20%2A%20apply-policy%20-%20evaluate%20the%20ruleset%20for%20every%20present%0A%23%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20device%0A%23%0AInsertedDevicePolicy%3Dapply-policy%0A%0A%23%0A%23%20Control%20which%20devices%20are%20authorized%20by%20default.%0A%23%0A%23%20The%20USBGuard%20daemon%20modifies%20some%20the%20default%20authorization%20state%20attributes%0A%23%20of%20controller%20devices.%20This%20setting%2C%20enables%20you%20to%20define%20what%20value%20the%0A%23%20default%20authorization%20is%20set%20to.%0A%23%0A%23%20%2A%20keep%20%20%20%20%20%20%20%20%20-%20do%20not%20change%20the%20authorization%20state%0A%23%20%2A%20none%20%20%20%20%20%20%20%20%20-%20every%20new%20device%20starts%20out%20deauthorized%0A%23%20%2A%20all%20%20%20%20%20%20%20%20%20%20-%20every%20new%20device%20starts%20out%20authorized%0A%23%20%2A%20internal%20%20%20%20%20-%20internal%20devices%20start%20out%20authorized%2C%20external%20devices%20start%0A%23%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20out%20deauthorized%20%28this%20requires%20the%20ACPI%20tables%20to%20properly%0A%23%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20label%20internal%20devices%2C%20and%20kernel%20support%29%0A%23%0A%23AuthorizedDefault%3Dnone%0A%0A%23%0A%23%20Restore%20controller%20device%20state.%0A%23%0A%23%20The%20USBGuard%20daemon%20modifies%20some%20attributes%20of%20controller%0A%23%20devices%20like%20the%20default%20authorization%20state%20of%20new%20child%20device%0A%23%20instances.%20Using%20this%20setting%2C%20you%20can%20control%20whether%20the%0A%23%20daemon%20will%20try%20to%20restore%20the%20attribute%20values%20to%20the%20state%0A%23%20before%20modification%20on%20shutdown.%0A%23%0A%23%20SECURITY%20CONSIDERATIONS%3A%20If%20set%20to%20true%2C%20the%20USB%20authorization%0A%23%20policy%20could%20be%20bypassed%20by%20performing%20some%20sort%20of%20attack%20on%20the%0A%23%20daemon%20%28via%20a%20local%20exploit%20or%20via%20a%20USB%20device%29%20to%20make%20it%20shutdown%0A%23%20and%20restore%20to%20the%20operating-system%20default%20state%20%28known%20to%20be%20permissive%29.%0A%23%0ARestoreControllerDeviceState%3Dfalse%0A%0A%23%0A%23%20Device%20manager%20backend%0A%23%0A%23%20Which%20device%20manager%20backend%20implementation%20to%20use.%20One%20of%3A%0A%23%0A%23%20%2A%20uevent%20%20%20-%20Netlink%20based%20implementation%20which%20uses%20sysfs%20to%20scan%20for%20present%0A%23%20%20%20%20%20%20%20%20%20%20%20%20%20%20devices%20and%20an%20uevent%20netlink%20socket%20for%20receiving%20USB%20device%0A%23%20%20%20%20%20%20%20%20%20%20%20%20%20%20related%20events.%0A%23%20%2A%20umockdev%20-%20umockdev%20based%20device%20manager%20capable%20of%20simulating%20devices%20based%0A%23%20%20%20%20%20%20%20%20%20%20%20%20%20%20on%20umockdev-record%20files.%20Useful%20for%20testing.%0A%23%0ADeviceManagerBackend%3Duevent%0A%0A%23%21%21%21%20WARNING%3A%20It%27s%20good%20practice%20to%20set%20at%20least%20one%20of%20the%20%21%21%21%0A%23%21%21%21%20%20%20%20%20%20%20%20%20%20two%20options%20bellow.%20If%20none%20of%20them%20are%20set%2C%20%20%21%21%21%0A%23%21%21%21%20%20%20%20%20%20%20%20%20%20the%20daemon%20will%20accept%20IPC%20connections%20from%20%20%20%21%21%21%0A%23%21%21%21%20%20%20%20%20%20%20%20%20%20anyone%2C%20thus%20allowing%20anyone%20to%20modify%20the%20%20%20%20%21%21%21%0A%23%21%21%21%20%20%20%20%20%20%20%20%20%20rule%20set%20and%20%28de%29authorize%20USB%20devices.%20%20%20%20%20%20%20%21%21%21%0A%0A%23%0A%23%20Users%20allowed%20to%20use%20the%20IPC%20interface.%0A%23%0A%23%20A%20space%20delimited%20list%20of%20usernames%20that%20the%20daemon%20will%0A%23%20accept%20IPC%20connections%20from.%0A%23%0A%23%20IPCAllowedUsers%3Dusername1%20username2%20...%0A%23%0AIPCAllowedUsers%3Droot%0A%0A%23%0A%23%20Groups%20allowed%20to%20use%20the%20IPC%20interface.%0A%23%0A%23%20A%20space%20delimited%20list%20of%20groupnames%20that%20the%20daemon%20will%0A%23%20accept%20IPC%20connections%20from.%0A%23%0A%23%20IPCAllowedGroups%3Dgroupname1%20groupname2%20...%0A%23%0AIPCAllowedGroups%3Dwheel%0A%0A%23%0A%23%20IPC%20access%20control%20definition%20files%20path.%0A%23%0A%23%20The%20files%20at%20this%20location%20will%20be%20interpreted%20by%20the%20daemon%0A%23%20as%20access%20control%20definition%20files.%20The%20%28base%29name%20of%20a%20file%0A%23%20should%20be%20in%20the%20form%3A%0A%23%0A%23%20%20%20%5Buser%5D%5B%3A%3Cgroup%3E%5D%0A%23%0A%23%20and%20should%20contain%20lines%20in%20the%20form%3A%0A%23%0A%23%20%20%20%3Csection%3E%3D%5Bprivilege%5D%20...%0A%23%0A%23%20This%20way%20each%20file%20defines%20who%20is%20able%20to%20connect%20to%20the%20IPC%0A%23%20bus%20and%20what%20privileges%20he%20has.%0A%23%0AIPCAccessControlFiles%3D/etc/usbguard/IPCAccessControl.d/%0A%0A%23%0A%23%20Generate%20device%20specific%20rules%20including%20the%20%22via-port%22%0A%23%20attribute.%0A%23%0A%23%20This%20option%20modifies%20the%20behavior%20of%20the%20allowDevice%0A%23%20action.%20When%20instructed%20to%20generate%20a%20permanent%20rule%2C%0A%23%20the%20action%20can%20generate%20a%20port%20specific%20rule.%20Because%0A%23%20some%20systems%20have%20unstable%20port%20numbering%2C%20the%20generated%0A%23%20rule%20might%20not%20match%20the%20device%20after%20rebooting%20the%20system.%0A%23%0A%23%20If%20set%20to%20false%2C%20the%20generated%20rule%20will%20still%20contain%0A%23%20the%20%22parent-hash%22%20attribute%20which%20also%20defines%20an%20association%0A%23%20to%20the%20parent%20device.%20See%20usbguard-rules.conf%285%29%20for%20more%0A%23%20details.%0A%23%0ADeviceRulesWithPort%3Dfalse%0A%0A%23%0A%23%20USBGuard%20Audit%20events%20log%20backend%0A%23%0A%23%20One%20of%3A%0A%23%0A%23%20%2A%20FileAudit%20-%20Log%20audit%20events%20into%20a%20file%20specified%20by%0A%23%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20AuditFilePath%20setting%20%28see%20below%29%0A%23%20%2A%20LinuxAudit%20-%20Log%20audit%20events%20using%20the%20Linux%20Audit%0A%23%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20subsystem%20%28using%20audit_log_user_message%29%0A%23%0AAuditBackend%3DLinuxAudit%0A%0A%23%0A%23%20USBGuard%20audit%20events%20log%20file%20path.%0A%23%0A%23AuditFilePath%3D/var/log/usbguard/usbguard-audit.log%0A%0A%23%0A%23%20Hides%20personally%20identifiable%20information%20such%20as%20device%20serial%20numbers%20and%0A%23%20hashes%20of%20descriptors%20%28which%20include%20the%20serial%20number%29%20from%20audit%20entries.%0A%23%0A%23HidePII%3Dfalse }} + mode: 0600 + path: /etc/usbguard/usbguard-daemon.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if ! grep -q s390x /proc/sys/kernel/osrelease && { rpm --quiet -q usbguard; }; then + +if [ -e "/etc/usbguard/usbguard-daemon.conf" ] ; then + + LC_ALL=C sed -i "/^\s*AuditBackend=/d" "/etc/usbguard/usbguard-daemon.conf" +else + touch "/etc/usbguard/usbguard-daemon.conf" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/usbguard/usbguard-daemon.conf" + +cp "/etc/usbguard/usbguard-daemon.conf" "/etc/usbguard/usbguard-daemon.conf.bak" +# Insert at the end of the file +printf '%s\n' "AuditBackend=LinuxAudit" >> "/etc/usbguard/usbguard-daemon.conf" +# Clean up after ourselves. +rm "/etc/usbguard/usbguard-daemon.conf.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Authorize Human Interface Devices in USBGuard daemon + To allow authorization of Human Interface Devices (keyboard, mouse) +by USBGuard daemon, +add the line +allow with-interface match-all { 03:*:* } +to /etc/usbguard/rules.conf. + This rule should be understood primarily as a convenience administration feature. This rule ensures that if the USBGuard default rules.conf file is present, it will alter it so that USB human interface devices are allowed. However, if the rules.conf file is altered by system administrator, the rule does not check if USB human interface devices are allowed. This assumes that an administrator modified the file with some purpose in mind. + FMT_SMF_EXT.1 + SRG-OS-000114-GPOS-00059 + Without allowing Human Interface Devices, it might not be possible +to interact with the system. + CCE-82274-2 + - name: allow HID devices + lineinfile: + path: /etc/usbguard/rules.conf + create: true + line: allow with-interface match-all { 03:*:* } + state: present + when: ansible_architecture != "s390x" + tags: + - CCE-82274-2 + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - usbguard_allow_hid + + # Remediation is applicable only in certain platforms +if ! grep -q s390x /proc/sys/kernel/osrelease; then + +# path of file with Usbguard rules +rulesfile="/etc/usbguard/rules.conf" + +echo "allow with-interface match-all { 03:*:* }" >> $rulesfile + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Authorize Human Interface Devices and USB hubs in USBGuard daemon + To allow authorization of USB devices combining human interface device and hub capabilities +by USBGuard daemon, +add the line +allow with-interface match-all { 03:*:* 09:00:* } +to /etc/usbguard/rules.conf. + This rule should be understood primarily as a convenience administration feature. This rule ensures that if the USBGuard default rules.conf file is present, it will alter it so that USB human interface devices and hubs are allowed. However, if the rules.conf file is altered by system administrator, the rule does not check if USB human interface devices and hubs are allowed. This assumes that an administrator modified the file with some purpose in mind. + CM-8(3) + IA-3 + FMT_SMF_EXT.1 + SRG-OS-000114-GPOS-00059 + Without allowing Human Interface Devices, it might not be possible +to interact with the system. Without allowing hubs, it might not be possible to use any +USB devices on the system. + CCE-82368-2 + - name: allow HID devices and hubs + lineinfile: + path: /etc/usbguard/rules.conf + create: true + line: allow with-interface match-all { 03:*:* 09:00:* } + state: present + when: ansible_architecture != "s390x" + tags: + - CCE-82368-2 + - NIST-800-53-CM-8(3) + - NIST-800-53-IA-3 + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - usbguard_allow_hid_and_hub + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +metadata: + annotations: + complianceascode.io/depends-on: xccdf_org.ssgproject.content_rule_package_usbguard_installed +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,{{ %0Aallow%20with-interface%20match-all%20%7B%2003%3A%2A%3A%2A%2009%3A00%3A%2A%20%7D }} + mode: 0600 + path: /etc/usbguard/rules.d/75-hid-and-hub.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if ! grep -q s390x /proc/sys/kernel/osrelease; then + +echo "allow with-interface match-all { 03:*:* 09:00:* }" >> /etc/usbguard/rules.conf + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Authorize USB hubs in USBGuard daemon + To allow authorization of USB hub devices by USBGuard daemon, +add line +allow with-interface match-all { 09:00:* } +to /etc/usbguard/rules.conf. + This rule should be understood primarily as a convenience administration feature. This rule ensures that if the USBGuard default rules.conf file is present, it will alter it so that USB hub devices are allowed. However, if the rules.conf file is altered by system administrator, the rule does not check if USB hub devices are allowed. This assumes that an administrator modified the file with some purpose in mind. + FMT_SMF_EXT.1 + SRG-OS-000114-GPOS-00059 + Without allowing hubs, it might not be possible to use any +USB devices on the system. + CCE-82273-4 + - name: allow hubs + lineinfile: + path: /etc/usbguard/rules.conf + create: true + line: allow with-interface match-all { 09:00:* } + state: present + when: ansible_architecture != "s390x" + tags: + - CCE-82273-4 + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - usbguard_allow_hub + + # Remediation is applicable only in certain platforms +if ! grep -q s390x /proc/sys/kernel/osrelease; then + +echo "allow with-interface match-all { 09:00:* }" >> /etc/usbguard/rules.conf + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Generate USBGuard Policy + By default USBGuard when enabled prevents access to all USB devices and this lead +to inaccessible system if they use USB mouse/keyboard. To prevent this scenario, +the initial policy configuration must be generated based on current connected USB +devices. + CCI-000416 + CCI-001958 + CM-8(3)(a) + IA-3 + FMT_SMF_EXT.1 + SRG-OS-000378-GPOS-00163 + RHEL-08-040140 + SV-230524r744026_rule + The usbguard must be configured to allow connected USB devices to work +properly, avoiding the system to become inaccessible. + + CCE-83774-0 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83774-0 + - DISA-STIG-RHEL-08-040140 + - NIST-800-53-CM-8(3)(a) + - NIST-800-53-IA-3 + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - usbguard_generate_policy + +- name: Generate USBGuard Policy + block: + + - name: Gather the package facts + package_facts: + manager: auto + + - name: Check that the /etc/usbguard/rules.conf exists + stat: + path: /etc/usbguard/rules.conf + register: policy_file + + - name: Create USBGuard Policy configuration + command: usbguard generate-policy + register: policy + when: not policy_file.stat.exists or policy_file.stat.size == 0 + + - name: Copy the Generated Policy configuration to a persistent file + copy: + content: '{{ policy.stdout }}' + dest: /etc/usbguard/rules.conf + mode: 384 + when: not policy_file.stat.exists or policy_file.stat.size == 0 + + - name: Add comment into /etc/usbguard/rules.conf when system has no USB devices + lineinfile: + path: /etc/usbguard/rules.conf + line: '# No USB devices found' + state: present + when: not policy_file.stat.exists or policy_file.stat.size == 0 + + - name: Enable service usbguard + service: + name: usbguard + enabled: 'yes' + state: started + masked: 'no' + when: + - ansible_architecture != "s390x" + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"usbguard" in ansible_facts.packages' + tags: + - CCE-83774-0 + - DISA-STIG-RHEL-08-040140 + - NIST-800-53-CM-8(3)(a) + - NIST-800-53-IA-3 + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - usbguard_generate_policy + + # Remediation is applicable only in certain platforms +if ! grep -q s390x /proc/sys/kernel/osrelease && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +if rpm --quiet -q usbguard +then + USBGUARD_CONF=/etc/usbguard/rules.conf + if [ ! -f "$USBGUARD_CONF" ] || [ ! -s "$USBGUARD_CONF" ]; then + usbguard generate-policy > $USBGUARD_CONF + if [ ! -s "$USBGUARD_CONF" ]; then + # make sure OVAL check doesn't fail on systems where + # generate-policy doesn't find any USB devices (for + # example a system might not have a USB bus) + echo "# No USB devices found" > $USBGUARD_CONF + fi + # make sure it has correct permissions + chmod 600 $USBGUARD_CONF + + SYSTEMCTL_EXEC='/usr/bin/systemctl' + "$SYSTEMCTL_EXEC" unmask 'usbguard.service' + "$SYSTEMCTL_EXEC" restart 'usbguard.service' + "$SYSTEMCTL_EXEC" enable 'usbguard.service' + fi +else + echo "USBGuard is not installed. No remediation was applied!" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + X Window System + The X Window System implementation included with the +system is called X.org. + + Disable X Windows + Unless there is a mission-critical reason for the +system to run a graphical user interface, ensure X is not set to start +automatically at boot and remove the X Windows software packages. +There is usually no reason to run X Windows +on a dedicated server system, as it increases the system's attack surface and consumes +system resources. Administrators of server systems should instead login via +SSH or on the text console. + + Remove the X Windows Package Group + By removing the xorg-x11-server-common package, the system no longer has X Windows +installed. If X Windows is not installed then the system cannot boot into graphical user mode. +This prevents the system from being accidentally or maliciously booted into a graphical.target +mode. To do so, run the following command:$ sudo yum groupremove base-x +$ sudo yum remove xorg-x11-server-common + The installation and use of a Graphical User Interface (GUI) increases your attack vector and decreases your +overall security posture. Removing the package xorg-x11-server-common package will remove the graphical target +which might bring your system to an inconsistent state requiring additional configuration to access the system +again. If a GUI is an operational requirement, a tailored profile that removes this rule should used before +continuing installation. + 12 + 15 + 8 + APO13.01 + DSS01.04 + DSS05.02 + DSS05.03 + CCI-000366 + 4.3.3.6.6 + SR 1.13 + SR 2.6 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + A.11.2.6 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.6.2.1 + A.6.2.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.AC-3 + PR.PT-4 + SRG-OS-000480-GPOS-00227 + 2.2.2 + Unnecessary service packages must not be installed to decrease the attack surface of the system. X windows has a long history of security +vulnerabilities and should not be installed unless approved and documented. + CCE-82757-6 + +package --remove=xorg-x11-server-common + + include remove_xorg-x11-server-common + +class remove_xorg-x11-server-common { + package { 'xorg-x11-server-common': + ensure => 'purged', + } +} + + - name: Ensure xorg-x11-server-common is removed + package: + name: xorg-x11-server-common + state: absent + tags: + - CCE-82757-6 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_xorg-x11-server-common_removed + + +# CAUTION: This remediation script will remove xorg-x11-server-common +# from the system, and may remove any packages +# that depend on xorg-x11-server-common. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "xorg-x11-server-common" ; then + + yum remove -y "xorg-x11-server-common" + +fi + + + + + + + + + + Disable graphical user interface + By removing the following packages, the system no longer has X Windows installed. + +xorg-x11-server-Xorg xorg-x11-server-common xorg-x11-server-utils xorg-x11-server-Xwayland + +If X Windows is not installed then the system cannot boot into graphical user mode. +This prevents the system from being accidentally or maliciously booted into a graphical.target +mode. To do so, run the following command: + +sudo yum remove xorg-x11-server-Xorg xorg-x11-server-common xorg-x11-server-utils xorg-x11-server-Xwayland + The installation and use of a Graphical User Interface (GUI) increases your attack vector and decreases your +overall security posture. Removing the package xorg-x11-server-common package will remove the graphical target +which might bring your system to an inconsistent state requiring additional configuration to access the system +again. +The rule xwindows_runlevel_target can be used to configure the system to boot into the multi-user.target. +If a GUI is an operational requirement, a tailored profile that removes this rule should be used before +continuing installation. + This rule is disabled on Red Hat Virtualization Hosts and Managers, it will report not applicable. +X11 graphic libraries are dependency of OpenStack Cinderlib storage provider. + CCI-000366 + CM-6(b) + SRG-OS-000480-GPOS-00227 + RHEL-08-040320 + SV-230553r809324_rule + Unnecessary service packages must not be installed to decrease the attack surface of the system. X windows has a long history of security +vulnerabilities and should not be installed unless approved and documented. + + CCE-83411-9 + +package --remove=xorg-x11-server-Xorg --remove=xorg-x11-server-common --remove=xorg-x11-server-utils --remove=xorg-x11-server-Xwayland + + - name: Ensure xorg packages are removed + package: + name: + - xorg-x11-server-Xorg + - xorg-x11-server-common + - xorg-x11-server-utils + - xorg-x11-server-Xwayland + state: absent + tags: + - CCE-83411-9 + - DISA-STIG-RHEL-08-040320 + - NIST-800-53-CM-6(b) + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + - xwindows_remove_packages + + + +# remove packages +if rpm -q --quiet "xorg-x11-server-Xorg" ; then + + yum remove -y "xorg-x11-server-Xorg" + +fi +if rpm -q --quiet "xorg-x11-server-utils" ; then + + yum remove -y "xorg-x11-server-utils" + +fi +if rpm -q --quiet "xorg-x11-server-common" ; then + + yum remove -y "xorg-x11-server-common" + +fi + +if rpm -q --quiet "xorg-x11-server-Xwayland" ; then + + yum remove -y "xorg-x11-server-Xwayland" + +fi + + + + + + + + + + Disable X Windows Startup By Setting Default Target + Systems that do not require a graphical user interface should only boot by +default into multi-user.target mode. This prevents accidental booting of the system +into a graphical.target mode. Setting the system's default target to +multi-user.target will prevent automatic startup of the X server. To do so, run: +$ systemctl set-default multi-user.target +You should see the following output: +Removed symlink /etc/systemd/system/default.target. +Created symlink from /etc/systemd/system/default.target to /usr/lib/systemd/system/multi-user.target. + 12 + 15 + 8 + APO13.01 + DSS01.04 + DSS05.02 + DSS05.03 + CCI-000366 + 4.3.3.6.6 + SR 1.13 + SR 2.6 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + A.11.2.6 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.6.2.1 + A.6.2.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.AC-3 + PR.PT-4 + SRG-OS-000480-GPOS-00227 + RHEL-08-040321 + 2.2.2 + SV-251718r809378_rule + Services that are not required for system and application processes +must not be active to decrease the attack surface of the system. X windows has a +long history of security vulnerabilities and should not be used unless approved +and documented. + + CCE-83380-6 + - name: Switch to multi-user runlevel + file: + src: /usr/lib/systemd/system/multi-user.target + dest: /etc/systemd/system/default.target + state: link + force: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83380-6 + - DISA-STIG-RHEL-08-040321 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + - xwindows_runlevel_target + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +systemctl set-default multi-user.target + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + + + Introduction + The purpose of this guidance is to provide security configuration +recommendations and baselines for the Red Hat Enterprise Linux 8 operating +system. Recommended settings for the basic operating system are provided, +as well as for many network services that the system can provide to other systems. +The guide is intended for system administrators. Readers are assumed to +possess basic system administration skills for Unix-like systems, as well +as some familiarity with the product's documentation and administration +conventions. Some instructions within this guide are complex. +All directions should be followed completely and with understanding of +their effects in order to avoid serious adverse effects on the system +and its security. + + General Principles + The following general principles motivate much of the advice in this +guide and should also influence any configuration decisions that are +not explicitly covered. + + Encrypt Transmitted Data Whenever Possible + Data transmitted over a network, whether wired or wireless, is susceptible +to passive monitoring. Whenever practical solutions for encrypting +such data exist, they should be applied. Even if data is expected to +be transmitted only over a local network, it should still be encrypted. +Encrypting authentication data, such as passwords, is particularly +important. Networks of Red Hat Enterprise Linux 8 machines can and should be configured +so that no unencrypted authentication data is ever transmitted between +machines. + + + Least Privilege + Grant the least privilege necessary for user accounts and software to perform tasks. +For example, sudo can be implemented to limit authorization to super user +accounts on the system only to designated personnel. Another example is to limit +logins on server systems to only those administrators who need to log into them in +order to perform administration tasks. Using SELinux also follows the principle of +least privilege: SELinux policy can confine software to perform only actions on the +system that are specifically allowed. This can be far more restrictive than the +actions permissible by the traditional Unix permissions model. + + + Minimize Software to Minimize Vulnerability + The simplest way to avoid vulnerabilities in software is to avoid +installing that software. On Red Hat Enterprise Linux 8,the RPM Package Manager (originally Red Hat Package Manager, abbreviated RPM) +allows for careful management of +the set of software packages installed on a system. Installed software +contributes to system vulnerability in several ways. Packages that +include setuid programs may provide local attackers a potential path to +privilege escalation. Packages that include network services may give +this opportunity to network-based attackers. Packages that include +programs which are predictably executed by local users (e.g. after +graphical login) may provide opportunities for trojan horses or other +attack code to be run undetected. The number of software packages +installed on a system can almost always be significantly pruned to include +only the software for which there is an environmental or operational need. + + + Run Different Network Services on Separate Systems + Whenever possible, a server should be dedicated to serving exactly one +network service. This limits the number of other services that can +be compromised in the event that an attacker is able to successfully +exploit a software flaw in one network service. + + + Configure Security Tools to Improve System Robustness + Several tools exist which can be effectively used to improve a system's +resistance to and detection of unknown attacks. These tools can improve +robustness against attack at the cost of relatively little configuration +effort. In particular, this guide recommends and discusses the use of +host-based firewalling, SELinux for protection against +vulnerable services, and a logging and auditing infrastructure for +detection of problems. + + + + How to Use This Guide + Readers should heed the following points when using the guide. + + Formatting Conventions + Commands intended for shell execution, as well as configuration file text, +are featured in a monospace font. Italics are used +to indicate instances where the system administrator must substitute +the appropriate information into a command or configuration file. + + + Read Sections Completely and in Order + Each section may build on information and recommendations discussed in +prior sections. Each section should be read and understood completely; +instructions should never be blindly applied. Relevant discussion may +occur after instructions for an action. + + + Reboot Required + A system reboot is implicitly required after some actions in order to +complete the reconfiguration of the system. In many cases, the changes +will not take effect until a reboot is performed. In order to ensure +that changes are applied properly and to test functionality, always +reboot the system after applying a set of recommendations from this guide. + + + Root Shell Environment Assumed + Most of the actions listed in this document are written with the +assumption that they will be executed by the root user running the +/bin/bash shell. Commands preceded with a hash mark (#) +assume that the administrator will execute the commands as root, i.e. +apply the command via sudo whenever possible, or use +su to gain root privileges if sudo cannot be +used. Commands which can be executed as a non-root user are are preceded +by a dollar sign ($) prompt. + + + Test in Non-Production Environment + This guidance should always be tested in a non-production environment +before deployment. This test environment should simulate the setup in +which the system will be deployed as closely as possible. + + + + + + + + + combine_ovals.py from SCAP Security Guide + ssg: [0, 1, 64], python: 3.10.4 + 5.11 + 2022-09-30T15:09:59 + + + + + Disable DHCP Client in ifcfg + + Red Hat Enterprise Linux 8 + + DHCP configuration should be static for all + interfaces. + + + + + + + + + Configure Fapolicy Module to Employ a Deny-all, Permit-by-exception Policy to Allow the Execution of Authorized Software Programs. + + Red Hat Enterprise Linux 8 + + Configure Fapolicy Module to Employ a Deny-all, Permit-by-exception Policy + + + + + + + + + + + Enable Logging of All FTP Transactions + + Red Hat Enterprise Linux 8 + + To trace malicious activity facilitated by the FTP + service, it must be configured to ensure that all commands sent to + the FTP server are logged using the verbose vsftpd log format. + + + + + + + + + + + + + + + Create Warning Banners for All FTP Users + + Red Hat Enterprise Linux 8 + + This setting will cause the system greeting banner to be + used for FTP connections as well. + + + + + + + + + + Set Permissions on the /etc/httpd/conf/ Directory + + Red Hat Enterprise Linux 8 + + Directory permissions for /etc/httpd/conf/ should be set to 0750 (or stronger). + + + + + + + + + + Set Permissions on the /var/log/httpd/ Directory + + Red Hat Enterprise Linux 8 + + Directory permissions for /var/log/httpd should be set to 0700 (or stronger). + + + + + + + + + + Set Permissions on All Configuration Files Inside /etc/httpd/conf.d/ + + Red Hat Enterprise Linux 8 + + The /etc/httpd/conf.d/* files should have the appropriate permissions (0640 or stronger). + + + + + + + + + + Set Permissions on All Configuration Files Inside /etc/httpd/conf/ + + Red Hat Enterprise Linux 8 + + The /etc/httpd/conf/* files should have the appropriate permissions (0640 or stronger). + + + + + + + + + + Set Permissions on All Configuration Files Inside /etc/httpd/conf.modules.d/ + + Red Hat Enterprise Linux 8 + + The /etc/httpd/conf.modules.d/* files should have the appropriate permissions (0640 or stronger). + + + + + + + + + + Disable Plaintext Authentication + + Red Hat Enterprise Linux 8 + + Plaintext authentication of mail clients should be disabled. + + + + + + + + + + Enable the SSL flag in /etc/dovecot.conf + + Red Hat Enterprise Linux 8 + + SSL capabilities should be enabled for the mail server. + + + + + + + + + + Disable Kerberos by removing host keytab + + Red Hat Enterprise Linux 8 + + Check that there is no Kerberos keytab file present in /etc + + + + + + + + + + Enable the LDAP Client For Use in Authconfig + + Red Hat Enterprise Linux 8 + + Enable LDAP in authconfig. + + + + + + + + + + Configure LDAP Client to Use TLS For All Transactions + + Red Hat Enterprise Linux 8 + + Require the use of TLS for LDAP clients. + + + + + + + + + + + Configure Certificate Directives for LDAP Use of TLS + + Red Hat Enterprise Linux 8 + + Require the use of TLS for LDAP clients. + + + + + + + + + + + Configure System to Forward All Mail For The Root Account + + Red Hat Enterprise Linux 8 + + Check if root has the correct mail alias. + + + + + + + + + + Configure System to Forward All Mail From Postmaster to The Root Account + + Red Hat Enterprise Linux 8 + + Check if postmaster has the correct mail alias. + + + + + + + + + + Disable Postfix Network Listening + + Red Hat Enterprise Linux 8 + + Postfix network listening should be disabled + + + + + + + + + + + Configure SMTP Greeting Banner + + Red Hat Enterprise Linux 8 + + Protect against unnecessary release of information. + + + + + + + + + + Prevent Unrestricted Mail Relaying + + Red Hat Enterprise Linux 8 + + Ensure 'smtpd_client_restrictions' is configured with value 'permit_mynetworks,reject' in /etc/postfix/main.cf + + + + + + + + + + + + + Ensure Insecure File Locking is Not Allowed + + Red Hat Enterprise Linux 8 + + Allowing insecure file locking could allow for sensitive + data to be viewed or edited by an unauthorized user. + + + + + + + + + Use Kerberos Security on All Exports + + Red Hat Enterprise Linux 8 + + Using Kerberos Security allows to cryptography authenticate a + valid user to an NFS share. + + + + + + + + + + + Disable chrony daemon from acting as server + + Red Hat Enterprise Linux 8 + + Configure the port setting in /etc/chrony.conf to disable + server operation. + + + + + + + + + + + Disable network management of chrony daemon + + Red Hat Enterprise Linux 8 + + Configure the cmdport setting in /etc/chrony.conf to disable + chronyc management connections over network. + + + + + + + + + + + Configure Time Service Maxpoll Interval + + Red Hat Enterprise Linux 8 + + Configure the maxpoll setting in /etc/ntp.conf or chrony.conf + to continuously poll the time source servers. + + + + + + + + + + + + + + + + + + + + + + + Specify Additional Remote NTP Servers + + Red Hat Enterprise Linux 8 + + Multiple remote chronyd or ntpd NTP Servers for time synchronization should be specified (and dependencies are met) + + + + + + + + + + + + + + + + + Specify a Remote NTP Server + + Red Hat Enterprise Linux 8 + + A remote chronyd or ntpd NTP Server for time synchronization should be specified (and dependencies are met) + + + + + + + + + + + + + + + + + Ensure that chronyd is running under chrony user account + + Red Hat Enterprise Linux 8 + + Ensure that no other user than chrony is configured to run the chrony service + + + + + + + + + + Ensure Chrony is only configured with the server directive + + Red Hat Enterprise Linux 8 + + Ensure Chrony has time sources configured with server directive + + + + + + + + + + + A remote time server for Chrony is configured + + Red Hat Enterprise Linux 8 + + A remote NTP Server for time synchronization should be + specified (and dependencies are met) + + + + + + + + + + Specify Additional Remote NTP Servers + + Red Hat Enterprise Linux 8 + + Multiple ntpd NTP Servers for time synchronization should be specified. + + + + + + + + + Specify a Remote NTP Server + + Red Hat Enterprise Linux 8 + + A remote ntpd NTP Server for time synchronization should be + specified (and dependencies are met) + + + + + + + + + Enable the NTP Daemon + + Red Hat Enterprise Linux 8 + + At least one of the chronyd or ntpd services should be enabled if possible. + + + + + + + + + + + Remove Host-Based Authentication Files + + Red Hat Enterprise Linux 8 + + There should not be any shosts.equiv files on the system. + + + + + + + + + + Remove Rsh Trust Files + + Red Hat Enterprise Linux 8 + + There should not be any .rhosts or hosts.equiv files on the system. + + + + + + + + + + + + Remove User Host-Based Authentication Files + + Red Hat Enterprise Linux 8 + + There should not be any .shosts files on the system. + + + + + + + + + + Ensure tftp Daemon Uses Secure Mode + + Red Hat Enterprise Linux 8 + + The TFTP daemon should use secure mode. + + + + + + + + + + + Disable Printer Browsing Entirely if Possible + + Red Hat Enterprise Linux 8 + + The CUPS print service can be configured to broadcast a list + of available printers to the network. Other machines on the network, also + running the CUPS print service, can be configured to listen to these + broadcasts and add and configure these printers for immediate use. By + disabling this browsing capability, the machine will no longer generate + or receive such broadcasts. + + + + + + + + + + Disable Print Server Capabilities + + Red Hat Enterprise Linux 8 + + By default, locally configured printers will not be shared + over the network, but if this functionality has somehow been enabled, + these recommendations will disable it again. Be sure to disable outgoing + printer list broadcasts, or remote users will still be able to see the + locally configured printers, even if they cannot actually print to them. + To limit print serving to a particular set of users, use the Policy + directive. + + + + + + + + + + Require Client SMB Packet Signing, if using mount.cifs + + Red Hat Enterprise Linux 8 + + Require packet signing of clients who mount + Samba shares using the mount.cifs program (e.g., those who + specify shares in /etc/fstab). To do so, ensure that signing + options (either sec=krb5i or sec=ntlmv2i) are + used. + + + + + + + + + + + + + + + + + + + Require Client SMB Packet Signing, if using smbclient + + Red Hat Enterprise Linux 8 + + Require samba clients which use smb.conf, such as smbclient, + to use packet signing. A Samba client should only communicate with + servers who can support SMB packet signing. + + + + + + + + + + + + + Ensure Default SNMP Password Is Not Used + + Red Hat Enterprise Linux 8 + + SNMP default communities must be removed. + + + + + + + + + Configure SNMP Service to Use Only SNMPv3 or Newer + + Red Hat Enterprise Linux 8 + + SNMP version 1 and 2c must not be enabled. + + + + + + + + + + + Verify Permissions on SSH Server Private *_key Key Files + + Red Hat Enterprise Linux 8 + + The system sshd key is owned by root:root and has the 0600 permission, or by a root:ssh_keys with the 0640 permission + + + + + + + + + + Remove SSH Server firewalld Firewall exception (Unusual) + + Red Hat Enterprise Linux 8 + + If inbound SSH access is not needed, the firewall should disallow or reject access to + the SSH port (22). + + + + + + + + + + + + Configure session renegotiation for SSH client + + Red Hat Enterprise Linux 8 + + Ensure 'RekeyLimit' is configured with the correct value in /etc/ssh/ssh_config and /etc/ssh/ssh_config.d/*.conf + + + + + + + + + + + SSH client uses strong entropy to seed (for CSH like shells) + + Red Hat Enterprise Linux 8 + + Ensure the SSH_USE_STRONG_RNG environment variable is exported in /etc/profile.d/cc-ssh-strong-rng.csh and is not overridden in /etc/profile + + + + + + + + + + + SSH client uses strong entropy to seed (Bash-like shells) + + Red Hat Enterprise Linux 8 + + Ensure the SSH_USE_STRONG_RNG environment variable is exported in /etc/profile.d/cc-ssh-strong-rng.sh and is not overridden in /etc/profile + + + + + + + + + + + Enable SSH Server firewalld Firewall Exception + + Red Hat Enterprise Linux 8 + + If inbound SSH access is needed, the firewall should allow access to + the SSH port (22). + + + + + + + + + + + + + + + + Allow Only SSH Protocol 2 + + Red Hat Enterprise Linux 8 + + The OpenSSH daemon should be running protocol 2. + + + + + + + + + + + + + + + + + + + + + Disable Compression Or Set Compression to delayed + + Red Hat Enterprise Linux 8 + + SSH should either have compression disabled or set to delayed. + + + + + + + + + + + + + + + + + + Disable SSH Support for Rhosts RSA Authentication + + Red Hat Enterprise Linux 8 + + SSH can allow authentication through the obsolete rsh command + through the use of the authenticating user's SSH keys. This should be disabled. + + + + + + + + + + + + + + + + + + + + + Force frequent session key renegotiation + + Red Hat Enterprise Linux 8 + + Ensure RekeyLimit is configured with the appropriate value in /etc/ssh/sshd_config + + + + + + + + + + + + + + + + + + + + Set SSH Idle Timeout Interval + + Red Hat Enterprise Linux 8 + + The SSH idle timeout interval should be set to an + appropriate value. + + + + + + + + + + + + + + + + + + + + Set SSH Client Alive Count Max + + Red Hat Enterprise Linux 8 + + The SSH ClientAliveCountMax should be set to an appropriate + value (and dependencies are met) + + + + + + + + + + + + + + + + + + + + Ensure SSH LoginGraceTime is configured + + Red Hat Enterprise Linux 8 + + The SSH number seconds for login grace time should be set to an + appropriate value. + + + + + + + + + + + + + + + + + + Set SSH authentication attempt limit + + Red Hat Enterprise Linux 8 + + The SSH MaxAuthTries should be set to an + appropriate value. + + + + + + + + + + + + + + + + + + Set SSH MaxSessions limit + + Red Hat Enterprise Linux 8 + + The SSH number of max sessions should be set to an + appropriate value. + + + + + + + + + + + + + + + + + + Ensure SSH MaxStartups is configured + + Red Hat Enterprise Linux 8 + + Ensure 'MaxStartups' is configured in + '/etc/ssh/sshd_config' + + + + + + + + + + + + + + + + + + Use Only FIPS 140-2 Validated Ciphers + + Red Hat Enterprise Linux 8 + + Limit the ciphers to those which are FIPS-approved. + + + + + + + + + + + + + + + + + + + + + Use Only FIPS 140-2 Validated MACs + + Red Hat Enterprise Linux 8 + + Limit the Message Authentication Codes (MACs) to those which are FIPS-approved. + + + + + + + + + + + + + + + + + + + + + Enable Use of Privilege Separation + + Red Hat Enterprise Linux 8 + + Ensure 'UsePrivilegeSeparation' is configured with value 'sandbox' in '/etc/ssh/sshd_config' + + + + + + + + + + + + + + Certificate status checking in SSSD + + Red Hat Enterprise Linux 8 + + SSSD should be configured with the correct ocsp_dgst + digest function + + + + + + + + + + Configure PAM in SSSD Services + + Red Hat Enterprise Linux 8 + + SSSD should be configured to run SSSD PAM services. + + + + + + + + + + + Enable Smartcards in SSSD + + Red Hat Enterprise Linux 8 + + SSSD should be configured to authenticate access to the system + using smart cards. + + + + + + + + + + + + Configure SSSD's Memory Cache to Expire + + Red Hat Enterprise Linux 8 + + SSSD's memory cache should be configured to set to expire records after 1 day. + + + + + + + + + + Configure SSSD to Expire Offline Credentials + + Red Hat Enterprise Linux 8 + + SSSD should be configured to expire offline credentials after 1 day. + + + + + + + + + + + Configure SSSD to run as user sssd + + Red Hat Enterprise Linux 8 + + SSSD processes should be configured to run as user sssd, not root. + + + + + + + + + + Configure SSSD to Expire SSH Known Hosts + + Red Hat Enterprise Linux 8 + + SSSD should be configured to expire keys from known SSH hosts after 1 day. + + + + + + + + + + Configure SSSD LDAP Backend Client CA Certificate Location + + Red Hat Enterprise Linux 8 + + Configure SSSD to implement cryptography to protect the integrity of LDAP remote access sessions. + + + + + + + + + + Configure SSSD LDAP Backend Client to Demand a Valid Certificate from the Server + + Red Hat Enterprise Linux 8 + + Configure SSSD to request a valid certificate from the server to protect LDAP remote access sessions. + + + + + + + + + + Configure SSSD LDAP Backend to Use TLS For All Transactions + + Red Hat Enterprise Linux 8 + + LDAP should be used for authentication and use STARTTLS + + + + + + + + + + Log USBGuard daemon audit events using Linux Audit + + Red Hat Enterprise Linux 8 + + Ensure 'AuditBackend' is configured with value 'LinuxAudit' in /etc/usbguard/usbguard-daemon.conf + + + + + + + + + + + + + Authorize Human Interface Devices in USBGuard daemon + + Red Hat Enterprise Linux 8 + + Check that /etc/usbguard/rules.conf exists and that it contains at least one non white space character. + + + + + + + + + + Authorize Human Interface Devices and USB hubs in USBGuard daemon + + Red Hat Enterprise Linux 8 + + Check that /etc/usbguard/rules.conf contains at least one non whitespace character and exists. + + + + + + + + + + Authorize USB hubs in USBGuard daemon + + Red Hat Enterprise Linux 8 + + Check that /etc/usbguard/rules.conf contains at least one non whitespace character and exists. + + + + + + + + + + Generate USBGuard Policy + + Red Hat Enterprise Linux 8 + + Check that /etc/usbguard/rules.conf contains at least one non whitespace character and exists. + + + + + + + + + + Disable graphical user interface + + Red Hat Enterprise Linux 8 + + Ensure that the default runlevel target is set to multi-user.target. + + + + + + + + + + + + + Disable X Windows Startup By Setting Default Target + + Red Hat Enterprise Linux 8 + + Ensure that the default runlevel target is set to multi-user.target. + + + + + + + + + + Enable authselect + + Red Hat Enterprise Linux 8 + + Check that authselect is enabled + + + + + + + + + + + + + + Modify the System Login Banner + + Red Hat Enterprise Linux 8 + + The system login banner text should be set correctly. + + + + + + + + + + Modify the System Message of the Day Banner + + Red Hat Enterprise Linux 8 + + The system login banner text should be set correctly. + + + + + + + + + + Enable GNOME3 Login Warning Banner + + Red Hat Enterprise Linux 8 + + Enable the GNOME3 Login warning banner. + + + + + + + + + + + + + + + Set the GNOME3 Login Warning Banner Text + + Red Hat Enterprise Linux 8 + + Enable the GUI warning banner. + + + + + + + + + + + + + + + Disallow Configuration to Bypass Password Requirements for Privilege Escalation + + Red Hat Enterprise Linux 8 + + Disallow Configuration to Bypass Password Requirements for Privilege Escalation. + + + + + + + + + Ensure PAM Displays Last Logon/Access Notification + + Red Hat Enterprise Linux 8 + + Configure the system to notify users of last login/access using pam_lastlog. + + + + + + + + + + + Set Up a Private Namespace in PAM Configuration + + Red Hat Enterprise Linux 8 + + Check presence of pam_namespace.so module in the /etc/pam.d/login file + + + + + + + + + + An SELinux Context must be configured for the pam_faillock.so records directory + + Red Hat Enterprise Linux 8 + + An SELinux Context must be configued for the Faillock directory. + + + + + + + + + + + Account Lockouts Must Be Logged + + Red Hat Enterprise Linux 8 + + Account Lockouts Must Be Logged + + + + + + + + + + + + + + + + + + + Limit Password Reuse: password-auth + + Red Hat Enterprise Linux 8 + + The passwords to remember should be set correctly. + + + + + + + + + + Limit Password Reuse: system-auth + + Red Hat Enterprise Linux 8 + + The passwords to remember should be set correctly. + + + + + + + + + + Limit Password Reuse + + Red Hat Enterprise Linux 8 + + The passwords to remember should be set correctly. + + + + + + + + + + + Lock Accounts After Failed Password Attempts + + Red Hat Enterprise Linux 8 + + Lockout account after failed login attempts + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Configure the root Account for Failed Password Attempts + + Red Hat Enterprise Linux 8 + + The root account should be configured to deny access after the number of + defined failed attempts has been reached. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Lock Accounts Must Persist + + Red Hat Enterprise Linux 8 + + Persist lockout account after reboot + + + + + + + + + + + + + + + + + + Enforce pam_faillock for Local Accounts Only + + Red Hat Enterprise Linux 8 + + Enforce pam_faillock for Local Accounts Only + + + + + + + + + + + + + + + + + + + + + + Set Interval For Counting Failed Password Attempts + + Red Hat Enterprise Linux 8 + + The number of allowed failed logins should be set correctly. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Set Lockout Time for Failed Password Attempts + + Red Hat Enterprise Linux 8 + + The unlock time after number of failed logins should be set correctly. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Ensure PAM password complexity module is enabled in password-auth + + Red Hat Enterprise Linux 8 + + The PAM module pam_pwquality is used in password-auth + + + + + + + + + + Ensure PAM password complexity module is enabled in system-auth + + Red Hat Enterprise Linux 8 + + The PAM module pam_pwquality is used in system-auth + + + + + + + + + + Ensure PAM Enforces Password Requirements - Authentication Retry Prompts Permitted Per-Session + + Red Hat Enterprise Linux 8 + + The password retry should meet minimum requirements + + + + + + + + + + + + + + + + + + Set Password Hashing Algorithm in /etc/libuser.conf + + Red Hat Enterprise Linux 8 + + The password hashing algorithm should be set correctly in /etc/libuser.conf. + + + + + + + + + + Set Password Hashing Algorithm in /etc/login.defs + + Red Hat Enterprise Linux 8 + + The password hashing algorithm should be set correctly in /etc/login.defs. + + + + + + + + + + Set PAM''s Password Hashing Algorithm - password-auth + + Red Hat Enterprise Linux 8 + + The password hashing algorithm should be set correctly in /etc/pam.d/password-auth. + + + + + + + + + + Set PAM''s Password Hashing Algorithm + + Red Hat Enterprise Linux 8 + + The password hashing algorithm should be set correctly in /etc/pam.d/system-auth. + + + + + + + + + + Set Password Hashing Minimum Rounds in /etc/login.defs + + Red Hat Enterprise Linux 8 + + The password hashing minimum rounds should be set correctly in /etc/login.defs. + + + + + + + + + + + + + + + + + + Disable Ctrl-Alt-Del Burst Action + + Red Hat Enterprise Linux 8 + + Configure the CtrlAltDelBurstAction setting in /etc/systemd/system.conf + or /etc/systemd/system.conf.d/* to none to prevent a reboot if Ctrl-Alt-Delete is + pressed more than 7 times in 2 seconds. + + + + + + + + + + Disable Ctrl-Alt-Del Reboot Activation + + Red Hat Enterprise Linux 8 + + By default, the system will reboot when the + Ctrl-Alt-Del key sequence is pressed. + + + + + + + + + + Verify that Interactive Boot is Disabled + + Red Hat Enterprise Linux 8 + + The ability for users to perform interactive startups should + be disabled. + + + + + + + + + + + + + + Require Authentication for Emergency Systemd Target + + Red Hat Enterprise Linux 8 + + The requirement for a password to boot into emergency mode + should be configured correctly. + + + + + + + + + + + + + Require Authentication for Single User Mode + + Red Hat Enterprise Linux 8 + + The requirement for a password to boot into single-user mode + should be configured correctly. + + + + + + + + + + Support session locking with tmux + + Red Hat Enterprise Linux 8 + + Check if tmux is configured to exec at the end of bashrc. + + + + + + + + + + Configure tmux to lock session after inactivity + + Red Hat Enterprise Linux 8 + + Check if tmux is configured to lock sessions after period of inactivity. + + + + + + + + + + + Configure the tmux Lock Command + + Red Hat Enterprise Linux 8 + + Check if the vlock command is configured to be used as a locking mechanism in tmux. + + + + + + + + + + + Prevent user from disabling the screen lock + + Red Hat Enterprise Linux 8 + + Check that tmux is not listed in /etc/shells + + + + + + + + + + Configure opensc Smart Card Drivers + + Red Hat Enterprise Linux 8 + + Configure the organization's smart card driver so that only + the smart card in use by the organization will be recognized by the system. + + + + + + + + + + Force opensc To Use Defined Smart Card Driver + + Red Hat Enterprise Linux 8 + + Force opensc to use the organization's smart card driver so that only + the smart card in use by the organization will be recognized by the system. + + + + + + + + + + Ensure All Accounts on the System Have Unique User IDs + + Red Hat Enterprise Linux 8 + + All accounts on the system should have unique IDs for proper accountability. + + + + + + + + + + Only Authorized Local User Accounts Exist on Operating System + + Red Hat Enterprise Linux 8 + + Besides the default operating system user, there should be no other users + except the users that are authorized to exist locally on the operating system. + + + + + + + + + + Ensure All Groups on the System Have Unique Group ID + + Red Hat Enterprise Linux 8 + + All groups on the system should have unique names for proper accountability. + + + + + + + + + + Ensure All Groups on the System Have Unique Group Names + + Red Hat Enterprise Linux 8 + + All groups on the system should have unique names for proper accountability. + + + + + + + + + + Set Account Expiration Following Inactivity + + Red Hat Enterprise Linux 8 + + The accounts should be configured to expire automatically following password expiration. + + + + + + + + + + Ensure All Accounts on the System Have Unique Names + + Red Hat Enterprise Linux 8 + + All accounts on the system should have unique names for proper accountability. + + + + + + + + + + Set Password Maximum Age + + Red Hat Enterprise Linux 8 + + The maximum password age policy should meet minimum requirements. + + + + + + + + + + Set Password Minimum Age + + Red Hat Enterprise Linux 8 + + The minimum password age policy should be set appropriately. + + + + + + + + + + Set Password Minimum Length in login.defs + + Red Hat Enterprise Linux 8 + + The password minimum length should be set appropriately. + + + + + + + + + + Set Existing Passwords Maximum Age + + Red Hat Enterprise Linux 8 + + Set Existing Passwords Maximum Age + + + + + + + + + + + Set Existing Passwords Minimum Age + + Red Hat Enterprise Linux 8 + + Passwords for existing accounts much satisfy minimum age requirements + + + + + + + + + + + Set Password Warning Age + + Red Hat Enterprise Linux 8 + + The password expiration warning age should be set appropriately. + + + + + + + + + + Verify All Account Password Hashes are Shadowed + + Red Hat Enterprise Linux 8 + + All password hashes should be shadowed. + + + + + + + + + + Verify All Account Password Hashes are Shadowed with SHA512 + + Red Hat Enterprise Linux 8 + + All password hashes should be shadowed. + + + + + + + + + + Set number of Password Hashing Rounds - password-auth + + Red Hat Enterprise Linux 8 + + The number of rounds for password hashing should be set correctly. + + + + + + + + + + + + + + Set number of Password Hashing Rounds - system-auth + + Red Hat Enterprise Linux 8 + + The number of rounds for password hashing should be set correctly. + + + + + + + + + + + + + + All GIDs referenced in /etc/passwd must be defined in /etc/group + + Red Hat Enterprise Linux 8 + + All GIDs referenced in /etc/passwd must be defined in /etc/group. + + + + + + + + + + Prevent Login to Accounts With Empty Password + + Red Hat Enterprise Linux 8 + + The file /etc/pam.d/system-auth should not contain the nullok option + + + + + + + + + + Ensure There Are No Accounts With Blank or Null Passwords + + Red Hat Enterprise Linux 8 + + The file /etc/shadow shows that there aren't empty passwords + + + + + + + + + + Ensure there are no legacy + NIS entries in /etc/group + + Red Hat Enterprise Linux 8 + + No lines starting with + are in /etc/group + + + + + + + + + + Ensure there are no legacy + NIS entries in /etc/passwd + + Red Hat Enterprise Linux 8 + + No lines starting with + are in /etc/passwd + + + + + + + + + + Ensure there are no legacy + NIS entries in /etc/shadow + + Red Hat Enterprise Linux 8 + + No lines starting with + are in /etc/shadow + + + + + + + + + + Verify No netrc Files Exist + + Red Hat Enterprise Linux 8 + + The .netrc files contain login information used to auto-login into FTP servers and reside in the user's home directory. Any .netrc files should be removed. + + + + + + + + + + Verify Only Root Has UID 0 + + Red Hat Enterprise Linux 8 + + Only the root account should be assigned a user id of 0. + + + + + + + + + + Verify Root Has A Primary GID 0 + + Red Hat Enterprise Linux 8 + + The root account should have primary group of 0 + + + + + + + + + + Direct root Logins Not Allowed + + Red Hat Enterprise Linux 8 + + Preventing direct root logins help ensure accountability for actions + taken on the system using the root account. + + + + + + + + + + + Ensure that System Accounts Do Not Run a Shell Upon Login + + Red Hat Enterprise Linux 8 + + The root account is the only system account that should have + a login shell. + + + + + + + + + + + + + + + + + + + + Restrict Serial Port Root Logins + + Red Hat Enterprise Linux 8 + + Preventing direct root login to serial port interfaces helps + ensure accountability for actions taken on the system using the root + account. + + + + + + + + + + Restrict Virtual Console Root Logins + + Red Hat Enterprise Linux 8 + + Preventing direct root login to virtual console devices + helps ensure accountability for actions taken on the system using the + root account. + + + + + + + + + + Enforce usage of pam_wheel for su authentication + + Red Hat Enterprise Linux 8 + + Only members of the wheel group should be able to authenticate through the su command. + + + + + + + + + + Ensure Home Directories are Created for New Users + + Red Hat Enterprise Linux 8 + + CREATE_HOME should be enabled + + + + + + + + + + Ensure the Logon Failure Delay is Set Correctly in login.defs + + Red Hat Enterprise Linux 8 + + The delay between failed authentication attempts should be + set for all users specified in /etc/login.defs + + + + + + + + + + Limit the Number of Concurrent Login Sessions Allowed Per User + + Red Hat Enterprise Linux 8 + + The maximum number of concurrent login sessions per user should meet + minimum requirements. + + + + + + + + + + + + + + Configure Polyinstantiation of /tmp Directories + + Red Hat Enterprise Linux 8 + + + + + + + + + + + + + Configure Polyinstantiation of /var/tmp Directories + + Red Hat Enterprise Linux 8 + + + + + + + + + + + + + Set Interactive Session Timeout + + Red Hat Enterprise Linux 8 + + Checks interactive shell timeout + + + + + + + + + + + User Initialization Files Must Be Group-Owned By The Primary User + + Red Hat Enterprise Linux 8 + + User Initialization Files Must Be Group-Owned By The Primary User + + + + + + + + + User Initialization Files Must Not Run World-Writable Programs + + Red Hat Enterprise Linux 8 + + User Initialization Files Must Not Execute World-Writable Programs + + + + + + + + + + User Initialization Files Must Be Owned By the Primary User + + Red Hat Enterprise Linux 8 + + User Initialization Files Must Be Owned By the Primary User + + + + + + + + + All Interactive Users Must Have A Home Directory Defined + + Red Hat Enterprise Linux 8 + + All Interactive Users Must Have A Home Directory Defined + + + + + + + + + + All Interactive Users Home Directories Must Exist + + Red Hat Enterprise Linux 8 + + All Interactive Users Home Directories Must Exist + + + + + + + + + + + All User Files and Directories In The Home Directory Must Be Group-Owned By The Primary User + + Red Hat Enterprise Linux 8 + + All User Files and Directories In The Home Directory Must Be Group-Owned By The Primary User + + + + + + + + + + All User Files and Directories In The Home Directory Must Have a Valid Owner + + Red Hat Enterprise Linux 8 + + All User Files and Directories In The Home Directory Must Have a Valid Owner + + + + + + + + + All User Files and Directories In The Home Directory Must Have Mode 0750 Or Less Permissive + + Red Hat Enterprise Linux 8 + + All User Files and Directories In The Home Directory Must Have Mode 0750 Or Less Permissive + + + + + + + + + + + All Interactive User Home Directories Must Be Group-Owned By The Primary User + + Red Hat Enterprise Linux 8 + + All interactive user's Home Directories must be group-owned by its user + + + + + + + + + + All Interactive User Home Directories Must Be Owned By The Primary User + + Red Hat Enterprise Linux 8 + + All interactive user's Home Directories must be owned by its user + + + + + + + + + + + All Interactive User Home Directories Must Have mode 0750 Or Less Permissive + + Red Hat Enterprise Linux 8 + + All Interactive User Home Directories Must Have mode 0750 Or Less Permissive + + + + + + + + + + Ensure that User Home Directories are not Group-Writable or World-Readable + + Red Hat Enterprise Linux 8 + + Ensure that User Home Directories are not Group-Writable or World-Readable + + + + + + + + + + Ensure that Root's Path Does Not Include World or Group-Writable Directories + + Red Hat Enterprise Linux 8 + + Check each directory in root's path and make use it does + not grant write permission to group and other + + + + + + + + + + Ensure that Root's Path Does Not Include Relative Paths or Null Directories + + Red Hat Enterprise Linux 8 + + The environment variable PATH should be set correctly for + the root user. + + + + + + + + + + + + + + + Ensure the Default Bash Umask is Set Correctly + + Red Hat Enterprise Linux 8 + + The default umask for users of the bash shell + + + + + + + + + + + Ensure the Default C Shell Umask is Set Correctly + + Red Hat Enterprise Linux 8 + + The default umask for users of the csh shell + + + + + + + + + + + Ensure the Default Umask is Set Correctly in login.defs + + Red Hat Enterprise Linux 8 + + The default umask for all users specified in /etc/login.defs + + + + + + + + + + + Ensure the Default Umask is Set Correctly in /etc/profile + + Red Hat Enterprise Linux 8 + + The default umask for all users should be set correctly + + + + + + + + + + + Ensure the Default Umask is Set Correctly For Interactive Users + + Red Hat Enterprise Linux 8 + + Ensure the Default Umask is Set Correctly For Interactive Users + + + + + + + + + + Enable Syscall Auditing + + Red Hat Enterprise Linux 8 + + Syscall auditing should not be disabled. + + + + + + + + + + + + + + + + Make the auditd Configuration Immutable + + Red Hat Enterprise Linux 8 + + Force a reboot to change audit rules is enabled + + + + + + + + + + + + + + + + + Record Events that Modify the System's Mandatory Access Controls + + Red Hat Enterprise Linux 8 + + Audit rules that detect changes to the system's rhisam access controls (SELinux) are enabled. + + + + + + + + + + + + + + + + + Record Events that Modify the System's Network Environment + + Red Hat Enterprise Linux 8 + + The network environment should not be modified by anything other than + administrator action. Any change to network parameters should be audited. + + + + + + + + + + + + + + + + + + + + + + + + + + + Record Attempts to Alter Process and Session Initiation Information + + Red Hat Enterprise Linux 8 + + Audit rules should capture information about session initiation. + + + + + + + + + + + + + + + + + + + + + Ensure auditd Collects System Administrator Actions - /etc/sudoers + + Red Hat Enterprise Linux 8 + + Audit actions taken by system administrators on the system - /etc/sudoers. + + + + + + + + + + + + + + + + + Ensure auditd Collects System Administrator Actions - /etc/sudoers.d/ + + Red Hat Enterprise Linux 8 + + Audit actions taken by system administrators on the system - /etc/sudoers.d/. + + + + + + + + + + + + + + + + + Record Events When Privileged Executables Are Run + + Red Hat Enterprise Linux 8 + + Ensure audit rule for all uses of privileged functions is enabled + + + + + + + + + + + + + + + + + + + + + + + Ensure auditd Collects System Administrator Actions + + Red Hat Enterprise Linux 8 + + Audit actions taken by system administrators on the system. + + + + + + + + + + + + + + + + + + + Shutdown System When Auditing Failures Occur + + Red Hat Enterprise Linux 8 + + The system will shutdown when auditing fails. + + + + + + + + + + + + + + + + + Record Events that Modify User/Group Information + + Red Hat Enterprise Linux 8 + + Audit rules should detect modification to system files that hold information about users and groups. + + + + + + + + + + + + + + + + + + + + + + + + + Record Access Events to Audit Log Directory + + Red Hat Enterprise Linux 8 + + Audit rules about the read events to /var/log/audit + + + + + + + + + + + + + + + + + System Audit Directories Must Be Group Owned By Root + + Red Hat Enterprise Linux 8 + + Checks that all /var/log/audit directories are group owned by the root user. + + + + + + + + + + + + + + + + + + + + + System Audit Directories Must Be Owned By Root + + Red Hat Enterprise Linux 8 + + Checks that all /var/log/audit directories are owned by the root user. + + + + + + + + + + + + + + + + + System Audit Logs Must Have Mode 0750 or Less Permissive + + Red Hat Enterprise Linux 8 + + Checks for correct permissions for audit logs. + + + + + + + + + + + + + + + + + System Audit Logs Must Be Group Owned By Root + + Red Hat Enterprise Linux 8 + + Checks that all audit log files are group owned by the root user. + + + + + + + + + + + + + + + + + + + + + + + System Audit Logs Must Be Owned By Root + + Red Hat Enterprise Linux 8 + + Checks that all /var/log/audit files and directories are owned by the root user and group. + + + + + + + + + + + + + + + + + + System Audit Logs Must Be Owned By Root + + Red Hat Enterprise Linux 8 + + Checks that all audit log files are owned by the root user. + + + + + + + + + + + + + + + + + System Audit Logs Must Have Mode 0640 or Less Permissive + + Red Hat Enterprise Linux 8 + + Checks for correct permissions for all audit log files. + + + + + + + + + + + + + + + + + Record Events that Modify the System's Discretionary Access Controls - umount + + Red Hat Enterprise Linux 8 + + The changing of file permissions and attributes should be audited. + + + + + + + + + + + + + + + + Ensure auditd Collects File Deletion Events by User + + Red Hat Enterprise Linux 8 + + Audit files deletion events. + + + + + + + + + + + + + + Ensure auditd Collects Unauthorized Access Attempts to Files (unsuccessful) + + Red Hat Enterprise Linux 8 + + Audit rules about the unauthorized access attempts to files (unsuccessful) are enabled. + + + + + + + + + + + + + + + Ensure auditd Collects Information on Kernel Module Loading and Unloading + + Red Hat Enterprise Linux 8 + + The audit rules should be configured to log information about kernel module loading and unloading. + + + + + + + + + + + + Ensure auditd Collects Information on Kernel Module Unloading - delete_module + + Red Hat Enterprise Linux 8 + + The audit rules should be configured to log information about kernel module loading and unloading. + + + + + + + + + + + + + + + + + + + + + + + + + Ensure auditd Collects Information on Kernel Module Loading and Unloading - finit_module + + Red Hat Enterprise Linux 8 + + The audit rules should be configured to log information about kernel module loading and unloading. + + + + + + + + + + + + + + + + + + + + + + + + + Ensure auditd Collects Information on Kernel Module Loading - init_module + + Red Hat Enterprise Linux 8 + + The audit rules should be configured to log information about kernel module loading and unloading. + + + + + + + + + + + + + + + + + + + + + + + + + Record Attempts to Alter Logon and Logout Events + + Red Hat Enterprise Linux 8 + + Audit rules should be configured to log successful and unsuccessful login and logout events. + + + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands + + Red Hat Enterprise Linux 8 + + Audit rules about the information on the use of privileged commands are enabled. + + + + + + + + + + + + + + + + + + + Record attempts to alter time through adjtimex + + Red Hat Enterprise Linux 8 + + Record attempts to alter time through adjtimex. + + + + + + + + + + + + + + + + + + + + + + + + + Record Attempts to Alter Time Through clock_settime + + Red Hat Enterprise Linux 8 + + Record attempts to alter time through clock_settime. + + + + + + + + + + + + + + + + + + + + + + + + + Record attempts to alter time through settimeofday + + Red Hat Enterprise Linux 8 + + Record attempts to alter time through settimeofday. + + + + + + + + + + + + + + + + + + + + + + + + + Record Attempts to Alter Time Through stime + + Red Hat Enterprise Linux 8 + + Record attempts to alter time through stime. Note that on + 64-bit architectures the stime system call is not defined in the audit + system calls lookup table. + + + + + + + + + + + + + + + + + + + + + + + Record Attempts to Alter the localtime File + + Red Hat Enterprise Linux 8 + + Record attempts to alter time through /etc/localtime. + + + + + + + + + + + + + + + + + Configure audispd Plugin To Send Logs To Remote Server + + Red Hat Enterprise Linux 8 + + remote_server setting in /etc/audit/audisp-remote.conf is set to a certain IP address or hostname + + + + + + + + + + Configure audispd's Plugin disk_full_action When Disk Is Full + + Red Hat Enterprise Linux 8 + + remote_server setting in /etc/audit/audisp-remote.conf is set to a certain IP address or hostname + + + + + + + + + Encrypt Audit Records Sent With audispd Plugin + + Red Hat Enterprise Linux 8 + + transport setting in /etc/audit/audisp-remote.conf is set to 'KRB5' + + + + + + + + + + Configure audispd's Plugin network_failure_action On Network Failure + + Red Hat Enterprise Linux 8 + + remote_server setting in /etc/audit/audisp-remote.conf is set to a certain IP address or hostname + + + + + + + + + Configure auditd to use audispd's syslog plugin + + Red Hat Enterprise Linux 8 + + active setting in /etc/audit/plugins.d/syslog.conf is set to 'yes' + + + + + + + + + + Configure auditd Disk Error Action on Disk Error + + Red Hat Enterprise Linux 8 + + disk_error_action setting in /etc/audit/auditd.conf is set to a certain action + + + + + + + + + + Configure auditd Disk Error Action on Disk Error + + Red Hat Enterprise Linux 8 + + disk_error_action setting in /etc/audit/auditd.conf is set to SYSLOG, SINGLE or HALT + + + + + + + + + + + Configure auditd Disk Full Action when Disk Space Is Full + + Red Hat Enterprise Linux 8 + + disk_full_action setting in /etc/audit/auditd.conf is set to a certain action + + + + + + + + + + Configure auditd Disk Full Action when Disk Space Is Full + + Red Hat Enterprise Linux 8 + + disk_full_action setting in /etc/audit/auditd.conf is set to SYSLOG, SINGLE or HALT + + + + + + + + + + + Configure auditd mail_acct Action on Low Disk Space + + Red Hat Enterprise Linux 8 + + action_mail_acct setting in /etc/audit/auditd.conf is set to a certain account + + + + + + + + + + Configure auditd admin_space_left Action on Low Disk Space + + Red Hat Enterprise Linux 8 + + admin_space_left_action setting in /etc/audit/auditd.conf is set to a certain action + + + + + + + + + + Configure auditd flush priority + + Red Hat Enterprise Linux 8 + + The setting for flush in /etc/audit/auditd.conf + + + + + + + + + + Configure auditd Max Log File Size + + Red Hat Enterprise Linux 8 + + max_log_file setting in /etc/audit/auditd.conf is set to at least a certain value + + + + + + + + + + Configure auditd max_log_file_action Upon Reaching Maximum Log Size + + Red Hat Enterprise Linux 8 + + max_log_file_action setting in /etc/audit/auditd.conf is set to a certain action + + + + + + + + + + Configure auditd max_log_file_action Upon Reaching Maximum Log Size + + Red Hat Enterprise Linux 8 + + max_log_file_action setting in /etc/audit/auditd.conf is set to a certain action + + + + + + + + + + Configure auditd Number of Logs Retained + + Red Hat Enterprise Linux 8 + + num_logs setting in /etc/audit/auditd.conf is set to at least a certain value + + + + + + + + + + Configure auditd space_left on Low Disk Space + + Red Hat Enterprise Linux 8 + + space_left setting in /etc/audit/auditd.conf is set to at least a certain value + + + + + + + + + + Configure auditd space_left Action on Low Disk Space + + Red Hat Enterprise Linux 8 + + space_left_action setting in /etc/audit/auditd.conf is set to a certain action + + + + + + + + + + Configure auditd space_left on Low Disk Space + + Red Hat Enterprise Linux 8 + + space_left setting in /etc/audit/auditd.conf is set to at least a certain value + + + + + + + + + + Set hostname as computer node name in audit logs + + Red Hat Enterprise Linux 8 + + Ensure 'name_format' is configured with value 'hostname' in /etc/audit/auditd.conf + + + + + + + + + + Appropriate Action Must be Setup When the Internal Audit Event Queue is Full + + Red Hat Enterprise Linux 8 + + Ensure 'overflow_action' is configured with value '(syslog|single|halt)' in /etc/audit/auditd.conf + + + + + + + + + + Configure audit according to OSPP requirements + + Red Hat Enterprise Linux 8 + + Compare configure audit rules against the recommended pre-configured files. + + + + + + + + + + + + + Disable Recovery Booting + + Red Hat Enterprise Linux 8 + + Recovery mode should be disabled. + + + + + + + + + + Configure kernel to trust the CPU random number generator + + Red Hat Enterprise Linux 8 + + Ensure the kernel is configured to trust the CPU hardware random number generator. + + + + + + + + + + + + + + Set the Boot Loader Admin Username to a Non-Default Value + + Red Hat Enterprise Linux 8 + + The grub2 boot loader superuser should have a username that is hard to guess. + + + + + + + + + + Boot Loader Is Not Installed On Removeable Media + + Red Hat Enterprise Linux 8 + + Ensure 'set root' is configured with value '['|\(](?!fd)(?!cd)(?!usb).*['|\)]' in /boot/grub2/grub.cfg + + + + + + + + + + + + Set Boot Loader Password in grub2 + + Red Hat Enterprise Linux 8 + + The grub2 boot loader should have password protection enabled. + + + + + + + + + + Set the UEFI Boot Loader Admin Username to a Non-Default Value + + Red Hat Enterprise Linux 8 + + The grub2 boot loader superuser should have a username that is hard to guess. + + + + + + + + + + Set the UEFI Boot Loader Password + + Red Hat Enterprise Linux 8 + + The UEFI grub2 boot loader should have password protection enabled. + + + + + + + + + + UEFI Boot Loader Is Not Installed On Removeable Media + + Red Hat Enterprise Linux 8 + + Ensure the system is not configured to use a boot loader on removable media. + + + + + + + + + + Ensure all zIPL boot entries are BLS compliant + + Red Hat Enterprise Linux 8 + + Check if /etc/zipl.conf configures any boot entry + + + + + + + + + + Ensure zIPL bootmap is up to date + + Red Hat Enterprise Linux 8 + + Check if /boot/bootmap is up to date + + + + + + + + + + Ensure debug-shell service is not enabled in zIPL + + Red Hat Enterprise Linux 8 + + Ensure systemd.debug-shell option is not configured in the 'options' line in /boot/loader/entries/*.conf. Make sure that newly installed kernels won't have this option, it should not be configured in /etc/kernel/cmdline. + + + + + + + + + + Configure Logwatch HostLimit Line + + Red Hat Enterprise Linux 8 + + Test if HostLimit line in logwatch.conf is set appropriately. + + + + + + + + + Configure Logwatch SplitHosts Line + + Red Hat Enterprise Linux 8 + + Check if SplitHosts line in logwatch.conf is set appropriately. + + + + + + + + + Ensure cron Is Logging To Rsyslog + + Red Hat Enterprise Linux 8 + + Rsyslog should be configured to capture cron messages. + + + + + + + + + + + Ensure Rsyslog Authenticates Off-Loaded Audit Records + + Red Hat Enterprise Linux 8 + + Rsyslogd must authenticate remote system its sending logs to. + + + + + + + + + + + + + Ensure Rsyslog Encrypts Off-Loaded Audit Records + + Red Hat Enterprise Linux 8 + + Rsyslogd must encrypt the off-loading of logs off of the system. + + + + + + + + + + + + + Ensure Rsyslog Encrypts Off-Loaded Audit Records + + Red Hat Enterprise Linux 8 + + Rsyslogd must encrypt the off-loading of logs off of the system. + + + + + + + + + + + + + Ensure Log Files Are Owned By Appropriate Group + + Red Hat Enterprise Linux 8 + + All syslog log files should be owned by the appropriate group. + + + + + + + + + + Ensure Log Files Are Owned By Appropriate User + + Red Hat Enterprise Linux 8 + + All syslog log files should be owned by the appropriate user. + + + + + + + + + + Ensure System Log Files Have Correct Permissions + + Red Hat Enterprise Linux 8 + + File permissions for all syslog log files should be set correctly. + + + + + + + + + + Ensure remote access methods are monitored in Rsyslog + + Red Hat Enterprise Linux 8 + + Rsyslog should be configured to monitor remote access methods. + + + + + + + + + + + + Ensure Logrotate Runs Periodically + + Red Hat Enterprise Linux 8 + + + The frequency of automatic log files rotation performed by the logrotate utility should be configured to run daily + + + + + + + + + + + + + Ensure rsyslog Does Not Accept Remote Messages Unless Acting As Log Server + + Red Hat Enterprise Linux 8 + + rsyslogd should reject remote messages + + + + + + + + + + Ensure Logs Sent To Remote Host + + Red Hat Enterprise Linux 8 + + Syslog logs should be sent to a remote loghost + + + + + + + + + + + Configure TLS for rsyslog remote logging + + Red Hat Enterprise Linux 8 + + Check that all needed TLS-related options are present + + + + + + + + + + Configure CA certificate for rsyslog remote logging + + Red Hat Enterprise Linux 8 + + Check that the CA certificate path is set + + + + + + + + + + Configure Multiple DNS Servers in /etc/resolv.conf + + Red Hat Enterprise Linux 8 + + Multiple Domain Name System (DNS) Servers should be configured + in /etc/resolv.conf. + + + + + + + + + + + + + + + + + Disable Client Dynamic DNS Updates + + Red Hat Enterprise Linux 8 + + Clients should not automatically update their own + DNS record. + + + + + + + + + + + Disable Zeroconf Networking + + Red Hat Enterprise Linux 8 + + Disable Zeroconf automatic route assignment in the + 169.254.0.0 subnet. + + + + + + + + + Prevent non-Privileged Users from Modifying Network Interfaces using nmcli + + Red Hat Enterprise Linux 8 + + polkit is properly configured to prevent non-privileged users from changing networking settings + + + + + + + + + + Ensure System is Not Acting as a Network Sniffer + + Red Hat Enterprise Linux 8 + + Disable the network sniffer + + + + + + + + + + Set Default firewalld Zone for Incoming Packets + + Red Hat Enterprise Linux 8 + + Change the default firewalld zone to drop. + + + + + + + + + + Manually Assign IPv6 Router Address + + Red Hat Enterprise Linux 8 + + Define default gateways for IPv6 traffic + + + + + + + + + + Use Privacy Extensions for Address + + Red Hat Enterprise Linux 8 + + Enable privacy extensions for IPv6 + + + + + + + + + + Manually Assign Global IPv6 Address + + Red Hat Enterprise Linux 8 + + Manually configure addresses for IPv6 + + + + + + + + + + + Disable IPv6 Networking Support Automatic Loading + + Red Hat Enterprise Linux 8 + + The disable option will allow the IPv6 module to be inserted, but prevent address assignment and activation of the network stack. + + + + + + + + + + Disable Support for RPC IPv6 + + Red Hat Enterprise Linux 8 + + Disable ipv6 based rpc services + + + + + + + + + + Deactivate Wireless Network Interfaces + + Red Hat Enterprise Linux 8 + + All wireless interfaces should be disabled. + + + + + + + + + + Ensure All World-Writable Directories Are Owned by root user + + Red Hat Enterprise Linux 8 + + All world writable directories should be owned by root. + + + + + + + + + + Verify that All World-Writable Directories Have Sticky Bits Set + + Red Hat Enterprise Linux 8 + + The sticky bit should be set for all world-writable directories. + + + + + + + + + + Ensure All World-Writable Directories Are Owned by a System Account + + Red Hat Enterprise Linux 8 + + All world writable directories should be owned by a system user. + + + + + + + + + Ensure All World-Writable Directories Are Group Owned by a System Account + + Red Hat Enterprise Linux 8 + + All world writable directories should be group owned by a system user. + + + + + + + + + + Verify that local System.map file (if exists) is readable only by root + + Red Hat Enterprise Linux 8 + + + Checks that /boot/System.map-* are only readable by root. + + + + + + + + + + + Ensure All SGID Executables Are Authorized + + Red Hat Enterprise Linux 8 + + Evaluates to true if all files with SGID set are owned by RPM packages. + + + + + + + + + + Ensure All SUID Executables Are Authorized + + Red Hat Enterprise Linux 8 + + Evaluates to true if all files with SUID set are owned by RPM packages. + + + + + + + + + + Ensure No World-Writable Files Exist + + Red Hat Enterprise Linux 8 + + The world-write permission should be disabled for all files. + + + + + + + + + + Ensure All Files Are Owned by a Group + + Red Hat Enterprise Linux 8 + + All files should be owned by a group + + + + + + + + + + Ensure All Files Are Owned by a User + + Red Hat Enterprise Linux 8 + + All files should be owned by a user + + + + + + + + + + Verify that system commands files are group owned by root or a system account + + Red Hat Enterprise Linux 8 + + + Checks that system commands in /bin /sbin /usr/bin /usr/sbin /usr/local/bin /usr/local/sbin + are owned by root group or a system account. + + + + + + + + + + + Verify that System Executables Have Root Ownership + + Red Hat Enterprise Linux 8 + + + Checks that /bin, /sbin, /usr/bin, /usr/sbin, /usr/local/bin, + /usr/local/sbin, /usr/libexec, and objects therein, are owned by root. + + + + + + + + + + + + Verify that System Executables Have Restrictive Permissions + + Red Hat Enterprise Linux 8 + + + Checks that binary files under /bin, /sbin, /usr/bin, /usr/sbin, + /usr/local/bin, /usr/local/sbin, and /usr/libexec are not group-writable or world-writable. + + + + + + + + + + + Disable Kernel Support for USB via Bootloader Configuration + + Red Hat Enterprise Linux 8 + + Ensure 'GRUB_CMDLINE_LINUX' is configured with value 'nousb' in /etc/default/grub + + + + + + + + + Add nodev Option to Non-Root Local Partitions + + Red Hat Enterprise Linux 8 + + The nodev mount option prevents files from being interpreted + as character or block devices. Legitimate character and block devices + should exist in the /dev directory on the root partition or within chroot + jails built for system services. All other locations should not allow + character and block devices. + + + + + + + + + + Bind Mount /var/tmp To /tmp + + Red Hat Enterprise Linux 8 + + The /var/tmp directory should be bind mounted to /tmp in + order to consolidate temporary storage into one location protected by the + same techniques as /tmp. + + + + + + + + + + + + + + Disable core dump backtraces + + Red Hat Enterprise Linux 8 + + Ensure 'ProcessSizeMax' is configured with value '0 in section 'Coredump' in /etc/systemd/coredump.conf + + + + + + + + + + Disable storing core dump + + Red Hat Enterprise Linux 8 + + Ensure 'Storage' is configured with value 'none in section 'Coredump' in /etc/systemd/coredump.conf + + + + + + + + + + Disable Core Dumps for All Users + + Red Hat Enterprise Linux 8 + + Core dumps for all users should be disabled + + + + + + + + + + + + + + Set Daemon Umask + + Red Hat Enterprise Linux 8 + + The daemon umask should be set as appropriate + + + + + + + + + + Enable ExecShield via sysctl + + Red Hat Enterprise Linux 8 + + The kernel runtime parameter 'kernel.exec-shield' should not be disabled and set to 1 on 32-bit systems. + + + + + + + + + + + + + Enable NX or XD Support in the BIOS + + Red Hat Enterprise Linux 8 + + The NX (no-execution) bit flag should be set on the system. + + + + + + + + + + + + Install PAE Kernel on Supported 32-bit x86 Systems + + Red Hat Enterprise Linux 8 + + The RPM package kernel-PAE should be installed on 32-bit + systems. + + + + + + + + + + + + + + + + + + Ensure SELinux Not Disabled in /etc/default/grub + + Red Hat Enterprise Linux 8 + + + Check if selinux=0 OR enforcing=0 within the GRUB2 configuration files, fail if found. + + + + + + + + + + + + + Ensure No Device Files are Unlabeled by SELinux + + Red Hat Enterprise Linux 8 + + All device files in /dev should be assigned an SELinux security context other than 'device_t' and 'unlabeled_t'. + + + + + + + + + + + Ensure No Daemons are Unconfined by SELinux + + Red Hat Enterprise Linux 8 + + All pids in /proc should be assigned an SELinux security context other than 'unconfined_service_t'. + + + + + + + + + + Configure SELinux Policy + + Red Hat Enterprise Linux 8 + + The SELinux policy should be set appropriately. + + + + + + + + + + Ensure SELinux State is Enforcing + + Red Hat Enterprise Linux 8 + + The SELinux state should be enforcing the local policy. + + + + + + + + + + Prefer to use a 64-bit Operating System when supported + + Red Hat Enterprise Linux 8 + + Check if the system supports a 64-bit Operating System + + + + + + + + + + + Make sure that the dconf databases are up-to-date with regards to respective keyfiles + + Red Hat Enterprise Linux 8 + + Make sure that the dconf databases are up-to-date with regards to respective keyfiles. + + + + + + + + + + + + + + + + + + + + Configure GNOME3 DConf User Profile + + Red Hat Enterprise Linux 8 + + The DConf User profile should have the local DB configured. + + + + + + + + + + Disable the GNOME3 Login Restart and Shutdown Buttons + + Red Hat Enterprise Linux 8 + + Disable the GNOME3 Login GUI Restart and Shutdown buttons to all users on the login screen. + + + + + + + + + + + + + + Disable the GNOME3 Login User List + + Red Hat Enterprise Linux 8 + + Disable the GNOME3 GUI listing of all known users on the login screen. + + + + + + + + + + + + + + + Enable the GNOME3 Login Smartcard Authentication + + Red Hat Enterprise Linux 8 + + Enable smartcard authentication in the GNOME3 Login GUI. + + + + + + + + + + + + + + Set the GNOME3 Login Number of Failures + + Red Hat Enterprise Linux 8 + + Set the GNOME3 number of login failure attempts. + + + + + + + + + + + + + + + Disable GDM Automatic Login + + Red Hat Enterprise Linux 8 + + Disable the GNOME Display Manager (GDM) ability to allow users to + automatically login. + + + + + + + + + + + Disable GDM Guest Login + + Red Hat Enterprise Linux 8 + + Disable the GNOME Display Manager (GDM) ability to allow guest users + to login. + + + + + + + + + + + Disable XDMCP in GDM + + Red Hat Enterprise Linux 8 + + Ensure 'Enable' is configured with value 'false in section 'xdmcp' in /etc/gdm/custom.conf + + + + + + + + + + + + Disable GNOME3 automount + + Red Hat Enterprise Linux 8 + + The system's default desktop environment, GNOME3, will mount + devices and removable media (such as DVDs, CDs and USB flash drives) + whenever they are inserted into the system. Disable automount within GNOME3. + + + + + + + + + + + + + + + Disable GNOME3 automount-open + + Red Hat Enterprise Linux 8 + + The system's default desktop environment, GNOME3, will mount + devices and removable media (such as DVDs, CDs and USB flash drives) + whenever they are inserted into the system. Disable automount-open within GNOME3. + + + + + + + + + + + + + + + Disable GNOME3 autorun + + Red Hat Enterprise Linux 8 + + The system's default desktop environment, GNOME3, will mount + devices and removable media (such as DVDs, CDs and USB flash drives) + whenever they are inserted into the system. Disable autorun within GNOME3. + + + + + + + + + + + + + + + Disable All GNOME3 Thumbnailers + + Red Hat Enterprise Linux 8 + + The system's default desktop environment, GNOME3, uses a + number of different thumbnailer programs to generate thumbnails for any + new or modified content in an opened folder. Disable the execution of + these thumbnail applications within GNOME3. + + + + + + + + + + + + + + Disable WIFI Network Connection Creation in GNOME3 + + Red Hat Enterprise Linux 8 + + Disable the GNOME3 wireless network creation settings. + + + + + + + + + + + + + + Disable WIFI Network Notification in GNOME3 + + Red Hat Enterprise Linux 8 + + Disable the GNOME3 wireless network notification. + + + + + + + + + + + + + + Require Credential Prompting for Remote Access in GNOME3 + + Red Hat Enterprise Linux 8 + + Configure GNOME3 to require credential prompting for remote access. + + + + + + + + + + + + + + + Require Encryption for Remote Access in GNOME3 + + Red Hat Enterprise Linux 8 + + Configure GNOME3 to require encryption for remote access connections. + + + + + + + + + + + + + + + Enable GNOME3 Screensaver Idle Activation + + Red Hat Enterprise Linux 8 + + Idle activation of the screen saver should be enabled. + + + + + + + + + + + + + + + Ensure Users Cannot Change GNOME3 Screensaver Idle Activation + + Red Hat Enterprise Linux 8 + + Idle activation of the screen saver should not be changed by users. + + + + + + + + + + + + + + Set GNOME3 Screensaver Inactivity Timeout + + Red Hat Enterprise Linux 8 + + The allowed period of inactivity before the screensaver is activated. + + + + + + + + + + + + + + + Set GNOME3 Screensaver Lock Delay After Activation Period + + Red Hat Enterprise Linux 8 + + Idle activation of the screen lock should be enabled immediately or + after a delay. + + + + + + + + + + + + + + + Enable GNOME3 Screensaver Lock After Idle Period + + Red Hat Enterprise Linux 8 + + Idle activation of the screen lock should be enabled. + + + + + + + + + + + + + + + Ensure Users Cannot Change GNOME3 Screensaver Lock After Idle Period + + Red Hat Enterprise Linux 8 + + Idle activation of the screen lock should not be changed by users. + + + + + + + + + + + + + + Implement Blank Screensaver + + Red Hat Enterprise Linux 8 + + The GNOME3 screensaver should be blank. + + + + + + + + + + + + + + + Disable Full User Name on Splash Shield + + Red Hat Enterprise Linux 8 + + GNOME3 screen splash shield should not display full name of logged in user. + + + + + + + + + + + + + + + Ensure Users Cannot Change GNOME3 Screensaver Settings + + Red Hat Enterprise Linux 8 + + Ensure that users cannot change GNOME3 screensaver idle and lock settings. + + + + + + + + + + + + + + Ensure Users Cannot Change GNOME3 Session Idle Settings + + Red Hat Enterprise Linux 8 + + Ensure that users cannot change GNOME3 session idle settings. + + + + + + + + + + + + + + Disable Ctrl-Alt-Del Reboot Key Sequence in GNOME3 + + Red Hat Enterprise Linux 8 + + Disable the GNOME3 ctrl-alt-del reboot key sequence in GNOME3. + + + + + + + + + + + + + + + Disable Geolocation in GNOME3 + + Red Hat Enterprise Linux 8 + + Disable GNOME3 Geolocation for the clock and system. + + + + + + + + + + + + + + + + Disable Power Settings in GNOME3 + + Red Hat Enterprise Linux 8 + + Disable GNOME3 power settings. + + + + + + + + + + + + + + The Installed Operating System Is FIPS 140-2 Certified + + Red Hat Enterprise Linux 8 + + + The operating system installed on the system is a certified operating system that meets FIPS 140-2 requirements. + + + + + + + + + + + + + + + + + + + The Installed Operating System Is Vendor Supported + + Red Hat Enterprise Linux 8 + + + The operating system installed on the system is supported by a vendor that provides security patches. + + + + + + + + + + + + + + + + + Configure BIND to use System Crypto Policy + + Red Hat Enterprise Linux 8 + + BIND should be configured to use the system-wide crypto policy setting. + + + + + + + + + + + Configure System Cryptography Policy + + Red Hat Enterprise Linux 8 + + Ensure crypto policy is correctly configured in /etc/crypto-policies/config, and the policy is current. + + + + + + + + + + + + + Configure GnuTLS library to use DoD-approved TLS Encryption + + Red Hat Enterprise Linux 8 + + Check presence of +VERS-ALL:-VERS-DTLS0.9:-VERS-SSL3.0:-VERS-TLS1.0:-VERS-TLS1.1:-VERS-DTLS1.0 in /etc/crypto-policies/back-ends/gnutls.config + + + + + + + + + + Configure Kerberos to use System Crypto Policy + + Red Hat Enterprise Linux 8 + + Kerberos should be configured to use the system-wide crypto policy setting. + + + + + + + + + + + Configure Libreswan to use System Crypto Policy + + Red Hat Enterprise Linux 8 + + Libreswan should be configured to use the system-wide crypto policy setting. + + + + + + + + + + + Configure OpenSSL library to use System Crypto Policy + + Red Hat Enterprise Linux 8 + + OpenSSL should be configured to use the system-wide crypto policy setting. + + + + + + + + + + Configure OpenSSL library to use TLS Encryption + + Red Hat Enterprise Linux 8 + + Configure OpenSSL library to use TLS Encryption + + + + + + + + + + + + + + Configure SSH to use System Crypto Policy + + Red Hat Enterprise Linux 8 + + SSH should be configured to use the system-wide crypto policy setting. + + + + + + + + + + Harden OpenSSL Crypto Policy + + Red Hat Enterprise Linux 8 + + Ensure 'Ciphersuites' is configured with value 'TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256' in /etc/crypto-policies/back-ends/opensslcnf.config + + + + + + + + + + Harden SSH client Crypto Policy + + Red Hat Enterprise Linux 8 + + Ensure the ssh client ciphers are configured correctly in /etc/ssh/ssh_config.d/02-ospp.conf + + + + + + + + + + + + + + + + Configure SSH Client to Use FIPS 140-2 Validated Ciphers: openssh.config + + Red Hat Enterprise Linux 8 + + Limit the Ciphers to those which are FIPS-approved. + + + + + + + + + + Configure SSH Server to Use FIPS 140-2 Validated Ciphers: opensshserver.config + + Red Hat Enterprise Linux 8 + + Limit the Ciphers to those which are FIPS-approved. + + + + + + + + + + Harden SSHD Crypto Policy + + Red Hat Enterprise Linux 8 + + Ensure 'CRYPTO_POLICY' is configured with value ''-oCiphers=aes256-ctr,aes128-ctr,aes256-cbc,aes128-cbc -oMACs=hmac-sha2-512,hmac-sha2-256 -oGSSAPIKeyExchange=no -oKexAlgorithms=ecdh-sha2-nistp521,ecdh-sha2-nistp384,ecdh-sha2-nistp256,diffie-hellman-group14-sha1 -oHostKeyAlgorithms=ssh-rsa,ecdsa-sha2-nistp384,ecdsa-sha2-nistp256 -oPubkeyAcceptedKeyTypes=rsa-sha2-512,rsa-sha2-256,ssh-rsa,ecdsa-sha2-nistp384,ecdsa-sha2-nistp256'' in /etc/crypto-policies/back-ends/opensshserver.config + + + + + + + + + + + + + + + + + + + + Configure SSH Client to Use FIPS 140-2 Validated MACs: openssh.config + + Red Hat Enterprise Linux 8 + + Limit the Message Authentication Codes (MACs) to those which are FIPS-approved. + + + + + + + + + + Configure SSH Server to Use FIPS 140-2 Validated MACs: opensshserver.config + + Red Hat Enterprise Linux 8 + + Limit the Message Authentication Codes (MACs) to those which are FIPS-approved. + + + + + + + + + + OpenSSL uses strong entropy source + + Red Hat Enterprise Linux 8 + + OpenSSL should be configured to generate random data with strong entropy. + + + + + + + + + + Install Virus Scanning Software + + Red Hat Enterprise Linux 8 + + Antivirus software should be installed. + + + + + + + + + + Install Intrusion Detection Software + + Red Hat Enterprise Linux 8 + + Intrusion detection software or SELinux should be installed and enabled. + + + + + + + + + + + Install McAfee Virus Scanning Software + + Red Hat Enterprise Linux 8 + + McAfee Antivirus software should be installed. + + + + + + + + + + Install the McAfee Runtime Libraries and Linux Agent + + Red Hat Enterprise Linux 8 + + Install the McAfee Runtime Libraries (MFErt) and Linux Agent (MFEcma). + + + + + + + + + + Virus Scanning Software Definitions Are Updated + + Red Hat Enterprise Linux 8 + + Verify that McAfee AntiVirus definitions have been updated. + + + + + + + + + Ensure McAfee Endpoint Security for Linux (ENSL) is running + + Red Hat Enterprise Linux 8 + + Ensure that McAfee Endpoint Security for Linux (ENSL) is running. + + + + + + + + + + Install the Asset Configuration Compliance Module (ACCM) + + Red Hat Enterprise Linux 8 + + Install the Asset Configuration Compliance Module (ACCM). + + + + + + + + + Install the Policy Auditor (PA) Module + + Red Hat Enterprise Linux 8 + + Install the Policy Auditor (PA) Module. + + + + + + + + + Enable Dracut FIPS Module + + Red Hat Enterprise Linux 8 + + fips module should be enabled in Dracut configuration + + + + + + + + + + Enable FIPS Mode + + Red Hat Enterprise Linux 8 + + Check if FIPS mode is enabled on the system + + + + + + + + + + + + + + + + + + Ensure '/etc/system-fips' exists + + Red Hat Enterprise Linux 8 + + Check /etc/system-fips exists + + + + + + + + + Set kernel parameter 'crypto.fips_enabled' to 1 + + Red Hat Enterprise Linux 8 + + The kernel 'crypto.fips_enabled' parameter should be set to '1' in system runtime. + + + + + + + + + + Build and Test AIDE Database + + Red Hat Enterprise Linux 8 + + The aide database must be initialized. + + + + + + + + + + + + Configure AIDE to Verify the Audit Tools + + Red Hat Enterprise Linux 8 + + The Red Hat Enterprise Linux 8 operating system file integrity tool must be configured to protect the integrity of the audit tools. + + + + + + + + + + + + + + + + + Configure Periodic Execution of AIDE + + Red Hat Enterprise Linux 8 + + By default, AIDE does not install itself for periodic + execution. Periodically running AIDE is necessary to reveal + unexpected changes in installed files. + + + + + + + + + + + + + + + + + Configure Notification of Post-AIDE Scan Details + + Red Hat Enterprise Linux 8 + + AIDE should notify appropriate personnel of the details + of a scan after the scan has been run. + + + + + + + + + + + + + + + Configure AIDE to Use FIPS 140-2 for Validating Hashes + + Red Hat Enterprise Linux 8 + + AIDE should be configured to use the FIPS 140-2 + cryptographic hashes. + + + + + + + + + + + + Configure AIDE to Verify Access Control Lists (ACLs) + + Red Hat Enterprise Linux 8 + + AIDE should be configured to verify Access Control Lists (ACLs). + + + + + + + + + + + Configure AIDE to Verify Extended Attributes + + Red Hat Enterprise Linux 8 + + AIDE should be configured to verify extended file attributes. + + + + + + + + + + + Verify File Hashes with RPM + + Red Hat Enterprise Linux 8 + + Verify the RPM digests of system binaries using the RPM database. + + + + + + + + + + Verify and Correct Ownership with RPM + + Red Hat Enterprise Linux 8 + + Verify ownership of installed packages + by comparing the installed files with information about the + files taken from the package metadata stored in the RPM + database. + + + + + + + + + + + Verify and Correct File Permissions with RPM + + Red Hat Enterprise Linux 8 + + Verify the permissions of installed packages + by comparing the installed files with information about the + files taken from the package metadata stored in the RPM + database. + + + + + + + + + + Ensure a dedicated group owns sudo + + Red Hat Enterprise Linux 8 + + This test makes sure that /usr/bin/sudo is owned by the group set in var_sudo_dedicated_group + + + + + + + + + + + Ensure Users Re-Authenticate for Privilege Escalation - sudo !authenticate + + Red Hat Enterprise Linux 8 + + Checks sudo usage without authentication + + + + + + + + + + + Ensure Users Re-Authenticate for Privilege Escalation - sudo NOPASSWD + + Red Hat Enterprise Linux 8 + + Checks sudo usage without password + + + + + + + + + + + Ensure Users Re-Authenticate for Privilege Escalation - sudo + + Red Hat Enterprise Linux 8 + + Checks sudo usage without password + + + + + + + + + + + The operating system must require Re-Authentication when using the sudo command. Ensure sudo timestamp_timeout is appropriate - sudo timestamp_timeout + + Red Hat Enterprise Linux 8 + + 'Ensure sudo timestamp_timeout is appropriate - sudo timestamp_timeout + + + + + + + + + + The operating system must restrict privilege elevation to authorized personnel + + Red Hat Enterprise Linux 8 + + Check that sudoers doesn't allow all users to run commands via sudo + + + + + + + + + + + Only the VDSM User Can Use sudo NOPASSWD + + Red Hat Enterprise Linux 8 + + Checks sudo usage for the vdsm user without a password + + + + + + + + + + + Ensure sudo only includes the default configuration directory + + Red Hat Enterprise Linux 8 + + Check if sudo includes only the default includedir + + + + + + + + + + + + + + + + + + + Explicit arguments in sudo specifications + + Red Hat Enterprise Linux 8 + + Check that sudoers doesn't contain commands without arguments specified + + + + + + + + + + Don't define allowed commands in sudoers by means of exclusion + + Red Hat Enterprise Linux 8 + + Check that sudoers doesn't contain command negations + + + + + + + + + + Don't target root user in the sudoers file + + Red Hat Enterprise Linux 8 + + Check that sudoers doesn't allow users to run commands as root + + + + + + + + + + + Ensure invoking users password for privilege escalation when using sudo + + Red Hat Enterprise Linux 8 + + Ensure invoking user's password for privilege escalation when using sudo + + + + + + + + + + + + + + + Ensure yum Removes Previous Package Versions + + Red Hat Enterprise Linux 8 + + The clean_requirements_on_remove option should be used to ensure that old + versions of software components are removed after updating. + + + + + + + + + + Configure dnf-automatic to Install Available Updates Automatically + + Red Hat Enterprise Linux 8 + + Ensure 'apply_updates' is configured with value 'yes in section 'commands' in /etc/dnf/automatic.conf + + + + + + + + + + + + + Configure dnf-automatic to Install Only Security Updates + + Red Hat Enterprise Linux 8 + + Ensure 'upgrade_type' is configured with value 'security in section 'commands' in /etc/dnf/automatic.conf + + + + + + + + + + + + + Ensure gpgcheck Enabled In Main yum Configuration + + Red Hat Enterprise Linux 8 + + The gpgcheck option should be used to ensure that checking + of an RPM package's signature always occurs prior to its + installation. + + + + + + + + + + Ensure gpgcheck Enabled for Local Packages + + Red Hat Enterprise Linux 8 + + The localpkg_gpgcheck option should be used to ensure that checking + of an RPM package's signature always occurs prior to its + installation. + + + + + + + + + + Ensure gpgcheck Enabled for All yum Package Repositories + + Red Hat Enterprise Linux 8 + + Ensure all yum or dnf repositories utilize signature checking. + + + + + + + + + + Ensure gpgcheck Enabled for Repository Metadata + + Red Hat Enterprise Linux 8 + + The repo_gpgcheck option should be used to ensure that checking + of repository metadata always occurs. + + + + + + + + + + Ensure Red Hat GPG Key Installed + + Red Hat Enterprise Linux 8 + + The Red Hat release and auxiliary key packages are required to be installed. + + + + + + + + + + + + + + + + + + + + + + Ensure PAM Enforces Password Requirements - Minimum Digit Characters + + Red Hat Enterprise Linux 8 + + The password dcredit should meet minimum requirements + + + + + + + + + + + + + Ensure PAM Enforces Password Requirements - Prevent the Use of Dictionary Words + + Red Hat Enterprise Linux 8 + + The password dictcheck should meet minimum requirements + + + + + + + + + + + + + Ensure PAM Enforces Password Requirements - Minimum Different Characters + + Red Hat Enterprise Linux 8 + + The password difok should meet minimum requirements + + + + + + + + + + + + + Ensure PAM Enforces Password Requirements - Enforce for Local Accounts Only + + Red Hat Enterprise Linux 8 + + Check presence of local_users_only in /etc/security/pwquality.conf + + + + + + + + + + + Ensure PAM Enforces Password Requirements - Enforce for root User + + Red Hat Enterprise Linux 8 + + Check presence of enforce_for_root in /etc/security/pwquality.conf + + + + + + + + + + + Ensure PAM Enforces Password Requirements - Minimum Lowercase Characters + + Red Hat Enterprise Linux 8 + + The password lcredit should meet minimum requirements + + + + + + + + + + + + + Ensure PAM Enforces Password Requirements - Maximum Consecutive Repeating Characters from Same Character Class + + Red Hat Enterprise Linux 8 + + The password maxclassrepeat should meet minimum requirements + + + + + + + + + + + + + Set Password Maximum Consecutive Repeating Characters + + Red Hat Enterprise Linux 8 + + The password maxrepeat should meet minimum requirements + + + + + + + + + + + + + Ensure PAM Enforces Password Requirements - Minimum Different Categories + + Red Hat Enterprise Linux 8 + + The password minclass should meet minimum requirements + + + + + + + + + + + + + Ensure PAM Enforces Password Requirements - Minimum Length + + Red Hat Enterprise Linux 8 + + The password minlen should meet minimum requirements + + + + + + + + + + + + + Ensure PAM Enforces Password Requirements - Minimum Special Characters + + Red Hat Enterprise Linux 8 + + The password ocredit should meet minimum requirements + + + + + + + + + + + + + Ensure PAM Enforces Password Requirements - Minimum Uppercase Characters + + Red Hat Enterprise Linux 8 + + The password ucredit should meet minimum requirements + + + + + + + + + + + + + Configure auditing of unsuccessful file accesses + + Red Hat Enterprise Linux 8 + + Inspect the contents of /etc/audit/rules.d/30-ospp-v42-3-access-failed.rules + + + + + + + + + + Configure auditing of successful file accesses + + Red Hat Enterprise Linux 8 + + Inspect the contents of /etc/audit/rules.d/30-ospp-v42-3-access-success.rules + + + + + + + + + + Configure basic parameters of Audit system + + Red Hat Enterprise Linux 8 + + Inspect the contents of /etc/audit/rules.d/10-base-config.rules + + + + + + + + + + Configure auditing of unsuccessful file creations + + Red Hat Enterprise Linux 8 + + Inspect the contents of /etc/audit/rules.d/30-ospp-v42-1-create-failed.rules + + + + + + + + + + Configure auditing of successful file creations + + Red Hat Enterprise Linux 8 + + Inspect the contents of /etc/audit/rules.d/30-ospp-v42-1-create-success.rules + + + + + + + + + + Configure auditing of unsuccessful file deletions + + Red Hat Enterprise Linux 8 + + Inspect the contents of /etc/audit/rules.d/30-ospp-v42-4-delete-failed.rules + + + + + + + + + + Configure auditing of successful file deletions + + Red Hat Enterprise Linux 8 + + Inspect the contents of /etc/audit/rules.d/30-ospp-v42-4-delete-success.rules + + + + + + + + + + Configure immutable Audit login UIDs + + Red Hat Enterprise Linux 8 + + Inspect the contents of /etc/audit/rules.d/11-loginuid.rules + + + + + + + + + + Configure auditing of unsuccessful file modifications + + Red Hat Enterprise Linux 8 + + Inspect the contents of /etc/audit/rules.d/30-ospp-v42-2-modify-failed.rules + + + + + + + + + + Configure auditing of successful file modifications + + Red Hat Enterprise Linux 8 + + Inspect the contents of /etc/audit/rules.d/30-ospp-v42-2-modify-success.rules + + + + + + + + + + Configure auditing of loading and unloading of kernel modules + + Red Hat Enterprise Linux 8 + + Inspect the contents of /etc/audit/rules.d/43-module-load.rules + + + + + + + + + + Perform general configuration of Audit for OSPP + + Red Hat Enterprise Linux 8 + + Inspect the contents of /etc/audit/rules.d/30-ospp-v42.rules + + + + + + + + + + Configure auditing of unsuccessful ownership changes + + Red Hat Enterprise Linux 8 + + Inspect the contents of /etc/audit/rules.d/30-ospp-v42-6-owner-change-failed.rules + + + + + + + + + + Configure auditing of successful ownership changes + + Red Hat Enterprise Linux 8 + + Inspect the contents of /etc/audit/rules.d/30-ospp-v42-6-owner-change-success.rules + + + + + + + + + + Configure auditing of unsuccessful permission changes + + Red Hat Enterprise Linux 8 + + Inspect the contents of /etc/audit/rules.d/30-ospp-v42-5-perm-change-failed.rules + + + + + + + + + + Configure auditing of successful permission changes + + Red Hat Enterprise Linux 8 + + Inspect the contents of /etc/audit/rules.d/30-ospp-v42-5-perm-change-success.rules + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - init + + Red Hat Enterprise Linux 8 + + Audit rules about the information on the use of init is enabled. + + + + + + + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - poweroff + + Red Hat Enterprise Linux 8 + + Audit rules about the information on the use of poweroff is enabled. + + + + + + + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - reboot + + Red Hat Enterprise Linux 8 + + Audit rules about the information on the use of reboot is enabled. + + + + + + + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - shutdown + + Red Hat Enterprise Linux 8 + + Audit rules about the information on the use of shutdown is enabled. + + + + + + + + + + + + + + + + Record Events that Modify the System's Discretionary Access Controls - chmod + + Red Hat Enterprise Linux 8 + + The changing of file permissions and attributes should be audited. + + + + + + + + + + + + + + + + + + + + + + + + + Record Events that Modify the System's Discretionary Access Controls - chown + + Red Hat Enterprise Linux 8 + + The changing of file permissions and attributes should be audited. + + + + + + + + + + + + + + + + + + + + + + + + + Record Events that Modify the System's Discretionary Access Controls - fchmod + + Red Hat Enterprise Linux 8 + + The changing of file permissions and attributes should be audited. + + + + + + + + + + + + + + + + + + + + + + + + + Record Events that Modify the System's Discretionary Access Controls - fchmodat + + Red Hat Enterprise Linux 8 + + The changing of file permissions and attributes should be audited. + + + + + + + + + + + + + + + + + + + + + + + + + Record Events that Modify the System's Discretionary Access Controls - fchown + + Red Hat Enterprise Linux 8 + + The changing of file permissions and attributes should be audited. + + + + + + + + + + + + + + + + + + + + + + + + + Record Events that Modify the System's Discretionary Access Controls - fchownat + + Red Hat Enterprise Linux 8 + + The changing of file permissions and attributes should be audited. + + + + + + + + + + + + + + + + + + + + + + + + + Record Events that Modify the System's Discretionary Access Controls - fremovexattr + + Red Hat Enterprise Linux 8 + + The changing of file permissions and attributes should be audited. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Record Events that Modify the System's Discretionary Access Controls - fsetxattr + + Red Hat Enterprise Linux 8 + + The changing of file permissions and attributes should be audited. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Record Events that Modify the System's Discretionary Access Controls - lchown + + Red Hat Enterprise Linux 8 + + The changing of file permissions and attributes should be audited. + + + + + + + + + + + + + + + + + + + + + + + + + Record Events that Modify the System's Discretionary Access Controls - lremovexattr + + Red Hat Enterprise Linux 8 + + The changing of file permissions and attributes should be audited. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Record Events that Modify the System's Discretionary Access Controls - lsetxattr + + Red Hat Enterprise Linux 8 + + The changing of file permissions and attributes should be audited. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Record Events that Modify the System's Discretionary Access Controls - removexattr + + Red Hat Enterprise Linux 8 + + The changing of file permissions and attributes should be audited. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Record Events that Modify the System's Discretionary Access Controls - setxattr + + Red Hat Enterprise Linux 8 + + The changing of file permissions and attributes should be audited. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Record Events that Modify the System's Discretionary Access Controls - umount2 + + Red Hat Enterprise Linux 8 + + The changing of file permissions and attributes should be audited. + + + + + + + + + + + + + + + + + + + + + + + + Record Events that Modify User/Group Information via open syscall - /etc/group + + Red Hat Enterprise Linux 8 + + Audit rules about the write events to /etc/group + + + + + + + + + + + + + + + + + + + + + + + + + Record Events that Modify User/Group Information via open_by_handle_at syscall - /etc/group + + Red Hat Enterprise Linux 8 + + Audit rules about the write events to /etc/group + + + + + + + + + + + + + + + + + + + + + + + + + Record Events that Modify User/Group Information via openat syscall - /etc/group + + Red Hat Enterprise Linux 8 + + Audit rules about the write events to /etc/group + + + + + + + + + + + + + + + + + + + + + + + + + Record Events that Modify User/Group Information via open syscall - /etc/gshadow + + Red Hat Enterprise Linux 8 + + Audit rules about the write events to /etc/gshadow + + + + + + + + + + + + + + + + + + + + + + + + + Record Events that Modify User/Group Information via open_by_handle_at syscall - /etc/gshadow + + Red Hat Enterprise Linux 8 + + Audit rules about the write events to /etc/gshadow + + + + + + + + + + + + + + + + + + + + + + + + + Record Events that Modify User/Group Information via openat syscall - /etc/gshadow + + Red Hat Enterprise Linux 8 + + Audit rules about the write events to /etc/gshadow + + + + + + + + + + + + + + + + + + + + + + + + + Record Events that Modify User/Group Information via open syscall - /etc/passwd + + Red Hat Enterprise Linux 8 + + Audit rules about the write events to /etc/passwd + + + + + + + + + + + + + + + + + + + + + + + + + Record Events that Modify User/Group Information via open_by_handle_at syscall - /etc/passwd + + Red Hat Enterprise Linux 8 + + Audit rules about the write events to /etc/passwd + + + + + + + + + + + + + + + + + + + + + + + + + Record Events that Modify User/Group Information via openat syscall - /etc/passwd + + Red Hat Enterprise Linux 8 + + Audit rules about the write events to /etc/passwd + + + + + + + + + + + + + + + + + + + + + + + + + Record Events that Modify User/Group Information via open syscall - /etc/shadow + + Red Hat Enterprise Linux 8 + + Audit rules about the write events to /etc/shadow + + + + + + + + + + + + + + + + + + + + + + + + + Record Events that Modify User/Group Information via open_by_handle_at syscall - /etc/shadow + + Red Hat Enterprise Linux 8 + + Audit rules about the write events to /etc/shadow + + + + + + + + + + + + + + + + + + + + + + + + + Record Events that Modify User/Group Information via openat syscall - /etc/shadow + + Red Hat Enterprise Linux 8 + + Audit rules about the write events to /etc/shadow + + + + + + + + + + + + + + + + + + + + + + + + + Record Any Attempts to Run chacl + + Red Hat Enterprise Linux 8 + + Audit rules about the information on the use of chacl is enabled. + + + + + + + + + + + + + + + + + Record Any Attempts to Run chcon + + Red Hat Enterprise Linux 8 + + Audit rules about the information on the use of chcon is enabled. + + + + + + + + + + + + + + + + + Record Any Attempts to Run restorecon + + Red Hat Enterprise Linux 8 + + Audit rules about the information on the use of restorecon is enabled. + + + + + + + + + + + + + + + + + Record Any Attempts to Run semanage + + Red Hat Enterprise Linux 8 + + Audit rules about the information on the use of semanage is enabled. + + + + + + + + + + + + + + + + + Record Any Attempts to Run setfacl + + Red Hat Enterprise Linux 8 + + Audit rules about the information on the use of setfacl is enabled. + + + + + + + + + + + + + + + + + Record Any Attempts to Run setfiles + + Red Hat Enterprise Linux 8 + + Audit rules about the information on the use of setfiles is enabled. + + + + + + + + + + + + + + + + + Record Any Attempts to Run setsebool + + Red Hat Enterprise Linux 8 + + Audit rules about the information on the use of setsebool is enabled. + + + + + + + + + + + + + + + + + Record Any Attempts to Run seunshare + + Red Hat Enterprise Linux 8 + + Audit rules about the information on the use of seunshare is enabled. + + + + + + + + + + + + + + + + + Ensure auditd Collects File Deletion Events by User - rename + + Red Hat Enterprise Linux 8 + + The deletion of files should be audited. + + + + + + + + + + + + + + + + + + + + + + + + + Ensure auditd Collects File Deletion Events by User - renameat + + Red Hat Enterprise Linux 8 + + The deletion of files should be audited. + + + + + + + + + + + + + + + + + + + + + + + + + Ensure auditd Collects File Deletion Events by User - rmdir + + Red Hat Enterprise Linux 8 + + The deletion of files should be audited. + + + + + + + + + + + + + + + + + + + + + + + + + Ensure auditd Collects File Deletion Events by User - unlink + + Red Hat Enterprise Linux 8 + + The deletion of files should be audited. + + + + + + + + + + + + + + + + + + + + + + + + + Ensure auditd Collects File Deletion Events by User - unlinkat + + Red Hat Enterprise Linux 8 + + The deletion of files should be audited. + + + + + + + + + + + + + + + + + + + + + + + + + Record Attempts to Alter Logon and Logout Events - faillock + + Red Hat Enterprise Linux 8 + + Audit rules should be configured to log successful and unsuccessful login and logout events. + + + + + + + + + + + + + + + + + Record Attempts to Alter Logon and Logout Events - lastlog + + Red Hat Enterprise Linux 8 + + Audit rules should be configured to log successful and unsuccessful login and logout events. + + + + + + + + + + + + + + + + + Record Attempts to Alter Logon and Logout Events - tallylog + + Red Hat Enterprise Linux 8 + + Audit rules should be configured to log successful and unsuccessful login and logout events. + + + + + + + + + + + + + + + + + Ensure auditd Collects Information on Exporting to Media (successful) + + Red Hat Enterprise Linux 8 + + The changing of file permissions and attributes should be audited. + + + + + + + + + + + + + + + + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - at + + Red Hat Enterprise Linux 8 + + Audit rules about the information on the use of at is enabled. + + + + + + + + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - chage + + Red Hat Enterprise Linux 8 + + Audit rules about the information on the use of chage is enabled. + + + + + + + + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - chsh + + Red Hat Enterprise Linux 8 + + Audit rules about the information on the use of chsh is enabled. + + + + + + + + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - crontab + + Red Hat Enterprise Linux 8 + + Audit rules about the information on the use of crontab is enabled. + + + + + + + + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - gpasswd + + Red Hat Enterprise Linux 8 + + Audit rules about the information on the use of gpasswd is enabled. + + + + + + + + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - kmod + + Red Hat Enterprise Linux 8 + + Audit rules about the information on the use of kmod is enabled. + + + + + + + + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - mount + + Red Hat Enterprise Linux 8 + + Audit rules about the information on the use of mount is enabled. + + + + + + + + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - newgidmap + + Red Hat Enterprise Linux 8 + + Audit rules about the information on the use of newgidmap is enabled. + + + + + + + + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - newgrp + + Red Hat Enterprise Linux 8 + + Audit rules about the information on the use of newgrp is enabled. + + + + + + + + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - newuidmap + + Red Hat Enterprise Linux 8 + + Audit rules about the information on the use of newuidmap is enabled. + + + + + + + + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - pam_timestamp_check + + Red Hat Enterprise Linux 8 + + Audit rules about the information on the use of pam_timestamp_check is enabled. + + + + + + + + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - passwd + + Red Hat Enterprise Linux 8 + + Audit rules about the information on the use of passwd is enabled. + + + + + + + + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - postdrop + + Red Hat Enterprise Linux 8 + + Audit rules about the information on the use of postdrop is enabled. + + + + + + + + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - postqueue + + Red Hat Enterprise Linux 8 + + Audit rules about the information on the use of postqueue is enabled. + + + + + + + + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - pt_chown + + Red Hat Enterprise Linux 8 + + Audit rules about the information on the use of pt_chown is enabled. + + + + + + + + + + + + + + + + + Record Any Attempts to Run ssh-agent + + Red Hat Enterprise Linux 8 + + Audit rules about the information on the use of ssh_agent is enabled. + + + + + + + + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - ssh-keysign + + Red Hat Enterprise Linux 8 + + Audit rules about the information on the use of ssh_keysign is enabled. + + + + + + + + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - su + + Red Hat Enterprise Linux 8 + + Audit rules about the information on the use of su is enabled. + + + + + + + + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - sudo + + Red Hat Enterprise Linux 8 + + Audit rules about the information on the use of sudo is enabled. + + + + + + + + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - sudoedit + + Red Hat Enterprise Linux 8 + + Audit rules about the information on the use of sudoedit is enabled. + + + + + + + + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - umount + + Red Hat Enterprise Linux 8 + + Audit rules about the information on the use of umount is enabled. + + + + + + + + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - unix_chkpwd + + Red Hat Enterprise Linux 8 + + Audit rules about the information on the use of unix_chkpwd is enabled. + + + + + + + + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - unix_update + + Red Hat Enterprise Linux 8 + + Audit rules about the information on the use of unix_update is enabled. + + + + + + + + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - userhelper + + Red Hat Enterprise Linux 8 + + Audit rules about the information on the use of userhelper is enabled. + + + + + + + + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - usermod + + Red Hat Enterprise Linux 8 + + Audit rules about the information on the use of usermod is enabled. + + + + + + + + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - usernetctl + + Red Hat Enterprise Linux 8 + + Audit rules about the information on the use of usernetctl is enabled. + + + + + + + + + + + + + + + + + Record Unsuccessful Permission Changes to Files - chmod + + Red Hat Enterprise Linux 8 + + Audit rules about the unauthorized access attempts to files (unsuccessful) are enabled. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Record Unsuccessful Ownership Changes to Files - chown + + Red Hat Enterprise Linux 8 + + Audit rules about the unauthorized access attempts to files (unsuccessful) are enabled. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Record Unsuccessful Access Attempts to Files - creat + + Red Hat Enterprise Linux 8 + + Audit rules about the unauthorized access attempts to files (unsuccessful) are enabled. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Record Unsuccessful Permission Changes to Files - fchmod + + Red Hat Enterprise Linux 8 + + Audit rules about the unauthorized access attempts to files (unsuccessful) are enabled. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Record Unsuccessful Permission Changes to Files - fchmodat + + Red Hat Enterprise Linux 8 + + Audit rules about the unauthorized access attempts to files (unsuccessful) are enabled. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Record Unsuccessful Ownership Changes to Files - fchown + + Red Hat Enterprise Linux 8 + + Audit rules about the unauthorized access attempts to files (unsuccessful) are enabled. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Record Unsuccessful Ownership Changes to Files - fchownat + + Red Hat Enterprise Linux 8 + + Audit rules about the unauthorized access attempts to files (unsuccessful) are enabled. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Record Unsuccessful Permission Changes to Files - fremovexattr + + Red Hat Enterprise Linux 8 + + Audit rules about the unauthorized access attempts to files (unsuccessful) are enabled. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Record Unsuccessful Permission Changes to Files - fsetxattr + + Red Hat Enterprise Linux 8 + + Audit rules about the unauthorized access attempts to files (unsuccessful) are enabled. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Record Unsuccessful Access Attempts to Files - ftruncate + + Red Hat Enterprise Linux 8 + + Audit rules about the unauthorized access attempts to files (unsuccessful) are enabled. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Record Unsuccessful Ownership Changes to Files - lchown + + Red Hat Enterprise Linux 8 + + Audit rules about the unauthorized access attempts to files (unsuccessful) are enabled. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Record Unsuccessful Permission Changes to Files - lremovexattr + + Red Hat Enterprise Linux 8 + + Audit rules about the unauthorized access attempts to files (unsuccessful) are enabled. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Record Unsuccessful Permission Changes to Files - lsetxattr + + Red Hat Enterprise Linux 8 + + Audit rules about the unauthorized access attempts to files (unsuccessful) are enabled. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Record Unsuccessful Access Attempts to Files - open + + Red Hat Enterprise Linux 8 + + Audit rules about the unauthorized access attempts to files (unsuccessful) are enabled. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Record Unsuccessful Access Attempts to Files - open_by_handle_at + + Red Hat Enterprise Linux 8 + + Audit rules about the unauthorized access attempts to files (unsuccessful) are enabled. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Record Unsuccessful Creation Attempts to Files - open_by_handle_at O_CREAT + + Red Hat Enterprise Linux 8 + + Audit rules about the information on the unsuccessful use of open_by_handle_at O_CREAT is enabled. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Record Unsuccessful Modification Attempts to Files - open_by_handle_at O_TRUNC_WRITE + + Red Hat Enterprise Linux 8 + + Audit rules about the information on the unsuccessful use of open_by_handle_at O_TRUNC is enabled. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Ensure auditd Unauthorized Access Attempts To open_by_handle_at Are Ordered Correctly + + Red Hat Enterprise Linux 8 + + Audit rules about the information on the unsuccessful use of open_by_handle_at is configured in the proper rule order. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Record Unsuccessful Creation Attempts to Files - open O_CREAT + + Red Hat Enterprise Linux 8 + + Audit rules about the information on the unsuccessful use of open O_CREAT is enabled. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Record Unsuccessful Modification Attempts to Files - open O_TRUNC_WRITE + + Red Hat Enterprise Linux 8 + + Audit rules about the information on the unsuccessful use of open O_TRUNC is enabled. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Ensure auditd Rules For Unauthorized Attempts To open Are Ordered Correctly + + Red Hat Enterprise Linux 8 + + Audit rules about the information on the unsuccessful use of open is configured in the proper rule order. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Record Unsuccessful Access Attempts to Files - openat + + Red Hat Enterprise Linux 8 + + Audit rules about the unauthorized access attempts to files (unsuccessful) are enabled. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Record Unsuccessful Creation Attempts to Files - openat O_CREAT + + Red Hat Enterprise Linux 8 + + Audit rules about the information on the unsuccessful use of openat O_CREAT is enabled. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Record Unsuccessful Modification Attempts to Files - openat O_TRUNC_WRITE + + Red Hat Enterprise Linux 8 + + Audit rules about the information on the unsuccessful use of openat O_TRUNC is enabled. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Ensure auditd Rules For Unauthorized Attempts To openat Are Ordered Correctly + + Red Hat Enterprise Linux 8 + + Audit rules about the information on the unsuccessful use of openat is configured in the proper rule order. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Record Unsuccessful Permission Changes to Files - removexattr + + Red Hat Enterprise Linux 8 + + Audit rules about the unauthorized access attempts to files (unsuccessful) are enabled. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Record Unsuccessful Delete Attempts to Files - rename + + Red Hat Enterprise Linux 8 + + Audit rules about the unauthorized access attempts to files (unsuccessful) are enabled. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Record Unsuccessful Delete Attempts to Files - renameat + + Red Hat Enterprise Linux 8 + + Audit rules about the unauthorized access attempts to files (unsuccessful) are enabled. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Record Unsuccessful Permission Changes to Files - setxattr + + Red Hat Enterprise Linux 8 + + Audit rules about the unauthorized access attempts to files (unsuccessful) are enabled. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Record Unsuccessful Access Attempts to Files - truncate + + Red Hat Enterprise Linux 8 + + Audit rules about the unauthorized access attempts to files (unsuccessful) are enabled. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Record Unsuccessful Delete Attempts to Files - unlink + + Red Hat Enterprise Linux 8 + + Audit rules about the unauthorized access attempts to files (unsuccessful) are enabled. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Record Unsuccessful Delete Attempts to Files - unlinkat + + Red Hat Enterprise Linux 8 + + Audit rules about the unauthorized access attempts to files (unsuccessful) are enabled. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Record Events that Modify User/Group Information - /etc/group + + Red Hat Enterprise Linux 8 + + Audit user/group modification. + + + + + + + + + + + + + + + + + Record Events that Modify User/Group Information - /etc/gshadow + + Red Hat Enterprise Linux 8 + + Audit user/group modification. + + + + + + + + + + + + + + + + + Record Events that Modify User/Group Information - /etc/security/opasswd + + Red Hat Enterprise Linux 8 + + Audit user/group modification. + + + + + + + + + + + + + + + + + Record Events that Modify User/Group Information - /etc/passwd + + Red Hat Enterprise Linux 8 + + Audit user/group modification. + + + + + + + + + + + + + + + + + Record Events that Modify User/Group Information - /etc/shadow + + Red Hat Enterprise Linux 8 + + Audit user/group modification. + + + + + + + + + + + + + + + + + Set number of records to cause an explicit flush to audit logs + + Red Hat Enterprise Linux 8 + + Ensure 'freq' is configured with value '50' in /etc/audit/auditd.conf + + + + + + + + + + Include Local Events in Audit Logs + + Red Hat Enterprise Linux 8 + + Ensure 'local_events' is configured with value 'yes' in /etc/audit/auditd.conf + + + + + + + + + + Resolve information before writing to audit logs + + Red Hat Enterprise Linux 8 + + Ensure 'log_format' is configured with value 'ENRICHED' in /etc/audit/auditd.conf + + + + + + + + + + Write Audit Logs to the Disk + + Red Hat Enterprise Linux 8 + + Ensure 'write_logs' is configured with value 'yes' in /etc/audit/auditd.conf + + + + + + + + + + + Ensure SELinux Not Disabled in the kernel arguments + + Red Hat Enterprise Linux 8 + + Ensure selinux=0 argument is not present in the 'options' line of /boot/loader/entries/ostree-2-*.conf (or ostree-1-*.conf if there is no ostree-2-*.conf as ostree has only two enries at the most, with *-2-*.conf entry always being the most recent). Also, ensure that kernel is currently running with this argument by checking /proc/cmdline. + + + + + + + + + + + + + + + + + + + + Disable User Administration in GNOME3 + + Red Hat Enterprise Linux 8 + + Ensure 'user-administration-disabled' is configured with value 'true in section 'org/gnome/desktop/lockdown' in /etc/dconf/db/local.d/ + + + + + + + + + + + Enable the GNOME3 Screen Locking On Smartcard Removal + + Red Hat Enterprise Linux 8 + + Ensure 'removal-action' is configured with value ''lock-screen' in section 'org/gnome/settings-daemon/peripherals/smartcard' in /etc/dconf/db/local.d/ + + + + + + + + + + + Verify that Shared Library Directories Have Root Group Ownership + + Red Hat Enterprise Linux 8 + + This test makes sure that /lib/, /lib64/, /usr/lib/, /usr/lib64/ is group owned by 0. + + + + + + + + + + + + + Verify that System Executable Have Root Ownership + + Red Hat Enterprise Linux 8 + + This test makes sure that /bin/, /sbin/, /usr/bin/, /usr/sbin/, /usr/local/bin/, /usr/local/sbin/ is owned by 0. + + + + + + + + + + + + + + Verify that Shared Library Directories Have Root Ownership + + Red Hat Enterprise Linux 8 + + This test makes sure that /lib/, /lib64/, /usr/lib/, /usr/lib64/ is owned by 0. + + + + + + + + + + + + + Verify that System Executable Directories Have Restrictive Permissions + + Red Hat Enterprise Linux 8 + + This test makes sure that /bin/, /sbin/, /usr/bin/, /usr/sbin/, /usr/local/bin/, /usr/local/sbin/ has mode 0755. + If the target file or directory has an extended ACL, then it will fail the mode check. + + + + + + + + + + + + + + + Verify that Shared Library Directories Have Restrictive Permissions + + Red Hat Enterprise Linux 8 + + This test makes sure that /lib/, /lib64/, /usr/lib/, /usr/lib64/ has mode 7755. + If the target file or directory has an extended ACL, then it will fail the mode check. + + + + + + + + + + + + + + Disable Host-Based Authentication + + Red Hat Enterprise Linux 8 + + Ensure 'HostbasedAuthentication' is configured with value 'no' in /etc/ssh/sshd_config + + + + + + + + + + + + + + + + + + + + Ensure that /etc/at.deny does not exist + + Red Hat Enterprise Linux 8 + + This test makes sure that/etc/at.deny does not exist. + + + + + + + + + + Audit Tools Must Be Group-owned by Root + + Red Hat Enterprise Linux 8 + + This test makes sure that /sbin/auditctl, /sbin/aureport, /sbin/ausearch, /sbin/autrace, /sbin/auditd, /sbin/rsyslogd, /sbin/augenrules is group owned by 0. + + + + + + + + + + + + + + + + Audit Tools Must Be Owned by Root + + Red Hat Enterprise Linux 8 + + This test makes sure that /sbin/auditctl, /sbin/aureport, /sbin/ausearch, /sbin/autrace, /sbin/auditd, /sbin/rsyslogd, /sbin/augenrules is owned by 0. + + + + + + + + + + + + + + + + Audit Tools Must Have a Mode of 0755 or Less Permissive + + Red Hat Enterprise Linux 8 + + This test makes sure that /sbin/auditctl, /sbin/aureport, /sbin/ausearch, /sbin/autrace, /sbin/auditd, /sbin/rsyslogd, /sbin/augenrules has mode 0755. + If the target file or directory has an extended ACL, then it will fail the mode check. + + + + + + + + + + + + + + + + + Ensure that /etc/cron.deny does not exist + + Red Hat Enterprise Linux 8 + + This test makes sure that/etc/cron.deny does not exist. + + + + + + + + + + Verify Group Who Owns /etc/at.allow file + + Red Hat Enterprise Linux 8 + + This test makes sure that /etc/at.allow is group owned by 0. + + + + + + + + + + Verify Group Who Owns Backup group File + + Red Hat Enterprise Linux 8 + + This test makes sure that /etc/group- is group owned by 0. + + + + + + + + + + Verify Group Who Owns Backup gshadow File + + Red Hat Enterprise Linux 8 + + This test makes sure that /etc/gshadow- is group owned by 0. + + + + + + + + + + Verify Group Who Owns Backup passwd File + + Red Hat Enterprise Linux 8 + + This test makes sure that /etc/passwd- is group owned by 0. + + + + + + + + + + Verify User Who Owns Backup shadow File + + Red Hat Enterprise Linux 8 + + This test makes sure that /etc/shadow- is group owned by 0. + + + + + + + + + + Verify Group Who Owns /etc/cron.allow file + + Red Hat Enterprise Linux 8 + + This test makes sure that /etc/cron.allow is group owned by 0. + + + + + + + + + + Verify Group Who Owns cron.d + + Red Hat Enterprise Linux 8 + + This test makes sure that /etc/cron.d/ is group owned by 0. + + + + + + + + + + Verify Group Who Owns cron.daily + + Red Hat Enterprise Linux 8 + + This test makes sure that /etc/cron.daily/ is group owned by 0. + + + + + + + + + + Verify Group Who Owns cron.hourly + + Red Hat Enterprise Linux 8 + + This test makes sure that /etc/cron.hourly/ is group owned by 0. + + + + + + + + + + Verify Group Who Owns cron.monthly + + Red Hat Enterprise Linux 8 + + This test makes sure that /etc/cron.monthly/ is group owned by 0. + + + + + + + + + + Verify Group Who Owns cron.weekly + + Red Hat Enterprise Linux 8 + + This test makes sure that /etc/cron.weekly/ is group owned by 0. + + + + + + + + + + Verify Group Who Owns Crontab + + Red Hat Enterprise Linux 8 + + This test makes sure that /etc/crontab is group owned by 0. + + + + + + + + + + Verify the UEFI Boot Loader grub.cfg Group Ownership + + Red Hat Enterprise Linux 8 + + This test makes sure that /boot/efi/EFI/redhat/grub.cfg is group owned by 0. + + + + + + + + + + Verify Group Who Owns group File + + Red Hat Enterprise Linux 8 + + This test makes sure that /etc/group is group owned by 0. + + + + + + + + + + Verify Group Who Owns gshadow File + + Red Hat Enterprise Linux 8 + + This test makes sure that /etc/gshadow is group owned by 0. + + + + + + + + + + Verify Group Ownership of System Login Banner + + Red Hat Enterprise Linux 8 + + This test makes sure that /etc/issue is group owned by 0. + + + + + + + + + + Verify Group Ownership of Message of the Day Banner + + Red Hat Enterprise Linux 8 + + This test makes sure that /etc/motd is group owned by 0. + + + + + + + + + + Verify Group Who Owns passwd File + + Red Hat Enterprise Linux 8 + + This test makes sure that /etc/passwd is group owned by 0. + + + + + + + + + + Verify Group Who Owns shadow File + + Red Hat Enterprise Linux 8 + + This test makes sure that /etc/shadow is group owned by 0. + + + + + + + + + + Verify /boot/grub2/grub.cfg Group Ownership + + Red Hat Enterprise Linux 8 + + This test makes sure that /boot/grub2/grub.cfg is group owned by 0. + + + + + + + + + + Verify Group Who Owns SSH Server config file + + Red Hat Enterprise Linux 8 + + This test makes sure that /etc/ssh/sshd_config is group owned by 0. + + + + + + + + + + Verify Group Who Owns /var/log Directory + + Red Hat Enterprise Linux 8 + + This test makes sure that /var/log/ is group owned by 0. + + + + + + + + + + Verify Group Who Owns /var/log/messages File + + Red Hat Enterprise Linux 8 + + This test makes sure that /var/log/messages is group owned by 0. + + + + + + + + + + Verify Group Who Owns /var/log/syslog File + + Red Hat Enterprise Linux 8 + + This test makes sure that /var/log/syslog is group owned by 4. + + + + + + + + + Audit Configuration Files Must Be Owned By Group root + + Red Hat Enterprise Linux 8 + + This test makes sure that /etc/audit/, /etc/audit/rules.d/ is group owned by 0. + + + + + + + + + + Verify User Who Owns Backup group File + + Red Hat Enterprise Linux 8 + + This test makes sure that /etc/group- is owned by 0. + + + + + + + + + + Verify User Who Owns Backup gshadow File + + Red Hat Enterprise Linux 8 + + This test makes sure that /etc/gshadow- is owned by 0. + + + + + + + + + + Verify User Who Owns Backup passwd File + + Red Hat Enterprise Linux 8 + + This test makes sure that /etc/passwd- is owned by 0. + + + + + + + + + + Verify Group Who Owns Backup shadow File + + Red Hat Enterprise Linux 8 + + This test makes sure that /etc/shadow- is owned by 0. + + + + + + + + + + Verify User Who Owns /etc/cron.allow file + + Red Hat Enterprise Linux 8 + + This test makes sure that /etc/cron.allow is owned by 0. + + + + + + + + + + Verify Owner on cron.d + + Red Hat Enterprise Linux 8 + + This test makes sure that /etc/cron.d/ is owned by 0. + + + + + + + + + + Verify Owner on cron.daily + + Red Hat Enterprise Linux 8 + + This test makes sure that /etc/cron.daily/ is owned by 0. + + + + + + + + + + Verify Owner on cron.hourly + + Red Hat Enterprise Linux 8 + + This test makes sure that /etc/cron.hourly/ is owned by 0. + + + + + + + + + + Verify Owner on cron.monthly + + Red Hat Enterprise Linux 8 + + This test makes sure that /etc/cron.monthly/ is owned by 0. + + + + + + + + + + Verify Owner on cron.weekly + + Red Hat Enterprise Linux 8 + + This test makes sure that /etc/cron.weekly/ is owned by 0. + + + + + + + + + + Verify Owner on crontab + + Red Hat Enterprise Linux 8 + + This test makes sure that /etc/crontab is owned by 0. + + + + + + + + + + Verify the UEFI Boot Loader grub.cfg User Ownership + + Red Hat Enterprise Linux 8 + + This test makes sure that /boot/efi/EFI/redhat/grub.cfg is owned by 0. + + + + + + + + + + Verify User Who Owns group File + + Red Hat Enterprise Linux 8 + + This test makes sure that /etc/group is owned by 0. + + + + + + + + + + Verify User Who Owns gshadow File + + Red Hat Enterprise Linux 8 + + This test makes sure that /etc/gshadow is owned by 0. + + + + + + + + + + Verify ownership of System Login Banner + + Red Hat Enterprise Linux 8 + + This test makes sure that /etc/issue is owned by 0. + + + + + + + + + + Verify ownership of Message of the Day Banner + + Red Hat Enterprise Linux 8 + + This test makes sure that /etc/motd is owned by 0. + + + + + + + + + + Verify User Who Owns passwd File + + Red Hat Enterprise Linux 8 + + This test makes sure that /etc/passwd is owned by 0. + + + + + + + + + + Verify User Who Owns shadow File + + Red Hat Enterprise Linux 8 + + This test makes sure that /etc/shadow is owned by 0. + + + + + + + + + + Verify /boot/grub2/grub.cfg User Ownership + + Red Hat Enterprise Linux 8 + + This test makes sure that /boot/grub2/grub.cfg is owned by 0. + + + + + + + + + + Verify Owner on SSH Server config file + + Red Hat Enterprise Linux 8 + + This test makes sure that /etc/ssh/sshd_config is owned by 0. + + + + + + + + + + Verify User Who Owns /var/log Directory + + Red Hat Enterprise Linux 8 + + This test makes sure that /var/log/ is owned by 0. + + + + + + + + + + Verify User Who Owns /var/log/messages File + + Red Hat Enterprise Linux 8 + + This test makes sure that /var/log/messages is owned by 0. + + + + + + + + + + Verify User Who Owns /var/log/syslog File + + Red Hat Enterprise Linux 8 + + This test makes sure that /var/log/syslog is owned by 104. + + + + + + + + + Audit Configuration Files Must Be Owned By Root + + Red Hat Enterprise Linux 8 + + This test makes sure that /etc/audit/, /etc/audit/rules.d/ is owned by 0. + + + + + + + + + + Verify that Shared Library Files Have Root Ownership + + Red Hat Enterprise Linux 8 + + This test makes sure that /lib/, /lib64/, /usr/lib/, /usr/lib64/ is owned by 0. + + + + + + + + + + + + + Verify Permissions on /etc/at.allow file + + Red Hat Enterprise Linux 8 + + This test makes sure that /etc/at.allow has mode 0600. + If the target file or directory has an extended ACL, then it will fail the mode check. + + + + + + + + + + + Verify Permissions on Backup group File + + Red Hat Enterprise Linux 8 + + This test makes sure that /etc/group- has mode 0644. + If the target file or directory has an extended ACL, then it will fail the mode check. + + + + + + + + + + + Verify Permissions on Backup gshadow File + + Red Hat Enterprise Linux 8 + + This test makes sure that /etc/gshadow- has mode 0000. + If the target file or directory has an extended ACL, then it will fail the mode check. + + + + + + + + + + + Verify Permissions on Backup passwd File + + Red Hat Enterprise Linux 8 + + This test makes sure that /etc/passwd- has mode 0644. + If the target file or directory has an extended ACL, then it will fail the mode check. + + + + + + + + + + + Verify Permissions on Backup shadow File + + Red Hat Enterprise Linux 8 + + This test makes sure that /etc/shadow- has mode 0000. + If the target file or directory has an extended ACL, then it will fail the mode check. + + + + + + + + + + + Verify Permissions on /etc/cron.allow file + + Red Hat Enterprise Linux 8 + + This test makes sure that /etc/cron.allow has mode 0600. + If the target file or directory has an extended ACL, then it will fail the mode check. + + + + + + + + + + + Verify Permissions on cron.d + + Red Hat Enterprise Linux 8 + + This test makes sure that /etc/cron.d/ has mode 0700. + If the target file or directory has an extended ACL, then it will fail the mode check. + + + + + + + + + + + Verify Permissions on cron.daily + + Red Hat Enterprise Linux 8 + + This test makes sure that /etc/cron.daily/ has mode 0700. + If the target file or directory has an extended ACL, then it will fail the mode check. + + + + + + + + + + + Verify Permissions on cron.hourly + + Red Hat Enterprise Linux 8 + + This test makes sure that /etc/cron.hourly/ has mode 0700. + If the target file or directory has an extended ACL, then it will fail the mode check. + + + + + + + + + + + Verify Permissions on cron.monthly + + Red Hat Enterprise Linux 8 + + This test makes sure that /etc/cron.monthly/ has mode 0700. + If the target file or directory has an extended ACL, then it will fail the mode check. + + + + + + + + + + + Verify Permissions on cron.weekly + + Red Hat Enterprise Linux 8 + + This test makes sure that /etc/cron.weekly/ has mode 0700. + If the target file or directory has an extended ACL, then it will fail the mode check. + + + + + + + + + + + Verify Permissions on crontab + + Red Hat Enterprise Linux 8 + + This test makes sure that /etc/crontab has mode 0600. + If the target file or directory has an extended ACL, then it will fail the mode check. + + + + + + + + + + + Verify the UEFI Boot Loader grub.cfg Permissions + + Red Hat Enterprise Linux 8 + + This test makes sure that /boot/efi/EFI/redhat/grub.cfg has mode 0700. + If the target file or directory has an extended ACL, then it will fail the mode check. + + + + + + + + + + + Verify Permissions on /etc/audit/auditd.conf + + Red Hat Enterprise Linux 8 + + This test makes sure that /etc/audit/auditd.conf has mode 0640. + If the target file or directory has an extended ACL, then it will fail the mode check. + + + + + + + + + + + Verify Permissions on /etc/audit/rules.d/*.rules + + Red Hat Enterprise Linux 8 + + This test makes sure that /etc/audit/rules.d/ has mode 0640. + If the target file or directory has an extended ACL, then it will fail the mode check. + + + + + + + + + + + Verify Permissions on group File + + Red Hat Enterprise Linux 8 + + This test makes sure that /etc/group has mode 0644. + If the target file or directory has an extended ACL, then it will fail the mode check. + + + + + + + + + + + Verify Permissions on gshadow File + + Red Hat Enterprise Linux 8 + + This test makes sure that /etc/gshadow has mode 0000. + If the target file or directory has an extended ACL, then it will fail the mode check. + + + + + + + + + + + Verify permissions on System Login Banner + + Red Hat Enterprise Linux 8 + + This test makes sure that /etc/issue has mode 0644. + If the target file or directory has an extended ACL, then it will fail the mode check. + + + + + + + + + + + Verify permissions on Message of the Day Banner + + Red Hat Enterprise Linux 8 + + This test makes sure that /etc/motd has mode 0644. + If the target file or directory has an extended ACL, then it will fail the mode check. + + + + + + + + + + + Verify Permissions on passwd File + + Red Hat Enterprise Linux 8 + + This test makes sure that /etc/passwd has mode 0644. + If the target file or directory has an extended ACL, then it will fail the mode check. + + + + + + + + + + + Verify Permissions on shadow File + + Red Hat Enterprise Linux 8 + + This test makes sure that /etc/shadow has mode 0000. + If the target file or directory has an extended ACL, then it will fail the mode check. + + + + + + + + + + + Verify /boot/grub2/grub.cfg Permissions + + Red Hat Enterprise Linux 8 + + This test makes sure that /boot/grub2/grub.cfg has mode 0600. + If the target file or directory has an extended ACL, then it will fail the mode check. + + + + + + + + + + + Verify that Shared Library Files Have Restrictive Permissions + + Red Hat Enterprise Linux 8 + + This test makes sure that /lib/, /lib64/, /usr/lib/, /usr/lib64/ has mode 7755. + If the target file or directory has an extended ACL, then it will fail the mode check. + + + + + + + + + + + + + + Verify Permissions on SSH Server config file + + Red Hat Enterprise Linux 8 + + This test makes sure that /etc/ssh/sshd_config has mode 0600. + If the target file or directory has an extended ACL, then it will fail the mode check. + + + + + + + + + + + Verify Permissions on SSH Server Public *.pub Key Files + + Red Hat Enterprise Linux 8 + + This test makes sure that /etc/ssh/ has mode 0644. + If the target file or directory has an extended ACL, then it will fail the mode check. + + + + + + + + + + + Verify Permissions on /var/log Directory + + Red Hat Enterprise Linux 8 + + This test makes sure that /var/log/ has mode 0755. + If the target file or directory has an extended ACL, then it will fail the mode check. + + + + + + + + + + + Verify Permissions on /var/log/messages File + + Red Hat Enterprise Linux 8 + + This test makes sure that /var/log/messages has mode 0640. + If the target file or directory has an extended ACL, then it will fail the mode check. + + + + + + + + + + + Verify Permissions on /var/log/syslog File + + Red Hat Enterprise Linux 8 + + This test makes sure that /var/log/syslog has mode 0640. + If the target file or directory has an extended ACL, then it will fail the mode check. + + + + + + + + + + Configure Firewalld to Use the Nftables Backend + + Red Hat Enterprise Linux 8 + + Ensure 'FirewallBackend' is configured with value 'nftables' in /etc/firewalld/firewalld.conf + + + + + + + + + + Enable Auditing for Processes Which Start Prior to the Audit Daemon + + Red Hat Enterprise Linux 8 + + Ensure audit=1 is configured in the kernel line in /etc/default/grub. + + + + + + + + + + + + + + + + + + + + + Extend Audit Backlog Limit for the Audit Daemon + + Red Hat Enterprise Linux 8 + + Ensure audit_backlog_limit=8192 is configured in the kernel line in /etc/default/grub. + + + + + + + + + + + + + + + + + + + + + IOMMU configuration directive + + Red Hat Enterprise Linux 8 + + Ensure iommu=force is configured in the kernel line in /etc/default/grub. + + + + + + + + + + + + + + + + + + + + + Ensure IPv6 is disabled through kernel boot parameter + + Red Hat Enterprise Linux 8 + + Ensure ipv6.disable=1 is configured in the kernel line in /etc/default/grub. + + + + + + + + + + + + + + + + + + + + + Configure L1 Terminal Fault mitigations + + Red Hat Enterprise Linux 8 + + Ensure l1tf is configured in the kernel line in /etc/default/grub. + + + + + + + + + + + + + + + + + + + + + Force kernel panic on uncorrected MCEs + + Red Hat Enterprise Linux 8 + + Ensure mce=0 is configured in the kernel line in /etc/default/grub. + + + + + + + + + + + + + + + + + + + + + Ensure SMAP is not disabled during boot + + Red Hat Enterprise Linux 8 + + Ensure nosmap is not set in the kernel line in /etc/default/grub. + + + + + + + + + + + + + + + + + + + + + Ensure SMEP is not disabled during boot + + Red Hat Enterprise Linux 8 + + Ensure nosmep is not set in the kernel line in /etc/default/grub. + + + + + + + + + + + + + + + + + + + + + Enable page allocator poisoning + + Red Hat Enterprise Linux 8 + + Ensure page_poison=1 is configured in the kernel line in /etc/default/grub. + + + + + + + + + + + + + + + + + + + + + Enable Kernel Page-Table Isolation (KPTI) + + Red Hat Enterprise Linux 8 + + Ensure pti=on is configured in the kernel line in /etc/default/grub. + + + + + + + + + + + + + + + + + + + + + Configure the confidence in TPM for entropy + + Red Hat Enterprise Linux 8 + + Ensure rng_core.default_quality is configured in the kernel line in /etc/default/grub. + + + + + + + + + + + + + + + + + + + + + Disable merging of slabs with similar size + + Red Hat Enterprise Linux 8 + + Ensure slab_nomerge=yes is configured in the kernel line in /etc/default/grub. + + + + + + + + + + + + + + + + + + + + + Enable SLUB/SLAB allocator poisoning + + Red Hat Enterprise Linux 8 + + Ensure slub_debug is configured in the kernel line in /etc/default/grub. + + + + + + + + + + + + + + + + + + + + + Configure Speculative Store Bypass Mitigation + + Red Hat Enterprise Linux 8 + + Ensure spec_store_bypass_disable is configured in the kernel line in /etc/default/grub. + + + + + + + + + + + + + + + + + + + + + Enforce Spectre v2 mitigation + + Red Hat Enterprise Linux 8 + + Ensure spectre_v2=on is configured in the kernel line in /etc/default/grub. + + + + + + + + + + + + + + + + + + + + + Ensure debug-shell service is not enabled during boot + + Red Hat Enterprise Linux 8 + + Ensure systemd.debug-shell is not set in the kernel line in /etc/default/grub. + + + + + + + + + + + + + + + + + + + + Disable vsyscalls + + Red Hat Enterprise Linux 8 + + Ensure vsyscall=none is configured in the kernel line in /etc/default/grub. + + + + + + + + + + + + + + + + + + + + + Install Smart Card Packages For Multifactor Authentication + + Red Hat Enterprise Linux 8 + + The RPM package openssl-pkcs11 should be installed. + + + + + + + + + + Ensure journald is configured to compress large log files + + Red Hat Enterprise Linux 8 + + Ensure 'Compress' is configured with value 'yes' in /etc/systemd/journald.conf + + + + + + + + + + Ensure journald is configured to send logs to rsyslog + + Red Hat Enterprise Linux 8 + + Ensure 'ForwardToSyslog' is configured with value 'yes' in /etc/systemd/journald.conf + + + + + + + + + + Ensure journald is configured to write log files to persistent disk + + Red Hat Enterprise Linux 8 + + Ensure 'Storage' is configured with value 'persistent' in /etc/systemd/journald.conf + + + + + + + + + + Do not allow ACPI methods to be inserted/replaced at run time + + Red Hat Enterprise Linux 8 + + The kernel CONFIG_ACPI_CUSTOM_METHOD should have value n + + + + + + + + + + + + + + Emulate Privileged Access Never (PAN) + + Red Hat Enterprise Linux 8 + + The kernel CONFIG_ARM64_SW_TTBR0_PAN should have value y + + + + + + + + + + + + + Disable kernel support for MISC binaries + + Red Hat Enterprise Linux 8 + + The kernel CONFIG_BINFMT_MISC should have value n + + + + + + + + + + + + + + Enable support for BUG() + + Red Hat Enterprise Linux 8 + + The kernel CONFIG_BUG should have value y + + + + + + + + + + + + + Trigger a kernel BUG when data corruption is detected + + Red Hat Enterprise Linux 8 + + The kernel CONFIG_BUG_ON_DATA_CORRUPTION should have value y + + + + + + + + + + + + + Disable compatibility with brk() + + Red Hat Enterprise Linux 8 + + The kernel CONFIG_COMPAT_BRK should have value n + + + + + + + + + + + + + + Disable the 32-bit vDSO + + Red Hat Enterprise Linux 8 + + The kernel CONFIG_COMPAT_VDSO should have value n + + + + + + + + + + + + + + Enable checks on credential management + + Red Hat Enterprise Linux 8 + + The kernel CONFIG_DEBUG_CREDENTIALS should have value y + + + + + + + + + + + + + Disable kernel debugfs + + Red Hat Enterprise Linux 8 + + The kernel CONFIG_DEBUG_FS should have value n + + + + + + + + + + + + + + Enable checks on linked list manipulation + + Red Hat Enterprise Linux 8 + + The kernel CONFIG_DEBUG_LIST should have value y + + + + + + + + + + + + + Enable checks on notifier call chains + + Red Hat Enterprise Linux 8 + + The kernel CONFIG_DEBUG_NOTIFIERS should have value y + + + + + + + + + + + + + Enable checks on scatter-gather (SG) table operations + + Red Hat Enterprise Linux 8 + + The kernel CONFIG_DEBUG_SG should have value y + + + + + + + + + + + + + Warn on W+X mappings found at boot + + Red Hat Enterprise Linux 8 + + The kernel CONFIG_DEBUG_WX should have value y + + + + + + + + + + + + + Configure low address space to protect from user allocation + + Red Hat Enterprise Linux 8 + + The kernel CONFIG_DEFAULT_MMAP_MIN_ADDR should have value 65536 + + + + + + + + + + + + + Disable /dev/kmem virtual device support + + Red Hat Enterprise Linux 8 + + The kernel CONFIG_DEVKMEM should have value n + + + + + + + + + + + + + + Harden common str/mem functions against buffer overflows + + Red Hat Enterprise Linux 8 + + The kernel CONFIG_FORTIFY_SOURCE should have value y + + + + + + + + + + + + + Generate some entropy during boot and runtime + + Red Hat Enterprise Linux 8 + + The kernel CONFIG_GCC_PLUGIN_LATENT_ENTROPY should have value y + + + + + + + + + + + + + Force initialization of variables containing userspace addresses + + Red Hat Enterprise Linux 8 + + The kernel CONFIG_GCC_PLUGIN_STRUCTLEAK should have value y + + + + + + + + + + + + + Harden memory copies between kernel and userspace + + Red Hat Enterprise Linux 8 + + The kernel CONFIG_HARDENED_USERCOPY should have value y + + + + + + + + + + + + + Do not allow usercopy whitelist violations to fallback to object size + + Red Hat Enterprise Linux 8 + + The kernel CONFIG_HARDENED_USERCOPY_FALLBACK should have value n + + + + + + + + + + + + + + Disable hibernation + + Red Hat Enterprise Linux 8 + + The kernel CONFIG_HIBERNATION should have value n + + + + + + + + + + + + + + Disable IA32 emulation + + Red Hat Enterprise Linux 8 + + The kernel CONFIG_IA32_EMULATION should have value n + + + + + + + + + + + + + + Disable the IPv6 protocol + + Red Hat Enterprise Linux 8 + + The kernel CONFIG_IPV6 should have value n + + + + + + + + + + + + + + Disable kexec system call + + Red Hat Enterprise Linux 8 + + The kernel CONFIG_KEXEC should have value n + + + + + + + + + + + + + + Disable legacy (BSD) PTY support + + Red Hat Enterprise Linux 8 + + The kernel CONFIG_LEGACY_PTYS should have value n + + + + + + + + + + + + + + Disable vsyscall emulation + + Red Hat Enterprise Linux 8 + + The kernel CONFIG_LEGACY_VSYSCALL_EMULATE should have value n + + + + + + + + + + + + + + Disable vsyscall mapping + + Red Hat Enterprise Linux 8 + + The kernel CONFIG_LEGACY_VSYSCALL_NONE should have value y + + + + + + + + + + + + + Disable the LDT (local descriptor table) + + Red Hat Enterprise Linux 8 + + The kernel CONFIG_MODIFY_LDT_SYSCALL should have value n + + + + + + + + + + + + + + Enable module signature verification + + Red Hat Enterprise Linux 8 + + The kernel CONFIG_MODULE_SIG should have value y + + + + + + + + + + + + + Enable automatic signing of all modules + + Red Hat Enterprise Linux 8 + + The kernel CONFIG_MODULE_SIG_ALL should have value y + + + + + + + + + + + + + Require modules to be validly signed + + Red Hat Enterprise Linux 8 + + The kernel CONFIG_MODULE_SIG_FORCE should have value y + + + + + + + + + + + + + Specify the hash to use when signing modules + + Red Hat Enterprise Linux 8 + + The kernel CONFIG_MODULE_SIG_HASH should have value according to var_kernel_config_module_sig_hash + + + + + + + + + + + + + Specify module signing key to use + + Red Hat Enterprise Linux 8 + + The kernel CONFIG_MODULE_SIG_KEY should have value according to var_kernel_config_module_sig_key + + + + + + + + + + + + + Sign kernel modules with SHA-512 + + Red Hat Enterprise Linux 8 + + The kernel CONFIG_MODULE_SIG_SHA512 should have value y + + + + + + + + + + + + + Enable poison of pages after freeing + + Red Hat Enterprise Linux 8 + + The kernel CONFIG_PAGE_POISONING should have value y + + + + + + + + + + + + + Enable poison without sanity check + + Red Hat Enterprise Linux 8 + + The kernel CONFIG_PAGE_POISONING_NO_SANITY should have value y + + + + + + + + + + + + + Use zero for poisoning instead of debugging value + + Red Hat Enterprise Linux 8 + + The kernel CONFIG_PAGE_POISONING_ZERO should have value y + + + + + + + + + + + + + Remove the kernel mapping in user mode + + Red Hat Enterprise Linux 8 + + The kernel CONFIG_PAGE_TABLE_ISOLATION should have value y + + + + + + + + + + + + + Kernel panic oops + + Red Hat Enterprise Linux 8 + + The kernel CONFIG_PANIC_ON_OOPS should have value y + + + + + + + + + + + + + Kernel panic timeout + + Red Hat Enterprise Linux 8 + + The kernel CONFIG_PANIC_TIMEOUT should have value according to var_kernel_config_panic_timeout + + + + + + + + + + + + + Disable support for /proc/kkcore + + Red Hat Enterprise Linux 8 + + The kernel CONFIG_PROC_KCORE should have value n + + + + + + + + + + + + + + Randomize the address of the kernel image (KASLR) + + Red Hat Enterprise Linux 8 + + The kernel CONFIG_RANDOMIZE_BASE should have value y + + + + + + + + + + + + + Randomize the kernel memory sections + + Red Hat Enterprise Linux 8 + + The kernel CONFIG_RANDOMIZE_MEMORY should have value y + + + + + + + + + + + + + Perform full reference count validation + + Red Hat Enterprise Linux 8 + + The kernel CONFIG_REFCOUNT_FULL should have value y + + + + + + + + + + + + + Avoid speculative indirect branches in kernel + + Red Hat Enterprise Linux 8 + + The kernel CONFIG_RETPOLINE should have value y + + + + + + + + + + + + + Detect stack corruption on calls to schedule() + + Red Hat Enterprise Linux 8 + + The kernel CONFIG_SCHED_STACK_END_CHECK should have value y + + + + + + + + + + + + + Enable seccomp to safely compute untrusted bytecode + + Red Hat Enterprise Linux 8 + + The kernel CONFIG_SECCOMP should have value y + + + + + + + + + + + + + Enable use of Berkeley Packet Filter with seccomp + + Red Hat Enterprise Linux 8 + + The kernel CONFIG_SECCOMP_FILTER should have value y + + + + + + + + + + + + + Enable different security models + + Red Hat Enterprise Linux 8 + + The kernel CONFIG_SECURITY should have value y + + + + + + + + + + + + + Restrict unprivileged access to the kernel syslog + + Red Hat Enterprise Linux 8 + + The kernel CONFIG_SECURITY_DMESG_RESTRICT should have value n + + + + + + + + + + + + + + Disable mutable hooks + + Red Hat Enterprise Linux 8 + + The kernel CONFIG_SECURITY_WRITABLE_HOOKS should have value y + + + + + + + + + + + + + Enable Yama support + + Red Hat Enterprise Linux 8 + + The kernel CONFIG_SECURITY_YAMA should have value y + + + + + + + + + + + + + Harden slab freelist metadata + + Red Hat Enterprise Linux 8 + + The kernel CONFIG_SLAB_FREELIST_HARDENED should have value y + + + + + + + + + + + + + Randomize slab freelist + + Red Hat Enterprise Linux 8 + + The kernel CONFIG_SLAB_FRELIST_RANDOM should have value y + + + + + + + + + + + + + Disallow merge of slab caches + + Red Hat Enterprise Linux 8 + + The kernel CONFIG_SLAB_MERGE_DEFAULT should have value n + + + + + + + + + + + + + + Enable SLUB debugging support + + Red Hat Enterprise Linux 8 + + The kernel CONFIG_SLUB_DEBUG should have value y + + + + + + + + + + + + + Stack Protector buffer overlow detection + + Red Hat Enterprise Linux 8 + + The kernel CONFIG_STACKPROTECTOR should have value y + + + + + + + + + + + + + Strong Stack Protector + + Red Hat Enterprise Linux 8 + + The kernel CONFIG_STACKPROTECTOR_STRONG should have value y + + + + + + + + + + + + + Make the kernel text and rodata read-only + + Red Hat Enterprise Linux 8 + + The kernel CONFIG_STRICT_KERNEL_RWX should have value y + + + + + + + + + + + + + Make the module text and rodata read-only + + Red Hat Enterprise Linux 8 + + The kernel CONFIG_STRICT_MODULE_RWX should have value y + + + + + + + + + + + + + Enable TCP/IP syncookie support + + Red Hat Enterprise Linux 8 + + The kernel CONFIG_SYN_COOKIES should have value y + + + + + + + + + + + + + Unmap kernel when running in userspace (aka KAISER) + + Red Hat Enterprise Linux 8 + + The kernel CONFIG_UNMAP_KERNEL_AT_EL0 should have value y + + + + + + + + + + + + + User a virtually-mapped stack + + Red Hat Enterprise Linux 8 + + The kernel CONFIG_VMAP_STACK should have value y + + + + + + + + + + + + + Disable x86 vsyscall emulation + + Red Hat Enterprise Linux 8 + + The kernel CONFIG_X86_VSYSCALL_EMULATION should have value n + + + + + + + + + + + + + + Disable ATM Support + + Red Hat Enterprise Linux 8 + + The kernel module atm should be disabled. + + + + + + + + + + + + + Disable Bluetooth Kernel Module + + Red Hat Enterprise Linux 8 + + The kernel module bluetooth should be disabled. + + + + + + + + + + + + + Disable CAN Support + + Red Hat Enterprise Linux 8 + + The kernel module can should be disabled. + + + + + + + + + + + + + Disable Kernel cfg80211 Module + + Red Hat Enterprise Linux 8 + + The kernel module cfg80211 should be disabled. + + + + + + + + + + + + Disable Mounting of cramfs + + Red Hat Enterprise Linux 8 + + The kernel module cramfs should be disabled. + + + + + + + + + + + + + Disable DCCP Support + + Red Hat Enterprise Linux 8 + + The kernel module dccp should be disabled. + + + + + + + + + + + + + Disable IEEE 1394 (FireWire) Support + + Red Hat Enterprise Linux 8 + + The kernel module firewire-core should be disabled. + + + + + + + + + + + + + Disable Mounting of freevxfs + + Red Hat Enterprise Linux 8 + + The kernel module freevxfs should be disabled. + + + + + + + + + + + + Disable Mounting of hfs + + Red Hat Enterprise Linux 8 + + The kernel module hfs should be disabled. + + + + + + + + + + + + Disable Mounting of hfsplus + + Red Hat Enterprise Linux 8 + + The kernel module hfsplus should be disabled. + + + + + + + + + + + + Disable Kernel iwlmvm Module + + Red Hat Enterprise Linux 8 + + The kernel module iwlmvm should be disabled. + + + + + + + + + + + + Disable Kernel iwlwifi Module + + Red Hat Enterprise Linux 8 + + The kernel module iwlwifi should be disabled. + + + + + + + + + + + + Disable Mounting of jffs2 + + Red Hat Enterprise Linux 8 + + The kernel module jffs2 should be disabled. + + + + + + + + + + + + Disable Kernel mac80211 Module + + Red Hat Enterprise Linux 8 + + The kernel module mac80211 should be disabled. + + + + + + + + + + + + Disable RDS Support + + Red Hat Enterprise Linux 8 + + The kernel module rds should be disabled. + + + + + + + + + + + + + Disable SCTP Support + + Red Hat Enterprise Linux 8 + + The kernel module sctp should be disabled. + + + + + + + + + + + + + Disable Mounting of squashfs + + Red Hat Enterprise Linux 8 + + The kernel module squashfs should be disabled. + + + + + + + + + + + + + Disable TIPC Support + + Red Hat Enterprise Linux 8 + + The kernel module tipc should be disabled. + + + + + + + + + + + + + Disable Mounting of udf + + Red Hat Enterprise Linux 8 + + The kernel module udf should be disabled. + + + + + + + + + + + + + Disable Modprobe Loading of USB Storage Driver + + Red Hat Enterprise Linux 8 + + The kernel module usb-storage should be disabled. + + + + + + + + + + + + + Disable the uvcvideo module + + Red Hat Enterprise Linux 8 + + The kernel module uvcvideo should be disabled. + + + + + + + + + + + + + Disable Mounting of vFAT filesystems + + Red Hat Enterprise Linux 8 + + The kernel module vfat should be disabled. + + + + + + + + + + + + + Add nosuid Option to /boot/efi + + Red Hat Enterprise Linux 8 + + /boot/efi should be mounted with mount option nosuid. + + + + + + + + + + + Add noauto Option to /boot + + Red Hat Enterprise Linux 8 + + /boot should be mounted with mount option noauto. + + + + + + + + + + Add nodev Option to /boot + + Red Hat Enterprise Linux 8 + + /boot should be mounted with mount option nodev. + + + + + + + + + + Add noexec Option to /boot + + Red Hat Enterprise Linux 8 + + /boot should be mounted with mount option noexec. + + + + + + + + + + Add nosuid Option to /boot + + Red Hat Enterprise Linux 8 + + /boot should be mounted with mount option nosuid. + + + + + + + + + + Add nodev Option to /dev/shm + + Red Hat Enterprise Linux 8 + + /dev/shm should be mounted with mount option nodev. + + + + + + + + + + + Add noexec Option to /dev/shm + + Red Hat Enterprise Linux 8 + + /dev/shm should be mounted with mount option noexec. + + + + + + + + + + + Add nosuid Option to /dev/shm + + Red Hat Enterprise Linux 8 + + /dev/shm should be mounted with mount option nosuid. + + + + + + + + + + + Add nodev Option to /home + + Red Hat Enterprise Linux 8 + + /home should be mounted with mount option nodev. + + + + + + + + + + Add noexec Option to /home + + Red Hat Enterprise Linux 8 + + /home should be mounted with mount option noexec. + + + + + + + + + + Add nosuid Option to /home + + Red Hat Enterprise Linux 8 + + /home should be mounted with mount option nosuid. + + + + + + + + + + Mount Remote Filesystems with Kerberos Security + + Red Hat Enterprise Linux 8 + + The sec_krb5_krb5i_krb5p option should be enabled for all NFS mounts in /etc/fstab. + + + + + + + + + + Mount Remote Filesystems with nodev + + Red Hat Enterprise Linux 8 + + The nodev option should be enabled for all NFS mounts in /etc/fstab. + + + + + + + + + + + Add nodev Option to Removable Media Partitions + + Red Hat Enterprise Linux 8 + + The nodev option should be enabled for all removable devices mounts in /etc/fstab. + + + + + + + + + + + + + + + + + + Mount Remote Filesystems with noexec + + Red Hat Enterprise Linux 8 + + The noexec option should be enabled for all NFS mounts in /etc/fstab. + + + + + + + + + + + Add noexec Option to Removable Media Partitions + + Red Hat Enterprise Linux 8 + + The noexec option should be enabled for all removable devices mounts in /etc/fstab. + + + + + + + + + + + + + + + + + + Mount Remote Filesystems with nosuid + + Red Hat Enterprise Linux 8 + + The nosuid option should be enabled for all NFS mounts in /etc/fstab. + + + + + + + + + + + Add nosuid Option to Removable Media Partitions + + Red Hat Enterprise Linux 8 + + The nosuid option should be enabled for all removable devices mounts in /etc/fstab. + + + + + + + + + + + + + + + + + + Add nosuid Option to /opt + + Red Hat Enterprise Linux 8 + + /opt should be mounted with mount option nosuid. + + + + + + + + + + Add hidepid Option to /proc + + Red Hat Enterprise Linux 8 + + /proc should be mounted with mount option hidepid. + + + + + + + + + + + Add nosuid Option to /srv + + Red Hat Enterprise Linux 8 + + /srv should be mounted with mount option nosuid. + + + + + + + + + + Add nodev Option to /tmp + + Red Hat Enterprise Linux 8 + + /tmp should be mounted with mount option nodev. + + + + + + + + + + Add noexec Option to /tmp + + Red Hat Enterprise Linux 8 + + /tmp should be mounted with mount option noexec. + + + + + + + + + + Add nosuid Option to /tmp + + Red Hat Enterprise Linux 8 + + /tmp should be mounted with mount option nosuid. + + + + + + + + + + Add nodev Option to /var/log/audit + + Red Hat Enterprise Linux 8 + + /var/log/audit should be mounted with mount option nodev. + + + + + + + + + + Add noexec Option to /var/log/audit + + Red Hat Enterprise Linux 8 + + /var/log/audit should be mounted with mount option noexec. + + + + + + + + + + Add nosuid Option to /var/log/audit + + Red Hat Enterprise Linux 8 + + /var/log/audit should be mounted with mount option nosuid. + + + + + + + + + + Add nodev Option to /var/log + + Red Hat Enterprise Linux 8 + + /var/log should be mounted with mount option nodev. + + + + + + + + + + Add noexec Option to /var/log + + Red Hat Enterprise Linux 8 + + /var/log should be mounted with mount option noexec. + + + + + + + + + + Add nosuid Option to /var/log + + Red Hat Enterprise Linux 8 + + /var/log should be mounted with mount option nosuid. + + + + + + + + + + Add nodev Option to /var + + Red Hat Enterprise Linux 8 + + /var should be mounted with mount option nodev. + + + + + + + + + + Add noexec Option to /var + + Red Hat Enterprise Linux 8 + + /var should be mounted with mount option noexec. + + + + + + + + + + Add nosuid Option to /var + + Red Hat Enterprise Linux 8 + + /var should be mounted with mount option nosuid. + + + + + + + + + + Add nodev Option to /var/tmp + + Red Hat Enterprise Linux 8 + + /var/tmp should be mounted with mount option nodev. + + + + + + + + + + Add noexec Option to /var/tmp + + Red Hat Enterprise Linux 8 + + /var/tmp should be mounted with mount option noexec. + + + + + + + + + + Add nosuid Option to /var/tmp + + Red Hat Enterprise Linux 8 + + /var/tmp should be mounted with mount option nosuid. + + + + + + + + + + package_GConf2_installed + + Red Hat Enterprise Linux 8 + + The RPM package GConf2 should be installed. + + + + + + + + + Install the Host Intrusion Prevention System (HIPS) Module + + Red Hat Enterprise Linux 8 + + The RPM package MFEhiplsm should be installed. + + + + + + + + + Uninstall abrt-addon-ccpp Package + + Red Hat Enterprise Linux 8 + + The RPM package abrt-addon-ccpp should be removed. + + + + + + + + + + Uninstall abrt-addon-kerneloops Package + + Red Hat Enterprise Linux 8 + + The RPM package abrt-addon-kerneloops should be removed. + + + + + + + + + + Uninstall abrt-cli Package + + Red Hat Enterprise Linux 8 + + The RPM package abrt-cli should be removed. + + + + + + + + + + Uninstall abrt-plugin-logger Package + + Red Hat Enterprise Linux 8 + + The RPM package abrt-plugin-logger should be removed. + + + + + + + + + + Uninstall abrt-plugin-rhtsupport Package + + Red Hat Enterprise Linux 8 + + The RPM package abrt-plugin-rhtsupport should be removed. + + + + + + + + + + Uninstall abrt-plugin-sosreport Package + + Red Hat Enterprise Linux 8 + + The RPM package abrt-plugin-sosreport should be removed. + + + + + + + + + + Uninstall Automatic Bug Reporting Tool (abrt) + + Red Hat Enterprise Linux 8 + + The RPM package abrt should be removed. + + + + + + + + + + Install AIDE + + Red Hat Enterprise Linux 8 + + The RPM package aide should be installed. + + + + + + + + + + Install audispd-plugins Package + + Red Hat Enterprise Linux 8 + + The RPM package audispd-plugins should be installed. + + + + + + + + + + Ensure the default plugins for the audit dispatcher are Installed + + Red Hat Enterprise Linux 8 + + The RPM package audit-audispd-plugins should be installed. + + + + + + + + + Ensure the audit Subsystem is Installed + + Red Hat Enterprise Linux 8 + + The RPM package audit should be installed. + + + + + + + + + + package_avahi_installed + + Red Hat Enterprise Linux 8 + + The RPM package avahi should be installed. + + + + + + + + + Uninstall bind Package + + Red Hat Enterprise Linux 8 + + The RPM package bind should be removed. + + + + + + + + + + Install binutils Package + + Red Hat Enterprise Linux 8 + + The RPM package binutils should be installed. + + + + + + + + + + The Chrony package is installed + + Red Hat Enterprise Linux 8 + + The RPM package chrony should be installed. + + + + + + + + + + Install the cron service + + Red Hat Enterprise Linux 8 + + The RPM package cron should be installed. + + + + + + + + + Install crypto-policies package + + Red Hat Enterprise Linux 8 + + The RPM package crypto-policies should be installed. + + + + + + + + + + package_dconf_installed + + Red Hat Enterprise Linux 8 + + The RPM package dconf should be installed. + + + + + + + + + Uninstall DHCP Server Package + + Red Hat Enterprise Linux 8 + + The RPM package dhcp-server should be removed. + + + + + + + + + + Install dnf-automatic Package + + Red Hat Enterprise Linux 8 + + The RPM package dnf-automatic should be installed. + + + + + + + + + + Install dnf-plugin-subscription-manager Package + + Red Hat Enterprise Linux 8 + + The RPM package dnf-plugin-subscription-manager should be installed. + + + + + + + + + + Uninstall dovecot Package + + Red Hat Enterprise Linux 8 + + The RPM package dovecot should be removed. + + + + + + + + + + package_esc_installed + + Red Hat Enterprise Linux 8 + + The RPM package esc should be installed. + + + + + + + + + Install fapolicyd Package + + Red Hat Enterprise Linux 8 + + The RPM package fapolicyd should be installed. + + + + + + + + + + Install firewalld Package + + Red Hat Enterprise Linux 8 + + The RPM package firewalld should be installed. + + + + + + + + + + Remove the FreeRadius Server Package + + Red Hat Enterprise Linux 8 + + The RPM package freeradius should be removed. + + + + + + + + + + package_gdm_installed + + Red Hat Enterprise Linux 8 + + The RPM package gdm should be installed. + + + + + + + + + Remove the GDM Package Group + + Red Hat Enterprise Linux 8 + + The RPM package gdm should be removed. + + + + + + + + + + Uninstall geolite2-city Package + + Red Hat Enterprise Linux 8 + + The RPM package geolite2-city should be removed. + + + + + + + + + + Uninstall geolite2-country Package + + Red Hat Enterprise Linux 8 + + The RPM package geolite2-country should be removed. + + + + + + + + + + Ensure gnutls-utils is installed + + Red Hat Enterprise Linux 8 + + The RPM package gnutls-utils should be installed. + + + + + + + + + + Uninstall gssproxy Package + + Red Hat Enterprise Linux 8 + + The RPM package gssproxy should be removed. + + + + + + + + + + Uninstall httpd Package + + Red Hat Enterprise Linux 8 + + The RPM package httpd should be removed. + + + + + + + + + + Uninstall the inet-based telnet server + + Red Hat Enterprise Linux 8 + + The RPM package inetutils-telnetd should be removed. + + + + + + + + + Uninstall iprutils Package + + Red Hat Enterprise Linux 8 + + The RPM package iprutils should be removed. + + + + + + + + + + Install iptables-services Package + + Red Hat Enterprise Linux 8 + + The RPM package iptables-services should be installed. + + + + + + + + + + Install iptables Package + + Red Hat Enterprise Linux 8 + + The RPM package iptables should be installed. + + + + + + + + + + Remove the Kerberos Server Package + + Red Hat Enterprise Linux 8 + + The RPM package krb5-server should be removed. + + + + + + + + + + Uninstall krb5-workstation Package + + Red Hat Enterprise Linux 8 + + The RPM package krb5-workstation should be removed. + + + + + + + + + + Install libcap-ng-utils Package + + Red Hat Enterprise Linux 8 + + The RPM package libcap-ng-utils should be installed. + + + + + + + + + + Uninstall libreport-plugin-logger Package + + Red Hat Enterprise Linux 8 + + The RPM package libreport-plugin-logger should be removed. + + + + + + + + + + Uninstall libreport-plugin-rhtsupport Package + + Red Hat Enterprise Linux 8 + + The RPM package libreport-plugin-rhtsupport should be removed. + + + + + + + + + + Install libreswan Package + + Red Hat Enterprise Linux 8 + + The RPM package libreswan should be installed. + + + + + + + + + + Install libselinux Package + + Red Hat Enterprise Linux 8 + + The RPM package libselinux should be installed. + + + + + + + + + + Install McAfee Endpoint Security for Linux (ENSL) + + Red Hat Enterprise Linux 8 + + The RPM package McAfeeTP should be installed. + + + + + + + + + + Uninstall mcstrans Package + + Red Hat Enterprise Linux 8 + + The RPM package mcstrans should be removed. + + + + + + + + + + Uninstall net-snmp Package + + Red Hat Enterprise Linux 8 + + The RPM package net-snmp should be removed. + + + + + + + + + + Uninstall nfs-utils Package + + Red Hat Enterprise Linux 8 + + The RPM package nfs-utils should be removed. + + + + + + + + + + Uninstall the nis package + + Red Hat Enterprise Linux 8 + + The RPM package nis should be removed. + + + + + + + + + Ensure nss-tools is installed + + Red Hat Enterprise Linux 8 + + The RPM package nss-tools should be installed. + + + + + + + + + + Install the ntp service + + Red Hat Enterprise Linux 8 + + The RPM package ntp should be installed. + + + + + + + + + Uninstall the ntpdate package + + Red Hat Enterprise Linux 8 + + The RPM package ntpdate should be removed. + + + + + + + + + Ensure LDAP client is not installed + + Red Hat Enterprise Linux 8 + + The RPM package openldap-clients should be removed. + + + + + + + + + + Uninstall openldap-servers Package + + Red Hat Enterprise Linux 8 + + The RPM package openldap-servers should be removed. + + + + + + + + + + Install the opensc Package For Multifactor Authentication + + Red Hat Enterprise Linux 8 + + The RPM package opensc should be installed. + + + + + + + + + + Install openscap-scanner Package + + Red Hat Enterprise Linux 8 + + The RPM package openscap-scanner should be installed. + + + + + + + + + + Install OpenSSH client software + + Red Hat Enterprise Linux 8 + + The RPM package openssh-clients should be installed. + + + + + + + + + + Install the OpenSSH Server Package + + Red Hat Enterprise Linux 8 + + The RPM package openssh-server should be installed. + + + + + + + + + + Remove the OpenSSH Server Package + + Red Hat Enterprise Linux 8 + + The RPM package openssh-server should be removed. + + + + + + + + + package_pam_ldap_removed + + Red Hat Enterprise Linux 8 + + The RPM package pam_ldap should be removed. + + + + + + + + + Install pam_pwquality Package + + Red Hat Enterprise Linux 8 + + The RPM package libpwquality should be installed. + + + + + + + + + Install the pcsc-lite package + + Red Hat Enterprise Linux 8 + + The RPM package pcsc-lite should be installed. + + + + + + + + + + Uninstall pigz Package + + Red Hat Enterprise Linux 8 + + The RPM package pigz should be removed. + + + + + + + + + + Install policycoreutils-python-utils package + + Red Hat Enterprise Linux 8 + + The RPM package policycoreutils-python-utils should be installed. + + + + + + + + + + Install policycoreutils Package + + Red Hat Enterprise Linux 8 + + The RPM package policycoreutils should be installed. + + + + + + + + + + The Postfix package is installed + + Red Hat Enterprise Linux 8 + + The RPM package postfix should be installed. + + + + + + + + + + package_prelink_removed + + Red Hat Enterprise Linux 8 + + The RPM package prelink should be removed. + + + + + + + + + Install the psacct package + + Red Hat Enterprise Linux 8 + + The RPM package psacct should be installed. + + + + + + + + + + Uninstall python3-abrt-addon Package + + Red Hat Enterprise Linux 8 + + The RPM package python3-abrt-addon should be removed. + + + + + + + + + + Uninstall quagga Package + + Red Hat Enterprise Linux 8 + + The RPM package quagga should be removed. + + + + + + + + + + Install rear Package + + Red Hat Enterprise Linux 8 + + The RPM package rear should be installed. + + + + + + + + + + Install rng-tools Package + + Red Hat Enterprise Linux 8 + + The RPM package rng-tools should be installed. + + + + + + + + + + Uninstall rsh-server Package + + Red Hat Enterprise Linux 8 + + The RPM package rsh-server should be removed. + + + + + + + + + + Uninstall rsh Package + + Red Hat Enterprise Linux 8 + + The RPM package rsh should be removed. + + + + + + + + + + Ensure rsyslog-gnutls is installed + + Red Hat Enterprise Linux 8 + + The RPM package rsyslog-gnutls should be installed. + + + + + + + + + + Ensure rsyslog is Installed + + Red Hat Enterprise Linux 8 + + The RPM package rsyslog should be installed. + + + + + + + + + + Install the Samba Common Package + + Red Hat Enterprise Linux 8 + + The RPM package samba-common should be installed. + + + + + + + + + package_samba-common_removed + + Red Hat Enterprise Linux 8 + + The RPM package samba-common should be removed. + + + + + + + + + Uninstall Samba Package + + Red Hat Enterprise Linux 8 + + The RPM package samba should be removed. + + + + + + + + + + Install scap-security-guide Package + + Red Hat Enterprise Linux 8 + + The RPM package scap-security-guide should be installed. + + + + + + + + + + Uninstall Sendmail Package + + Red Hat Enterprise Linux 8 + + The RPM package sendmail should be removed. + + + + + + + + + + Uninstall setroubleshoot-plugins Package + + Red Hat Enterprise Linux 8 + + The RPM package setroubleshoot-plugins should be removed. + + + + + + + + + + Uninstall setroubleshoot-server Package + + Red Hat Enterprise Linux 8 + + The RPM package setroubleshoot-server should be removed. + + + + + + + + + + Uninstall setroubleshoot Package + + Red Hat Enterprise Linux 8 + + The RPM package setroubleshoot should be removed. + + + + + + + + + + Uninstall squid Package + + Red Hat Enterprise Linux 8 + + The RPM package squid should be removed. + + + + + + + + + + Install sssd-ipa Package + + Red Hat Enterprise Linux 8 + + The RPM package sssd-ipa should be installed. + + + + + + + + + + Install the SSSD Package + + Red Hat Enterprise Linux 8 + + The RPM package sssd should be installed. + + + + + + + + + + Install subscription-manager Package + + Red Hat Enterprise Linux 8 + + The RPM package subscription-manager should be installed. + + + + + + + + + + Install sudo Package + + Red Hat Enterprise Linux 8 + + The RPM package sudo should be installed. + + + + + + + + + + Ensure syslog-ng is Installed + + Red Hat Enterprise Linux 8 + + The RPM package syslog-ng should be installed. + + + + + + + + + Uninstall talk-server Package + + Red Hat Enterprise Linux 8 + + The RPM package talk-server should be removed. + + + + + + + + + + Uninstall talk Package + + Red Hat Enterprise Linux 8 + + The RPM package talk should be removed. + + + + + + + + + + Install tar Package + + Red Hat Enterprise Linux 8 + + The RPM package tar should be installed. + + + + + + + + + + Uninstall telnet-server Package + + Red Hat Enterprise Linux 8 + + The RPM package telnet-server should be removed. + + + + + + + + + + Remove telnet Clients + + Red Hat Enterprise Linux 8 + + The RPM package telnet should be removed. + + + + + + + + + + Uninstall the ssl compliant telnet server + + Red Hat Enterprise Linux 8 + + The RPM package telnetd-ssl should be removed. + + + + + + + + + Uninstall the telnet server + + Red Hat Enterprise Linux 8 + + The RPM package telnetd should be removed. + + + + + + + + + + Uninstall tftp-server Package + + Red Hat Enterprise Linux 8 + + The RPM package tftp-server should be removed. + + + + + + + + + + Remove tftp Daemon + + Red Hat Enterprise Linux 8 + + The RPM package tftp should be removed. + + + + + + + + + + Install the tmux Package + + Red Hat Enterprise Linux 8 + + The RPM package tmux should be installed. + + + + + + + + + + Uninstall tuned Package + + Red Hat Enterprise Linux 8 + + The RPM package tuned should be removed. + + + + + + + + + + Install usbguard Package + + Red Hat Enterprise Linux 8 + + The RPM package usbguard should be installed. + + + + + + + + + + Install vim Package + + Red Hat Enterprise Linux 8 + + The RPM package vim-enhanced should be installed. + + + + + + + + + + Install vsftpd Package + + Red Hat Enterprise Linux 8 + + The RPM package vsftpd should be installed. + + + + + + + + + + Uninstall vsftpd Package + + Red Hat Enterprise Linux 8 + + The RPM package vsftpd should be removed. + + + + + + + + + + Uninstall xinetd Package + + Red Hat Enterprise Linux 8 + + The RPM package xinetd should be removed. + + + + + + + + + + Remove the X Windows Package Group + + Red Hat Enterprise Linux 8 + + The RPM package xorg-x11-server-common should be removed. + + + + + + + + + + Remove NIS Client + + Red Hat Enterprise Linux 8 + + The RPM package ypbind should be removed. + + + + + + + + + + Uninstall ypserv Package + + Red Hat Enterprise Linux 8 + + The RPM package ypserv should be removed. + + + + + + + + + + Ensure /boot Located On Separate Partition + + Red Hat Enterprise Linux 8 + + If stored locally, create a separate partition for + /boot. If /boot will be mounted from another + system such as an NFS server, then creating a separate partition is not + necessary at this time, and the mountpoint can instead be configured + later. + + + + + + + + + + Ensure /home Located On Separate Partition + + Red Hat Enterprise Linux 8 + + If stored locally, create a separate partition for + /home. If /home will be mounted from another + system such as an NFS server, then creating a separate partition is not + necessary at this time, and the mountpoint can instead be configured + later. + + + + + + + + + + Ensure /opt Located On Separate Partition + + Red Hat Enterprise Linux 8 + + If stored locally, create a separate partition for + /opt. If /opt will be mounted from another + system such as an NFS server, then creating a separate partition is not + necessary at this time, and the mountpoint can instead be configured + later. + + + + + + + + + + Ensure /srv Located On Separate Partition + + Red Hat Enterprise Linux 8 + + If stored locally, create a separate partition for + /srv. If /srv will be mounted from another + system such as an NFS server, then creating a separate partition is not + necessary at this time, and the mountpoint can instead be configured + later. + + + + + + + + + + Ensure /tmp Located On Separate Partition + + Red Hat Enterprise Linux 8 + + If stored locally, create a separate partition for + /tmp. If /tmp will be mounted from another + system such as an NFS server, then creating a separate partition is not + necessary at this time, and the mountpoint can instead be configured + later. + + + + + + + + + + Ensure /usr Located On Separate Partition + + Red Hat Enterprise Linux 8 + + If stored locally, create a separate partition for + /usr. If /usr will be mounted from another + system such as an NFS server, then creating a separate partition is not + necessary at this time, and the mountpoint can instead be configured + later. + + + + + + + + + + Ensure /var Located On Separate Partition + + Red Hat Enterprise Linux 8 + + If stored locally, create a separate partition for + /var. If /var will be mounted from another + system such as an NFS server, then creating a separate partition is not + necessary at this time, and the mountpoint can instead be configured + later. + + + + + + + + + + Ensure /var/log Located On Separate Partition + + Red Hat Enterprise Linux 8 + + If stored locally, create a separate partition for + /var/log. If /var/log will be mounted from another + system such as an NFS server, then creating a separate partition is not + necessary at this time, and the mountpoint can instead be configured + later. + + + + + + + + + + Ensure /var/log/audit Located On Separate Partition + + Red Hat Enterprise Linux 8 + + If stored locally, create a separate partition for + /var/log/audit. If /var/log/audit will be mounted from another + system such as an NFS server, then creating a separate partition is not + necessary at this time, and the mountpoint can instead be configured + later. + + + + + + + + + + Ensure /var/tmp Located On Separate Partition + + Red Hat Enterprise Linux 8 + + If stored locally, create a separate partition for + /var/tmp. If /var/tmp will be mounted from another + system such as an NFS server, then creating a separate partition is not + necessary at this time, and the mountpoint can instead be configured + later. + + + + + + + + + + Verify the system-wide library files in directories +"/lib", "/lib64", "/usr/lib/" and "/usr/lib64" are group-owned by root. + + Red Hat Enterprise Linux 8 + + This test makes sure that /lib/, /lib64/, /usr/lib/, /usr/lib64/ is group owned by 0. + + + + + + + + + + + + + Disable the abrt_anon_write SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'abrt_anon_write' boolean should be set in the system configuration. + + + + + + + + + Disable the abrt_handle_event SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'abrt_handle_event' boolean should be set in the system configuration. + + + + + + + + + Disable the abrt_upload_watch_anon_write SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'abrt_upload_watch_anon_write' boolean should be set in the system configuration. + + + + + + + + + Enable the antivirus_can_scan_system SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'antivirus_can_scan_system' boolean should be set in the system configuration. + + + + + + + + + Disable the antivirus_use_jit SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'antivirus_use_jit' boolean should be set in the system configuration. + + + + + + + + + Enable the auditadm_exec_content SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'auditadm_exec_content' boolean should be set in the system configuration. + + + + + + + + + + Disable the authlogin_nsswitch_use_ldap SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'authlogin_nsswitch_use_ldap' boolean should be set in the system configuration. + + + + + + + + + + Disable the authlogin_radius SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'authlogin_radius' boolean should be set in the system configuration. + + + + + + + + + + Disable the authlogin_yubikey SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'authlogin_yubikey' boolean should be set in the system configuration. + + + + + + + + + Disable the awstats_purge_apache_log_files SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'awstats_purge_apache_log_files' boolean should be set in the system configuration. + + + + + + + + + Disable the boinc_execmem SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'boinc_execmem' boolean should be set in the system configuration. + + + + + + + + + + Disable the cdrecord_read_content SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'cdrecord_read_content' boolean should be set in the system configuration. + + + + + + + + + Disable the cluster_can_network_connect SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'cluster_can_network_connect' boolean should be set in the system configuration. + + + + + + + + + Disable the cluster_manage_all_files SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'cluster_manage_all_files' boolean should be set in the system configuration. + + + + + + + + + Disable the cluster_use_execmem SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'cluster_use_execmem' boolean should be set in the system configuration. + + + + + + + + + + Disable the cobbler_anon_write SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'cobbler_anon_write' boolean should be set in the system configuration. + + + + + + + + + Disable the cobbler_can_network_connect SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'cobbler_can_network_connect' boolean should be set in the system configuration. + + + + + + + + + Disable the cobbler_use_cifs SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'cobbler_use_cifs' boolean should be set in the system configuration. + + + + + + + + + Disable the cobbler_use_nfs SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'cobbler_use_nfs' boolean should be set in the system configuration. + + + + + + + + + Disable the collectd_tcp_network_connect SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'collectd_tcp_network_connect' boolean should be set in the system configuration. + + + + + + + + + Disable the condor_tcp_network_connect SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'condor_tcp_network_connect' boolean should be set in the system configuration. + + + + + + + + + Disable the conman_can_network SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'conman_can_network' boolean should be set in the system configuration. + + + + + + + + + Disable the container_connect_any SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'container_connect_any' boolean should be set in the system configuration. + + + + + + + + + Disable the cron_can_relabel SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'cron_can_relabel' boolean should be set in the system configuration. + + + + + + + + + Disable the cron_system_cronjob_use_shares SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'cron_system_cronjob_use_shares' boolean should be set in the system configuration. + + + + + + + + + Enable the cron_userdomain_transition SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'cron_userdomain_transition' boolean should be set in the system configuration. + + + + + + + + + Disable the cups_execmem SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'cups_execmem' boolean should be set in the system configuration. + + + + + + + + + + Disable the cvs_read_shadow SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'cvs_read_shadow' boolean should be set in the system configuration. + + + + + + + + + Disable the daemons_dump_core SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'daemons_dump_core' boolean should be set in the system configuration. + + + + + + + + + Disable the daemons_enable_cluster_mode SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'daemons_enable_cluster_mode' boolean should be set in the system configuration. + + + + + + + + + Disable the daemons_use_tcp_wrapper SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'daemons_use_tcp_wrapper' boolean should be set in the system configuration. + + + + + + + + + Disable the daemons_use_tty SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'daemons_use_tty' boolean should be set in the system configuration. + + + + + + + + + Enable the dbadm_exec_content SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'dbadm_exec_content' boolean should be set in the system configuration. + + + + + + + + + Disable the dbadm_manage_user_files SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'dbadm_manage_user_files' boolean should be set in the system configuration. + + + + + + + + + Disable the dbadm_read_user_files SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'dbadm_read_user_files' boolean should be set in the system configuration. + + + + + + + + + Configure the deny_execmem SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'deny_execmem' boolean should be set in the system configuration. + + + + + + + + + + Disable the deny_ptrace SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'deny_ptrace' boolean should be set in the system configuration. + + + + + + + + + Disable the dhcpc_exec_iptables SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'dhcpc_exec_iptables' boolean should be set in the system configuration. + + + + + + + + + Disable the dhcpd_use_ldap SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'dhcpd_use_ldap' boolean should be set in the system configuration. + + + + + + + + + Enable the domain_fd_use SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'domain_fd_use' boolean should be set in the system configuration. + + + + + + + + + Disable the domain_kernel_load_modules SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'domain_kernel_load_modules' boolean should be set in the system configuration. + + + + + + + + + Disable the entropyd_use_audio SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'entropyd_use_audio' boolean should be set in the system configuration. + + + + + + + + + Disable the exim_can_connect_db SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'exim_can_connect_db' boolean should be set in the system configuration. + + + + + + + + + Disable the exim_manage_user_files SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'exim_manage_user_files' boolean should be set in the system configuration. + + + + + + + + + Disable the exim_read_user_files SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'exim_read_user_files' boolean should be set in the system configuration. + + + + + + + + + Disable the fcron_crond SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'fcron_crond' boolean should be set in the system configuration. + + + + + + + + + Disable the fenced_can_network_connect SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'fenced_can_network_connect' boolean should be set in the system configuration. + + + + + + + + + Disable the fenced_can_ssh SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'fenced_can_ssh' boolean should be set in the system configuration. + + + + + + + + + Enable the fips_mode SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'fips_mode' boolean should be set in the system configuration. + + + + + + + + + Disable the ftpd_anon_write SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'ftpd_anon_write' boolean should be set in the system configuration. + + + + + + + + + Disable the ftpd_connect_all_unreserved SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'ftpd_connect_all_unreserved' boolean should be set in the system configuration. + + + + + + + + + Disable the ftpd_connect_db SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'ftpd_connect_db' boolean should be set in the system configuration. + + + + + + + + + Disable the ftpd_full_access SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'ftpd_full_access' boolean should be set in the system configuration. + + + + + + + + + Disable the ftpd_use_cifs SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'ftpd_use_cifs' boolean should be set in the system configuration. + + + + + + + + + Disable the ftpd_use_fusefs SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'ftpd_use_fusefs' boolean should be set in the system configuration. + + + + + + + + + Disable the ftpd_use_nfs SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'ftpd_use_nfs' boolean should be set in the system configuration. + + + + + + + + + Disable the ftpd_use_passive_mode SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'ftpd_use_passive_mode' boolean should be set in the system configuration. + + + + + + + + + Disable the git_cgi_enable_homedirs SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'git_cgi_enable_homedirs' boolean should be set in the system configuration. + + + + + + + + + Disable the git_cgi_use_cifs SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'git_cgi_use_cifs' boolean should be set in the system configuration. + + + + + + + + + Disable the git_cgi_use_nfs SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'git_cgi_use_nfs' boolean should be set in the system configuration. + + + + + + + + + Disable the git_session_bind_all_unreserved_ports SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'git_session_bind_all_unreserved_ports' boolean should be set in the system configuration. + + + + + + + + + Disable the git_session_users SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'git_session_users' boolean should be set in the system configuration. + + + + + + + + + Disable the git_system_enable_homedirs SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'git_system_enable_homedirs' boolean should be set in the system configuration. + + + + + + + + + Disable the git_system_use_cifs SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'git_system_use_cifs' boolean should be set in the system configuration. + + + + + + + + + Disable the git_system_use_nfs SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'git_system_use_nfs' boolean should be set in the system configuration. + + + + + + + + + Disable the gitosis_can_sendmail SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'gitosis_can_sendmail' boolean should be set in the system configuration. + + + + + + + + + Disable the glance_api_can_network SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'glance_api_can_network' boolean should be set in the system configuration. + + + + + + + + + Disable the glance_use_execmem SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'glance_use_execmem' boolean should be set in the system configuration. + + + + + + + + + + Disable the glance_use_fusefs SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'glance_use_fusefs' boolean should be set in the system configuration. + + + + + + + + + Disable the global_ssp SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'global_ssp' boolean should be set in the system configuration. + + + + + + + + + Disable the gluster_anon_write SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'gluster_anon_write' boolean should be set in the system configuration. + + + + + + + + + Disable the gluster_export_all_ro SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'gluster_export_all_ro' boolean should be set in the system configuration. + + + + + + + + + Configure the gluster_export_all_rw SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'gluster_export_all_rw' boolean should be set in the system configuration. + + + + + + + + + Disable the gpg_web_anon_write SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'gpg_web_anon_write' boolean should be set in the system configuration. + + + + + + + + + Enable the gssd_read_tmp SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'gssd_read_tmp' boolean should be set in the system configuration. + + + + + + + + + Disable the guest_exec_content SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'guest_exec_content' boolean should be set in the system configuration. + + + + + + + + + Disable the haproxy_connect_any SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'haproxy_connect_any' boolean should be set in the system configuration. + + + + + + + + + Disable the httpd_anon_write SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'httpd_anon_write' boolean should be set in the system configuration. + + + + + + + + + Configure the httpd_builtin_scripting SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'httpd_builtin_scripting' boolean should be set in the system configuration. + + + + + + + + + Disable the httpd_can_check_spam SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'httpd_can_check_spam' boolean should be set in the system configuration. + + + + + + + + + Disable the httpd_can_connect_ftp SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'httpd_can_connect_ftp' boolean should be set in the system configuration. + + + + + + + + + Disable the httpd_can_connect_ldap SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'httpd_can_connect_ldap' boolean should be set in the system configuration. + + + + + + + + + Disable the httpd_can_connect_mythtv SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'httpd_can_connect_mythtv' boolean should be set in the system configuration. + + + + + + + + + Disable the httpd_can_connect_zabbix SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'httpd_can_connect_zabbix' boolean should be set in the system configuration. + + + + + + + + + Disable the httpd_can_network_connect SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'httpd_can_network_connect' boolean should be set in the system configuration. + + + + + + + + + Disable the httpd_can_network_connect_cobbler SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'httpd_can_network_connect_cobbler' boolean should be set in the system configuration. + + + + + + + + + Disable the httpd_can_network_connect_db SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'httpd_can_network_connect_db' boolean should be set in the system configuration. + + + + + + + + + Disable the httpd_can_network_memcache SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'httpd_can_network_memcache' boolean should be set in the system configuration. + + + + + + + + + Disable the httpd_can_network_relay SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'httpd_can_network_relay' boolean should be set in the system configuration. + + + + + + + + + Disable the httpd_can_sendmail SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'httpd_can_sendmail' boolean should be set in the system configuration. + + + + + + + + + Disable the httpd_dbus_avahi SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'httpd_dbus_avahi' boolean should be set in the system configuration. + + + + + + + + + Disable the httpd_dbus_sssd SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'httpd_dbus_sssd' boolean should be set in the system configuration. + + + + + + + + + Disable the httpd_dontaudit_search_dirs SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'httpd_dontaudit_search_dirs' boolean should be set in the system configuration. + + + + + + + + + Configure the httpd_enable_cgi SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'httpd_enable_cgi' boolean should be set in the system configuration. + + + + + + + + + Disable the httpd_enable_ftp_server SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'httpd_enable_ftp_server' boolean should be set in the system configuration. + + + + + + + + + Disable the httpd_enable_homedirs SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'httpd_enable_homedirs' boolean should be set in the system configuration. + + + + + + + + + Disable the httpd_execmem SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'httpd_execmem' boolean should be set in the system configuration. + + + + + + + + + + Enable the httpd_graceful_shutdown SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'httpd_graceful_shutdown' boolean should be set in the system configuration. + + + + + + + + + Disable the httpd_manage_ipa SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'httpd_manage_ipa' boolean should be set in the system configuration. + + + + + + + + + Disable the httpd_mod_auth_ntlm_winbind SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'httpd_mod_auth_ntlm_winbind' boolean should be set in the system configuration. + + + + + + + + + Disable the httpd_mod_auth_pam SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'httpd_mod_auth_pam' boolean should be set in the system configuration. + + + + + + + + + Disable the httpd_read_user_content SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'httpd_read_user_content' boolean should be set in the system configuration. + + + + + + + + + Disable the httpd_run_ipa SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'httpd_run_ipa' boolean should be set in the system configuration. + + + + + + + + + Disable the httpd_run_preupgrade SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'httpd_run_preupgrade' boolean should be set in the system configuration. + + + + + + + + + Disable the httpd_run_stickshift SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'httpd_run_stickshift' boolean should be set in the system configuration. + + + + + + + + + Disable the httpd_serve_cobbler_files SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'httpd_serve_cobbler_files' boolean should be set in the system configuration. + + + + + + + + + Disable the httpd_setrlimit SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'httpd_setrlimit' boolean should be set in the system configuration. + + + + + + + + + Disable the httpd_ssi_exec SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'httpd_ssi_exec' boolean should be set in the system configuration. + + + + + + + + + Disable the httpd_sys_script_anon_write SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'httpd_sys_script_anon_write' boolean should be set in the system configuration. + + + + + + + + + Disable the httpd_tmp_exec SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'httpd_tmp_exec' boolean should be set in the system configuration. + + + + + + + + + Disable the httpd_tty_comm SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'httpd_tty_comm' boolean should be set in the system configuration. + + + + + + + + + Disable the httpd_unified SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'httpd_unified' boolean should be set in the system configuration. + + + + + + + + + Disable the httpd_use_cifs SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'httpd_use_cifs' boolean should be set in the system configuration. + + + + + + + + + Disable the httpd_use_fusefs SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'httpd_use_fusefs' boolean should be set in the system configuration. + + + + + + + + + Disable the httpd_use_gpg SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'httpd_use_gpg' boolean should be set in the system configuration. + + + + + + + + + Disable the httpd_use_nfs SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'httpd_use_nfs' boolean should be set in the system configuration. + + + + + + + + + Disable the httpd_use_openstack SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'httpd_use_openstack' boolean should be set in the system configuration. + + + + + + + + + Disable the httpd_use_sasl SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'httpd_use_sasl' boolean should be set in the system configuration. + + + + + + + + + Disable the httpd_verify_dns SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'httpd_verify_dns' boolean should be set in the system configuration. + + + + + + + + + Disable the icecast_use_any_tcp_ports SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'icecast_use_any_tcp_ports' boolean should be set in the system configuration. + + + + + + + + + Disable the irc_use_any_tcp_ports SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'irc_use_any_tcp_ports' boolean should be set in the system configuration. + + + + + + + + + Disable the irssi_use_full_network SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'irssi_use_full_network' boolean should be set in the system configuration. + + + + + + + + + Disable the kdumpgui_run_bootloader SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'kdumpgui_run_bootloader' boolean should be set in the system configuration. + + + + + + + + + Enable the kerberos_enabled SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'kerberos_enabled' boolean should be set in the system configuration. + + + + + + + + + + Disable the ksmtuned_use_cifs SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'ksmtuned_use_cifs' boolean should be set in the system configuration. + + + + + + + + + Disable the ksmtuned_use_nfs SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'ksmtuned_use_nfs' boolean should be set in the system configuration. + + + + + + + + + Enable the logadm_exec_content SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'logadm_exec_content' boolean should be set in the system configuration. + + + + + + + + + Disable the logging_syslogd_can_sendmail SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'logging_syslogd_can_sendmail' boolean should be set in the system configuration. + + + + + + + + + Disable the logging_syslogd_run_nagios_plugins SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'logging_syslogd_run_nagios_plugins' boolean should be set in the system configuration. + + + + + + + + + Enable the logging_syslogd_use_tty SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'logging_syslogd_use_tty' boolean should be set in the system configuration. + + + + + + + + + Enable the login_console_enabled SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'login_console_enabled' boolean should be set in the system configuration. + + + + + + + + + Disable the logrotate_use_nfs SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'logrotate_use_nfs' boolean should be set in the system configuration. + + + + + + + + + Disable the logwatch_can_network_connect_mail SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'logwatch_can_network_connect_mail' boolean should be set in the system configuration. + + + + + + + + + Disable the lsmd_plugin_connect_any SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'lsmd_plugin_connect_any' boolean should be set in the system configuration. + + + + + + + + + Disable the mailman_use_fusefs SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'mailman_use_fusefs' boolean should be set in the system configuration. + + + + + + + + + Disable the mcelog_client SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'mcelog_client' boolean should be set in the system configuration. + + + + + + + + + Enable the mcelog_exec_scripts SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'mcelog_exec_scripts' boolean should be set in the system configuration. + + + + + + + + + Disable the mcelog_foreground SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'mcelog_foreground' boolean should be set in the system configuration. + + + + + + + + + Disable the mcelog_server SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'mcelog_server' boolean should be set in the system configuration. + + + + + + + + + Disable the minidlna_read_generic_user_content SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'minidlna_read_generic_user_content' boolean should be set in the system configuration. + + + + + + + + + Disable the mmap_low_allowed SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'mmap_low_allowed' boolean should be set in the system configuration. + + + + + + + + + Disable the mock_enable_homedirs SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'mock_enable_homedirs' boolean should be set in the system configuration. + + + + + + + + + Enable the mount_anyfile SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'mount_anyfile' boolean should be set in the system configuration. + + + + + + + + + Disable the mozilla_plugin_bind_unreserved_ports SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'mozilla_plugin_bind_unreserved_ports' boolean should be set in the system configuration. + + + + + + + + + Disable the mozilla_plugin_can_network_connect SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'mozilla_plugin_can_network_connect' boolean should be set in the system configuration. + + + + + + + + + Disable the mozilla_plugin_use_bluejeans SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'mozilla_plugin_use_bluejeans' boolean should be set in the system configuration. + + + + + + + + + Disable the mozilla_plugin_use_gps SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'mozilla_plugin_use_gps' boolean should be set in the system configuration. + + + + + + + + + Disable the mozilla_plugin_use_spice SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'mozilla_plugin_use_spice' boolean should be set in the system configuration. + + + + + + + + + Disable the mozilla_read_content SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'mozilla_read_content' boolean should be set in the system configuration. + + + + + + + + + Disable the mpd_enable_homedirs SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'mpd_enable_homedirs' boolean should be set in the system configuration. + + + + + + + + + Disable the mpd_use_cifs SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'mpd_use_cifs' boolean should be set in the system configuration. + + + + + + + + + Disable the mpd_use_nfs SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'mpd_use_nfs' boolean should be set in the system configuration. + + + + + + + + + Disable the mplayer_execstack SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'mplayer_execstack' boolean should be set in the system configuration. + + + + + + + + + Disable the mysql_connect_any SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'mysql_connect_any' boolean should be set in the system configuration. + + + + + + + + + Disable the nagios_run_pnp4nagios SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'nagios_run_pnp4nagios' boolean should be set in the system configuration. + + + + + + + + + Disable the nagios_run_sudo SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'nagios_run_sudo' boolean should be set in the system configuration. + + + + + + + + + Disable the named_tcp_bind_http_port SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'named_tcp_bind_http_port' boolean should be set in the system configuration. + + + + + + + + + Disable the named_write_master_zones SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'named_write_master_zones' boolean should be set in the system configuration. + + + + + + + + + Disable the neutron_can_network SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'neutron_can_network' boolean should be set in the system configuration. + + + + + + + + + Enable the nfs_export_all_ro SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'nfs_export_all_ro' boolean should be set in the system configuration. + + + + + + + + + Enable the nfs_export_all_rw SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'nfs_export_all_rw' boolean should be set in the system configuration. + + + + + + + + + Disable the nfsd_anon_write SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'nfsd_anon_write' boolean should be set in the system configuration. + + + + + + + + + Disable the nis_enabled SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'nis_enabled' boolean should be set in the system configuration. + + + + + + + + + Enable the nscd_use_shm SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'nscd_use_shm' boolean should be set in the system configuration. + + + + + + + + + Disable the openshift_use_nfs SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'openshift_use_nfs' boolean should be set in the system configuration. + + + + + + + + + Disable the openvpn_can_network_connect SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'openvpn_can_network_connect' boolean should be set in the system configuration. + + + + + + + + + Disable the openvpn_enable_homedirs SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'openvpn_enable_homedirs' boolean should be set in the system configuration. + + + + + + + + + Disable the openvpn_run_unconfined SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'openvpn_run_unconfined' boolean should be set in the system configuration. + + + + + + + + + Disable the pcp_bind_all_unreserved_ports SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'pcp_bind_all_unreserved_ports' boolean should be set in the system configuration. + + + + + + + + + Disable the pcp_read_generic_logs SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'pcp_read_generic_logs' boolean should be set in the system configuration. + + + + + + + + + Disable the piranha_lvs_can_network_connect SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'piranha_lvs_can_network_connect' boolean should be set in the system configuration. + + + + + + + + + Disable the polipo_connect_all_unreserved SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'polipo_connect_all_unreserved' boolean should be set in the system configuration. + + + + + + + + + Disable the polipo_session_bind_all_unreserved_ports SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'polipo_session_bind_all_unreserved_ports' boolean should be set in the system configuration. + + + + + + + + + Disable the polipo_session_users SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'polipo_session_users' boolean should be set in the system configuration. + + + + + + + + + Disable the polipo_use_cifs SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'polipo_use_cifs' boolean should be set in the system configuration. + + + + + + + + + Disable the polipo_use_nfs SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'polipo_use_nfs' boolean should be set in the system configuration. + + + + + + + + + Configure the polyinstantiation_enabled SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'polyinstantiation_enabled' boolean should be set in the system configuration. + + + + + + + + + + Enable the postfix_local_write_mail_spool SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'postfix_local_write_mail_spool' boolean should be set in the system configuration. + + + + + + + + + Disable the postgresql_can_rsync SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'postgresql_can_rsync' boolean should be set in the system configuration. + + + + + + + + + Disable the postgresql_selinux_transmit_client_label SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'postgresql_selinux_transmit_client_label' boolean should be set in the system configuration. + + + + + + + + + Enable the postgresql_selinux_unconfined_dbadm SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'postgresql_selinux_unconfined_dbadm' boolean should be set in the system configuration. + + + + + + + + + Enable the postgresql_selinux_users_ddl SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'postgresql_selinux_users_ddl' boolean should be set in the system configuration. + + + + + + + + + Disable the pppd_can_insmod SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'pppd_can_insmod' boolean should be set in the system configuration. + + + + + + + + + Disable the pppd_for_user SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'pppd_for_user' boolean should be set in the system configuration. + + + + + + + + + Disable the privoxy_connect_any SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'privoxy_connect_any' boolean should be set in the system configuration. + + + + + + + + + Disable the prosody_bind_http_port SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'prosody_bind_http_port' boolean should be set in the system configuration. + + + + + + + + + Disable the puppetagent_manage_all_files SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'puppetagent_manage_all_files' boolean should be set in the system configuration. + + + + + + + + + Disable the puppetmaster_use_db SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'puppetmaster_use_db' boolean should be set in the system configuration. + + + + + + + + + Disable the racoon_read_shadow SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'racoon_read_shadow' boolean should be set in the system configuration. + + + + + + + + + Disable the rsync_anon_write SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'rsync_anon_write' boolean should be set in the system configuration. + + + + + + + + + Disable the rsync_client SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'rsync_client' boolean should be set in the system configuration. + + + + + + + + + Disable the rsync_export_all_ro SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'rsync_export_all_ro' boolean should be set in the system configuration. + + + + + + + + + Disable the rsync_full_access SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'rsync_full_access' boolean should be set in the system configuration. + + + + + + + + + Disable the samba_create_home_dirs SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'samba_create_home_dirs' boolean should be set in the system configuration. + + + + + + + + + Disable the samba_domain_controller SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'samba_domain_controller' boolean should be set in the system configuration. + + + + + + + + + Disable the samba_enable_home_dirs SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'samba_enable_home_dirs' boolean should be set in the system configuration. + + + + + + + + + Disable the samba_export_all_ro SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'samba_export_all_ro' boolean should be set in the system configuration. + + + + + + + + + Disable the samba_export_all_rw SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'samba_export_all_rw' boolean should be set in the system configuration. + + + + + + + + + Disable the samba_load_libgfapi SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'samba_load_libgfapi' boolean should be set in the system configuration. + + + + + + + + + Disable the samba_portmapper SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'samba_portmapper' boolean should be set in the system configuration. + + + + + + + + + Disable the samba_run_unconfined SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'samba_run_unconfined' boolean should be set in the system configuration. + + + + + + + + + Disable the samba_share_fusefs SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'samba_share_fusefs' boolean should be set in the system configuration. + + + + + + + + + Disable the samba_share_nfs SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'samba_share_nfs' boolean should be set in the system configuration. + + + + + + + + + Disable the sanlock_use_fusefs SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'sanlock_use_fusefs' boolean should be set in the system configuration. + + + + + + + + + Disable the sanlock_use_nfs SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'sanlock_use_nfs' boolean should be set in the system configuration. + + + + + + + + + Disable the sanlock_use_samba SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'sanlock_use_samba' boolean should be set in the system configuration. + + + + + + + + + Disable the saslauthd_read_shadow SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'saslauthd_read_shadow' boolean should be set in the system configuration. + + + + + + + + + Enable the secadm_exec_content SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'secadm_exec_content' boolean should be set in the system configuration. + + + + + + + + + Disable the secure_mode SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'secure_mode' boolean should be set in the system configuration. + + + + + + + + + Configure the secure_mode_insmod SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'secure_mode_insmod' boolean should be set in the system configuration. + + + + + + + + + + Disable the secure_mode_policyload SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'secure_mode_policyload' boolean should be set in the system configuration. + + + + + + + + + Configure the selinuxuser_direct_dri_enabled SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'selinuxuser_direct_dri_enabled' boolean should be set in the system configuration. + + + + + + + + + Disable the selinuxuser_execheap SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'selinuxuser_execheap' boolean should be set in the system configuration. + + + + + + + + + + Enable the selinuxuser_execmod SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'selinuxuser_execmod' boolean should be set in the system configuration. + + + + + + + + + + disable the selinuxuser_execstack SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'selinuxuser_execstack' boolean should be set in the system configuration. + + + + + + + + + + Disable the selinuxuser_mysql_connect_enabled SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'selinuxuser_mysql_connect_enabled' boolean should be set in the system configuration. + + + + + + + + + Enable the selinuxuser_ping SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'selinuxuser_ping' boolean should be set in the system configuration. + + + + + + + + + Disable the selinuxuser_postgresql_connect_enabled SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'selinuxuser_postgresql_connect_enabled' boolean should be set in the system configuration. + + + + + + + + + Disable the selinuxuser_rw_noexattrfile SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'selinuxuser_rw_noexattrfile' boolean should be set in the system configuration. + + + + + + + + + Disable the selinuxuser_share_music SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'selinuxuser_share_music' boolean should be set in the system configuration. + + + + + + + + + Disable the selinuxuser_tcp_server SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'selinuxuser_tcp_server' boolean should be set in the system configuration. + + + + + + + + + Disable the selinuxuser_udp_server SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'selinuxuser_udp_server' boolean should be set in the system configuration. + + + + + + + + + Disable the selinuxuser_use_ssh_chroot SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'selinuxuser_use_ssh_chroot' boolean should be set in the system configuration. + + + + + + + + + Disable the sge_domain_can_network_connect SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'sge_domain_can_network_connect' boolean should be set in the system configuration. + + + + + + + + + Disable the sge_use_nfs SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'sge_use_nfs' boolean should be set in the system configuration. + + + + + + + + + Disable the smartmon_3ware SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'smartmon_3ware' boolean should be set in the system configuration. + + + + + + + + + Disable the smbd_anon_write SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'smbd_anon_write' boolean should be set in the system configuration. + + + + + + + + + Disable the spamassassin_can_network SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'spamassassin_can_network' boolean should be set in the system configuration. + + + + + + + + + Enable the spamd_enable_home_dirs SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'spamd_enable_home_dirs' boolean should be set in the system configuration. + + + + + + + + + Disable the squid_connect_any SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'squid_connect_any' boolean should be set in the system configuration. + + + + + + + + + Disable the squid_use_tproxy SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'squid_use_tproxy' boolean should be set in the system configuration. + + + + + + + + + Disable the ssh_chroot_rw_homedirs SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'ssh_chroot_rw_homedirs' boolean should be set in the system configuration. + + + + + + + + + Disable the ssh_keysign SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'ssh_keysign' boolean should be set in the system configuration. + + + + + + + + + Disable the ssh_sysadm_login SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'ssh_sysadm_login' boolean should be set in the system configuration. + + + + + + + + + + Enable the staff_exec_content SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'staff_exec_content' boolean should be set in the system configuration. + + + + + + + + + Disable the staff_use_svirt SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'staff_use_svirt' boolean should be set in the system configuration. + + + + + + + + + Disable the swift_can_network SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'swift_can_network' boolean should be set in the system configuration. + + + + + + + + + Enable the sysadm_exec_content SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'sysadm_exec_content' boolean should be set in the system configuration. + + + + + + + + + Disable the telepathy_connect_all_ports SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'telepathy_connect_all_ports' boolean should be set in the system configuration. + + + + + + + + + Disable the telepathy_tcp_connect_generic_network_ports SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'telepathy_tcp_connect_generic_network_ports' boolean should be set in the system configuration. + + + + + + + + + Disable the tftp_anon_write SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'tftp_anon_write' boolean should be set in the system configuration. + + + + + + + + + Disable the tftp_home_dir SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'tftp_home_dir' boolean should be set in the system configuration. + + + + + + + + + Disable the tmpreaper_use_nfs SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'tmpreaper_use_nfs' boolean should be set in the system configuration. + + + + + + + + + Disable the tmpreaper_use_samba SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'tmpreaper_use_samba' boolean should be set in the system configuration. + + + + + + + + + Disable the tor_bind_all_unreserved_ports SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'tor_bind_all_unreserved_ports' boolean should be set in the system configuration. + + + + + + + + + Disable the tor_can_network_relay SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'tor_can_network_relay' boolean should be set in the system configuration. + + + + + + + + + Enable the unconfined_chrome_sandbox_transition SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'unconfined_chrome_sandbox_transition' boolean should be set in the system configuration. + + + + + + + + + Enable the unconfined_login SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'unconfined_login' boolean should be set in the system configuration. + + + + + + + + + Enable the unconfined_mozilla_plugin_transition SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'unconfined_mozilla_plugin_transition' boolean should be set in the system configuration. + + + + + + + + + Disable the unprivuser_use_svirt SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'unprivuser_use_svirt' boolean should be set in the system configuration. + + + + + + + + + Disable the use_ecryptfs_home_dirs SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'use_ecryptfs_home_dirs' boolean should be set in the system configuration. + + + + + + + + + Disable the use_fusefs_home_dirs SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'use_fusefs_home_dirs' boolean should be set in the system configuration. + + + + + + + + + Disable the use_lpd_server SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'use_lpd_server' boolean should be set in the system configuration. + + + + + + + + + Disable the use_nfs_home_dirs SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'use_nfs_home_dirs' boolean should be set in the system configuration. + + + + + + + + + Disable the use_samba_home_dirs SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'use_samba_home_dirs' boolean should be set in the system configuration. + + + + + + + + + Enable the user_exec_content SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'user_exec_content' boolean should be set in the system configuration. + + + + + + + + + Disable the varnishd_connect_any SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'varnishd_connect_any' boolean should be set in the system configuration. + + + + + + + + + Disable the virt_read_qemu_ga_data SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'virt_read_qemu_ga_data' boolean should be set in the system configuration. + + + + + + + + + Disable the virt_rw_qemu_ga_data SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'virt_rw_qemu_ga_data' boolean should be set in the system configuration. + + + + + + + + + Disable the virt_sandbox_use_all_caps SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'virt_sandbox_use_all_caps' boolean should be set in the system configuration. + + + + + + + + + Enable the virt_sandbox_use_audit SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'virt_sandbox_use_audit' boolean should be set in the system configuration. + + + + + + + + + Disable the virt_sandbox_use_mknod SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'virt_sandbox_use_mknod' boolean should be set in the system configuration. + + + + + + + + + Disable the virt_sandbox_use_netlink SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'virt_sandbox_use_netlink' boolean should be set in the system configuration. + + + + + + + + + Disable the virt_sandbox_use_sys_admin SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'virt_sandbox_use_sys_admin' boolean should be set in the system configuration. + + + + + + + + + Disable the virt_transition_userdomain SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'virt_transition_userdomain' boolean should be set in the system configuration. + + + + + + + + + Disable the virt_use_comm SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'virt_use_comm' boolean should be set in the system configuration. + + + + + + + + + Disable the virt_use_execmem SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'virt_use_execmem' boolean should be set in the system configuration. + + + + + + + + + + Disable the virt_use_fusefs SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'virt_use_fusefs' boolean should be set in the system configuration. + + + + + + + + + Disable the virt_use_nfs SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'virt_use_nfs' boolean should be set in the system configuration. + + + + + + + + + Disable the virt_use_rawip SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'virt_use_rawip' boolean should be set in the system configuration. + + + + + + + + + Disable the virt_use_samba SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'virt_use_samba' boolean should be set in the system configuration. + + + + + + + + + Disable the virt_use_sanlock SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'virt_use_sanlock' boolean should be set in the system configuration. + + + + + + + + + Disable the virt_use_usb SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'virt_use_usb' boolean should be set in the system configuration. + + + + + + + + + Disable the virt_use_xserver SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'virt_use_xserver' boolean should be set in the system configuration. + + + + + + + + + Disable the webadm_manage_user_files SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'webadm_manage_user_files' boolean should be set in the system configuration. + + + + + + + + + Disable the webadm_read_user_files SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'webadm_read_user_files' boolean should be set in the system configuration. + + + + + + + + + Disable the wine_mmap_zero_ignore SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'wine_mmap_zero_ignore' boolean should be set in the system configuration. + + + + + + + + + Disable the xdm_bind_vnc_tcp_port SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'xdm_bind_vnc_tcp_port' boolean should be set in the system configuration. + + + + + + + + + Disable the xdm_exec_bootloader SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'xdm_exec_bootloader' boolean should be set in the system configuration. + + + + + + + + + Disable the xdm_sysadm_login SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'xdm_sysadm_login' boolean should be set in the system configuration. + + + + + + + + + Disable the xdm_write_home SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'xdm_write_home' boolean should be set in the system configuration. + + + + + + + + + Disable the xen_use_nfs SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'xen_use_nfs' boolean should be set in the system configuration. + + + + + + + + + Enable the xend_run_blktap SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'xend_run_blktap' boolean should be set in the system configuration. + + + + + + + + + Enable the xend_run_qemu SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'xend_run_qemu' boolean should be set in the system configuration. + + + + + + + + + Disable the xguest_connect_network SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'xguest_connect_network' boolean should be set in the system configuration. + + + + + + + + + Disable the xguest_exec_content SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'xguest_exec_content' boolean should be set in the system configuration. + + + + + + + + + Disable the xguest_mount_media SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'xguest_mount_media' boolean should be set in the system configuration. + + + + + + + + + Disable the xguest_use_bluetooth SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'xguest_use_bluetooth' boolean should be set in the system configuration. + + + + + + + + + Disable the xserver_clients_write_xshm SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'xserver_clients_write_xshm' boolean should be set in the system configuration. + + + + + + + + + Disable the xserver_execmem SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'xserver_execmem' boolean should be set in the system configuration. + + + + + + + + + + Disable the xserver_object_manager SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'xserver_object_manager' boolean should be set in the system configuration. + + + + + + + + + Disable the zabbix_can_network SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'zabbix_can_network' boolean should be set in the system configuration. + + + + + + + + + Disable the zarafa_setrlimit SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'zarafa_setrlimit' boolean should be set in the system configuration. + + + + + + + + + Disable the zebra_write_config SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'zebra_write_config' boolean should be set in the system configuration. + + + + + + + + + Disable the zoneminder_anon_write SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'zoneminder_anon_write' boolean should be set in the system configuration. + + + + + + + + + Disable the zoneminder_run_sudo SELinux Boolean + + Red Hat Enterprise Linux 8 + + The SELinux 'zoneminder_run_sudo' boolean should be set in the system configuration. + + + + + + + + + Disable Automatic Bug Reporting Tool (abrtd) + + Red Hat Enterprise Linux 8 + + The abrtd service should be disabled if possible. + + + + + + + + + + + + + + Disable Advanced Configuration and Power Interface (acpid) + + Red Hat Enterprise Linux 8 + + The acpid service should be disabled if possible. + + + + + + + + + + + + + + Disable At Service (atd) + + Red Hat Enterprise Linux 8 + + The atd service should be disabled if possible. + + + + + + + + + + + + + + Enable auditd Service + + Red Hat Enterprise Linux 8 + + The auditd service should be enabled if possible. + + + + + + + + + + + + + + + + + Disable the Automounter + + Red Hat Enterprise Linux 8 + + The autofs service should be disabled if possible. + + + + + + + + + + + + + + Disable Avahi Server Software + + Red Hat Enterprise Linux 8 + + The avahi-daemon service should be disabled if possible. + + + + + + + + + + + + + + Disable Bluetooth Service + + Red Hat Enterprise Linux 8 + + The bluetooth service should be disabled if possible. + + + + + + + + + + + + + Disable Certmonger Service (certmonger) + + Red Hat Enterprise Linux 8 + + The certmonger service should be disabled if possible. + + + + + + + + + + + + + + The Chronyd service is enabled + + Red Hat Enterprise Linux 8 + + The chronyd service should be enabled if possible. + + + + + + + + + + + + + + + + + Disable Cockpit Management Server + + Red Hat Enterprise Linux 8 + + The cockpit service should be disabled if possible. + + + + + + + + + + + + + Disable CPU Speed (cpupower) + + Red Hat Enterprise Linux 8 + + The cpupower service should be disabled if possible. + + + + + + + + + + + + + + Enable cron Service + + Red Hat Enterprise Linux 8 + + The cron service should be enabled if possible. + + + + + + + + + + + + + + + + Enable cron Service + + Red Hat Enterprise Linux 8 + + The crond service should be enabled if possible. + + + + + + + + + + + + + + + + + Disable the CUPS Service + + Red Hat Enterprise Linux 8 + + The cups service should be disabled if possible. + + + + + + + + + + + + + + Disable debug-shell SystemD Service + + Red Hat Enterprise Linux 8 + + The debug-shell service should be disabled if possible. + + + + + + + + + + + + + + Disable DHCP Service + + Red Hat Enterprise Linux 8 + + The dhcpd service should be disabled if possible. + + + + + + + + + + + + + + Disable Dovecot Service + + Red Hat Enterprise Linux 8 + + The dovecot service should be disabled if possible. + + + + + + + + + + + + + + Enable the File Access Policy Service + + Red Hat Enterprise Linux 8 + + The fapolicyd service should be enabled if possible. + + + + + + + + + + + + + + + + + Verify firewalld Enabled + + Red Hat Enterprise Linux 8 + + The firewalld service should be enabled if possible. + + + + + + + + + + + + + + + + + Disable httpd Service + + Red Hat Enterprise Linux 8 + + The httpd service should be disabled if possible. + + + + + + + + + + + + + + Verify ip6tables Enabled if Using IPv6 + + Red Hat Enterprise Linux 8 + + The ip6tables service should be enabled if possible. + + + + + + + + + + + + + + + + + Verify iptables Enabled + + Red Hat Enterprise Linux 8 + + The iptables service should be enabled if possible. + + + + + + + + + + + + + + + + + Disable KDump Kernel Crash Analyzer (kdump) + + Red Hat Enterprise Linux 8 + + The kdump service should be disabled if possible. + + + + + + + + + + + + + + Disable Software RAID Monitor (mdmonitor) + + Red Hat Enterprise Linux 8 + + The mdmonitor service should be disabled if possible. + + + + + + + + + + + + + + Enable nails Service + + Red Hat Enterprise Linux 8 + + The nails service should be enabled if possible. + + + + + + + + + + + + + + + + Disable named Service + + Red Hat Enterprise Linux 8 + + The named service should be disabled if possible. + + + + + + + + + + + + + + Disable Network Console (netconsole) + + Red Hat Enterprise Linux 8 + + The netconsole service should be disabled if possible. + + + + + + + + + + + + + + Disable Network File Systems (netfs) + + Red Hat Enterprise Linux 8 + + The netfs service should be disabled if possible. + + + + + + + + + + + + + Disable Network File System (nfs) + + Red Hat Enterprise Linux 8 + + The nfs-server service should be disabled if possible. + + + + + + + + + + + + + + Disable Network File System Lock Service (nfslock) + + Red Hat Enterprise Linux 8 + + The nfslock service should be disabled if possible. + + + + + + + + + + + + + Enable the NTP Daemon + + Red Hat Enterprise Linux 8 + + The ntp service should be enabled if possible. + + + + + + + + + + + + + + + + Enable the NTP Daemon + + Red Hat Enterprise Linux 8 + + The ntpd service should be enabled if possible. + + + + + + + + + + + + + + + + Disable ntpdate Service (ntpdate) + + Red Hat Enterprise Linux 8 + + The ntpdate service should be disabled if possible. + + + + + + + + + + + + + + Disable Odd Job Daemon (oddjobd) + + Red Hat Enterprise Linux 8 + + The oddjobd service should be disabled if possible. + + + + + + + + + + + + + + Enable the pcscd Service + + Red Hat Enterprise Linux 8 + + The pcscd service should be enabled if possible. + + + + + + + + + + + + + + + + + Disable Portreserve (portreserve) + + Red Hat Enterprise Linux 8 + + The portreserve service should be disabled if possible. + + + + + + + + + + + + + + Enable Postfix Service + + Red Hat Enterprise Linux 8 + + The postfix service should be enabled if possible. + + + + + + + + + + + + + + + + Enable Process Accounting (psacct) + + Red Hat Enterprise Linux 8 + + The psacct service should be enabled if possible. + + + + + + + + + + + + + + + + + Disable Apache Qpid (qpidd) + + Red Hat Enterprise Linux 8 + + The qpidd service should be disabled if possible. + + + + + + + + + + + + + + Disable Quota Netlink (quota_nld) + + Red Hat Enterprise Linux 8 + + The quota_nld service should be disabled if possible. + + + + + + + + + + + + + + Disable Network Router Discovery Daemon (rdisc) + + Red Hat Enterprise Linux 8 + + The rdisc service should be disabled if possible. + + + + + + + + + + + + + + Disable rexec Service + + Red Hat Enterprise Linux 8 + + The rexec service should be disabled if possible. + + + + + + + + + + + + + + Disable Red Hat Network Service (rhnsd) + + Red Hat Enterprise Linux 8 + + The rhnsd service should be disabled if possible. + + + + + + + + + + + + + + Disable Red Hat Subscription Manager Daemon (rhsmcertd) + + Red Hat Enterprise Linux 8 + + The rhsmcertd service should be disabled if possible. + + + + + + + + + + + + + + Disable rlogin Service + + Red Hat Enterprise Linux 8 + + The rlogin service should be disabled if possible. + + + + + + + + + + + + + + Enable the Hardware RNG Entropy Gatherer Service + + Red Hat Enterprise Linux 8 + + The rngd service should be enabled if possible. + + + + + + + + + + + + + + + + + Disable rpcbind Service + + Red Hat Enterprise Linux 8 + + The rpcbind service should be disabled if possible. + + + + + + + + + + + + + + Disable Secure RPC Client Service (rpcgssd) + + Red Hat Enterprise Linux 8 + + The rpcgssd service should be disabled if possible. + + + + + + + + + + + + + Disable RPC ID Mapping Service (rpcidmapd) + + Red Hat Enterprise Linux 8 + + The rpcidmapd service should be disabled if possible. + + + + + + + + + + + + + Disable Secure RPC Server Service (rpcsvcgssd) + + Red Hat Enterprise Linux 8 + + The rpcsvcgssd service should be disabled if possible. + + + + + + + + + + + + + Disable rsh Service + + Red Hat Enterprise Linux 8 + + The rsh service should be disabled if possible. + + + + + + + + + + + + + + Ensure rsyncd service is diabled + + Red Hat Enterprise Linux 8 + + The rsyncd service should be disabled if possible. + + + + + + + + + + + + + + Enable rsyslog Service + + Red Hat Enterprise Linux 8 + + The rsyslog service should be enabled if possible. + + + + + + + + + + + + + + + + + Disable Cyrus SASL Authentication Daemon (saslauthd) + + Red Hat Enterprise Linux 8 + + The saslauthd service should be disabled if possible. + + + + + + + + + + + + + + Disable LDAP Server (slapd) + + Red Hat Enterprise Linux 8 + + The slapd service should be disabled if possible. + + + + + + + + + + + + + + Disable Samba + + Red Hat Enterprise Linux 8 + + The smb service should be disabled if possible. + + + + + + + + + + + + + + Disable snmpd Service + + Red Hat Enterprise Linux 8 + + The snmpd service should be disabled if possible. + + + + + + + + + + + + + + Disable Squid + + Red Hat Enterprise Linux 8 + + The squid service should be disabled if possible. + + + + + + + + + + + + + + Disable SSH Server If Possible (Unusual) + + Red Hat Enterprise Linux 8 + + The sshd service should be disabled if possible. + + + + + + + + + + + + + Enable the OpenSSH Service + + Red Hat Enterprise Linux 8 + + The sshd service should be enabled if possible. + + + + + + + + + + + + + + + + + Enable the SSSD Service + + Red Hat Enterprise Linux 8 + + The sssd service should be enabled if possible. + + + + + + + + + + + + + + + + + service_syslog_disabled + + Red Hat Enterprise Linux 8 + + The syslog service should be disabled if possible. + + + + + + + + + + + + + Enable syslog-ng Service + + Red Hat Enterprise Linux 8 + + The syslog-ng service should be enabled if possible. + + + + + + + + + + + + + + + + Disable System Statistics Reset Service (sysstat) + + Red Hat Enterprise Linux 8 + + The sysstat service should be disabled if possible. + + + + + + + + + + + + + + Disable acquiring, saving, and processing core dumps + + Red Hat Enterprise Linux 8 + + The systemd-coredump service should be disabled if possible. + + + + + + + + + + + + + + Enable systemd-journald Service + + Red Hat Enterprise Linux 8 + + The systemd-journald service should be enabled if possible. + + + + + + + + + + + + + + + + + Disable telnet Service + + Red Hat Enterprise Linux 8 + + The telnet service should be disabled if possible. + + + + + + + + + + + + + + Disable tftp Service + + Red Hat Enterprise Linux 8 + + The tftp service should be disabled if possible. + + + + + + + + + + + + + + Verify ufw Enabled + + Red Hat Enterprise Linux 8 + + The ufw service should be enabled if possible. + + + + + + + + + + + + + + + + Enable the USBGuard Service + + Red Hat Enterprise Linux 8 + + The usbguard service should be enabled if possible. + + + + + + + + + + + + + + + + + Disable vsftpd Service + + Red Hat Enterprise Linux 8 + + The vsftpd service should be disabled if possible. + + + + + + + + + + + + + + Disable xinetd Service + + Red Hat Enterprise Linux 8 + + The xinetd service should be disabled if possible. + + + + + + + + + + + + + + Disable ypbind Service + + Red Hat Enterprise Linux 8 + + The ypbind service should be disabled if possible. + + + + + + + + + + + + + + Disable ypserv Service + + Red Hat Enterprise Linux 8 + + The ypserv service should be disabled if possible. + + + + + + + + + + + + + + Disable Quagga Service + + Red Hat Enterprise Linux 8 + + The zebra service should be disabled if possible. + + + + + + + + + + + + + + Disable SSH Access via Empty Passwords + + Red Hat Enterprise Linux 8 + + Ensure 'PermitEmptyPasswords' is configured with value 'no' in /etc/ssh/sshd_config + + + + + + + + + + + + + + + + + + + + Disable GSSAPI Authentication + + Red Hat Enterprise Linux 8 + + Ensure 'GSSAPIAuthentication' is configured with value 'no' in /etc/ssh/sshd_config + + + + + + + + + + + + + + + + + + + + Disable Kerberos Authentication + + Red Hat Enterprise Linux 8 + + Ensure 'KerberosAuthentication' is configured with value 'no' in /etc/ssh/sshd_config + + + + + + + + + + + + + + + + + + + + Disable PubkeyAuthentication Authentication + + Red Hat Enterprise Linux 8 + + Ensure 'PubkeyAuthentication' is configured with value 'no' in /etc/ssh/sshd_config + + + + + + + + + + + + + + + + + + + + Disable SSH Support for .rhosts Files + + Red Hat Enterprise Linux 8 + + Ensure 'IgnoreRhosts' is configured with value 'yes' in /etc/ssh/sshd_config + + + + + + + + + + + + + + + + + + + + Disable SSH Root Login + + Red Hat Enterprise Linux 8 + + Ensure 'PermitRootLogin' is configured with value 'no' in /etc/ssh/sshd_config + + + + + + + + + + + + + + + + + + + + Disable SSH root Login with a Password (Insecure) + + Red Hat Enterprise Linux 8 + + Ensure 'PermitRootLogin' is configured with value 'prohibit-password' in /etc/ssh/sshd_config + + + + + + + + + + + + + + + + + + + Disable SSH TCP Forwarding + + Red Hat Enterprise Linux 8 + + Ensure 'AllowTcpForwarding' is configured with value 'no' in /etc/ssh/sshd_config + + + + + + + + + + + + + + + + + + + + Disable SSH Support for User Known Hosts + + Red Hat Enterprise Linux 8 + + Ensure 'IgnoreUserKnownHosts' is configured with value 'yes' in /etc/ssh/sshd_config + + + + + + + + + + + + + + + + + + + + Disable X11 Forwarding + + Red Hat Enterprise Linux 8 + + Ensure 'X11Forwarding' is configured with value 'no' in /etc/ssh/sshd_config + + + + + + + + + + + + + + + + + + + + Do Not Allow SSH Environment Options + + Red Hat Enterprise Linux 8 + + Ensure 'PermitUserEnvironment' is configured with value 'no' in /etc/ssh/sshd_config + + + + + + + + + + + + + + + + + + + + Enable GSSAPI Authentication + + Red Hat Enterprise Linux 8 + + Ensure 'GSSAPIAuthentication' is configured with value 'yes' in /etc/ssh/sshd_config + + + + + + + + + + + + + + + + + + + Enable PAM + + Red Hat Enterprise Linux 8 + + Ensure 'UsePAM' is configured with value 'yes' in /etc/ssh/sshd_config + + + + + + + + + + + + + + + + + + + + Enable Public Key Authentication + + Red Hat Enterprise Linux 8 + + Ensure 'PubkeyAuthentication' is configured with value 'yes' in /etc/ssh/sshd_config + + + + + + + + + + + + + + + + + + + Enable Use of Strict Mode Checking + + Red Hat Enterprise Linux 8 + + Ensure 'StrictModes' is configured with value 'yes' in /etc/ssh/sshd_config + + + + + + + + + + + + + + + + + + + + Enable SSH Warning Banner + + Red Hat Enterprise Linux 8 + + Ensure 'Banner' is configured with value '/etc/issue' in /etc/ssh/sshd_config + + + + + + + + + + + + + + + + + + + + Enable SSH Warning Banner + + Red Hat Enterprise Linux 8 + + Ensure 'Banner' is configured with value '/etc/issue.net' in /etc/ssh/sshd_config + + + + + + + + + + + + + + + + + + + Enable Encrypted X11 Forwarding + + Red Hat Enterprise Linux 8 + + Ensure 'X11Forwarding' is configured with value 'yes' in /etc/ssh/sshd_config + + + + + + + + + + + + + + + + + + + + sshd_includes_config_files + + Red Hat Enterprise Linux 8 + + Check presence of Include /etc/ssh/sshd_config.d/*.conf in /etc/ssh/sshd_config + + + + + + + + + Enable SSH Print Last Log + + Red Hat Enterprise Linux 8 + + Ensure 'PrintLastLog' is configured with value 'yes' in /etc/ssh/sshd_config + + + + + + + + + + + + + + + + + + + + Set SSH Client Alive Count Max to zero + + Red Hat Enterprise Linux 8 + + Ensure 'ClientAliveCountMax' is configured with value '0' in /etc/ssh/sshd_config + + + + + + + + + + + + + + + + + + + + Set LogLevel to INFO + + Red Hat Enterprise Linux 8 + + Ensure 'LogLevel' is configured with value 'INFO' in /etc/ssh/sshd_config + + + + + + + + + + + + + + + + + + + + Set SSH Daemon LogLevel to VERBOSE + + Red Hat Enterprise Linux 8 + + Ensure 'LogLevel' is configured with value 'VERBOSE' in /etc/ssh/sshd_config + + + + + + + + + + + + + + + + + + + + SSH server uses strong entropy to seed + + Red Hat Enterprise Linux 8 + + Ensure 'SSH_USE_STRONG_RNG' is configured with value '32' in /etc/sysconfig/sshd + + + + + + + + + + Prevent remote hosts from connecting to the proxy display + + Red Hat Enterprise Linux 8 + + Ensure 'X11UseLocalhost' is configured with value 'yes' in /etc/ssh/sshd_config + + + + + + + + + + + + + + + + + + + + Enable Certmap in SSSD + + Red Hat Enterprise Linux 8 + + Check presence of \[certmap\/.+\/.+\] in /etc/sssd/sssd.conf + + + + + + + + + + Ensure sudo Runs In A Minimal Environment - sudo env_reset + + Red Hat Enterprise Linux 8 + + Checks sudoers Defaults {{ OPTION }} configuration + + + + + + + + + + Ensure sudo Ignores Commands In Current Dir - sudo ignore_dot + + Red Hat Enterprise Linux 8 + + Checks sudoers Defaults {{ OPTION }} configuration + + + + + + + + + + Ensure Privileged Escalated Commands Cannot Execute Other Commands - sudo NOEXEC + + Red Hat Enterprise Linux 8 + + Checks sudoers Defaults {{ OPTION }} configuration + + + + + + + + + + Ensure sudo passwd_timeout is appropriate - sudo passwd_timeout + + Red Hat Enterprise Linux 8 + + Checks sudoers Defaults {{ OPTION }} configuration + + + + + + + + + + Ensure Only Users Logged In To Real tty Can Execute Sudo - sudo requiretty + + Red Hat Enterprise Linux 8 + + Checks sudoers Defaults {{ OPTION }} configuration + + + + + + + + + + Ensure sudo umask is appropriate - sudo umask + + Red Hat Enterprise Linux 8 + + Checks sudoers Defaults {{ OPTION }} configuration + + + + + + + + + + Ensure Only Users Logged In To Real tty Can Execute Sudo - sudo use_pty + + Red Hat Enterprise Linux 8 + + Checks sudoers Defaults {{ OPTION }} configuration + + + + + + + + + + Ensure Sudo Logfile Exists - sudo logfile + + Red Hat Enterprise Linux 8 + + Checks sudoers Defaults {{ OPTION }} configuration + + + + + + + + + + Ensure only owner and members of group owner of /usr/bin/sudo can execute it + + Red Hat Enterprise Linux 8 + + This test makes sure that /usr/bin/sudo has mode 4110. + If the target file or directory has an extended ACL, then it will fail the mode check. + + + + + + + + + + + Enable Kernel Parameter to Enforce DAC on Hardlinks + + Red Hat Enterprise Linux 8 + + The 'fs.protected_hardlinks' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Enable Kernel Parameter to Enforce DAC on Hardlinks + + Red Hat Enterprise Linux 8 + + The kernel 'fs.protected_hardlinks' parameter should be set to 1 in the system runtime. + + + + + + + + + Enable Kernel Parameter to Enforce DAC on Hardlinks + + Red Hat Enterprise Linux 8 + + The kernel 'fs.protected_hardlinks' parameter should be set to 1 in the system configuration. + + + + + + + + + + + + + + Enable Kernel Parameter to Enforce DAC on Symlinks + + Red Hat Enterprise Linux 8 + + The 'fs.protected_symlinks' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Enable Kernel Parameter to Enforce DAC on Symlinks + + Red Hat Enterprise Linux 8 + + The kernel 'fs.protected_symlinks' parameter should be set to 1 in the system runtime. + + + + + + + + + Enable Kernel Parameter to Enforce DAC on Symlinks + + Red Hat Enterprise Linux 8 + + The kernel 'fs.protected_symlinks' parameter should be set to 1 in the system configuration. + + + + + + + + + + + + + + Disable Core Dumps for SUID programs + + Red Hat Enterprise Linux 8 + + The 'fs.suid_dumpable' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Disable Core Dumps for SUID programs + + Red Hat Enterprise Linux 8 + + The kernel 'fs.suid_dumpable' parameter should be set to 0 in the system runtime. + + + + + + + + + Disable Core Dumps for SUID programs + + Red Hat Enterprise Linux 8 + + The kernel 'fs.suid_dumpable' parameter should be set to 0 in the system configuration. + + + + + + + + + + + + + + Disable storing core dumps + + Red Hat Enterprise Linux 8 + + The 'kernel.core_pattern' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Disable storing core dumps + + Red Hat Enterprise Linux 8 + + The kernel 'kernel.core_pattern' parameter should be set to |/bin/false in the system runtime. + + + + + + + + + Disable storing core dumps + + Red Hat Enterprise Linux 8 + + The kernel 'kernel.core_pattern' parameter should be set to |/bin/false in the system configuration. + + + + + + + + + + + + + + Configure file name of core dumps + + Red Hat Enterprise Linux 8 + + The 'kernel.core_uses_pid' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + Configure file name of core dumps + + Red Hat Enterprise Linux 8 + + The kernel 'kernel.core_uses_pid' parameter should be set to 0 in the system runtime. + + + + + + + + + Configure file name of core dumps + + Red Hat Enterprise Linux 8 + + The kernel 'kernel.core_uses_pid' parameter should be set to 0 in the system configuration. + + + + + + + + + + + + + + Restrict Access to Kernel Message Buffer + + Red Hat Enterprise Linux 8 + + The 'kernel.dmesg_restrict' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Restrict Access to Kernel Message Buffer + + Red Hat Enterprise Linux 8 + + The kernel 'kernel.dmesg_restrict' parameter should be set to 1 in the system runtime. + + + + + + + + + Restrict Access to Kernel Message Buffer + + Red Hat Enterprise Linux 8 + + The kernel 'kernel.dmesg_restrict' parameter should be set to 1 in the system configuration. + + + + + + + + + + + + + + Disable Kernel Image Loading + + Red Hat Enterprise Linux 8 + + The 'kernel.kexec_load_disabled' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Disable Kernel Image Loading + + Red Hat Enterprise Linux 8 + + The kernel 'kernel.kexec_load_disabled' parameter should be set to 1 in the system runtime. + + + + + + + + + Disable Kernel Image Loading + + Red Hat Enterprise Linux 8 + + The kernel 'kernel.kexec_load_disabled' parameter should be set to 1 in the system configuration. + + + + + + + + + + + + + + Restrict Exposed Kernel Pointer Addresses Access + + Red Hat Enterprise Linux 8 + + The 'kernel.kptr_restrict' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Restrict Exposed Kernel Pointer Addresses Access + + Red Hat Enterprise Linux 8 + + The kernel 'kernel.kptr_restrict' parameter should be set to 1 or 2 in the system runtime. + + + + + + + + + Restrict Exposed Kernel Pointer Addresses Access + + Red Hat Enterprise Linux 8 + + The kernel 'kernel.kptr_restrict' parameter should be set to 1 or 2 in the system configuration. + + + + + + + + + + + + + + Disable loading and unloading of kernel modules + + Red Hat Enterprise Linux 8 + + The 'kernel.modules_disabled' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Disable loading and unloading of kernel modules + + Red Hat Enterprise Linux 8 + + The kernel 'kernel.modules_disabled' parameter should be set to 1 in the system runtime. + + + + + + + + + Disable loading and unloading of kernel modules + + Red Hat Enterprise Linux 8 + + The kernel 'kernel.modules_disabled' parameter should be set to 1 in the system configuration. + + + + + + + + + + + + + + Kernel panic on oops + + Red Hat Enterprise Linux 8 + + The 'kernel.panic_on_oops' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Kernel panic on oops + + Red Hat Enterprise Linux 8 + + The kernel 'kernel.panic_on_oops' parameter should be set to 1 in the system runtime. + + + + + + + + + Kernel panic on oops + + Red Hat Enterprise Linux 8 + + The kernel 'kernel.panic_on_oops' parameter should be set to 1 in the system configuration. + + + + + + + + + + + + + + Limit CPU consumption of the Perf system + + Red Hat Enterprise Linux 8 + + The 'kernel.perf_cpu_time_max_percent' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Limit CPU consumption of the Perf system + + Red Hat Enterprise Linux 8 + + The kernel 'kernel.perf_cpu_time_max_percent' parameter should be set to 1 in the system runtime. + + + + + + + + + Limit CPU consumption of the Perf system + + Red Hat Enterprise Linux 8 + + The kernel 'kernel.perf_cpu_time_max_percent' parameter should be set to 1 in the system configuration. + + + + + + + + + + + + + + Limit sampling frequency of the Perf system + + Red Hat Enterprise Linux 8 + + The 'kernel.perf_event_max_sample_rate' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Limit sampling frequency of the Perf system + + Red Hat Enterprise Linux 8 + + The kernel 'kernel.perf_event_max_sample_rate' parameter should be set to 1 in the system runtime. + + + + + + + + + Limit sampling frequency of the Perf system + + Red Hat Enterprise Linux 8 + + The kernel 'kernel.perf_event_max_sample_rate' parameter should be set to 1 in the system configuration. + + + + + + + + + + + + + + Disallow kernel profiling by unprivileged users + + Red Hat Enterprise Linux 8 + + The 'kernel.perf_event_paranoid' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Disallow kernel profiling by unprivileged users + + Red Hat Enterprise Linux 8 + + The kernel 'kernel.perf_event_paranoid' parameter should be set to 2 in the system runtime. + + + + + + + + + Disallow kernel profiling by unprivileged users + + Red Hat Enterprise Linux 8 + + The kernel 'kernel.perf_event_paranoid' parameter should be set to 2 in the system configuration. + + + + + + + + + + + + + + Configure maximum number of process identifiers + + Red Hat Enterprise Linux 8 + + The 'kernel.pid_max' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Configure maximum number of process identifiers + + Red Hat Enterprise Linux 8 + + The kernel 'kernel.pid_max' parameter should be set to 65536 in the system runtime. + + + + + + + + + Configure maximum number of process identifiers + + Red Hat Enterprise Linux 8 + + The kernel 'kernel.pid_max' parameter should be set to 65536 in the system configuration. + + + + + + + + + + + + + + Enable Randomized Layout of Virtual Address Space + + Red Hat Enterprise Linux 8 + + The 'kernel.randomize_va_space' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Enable Randomized Layout of Virtual Address Space + + Red Hat Enterprise Linux 8 + + The kernel 'kernel.randomize_va_space' parameter should be set to 2 in the system runtime. + + + + + + + + + Enable Randomized Layout of Virtual Address Space + + Red Hat Enterprise Linux 8 + + The kernel 'kernel.randomize_va_space' parameter should be set to 2 in the system configuration. + + + + + + + + + + + + + + Disallow magic SysRq key + + Red Hat Enterprise Linux 8 + + The 'kernel.sysrq' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Disallow magic SysRq key + + Red Hat Enterprise Linux 8 + + The kernel 'kernel.sysrq' parameter should be set to 0 in the system runtime. + + + + + + + + + Disallow magic SysRq key + + Red Hat Enterprise Linux 8 + + The kernel 'kernel.sysrq' parameter should be set to 0 in the system configuration. + + + + + + + + + + + + + + Disable Access to Network bpf() Syscall From Unprivileged Processes + + Red Hat Enterprise Linux 8 + + The 'kernel.unprivileged_bpf_disabled' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Disable Access to Network bpf() Syscall From Unprivileged Processes + + Red Hat Enterprise Linux 8 + + The kernel 'kernel.unprivileged_bpf_disabled' parameter should be set to 1 in the system runtime. + + + + + + + + + Disable Access to Network bpf() Syscall From Unprivileged Processes + + Red Hat Enterprise Linux 8 + + The kernel 'kernel.unprivileged_bpf_disabled' parameter should be set to 1 in the system configuration. + + + + + + + + + + + + + + Restrict usage of ptrace to descendant processes + + Red Hat Enterprise Linux 8 + + The 'kernel.yama.ptrace_scope' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Restrict usage of ptrace to descendant processes + + Red Hat Enterprise Linux 8 + + The kernel 'kernel.yama.ptrace_scope' parameter should be set to 1 in the system runtime. + + + + + + + + + Restrict usage of ptrace to descendant processes + + Red Hat Enterprise Linux 8 + + The kernel 'kernel.yama.ptrace_scope' parameter should be set to 1 in the system configuration. + + + + + + + + + + + + + + Harden the operation of the BPF just-in-time compiler + + Red Hat Enterprise Linux 8 + + The 'net.core.bpf_jit_harden' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Harden the operation of the BPF just-in-time compiler + + Red Hat Enterprise Linux 8 + + The kernel 'net.core.bpf_jit_harden' parameter should be set to 2 in the system runtime. + + + + + + + + + Harden the operation of the BPF just-in-time compiler + + Red Hat Enterprise Linux 8 + + The kernel 'net.core.bpf_jit_harden' parameter should be set to 2 in the system configuration. + + + + + + + + + + + + + + Disable Accepting Packets Routed Between Local Interfaces + + Red Hat Enterprise Linux 8 + + The 'net.ipv4.conf.all.accept_local' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Disable Accepting Packets Routed Between Local Interfaces + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv4.conf.all.accept_local' parameter should be set to 0 in the system runtime. + + + + + + + + + Disable Accepting Packets Routed Between Local Interfaces + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv4.conf.all.accept_local' parameter should be set to 0 in the system configuration. + + + + + + + + + + + + + + Disable Accepting ICMP Redirects for All IPv4 Interfaces + + Red Hat Enterprise Linux 8 + + The 'net.ipv4.conf.all.accept_redirects' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Disable Accepting ICMP Redirects for All IPv4 Interfaces + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv4.conf.all.accept_redirects' parameter should be set to the appropriate value in the system runtime. + + + + + + + + + Disable Accepting ICMP Redirects for All IPv4 Interfaces + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv4.conf.all.accept_redirects' parameter should be set to the appropriate value in the system configuration. + + + + + + + + + + + + + + Disable Kernel Parameter for Accepting Source-Routed Packets on all IPv4 Interfaces + + Red Hat Enterprise Linux 8 + + The 'net.ipv4.conf.all.accept_source_route' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Disable Kernel Parameter for Accepting Source-Routed Packets on all IPv4 Interfaces + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv4.conf.all.accept_source_route' parameter should be set to the appropriate value in the system runtime. + + + + + + + + + Disable Kernel Parameter for Accepting Source-Routed Packets on all IPv4 Interfaces + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv4.conf.all.accept_source_route' parameter should be set to the appropriate value in the system configuration. + + + + + + + + + + + + + + Configure ARP filtering for All IPv4 Interfaces + + Red Hat Enterprise Linux 8 + + The 'net.ipv4.conf.all.arp_filter' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Configure ARP filtering for All IPv4 Interfaces + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv4.conf.all.arp_filter' parameter should be set to the appropriate value in the system runtime. + + + + + + + + + Configure ARP filtering for All IPv4 Interfaces + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv4.conf.all.arp_filter' parameter should be set to the appropriate value in the system configuration. + + + + + + + + + + + + + + Configure Response Mode of ARP Requests for All IPv4 Interfaces + + Red Hat Enterprise Linux 8 + + The 'net.ipv4.conf.all.arp_ignore' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Configure Response Mode of ARP Requests for All IPv4 Interfaces + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv4.conf.all.arp_ignore' parameter should be set to the appropriate value in the system runtime. + + + + + + + + + Configure Response Mode of ARP Requests for All IPv4 Interfaces + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv4.conf.all.arp_ignore' parameter should be set to the appropriate value in the system configuration. + + + + + + + + + + + + + + Drop Gratuitious ARP frames on All IPv4 Interfaces + + Red Hat Enterprise Linux 8 + + The 'net.ipv4.conf.all.drop_gratuitous_arp' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Drop Gratuitious ARP frames on All IPv4 Interfaces + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv4.conf.all.drop_gratuitous_arp' parameter should be set to 1 in the system runtime. + + + + + + + + + Drop Gratuitious ARP frames on All IPv4 Interfaces + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv4.conf.all.drop_gratuitous_arp' parameter should be set to 1 in the system configuration. + + + + + + + + + + + + + + Disable Kernel Parameter for IPv4 Forwarding on all IPv4 Interfaces + + Red Hat Enterprise Linux 8 + + The 'net.ipv4.conf.all.forwarding' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Disable Kernel Parameter for IPv4 Forwarding on all IPv4 Interfaces + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv4.conf.all.forwarding' parameter should be set to the appropriate value in the system runtime. + + + + + + + + + Disable Kernel Parameter for IPv4 Forwarding on all IPv4 Interfaces + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv4.conf.all.forwarding' parameter should be set to the appropriate value in the system configuration. + + + + + + + + + + + + + + Enable Kernel Parameter to Log Martian Packets on all IPv4 Interfaces + + Red Hat Enterprise Linux 8 + + The 'net.ipv4.conf.all.log_martians' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Enable Kernel Parameter to Log Martian Packets on all IPv4 Interfaces + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv4.conf.all.log_martians' parameter should be set to the appropriate value in the system runtime. + + + + + + + + + Enable Kernel Parameter to Log Martian Packets on all IPv4 Interfaces + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv4.conf.all.log_martians' parameter should be set to the appropriate value in the system configuration. + + + + + + + + + + + + + + Prevent Routing External Traffic to Local Loopback on All IPv4 Interfaces + + Red Hat Enterprise Linux 8 + + The 'net.ipv4.conf.all.route_localnet' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Prevent Routing External Traffic to Local Loopback on All IPv4 Interfaces + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv4.conf.all.route_localnet' parameter should be set to 0 in the system runtime. + + + + + + + + + Prevent Routing External Traffic to Local Loopback on All IPv4 Interfaces + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv4.conf.all.route_localnet' parameter should be set to 0 in the system configuration. + + + + + + + + + + + + + + Enable Kernel Parameter to Use Reverse Path Filtering on all IPv4 Interfaces + + Red Hat Enterprise Linux 8 + + The 'net.ipv4.conf.all.rp_filter' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Enable Kernel Parameter to Use Reverse Path Filtering on all IPv4 Interfaces + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv4.conf.all.rp_filter' parameter should be set to 1 or 2 in the system runtime. + + + + + + + + + Enable Kernel Parameter to Use Reverse Path Filtering on all IPv4 Interfaces + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv4.conf.all.rp_filter' parameter should be set to 1 or 2 in the system configuration. + + + + + + + + + + + + + + Disable Kernel Parameter for Accepting Secure ICMP Redirects on all IPv4 Interfaces + + Red Hat Enterprise Linux 8 + + The 'net.ipv4.conf.all.secure_redirects' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Disable Kernel Parameter for Accepting Secure ICMP Redirects on all IPv4 Interfaces + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv4.conf.all.secure_redirects' parameter should be set to the appropriate value in the system runtime. + + + + + + + + + Disable Kernel Parameter for Accepting Secure ICMP Redirects on all IPv4 Interfaces + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv4.conf.all.secure_redirects' parameter should be set to the appropriate value in the system configuration. + + + + + + + + + + + + + + Disable Kernel Parameter for Sending ICMP Redirects on all IPv4 Interfaces + + Red Hat Enterprise Linux 8 + + The 'net.ipv4.conf.all.send_redirects' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Disable Kernel Parameter for Sending ICMP Redirects on all IPv4 Interfaces + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv4.conf.all.send_redirects' parameter should be set to 0 in the system runtime. + + + + + + + + + Disable Kernel Parameter for Sending ICMP Redirects on all IPv4 Interfaces + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv4.conf.all.send_redirects' parameter should be set to 0 in the system configuration. + + + + + + + + + + + + + + Configure Sending and Accepting Shared Media Redirects for All IPv4 Interfaces + + Red Hat Enterprise Linux 8 + + The 'net.ipv4.conf.all.shared_media' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Configure Sending and Accepting Shared Media Redirects for All IPv4 Interfaces + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv4.conf.all.shared_media' parameter should be set to the appropriate value in the system runtime. + + + + + + + + + Configure Sending and Accepting Shared Media Redirects for All IPv4 Interfaces + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv4.conf.all.shared_media' parameter should be set to the appropriate value in the system configuration. + + + + + + + + + + + + + + Disable Kernel Parameter for Accepting ICMP Redirects by Default on IPv4 Interfaces + + Red Hat Enterprise Linux 8 + + The 'net.ipv4.conf.default.accept_redirects' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Disable Kernel Parameter for Accepting ICMP Redirects by Default on IPv4 Interfaces + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv4.conf.default.accept_redirects' parameter should be set to the appropriate value in the system runtime. + + + + + + + + + Disable Kernel Parameter for Accepting ICMP Redirects by Default on IPv4 Interfaces + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv4.conf.default.accept_redirects' parameter should be set to the appropriate value in the system configuration. + + + + + + + + + + + + + + Disable Kernel Parameter for Accepting Source-Routed Packets on IPv4 Interfaces by Default + + Red Hat Enterprise Linux 8 + + The 'net.ipv4.conf.default.accept_source_route' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Disable Kernel Parameter for Accepting Source-Routed Packets on IPv4 Interfaces by Default + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv4.conf.default.accept_source_route' parameter should be set to the appropriate value in the system runtime. + + + + + + + + + Disable Kernel Parameter for Accepting Source-Routed Packets on IPv4 Interfaces by Default + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv4.conf.default.accept_source_route' parameter should be set to the appropriate value in the system configuration. + + + + + + + + + + + + + + Enable Kernel Paremeter to Log Martian Packets on all IPv4 Interfaces by Default + + Red Hat Enterprise Linux 8 + + The 'net.ipv4.conf.default.log_martians' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Enable Kernel Paremeter to Log Martian Packets on all IPv4 Interfaces by Default + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv4.conf.default.log_martians' parameter should be set to the appropriate value in the system runtime. + + + + + + + + + Enable Kernel Paremeter to Log Martian Packets on all IPv4 Interfaces by Default + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv4.conf.default.log_martians' parameter should be set to the appropriate value in the system configuration. + + + + + + + + + + + + + + Enable Kernel Parameter to Use Reverse Path Filtering on all IPv4 Interfaces by Default + + Red Hat Enterprise Linux 8 + + The 'net.ipv4.conf.default.rp_filter' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Enable Kernel Parameter to Use Reverse Path Filtering on all IPv4 Interfaces by Default + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv4.conf.default.rp_filter' parameter should be set to the appropriate value in the system runtime. + + + + + + + + + Enable Kernel Parameter to Use Reverse Path Filtering on all IPv4 Interfaces by Default + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv4.conf.default.rp_filter' parameter should be set to the appropriate value in the system configuration. + + + + + + + + + + + + + + Configure Kernel Parameter for Accepting Secure Redirects By Default + + Red Hat Enterprise Linux 8 + + The 'net.ipv4.conf.default.secure_redirects' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Configure Kernel Parameter for Accepting Secure Redirects By Default + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv4.conf.default.secure_redirects' parameter should be set to the appropriate value in the system runtime. + + + + + + + + + Configure Kernel Parameter for Accepting Secure Redirects By Default + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv4.conf.default.secure_redirects' parameter should be set to the appropriate value in the system configuration. + + + + + + + + + + + + + + Disable Kernel Parameter for Sending ICMP Redirects on all IPv4 Interfaces by Default + + Red Hat Enterprise Linux 8 + + The 'net.ipv4.conf.default.send_redirects' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Disable Kernel Parameter for Sending ICMP Redirects on all IPv4 Interfaces by Default + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv4.conf.default.send_redirects' parameter should be set to 0 in the system runtime. + + + + + + + + + Disable Kernel Parameter for Sending ICMP Redirects on all IPv4 Interfaces by Default + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv4.conf.default.send_redirects' parameter should be set to 0 in the system configuration. + + + + + + + + + + + + + + Configure Sending and Accepting Shared Media Redirects by Default + + Red Hat Enterprise Linux 8 + + The 'net.ipv4.conf.default.shared_media' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Configure Sending and Accepting Shared Media Redirects by Default + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv4.conf.default.shared_media' parameter should be set to the appropriate value in the system runtime. + + + + + + + + + Configure Sending and Accepting Shared Media Redirects by Default + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv4.conf.default.shared_media' parameter should be set to the appropriate value in the system configuration. + + + + + + + + + + + + + + Enable Kernel Parameter to Ignore ICMP Broadcast Echo Requests on IPv4 Interfaces + + Red Hat Enterprise Linux 8 + + The 'net.ipv4.icmp_echo_ignore_broadcasts' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Enable Kernel Parameter to Ignore ICMP Broadcast Echo Requests on IPv4 Interfaces + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv4.icmp_echo_ignore_broadcasts' parameter should be set to the appropriate value in the system runtime. + + + + + + + + + Enable Kernel Parameter to Ignore ICMP Broadcast Echo Requests on IPv4 Interfaces + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv4.icmp_echo_ignore_broadcasts' parameter should be set to the appropriate value in the system configuration. + + + + + + + + + + + + + + Enable Kernel Parameter to Ignore Bogus ICMP Error Responses on IPv4 Interfaces + + Red Hat Enterprise Linux 8 + + The 'net.ipv4.icmp_ignore_bogus_error_responses' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Enable Kernel Parameter to Ignore Bogus ICMP Error Responses on IPv4 Interfaces + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv4.icmp_ignore_bogus_error_responses' parameter should be set to the appropriate value in the system runtime. + + + + + + + + + Enable Kernel Parameter to Ignore Bogus ICMP Error Responses on IPv4 Interfaces + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv4.icmp_ignore_bogus_error_responses' parameter should be set to the appropriate value in the system configuration. + + + + + + + + + + + + + + Disable Kernel Parameter for IP Forwarding on IPv4 Interfaces + + Red Hat Enterprise Linux 8 + + The 'net.ipv4.ip_forward' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Disable Kernel Parameter for IP Forwarding on IPv4 Interfaces + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv4.ip_forward' parameter should be set to 0 in the system runtime. + + + + + + + + + Disable Kernel Parameter for IP Forwarding on IPv4 Interfaces + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv4.ip_forward' parameter should be set to 0 in the system configuration. + + + + + + + + + + + + + + Set Kernel Parameter to Increase Local Port Range + + Red Hat Enterprise Linux 8 + + The 'net.ipv4.ip_local_port_range' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Set Kernel Parameter to Increase Local Port Range + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv4.ip_local_port_range' parameter should be set to 32768 65535 in the system runtime. + + + + + + + + + Set Kernel Parameter to Increase Local Port Range + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv4.ip_local_port_range' parameter should be set to 32768 65535 in the system configuration. + + + + + + + + + + + + + + Configure Kernel to Rate Limit Sending of Duplicate TCP Acknowledgments + + Red Hat Enterprise Linux 8 + + The 'net.ipv4.tcp_invalid_ratelimit' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + Configure Kernel to Rate Limit Sending of Duplicate TCP Acknowledgments + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv4.tcp_invalid_ratelimit' parameter should be set to the appropriate value in the system runtime. + + + + + + + + + Configure Kernel to Rate Limit Sending of Duplicate TCP Acknowledgments + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv4.tcp_invalid_ratelimit' parameter should be set to the appropriate value in the system configuration. + + + + + + + + + + + + + + Enable Kernel Parameter to Use TCP RFC 1337 on IPv4 Interfaces + + Red Hat Enterprise Linux 8 + + The 'net.ipv4.tcp_rfc1337' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Enable Kernel Parameter to Use TCP RFC 1337 on IPv4 Interfaces + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv4.tcp_rfc1337' parameter should be set to the appropriate value in the system runtime. + + + + + + + + + Enable Kernel Parameter to Use TCP RFC 1337 on IPv4 Interfaces + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv4.tcp_rfc1337' parameter should be set to the appropriate value in the system configuration. + + + + + + + + + + + + + + Enable Kernel Parameter to Use TCP Syncookies on Network Interfaces + + Red Hat Enterprise Linux 8 + + The 'net.ipv4.tcp_syncookies' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Enable Kernel Parameter to Use TCP Syncookies on Network Interfaces + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv4.tcp_syncookies' parameter should be set to the appropriate value in the system runtime. + + + + + + + + + Enable Kernel Parameter to Use TCP Syncookies on Network Interfaces + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv4.tcp_syncookies' parameter should be set to the appropriate value in the system configuration. + + + + + + + + + + + + + + Configure Accepting Router Advertisements on All IPv6 Interfaces + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv6.conf.all.accept_ra' parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + + + + Configure Accepting Router Advertisements on All IPv6 Interfaces + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv6.conf.all.accept_ra' parameter should be set to the appropriate value in the system runtime. + + + + + + + + + Configure Accepting Router Advertisements on All IPv6 Interfaces + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv6.conf.all.accept_ra' parameter should be set to the appropriate value in the system configuration. + + + + + + + + + + + + + + Configure Accepting Default Router in Router Advertisements on All IPv6 Interfaces + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv6.conf.all.accept_ra_defrtr' parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + + + + Configure Accepting Default Router in Router Advertisements on All IPv6 Interfaces + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv6.conf.all.accept_ra_defrtr' parameter should be set to the appropriate value in the system runtime. + + + + + + + + + Configure Accepting Default Router in Router Advertisements on All IPv6 Interfaces + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv6.conf.all.accept_ra_defrtr' parameter should be set to the appropriate value in the system configuration. + + + + + + + + + + + + + + Configure Accepting Prefix Information in Router Advertisements on All IPv6 Interfaces + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv6.conf.all.accept_ra_pinfo' parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + + + + Configure Accepting Prefix Information in Router Advertisements on All IPv6 Interfaces + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv6.conf.all.accept_ra_pinfo' parameter should be set to the appropriate value in the system runtime. + + + + + + + + + Configure Accepting Prefix Information in Router Advertisements on All IPv6 Interfaces + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv6.conf.all.accept_ra_pinfo' parameter should be set to the appropriate value in the system configuration. + + + + + + + + + + + + + + Configure Accepting Router Preference in Router Advertisements on All IPv6 Interfaces + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv6.conf.all.accept_ra_rtr_pref' parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + + + + Configure Accepting Router Preference in Router Advertisements on All IPv6 Interfaces + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv6.conf.all.accept_ra_rtr_pref' parameter should be set to the appropriate value in the system runtime. + + + + + + + + + Configure Accepting Router Preference in Router Advertisements on All IPv6 Interfaces + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv6.conf.all.accept_ra_rtr_pref' parameter should be set to the appropriate value in the system configuration. + + + + + + + + + + + + + + Disable Accepting ICMP Redirects for All IPv6 Interfaces + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv6.conf.all.accept_redirects' parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + + + + Disable Accepting ICMP Redirects for All IPv6 Interfaces + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv6.conf.all.accept_redirects' parameter should be set to the appropriate value in the system runtime. + + + + + + + + + Disable Accepting ICMP Redirects for All IPv6 Interfaces + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv6.conf.all.accept_redirects' parameter should be set to the appropriate value in the system configuration. + + + + + + + + + + + + + + Disable Kernel Parameter for Accepting Source-Routed Packets on all IPv6 Interfaces + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv6.conf.all.accept_source_route' parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + + + + Disable Kernel Parameter for Accepting Source-Routed Packets on all IPv6 Interfaces + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv6.conf.all.accept_source_route' parameter should be set to the appropriate value in the system runtime. + + + + + + + + + Disable Kernel Parameter for Accepting Source-Routed Packets on all IPv6 Interfaces + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv6.conf.all.accept_source_route' parameter should be set to the appropriate value in the system configuration. + + + + + + + + + + + + + + Configure Auto Configuration on All IPv6 Interfaces + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv6.conf.all.autoconf' parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + + + + Configure Auto Configuration on All IPv6 Interfaces + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv6.conf.all.autoconf' parameter should be set to the appropriate value in the system runtime. + + + + + + + + + Configure Auto Configuration on All IPv6 Interfaces + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv6.conf.all.autoconf' parameter should be set to the appropriate value in the system configuration. + + + + + + + + + + + + + + Disable IPv6 Addressing on All IPv6 Interfaces + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv6.conf.all.disable_ipv6' parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + + + + Disable IPv6 Addressing on All IPv6 Interfaces + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv6.conf.all.disable_ipv6' parameter should be set to 1 in the system runtime. + + + + + + + + + Disable IPv6 Addressing on All IPv6 Interfaces + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv6.conf.all.disable_ipv6' parameter should be set to 1 in the system configuration. + + + + + + + + + + + + + + Disable Kernel Parameter for IPv6 Forwarding + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv6.conf.all.forwarding' parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + + + + Disable Kernel Parameter for IPv6 Forwarding + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv6.conf.all.forwarding' parameter should be set to the appropriate value in the system runtime. + + + + + + + + + Disable Kernel Parameter for IPv6 Forwarding + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv6.conf.all.forwarding' parameter should be set to the appropriate value in the system configuration. + + + + + + + + + + + + + + Configure Maximum Number of Autoconfigured Addresses on All IPv6 Interfaces + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv6.conf.all.max_addresses' parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + + + + Configure Maximum Number of Autoconfigured Addresses on All IPv6 Interfaces + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv6.conf.all.max_addresses' parameter should be set to the appropriate value in the system runtime. + + + + + + + + + Configure Maximum Number of Autoconfigured Addresses on All IPv6 Interfaces + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv6.conf.all.max_addresses' parameter should be set to the appropriate value in the system configuration. + + + + + + + + + + + + + + Configure Denying Router Solicitations on All IPv6 Interfaces + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv6.conf.all.router_solicitations' parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + + + + Configure Denying Router Solicitations on All IPv6 Interfaces + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv6.conf.all.router_solicitations' parameter should be set to the appropriate value in the system runtime. + + + + + + + + + Configure Denying Router Solicitations on All IPv6 Interfaces + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv6.conf.all.router_solicitations' parameter should be set to the appropriate value in the system configuration. + + + + + + + + + + + + + + Disable Accepting Router Advertisements on all IPv6 Interfaces by Default + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv6.conf.default.accept_ra' parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + + + + Disable Accepting Router Advertisements on all IPv6 Interfaces by Default + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv6.conf.default.accept_ra' parameter should be set to the appropriate value in the system runtime. + + + + + + + + + Disable Accepting Router Advertisements on all IPv6 Interfaces by Default + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv6.conf.default.accept_ra' parameter should be set to the appropriate value in the system configuration. + + + + + + + + + + + + + + Configure Accepting Default Router in Router Advertisements on All IPv6 Interfaces By Default + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv6.conf.default.accept_ra_defrtr' parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + + + + Configure Accepting Default Router in Router Advertisements on All IPv6 Interfaces By Default + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv6.conf.default.accept_ra_defrtr' parameter should be set to the appropriate value in the system runtime. + + + + + + + + + Configure Accepting Default Router in Router Advertisements on All IPv6 Interfaces By Default + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv6.conf.default.accept_ra_defrtr' parameter should be set to the appropriate value in the system configuration. + + + + + + + + + + + + + + Configure Accepting Prefix Information in Router Advertisements on All IPv6 Interfaces By Default + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv6.conf.default.accept_ra_pinfo' parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + + + + Configure Accepting Prefix Information in Router Advertisements on All IPv6 Interfaces By Default + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv6.conf.default.accept_ra_pinfo' parameter should be set to the appropriate value in the system runtime. + + + + + + + + + Configure Accepting Prefix Information in Router Advertisements on All IPv6 Interfaces By Default + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv6.conf.default.accept_ra_pinfo' parameter should be set to the appropriate value in the system configuration. + + + + + + + + + + + + + + Configure Accepting Router Preference in Router Advertisements on All IPv6 Interfaces By Default + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv6.conf.default.accept_ra_rtr_pref' parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + + + + Configure Accepting Router Preference in Router Advertisements on All IPv6 Interfaces By Default + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv6.conf.default.accept_ra_rtr_pref' parameter should be set to the appropriate value in the system runtime. + + + + + + + + + Configure Accepting Router Preference in Router Advertisements on All IPv6 Interfaces By Default + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv6.conf.default.accept_ra_rtr_pref' parameter should be set to the appropriate value in the system configuration. + + + + + + + + + + + + + + Disable Kernel Parameter for Accepting ICMP Redirects by Default on IPv6 Interfaces + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv6.conf.default.accept_redirects' parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + + + + Disable Kernel Parameter for Accepting ICMP Redirects by Default on IPv6 Interfaces + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv6.conf.default.accept_redirects' parameter should be set to the appropriate value in the system runtime. + + + + + + + + + Disable Kernel Parameter for Accepting ICMP Redirects by Default on IPv6 Interfaces + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv6.conf.default.accept_redirects' parameter should be set to the appropriate value in the system configuration. + + + + + + + + + + + + + + Disable Kernel Parameter for Accepting Source-Routed Packets on IPv6 Interfaces by Default + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv6.conf.default.accept_source_route' parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + + + + Disable Kernel Parameter for Accepting Source-Routed Packets on IPv6 Interfaces by Default + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv6.conf.default.accept_source_route' parameter should be set to the appropriate value in the system runtime. + + + + + + + + + Disable Kernel Parameter for Accepting Source-Routed Packets on IPv6 Interfaces by Default + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv6.conf.default.accept_source_route' parameter should be set to the appropriate value in the system configuration. + + + + + + + + + + + + + + Configure Auto Configuration on All IPv6 Interfaces By Default + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv6.conf.default.autoconf' parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + + + + Configure Auto Configuration on All IPv6 Interfaces By Default + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv6.conf.default.autoconf' parameter should be set to the appropriate value in the system runtime. + + + + + + + + + Configure Auto Configuration on All IPv6 Interfaces By Default + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv6.conf.default.autoconf' parameter should be set to the appropriate value in the system configuration. + + + + + + + + + + + + + + Disable IPv6 Addressing on IPv6 Interfaces by Default + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv6.conf.default.disable_ipv6' parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + + + + Disable IPv6 Addressing on IPv6 Interfaces by Default + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv6.conf.default.disable_ipv6' parameter should be set to 1 in the system runtime. + + + + + + + + + Disable IPv6 Addressing on IPv6 Interfaces by Default + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv6.conf.default.disable_ipv6' parameter should be set to 1 in the system configuration. + + + + + + + + + + + + + + Configure Maximum Number of Autoconfigured Addresses on All IPv6 Interfaces By Default + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv6.conf.default.max_addresses' parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + + + + Configure Maximum Number of Autoconfigured Addresses on All IPv6 Interfaces By Default + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv6.conf.default.max_addresses' parameter should be set to the appropriate value in the system runtime. + + + + + + + + + Configure Maximum Number of Autoconfigured Addresses on All IPv6 Interfaces By Default + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv6.conf.default.max_addresses' parameter should be set to the appropriate value in the system configuration. + + + + + + + + + + + + + + Configure Denying Router Solicitations on All IPv6 Interfaces By Default + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv6.conf.default.router_solicitations' parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + + + + Configure Denying Router Solicitations on All IPv6 Interfaces By Default + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv6.conf.default.router_solicitations' parameter should be set to the appropriate value in the system runtime. + + + + + + + + + Configure Denying Router Solicitations on All IPv6 Interfaces By Default + + Red Hat Enterprise Linux 8 + + The kernel 'net.ipv6.conf.default.router_solicitations' parameter should be set to the appropriate value in the system configuration. + + + + + + + + + + + + + + Disable the use of user namespaces + + Red Hat Enterprise Linux 8 + + The 'user.max_user_namespaces' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Disable the use of user namespaces + + Red Hat Enterprise Linux 8 + + The kernel 'user.max_user_namespaces' parameter should be set to 0 in the system runtime. + + + + + + + + + Disable the use of user namespaces + + Red Hat Enterprise Linux 8 + + The kernel 'user.max_user_namespaces' parameter should be set to 0 in the system configuration. + + + + + + + + + + + + + + Prevent applications from mapping low portion of virtual memory + + Red Hat Enterprise Linux 8 + + The 'vm.mmap_min_addr' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Prevent applications from mapping low portion of virtual memory + + Red Hat Enterprise Linux 8 + + The kernel 'vm.mmap_min_addr' parameter should be set to 65536 in the system runtime. + + + + + + + + + Prevent applications from mapping low portion of virtual memory + + Red Hat Enterprise Linux 8 + + The kernel 'vm.mmap_min_addr' parameter should be set to 65536 in the system configuration. + + + + + + + + + + + + + + Enable dnf-automatic Timer + + Red Hat Enterprise Linux 8 + + The dnf-automatic timer should be enabled if possible. + + + + + + + + + + + + + + Enable Auditing to Start Prior to the Audit Daemon in zIPL + + Red Hat Enterprise Linux 8 + + Ensure audit=1 option is configured in the 'options' line in /boot/loader/entries/*.conf. Make sure that newly installed kernels will retain this option, it should be configured in /etc/kernel/cmdline as well. + + + + + + + + + + + Extend Audit Backlog Limit for the Audit Daemon in zIPL + + Red Hat Enterprise Linux 8 + + Ensure audit_backlog_limit=8192 option is configured in the 'options' line in /boot/loader/entries/*.conf. Make sure that newly installed kernels will retain this option, it should be configured in /etc/kernel/cmdline as well. + + + + + + + + + + + Enable page allocator poisoning in zIPL + + Red Hat Enterprise Linux 8 + + Ensure page_poison=1 option is configured in the 'options' line in /boot/loader/entries/*.conf. Make sure that newly installed kernels will retain this option, it should be configured in /etc/kernel/cmdline as well. + + + + + + + + + + + Enable SLUB/SLAB allocator poisoning in zIPL + + Red Hat Enterprise Linux 8 + + Ensure slub_debug=P option is configured in the 'options' line in /boot/loader/entries/*.conf. Make sure that newly installed kernels will retain this option, it should be configured in /etc/kernel/cmdline as well. + + + + + + + + + + + Disable vsyscalls in zIPL + + Red Hat Enterprise Linux 8 + + Ensure vsyscall=none option is configured in the 'options' line in /boot/loader/entries/*.conf. Make sure that newly installed kernels will retain this option, it should be configured in /etc/kernel/cmdline as well. + + + + + + + + + + + Check pam_faillock Existence in system-auth + + Red Hat Enterprise Linux 8 + + Check that pam_faillock.so exists in system-auth + + + + + + + + + Check pam_pwquality Existence in system-auth + + Red Hat Enterprise Linux 8 + + Check that pam_pwquality.so exists in system-auth + + + + + + + + + Record Any Attempts to Run semanage + + Red Hat Enterprise Linux 8 + + Test if auditctl is in use for audit rules. + + + + + + + + + Record Any Attempts to Run semanage + + Red Hat Enterprise Linux 8 + + Test if augenrules is enabled for audit rules. + + + + + + + + + Record Events that Modify the System's Network Environment + + Red Hat Enterprise Linux 8 + + The network environment should not be modified by anything other than + administrator action. Any change to network parameters should be audited. + + + + + + + + + + + + + + + + + + + + + + + + Record Events that Modify the System's Network Environment + + Red Hat Enterprise Linux 8 + + The network environment should not be modified by anything other than + administrator action. Any change to network parameters should be audited. + + + + + + + + + + + + + + + + + + + + + + + + 'log_file' Not Set In /etc/audit/auditd.conf + + Red Hat Enterprise Linux 8 + + Verify 'log_file' is not set in /etc/audit/auditd.conf. + + + + + + + + + 'log_group' Not Set To 'root' In /etc/audit/auditd.conf + + Red Hat Enterprise Linux 8 + + Verify 'log_group' is not set to 'root' in + /etc/audit/auditd.conf. + + + + + + + + + + Verify GRUB_DISABLE_RECOVERY Set to true + + Red Hat Enterprise Linux 8 + + GRUB_DISABLE_RECOVERY set to 'true' in + /etc/default/grub + + + + + + + + + Specify Multiple Remote chronyd NTP Servers for Time Data + + Red Hat Enterprise Linux 8 + + Multiple chronyd NTP Servers for time synchronization should be specified. + + + + + + + + + GRUB_CMDLINE_LINUX_DEFAULT existance check + + Red Hat Enterprise Linux 8 + + Check if GRUB_CMDLINE_LINUX_DEFAULT exists in /etc/default/grub. + + + + + + + + + Use $kernelopts in /boot/loader/entries/*.conf + + Red Hat Enterprise Linux 8 + + Ensure that grubenv-defined kernel options are referenced in individual boot loader entries + + + + + + + + + Install McAfee Host-Based Intrusion Detection Software (HBSS) + + Red Hat Enterprise Linux 8 + + McAfee Host-Based Intrusion Detection Software (HBSS) software + should be installed. + + + + + + + + + + + + Alibaba Cloud Linux 2 + + Red Hat Enterprise Linux 8 + + + The operating system installed on the system is Alibaba Cloud Linux 2 + + + + + + + + + + Alibaba Cloud Linux 3 + + Red Hat Enterprise Linux 8 + + + The operating system installed on the system is Alibaba Cloud Linux 3 + + + + + + + + + + CentOS 7 + + Red Hat Enterprise Linux 8 + + + The operating system installed on the system is + CentOS 7 + + + + + + + + + + CentOS 8 + + Red Hat Enterprise Linux 8 + + + The operating system installed on the system is + CentOS 8 + + + + + + + + + + + CentOS Stream 9 + + Red Hat Enterprise Linux 8 + + + The operating system installed on the system is + CentOS Stream 9 + + + + + + + + + + + Debian + + Red Hat Enterprise Linux 8 + + The operating system installed is a Debian System + + + + + + + + + + Debian Linux 10 + + Red Hat Enterprise Linux 8 + + + The operating system installed on the system is Debian 10 + + + + + + + + + + Debian Linux 11 + + Red Hat Enterprise Linux 8 + + + The operating system installed on the system is Debian 11 + + + + + + + + + + Debian 9 + + Red Hat Enterprise Linux 8 + + + The operating system installed on the system is Debian 9 + + + + + + + + + + Installed operating system is Fedora + + Red Hat Enterprise Linux 8 + + + + + + The operating system installed on the system is Fedora + + + + + + + + + + + Oracle Linux 7 + + Red Hat Enterprise Linux 8 + + + The operating system installed on the system is + Oracle Linux 7 + + + + + + + + + + + + Oracle Linux 8 + + Red Hat Enterprise Linux 8 + + + The operating system installed on the system is + Oracle Linux 8 + + + + + + + + + + + + Oracle Linux 9 + + Red Hat Enterprise Linux 8 + + + The operating system installed on the system is + Oracle Linux 9 + + + + + + + + + + + + openSUSE + + Red Hat Enterprise Linux 8 + + The operating system installed on the system is openSUSE. + + + + + + + + + + openSUSE Leap 15 + + Red Hat Enterprise Linux 8 + + + The operating system installed on the system is openSUSE Leap 15. + + + + + + + + + + openSUSE Leap 42 + + Red Hat Enterprise Linux 8 + + + + + The operating system installed on the system is openSUSE Leap 42. + + + + + + + + + + Installed operating system is part of the Unix family + + Red Hat Enterprise Linux 8 + + The operating system installed on the system is part of the Unix OS family + + + + + + + + + Red Hat Enterprise Linux CoreOS + + Red Hat Enterprise Linux 8 + + + The operating system installed on the system is + Red Hat Enterprise Linux CoreOS release 4 + + + + + + + + + + + + Red Hat Enterprise Linux 7 + + Red Hat Enterprise Linux 8 + + + The operating system installed on the system is + Red Hat Enterprise Linux 7 + + + + + + + + + + + + + + + + + + + Red Hat Enterprise Linux 8 + + Red Hat Enterprise Linux 8 + + + The operating system installed on the system is + Red Hat Enterprise Linux 8 + + + + + + + + + + + + + + + + Red Hat Enterprise Linux 8.0 + + Red Hat Enterprise Linux 8 + + + The operating system installed on the system is Red Hat Enterprise Linux 8.0 + + + + + + + + + Red Hat Enterprise Linux 8.1 + + Red Hat Enterprise Linux 8 + + + The operating system installed on the system is Red Hat Enterprise Linux 8.1 + + + + + + + + + Red Hat Enterprise Linux 8.2 + + Red Hat Enterprise Linux 8 + + + The operating system installed on the system is Red Hat Enterprise Linux 8.2 + + + + + + + + + Red Hat Enterprise Linux 8.3 + + Red Hat Enterprise Linux 8 + + + The operating system installed on the system is Red Hat Enterprise Linux 8.3 + + + + + + + + + Red Hat Enterprise Linux 8.4 + + Red Hat Enterprise Linux 8 + + + The operating system installed on the system is Red Hat Enterprise Linux 8.4 + + + + + + + + + Red Hat Enterprise Linux 8.5 + + Red Hat Enterprise Linux 8 + + + The operating system installed on the system is Red Hat Enterprise Linux 8.5 + + + + + + + + + Red Hat Enterprise Linux 8.6 + + Red Hat Enterprise Linux 8 + + + The operating system installed on the system is Red Hat Enterprise Linux 8.6 + + + + + + + + + Red Hat Enterprise Linux 8.7 + + Red Hat Enterprise Linux 8 + + + The operating system installed on the system is Red Hat Enterprise Linux 8.7 + + + + + + + + + Red Hat Enterprise Linux 8.8 + + Red Hat Enterprise Linux 8 + + + The operating system installed on the system is Red Hat Enterprise Linux 8.8 + + + + + + + + + Red Hat Enterprise Linux 8.9 + + Red Hat Enterprise Linux 8 + + + The operating system installed on the system is Red Hat Enterprise Linux 8.9 + + + + + + + + + Red Hat Enterprise Linux 8.10 + + Red Hat Enterprise Linux 8 + + + The operating system installed on the system is Red Hat Enterprise Linux 8.10 + + + + + + + + + Red Hat Enterprise Linux 9 + + Red Hat Enterprise Linux 8 + + + The operating system installed on the system is + Red Hat Enterprise Linux 9 + + + + + + + + + + + + + + + + Red Hat Virtualization 4 + + Red Hat Enterprise Linux 8 + + + The operating system installed on the system is + Red Hat Virtualization Host 4.4+ or Red Hat Enterprise Host. + + + + + + + + + + Scientific Linux 7 + + Red Hat Enterprise Linux 8 + + + The operating system installed on the system is + Scientific Linux 7 + + + + + + + + + + SUSE Linux Enterprise 12 + + Red Hat Enterprise Linux 8 + + + + The operating system installed on the system is + SUSE Linux Enterprise 12. + + + + + + + + + + + + + + SUSE Linux Enterprise 15 + + Red Hat Enterprise Linux 8 + + + + The operating system installed on the system is + SUSE Linux Enterprise 15. + + + + + + + + + + + + + + Ubuntu + + Red Hat Enterprise Linux 8 + + The operating system installed is an Ubuntu System + + + + + + + + + + + Ubuntu 1604 + + Red Hat Enterprise Linux 8 + + + The operating system installed on the system is Ubuntu 1604 + + + + + + + + + + Ubuntu 1804 + + Red Hat Enterprise Linux 8 + + + The operating system installed on the system is Ubuntu 1804 + + + + + + + + + + Ubuntu 2004 + + Red Hat Enterprise Linux 8 + + + The operating system installed on the system is Ubuntu 2004 + + + + + + + + + + Ubuntu 2204 + + Red Hat Enterprise Linux 8 + + + The operating system installed on the system is Ubuntu 2204 + + + + + + + + + + UnionTech OS Server 20 + + Red Hat Enterprise Linux 8 + + + The operating system installed on the system is UnionTech OS Server 20 + + + + + + + + + + Red Hat Virtualization 4 + + Red Hat Enterprise Linux 8 + + + The application installed installed on the system is + Red Hat Virtualization 4. + + + + + + + + + + Package audit is installed + + Red Hat Enterprise Linux 8 + + Checks if package audit is installed. + + + + + + + + + + Package chrony is installed + + Red Hat Enterprise Linux 8 + + Checks if package chrony is installed. + + + + + + + + + + Package gdm is installed + + Red Hat Enterprise Linux 8 + + Checks if package gdm is installed. + + + + + + + + + + Package grub2 is installed + + Red Hat Enterprise Linux 8 + + Checks if package grub2-common is installed. + + + + + + + + + + + + + + Package libuser is installed + + Red Hat Enterprise Linux 8 + + Checks if package libuser is installed. + + + + + + + + + + Package providing /etc/login.defs is installed + + Red Hat Enterprise Linux 8 + + Checks if package providing /etc/login.defs and is installed. + + + + + + + + + + Package net-snmp is installed + + Red Hat Enterprise Linux 8 + + Checks if package net-snmp is installed. + + + + + + + + + + Check if the system doesn't act as an oVirt host or manager + + Red Hat Enterprise Linux 8 + + Check if the system has neither ovirt-host nor ovirt-engine installed. + + + + + + + + + Package nss-pam-ldapd is installed + + Red Hat Enterprise Linux 8 + + Checks if package nss-pam-ldapd is installed. + + + + + + + + + + Package ntp is installed + + Red Hat Enterprise Linux 8 + + Checks if package ntp is installed. + + + + + + + + + + Check if the system acts as an oVirt host or manager + + Red Hat Enterprise Linux 8 + + Check if the system has ovirt-host or ovirt-engine installed + + + + + + + + + + + Package pam is installed + + Red Hat Enterprise Linux 8 + + Checks if package pam is installed. + + + + + + + + + + Package polkit is installed + + Red Hat Enterprise Linux 8 + + Checks if package polkit is installed. + + + + + + + + + + Package postfix is installed + + Red Hat Enterprise Linux 8 + + Checks if package postfix is installed. + + + + + + + + + + Package sssd-common is installed + + Red Hat Enterprise Linux 8 + + Checks if package sssd-common is installed. + + + + + + + + + + Package sudo is installed + + Red Hat Enterprise Linux 8 + + Checks if package sudo is installed. + + + + + + + + + + Package systemd is installed + + Red Hat Enterprise Linux 8 + + Checks if package systemd is installed. + + + + + + + + + + Package tftp-server is installed + + Red Hat Enterprise Linux 8 + + Checks if package tftp-server is installed. + + + + + + + + + + Package tmux is installed + + Red Hat Enterprise Linux 8 + + Checks if package tmux is installed. + + + + + + + + + + Package usbguard is installed + + Red Hat Enterprise Linux 8 + + Checks if package usbguard is installed. + + + + + + + + + + WiFi interface is present + + Red Hat Enterprise Linux 8 + + Checks if any wifi interface is present. + + + + + + + + + + Package yum is installed + + Red Hat Enterprise Linux 8 + + Checks if package yum is installed. + + + + + + + + + + System uses zIPL + + Red Hat Enterprise Linux 8 + + Checks if system uses zIPL bootloader. + + + + + + + + + + Check if the scan target is a container + + Red Hat Enterprise Linux 8 + + Check for presence of files characterizing container filesystems. + + + + + + + + + + + Check if the scan target is a machine + + Red Hat Enterprise Linux 8 + + Check for absence of files characterizing container filesystems. + + + + + + + + + + Partition /tmp exists + + Red Hat Enterprise Linux 8 + + + + + + + + + + + Partition /var/tmp exists + + Red Hat Enterprise Linux 8 + + + + + + + + + + + Kerberos server is older than 1.17-18 + + Red Hat Enterprise Linux 8 + + + Check if version of Kerberos server is lesser than 1.17-18 + + + + + + + + + + Kerberos workstation is older than 1.17-18 + + Red Hat Enterprise Linux 8 + + + Check if version of Kerberos workstation is lesser than 1.17-18 + + + + + + + + + + No CD/DVD drive is configured to automount in /etc/fstab + + Red Hat Enterprise Linux 8 + + Check the /etc/fstab and check if a CD/DVD drive + is not configured for automount. + + + + + + + + + Test that the architecture is aarch64 + + Red Hat Enterprise Linux 8 + + Check that architecture of kernel in /proc/sys/kernel/osrelease is aarch64 + + + + + + + + + Test for different architecture than aarch64 + + Red Hat Enterprise Linux 8 + + Check that architecture of kernel in /proc/sys/kernel/osrelease is not aarch64 + + + + + + + + + Test for different architecture than s390x + + Red Hat Enterprise Linux 8 + + Check that architecture of kernel in /proc/sys/kernel/osrelease is not s390x + + + + + + + + + Test that the architecture is ppc64le + + Red Hat Enterprise Linux 8 + + Check that architecture of kernel in /proc/sys/kernel/osrelease is ppc64le + + + + + + + + + Test that the architecture is s390x + + Red Hat Enterprise Linux 8 + + Check that architecture of kernel in /proc/sys/kernel/osrelease is s390x + + + + + + + + + Device Files for Removable Media Partitions Does Not Exist on the System + + Red Hat Enterprise Linux 8 + + Verify if device file representing removable partitions + exist on the system + + + + + + + + + SSHD is not required to be installed or requirement not set + + Red Hat Enterprise Linux 8 + + If SSHD is not required, we check it is not installed. If SSH requirement is unset, we are good. + + + + + + + + + + SSHD is required to be installed or requirement not set + + Red Hat Enterprise Linux 8 + + If SSHD is required, we check it is installed. If SSH requirement is unset, we are good. + + + + + + + + + + It doesn't matter if sshd is installed or not + + Red Hat Enterprise Linux 8 + + Test if value sshd_required is 0. + + + + + + + + + OpenSSH Server is 7.4 or newer + + Red Hat Enterprise Linux 8 + + Check if version of OpenSSH Server is equal or higher than 7.4 + + + + + + + + + SSSD is configured to use LDAP + + Red Hat Enterprise Linux 8 + + Identification provider is not set to ad within /etc/sssd/sssd.conf + + + + + + + + + + Kernel Runtime Parameter IPv6 Check + + Red Hat Enterprise Linux 8 + + Disables IPv6 for all network interfaces. + + + + + + + + + + + + Non-UEFI system boot mode check + + Red Hat Enterprise Linux 8 + + Check if System boot mode is non-UEFI. + + + + + + + + + + UEFI system boot mode check + + Red Hat Enterprise Linux 8 + + Check if system boot mode is UEFI. + + + + + + + + + + Test for 64-bit Architecture + + Red Hat Enterprise Linux 8 + + Generic test for 64-bit architectures to be used by other tests + + + + + + + + + + + + Test for aarch_64 Architecture + + Red Hat Enterprise Linux 8 + + Generic test for aarch_64 architecture to be used by other tests + + + + + + + + + Test for PPC and PPCLE Architecture + + Red Hat Enterprise Linux 8 + + Generic test for PPC PPC64LE architecture to be used by other tests + + + + + + + + + + Test for s390_64 Architecture + + Red Hat Enterprise Linux 8 + + Generic test for s390_64 architecture to be used by other tests + + + + + + + + + Test for x86 Architecture + + Red Hat Enterprise Linux 8 + + Generic test for x86 architecture to be used by other tests + + + + + + + + + Test for x86_64 Architecture + + Red Hat Enterprise Linux 8 + + Generic test for x86_64 architecture to be used by other tests + + + + + + + + + + + Red Hat Enterprise Linux 8 + + Check /etc/tmux.conf is readable by others + + + + + + + + + Check that file storing USBGuard rules exists and is not empty + + Red Hat Enterprise Linux 8 + + Check that file storing USBGuard rules at /etc/usbguard/rules.conf exists and is not empty + + + + + + + + + Value of 'var_accounts_user_umask' variable represented as octal number + + Red Hat Enterprise Linux 8 + + Value of 'var_accounts_user_umask' variable represented as octal number + + + + + + + + + Value of 'var_removable_partition' variable is set to '/dev/cdrom' + + Red Hat Enterprise Linux 8 + + Verify if value of 'var_removable_partition' variable is set + to '/dev/cdrom' + + + + + + + + + Value of 'var_umask_for_daemons' variable represented as octal number + + Red Hat Enterprise Linux 8 + + Value of 'var_umask_for_daemons' variable represented as octal number + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + /etc/sysconfig/network-scripts + ifcfg-.* + ^[\s]*BOOTPROTO[\s]*=[\s"]*([^#"\s]*) + 1 + + + + /etc/fapolicyd/fapolicyd.rules + (^|\n)\s*deny\s*perm=any\s*all\s*:\s*all\s*$ + 1 + + + /etc/fapolicyd/fapolicyd.conf + ^\s*permissive\s*=\s*(\d+) + 1 + + + /etc/vsftpd/vsftpd.conf + ^[\s]*xferlog_enable[\s]*=[\s]*YES$ + 1 + + + /etc/vsftpd/vsftpd.conf + ^[\s]*xferlog_std_format[\s]*=[\s]*NO$ + 1 + + + /etc/vsftpd/vsftpd.conf + ^[\s]*log_ftp_protocol[\s]*=[\s]*YES$ + 1 + + + /etc/vsftpd/vsftpd.conf + ^[\s]*banner_file=/etc/issue[\s]*$ + 1 + + + /etc/httpd/conf + + + + /var/log/httpd + + + + + /etc/httpd/conf.d/ + ^.*$ + + + + /etc/httpd/conf + ^.*$ + + + + /etc/httpd/conf.modules.d/ + ^.*$ + + + /etc/dovecot/conf.d/10-auth.conf + ^[\s]*disable_plaintext_auth[\s]*=[\s]*yes[\s]*$ + 1 + + + /etc/dovecot/conf.d/10-ssl.conf + ^[\s]*ssl[\s]*=[\s]*(yes|required)[\s]*$ + 1 + + + ^/etc/.+\.keytab$ + + + /etc/sysconfig/authconfig + ^[\s]*USELDAPAUTH=yes[\s]*$ + 1 + + + /etc/nslcd.conf + ^[\s]*ssl[\s]+start_tls[\s]*$ + 1 + + + /etc/nslcd.conf + ^[\s]*tls_cacertdir[\s]+/etc/pki/tls/CA$ + 1 + + + /etc/nslcd.conf + ^[\s]*tls_cacertfile[\s]+/etc/pki/tls/CA/.*\.(pem|crt)$ + 1 + + + /etc/aliases + ^(?:[rR][oO][oO][tT]|"[rR][oO][oO][tT]")\s*:\s*(.+)$ + 1 + + + /etc/aliases + ^(?i)postmaster\s*:\s*(.+)$ + 1 + + + /etc/postfix/main.cf + ^[\s]*inet_interfaces[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/postfix/main.cf + ^[\s]*smtpd_banner[\s]*=[\s]*\$myhostname[\s]+ESMTP[\s]*$ + 1 + + + /etc/postfix/main.cf + ^[ \t]*smtpd_client_restrictions = (.+?)[ \t]*(?:$|#) + 1 + + + ^/etc/postfix/main.cf + + + /etc/exports + ^(.*?(\binsecure_locks\b)[^$]*)$ + 1 + + + /etc/exports + ^\/.*\((\S+)\)$ + 0 + + + /etc/exports + ^\/.*$ + 0 + + + /etc/chrony.conf + ^\s*port[\s]+(\S+) + 1 + + + /etc/chrony.conf + ^\s*cmdport[\s]+(\S+) + 1 + + + /etc/ntp.conf + ^server[\s]+[\S]+.*maxpoll[\s]+(\d+) + 1 + + + ^/etc/chrony\.(conf|d/.+\.conf)$ + ^(?:server|pool|peer)[\s]+[\S]+.*maxpoll[\s]+(\d+) + 1 + + + /etc/ntp.conf + ^server[\s]+[\S]+[\s]+(.*) + 1 + + + ^/etc/chrony\.(conf|d/.+\.conf)$ + ^(?:server|pool|peer)[\s]+[\S]+[\s]+(.*) + 1 + + + ^/etc/chrony\.(conf|d/.+\.conf)$ + ^(?:server|pool).* + 1 + + + /etc/ntp.conf + ^server.* + 1 + + + + /etc/sysconfig/chronyd + ^\s*OPTIONS=.*[\s'"]-u(?!\s*chrony\b).* + 0 + + + ^/etc/chrony\.(conf|d/.+\.conf)$ + ^[\s]*server.*$ + 1 + + + ^/etc/chrony\.(conf|d/.+\.conf)$ + ^[\s]+pool.*$ + 1 + + + ^/etc/chrony\.(conf|d/.+\.conf)$ + ^[\s]*(?:server|pool)[\s]+.+$ + 1 + + + /etc/ntp.conf + ^([\s]*server[\s]+.+$){2,}$ + 1 + + + /etc/ntp.conf + ^[\s]*server[\s]+.+$ + 1 + + + + / + shosts.equiv + + + /root + ^\.rhosts$ + + + + /home + ^\.rhosts$ + + + /etc + ^hosts\.equiv$ + + + + / + .shosts + + + /etc/xinetd.d/tftp + ^[\s]*server_args[\s]+=[\s]+.*?-s[\s]+([/\.\w]+).*$ + 1 + + + /etc/cups/cupsd.conf + ^[\s]*Browsing[\s]+(?:Off|No) + 1 + + + /etc/cups/cupsd.conf + ^[\s]*BrowseAllow[\s]+(?:none) + 1 + + + /etc/cups/cupsd.conf + ^[\s]*Port[\s]+(\d)+ + 1 + + + /etc/cups/cupsd.conf + ^[\s]*Listen[\s]+(?:localhost|127\.0\.0\.1|::1):(\d)+ + 1 + + + /etc/fstab + ^[\s]*[\S]+[\s]+[\S]+[\s]+cifs[\s]+([\S]+) + 1 + + + /etc/mtab + ^[\s]*[\S]+[\s]+[\S]+[\s]+cifs[\s]+([\S]+) + 1 + + + /etc/samba/smb.conf + ^[\s]*client[\s]+signing[\s]*=[\s]*rhisam + 1 + + + /etc/snmp/snmpd.conf + ^((?!#).)*(public|private).* + 1 + + + /etc/snmp/snmpd.conf + ^[\s]*(com2se|rocommunity|rwcommunity) + 1 + + + /etc/ssh + .*_key$ + oval:ssg-exclude_symlinks__sshd_private_key:ste:1 + oval:ssg-filter_ssh_key_owner_root:ste:1 + oval:ssg-filter_ssh_key_owner_ssh_keys:ste:1 + + + /etc/group + ^ssh_keys:\w+:(\w+):.* + 1 + + + /etc/firewalld/services + ^.*\.xml$ + /service/service[@name='ssh'] + + + /etc/firewalld/services + ^.*\.xml$ + /service/port[@port='22'] + + + /etc/firewalld/zones + ^.*\.xml$ + /zone/service[@name='ssh'] + + + /etc/firewalld/zones + ^.*\.xml$ + /zone/port[@port='22'] + + + /etc/ssh/ssh_config + ^[\s]*RekeyLimit.*$ + 1 + + + ^/etc/ssh/ssh_config\.d/.*\.conf$ + + 1 + + + /etc/profile.d/cc-ssh-strong-rng.csh + ^[\s]*setenv[\s]+SSH_USE_STRONG_RNG[\s]+([\d]+)$ + 1 + + + /etc/profile + ^[\s]*setenv[\s]+SSH_USE_STRONG_RNG.*$ + 1 + + + /etc/profile.d/cc-ssh-strong-rng.sh + ^[\s]*export[\s]+SSH_USE_STRONG_RNG=([\d]+)$ + 1 + + + /etc/profile + ^[\s]*export[\s]+SSH_USE_STRONG_RNG=.*$ + 1 + + + /etc/firewalld/services + ^.*\.xml$ + /service/service[@name='ssh'] + + + /etc/firewalld/services + ^.*\.xml$ + <port.*port="(\d+)" + 1 + + + /etc/firewalld/zones + ^.*\.xml$ + /zone/service[@name='ssh'] + + + /etc/firewalld/zones + ^.*\.xml$ + <port.*port="(\d+)" + 1 + + + /etc/firewalld/zones + + /zone/service[@name='ssh'] + + + /etc/sysconfig/network-scripts + ifcfg-.* + ^ZONE=(.*)$ + 1 + + + /etc/ssh/sshd_config + ^[\s]*(?i)Protocol[\s]+2[\s]*(?:|(?:#.*))?$ + 1 + + + /etc/ssh/sshd_config + ^[ \t]*(?i)Compression(?-i)[ \t]+(.+?)[ \t]*(?:$|#) + 1 + + + /etc/ssh/sshd_config + ^[\s]*(?i)RhostsRSAAuthentication(?-i)[\s]+no[\s]*(?:#.*)?$ + 1 + + + /etc/ssh/sshd_config + + 1 + + + /etc/ssh/sshd_config + ^[\s]*(?i)ClientAliveInterval[\s]+(\d+)[\s]*(?:#.*)?$ + 1 + + + /etc/ssh/sshd_config + ^[\s]*(?i)ClientAliveCountMax[\s]+([\d]+)[\s]*(?:#.*)?$ + 1 + + + /etc/ssh/sshd_config + ^[\s]*(?i)LoginGraceTime[\s]+(\d+)[\s]*(?:#.*)?$ + 1 + + + /etc/ssh/sshd_config + ^[\s]*(?i)MaxAuthTries[\s]+(\d+)[\s]*(?:#.*)?$ + 1 + + + /etc/ssh/sshd_config + ^[\s]*(?i)MaxSessions[\s]+(\d+)[\s]*(?:#.*)?$ + 1 + + + /etc/ssh/sshd_config + (?i)^\s*MaxStartups\s+(\d+):\d+:\d+\s*$ + 1 + + + /etc/ssh/sshd_config + (?i)^\s*MaxStartups\s+\d+:(\d+):\d+\s*$ + 1 + + + /etc/ssh/sshd_config + (?i)^\s*MaxStartups\s+\d+:\d+:(\d+)\s*$ + 1 + + + oval:ssg-var_sshd_config_ciphers:var:1 + + + /etc/ssh/sshd_config + ^[\s]*(?i)Ciphers(?-i)[\s]+([\w,-@]+)+[\s]*(?:#.*)?$ + 1 + + + oval:ssg-var_sshd_config_macs:var:1 + + + /etc/ssh/sshd_config + ^[\s]*(?i)MACs(?-i)[\s]+([\w,-@]+)+[\s]*(?:#.*)?$ + 1 + + + /etc/ssh/sshd_config + ^[ \t]*(?i)UsePrivilegeSeparation(?-i)[ \t]+(.+?)[ \t]*(?:$|#) + 1 + + + ^/etc/sssd/(sssd|conf\.d/.*)\.conf$ + ^[\s]*\[sssd](?:[^\n\[]*\n+)+?[\s]*certificate_verification\s*=\s*ocsp_dgst\s*=\s*(\w+)$ + 1 + + + ^/etc/sssd/(sssd|conf\.d/.*)\.conf$ + ^\s*\[sssd\].*(?:\n\s*[^[\s].*)*\n\s*services[ \t]*=[ \t]*(.*)$ + 1 + + + /etc/sssd/sssd.conf + ^[\s]*\[pam](?:[^\n\[]*\n+)+?[\s]*pam_cert_auth[\s]*=[\s]*(?i)true\s*$ + 1 + + + /etc/pam.d/smartcard-auth + ^\s*auth.*?pam_sss\.so(.*) + 1 + + + /etc/pam.d/system-auth + ^\s*auth.*?pam_sss\.so(.*) + 1 + + + /etc/sssd/sssd.conf + ^[\s]*\[nss](?:[^\n\[]*\n+)+?[\s]*memcache_timeout[\s]*=[\s]*(\d+)$ + 1 + + + /etc/sssd/sssd.conf + ^[\s]*\[pam](?:[^\n\[]*\n+)+?[\s]*offline_credentials_expiration[\s]*=[\s]*1\s*(?:#.*)?$ + 1 + + + /etc/sssd/sssd.conf + ^[\s]*cache_credentials\s*=\s*(\w+)\s*(?:#.*)?$ + 1 + + + ^/etc/sssd/(sssd|conf\.d/.*)\.conf$ + ^\s*\[sssd\].*(?:\n\s*[^[\s].*)*\n\s*user[ \t]*=[ \t]*(\S*) + 1 + + + /etc/sssd/sssd.conf + ^[\s]*\[ssh](?:[^\n\[]*\n+)+?[\s]*ssh_known_hosts_timeout[\s]*=[\s]*(\d+)$ + 1 + + + /etc/sssd/sssd.conf + ^[\s]*\[domain\/[^]]*](?:[^\n[\]]*\n+)+?[\s]*ldap_tls_cacertdir[\s]+=[\s]+([^\s]+)[\s]*$ + 1 + + + /etc/sssd/sssd.conf + ^[\s]*\[domain\/[^]]*]([^\n\[\]]*\n+)+?[\s]*ldap_tls_reqcert[ \t]*=[ \t]*((?i)demand)[ \t]*$ + 1 + + + /etc/sssd/sssd.conf + ^[\s]*\[domain\/[^]]*]([^\n\[\]]*\n+)+?[\s]*ldap_id_use_start_tls[ \t]*=[ \t]*((?i)true)[ \t]*$ + 1 + + + /etc/usbguard/usbguard-daemon.conf + ^[ \t]*AuditBackend=(.+?)[ \t]*(?:$|#) + 1 + + + ^/etc/usbguard/usbguard-daemon.conf + + + xorg-x11-server-Xorg + + + xorg-x11-server-utils + + + xorg-x11-server-Xwayland + + + /etc/systemd/system/default.target + + + /etc/pam.d/fingerprint-auth + + + /etc/pam.d/password-auth + + + /etc/pam.d/postlogin + + + /etc/pam.d/smartcard-auth + + + /etc/pam.d/system-auth + + + + ^/etc/issue(\.d/.*)?$ + ^(.*)$ + 1 + + + + /etc/motd + ^(.*)$ + 1 + + + /etc/dconf/db/gdm.d/ + ^.*$ + ^\[org/gnome/login-screen\]([^\n]*\n+)+?banner-message-enable=true$ + 1 + + + /etc/dconf/db/gdm.d/locks/ + ^.*$ + ^/org/gnome/login-screen/banner-message-enable$ + 1 + + + /etc/dconf/db/gdm.d/locks/ + ^.*$ + ^/org/gnome/login-screen/banner-message-text$ + 1 + + + /etc/dconf/db/gdm.d/ + ^.*$ + ^banner-message-text=[\s]*'*(.*?)'$ + 1 + + + /etc/pam.d/sudo + ^.*pam_succeed_if.*$ + 1 + + + /etc/pam.d/postlogin + ^\s*session\s+required\s+pam_lastlog\.so(?:\s+[\w=]+)*\s+showfailed(\s|$) + 1 + + + /etc/pam.d/postlogin + ^\s*session\s+.*\s+pam_lastlog\.so(?:\s+[\w=]+)*\s+silent(\s|$) + 1 + + + /etc/pam.d/login + ^\s*session\s+required\s+pam_namespace\.so\s*$ + 1 + + + /etc/pam.d/password-auth|/etc/pam.d/system-auth|/etc/security/faillock.conf + ^\s*(?:auth.*pam_faillock\.so.*)?dir\s*=\s*(\S+) + 1 + + + + + + + oval:ssg-var_account_password_selinux_faillock_dir_collector:var:1 + + + /etc/pam.d/system-auth + + 1 + + + /etc/pam.d/password-auth + + 1 + + + /etc/security/faillock.conf + ^\s*audit + 1 + + + /etc/pam.d/password-auth + + 1 + + + /etc/pam.d/system-auth + + 1 + + + /etc/pam.d/system-auth + ^\s*password\s+(?:(?:sufficient)|(?:required))\s+pam_unix\.so.*remember=([0-9]*).*$ + 1 + + + /etc/pam.d/system-auth + ^\s*password\s+(?:(?:requisite)|(?:required))\s+pam_pwhistory\.so.*remember=([0-9]*).*$ + 1 + + + ^/etc/pam.d/system-auth$ + + 1 + + + ^/etc/pam.d/password-auth$ + + 1 + + + ^/etc/pam.d/system-auth$ + + 1 + + + ^/etc/pam.d/system-auth$ + + 1 + + + ^/etc/pam.d/password-auth$ + + 1 + + + ^/etc/pam.d/password-auth$ + + 1 + + + ^/etc/pam.d/system-auth$ + + 1 + + + ^/etc/pam.d/password-auth$ + + 1 + + + ^/etc/security/faillock.conf$ + + 1 + + + ^/etc/pam.d/system-auth$ + + 1 + + + ^/etc/pam.d/password-auth$ + + 1 + + + ^/etc/pam.d/system-auth$ + + 1 + + + ^/etc/pam.d/system-auth$ + + 1 + + + ^/etc/pam.d/password-auth$ + + 1 + + + ^/etc/pam.d/password-auth$ + + 1 + + + ^/etc/pam.d/system-auth$ + + 1 + + + ^/etc/pam.d/password-auth$ + + 1 + + + ^/etc/security/faillock.conf$ + + 1 + + + /etc/pam.d/system-auth + + 1 + oval:ssg-state_pam_faillock_dir_parameter_not_default_value:ste:1 + + + /etc/pam.d/password-auth + + 1 + oval:ssg-state_pam_faillock_dir_parameter_not_default_value:ste:1 + + + /etc/pam.d/system-auth + + 1 + + + oval:ssg-var_faillock_dir_set_both_preauth_authfail_system_auth:var:1 + + + oval:ssg-var_faillock_dir_set_both_preauth_authfail_password_auth:var:1 + + + /etc/security/faillock.conf + + 1 + + + ^/etc/pam.d/system-auth$ + + 1 + + + ^/etc/pam.d/password-auth$ + + 1 + + + ^/etc/pam.d/system-auth$ + + 1 + + + ^/etc/pam.d/system-auth$ + + 1 + + + ^/etc/pam.d/password-auth$ + + 1 + + + ^/etc/pam.d/password-auth$ + + 1 + + + ^/etc/security/faillock.conf$ + + 1 + + + ^/etc/pam.d/system-auth$ + + 1 + + + ^/etc/pam.d/password-auth$ + + 1 + + + ^/etc/pam.d/system-auth$ + + 1 + + + ^/etc/pam.d/system-auth$ + + 1 + + + ^/etc/pam.d/password-auth$ + + 1 + + + ^/etc/pam.d/password-auth$ + + 1 + + + ^/etc/pam.d/system-auth$ + + 1 + + + ^/etc/pam.d/password-auth$ + + 1 + + + ^/etc/security/faillock.conf$ + + 1 + + + ^/etc/pam.d/system-auth$ + + 1 + + + ^/etc/pam.d/password-auth$ + + 1 + + + ^/etc/pam.d/system-auth$ + + 1 + + + ^/etc/pam.d/system-auth$ + + 1 + + + ^/etc/pam.d/password-auth$ + + 1 + + + ^/etc/pam.d/password-auth$ + + 1 + + + ^/etc/pam.d/system-auth$ + + 1 + + + ^/etc/pam.d/password-auth$ + + 1 + + + ^/etc/security/faillock.conf$ + + 1 + + + /etc/pam.d/password-auth + ^password[\s]*requisite[\s]*pam_pwquality\.so + 1 + + + /etc/pam.d/system-auth + ^password[\s]*requisite[\s]*pam_pwquality\.so + 1 + + + /etc/pam.d/password-auth + ^\s*password\s+(?:(?:required)|(?:requisite))\s+pam_pwquality\.so.*retry=([0-9]*).*$ + 1 + + + /etc/pam.d/system-auth + ^\s*password\s+(?:(?:required)|(?:requisite))\s+pam_pwquality\.so.*retry=([0-9]*).*$ + 1 + + + /etc/security/pwquality.conf + ^[\s]*retry[\s]*=[\s]*(\d+)(?:[\s]|$) + 1 + + + /etc/libuser.conf + ^[\s]*crypt_style[\s]+=[\s]+(?i)sha512[\s]*$ + 1 + + + + /etc/login.defs + .*\n[^#]*(ENCRYPT_METHOD\s+\w+)\s*\n + 1 + + + oval:ssg-variable_last_encrypt_method_instance_value:var:1 + + + /etc/pam.d/password-auth + ^[\s]*password[\s]+(?:(?:required)|(?:sufficient))[\s]+pam_unix\.so[\s]+.*sha512.*$ + 1 + + + /etc/pam.d/system-auth + ^[\s]*password[\s]+(?:(?:required)|(?:sufficient))[\s]+pam_unix\.so[\s]+.*sha512.*$ + 1 + + + /etc/login.defs + ^\s*SHA_CRYPT_MIN_ROUNDS\s* + 1 + + + /etc/login.defs + ^\s*SHA_CRYPT_MIN_ROUNDS\s+(\d+)\s*$ + 1 + + + /etc/login.defs + ^\s*SHA_CRYPT_MAX_ROUNDS\s* + 1 + + + /etc/login.defs + ^\s*SHA_CRYPT_MAX_ROUNDS\s+(\d+)\s*$ + 1 + + + ^/etc/systemd/system.conf(\.d/.*\.conf)?$ + ^[\s]*CtrlAltDelBurstAction[\s]*=[\s]*none$ + 1 + + + /etc/systemd/system/ctrl-alt-del.target + + + /etc/default/grub + ^\s*GRUB_CMDLINE_LINUX="(?:.*\s)?systemd\.confirm_spawn(?:=(?:1|yes|true|on))?(?:\s.*)?"$ + 1 + + + /etc/default/grub + ^\s*GRUB_CMDLINE_LINUX_DEFAULT=".*systemd\.confirm_spawn=(?:1|yes|true|on).*$ + 1 + + + /usr/lib/systemd/system/emergency.service + ^ExecStart=\-/usr/lib/systemd/systemd-sulogin-shell[\s]+emergency + 1 + + + /usr/lib/systemd/system/emergency.target + ^Requires=.*emergency\.service + 1 + + + + /etc/systemd/system + ^emergency.service$ + + + + /etc/systemd/system + ^emergency.target$ + + + /usr/lib/systemd/system/rescue.service + ^ExecStart=\-.*/usr/lib/systemd/systemd-sulogin-shell[ ]+rescue + 1 + + + + ^/etc/bashrc$|^/etc/profile\.d/.*$ + if \[ "\$PS1" \]; then\n\s+parent=\$\(ps -o ppid= -p \$\$\)\n\s+name=\$\(ps -o comm= -p \$parent\)\n\s+case "\$name" in sshd\|login\) exec tmux ;; esac\nfi + 1 + + + /etc/tmux.conf + ^\s*set\s+-g\s+lock-after-time\s+(\d+)\s*(?:#.*)?$ + 1 + + + /etc/tmux.conf + ^\s*set\s+-g\s+lock-command\s+vlock\s*(?:#.*)?$ + 1 + + + /etc/shells + tmux\s*$ + 1 + + + ^/etc/opensc.*\.conf$ + ^[\s]+card_drivers[\s]+=[\s]+(\S+);$ + 1 + + + ^/etc/opensc.*\.conf$ + ^[\s]+force_card_driver[\s]+=[\s]+(\S+);$ + 1 + + + .* + + + oval:ssg-variable_count_of_all_uids:var:1 + + + /etc/passwd + ^([a-zA-Z0-9_.-]+?): + 1 + oval:ssg-state_default_os_user:ste:1 + + + /etc/group + ^.+:.+:(\d+):.*$ + 1 + + + oval:ssg-variable_count_of_all_group_ids:var:1 + + + /etc/group + ^(.+):.+ + 1 + + + oval:ssg-variable_count_of_all_group_names:var:1 + + + /etc/default/useradd + ^\s*INACTIVE\s*=\s*(\d+)\s*$ + 1 + + + /etc/passwd + ^([^:]+):.*$ + 1 + + + oval:ssg-variable_count_of_all_usernames_from_etc_passwd:var:1 + + + /etc/login.defs + ^(?:.*\n)*\s*[^#]*(PASS_MAX_DAYS\s+\d+)\s*\n + 1 + + + oval:ssg-variable_last_pass_max_days_instance_value:var:1 + + + + /etc/login.defs + .*\n[^#]*(PASS_MIN_DAYS\s+\d+)\s*\n + 1 + + + oval:ssg-variable_last_pass_min_days_instance_value:var:1 + + + + /etc/login.defs + .*\n[^#]*(PASS_MIN_LEN\s+\d+)\s*\n + 1 + + + oval:ssg-variable_last_pass_min_len_instance_value:var:1 + + + .* + + + .* + + + + /etc/login.defs + .*\n[^#]*(PASS_WARN_AGE\s+\d+)\s*\n + 1 + + + oval:ssg-variable_last_pass_warn_age_instance_value:var:1 + + + .* + + + .* + oval:ssg-state_accounts_password_all_shadowed_has_no_password:ste:1 + oval:ssg-state_accounts_password_all_shadowed_sha512:ste:1 + + + ^/etc/pam.d/password-auth$ + ^\s*password\s+(?:(?:sufficient)|(?:required))\s+pam_unix\.so.*rounds=([0-9]*).*$ + 1 + + + oval:ssg-var_password_pam_unix_rounds:var:1 + + + ^/etc/pam.d/system-auth$ + ^\s*password\s+(?:(?:sufficient)|(?:required))\s+pam_unix\.so.*rounds=([0-9]*).*$ + 1 + + + oval:ssg-var_password_pam_unix_rounds:var:1 + + + /etc/group + ^[^:]+:[^:]+:([0-9]+): + 1 + + + /etc/passwd + ^[^:]+:[^:]+:[0-9]+:([0-9]+): + 1 + + + ^/etc/pam.d/(system|password)-auth$ + ^[^#]*\bnullok\b.*$ + 1 + + + /etc/shadow + ^[^:]+::.*$ + 1 + + + /etc/group + ^\+.*$ + 1 + + + /etc/passwd + ^\+.*$ + 1 + + + /etc/shadow + ^\+.*$ + 1 + + + + /home + ^\.netrc$ + + + /etc/passwd + ^(?!root:)[^:]*:[^:]*:0 + 1 + + + /etc/passwd + ^root:.+:\d+:(\d+).+ + 1 + + + /etc/securetty + ^.*$ + 1 + + + /etc/securetty + ^$ + 1 + + + + /etc/login.defs + .*(?:^|\n)\s*(UID_MIN[\s]+[\d]+)\s*(?:$|\n) + 1 + + + + /etc/login.defs + .*(?:^|\n)\s*(SYS_UID_MIN[\s]+[\d]+)\s*(?:$|\n) + 1 + + + + /etc/login.defs + .*(?:^|\n)\s*(SYS_UID_MAX[\s]+[\d]+)\s*(?:$|\n) + 1 + + + /etc/passwd + ^(?!root).*:x:([\d]+):[\d]+:[^:]*:[^:]*:(?!\/usr\/sbin\/nologin|\/sbin\/nologin|\/bin\/sync|\/sbin\/shutdown|\/sbin\/halt|\/bin\/false|\/usr\/bin\/false).*$ + 1 + + + /etc/securetty + ^ttyS[0-9]+$ + 1 + + + /etc/securetty + ^vc/[0-9]+$ + 1 + + + /etc/pam.d/su + ^[\s]*auth[\s]+required[\s]+pam_wheel\.so[\s]+use_uid$ + 1 + + + /etc/login.defs + ^[\s]*(?i)CREATE_HOME(?-i)[\s]+yes[\s]*(?:#.*)?$ + 1 + + + /etc/login.defs + ^[\s]*(?i)FAIL_DELAY(?-i)[\s]+([^#\s]*) + 1 + + + /etc/security/limits.conf + ^[\s]*\*[\s]+(?:(?:hard)|(?:-))[\s]+maxlogins[\s]+(\d+)\s*$ + 1 + + + /etc/security/limits.d + ^.*\.conf$ + ^[\s]*\*[\s]+(?:(?:hard)|(?:-))[\s]+maxlogins[\s]+(\d+)\s*$ + 1 + + + /etc/security/limits.d + ^.*\.conf$ + ^[\s]*\*[\s]+(?:(?:hard)|(?:-))[\s]+maxlogins + 1 + + + /tmp/tmp-inst + + + + /etc/security/namespace.conf + ^\s*/tmp\s+/tmp/tmp-inst/\s+level\s+root,adm$ + 1 + + + /var/tmp/tmp-inst + + + + /etc/security/namespace.conf + ^\s*/var/tmp\s+/var/tmp/tmp-inst/\s+level\s+root,adm$ + 1 + + + /etc/profile + ^[\s]*declare[\s]+-xr[\s]+TMOUT=([\w$]+).*$ + 1 + + + /etc/profile.d + ^.*\.sh$ + ^[\s]*declare[\s]+-xr[\s]+TMOUT=([\w$]+).*$ + 1 + + + nobody + oval:ssg-state_accounts_user_dot_group_ownership_interactive_gids:ste:1 + + + + + ^\..* + + + nobody + oval:ssg-state_accounts_user_dot_no_world_writable_programs_interactive_uids:ste:1 + + + + / + ^.*$ + oval:ssg-state_world_writable_programs:ste:1 + + + + + + + 1 + + + nobody + oval:ssg-state_accounts_user_dot_user_ownership_interactive_uids:ste:1 + + + + + ^\..* + + + nobody + oval:ssg-state_accounts_user_interactive_home_directory_defined_uids:ste:1 + + + nobody + oval:ssg-state_accounts_user_interactive_home_directory_exists_uids:ste:1 + + + + + + + oval:ssg-var_accounts_user_interactive_home_directory_exists_dirs_count_fs:var:1 + + + oval:ssg-var_accounts_user_interactive_home_directory_exists_dirs_count:var:1 + + + .* + oval:ssg-state_accounts_users_home_files_groupownership_interactive_gids:ste:1 + oval:ssg-state_accounts_users_home_files_groupownership_user_list:ste:1 + + + + + .* + + + .* + oval:ssg-state_accounts_users_home_files_ownership_interactive_uids:ste:1 + oval:ssg-state_accounts_users_home_files_ownership_user_list:ste:1 + + + + + .* + + + .* + oval:ssg-state_accounts_users_home_files_permissions_interactive_uids:ste:1 + oval:ssg-state_accounts_users_home_files_permissions_user_list:ste:1 + + + + + + + + + + ^[^\.].* + + + .* + oval:ssg-state_file_groupownership_home_directories_interactive_uids:ste:1 + oval:ssg-state_file_permissions_groupownership_user_list:ste:1 + + + + + + + .* + oval:ssg-state_file_ownership_home_directories_interactive_uids:ste:1 + oval:ssg-state_file_ownership_home_directories_user_list:ste:1 + + + + + + + oval:ssg-var_file_ownership_home_directories_uids_count:var:1 + + + .* + oval:ssg-state_file_permissions_home_directories_interactive_uids:ste:1 + oval:ssg-state_file_permissions_home_files_permissions_user_list:ste:1 + + + + + + + nobody + oval:ssg-state_file_permissions_home_dirs_interactive_uids:ste:1 + + + + + + + + PATH + + + + + oval:ssg-state_accounts_root_path_dirs_wrong_perms:ste:1 + oval:ssg-state_accounts_root_path_dirs_symlink:ste:1 + + + + PATH + + + /etc/bashrc + ^[\s]*umask[\s]+([^#\s]*) + 1 + + + oval:ssg-var_etc_bashrc_umask_as_number:var:1 + + + /etc/csh.cshrc + ^[\s]*(?i)UMASK(?-i)[\s]+([^#\s]*) + 1 + + + oval:ssg-var_etc_csh_cshrc_umask_as_number:var:1 + + + /etc/login.defs + ^[\s]*UMASK[\s]+([^#\s]*) + 1 + + + oval:ssg-var_etc_login_defs_umask_as_number:var:1 + + + /etc/profile + ^[\s]*umask[\s]+([^#\s]*) + 1 + + + oval:ssg-var_etc_profile_umask_as_number:var:1 + + + nobody + oval:ssg-state_accounts_umask_interactive_users_interactive_uids:ste:1 + + + + ^\..* + ^[\s]*umask\s* + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+task,never[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+task,never[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^\-e\s+2\s*$ + 1 + + + /etc/audit/audit.rules + ^\-e\s+2\s*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^\-w[\s]+/etc/selinux/[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ + 1 + + + /etc/audit/audit.rules + ^\-w[\s]+/etc/selinux/[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^\-w[\s]+/etc/issue[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^\-w[\s]+/etc/issue\.net[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^\-w[\s]+/etc/hosts[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^\-w[\s]+/etc/sysconfig/network[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ + 1 + + + /etc/audit/audit.rules + ^\-w[\s]+/etc/issue[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ + 1 + + + /etc/audit/audit.rules + ^\-w[\s]+/etc/issue\.net[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ + 1 + + + /etc/audit/audit.rules + ^\-w[\s]+/etc/hosts[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ + 1 + + + /etc/audit/audit.rules + ^\-w[\s]+/etc/sysconfig/network[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^\-w\s+/var/run/utmp\s+\-p\s+wa\b.*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^\-w\s+/var/log/btmp\s+\-p\s+wa\b.*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^\-w\s+/var/log/wtmp\s+\-p\s+wa\b.*$ + 1 + + + /etc/audit/audit.rules + ^\-w\s+/var/run/utmp\s+\-p\s+wa\b.*$ + 1 + + + /etc/audit/audit.rules + ^\-w\s+/var/log/btmp\s+\-p\s+wa\b.*$ + 1 + + + /etc/audit/audit.rules + ^\-w\s+/var/log/wtmp\s+\-p\s+wa\b.*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^\-w[\s]+/etc/sudoers[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ + 1 + + + /etc/audit/audit.rules + ^\-w[\s]+/etc/sudoers[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^\-w[\s]+/etc/sudoers\.d/[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ + 1 + + + /etc/audit/audit.rules + ^\-w[\s]+/etc/sudoers\.d/[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+arch=b32[\s]+-S[\s]+execve[\s]+-C[\s]+uid!=euid[\s]+-F[\s]+euid=0[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+arch=b64[\s]+-S[\s]+execve[\s]+-C[\s]+uid!=euid[\s]+-F[\s]+euid=0[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+arch=b32[\s]+-S[\s]+execve[\s]+-C[\s]+uid!=euid[\s]+-F[\s]+euid=0[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+arch=b64[\s]+-S[\s]+execve[\s]+-C[\s]+uid!=euid[\s]+-F[\s]+euid=0[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+arch=b32[\s]+-S[\s]+execve[\s]+-C[\s]+gid!=egid[\s]+-F[\s]+egid=0[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+arch=b64[\s]+-S[\s]+execve[\s]+-C[\s]+gid!=egid[\s]+-F[\s]+egid=0[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+arch=b32[\s]+-S[\s]+execve[\s]+-C[\s]+gid!=egid[\s]+-F[\s]+egid=0[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+arch=b64[\s]+-S[\s]+execve[\s]+-C[\s]+gid!=egid[\s]+-F[\s]+egid=0[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^\-w[\s]+/etc/sudoers[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^\-w[\s]+/etc/sudoers\.d/[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ + 1 + + + /etc/audit/audit.rules + ^\-w[\s]+/etc/sudoers[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ + 1 + + + /etc/audit/audit.rules + ^\-w[\s]+/etc/sudoers\.d/[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^\-f\s+2\s*$ + 1 + + + /etc/audit/audit.rules + ^\-f\s+2\s*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^\-w[\s]+/etc/group[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^\-w[\s]+/etc/passwd[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^\-w[\s]+/etc/gshadow[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^\-w[\s]+/etc/shadow[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^\-w[\s]+/etc/security/opasswd[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ + 1 + + + /etc/audit/audit.rules + ^\-w[\s]+/etc/group[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ + 1 + + + /etc/audit/audit.rules + ^\-w[\s]+/etc/passwd[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ + 1 + + + /etc/audit/audit.rules + ^\-w[\s]+/etc/gshadow[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ + 1 + + + /etc/audit/audit.rules + ^\-w[\s]+/etc/shadow[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ + 1 + + + /etc/audit/audit.rules + ^\-w[\s]+/etc/security/opasswd[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + /etc/audit/audit.rules + + 1 + + + + /var/log/audit + + oval:ssg-state_group_owner_not_root_var_log_audit_directories:ste:1 + + + + /var/log/audit + + oval:ssg-state_group_owner_not_root_var_log_audit_directories-non_root:ste:1 + + + + + + oval:ssg-state_group_owner_not_root_var_log_audit_directories:ste:1 + + + + + + + + oval:ssg-state_owner_not_root_var_log_audit_directories:ste:1 + + + /var/log/audit + + oval:ssg-state_owner_not_root_var_log_audit_directories:ste:1 + + + + + oval:ssg-state_not_mode_0700:ste:1 + + + /var/log/audit + + oval:ssg-state_not_mode_0700:ste:1 + + + + oval:ssg-state_group_owner_not_root_var_log_audit:ste:1 + + + /var/log/audit/audit.log + oval:ssg-state_group_owner_not_root_var_log_audit:ste:1 + + + + /var/log/audit + + oval:ssg-state_owner_not_root_root_var_log_audit:ste:1 + + + + /var/log/audit + ^.*$ + oval:ssg-state_owner_not_root_root_var_log_audit:ste:1 + + + + /var/log/audit + + oval:ssg-state_owner_not_root_var_log_audit-non_root:ste:1 + + + + /var/log/audit + ^.*$ + oval:ssg-state_owner_not_root_var_log_audit-non_root:ste:1 + + + + oval:ssg-state_owner_not_root_var_log_audit:ste:1 + + + /var/log/audit/audit.log + oval:ssg-state_owner_not_root_var_log_audit:ste:1 + + + + oval:ssg-state_not_mode_0600:ste:1 + + + /var/log/audit/audit.log + oval:ssg-state_not_mode_0600:ste:1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+umount[\s]+|([\s]+|[,])umount([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+umount[\s]+|([\s]+|[,])umount([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+delete_module[\s]+|([\s]+|[,])delete_module([\s]+|[,]))).*(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295))\s+(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+delete_module[\s]+|([\s]+|[,])delete_module([\s]+|[,]))).*(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295))\s+(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+delete_module[\s]+|([\s]+|[,])delete_module([\s]+|[,]))).*(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295))\s+(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+delete_module[\s]+|([\s]+|[,])delete_module([\s]+|[,]))).*(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295))\s+(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+finit_module[\s]+|([\s]+|[,])finit_module([\s]+|[,]))).*(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295))\s+(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+finit_module[\s]+|([\s]+|[,])finit_module([\s]+|[,]))).*(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295))\s+(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+finit_module[\s]+|([\s]+|[,])finit_module([\s]+|[,]))).*(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295))\s+(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+finit_module[\s]+|([\s]+|[,])finit_module([\s]+|[,]))).*(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295))\s+(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+init_module[\s]+|([\s]+|[,])init_module([\s]+|[,]))).*(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295))\s+(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+init_module[\s]+|([\s]+|[,])init_module([\s]+|[,]))).*(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295))\s+(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+init_module[\s]+|([\s]+|[,])init_module([\s]+|[,]))).*(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295))\s+(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+init_module[\s]+|([\s]+|[,])init_module([\s]+|[,]))).*(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295))\s+(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + + / + [a-z]+ + oval:ssg-state_setuid_or_setgid_set:ste:1 + oval:ssg-state_dev_proc_sys_dirs:ste:1 + + + oval:ssg-variable_count_of_suid_sgid_binaries_on_system:var:1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a always,exit (?:-F path=([\S]+) )+-F auid>=1000 -F auid!=(?:4294967295|unset)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + oval:ssg-state_proper_audit_rule_but_for_unprivileged_command:ste:1 + + + /etc/audit/audit.rules + ^[\s]*-a always,exit (?:-F path=([\S]+) )+-F auid>=1000 -F auid!=(?:4294967295|unset)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + oval:ssg-state_proper_audit_rule_but_for_unprivileged_command:ste:1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+arch=b32.*(-S[\s]+adjtimex[\s]+|([\s]+|[,])adjtimex([\s]+|[,])).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+arch=b64.*(-S[\s]+adjtimex[\s]+|([\s]+|[,])adjtimex([\s]+|[,])).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+arch=b32.*(-S[\s]+adjtimex[\s]+|([\s]+|[,])adjtimex([\s]+|[,])).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+arch=b64.*(-S[\s]+adjtimex[\s]+|([\s]+|[,])adjtimex([\s]+|[,])).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+arch=b32[\s]+(-S[\s]+clock_settime[\s]+|([\s]+|[,])clock_settime([\s]+|[,]))-F[\s]+a0=(?:0x)?0[\s]+(?:-F[\s]+key=|-k[\s]+)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+arch=b64[\s]+(-S[\s]+clock_settime[\s]+|([\s]+|[,])clock_settime([\s]+|[,]))-F[\s]+a0=(?:0x)?0[\s]+(?:-F[\s]+key=|-k[\s]+)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+arch=b32[\s]+(-S[\s]+clock_settime[\s]+|([\s]+|[,])clock_settime([\s]+|[,]))-F[\s]+a0=(?:0x)?0[\s]+(?:-F[\s]+key=|-k[\s]+)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+arch=b64[\s]+(-S[\s]+clock_settime[\s]+|([\s]+|[,])clock_settime([\s]+|[,]))-F[\s]+a0=(?:0x)?0[\s]+(?:-F[\s]+key=|-k[\s]+)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+arch=b32.*(-S[\s]+settimeofday[\s]+|([\s]+|[,])settimeofday([\s]+|[,])).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+arch=b64.*(-S[\s]+settimeofday[\s]+|([\s]+|[,])settimeofday([\s]+|[,])).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+arch=b32.*(-S[\s]+settimeofday[\s]+|([\s]+|[,])settimeofday([\s]+|[,])).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+arch=b64.*(-S[\s]+settimeofday[\s]+|([\s]+|[,])settimeofday([\s]+|[,])).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+arch=b32.*(-S[\s]+stime[\s]+|([\s]+|[,])stime([\s]+|[,])).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+arch=b32.*(-S[\s]+stime[\s]+|([\s]+|[,])stime([\s]+|[,])).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-w[\s]+\/etc\/localtime[\s]+-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-w[\s]+\/etc\/localtime[\s]+-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ + 1 + + + /etc/audit/audisp-remote.conf + ^[ ]*remote_server[ ]+=[ ]+(\S+)[ ]*$ + 1 + + + /etc/audit/audisp-remote.conf + ^[ ]*disk_full_action[ ]+=[ ]+(\S+)[ ]*$ + 1 + + + /etc/audit/audisp-remote.conf + ^[ ]*transport[ ]+=[ ]+KRB5[ ]*$ + 1 + + + /etc/audit/audisp-remote.conf + ^[ ]*network_failure_action[ ]+=[ ]+(\S+)[ ]*$ + 1 + + + /etc/audit/plugins.d/syslog.conf + ^[ ]*active[ ]+=[ ]+yes[ ]*$ + 1 + + + /etc/audit/auditd.conf + ^[ ]*disk_error_action[ ]+=[ ]+(\S+)[ ]*$ + 1 + + + /etc/audit/auditd.conf + ^[ ]*disk_error_action[ ]+=[ ]+(\S+)[ ]*$ + 1 + + + /etc/audit/auditd.conf + ^[ ]*disk_full_action[ ]+=[ ]+(\S+)[ ]*$ + 1 + + + /etc/audit/auditd.conf + ^[ ]*disk_full_action[ ]+=[ ]+(\S+)[ ]*$ + 1 + + + /etc/audit/auditd.conf + ^[ ]*action_mail_acct[ ]+=[ ]+(\S+)[ ]*$ + 1 + + + /etc/audit/auditd.conf + ^[ ]*admin_space_left_action[ ]+=[ ]+(\S+)[ ]*$ + 1 + + + /etc/audit/auditd.conf + ^[ ]*flush[ ]+=[ ]+(\S+)[ ]*$ + 1 + + + /etc/audit/auditd.conf + ^[ ]*max_log_file[ ]+=[ ]+(\d+)[ ]*$ + 1 + + + /etc/audit/auditd.conf + ^[ ]*max_log_file_action[ ]+=[ ]+(\S+)[ ]*$ + 1 + + + /etc/audit/auditd.conf + ^[ ]*max_log_file_action[ ]+=[ ]+(\S+)[ ]*$ + 1 + + + /etc/audit/auditd.conf + ^[ ]*num_logs[ ]+=[ ]+(\d+)[ ]*$ + 1 + + + /etc/audit/auditd.conf + ^[\s]*space_left[\s]+=[\s]+(\d+)[\s]*$ + 1 + + + /etc/audit/auditd.conf + ^[ ]*space_left_action[ ]+=[ ]+(\S+)[ ]*$ + 1 + + + /etc/audit/auditd.conf + ^[\s]*space_left[\s]+=[\s]+(\d+)%[\s]*$ + 1 + + + /etc/audit/auditd.conf + ^[ \t]*(?i)name_format(?-i)[ \t]*=[ \t]*(.+?)[ \t]*(?:$|#) + 1 + + + /etc/audit/auditd.conf + ^[ \t]*(?i)overflow_action(?-i)[ \t]*=[ \t]*(.+?)[ \t]*(?:$|#) + 1 + + + /etc/audit/rules.d/10-base-config.rules + (?:.*\n)* + 1 + + + ^/usr/share/doc/audit(?:-\d.\d.\d)?/rules/10-base-config.rules + (?:.*\n)* + 1 + + + /etc/audit/rules.d/11-loginuid.rules + (?:.*\n)* + 1 + + + ^/usr/share/doc/audit(?:-\d.\d.\d)?/rules/11-loginuid.rules + (?:.*\n)* + 1 + + + /etc/audit/rules.d/30-ospp-v42.rules + (?:.*\n)* + 1 + + + ^/usr/share/doc/audit(?:-\d.\d.\d)?/rules/30-ospp-v42.rules + (?:.*\n)* + 1 + + + /etc/audit/rules.d/43-module-load.rules + (?:.*\n)* + 1 + + + ^/usr/share/doc/audit(?:-\d.\d.\d)?/rules/43-module-load.rules + (?:.*\n)* + 1 + + + + + ^CONFIG_RANDOM_TRUST_CPU=(y|Y)$ + 1 + + + /boot/grub2/grubenv + ^kernelopts=(.*)$ + 1 + + + .* + + + /boot/grub2/grub.cfg + ^[\s]*set[\s]+superusers="(?i)\b(?!(?:root|admin|administrator)\b)(\w+)"$ + 1 + + + /boot/grub2/grub.cfg + ^[ \t]*set root=(.+?)[ \t]*(?:$|#) + 1 + + + ^/boot/grub2/grub.cfg + + + /boot/grub2/user.cfg + ^[\s]*GRUB2_PASSWORD=grub\.pbkdf2\.sha512.*$ + 1 + + + .* + + + /boot/efi/EFI/redhat/grub.cfg + ^[\s]*set[\s]+superusers="(?i)\b(?!(?:root|admin|administrator)\b)(\w+)"$ + 1 + + + /boot/efi/EFI/redhat/user.cfg + ^[\s]*GRUB2_PASSWORD=grub\.pbkdf2\.sha512.*$ + 1 + + + /boot/efi/EFI/redhat/grub.cfg + ^[ \t]*set root=(.+?)[ \t]*(?:$|#) + 1 + + + ^/boot/efi/EFI/redhat/grub.cfg + + + /etc/zipl.conf + ^\s*image\s*=.*$ + 1 + + + /boot/bootmap + + + /etc/zipl.conf + + + ^/boot/loader/entries/.*\.conf$ + + + ^/boot/loader/entries/.*\.conf + ^options (.*)$ + 1 + + + ^/etc/kernel/cmdline + ^(.*)$ + 1 + + + /etc/logwatch/conf/logwatch.conf + ^[\s]HostLimit[\s]*=[\s]*no[\s]*$ + 1 + + + /etc/logwatch/conf/logwatch.conf + ^[\s]SplitHosts[\s]*=[\s]*yes[\s]*$ + 1 + + + /etc/rsyslog.conf + ^[\s]*cron\.\*[\s]+/var/log/cron\s*(?:#.*)?$ + 1 + + + /etc/rsyslog.d + ^.*$ + ^[\s]*cron\.\*[\s]+/var/log/cron\s*(?:#.*)?$ + 1 + + + /etc/rsyslog.conf + ^\$ActionSendStreamDriverAuthMode x509/name$ + 1 + + + /etc/rsyslog.d + ^.*conf$ + ^\$ActionSendStreamDriverAuthMode x509/name$ + 1 + + + /etc/rsyslog.conf + ^\$ActionSendStreamDriverMode 1$ + 1 + + + /etc/rsyslog.d + ^.*conf$ + ^\$ActionSendStreamDriverMode 1$ + 1 + + + /etc/rsyslog.conf + ^\$DefaultNetstreamDriver gtls$ + 1 + + + /etc/rsyslog.d + ^.*conf$ + ^\$DefaultNetstreamDriver gtls$ + 1 + + + /etc/rsyslog.conf + ^(?:include\([\n\s]*file="([^\s;]+)".*|\$IncludeConfig[\s]+([^\s;]+))$ + 1 + + + oval:ssg-var_rfg_include_config_regex:var:1 + + + oval:ssg-var_rfg_syslog_config:var:1 + + + + oval:ssg-object_var_rfg_include_config_regex:obj:1 + oval:ssg-object_var_rfg_syslog_config:obj:1 + + + + + ^[^(\s|#|\$)]+[\s]+.*[\s]+-?(/+[^:;\s]+);*\.*$ + 1 + oval:ssg-state_groupownership_ignore_include_paths:ste:1 + + + + + + /etc/rsyslog.conf + ^(?:include\([\n\s]*file="([^\s;]+)".*|\$IncludeConfig[\s]+([^\s;]+))$ + 1 + + + oval:ssg-var_rfo_include_config_regex:var:1 + + + oval:ssg-var_rfo_syslog_config:var:1 + + + + oval:ssg-object_var_rfo_include_config_regex:obj:1 + oval:ssg-object_var_rfo_syslog_config:obj:1 + + + + + ^[^(#|\$)]+[\s]+.*[\s]+-?(/+[^:;\s]+);*\.*$ + 1 + oval:ssg-state_owner_ignore_include_paths:ste:1 + + + + + + /etc/rsyslog.conf + ^(?:include\([\n\s]*file="([^\s;]+)".*|\$IncludeConfig[\s]+([^\s;]+))$ + 1 + oval:ssg-state_permissions_ignore_hidden_paths:ste:1 + + + oval:ssg-var_rfp_include_config_regex:var:1 + + + oval:ssg-var_rfp_syslog_config:var:1 + + + + oval:ssg-object_var_rfp_include_config_regex:obj:1 + oval:ssg-object_var_rfp_syslog_config:obj:1 + + + + + ^[^(\s|#|\$)]+[\s]+.*[\s]+-?(/+[^:;\s]+);*\.*$ + 1 + oval:ssg-state_permissions_ignore_include_paths:ste:1 + + + + + + ^/etc/rsyslog\.(conf|d/.+\.conf)$ + ^.*auth\.\*.*$ + 1 + + + ^/etc/rsyslog\.(conf|d/.+\.conf)$ + ^.*authpriv\.\*.*$ + 1 + + + ^/etc/rsyslog\.(conf|d/.+\.conf)$ + ^.*daemon\.\*.*$ + 1 + + + /etc/logrotate.conf + ^\s*daily[\s#]*$ + 1 + + + /etc/logrotate.conf + ^\s*(weekly|monthly|yearly)[\s#]*$ + 1 + + + /etc/cron.daily/logrotate + ^[\s]*/usr/sbin/logrotate[\s\S]*/etc/logrotate.conf$ + 1 + + + /etc/rsyslog.conf + ^[\s]*\$((?:Input(?:TCP|RELP)|UDP)ServerRun|ModLoad[\s]+(imtcp|imudp|imrelp)) + 1 + + + /etc/rsyslog.conf + ^\*\.\*[\s]+(?:@|\:omrelp\:) + 1 + + + /etc/rsyslog.d + ^.+\.conf$ + ^\*\.\*[\s]+(?:@|\:omrelp\:) + 1 + + + + ^/etc/rsyslog\.(conf|d/.+\.conf)$ + ^\s*action\((?i)type(?-i)="omfwd"(.+?)\) + 0 + + + ^/etc/rsyslog\.(conf|d/.+\.conf)$ + ^\s*global\(DefaultNetstreamDriverCAFile="(.+?)"\)\s*\n + 0 + + + /etc/resolv.conf + ^[\s]*nameserver[\s]+([0-9\.]+)$ + 1 + + + /etc/nsswitch.conf + ^\s*hosts\s*:\s*.*dns.*$ + 1 + + + /etc/resolv.conf + + + /etc/sysconfig/network-scripts + ifcfg-.* + ^[\s]*DHCP_HOSTNAME[\s]*=.*$ + 1 + + + ^/etc/dhclient.*\.conf$ + ^[\s]*send[\s]+host-name.*$ + 1 + + + /etc/dhcp + ^.*$ + ^[\s]*send[\s]+host-name.*$ + 1 + + + /etc/sysconfig/network + ^[\s]*NOZEROCONF[\s]*=[\s]*yes + 1 + + + ^/etc/polkit-1/localauthority/20-org.d/.*$ + ^\[.*\]\n\s*Identity=default\n\s*Action=org\.freedesktop\.NetworkManager\.\*\n\s*ResultAny=no\n\s*ResultInactive=no\n\s*(ResultActive=auth_admin)\n*\s*$ + 1 + + + ^.*$ + oval:ssg-state_promisc:ste:1 + + + /etc/firewalld/firewalld.conf + ^DefaultZone=drop$ + 1 + + + /etc/sysconfig/network-scripts + ifcfg-.* + ^IPV6_DEFAULTGW=.+$ + 1 + + + /etc/sysconfig/network-scripts + ifcfg-.* + ^IPV6_PRIVACY=rfc3041$ + 1 + + + /etc/sysconfig/network-scripts + ifcfg-.* + ^IPV6ADDR=.+$ + 1 + + + /etc/modprobe.d + ^.*\.conf$ + ^\s*options\s+ipv6\s+.*disable=1.*$ + 1 + + + /etc/netconfig + ^udp6\s+tpi_clts\s+v\s+inet6\s+udp\s+-\s+-$ + 1 + + + /etc/netconfig + ^tcp6\s+tpi_cots_ord\s+v\s+inet6\s+tcp\s+-\s+-$ + 1 + + + ^wl.*$ + + + + / + + oval:ssg-state_uid_is_not_root_and_world_writable:ste:1 + + + + / + + oval:ssg-state_world_writable_and_not_sticky:ste:1 + + + + / + + oval:ssg-state_uid_is_user_and_world_writable:ste:1 + + + + / + + oval:ssg-state_gid_is_user_and_world_writable:ste:1 + + + /boot + ^System\.map.*$ + + + + / + ^.*$ + oval:ssg-state_file_permissions_unauthorized_sgid_sgid_set:ste:1 + oval:ssg-state_file_permissions_unauthorized_sgid_filepaths:ste:1 + + + + .* + .* + .* + .* + .* + + + + + / + ^.*$ + oval:ssg-state_file_permissions_unauthorized_sgid_sgid_set:ste:1 + + + + / + ^.*$ + oval:ssg-state_file_permissions_unauthorized_suid_suid_set:ste:1 + oval:ssg-state_file_permissions_unauthorized_suid_filepaths:ste:1 + + + + .* + .* + .* + .* + .* + + + + + / + ^.*$ + oval:ssg-state_file_permissions_unauthorized_suid_suid_set:ste:1 + + + + / + ^.*$ + oval:ssg-state_file_permissions_unauthorized_world_write:ste:1 + oval:ssg-state_file_permissions_unauthorized_world_write_exclude_special_selinux_files:ste:1 + oval:ssg-state_file_permissions_unauthorized_world_write_exclude_proc:ste:1 + oval:ssg-state_file_permissions_unauthorized_world_write_exclude_sys:ste:1 + + + + / + .* + oval:ssg-state_file_permissions_ungroupowned:ste:1 + + + /etc/group + ^[^:]+:[^:]*:([\d]+):[^:]*$ + 1 + + + .* + + + + / + .* + oval:ssg-file_permissions_unowned_userid_list_match:ste:1 + + + ^\/s?bin|^\/usr\/s?bin|^\/usr\/local\/s?bin + ^.*$ + oval:ssg-state_groupowner_system_commands_dirs_not_root_or_system_account:ste:1 + + + ^\/(|s)bin|^\/usr\/(|local\/)(|s)bin|^\/usr\/libexec + + oval:ssg-state_owner_binaries_not_root:ste:1 + + + ^\/(|s)bin|^\/usr\/(|local\/)(|s)bin|^\/usr\/libexec + ^.*$ + oval:ssg-state_owner_binaries_not_root:ste:1 + + + ^\/(|s)bin|^\/usr\/(|local\/)(|s)bin|^\/usr\/libexec + ^.*$ + oval:ssg-state_perms_binary_files_nogroupwrite_noworldwrite:ste:1 + oval:ssg-state_perms_binary_files_symlink:ste:1 + + + /etc/default/grub + ^[ \t]*GRUB_CMDLINE_LINUX=([^#]*).*$ + 1 + + + ^/\w.*$ + oval:ssg-state_local_nodev:ste:1 + + + /etc/fstab + ^[\s]*/tmp[\s]+/var/tmp[\s]+.*bind.*$ + 1 + + + ^/var/tmp$ + + + /etc/mtab + ^[\s]*/tmp[\s]+/var/tmp[\s]+.*bind.*$ + 1 + + + ^/tmp$ + + + /etc/systemd/coredump.conf + ^\s*\[Coredump\].*(?:\n\s*[^[\s].*)*\n^[ \t]*(?i)ProcessSizeMax(?-i)[ \t]*=[ \t]*(.+?)[ \t]*(?:$|#) + 1 + + + /etc/systemd/coredump.conf + ^\s*\[Coredump\].*(?:\n\s*[^[\s].*)*\n^[ \t]*(?i)Storage(?-i)[ \t]*=[ \t]*(.+?)[ \t]*(?:$|#) + 1 + + + /etc/security/limits.conf + ^[\s]*\*[\s]+(?:hard|-)[\s]+core[\s]+([\d]+) + 1 + + + /etc/security/limits.d + ^.*\.conf$ + ^[\s]*\*[\s]+(?:hard|-)[\s]+core[\s]+([\d]+) + 1 + + + /etc/security/limits.d + ^.*\.conf$ + ^[\s]*\*[\s]+(?:hard|-)[\s]+core + 1 + + + /etc/init.d/functions + ^[\s]*(?i)UMASK(?-i)[\s]+([^#\s]*) + 1 + + + oval:ssg-var_etc_init_d_functions_umask_as_number:var:1 + + + /etc/sysctl.conf + ^[\s]*kernel.exec-shield[\s]*=[\s]*1[\s]*$ + 1 + + + kernel.exec-shield + + + /boot/grub2/grub.cfg + [\s]*noexec[\s]*=[\s]*off + 1 + + + /proc/cpuinfo + ^flags[\s]+:.*[\s]+nx[\s]+.*$ + 1 + + + /var/log/messages + ^.+protection: disabled.+ + 1 + + + /proc/cmdline + .+noexec[0-9]*=off.+ + 1 + + + kernel-PAE + + + /proc/cpuinfo + ^flags[\s]+:.*[\s]+pae[\s]+.*[\s]+nx[\s]+.*$ + 1 + + + /etc/sysconfig/kernel + ^\s*DEFAULTKERNEL[\s]*=[\s]*kernel-PAE$ + 1 + + + /etc/default/grub + ^[\s]*GRUB_CMDLINE_LINUX.*(selinux|enforcing)=0.*$ + 1 + + + /etc/grub2.cfg + ^.*(selinux|enforcing)=0.*$ + 1 + + + /etc/grub.d + ^.*$ + ^.*(selinux|enforcing)=0.*$ + 1 + + + + /dev + ^.*$ + oval:ssg-state_block_or_char_device_file:ste:1 + + + + oval:ssg-state_selinux_dev_device_t:ste:1 + + + + oval:ssg-state_selinux_dev_unlabeled_t:ste:1 + + + + /proc + ^.*$ + oval:ssg-state_selinux_confinement_of_daemons:ste:1 + + + /etc/selinux/config + ^SELINUXTYPE=([\w]*)[\s]*$ + 1 + + + /etc/selinux/config + ^SELINUX=(.*)$ + 1 + + + /proc/cpuinfo + ^flags\s+:\s+(.*)$ + 1 + + + /proc/sys/kernel/osrelease + ^.*\.(.*)$ + 1 + + + /etc/dconf/db/gdm + + + ^/etc/dconf/db/gdm.d/.* + + + oval:ssg-var_dconf_gdm_db_modified_time:var:1 + + + /etc/dconf/db/local + + + ^/etc/dconf/db/local.d/.* + + + oval:ssg-var_dconf_local_db_modified_time:var:1 + + + /etc/dconf/profile/user + ^user-db:user\nsystem-db:local$ + 1 + + + /etc/dconf/db/gdm.d/ + ^.*$ + ^\[org/gnome/login-screen\]([^\n]*\n+)+?disable-restart-buttons=true$ + 1 + + + /etc/dconf/db/gdm.d/locks/ + ^.*$ + ^/org/gnome/login-screen/disable-restart-buttons$ + 1 + + + /etc/dconf/db/gdm.d/ + ^.*$ + ^\[org/gnome/login-screen\]([^\n]*\n+)+?disable-user-list=true$ + 1 + + + /etc/dconf/db/gdm.d/locks/ + ^.*$ + ^/org/gnome/login-screen/disable-user-list$ + 1 + + + /etc/dconf/db/gdm.d/ + ^.*$ + ^\[org/gnome/login-screen\]([^\n]*\n+)+?enable-smartcard-authentication=true$ + 1 + + + /etc/dconf/db/gdm.d/locks/ + ^.*$ + ^/org/gnome/login-screen/enable-smartcard-authentication$ + 1 + + + /etc/dconf/db/gdm.d/ + ^.*$ + ^\[org/gnome/login-screen\]([^\n]*\n+)+?allowed-failures=3$ + 1 + + + /etc/dconf/db/gdm.d/locks/ + ^.*$ + ^/org/gnome/login-screen/allowed-failures$ + 1 + + + /etc/gdm/custom.conf + ^\[daemon]([^\n]*\n+)+?AutomaticLoginEnable=[Ff]alse$ + 1 + + + /etc/gdm/custom.conf + ^\[daemon]([^\n]*\n+)+?TimedLoginEnable=[Ff]alse$ + 1 + + + /etc/gdm/custom.conf + ^\s*\[xdmcp\].*(?:\n\s*[^[\s].*)*\n^\s*Enable[ \t]*=[ \t]*(.+?)[ \t]*(?:$|#) + 1 + + + ^/etc/gdm/custom.conf + + + /etc/dconf/db/local.d/ + ^.*$ + ^\[org/gnome/desktop/media-handling\]([^\n]*\n+)+?automount=false$ + 1 + + + /etc/dconf/db/local.d/locks/ + ^.*$ + ^/org/gnome/desktop/media-handling/automount$ + 1 + + + /etc/dconf/db/local.d/ + ^.*$ + ^\[org/gnome/desktop/media-handling\]([^\n]*\n+)+?automount-open=false$ + 1 + + + /etc/dconf/db/local.d/locks/ + ^.*$ + ^/org/gnome/desktop/media-handling/automount-open$ + 1 + + + /etc/dconf/db/local.d/ + ^.*$ + ^\[org/gnome/desktop/media-handling\]([^\n]*\n+)+?autorun-never=true$ + 1 + + + /etc/dconf/db/local.d/locks/ + ^.*$ + ^/org/gnome/desktop/media-handling/autorun-never$ + 1 + + + /etc/dconf/db/local.d/ + ^.*$ + ^\[org/gnome/desktop/thumbnailers\]([^\n]*\n+)+?disable-all=true$ + 1 + + + /etc/dconf/db/local.d/locks/ + ^.*$ + ^/org/gnome/desktop/thumbnailers/disable-all$ + 1 + + + /etc/dconf/db/local.d/ + ^.*$ + ^\[org/gnome/nm-applet\]([^\n]*\n+)+?disable-wifi-create=true$ + 1 + + + /etc/dconf/db/local.d/locks/ + ^.*$ + ^/org/gnome/nm-applet/disable-wifi-create$ + 1 + + + /etc/dconf/db/local.d/ + ^.*$ + ^\[org/gnome/nm-applet\]([^\n]*\n+)+?suppress-wireless-networks-available=true$ + 1 + + + /etc/dconf/db/local.d/locks/ + ^.*$ + ^/org/gnome/nm-applet/suppress-wireless-networks-available$ + 1 + + + /etc/dconf/db/local.d/ + ^.*$ + ^\[org/gnome/Vino\]([^\n]*\n+)+?authentication-methods=\['vnc'\]$ + 1 + + + /etc/dconf/db/local.d/locks/ + ^.*$ + ^/org/gnome/Vino/authentication-methods$ + 1 + + + /etc/dconf/db/local.d/ + ^.*$ + ^\[org/gnome/Vino\]([^\n]*\n+)+?require-encryption=true$ + 1 + + + /etc/dconf/db/local.d/locks/ + ^.*$ + ^/org/gnome/Vino/require-encryption$ + 1 + + + /etc/dconf/db/local.d/ + ^.*$ + ^\[org/gnome/desktop/screensaver\]([^\n]*\n+)+?idle-activation-enabled=true$ + 1 + + + /etc/dconf/db/local.d/locks/ + ^.*$ + ^/org/gnome/desktop/screensaver/idle-activation-enabled$ + 1 + + + /etc/dconf/db/local.d/locks/ + ^.*$ + ^/org/gnome/desktop/screensaver/idle-activation-enabled$ + 1 + + + /etc/dconf/db/local.d/ + ^.*$ + ^\[org/gnome/desktop/session\]([^\n]*\n+)+?idle-delay=uint32[\s][0-9]*$ + 1 + + + /etc/dconf/db/local.d/ + ^.*$ + ^idle-delay[\s=]*uint32[\s]([^=\s]*) + 1 + + + /etc/dconf/db/local.d/ + ^.*$ + ^\[org/gnome/desktop/screensaver\]([^\n]*\n+)+?lock-delay=uint32[\s][0-9]*$ + 1 + + + /etc/dconf/db/local.d/ + ^.*$ + ^lock-delay[\s=]*uint32[\s]([^=\s]*) + 1 + + + /etc/dconf/db/local.d/ + ^.*$ + ^\[org/gnome/desktop/screensaver\]([^\n]*\n+)+?lock-enabled=true$ + 1 + + + /etc/dconf/db/local.d/locks/ + ^.*$ + ^/org/gnome/desktop/screensaver/lock-enabled$ + 1 + + + /etc/dconf/db/local.d/locks/ + ^.*$ + ^/org/gnome/desktop/screensaver/lock-enabled$ + 1 + + + /etc/dconf/db/local.d/ + ^.*$ + ^\[org/gnome/desktop/screensaver\]([^\n]*\n+)+?picture-uri=string \'\'$ + 1 + + + /etc/dconf/db/local.d/locks/ + ^.*$ + ^/org/gnome/desktop/screensaver/picture-uri$ + 1 + + + /etc/dconf/db/local.d/ + ^.*$ + ^\[org/gnome/desktop/screensaver\]([^\n]*\n+)+?show-full-name-in-top-bar=false$ + 1 + + + /etc/dconf/db/local.d/locks/ + ^.*$ + ^/org/gnome/desktop/screensaver/show-full-name-in-top-bar$ + 1 + + + /etc/dconf/db/local.d/locks/ + ^.*$ + ^/org/gnome/desktop/screensaver/lock-delay$ + 1 + + + /etc/dconf/db/local.d/locks/ + ^.*$ + ^/org/gnome/desktop/session/idle-delay$ + 1 + + + /etc/dconf/db/local.d/ + ^.*$ + ^\[org/gnome/settings-daemon/plugins/media-keys\]([^\n]*\n+)+?logout[\s]*=[\s]*''$ + 1 + + + /etc/dconf/db/local.d/locks/ + ^.*$ + ^/org/gnome/settings-daemon/plugins/media-keys/logout$ + 1 + + + /etc/dconf/db/local.d/ + ^.*$ + ^\[org/gnome/system/location\]([^\n]*\n+)+?enabled=false$ + 1 + + + /etc/dconf/db/local.d/locks/ + ^.*$ + ^/org/gnome/system/location/enabled$ + 1 + + + /etc/dconf/db/local.d/ + ^.*$ + ^\[org/gnome/clocks\]([^\n]*\n+)+?geolocation=false$ + 1 + + + /etc/dconf/db/local.d/locks/ + ^.*$ + ^/org/gnome/clocks/geolocation$ + 1 + + + /etc/dconf/db/local.d/ + ^.*$ + ^\[org/gnome/settings-daemon/plugins/power\]([^\n]*\n+)+?active=false$ + 1 + + + /etc/dconf/db/local.d/locks/ + ^.*$ + ^/org/gnome/settings-daemon/plugins/power/active$ + 1 + + + /etc/named.conf + ^\s*include\s+"/etc/crypto-policies/back-ends/bind.config"\s*;\s*$ + 1 + + + /etc/crypto-policies/state/current + + + /etc/crypto-policies/config + + + oval:ssg-variable_crypto_policies_config_file_timestamp:var:1 + + + /etc/crypto-policies/config + ^(?!#)(\S+)$ + 1 + + + /etc/crypto-policies/state/current + ^(?!#)(\S+)$ + 1 + + + /etc/crypto-policies/back-ends/nss.config + + + /etc/crypto-policies/back-ends/gnutls.config + \+VERS-ALL:-VERS-DTLS0\.9:-VERS-SSL3\.0:-VERS-TLS1\.0:-VERS-TLS1\.1:-VERS-DTLS1\.0 + 1 + + + oval:ssg-var_symlink_kerberos_crypto_policy_configuration:var:1 + + + /etc/krb5.conf.d/crypto-policies + + + /etc/crypto-policies/back-ends/krb5.config + + + /etc/ipsec.conf + ^\s*include\s+/etc/crypto-policies/back-ends/libreswan.config\s*(?:#.*)?$ + 1 + + + /etc/pki/tls/openssl.cnf + ^\s*\[\s*crypto_policy\s*\]\s*\n*\s*\.include\s*(?:=\s*)?/etc/crypto-policies/back-ends/opensslcnf.config\s*$ + 1 + + + /etc/crypto-policies/back-ends/opensslcnf.config + ^\s*(?:TLS\.)?(?i)MinProtocol\s*=\s*TLSv(\S*) + 1 + + + /etc/crypto-policies/back-ends/opensslcnf.config + ^\s*(?:DTLS\.)?(?i)MinProtocol\s*=\s*DTLSv(\S*) + 1 + + + crypto-policies + + + /etc/sysconfig/sshd + ^\s*(?i)CRYPTO_POLICY\s*=.*$ + 1 + + + /etc/crypto-policies/back-ends/opensslcnf.config + ^(?:.*\n)*\s*Ciphersuites\s*=\s*(.+?)[ \t]*(?:$|#) + 1 + + + /etc/ssh/ssh_config.d/02-ospp.conf + ^[ \t]*Match[\s]+(.+?)[ \t]*(?:$|#) + 1 + + + /etc/ssh/ssh_config.d/02-ospp.conf + ^Match final all(?:.* +)*?\s*RekeyLimit[\s]+(.+?)[ \t]*(?:$|#) + 1 + + + /etc/ssh/ssh_config.d/02-ospp.conf + ^Match final all(?:.* +)*?\s*GSSAPIAuthentication[\s]+(.+?)[ \t]*(?:$|#) + 1 + + + /etc/ssh/ssh_config.d/02-ospp.conf + ^Match final all(?:.* +)*?\s*Ciphers[\s]+(.+?)[ \t]*(?:$|#) + 1 + + + /etc/ssh/ssh_config.d/02-ospp.conf + ^Match final all(?:.* +)*?\s*PubkeyAcceptedKeyTypes[\s]+(.+?)[ \t]*(?:$|#) + 1 + + + /etc/ssh/ssh_config.d/02-ospp.conf + ^Match final all(?:.* +)*?\s*MACs[\s]+(.+?)[ \t]*(?:$|#) + 1 + + + /etc/ssh/ssh_config.d/02-ospp.conf + ^Match final all(?:.* +)*?\s*KexAlgorithms[\s]+(.+?)[ \t]*(?:$|#) + 1 + + + /etc/crypto-policies/back-ends/openssh.config + ^Ciphers.*$ + 1 + + + /etc/crypto-policies/back-ends/opensshserver.config + ^(?!#).*(-oCiphers=[^\s']+).*$ + 1 + + + /etc/crypto-policies/back-ends/opensshserver.config + ^(?:.*\n)*\s*CRYPTO_POLICY=(.+?)[ \t]*(?:$|#) + 1 + + + /etc/crypto-policies/back-ends/openssh.config + ^MACs.*$ + 1 + + + /etc/crypto-policies/back-ends/opensshserver.config + ^(?!#).*(-oMACs=\S+).+$ + 1 + + + /etc/profile.d/openssl-rand.sh + SHA-256 + + + /etc/selinux/config + ^[\s]*SELINUX[\s]*=[\s]*enforcing[\s]*$ + 1 + + + McAfeeVSEForLinux + + + MFErt + + + MFEcma + + + /opt/NAI/LinuxShield/engine/dat + ^.*\.dat$ + + + oval:ssg-variable_mcafee_dat_files_mtime:var:1 + + + ^mfetpd.*$ + 0 + + + /opt/McAfee/accm/bin + accm + + + /opt/McAfee/auditengine/bin + auditmanager + + + /etc/dracut.conf.d/40-fips.conf + ^\s*add_dracutmodules\+="\s*(\w*)\s*"\s*(?:#.*)?$ + 1 + + + oval:ssg-var_system_crypto_policy:var:1 + + + /boot/grub2/grubenv + fips=1 + 1 + + + /etc/system-fips + + + crypto.fips_enabled + + + /etc/aide.conf + ^@@define[\s]DBDIR[\s]+(/.*)$ + 1 + + + /etc/aide.conf + ^database_out=file:@@{DBDIR}/([a-z.]+)$ + 1 + + + /etc/aide.conf + ^database=file:@@{DBDIR}/([a-z.]+)$ + 1 + + + + + + + + + /etc/aide.conf + ^\/usr\/sbin\/auditctl\s+([^\n]+)$ + 1 + + + /etc/aide.conf + ^/usr/sbin/auditd\s+([^\n]+)$ + 1 + + + /etc/aide.conf + ^/usr/sbin/ausearch\s+([^\n]+)$ + 1 + + + /etc/aide.conf + ^/usr/sbin/aureport\s+([^\n]+)$ + 1 + + + /etc/aide.conf + ^/usr/sbin/autrace\s+([^\n]+)$ + 1 + + + /etc/aide.conf + ^/usr/sbin/audispd\s+([^\n]+)$ + 1 + + + /etc/aide.conf + ^/usr/sbin/rsyslogd\s+([^\n]+)$ + 1 + + + /etc/aide.conf + ^/usr/sbin/augenrules\s+([^\n]+)$ + 1 + + + /etc/crontab + ^(([0-9]*[\s]*[0-9]*[\s]*\*[\s]*\*[\s]*(\*|([0-7]|mon|tue|wed|thu|fri|sat|sun)|[0-7]-[0-7]))|@(hourly|daily|weekly))[\s]*root[\s]*\/usr\/sbin\/aide[\s]*\-\-check.*$ + 1 + + + /etc/cron.d + ^.*$ + ^(([0-9]*[\s]*[0-9]*[\s]*\*[\s]*\*[\s]*(\*|([0-7]|mon|tue|wed|thu|fri|sat|sun)|[0-7]-[0-7]))|@(hourly|daily|weekly))[\s]*root[\s]*\/usr\/sbin\/aide[\s]*\-\-check.*$ + 1 + + + /var/spool/cron/root + ^(([0-9]*[\s]*[0-9]*[\s]*\*[\s]*\*[\s]*(\*|([0-7]|mon|tue|wed|thu|fri|sat|sun)|[0-7]-[0-7]))|@(hourly|daily|weekly))[\s]*(root)?[\s]*\/usr\/sbin\/aide[\s]*\-\-check.*$ + 1 + + + ^/etc/cron.(daily|weekly)$ + ^.*$ + ^\s*\/usr\/sbin\/aide[\s]*\-\-check.*$ + 1 + + + /etc/crontab + ^.*/usr/sbin/aide[\s]*\-\-check.*\|.*/bin/mail[\s]*-s[\s]*".*"[\s]*.+@.+$ + 1 + + + /var/spool/cron/root + ^.*/usr/sbin/aide[\s]*\-\-check.*\|.*/bin/mail[\s]*-s[\s]*".*"[\s]*.+@.+$ + 1 + + + ^/etc/cron.(d|daily|weekly|monthly)$ + ^.*$ + ^.*/usr/sbin/aide[\s]*\-\-check.*\|.*/bin/mail[\s]*-s[\s]*".*"[\s]*.+@.+$ + 1 + + + /etc/aide.conf + ^[A-Z][a-zA-Z_]*[\s]*=[\s]*.*(sha1|rmd160|sha256|whirlpool|tiger|haval|gost|crc32).*$ + 0 + + + /etc/aide.conf + ^[A-Z][A-Za-z_]*[\s]*=[\s]*([a-zA-Z0-9\+]*)$ + 1 + + + /etc/aide.conf + ^(?!ALLXTRAHASHES)[A-Z][a-zA-Z_]*[\s]*=[\s]*([a-zA-Z0-9\+]*)$ + 1 + + + /etc/aide.conf + ^(?!ALLXTRAHASHES)[A-Z][a-zA-Z_]*[\s]*=[\s]*([a-zA-Z0-9\+]*)$ + 1 + + + + .* + .* + .* + .* + .* + ^/(bin|sbin|lib|lib64|usr)/.+$ + oval:ssg-state_files_fail_md5_hash:ste:1 + + + + .* + .* + .* + .* + .* + .* + oval:ssg-state_files_fail_user_ownership:ste:1 + + + + .* + .* + .* + .* + .* + .* + oval:ssg-state_files_fail_group_ownership:ste:1 + + + + .* + .* + .* + .* + .* + .* + oval:ssg-state_files_fail_mode:ste:1 + + + /usr/bin/sudo + + + /etc/group + + 1 + + + /etc/sudoers + ^(?!#).*[\s]+\!authenticate.*$ + 1 + + + /etc/sudoers.d + ^.*$ + ^(?!#).*[\s]+\!authenticate.*$ + 1 + + + /etc/sudoers + ^(?!#).*[\s]+NOPASSWD[\s]*\:.*$ + 1 + + + /etc/sudoers.d + ^.*$ + ^(?!#).*[\s]+NOPASSWD[\s]*\:.*$ + 1 + + + ^/etc/sudoers(\.d/.*)?$ + ^[\s]*Defaults[\s]+timestamp_timeout=([-]?[\d]+)$ + 1 + + + ^/etc/sudoers(\.d/.*)?$ + ^\s*ALL\s+ALL\=\(ALL\)\s+ALL\s*$ + 1 + + + ^/etc/sudoers(\.d/.*)?$ + ^\s*ALL\s+ALL\=\(ALL\:ALL\)\s+ALL\s* + 1 + + + /etc/sudoers + ^(?!(#|vdsm.*)).*[\s]+NOPASSWD[\s]*\:.*$ + 1 + + + /etc/sudoers.d + ^.*$ + ^(?!(#|vdsm.*)).*[\s]+NOPASSWD[\s]*\:.*$ + 1 + + + /etc/sudoers + ^#includedir[\s]+(.*)$ + 1 + + + /etc/sudoers + ^[#@]include[\s]+.*$ + 1 + + + /etc/sudoers + ^@includedir[\s]+.*$ + 1 + + + /etc/sudoers + ^[#@]includedir[\s]+.*$ + 1 + + + /etc/sudoers.d/ + .* + ^[#@]include(?:dir)?[\s]+.*$ + 1 + + + ^/etc/sudoers(\.d/.*)?$ + ^(?:\s*[^#=]+)=(?:\s*(?:\([^\)]+\))?\s*(?!\s*\()[^,\s]+(?:[ \t]+[^,\s]+)+[ \t]*,)*(\s*(?:\([^\)]+\))?\s*(?!\s*\()[^,\s]+[ \t]*(?:,|$)) + 1 + + + ^/etc/sudoers(\.d/.*)?$ + ^(?:\s*[^#=]+)=(?:\s*(?:\([^\)]+\))?\s*(?!\s*\()[^,!\n][^,\n]+,)*\s*(?:\([^\)]+\))?\s*(?!\s*\()(!\S+).* + 1 + + + ^/etc/sudoers(\.d/.*)?$ + ^\s*((?!root\b)[\w]+)\s*(\w+)\s*=\s*(.*,)?\s*\([\w\s]*\b(root|ALL)\b[\w\s]*\) + 1 + + + ^/etc/sudoers(\.d/.*)?$ + ^\s*((?!root\b)[\w]+)\s*(\w+)\s*=\s*(.*,)?\s*[^\(\s] + 1 + + + ^/etc/sudoers(\.d/.*)?$ + ^Defaults !targetpw$\r?\n + 1 + + + ^/etc/sudoers(\.d/.*)?$ + ^Defaults !rootpw$\r?\n + 1 + + + ^/etc/sudoers(\.d/.*)?$ + ^Defaults !runaspw$\r?\n + 1 + + + ^/etc/sudoers(\.d/.*)?$ + ^Defaults targetpw$\r?\n + 1 + + + ^/etc/sudoers(\.d/.*)?$ + ^Defaults rootpw$\r?\n + 1 + + + ^/etc/sudoers(\.d/.*)?$ + ^Defaults runaspw$\r?\n + 1 + + + /etc/yum.conf + ^\s*clean_requirements_on_remove\s*=\s*(1|True|yes)\s*$ + 1 + + + /etc/dnf/automatic.conf + ^\s*\[commands\].*(?:\n\s*[^[\s].*)*\n^\s*apply_updates[ \t]*=[ \t]*(.+?)[ \t]*(?:$|#) + 1 + + + ^/etc/dnf/automatic.conf + + + /etc/dnf/automatic.conf + ^\s*\[commands\].*(?:\n\s*[^[\s].*)*\n^\s*upgrade_type[ \t]*=[ \t]*(.+?)[ \t]*(?:$|#) + 1 + + + ^/etc/dnf/automatic.conf + + + /etc/yum.conf + ^\s*gpgcheck\s*=\s*1\s*$ + 1 + + + /etc/yum.conf + ^\s*localpkg_gpgcheck\s*=\s*(1|True|yes)\s*$ + 1 + + + /etc/yum.repos.d + .* + ^\s*gpgcheck\s*=\s*0\s*$ + 1 + + + /etc/yum.conf + ^\s*repo_gpgcheck\s*=\s*(1|True|yes)\s*$ + 1 + + + gpg-pubkey + + + /etc/security/pwquality.conf + ^\s*dcredit[\s]*=[\s]*(-?\d+)(?:[\s]|$) + 1 + + + /etc/security/pwquality.conf + ^\s*dictcheck[\s]*=[\s]*(\d+)(?:[\s]|$) + 1 + + + /etc/security/pwquality.conf + ^\s*difok[\s]*=[\s]*(\d+)(?:[\s]|$) + 1 + + + /etc/security/pwquality.conf + ^[\s]*local_users_only[\s]*$ + 1 + + + /etc/security/pwquality.conf + ^[\s]*enforce_for_root[\s]*$ + 1 + + + /etc/security/pwquality.conf + ^\s*lcredit[\s]*=[\s]*(-?\d+)(?:[\s]|$) + 1 + + + /etc/security/pwquality.conf + ^\s*maxclassrepeat[\s]*=[\s]*(\d+)(?:[\s]|$) + 1 + + + /etc/security/pwquality.conf + ^\s*maxrepeat[\s]*=[\s]*(\d+)(?:[\s]|$) + 1 + + + /etc/security/pwquality.conf + ^\s*minclass[\s]*=[\s]*(\d+)(?:[\s]|$) + 1 + + + /etc/security/pwquality.conf + ^\s*minlen[\s]*=[\s]*(\d+)(?:[\s]|$) + 1 + + + /etc/security/pwquality.conf + ^\s*ocredit[\s]*=[\s]*(-?\d+)(?:[\s]|$) + 1 + + + /etc/security/pwquality.conf + ^\s*ucredit[\s]*=[\s]*(-?\d+)(?:[\s]|$) + 1 + + + + /etc/audit/rules.d/30-ospp-v42-3-access-failed.rules + ^.*$ + 1 + + + + /etc/audit/rules.d/30-ospp-v42-3-access-success.rules + ^.*$ + 1 + + + + /etc/audit/rules.d/10-base-config.rules + ^.*$ + 1 + + + + /etc/audit/rules.d/30-ospp-v42-1-create-failed.rules + ^.*$ + 1 + + + + /etc/audit/rules.d/30-ospp-v42-1-create-success.rules + ^.*$ + 1 + + + + /etc/audit/rules.d/30-ospp-v42-4-delete-failed.rules + ^.*$ + 1 + + + + /etc/audit/rules.d/30-ospp-v42-4-delete-success.rules + ^.*$ + 1 + + + + /etc/audit/rules.d/11-loginuid.rules + ^.*$ + 1 + + + + /etc/audit/rules.d/30-ospp-v42-2-modify-failed.rules + ^.*$ + 1 + + + + /etc/audit/rules.d/30-ospp-v42-2-modify-success.rules + ^.*$ + 1 + + + + /etc/audit/rules.d/43-module-load.rules + ^.*$ + 1 + + + + /etc/audit/rules.d/30-ospp-v42.rules + ^.*$ + 1 + + + + /etc/audit/rules.d/30-ospp-v42-6-owner-change-failed.rules + ^.*$ + 1 + + + + /etc/audit/rules.d/30-ospp-v42-6-owner-change-success.rules + ^.*$ + 1 + + + + /etc/audit/rules.d/30-ospp-v42-5-perm-change-failed.rules + ^.*$ + 1 + + + + /etc/audit/rules.d/30-ospp-v42-5-perm-change-success.rules + ^.*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/sbin\/init(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/sbin\/init(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/sbin\/poweroff(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/sbin\/poweroff(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/sbin\/reboot(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/sbin\/reboot(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/sbin\/shutdown(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/sbin\/shutdown(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+chmod[\s]+|([\s]+|[,])chmod([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+chmod[\s]+|([\s]+|[,])chmod([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+chmod[\s]+|([\s]+|[,])chmod([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+chmod[\s]+|([\s]+|[,])chmod([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+chown[\s]+|([\s]+|[,])chown([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+chown[\s]+|([\s]+|[,])chown([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+chown[\s]+|([\s]+|[,])chown([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+chown[\s]+|([\s]+|[,])chown([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+fchmod[\s]+|([\s]+|[,])fchmod([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+fchmod[\s]+|([\s]+|[,])fchmod([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+fchmod[\s]+|([\s]+|[,])fchmod([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+fchmod[\s]+|([\s]+|[,])fchmod([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+fchmodat[\s]+|([\s]+|[,])fchmodat([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+fchmodat[\s]+|([\s]+|[,])fchmodat([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+fchmodat[\s]+|([\s]+|[,])fchmodat([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+fchmodat[\s]+|([\s]+|[,])fchmodat([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+fchown[\s]+|([\s]+|[,])fchown([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+fchown[\s]+|([\s]+|[,])fchown([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+fchown[\s]+|([\s]+|[,])fchown([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+fchown[\s]+|([\s]+|[,])fchown([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+fchownat[\s]+|([\s]+|[,])fchownat([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+fchownat[\s]+|([\s]+|[,])fchownat([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+fchownat[\s]+|([\s]+|[,])fchownat([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+fchownat[\s]+|([\s]+|[,])fchownat([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+fremovexattr[\s]+|([\s]+|[,])fremovexattr([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+fremovexattr[\s]+|([\s]+|[,])fremovexattr([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+fremovexattr[\s]+|([\s]+|[,])fremovexattr([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+fremovexattr[\s]+|([\s]+|[,])fremovexattr([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+fremovexattr[\s]+|([\s]+|[,])fremovexattr([\s]+|[,])))(?:.*-F\s+auid=0[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+fremovexattr[\s]+|([\s]+|[,])fremovexattr([\s]+|[,])))(?:.*-F\s+auid=0[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+fremovexattr[\s]+|([\s]+|[,])fremovexattr([\s]+|[,])))(?:.*-F\s+auid=0[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+fremovexattr[\s]+|([\s]+|[,])fremovexattr([\s]+|[,])))(?:.*-F\s+auid=0[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+fsetxattr[\s]+|([\s]+|[,])fsetxattr([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+fsetxattr[\s]+|([\s]+|[,])fsetxattr([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+fsetxattr[\s]+|([\s]+|[,])fsetxattr([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+fsetxattr[\s]+|([\s]+|[,])fsetxattr([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+fsetxattr[\s]+|([\s]+|[,])fsetxattr([\s]+|[,])))(?:.*-F\s+auid=0[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+fsetxattr[\s]+|([\s]+|[,])fsetxattr([\s]+|[,])))(?:.*-F\s+auid=0[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+fsetxattr[\s]+|([\s]+|[,])fsetxattr([\s]+|[,])))(?:.*-F\s+auid=0[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+fsetxattr[\s]+|([\s]+|[,])fsetxattr([\s]+|[,])))(?:.*-F\s+auid=0[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+lchown[\s]+|([\s]+|[,])lchown([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+lchown[\s]+|([\s]+|[,])lchown([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+lchown[\s]+|([\s]+|[,])lchown([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+lchown[\s]+|([\s]+|[,])lchown([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+lremovexattr[\s]+|([\s]+|[,])lremovexattr([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+lremovexattr[\s]+|([\s]+|[,])lremovexattr([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+lremovexattr[\s]+|([\s]+|[,])lremovexattr([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+lremovexattr[\s]+|([\s]+|[,])lremovexattr([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+lremovexattr[\s]+|([\s]+|[,])lremovexattr([\s]+|[,])))(?:.*-F\s+auid=0[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+lremovexattr[\s]+|([\s]+|[,])lremovexattr([\s]+|[,])))(?:.*-F\s+auid=0[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+lremovexattr[\s]+|([\s]+|[,])lremovexattr([\s]+|[,])))(?:.*-F\s+auid=0[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+lremovexattr[\s]+|([\s]+|[,])lremovexattr([\s]+|[,])))(?:.*-F\s+auid=0[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+lsetxattr[\s]+|([\s]+|[,])lsetxattr([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+lsetxattr[\s]+|([\s]+|[,])lsetxattr([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+lsetxattr[\s]+|([\s]+|[,])lsetxattr([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+lsetxattr[\s]+|([\s]+|[,])lsetxattr([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+lsetxattr[\s]+|([\s]+|[,])lsetxattr([\s]+|[,])))(?:.*-F\s+auid=0[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+lsetxattr[\s]+|([\s]+|[,])lsetxattr([\s]+|[,])))(?:.*-F\s+auid=0[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+lsetxattr[\s]+|([\s]+|[,])lsetxattr([\s]+|[,])))(?:.*-F\s+auid=0[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+lsetxattr[\s]+|([\s]+|[,])lsetxattr([\s]+|[,])))(?:.*-F\s+auid=0[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+removexattr[\s]+|([\s]+|[,])removexattr([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+removexattr[\s]+|([\s]+|[,])removexattr([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+removexattr[\s]+|([\s]+|[,])removexattr([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+removexattr[\s]+|([\s]+|[,])removexattr([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+removexattr[\s]+|([\s]+|[,])removexattr([\s]+|[,])))(?:.*-F\s+auid=0[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+removexattr[\s]+|([\s]+|[,])removexattr([\s]+|[,])))(?:.*-F\s+auid=0[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+removexattr[\s]+|([\s]+|[,])removexattr([\s]+|[,])))(?:.*-F\s+auid=0[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+removexattr[\s]+|([\s]+|[,])removexattr([\s]+|[,])))(?:.*-F\s+auid=0[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+setxattr[\s]+|([\s]+|[,])setxattr([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+setxattr[\s]+|([\s]+|[,])setxattr([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+setxattr[\s]+|([\s]+|[,])setxattr([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+setxattr[\s]+|([\s]+|[,])setxattr([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+setxattr[\s]+|([\s]+|[,])setxattr([\s]+|[,])))(?:.*-F\s+auid=0[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+setxattr[\s]+|([\s]+|[,])setxattr([\s]+|[,])))(?:.*-F\s+auid=0[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+setxattr[\s]+|([\s]+|[,])setxattr([\s]+|[,])))(?:.*-F\s+auid=0[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+setxattr[\s]+|([\s]+|[,])setxattr([\s]+|[,])))(?:.*-F\s+auid=0[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+umount2[\s]+|([\s]+|[,])umount2([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+umount2[\s]+|([\s]+|[,])umount2([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+umount2[\s]+|([\s]+|[,])umount2([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+umount2[\s]+|([\s]+|[,])umount2([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/chacl(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/chacl(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/chcon(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/chcon(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/sbin\/restorecon(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/sbin\/restorecon(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/sbin\/semanage(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/sbin\/semanage(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/setfacl(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/setfacl(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/sbin\/setfiles(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/sbin\/setfiles(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/sbin\/setsebool(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/sbin\/setsebool(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/sbin\/seunshare(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/sbin\/seunshare(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+rename[\s]+|([\s]+|[,])rename([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+rename[\s]+|([\s]+|[,])rename([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+rename[\s]+|([\s]+|[,])rename([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+rename[\s]+|([\s]+|[,])rename([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+renameat[\s]+|([\s]+|[,])renameat([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+renameat[\s]+|([\s]+|[,])renameat([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+renameat[\s]+|([\s]+|[,])renameat([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+renameat[\s]+|([\s]+|[,])renameat([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+rmdir[\s]+|([\s]+|[,])rmdir([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+rmdir[\s]+|([\s]+|[,])rmdir([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+rmdir[\s]+|([\s]+|[,])rmdir([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+rmdir[\s]+|([\s]+|[,])rmdir([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+unlink[\s]+|([\s]+|[,])unlink([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+unlink[\s]+|([\s]+|[,])unlink([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+unlink[\s]+|([\s]+|[,])unlink([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+unlink[\s]+|([\s]+|[,])unlink([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+unlinkat[\s]+|([\s]+|[,])unlinkat([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+unlinkat[\s]+|([\s]+|[,])unlinkat([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+unlinkat[\s]+|([\s]+|[,])unlinkat([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+unlinkat[\s]+|([\s]+|[,])unlinkat([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^\-w[\s]+\/var\/log\/faillock[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ + 1 + + + /etc/audit/audit.rules + ^\-w[\s]+\/var\/log\/faillock[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^\-w[\s]+\/var\/log\/lastlog[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ + 1 + + + /etc/audit/audit.rules + ^\-w[\s]+\/var\/log\/lastlog[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^\-w[\s]+\/var\/log\/tallylog[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ + 1 + + + /etc/audit/audit.rules + ^\-w[\s]+\/var\/log\/tallylog[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+mount[\s]+|([\s]+|[,])mount([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+mount[\s]+|([\s]+|[,])mount([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+mount[\s]+|([\s]+|[,])mount([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+mount[\s]+|([\s]+|[,])mount([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/at(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/at(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/chage(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/chage(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/chsh(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/chsh(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/crontab(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/crontab(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/gpasswd(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/gpasswd(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/kmod(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/kmod(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/mount(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/mount(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/newgidmap(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/newgidmap(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/newgrp(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/newgrp(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/newuidmap(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/newuidmap(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/sbin\/pam_timestamp_check(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/sbin\/pam_timestamp_check(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/passwd(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/passwd(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/sbin\/postdrop(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/sbin\/postdrop(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/sbin\/postqueue(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/sbin\/postqueue(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/libexec\/pt_chown(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/libexec\/pt_chown(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/ssh-agent(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/ssh-agent(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/libexec\/openssh\/ssh-keysign(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/libexec\/openssh\/ssh-keysign(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/su(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/su(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/sudo(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/sudo(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/sudoedit(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/sudoedit(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/umount(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/umount(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/sbin\/unix_chkpwd(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/sbin\/unix_chkpwd(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/sbin\/unix_update(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/sbin\/unix_update(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/sbin\/userhelper(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/sbin\/userhelper(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/sbin\/usermod(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/sbin\/usermod(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/sbin\/usernetctl(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/sbin\/usernetctl(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^\-w[\s]+\/etc\/group[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ + 1 + + + /etc/audit/audit.rules + ^\-w[\s]+\/etc\/group[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^\-w[\s]+\/etc\/gshadow[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ + 1 + + + /etc/audit/audit.rules + ^\-w[\s]+\/etc\/gshadow[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^\-w[\s]+\/etc\/security\/opasswd[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ + 1 + + + /etc/audit/audit.rules + ^\-w[\s]+\/etc\/security\/opasswd[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^\-w[\s]+\/etc\/passwd[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ + 1 + + + /etc/audit/audit.rules + ^\-w[\s]+\/etc\/passwd[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^\-w[\s]+\/etc\/shadow[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ + 1 + + + /etc/audit/audit.rules + ^\-w[\s]+\/etc\/shadow[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ + 1 + + + /etc/audit/auditd.conf + ^[ \t]*(?i)freq(?-i)[ \t]*=[ \t]*(.+?)[ \t]*(?:$|#) + 1 + + + /etc/audit/auditd.conf + ^[ \t]*(?i)local_events(?-i)[ \t]*=[ \t]*(.+?)[ \t]*(?:$|#) + 1 + + + /etc/audit/auditd.conf + ^[ \t]*(?i)log_format(?-i)[ \t]*=[ \t]*(.+?)[ \t]*(?:$|#) + 1 + + + /etc/audit/auditd.conf + ^[ \t]*(?i)write_logs(?-i)[ \t]*=[ \t]*(.+?)[ \t]*(?:$|#) + 1 + + + /etc/audit/auditd.conf + ^[ \t]*(?i)write_logs(?-i)[ \t]*=[ \t]* + 1 + + + ^/boot/loader/entries/ostree-2-.*.conf + + + ^/boot/loader/entries/ostree-1-.*.conf + ^options (.*)$ + 1 + + + ^/boot/loader/entries/ostree-2-.*.conf + ^options (.*)$ + 1 + + + ^/proc/cmdline + ^BOOT_IMAGE(.*)$ + 1 + + + /etc/dconf/db/local.d/ + ^.*$ + ^\s*\[org/gnome/desktop/lockdown\].*(?:\n\s*[^[\s].*)*\n^\s*user-administration-disabled[ \t]*=[ \t]*(.+?)[ \t]*(?:$|#) + 1 + + + /etc/dconf/db/local.d/locks + ^.*$ + ^/org/gnome/desktop/lockdown/user-administration-disabled$ + 1 + + + /etc/dconf/db/local.d/ + ^.*$ + ^\s*\[org/gnome/settings-daemon/peripherals/smartcard\].*(?:\n\s*[^[\s].*)*\n^\s*removal-action[ \t]*=[ \t]*(.+?)[ \t]*(?:$|#) + 1 + + + /etc/dconf/db/local.d/locks + ^.*$ + ^/org/gnome/settings-daemon/peripherals/smartcard/removal-action$ + 1 + + + + /lib + + oval:ssg-symlink_file_groupownerdir_group_ownership_library_dirs_uid_0:ste:1 + oval:ssg-state_file_groupownerdir_group_ownership_library_dirs_gid_0_0:ste:1 + + + + /lib64 + + oval:ssg-symlink_file_groupownerdir_group_ownership_library_dirs_uid_0:ste:1 + oval:ssg-state_file_groupownerdir_group_ownership_library_dirs_gid_0_1:ste:1 + + + + /usr/lib + + oval:ssg-symlink_file_groupownerdir_group_ownership_library_dirs_uid_0:ste:1 + oval:ssg-state_file_groupownerdir_group_ownership_library_dirs_gid_0_2:ste:1 + + + + /usr/lib64 + + oval:ssg-symlink_file_groupownerdir_group_ownership_library_dirs_uid_0:ste:1 + oval:ssg-state_file_groupownerdir_group_ownership_library_dirs_gid_0_3:ste:1 + + + + /bin + + oval:ssg-symlink_file_ownerdir_ownership_binary_dirs_uid_0:ste:1 + oval:ssg-state_file_ownerdir_ownership_binary_dirs_uid_0_0:ste:1 + + + + /sbin + + oval:ssg-symlink_file_ownerdir_ownership_binary_dirs_uid_0:ste:1 + oval:ssg-state_file_ownerdir_ownership_binary_dirs_uid_0_1:ste:1 + + + + /usr/bin + + oval:ssg-symlink_file_ownerdir_ownership_binary_dirs_uid_0:ste:1 + oval:ssg-state_file_ownerdir_ownership_binary_dirs_uid_0_2:ste:1 + + + + /usr/sbin + + oval:ssg-symlink_file_ownerdir_ownership_binary_dirs_uid_0:ste:1 + oval:ssg-state_file_ownerdir_ownership_binary_dirs_uid_0_3:ste:1 + + + + /usr/local/bin + + oval:ssg-symlink_file_ownerdir_ownership_binary_dirs_uid_0:ste:1 + oval:ssg-state_file_ownerdir_ownership_binary_dirs_uid_0_4:ste:1 + + + + /usr/local/sbin + + oval:ssg-symlink_file_ownerdir_ownership_binary_dirs_uid_0:ste:1 + oval:ssg-state_file_ownerdir_ownership_binary_dirs_uid_0_5:ste:1 + + + + /lib + + oval:ssg-symlink_file_ownerdir_ownership_library_dirs_uid_0:ste:1 + oval:ssg-state_file_ownerdir_ownership_library_dirs_uid_0_0:ste:1 + + + + /lib64 + + oval:ssg-symlink_file_ownerdir_ownership_library_dirs_uid_0:ste:1 + oval:ssg-state_file_ownerdir_ownership_library_dirs_uid_0_1:ste:1 + + + + /usr/lib + + oval:ssg-symlink_file_ownerdir_ownership_library_dirs_uid_0:ste:1 + oval:ssg-state_file_ownerdir_ownership_library_dirs_uid_0_2:ste:1 + + + + /usr/lib64 + + oval:ssg-symlink_file_ownerdir_ownership_library_dirs_uid_0:ste:1 + oval:ssg-state_file_ownerdir_ownership_library_dirs_uid_0_3:ste:1 + + + + /bin + + oval:ssg-exclude_symlinks_dir_permissions_binary_dirs:ste:1 + oval:ssg-state_file_permissionsdir_permissions_binary_dirs_0_mode_0755or_stricter_:ste:1 + + + + /sbin + + oval:ssg-exclude_symlinks_dir_permissions_binary_dirs:ste:1 + oval:ssg-state_file_permissionsdir_permissions_binary_dirs_1_mode_0755or_stricter_:ste:1 + + + + /usr/bin + + oval:ssg-exclude_symlinks_dir_permissions_binary_dirs:ste:1 + oval:ssg-state_file_permissionsdir_permissions_binary_dirs_2_mode_0755or_stricter_:ste:1 + + + + /usr/sbin + + oval:ssg-exclude_symlinks_dir_permissions_binary_dirs:ste:1 + oval:ssg-state_file_permissionsdir_permissions_binary_dirs_3_mode_0755or_stricter_:ste:1 + + + + /usr/local/bin + + oval:ssg-exclude_symlinks_dir_permissions_binary_dirs:ste:1 + oval:ssg-state_file_permissionsdir_permissions_binary_dirs_4_mode_0755or_stricter_:ste:1 + + + + /usr/local/sbin + + oval:ssg-exclude_symlinks_dir_permissions_binary_dirs:ste:1 + oval:ssg-state_file_permissionsdir_permissions_binary_dirs_5_mode_0755or_stricter_:ste:1 + + + + /lib + + oval:ssg-exclude_symlinks_dir_permissions_library_dirs:ste:1 + oval:ssg-state_file_permissionsdir_permissions_library_dirs_0_mode_7755or_stricter_:ste:1 + + + + /lib64 + + oval:ssg-exclude_symlinks_dir_permissions_library_dirs:ste:1 + oval:ssg-state_file_permissionsdir_permissions_library_dirs_1_mode_7755or_stricter_:ste:1 + + + + /usr/lib + + oval:ssg-exclude_symlinks_dir_permissions_library_dirs:ste:1 + oval:ssg-state_file_permissionsdir_permissions_library_dirs_2_mode_7755or_stricter_:ste:1 + + + + /usr/lib64 + + oval:ssg-exclude_symlinks_dir_permissions_library_dirs:ste:1 + oval:ssg-state_file_permissionsdir_permissions_library_dirs_3_mode_7755or_stricter_:ste:1 + + + /etc/ssh/sshd_config + ^[ \t]*(?i)HostbasedAuthentication(?-i)[ \t]+(.+?)[ \t]*(?:$|#) + 1 + + + /etc/at.deny + + + /sbin/auditctl + oval:ssg-symlink_file_groupownerfile_audit_tools_group_ownership_uid_0:ste:1 + oval:ssg-state_file_groupownerfile_audit_tools_group_ownership_gid_0_0:ste:1 + + + /sbin/aureport + oval:ssg-symlink_file_groupownerfile_audit_tools_group_ownership_uid_0:ste:1 + oval:ssg-state_file_groupownerfile_audit_tools_group_ownership_gid_0_1:ste:1 + + + /sbin/ausearch + oval:ssg-symlink_file_groupownerfile_audit_tools_group_ownership_uid_0:ste:1 + oval:ssg-state_file_groupownerfile_audit_tools_group_ownership_gid_0_2:ste:1 + + + /sbin/autrace + oval:ssg-symlink_file_groupownerfile_audit_tools_group_ownership_uid_0:ste:1 + oval:ssg-state_file_groupownerfile_audit_tools_group_ownership_gid_0_3:ste:1 + + + /sbin/auditd + oval:ssg-symlink_file_groupownerfile_audit_tools_group_ownership_uid_0:ste:1 + oval:ssg-state_file_groupownerfile_audit_tools_group_ownership_gid_0_4:ste:1 + + + /sbin/rsyslogd + oval:ssg-symlink_file_groupownerfile_audit_tools_group_ownership_uid_0:ste:1 + oval:ssg-state_file_groupownerfile_audit_tools_group_ownership_gid_0_5:ste:1 + + + /sbin/augenrules + oval:ssg-symlink_file_groupownerfile_audit_tools_group_ownership_uid_0:ste:1 + oval:ssg-state_file_groupownerfile_audit_tools_group_ownership_gid_0_6:ste:1 + + + /sbin/auditctl + oval:ssg-symlink_file_ownerfile_audit_tools_ownership_uid_0:ste:1 + oval:ssg-state_file_ownerfile_audit_tools_ownership_uid_0_0:ste:1 + + + /sbin/aureport + oval:ssg-symlink_file_ownerfile_audit_tools_ownership_uid_0:ste:1 + oval:ssg-state_file_ownerfile_audit_tools_ownership_uid_0_1:ste:1 + + + /sbin/ausearch + oval:ssg-symlink_file_ownerfile_audit_tools_ownership_uid_0:ste:1 + oval:ssg-state_file_ownerfile_audit_tools_ownership_uid_0_2:ste:1 + + + /sbin/autrace + oval:ssg-symlink_file_ownerfile_audit_tools_ownership_uid_0:ste:1 + oval:ssg-state_file_ownerfile_audit_tools_ownership_uid_0_3:ste:1 + + + /sbin/auditd + oval:ssg-symlink_file_ownerfile_audit_tools_ownership_uid_0:ste:1 + oval:ssg-state_file_ownerfile_audit_tools_ownership_uid_0_4:ste:1 + + + /sbin/rsyslogd + oval:ssg-symlink_file_ownerfile_audit_tools_ownership_uid_0:ste:1 + oval:ssg-state_file_ownerfile_audit_tools_ownership_uid_0_5:ste:1 + + + /sbin/augenrules + oval:ssg-symlink_file_ownerfile_audit_tools_ownership_uid_0:ste:1 + oval:ssg-state_file_ownerfile_audit_tools_ownership_uid_0_6:ste:1 + + + /sbin/auditctl + oval:ssg-exclude_symlinks_file_audit_tools_permissions:ste:1 + oval:ssg-state_file_permissionsfile_audit_tools_permissions_0_mode_0755or_stricter_:ste:1 + + + /sbin/aureport + oval:ssg-exclude_symlinks_file_audit_tools_permissions:ste:1 + oval:ssg-state_file_permissionsfile_audit_tools_permissions_1_mode_0755or_stricter_:ste:1 + + + /sbin/ausearch + oval:ssg-exclude_symlinks_file_audit_tools_permissions:ste:1 + oval:ssg-state_file_permissionsfile_audit_tools_permissions_2_mode_0755or_stricter_:ste:1 + + + /sbin/autrace + oval:ssg-exclude_symlinks_file_audit_tools_permissions:ste:1 + oval:ssg-state_file_permissionsfile_audit_tools_permissions_3_mode_0755or_stricter_:ste:1 + + + /sbin/auditd + oval:ssg-exclude_symlinks_file_audit_tools_permissions:ste:1 + oval:ssg-state_file_permissionsfile_audit_tools_permissions_4_mode_0755or_stricter_:ste:1 + + + /sbin/rsyslogd + oval:ssg-exclude_symlinks_file_audit_tools_permissions:ste:1 + oval:ssg-state_file_permissionsfile_audit_tools_permissions_5_mode_0755or_stricter_:ste:1 + + + /sbin/augenrules + oval:ssg-exclude_symlinks_file_audit_tools_permissions:ste:1 + oval:ssg-state_file_permissionsfile_audit_tools_permissions_6_mode_0755or_stricter_:ste:1 + + + /etc/cron.deny + + + /etc/at.allow + oval:ssg-symlink_file_groupowner_at_allow_uid_0:ste:1 + oval:ssg-state_file_groupowner_at_allow_gid_0_0:ste:1 + + + /etc/group- + oval:ssg-symlink_file_groupowner_backup_etc_group_uid_0:ste:1 + oval:ssg-state_file_groupowner_backup_etc_group_gid_0_0:ste:1 + + + /etc/gshadow- + oval:ssg-symlink_file_groupowner_backup_etc_gshadow_uid_0:ste:1 + oval:ssg-state_file_groupowner_backup_etc_gshadow_gid_0_0:ste:1 + + + /etc/passwd- + oval:ssg-symlink_file_groupowner_backup_etc_passwd_uid_0:ste:1 + oval:ssg-state_file_groupowner_backup_etc_passwd_gid_0_0:ste:1 + + + /etc/shadow- + oval:ssg-symlink_file_groupowner_backup_etc_shadow_uid_0:ste:1 + oval:ssg-state_file_groupowner_backup_etc_shadow_gid_0_0:ste:1 + + + /etc/cron.allow + oval:ssg-symlink_file_groupowner_cron_allow_uid_0:ste:1 + oval:ssg-state_file_groupowner_cron_allow_gid_0_0:ste:1 + + + /etc/cron.d + + oval:ssg-symlink_file_groupowner_cron_d_uid_0:ste:1 + oval:ssg-state_file_groupowner_cron_d_gid_0_0:ste:1 + + + /etc/cron.daily + + oval:ssg-symlink_file_groupowner_cron_daily_uid_0:ste:1 + oval:ssg-state_file_groupowner_cron_daily_gid_0_0:ste:1 + + + /etc/cron.hourly + + oval:ssg-symlink_file_groupowner_cron_hourly_uid_0:ste:1 + oval:ssg-state_file_groupowner_cron_hourly_gid_0_0:ste:1 + + + /etc/cron.monthly + + oval:ssg-symlink_file_groupowner_cron_monthly_uid_0:ste:1 + oval:ssg-state_file_groupowner_cron_monthly_gid_0_0:ste:1 + + + /etc/cron.weekly + + oval:ssg-symlink_file_groupowner_cron_weekly_uid_0:ste:1 + oval:ssg-state_file_groupowner_cron_weekly_gid_0_0:ste:1 + + + /etc/crontab + oval:ssg-symlink_file_groupowner_crontab_uid_0:ste:1 + oval:ssg-state_file_groupowner_crontab_gid_0_0:ste:1 + + + /boot/efi/EFI/redhat/grub.cfg + oval:ssg-symlink_file_groupowner_efi_grub2_cfg_uid_0:ste:1 + oval:ssg-state_file_groupowner_efi_grub2_cfg_gid_0_0:ste:1 + + + /etc/group + oval:ssg-symlink_file_groupowner_etc_group_uid_0:ste:1 + oval:ssg-state_file_groupowner_etc_group_gid_0_0:ste:1 + + + /etc/gshadow + oval:ssg-symlink_file_groupowner_etc_gshadow_uid_0:ste:1 + oval:ssg-state_file_groupowner_etc_gshadow_gid_0_0:ste:1 + + + /etc/issue + oval:ssg-symlink_file_groupowner_etc_issue_uid_0:ste:1 + oval:ssg-state_file_groupowner_etc_issue_gid_0_0:ste:1 + + + /etc/motd + oval:ssg-symlink_file_groupowner_etc_motd_uid_0:ste:1 + oval:ssg-state_file_groupowner_etc_motd_gid_0_0:ste:1 + + + /etc/passwd + oval:ssg-symlink_file_groupowner_etc_passwd_uid_0:ste:1 + oval:ssg-state_file_groupowner_etc_passwd_gid_0_0:ste:1 + + + /etc/shadow + oval:ssg-symlink_file_groupowner_etc_shadow_uid_0:ste:1 + oval:ssg-state_file_groupowner_etc_shadow_gid_0_0:ste:1 + + + /boot/grub2/grub.cfg + oval:ssg-symlink_file_groupowner_grub2_cfg_uid_0:ste:1 + oval:ssg-state_file_groupowner_grub2_cfg_gid_0_0:ste:1 + + + /etc/ssh/sshd_config + oval:ssg-symlink_file_groupowner_sshd_config_uid_0:ste:1 + oval:ssg-state_file_groupowner_sshd_config_gid_0_0:ste:1 + + + /var/log + + oval:ssg-symlink_file_groupowner_var_log_uid_0:ste:1 + oval:ssg-state_file_groupowner_var_log_gid_0_0:ste:1 + + + /var/log/messages + oval:ssg-symlink_file_groupowner_var_log_messages_uid_0:ste:1 + oval:ssg-state_file_groupowner_var_log_messages_gid_0_0:ste:1 + + + /var/log/syslog + oval:ssg-symlink_file_groupowner_var_log_syslog_uid_4:ste:1 + oval:ssg-state_file_groupowner_var_log_syslog_gid_4_0:ste:1 + + + /etc/audit + ^audit(\.rules|d\.conf)$ + oval:ssg-symlink_file_groupownership_audit_configuration_uid_0:ste:1 + oval:ssg-state_file_groupownership_audit_configuration_gid_0_0:ste:1 + + + /etc/audit/rules.d + ^.*\.rules$ + oval:ssg-symlink_file_groupownership_audit_configuration_uid_0:ste:1 + oval:ssg-state_file_groupownership_audit_configuration_gid_0_1:ste:1 + + + /etc/group- + oval:ssg-symlink_file_owner_backup_etc_group_uid_0:ste:1 + oval:ssg-state_file_owner_backup_etc_group_uid_0_0:ste:1 + + + /etc/gshadow- + oval:ssg-symlink_file_owner_backup_etc_gshadow_uid_0:ste:1 + oval:ssg-state_file_owner_backup_etc_gshadow_uid_0_0:ste:1 + + + /etc/passwd- + oval:ssg-symlink_file_owner_backup_etc_passwd_uid_0:ste:1 + oval:ssg-state_file_owner_backup_etc_passwd_uid_0_0:ste:1 + + + /etc/shadow- + oval:ssg-symlink_file_owner_backup_etc_shadow_uid_0:ste:1 + oval:ssg-state_file_owner_backup_etc_shadow_uid_0_0:ste:1 + + + /etc/cron.allow + oval:ssg-symlink_file_owner_cron_allow_uid_0:ste:1 + oval:ssg-state_file_owner_cron_allow_uid_0_0:ste:1 + + + /etc/cron.d + + oval:ssg-symlink_file_owner_cron_d_uid_0:ste:1 + oval:ssg-state_file_owner_cron_d_uid_0_0:ste:1 + + + /etc/cron.daily + + oval:ssg-symlink_file_owner_cron_daily_uid_0:ste:1 + oval:ssg-state_file_owner_cron_daily_uid_0_0:ste:1 + + + /etc/cron.hourly + + oval:ssg-symlink_file_owner_cron_hourly_uid_0:ste:1 + oval:ssg-state_file_owner_cron_hourly_uid_0_0:ste:1 + + + /etc/cron.monthly + + oval:ssg-symlink_file_owner_cron_monthly_uid_0:ste:1 + oval:ssg-state_file_owner_cron_monthly_uid_0_0:ste:1 + + + /etc/cron.weekly + + oval:ssg-symlink_file_owner_cron_weekly_uid_0:ste:1 + oval:ssg-state_file_owner_cron_weekly_uid_0_0:ste:1 + + + /etc/crontab + oval:ssg-symlink_file_owner_crontab_uid_0:ste:1 + oval:ssg-state_file_owner_crontab_uid_0_0:ste:1 + + + /boot/efi/EFI/redhat/grub.cfg + oval:ssg-symlink_file_owner_efi_grub2_cfg_uid_0:ste:1 + oval:ssg-state_file_owner_efi_grub2_cfg_uid_0_0:ste:1 + + + /etc/group + oval:ssg-symlink_file_owner_etc_group_uid_0:ste:1 + oval:ssg-state_file_owner_etc_group_uid_0_0:ste:1 + + + /etc/gshadow + oval:ssg-symlink_file_owner_etc_gshadow_uid_0:ste:1 + oval:ssg-state_file_owner_etc_gshadow_uid_0_0:ste:1 + + + /etc/issue + oval:ssg-symlink_file_owner_etc_issue_uid_0:ste:1 + oval:ssg-state_file_owner_etc_issue_uid_0_0:ste:1 + + + /etc/motd + oval:ssg-symlink_file_owner_etc_motd_uid_0:ste:1 + oval:ssg-state_file_owner_etc_motd_uid_0_0:ste:1 + + + /etc/passwd + oval:ssg-symlink_file_owner_etc_passwd_uid_0:ste:1 + oval:ssg-state_file_owner_etc_passwd_uid_0_0:ste:1 + + + /etc/shadow + oval:ssg-symlink_file_owner_etc_shadow_uid_0:ste:1 + oval:ssg-state_file_owner_etc_shadow_uid_0_0:ste:1 + + + /boot/grub2/grub.cfg + oval:ssg-symlink_file_owner_grub2_cfg_uid_0:ste:1 + oval:ssg-state_file_owner_grub2_cfg_uid_0_0:ste:1 + + + /etc/ssh/sshd_config + oval:ssg-symlink_file_owner_sshd_config_uid_0:ste:1 + oval:ssg-state_file_owner_sshd_config_uid_0_0:ste:1 + + + /var/log + + oval:ssg-symlink_file_owner_var_log_uid_0:ste:1 + oval:ssg-state_file_owner_var_log_uid_0_0:ste:1 + + + /var/log/messages + oval:ssg-symlink_file_owner_var_log_messages_uid_0:ste:1 + oval:ssg-state_file_owner_var_log_messages_uid_0_0:ste:1 + + + /var/log/syslog + oval:ssg-symlink_file_owner_var_log_syslog_uid_104:ste:1 + oval:ssg-state_file_owner_var_log_syslog_uid_104_0:ste:1 + + + /etc/audit + ^audit(\.rules|d\.conf)$ + oval:ssg-symlink_file_ownership_audit_configuration_uid_0:ste:1 + oval:ssg-state_file_ownership_audit_configuration_uid_0_0:ste:1 + + + /etc/audit/rules.d + ^.*\.rules$ + oval:ssg-symlink_file_ownership_audit_configuration_uid_0:ste:1 + oval:ssg-state_file_ownership_audit_configuration_uid_0_1:ste:1 + + + + /lib + ^.*$ + oval:ssg-symlink_file_ownership_library_dirs_uid_0:ste:1 + oval:ssg-state_file_ownership_library_dirs_uid_0_0:ste:1 + + + + /lib64 + ^.*$ + oval:ssg-symlink_file_ownership_library_dirs_uid_0:ste:1 + oval:ssg-state_file_ownership_library_dirs_uid_0_1:ste:1 + + + + /usr/lib + ^.*$ + oval:ssg-symlink_file_ownership_library_dirs_uid_0:ste:1 + oval:ssg-state_file_ownership_library_dirs_uid_0_2:ste:1 + + + + /usr/lib64 + ^.*$ + oval:ssg-symlink_file_ownership_library_dirs_uid_0:ste:1 + oval:ssg-state_file_ownership_library_dirs_uid_0_3:ste:1 + + + /etc/at.allow + oval:ssg-exclude_symlinks__at_allow:ste:1 + oval:ssg-state_file_permissions_at_allow_0_mode_0600or_stricter_:ste:1 + + + /etc/group- + oval:ssg-exclude_symlinks__backup_etc_group:ste:1 + oval:ssg-state_file_permissions_backup_etc_group_0_mode_0644or_stricter_:ste:1 + + + /etc/gshadow- + oval:ssg-exclude_symlinks__backup_etc_gshadow:ste:1 + oval:ssg-state_file_permissions_backup_etc_gshadow_0_mode_0000or_stricter_:ste:1 + + + /etc/passwd- + oval:ssg-exclude_symlinks__backup_etc_passwd:ste:1 + oval:ssg-state_file_permissions_backup_etc_passwd_0_mode_0644or_stricter_:ste:1 + + + /etc/shadow- + oval:ssg-exclude_symlinks__backup_etc_shadow:ste:1 + oval:ssg-state_file_permissions_backup_etc_shadow_0_mode_0000or_stricter_:ste:1 + + + /etc/cron.allow + oval:ssg-exclude_symlinks__cron_allow:ste:1 + oval:ssg-state_file_permissions_cron_allow_0_mode_0600or_stricter_:ste:1 + + + /etc/cron.d + + oval:ssg-exclude_symlinks__cron_d:ste:1 + oval:ssg-state_file_permissions_cron_d_0_mode_0700or_stricter_:ste:1 + + + /etc/cron.daily + + oval:ssg-exclude_symlinks__cron_daily:ste:1 + oval:ssg-state_file_permissions_cron_daily_0_mode_0700or_stricter_:ste:1 + + + /etc/cron.hourly + + oval:ssg-exclude_symlinks__cron_hourly:ste:1 + oval:ssg-state_file_permissions_cron_hourly_0_mode_0700or_stricter_:ste:1 + + + /etc/cron.monthly + + oval:ssg-exclude_symlinks__cron_monthly:ste:1 + oval:ssg-state_file_permissions_cron_monthly_0_mode_0700or_stricter_:ste:1 + + + /etc/cron.weekly + + oval:ssg-exclude_symlinks__cron_weekly:ste:1 + oval:ssg-state_file_permissions_cron_weekly_0_mode_0700or_stricter_:ste:1 + + + /etc/crontab + oval:ssg-exclude_symlinks__crontab:ste:1 + oval:ssg-state_file_permissions_crontab_0_mode_0600or_stricter_:ste:1 + + + /boot/efi/EFI/redhat/grub.cfg + oval:ssg-exclude_symlinks__efi_grub2_cfg:ste:1 + oval:ssg-state_file_permissions_efi_grub2_cfg_0_mode_0700or_stricter_:ste:1 + + + /etc/audit/auditd.conf + oval:ssg-exclude_symlinks__etc_audit_auditd:ste:1 + oval:ssg-state_file_permissions_etc_audit_auditd_0_mode_0640or_stricter_:ste:1 + + + /etc/audit/rules.d + ^.*rules$ + oval:ssg-exclude_symlinks__etc_audit_rulesd:ste:1 + oval:ssg-state_file_permissions_etc_audit_rulesd_0_mode_0640or_stricter_:ste:1 + + + /etc/group + oval:ssg-exclude_symlinks__etc_group:ste:1 + oval:ssg-state_file_permissions_etc_group_0_mode_0644or_stricter_:ste:1 + + + /etc/gshadow + oval:ssg-exclude_symlinks__etc_gshadow:ste:1 + oval:ssg-state_file_permissions_etc_gshadow_0_mode_0000or_stricter_:ste:1 + + + /etc/issue + oval:ssg-exclude_symlinks__etc_issue:ste:1 + oval:ssg-state_file_permissions_etc_issue_0_mode_0644or_stricter_:ste:1 + + + /etc/motd + oval:ssg-exclude_symlinks__etc_motd:ste:1 + oval:ssg-state_file_permissions_etc_motd_0_mode_0644or_stricter_:ste:1 + + + /etc/passwd + oval:ssg-exclude_symlinks__etc_passwd:ste:1 + oval:ssg-state_file_permissions_etc_passwd_0_mode_0644or_stricter_:ste:1 + + + /etc/shadow + oval:ssg-exclude_symlinks__etc_shadow:ste:1 + oval:ssg-state_file_permissions_etc_shadow_0_mode_0000or_stricter_:ste:1 + + + /boot/grub2/grub.cfg + oval:ssg-exclude_symlinks__grub2_cfg:ste:1 + oval:ssg-state_file_permissions_grub2_cfg_0_mode_0600or_stricter_:ste:1 + + + + /lib + ^.*$ + oval:ssg-exclude_symlinks__library_dirs:ste:1 + oval:ssg-state_file_permissions_library_dirs_0_mode_7755or_stricter_:ste:1 + + + + /lib64 + ^.*$ + oval:ssg-exclude_symlinks__library_dirs:ste:1 + oval:ssg-state_file_permissions_library_dirs_1_mode_7755or_stricter_:ste:1 + + + + /usr/lib + ^.*$ + oval:ssg-exclude_symlinks__library_dirs:ste:1 + oval:ssg-state_file_permissions_library_dirs_2_mode_7755or_stricter_:ste:1 + + + + /usr/lib64 + ^.*$ + oval:ssg-exclude_symlinks__library_dirs:ste:1 + oval:ssg-state_file_permissions_library_dirs_3_mode_7755or_stricter_:ste:1 + + + /etc/ssh/sshd_config + oval:ssg-exclude_symlinks__sshd_config:ste:1 + oval:ssg-state_file_permissions_sshd_config_0_mode_0600or_stricter_:ste:1 + + + /etc/ssh + ^.*\.pub$ + oval:ssg-exclude_symlinks__sshd_pub_key:ste:1 + oval:ssg-state_file_permissions_sshd_pub_key_0_mode_0644or_stricter_:ste:1 + + + /var/log + + oval:ssg-exclude_symlinks__var_log:ste:1 + oval:ssg-state_file_permissions_var_log_0_mode_0755or_stricter_:ste:1 + + + /var/log/messages + oval:ssg-exclude_symlinks__var_log_messages:ste:1 + oval:ssg-state_file_permissions_var_log_messages_0_mode_0640or_stricter_:ste:1 + + + /var/log/syslog + oval:ssg-exclude_symlinks__var_log_syslog:ste:1 + oval:ssg-state_file_permissions_var_log_syslog_0_mode_0640or_stricter_:ste:1 + + + /etc/firewalld/firewalld.conf + ^[ \t]*FirewallBackend=(.+?)[ \t]*(?:$|#) + 1 + + + /etc/default/grub + ^\s*GRUB_CMDLINE_LINUX="(.*)"$ + 1 + + + /etc/default/grub + ^\s*GRUB_CMDLINE_LINUX_DEFAULT="(.*)"$ + 1 + + + /boot/grub2/grubenv + ^kernelopts=(.*)$ + 1 + + + /boot/efi/EFI/redhat/grubenv + ^kernelopts=(.*)$ + 1 + + + /etc/default/grub + ^\s*GRUB_CMDLINE_LINUX="(.*)"$ + 1 + + + /etc/default/grub + ^\s*GRUB_CMDLINE_LINUX_DEFAULT="(.*)"$ + 1 + + + /boot/grub2/grubenv + ^kernelopts=(.*)$ + 1 + + + /boot/efi/EFI/redhat/grubenv + ^kernelopts=(.*)$ + 1 + + + /etc/default/grub + ^\s*GRUB_CMDLINE_LINUX="(.*)"$ + 1 + + + /etc/default/grub + ^\s*GRUB_CMDLINE_LINUX_DEFAULT="(.*)"$ + 1 + + + /boot/grub2/grubenv + ^kernelopts=(.*)$ + 1 + + + /boot/efi/EFI/redhat/grubenv + ^kernelopts=(.*)$ + 1 + + + /etc/default/grub + ^\s*GRUB_CMDLINE_LINUX="(.*)"$ + 1 + + + /etc/default/grub + ^\s*GRUB_CMDLINE_LINUX_DEFAULT="(.*)"$ + 1 + + + /boot/grub2/grubenv + ^kernelopts=(.*)$ + 1 + + + /boot/efi/EFI/redhat/grubenv + ^kernelopts=(.*)$ + 1 + + + /etc/default/grub + ^\s*GRUB_CMDLINE_LINUX="(.*)"$ + 1 + + + /etc/default/grub + ^\s*GRUB_CMDLINE_LINUX_DEFAULT="(.*)"$ + 1 + + + /boot/grub2/grubenv + ^kernelopts=(.*)$ + 1 + + + /boot/efi/EFI/redhat/grubenv + ^kernelopts=(.*)$ + 1 + + + /etc/default/grub + ^\s*GRUB_CMDLINE_LINUX="(.*)"$ + 1 + + + /etc/default/grub + ^\s*GRUB_CMDLINE_LINUX_DEFAULT="(.*)"$ + 1 + + + /boot/grub2/grubenv + ^kernelopts=(.*)$ + 1 + + + /boot/efi/EFI/redhat/grubenv + ^kernelopts=(.*)$ + 1 + + + /etc/default/grub + ^\s*GRUB_CMDLINE_LINUX="(?!.*\bnosmap\b.*).*"$ + 1 + + + /etc/default/grub + ^\s*GRUB_CMDLINE_LINUX_DEFAULT="(?!.*\bnosmap\b).*"$ + 1 + + + /boot/grub2/grubenv + ^kernelopts=(?!.*\bnosmap\b).*$ + 1 + + + /boot/efi/EFI/redhat/grubenv + ^kernelopts=(?!.*\bnosmap\b).*$ + 1 + + + /etc/default/grub + ^\s*GRUB_CMDLINE_LINUX="(?!.*\bnosmep\b.*).*"$ + 1 + + + /etc/default/grub + ^\s*GRUB_CMDLINE_LINUX_DEFAULT="(?!.*\bnosmep\b).*"$ + 1 + + + /boot/grub2/grubenv + ^kernelopts=(?!.*\bnosmep\b).*$ + 1 + + + /boot/efi/EFI/redhat/grubenv + ^kernelopts=(?!.*\bnosmep\b).*$ + 1 + + + /etc/default/grub + ^\s*GRUB_CMDLINE_LINUX="(.*)"$ + 1 + + + /etc/default/grub + ^\s*GRUB_CMDLINE_LINUX_DEFAULT="(.*)"$ + 1 + + + /boot/grub2/grubenv + ^kernelopts=(.*)$ + 1 + + + /boot/efi/EFI/redhat/grubenv + ^kernelopts=(.*)$ + 1 + + + /etc/default/grub + ^\s*GRUB_CMDLINE_LINUX="(.*)"$ + 1 + + + /etc/default/grub + ^\s*GRUB_CMDLINE_LINUX_DEFAULT="(.*)"$ + 1 + + + /boot/grub2/grubenv + ^kernelopts=(.*)$ + 1 + + + /boot/efi/EFI/redhat/grubenv + ^kernelopts=(.*)$ + 1 + + + /etc/default/grub + ^\s*GRUB_CMDLINE_LINUX="(.*)"$ + 1 + + + /etc/default/grub + ^\s*GRUB_CMDLINE_LINUX_DEFAULT="(.*)"$ + 1 + + + /boot/grub2/grubenv + ^kernelopts=(.*)$ + 1 + + + /boot/efi/EFI/redhat/grubenv + ^kernelopts=(.*)$ + 1 + + + /etc/default/grub + ^\s*GRUB_CMDLINE_LINUX="(.*)"$ + 1 + + + /etc/default/grub + ^\s*GRUB_CMDLINE_LINUX_DEFAULT="(.*)"$ + 1 + + + /boot/grub2/grubenv + ^kernelopts=(.*)$ + 1 + + + /boot/efi/EFI/redhat/grubenv + ^kernelopts=(.*)$ + 1 + + + /etc/default/grub + ^\s*GRUB_CMDLINE_LINUX="(.*)"$ + 1 + + + /etc/default/grub + ^\s*GRUB_CMDLINE_LINUX_DEFAULT="(.*)"$ + 1 + + + /boot/grub2/grubenv + ^kernelopts=(.*)$ + 1 + + + /boot/efi/EFI/redhat/grubenv + ^kernelopts=(.*)$ + 1 + + + /etc/default/grub + ^\s*GRUB_CMDLINE_LINUX="(.*)"$ + 1 + + + /etc/default/grub + ^\s*GRUB_CMDLINE_LINUX_DEFAULT="(.*)"$ + 1 + + + /boot/grub2/grubenv + ^kernelopts=(.*)$ + 1 + + + /boot/efi/EFI/redhat/grubenv + ^kernelopts=(.*)$ + 1 + + + /etc/default/grub + ^\s*GRUB_CMDLINE_LINUX="(.*)"$ + 1 + + + /etc/default/grub + ^\s*GRUB_CMDLINE_LINUX_DEFAULT="(.*)"$ + 1 + + + /boot/grub2/grubenv + ^kernelopts=(.*)$ + 1 + + + /boot/efi/EFI/redhat/grubenv + ^kernelopts=(.*)$ + 1 + + + /etc/default/grub + ^\s*GRUB_CMDLINE_LINUX="(?!.*\bsystemd.debug-shell\b.*).*"$ + 1 + + + /etc/default/grub + ^\s*GRUB_CMDLINE_LINUX_DEFAULT="(?!.*\bsystemd.debug-shell\b).*"$ + 1 + + + /boot/grub2/grubenv + ^kernelopts=(?!.*\bsystemd.debug-shell\b).*$ + 1 + + + /boot/efi/EFI/redhat/grubenv + ^kernelopts=(?!.*\bsystemd.debug-shell\b).*$ + 1 + + + /etc/default/grub + ^\s*GRUB_CMDLINE_LINUX="(.*)"$ + 1 + + + /etc/default/grub + ^\s*GRUB_CMDLINE_LINUX_DEFAULT="(.*)"$ + 1 + + + /boot/grub2/grubenv + ^kernelopts=(.*)$ + 1 + + + /boot/efi/EFI/redhat/grubenv + ^kernelopts=(.*)$ + 1 + + + openssl-pkcs11 + + + /etc/systemd/journald.conf + ^[ \t]*Compress=(.+?)[ \t]*(?:$|#) + 1 + + + /etc/systemd/journald.conf + ^[ \t]*ForwardToSyslog=(.+?)[ \t]*(?:$|#) + 1 + + + /etc/systemd/journald.conf + ^[ \t]*Storage=(.+?)[ \t]*(?:$|#) + 1 + + + ^/boot/config-.*$ + ^CONFIG_ACPI_CUSTOM_METHOD="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_acpi_custom_method_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_ARM64_SW_TTBR0_PAN="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_arm64_sw_ttbr0_pan_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_BINFMT_MISC="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_binfmt_misc_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_BUG="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_bug_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_BUG_ON_DATA_CORRUPTION="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_bug_on_data_corruption_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_COMPAT_BRK="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_compat_brk_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_COMPAT_VDSO="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_compat_vdso_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_DEBUG_CREDENTIALS="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_debug_credentials_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_DEBUG_FS="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_debug_fs_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_DEBUG_LIST="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_debug_list_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_DEBUG_NOTIFIERS="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_debug_notifiers_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_DEBUG_SG="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_debug_sg_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_DEBUG_WX="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_debug_wx_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_DEFAULT_MMAP_MIN_ADDR="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_default_mmap_min_addr_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_DEVKMEM="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_devkmem_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_FORTIFY_SOURCE="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_fortify_source_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_GCC_PLUGIN_LATENT_ENTROPY="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_gcc_plugin_latent_entropy_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_GCC_PLUGIN_STRUCTLEAK="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_gcc_plugin_structleak_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_HARDENED_USERCOPY="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_hardened_usercopy_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_HARDENED_USERCOPY_FALLBACK="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_hardened_usercopy_fallback_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_HIBERNATION="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_hibernation_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_IA32_EMULATION="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_ia32_emulation_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_IPV6="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_ipv6_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_KEXEC="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_kexec_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_LEGACY_PTYS="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_legacy_ptys_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_LEGACY_VSYSCALL_EMULATE="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_legacy_vsyscall_emulate_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_LEGACY_VSYSCALL_NONE="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_legacy_vsyscall_none_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_MODIFY_LDT_SYSCALL="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_modify_ldt_syscall_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_MODULE_SIG="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_module_sig_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_MODULE_SIG_ALL="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_module_sig_all_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_MODULE_SIG_FORCE="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_module_sig_force_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_MODULE_SIG_HASH="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_module_sig_hash_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_MODULE_SIG_KEY="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_module_sig_key_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_MODULE_SIG_SHA512="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_module_sig_sha512_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_PAGE_POISONING="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_page_poisoning_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_PAGE_POISONING_NO_SANITY="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_page_poisoning_no_sanity_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_PAGE_POISONING_ZERO="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_page_poisoning_zero_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_PAGE_TABLE_ISOLATION="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_page_table_isolation_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_PANIC_ON_OOPS="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_panic_on_oops_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_PANIC_TIMEOUT="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_panic_timeout_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_PROC_KCORE="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_proc_kcore_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_RANDOMIZE_BASE="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_randomize_base_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_RANDOMIZE_MEMORY="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_randomize_memory_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_REFCOUNT_FULL="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_refcount_full_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_RETPOLINE="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_retpoline_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_SCHED_STACK_END_CHECK="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_sched_stack_end_check_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_SECCOMP="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_seccomp_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_SECCOMP_FILTER="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_seccomp_filter_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_SECURITY="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_security_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_SECURITY_DMESG_RESTRICT="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_security_dmesg_restrict_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_SECURITY_WRITABLE_HOOKS="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_security_writable_hooks_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_SECURITY_YAMA="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_security_yama_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_SLAB_FREELIST_HARDENED="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_slab_freelist_hardened_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_SLAB_FRELIST_RANDOM="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_slab_frelist_random_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_SLAB_MERGE_DEFAULT="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_slab_merge_default_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_SLUB_DEBUG="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_slub_debug_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_STACKPROTECTOR="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_stackprotector_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_STACKPROTECTOR_STRONG="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_stackprotector_strong_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_STRICT_KERNEL_RWX="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_strict_kernel_rwx_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_STRICT_MODULE_RWX="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_strict_module_rwx_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_SYN_COOKIES="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_syn_cookies_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_UNMAP_KERNEL_AT_EL0="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_unmap_kernel_at_el0_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_VMAP_STACK="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_vmap_stack_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_X86_VSYSCALL_EMULATION="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_x86_vsyscall_emulation_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + + ^.*\.conf$ + ^\s*install\s+atm\s+(/bin/false|/bin/true)$ + 1 + + + + ^.*\.conf$ + ^blacklist\s+atm$ + 1 + + + + ^.*\.conf$ + ^\s*install\s+bluetooth\s+(/bin/false|/bin/true)$ + 1 + + + + ^.*\.conf$ + ^blacklist\s+bluetooth$ + 1 + + + + ^.*\.conf$ + ^\s*install\s+can\s+(/bin/false|/bin/true)$ + 1 + + + + ^.*\.conf$ + ^blacklist\s+can$ + 1 + + + + ^.*\.conf$ + ^\s*install\s+cfg80211\s+(/bin/false|/bin/true)$ + 1 + + + + ^.*\.conf$ + ^blacklist\s+cfg80211$ + 1 + + + + ^.*\.conf$ + ^\s*install\s+cramfs\s+(/bin/false|/bin/true)$ + 1 + + + + ^.*\.conf$ + ^blacklist\s+cramfs$ + 1 + + + + ^.*\.conf$ + ^\s*install\s+dccp\s+(/bin/false|/bin/true)$ + 1 + + + + ^.*\.conf$ + ^blacklist\s+dccp$ + 1 + + + + ^.*\.conf$ + ^\s*install\s+firewire-core\s+(/bin/false|/bin/true)$ + 1 + + + + ^.*\.conf$ + ^blacklist\s+firewire-core$ + 1 + + + + ^.*\.conf$ + ^\s*install\s+freevxfs\s+(/bin/false|/bin/true)$ + 1 + + + + ^.*\.conf$ + ^blacklist\s+freevxfs$ + 1 + + + + ^.*\.conf$ + ^\s*install\s+hfs\s+(/bin/false|/bin/true)$ + 1 + + + + ^.*\.conf$ + ^blacklist\s+hfs$ + 1 + + + + ^.*\.conf$ + ^\s*install\s+hfsplus\s+(/bin/false|/bin/true)$ + 1 + + + + ^.*\.conf$ + ^blacklist\s+hfsplus$ + 1 + + + + ^.*\.conf$ + ^\s*install\s+iwlmvm\s+(/bin/false|/bin/true)$ + 1 + + + + ^.*\.conf$ + ^blacklist\s+iwlmvm$ + 1 + + + + ^.*\.conf$ + ^\s*install\s+iwlwifi\s+(/bin/false|/bin/true)$ + 1 + + + + ^.*\.conf$ + ^blacklist\s+iwlwifi$ + 1 + + + + ^.*\.conf$ + ^\s*install\s+jffs2\s+(/bin/false|/bin/true)$ + 1 + + + + ^.*\.conf$ + ^blacklist\s+jffs2$ + 1 + + + + ^.*\.conf$ + ^\s*install\s+mac80211\s+(/bin/false|/bin/true)$ + 1 + + + + ^.*\.conf$ + ^blacklist\s+mac80211$ + 1 + + + + ^.*\.conf$ + ^\s*install\s+rds\s+(/bin/false|/bin/true)$ + 1 + + + + ^.*\.conf$ + ^blacklist\s+rds$ + 1 + + + + ^.*\.conf$ + ^\s*install\s+sctp\s+(/bin/false|/bin/true)$ + 1 + + + + ^.*\.conf$ + ^blacklist\s+sctp$ + 1 + + + + ^.*\.conf$ + ^\s*install\s+squashfs\s+(/bin/false|/bin/true)$ + 1 + + + + ^.*\.conf$ + ^blacklist\s+squashfs$ + 1 + + + + ^.*\.conf$ + ^\s*install\s+tipc\s+(/bin/false|/bin/true)$ + 1 + + + + ^.*\.conf$ + ^blacklist\s+tipc$ + 1 + + + + ^.*\.conf$ + ^\s*install\s+udf\s+(/bin/false|/bin/true)$ + 1 + + + + ^.*\.conf$ + ^blacklist\s+udf$ + 1 + + + + ^.*\.conf$ + ^\s*install\s+usb-storage\s+(/bin/false|/bin/true)$ + 1 + + + + ^.*\.conf$ + ^blacklist\s+usb-storage$ + 1 + + + + ^.*\.conf$ + ^\s*install\s+uvcvideo\s+(/bin/false|/bin/true)$ + 1 + + + + ^.*\.conf$ + ^blacklist\s+uvcvideo$ + 1 + + + + ^.*\.conf$ + ^\s*install\s+vfat\s+(/bin/false|/bin/true)$ + 1 + + + + ^.*\.conf$ + ^blacklist\s+vfat$ + 1 + + + /boot/efi + + + /boot + + + /boot + + + /boot + + + /boot + + + /dev/shm + + + /dev/shm + + + /dev/shm + + + /home + + + /home + + + /home + + + /etc/fstab + ^\s*\[?[\.\w:-]+\]?[:=][/\w-]+\s+[/\w\\-]+\s+nfs[4]?\s+(.*)$ + 0 + + + /etc/fstab + ^\s*\[?[\.\w:-]+\]?[:=][/\w-]+\s+[/\w\\-]+\s+nfs[4]?\s+(.*)$ + 0 + + + /etc/fstab + ^\s*\[?[\.\w:-]+\]?[:=][/\w-]+\s+[/\w\\-]+\s+nfs[4]?\s+(.*)$ + 0 + + + /etc/fstab + ^\s*\[?[\.\w:-]+\]?[:=][/\w-]+\s+[/\w\\-]+\s+nfs[4]?\s+(.*)$ + 0 + + + /etc/fstab + + 1 + + + /etc/fstab + + 1 + + + /etc/fstab + ^\s*\[?[\.\w:-]+\]?[:=][/\w-]+\s+[/\w\\-]+\s+nfs[4]?\s+(.*)$ + 0 + + + /etc/fstab + ^\s*\[?[\.\w:-]+\]?[:=][/\w-]+\s+[/\w\\-]+\s+nfs[4]?\s+(.*)$ + 0 + + + /etc/fstab + + 1 + + + /etc/fstab + + 1 + + + /etc/fstab + ^\s*\[?[\.\w:-]+\]?[:=][/\w-]+\s+[/\w\\-]+\s+nfs[4]?\s+(.*)$ + 0 + + + /etc/fstab + ^\s*\[?[\.\w:-]+\]?[:=][/\w-]+\s+[/\w\\-]+\s+nfs[4]?\s+(.*)$ + 0 + + + /etc/fstab + + 1 + + + /etc/fstab + + 1 + + + /opt + + + /proc + + + /srv + + + /tmp + + + /tmp + + + /tmp + + + /var/log/audit + + + /var/log/audit + + + /var/log/audit + + + /var/log + + + /var/log + + + /var/log + + + /var + + + /var + + + /var + + + /var/tmp + + + /var/tmp + + + /var/tmp + + + GConf2 + + + MFEhiplsm + + + abrt-addon-ccpp + + + abrt-addon-kerneloops + + + abrt-cli + + + abrt-plugin-logger + + + abrt-plugin-rhtsupport + + + abrt-plugin-sosreport + + + abrt + + + aide + + + audispd-plugins + + + audit-audispd-plugins + + + audit + + + avahi + + + bind + + + binutils + + + chrony + + + cron + + + crypto-policies + + + dconf + + + dhcp-server + + + dnf-automatic + + + dnf-plugin-subscription-manager + + + dovecot + + + esc + + + fapolicyd + + + firewalld + + + freeradius + + + gdm + + + gdm + + + geolite2-city + + + geolite2-country + + + gnutls-utils + + + gssproxy + + + httpd + + + inetutils-telnetd + + + iprutils + + + iptables-services + + + iptables + + + krb5-server + + + krb5-workstation + + + libcap-ng-utils + + + libreport-plugin-logger + + + libreport-plugin-rhtsupport + + + libreswan + + + libselinux + + + McAfeeTP + + + mcstrans + + + net-snmp + + + nfs-utils + + + nis + + + nss-tools + + + ntp + + + ntpdate + + + openldap-clients + + + openldap-servers + + + opensc + + + openscap-scanner + + + openssh-clients + + + openssh-server + + + openssh-server + + + pam_ldap + + + libpwquality + + + pcsc-lite + + + pigz + + + policycoreutils-python-utils + + + policycoreutils + + + postfix + + + prelink + + + psacct + + + python3-abrt-addon + + + quagga + + + rear + + + rng-tools + + + rsh-server + + + rsh + + + rsyslog-gnutls + + + rsyslog + + + samba-common + + + samba-common + + + samba + + + scap-security-guide + + + sendmail + + + setroubleshoot-plugins + + + setroubleshoot-server + + + setroubleshoot + + + squid + + + sssd-ipa + + + sssd + + + subscription-manager + + + sudo + + + syslog-ng + + + talk-server + + + talk + + + tar + + + telnet-server + + + telnet + + + telnetd-ssl + + + telnetd + + + tftp-server + + + tftp + + + tmux + + + tuned + + + usbguard + + + vim-enhanced + + + vsftpd + + + vsftpd + + + xinetd + + + xorg-x11-server-common + + + ypbind + + + ypserv + + + /boot + + + /home + + + /opt + + + /srv + + + /tmp + + + /usr + + + /var + + + /var/log + + + /var/log/audit + + + /var/tmp + + + + /lib + + oval:ssg-symlink_file_groupownerroot_permissions_syslibrary_files_uid_0:ste:1 + oval:ssg-state_file_groupownerroot_permissions_syslibrary_files_gid_0_0:ste:1 + + + + /lib64 + + oval:ssg-symlink_file_groupownerroot_permissions_syslibrary_files_uid_0:ste:1 + oval:ssg-state_file_groupownerroot_permissions_syslibrary_files_gid_0_1:ste:1 + + + + /usr/lib + + oval:ssg-symlink_file_groupownerroot_permissions_syslibrary_files_uid_0:ste:1 + oval:ssg-state_file_groupownerroot_permissions_syslibrary_files_gid_0_2:ste:1 + + + + /usr/lib64 + + oval:ssg-symlink_file_groupownerroot_permissions_syslibrary_files_uid_0:ste:1 + oval:ssg-state_file_groupownerroot_permissions_syslibrary_files_gid_0_3:ste:1 + + + abrt_anon_write + + + abrt_handle_event + + + abrt_upload_watch_anon_write + + + antivirus_can_scan_system + + + antivirus_use_jit + + + auditadm_exec_content + + + authlogin_nsswitch_use_ldap + + + authlogin_radius + + + authlogin_yubikey + + + awstats_purge_apache_log_files + + + boinc_execmem + + + cdrecord_read_content + + + cluster_can_network_connect + + + cluster_manage_all_files + + + cluster_use_execmem + + + cobbler_anon_write + + + cobbler_can_network_connect + + + cobbler_use_cifs + + + cobbler_use_nfs + + + collectd_tcp_network_connect + + + condor_tcp_network_connect + + + conman_can_network + + + container_connect_any + + + cron_can_relabel + + + cron_system_cronjob_use_shares + + + cron_userdomain_transition + + + cups_execmem + + + cvs_read_shadow + + + daemons_dump_core + + + daemons_enable_cluster_mode + + + daemons_use_tcp_wrapper + + + daemons_use_tty + + + dbadm_exec_content + + + dbadm_manage_user_files + + + dbadm_read_user_files + + + deny_execmem + + + deny_ptrace + + + dhcpc_exec_iptables + + + dhcpd_use_ldap + + + domain_fd_use + + + domain_kernel_load_modules + + + entropyd_use_audio + + + exim_can_connect_db + + + exim_manage_user_files + + + exim_read_user_files + + + fcron_crond + + + fenced_can_network_connect + + + fenced_can_ssh + + + fips_mode + + + ftpd_anon_write + + + ftpd_connect_all_unreserved + + + ftpd_connect_db + + + ftpd_full_access + + + ftpd_use_cifs + + + ftpd_use_fusefs + + + ftpd_use_nfs + + + ftpd_use_passive_mode + + + git_cgi_enable_homedirs + + + git_cgi_use_cifs + + + git_cgi_use_nfs + + + git_session_bind_all_unreserved_ports + + + git_session_users + + + git_system_enable_homedirs + + + git_system_use_cifs + + + git_system_use_nfs + + + gitosis_can_sendmail + + + glance_api_can_network + + + glance_use_execmem + + + glance_use_fusefs + + + global_ssp + + + gluster_anon_write + + + gluster_export_all_ro + + + gluster_export_all_rw + + + gpg_web_anon_write + + + gssd_read_tmp + + + guest_exec_content + + + haproxy_connect_any + + + httpd_anon_write + + + httpd_builtin_scripting + + + httpd_can_check_spam + + + httpd_can_connect_ftp + + + httpd_can_connect_ldap + + + httpd_can_connect_mythtv + + + httpd_can_connect_zabbix + + + httpd_can_network_connect + + + httpd_can_network_connect_cobbler + + + httpd_can_network_connect_db + + + httpd_can_network_memcache + + + httpd_can_network_relay + + + httpd_can_sendmail + + + httpd_dbus_avahi + + + httpd_dbus_sssd + + + httpd_dontaudit_search_dirs + + + httpd_enable_cgi + + + httpd_enable_ftp_server + + + httpd_enable_homedirs + + + httpd_execmem + + + httpd_graceful_shutdown + + + httpd_manage_ipa + + + httpd_mod_auth_ntlm_winbind + + + httpd_mod_auth_pam + + + httpd_read_user_content + + + httpd_run_ipa + + + httpd_run_preupgrade + + + httpd_run_stickshift + + + httpd_serve_cobbler_files + + + httpd_setrlimit + + + httpd_ssi_exec + + + httpd_sys_script_anon_write + + + httpd_tmp_exec + + + httpd_tty_comm + + + httpd_unified + + + httpd_use_cifs + + + httpd_use_fusefs + + + httpd_use_gpg + + + httpd_use_nfs + + + httpd_use_openstack + + + httpd_use_sasl + + + httpd_verify_dns + + + icecast_use_any_tcp_ports + + + irc_use_any_tcp_ports + + + irssi_use_full_network + + + kdumpgui_run_bootloader + + + kerberos_enabled + + + ksmtuned_use_cifs + + + ksmtuned_use_nfs + + + logadm_exec_content + + + logging_syslogd_can_sendmail + + + logging_syslogd_run_nagios_plugins + + + logging_syslogd_use_tty + + + login_console_enabled + + + logrotate_use_nfs + + + logwatch_can_network_connect_mail + + + lsmd_plugin_connect_any + + + mailman_use_fusefs + + + mcelog_client + + + mcelog_exec_scripts + + + mcelog_foreground + + + mcelog_server + + + minidlna_read_generic_user_content + + + mmap_low_allowed + + + mock_enable_homedirs + + + mount_anyfile + + + mozilla_plugin_bind_unreserved_ports + + + mozilla_plugin_can_network_connect + + + mozilla_plugin_use_bluejeans + + + mozilla_plugin_use_gps + + + mozilla_plugin_use_spice + + + mozilla_read_content + + + mpd_enable_homedirs + + + mpd_use_cifs + + + mpd_use_nfs + + + mplayer_execstack + + + mysql_connect_any + + + nagios_run_pnp4nagios + + + nagios_run_sudo + + + named_tcp_bind_http_port + + + named_write_master_zones + + + neutron_can_network + + + nfs_export_all_ro + + + nfs_export_all_rw + + + nfsd_anon_write + + + nis_enabled + + + nscd_use_shm + + + openshift_use_nfs + + + openvpn_can_network_connect + + + openvpn_enable_homedirs + + + openvpn_run_unconfined + + + pcp_bind_all_unreserved_ports + + + pcp_read_generic_logs + + + piranha_lvs_can_network_connect + + + polipo_connect_all_unreserved + + + polipo_session_bind_all_unreserved_ports + + + polipo_session_users + + + polipo_use_cifs + + + polipo_use_nfs + + + polyinstantiation_enabled + + + postfix_local_write_mail_spool + + + postgresql_can_rsync + + + postgresql_selinux_transmit_client_label + + + postgresql_selinux_unconfined_dbadm + + + postgresql_selinux_users_ddl + + + pppd_can_insmod + + + pppd_for_user + + + privoxy_connect_any + + + prosody_bind_http_port + + + puppetagent_manage_all_files + + + puppetmaster_use_db + + + racoon_read_shadow + + + rsync_anon_write + + + rsync_client + + + rsync_export_all_ro + + + rsync_full_access + + + samba_create_home_dirs + + + samba_domain_controller + + + samba_enable_home_dirs + + + samba_export_all_ro + + + samba_export_all_rw + + + samba_load_libgfapi + + + samba_portmapper + + + samba_run_unconfined + + + samba_share_fusefs + + + samba_share_nfs + + + sanlock_use_fusefs + + + sanlock_use_nfs + + + sanlock_use_samba + + + saslauthd_read_shadow + + + secadm_exec_content + + + secure_mode + + + secure_mode_insmod + + + secure_mode_policyload + + + selinuxuser_direct_dri_enabled + + + selinuxuser_execheap + + + selinuxuser_execmod + + + selinuxuser_execstack + + + selinuxuser_mysql_connect_enabled + + + selinuxuser_ping + + + selinuxuser_postgresql_connect_enabled + + + selinuxuser_rw_noexattrfile + + + selinuxuser_share_music + + + selinuxuser_tcp_server + + + selinuxuser_udp_server + + + selinuxuser_use_ssh_chroot + + + sge_domain_can_network_connect + + + sge_use_nfs + + + smartmon_3ware + + + smbd_anon_write + + + spamassassin_can_network + + + spamd_enable_home_dirs + + + squid_connect_any + + + squid_use_tproxy + + + ssh_chroot_rw_homedirs + + + ssh_keysign + + + ssh_sysadm_login + + + staff_exec_content + + + staff_use_svirt + + + swift_can_network + + + sysadm_exec_content + + + telepathy_connect_all_ports + + + telepathy_tcp_connect_generic_network_ports + + + tftp_anon_write + + + tftp_home_dir + + + tmpreaper_use_nfs + + + tmpreaper_use_samba + + + tor_bind_all_unreserved_ports + + + tor_can_network_relay + + + unconfined_chrome_sandbox_transition + + + unconfined_login + + + unconfined_mozilla_plugin_transition + + + unprivuser_use_svirt + + + use_ecryptfs_home_dirs + + + use_fusefs_home_dirs + + + use_lpd_server + + + use_nfs_home_dirs + + + use_samba_home_dirs + + + user_exec_content + + + varnishd_connect_any + + + virt_read_qemu_ga_data + + + virt_rw_qemu_ga_data + + + virt_sandbox_use_all_caps + + + virt_sandbox_use_audit + + + virt_sandbox_use_mknod + + + virt_sandbox_use_netlink + + + virt_sandbox_use_sys_admin + + + virt_transition_userdomain + + + virt_use_comm + + + virt_use_execmem + + + virt_use_fusefs + + + virt_use_nfs + + + virt_use_rawip + + + virt_use_samba + + + virt_use_sanlock + + + virt_use_usb + + + virt_use_xserver + + + webadm_manage_user_files + + + webadm_read_user_files + + + wine_mmap_zero_ignore + + + xdm_bind_vnc_tcp_port + + + xdm_exec_bootloader + + + xdm_sysadm_login + + + xdm_write_home + + + xen_use_nfs + + + xend_run_blktap + + + xend_run_qemu + + + xguest_connect_network + + + xguest_exec_content + + + xguest_mount_media + + + xguest_use_bluetooth + + + xserver_clients_write_xshm + + + xserver_execmem + + + xserver_object_manager + + + zabbix_can_network + + + zarafa_setrlimit + + + zebra_write_config + + + zoneminder_anon_write + + + zoneminder_run_sudo + + + ^abrtd\.(service|socket)$ + ActiveState + + + ^abrtd\.(service|socket)$ + LoadState + + + abrt + + + ^acpid\.(service|socket)$ + ActiveState + + + ^acpid\.(service|socket)$ + LoadState + + + acpid + + + ^atd\.(service|socket)$ + ActiveState + + + ^atd\.(service|socket)$ + LoadState + + + at + + + multi-user.target + + + multi-user.target + + + ^auditd\.(socket|service)$ + ActiveState + + + audit + + + ^autofs\.(service|socket)$ + ActiveState + + + ^autofs\.(service|socket)$ + LoadState + + + autofs + + + ^avahi-daemon\.(service|socket)$ + ActiveState + + + ^avahi-daemon\.(service|socket)$ + LoadState + + + avahi + + + ^bluetooth\.(service|socket)$ + ActiveState + + + ^bluetooth\.(service|socket)$ + LoadState + + + bluez + + + ^certmonger\.(service|socket)$ + ActiveState + + + ^certmonger\.(service|socket)$ + LoadState + + + certmonger + + + multi-user.target + + + multi-user.target + + + ^chronyd\.(socket|service)$ + ActiveState + + + chrony + + + ^cockpit\.(service|socket)$ + ActiveState + + + ^cockpit\.(service|socket)$ + LoadState + + + cockpit + + + ^cpupower\.(service|socket)$ + ActiveState + + + ^cpupower\.(service|socket)$ + LoadState + + + kernel-tools + + + multi-user.target + + + multi-user.target + + + ^cron\.(socket|service)$ + ActiveState + + + cron + + + multi-user.target + + + multi-user.target + + + ^crond\.(socket|service)$ + ActiveState + + + cronie + + + ^cups\.(service|socket)$ + ActiveState + + + ^cups\.(service|socket)$ + LoadState + + + cups + + + ^debug-shell\.(service|socket)$ + ActiveState + + + ^debug-shell\.(service|socket)$ + LoadState + + + systemd + + + ^dhcpd\.(service|socket)$ + ActiveState + + + ^dhcpd\.(service|socket)$ + LoadState + + + dhcp-server + + + ^dovecot\.(service|socket)$ + ActiveState + + + ^dovecot\.(service|socket)$ + LoadState + + + dovecot + + + multi-user.target + + + multi-user.target + + + ^fapolicyd\.(socket|service)$ + ActiveState + + + fapolicyd + + + multi-user.target + + + multi-user.target + + + ^firewalld\.(socket|service)$ + ActiveState + + + firewalld + + + ^httpd\.(service|socket)$ + ActiveState + + + ^httpd\.(service|socket)$ + LoadState + + + httpd + + + multi-user.target + + + multi-user.target + + + ^ip6tables\.(socket|service)$ + ActiveState + + + iptables-ipv6 + + + multi-user.target + + + multi-user.target + + + ^iptables\.(socket|service)$ + ActiveState + + + iptables + + + ^kdump\.(service|socket)$ + ActiveState + + + ^kdump\.(service|socket)$ + LoadState + + + kexec-tools + + + ^mdmonitor\.(service|socket)$ + ActiveState + + + ^mdmonitor\.(service|socket)$ + LoadState + + + mdadm + + + multi-user.target + + + multi-user.target + + + ^nails\.(socket|service)$ + ActiveState + + + nails + + + ^named\.(service|socket)$ + ActiveState + + + ^named\.(service|socket)$ + LoadState + + + bind + + + ^netconsole\.(service|socket)$ + ActiveState + + + ^netconsole\.(service|socket)$ + LoadState + + + netconsole + + + ^netfs\.(service|socket)$ + ActiveState + + + ^netfs\.(service|socket)$ + LoadState + + + netfs + + + ^nfs-server\.(service|socket)$ + ActiveState + + + ^nfs-server\.(service|socket)$ + LoadState + + + nfs-utils + + + ^nfslock\.(service|socket)$ + ActiveState + + + ^nfslock\.(service|socket)$ + LoadState + + + nfs-utils + + + multi-user.target + + + multi-user.target + + + ^ntp\.(socket|service)$ + ActiveState + + + ntp + + + multi-user.target + + + multi-user.target + + + ^ntpd\.(socket|service)$ + ActiveState + + + ntp + + + ^ntpdate\.(service|socket)$ + ActiveState + + + ^ntpdate\.(service|socket)$ + LoadState + + + ntpdate + + + ^oddjobd\.(service|socket)$ + ActiveState + + + ^oddjobd\.(service|socket)$ + LoadState + + + oddjob + + + multi-user.target + + + multi-user.target + + + ^pcscd\.(socket|service)$ + ActiveState + + + pcsc-lite + + + ^portreserve\.(service|socket)$ + ActiveState + + + ^portreserve\.(service|socket)$ + LoadState + + + portreserve + + + multi-user.target + + + multi-user.target + + + ^postfix\.(socket|service)$ + ActiveState + + + postfix + + + multi-user.target + + + multi-user.target + + + ^psacct\.(socket|service)$ + ActiveState + + + psacct + + + ^qpidd\.(service|socket)$ + ActiveState + + + ^qpidd\.(service|socket)$ + LoadState + + + qpid-cpp-server + + + ^quota_nld\.(service|socket)$ + ActiveState + + + ^quota_nld\.(service|socket)$ + LoadState + + + quota-nld + + + ^rdisc\.(service|socket)$ + ActiveState + + + ^rdisc\.(service|socket)$ + LoadState + + + iputils + + + ^rexec\.(service|socket)$ + ActiveState + + + ^rexec\.(service|socket)$ + LoadState + + + rsh-server + + + ^rhnsd\.(service|socket)$ + ActiveState + + + ^rhnsd\.(service|socket)$ + LoadState + + + rhnsd + + + ^rhsmcertd\.(service|socket)$ + ActiveState + + + ^rhsmcertd\.(service|socket)$ + LoadState + + + subscription-manager + + + ^rlogin\.(service|socket)$ + ActiveState + + + ^rlogin\.(service|socket)$ + LoadState + + + rsh-server + + + multi-user.target + + + multi-user.target + + + ^rngd\.(socket|service)$ + ActiveState + + + rng-tools + + + ^rpcbind\.(service|socket)$ + ActiveState + + + ^rpcbind\.(service|socket)$ + LoadState + + + nfs-utils + + + ^rpcgssd\.(service|socket)$ + ActiveState + + + ^rpcgssd\.(service|socket)$ + LoadState + + + nfs-utils + + + ^rpcidmapd\.(service|socket)$ + ActiveState + + + ^rpcidmapd\.(service|socket)$ + LoadState + + + nfs-utils + + + ^rpcsvcgssd\.(service|socket)$ + ActiveState + + + ^rpcsvcgssd\.(service|socket)$ + LoadState + + + nfs-utils + + + ^rsh\.(service|socket)$ + ActiveState + + + ^rsh\.(service|socket)$ + LoadState + + + rsh + + + ^rsyncd\.(service|socket)$ + ActiveState + + + ^rsyncd\.(service|socket)$ + LoadState + + + rsync-daemon + + + multi-user.target + + + multi-user.target + + + ^rsyslog\.(socket|service)$ + ActiveState + + + rsyslog + + + ^saslauthd\.(service|socket)$ + ActiveState + + + ^saslauthd\.(service|socket)$ + LoadState + + + cyrus-sasl + + + ^slapd\.(service|socket)$ + ActiveState + + + ^slapd\.(service|socket)$ + LoadState + + + openldap-servers + + + ^smb\.(service|socket)$ + ActiveState + + + ^smb\.(service|socket)$ + LoadState + + + samba + + + ^snmpd\.(service|socket)$ + ActiveState + + + ^snmpd\.(service|socket)$ + LoadState + + + net-snmp + + + ^squid\.(service|socket)$ + ActiveState + + + ^squid\.(service|socket)$ + LoadState + + + squid + + + ^sshd\.(service|socket)$ + ActiveState + + + ^sshd\.(service|socket)$ + LoadState + + + openssh-server + + + multi-user.target + + + multi-user.target + + + ^sshd\.(socket|service)$ + ActiveState + + + openssh-server + + + multi-user.target + + + multi-user.target + + + ^sssd\.(socket|service)$ + ActiveState + + + sssd + + + ^syslog\.(service|socket)$ + ActiveState + + + ^syslog\.(service|socket)$ + LoadState + + + rsyslog + + + multi-user.target + + + multi-user.target + + + ^syslog-ng\.(socket|service)$ + ActiveState + + + syslog-ng + + + ^sysstat\.(service|socket)$ + ActiveState + + + ^sysstat\.(service|socket)$ + LoadState + + + sysstat + + + ^systemd-coredump\.(service|socket)$ + ActiveState + + + ^systemd-coredump\.(service|socket)$ + LoadState + + + systemd + + + multi-user.target + + + multi-user.target + + + ^systemd-journald\.(socket|service)$ + ActiveState + + + systemd + + + ^telnet\.(service|socket)$ + ActiveState + + + ^telnet\.(service|socket)$ + LoadState + + + telnet + + + ^tftp\.(service|socket)$ + ActiveState + + + ^tftp\.(service|socket)$ + LoadState + + + tftp-server + + + multi-user.target + + + multi-user.target + + + ^ufw\.(socket|service)$ + ActiveState + + + ufw + + + multi-user.target + + + multi-user.target + + + ^usbguard\.(socket|service)$ + ActiveState + + + usbguard + + + ^vsftpd\.(service|socket)$ + ActiveState + + + ^vsftpd\.(service|socket)$ + LoadState + + + vsftpd + + + ^xinetd\.(service|socket)$ + ActiveState + + + ^xinetd\.(service|socket)$ + LoadState + + + xinetd + + + ^ypbind\.(service|socket)$ + ActiveState + + + ^ypbind\.(service|socket)$ + LoadState + + + ypbind + + + ^ypserv\.(service|socket)$ + ActiveState + + + ^ypserv\.(service|socket)$ + LoadState + + + ypserv + + + ^zebra\.(service|socket)$ + ActiveState + + + ^zebra\.(service|socket)$ + LoadState + + + quagga + + + /etc/ssh/sshd_config + ^[ \t]*(?i)PermitEmptyPasswords(?-i)[ \t]+(.+?)[ \t]*(?:$|#) + 1 + + + /etc/ssh/sshd_config + ^[ \t]*(?i)GSSAPIAuthentication(?-i)[ \t]+(.+?)[ \t]*(?:$|#) + 1 + + + /etc/ssh/sshd_config + ^[ \t]*(?i)KerberosAuthentication(?-i)[ \t]+(.+?)[ \t]*(?:$|#) + 1 + + + /etc/ssh/sshd_config + ^[ \t]*(?i)PubkeyAuthentication(?-i)[ \t]+(.+?)[ \t]*(?:$|#) + 1 + + + /etc/ssh/sshd_config + ^[ \t]*(?i)IgnoreRhosts(?-i)[ \t]+(.+?)[ \t]*(?:$|#) + 1 + + + /etc/ssh/sshd_config + ^[ \t]*(?i)PermitRootLogin(?-i)[ \t]+(.+?)[ \t]*(?:$|#) + 1 + + + /etc/ssh/sshd_config + ^[ \t]*(?i)PermitRootLogin(?-i)[ \t]+(.+?)[ \t]*(?:$|#) + 1 + + + /etc/ssh/sshd_config + ^[ \t]*(?i)AllowTcpForwarding(?-i)[ \t]+(.+?)[ \t]*(?:$|#) + 1 + + + /etc/ssh/sshd_config + ^[ \t]*(?i)IgnoreUserKnownHosts(?-i)[ \t]+(.+?)[ \t]*(?:$|#) + 1 + + + /etc/ssh/sshd_config + ^[ \t]*(?i)X11Forwarding(?-i)[ \t]+(.+?)[ \t]*(?:$|#) + 1 + + + /etc/ssh/sshd_config + ^[ \t]*(?i)PermitUserEnvironment(?-i)[ \t]+(.+?)[ \t]*(?:$|#) + 1 + + + /etc/ssh/sshd_config + ^[ \t]*(?i)GSSAPIAuthentication(?-i)[ \t]+(.+?)[ \t]*(?:$|#) + 1 + + + /etc/ssh/sshd_config + ^[ \t]*(?i)UsePAM(?-i)[ \t]+(.+?)[ \t]*(?:$|#) + 1 + + + /etc/ssh/sshd_config + ^[ \t]*(?i)PubkeyAuthentication(?-i)[ \t]+(.+?)[ \t]*(?:$|#) + 1 + + + /etc/ssh/sshd_config + ^[ \t]*(?i)StrictModes(?-i)[ \t]+(.+?)[ \t]*(?:$|#) + 1 + + + /etc/ssh/sshd_config + ^[ \t]*(?i)Banner(?-i)[ \t]+(.+?)[ \t]*(?:$|#) + 1 + + + /etc/ssh/sshd_config + ^[ \t]*(?i)Banner(?-i)[ \t]+(.+?)[ \t]*(?:$|#) + 1 + + + /etc/ssh/sshd_config + ^[ \t]*(?i)X11Forwarding(?-i)[ \t]+(.+?)[ \t]*(?:$|#) + 1 + + + /etc/ssh/sshd_config + ^[\s]*Include /etc/ssh/sshd_config\.d/\*\.conf[\s]*$ + 1 + + + /etc/ssh/sshd_config + ^[ \t]*(?i)PrintLastLog(?-i)[ \t]+(.+?)[ \t]*(?:$|#) + 1 + + + /etc/ssh/sshd_config + ^[ \t]*(?i)ClientAliveCountMax(?-i)[ \t]+(.+?)[ \t]*(?:$|#) + 1 + + + /etc/ssh/sshd_config + ^[ \t]*(?i)LogLevel(?-i)[ \t]+(.+?)[ \t]*(?:$|#) + 1 + + + /etc/ssh/sshd_config + ^[ \t]*(?i)LogLevel(?-i)[ \t]+(.+?)[ \t]*(?:$|#) + 1 + + + /etc/sysconfig/sshd + ^[ \t]*SSH_USE_STRONG_RNG=(.+?)[ \t]*(?:$|#) + 1 + + + /etc/ssh/sshd_config + ^[ \t]*(?i)X11UseLocalhost(?-i)[ \t]+(.+?)[ \t]*(?:$|#) + 1 + + + /etc/sssd/sssd.conf + ^[\s]*\[certmap\/.+\/.+\][\s]*$ + 1 + + + ^/etc/sudoers(|\.d/.*)$ + ^[\s]*Defaults[\s]*\benv_reset.*$ + 1 + + + ^/etc/sudoers(|\.d/.*)$ + ^[\s]*Defaults[\s]*\bignore_dot.*$ + 1 + + + ^/etc/sudoers(|\.d/.*)$ + ^[\s]*Defaults[\s]*\bnoexec.*$ + 1 + + + ^/etc/sudoers(|\.d/.*)$ + ^[\s]*Defaults[\s]*\bpasswd_timeout=(\w+)\b.*$ + 1 + + + ^/etc/sudoers(|\.d/.*)$ + ^[\s]*Defaults[\s]*\brequiretty.*$ + 1 + + + ^/etc/sudoers(|\.d/.*)$ + ^[\s]*Defaults[\s]*\bumask=(\w+)\b.*$ + 1 + + + ^/etc/sudoers(|\.d/.*)$ + ^[\s]*Defaults[\s]*\buse_pty.*$ + 1 + + + ^/etc/sudoers(|\.d/.*)$ + ^[\s]*Defaults[\s]*\blogfile=("(?:\\"|\\\\|[^"\\\n])*"\B|[^"](?:(?:\\,|\\"|\\ |\\\\|[^", \\\n])*)\b).*$ + 1 + + + /usr/bin/sudo + oval:ssg-exclude_symlinks_sudo_restrict_others_executable_permission:ste:1 + oval:ssg-state_file_permissionssudo_restrict_others_executable_permission_0_mode_4110or_stricter_:ste:1 + + + fs.protected_hardlinks + + + oval:ssg-local_var_sysctl_fs_protected_hardlinks_counter:var:1 + + + + oval:ssg-object_sysctl_fs_protected_hardlinks_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_fs_protected_hardlinks_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_fs_protected_hardlinks:obj:1 + oval:ssg-var_obj_blank_sysctl_fs_protected_hardlinks:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_fs_protected_hardlinks:var:1 + + + oval:ssg-local_var_symlinks_sysctl_fs_protected_hardlinks:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_fs_protected_hardlinks:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_fs_protected_hardlinks:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_fs_protected_hardlinks:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_fs_protected_hardlinks:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_fs_protected_hardlinks:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_fs_protected_hardlinks:obj:1 + + + + /etc/sysctl.conf + ^[\s]*fs.protected_hardlinks[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*fs.protected_hardlinks[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*fs.protected_hardlinks[\s]*=[\s]*(.*)[\s]*$ + 1 + + + fs.protected_symlinks + + + oval:ssg-local_var_sysctl_fs_protected_symlinks_counter:var:1 + + + + oval:ssg-object_sysctl_fs_protected_symlinks_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_fs_protected_symlinks_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_fs_protected_symlinks:obj:1 + oval:ssg-var_obj_blank_sysctl_fs_protected_symlinks:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_fs_protected_symlinks:var:1 + + + oval:ssg-local_var_symlinks_sysctl_fs_protected_symlinks:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_fs_protected_symlinks:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_fs_protected_symlinks:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_fs_protected_symlinks:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_fs_protected_symlinks:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_fs_protected_symlinks:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_fs_protected_symlinks:obj:1 + + + + /etc/sysctl.conf + ^[\s]*fs.protected_symlinks[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*fs.protected_symlinks[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*fs.protected_symlinks[\s]*=[\s]*(.*)[\s]*$ + 1 + + + fs.suid_dumpable + + + oval:ssg-local_var_sysctl_fs_suid_dumpable_counter:var:1 + + + + oval:ssg-object_sysctl_fs_suid_dumpable_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_fs_suid_dumpable_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_fs_suid_dumpable:obj:1 + oval:ssg-var_obj_blank_sysctl_fs_suid_dumpable:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_fs_suid_dumpable:var:1 + + + oval:ssg-local_var_symlinks_sysctl_fs_suid_dumpable:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_fs_suid_dumpable:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_fs_suid_dumpable:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_fs_suid_dumpable:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_fs_suid_dumpable:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_fs_suid_dumpable:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_fs_suid_dumpable:obj:1 + + + + /etc/sysctl.conf + ^[\s]*fs.suid_dumpable[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*fs.suid_dumpable[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*fs.suid_dumpable[\s]*=[\s]*(.*)[\s]*$ + 1 + + + kernel.core_pattern + + + oval:ssg-local_var_sysctl_kernel_core_pattern_counter:var:1 + + + + oval:ssg-object_sysctl_kernel_core_pattern_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_kernel_core_pattern_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_kernel_core_pattern:obj:1 + oval:ssg-var_obj_blank_sysctl_kernel_core_pattern:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_kernel_core_pattern:var:1 + + + oval:ssg-local_var_symlinks_sysctl_kernel_core_pattern:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_kernel_core_pattern:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_kernel_core_pattern:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_kernel_core_pattern:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_kernel_core_pattern:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_kernel_core_pattern:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_kernel_core_pattern:obj:1 + + + + /etc/sysctl.conf + ^[\s]*kernel.core_pattern[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*kernel.core_pattern[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*kernel.core_pattern[\s]*=[\s]*(.*)[\s]*$ + 1 + + + kernel.core_uses_pid + + + oval:ssg-local_var_sysctl_kernel_core_uses_pid_counter:var:1 + + + + oval:ssg-object_sysctl_kernel_core_uses_pid_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_kernel_core_uses_pid_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_kernel_core_uses_pid:obj:1 + oval:ssg-var_obj_blank_sysctl_kernel_core_uses_pid:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_kernel_core_uses_pid:var:1 + + + oval:ssg-local_var_symlinks_sysctl_kernel_core_uses_pid:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_kernel_core_uses_pid:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_kernel_core_uses_pid:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_kernel_core_uses_pid:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_kernel_core_uses_pid:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_kernel_core_uses_pid:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_kernel_core_uses_pid:obj:1 + + + + /etc/sysctl.conf + ^[\s]*kernel.core_uses_pid[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*kernel.core_uses_pid[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*kernel.core_uses_pid[\s]*=[\s]*(.*)[\s]*$ + 1 + + + kernel.dmesg_restrict + + + oval:ssg-local_var_sysctl_kernel_dmesg_restrict_counter:var:1 + + + + oval:ssg-object_sysctl_kernel_dmesg_restrict_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_kernel_dmesg_restrict_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_kernel_dmesg_restrict:obj:1 + oval:ssg-var_obj_blank_sysctl_kernel_dmesg_restrict:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_kernel_dmesg_restrict:var:1 + + + oval:ssg-local_var_symlinks_sysctl_kernel_dmesg_restrict:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_kernel_dmesg_restrict:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_kernel_dmesg_restrict:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_kernel_dmesg_restrict:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_kernel_dmesg_restrict:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_kernel_dmesg_restrict:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_kernel_dmesg_restrict:obj:1 + + + + /etc/sysctl.conf + ^[\s]*kernel.dmesg_restrict[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*kernel.dmesg_restrict[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*kernel.dmesg_restrict[\s]*=[\s]*(.*)[\s]*$ + 1 + + + kernel.kexec_load_disabled + + + oval:ssg-local_var_sysctl_kernel_kexec_load_disabled_counter:var:1 + + + + oval:ssg-object_sysctl_kernel_kexec_load_disabled_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_kernel_kexec_load_disabled_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_kernel_kexec_load_disabled:obj:1 + oval:ssg-var_obj_blank_sysctl_kernel_kexec_load_disabled:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_kernel_kexec_load_disabled:var:1 + + + oval:ssg-local_var_symlinks_sysctl_kernel_kexec_load_disabled:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_kernel_kexec_load_disabled:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_kernel_kexec_load_disabled:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_kernel_kexec_load_disabled:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_kernel_kexec_load_disabled:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_kernel_kexec_load_disabled:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_kernel_kexec_load_disabled:obj:1 + + + + /etc/sysctl.conf + ^[\s]*kernel.kexec_load_disabled[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*kernel.kexec_load_disabled[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*kernel.kexec_load_disabled[\s]*=[\s]*(.*)[\s]*$ + 1 + + + kernel.kptr_restrict + + + oval:ssg-local_var_sysctl_kernel_kptr_restrict_counter:var:1 + + + + oval:ssg-object_sysctl_kernel_kptr_restrict_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_kernel_kptr_restrict_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_kernel_kptr_restrict:obj:1 + oval:ssg-var_obj_blank_sysctl_kernel_kptr_restrict:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_kernel_kptr_restrict:var:1 + + + oval:ssg-local_var_symlinks_sysctl_kernel_kptr_restrict:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_kernel_kptr_restrict:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_kernel_kptr_restrict:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_kernel_kptr_restrict:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_kernel_kptr_restrict:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_kernel_kptr_restrict:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_kernel_kptr_restrict:obj:1 + + + + /etc/sysctl.conf + ^[\s]*kernel.kptr_restrict[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*kernel.kptr_restrict[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*kernel.kptr_restrict[\s]*=[\s]*(.*)[\s]*$ + 1 + + + kernel.modules_disabled + + + oval:ssg-local_var_sysctl_kernel_modules_disabled_counter:var:1 + + + + oval:ssg-object_sysctl_kernel_modules_disabled_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_kernel_modules_disabled_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_kernel_modules_disabled:obj:1 + oval:ssg-var_obj_blank_sysctl_kernel_modules_disabled:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_kernel_modules_disabled:var:1 + + + oval:ssg-local_var_symlinks_sysctl_kernel_modules_disabled:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_kernel_modules_disabled:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_kernel_modules_disabled:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_kernel_modules_disabled:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_kernel_modules_disabled:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_kernel_modules_disabled:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_kernel_modules_disabled:obj:1 + + + + /etc/sysctl.conf + ^[\s]*kernel.modules_disabled[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*kernel.modules_disabled[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*kernel.modules_disabled[\s]*=[\s]*(.*)[\s]*$ + 1 + + + kernel.panic_on_oops + + + oval:ssg-local_var_sysctl_kernel_panic_on_oops_counter:var:1 + + + + oval:ssg-object_sysctl_kernel_panic_on_oops_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_kernel_panic_on_oops_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_kernel_panic_on_oops:obj:1 + oval:ssg-var_obj_blank_sysctl_kernel_panic_on_oops:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_kernel_panic_on_oops:var:1 + + + oval:ssg-local_var_symlinks_sysctl_kernel_panic_on_oops:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_kernel_panic_on_oops:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_kernel_panic_on_oops:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_kernel_panic_on_oops:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_kernel_panic_on_oops:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_kernel_panic_on_oops:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_kernel_panic_on_oops:obj:1 + + + + /etc/sysctl.conf + ^[\s]*kernel.panic_on_oops[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*kernel.panic_on_oops[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*kernel.panic_on_oops[\s]*=[\s]*(.*)[\s]*$ + 1 + + + kernel.perf_cpu_time_max_percent + + + oval:ssg-local_var_sysctl_kernel_perf_cpu_time_max_percent_counter:var:1 + + + + oval:ssg-object_sysctl_kernel_perf_cpu_time_max_percent_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_kernel_perf_cpu_time_max_percent_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_kernel_perf_cpu_time_max_percent:obj:1 + oval:ssg-var_obj_blank_sysctl_kernel_perf_cpu_time_max_percent:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_kernel_perf_cpu_time_max_percent:var:1 + + + oval:ssg-local_var_symlinks_sysctl_kernel_perf_cpu_time_max_percent:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_kernel_perf_cpu_time_max_percent:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_kernel_perf_cpu_time_max_percent:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_kernel_perf_cpu_time_max_percent:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_kernel_perf_cpu_time_max_percent:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_kernel_perf_cpu_time_max_percent:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_kernel_perf_cpu_time_max_percent:obj:1 + + + + /etc/sysctl.conf + ^[\s]*kernel.perf_cpu_time_max_percent[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*kernel.perf_cpu_time_max_percent[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*kernel.perf_cpu_time_max_percent[\s]*=[\s]*(.*)[\s]*$ + 1 + + + kernel.perf_event_max_sample_rate + + + oval:ssg-local_var_sysctl_kernel_perf_event_max_sample_rate_counter:var:1 + + + + oval:ssg-object_sysctl_kernel_perf_event_max_sample_rate_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_kernel_perf_event_max_sample_rate_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_kernel_perf_event_max_sample_rate:obj:1 + oval:ssg-var_obj_blank_sysctl_kernel_perf_event_max_sample_rate:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_kernel_perf_event_max_sample_rate:var:1 + + + oval:ssg-local_var_symlinks_sysctl_kernel_perf_event_max_sample_rate:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_kernel_perf_event_max_sample_rate:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_kernel_perf_event_max_sample_rate:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_kernel_perf_event_max_sample_rate:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_kernel_perf_event_max_sample_rate:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_kernel_perf_event_max_sample_rate:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_kernel_perf_event_max_sample_rate:obj:1 + + + + /etc/sysctl.conf + ^[\s]*kernel.perf_event_max_sample_rate[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*kernel.perf_event_max_sample_rate[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*kernel.perf_event_max_sample_rate[\s]*=[\s]*(.*)[\s]*$ + 1 + + + kernel.perf_event_paranoid + + + oval:ssg-local_var_sysctl_kernel_perf_event_paranoid_counter:var:1 + + + + oval:ssg-object_sysctl_kernel_perf_event_paranoid_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_kernel_perf_event_paranoid_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_kernel_perf_event_paranoid:obj:1 + oval:ssg-var_obj_blank_sysctl_kernel_perf_event_paranoid:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_kernel_perf_event_paranoid:var:1 + + + oval:ssg-local_var_symlinks_sysctl_kernel_perf_event_paranoid:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_kernel_perf_event_paranoid:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_kernel_perf_event_paranoid:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_kernel_perf_event_paranoid:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_kernel_perf_event_paranoid:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_kernel_perf_event_paranoid:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_kernel_perf_event_paranoid:obj:1 + + + + /etc/sysctl.conf + ^[\s]*kernel.perf_event_paranoid[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*kernel.perf_event_paranoid[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*kernel.perf_event_paranoid[\s]*=[\s]*(.*)[\s]*$ + 1 + + + kernel.pid_max + + + oval:ssg-local_var_sysctl_kernel_pid_max_counter:var:1 + + + + oval:ssg-object_sysctl_kernel_pid_max_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_kernel_pid_max_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_kernel_pid_max:obj:1 + oval:ssg-var_obj_blank_sysctl_kernel_pid_max:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_kernel_pid_max:var:1 + + + oval:ssg-local_var_symlinks_sysctl_kernel_pid_max:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_kernel_pid_max:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_kernel_pid_max:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_kernel_pid_max:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_kernel_pid_max:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_kernel_pid_max:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_kernel_pid_max:obj:1 + + + + /etc/sysctl.conf + ^[\s]*kernel.pid_max[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*kernel.pid_max[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*kernel.pid_max[\s]*=[\s]*(.*)[\s]*$ + 1 + + + kernel.randomize_va_space + + + oval:ssg-local_var_sysctl_kernel_randomize_va_space_counter:var:1 + + + + oval:ssg-object_sysctl_kernel_randomize_va_space_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_kernel_randomize_va_space_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_kernel_randomize_va_space:obj:1 + oval:ssg-var_obj_blank_sysctl_kernel_randomize_va_space:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_kernel_randomize_va_space:var:1 + + + oval:ssg-local_var_symlinks_sysctl_kernel_randomize_va_space:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_kernel_randomize_va_space:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_kernel_randomize_va_space:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_kernel_randomize_va_space:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_kernel_randomize_va_space:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_kernel_randomize_va_space:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_kernel_randomize_va_space:obj:1 + + + + /etc/sysctl.conf + ^[\s]*kernel.randomize_va_space[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*kernel.randomize_va_space[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*kernel.randomize_va_space[\s]*=[\s]*(.*)[\s]*$ + 1 + + + kernel.sysrq + + + oval:ssg-local_var_sysctl_kernel_sysrq_counter:var:1 + + + + oval:ssg-object_sysctl_kernel_sysrq_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_kernel_sysrq_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_kernel_sysrq:obj:1 + oval:ssg-var_obj_blank_sysctl_kernel_sysrq:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_kernel_sysrq:var:1 + + + oval:ssg-local_var_symlinks_sysctl_kernel_sysrq:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_kernel_sysrq:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_kernel_sysrq:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_kernel_sysrq:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_kernel_sysrq:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_kernel_sysrq:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_kernel_sysrq:obj:1 + + + + /etc/sysctl.conf + ^[\s]*kernel.sysrq[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*kernel.sysrq[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*kernel.sysrq[\s]*=[\s]*(.*)[\s]*$ + 1 + + + kernel.unprivileged_bpf_disabled + + + oval:ssg-local_var_sysctl_kernel_unprivileged_bpf_disabled_counter:var:1 + + + + oval:ssg-object_sysctl_kernel_unprivileged_bpf_disabled_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_kernel_unprivileged_bpf_disabled_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_kernel_unprivileged_bpf_disabled:obj:1 + oval:ssg-var_obj_blank_sysctl_kernel_unprivileged_bpf_disabled:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_kernel_unprivileged_bpf_disabled:var:1 + + + oval:ssg-local_var_symlinks_sysctl_kernel_unprivileged_bpf_disabled:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_kernel_unprivileged_bpf_disabled:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_kernel_unprivileged_bpf_disabled:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_kernel_unprivileged_bpf_disabled:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_kernel_unprivileged_bpf_disabled:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_kernel_unprivileged_bpf_disabled:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_kernel_unprivileged_bpf_disabled:obj:1 + + + + /etc/sysctl.conf + ^[\s]*kernel.unprivileged_bpf_disabled[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*kernel.unprivileged_bpf_disabled[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*kernel.unprivileged_bpf_disabled[\s]*=[\s]*(.*)[\s]*$ + 1 + + + kernel.yama.ptrace_scope + + + oval:ssg-local_var_sysctl_kernel_yama_ptrace_scope_counter:var:1 + + + + oval:ssg-object_sysctl_kernel_yama_ptrace_scope_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_kernel_yama_ptrace_scope_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_kernel_yama_ptrace_scope:obj:1 + oval:ssg-var_obj_blank_sysctl_kernel_yama_ptrace_scope:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_kernel_yama_ptrace_scope:var:1 + + + oval:ssg-local_var_symlinks_sysctl_kernel_yama_ptrace_scope:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_kernel_yama_ptrace_scope:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_kernel_yama_ptrace_scope:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_kernel_yama_ptrace_scope:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_kernel_yama_ptrace_scope:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_kernel_yama_ptrace_scope:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_kernel_yama_ptrace_scope:obj:1 + + + + /etc/sysctl.conf + ^[\s]*kernel.yama.ptrace_scope[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*kernel.yama.ptrace_scope[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*kernel.yama.ptrace_scope[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.core.bpf_jit_harden + + + oval:ssg-local_var_sysctl_net_core_bpf_jit_harden_counter:var:1 + + + + oval:ssg-object_sysctl_net_core_bpf_jit_harden_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_core_bpf_jit_harden_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_core_bpf_jit_harden:obj:1 + oval:ssg-var_obj_blank_sysctl_net_core_bpf_jit_harden:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_core_bpf_jit_harden:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_core_bpf_jit_harden:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_core_bpf_jit_harden:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_core_bpf_jit_harden:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_core_bpf_jit_harden:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_core_bpf_jit_harden:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_core_bpf_jit_harden:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_core_bpf_jit_harden:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.core.bpf_jit_harden[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.core.bpf_jit_harden[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.core.bpf_jit_harden[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.ipv4.conf.all.accept_local + + + oval:ssg-local_var_sysctl_net_ipv4_conf_all_accept_local_counter:var:1 + + + + oval:ssg-object_sysctl_net_ipv4_conf_all_accept_local_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_ipv4_conf_all_accept_local_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_ipv4_conf_all_accept_local:obj:1 + oval:ssg-var_obj_blank_sysctl_net_ipv4_conf_all_accept_local:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_ipv4_conf_all_accept_local:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_ipv4_conf_all_accept_local:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_ipv4_conf_all_accept_local:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_ipv4_conf_all_accept_local:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_ipv4_conf_all_accept_local:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_ipv4_conf_all_accept_local:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_ipv4_conf_all_accept_local:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_ipv4_conf_all_accept_local:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.ipv4.conf.all.accept_local[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.conf.all.accept_local[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.conf.all.accept_local[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.ipv4.conf.all.accept_redirects + + + oval:ssg-local_var_sysctl_net_ipv4_conf_all_accept_redirects_counter:var:1 + + + + oval:ssg-object_sysctl_net_ipv4_conf_all_accept_redirects_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_ipv4_conf_all_accept_redirects_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_ipv4_conf_all_accept_redirects:obj:1 + oval:ssg-var_obj_blank_sysctl_net_ipv4_conf_all_accept_redirects:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_ipv4_conf_all_accept_redirects:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_ipv4_conf_all_accept_redirects:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_ipv4_conf_all_accept_redirects:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_ipv4_conf_all_accept_redirects:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_ipv4_conf_all_accept_redirects:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_ipv4_conf_all_accept_redirects:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_ipv4_conf_all_accept_redirects:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_ipv4_conf_all_accept_redirects:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.ipv4.conf.all.accept_redirects[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.conf.all.accept_redirects[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.conf.all.accept_redirects[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.ipv4.conf.all.accept_source_route + + + oval:ssg-local_var_sysctl_net_ipv4_conf_all_accept_source_route_counter:var:1 + + + + oval:ssg-object_sysctl_net_ipv4_conf_all_accept_source_route_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_ipv4_conf_all_accept_source_route_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_ipv4_conf_all_accept_source_route:obj:1 + oval:ssg-var_obj_blank_sysctl_net_ipv4_conf_all_accept_source_route:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_ipv4_conf_all_accept_source_route:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_ipv4_conf_all_accept_source_route:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_ipv4_conf_all_accept_source_route:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_ipv4_conf_all_accept_source_route:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_ipv4_conf_all_accept_source_route:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_ipv4_conf_all_accept_source_route:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_ipv4_conf_all_accept_source_route:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_ipv4_conf_all_accept_source_route:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.ipv4.conf.all.accept_source_route[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.conf.all.accept_source_route[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.conf.all.accept_source_route[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.ipv4.conf.all.arp_filter + + + oval:ssg-local_var_sysctl_net_ipv4_conf_all_arp_filter_counter:var:1 + + + + oval:ssg-object_sysctl_net_ipv4_conf_all_arp_filter_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_ipv4_conf_all_arp_filter_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_ipv4_conf_all_arp_filter:obj:1 + oval:ssg-var_obj_blank_sysctl_net_ipv4_conf_all_arp_filter:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_ipv4_conf_all_arp_filter:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_ipv4_conf_all_arp_filter:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_ipv4_conf_all_arp_filter:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_ipv4_conf_all_arp_filter:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_ipv4_conf_all_arp_filter:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_ipv4_conf_all_arp_filter:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_ipv4_conf_all_arp_filter:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_ipv4_conf_all_arp_filter:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.ipv4.conf.all.arp_filter[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.conf.all.arp_filter[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.conf.all.arp_filter[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.ipv4.conf.all.arp_ignore + + + oval:ssg-local_var_sysctl_net_ipv4_conf_all_arp_ignore_counter:var:1 + + + + oval:ssg-object_sysctl_net_ipv4_conf_all_arp_ignore_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_ipv4_conf_all_arp_ignore_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_ipv4_conf_all_arp_ignore:obj:1 + oval:ssg-var_obj_blank_sysctl_net_ipv4_conf_all_arp_ignore:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_ipv4_conf_all_arp_ignore:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_ipv4_conf_all_arp_ignore:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_ipv4_conf_all_arp_ignore:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_ipv4_conf_all_arp_ignore:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_ipv4_conf_all_arp_ignore:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_ipv4_conf_all_arp_ignore:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_ipv4_conf_all_arp_ignore:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_ipv4_conf_all_arp_ignore:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.ipv4.conf.all.arp_ignore[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.conf.all.arp_ignore[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.conf.all.arp_ignore[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.ipv4.conf.all.drop_gratuitous_arp + + + oval:ssg-local_var_sysctl_net_ipv4_conf_all_drop_gratuitous_arp_counter:var:1 + + + + oval:ssg-object_sysctl_net_ipv4_conf_all_drop_gratuitous_arp_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_ipv4_conf_all_drop_gratuitous_arp_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_ipv4_conf_all_drop_gratuitous_arp:obj:1 + oval:ssg-var_obj_blank_sysctl_net_ipv4_conf_all_drop_gratuitous_arp:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_ipv4_conf_all_drop_gratuitous_arp:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_ipv4_conf_all_drop_gratuitous_arp:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_ipv4_conf_all_drop_gratuitous_arp:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_ipv4_conf_all_drop_gratuitous_arp:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_ipv4_conf_all_drop_gratuitous_arp:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_ipv4_conf_all_drop_gratuitous_arp:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_ipv4_conf_all_drop_gratuitous_arp:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_ipv4_conf_all_drop_gratuitous_arp:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.ipv4.conf.all.drop_gratuitous_arp[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.conf.all.drop_gratuitous_arp[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.conf.all.drop_gratuitous_arp[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.ipv4.conf.all.forwarding + + + oval:ssg-local_var_sysctl_net_ipv4_conf_all_forwarding_counter:var:1 + + + + oval:ssg-object_sysctl_net_ipv4_conf_all_forwarding_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_ipv4_conf_all_forwarding_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_ipv4_conf_all_forwarding:obj:1 + oval:ssg-var_obj_blank_sysctl_net_ipv4_conf_all_forwarding:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_ipv4_conf_all_forwarding:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_ipv4_conf_all_forwarding:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_ipv4_conf_all_forwarding:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_ipv4_conf_all_forwarding:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_ipv4_conf_all_forwarding:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_ipv4_conf_all_forwarding:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_ipv4_conf_all_forwarding:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_ipv4_conf_all_forwarding:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.ipv4.conf.all.forwarding[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.conf.all.forwarding[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.conf.all.forwarding[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.ipv4.conf.all.log_martians + + + oval:ssg-local_var_sysctl_net_ipv4_conf_all_log_martians_counter:var:1 + + + + oval:ssg-object_sysctl_net_ipv4_conf_all_log_martians_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_ipv4_conf_all_log_martians_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_ipv4_conf_all_log_martians:obj:1 + oval:ssg-var_obj_blank_sysctl_net_ipv4_conf_all_log_martians:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_ipv4_conf_all_log_martians:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_ipv4_conf_all_log_martians:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_ipv4_conf_all_log_martians:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_ipv4_conf_all_log_martians:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_ipv4_conf_all_log_martians:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_ipv4_conf_all_log_martians:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_ipv4_conf_all_log_martians:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_ipv4_conf_all_log_martians:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.ipv4.conf.all.log_martians[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.conf.all.log_martians[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.conf.all.log_martians[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.ipv4.conf.all.route_localnet + + + oval:ssg-local_var_sysctl_net_ipv4_conf_all_route_localnet_counter:var:1 + + + + oval:ssg-object_sysctl_net_ipv4_conf_all_route_localnet_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_ipv4_conf_all_route_localnet_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_ipv4_conf_all_route_localnet:obj:1 + oval:ssg-var_obj_blank_sysctl_net_ipv4_conf_all_route_localnet:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_ipv4_conf_all_route_localnet:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_ipv4_conf_all_route_localnet:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_ipv4_conf_all_route_localnet:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_ipv4_conf_all_route_localnet:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_ipv4_conf_all_route_localnet:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_ipv4_conf_all_route_localnet:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_ipv4_conf_all_route_localnet:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_ipv4_conf_all_route_localnet:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.ipv4.conf.all.route_localnet[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.conf.all.route_localnet[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.conf.all.route_localnet[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.ipv4.conf.all.rp_filter + + + oval:ssg-local_var_sysctl_net_ipv4_conf_all_rp_filter_counter:var:1 + + + + oval:ssg-object_sysctl_net_ipv4_conf_all_rp_filter_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_ipv4_conf_all_rp_filter_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_ipv4_conf_all_rp_filter:obj:1 + oval:ssg-var_obj_blank_sysctl_net_ipv4_conf_all_rp_filter:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_ipv4_conf_all_rp_filter:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_ipv4_conf_all_rp_filter:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_ipv4_conf_all_rp_filter:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_ipv4_conf_all_rp_filter:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_ipv4_conf_all_rp_filter:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_ipv4_conf_all_rp_filter:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_ipv4_conf_all_rp_filter:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_ipv4_conf_all_rp_filter:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.ipv4.conf.all.rp_filter[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.conf.all.rp_filter[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.conf.all.rp_filter[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.ipv4.conf.all.secure_redirects + + + oval:ssg-local_var_sysctl_net_ipv4_conf_all_secure_redirects_counter:var:1 + + + + oval:ssg-object_sysctl_net_ipv4_conf_all_secure_redirects_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_ipv4_conf_all_secure_redirects_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_ipv4_conf_all_secure_redirects:obj:1 + oval:ssg-var_obj_blank_sysctl_net_ipv4_conf_all_secure_redirects:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_ipv4_conf_all_secure_redirects:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_ipv4_conf_all_secure_redirects:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_ipv4_conf_all_secure_redirects:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_ipv4_conf_all_secure_redirects:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_ipv4_conf_all_secure_redirects:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_ipv4_conf_all_secure_redirects:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_ipv4_conf_all_secure_redirects:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_ipv4_conf_all_secure_redirects:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.ipv4.conf.all.secure_redirects[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.conf.all.secure_redirects[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.conf.all.secure_redirects[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.ipv4.conf.all.send_redirects + + + oval:ssg-local_var_sysctl_net_ipv4_conf_all_send_redirects_counter:var:1 + + + + oval:ssg-object_sysctl_net_ipv4_conf_all_send_redirects_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_ipv4_conf_all_send_redirects_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_ipv4_conf_all_send_redirects:obj:1 + oval:ssg-var_obj_blank_sysctl_net_ipv4_conf_all_send_redirects:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_ipv4_conf_all_send_redirects:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_ipv4_conf_all_send_redirects:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_ipv4_conf_all_send_redirects:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_ipv4_conf_all_send_redirects:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_ipv4_conf_all_send_redirects:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_ipv4_conf_all_send_redirects:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_ipv4_conf_all_send_redirects:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_ipv4_conf_all_send_redirects:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.ipv4.conf.all.send_redirects[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.conf.all.send_redirects[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.conf.all.send_redirects[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.ipv4.conf.all.shared_media + + + oval:ssg-local_var_sysctl_net_ipv4_conf_all_shared_media_counter:var:1 + + + + oval:ssg-object_sysctl_net_ipv4_conf_all_shared_media_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_ipv4_conf_all_shared_media_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_ipv4_conf_all_shared_media:obj:1 + oval:ssg-var_obj_blank_sysctl_net_ipv4_conf_all_shared_media:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_ipv4_conf_all_shared_media:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_ipv4_conf_all_shared_media:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_ipv4_conf_all_shared_media:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_ipv4_conf_all_shared_media:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_ipv4_conf_all_shared_media:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_ipv4_conf_all_shared_media:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_ipv4_conf_all_shared_media:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_ipv4_conf_all_shared_media:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.ipv4.conf.all.shared_media[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.conf.all.shared_media[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.conf.all.shared_media[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.ipv4.conf.default.accept_redirects + + + oval:ssg-local_var_sysctl_net_ipv4_conf_default_accept_redirects_counter:var:1 + + + + oval:ssg-object_sysctl_net_ipv4_conf_default_accept_redirects_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_ipv4_conf_default_accept_redirects_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_ipv4_conf_default_accept_redirects:obj:1 + oval:ssg-var_obj_blank_sysctl_net_ipv4_conf_default_accept_redirects:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_ipv4_conf_default_accept_redirects:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_ipv4_conf_default_accept_redirects:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_ipv4_conf_default_accept_redirects:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_ipv4_conf_default_accept_redirects:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_ipv4_conf_default_accept_redirects:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_ipv4_conf_default_accept_redirects:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_ipv4_conf_default_accept_redirects:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_ipv4_conf_default_accept_redirects:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.ipv4.conf.default.accept_redirects[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.conf.default.accept_redirects[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.conf.default.accept_redirects[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.ipv4.conf.default.accept_source_route + + + oval:ssg-local_var_sysctl_net_ipv4_conf_default_accept_source_route_counter:var:1 + + + + oval:ssg-object_sysctl_net_ipv4_conf_default_accept_source_route_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_ipv4_conf_default_accept_source_route_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_ipv4_conf_default_accept_source_route:obj:1 + oval:ssg-var_obj_blank_sysctl_net_ipv4_conf_default_accept_source_route:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_ipv4_conf_default_accept_source_route:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_ipv4_conf_default_accept_source_route:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_ipv4_conf_default_accept_source_route:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_ipv4_conf_default_accept_source_route:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_ipv4_conf_default_accept_source_route:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_ipv4_conf_default_accept_source_route:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_ipv4_conf_default_accept_source_route:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_ipv4_conf_default_accept_source_route:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.ipv4.conf.default.accept_source_route[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.conf.default.accept_source_route[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.conf.default.accept_source_route[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.ipv4.conf.default.log_martians + + + oval:ssg-local_var_sysctl_net_ipv4_conf_default_log_martians_counter:var:1 + + + + oval:ssg-object_sysctl_net_ipv4_conf_default_log_martians_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_ipv4_conf_default_log_martians_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_ipv4_conf_default_log_martians:obj:1 + oval:ssg-var_obj_blank_sysctl_net_ipv4_conf_default_log_martians:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_ipv4_conf_default_log_martians:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_ipv4_conf_default_log_martians:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_ipv4_conf_default_log_martians:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_ipv4_conf_default_log_martians:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_ipv4_conf_default_log_martians:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_ipv4_conf_default_log_martians:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_ipv4_conf_default_log_martians:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_ipv4_conf_default_log_martians:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.ipv4.conf.default.log_martians[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.conf.default.log_martians[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.conf.default.log_martians[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.ipv4.conf.default.rp_filter + + + oval:ssg-local_var_sysctl_net_ipv4_conf_default_rp_filter_counter:var:1 + + + + oval:ssg-object_sysctl_net_ipv4_conf_default_rp_filter_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_ipv4_conf_default_rp_filter_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_ipv4_conf_default_rp_filter:obj:1 + oval:ssg-var_obj_blank_sysctl_net_ipv4_conf_default_rp_filter:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_ipv4_conf_default_rp_filter:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_ipv4_conf_default_rp_filter:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_ipv4_conf_default_rp_filter:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_ipv4_conf_default_rp_filter:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_ipv4_conf_default_rp_filter:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_ipv4_conf_default_rp_filter:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_ipv4_conf_default_rp_filter:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_ipv4_conf_default_rp_filter:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.ipv4.conf.default.rp_filter[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.conf.default.rp_filter[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.conf.default.rp_filter[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.ipv4.conf.default.secure_redirects + + + oval:ssg-local_var_sysctl_net_ipv4_conf_default_secure_redirects_counter:var:1 + + + + oval:ssg-object_sysctl_net_ipv4_conf_default_secure_redirects_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_ipv4_conf_default_secure_redirects_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_ipv4_conf_default_secure_redirects:obj:1 + oval:ssg-var_obj_blank_sysctl_net_ipv4_conf_default_secure_redirects:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_ipv4_conf_default_secure_redirects:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_ipv4_conf_default_secure_redirects:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_ipv4_conf_default_secure_redirects:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_ipv4_conf_default_secure_redirects:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_ipv4_conf_default_secure_redirects:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_ipv4_conf_default_secure_redirects:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_ipv4_conf_default_secure_redirects:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_ipv4_conf_default_secure_redirects:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.ipv4.conf.default.secure_redirects[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.conf.default.secure_redirects[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.conf.default.secure_redirects[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.ipv4.conf.default.send_redirects + + + oval:ssg-local_var_sysctl_net_ipv4_conf_default_send_redirects_counter:var:1 + + + + oval:ssg-object_sysctl_net_ipv4_conf_default_send_redirects_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_ipv4_conf_default_send_redirects_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_ipv4_conf_default_send_redirects:obj:1 + oval:ssg-var_obj_blank_sysctl_net_ipv4_conf_default_send_redirects:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_ipv4_conf_default_send_redirects:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_ipv4_conf_default_send_redirects:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_ipv4_conf_default_send_redirects:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_ipv4_conf_default_send_redirects:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_ipv4_conf_default_send_redirects:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_ipv4_conf_default_send_redirects:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_ipv4_conf_default_send_redirects:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_ipv4_conf_default_send_redirects:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.ipv4.conf.default.send_redirects[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.conf.default.send_redirects[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.conf.default.send_redirects[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.ipv4.conf.default.shared_media + + + oval:ssg-local_var_sysctl_net_ipv4_conf_default_shared_media_counter:var:1 + + + + oval:ssg-object_sysctl_net_ipv4_conf_default_shared_media_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_ipv4_conf_default_shared_media_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_ipv4_conf_default_shared_media:obj:1 + oval:ssg-var_obj_blank_sysctl_net_ipv4_conf_default_shared_media:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_ipv4_conf_default_shared_media:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_ipv4_conf_default_shared_media:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_ipv4_conf_default_shared_media:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_ipv4_conf_default_shared_media:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_ipv4_conf_default_shared_media:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_ipv4_conf_default_shared_media:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_ipv4_conf_default_shared_media:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_ipv4_conf_default_shared_media:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.ipv4.conf.default.shared_media[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.conf.default.shared_media[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.conf.default.shared_media[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.ipv4.icmp_echo_ignore_broadcasts + + + oval:ssg-local_var_sysctl_net_ipv4_icmp_echo_ignore_broadcasts_counter:var:1 + + + + oval:ssg-object_sysctl_net_ipv4_icmp_echo_ignore_broadcasts_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_ipv4_icmp_echo_ignore_broadcasts_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_ipv4_icmp_echo_ignore_broadcasts:obj:1 + oval:ssg-var_obj_blank_sysctl_net_ipv4_icmp_echo_ignore_broadcasts:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_ipv4_icmp_echo_ignore_broadcasts:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_ipv4_icmp_echo_ignore_broadcasts:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_ipv4_icmp_echo_ignore_broadcasts:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_ipv4_icmp_echo_ignore_broadcasts:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_ipv4_icmp_echo_ignore_broadcasts:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_ipv4_icmp_echo_ignore_broadcasts:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_ipv4_icmp_echo_ignore_broadcasts:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_ipv4_icmp_echo_ignore_broadcasts:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.ipv4.icmp_echo_ignore_broadcasts[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.icmp_echo_ignore_broadcasts[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.icmp_echo_ignore_broadcasts[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.ipv4.icmp_ignore_bogus_error_responses + + + oval:ssg-local_var_sysctl_net_ipv4_icmp_ignore_bogus_error_responses_counter:var:1 + + + + oval:ssg-object_sysctl_net_ipv4_icmp_ignore_bogus_error_responses_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_ipv4_icmp_ignore_bogus_error_responses_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_ipv4_icmp_ignore_bogus_error_responses:obj:1 + oval:ssg-var_obj_blank_sysctl_net_ipv4_icmp_ignore_bogus_error_responses:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_ipv4_icmp_ignore_bogus_error_responses:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_ipv4_icmp_ignore_bogus_error_responses:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_ipv4_icmp_ignore_bogus_error_responses:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_ipv4_icmp_ignore_bogus_error_responses:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_ipv4_icmp_ignore_bogus_error_responses:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_ipv4_icmp_ignore_bogus_error_responses:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_ipv4_icmp_ignore_bogus_error_responses:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_ipv4_icmp_ignore_bogus_error_responses:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.ipv4.icmp_ignore_bogus_error_responses[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.icmp_ignore_bogus_error_responses[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.icmp_ignore_bogus_error_responses[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.ipv4.ip_forward + + + oval:ssg-local_var_sysctl_net_ipv4_ip_forward_counter:var:1 + + + + oval:ssg-object_sysctl_net_ipv4_ip_forward_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_ipv4_ip_forward_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_ipv4_ip_forward:obj:1 + oval:ssg-var_obj_blank_sysctl_net_ipv4_ip_forward:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_ipv4_ip_forward:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_ipv4_ip_forward:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_ipv4_ip_forward:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_ipv4_ip_forward:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_ipv4_ip_forward:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_ipv4_ip_forward:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_ipv4_ip_forward:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_ipv4_ip_forward:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.ipv4.ip_forward[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.ip_forward[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.ip_forward[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.ipv4.ip_local_port_range + + + oval:ssg-local_var_sysctl_net_ipv4_ip_local_port_range_counter:var:1 + + + + oval:ssg-object_sysctl_net_ipv4_ip_local_port_range_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_ipv4_ip_local_port_range_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_ipv4_ip_local_port_range:obj:1 + oval:ssg-var_obj_blank_sysctl_net_ipv4_ip_local_port_range:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_ipv4_ip_local_port_range:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_ipv4_ip_local_port_range:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_ipv4_ip_local_port_range:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_ipv4_ip_local_port_range:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_ipv4_ip_local_port_range:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_ipv4_ip_local_port_range:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_ipv4_ip_local_port_range:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_ipv4_ip_local_port_range:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.ipv4.ip_local_port_range[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.ip_local_port_range[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.ip_local_port_range[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.ipv4.tcp_invalid_ratelimit + + + oval:ssg-local_var_sysctl_net_ipv4_tcp_invalid_ratelimit_counter:var:1 + + + + oval:ssg-object_sysctl_net_ipv4_tcp_invalid_ratelimit_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_ipv4_tcp_invalid_ratelimit_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_ipv4_tcp_invalid_ratelimit:obj:1 + oval:ssg-var_obj_blank_sysctl_net_ipv4_tcp_invalid_ratelimit:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_ipv4_tcp_invalid_ratelimit:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_ipv4_tcp_invalid_ratelimit:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_ipv4_tcp_invalid_ratelimit:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_ipv4_tcp_invalid_ratelimit:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_ipv4_tcp_invalid_ratelimit:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_ipv4_tcp_invalid_ratelimit:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_ipv4_tcp_invalid_ratelimit:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_ipv4_tcp_invalid_ratelimit:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.ipv4.tcp_invalid_ratelimit[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.tcp_invalid_ratelimit[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.tcp_invalid_ratelimit[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.ipv4.tcp_rfc1337 + + + oval:ssg-local_var_sysctl_net_ipv4_tcp_rfc1337_counter:var:1 + + + + oval:ssg-object_sysctl_net_ipv4_tcp_rfc1337_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_ipv4_tcp_rfc1337_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_ipv4_tcp_rfc1337:obj:1 + oval:ssg-var_obj_blank_sysctl_net_ipv4_tcp_rfc1337:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_ipv4_tcp_rfc1337:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_ipv4_tcp_rfc1337:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_ipv4_tcp_rfc1337:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_ipv4_tcp_rfc1337:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_ipv4_tcp_rfc1337:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_ipv4_tcp_rfc1337:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_ipv4_tcp_rfc1337:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_ipv4_tcp_rfc1337:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.ipv4.tcp_rfc1337[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.tcp_rfc1337[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.tcp_rfc1337[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.ipv4.tcp_syncookies + + + oval:ssg-local_var_sysctl_net_ipv4_tcp_syncookies_counter:var:1 + + + + oval:ssg-object_sysctl_net_ipv4_tcp_syncookies_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_ipv4_tcp_syncookies_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_ipv4_tcp_syncookies:obj:1 + oval:ssg-var_obj_blank_sysctl_net_ipv4_tcp_syncookies:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_ipv4_tcp_syncookies:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_ipv4_tcp_syncookies:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_ipv4_tcp_syncookies:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_ipv4_tcp_syncookies:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_ipv4_tcp_syncookies:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_ipv4_tcp_syncookies:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_ipv4_tcp_syncookies:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_ipv4_tcp_syncookies:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.ipv4.tcp_syncookies[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.tcp_syncookies[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.tcp_syncookies[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.ipv6.conf.all.accept_ra + + + oval:ssg-local_var_sysctl_net_ipv6_conf_all_accept_ra_counter:var:1 + + + + oval:ssg-object_sysctl_net_ipv6_conf_all_accept_ra_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_ipv6_conf_all_accept_ra_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_ipv6_conf_all_accept_ra:obj:1 + oval:ssg-var_obj_blank_sysctl_net_ipv6_conf_all_accept_ra:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_ipv6_conf_all_accept_ra:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_ipv6_conf_all_accept_ra:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_ipv6_conf_all_accept_ra:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_ipv6_conf_all_accept_ra:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_ipv6_conf_all_accept_ra:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_ipv6_conf_all_accept_ra:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_ipv6_conf_all_accept_ra:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_ipv6_conf_all_accept_ra:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.ipv6.conf.all.accept_ra[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv6.conf.all.accept_ra[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv6.conf.all.accept_ra[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.ipv6.conf.all.accept_ra_defrtr + + + oval:ssg-local_var_sysctl_net_ipv6_conf_all_accept_ra_defrtr_counter:var:1 + + + + oval:ssg-object_sysctl_net_ipv6_conf_all_accept_ra_defrtr_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_ipv6_conf_all_accept_ra_defrtr_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_ipv6_conf_all_accept_ra_defrtr:obj:1 + oval:ssg-var_obj_blank_sysctl_net_ipv6_conf_all_accept_ra_defrtr:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_ipv6_conf_all_accept_ra_defrtr:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_ipv6_conf_all_accept_ra_defrtr:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_ipv6_conf_all_accept_ra_defrtr:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_ipv6_conf_all_accept_ra_defrtr:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_ipv6_conf_all_accept_ra_defrtr:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_ipv6_conf_all_accept_ra_defrtr:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_ipv6_conf_all_accept_ra_defrtr:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_ipv6_conf_all_accept_ra_defrtr:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.ipv6.conf.all.accept_ra_defrtr[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv6.conf.all.accept_ra_defrtr[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv6.conf.all.accept_ra_defrtr[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.ipv6.conf.all.accept_ra_pinfo + + + oval:ssg-local_var_sysctl_net_ipv6_conf_all_accept_ra_pinfo_counter:var:1 + + + + oval:ssg-object_sysctl_net_ipv6_conf_all_accept_ra_pinfo_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_ipv6_conf_all_accept_ra_pinfo_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_ipv6_conf_all_accept_ra_pinfo:obj:1 + oval:ssg-var_obj_blank_sysctl_net_ipv6_conf_all_accept_ra_pinfo:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_ipv6_conf_all_accept_ra_pinfo:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_ipv6_conf_all_accept_ra_pinfo:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_ipv6_conf_all_accept_ra_pinfo:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_ipv6_conf_all_accept_ra_pinfo:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_ipv6_conf_all_accept_ra_pinfo:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_ipv6_conf_all_accept_ra_pinfo:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_ipv6_conf_all_accept_ra_pinfo:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_ipv6_conf_all_accept_ra_pinfo:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.ipv6.conf.all.accept_ra_pinfo[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv6.conf.all.accept_ra_pinfo[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv6.conf.all.accept_ra_pinfo[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.ipv6.conf.all.accept_ra_rtr_pref + + + oval:ssg-local_var_sysctl_net_ipv6_conf_all_accept_ra_rtr_pref_counter:var:1 + + + + oval:ssg-object_sysctl_net_ipv6_conf_all_accept_ra_rtr_pref_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_ipv6_conf_all_accept_ra_rtr_pref_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_ipv6_conf_all_accept_ra_rtr_pref:obj:1 + oval:ssg-var_obj_blank_sysctl_net_ipv6_conf_all_accept_ra_rtr_pref:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_ipv6_conf_all_accept_ra_rtr_pref:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_ipv6_conf_all_accept_ra_rtr_pref:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_ipv6_conf_all_accept_ra_rtr_pref:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_ipv6_conf_all_accept_ra_rtr_pref:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_ipv6_conf_all_accept_ra_rtr_pref:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_ipv6_conf_all_accept_ra_rtr_pref:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_ipv6_conf_all_accept_ra_rtr_pref:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_ipv6_conf_all_accept_ra_rtr_pref:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.ipv6.conf.all.accept_ra_rtr_pref[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv6.conf.all.accept_ra_rtr_pref[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv6.conf.all.accept_ra_rtr_pref[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.ipv6.conf.all.accept_redirects + + + oval:ssg-local_var_sysctl_net_ipv6_conf_all_accept_redirects_counter:var:1 + + + + oval:ssg-object_sysctl_net_ipv6_conf_all_accept_redirects_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_ipv6_conf_all_accept_redirects_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_ipv6_conf_all_accept_redirects:obj:1 + oval:ssg-var_obj_blank_sysctl_net_ipv6_conf_all_accept_redirects:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_ipv6_conf_all_accept_redirects:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_ipv6_conf_all_accept_redirects:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_ipv6_conf_all_accept_redirects:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_ipv6_conf_all_accept_redirects:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_ipv6_conf_all_accept_redirects:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_ipv6_conf_all_accept_redirects:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_ipv6_conf_all_accept_redirects:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_ipv6_conf_all_accept_redirects:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.ipv6.conf.all.accept_redirects[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv6.conf.all.accept_redirects[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv6.conf.all.accept_redirects[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.ipv6.conf.all.accept_source_route + + + oval:ssg-local_var_sysctl_net_ipv6_conf_all_accept_source_route_counter:var:1 + + + + oval:ssg-object_sysctl_net_ipv6_conf_all_accept_source_route_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_ipv6_conf_all_accept_source_route_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_ipv6_conf_all_accept_source_route:obj:1 + oval:ssg-var_obj_blank_sysctl_net_ipv6_conf_all_accept_source_route:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_ipv6_conf_all_accept_source_route:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_ipv6_conf_all_accept_source_route:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_ipv6_conf_all_accept_source_route:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_ipv6_conf_all_accept_source_route:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_ipv6_conf_all_accept_source_route:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_ipv6_conf_all_accept_source_route:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_ipv6_conf_all_accept_source_route:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_ipv6_conf_all_accept_source_route:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.ipv6.conf.all.accept_source_route[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv6.conf.all.accept_source_route[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv6.conf.all.accept_source_route[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.ipv6.conf.all.autoconf + + + oval:ssg-local_var_sysctl_net_ipv6_conf_all_autoconf_counter:var:1 + + + + oval:ssg-object_sysctl_net_ipv6_conf_all_autoconf_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_ipv6_conf_all_autoconf_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_ipv6_conf_all_autoconf:obj:1 + oval:ssg-var_obj_blank_sysctl_net_ipv6_conf_all_autoconf:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_ipv6_conf_all_autoconf:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_ipv6_conf_all_autoconf:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_ipv6_conf_all_autoconf:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_ipv6_conf_all_autoconf:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_ipv6_conf_all_autoconf:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_ipv6_conf_all_autoconf:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_ipv6_conf_all_autoconf:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_ipv6_conf_all_autoconf:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.ipv6.conf.all.autoconf[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv6.conf.all.autoconf[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv6.conf.all.autoconf[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.ipv6.conf.all.disable_ipv6 + + + oval:ssg-local_var_sysctl_net_ipv6_conf_all_disable_ipv6_counter:var:1 + + + + oval:ssg-object_sysctl_net_ipv6_conf_all_disable_ipv6_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_ipv6_conf_all_disable_ipv6_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_ipv6_conf_all_disable_ipv6:obj:1 + oval:ssg-var_obj_blank_sysctl_net_ipv6_conf_all_disable_ipv6:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_ipv6_conf_all_disable_ipv6:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_ipv6_conf_all_disable_ipv6:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_ipv6_conf_all_disable_ipv6:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_ipv6_conf_all_disable_ipv6:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_ipv6_conf_all_disable_ipv6:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_ipv6_conf_all_disable_ipv6:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_ipv6_conf_all_disable_ipv6:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_ipv6_conf_all_disable_ipv6:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.ipv6.conf.all.disable_ipv6[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv6.conf.all.disable_ipv6[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv6.conf.all.disable_ipv6[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.ipv6.conf.all.forwarding + + + oval:ssg-local_var_sysctl_net_ipv6_conf_all_forwarding_counter:var:1 + + + + oval:ssg-object_sysctl_net_ipv6_conf_all_forwarding_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_ipv6_conf_all_forwarding_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_ipv6_conf_all_forwarding:obj:1 + oval:ssg-var_obj_blank_sysctl_net_ipv6_conf_all_forwarding:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_ipv6_conf_all_forwarding:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_ipv6_conf_all_forwarding:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_ipv6_conf_all_forwarding:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_ipv6_conf_all_forwarding:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_ipv6_conf_all_forwarding:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_ipv6_conf_all_forwarding:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_ipv6_conf_all_forwarding:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_ipv6_conf_all_forwarding:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.ipv6.conf.all.forwarding[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv6.conf.all.forwarding[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv6.conf.all.forwarding[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.ipv6.conf.all.max_addresses + + + oval:ssg-local_var_sysctl_net_ipv6_conf_all_max_addresses_counter:var:1 + + + + oval:ssg-object_sysctl_net_ipv6_conf_all_max_addresses_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_ipv6_conf_all_max_addresses_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_ipv6_conf_all_max_addresses:obj:1 + oval:ssg-var_obj_blank_sysctl_net_ipv6_conf_all_max_addresses:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_ipv6_conf_all_max_addresses:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_ipv6_conf_all_max_addresses:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_ipv6_conf_all_max_addresses:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_ipv6_conf_all_max_addresses:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_ipv6_conf_all_max_addresses:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_ipv6_conf_all_max_addresses:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_ipv6_conf_all_max_addresses:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_ipv6_conf_all_max_addresses:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.ipv6.conf.all.max_addresses[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv6.conf.all.max_addresses[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv6.conf.all.max_addresses[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.ipv6.conf.all.router_solicitations + + + oval:ssg-local_var_sysctl_net_ipv6_conf_all_router_solicitations_counter:var:1 + + + + oval:ssg-object_sysctl_net_ipv6_conf_all_router_solicitations_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_ipv6_conf_all_router_solicitations_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_ipv6_conf_all_router_solicitations:obj:1 + oval:ssg-var_obj_blank_sysctl_net_ipv6_conf_all_router_solicitations:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_ipv6_conf_all_router_solicitations:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_ipv6_conf_all_router_solicitations:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_ipv6_conf_all_router_solicitations:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_ipv6_conf_all_router_solicitations:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_ipv6_conf_all_router_solicitations:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_ipv6_conf_all_router_solicitations:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_ipv6_conf_all_router_solicitations:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_ipv6_conf_all_router_solicitations:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.ipv6.conf.all.router_solicitations[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv6.conf.all.router_solicitations[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv6.conf.all.router_solicitations[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.ipv6.conf.default.accept_ra + + + oval:ssg-local_var_sysctl_net_ipv6_conf_default_accept_ra_counter:var:1 + + + + oval:ssg-object_sysctl_net_ipv6_conf_default_accept_ra_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_ipv6_conf_default_accept_ra_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_ipv6_conf_default_accept_ra:obj:1 + oval:ssg-var_obj_blank_sysctl_net_ipv6_conf_default_accept_ra:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_ipv6_conf_default_accept_ra:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_ipv6_conf_default_accept_ra:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_ipv6_conf_default_accept_ra:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_ipv6_conf_default_accept_ra:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_ipv6_conf_default_accept_ra:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_ipv6_conf_default_accept_ra:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_ipv6_conf_default_accept_ra:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_ipv6_conf_default_accept_ra:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.ipv6.conf.default.accept_ra[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv6.conf.default.accept_ra[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv6.conf.default.accept_ra[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.ipv6.conf.default.accept_ra_defrtr + + + oval:ssg-local_var_sysctl_net_ipv6_conf_default_accept_ra_defrtr_counter:var:1 + + + + oval:ssg-object_sysctl_net_ipv6_conf_default_accept_ra_defrtr_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_ipv6_conf_default_accept_ra_defrtr_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_ipv6_conf_default_accept_ra_defrtr:obj:1 + oval:ssg-var_obj_blank_sysctl_net_ipv6_conf_default_accept_ra_defrtr:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_ipv6_conf_default_accept_ra_defrtr:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_ipv6_conf_default_accept_ra_defrtr:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_ipv6_conf_default_accept_ra_defrtr:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_ipv6_conf_default_accept_ra_defrtr:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_ipv6_conf_default_accept_ra_defrtr:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_ipv6_conf_default_accept_ra_defrtr:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_ipv6_conf_default_accept_ra_defrtr:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_ipv6_conf_default_accept_ra_defrtr:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.ipv6.conf.default.accept_ra_defrtr[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv6.conf.default.accept_ra_defrtr[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv6.conf.default.accept_ra_defrtr[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.ipv6.conf.default.accept_ra_pinfo + + + oval:ssg-local_var_sysctl_net_ipv6_conf_default_accept_ra_pinfo_counter:var:1 + + + + oval:ssg-object_sysctl_net_ipv6_conf_default_accept_ra_pinfo_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_ipv6_conf_default_accept_ra_pinfo_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_ipv6_conf_default_accept_ra_pinfo:obj:1 + oval:ssg-var_obj_blank_sysctl_net_ipv6_conf_default_accept_ra_pinfo:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_ipv6_conf_default_accept_ra_pinfo:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_ipv6_conf_default_accept_ra_pinfo:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_ipv6_conf_default_accept_ra_pinfo:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_ipv6_conf_default_accept_ra_pinfo:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_ipv6_conf_default_accept_ra_pinfo:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_ipv6_conf_default_accept_ra_pinfo:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_ipv6_conf_default_accept_ra_pinfo:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_ipv6_conf_default_accept_ra_pinfo:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.ipv6.conf.default.accept_ra_pinfo[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv6.conf.default.accept_ra_pinfo[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv6.conf.default.accept_ra_pinfo[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.ipv6.conf.default.accept_ra_rtr_pref + + + oval:ssg-local_var_sysctl_net_ipv6_conf_default_accept_ra_rtr_pref_counter:var:1 + + + + oval:ssg-object_sysctl_net_ipv6_conf_default_accept_ra_rtr_pref_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_ipv6_conf_default_accept_ra_rtr_pref_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_ipv6_conf_default_accept_ra_rtr_pref:obj:1 + oval:ssg-var_obj_blank_sysctl_net_ipv6_conf_default_accept_ra_rtr_pref:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_ipv6_conf_default_accept_ra_rtr_pref:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_ipv6_conf_default_accept_ra_rtr_pref:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_ipv6_conf_default_accept_ra_rtr_pref:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_ipv6_conf_default_accept_ra_rtr_pref:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_ipv6_conf_default_accept_ra_rtr_pref:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_ipv6_conf_default_accept_ra_rtr_pref:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_ipv6_conf_default_accept_ra_rtr_pref:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_ipv6_conf_default_accept_ra_rtr_pref:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.ipv6.conf.default.accept_ra_rtr_pref[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv6.conf.default.accept_ra_rtr_pref[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv6.conf.default.accept_ra_rtr_pref[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.ipv6.conf.default.accept_redirects + + + oval:ssg-local_var_sysctl_net_ipv6_conf_default_accept_redirects_counter:var:1 + + + + oval:ssg-object_sysctl_net_ipv6_conf_default_accept_redirects_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_ipv6_conf_default_accept_redirects_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_ipv6_conf_default_accept_redirects:obj:1 + oval:ssg-var_obj_blank_sysctl_net_ipv6_conf_default_accept_redirects:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_ipv6_conf_default_accept_redirects:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_ipv6_conf_default_accept_redirects:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_ipv6_conf_default_accept_redirects:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_ipv6_conf_default_accept_redirects:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_ipv6_conf_default_accept_redirects:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_ipv6_conf_default_accept_redirects:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_ipv6_conf_default_accept_redirects:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_ipv6_conf_default_accept_redirects:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.ipv6.conf.default.accept_redirects[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv6.conf.default.accept_redirects[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv6.conf.default.accept_redirects[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.ipv6.conf.default.accept_source_route + + + oval:ssg-local_var_sysctl_net_ipv6_conf_default_accept_source_route_counter:var:1 + + + + oval:ssg-object_sysctl_net_ipv6_conf_default_accept_source_route_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_ipv6_conf_default_accept_source_route_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_ipv6_conf_default_accept_source_route:obj:1 + oval:ssg-var_obj_blank_sysctl_net_ipv6_conf_default_accept_source_route:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_ipv6_conf_default_accept_source_route:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_ipv6_conf_default_accept_source_route:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_ipv6_conf_default_accept_source_route:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_ipv6_conf_default_accept_source_route:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_ipv6_conf_default_accept_source_route:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_ipv6_conf_default_accept_source_route:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_ipv6_conf_default_accept_source_route:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_ipv6_conf_default_accept_source_route:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.ipv6.conf.default.accept_source_route[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv6.conf.default.accept_source_route[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv6.conf.default.accept_source_route[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.ipv6.conf.default.autoconf + + + oval:ssg-local_var_sysctl_net_ipv6_conf_default_autoconf_counter:var:1 + + + + oval:ssg-object_sysctl_net_ipv6_conf_default_autoconf_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_ipv6_conf_default_autoconf_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_ipv6_conf_default_autoconf:obj:1 + oval:ssg-var_obj_blank_sysctl_net_ipv6_conf_default_autoconf:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_ipv6_conf_default_autoconf:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_ipv6_conf_default_autoconf:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_ipv6_conf_default_autoconf:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_ipv6_conf_default_autoconf:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_ipv6_conf_default_autoconf:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_ipv6_conf_default_autoconf:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_ipv6_conf_default_autoconf:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_ipv6_conf_default_autoconf:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.ipv6.conf.default.autoconf[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv6.conf.default.autoconf[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv6.conf.default.autoconf[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.ipv6.conf.default.disable_ipv6 + + + oval:ssg-local_var_sysctl_net_ipv6_conf_default_disable_ipv6_counter:var:1 + + + + oval:ssg-object_sysctl_net_ipv6_conf_default_disable_ipv6_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_ipv6_conf_default_disable_ipv6_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_ipv6_conf_default_disable_ipv6:obj:1 + oval:ssg-var_obj_blank_sysctl_net_ipv6_conf_default_disable_ipv6:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_ipv6_conf_default_disable_ipv6:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_ipv6_conf_default_disable_ipv6:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_ipv6_conf_default_disable_ipv6:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_ipv6_conf_default_disable_ipv6:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_ipv6_conf_default_disable_ipv6:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_ipv6_conf_default_disable_ipv6:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_ipv6_conf_default_disable_ipv6:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_ipv6_conf_default_disable_ipv6:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.ipv6.conf.default.disable_ipv6[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv6.conf.default.disable_ipv6[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv6.conf.default.disable_ipv6[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.ipv6.conf.default.max_addresses + + + oval:ssg-local_var_sysctl_net_ipv6_conf_default_max_addresses_counter:var:1 + + + + oval:ssg-object_sysctl_net_ipv6_conf_default_max_addresses_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_ipv6_conf_default_max_addresses_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_ipv6_conf_default_max_addresses:obj:1 + oval:ssg-var_obj_blank_sysctl_net_ipv6_conf_default_max_addresses:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_ipv6_conf_default_max_addresses:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_ipv6_conf_default_max_addresses:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_ipv6_conf_default_max_addresses:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_ipv6_conf_default_max_addresses:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_ipv6_conf_default_max_addresses:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_ipv6_conf_default_max_addresses:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_ipv6_conf_default_max_addresses:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_ipv6_conf_default_max_addresses:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.ipv6.conf.default.max_addresses[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv6.conf.default.max_addresses[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv6.conf.default.max_addresses[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.ipv6.conf.default.router_solicitations + + + oval:ssg-local_var_sysctl_net_ipv6_conf_default_router_solicitations_counter:var:1 + + + + oval:ssg-object_sysctl_net_ipv6_conf_default_router_solicitations_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_ipv6_conf_default_router_solicitations_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_ipv6_conf_default_router_solicitations:obj:1 + oval:ssg-var_obj_blank_sysctl_net_ipv6_conf_default_router_solicitations:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_ipv6_conf_default_router_solicitations:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_ipv6_conf_default_router_solicitations:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_ipv6_conf_default_router_solicitations:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_ipv6_conf_default_router_solicitations:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_ipv6_conf_default_router_solicitations:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_ipv6_conf_default_router_solicitations:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_ipv6_conf_default_router_solicitations:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_ipv6_conf_default_router_solicitations:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.ipv6.conf.default.router_solicitations[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv6.conf.default.router_solicitations[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv6.conf.default.router_solicitations[\s]*=[\s]*(.*)[\s]*$ + 1 + + + user.max_user_namespaces + + + oval:ssg-local_var_sysctl_user_max_user_namespaces_counter:var:1 + + + + oval:ssg-object_sysctl_user_max_user_namespaces_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_user_max_user_namespaces_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_user_max_user_namespaces:obj:1 + oval:ssg-var_obj_blank_sysctl_user_max_user_namespaces:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_user_max_user_namespaces:var:1 + + + oval:ssg-local_var_symlinks_sysctl_user_max_user_namespaces:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_user_max_user_namespaces:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_user_max_user_namespaces:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_user_max_user_namespaces:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_user_max_user_namespaces:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_user_max_user_namespaces:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_user_max_user_namespaces:obj:1 + + + + /etc/sysctl.conf + ^[\s]*user.max_user_namespaces[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*user.max_user_namespaces[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*user.max_user_namespaces[\s]*=[\s]*(.*)[\s]*$ + 1 + + + vm.mmap_min_addr + + + oval:ssg-local_var_sysctl_vm_mmap_min_addr_counter:var:1 + + + + oval:ssg-object_sysctl_vm_mmap_min_addr_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_vm_mmap_min_addr_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_vm_mmap_min_addr:obj:1 + oval:ssg-var_obj_blank_sysctl_vm_mmap_min_addr:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_vm_mmap_min_addr:var:1 + + + oval:ssg-local_var_symlinks_sysctl_vm_mmap_min_addr:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_vm_mmap_min_addr:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_vm_mmap_min_addr:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_vm_mmap_min_addr:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_vm_mmap_min_addr:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_vm_mmap_min_addr:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_vm_mmap_min_addr:obj:1 + + + + /etc/sysctl.conf + ^[\s]*vm.mmap_min_addr[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*vm.mmap_min_addr[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*vm.mmap_min_addr[\s]*=[\s]*(.*)[\s]*$ + 1 + + + multi-user.target + + + dnf-automatic\.timer + ActiveState + + + ^/boot/loader/entries/.*.conf + ^options (.*)$ + 1 + + + ^/etc/kernel/cmdline + ^(.*)$ + 1 + + + ^/boot/loader/entries/.*.conf + ^options (.*)$ + 1 + + + ^/etc/kernel/cmdline + ^(.*)$ + 1 + + + ^/boot/loader/entries/.*.conf + ^options (.*)$ + 1 + + + ^/etc/kernel/cmdline + ^(.*)$ + 1 + + + ^/boot/loader/entries/.*.conf + ^options (.*)$ + 1 + + + ^/etc/kernel/cmdline + ^(.*)$ + 1 + + + ^/boot/loader/entries/.*.conf + ^options (.*)$ + 1 + + + ^/etc/kernel/cmdline + ^(.*)$ + 1 + + + /etc/pam.d/system-auth + ^\s*password\s+(?:(?:required)|(?:requisite))\s+pam_faillock\.so.*$ + 1 + + + + ^\s*password\s+(?:(?:required)|(?:requisite))\s+pam_pwquality\.so.*$ + 1 + + + /usr/lib/systemd/system/auditd.service + ^ExecStartPost=\-\/sbin\/auditctl.*$ + 1 + + + /usr/lib/systemd/system/auditd.service + ^(ExecStartPost=\-\/sbin\/augenrules.*$|Requires=augenrules.service) + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+setdomainname[\s]+|([\s]+|[,])setdomainname([\s]+|[,]))).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+setdomainname[\s]+|([\s]+|[,])setdomainname([\s]+|[,]))).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+setdomainname[\s]+|([\s]+|[,])setdomainname([\s]+|[,]))).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+setdomainname[\s]+|([\s]+|[,])setdomainname([\s]+|[,]))).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+sethostname[\s]+|([\s]+|[,])sethostname([\s]+|[,]))).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+sethostname[\s]+|([\s]+|[,])sethostname([\s]+|[,]))).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+sethostname[\s]+|([\s]+|[,])sethostname([\s]+|[,]))).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+sethostname[\s]+|([\s]+|[,])sethostname([\s]+|[,]))).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/auditd.conf + ^(log_file\s*=\s*.*)$ + 1 + + + /etc/audit/auditd.conf + ^[ ]*log_group[ ]+=[ ]+root[ ]*$ + 1 + + + /etc/audit/auditd.conf + ^[ ]*log_group[ ]+=.*$ + 1 + + + /etc/default/grub + ^\s*GRUB_DISABLE_RECOVERY=(.*)$ + 1 + + + ^/etc/chrony\.(conf|d/.+\.conf)$ + ^([\s]*server[\s]+.+$){2,}$ + 1 + + + /etc/default/grub + ^\s*GRUB_CMDLINE_LINUX_DEFAULT=.*$ + 1 + + + /boot/loader/entries/ + ^.*\.conf$ + ^options(?:\s+.*)?\s+\$kernelopts\b.*$ + 1 + + + alinux-release + + + alinux-release + + + centos-release + + + /etc/os-release + ^ID="(\w+)"$ + 1 + + + /etc/os-release + ^VERSION_ID="(\d)"$ + 1 + + + /etc/os-release + ^ID="(\w+)"$ + 1 + + + /etc/os-release + ^VERSION_ID="(\d)"$ + 1 + + + /etc/debian_version + + + /etc/debian_version + ^10.[0-9]+$ + 1 + + + /etc/debian_version + ^11.[0-9]+$ + 1 + + + /etc/debian_version + ^9.[0-9]+$ + 1 + + + fedora-release.* + + + /etc/system-release-cpe + ^cpe:\/o:fedoraproject:fedora:[\d]+$ + 1 + + + oraclelinux-release + + + oraclelinux-release + + + oraclelinux-release + + + openSUSE-release + + + openSUSE-release + + + openSUSE-release + + + + /etc/os-release + ^ID="(\w+)"$ + 1 + + + /etc/os-release + ^VERSION_ID="(\d)\.\d+"$ + 1 + + + + redhat-release-client + + + redhat-release-workstation + + + redhat-release-server + + + redhat-release-computenode + + + /etc/redhat-release + ^Red Hat Enterprise Linux release (\d)\.\d+$ + 1 + + + + redhat-release + + + redhat-release + + + redhat-release + + + redhat-release + + + redhat-release + + + redhat-release + + + redhat-release + + + redhat-release + + + redhat-release + + + redhat-release + + + redhat-release + + + redhat-release + + + /etc/redhat-release + ^Red Hat Enterprise Linux release (\d)\.\d+$ + 1 + + + + redhat-release + + + /etc/redhat-release + ^Red Hat Enterprise Linux release (\d)\.\d+$ + 1 + + + redhat-release-virtualization-host + + + sl-release + + + + sled-release + + + sles-release + + + SLES_SAP-release + + + + sled-release + + + sles-release + + + SLES_SAP-release + + + /etc/lsb-release + + + /etc/lsb-release + ^DISTRIB_ID=Ubuntu$ + 1 + + + /etc/lsb-release + ^DISTRIB_CODENAME=xenial$ + 1 + + + /etc/lsb-release + ^DISTRIB_CODENAME=bionic$ + 1 + + + /etc/lsb-release + ^DISTRIB_CODENAME=focal$ + 1 + + + /etc/lsb-release + ^DISTRIB_CODENAME=jammy$ + 1 + + + uos-release + + + rhvm-appliance + + + audit + + + chrony + + + gdm + + + grub2-common + + + /sys/firmware/opal + + + libuser + + + shadow-utils + + + net-snmp + + + nss-pam-ldapd + + + ntp + + + ovirt-host + + + ovirt-engine + + + pam + + + polkit + + + postfix + + + sssd-common + + + sudo + + + systemd + + + tftp-server + + + tmux + + + usbguard + + + /proc/net/wireless + + + yum + + + s390utils-base + + + /.dockerenv + + + /run/.containerenv + + + /tmp + + + /var/tmp + + + krb5-server + + + krb5-workstation + + + /etc/fstab + + 1 + + + /proc/sys/kernel/osrelease + ^.*\.(.*)$ + 1 + + + /proc/sys/kernel/osrelease + ^.*\.(.*)$ + 1 + + + /proc/sys/kernel/osrelease + ^.*\.(.*)$ + 1 + + + + + + oval:ssg-sshd_required:var:1 + + + oval:ssg-sshd_required:var:1 + + + oval:ssg-sshd_required:var:1 + + + openssh-server + + + /etc/sssd/sssd.conf + ^[\s]*\[domain\/[^]]*]([^\n\[\]]*\n+)+?[\s]*id_provider[ \t]*=[ \t]*((?i)ad)[ \t]*$ + 1 + + + /sys/firmware/efi + + + + + + + + + + /etc/tmux.conf + + + ^/etc/usbguard/(rules|rules\.d/.*)\.conf$ + ^.*\S+.*$ + 1 + + + oval:ssg-var_accounts_user_umask_umask_as_number:var:1 + + + oval:ssg-var_removable_partition:var:1 + + + oval:ssg-var_umask_for_daemons_umask_as_number:var:1 + + + + + ^(static|none)$ + + + 0 + + + false + false + false + false + false + false + false + + + false + false + false + false + false + false + false + false + false + + + false + false + false + false + false + false + false + false + false + + + false + false + false + false + false + false + false + false + false + + + false + false + false + false + false + false + false + false + false + + + + + + (?i)root + + + + + + ^permit_mynetworks,reject$ + + + ^.*,sec=krb5\:krb5i\:krb5p.*$ + + + 0 + + + 0 + + + + + + maxpoll \d+ + + + + + + 2 + sec=(krb5i|ntlmv2i) + + + symbolic link + + + /etc/ssh + .*_key$ + 0 + 0 + false + false + false + false + false + false + false + false + false + false + + + /etc/ssh + .*_key$ + + 0 + false + false + false + false + false + false + false + false + false + + + 32 + + + 32 + + + + + + + + + + + + 0 + + + + + + + + + 0 + + + + + + 0 + + + + + + 0 + + + 10 + + + 30 + + + 100 + + + + + + + + + + + + + + + ^.*pam.*$ + + + ^.*(try_cert_auth|require_cert_auth).*$ + + + ^.*allow_missing_name.*$ + + + + + + false + + + sssd + + + + + + + + + ^LinuxAudit$ + + + /etc/systemd/system/default.target + ^(/usr)?/lib/systemd/system/multi-user.target$ + + + /etc/pam.d/fingerprint-auth + /etc/authselect/fingerprint-auth + + + /etc/pam.d/password-auth + /etc/authselect/password-auth + + + /etc/pam.d/postlogin + /etc/authselect/postlogin + + + /etc/pam.d/smartcard-auth + /etc/authselect/smartcard-auth + + + /etc/pam.d/system-auth + /etc/authselect/system-auth + + + + + + + + + + + + faillog_t + + + + + + + + + + + + + + + 0 + + + /var/run/faillock + + + 2 + + + 2 + + + + + + + + + + + + 0 + + + + + + 5000 + + + /etc/systemd/system/ctrl-alt-del.target + /dev/null + + + 0 + + + 900 + + + + + + + + + + + + ^root$ + + + + + + + + + + + + + + + -1 + + + + + + + + + + + + + + + .* + + + + .* + + + + + + + + + + + + + ^[x*]$ + + + ^(!|!!|!\*|\*|!locked)$ + + + SHA-512 + + + .* + + + + + + + + + + + + + + + + + + 0 + + + 0 + + + + + + + + + + + + + + + + + + + + + directory + false + false + false + false + false + false + false + false + false + + + directory + false + false + false + false + false + false + false + false + false + + + + + + 1000 + + + + + + 1000 + + + regular + true + + + 1000 + + + + + + 1000 + + + ^\/[^\/\n]*\/[^\/\n]{1,}.*$ + + + 1000 + + + + + + 1000 + + + ^nobody$ + + + + + + 1000 + + + ^nobody$ + + + + + + 1000 + + + ^nobody$ + + + false + false + false + false + false + false + false + + + 1000 + + + ^nobody$ + + + + + + 1000 + + + ^nobody$ + + + + + + + + + 1000 + + + ^nobody$ + + + directory + false + false + false + false + false + false + false + + + 1000 + + + directory + false + false + false + false + false + false + false + + + true + true + + + symbolic link + + + ^[:\.] + + + :: + + + \.\. + + + [:\.]$ + + + ^[^/] + + + [^\\]:[^/] + + + + + + + + + + + + + + + 1000 + + + 0 + + + 0 + + + 0 + + + true + true + true + true + true + true + true + true + true + + + 0 + + + 0 + 0 + + + 0 + 0 + + + 0 + + + true + true + true + true + true + true + true + true + true + true + + + true + true + + + ^\/(dev|proc|sys)\/.*$ + + + + + + + + + + + + + + + + + + + + + + + + + + + SYSLOG + + + SINGLE + + + HALT + + + + + + SYSLOG + + + SINGLE + + + HALT + + + + + + + + + + + + + + + + + + rotate + + + single + + + + + + + + + + + + + + + ^(?i)hostname(?-i)$ + + + ^(?i)(syslog|single|halt)(?-i)$ + + + + + + + + + + + + + + + ^(?:.*\s)?random\.trust_cpu=on(?:\s.*)?$ + + + ^(?:.*\s)?random\.trust_cpu=off(?:\s.*)?$ + + + + + + ^['|\(](?!fd)(?!cd)(?!usb).*['|\)]$ + + + + + + ^['|\(](?!fd)(?!cd)(?!usb).*['|\)]$ + + + + + + + + + \bsystemd.debug-shell\b + + + \bsystemd.debug-shell\b + + + (?:file="[^\s;]+"|\$IncludeConfig[\s]+[^\s;]+|\/dev\/.*) + + + regular + 0 + + + (?:file="[^\s;]+"|\$IncludeConfig[\s]+[^\s;]+|\/dev\/.*) + + + regular + 0 + + + ^.*\/\..*$ + + + (?:file="[^\s;]+"|\$IncludeConfig[\s]+[^\s;]+|\/dev\/.*) + + + regular + false + false + false + false + false + false + false + + + (?=[\S\s]*\s(?i)protocol(?-i)="tcp")(?=[\S\s]*\s(?i)Target(?-i)="[^"]+?")(?=[\S\s]*\s(?i)port(?-i)="6514")(?=[\S\s]*\s(?i)StreamDriver(?-i)="gtls")(?=[\S\s]*\s(?i)StreamDriverMode(?-i)="1")(?=[\S\s]*\s(?i)StreamDriverAuthMode(?-i)="x509/name")(?=[\S\s]*\s(?i)StreamDriver\.CheckExtendedKeyPurpose(?-i)="on") + + + 0 + + + ResultActive=auth_admin + + + PROMISC + + + UP + + + 0 + true + + + false + true + + + 1000 + true + + + 1000 + true + + + 0 + + + false + false + false + false + false + false + false + false + false + false + + + true + + + + + + true + + + + + + regular + true + + + ^/selinux/(?:(?:member)|(?:user)|(?:relabel)|(?:create)|(?:access)|(?:context))$ + + + ^/proc/.*$ + + + ^/sys/.*$ + + + + + + + + + 1000 + + + 0 + + + true + true + + + symbolic link + + + ^.*\bnousb\b.*$ + + + ^/dev/.*$ + nodev + + + + + + ^(?i)0(?-i)$ + + + ^(?i)none(?-i)$ + + + 0 + + + 0 + + + + + + 1 + + + ^(block|character) special$ + + + device_t + + + unlabeled_t + + + unconfined_service_t + + + + + + + + + \blm\b + + + ^(x86_64|aarch64|ppc64le|s390x)$ + + + + + + + + + ^false$ + + + + + + + + + + + + + + + + + + + + + /etc/crypto-policies/back-ends/krb5.config + + + 1.2 + + + + + + + + + 0:20210617-1 + + + ^TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256$ + + + ^final all$ + + + ^512M 1h$ + + + ^no$ + + + ^aes256-ctr,aes256-cbc,aes128-ctr,aes128-cbc$ + + + ^ssh-rsa,ecdsa-sha2-nistp384,ecdsa-sha2-nistp256$ + + + ^hmac-sha2-512,hmac-sha2-256$ + + + ^ecdh-sha2-nistp521,ecdh-sha2-nistp384,ecdh-sha2-nistp256,diffie-hellman-group14-sha1$ + + + + + + + + + ^'-oCiphers=aes256-ctr,aes128-ctr,aes256-cbc,aes128-cbc -oMACs=hmac-sha2-512,hmac-sha2-256 -oGSSAPIKeyExchange=no -oKexAlgorithms=ecdh-sha2-nistp521,ecdh-sha2-nistp384,ecdh-sha2-nistp256,diffie-hellman-group14-sha1 -oHostKeyAlgorithms=ssh-rsa,ecdsa-sha2-nistp384,ecdsa-sha2-nistp256 -oPubkeyAcceptedKeyTypes=rsa-sha2-512,rsa-sha2-256,ssh-rsa,ecdsa-sha2-nistp384,ecdsa-sha2-nistp256'$ + + + + + + + + + /etc/profile.d/openssl-rand.sh + SHA-256 + 6488c757642cd493da09dd78ee27f039711a1ad79039900970553772fd2106af + + + + + + fips + + + ^FIPS(:(OSPP|NO-SHA1|NO-CAMELLIA))?$ + + + 1 + + + p\+i\+n\+u\+g\+s\+b\+acl(|\+selinux)\+xattrs\+sha512 + + + ^.*sha512.*$ + + + ^.*acl.*$ + + + ^.*xattrs.*$ + + + fail + false + false + + + fail + + + fail + + + fail + + + + + + 0 + + + /etc/sudoers.d + + + ^yes$ + + + ^security$ + + + 4ae0493b + fd431d51 + + + 5b32db75 + d4082792 + + + 5ccc5b19 + 8483c65d + + + + + + + + + + + + + + + + + + 0 + + + + + + + + + + + + + + + + + + ## Unsuccessful file access (any other opens) This has to go last. +-a always,exit -F arch=b32 -S open,openat,openat2,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-access +-a always,exit -F arch=b64 -S open,openat,openat2,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-access +-a always,exit -F arch=b32 -S open,openat,openat2,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-access +-a always,exit -F arch=b64 -S open,openat,openat2,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-access + + + + ## Successful file access (any other opens) This has to go last. +## These next two are likely to result in a whole lot of events +-a always,exit -F arch=b32 -S open,openat,openat2,open_by_handle_at -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-access +-a always,exit -F arch=b64 -S open,openat,openat2,open_by_handle_at -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-access + + + + ## First rule - delete all +-D + +## Increase the buffers to survive stress events. +## Make this bigger for busy systems +-b 8192 + +## This determine how long to wait in burst of events +--backlog_wait_time 60000 + +## Set failure mode to syslog +-f 1 + + + + + ## Unsuccessful file creation (open with O_CREAT) +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b32 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b64 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b32 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b64 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create + + + + ## Successful file creation (open with O_CREAT) +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create +-a always,exit -F arch=b32 -S open -F a1&0100 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create +-a always,exit -F arch=b64 -S open -F a1&0100 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create +-a always,exit -F arch=b32 -S creat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create +-a always,exit -F arch=b64 -S creat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create + + + + ## Unsuccessful file delete +-a always,exit -F arch=b32 -S unlink,unlinkat,rename,renameat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete +-a always,exit -F arch=b64 -S unlink,unlinkat,rename,renameat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete +-a always,exit -F arch=b32 -S unlink,unlinkat,rename,renameat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete +-a always,exit -F arch=b64 -S unlink,unlinkat,rename,renameat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete + + + + ## Successful file delete +-a always,exit -F arch=b32 -S unlink,unlinkat,rename,renameat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-delete +-a always,exit -F arch=b64 -S unlink,unlinkat,rename,renameat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-delete + + + + ## Make the loginuid immutable. This prevents tampering with the auid. +--loginuid-immutable + + + + + ## Unsuccessful file modifications (open for write or truncate) +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b32 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b32 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification + + + + ## Successful file modifications (open for write or truncate) +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification +-a always,exit -F arch=b32 -S open -F a1&01003 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification +-a always,exit -F arch=b64 -S open -F a1&01003 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification +-a always,exit -F arch=b32 -S truncate,ftruncate -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification +-a always,exit -F arch=b64 -S truncate,ftruncate -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification + + + + ## These rules watch for kernel module insertion. By monitoring +## the syscall, we do not need any watches on programs. +-a always,exit -F arch=b32 -S init_module,finit_module -F key=module-load +-a always,exit -F arch=b64 -S init_module,finit_module -F key=module-load +-a always,exit -F arch=b32 -S delete_module -F key=module-unload +-a always,exit -F arch=b64 -S delete_module -F key=module-unload + + + + ## The purpose of these rules is to meet the requirements for Operating +## System Protection Profile (OSPP)v4.2. These rules depends on having +## the following rule files copied to /etc/audit/rules.d: +## +## 10-base-config.rules, 11-loginuid.rules, +## 30-ospp-v42-1-create-failed.rules, 30-ospp-v42-1-create-success.rules, +## 30-ospp-v42-2-modify-failed.rules, 30-ospp-v42-2-modify-success.rules, +## 30-ospp-v42-3-access-failed.rules, 30-ospp-v42-3-access-success.rules, +## 30-ospp-v42-4-delete-failed.rules, 30-ospp-v42-4-delete-success.rules, +## 30-ospp-v42-5-perm-change-failed.rules, +## 30-ospp-v42-5-perm-change-success.rules, +## 30-ospp-v42-6-owner-change-failed.rules, +## 30-ospp-v42-6-owner-change-success.rules +## +## original copies may be found in /usr/share/audit/sample-rules/ + + +## User add delete modify. This is covered by pam. However, someone could +## open a file and directly create or modify a user, so we'll watch passwd and +## shadow for writes +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b32 -S open -F a1&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b64 -S open -F a1&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b32 -S open -F a1&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b64 -S open -F a1&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify + +## User enable and disable. This is entirely handled by pam. + +## Group add delete modify. This is covered by pam. However, someone could +## open a file and directly create or modify a user, so we'll watch group and +## gshadow for writes +-a always,exit -F path=/etc/passwd -F perm=wa -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F path=/etc/shadow -F perm=wa -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F path=/etc/group -F perm=wa -F auid>=1000 -F auid!=unset -F key=group-modify +-a always,exit -F path=/etc/gshadow -F perm=wa -F auid>=1000 -F auid!=unset -F key=group-modify + + +## Use of special rights for config changes. This would be use of setuid +## programs that relate to user accts. This is not all setuid apps because +## requirements are only for ones that affect system configuration. +-a always,exit -F path=/usr/sbin/unix_chkpwd -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/sbin/usernetctl -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/sbin/userhelper -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/sbin/seunshare -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/mount -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/newgrp -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/newuidmap -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/gpasswd -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/newgidmap -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/umount -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/passwd -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/crontab -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/at -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes + +## Privilege escalation via su or sudo. This is entirely handled by pam. + +## Watch for configuration changes to privilege escalation. +-a always,exit -F path=/etc/sudoers -F perm=wa -F key=special-config-changes +-a always,exit -F dir=/etc/sudoers.d/ -F perm=wa -F key=special-config-changes + +## Audit log access +-a always,exit -F dir=/var/log/audit/ -F perm=r -F auid>=1000 -F auid!=unset -F key=access-audit-trail +## Attempts to Alter Process and Session Initiation Information +-a always,exit -F path=/var/run/utmp -F perm=wa -F auid>=1000 -F auid!=unset -F key=session +-a always,exit -F path=/var/log/btmp -F perm=wa -F auid>=1000 -F auid!=unset -F key=session +-a always,exit -F path=/var/log/wtmp -F perm=wa -F auid>=1000 -F auid!=unset -F key=session + +## Attempts to modify MAC controls +-a always,exit -F dir=/etc/selinux/ -F perm=wa -F auid>=1000 -F auid!=unset -F key=MAC-policy + +## Software updates. This is entirely handled by rpm. + +## System start and shutdown. This is entirely handled by systemd + +## Kernel Module loading. This is handled in 43-module-load.rules + +## Application invocation. The requirements list an user requirement +## FPT_SRP_EXT.1 Software Restriction Policies. This event is intended to +## state results from that policy. This would be handled entirely by +## that daemon. + + + + + ## Unsuccessful ownership change +-a always,exit -F arch=b32 -S lchown,fchown,chown,fchownat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-owner-change +-a always,exit -F arch=b64 -S lchown,fchown,chown,fchownat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-owner-change +-a always,exit -F arch=b32 -S lchown,fchown,chown,fchownat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-owner-change +-a always,exit -F arch=b64 -S lchown,fchown,chown,fchownat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-owner-change + + + + ## Successful ownership change +-a always,exit -F arch=b32 -S lchown,fchown,chown,fchownat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-owner-change +-a always,exit -F arch=b64 -S lchown,fchown,chown,fchownat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-owner-change + + + + ## Unsuccessful permission change +-a always,exit -F arch=b32 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-perm-change +-a always,exit -F arch=b64 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-perm-change +-a always,exit -F arch=b32 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-perm-change +-a always,exit -F arch=b64 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-perm-change + + + + ## Successful permission change +-a always,exit -F arch=b32 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change +-a always,exit -F arch=b64 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + + + + ^(?i)50(?-i)$ + + + ^(?i)yes(?-i)$ + + + ^(?i)ENRICHED(?-i)$ + + + ^(?i)yes(?-i)$ + + + ^(?:.*\s)?selinux=0(?:\s.*)?$ + + + ^(?:.*\s)?selinux=0(?:\s.*)?$ + + + ^(?:.*\s)?selinux=0(?:\s.*)?$ + + + ^true$ + + + ^'lock-screen'$ + + + 0 + + + 0 + + + 0 + + + 0 + + + symbolic link + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + symbolic link + + + 0 + + + 0 + + + 0 + + + 0 + + + symbolic link + + + false + false + false + false + false + + + false + false + false + false + false + + + false + false + false + false + false + + + false + false + false + false + false + + + false + false + false + false + false + + + false + false + false + false + false + + + symbolic link + + + false + false + + + false + false + + + false + false + + + false + false + + + symbolic link + + + ^no$ + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + symbolic link + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + symbolic link + + + false + false + false + false + false + + + false + false + false + false + false + + + false + false + false + false + false + + + false + false + false + false + false + + + false + false + false + false + false + + + false + false + false + false + false + + + false + false + false + false + false + + + symbolic link + + + 0 + + + symbolic link + + + 0 + + + symbolic link + + + 0 + + + symbolic link + + + 0 + + + symbolic link + + + 0 + + + symbolic link + + + 0 + + + symbolic link + + + 0 + + + symbolic link + + + 0 + + + symbolic link + + + 0 + + + symbolic link + + + 0 + + + symbolic link + + + 0 + + + symbolic link + + + 0 + + + symbolic link + + + 0 + + + symbolic link + + + 0 + + + symbolic link + + + 0 + + + symbolic link + + + 0 + + + symbolic link + + + 0 + + + symbolic link + + + 0 + + + symbolic link + + + 0 + + + symbolic link + + + 0 + + + symbolic link + + + 0 + + + symbolic link + + + 0 + + + symbolic link + + + 0 + + + symbolic link + + + 4 + + + symbolic link + + + 0 + + + 0 + + + symbolic link + + + 0 + + + symbolic link + + + 0 + + + symbolic link + + + 0 + + + symbolic link + + + 0 + + + symbolic link + + + 0 + + + symbolic link + + + 0 + + + symbolic link + + + 0 + + + symbolic link + + + 0 + + + symbolic link + + + 0 + + + symbolic link + + + 0 + + + symbolic link + + + 0 + + + symbolic link + + + 0 + + + symbolic link + + + 0 + + + symbolic link + + + 0 + + + symbolic link + + + 0 + + + symbolic link + + + 0 + + + symbolic link + + + 0 + + + symbolic link + + + 0 + + + symbolic link + + + 0 + + + symbolic link + + + 0 + + + symbolic link + + + 0 + + + symbolic link + + + 0 + + + symbolic link + + + 104 + + + symbolic link + + + 0 + + + 0 + + + symbolic link + + + 0 + + + 0 + + + 0 + + + 0 + + + symbolic link + + + false + false + false + false + false + false + false + false + false + false + + + symbolic link + + + false + false + false + false + false + false + false + false + + + symbolic link + + + false + false + false + false + false + false + false + false + false + false + false + false + + + symbolic link + + + false + false + false + false + false + false + false + false + + + symbolic link + + + false + false + false + false + false + false + false + false + false + false + false + false + + + symbolic link + + + false + false + false + false + false + false + false + false + false + false + + + symbolic link + + + false + false + false + false + false + false + false + false + false + + + symbolic link + + + false + false + false + false + false + false + false + false + false + + + symbolic link + + + false + false + false + false + false + false + false + false + false + + + symbolic link + + + false + false + false + false + false + false + false + false + false + + + symbolic link + + + false + false + false + false + false + false + false + false + false + + + symbolic link + + + false + false + false + false + false + false + false + false + false + false + + + symbolic link + + + false + false + false + false + false + false + false + false + false + + + symbolic link + + + false + false + false + false + false + false + false + false + false + + + symbolic link + + + false + false + false + false + false + false + false + false + false + + + symbolic link + + + false + false + false + false + false + false + false + false + + + symbolic link + + + false + false + false + false + false + false + false + false + false + false + false + false + + + symbolic link + + + false + false + false + false + false + false + false + false + + + symbolic link + + + false + false + false + false + false + false + false + false + + + symbolic link + + + false + false + false + false + false + false + false + false + + + symbolic link + + + false + false + false + false + false + false + false + false + false + false + false + false + + + symbolic link + + + false + false + false + false + false + false + false + false + false + false + + + symbolic link + + + false + false + + + false + false + + + false + false + + + false + false + + + symbolic link + + + false + false + false + false + false + false + false + false + false + false + + + symbolic link + + + false + false + false + false + false + false + false + false + + + symbolic link + + + false + false + false + false + false + + + symbolic link + + + false + false + false + false + false + false + false + false + false + + + symbolic link + + + false + false + false + false + false + false + false + false + false + + + symbolic link + + + ^((?:"|')?)nftables\1$ + + + ^(?:.*\s)?audit=1(?:\s.*)?$ + + + ^(?:.*\s)?audit_backlog_limit=8192(?:\s.*)?$ + + + ^(?:.*\s)?iommu=force(?:\s.*)?$ + + + ^(?:.*\s)?ipv6\.disable=1(?:\s.*)?$ + + + + + + ^(?:.*\s)?mce=0(?:\s.*)?$ + + + ^(?:.*\s)?page_poison=1(?:\s.*)?$ + + + ^(?:.*\s)?pti=on(?:\s.*)?$ + + + + + + ^(?:.*\s)?slab_nomerge=yes(?:\s.*)?$ + + + + + + + + + ^(?:.*\s)?spectre_v2=on(?:\s.*)?$ + + + ^(?:.*\s)?vsyscall=none(?:\s.*)?$ + + + ^((?:"|')?)yes\1$ + + + ^((?:"|')?)yes\1$ + + + ^((?:"|')?)persistent\1$ + + + n + + + + + + y + + + + + + n + + + + + + y + + + + + + y + + + + + + n + + + + + + n + + + + + + y + + + + + + n + + + + + + y + + + + + + y + + + + + + y + + + + + + y + + + + + + 65536 + + + + + + n + + + + + + y + + + + + + y + + + + + + y + + + + + + y + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + y + + + + + + n + + + + + + y + + + + + + y + + + + + + y + + + + + + + + + + + + + + + + + + y + + + + + + y + + + + + + y + + + + + + y + + + + + + y + + + + + + y + + + + + + + + + + + + n + + + + + + y + + + + + + y + + + + + + y + + + + + + y + + + + + + y + + + + + + y + + + + + + y + + + + + + y + + + + + + n + + + + + + y + + + + + + y + + + + + + y + + + + + + y + + + + + + n + + + + + + y + + + + + + y + + + + + + y + + + + + + y + + + + + + y + + + + + + y + + + + + + y + + + + + + y + + + + + + n + + + + + + nosuid + + + noauto + + + nodev + + + noexec + + + nosuid + + + nodev + + + noexec + + + nosuid + + + nodev + + + noexec + + + nosuid + + + ^.*sec=krb5:krb5i:krb5p.*$ + + + ^.*nodev.*$ + + + ^.*,?nodev,?.*$ + + + ^.*,?nodev,?.* + + + ^.*noexec.*$ + + + ^.*,?noexec,?.*$ + + + ^.*,?noexec,?.* + + + ^.*nosuid.*$ + + + ^.*,?nosuid,?.*$ + + + ^.*,?nosuid,?.* + + + nosuid + + + + + + nosuid + + + nodev + + + noexec + + + nosuid + + + nodev + + + noexec + + + nosuid + + + nodev + + + noexec + + + nosuid + + + nodev + + + noexec + + + nosuid + + + nodev + + + noexec + + + nosuid + + + 0 + + + 0 + + + 0 + + + 0 + + + symbolic link + + + abrt_anon_write + + + + + abrt_handle_event + + + + + abrt_upload_watch_anon_write + + + + + antivirus_can_scan_system + + + + + antivirus_use_jit + + + + + auditadm_exec_content + + + + + authlogin_nsswitch_use_ldap + + + + + authlogin_radius + + + + + authlogin_yubikey + + + + + awstats_purge_apache_log_files + + + + + boinc_execmem + + + + + cdrecord_read_content + + + + + cluster_can_network_connect + + + + + cluster_manage_all_files + + + + + cluster_use_execmem + + + + + cobbler_anon_write + + + + + cobbler_can_network_connect + + + + + cobbler_use_cifs + + + + + cobbler_use_nfs + + + + + collectd_tcp_network_connect + + + + + condor_tcp_network_connect + + + + + conman_can_network + + + + + container_connect_any + + + + + cron_can_relabel + + + + + cron_system_cronjob_use_shares + + + + + cron_userdomain_transition + + + + + cups_execmem + + + + + cvs_read_shadow + + + + + daemons_dump_core + + + + + daemons_enable_cluster_mode + + + + + daemons_use_tcp_wrapper + + + + + daemons_use_tty + + + + + dbadm_exec_content + + + + + dbadm_manage_user_files + + + + + dbadm_read_user_files + + + + + deny_execmem + + + + + deny_ptrace + + + + + dhcpc_exec_iptables + + + + + dhcpd_use_ldap + + + + + domain_fd_use + + + + + domain_kernel_load_modules + + + + + entropyd_use_audio + + + + + exim_can_connect_db + + + + + exim_manage_user_files + + + + + exim_read_user_files + + + + + fcron_crond + + + + + fenced_can_network_connect + + + + + fenced_can_ssh + + + + + fips_mode + + + + + ftpd_anon_write + + + + + ftpd_connect_all_unreserved + + + + + ftpd_connect_db + + + + + ftpd_full_access + + + + + ftpd_use_cifs + + + + + ftpd_use_fusefs + + + + + ftpd_use_nfs + + + + + ftpd_use_passive_mode + + + + + git_cgi_enable_homedirs + + + + + git_cgi_use_cifs + + + + + git_cgi_use_nfs + + + + + git_session_bind_all_unreserved_ports + + + + + git_session_users + + + + + git_system_enable_homedirs + + + + + git_system_use_cifs + + + + + git_system_use_nfs + + + + + gitosis_can_sendmail + + + + + glance_api_can_network + + + + + glance_use_execmem + + + + + glance_use_fusefs + + + + + global_ssp + + + + + gluster_anon_write + + + + + gluster_export_all_ro + + + + + gluster_export_all_rw + + + + + gpg_web_anon_write + + + + + gssd_read_tmp + + + + + guest_exec_content + + + + + haproxy_connect_any + + + + + httpd_anon_write + + + + + httpd_builtin_scripting + + + + + httpd_can_check_spam + + + + + httpd_can_connect_ftp + + + + + httpd_can_connect_ldap + + + + + httpd_can_connect_mythtv + + + + + httpd_can_connect_zabbix + + + + + httpd_can_network_connect + + + + + httpd_can_network_connect_cobbler + + + + + httpd_can_network_connect_db + + + + + httpd_can_network_memcache + + + + + httpd_can_network_relay + + + + + httpd_can_sendmail + + + + + httpd_dbus_avahi + + + + + httpd_dbus_sssd + + + + + httpd_dontaudit_search_dirs + + + + + httpd_enable_cgi + + + + + httpd_enable_ftp_server + + + + + httpd_enable_homedirs + + + + + httpd_execmem + + + + + httpd_graceful_shutdown + + + + + httpd_manage_ipa + + + + + httpd_mod_auth_ntlm_winbind + + + + + httpd_mod_auth_pam + + + + + httpd_read_user_content + + + + + httpd_run_ipa + + + + + httpd_run_preupgrade + + + + + httpd_run_stickshift + + + + + httpd_serve_cobbler_files + + + + + httpd_setrlimit + + + + + httpd_ssi_exec + + + + + httpd_sys_script_anon_write + + + + + httpd_tmp_exec + + + + + httpd_tty_comm + + + + + httpd_unified + + + + + httpd_use_cifs + + + + + httpd_use_fusefs + + + + + httpd_use_gpg + + + + + httpd_use_nfs + + + + + httpd_use_openstack + + + + + httpd_use_sasl + + + + + httpd_verify_dns + + + + + icecast_use_any_tcp_ports + + + + + irc_use_any_tcp_ports + + + + + irssi_use_full_network + + + + + kdumpgui_run_bootloader + + + + + kerberos_enabled + + + + + ksmtuned_use_cifs + + + + + ksmtuned_use_nfs + + + + + logadm_exec_content + + + + + logging_syslogd_can_sendmail + + + + + logging_syslogd_run_nagios_plugins + + + + + logging_syslogd_use_tty + + + + + login_console_enabled + + + + + logrotate_use_nfs + + + + + logwatch_can_network_connect_mail + + + + + lsmd_plugin_connect_any + + + + + mailman_use_fusefs + + + + + mcelog_client + + + + + mcelog_exec_scripts + + + + + mcelog_foreground + + + + + mcelog_server + + + + + minidlna_read_generic_user_content + + + + + mmap_low_allowed + + + + + mock_enable_homedirs + + + + + mount_anyfile + + + + + mozilla_plugin_bind_unreserved_ports + + + + + mozilla_plugin_can_network_connect + + + + + mozilla_plugin_use_bluejeans + + + + + mozilla_plugin_use_gps + + + + + mozilla_plugin_use_spice + + + + + mozilla_read_content + + + + + mpd_enable_homedirs + + + + + mpd_use_cifs + + + + + mpd_use_nfs + + + + + mplayer_execstack + + + + + mysql_connect_any + + + + + nagios_run_pnp4nagios + + + + + nagios_run_sudo + + + + + named_tcp_bind_http_port + + + + + named_write_master_zones + + + + + neutron_can_network + + + + + nfs_export_all_ro + + + + + nfs_export_all_rw + + + + + nfsd_anon_write + + + + + nis_enabled + + + + + nscd_use_shm + + + + + openshift_use_nfs + + + + + openvpn_can_network_connect + + + + + openvpn_enable_homedirs + + + + + openvpn_run_unconfined + + + + + pcp_bind_all_unreserved_ports + + + + + pcp_read_generic_logs + + + + + piranha_lvs_can_network_connect + + + + + polipo_connect_all_unreserved + + + + + polipo_session_bind_all_unreserved_ports + + + + + polipo_session_users + + + + + polipo_use_cifs + + + + + polipo_use_nfs + + + + + polyinstantiation_enabled + + + + + postfix_local_write_mail_spool + + + + + postgresql_can_rsync + + + + + postgresql_selinux_transmit_client_label + + + + + postgresql_selinux_unconfined_dbadm + + + + + postgresql_selinux_users_ddl + + + + + pppd_can_insmod + + + + + pppd_for_user + + + + + privoxy_connect_any + + + + + prosody_bind_http_port + + + + + puppetagent_manage_all_files + + + + + puppetmaster_use_db + + + + + racoon_read_shadow + + + + + rsync_anon_write + + + + + rsync_client + + + + + rsync_export_all_ro + + + + + rsync_full_access + + + + + samba_create_home_dirs + + + + + samba_domain_controller + + + + + samba_enable_home_dirs + + + + + samba_export_all_ro + + + + + samba_export_all_rw + + + + + samba_load_libgfapi + + + + + samba_portmapper + + + + + samba_run_unconfined + + + + + samba_share_fusefs + + + + + samba_share_nfs + + + + + sanlock_use_fusefs + + + + + sanlock_use_nfs + + + + + sanlock_use_samba + + + + + saslauthd_read_shadow + + + + + secadm_exec_content + + + + + secure_mode + + + + + secure_mode_insmod + + + + + secure_mode_policyload + + + + + selinuxuser_direct_dri_enabled + + + + + selinuxuser_execheap + + + + + selinuxuser_execmod + + + + + selinuxuser_execstack + + + + + selinuxuser_mysql_connect_enabled + + + + + selinuxuser_ping + + + + + selinuxuser_postgresql_connect_enabled + + + + + selinuxuser_rw_noexattrfile + + + + + selinuxuser_share_music + + + + + selinuxuser_tcp_server + + + + + selinuxuser_udp_server + + + + + selinuxuser_use_ssh_chroot + + + + + sge_domain_can_network_connect + + + + + sge_use_nfs + + + + + smartmon_3ware + + + + + smbd_anon_write + + + + + spamassassin_can_network + + + + + spamd_enable_home_dirs + + + + + squid_connect_any + + + + + squid_use_tproxy + + + + + ssh_chroot_rw_homedirs + + + + + ssh_keysign + + + + + ssh_sysadm_login + + + + + staff_exec_content + + + + + staff_use_svirt + + + + + swift_can_network + + + + + sysadm_exec_content + + + + + telepathy_connect_all_ports + + + + + telepathy_tcp_connect_generic_network_ports + + + + + tftp_anon_write + + + + + tftp_home_dir + + + + + tmpreaper_use_nfs + + + + + tmpreaper_use_samba + + + + + tor_bind_all_unreserved_ports + + + + + tor_can_network_relay + + + + + unconfined_chrome_sandbox_transition + + + + + unconfined_login + + + + + unconfined_mozilla_plugin_transition + + + + + unprivuser_use_svirt + + + + + use_ecryptfs_home_dirs + + + + + use_fusefs_home_dirs + + + + + use_lpd_server + + + + + use_nfs_home_dirs + + + + + use_samba_home_dirs + + + + + user_exec_content + + + + + varnishd_connect_any + + + + + virt_read_qemu_ga_data + + + + + virt_rw_qemu_ga_data + + + + + virt_sandbox_use_all_caps + + + + + virt_sandbox_use_audit + + + + + virt_sandbox_use_mknod + + + + + virt_sandbox_use_netlink + + + + + virt_sandbox_use_sys_admin + + + + + virt_transition_userdomain + + + + + virt_use_comm + + + + + virt_use_execmem + + + + + virt_use_fusefs + + + + + virt_use_nfs + + + + + virt_use_rawip + + + + + virt_use_samba + + + + + virt_use_sanlock + + + + + virt_use_usb + + + + + virt_use_xserver + + + + + webadm_manage_user_files + + + + + webadm_read_user_files + + + + + wine_mmap_zero_ignore + + + + + xdm_bind_vnc_tcp_port + + + + + xdm_exec_bootloader + + + + + xdm_sysadm_login + + + + + xdm_write_home + + + + + xen_use_nfs + + + + + xend_run_blktap + + + + + xend_run_qemu + + + + + xguest_connect_network + + + + + xguest_exec_content + + + + + xguest_mount_media + + + + + xguest_use_bluetooth + + + + + xserver_clients_write_xshm + + + + + xserver_execmem + + + + + xserver_object_manager + + + + + zabbix_can_network + + + + + zarafa_setrlimit + + + + + zebra_write_config + + + + + zoneminder_anon_write + + + + + zoneminder_run_sudo + + + + + inactive|failed + + + masked + + + inactive|failed + + + masked + + + inactive|failed + + + masked + + + auditd.service + + + auditd.socket + + + active + + + inactive|failed + + + masked + + + inactive|failed + + + masked + + + inactive|failed + + + masked + + + inactive|failed + + + masked + + + chronyd.service + + + chronyd.socket + + + active + + + inactive|failed + + + masked + + + inactive|failed + + + masked + + + cron.service + + + cron.socket + + + active + + + crond.service + + + crond.socket + + + active + + + inactive|failed + + + masked + + + inactive|failed + + + masked + + + inactive|failed + + + masked + + + inactive|failed + + + masked + + + fapolicyd.service + + + fapolicyd.socket + + + active + + + firewalld.service + + + firewalld.socket + + + active + + + inactive|failed + + + masked + + + ip6tables.service + + + ip6tables.socket + + + active + + + iptables.service + + + iptables.socket + + + active + + + inactive|failed + + + masked + + + inactive|failed + + + masked + + + nails.service + + + nails.socket + + + active + + + inactive|failed + + + masked + + + inactive|failed + + + masked + + + inactive|failed + + + masked + + + inactive|failed + + + masked + + + inactive|failed + + + masked + + + ntp.service + + + ntp.socket + + + active + + + ntpd.service + + + ntpd.socket + + + active + + + inactive|failed + + + masked + + + inactive|failed + + + masked + + + pcscd.service + + + pcscd.socket + + + active + + + inactive|failed + + + masked + + + postfix.service + + + postfix.socket + + + active + + + psacct.service + + + psacct.socket + + + active + + + inactive|failed + + + masked + + + inactive|failed + + + masked + + + inactive|failed + + + masked + + + inactive|failed + + + masked + + + inactive|failed + + + masked + + + inactive|failed + + + masked + + + inactive|failed + + + masked + + + rngd.service + + + rngd.socket + + + active + + + inactive|failed + + + masked + + + inactive|failed + + + masked + + + inactive|failed + + + masked + + + inactive|failed + + + masked + + + inactive|failed + + + masked + + + inactive|failed + + + masked + + + rsyslog.service + + + rsyslog.socket + + + active + + + inactive|failed + + + masked + + + inactive|failed + + + masked + + + inactive|failed + + + masked + + + inactive|failed + + + masked + + + inactive|failed + + + masked + + + inactive|failed + + + masked + + + sshd.service + + + sshd.socket + + + active + + + sssd.service + + + sssd.socket + + + active + + + inactive|failed + + + masked + + + syslog-ng.service + + + syslog-ng.socket + + + active + + + inactive|failed + + + masked + + + inactive|failed + + + masked + + + systemd-journald.service + + + systemd-journald.socket + + + active + + + inactive|failed + + + masked + + + inactive|failed + + + masked + + + ufw.service + + + ufw.socket + + + active + + + usbguard.service + + + usbguard.socket + + + active + + + inactive|failed + + + masked + + + inactive|failed + + + masked + + + inactive|failed + + + masked + + + inactive|failed + + + masked + + + inactive|failed + + + masked + + + ^no$ + + + ^no$ + + + ^no$ + + + ^no$ + + + ^yes$ + + + ^no$ + + + ^prohibit-password$ + + + ^no$ + + + ^yes$ + + + ^no$ + + + ^no$ + + + ^yes$ + + + ^yes$ + + + ^yes$ + + + ^yes$ + + + ^/etc/issue$ + + + ^/etc/issue.net$ + + + ^yes$ + + + ^yes$ + + + ^0$ + + + ^INFO$ + + + ^VERBOSE$ + + + ^32$ + + + ^yes$ + + + + + + + + + + + + false + false + false + false + false + false + false + false + false + + + symbolic link + + + 1 + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + 1 + + + 1 + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + 1 + + + 0 + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + 0 + + + |/bin/false + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + |/bin/false + + + 0 + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + 0 + + + 1 + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + 1 + + + 1 + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + 1 + + + 1 + + + 2 + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + 1 + + + 2 + + + 1 + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + 1 + + + 1 + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + 1 + + + 1 + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + 1 + + + 1 + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + 1 + + + 2 + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + 2 + + + 65536 + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + 65536 + + + 2 + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + 2 + + + 0 + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + 0 + + + 1 + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + 1 + + + 1 + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + 1 + + + 2 + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + 2 + + + 0 + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + 0 + + + + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + + + + + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + + + + + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + + + + + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + + + + 1 + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + 1 + + + + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + + + + + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + + + + 0 + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + 0 + + + 1 + + + 2 + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + 1 + + + 2 + + + + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + + + + 0 + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + 0 + + + + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + + + + + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + + + + + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + + + + + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + + + + + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + + + + + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + + + + 0 + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + 0 + + + + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + + + + + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + + + + + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + + + + 0 + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + 0 + + + 32768\s*65535 + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + 32768\s*65535 + + + + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + + + + + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + + + + + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + + + + + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + + + + + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + + + + + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + + + + + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + + + + + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + + + + + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + + + + + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + + + + 1 + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + 1 + + + + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + + + + + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + + + + + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + + + + + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + + + + + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + + + + + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + + + + + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + + + + + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + + + + + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + + + + + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + + + + 1 + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + 1 + + + + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + + + + + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + + + + 0 + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + 0 + + + 65536 + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + 65536 + + + dnf-automatic.timer + + + active + + + ^(?:.*\s)?audit=1(?:\s.*)?$ + + + ^(?:.*\s)?audit=1(?:\s.*)?$ + + + ^(?:.*\s)?audit_backlog_limit=8192(?:\s.*)?$ + + + ^(?:.*\s)?audit_backlog_limit=8192(?:\s.*)?$ + + + ^(?:.*\s)?page_poison=1(?:\s.*)?$ + + + ^(?:.*\s)?page_poison=1(?:\s.*)?$ + + + ^(?:.*\s)?slub_debug=P(?:\s.*)?$ + + + ^(?:.*\s)?slub_debug=P(?:\s.*)?$ + + + ^(?:.*\s)?vsyscall=none(?:\s.*)?$ + + + ^(?:.*\s)?vsyscall=none(?:\s.*)?$ + + + ^(true|"true")$ + + + ^2.*$ + + + ^3.*$ + + + ^7.*$ + + + centos + + + 8 + + + centos + + + 9 + + + ^7.*$ + + + ^8.*$ + + + ^9.*$ + + + openSUSE-release + + + ^15.*$ + + + ^42.*$ + + + unix + + + rhcos + + + 4 + + + unix + + + ^7.*$ + + + ^7.*$ + + + ^7.*$ + + + ^7.*$ + + + 7 + + + unix + + + ^8.*$ + + + ^8.0*$ + + + ^8.1*$ + + + ^8.2*$ + + + ^8.3*$ + + + ^8.4*$ + + + ^8.5*$ + + + ^8.6*$ + + + ^8.7*$ + + + ^8.8*$ + + + ^8.9*$ + + + ^8.10*$ + + + 8 + + + unix + + + ^9.*$ + + + 9 + + + 0:4.4 + + + ^7.*$ + + + unix + + + ^12.*$ + + + ^12.*$ + + + ^12.*$ + + + unix + + + ^15.*$ + + + ^15.*$ + + + ^15.*$ + + + ^20.*$ + + + ^4.*$ + + + 0:1.17-18 + + + 0:1.17-18 + + + ^aarch64$ + + + ^ppc64le$ + + + ^s390x$ + + + 1 + + + 2 + + + 0 + + + 0:7.4 + + + aarch64 + + + ppc64 + + + ppc64le + + + s390x + + + i686 + + + x86_64 + + + true + + + /dev/cdrom + + + + + + + + + + + + + ^[\s]*RekeyLimit[\s]+ + + [\s]+ + + [\s]*$ + + + + + + + + + + + + + .xml + + + + + + + ^[\s]*RekeyLimit[\s]+ + + [\s]+ + + [\s]*$ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ^[\s]*auth[\s]+(?:required|requisite)[\s]+pam_faillock.so[^\n#]preauth[^\n#]*audit + + + + ^\s*password\s+(?: + + )\s+pam_pwhistory\.so.*remember=([0-9]*).*$ + + + + + + + + + + + + ^\s*password\s+(?: + + )\s+pam_pwhistory\.so.*remember=([0-9]*).*$ + + + + + + + + + + ^[\s]*auth\N+pam_unix\.so + + + ^[\s]*auth[\s]+(required|\[(?=.*?\bsuccess=ok\b)(?=.*?\bnew_authtok_reqd=ok\b)(?=.*?\bignore=ignore\b)(?=.*?\bdefault=bad\b).*\])[\s]+pam_faillock\.so[\s\w\d=]+preauth[\s\S]*^[\s]*auth[\s]+(sufficient|\[(?=.*\bsuccess=done\b)(?=.*?\bnew_authtok_reqd=done\b)(?=.*?\bdefault=ignore\b).*\])[\s]+pam_unix\.so[\s\S]*^[\s]*auth[\s]+(required|\[(?=.*?\bsuccess=ok\b)(?=.*?\bnew_authtok_reqd=ok\b)(?=.*?\bignore=ignore\b)(?=.*?\bdefault=bad\b).*\])[\s]+pam_faillock\.so[\s\w\d=]+authfail + + + ^[\s]*account[\s]+(required|\[(?=.*?\bsuccess=ok\b)(?=.*?\bnew_authtok_reqd=ok\b)(?=.*?\bignore=ignore\b)(?=.*?\bdefault=bad\b).*\])[\s]+pam_faillock\.so[\s\S]*^[\s]*account[\s]+(required|\[(?=.*?\bsuccess=ok\b)(?=.*?\bnew_authtok_reqd=ok\b)(?=.*?\bignore=ignore\b)(?=.*?\bdefault=bad\b).*\])[\s]+pam_unix\.so + + + ^[\s]*auth[\s]+.+[\s]+pam_faillock.so[\s]+[^\n]*deny=([0-9]+) + + + ^[\s]*deny[\s]*=[\s]*([0-9]+) + + + + ^[\s]*auth\N+pam_unix\.so + + + ^[\s]*auth[\s]+(required|\[(?=.*?\bsuccess=ok\b)(?=.*?\bnew_authtok_reqd=ok\b)(?=.*?\bignore=ignore\b)(?=.*?\bdefault=bad\b).*\])[\s]+pam_faillock\.so[\s\w\d=]+preauth[\s\S]*^[\s]*auth[\s]+(sufficient|\[(?=.*\bsuccess=done\b)(?=.*?\bnew_authtok_reqd=done\b)(?=.*?\bdefault=ignore\b).*\])[\s]+pam_unix\.so[\s\S]*^[\s]*auth[\s]+(required|\[(?=.*?\bsuccess=ok\b)(?=.*?\bnew_authtok_reqd=ok\b)(?=.*?\bignore=ignore\b)(?=.*?\bdefault=bad\b).*\])[\s]+pam_faillock\.so[\s\w\d=]+authfail + + + ^[\s]*account[\s]+(required|\[(?=.*?\bsuccess=ok\b)(?=.*?\bnew_authtok_reqd=ok\b)(?=.*?\bignore=ignore\b)(?=.*?\bdefault=bad\b).*\])[\s]+pam_faillock\.so[\s\S]*^[\s]*account[\s]+(required|\[(?=.*?\bsuccess=ok\b)(?=.*?\bnew_authtok_reqd=ok\b)(?=.*?\bignore=ignore\b)(?=.*?\bdefault=bad\b).*\])[\s]+pam_unix\.so + + + ^[\s]*auth[\s]+.+[\s]+pam_faillock.so[\s]+[^\n]*even_deny_root + + + ^[\s]*even_deny_root + + + dir\s*=\s*(\S+|"[^"]+) + + + + ^[\s]*auth[\s]+(?:required|requisite) + [\s]+pam_faillock.so[^\n#]* + + + + + + ^[\s]* + + + + + + + + + + + + + + + + + + + + + + + ^[\s]*auth\N+pam_unix\.so + + + ^[\s]*auth[\s]+(required|\[(?=.*?\bsuccess=ok\b)(?=.*?\bnew_authtok_reqd=ok\b)(?=.*?\bignore=ignore\b)(?=.*?\bdefault=bad\b).*\])[\s]+pam_faillock\.so[\s\w\d=]+preauth[\s\S]*^[\s]*auth[\s]+(sufficient|\[(?=.*\bsuccess=done\b)(?=.*?\bnew_authtok_reqd=done\b)(?=.*?\bdefault=ignore\b).*\])[\s]+pam_unix\.so[\s\S]*^[\s]*auth[\s]+(required|\[(?=.*?\bsuccess=ok\b)(?=.*?\bnew_authtok_reqd=ok\b)(?=.*?\bignore=ignore\b)(?=.*?\bdefault=bad\b).*\])[\s]+pam_faillock\.so[\s\w\d=]+authfail + + + ^[\s]*account[\s]+(required|\[(?=.*?\bsuccess=ok\b)(?=.*?\bnew_authtok_reqd=ok\b)(?=.*?\bignore=ignore\b)(?=.*?\bdefault=bad\b).*\])[\s]+pam_faillock\.so[\s\S]*^[\s]*account[\s]+(required|\[(?=.*?\bsuccess=ok\b)(?=.*?\bnew_authtok_reqd=ok\b)(?=.*?\bignore=ignore\b)(?=.*?\bdefault=bad\b).*\])[\s]+pam_unix\.so + + + ^[\s]*local_users_only + + + ^[\s]*auth\N+pam_unix\.so + + + ^[\s]*auth[\s]+(required|\[(?=.*?\bsuccess=ok\b)(?=.*?\bnew_authtok_reqd=ok\b)(?=.*?\bignore=ignore\b)(?=.*?\bdefault=bad\b).*\])[\s]+pam_faillock\.so[\s\w\d=]+preauth[\s\S]*^[\s]*auth[\s]+(sufficient|\[(?=.*\bsuccess=done\b)(?=.*?\bnew_authtok_reqd=done\b)(?=.*?\bdefault=ignore\b).*\])[\s]+pam_unix\.so[\s\S]*^[\s]*auth[\s]+(required|\[(?=.*?\bsuccess=ok\b)(?=.*?\bnew_authtok_reqd=ok\b)(?=.*?\bignore=ignore\b)(?=.*?\bdefault=bad\b).*\])[\s]+pam_faillock\.so[\s\w\d=]+authfail + + + ^[\s]*account[\s]+(required|\[(?=.*?\bsuccess=ok\b)(?=.*?\bnew_authtok_reqd=ok\b)(?=.*?\bignore=ignore\b)(?=.*?\bdefault=bad\b).*\])[\s]+pam_faillock\.so[\s\S]*^[\s]*account[\s]+(required|\[(?=.*?\bsuccess=ok\b)(?=.*?\bnew_authtok_reqd=ok\b)(?=.*?\bignore=ignore\b)(?=.*?\bdefault=bad\b).*\])[\s]+pam_unix\.so + + + ^[\s]*auth[\s]+.+[\s]+pam_faillock.so[\s]+[^\n]*fail_interval=([0-9]+) + + + ^[\s]*fail_interval[\s]*=[\s]*([0-9]+) + + + + ^[\s]*auth\N+pam_unix\.so + + + ^[\s]*auth[\s]+(required|\[(?=.*?\bsuccess=ok\b)(?=.*?\bnew_authtok_reqd=ok\b)(?=.*?\bignore=ignore\b)(?=.*?\bdefault=bad\b).*\])[\s]+pam_faillock\.so[\s\w\d=]+preauth[\s\S]*^[\s]*auth[\s]+(sufficient|\[(?=.*\bsuccess=done\b)(?=.*?\bnew_authtok_reqd=done\b)(?=.*?\bdefault=ignore\b).*\])[\s]+pam_unix\.so[\s\S]*^[\s]*auth[\s]+(required|\[(?=.*?\bsuccess=ok\b)(?=.*?\bnew_authtok_reqd=ok\b)(?=.*?\bignore=ignore\b)(?=.*?\bdefault=bad\b).*\])[\s]+pam_faillock\.so[\s\w\d=]+authfail + + + ^[\s]*account[\s]+(required|\[(?=.*?\bsuccess=ok\b)(?=.*?\bnew_authtok_reqd=ok\b)(?=.*?\bignore=ignore\b)(?=.*?\bdefault=bad\b).*\])[\s]+pam_faillock\.so[\s\S]*^[\s]*account[\s]+(required|\[(?=.*?\bsuccess=ok\b)(?=.*?\bnew_authtok_reqd=ok\b)(?=.*?\bignore=ignore\b)(?=.*?\bdefault=bad\b).*\])[\s]+pam_unix\.so + + + ^[\s]*auth[\s]+.+[\s]+pam_faillock.so[\s]+[^\n]*unlock_time=([0-9]+) + + + ^[\s]*unlock_time[\s]*=[\s]*([0-9]+) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5000 + + + + 5000 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ^[^#]* + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 64 + + + + 8 + + + + + + + + + + + + + + + + + + + + + + + + 64 + + + + 8 + + + + + + + + + + + + + + + + + + + + + + + + 64 + + + + 8 + + + + + + + + + + + + + + + + + + + + + + + + 64 + + + + 8 + + + + + + + + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+dir=/var/log/audit/)[\s]+(?:-F[\s]+perm=r)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + (?i) + + + + + + + (?i) + + + + + + + + + + + + + + + + + + + + + + + + + + + + /boot/config- + + + + + + + + + + + + + + + + + + + + + + + + ^/etc/rsyslog.conf$ + + + + + + + + + + + + + + + + ^/etc/rsyslog.conf$ + + + + + + + + + + + + + + + + ^/etc/rsyslog.conf$ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 64 + + + + 8 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Ciphers + + + + + + -oCiphers= + + + + + + MACs + + + + + + -oMACs= + + + + + + + + + + + + + / + + + + + + + / + + + + + + + + + ^ + + :x:(\d+):.*$ + + + + + + + + + + + + + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:-S[\s]+(?:[\S]+,)*(open)(?:,[\S]+)*)[\s]+(?:-F[\s]+a1&03)[\s]+(?:-F[\s]+path=/etc/group)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:-S[\s]+(?:[\S]+,)*(open)(?:,[\S]+)*)[\s]+(?:-F[\s]+a1&03)[\s]+(?:-F[\s]+path=/etc/group)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:-S[\s]+(?:[\S]+,)*(open_by_handle_at)(?:,[\S]+)*)[\s]+(?:-F[\s]+a2&03)[\s]+(?:-F[\s]+path=/etc/group)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:-S[\s]+(?:[\S]+,)*(open_by_handle_at)(?:,[\S]+)*)[\s]+(?:-F[\s]+a2&03)[\s]+(?:-F[\s]+path=/etc/group)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:-S[\s]+(?:[\S]+,)*(openat)(?:,[\S]+)*)[\s]+(?:-F[\s]+a2&03)[\s]+(?:-F[\s]+path=/etc/group)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:-S[\s]+(?:[\S]+,)*(openat)(?:,[\S]+)*)[\s]+(?:-F[\s]+a2&03)[\s]+(?:-F[\s]+path=/etc/group)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:-S[\s]+(?:[\S]+,)*(open)(?:,[\S]+)*)[\s]+(?:-F[\s]+a1&03)[\s]+(?:-F[\s]+path=/etc/gshadow)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:-S[\s]+(?:[\S]+,)*(open)(?:,[\S]+)*)[\s]+(?:-F[\s]+a1&03)[\s]+(?:-F[\s]+path=/etc/gshadow)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:-S[\s]+(?:[\S]+,)*(open_by_handle_at)(?:,[\S]+)*)[\s]+(?:-F[\s]+a2&03)[\s]+(?:-F[\s]+path=/etc/gshadow)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:-S[\s]+(?:[\S]+,)*(open_by_handle_at)(?:,[\S]+)*)[\s]+(?:-F[\s]+a2&03)[\s]+(?:-F[\s]+path=/etc/gshadow)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:-S[\s]+(?:[\S]+,)*(openat)(?:,[\S]+)*)[\s]+(?:-F[\s]+a2&03)[\s]+(?:-F[\s]+path=/etc/gshadow)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:-S[\s]+(?:[\S]+,)*(openat)(?:,[\S]+)*)[\s]+(?:-F[\s]+a2&03)[\s]+(?:-F[\s]+path=/etc/gshadow)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:-S[\s]+(?:[\S]+,)*(open)(?:,[\S]+)*)[\s]+(?:-F[\s]+a1&03)[\s]+(?:-F[\s]+path=/etc/passwd)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:-S[\s]+(?:[\S]+,)*(open)(?:,[\S]+)*)[\s]+(?:-F[\s]+a1&03)[\s]+(?:-F[\s]+path=/etc/passwd)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:-S[\s]+(?:[\S]+,)*(open_by_handle_at)(?:,[\S]+)*)[\s]+(?:-F[\s]+a2&03)[\s]+(?:-F[\s]+path=/etc/passwd)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:-S[\s]+(?:[\S]+,)*(open_by_handle_at)(?:,[\S]+)*)[\s]+(?:-F[\s]+a2&03)[\s]+(?:-F[\s]+path=/etc/passwd)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:-S[\s]+(?:[\S]+,)*(openat)(?:,[\S]+)*)[\s]+(?:-F[\s]+a2&03)[\s]+(?:-F[\s]+path=/etc/passwd)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:-S[\s]+(?:[\S]+,)*(openat)(?:,[\S]+)*)[\s]+(?:-F[\s]+a2&03)[\s]+(?:-F[\s]+path=/etc/passwd)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:-S[\s]+(?:[\S]+,)*(open)(?:,[\S]+)*)[\s]+(?:-F[\s]+a1&03)[\s]+(?:-F[\s]+path=/etc/shadow)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:-S[\s]+(?:[\S]+,)*(open)(?:,[\S]+)*)[\s]+(?:-F[\s]+a1&03)[\s]+(?:-F[\s]+path=/etc/shadow)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:-S[\s]+(?:[\S]+,)*(open_by_handle_at)(?:,[\S]+)*)[\s]+(?:-F[\s]+a2&03)[\s]+(?:-F[\s]+path=/etc/shadow)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:-S[\s]+(?:[\S]+,)*(open_by_handle_at)(?:,[\S]+)*)[\s]+(?:-F[\s]+a2&03)[\s]+(?:-F[\s]+path=/etc/shadow)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:-S[\s]+(?:[\S]+,)*(openat)(?:,[\S]+)*)[\s]+(?:-F[\s]+a2&03)[\s]+(?:-F[\s]+path=/etc/shadow)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:-S[\s]+(?:[\S]+,)*(openat)(?:,[\S]+)*)[\s]+(?:-F[\s]+a2&03)[\s]+(?:-F[\s]+path=/etc/shadow)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+chmod[\s]+|([\s]+|[,])chmod([\s]+|[,])))(?:(?!-F[\s]+a\d&).)* + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+chmod[\s]+|([\s]+|[,])chmod([\s]+|[,])))(?:(?!-F[\s]+a\d&).)* + + + [\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+chown[\s]+|([\s]+|[,])chown([\s]+|[,])))(?:(?!-F[\s]+a\d&).)* + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+chown[\s]+|([\s]+|[,])chown([\s]+|[,])))(?:(?!-F[\s]+a\d&).)* + + + [\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+creat[\s]+|([\s]+|[,])creat([\s]+|[,])))(?:(?!-F[\s]+a\d&).)* + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+creat[\s]+|([\s]+|[,])creat([\s]+|[,])))(?:(?!-F[\s]+a\d&).)* + + + [\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+fchmod[\s]+|([\s]+|[,])fchmod([\s]+|[,])))(?:(?!-F[\s]+a\d&).)* + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+fchmod[\s]+|([\s]+|[,])fchmod([\s]+|[,])))(?:(?!-F[\s]+a\d&).)* + + + [\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+fchmodat[\s]+|([\s]+|[,])fchmodat([\s]+|[,])))(?:(?!-F[\s]+a\d&).)* + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+fchmodat[\s]+|([\s]+|[,])fchmodat([\s]+|[,])))(?:(?!-F[\s]+a\d&).)* + + + [\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+fchown[\s]+|([\s]+|[,])fchown([\s]+|[,])))(?:(?!-F[\s]+a\d&).)* + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+fchown[\s]+|([\s]+|[,])fchown([\s]+|[,])))(?:(?!-F[\s]+a\d&).)* + + + [\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+fchownat[\s]+|([\s]+|[,])fchownat([\s]+|[,])))(?:(?!-F[\s]+a\d&).)* + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+fchownat[\s]+|([\s]+|[,])fchownat([\s]+|[,])))(?:(?!-F[\s]+a\d&).)* + + + [\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+fremovexattr[\s]+|([\s]+|[,])fremovexattr([\s]+|[,])))(?:(?!-F[\s]+a\d&).)* + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+fremovexattr[\s]+|([\s]+|[,])fremovexattr([\s]+|[,])))(?:(?!-F[\s]+a\d&).)* + + + [\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+fsetxattr[\s]+|([\s]+|[,])fsetxattr([\s]+|[,])))(?:(?!-F[\s]+a\d&).)* + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+fsetxattr[\s]+|([\s]+|[,])fsetxattr([\s]+|[,])))(?:(?!-F[\s]+a\d&).)* + + + [\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+ftruncate[\s]+|([\s]+|[,])ftruncate([\s]+|[,])))(?:(?!-F[\s]+a\d&).)* + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+ftruncate[\s]+|([\s]+|[,])ftruncate([\s]+|[,])))(?:(?!-F[\s]+a\d&).)* + + + [\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+lchown[\s]+|([\s]+|[,])lchown([\s]+|[,])))(?:(?!-F[\s]+a\d&).)* + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+lchown[\s]+|([\s]+|[,])lchown([\s]+|[,])))(?:(?!-F[\s]+a\d&).)* + + + [\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+lremovexattr[\s]+|([\s]+|[,])lremovexattr([\s]+|[,])))(?:(?!-F[\s]+a\d&).)* + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+lremovexattr[\s]+|([\s]+|[,])lremovexattr([\s]+|[,])))(?:(?!-F[\s]+a\d&).)* + + + [\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+lsetxattr[\s]+|([\s]+|[,])lsetxattr([\s]+|[,])))(?:(?!-F[\s]+a\d&).)* + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+lsetxattr[\s]+|([\s]+|[,])lsetxattr([\s]+|[,])))(?:(?!-F[\s]+a\d&).)* + + + [\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+open[\s]+|([\s]+|[,])open([\s]+|[,])))(?:(?!-F[\s]+a\d&).)* + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+open[\s]+|([\s]+|[,])open([\s]+|[,])))(?:(?!-F[\s]+a\d&).)* + + + [\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+open_by_handle_at[\s]+|([\s]+|[,])open_by_handle_at([\s]+|[,])))(?:(?!-F[\s]+a\d&).)* + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+open_by_handle_at[\s]+|([\s]+|[,])open_by_handle_at([\s]+|[,])))(?:(?!-F[\s]+a\d&).)* + + + [\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:-S[\s]+(?:[\S]+,)*(open_by_handle_at)(?:,[\S]+)*)[\s]+ + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:-S[\s]+(?:[\S]+,)*(open_by_handle_at)(?:,[\S]+)*)[\s]+ + + + [\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + (?:[^.]|\.\s)* + + + + + (?:-F\s+a2&0100)[\s]+(?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+a2&0100)[\s]+(?:-F\s+exit=-EPERM) + + + + + + + (?:-F\s+a2&0100)[\s]+(?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+a2&0100)[\s]+(?:-F\s+exit=-EPERM) + + + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:-S[\s]+(?:[\S]+,)*(open_by_handle_at)(?:,[\S]+)*)[\s]+ + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:-S[\s]+(?:[\S]+,)*(open_by_handle_at)(?:,[\S]+)*)[\s]+ + + + [\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + (?:[^.]|\.\s)* + + + + + (?:-F\s+a2&01003)[\s]+(?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+a2&01003)[\s]+(?:-F\s+exit=-EPERM) + + + + + + + (?:-F\s+a2&01003)[\s]+(?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+a2&01003)[\s]+(?:-F\s+exit=-EPERM) + + + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:-S[\s]+(?:[\S]+,)*(open_by_handle_at)(?:,[\S]+)*)[\s]+ + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:-S[\s]+(?:[\S]+,)*(open_by_handle_at)(?:,[\S]+)*)[\s]+ + + + [\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(?:unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + + + (?:-F\s+a2&0100)[\s]+(?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+a2&01003)[\s]+(?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+a2&0100)[\s]+(?:-F\s+exit=-EPERM) + + + + + + + (?:-F\s+a2&01003)[\s]+(?:-F\s+exit=-EPERM) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + + + (?:-F\s+a2&0100)[\s]+(?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+a2&01003)[\s]+(?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+a2&0100)[\s]+(?:-F\s+exit=-EPERM) + + + + + + + (?:-F\s+a2&01003)[\s]+(?:-F\s+exit=-EPERM) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + + ^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $ + + + + + ^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $ + + + + + ^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $ + + + + + ^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $ + + + + + ^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $ + + + + + ^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $ + + + + + ^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $ + + + + + ^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $ + + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:-S[\s]+(?:[\S]+,)*(open)(?:,[\S]+)*)[\s]+ + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:-S[\s]+(?:[\S]+,)*(open)(?:,[\S]+)*)[\s]+ + + + [\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + (?:[^.]|\.\s)* + + + + + (?:-F\s+a1&0100)[\s]+(?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+a1&0100)[\s]+(?:-F\s+exit=-EPERM) + + + + + + + (?:-F\s+a1&0100)[\s]+(?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+a1&0100)[\s]+(?:-F\s+exit=-EPERM) + + + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:-S[\s]+(?:[\S]+,)*(open)(?:,[\S]+)*)[\s]+ + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:-S[\s]+(?:[\S]+,)*(open)(?:,[\S]+)*)[\s]+ + + + [\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + (?:[^.]|\.\s)* + + + + + (?:-F\s+a1&01003)[\s]+(?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+a1&01003)[\s]+(?:-F\s+exit=-EPERM) + + + + + + + (?:-F\s+a1&01003)[\s]+(?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+a1&01003)[\s]+(?:-F\s+exit=-EPERM) + + + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:-S[\s]+(?:[\S]+,)*(open)(?:,[\S]+)*)[\s]+ + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:-S[\s]+(?:[\S]+,)*(open)(?:,[\S]+)*)[\s]+ + + + [\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(?:unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + + + (?:-F\s+a1&0100)[\s]+(?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+a1&01003)[\s]+(?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+a1&0100)[\s]+(?:-F\s+exit=-EPERM) + + + + + + + (?:-F\s+a1&01003)[\s]+(?:-F\s+exit=-EPERM) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + + + (?:-F\s+a1&0100)[\s]+(?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+a1&01003)[\s]+(?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+a1&0100)[\s]+(?:-F\s+exit=-EPERM) + + + + + + + (?:-F\s+a1&01003)[\s]+(?:-F\s+exit=-EPERM) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + + ^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $ + + + + + ^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $ + + + + + ^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $ + + + + + ^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $ + + + + + ^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $ + + + + + ^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $ + + + + + ^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $ + + + + + ^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $ + + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+openat[\s]+|([\s]+|[,])openat([\s]+|[,])))(?:(?!-F[\s]+a\d&).)* + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+openat[\s]+|([\s]+|[,])openat([\s]+|[,])))(?:(?!-F[\s]+a\d&).)* + + + [\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:-S[\s]+(?:[\S]+,)*(openat)(?:,[\S]+)*)[\s]+ + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:-S[\s]+(?:[\S]+,)*(openat)(?:,[\S]+)*)[\s]+ + + + [\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + (?:[^.]|\.\s)* + + + + + (?:-F\s+a2&0100)[\s]+(?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+a2&0100)[\s]+(?:-F\s+exit=-EPERM) + + + + + + + (?:-F\s+a2&0100)[\s]+(?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+a2&0100)[\s]+(?:-F\s+exit=-EPERM) + + + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:-S[\s]+(?:[\S]+,)*(openat)(?:,[\S]+)*)[\s]+ + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:-S[\s]+(?:[\S]+,)*(openat)(?:,[\S]+)*)[\s]+ + + + [\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + (?:[^.]|\.\s)* + + + + + (?:-F\s+a2&01003)[\s]+(?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+a2&01003)[\s]+(?:-F\s+exit=-EPERM) + + + + + + + (?:-F\s+a2&01003)[\s]+(?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+a2&01003)[\s]+(?:-F\s+exit=-EPERM) + + + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:-S[\s]+(?:[\S]+,)*(openat)(?:,[\S]+)*)[\s]+ + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:-S[\s]+(?:[\S]+,)*(openat)(?:,[\S]+)*)[\s]+ + + + [\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(?:unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + + + (?:-F\s+a2&0100)[\s]+(?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+a2&01003)[\s]+(?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+a2&0100)[\s]+(?:-F\s+exit=-EPERM) + + + + + + + (?:-F\s+a2&01003)[\s]+(?:-F\s+exit=-EPERM) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + + + (?:-F\s+a2&0100)[\s]+(?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+a2&01003)[\s]+(?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+a2&0100)[\s]+(?:-F\s+exit=-EPERM) + + + + + + + (?:-F\s+a2&01003)[\s]+(?:-F\s+exit=-EPERM) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + + ^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $ + + + + + ^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $ + + + + + ^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $ + + + + + ^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $ + + + + + ^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $ + + + + + ^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $ + + + + + ^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $ + + + + + ^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $ + + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+removexattr[\s]+|([\s]+|[,])removexattr([\s]+|[,])))(?:(?!-F[\s]+a\d&).)* + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+removexattr[\s]+|([\s]+|[,])removexattr([\s]+|[,])))(?:(?!-F[\s]+a\d&).)* + + + [\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+rename[\s]+|([\s]+|[,])rename([\s]+|[,])))(?:(?!-F[\s]+a\d&).)* + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+rename[\s]+|([\s]+|[,])rename([\s]+|[,])))(?:(?!-F[\s]+a\d&).)* + + + [\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+renameat[\s]+|([\s]+|[,])renameat([\s]+|[,])))(?:(?!-F[\s]+a\d&).)* + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+renameat[\s]+|([\s]+|[,])renameat([\s]+|[,])))(?:(?!-F[\s]+a\d&).)* + + + [\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+setxattr[\s]+|([\s]+|[,])setxattr([\s]+|[,])))(?:(?!-F[\s]+a\d&).)* + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+setxattr[\s]+|([\s]+|[,])setxattr([\s]+|[,])))(?:(?!-F[\s]+a\d&).)* + + + [\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+truncate[\s]+|([\s]+|[,])truncate([\s]+|[,])))(?:(?!-F[\s]+a\d&).)* + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+truncate[\s]+|([\s]+|[,])truncate([\s]+|[,])))(?:(?!-F[\s]+a\d&).)* + + + [\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+unlink[\s]+|([\s]+|[,])unlink([\s]+|[,])))(?:(?!-F[\s]+a\d&).)* + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+unlink[\s]+|([\s]+|[,])unlink([\s]+|[,])))(?:(?!-F[\s]+a\d&).)* + + + [\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+unlinkat[\s]+|([\s]+|[,])unlinkat([\s]+|[,])))(?:(?!-F[\s]+a\d&).)* + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+unlinkat[\s]+|([\s]+|[,])unlinkat([\s]+|[,])))(?:(?!-F[\s]+a\d&).)* + + + [\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + + ^(?:.*\s)?l1tf= + + (?:\s.*)?$ + + + + + + ^(?:.*\s)?rng_core.default_quality= + + (?:\s.*)?$ + + + + + + ^(?:.*\s)?slub_debug= + + (?:\s.*)?$ + + + + + + ^(?:.*\s)?spec_store_bypass_disable= + + (?:\s.*)?$ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + /etc/modprobe.d + /etc/modules-load.d + /run/modprobe.d + /run/modules-load.d + /usr/lib/modprobe.d + /usr/lib/modules-load.d + + + /etc/modprobe.d + /etc/modules-load.d + /run/modprobe.d + /run/modules-load.d + /usr/lib/modprobe.d + /usr/lib/modules-load.d + + + /etc/modprobe.d + /etc/modules-load.d + /run/modprobe.d + /run/modules-load.d + /usr/lib/modprobe.d + /usr/lib/modules-load.d + + + /etc/modprobe.d + /etc/modules-load.d + /run/modprobe.d + /run/modules-load.d + /usr/lib/modprobe.d + /usr/lib/modules-load.d + + + /etc/modprobe.d + /etc/modules-load.d + /run/modprobe.d + /run/modules-load.d + /usr/lib/modprobe.d + /usr/lib/modules-load.d + + + /etc/modprobe.d + /etc/modules-load.d + /run/modprobe.d + /run/modules-load.d + /usr/lib/modprobe.d + /usr/lib/modules-load.d + + + /etc/modprobe.d + /etc/modules-load.d + /run/modprobe.d + /run/modules-load.d + /usr/lib/modprobe.d + /usr/lib/modules-load.d + + + /etc/modprobe.d + /etc/modules-load.d + /run/modprobe.d + /run/modules-load.d + /usr/lib/modprobe.d + /usr/lib/modules-load.d + + + /etc/modprobe.d + /etc/modules-load.d + /run/modprobe.d + /run/modules-load.d + /usr/lib/modprobe.d + /usr/lib/modules-load.d + + + /etc/modprobe.d + /etc/modules-load.d + /run/modprobe.d + /run/modules-load.d + /usr/lib/modprobe.d + /usr/lib/modules-load.d + + + /etc/modprobe.d + /etc/modules-load.d + /run/modprobe.d + /run/modules-load.d + /usr/lib/modprobe.d + /usr/lib/modules-load.d + + + /etc/modprobe.d + /etc/modules-load.d + /run/modprobe.d + /run/modules-load.d + /usr/lib/modprobe.d + /usr/lib/modules-load.d + + + /etc/modprobe.d + /etc/modules-load.d + /run/modprobe.d + /run/modules-load.d + /usr/lib/modprobe.d + /usr/lib/modules-load.d + + + /etc/modprobe.d + /etc/modules-load.d + /run/modprobe.d + /run/modules-load.d + /usr/lib/modprobe.d + /usr/lib/modules-load.d + + + /etc/modprobe.d + /etc/modules-load.d + /run/modprobe.d + /run/modules-load.d + /usr/lib/modprobe.d + /usr/lib/modules-load.d + + + /etc/modprobe.d + /etc/modules-load.d + /run/modprobe.d + /run/modules-load.d + /usr/lib/modprobe.d + /usr/lib/modules-load.d + + + /etc/modprobe.d + /etc/modules-load.d + /run/modprobe.d + /run/modules-load.d + /usr/lib/modprobe.d + /usr/lib/modules-load.d + + + /etc/modprobe.d + /etc/modules-load.d + /run/modprobe.d + /run/modules-load.d + /usr/lib/modprobe.d + /usr/lib/modules-load.d + + + /etc/modprobe.d + /etc/modules-load.d + /run/modprobe.d + /run/modules-load.d + /usr/lib/modprobe.d + /usr/lib/modules-load.d + + + /etc/modprobe.d + /etc/modules-load.d + /run/modprobe.d + /run/modules-load.d + /usr/lib/modprobe.d + /usr/lib/modules-load.d + + + /etc/modprobe.d + /etc/modules-load.d + /run/modprobe.d + /run/modules-load.d + /usr/lib/modprobe.d + /usr/lib/modules-load.d + + + /etc/modprobe.d + /etc/modules-load.d + /run/modprobe.d + /run/modules-load.d + /usr/lib/modprobe.d + /usr/lib/modules-load.d + + + /dev/cdrom + /dev/dvd + /dev/scd0 + /dev/sr0 + + + + ^[\s]* + + [\s]+[/\w]+[\s]+[\w]+[\s]+([^\s]+)(?:[\s]+[\d]+){2}$ + + + + + ^[\s]* + + [\s]+[/\w]+[\s]+[\w]+[\s]+([^\s]+)(?:[\s]+[\d]+){2}$ + + + + + /dev/cdrom + /dev/dvd + /dev/scd0 + /dev/sr0 + + + + ^[\s]* + + [\s]+[/\w]+[\s]+[\w]+[\s]+([^\s]+)(?:[\s]+[\d]+){2}$ + + + + + ^[\s]* + + [\s]+[/\w]+[\s]+[\w]+[\s]+([^\s]+)(?:[\s]+[\d]+){2}$ + + + + /dev/cdrom + /dev/dvd + /dev/scd0 + /dev/sr0 + + + + ^[\s]* + + [\s]+[/\w]+[\s]+[\w]+[\s]+([^\s]+)(?:[\s]+[\d]+){2}$ + + + + + ^[\s]* + + [\s]+[/\w]+[\s]+[\w]+[\s]+([^\s]+)(?:[\s]+[\d]+){2}$ + + + + + + hidepid= + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + /etc/pam.d/system-auth + + + + + + + + /dev/cdrom + /dev/dvd + /dev/scd0 + /dev/sr0 + + + + + + + + + + + + + + + + + + + + + + 64 + + + + 8 + + + + + + + + + + + + + + + + + + + + + + + + + 64 + + + + 8 + + + + + + + + + + + + build_shorthand.py from SCAP Security Guide + ssg: 0.1.64 + 2.0 + 2022-09-30T15:13:14 + + + + Disable the dhcpd_use_ldap SELinux Boolean + + ocil:ssg-sebool_dhcpd_use_ldap_action:testaction:1 + + + + Enable the OpenSSH Service + + ocil:ssg-service_sshd_enabled_action:testaction:1 + + + + Enable Kernel Parameter to Log Martian Packets on all IPv4 Interfaces + + ocil:ssg-sysctl_net_ipv4_conf_all_log_martians_action:testaction:1 + + + + Verify /boot/grub2/grub.cfg Group Ownership + + ocil:ssg-file_groupowner_grub2_cfg_action:testaction:1 + + + + Ensure the Default Umask is Set Correctly in /etc/profile + + ocil:ssg-accounts_umask_etc_profile_action:testaction:1 + + + + Uninstall iprutils Package + + ocil:ssg-package_iprutils_removed_action:testaction:1 + + + + HTTPD Log Files Must Be Owned By Root + + ocil:ssg-http_configure_log_file_ownership_action:testaction:1 + + + + Uninstall Sendmail Package + + ocil:ssg-package_sendmail_removed_action:testaction:1 + + + + Harden slab freelist metadata + + ocil:ssg-kernel_config_slab_freelist_hardened_action:testaction:1 + + + + OpenSSL uses strong entropy source + + ocil:ssg-openssl_use_strong_entropy_action:testaction:1 + + + + Disable the git_system_use_nfs SELinux Boolean + + ocil:ssg-sebool_git_system_use_nfs_action:testaction:1 + + + + Configure the Use of the pam_faillock.so Module in the /etc/pam.d/system-auth File. + + ocil:ssg-account_password_pam_faillock_system_auth_action:testaction:1 + + + + Set Password Hashing Algorithm in /etc/libuser.conf + + ocil:ssg-set_password_hashing_algorithm_libuserconf_action:testaction:1 + + + + Record Successful Creation Attempts to Files - open O_CREAT + + ocil:ssg-audit_rules_successful_file_modification_open_o_creat_action:testaction:1 + + + + Ensure auditd Collects Information on the Use of Privileged Commands - newuidmap + + ocil:ssg-audit_rules_privileged_commands_newuidmap_action:testaction:1 + + + + Remove Rsh Trust Files + + ocil:ssg-no_rsh_trust_files_action:testaction:1 + + + + Configure SSH to use System Crypto Policy + + ocil:ssg-configure_ssh_crypto_policy_action:testaction:1 + + + + Disable the haproxy_connect_any SELinux Boolean + + ocil:ssg-sebool_haproxy_connect_any_action:testaction:1 + + + + Configure Error Log Format + + ocil:ssg-httpd_configure_log_format_action:testaction:1 + + + + Ensure auditd Collects Information on the Use of Privileged Commands - chage + + ocil:ssg-audit_rules_privileged_commands_chage_action:testaction:1 + + + + Web Content Directories Must Not Be Shared Anonymously + + ocil:ssg-httpd_anonymous_content_sharing_action:testaction:1 + + + + Disable vsyscall mapping + + ocil:ssg-kernel_config_legacy_vsyscall_none_action:testaction:1 + + + + Uninstall abrt-cli Package + + ocil:ssg-package_abrt-cli_removed_action:testaction:1 + + + + Install McAfee Virus Scanning Software + + ocil:ssg-install_mcafee_antivirus_action:testaction:1 + + + + Record Unsuccessful Permission Changes to Files - fchmod + + ocil:ssg-audit_rules_unsuccessful_file_modification_fchmod_action:testaction:1 + + + + Verify Group Ownership of System Login Banner + + ocil:ssg-file_groupowner_etc_issue_action:testaction:1 + + + + Disable the smbd_anon_write SELinux Boolean + + ocil:ssg-sebool_smbd_anon_write_action:testaction:1 + + + + Disable the httpd_anon_write SELinux Boolean + + ocil:ssg-sebool_httpd_anon_write_action:testaction:1 + + + + Disable Red Hat Subscription Manager Daemon (rhsmcertd) + + ocil:ssg-service_rhsmcertd_disabled_action:testaction:1 + + + + Disable the logwatch_can_network_connect_mail SELinux Boolean + + ocil:ssg-sebool_logwatch_can_network_connect_mail_action:testaction:1 + + + + Set Default firewalld Zone for Incoming Packets + + ocil:ssg-set_firewalld_default_zone_action:testaction:1 + + + + Disable the httpd_can_connect_mythtv SELinux Boolean + + ocil:ssg-sebool_httpd_can_connect_mythtv_action:testaction:1 + + + + Disable Network Console (netconsole) + + ocil:ssg-service_netconsole_disabled_action:testaction:1 + + + + Disable the gpg_web_anon_write SELinux Boolean + + ocil:ssg-sebool_gpg_web_anon_write_action:testaction:1 + + + + Record Attempts to Alter Time Through stime + + ocil:ssg-audit_rules_time_stime_action:testaction:1 + + + + Ensure that /etc/cron.deny does not exist + + ocil:ssg-file_cron_deny_not_exist_action:testaction:1 + + + + Disable the httpd_tty_comm SELinux Boolean + + ocil:ssg-sebool_httpd_tty_comm_action:testaction:1 + + + + Uninstall talk Package + + ocil:ssg-package_talk_removed_action:testaction:1 + + + + Log USBGuard daemon audit events using Linux Audit + + ocil:ssg-configure_usbguard_auditbackend_action:testaction:1 + + + + Prevent user from disabling the screen lock + + ocil:ssg-no_tmux_in_shells_action:testaction:1 + + + + Account Lockouts Must Be Logged + + ocil:ssg-account_passwords_pam_faillock_audit_action:testaction:1 + + + + Require Authentication for Single User Mode + + ocil:ssg-require_singleuser_auth_action:testaction:1 + + + + Set Default iptables Policy for Forwarded Packets + + ocil:ssg-set_iptables_default_rule_forward_action:testaction:1 + + + + Disable the httpd_unified SELinux Boolean + + ocil:ssg-sebool_httpd_unified_action:testaction:1 + + + + Disable Dovecot Service + + ocil:ssg-service_dovecot_disabled_action:testaction:1 + + + + Verify that System Executables Have Restrictive Permissions + + ocil:ssg-file_permissions_binary_dirs_action:testaction:1 + + + + Record Unsuccessful Creation Attempts to Files - open O_CREAT + + ocil:ssg-audit_rules_unsuccessful_file_modification_open_o_creat_action:testaction:1 + + + + Ensure All User Initialization Files Have Mode 0740 Or Less Permissive + + ocil:ssg-file_permission_user_init_files_action:testaction:1 + + + + Disable the use_samba_home_dirs SELinux Boolean + + ocil:ssg-sebool_use_samba_home_dirs_action:testaction:1 + + + + Install sudo Package + + ocil:ssg-package_sudo_installed_action:testaction:1 + + + + Set SSH authentication attempt limit + + ocil:ssg-sshd_set_max_auth_tries_action:testaction:1 + + + + Disable the mozilla_read_content SELinux Boolean + + ocil:ssg-sebool_mozilla_read_content_action:testaction:1 + + + + Disable SSH Access via Empty Passwords + + ocil:ssg-sshd_disable_empty_passwords_action:testaction:1 + + + + Set SSH Client Alive Count Max to zero + + ocil:ssg-sshd_set_keepalive_0_action:testaction:1 + + + + Install rear Package + + ocil:ssg-package_rear_installed_action:testaction:1 + + + + Specify a Remote NTP Server + + ocil:ssg-ntpd_specify_remote_server_action:testaction:1 + + + + Disable the httpd_use_sasl SELinux Boolean + + ocil:ssg-sebool_httpd_use_sasl_action:testaction:1 + + + + Verify the system-wide library files in directories +"/lib", "/lib64", "/usr/lib/" and "/usr/lib64" are group-owned by root. + + ocil:ssg-root_permissions_syslibrary_files_action:testaction:1 + + + + The operating system must require Re-Authentication when using the sudo command. Ensure sudo timestamp_timeout is appropriate - sudo timestamp_timeout + + ocil:ssg-sudo_require_reauthentication_action:testaction:1 + + + + Configure dnf-automatic to Install Only Security Updates + + ocil:ssg-dnf-automatic_security_updates_only_action:testaction:1 + + + + Configure audispd's Plugin network_failure_action On Network Failure + + ocil:ssg-auditd_audispd_network_failure_action_action:testaction:1 + + + + Ensure auditd Collects Information on the Use of Privileged Commands - usernetctl + + ocil:ssg-audit_rules_privileged_commands_usernetctl_action:testaction:1 + + + + Ensure Users Re-Authenticate for Privilege Escalation - sudo !authenticate + + ocil:ssg-sudo_remove_no_authenticate_action:testaction:1 + + + + Install pam_pwquality Package + + ocil:ssg-package_pam_pwquality_installed_action:testaction:1 + + + + Disallow magic SysRq key + + ocil:ssg-sysctl_kernel_sysrq_action:testaction:1 + + + + Disable the httpd_can_connect_ldap SELinux Boolean + + ocil:ssg-sebool_httpd_can_connect_ldap_action:testaction:1 + + + + Disable IEEE 1394 (FireWire) Support + + ocil:ssg-kernel_module_firewire-core_disabled_action:testaction:1 + + + + Disable X Windows Startup By Setting Default Target + + ocil:ssg-xwindows_runlevel_target_action:testaction:1 + + + + Deactivate Wireless Network Interfaces + + ocil:ssg-wireless_disable_interfaces_action:testaction:1 + + + + Disable the postgresql_can_rsync SELinux Boolean + + ocil:ssg-sebool_postgresql_can_rsync_action:testaction:1 + + + + Install the OpenSSH Server Package + + ocil:ssg-package_openssh-server_installed_action:testaction:1 + + + + Disable the httpd_use_cifs SELinux Boolean + + ocil:ssg-sebool_httpd_use_cifs_action:testaction:1 + + + + An SELinux Context must be configured for the pam_faillock.so records directory + + ocil:ssg-account_password_selinux_faillock_dir_action:testaction:1 + + + + All Interactive User Home Directories Must Be Owned By The Primary User + + ocil:ssg-file_ownership_home_directories_action:testaction:1 + + + + Disable merging of slabs with similar size + + ocil:ssg-grub2_slab_nomerge_argument_action:testaction:1 + + + + Record Unsuccessful Delete Attempts to Files - unlinkat + + ocil:ssg-audit_rules_unsuccessful_file_modification_unlinkat_action:testaction:1 + + + + Configure dnf-automatic to Install Available Updates Automatically + + ocil:ssg-dnf-automatic_apply_updates_action:testaction:1 + + + + Uninstall Automatic Bug Reporting Tool (abrt) + + ocil:ssg-package_abrt_removed_action:testaction:1 + + + + Disable the IPv6 protocol + + ocil:ssg-kernel_config_ipv6_action:testaction:1 + + + + Record Successful Access Attempts to Files - openat + + ocil:ssg-audit_rules_successful_file_modification_openat_action:testaction:1 + + + + UEFI Boot Loader Is Not Installed On Removeable Media + + ocil:ssg-uefi_no_removeable_media_action:testaction:1 + + + + Disable the httpd_use_openstack SELinux Boolean + + ocil:ssg-sebool_httpd_use_openstack_action:testaction:1 + + + + Ensure /var Located On Separate Partition + + ocil:ssg-partition_for_var_action:testaction:1 + + + + Record Unsuccessful Access Attempts to Files - open + + ocil:ssg-audit_rules_unsuccessful_file_modification_open_action:testaction:1 + + + + Disable the puppetagent_manage_all_files SELinux Boolean + + ocil:ssg-sebool_puppetagent_manage_all_files_action:testaction:1 + + + + Generate USBGuard Policy + + ocil:ssg-usbguard_generate_policy_action:testaction:1 + + + + Configure auditd space_left Action on Low Disk Space + + ocil:ssg-auditd_data_retention_space_left_action_action:testaction:1 + + + + Disable the xdm_sysadm_login SELinux Boolean + + ocil:ssg-sebool_xdm_sysadm_login_action:testaction:1 + + + + Add nosuid Option to /var/tmp + + ocil:ssg-mount_option_var_tmp_nosuid_action:testaction:1 + + + + Disable the uvcvideo module + + ocil:ssg-kernel_module_uvcvideo_disabled_action:testaction:1 + + + + Disable the mcelog_client SELinux Boolean + + ocil:ssg-sebool_mcelog_client_action:testaction:1 + + + + Enable systemd-journald Service + + ocil:ssg-service_systemd-journald_enabled_action:testaction:1 + + + + Add noexec Option to /var/log/audit + + ocil:ssg-mount_option_var_log_audit_noexec_action:testaction:1 + + + + Set Existing Passwords Minimum Age + + ocil:ssg-accounts_password_set_min_life_existing_action:testaction:1 + + + + Ensure rsyslog Does Not Accept Remote Messages Unless Acting As Log Server + + ocil:ssg-rsyslog_nolisten_action:testaction:1 + + + + Prevent applications from mapping low portion of virtual memory + + ocil:ssg-sysctl_vm_mmap_min_addr_action:testaction:1 + + + + Enable the File Access Policy Service + + ocil:ssg-service_fapolicyd_enabled_action:testaction:1 + + + + Verify Permissions on gshadow File + + ocil:ssg-file_permissions_etc_gshadow_action:testaction:1 + + + + Configure the httpd_enable_cgi SELinux Boolean + + ocil:ssg-sebool_httpd_enable_cgi_action:testaction:1 + + + + Ensure auditd Collects System Administrator Actions + + ocil:ssg-audit_rules_sysadmin_actions_action:testaction:1 + + + + Verify permissions on System Login Banner + + ocil:ssg-file_permissions_etc_issue_action:testaction:1 + + + + Ensure Privileged Escalated Commands Cannot Execute Other Commands - sudo NOEXEC + + ocil:ssg-sudo_add_noexec_action:testaction:1 + + + + Disable Core Dumps for All Users + + ocil:ssg-disable_users_coredumps_action:testaction:1 + + + + Disable the daemons_use_tty SELinux Boolean + + ocil:ssg-sebool_daemons_use_tty_action:testaction:1 + + + + Disable the cluster_manage_all_files SELinux Boolean + + ocil:ssg-sebool_cluster_manage_all_files_action:testaction:1 + + + + Record Successful Delete Attempts to Files - rename + + ocil:ssg-audit_rules_successful_file_modification_rename_action:testaction:1 + + + + Enable the selinuxuser_execmod SELinux Boolean + + ocil:ssg-sebool_selinuxuser_execmod_action:testaction:1 + + + + Install sssd-ipa Package + + ocil:ssg-package_sssd-ipa_installed_action:testaction:1 + + + + Add noexec Option to /var + + ocil:ssg-mount_option_var_noexec_action:testaction:1 + + + + Configure Notification of Post-AIDE Scan Details + + ocil:ssg-aide_scan_notification_action:testaction:1 + + + + Disable the antivirus_use_jit SELinux Boolean + + ocil:ssg-sebool_antivirus_use_jit_action:testaction:1 + + + + Ensure nss-tools is installed + + ocil:ssg-package_nss-tools_installed_action:testaction:1 + + + + Enable the domain_fd_use SELinux Boolean + + ocil:ssg-sebool_domain_fd_use_action:testaction:1 + + + + Disable Full User Name on Splash Shield + + ocil:ssg-dconf_gnome_screensaver_user_info_action:testaction:1 + + + + Disable the cvs_read_shadow SELinux Boolean + + ocil:ssg-sebool_cvs_read_shadow_action:testaction:1 + + + + Configure L1 Terminal Fault mitigations + + ocil:ssg-grub2_l1tf_argument_action:testaction:1 + + + + Verify Permissions on /etc/cron.allow file + + ocil:ssg-file_permissions_cron_allow_action:testaction:1 + + + + Install rng-tools Package + + ocil:ssg-package_rng-tools_installed_action:testaction:1 + + + + Disable the cups_execmem SELinux Boolean + + ocil:ssg-sebool_cups_execmem_action:testaction:1 + + + + Disable the rsync_anon_write SELinux Boolean + + ocil:ssg-sebool_rsync_anon_write_action:testaction:1 + + + + Limit Password Reuse + + ocil:ssg-accounts_password_pam_unix_remember_action:testaction:1 + + + + Disable the pcp_bind_all_unreserved_ports SELinux Boolean + + ocil:ssg-sebool_pcp_bind_all_unreserved_ports_action:testaction:1 + + + + Disable the httpd_run_stickshift SELinux Boolean + + ocil:ssg-sebool_httpd_run_stickshift_action:testaction:1 + + + + Enable auditd Service + + ocil:ssg-service_auditd_enabled_action:testaction:1 + + + + Disable IPv6 Addressing on IPv6 Interfaces by Default + + ocil:ssg-sysctl_net_ipv6_conf_default_disable_ipv6_action:testaction:1 + + + + Ensure Users Re-Authenticate for Privilege Escalation - sudo + + ocil:ssg-sudo_require_authentication_action:testaction:1 + + + + Disable the httpd_use_gpg SELinux Boolean + + ocil:ssg-sebool_httpd_use_gpg_action:testaction:1 + + + + Ensure journald is configured to write log files to persistent disk + + ocil:ssg-journald_storage_action:testaction:1 + + + + Disable the condor_tcp_network_connect SELinux Boolean + + ocil:ssg-sebool_condor_tcp_network_connect_action:testaction:1 + + + + Ensure auditd Collects Information on the Use of Privileged Commands - gpasswd + + ocil:ssg-audit_rules_privileged_commands_gpasswd_action:testaction:1 + + + + Verify User Who Owns group File + + ocil:ssg-file_owner_etc_group_action:testaction:1 + + + + Enable support for BUG() + + ocil:ssg-kernel_config_bug_action:testaction:1 + + + + Ensure gpgcheck Enabled for Repository Metadata + + ocil:ssg-ensure_gpgcheck_repo_metadata_action:testaction:1 + + + + Enable poison without sanity check + + ocil:ssg-kernel_config_page_poisoning_no_sanity_action:testaction:1 + + + + Disable the httpd_enable_ftp_server SELinux Boolean + + ocil:ssg-sebool_httpd_enable_ftp_server_action:testaction:1 + + + + Disable the ftpd_connect_all_unreserved SELinux Boolean + + ocil:ssg-sebool_ftpd_connect_all_unreserved_action:testaction:1 + + + + Enable the postfix_local_write_mail_spool SELinux Boolean + + ocil:ssg-sebool_postfix_local_write_mail_spool_action:testaction:1 + + + + Disable mutable hooks + + ocil:ssg-kernel_config_security_writable_hooks_action:testaction:1 + + + + Set hostname as computer node name in audit logs + + ocil:ssg-auditd_name_format_action:testaction:1 + + + + Record Events that Modify the System's Discretionary Access Controls - fchown + + ocil:ssg-audit_rules_dac_modification_fchown_action:testaction:1 + + + + Ignore HTTPD .htaccess Files + + ocil:ssg-httpd_ignore_htaccess_files_action:testaction:1 + + + + Verify Permissions on group File + + ocil:ssg-file_permissions_etc_group_action:testaction:1 + + + + Disable the nfsd_anon_write SELinux Boolean + + ocil:ssg-sebool_nfsd_anon_write_action:testaction:1 + + + + Disable Kernel Parameter for Accepting Secure ICMP Redirects on all IPv4 Interfaces + + ocil:ssg-sysctl_net_ipv4_conf_all_secure_redirects_action:testaction:1 + + + + Disable the virt_use_comm SELinux Boolean + + ocil:ssg-sebool_virt_use_comm_action:testaction:1 + + + + Record Unsuccessful Ownership Changes to Files - lchown + + ocil:ssg-audit_rules_unsuccessful_file_modification_lchown_action:testaction:1 + + + + Disable DCCP Support + + ocil:ssg-kernel_module_dccp_disabled_action:testaction:1 + + + + Force kernel panic on uncorrected MCEs + + ocil:ssg-grub2_mce_argument_action:testaction:1 + + + + Uninstall net-snmp Package + + ocil:ssg-package_net-snmp_removed_action:testaction:1 + + + + Ensure PAM Enforces Password Requirements - Authentication Retry Prompts Permitted Per-Session + + ocil:ssg-accounts_password_pam_retry_action:testaction:1 + + + + Disable the abrt_anon_write SELinux Boolean + + ocil:ssg-sebool_abrt_anon_write_action:testaction:1 + + + + Disable the selinuxuser_tcp_server SELinux Boolean + + ocil:ssg-sebool_selinuxuser_tcp_server_action:testaction:1 + + + + Make the module text and rodata read-only + + ocil:ssg-kernel_config_strict_module_rwx_action:testaction:1 + + + + Ensure debug-shell service is not enabled during boot + + ocil:ssg-grub2_systemd_debug-shell_argument_absent_action:testaction:1 + + + + Verify and Correct File Permissions with RPM + + ocil:ssg-rpm_verify_permissions_action:testaction:1 + + + + Enable ExecShield via sysctl + + ocil:ssg-sysctl_kernel_exec_shield_action:testaction:1 + + + + Ensure auditd Collects File Deletion Events by User - rename + + ocil:ssg-audit_rules_file_deletion_events_rename_action:testaction:1 + + + + Record Events that Modify the System's Discretionary Access Controls - umount + + ocil:ssg-audit_rules_dac_modification_umount_action:testaction:1 + + + + Record Successful Permission Changes to Files - fremovexattr + + ocil:ssg-audit_rules_successful_file_modification_fremovexattr_action:testaction:1 + + + + Record Events that Modify the System's Discretionary Access Controls - chown + + ocil:ssg-audit_rules_dac_modification_chown_action:testaction:1 + + + + Disable the racoon_read_shadow SELinux Boolean + + ocil:ssg-sebool_racoon_read_shadow_action:testaction:1 + + + + Enable SSH Server firewalld Firewall Exception + + ocil:ssg-firewalld_sshd_port_enabled_action:testaction:1 + + + + Record Events that Modify User/Group Information via openat syscall - /etc/gshadow + + ocil:ssg-audit_rules_etc_gshadow_openat_action:testaction:1 + + + + Verify Owner on cron.d + + ocil:ssg-file_owner_cron_d_action:testaction:1 + + + + Enable Dracut FIPS Module + + ocil:ssg-enable_dracut_fips_module_action:testaction:1 + + + + Restrict Exposed Kernel Pointer Addresses Access + + ocil:ssg-sysctl_kernel_kptr_restrict_action:testaction:1 + + + + Sign kernel modules with SHA-512 + + ocil:ssg-kernel_config_module_sig_sha512_action:testaction:1 + + + + Disable the xguest_use_bluetooth SELinux Boolean + + ocil:ssg-sebool_xguest_use_bluetooth_action:testaction:1 + + + + Disable the polipo_session_bind_all_unreserved_ports SELinux Boolean + + ocil:ssg-sebool_polipo_session_bind_all_unreserved_ports_action:testaction:1 + + + + Configure the secure_mode_insmod SELinux Boolean + + ocil:ssg-sebool_secure_mode_insmod_action:testaction:1 + + + + Install the Host Intrusion Prevention System (HIPS) Module + + ocil:ssg-package_MFEhiplsm_installed_action:testaction:1 + + + + Record Successful Creation Attempts to Files - open O_TRUNC_WRITE + + ocil:ssg-audit_rules_successful_file_modification_open_o_trunc_write_action:testaction:1 + + + + Disable the Automounter + + ocil:ssg-service_autofs_disabled_action:testaction:1 + + + + Disable Geolocation in GNOME3 + + ocil:ssg-dconf_gnome_disable_geolocation_action:testaction:1 + + + + Disable the irc_use_any_tcp_ports SELinux Boolean + + ocil:ssg-sebool_irc_use_any_tcp_ports_action:testaction:1 + + + + Ensure that System Accounts Do Not Run a Shell Upon Login + + ocil:ssg-no_shelllogin_for_systemaccounts_action:testaction:1 + + + + Disable the cluster_use_execmem SELinux Boolean + + ocil:ssg-sebool_cluster_use_execmem_action:testaction:1 + + + + Disable kernel debugfs + + ocil:ssg-kernel_config_debug_fs_action:testaction:1 + + + + Ensure Users Cannot Change GNOME3 Screensaver Lock After Idle Period + + ocil:ssg-dconf_gnome_screensaver_lock_locked_action:testaction:1 + + + + Enable the fips_mode SELinux Boolean + + ocil:ssg-sebool_fips_mode_action:testaction:1 + + + + Disable ypbind Service + + ocil:ssg-service_ypbind_disabled_action:testaction:1 + + + + Disable the httpd_serve_cobbler_files SELinux Boolean + + ocil:ssg-sebool_httpd_serve_cobbler_files_action:testaction:1 + + + + Disable the LDT (local descriptor table) + + ocil:ssg-kernel_config_modify_ldt_syscall_action:testaction:1 + + + + Enable module signature verification + + ocil:ssg-kernel_config_module_sig_action:testaction:1 + + + + Configure auditing of unsuccessful file deletions + + ocil:ssg-audit_delete_failed_action:testaction:1 + + + + Disable the unprivuser_use_svirt SELinux Boolean + + ocil:ssg-sebool_unprivuser_use_svirt_action:testaction:1 + + + + Disable Kernel iwlmvm Module + + ocil:ssg-kernel_module_iwlmvm_disabled_action:testaction:1 + + + + Generate some entropy during boot and runtime + + ocil:ssg-kernel_config_gcc_plugin_latent_entropy_action:testaction:1 + + + + Enable the GNOME3 Screen Locking On Smartcard Removal + + ocil:ssg-dconf_gnome_lock_screen_on_smartcard_removal_action:testaction:1 + + + + Disable the daemons_dump_core SELinux Boolean + + ocil:ssg-sebool_daemons_dump_core_action:testaction:1 + + + + Verify All Account Password Hashes are Shadowed + + ocil:ssg-accounts_password_all_shadowed_action:testaction:1 + + + + Disable Samba + + ocil:ssg-service_smb_disabled_action:testaction:1 + + + + Set the UEFI Boot Loader Password + + ocil:ssg-grub2_uefi_password_action:testaction:1 + + + + Enable Use of Strict Mode Checking + + ocil:ssg-sshd_enable_strictmodes_action:testaction:1 + + + + Ensure PAM Enforces Password Requirements - Minimum Lowercase Characters + + ocil:ssg-accounts_password_pam_lcredit_action:testaction:1 + + + + Disable legacy (BSD) PTY support + + ocil:ssg-kernel_config_legacy_ptys_action:testaction:1 + + + + Disable the use of user namespaces + + ocil:ssg-sysctl_user_max_user_namespaces_action:testaction:1 + + + + Disable the zoneminder_run_sudo SELinux Boolean + + ocil:ssg-sebool_zoneminder_run_sudo_action:testaction:1 + + + + Record Successful Ownership Changes to Files - fchownat + + ocil:ssg-audit_rules_successful_file_modification_fchownat_action:testaction:1 + + + + Ensure auditd Collects File Deletion Events by User - renameat + + ocil:ssg-audit_rules_file_deletion_events_renameat_action:testaction:1 + + + + Configure Polyinstantiation of /tmp Directories + + ocil:ssg-accounts_polyinstantiated_tmp_action:testaction:1 + + + + Verify and Correct Ownership with RPM + + ocil:ssg-rpm_verify_ownership_action:testaction:1 + + + + Disable the httpd_can_sendmail SELinux Boolean + + ocil:ssg-sebool_httpd_can_sendmail_action:testaction:1 + + + + Disable Kernel Parameter for Accepting Source-Routed Packets on all IPv4 Interfaces + + ocil:ssg-sysctl_net_ipv4_conf_all_accept_source_route_action:testaction:1 + + + + Enable Process Accounting (psacct) + + ocil:ssg-service_psacct_enabled_action:testaction:1 + + + + Disable the httpd_mod_auth_pam SELinux Boolean + + ocil:ssg-sebool_httpd_mod_auth_pam_action:testaction:1 + + + + Record Unsuccessful Creation Attempts to Files - openat O_CREAT + + ocil:ssg-audit_rules_unsuccessful_file_modification_openat_o_creat_action:testaction:1 + + + + Uninstall tuned Package + + ocil:ssg-package_tuned_removed_action:testaction:1 + + + + Install Intrusion Detection Software + + ocil:ssg-install_hids_action:testaction:1 + + + + Ensure debug-shell service is not enabled in zIPL + + ocil:ssg-zipl_systemd_debug-shell_argument_absent_action:testaction:1 + + + + Add nosuid Option to /dev/shm + + ocil:ssg-mount_option_dev_shm_nosuid_action:testaction:1 + + + + Uninstall bind Package + + ocil:ssg-package_bind_removed_action:testaction:1 + + + + Disable the mpd_use_cifs SELinux Boolean + + ocil:ssg-sebool_mpd_use_cifs_action:testaction:1 + + + + Detect stack corruption on calls to schedule() + + ocil:ssg-kernel_config_sched_stack_end_check_action:testaction:1 + + + + Extend Audit Backlog Limit for the Audit Daemon + + ocil:ssg-grub2_audit_backlog_limit_argument_action:testaction:1 + + + + Disable the logrotate_use_nfs SELinux Boolean + + ocil:ssg-sebool_logrotate_use_nfs_action:testaction:1 + + + + Configure basic parameters of Audit system + + ocil:ssg-audit_basic_configuration_action:testaction:1 + + + + Disable the gluster_anon_write SELinux Boolean + + ocil:ssg-sebool_gluster_anon_write_action:testaction:1 + + + + Disable the authlogin_radius SELinux Boolean + + ocil:ssg-sebool_authlogin_radius_action:testaction:1 + + + + Record Events that Modify User/Group Information via openat syscall - /etc/group + + ocil:ssg-audit_rules_etc_group_openat_action:testaction:1 + + + + Enable the login_console_enabled SELinux Boolean + + ocil:ssg-sebool_login_console_enabled_action:testaction:1 + + + + Ensure Chrony is only configured with the server directive + + ocil:ssg-chronyd_server_directive_action:testaction:1 + + + + Configure auditd admin_space_left Action on Low Disk Space + + ocil:ssg-auditd_data_retention_admin_space_left_action_action:testaction:1 + + + + Enable Randomized Layout of Virtual Address Space + + ocil:ssg-sysctl_kernel_randomize_va_space_action:testaction:1 + + + + Configure auditing of successful file modifications + + ocil:ssg-audit_modify_success_action:testaction:1 + + + + Require Credential Prompting for Remote Access in GNOME3 + + ocil:ssg-dconf_gnome_remote_access_credential_prompt_action:testaction:1 + + + + Explicit arguments in sudo specifications + + ocil:ssg-sudoers_explicit_command_args_action:testaction:1 + + + + Disable TIPC Support + + ocil:ssg-kernel_module_tipc_disabled_action:testaction:1 + + + + Disable the samba_share_fusefs SELinux Boolean + + ocil:ssg-sebool_samba_share_fusefs_action:testaction:1 + + + + User Initialization Files Must Not Run World-Writable Programs + + ocil:ssg-accounts_user_dot_no_world_writable_programs_action:testaction:1 + + + + Ensure auditd Rules For Unauthorized Attempts To openat Are Ordered Correctly + + ocil:ssg-audit_rules_unsuccessful_file_modification_openat_rule_order_action:testaction:1 + + + + Record Events that Modify User/Group Information - /etc/shadow + + ocil:ssg-audit_rules_usergroup_modification_shadow_action:testaction:1 + + + + Ensure Only Users Logged In To Real tty Can Execute Sudo - sudo use_pty + + ocil:ssg-sudo_add_use_pty_action:testaction:1 + + + + Configure audispd's Plugin disk_full_action When Disk Is Full + + ocil:ssg-auditd_audispd_disk_full_action_action:testaction:1 + + + + Verify Owner on SSH Server config file + + ocil:ssg-file_owner_sshd_config_action:testaction:1 + + + + Add nosuid Option to /tmp + + ocil:ssg-mount_option_tmp_nosuid_action:testaction:1 + + + + Uninstall vsftpd Package + + ocil:ssg-package_vsftpd_removed_action:testaction:1 + + + + Enable page allocator poisoning in zIPL + + ocil:ssg-zipl_page_poison_argument_action:testaction:1 + + + + Ensure gpgcheck Enabled for Local Packages + + ocil:ssg-ensure_gpgcheck_local_packages_action:testaction:1 + + + + Ensure rsyncd service is diabled + + ocil:ssg-service_rsyncd_disabled_action:testaction:1 + + + + Add noexec Option to /home + + ocil:ssg-mount_option_home_noexec_action:testaction:1 + + + + Set Password Minimum Length in login.defs + + ocil:ssg-accounts_password_minlen_login_defs_action:testaction:1 + + + + Verify Permissions on cron.daily + + ocil:ssg-file_permissions_cron_daily_action:testaction:1 + + + + Remove the kernel mapping in user mode + + ocil:ssg-kernel_config_page_table_isolation_action:testaction:1 + + + + Disable vsyscall emulation + + ocil:ssg-kernel_config_legacy_vsyscall_emulate_action:testaction:1 + + + + Enable PAM + + ocil:ssg-sshd_enable_pam_action:testaction:1 + + + + Authorize USB hubs in USBGuard daemon + + ocil:ssg-usbguard_allow_hub_action:testaction:1 + + + + Enable the NTP Daemon + + ocil:ssg-service_ntpd_enabled_action:testaction:1 + + + + Configure auditing of unsuccessful ownership changes + + ocil:ssg-audit_owner_change_failed_action:testaction:1 + + + + Disable the use_lpd_server SELinux Boolean + + ocil:ssg-sebool_use_lpd_server_action:testaction:1 + + + + Configure the root Account for Failed Password Attempts + + ocil:ssg-accounts_passwords_pam_faillock_deny_root_action:testaction:1 + + + + Disable the conman_can_network SELinux Boolean + + ocil:ssg-sebool_conman_can_network_action:testaction:1 + + + + Record Successful Permission Changes to Files - removexattr + + ocil:ssg-audit_rules_successful_file_modification_removexattr_action:testaction:1 + + + + Verify Group Who Owns Backup group File + + ocil:ssg-file_groupowner_backup_etc_group_action:testaction:1 + + + + Record Events that Modify User/Group Information via open_by_handle_at syscall - /etc/passwd + + ocil:ssg-audit_rules_etc_passwd_open_by_handle_at_action:testaction:1 + + + + Disable IPv6 Addressing on All IPv6 Interfaces + + ocil:ssg-sysctl_net_ipv6_conf_all_disable_ipv6_action:testaction:1 + + + + Record Attempts to Alter Time Through clock_settime + + ocil:ssg-audit_rules_time_clock_settime_action:testaction:1 + + + + Configure Accepting Prefix Information in Router Advertisements on All IPv6 Interfaces By Default + + ocil:ssg-sysctl_net_ipv6_conf_default_accept_ra_pinfo_action:testaction:1 + + + + Ensure PAM password complexity module is enabled in system-auth + + ocil:ssg-accounts_password_pam_pwquality_system_auth_action:testaction:1 + + + + Install audispd-plugins Package + + ocil:ssg-package_audispd-plugins_installed_action:testaction:1 + + + + Disable Quota Netlink (quota_nld) + + ocil:ssg-service_quota_nld_disabled_action:testaction:1 + + + + Verify User Who Owns shadow File + + ocil:ssg-file_owner_etc_shadow_action:testaction:1 + + + + Disable the httpd_can_network_memcache SELinux Boolean + + ocil:ssg-sebool_httpd_can_network_memcache_action:testaction:1 + + + + Disable the use_ecryptfs_home_dirs SELinux Boolean + + ocil:ssg-sebool_use_ecryptfs_home_dirs_action:testaction:1 + + + + Install the psacct package + + ocil:ssg-package_psacct_installed_action:testaction:1 + + + + Verify Owner on cron.daily + + ocil:ssg-file_owner_cron_daily_action:testaction:1 + + + + Configure auditd Disk Error Action on Disk Error + + ocil:ssg-auditd_data_disk_error_action_action:testaction:1 + + + + System Audit Logs Must Be Owned By Root + + ocil:ssg-file_ownership_var_log_audit_action:testaction:1 + + + + Disable Kernel Parameter for Sending ICMP Redirects on all IPv4 Interfaces + + ocil:ssg-sysctl_net_ipv4_conf_all_send_redirects_action:testaction:1 + + + + Harden memory copies between kernel and userspace + + ocil:ssg-kernel_config_hardened_usercopy_action:testaction:1 + + + + SSH client uses strong entropy to seed (Bash-like shells) + + ocil:ssg-ssh_client_use_strong_rng_sh_action:testaction:1 + + + + Configure GNOME3 DConf User Profile + + ocil:ssg-enable_dconf_user_profile_action:testaction:1 + + + + Encrypt All File Uploads + + ocil:ssg-httpd_encrypt_file_uploads_action:testaction:1 + + + + Disable Mounting of cramfs + + ocil:ssg-kernel_module_cramfs_disabled_action:testaction:1 + + + + Ensure PAM Enforces Password Requirements - Minimum Different Characters + + ocil:ssg-accounts_password_pam_difok_action:testaction:1 + + + + Disable the virt_use_xserver SELinux Boolean + + ocil:ssg-sebool_virt_use_xserver_action:testaction:1 + + + + Enable Postfix Service + + ocil:ssg-service_postfix_enabled_action:testaction:1 + + + + Enable the postgresql_selinux_unconfined_dbadm SELinux Boolean + + ocil:ssg-sebool_postgresql_selinux_unconfined_dbadm_action:testaction:1 + + + + Enable the gssd_read_tmp SELinux Boolean + + ocil:ssg-sebool_gssd_read_tmp_action:testaction:1 + + + + Enable FIPS Mode + + ocil:ssg-enable_fips_mode_action:testaction:1 + + + + Disable CPU Speed (cpupower) + + ocil:ssg-service_cpupower_disabled_action:testaction:1 + + + + Ensure PAM Enforces Password Requirements - Minimum Different Categories + + ocil:ssg-accounts_password_pam_minclass_action:testaction:1 + + + + Record Events that Modify User/Group Information + + ocil:ssg-audit_rules_usergroup_modification_action:testaction:1 + + + + Disable network management of chrony daemon + + ocil:ssg-chronyd_no_chronyc_network_action:testaction:1 + + + + Ensure All World-Writable Directories Are Owned by root user + + ocil:ssg-dir_perms_world_writable_root_owned_action:testaction:1 + + + + Enable the spamd_enable_home_dirs SELinux Boolean + + ocil:ssg-sebool_spamd_enable_home_dirs_action:testaction:1 + + + + Set Password Maximum Consecutive Repeating Characters + + ocil:ssg-accounts_password_pam_maxrepeat_action:testaction:1 + + + + Avoid speculative indirect branches in kernel + + ocil:ssg-kernel_config_retpoline_action:testaction:1 + + + + Disable Kernel Parameter for Sending ICMP Redirects on all IPv4 Interfaces by Default + + ocil:ssg-sysctl_net_ipv4_conf_default_send_redirects_action:testaction:1 + + + + Disable the exim_can_connect_db SELinux Boolean + + ocil:ssg-sebool_exim_can_connect_db_action:testaction:1 + + + + Make sure that the dconf databases are up-to-date with regards to respective keyfiles + + ocil:ssg-dconf_db_up_to_date_action:testaction:1 + + + + Each Web Content Directory Must Contain An index.html File + + ocil:ssg-httpd_configure_documentroot_action:testaction:1 + + + + Install Virus Scanning Software + + ocil:ssg-install_antivirus_action:testaction:1 + + + + Disable the dbadm_read_user_files SELinux Boolean + + ocil:ssg-sebool_dbadm_read_user_files_action:testaction:1 + + + + Disable the webadm_manage_user_files SELinux Boolean + + ocil:ssg-sebool_webadm_manage_user_files_action:testaction:1 + + + + Disable Access to Network bpf() Syscall From Unprivileged Processes + + ocil:ssg-sysctl_kernel_unprivileged_bpf_disabled_action:testaction:1 + + + + Disable the nagios_run_sudo SELinux Boolean + + ocil:ssg-sebool_nagios_run_sudo_action:testaction:1 + + + + Configure SSSD to Expire Offline Credentials + + ocil:ssg-sssd_offline_cred_expiration_action:testaction:1 + + + + Enable Kernel Paremeter to Log Martian Packets on all IPv4 Interfaces by Default + + ocil:ssg-sysctl_net_ipv4_conf_default_log_martians_action:testaction:1 + + + + Emulate Privileged Access Never (PAN) + + ocil:ssg-kernel_config_arm64_sw_ttbr0_pan_action:testaction:1 + + + + Configure audit according to OSPP requirements + + ocil:ssg-audit_rules_for_ospp_action:testaction:1 + + + + Modify the System Login Banner + + ocil:ssg-banner_etc_issue_action:testaction:1 + + + + Disable the httpd_execmem SELinux Boolean + + ocil:ssg-sebool_httpd_execmem_action:testaction:1 + + + + Disable the polipo_session_users SELinux Boolean + + ocil:ssg-sebool_polipo_session_users_action:testaction:1 + + + + Require Client SMB Packet Signing, if using mount.cifs + + ocil:ssg-mount_option_smb_client_signing_action:testaction:1 + + + + Enable the GNOME3 Login Smartcard Authentication + + ocil:ssg-dconf_gnome_enable_smartcard_auth_action:testaction:1 + + + + Enable use of Berkeley Packet Filter with seccomp + + ocil:ssg-kernel_config_seccomp_filter_action:testaction:1 + + + + Record Unsuccessful Access Attempts to Files - ftruncate + + ocil:ssg-audit_rules_unsuccessful_file_modification_ftruncate_action:testaction:1 + + + + Record Unsuccessful Permission Changes to Files - setxattr + + ocil:ssg-audit_rules_unsuccessful_file_modification_setxattr_action:testaction:1 + + + + Remove the Kerberos Server Package + + ocil:ssg-package_krb5-server_removed_action:testaction:1 + + + + Enable checks on credential management + + ocil:ssg-kernel_config_debug_credentials_action:testaction:1 + + + + Record Events that Modify the System's Discretionary Access Controls - setxattr + + ocil:ssg-audit_rules_dac_modification_setxattr_action:testaction:1 + + + + Disable Kernel Parameter for IPv4 Forwarding on all IPv4 Interfaces + + ocil:ssg-sysctl_net_ipv4_conf_all_forwarding_action:testaction:1 + + + + Disable the sanlock_use_samba SELinux Boolean + + ocil:ssg-sebool_sanlock_use_samba_action:testaction:1 + + + + Uninstall abrt-addon-ccpp Package + + ocil:ssg-package_abrt-addon-ccpp_removed_action:testaction:1 + + + + Ensure auditd Collects Information on the Use of Privileged Commands - at + + ocil:ssg-audit_rules_privileged_commands_at_action:testaction:1 + + + + Disable the mcelog_server SELinux Boolean + + ocil:ssg-sebool_mcelog_server_action:testaction:1 + + + + Ensure rsyslog is Installed + + ocil:ssg-package_rsyslog_installed_action:testaction:1 + + + + Disable GDM Guest Login + + ocil:ssg-gnome_gdm_disable_guest_login_action:testaction:1 + + + + Disable the xdm_bind_vnc_tcp_port SELinux Boolean + + ocil:ssg-sebool_xdm_bind_vnc_tcp_port_action:testaction:1 + + + + Disable the entropyd_use_audio SELinux Boolean + + ocil:ssg-sebool_entropyd_use_audio_action:testaction:1 + + + + Disable the minidlna_read_generic_user_content SELinux Boolean + + ocil:ssg-sebool_minidlna_read_generic_user_content_action:testaction:1 + + + + Configure Accepting Router Preference in Router Advertisements on All IPv6 Interfaces By Default + + ocil:ssg-sysctl_net_ipv6_conf_default_accept_ra_rtr_pref_action:testaction:1 + + + + Configure A Banner Page For Each Website + + ocil:ssg-httpd_configure_banner_page_action:testaction:1 + + + + Uninstall geolite2-country Package + + ocil:ssg-package_geolite2-country_removed_action:testaction:1 + + + + Ensure auditd Collects Information on the Use of Privileged Commands - poweroff + + ocil:ssg-audit_privileged_commands_poweroff_action:testaction:1 + + + + Set Default iptables Policy for Incoming Packets + + ocil:ssg-set_iptables_default_rule_action:testaction:1 + + + + Install tar Package + + ocil:ssg-package_tar_installed_action:testaction:1 + + + + Remove User Host-Based Authentication Files + + ocil:ssg-no_user_host_based_files_action:testaction:1 + + + + Disable the git_system_use_cifs SELinux Boolean + + ocil:ssg-sebool_git_system_use_cifs_action:testaction:1 + + + + Install AIDE + + ocil:ssg-package_aide_installed_action:testaction:1 + + + + Disable the httpd_tmp_exec SELinux Boolean + + ocil:ssg-sebool_httpd_tmp_exec_action:testaction:1 + + + + Install usbguard Package + + ocil:ssg-package_usbguard_installed_action:testaction:1 + + + + Disable the ftpd_use_cifs SELinux Boolean + + ocil:ssg-sebool_ftpd_use_cifs_action:testaction:1 + + + + Disable the abrt_handle_event SELinux Boolean + + ocil:ssg-sebool_abrt_handle_event_action:testaction:1 + + + + Verify Permissions on Backup group File + + ocil:ssg-file_permissions_backup_etc_group_action:testaction:1 + + + + Install the pcsc-lite package + + ocil:ssg-package_pcsc-lite_installed_action:testaction:1 + + + + Ensure McAfee Endpoint Security for Linux (ENSL) is running + + ocil:ssg-agent_mfetpd_running_action:testaction:1 + + + + Disable Software RAID Monitor (mdmonitor) + + ocil:ssg-service_mdmonitor_disabled_action:testaction:1 + + + + Warn on W+X mappings found at boot + + ocil:ssg-kernel_config_debug_wx_action:testaction:1 + + + + Disable the httpd_use_nfs SELinux Boolean + + ocil:ssg-sebool_httpd_use_nfs_action:testaction:1 + + + + Configure Polyinstantiation of /var/tmp Directories + + ocil:ssg-accounts_polyinstantiated_var_tmp_action:testaction:1 + + + + Disable GNOME3 Automounting + + ocil:ssg-dconf_gnome_disable_automount_action:testaction:1 + + + + Build and Test AIDE Database + + ocil:ssg-aide_build_database_action:testaction:1 + + + + Ensure auditd Collects Information on the Use of Privileged Commands - postdrop + + ocil:ssg-audit_rules_privileged_commands_postdrop_action:testaction:1 + + + + Disable the authlogin_yubikey SELinux Boolean + + ocil:ssg-sebool_authlogin_yubikey_action:testaction:1 + + + + Disable Kernel cfg80211 Module + + ocil:ssg-kernel_module_cfg80211_disabled_action:testaction:1 + + + + Enable SLUB/SLAB allocator poisoning in zIPL + + ocil:ssg-zipl_slub_debug_argument_action:testaction:1 + + + + User a virtually-mapped stack + + ocil:ssg-kernel_config_vmap_stack_action:testaction:1 + + + + Disable the httpd_can_check_spam SELinux Boolean + + ocil:ssg-sebool_httpd_can_check_spam_action:testaction:1 + + + + Disable the httpd_can_network_relay SELinux Boolean + + ocil:ssg-sebool_httpd_can_network_relay_action:testaction:1 + + + + Create Warning Banners for All FTP Users + + ocil:ssg-ftp_present_banner_action:testaction:1 + + + + Verify Any Configured IPSec Tunnel Connections + + ocil:ssg-libreswan_approved_tunnels_action:testaction:1 + + + + Disable the gluster_export_all_ro SELinux Boolean + + ocil:ssg-sebool_gluster_export_all_ro_action:testaction:1 + + + + Ensure Logrotate Runs Periodically + + ocil:ssg-ensure_logrotate_activated_action:testaction:1 + + + + Disable the dbadm_manage_user_files SELinux Boolean + + ocil:ssg-sebool_dbadm_manage_user_files_action:testaction:1 + + + + Disable the openvpn_run_unconfined SELinux Boolean + + ocil:ssg-sebool_openvpn_run_unconfined_action:testaction:1 + + + + Disable Ctrl-Alt-Del Reboot Activation + + ocil:ssg-disable_ctrlaltdel_reboot_action:testaction:1 + + + + Audit Configuration Files Must Be Owned By Root + + ocil:ssg-file_ownership_audit_configuration_action:testaction:1 + + + + Set Interval For Counting Failed Password Attempts + + ocil:ssg-accounts_passwords_pam_faillock_interval_action:testaction:1 + + + + Record Events that Modify User/Group Information via open_by_handle_at syscall - /etc/group + + ocil:ssg-audit_rules_etc_group_open_by_handle_at_action:testaction:1 + + + + Verify the UEFI Boot Loader grub.cfg Group Ownership + + ocil:ssg-file_groupowner_efi_grub2_cfg_action:testaction:1 + + + + Enable Kernel Parameter to Enforce DAC on Symlinks + + ocil:ssg-sysctl_fs_protected_symlinks_action:testaction:1 + + + + Disable vsyscalls + + ocil:ssg-grub2_vsyscall_argument_action:testaction:1 + + + + Disable the saslauthd_read_shadow SELinux Boolean + + ocil:ssg-sebool_saslauthd_read_shadow_action:testaction:1 + + + + Configure auditing of unsuccessful file modifications + + ocil:ssg-audit_modify_failed_action:testaction:1 + + + + Disable Cockpit Management Server + + ocil:ssg-service_cockpit_disabled_action:testaction:1 + + + + Disable the sge_domain_can_network_connect SELinux Boolean + + ocil:ssg-sebool_sge_domain_can_network_connect_action:testaction:1 + + + + Install libselinux Package + + ocil:ssg-package_libselinux_installed_action:testaction:1 + + + + Verify File Hashes with RPM + + ocil:ssg-rpm_verify_hashes_action:testaction:1 + + + + Disable the virt_use_execmem SELinux Boolean + + ocil:ssg-sebool_virt_use_execmem_action:testaction:1 + + + + Record Unsuccessful Delete Attempts to Files - rename + + ocil:ssg-audit_rules_unsuccessful_file_modification_rename_action:testaction:1 + + + + Record Unsuccessful Access Attempts to Files - open_by_handle_at + + ocil:ssg-audit_rules_unsuccessful_file_modification_open_by_handle_at_action:testaction:1 + + + + Remove the GDM Package Group + + ocil:ssg-package_gdm_removed_action:testaction:1 + + + + Disable support for /proc/kkcore + + ocil:ssg-kernel_config_proc_kcore_action:testaction:1 + + + + Record Unsuccessful Creation Attempts to Files - open_by_handle_at O_CREAT + + ocil:ssg-audit_rules_unsuccessful_file_modification_open_by_handle_at_o_creat_action:testaction:1 + + + + Disable ntpdate Service (ntpdate) + + ocil:ssg-service_ntpdate_disabled_action:testaction:1 + + + + Record Attempts to Alter Logon and Logout Events - tallylog + + ocil:ssg-audit_rules_login_events_tallylog_action:testaction:1 + + + + SSH client uses strong entropy to seed (for CSH like shells) + + ocil:ssg-ssh_client_use_strong_rng_csh_action:testaction:1 + + + + Configure Maximum Number of Autoconfigured Addresses on All IPv6 Interfaces + + ocil:ssg-sysctl_net_ipv6_conf_all_max_addresses_action:testaction:1 + + + + Disable rsh Service + + ocil:ssg-service_rsh_disabled_action:testaction:1 + + + + Restrict Serial Port Root Logins + + ocil:ssg-restrict_serial_port_logins_action:testaction:1 + + + + Record Events that Modify User/Group Information via open syscall - /etc/group + + ocil:ssg-audit_rules_etc_group_open_action:testaction:1 + + + + Disable KDump Kernel Crash Analyzer (kdump) + + ocil:ssg-service_kdump_disabled_action:testaction:1 + + + + Disable telnet Service + + ocil:ssg-service_telnet_disabled_action:testaction:1 + + + + Uninstall rsh Package + + ocil:ssg-package_rsh_removed_action:testaction:1 + + + + Add nodev Option to /home + + ocil:ssg-mount_option_home_nodev_action:testaction:1 + + + + Disable kernel support for MISC binaries + + ocil:ssg-kernel_config_binfmt_misc_action:testaction:1 + + + + Ensure auditd Collects Information on the Use of Privileged Commands - usermod + + ocil:ssg-audit_rules_privileged_commands_usermod_action:testaction:1 + + + + Install OpenSSH client software + + ocil:ssg-package_openssh-clients_installed_action:testaction:1 + + + + Disable the glance_api_can_network SELinux Boolean + + ocil:ssg-sebool_glance_api_can_network_action:testaction:1 + + + + Enable the USBGuard Service + + ocil:ssg-service_usbguard_enabled_action:testaction:1 + + + + Disable Kernel mac80211 Module + + ocil:ssg-kernel_module_mac80211_disabled_action:testaction:1 + + + + Firewalld Must Employ a Deny-all, Allow-by-exception Policy for Allowing Connections to Other Systems + + ocil:ssg-configured_firewalld_default_deny_action:testaction:1 + + + + Disable Host-Based Authentication + + ocil:ssg-disable_host_auth_action:testaction:1 + + + + Disable compatibility with brk() + + ocil:ssg-kernel_config_compat_brk_action:testaction:1 + + + + Disable the lsmd_plugin_connect_any SELinux Boolean + + ocil:ssg-sebool_lsmd_plugin_connect_any_action:testaction:1 + + + + Set SSH Daemon LogLevel to VERBOSE + + ocil:ssg-sshd_set_loglevel_verbose_action:testaction:1 + + + + Ensure gnutls-utils is installed + + ocil:ssg-package_gnutls-utils_installed_action:testaction:1 + + + + MIME types for csh or sh shell programs must be disabled + + ocil:ssg-httpd_disable_mime_types_action:testaction:1 + + + + Configure auditd max_log_file_action Upon Reaching Maximum Log Size + + ocil:ssg-auditd_data_retention_max_log_file_action_action:testaction:1 + + + + Configure auditing of successful ownership changes + + ocil:ssg-audit_owner_change_success_action:testaction:1 + + + + Uninstall python3-abrt-addon Package + + ocil:ssg-package_python3-abrt-addon_removed_action:testaction:1 + + + + Ensure journald is configured to send logs to rsyslog + + ocil:ssg-journald_forward_to_syslog_action:testaction:1 + + + + Disable the ftpd_full_access SELinux Boolean + + ocil:ssg-sebool_ftpd_full_access_action:testaction:1 + + + + Ensure Users Re-Authenticate for Privilege Escalation - sudo NOPASSWD + + ocil:ssg-sudo_remove_nopasswd_action:testaction:1 + + + + Set Boot Loader Password in grub2 + + ocil:ssg-grub2_password_action:testaction:1 + + + + Uninstall libreport-plugin-logger Package + + ocil:ssg-package_libreport-plugin-logger_removed_action:testaction:1 + + + + Ensure PAM Enforces Password Requirements - Minimum Uppercase Characters + + ocil:ssg-accounts_password_pam_ucredit_action:testaction:1 + + + + Disable the use_fusefs_home_dirs SELinux Boolean + + ocil:ssg-sebool_use_fusefs_home_dirs_action:testaction:1 + + + + Add nosuid Option to /srv + + ocil:ssg-mount_option_srv_nosuid_action:testaction:1 + + + + Configure Backups of User Data + + ocil:ssg-configure_user_data_backups_action:testaction:1 + + + + Record Successful Access Attempts to Files - ftruncate + + ocil:ssg-audit_rules_successful_file_modification_ftruncate_action:testaction:1 + + + + Uninstall libreport-plugin-rhtsupport Package + + ocil:ssg-package_libreport-plugin-rhtsupport_removed_action:testaction:1 + + + + Configure SSSD to Expire SSH Known Hosts + + ocil:ssg-sssd_ssh_known_hosts_timeout_action:testaction:1 + + + + Disable the cobbler_use_nfs SELinux Boolean + + ocil:ssg-sebool_cobbler_use_nfs_action:testaction:1 + + + + Verify User Who Owns /var/log/messages File + + ocil:ssg-file_owner_var_log_messages_action:testaction:1 + + + + Record Successful Creation Attempts to Files - openat O_CREAT + + ocil:ssg-audit_rules_successful_file_modification_openat_o_creat_action:testaction:1 + + + + Disable the cluster_can_network_connect SELinux Boolean + + ocil:ssg-sebool_cluster_can_network_connect_action:testaction:1 + + + + Enable Smartcards in SSSD + + ocil:ssg-sssd_enable_smartcards_action:testaction:1 + + + + Disable the collectd_tcp_network_connect SELinux Boolean + + ocil:ssg-sebool_collectd_tcp_network_connect_action:testaction:1 + + + + Ensure Rsyslog Encrypts Off-Loaded Audit Records + + ocil:ssg-rsyslog_encrypt_offload_actionsendstreamdrivermode_action:testaction:1 + + + + Audit Tools Must Be Owned by Root + + ocil:ssg-file_audit_tools_ownership_action:testaction:1 + + + + Enable Auditing to Start Prior to the Audit Daemon in zIPL + + ocil:ssg-zipl_audit_argument_action:testaction:1 + + + + Verify that Shared Library Directories Have Root Ownership + + ocil:ssg-dir_ownership_library_dirs_action:testaction:1 + + + + Disable debug-shell SystemD Service + + ocil:ssg-service_debug-shell_disabled_action:testaction:1 + + + + Configure the polyinstantiation_enabled SELinux Boolean + + ocil:ssg-sebool_polyinstantiation_enabled_action:testaction:1 + + + + Ensure auditd Collects Information on the Use of Privileged Commands - unix_update + + ocil:ssg-audit_rules_privileged_commands_unix_update_action:testaction:1 + + + + Restrict Access to Kernel Message Buffer + + ocil:ssg-sysctl_kernel_dmesg_restrict_action:testaction:1 + + + + Configure ARP filtering for All IPv4 Interfaces + + ocil:ssg-sysctl_net_ipv4_conf_all_arp_filter_action:testaction:1 + + + + Only the VDSM User Can Use sudo NOPASSWD + + ocil:ssg-sudo_vdsm_nopasswd_action:testaction:1 + + + + Disable the ssh_chroot_rw_homedirs SELinux Boolean + + ocil:ssg-sebool_ssh_chroot_rw_homedirs_action:testaction:1 + + + + Limit Users' SSH Access + + ocil:ssg-sshd_limit_user_access_action:testaction:1 + + + + Record Unsuccessful Access Attempts to Files - truncate + + ocil:ssg-audit_rules_unsuccessful_file_modification_truncate_action:testaction:1 + + + + Configure tmux to lock session after inactivity + + ocil:ssg-configure_tmux_lock_after_time_action:testaction:1 + + + + Ensure cron Is Logging To Rsyslog + + ocil:ssg-rsyslog_cron_logging_action:testaction:1 + + + + Disable the exim_manage_user_files SELinux Boolean + + ocil:ssg-sebool_exim_manage_user_files_action:testaction:1 + + + + Install the tmux Package + + ocil:ssg-package_tmux_installed_action:testaction:1 + + + + Disable the virt_transition_userdomain SELinux Boolean + + ocil:ssg-sebool_virt_transition_userdomain_action:testaction:1 + + + + Disable the polipo_use_nfs SELinux Boolean + + ocil:ssg-sebool_polipo_use_nfs_action:testaction:1 + + + + Disable the rsync_client SELinux Boolean + + ocil:ssg-sebool_rsync_client_action:testaction:1 + + + + Enable the mount_anyfile SELinux Boolean + + ocil:ssg-sebool_mount_anyfile_action:testaction:1 + + + + Disable the httpd_dbus_avahi SELinux Boolean + + ocil:ssg-sebool_httpd_dbus_avahi_action:testaction:1 + + + + Ensure auditd Collects Information on the Use of Privileged Commands - unix_chkpwd + + ocil:ssg-audit_rules_privileged_commands_unix_chkpwd_action:testaction:1 + + + + Disable rexec Service + + ocil:ssg-service_rexec_disabled_action:testaction:1 + + + + Disable Quagga Service + + ocil:ssg-service_zebra_disabled_action:testaction:1 + + + + Use Kerberos Security on All Exports + + ocil:ssg-use_kerberos_security_all_exports_action:testaction:1 + + + + Require Client Certificates + + ocil:ssg-httpd_require_client_certs_action:testaction:1 + + + + Disable the daemons_use_tcp_wrapper SELinux Boolean + + ocil:ssg-sebool_daemons_use_tcp_wrapper_action:testaction:1 + + + + Configure GnuTLS library to use DoD-approved TLS Encryption + + ocil:ssg-configure_gnutls_tls_crypto_policy_action:testaction:1 + + + + Ensure /var/tmp Located On Separate Partition + + ocil:ssg-partition_for_var_tmp_action:testaction:1 + + + + Record Unsuccessful Permission Changes to Files - removexattr + + ocil:ssg-audit_rules_unsuccessful_file_modification_removexattr_action:testaction:1 + + + + Verify ip6tables Enabled if Using IPv6 + + ocil:ssg-service_ip6tables_enabled_action:testaction:1 + + + + Disable the virt_use_usb SELinux Boolean + + ocil:ssg-sebool_virt_use_usb_action:testaction:1 + + + + Record Attempts to Alter Logon and Logout Events - faillock + + ocil:ssg-audit_rules_login_events_faillock_action:testaction:1 + + + + Disable the rsync_export_all_ro SELinux Boolean + + ocil:ssg-sebool_rsync_export_all_ro_action:testaction:1 + + + + Configure HTTP PERL Scripts To Use TAINT Option + + ocil:ssg-httpd_configure_perl_taint_action:testaction:1 + + + + Enable the logging_syslogd_use_tty SELinux Boolean + + ocil:ssg-sebool_logging_syslogd_use_tty_action:testaction:1 + + + + Enable GNOME3 Screensaver Idle Activation + + ocil:ssg-dconf_gnome_screensaver_idle_activation_enabled_action:testaction:1 + + + + Specify UID and GID for Anonymous NFS Connections + + ocil:ssg-nfs_no_anonymous_action:testaction:1 + + + + Enable the kerberos_enabled SELinux Boolean + + ocil:ssg-sebool_kerberos_enabled_action:testaction:1 + + + + Disable the mcelog_foreground SELinux Boolean + + ocil:ssg-sebool_mcelog_foreground_action:testaction:1 + + + + Audit Tools Must Be Group-owned by Root + + ocil:ssg-file_audit_tools_group_ownership_action:testaction:1 + + + + Ensure auditd Collects Information on the Use of Privileged Commands - userhelper + + ocil:ssg-audit_rules_privileged_commands_userhelper_action:testaction:1 + + + + Ensure /home Located On Separate Partition + + ocil:ssg-partition_for_home_action:testaction:1 + + + + Certificate status checking in SSSD + + ocil:ssg-sssd_certificate_verification_action:testaction:1 + + + + Set kernel parameter 'crypto.fips_enabled' to 1 + + ocil:ssg-sysctl_crypto_fips_enabled_action:testaction:1 + + + + Set Password Maximum Age + + ocil:ssg-accounts_maximum_age_login_defs_action:testaction:1 + + + + Remove the OpenSSH Server Package + + ocil:ssg-package_openssh-server_removed_action:testaction:1 + + + + Add nosuid Option to /home + + ocil:ssg-mount_option_home_nosuid_action:testaction:1 + + + + Disable the ssh_keysign SELinux Boolean + + ocil:ssg-sebool_ssh_keysign_action:testaction:1 + + + + Disable the mozilla_plugin_can_network_connect SELinux Boolean + + ocil:ssg-sebool_mozilla_plugin_can_network_connect_action:testaction:1 + + + + Configure SSSD LDAP Backend Client CA Certificate + + ocil:ssg-sssd_ldap_configure_tls_ca_action:testaction:1 + + + + Enable authselect + + ocil:ssg-enable_authselect_action:testaction:1 + + + + Disable Kernel iwlwifi Module + + ocil:ssg-kernel_module_iwlwifi_disabled_action:testaction:1 + + + + Support session locking with tmux + + ocil:ssg-configure_bashrc_exec_tmux_action:testaction:1 + + + + Configure auditd Number of Logs Retained + + ocil:ssg-auditd_data_retention_num_logs_action:testaction:1 + + + + Configure Libreswan to use System Crypto Policy + + ocil:ssg-configure_libreswan_crypto_policy_action:testaction:1 + + + + Authorize Human Interface Devices in USBGuard daemon + + ocil:ssg-usbguard_allow_hid_action:testaction:1 + + + + Set number of records to cause an explicit flush to audit logs + + ocil:ssg-auditd_freq_action:testaction:1 + + + + Disable the mplayer_execstack SELinux Boolean + + ocil:ssg-sebool_mplayer_execstack_action:testaction:1 + + + + Set Permissions on the /var/log/httpd/ Directory + + ocil:ssg-dir_perms_var_log_httpd_action:testaction:1 + + + + Disable the fcron_crond SELinux Boolean + + ocil:ssg-sebool_fcron_crond_action:testaction:1 + + + + Disable the exim_read_user_files SELinux Boolean + + ocil:ssg-sebool_exim_read_user_files_action:testaction:1 + + + + Set Existing Passwords Maximum Age + + ocil:ssg-accounts_password_set_max_life_existing_action:testaction:1 + + + + Record Unsuccessful Ownership Changes to Files - fchown + + ocil:ssg-audit_rules_unsuccessful_file_modification_fchown_action:testaction:1 + + + + Configure maximum number of process identifiers + + ocil:ssg-sysctl_kernel_pid_max_action:testaction:1 + + + + Verify Group Who Owns SSH Server config file + + ocil:ssg-file_groupowner_sshd_config_action:testaction:1 + + + + Add nosuid Option to /boot + + ocil:ssg-mount_option_boot_nosuid_action:testaction:1 + + + + Lock Accounts After Failed Password Attempts + + ocil:ssg-accounts_passwords_pam_faillock_deny_action:testaction:1 + + + + Uninstall dovecot Package + + ocil:ssg-package_dovecot_removed_action:testaction:1 + + + + Disable Modprobe Loading of USB Storage Driver + + ocil:ssg-kernel_module_usb-storage_disabled_action:testaction:1 + + + + Restrict Web Browser Use for Administrative Accounts + + ocil:ssg-no_root_webbrowsing_action:testaction:1 + + + + Verify Group Who Owns cron.hourly + + ocil:ssg-file_groupowner_cron_hourly_action:testaction:1 + + + + Disable the virt_sandbox_use_netlink SELinux Boolean + + ocil:ssg-sebool_virt_sandbox_use_netlink_action:testaction:1 + + + + Disable the ftpd_anon_write SELinux Boolean + + ocil:ssg-sebool_ftpd_anon_write_action:testaction:1 + + + + Ensure Only Users Logged In To Real tty Can Execute Sudo - sudo requiretty + + ocil:ssg-sudo_add_requiretty_action:testaction:1 + + + + Verify Permissions on Backup gshadow File + + ocil:ssg-file_permissions_backup_etc_gshadow_action:testaction:1 + + + + Disable chrony daemon from acting as server + + ocil:ssg-chronyd_client_only_action:testaction:1 + + + + Perform full reference count validation + + ocil:ssg-kernel_config_refcount_full_action:testaction:1 + + + + Ensure All World-Writable Directories Are Group Owned by a System Account + + ocil:ssg-dir_perms_world_writable_system_owned_group_action:testaction:1 + + + + Disable loading and unloading of kernel modules + + ocil:ssg-sysctl_kernel_modules_disabled_action:testaction:1 + + + + Configure System Cryptography Policy + + ocil:ssg-configure_crypto_policy_action:testaction:1 + + + + Modify the System Message of the Day Banner + + ocil:ssg-banner_etc_motd_action:testaction:1 + + + + System Audit Logs Must Have Mode 0640 or Less Permissive + + ocil:ssg-file_permissions_var_log_audit_action:testaction:1 + + + + Disable the mozilla_plugin_use_gps SELinux Boolean + + ocil:ssg-sebool_mozilla_plugin_use_gps_action:testaction:1 + + + + Uninstall nfs-utils Package + + ocil:ssg-package_nfs-utils_removed_action:testaction:1 + + + + Enable the nfs_export_all_ro SELinux Boolean + + ocil:ssg-sebool_nfs_export_all_ro_action:testaction:1 + + + + Ensure zIPL bootmap is up to date + + ocil:ssg-zipl_bootmap_is_up_to_date_action:testaction:1 + + + + Install McAfee Endpoint Security for Linux (ENSL) + + ocil:ssg-package_mcafeetp_installed_action:testaction:1 + + + + Verify Group Ownership of Message of the Day Banner + + ocil:ssg-file_groupowner_etc_motd_action:testaction:1 + + + + Ensure syslog-ng is Installed + + ocil:ssg-package_syslogng_installed_action:testaction:1 + + + + Disable the glance_use_fusefs SELinux Boolean + + ocil:ssg-sebool_glance_use_fusefs_action:testaction:1 + + + + Ensure SNMP Read Write is disabled + + ocil:ssg-snmpd_no_rwusers_action:testaction:1 + + + + Kernel panic oops + + ocil:ssg-kernel_config_panic_on_oops_action:testaction:1 + + + + Implement Blank Screensaver + + ocil:ssg-dconf_gnome_screensaver_mode_blank_action:testaction:1 + + + + Verify Permissions on cron.d + + ocil:ssg-file_permissions_cron_d_action:testaction:1 + + + + A remote time server for Chrony is configured + + ocil:ssg-chronyd_specify_remote_server_action:testaction:1 + + + + Configure auditd Max Log File Size + + ocil:ssg-auditd_data_retention_max_log_file_action:testaction:1 + + + + Ensure Home Directories are Created for New Users + + ocil:ssg-accounts_have_homedir_login_defs_action:testaction:1 + + + + Disable Ctrl-Alt-Del Burst Action + + ocil:ssg-disable_ctrlaltdel_burstaction_action:testaction:1 + + + + Disable the httpd_manage_ipa SELinux Boolean + + ocil:ssg-sebool_httpd_manage_ipa_action:testaction:1 + + + + Disable DHCP Client in ifcfg + + ocil:ssg-sysconfig_networking_bootproto_ifcfg_action:testaction:1 + + + + Verify Owner on cron.hourly + + ocil:ssg-file_owner_cron_hourly_action:testaction:1 + + + + Disable the boinc_execmem SELinux Boolean + + ocil:ssg-sebool_boinc_execmem_action:testaction:1 + + + + Ensure auditd Rules For Unauthorized Attempts To open Are Ordered Correctly + + ocil:ssg-audit_rules_unsuccessful_file_modification_open_rule_order_action:testaction:1 + + + + Record Successful Creation Attempts to Files - open_by_handle_at O_TRUNC_WRITE + + ocil:ssg-audit_rules_successful_file_modification_open_by_handle_at_o_trunc_write_action:testaction:1 + + + + Disable snmpd Service + + ocil:ssg-service_snmpd_disabled_action:testaction:1 + + + + Configure Response Mode of ARP Requests for All IPv4 Interfaces + + ocil:ssg-sysctl_net_ipv4_conf_all_arp_ignore_action:testaction:1 + + + + Uninstall krb5-workstation Package + + ocil:ssg-package_krb5-workstation_removed_action:testaction:1 + + + + Disable SSH Support for User Known Hosts + + ocil:ssg-sshd_disable_user_known_hosts_action:testaction:1 + + + + Enable Yama support + + ocil:ssg-kernel_config_security_yama_action:testaction:1 + + + + Enable automatic signing of all modules + + ocil:ssg-kernel_config_module_sig_all_action:testaction:1 + + + + Verify Permissions on cron.weekly + + ocil:ssg-file_permissions_cron_weekly_action:testaction:1 + + + + Extend Audit Backlog Limit for the Audit Daemon in zIPL + + ocil:ssg-zipl_audit_backlog_limit_argument_action:testaction:1 + + + + Uninstall quagga Package + + ocil:ssg-package_quagga_removed_action:testaction:1 + + + + Enforce pam_faillock for Local Accounts Only + + ocil:ssg-accounts_passwords_pam_faillock_enforce_local_action:testaction:1 + + + + Disable Automatic Bug Reporting Tool (abrtd) + + ocil:ssg-service_abrtd_disabled_action:testaction:1 + + + + Disable the cobbler_can_network_connect SELinux Boolean + + ocil:ssg-sebool_cobbler_can_network_connect_action:testaction:1 + + + + Verify Permissions on cron.hourly + + ocil:ssg-file_permissions_cron_hourly_action:testaction:1 + + + + Configure Kernel Parameter for Accepting Secure Redirects By Default + + ocil:ssg-sysctl_net_ipv4_conf_default_secure_redirects_action:testaction:1 + + + + Ensure auditd Collects Information on the Use of Privileged Commands - passwd + + ocil:ssg-audit_rules_privileged_commands_passwd_action:testaction:1 + + + + Verify Permissions on cron.monthly + + ocil:ssg-file_permissions_cron_monthly_action:testaction:1 + + + + All Interactive User Home Directories Must Be Group-Owned By The Primary User + + ocil:ssg-file_groupownership_home_directories_action:testaction:1 + + + + Configure auditing of successful file deletions + + ocil:ssg-audit_delete_success_action:testaction:1 + + + + Disable the logging_syslogd_run_nagios_plugins SELinux Boolean + + ocil:ssg-sebool_logging_syslogd_run_nagios_plugins_action:testaction:1 + + + + Disable the named_tcp_bind_http_port SELinux Boolean + + ocil:ssg-sebool_named_tcp_bind_http_port_action:testaction:1 + + + + Uninstall geolite2-city Package + + ocil:ssg-package_geolite2-city_removed_action:testaction:1 + + + + The Chrony package is installed + + ocil:ssg-package_chrony_installed_action:testaction:1 + + + + Ensure the Default Umask is Set Correctly For Interactive Users + + ocil:ssg-accounts_umask_interactive_users_action:testaction:1 + + + + Disable vsyscalls in zIPL + + ocil:ssg-zipl_vsyscall_argument_action:testaction:1 + + + + Verify Permissions on SSH Server Private *_key Key Files + + ocil:ssg-file_permissions_sshd_private_key_action:testaction:1 + + + + Disable the piranha_lvs_can_network_connect SELinux Boolean + + ocil:ssg-sebool_piranha_lvs_can_network_connect_action:testaction:1 + + + + Verify the UEFI Boot Loader grub.cfg User Ownership + + ocil:ssg-file_owner_efi_grub2_cfg_action:testaction:1 + + + + Verify User Who Owns /etc/cron.allow file + + ocil:ssg-file_owner_cron_allow_action:testaction:1 + + + + Mount Remote Filesystems with Kerberos Security + + ocil:ssg-mount_option_krb_sec_remote_filesystems_action:testaction:1 + + + + Configure OpenSSL library to use TLS Encryption + + ocil:ssg-configure_openssl_tls_crypto_policy_action:testaction:1 + + + + Record Events that Modify User/Group Information via open syscall - /etc/shadow + + ocil:ssg-audit_rules_etc_shadow_open_action:testaction:1 + + + + Enable the unconfined_chrome_sandbox_transition SELinux Boolean + + ocil:ssg-sebool_unconfined_chrome_sandbox_transition_action:testaction:1 + + + + Verify Group Who Owns /var/log/syslog File + + ocil:ssg-file_groupowner_var_log_syslog_action:testaction:1 + + + + Disable the mozilla_plugin_bind_unreserved_ports SELinux Boolean + + ocil:ssg-sebool_mozilla_plugin_bind_unreserved_ports_action:testaction:1 + + + + Disable Kernel Image Loading + + ocil:ssg-sysctl_kernel_kexec_load_disabled_action:testaction:1 + + + + Configure auditing of successful file accesses + + ocil:ssg-audit_access_success_action:testaction:1 + + + + Disable the xserver_clients_write_xshm SELinux Boolean + + ocil:ssg-sebool_xserver_clients_write_xshm_action:testaction:1 + + + + Disable the virt_use_fusefs SELinux Boolean + + ocil:ssg-sebool_virt_use_fusefs_action:testaction:1 + + + + Verify Group Who Owns cron.weekly + + ocil:ssg-file_groupowner_cron_weekly_action:testaction:1 + + + + Configure the httpd_builtin_scripting SELinux Boolean + + ocil:ssg-sebool_httpd_builtin_scripting_action:testaction:1 + + + + Ensure auditd Collects Information on the Use of Privileged Commands - reboot + + ocil:ssg-audit_privileged_commands_reboot_action:testaction:1 + + + + Set Account Expiration Following Inactivity + + ocil:ssg-account_disable_post_pw_expiration_action:testaction:1 + + + + Ensure All Groups on the System Have Unique Group ID + + ocil:ssg-group_unique_id_action:testaction:1 + + + + Disable the httpd_verify_dns SELinux Boolean + + ocil:ssg-sebool_httpd_verify_dns_action:testaction:1 + + + + Record Events that Modify the System's Discretionary Access Controls - fchmod + + ocil:ssg-audit_rules_dac_modification_fchmod_action:testaction:1 + + + + Disable the httpd_can_network_connect_db SELinux Boolean + + ocil:ssg-sebool_httpd_can_network_connect_db_action:testaction:1 + + + + Disable the tmpreaper_use_nfs SELinux Boolean + + ocil:ssg-sebool_tmpreaper_use_nfs_action:testaction:1 + + + + Ensure gpgcheck Enabled In Main yum Configuration + + ocil:ssg-ensure_gpgcheck_globally_activated_action:testaction:1 + + + + Uninstall openldap-servers Package + + ocil:ssg-package_openldap-servers_removed_action:testaction:1 + + + + Configure SSH Server to Use FIPS 140-2 Validated MACs: opensshserver.config + + ocil:ssg-harden_sshd_macs_opensshserver_conf_crypto_policy_action:testaction:1 + + + + Prevent remote hosts from connecting to the proxy display + + ocil:ssg-sshd_x11_use_localhost_action:testaction:1 + + + + Harden the operation of the BPF just-in-time compiler + + ocil:ssg-sysctl_net_core_bpf_jit_harden_action:testaction:1 + + + + Backup interactive scripts on the production web server are prohibited + + ocil:ssg-httpd_remove_backups_action:testaction:1 + + + + Disable the xserver_object_manager SELinux Boolean + + ocil:ssg-sebool_xserver_object_manager_action:testaction:1 + + + + Ensure PAM Enforces Password Requirements - Maximum Consecutive Repeating Characters from Same Character Class + + ocil:ssg-accounts_password_pam_maxclassrepeat_action:testaction:1 + + + + Enable cron Service + + ocil:ssg-service_cron_enabled_action:testaction:1 + + + + Ensure SELinux Not Disabled in /etc/default/grub + + ocil:ssg-grub2_enable_selinux_action:testaction:1 + + + + Ensure Red Hat GPG Key Installed + + ocil:ssg-ensure_redhat_gpgkey_installed_action:testaction:1 + + + + Ensure there are no legacy + NIS entries in /etc/passwd + + ocil:ssg-no_legacy_plus_entries_etc_passwd_action:testaction:1 + + + + A public web server, if hosted on the NIPRNet, must be isolated in an accredited DoD DMZ extension + + ocil:ssg-httpd_nipr_accredited_dmz_action:testaction:1 + + + + Disable the cobbler_use_cifs SELinux Boolean + + ocil:ssg-sebool_cobbler_use_cifs_action:testaction:1 + + + + Configure Accepting Default Router in Router Advertisements on All IPv6 Interfaces By Default + + ocil:ssg-sysctl_net_ipv6_conf_default_accept_ra_defrtr_action:testaction:1 + + + + Disable the use_nfs_home_dirs SELinux Boolean + + ocil:ssg-sebool_use_nfs_home_dirs_action:testaction:1 + + + + Disable the httpd_dontaudit_search_dirs SELinux Boolean + + ocil:ssg-sebool_httpd_dontaudit_search_dirs_action:testaction:1 + + + + Disable the fenced_can_network_connect SELinux Boolean + + ocil:ssg-sebool_fenced_can_network_connect_action:testaction:1 + + + + Mount Remote Filesystems with nodev + + ocil:ssg-mount_option_nodev_remote_filesystems_action:testaction:1 + + + + Ensure auditd Collects Information on the Use of Privileged Commands - sudoedit + + ocil:ssg-audit_rules_privileged_commands_sudoedit_action:testaction:1 + + + + Disable Kernel Parameter for IP Forwarding on IPv4 Interfaces + + ocil:ssg-sysctl_net_ipv4_ip_forward_action:testaction:1 + + + + Install iptables-services Package + + ocil:ssg-package_iptables-services_installed_action:testaction:1 + + + + Configure the selinuxuser_direct_dri_enabled SELinux Boolean + + ocil:ssg-sebool_selinuxuser_direct_dri_enabled_action:testaction:1 + + + + Disable WIFI Network Notification in GNOME3 + + ocil:ssg-dconf_gnome_disable_wifi_notification_action:testaction:1 + + + + Record Events that Modify the System's Network Environment + + ocil:ssg-audit_rules_networkconfig_modification_action:testaction:1 + + + + Ensure yum Removes Previous Package Versions + + ocil:ssg-clean_components_post_updating_action:testaction:1 + + + + Disable Kernel Parameter for Accepting Source-Routed Packets on IPv6 Interfaces by Default + + ocil:ssg-sysctl_net_ipv6_conf_default_accept_source_route_action:testaction:1 + + + + Configure auditing of loading and unloading of kernel modules + + ocil:ssg-audit_module_load_action:testaction:1 + + + + Record Access Events to Audit Log Directory + + ocil:ssg-directory_access_var_log_audit_action:testaction:1 + + + + Disable the rsync_full_access SELinux Boolean + + ocil:ssg-sebool_rsync_full_access_action:testaction:1 + + + + Perform general configuration of Audit for OSPP + + ocil:ssg-audit_ospp_general_action:testaction:1 + + + + Configure Accepting Prefix Information in Router Advertisements on All IPv6 Interfaces + + ocil:ssg-sysctl_net_ipv6_conf_all_accept_ra_pinfo_action:testaction:1 + + + + Record Events that Modify the System's Discretionary Access Controls - fsetxattr + + ocil:ssg-audit_rules_dac_modification_fsetxattr_action:testaction:1 + + + + Disable the httpd_run_preupgrade SELinux Boolean + + ocil:ssg-sebool_httpd_run_preupgrade_action:testaction:1 + + + + Verify Group Who Owns group File + + ocil:ssg-file_groupowner_etc_group_action:testaction:1 + + + + Virus Scanning Software Definitions Are Updated + + ocil:ssg-mcafee_antivirus_definitions_updated_action:testaction:1 + + + + Ensure /tmp Located On Separate Partition + + ocil:ssg-partition_for_tmp_action:testaction:1 + + + + Disable the selinuxuser_mysql_connect_enabled SELinux Boolean + + ocil:ssg-sebool_selinuxuser_mysql_connect_enabled_action:testaction:1 + + + + Ensure sudo Ignores Commands In Current Dir - sudo ignore_dot + + ocil:ssg-sudo_add_ignore_dot_action:testaction:1 + + + + Randomize the kernel memory sections + + ocil:ssg-kernel_config_randomize_memory_action:testaction:1 + + + + Ensure auditd Collects Information on the Use of Privileged Commands - shutdown + + ocil:ssg-audit_privileged_commands_shutdown_action:testaction:1 + + + + Make the auditd Configuration Immutable + + ocil:ssg-audit_rules_immutable_action:testaction:1 + + + + Disable graphical user interface + + ocil:ssg-xwindows_remove_packages_action:testaction:1 + + + + Disable Avahi Server Software + + ocil:ssg-service_avahi-daemon_disabled_action:testaction:1 + + + + Configure the gluster_export_all_rw SELinux Boolean + + ocil:ssg-sebool_gluster_export_all_rw_action:testaction:1 + + + + Record Any Attempts to Run setfiles + + ocil:ssg-audit_rules_execution_setfiles_action:testaction:1 + + + + Enable Use of Privilege Separation + + ocil:ssg-sshd_use_priv_separation_action:testaction:1 + + + + Enable the NTP Daemon + + ocil:ssg-service_ntp_enabled_action:testaction:1 + + + + Disable the logging_syslogd_can_sendmail SELinux Boolean + + ocil:ssg-sebool_logging_syslogd_can_sendmail_action:testaction:1 + + + + Configure Sending and Accepting Shared Media Redirects by Default + + ocil:ssg-sysctl_net_ipv4_conf_default_shared_media_action:testaction:1 + + + + System Audit Logs Must Have Mode 0750 or Less Permissive + + ocil:ssg-directory_permissions_var_log_audit_action:testaction:1 + + + + Disable the mozilla_plugin_use_spice SELinux Boolean + + ocil:ssg-sebool_mozilla_plugin_use_spice_action:testaction:1 + + + + Disable GSSAPI Authentication + + ocil:ssg-sshd_disable_gssapi_auth_action:testaction:1 + + + + Ensure SELinux State is Enforcing + + ocil:ssg-selinux_state_action:testaction:1 + + + + Configure SSSD to run as user sssd + + ocil:ssg-sssd_run_as_sssd_user_action:testaction:1 + + + + Disable the sanlock_use_fusefs SELinux Boolean + + ocil:ssg-sebool_sanlock_use_fusefs_action:testaction:1 + + + + Verify User Who Owns /var/log/syslog File + + ocil:ssg-file_owner_var_log_syslog_action:testaction:1 + + + + Disable the tor_bind_all_unreserved_ports SELinux Boolean + + ocil:ssg-sebool_tor_bind_all_unreserved_ports_action:testaction:1 + + + + Ensure the audit Subsystem is Installed + + ocil:ssg-package_audit_installed_action:testaction:1 + + + + Record Events that Modify User/Group Information - /etc/group + + ocil:ssg-audit_rules_usergroup_modification_group_action:testaction:1 + + + + Ensure the default plugins for the audit dispatcher are Installed + + ocil:ssg-package_audit-audispd-plugins_installed_action:testaction:1 + + + + Ensure gpgcheck Enabled for All yum Package Repositories + + ocil:ssg-ensure_gpgcheck_never_disabled_action:testaction:1 + + + + Set GNOME3 Screensaver Inactivity Timeout + + ocil:ssg-dconf_gnome_screensaver_idle_delay_action:testaction:1 + + + + All Interactive User Home Directories Must Have mode 0750 Or Less Permissive + + ocil:ssg-file_permissions_home_directories_action:testaction:1 + + + + Record Events that Modify User/Group Information via open_by_handle_at syscall - /etc/gshadow + + ocil:ssg-audit_rules_etc_gshadow_open_by_handle_at_action:testaction:1 + + + + Record Events that Modify User/Group Information via open syscall - /etc/gshadow + + ocil:ssg-audit_rules_etc_gshadow_open_action:testaction:1 + + + + Disable the tor_can_network_relay SELinux Boolean + + ocil:ssg-sebool_tor_can_network_relay_action:testaction:1 + + + + Disable the samba_run_unconfined SELinux Boolean + + ocil:ssg-sebool_samba_run_unconfined_action:testaction:1 + + + + User Initialization Files Must Be Group-Owned By The Primary User + + ocil:ssg-accounts_user_dot_group_ownership_action:testaction:1 + + + + Ensure PAM Displays Last Logon/Access Notification + + ocil:ssg-display_login_attempts_action:testaction:1 + + + + Configure System to Forward All Mail From Postmaster to The Root Account + + ocil:ssg-postfix_client_configure_mail_alias_postmaster_action:testaction:1 + + + + Disable the guest_exec_content SELinux Boolean + + ocil:ssg-sebool_guest_exec_content_action:testaction:1 + + + + Disable the tmpreaper_use_samba SELinux Boolean + + ocil:ssg-sebool_tmpreaper_use_samba_action:testaction:1 + + + + Prevent Unrestricted Mail Relaying + + ocil:ssg-postfix_prevent_unrestricted_relay_action:testaction:1 + + + + Ensure auditd Unauthorized Access Attempts To open_by_handle_at Are Ordered Correctly + + ocil:ssg-audit_rules_unsuccessful_file_modification_open_by_handle_at_rule_order_action:testaction:1 + + + + Ensure sudo only includes the default configuration directory + + ocil:ssg-sudoers_default_includedir_action:testaction:1 + + + + Disable Printer Browsing Entirely if Possible + + ocil:ssg-cups_disable_browsing_action:testaction:1 + + + + Configure System to Forward All Mail through a specific host + + ocil:ssg-postfix_client_configure_relayhost_action:testaction:1 + + + + Disable httpd Service + + ocil:ssg-service_httpd_disabled_action:testaction:1 + + + + Install policycoreutils-python-utils package + + ocil:ssg-package_policycoreutils-python-utils_installed_action:testaction:1 + + + + Verify Permissions on SSH Server Public *.pub Key Files + + ocil:ssg-file_permissions_sshd_pub_key_action:testaction:1 + + + + Ensure the Default Bash Umask is Set Correctly + + ocil:ssg-accounts_umask_etc_bashrc_action:testaction:1 + + + + Disable SCTP Support + + ocil:ssg-kernel_module_sctp_disabled_action:testaction:1 + + + + Enable the mcelog_exec_scripts SELinux Boolean + + ocil:ssg-sebool_mcelog_exec_scripts_action:testaction:1 + + + + Prevent Login to Accounts With Empty Password + + ocil:ssg-no_empty_passwords_action:testaction:1 + + + + Record Unsuccessful Permission Changes to Files - fremovexattr + + ocil:ssg-audit_rules_unsuccessful_file_modification_fremovexattr_action:testaction:1 + + + + Configure Firewalld to Use the Nftables Backend + + ocil:ssg-firewalld-backend_action:testaction:1 + + + + Record Successful Permission Changes to Files - setxattr + + ocil:ssg-audit_rules_successful_file_modification_setxattr_action:testaction:1 + + + + Uninstall abrt-addon-kerneloops Package + + ocil:ssg-package_abrt-addon-kerneloops_removed_action:testaction:1 + + + + Ensure '/etc/system-fips' exists + + ocil:ssg-etc_system_fips_exists_action:testaction:1 + + + + Disable IPv6 Networking Support Automatic Loading + + ocil:ssg-kernel_module_ipv6_option_disabled_action:testaction:1 + + + + Disable the mailman_use_fusefs SELinux Boolean + + ocil:ssg-sebool_mailman_use_fusefs_action:testaction:1 + + + + Disable the selinuxuser_use_ssh_chroot SELinux Boolean + + ocil:ssg-sebool_selinuxuser_use_ssh_chroot_action:testaction:1 + + + + Verify permissions on Message of the Day Banner + + ocil:ssg-file_permissions_etc_motd_action:testaction:1 + + + + disable the selinuxuser_execstack SELinux Boolean + + ocil:ssg-sebool_selinuxuser_execstack_action:testaction:1 + + + + Ensure auditd Collects Information on the Use of Privileged Commands - postqueue + + ocil:ssg-audit_rules_privileged_commands_postqueue_action:testaction:1 + + + + Enable SLUB debugging support + + ocil:ssg-kernel_config_slub_debug_action:testaction:1 + + + + Disable Apache Qpid (qpidd) + + ocil:ssg-service_qpidd_disabled_action:testaction:1 + + + + Disable the xguest_exec_content SELinux Boolean + + ocil:ssg-sebool_xguest_exec_content_action:testaction:1 + + + + Set Password Warning Age + + ocil:ssg-accounts_password_warn_age_login_defs_action:testaction:1 + + + + Configure a Sufficiently Large Partition for Audit Logs + + ocil:ssg-auditd_audispd_configure_sufficiently_large_partition_action:testaction:1 + + + + Ensure All SUID Executables Are Authorized + + ocil:ssg-file_permissions_unauthorized_suid_action:testaction:1 + + + + Require Authentication for Emergency Systemd Target + + ocil:ssg-require_emergency_target_auth_action:testaction:1 + + + + Stack Protector buffer overlow detection + + ocil:ssg-kernel_config_stackprotector_action:testaction:1 + + + + The Postfix package is installed + + ocil:ssg-package_postfix_installed_action:testaction:1 + + + + Verify User Who Owns Backup gshadow File + + ocil:ssg-file_owner_backup_etc_gshadow_action:testaction:1 + + + + Disable the nagios_run_pnp4nagios SELinux Boolean + + ocil:ssg-sebool_nagios_run_pnp4nagios_action:testaction:1 + + + + Ensure auditd Collects Information on Kernel Module Unloading - delete_module + + ocil:ssg-audit_rules_kernel_module_loading_delete_action:testaction:1 + + + + Add hidepid Option to /proc + + ocil:ssg-mount_option_proc_hidepid_action:testaction:1 + + + + Verify that Shared Library Directories Have Restrictive Permissions + + ocil:ssg-dir_permissions_library_dirs_action:testaction:1 + + + + Disable the mock_enable_homedirs SELinux Boolean + + ocil:ssg-sebool_mock_enable_homedirs_action:testaction:1 + + + + Disable XDMCP in GDM + + ocil:ssg-gnome_gdm_disable_xdmcp_action:testaction:1 + + + + Specify a Remote NTP Server + + ocil:ssg-chronyd_or_ntpd_specify_remote_server_action:testaction:1 + + + + Verify ownership of System Login Banner + + ocil:ssg-file_owner_etc_issue_action:testaction:1 + + + + Do not allow ACPI methods to be inserted/replaced at run time + + ocil:ssg-kernel_config_acpi_custom_method_action:testaction:1 + + + + Disable the openshift_use_nfs SELinux Boolean + + ocil:ssg-sebool_openshift_use_nfs_action:testaction:1 + + + + Disable the mysql_connect_any SELinux Boolean + + ocil:ssg-sebool_mysql_connect_any_action:testaction:1 + + + + Configure auditd space_left on Low Disk Space + + ocil:ssg-auditd_data_retention_space_left_percentage_action:testaction:1 + + + + Configure auditing of successful file creations + + ocil:ssg-audit_create_success_action:testaction:1 + + + + Ensure PAM Enforces Password Requirements - Minimum Special Characters + + ocil:ssg-accounts_password_pam_ocredit_action:testaction:1 + + + + Configure AIDE to Verify the Audit Tools + + ocil:ssg-aide_check_audit_tools_action:testaction:1 + + + + Verify No netrc Files Exist + + ocil:ssg-no_netrc_files_action:testaction:1 + + + + Verify Group Who Owns cron.daily + + ocil:ssg-file_groupowner_cron_daily_action:testaction:1 + + + + Enable checks on scatter-gather (SG) table operations + + ocil:ssg-kernel_config_debug_sg_action:testaction:1 + + + + Ensure that chronyd is running under chrony user account + + ocil:ssg-chronyd_run_as_chrony_user_action:testaction:1 + + + + Enable dnf-automatic Timer + + ocil:ssg-timer_dnf-automatic_enabled_action:testaction:1 + + + + Set the GNOME3 Login Warning Banner Text + + ocil:ssg-dconf_gnome_login_banner_text_action:testaction:1 + + + + Disable the privoxy_connect_any SELinux Boolean + + ocil:ssg-sebool_privoxy_connect_any_action:testaction:1 + + + + Record Successful Access Attempts to Files - truncate + + ocil:ssg-audit_rules_successful_file_modification_truncate_action:testaction:1 + + + + Uninstall abrt-plugin-sosreport Package + + ocil:ssg-package_abrt-plugin-sosreport_removed_action:testaction:1 + + + + Record Events that Modify User/Group Information via openat syscall - /etc/shadow + + ocil:ssg-audit_rules_etc_shadow_openat_action:testaction:1 + + + + Disable the pcp_read_generic_logs SELinux Boolean + + ocil:ssg-sebool_pcp_read_generic_logs_action:testaction:1 + + + + Disable the glance_use_execmem SELinux Boolean + + ocil:ssg-sebool_glance_use_execmem_action:testaction:1 + + + + The operating system must restrict privilege elevation to authorized personnel + + ocil:ssg-sudo_restrict_privilege_elevation_to_authorized_action:testaction:1 + + + + Record Successful Permission Changes to Files - chmod + + ocil:ssg-audit_rules_successful_file_modification_chmod_action:testaction:1 + + + + Ensure /boot Located On Separate Partition + + ocil:ssg-partition_for_boot_action:testaction:1 + + + + Set Up a Private Namespace in PAM Configuration + + ocil:ssg-enable_pam_namespace_action:testaction:1 + + + + Enable Kernel Parameter to Use Reverse Path Filtering on all IPv4 Interfaces + + ocil:ssg-sysctl_net_ipv4_conf_all_rp_filter_action:testaction:1 + + + + Add nodev Option to /boot + + ocil:ssg-mount_option_boot_nodev_action:testaction:1 + + + + Ensure Users Cannot Change GNOME3 Screensaver Idle Activation + + ocil:ssg-dconf_gnome_screensaver_idle_activation_locked_action:testaction:1 + + + + Ensure auditd Collects Information on the Use of Privileged Commands - sudo + + ocil:ssg-audit_rules_privileged_commands_sudo_action:testaction:1 + + + + Disable the gitosis_can_sendmail SELinux Boolean + + ocil:ssg-sebool_gitosis_can_sendmail_action:testaction:1 + + + + Disable the httpd_can_network_connect SELinux Boolean + + ocil:ssg-sebool_httpd_can_network_connect_action:testaction:1 + + + + Verify Group Who Owns Backup shadow File + + ocil:ssg-file_owner_backup_etc_shadow_action:testaction:1 + + + + Record Any Attempts to Run semanage + + ocil:ssg-audit_rules_execution_semanage_action:testaction:1 + + + + Disable the sanlock_use_nfs SELinux Boolean + + ocil:ssg-sebool_sanlock_use_nfs_action:testaction:1 + + + + All User Files and Directories In The Home Directory Must Have Mode 0750 Or Less Permissive + + ocil:ssg-accounts_users_home_files_permissions_action:testaction:1 + + + + Disable RDS Support + + ocil:ssg-kernel_module_rds_disabled_action:testaction:1 + + + + Set PAM''s Password Hashing Algorithm + + ocil:ssg-set_password_hashing_algorithm_systemauth_action:testaction:1 + + + + Specify module signing key to use + + ocil:ssg-kernel_config_module_sig_key_action:testaction:1 + + + + Configure the tmux Lock Command + + ocil:ssg-configure_tmux_lock_command_action:testaction:1 + + + + Ensure auditd Collects System Administrator Actions - /etc/sudoers.d/ + + ocil:ssg-audit_rules_sudoers_d_action:testaction:1 + + + + Ensure tftp Daemon Uses Secure Mode + + ocil:ssg-tftpd_uses_secure_mode_action:testaction:1 + + + + Verify Group Who Owns /etc/at.allow file + + ocil:ssg-file_groupowner_at_allow_action:testaction:1 + + + + Record Events that Modify User/Group Information via open syscall - /etc/passwd + + ocil:ssg-audit_rules_etc_passwd_open_action:testaction:1 + + + + Configure firewall to Allow Access to the Web Server + + ocil:ssg-httpd_configure_firewall_action:testaction:1 + + + + Record Unsuccessful Permission Changes to Files - lremovexattr + + ocil:ssg-audit_rules_unsuccessful_file_modification_lremovexattr_action:testaction:1 + + + + Configure auditd Disk Full Action when Disk Space Is Full + + ocil:ssg-auditd_data_disk_full_action_action:testaction:1 + + + + Ensure auditd Collects Information on the Use of Privileged Commands - init + + ocil:ssg-audit_privileged_commands_init_action:testaction:1 + + + + Verify the UEFI Boot Loader grub.cfg Permissions + + ocil:ssg-file_permissions_efi_grub2_cfg_action:testaction:1 + + + + Add noexec Option to /tmp + + ocil:ssg-mount_option_tmp_noexec_action:testaction:1 + + + + Disable Ctrl-Alt-Del Reboot Key Sequence in GNOME3 + + ocil:ssg-dconf_gnome_disable_ctrlaltdel_reboot_action:testaction:1 + + + + Force frequent session key renegotiation + + ocil:ssg-sshd_rekey_limit_action:testaction:1 + + + + Ensure that User Home Directories are not Group-Writable or World-Readable + + ocil:ssg-file_permissions_home_dirs_action:testaction:1 + + + + Disable the nis_enabled SELinux Boolean + + ocil:ssg-sebool_nis_enabled_action:testaction:1 + + + + Verify iptables Enabled + + ocil:ssg-service_iptables_enabled_action:testaction:1 + + + + Disable the neutron_can_network SELinux Boolean + + ocil:ssg-sebool_neutron_can_network_action:testaction:1 + + + + Install dnf-automatic Package + + ocil:ssg-package_dnf-automatic_installed_action:testaction:1 + + + + Add nodev Option to /tmp + + ocil:ssg-mount_option_tmp_nodev_action:testaction:1 + + + + Disable the httpd_can_connect_zabbix SELinux Boolean + + ocil:ssg-sebool_httpd_can_connect_zabbix_action:testaction:1 + + + + Record Unsuccessful Modification Attempts to Files - open O_TRUNC_WRITE + + ocil:ssg-audit_rules_unsuccessful_file_modification_open_o_trunc_write_action:testaction:1 + + + + Add nodev Option to /var + + ocil:ssg-mount_option_var_nodev_action:testaction:1 + + + + Disable the openvpn_enable_homedirs SELinux Boolean + + ocil:ssg-sebool_openvpn_enable_homedirs_action:testaction:1 + + + + Disable Accepting ICMP Redirects for All IPv4 Interfaces + + ocil:ssg-sysctl_net_ipv4_conf_all_accept_redirects_action:testaction:1 + + + + Disable Network Router Discovery Daemon (rdisc) + + ocil:ssg-service_rdisc_disabled_action:testaction:1 + + + + IOMMU configuration directive + + ocil:ssg-grub2_enable_iommu_force_action:testaction:1 + + + + Configure Accepting Router Preference in Router Advertisements on All IPv6 Interfaces + + ocil:ssg-sysctl_net_ipv6_conf_all_accept_ra_rtr_pref_action:testaction:1 + + + + Ensure PAM Enforces Password Requirements - Enforce for root User + + ocil:ssg-accounts_password_pam_enforce_root_action:testaction:1 + + + + Install the ntp service + + ocil:ssg-package_ntp_installed_action:testaction:1 + + + + Set Interactive Session Timeout + + ocil:ssg-accounts_tmout_action:testaction:1 + + + + Enable the Hardware RNG Entropy Gatherer Service + + ocil:ssg-service_rngd_enabled_action:testaction:1 + + + + Enable the sysadm_exec_content SELinux Boolean + + ocil:ssg-sebool_sysadm_exec_content_action:testaction:1 + + + + Configure System to Forward All Mail For The Root Account + + ocil:ssg-postfix_client_configure_mail_alias_action:testaction:1 + + + + Remove the X Windows Package Group + + ocil:ssg-package_xorg-x11-server-common_removed_action:testaction:1 + + + + Verify that System Executable Have Root Ownership + + ocil:ssg-dir_ownership_binary_dirs_action:testaction:1 + + + + Disable the git_session_users SELinux Boolean + + ocil:ssg-sebool_git_session_users_action:testaction:1 + + + + Disable the httpd_run_ipa SELinux Boolean + + ocil:ssg-sebool_httpd_run_ipa_action:testaction:1 + + + + Set PAM''s Password Hashing Algorithm - password-auth + + ocil:ssg-set_password_hashing_algorithm_passwordauth_action:testaction:1 + + + + Configure auditing of unsuccessful file creations + + ocil:ssg-audit_create_failed_action:testaction:1 + + + + Configure SSSD LDAP Backend Client to Demand a Valid Certificate from the Server + + ocil:ssg-sssd_ldap_configure_tls_reqcert_action:testaction:1 + + + + Disable ATM Support + + ocil:ssg-kernel_module_atm_disabled_action:testaction:1 + + + + Ensure SSH MaxStartups is configured + + ocil:ssg-sshd_set_maxstartups_action:testaction:1 + + + + Randomize the address of the kernel image (KASLR) + + ocil:ssg-kernel_config_randomize_base_action:testaction:1 + + + + Record Unsuccessful Permission Changes to Files - lsetxattr + + ocil:ssg-audit_rules_unsuccessful_file_modification_lsetxattr_action:testaction:1 + + + + Disable the httpd_dbus_sssd SELinux Boolean + + ocil:ssg-sebool_httpd_dbus_sssd_action:testaction:1 + + + + Verify that System Executable Directories Have Restrictive Permissions + + ocil:ssg-dir_permissions_binary_dirs_action:testaction:1 + + + + Configure auditing of unsuccessful file accesses + + ocil:ssg-audit_access_failed_action:testaction:1 + + + + Disable SSH Support for Rhosts RSA Authentication + + ocil:ssg-sshd_disable_rhosts_rsa_action:testaction:1 + + + + Trigger a kernel BUG when data corruption is detected + + ocil:ssg-kernel_config_bug_on_data_corruption_action:testaction:1 + + + + Record Unsuccessful Access Attempts to Files - openat + + ocil:ssg-audit_rules_unsuccessful_file_modification_openat_action:testaction:1 + + + + Ensure SMEP is not disabled during boot + + ocil:ssg-grub2_nosmep_argument_absent_action:testaction:1 + + + + Enable Kernel Parameter to Enforce DAC on Hardlinks + + ocil:ssg-sysctl_fs_protected_hardlinks_action:testaction:1 + + + + Ensure sudo Runs In A Minimal Environment - sudo env_reset + + ocil:ssg-sudo_add_env_reset_action:testaction:1 + + + + Record Attempts to Alter Logon and Logout Events - lastlog + + ocil:ssg-audit_rules_login_events_lastlog_action:testaction:1 + + + + Public web server resources must not be shared with private assets + + ocil:ssg-httpd_public_resources_not_shared_action:testaction:1 + + + + Configure auditd max_log_file_action Upon Reaching Maximum Log Size + + ocil:ssg-auditd_data_retention_max_log_file_action_stig_action:testaction:1 + + + + Verify User Who Owns Backup passwd File + + ocil:ssg-file_owner_backup_etc_passwd_action:testaction:1 + + + + Install vim Package + + ocil:ssg-package_vim_installed_action:testaction:1 + + + + Enable the unconfined_mozilla_plugin_transition SELinux Boolean + + ocil:ssg-sebool_unconfined_mozilla_plugin_transition_action:testaction:1 + + + + Record Events that Modify the System's Discretionary Access Controls - lsetxattr + + ocil:ssg-audit_rules_dac_modification_lsetxattr_action:testaction:1 + + + + Disable the git_cgi_use_cifs SELinux Boolean + + ocil:ssg-sebool_git_cgi_use_cifs_action:testaction:1 + + + + Verify /boot/grub2/grub.cfg User Ownership + + ocil:ssg-file_owner_grub2_cfg_action:testaction:1 + + + + Disable the pppd_for_user SELinux Boolean + + ocil:ssg-sebool_pppd_for_user_action:testaction:1 + + + + Install crypto-policies package + + ocil:ssg-package_crypto-policies_installed_action:testaction:1 + + + + Verify Only Root Has UID 0 + + ocil:ssg-accounts_no_uid_except_zero_action:testaction:1 + + + + Specify the hash to use when signing modules + + ocil:ssg-kernel_config_module_sig_hash_action:testaction:1 + + + + Ensure auditd Collects Information on Kernel Module Loading and Unloading - finit_module + + ocil:ssg-audit_rules_kernel_module_loading_finit_action:testaction:1 + + + + Disable the selinuxuser_rw_noexattrfile SELinux Boolean + + ocil:ssg-sebool_selinuxuser_rw_noexattrfile_action:testaction:1 + + + + Ensure No Daemons are Unconfined by SELinux + + ocil:ssg-selinux_confinement_of_daemons_action:testaction:1 + + + + Verify Group Who Owns /var/log Directory + + ocil:ssg-file_groupowner_var_log_action:testaction:1 + + + + Enable TCP/IP syncookie support + + ocil:ssg-kernel_config_syn_cookies_action:testaction:1 + + + + Record Successful Ownership Changes to Files - lchown + + ocil:ssg-audit_rules_successful_file_modification_lchown_action:testaction:1 + + + + Record Events that Modify User/Group Information via open_by_handle_at syscall - /etc/shadow + + ocil:ssg-audit_rules_etc_shadow_open_by_handle_at_action:testaction:1 + + + + Don't define allowed commands in sudoers by means of exclusion + + ocil:ssg-sudoers_no_command_negation_action:testaction:1 + + + + User Initialization Files Must Be Owned By the Primary User + + ocil:ssg-accounts_user_dot_user_ownership_action:testaction:1 + + + + Disable Portreserve (portreserve) + + ocil:ssg-service_portreserve_disabled_action:testaction:1 + + + + All Interactive Users Must Have A Home Directory Defined + + ocil:ssg-accounts_user_interactive_home_directory_defined_action:testaction:1 + + + + Verify Permissions on /etc/audit/rules.d/*.rules + + ocil:ssg-file_permissions_etc_audit_rulesd_action:testaction:1 + + + + Disable the xen_use_nfs SELinux Boolean + + ocil:ssg-sebool_xen_use_nfs_action:testaction:1 + + + + System Audit Directories Must Be Owned By Root + + ocil:ssg-directory_ownership_var_log_audit_action:testaction:1 + + + + Write Audit Logs to the Disk + + ocil:ssg-auditd_write_logs_action:testaction:1 + + + + Enable Kernel Parameter to Use Reverse Path Filtering on all IPv4 Interfaces by Default + + ocil:ssg-sysctl_net_ipv4_conf_default_rp_filter_action:testaction:1 + + + + Install the Asset Configuration Compliance Module (ACCM) + + ocil:ssg-install_mcafee_hbss_accm_action:testaction:1 + + + + Verify All Account Password Hashes are Shadowed with SHA512 + + ocil:ssg-accounts_password_all_shadowed_sha512_action:testaction:1 + + + + Remove telnet Clients + + ocil:ssg-package_telnet_removed_action:testaction:1 + + + + Disable Anonymous FTP Access + + ocil:ssg-httpd_disable_anonymous_ftp_access_action:testaction:1 + + + + Record Any Attempts to Run setsebool + + ocil:ssg-audit_rules_execution_setsebool_action:testaction:1 + + + + Disable the webadm_read_user_files SELinux Boolean + + ocil:ssg-sebool_webadm_read_user_files_action:testaction:1 + + + + Only Authorized Local User Accounts Exist on Operating System + + ocil:ssg-accounts_authorized_local_users_action:testaction:1 + + + + Ensure auditd Collects Unauthorized Access Attempts to Files (unsuccessful) + + ocil:ssg-audit_rules_unsuccessful_file_modification_action:testaction:1 + + + + Disable the smartmon_3ware SELinux Boolean + + ocil:ssg-sebool_smartmon_3ware_action:testaction:1 + + + + Verify Root Has A Primary GID 0 + + ocil:ssg-accounts_root_gid_zero_action:testaction:1 + + + + Disable the httpd_enable_homedirs SELinux Boolean + + ocil:ssg-sebool_httpd_enable_homedirs_action:testaction:1 + + + + Record Unsuccessful Access Attempts to Files - creat + + ocil:ssg-audit_rules_unsuccessful_file_modification_creat_action:testaction:1 + + + + Disable the domain_kernel_load_modules SELinux Boolean + + ocil:ssg-sebool_domain_kernel_load_modules_action:testaction:1 + + + + Enable Transport Layer Security (TLS) Encryption + + ocil:ssg-httpd_configure_tls_action:testaction:1 + + + + Configure Kerberos to use System Crypto Policy + + ocil:ssg-configure_kerberos_crypto_policy_action:testaction:1 + + + + Use Centralized and Automated Authentication + + ocil:ssg-account_use_centralized_automated_auth_action:testaction:1 + + + + Uninstall gssproxy Package + + ocil:ssg-package_gssproxy_removed_action:testaction:1 + + + + Uninstall tftp-server Package + + ocil:ssg-package_tftp-server_removed_action:testaction:1 + + + + Ensure Insecure File Locking is Not Allowed + + ocil:ssg-no_insecure_locks_exports_action:testaction:1 + + + + Restrict unprivileged access to the kernel syslog + + ocil:ssg-kernel_config_security_dmesg_restrict_action:testaction:1 + + + + Disable the secure_mode_policyload SELinux Boolean + + ocil:ssg-sebool_secure_mode_policyload_action:testaction:1 + + + + Enable HTTPD System Logging + + ocil:ssg-httpd_enable_system_logging_action:testaction:1 + + + + Disable Accepting Packets Routed Between Local Interfaces + + ocil:ssg-sysctl_net_ipv4_conf_all_accept_local_action:testaction:1 + + + + Configure auditd to use audispd's syslog plugin + + ocil:ssg-auditd_audispd_syslog_plugin_activated_action:testaction:1 + + + + Disable Core Dumps for SUID programs + + ocil:ssg-sysctl_fs_suid_dumpable_action:testaction:1 + + + + Ensure All Files Are Owned by a User + + ocil:ssg-no_files_unowned_by_user_action:testaction:1 + + + + The Chronyd service is enabled + + ocil:ssg-service_chronyd_enabled_action:testaction:1 + + + + Record Successful Delete Attempts to Files - renameat + + ocil:ssg-audit_rules_successful_file_modification_renameat_action:testaction:1 + + + + Enable syslog-ng Service + + ocil:ssg-service_syslogng_enabled_action:testaction:1 + + + + Ensure auditd Collects Information on the Use of Privileged Commands + + ocil:ssg-audit_rules_privileged_commands_action:testaction:1 + + + + Verify Permissions on crontab + + ocil:ssg-file_permissions_crontab_action:testaction:1 + + + + Install dnf-plugin-subscription-manager Package + + ocil:ssg-package_dnf-plugin-subscription-manager_installed_action:testaction:1 + + + + Configure BIND to use System Crypto Policy + + ocil:ssg-configure_bind_crypto_policy_action:testaction:1 + + + + Record Events that Modify User/Group Information via openat syscall - /etc/passwd + + ocil:ssg-audit_rules_etc_passwd_openat_action:testaction:1 + + + + Limit Password Reuse: password-auth + + ocil:ssg-accounts_password_pam_pwhistory_remember_password_auth_action:testaction:1 + + + + Force initialization of variables containing userspace addresses + + ocil:ssg-kernel_config_gcc_plugin_structleak_action:testaction:1 + + + + Configure The Number of Allowed Simultaneous Requests + + ocil:ssg-httpd_configure_max_keepalive_requests_action:testaction:1 + + + + Disable the abrt_upload_watch_anon_write SELinux Boolean + + ocil:ssg-sebool_abrt_upload_watch_anon_write_action:testaction:1 + + + + Enable the antivirus_can_scan_system SELinux Boolean + + ocil:ssg-sebool_antivirus_can_scan_system_action:testaction:1 + + + + Disable tftp Service + + ocil:ssg-service_tftp_disabled_action:testaction:1 + + + + Ensure Rsyslog Encrypts Off-Loaded Audit Records + + ocil:ssg-rsyslog_encrypt_offload_defaultnetstreamdriver_action:testaction:1 + + + + Add nosuid Option to /var + + ocil:ssg-mount_option_var_nosuid_action:testaction:1 + + + + Add nodev Option to /var/log + + ocil:ssg-mount_option_var_log_nodev_action:testaction:1 + + + + Disable the icecast_use_any_tcp_ports SELinux Boolean + + ocil:ssg-sebool_icecast_use_any_tcp_ports_action:testaction:1 + + + + Verify Permissions on /var/log/messages File + + ocil:ssg-file_permissions_var_log_messages_action:testaction:1 + + + + Set Password Hashing Rounds in /etc/login.defs + + ocil:ssg-set_password_hashing_min_rounds_logindefs_action:testaction:1 + + + + Ensure auditd Collects Information on Kernel Module Loading - init_module + + ocil:ssg-audit_rules_kernel_module_loading_init_action:testaction:1 + + + + Do not allow usercopy whitelist violations to fallback to object size + + ocil:ssg-kernel_config_hardened_usercopy_fallback_action:testaction:1 + + + + Use Only FIPS 140-2 Validated MACs + + ocil:ssg-sshd_use_approved_macs_action:testaction:1 + + + + Remove tftp Daemon + + ocil:ssg-package_tftp_removed_action:testaction:1 + + + + Ensure Web Content Located on Separate partition + + ocil:ssg-partition_for_web_content_action:testaction:1 + + + + Ensure auditd Collects Information on Exporting to Media (successful) + + ocil:ssg-audit_rules_media_export_action:testaction:1 + + + + Disable core dump backtraces + + ocil:ssg-coredump_disable_backtraces_action:testaction:1 + + + + Disable Secure RPC Server Service (rpcsvcgssd) + + ocil:ssg-service_rpcsvcgssd_disabled_action:testaction:1 + + + + Remove Host-Based Authentication Files + + ocil:ssg-no_host_based_files_action:testaction:1 + + + + Disable the httpd_ssi_exec SELinux Boolean + + ocil:ssg-sebool_httpd_ssi_exec_action:testaction:1 + + + + Ensure All-Squashing Disabled On All Exports + + ocil:ssg-no_all_squash_exports_action:testaction:1 + + + + Disable the zoneminder_anon_write SELinux Boolean + + ocil:ssg-sebool_zoneminder_anon_write_action:testaction:1 + + + + Ensure that Users Path Contains Only Local Directories + + ocil:ssg-accounts_user_home_paths_only_action:testaction:1 + + + + Disable the fenced_can_ssh SELinux Boolean + + ocil:ssg-sebool_fenced_can_ssh_action:testaction:1 + + + + Enable the dbadm_exec_content SELinux Boolean + + ocil:ssg-sebool_dbadm_exec_content_action:testaction:1 + + + + Disable the CUPS Service + + ocil:ssg-service_cups_disabled_action:testaction:1 + + + + Ensure auditd Collects Information on the Use of Privileged Commands - crontab + + ocil:ssg-audit_rules_privileged_commands_crontab_action:testaction:1 + + + + Ensure All World-Writable Directories Are Owned by a System Account + + ocil:ssg-dir_perms_world_writable_system_owned_action:testaction:1 + + + + Add nodev Option to /var/log/audit + + ocil:ssg-mount_option_var_log_audit_nodev_action:testaction:1 + + + + Record Successful Permission Changes to Files - lremovexattr + + ocil:ssg-audit_rules_successful_file_modification_lremovexattr_action:testaction:1 + + + + Ensure System is Not Acting as a Network Sniffer + + ocil:ssg-network_sniffer_disabled_action:testaction:1 + + + + Ensure /var/log/audit Located On Separate Partition + + ocil:ssg-partition_for_var_log_audit_action:testaction:1 + + + + Record Unsuccessful Ownership Changes to Files - fchownat + + ocil:ssg-audit_rules_unsuccessful_file_modification_fchownat_action:testaction:1 + + + + Record Any Attempts to Run chacl + + ocil:ssg-audit_rules_execution_chacl_action:testaction:1 + + + + Ensure PAM password complexity module is enabled in password-auth + + ocil:ssg-accounts_password_pam_pwquality_password_auth_action:testaction:1 + + + + Disable the mozilla_plugin_use_bluejeans SELinux Boolean + + ocil:ssg-sebool_mozilla_plugin_use_bluejeans_action:testaction:1 + + + + Enable the nscd_use_shm SELinux Boolean + + ocil:ssg-sebool_nscd_use_shm_action:testaction:1 + + + + Disable the wine_mmap_zero_ignore SELinux Boolean + + ocil:ssg-sebool_wine_mmap_zero_ignore_action:testaction:1 + + + + Record Attempts to Alter the localtime File + + ocil:ssg-audit_rules_time_watch_localtime_action:testaction:1 + + + + Verify Permissions on Backup shadow File + + ocil:ssg-file_permissions_backup_etc_shadow_action:testaction:1 + + + + Ensure journald is configured to compress large log files + + ocil:ssg-journald_compress_action:testaction:1 + + + + Configure Speculative Store Bypass Mitigation + + ocil:ssg-grub2_spec_store_bypass_disable_argument_action:testaction:1 + + + + Disable Certmonger Service (certmonger) + + ocil:ssg-service_certmonger_disabled_action:testaction:1 + + + + Uninstall abrt-plugin-logger Package + + ocil:ssg-package_abrt-plugin-logger_removed_action:testaction:1 + + + + Disallow merge of slab caches + + ocil:ssg-kernel_config_slab_merge_default_action:testaction:1 + + + + Record Unsuccessful Modification Attempts to Files - openat O_TRUNC_WRITE + + ocil:ssg-audit_rules_unsuccessful_file_modification_openat_o_trunc_write_action:testaction:1 + + + + Configure OpenSSL library to use System Crypto Policy + + ocil:ssg-configure_openssl_crypto_policy_action:testaction:1 + + + + Configure PAM in SSSD Services + + ocil:ssg-sssd_enable_pam_services_action:testaction:1 + + + + Enable HTTPD LogLevel + + ocil:ssg-httpd_enable_loglevel_action:testaction:1 + + + + Disable anacron Service + + ocil:ssg-disable_anacron_action:testaction:1 + + + + Enable HTTPD Error Logging + + ocil:ssg-httpd_enable_error_logging_action:testaction:1 + + + + Install libcap-ng-utils Package + + ocil:ssg-package_libcap-ng-utils_installed_action:testaction:1 + + + + Set the Boot Loader Admin Username to a Non-Default Value + + ocil:ssg-grub2_admin_username_action:testaction:1 + + + + Disable the xdm_exec_bootloader SELinux Boolean + + ocil:ssg-sebool_xdm_exec_bootloader_action:testaction:1 + + + + Disable the virt_use_sanlock SELinux Boolean + + ocil:ssg-sebool_virt_use_sanlock_action:testaction:1 + + + + Configure Auto Configuration on All IPv6 Interfaces By Default + + ocil:ssg-sysctl_net_ipv6_conf_default_autoconf_action:testaction:1 + + + + Disable the awstats_purge_apache_log_files SELinux Boolean + + ocil:ssg-sebool_awstats_purge_apache_log_files_action:testaction:1 + + + + Verify Group Who Owns Backup gshadow File + + ocil:ssg-file_groupowner_backup_etc_gshadow_action:testaction:1 + + + + Install libreswan Package + + ocil:ssg-package_libreswan_installed_action:testaction:1 + + + + Disable the pppd_can_insmod SELinux Boolean + + ocil:ssg-sebool_pppd_can_insmod_action:testaction:1 + + + + Ensure /opt Located On Separate Partition + + ocil:ssg-partition_for_opt_action:testaction:1 + + + + Configure Accepting Default Router in Router Advertisements on All IPv6 Interfaces + + ocil:ssg-sysctl_net_ipv6_conf_all_accept_ra_defrtr_action:testaction:1 + + + + Verify that Shared Library Directories Have Root Group Ownership + + ocil:ssg-dir_group_ownership_library_dirs_action:testaction:1 + + + + Disable Recovery Booting + + ocil:ssg-grub2_disable_recovery_action:testaction:1 + + + + Record Unsuccessful Delete Attempts to Files - unlink + + ocil:ssg-audit_rules_unsuccessful_file_modification_unlink_action:testaction:1 + + + + Verify User Who Owns passwd File + + ocil:ssg-file_owner_etc_passwd_action:testaction:1 + + + + Ensure Remote Administrative Access Is Encrypted + + ocil:ssg-httpd_configure_remote_session_encryption_action:testaction:1 + + + + Configure Denying Router Solicitations on All IPv6 Interfaces + + ocil:ssg-sysctl_net_ipv6_conf_all_router_solicitations_action:testaction:1 + + + + Record Events that Modify the System's Discretionary Access Controls - umount2 + + ocil:ssg-audit_rules_dac_modification_umount2_action:testaction:1 + + + + Ensure Software Patches Installed + + ocil:ssg-security_patches_up_to_date_action:testaction:1 + + + + Ensure auditd Collects Information on the Use of Privileged Commands - umount + + ocil:ssg-audit_rules_privileged_commands_umount_action:testaction:1 + + + + Disable WIFI Network Connection Creation in GNOME3 + + ocil:ssg-dconf_gnome_disable_wifi_create_action:testaction:1 + + + + Disable the xdm_write_home SELinux Boolean + + ocil:ssg-sebool_xdm_write_home_action:testaction:1 + + + + Configure AIDE to Verify Extended Attributes + + ocil:ssg-aide_verify_ext_attributes_action:testaction:1 + + + + Ensure that /etc/at.deny does not exist + + ocil:ssg-file_at_deny_not_exist_action:testaction:1 + + + + Ensure that Root's Path Does Not Include World or Group-Writable Directories + + ocil:ssg-accounts_root_path_dirs_no_write_action:testaction:1 + + + + Disable Postfix Network Listening + + ocil:ssg-postfix_network_listening_disabled_action:testaction:1 + + + + Record Unsuccessful Delete Attempts to Files - renameat + + ocil:ssg-audit_rules_unsuccessful_file_modification_renameat_action:testaction:1 + + + + Disable the samba_create_home_dirs SELinux Boolean + + ocil:ssg-sebool_samba_create_home_dirs_action:testaction:1 + + + + Disable the openvpn_can_network_connect SELinux Boolean + + ocil:ssg-sebool_openvpn_can_network_connect_action:testaction:1 + + + + Disable acquiring, saving, and processing core dumps + + ocil:ssg-service_systemd-coredump_disabled_action:testaction:1 + + + + Add nosuid Option to /boot/efi + + ocil:ssg-mount_option_boot_efi_nosuid_action:testaction:1 + + + + Add nodev Option to /var/tmp + + ocil:ssg-mount_option_var_tmp_nodev_action:testaction:1 + + + + Disable the samba_export_all_ro SELinux Boolean + + ocil:ssg-sebool_samba_export_all_ro_action:testaction:1 + + + + Ensure rsyslog-gnutls is installed + + ocil:ssg-package_rsyslog-gnutls_installed_action:testaction:1 + + + + Enable the cron_userdomain_transition SELinux Boolean + + ocil:ssg-sebool_cron_userdomain_transition_action:testaction:1 + + + + Do Not Allow SSH Environment Options + + ocil:ssg-sshd_do_not_permit_user_env_action:testaction:1 + + + + Disable the samba_export_all_rw SELinux Boolean + + ocil:ssg-sebool_samba_export_all_rw_action:testaction:1 + + + + The robots.txt Files Must Not Exist + + ocil:ssg-httpd_remove_robots_file_action:testaction:1 + + + + Disable Kernel Parameter for Accepting Source-Routed Packets on IPv4 Interfaces by Default + + ocil:ssg-sysctl_net_ipv4_conf_default_accept_source_route_action:testaction:1 + + + + Install scap-security-guide Package + + ocil:ssg-package_scap-security-guide_installed_action:testaction:1 + + + + Disable the squid_connect_any SELinux Boolean + + ocil:ssg-sebool_squid_connect_any_action:testaction:1 + + + + Set Password Hashing Algorithm in /etc/login.defs + + ocil:ssg-set_password_hashing_algorithm_logindefs_action:testaction:1 + + + + Ensure Users Cannot Change GNOME3 Screensaver Settings + + ocil:ssg-dconf_gnome_screensaver_user_locks_action:testaction:1 + + + + Verify firewalld Enabled + + ocil:ssg-service_firewalld_enabled_action:testaction:1 + + + + Disable the ftpd_use_passive_mode SELinux Boolean + + ocil:ssg-sebool_ftpd_use_passive_mode_action:testaction:1 + + + + Enable the selinuxuser_ping SELinux Boolean + + ocil:ssg-sebool_selinuxuser_ping_action:testaction:1 + + + + Unmap kernel when running in userspace (aka KAISER) + + ocil:ssg-kernel_config_unmap_kernel_at_el0_action:testaction:1 + + + + Disable xinetd Service + + ocil:ssg-service_xinetd_disabled_action:testaction:1 + + + + Ensure sudo passwd_timeout is appropriate - sudo passwd_timeout + + ocil:ssg-sudo_add_passwd_timeout_action:testaction:1 + + + + Allow Only SSH Protocol 2 + + ocil:ssg-sshd_allow_only_protocol2_action:testaction:1 + + + + Uninstall telnet-server Package + + ocil:ssg-package_telnet-server_removed_action:testaction:1 + + + + Add noexec Option to /var/log + + ocil:ssg-mount_option_var_log_noexec_action:testaction:1 + + + + Configure the deny_execmem SELinux Boolean + + ocil:ssg-sebool_deny_execmem_action:testaction:1 + + + + Disable vsftpd Service + + ocil:ssg-service_vsftpd_disabled_action:testaction:1 + + + + Ensure the Logon Failure Delay is Set Correctly in login.defs + + ocil:ssg-accounts_logon_fail_delay_action:testaction:1 + + + + Verify User Who Owns Backup shadow File + + ocil:ssg-file_groupowner_backup_etc_shadow_action:testaction:1 + + + + Disable Squid + + ocil:ssg-service_squid_disabled_action:testaction:1 + + + + Disable the 32-bit vDSO + + ocil:ssg-kernel_config_compat_vdso_action:testaction:1 + + + + Enable different security models + + ocil:ssg-kernel_config_security_action:testaction:1 + + + + Verify ownership of Message of the Day Banner + + ocil:ssg-file_owner_etc_motd_action:testaction:1 + + + + Enable SSH Warning Banner + + ocil:ssg-sshd_enable_warning_banner_net_action:testaction:1 + + + + Encrypt Audit Records Sent With audispd Plugin + + ocil:ssg-auditd_audispd_encrypt_sent_records_action:testaction:1 + + + + Configure Auto Configuration on All IPv6 Interfaces + + ocil:ssg-sysctl_net_ipv6_conf_all_autoconf_action:testaction:1 + + + + Ensure /srv Located On Separate Partition + + ocil:ssg-partition_for_srv_action:testaction:1 + + + + Set Default ip6tables Policy for Incoming Packets + + ocil:ssg-set_ip6tables_default_rule_action:testaction:1 + + + + Verify User Who Owns gshadow File + + ocil:ssg-file_owner_etc_gshadow_action:testaction:1 + + + + The Installed Operating System Is Vendor Supported + + ocil:ssg-installed_OS_is_vendor_supported_action:testaction:1 + + + + Disable x86 vsyscall emulation + + ocil:ssg-kernel_config_x86_vsyscall_emulation_action:testaction:1 + + + + Configure SSSD's Memory Cache to Expire + + ocil:ssg-sssd_memcache_timeout_action:testaction:1 + + + + Drop Gratuitious ARP frames on All IPv4 Interfaces + + ocil:ssg-sysctl_net_ipv4_conf_all_drop_gratuitous_arp_action:testaction:1 + + + + Configure Multiple DNS Servers in /etc/resolv.conf + + ocil:ssg-network_configure_name_resolution_action:testaction:1 + + + + Enable SSH Warning Banner + + ocil:ssg-sshd_enable_warning_banner_action:testaction:1 + + + + Disable the kdumpgui_run_bootloader SELinux Boolean + + ocil:ssg-sebool_kdumpgui_run_bootloader_action:testaction:1 + + + + Disable the dhcpc_exec_iptables SELinux Boolean + + ocil:ssg-sebool_dhcpc_exec_iptables_action:testaction:1 + + + + Configure Kernel to Rate Limit Sending of Duplicate TCP Acknowledgments + + ocil:ssg-sysctl_net_ipv4_tcp_invalid_ratelimit_action:testaction:1 + + + + Disable the git_cgi_use_nfs SELinux Boolean + + ocil:ssg-sebool_git_cgi_use_nfs_action:testaction:1 + + + + Enable Kernel Page-Table Isolation (KPTI) + + ocil:ssg-grub2_pti_argument_action:testaction:1 + + + + Disable the GNOME3 Login User List + + ocil:ssg-dconf_gnome_disable_user_list_action:testaction:1 + + + + Restrict Virtual Console Root Logins + + ocil:ssg-securetty_root_login_console_only_action:testaction:1 + + + + Ensure auditd Collects File Deletion Events by User - unlinkat + + ocil:ssg-audit_rules_file_deletion_events_unlinkat_action:testaction:1 + + + + Enable poison of pages after freeing + + ocil:ssg-kernel_config_page_poisoning_action:testaction:1 + + + + Ensure SMAP is not disabled during boot + + ocil:ssg-grub2_nosmap_argument_absent_action:testaction:1 + + + + Disable Kernel Parameter for Accepting Source-Routed Packets on all IPv6 Interfaces + + ocil:ssg-sysctl_net_ipv6_conf_all_accept_source_route_action:testaction:1 + + + + Disable rlogin Service + + ocil:ssg-service_rlogin_disabled_action:testaction:1 + + + + Record Events When Privileged Executables Are Run + + ocil:ssg-audit_rules_suid_privilege_function_action:testaction:1 + + + + Configure Denying Router Solicitations on All IPv6 Interfaces By Default + + ocil:ssg-sysctl_net_ipv6_conf_default_router_solicitations_action:testaction:1 + + + + Enable the httpd_graceful_shutdown SELinux Boolean + + ocil:ssg-sebool_httpd_graceful_shutdown_action:testaction:1 + + + + Configure auditing of unsuccessful permission changes + + ocil:ssg-audit_perm_change_failed_action:testaction:1 + + + + Configure auditing of successful permission changes + + ocil:ssg-audit_perm_change_success_action:testaction:1 + + + + Enable Certmap in SSSD + + ocil:ssg-sssd_enable_certmap_action:testaction:1 + + + + SSH server uses strong entropy to seed + + ocil:ssg-sshd_use_strong_rng_action:testaction:1 + + + + Uninstall ypserv Package + + ocil:ssg-package_ypserv_removed_action:testaction:1 + + + + Don't target root user in the sudoers file + + ocil:ssg-sudoers_no_root_target_action:testaction:1 + + + + Disable User Administration in GNOME3 + + ocil:ssg-dconf_gnome_disable_user_admin_action:testaction:1 + + + + Add noexec Option to /boot + + ocil:ssg-mount_option_boot_noexec_action:testaction:1 + + + + Disable the ksmtuned_use_cifs SELinux Boolean + + ocil:ssg-sebool_ksmtuned_use_cifs_action:testaction:1 + + + + Randomize slab freelist + + ocil:ssg-kernel_config_slab_freelist_random_action:testaction:1 + + + + Ensure there are no legacy + NIS entries in /etc/group + + ocil:ssg-no_legacy_plus_entries_etc_group_action:testaction:1 + + + + Configure SELinux Policy + + ocil:ssg-selinux_policytype_action:testaction:1 + + + + Disable the container_connect_any SELinux Boolean + + ocil:ssg-sebool_container_connect_any_action:testaction:1 + + + + Disable the xguest_mount_media SELinux Boolean + + ocil:ssg-sebool_xguest_mount_media_action:testaction:1 + + + + Disable the httpd_can_network_connect_cobbler SELinux Boolean + + ocil:ssg-sebool_httpd_can_network_connect_cobbler_action:testaction:1 + + + + Verify User Who Owns Backup group File + + ocil:ssg-file_owner_backup_etc_group_action:testaction:1 + + + + Record Events that Modify the System's Discretionary Access Controls - fremovexattr + + ocil:ssg-audit_rules_dac_modification_fremovexattr_action:testaction:1 + + + + Enable Auditing for Processes Which Start Prior to the Audit Daemon + + ocil:ssg-grub2_audit_argument_action:testaction:1 + + + + Disable the daemons_enable_cluster_mode SELinux Boolean + + ocil:ssg-sebool_daemons_enable_cluster_mode_action:testaction:1 + + + + Disable LDAP Server (slapd) + + ocil:ssg-service_slapd_disabled_action:testaction:1 + + + + Install firewalld Package + + ocil:ssg-package_firewalld_installed_action:testaction:1 + + + + Disable the httpd_sys_script_anon_write SELinux Boolean + + ocil:ssg-sebool_httpd_sys_script_anon_write_action:testaction:1 + + + + Record Successful Creation Attempts to Files - open_by_handle_at O_CREAT + + ocil:ssg-audit_rules_successful_file_modification_open_by_handle_at_o_creat_action:testaction:1 + + + + Disable the tftp_home_dir SELinux Boolean + + ocil:ssg-sebool_tftp_home_dir_action:testaction:1 + + + + Disable Odd Job Daemon (oddjobd) + + ocil:ssg-service_oddjobd_disabled_action:testaction:1 + + + + Map System Users To The Appropriate SELinux Role + + ocil:ssg-selinux_user_login_roles_action:testaction:1 + + + + Disable the irssi_use_full_network SELinux Boolean + + ocil:ssg-sebool_irssi_use_full_network_action:testaction:1 + + + + Verify /boot/grub2/grub.cfg Permissions + + ocil:ssg-file_permissions_grub2_cfg_action:testaction:1 + + + + Ensure auditd Collects File Deletion Events by User - rmdir + + ocil:ssg-audit_rules_file_deletion_events_rmdir_action:testaction:1 + + + + Kernel panic on oops + + ocil:ssg-sysctl_kernel_panic_on_oops_action:testaction:1 + + + + Boot Loader Is Not Installed On Removeable Media + + ocil:ssg-grub2_no_removeable_media_action:testaction:1 + + + + Configure immutable Audit login UIDs + + ocil:ssg-audit_immutable_login_uids_action:testaction:1 + + + + Disable the xguest_connect_network SELinux Boolean + + ocil:ssg-sebool_xguest_connect_network_action:testaction:1 + + + + Disable the staff_use_svirt SELinux Boolean + + ocil:ssg-sebool_staff_use_svirt_action:testaction:1 + + + + Configure Accepting Router Advertisements on All IPv6 Interfaces + + ocil:ssg-sysctl_net_ipv6_conf_all_accept_ra_action:testaction:1 + + + + Uninstall httpd Package + + ocil:ssg-package_httpd_removed_action:testaction:1 + + + + Configure SSSD LDAP Backend Client CA Certificate Location + + ocil:ssg-sssd_ldap_configure_tls_ca_dir_action:testaction:1 + + + + Enable Public Key Authentication + + ocil:ssg-sshd_enable_pubkey_auth_action:testaction:1 + + + + Disable the prosody_bind_http_port SELinux Boolean + + ocil:ssg-sebool_prosody_bind_http_port_action:testaction:1 + + + + Uninstall pigz Package + + ocil:ssg-package_pigz_removed_action:testaction:1 + + + + Verify that Shared Library Files Have Restrictive Permissions + + ocil:ssg-file_permissions_library_dirs_action:testaction:1 + + + + Install openscap-scanner Package + + ocil:ssg-package_openscap-scanner_installed_action:testaction:1 + + + + Disable DHCP Service + + ocil:ssg-service_dhcpd_disabled_action:testaction:1 + + + + Disable the httpd_read_user_content SELinux Boolean + + ocil:ssg-sebool_httpd_read_user_content_action:testaction:1 + + + + Configure SNMP Service to Use Only SNMPv3 or Newer + + ocil:ssg-snmpd_use_newer_protocol_action:testaction:1 + + + + Disable the varnishd_connect_any SELinux Boolean + + ocil:ssg-sebool_varnishd_connect_any_action:testaction:1 + + + + Configure the Use of the pam_faillock.so Module in the /etc/pam.d/password-auth File. + + ocil:ssg-account_password_pam_faillock_password_auth_action:testaction:1 + + + + Record Events that Modify User/Group Information - /etc/security/opasswd + + ocil:ssg-audit_rules_usergroup_modification_opasswd_action:testaction:1 + + + + Configure SSSD LDAP Backend to Use TLS For All Transactions + + ocil:ssg-sssd_ldap_start_tls_action:testaction:1 + + + + Set Password Minimum Age + + ocil:ssg-accounts_minimum_age_login_defs_action:testaction:1 + + + + Disable the zabbix_can_network SELinux Boolean + + ocil:ssg-sebool_zabbix_can_network_action:testaction:1 + + + + Verify Group Who Owns shadow File + + ocil:ssg-file_groupowner_etc_shadow_action:testaction:1 + + + + Enable Kernel Parameter to Ignore Bogus ICMP Error Responses on IPv4 Interfaces + + ocil:ssg-sysctl_net_ipv4_icmp_ignore_bogus_error_responses_action:testaction:1 + + + + Ensure PAM Enforces Password Requirements - Minimum Length + + ocil:ssg-accounts_password_pam_minlen_action:testaction:1 + + + + Disable the virt_sandbox_use_all_caps SELinux Boolean + + ocil:ssg-sebool_virt_sandbox_use_all_caps_action:testaction:1 + + + + Set LogLevel to INFO + + ocil:ssg-sshd_set_loglevel_info_action:testaction:1 + + + + Add nosuid Option to /var/log/audit + + ocil:ssg-mount_option_var_log_audit_nosuid_action:testaction:1 + + + + Kernel panic timeout + + ocil:ssg-kernel_config_panic_timeout_action:testaction:1 + + + + Uninstall setroubleshoot-server Package + + ocil:ssg-package_setroubleshoot-server_removed_action:testaction:1 + + + + Harden SSHD Crypto Policy + + ocil:ssg-harden_sshd_crypto_policy_action:testaction:1 + + + + Add noexec Option to /var/tmp + + ocil:ssg-mount_option_var_tmp_noexec_action:testaction:1 + + + + Install the opensc Package For Multifactor Authentication + + ocil:ssg-package_opensc_installed_action:testaction:1 + + + + Disable /dev/kmem virtual device support + + ocil:ssg-kernel_config_devkmem_action:testaction:1 + + + + Ensure the Default C Shell Umask is Set Correctly + + ocil:ssg-accounts_umask_etc_csh_cshrc_action:testaction:1 + + + + Ensure auditd Collects Information on the Use of Privileged Commands - chsh + + ocil:ssg-audit_rules_privileged_commands_chsh_action:testaction:1 + + + + Enable the xend_run_qemu SELinux Boolean + + ocil:ssg-sebool_xend_run_qemu_action:testaction:1 + + + + Verify that local System.map file (if exists) is readable only by root + + ocil:ssg-file_permissions_systemmap_action:testaction:1 + + + + Configure low address space to protect from user allocation + + ocil:ssg-kernel_config_default_mmap_min_addr_action:testaction:1 + + + + Record Any Attempts to Run chcon + + ocil:ssg-audit_rules_execution_chcon_action:testaction:1 + + + + Lock Accounts Must Persist + + ocil:ssg-accounts_passwords_pam_faillock_dir_action:testaction:1 + + + + Verify Permissions on SSH Server config file + + ocil:ssg-file_permissions_sshd_config_action:testaction:1 + + + + Disable the virt_use_rawip SELinux Boolean + + ocil:ssg-sebool_virt_use_rawip_action:testaction:1 + + + + Ensure All SGID Executables Are Authorized + + ocil:ssg-file_permissions_unauthorized_sgid_action:testaction:1 + + + + Ensure All Accounts on the System Have Unique Names + + ocil:ssg-account_unique_name_action:testaction:1 + + + + Configure the confidence in TPM for entropy + + ocil:ssg-grub2_rng_core_default_quality_argument_action:testaction:1 + + + + All GIDs referenced in /etc/passwd must be defined in /etc/group + + ocil:ssg-gid_passwd_group_same_action:testaction:1 + + + + Ensure System Log Files Have Correct Permissions + + ocil:ssg-rsyslog_files_permissions_action:testaction:1 + + + + Record Events that Modify the System's Discretionary Access Controls - fchmodat + + ocil:ssg-audit_rules_dac_modification_fchmodat_action:testaction:1 + + + + Ensure auditd Collects Information on Kernel Module Loading and Unloading + + ocil:ssg-audit_rules_kernel_module_loading_action:testaction:1 + + + + Ensure remote access methods are monitored in Rsyslog + + ocil:ssg-rsyslog_remote_access_monitoring_action:testaction:1 + + + + Ensure a dedicated group owns sudo + + ocil:ssg-sudo_dedicated_group_action:testaction:1 + + + + Verify Owner on crontab + + ocil:ssg-file_owner_crontab_action:testaction:1 + + + + Configure TLS for rsyslog remote logging + + ocil:ssg-rsyslog_remote_tls_action:testaction:1 + + + + Ensure auditd Collects Information on the Use of Privileged Commands - ssh-keysign + + ocil:ssg-audit_rules_privileged_commands_ssh_keysign_action:testaction:1 + + + + Configure SSH Client to Use FIPS 140-2 Validated Ciphers: openssh.config + + ocil:ssg-harden_sshd_ciphers_openssh_conf_crypto_policy_action:testaction:1 + + + + Ensure Logs Sent To Remote Host + + ocil:ssg-rsyslog_remote_loghost_action:testaction:1 + + + + Record Successful Creation Attempts to Files - openat O_TRUNC_WRITE + + ocil:ssg-audit_rules_successful_file_modification_openat_o_trunc_write_action:testaction:1 + + + + Verify Owner on cron.weekly + + ocil:ssg-file_owner_cron_weekly_action:testaction:1 + + + + Disable the git_session_bind_all_unreserved_ports SELinux Boolean + + ocil:ssg-sebool_git_session_bind_all_unreserved_ports_action:testaction:1 + + + + Disable Red Hat Network Service (rhnsd) + + ocil:ssg-service_rhnsd_disabled_action:testaction:1 + + + + Record Any Attempts to Run restorecon + + ocil:ssg-audit_rules_execution_restorecon_action:testaction:1 + + + + Appropriate Action Must be Setup When the Internal Audit Event Queue is Full + + ocil:ssg-auditd_overflow_action_action:testaction:1 + + + + Disable the samba_portmapper SELinux Boolean + + ocil:ssg-sebool_samba_portmapper_action:testaction:1 + + + + Ensure /usr Located On Separate Partition + + ocil:ssg-partition_for_usr_action:testaction:1 + + + + Configure Fapolicy Module to Employ a Deny-all, Permit-by-exception Policy to Allow the Execution of Authorized Software Programs. + + ocil:ssg-fapolicy_default_deny_action:testaction:1 + + + + System Audit Logs Must Be Group Owned By Root + + ocil:ssg-file_group_ownership_var_log_audit_action:testaction:1 + + + + Enable the logadm_exec_content SELinux Boolean + + ocil:ssg-sebool_logadm_exec_content_action:testaction:1 + + + + Ensure Log Files Are Owned By Appropriate Group + + ocil:ssg-rsyslog_files_groupownership_action:testaction:1 + + + + Enable the LDAP Client For Use in Authconfig + + ocil:ssg-enable_ldap_client_action:testaction:1 + + + + Record Successful Permission Changes to Files - fchmod + + ocil:ssg-audit_rules_successful_file_modification_fchmod_action:testaction:1 + + + + Record attempts to alter time through settimeofday + + ocil:ssg-audit_rules_time_settimeofday_action:testaction:1 + + + + Disable GNOME3 Automount Opening + + ocil:ssg-dconf_gnome_disable_automount_open_action:testaction:1 + + + + Set Kernel Parameter to Increase Local Port Range + + ocil:ssg-sysctl_net_ipv4_ip_local_port_range_action:testaction:1 + + + + Ensure auditd Collects System Administrator Actions - /etc/sudoers + + ocil:ssg-audit_rules_sudoers_action:testaction:1 + + + + Configure Periodic Execution of AIDE + + ocil:ssg-aide_periodic_cron_checking_action:testaction:1 + + + + Ensure only owner and members of group owner of /usr/bin/sudo can execute it + + ocil:ssg-sudo_restrict_others_executable_permission_action:testaction:1 + + + + Set the GNOME3 Login Number of Failures + + ocil:ssg-dconf_gnome_login_retries_action:testaction:1 + + + + Make the kernel text and rodata read-only + + ocil:ssg-kernel_config_strict_kernel_rwx_action:testaction:1 + + + + Disable the virt_use_nfs SELinux Boolean + + ocil:ssg-sebool_virt_use_nfs_action:testaction:1 + + + + Disable the ksmtuned_use_nfs SELinux Boolean + + ocil:ssg-sebool_ksmtuned_use_nfs_action:testaction:1 + + + + Configure auditd Disk Full Action when Disk Space Is Full + + ocil:ssg-auditd_data_disk_full_action_stig_action:testaction:1 + + + + Ensure auditd Collects Information on the Use of Privileged Commands - pt_chown + + ocil:ssg-audit_rules_privileged_commands_pt_chown_action:testaction:1 + + + + Verify Permissions on Backup passwd File + + ocil:ssg-file_permissions_backup_etc_passwd_action:testaction:1 + + + + Disable the named_write_master_zones SELinux Boolean + + ocil:ssg-sebool_named_write_master_zones_action:testaction:1 + + + + Enable Kernel Parameter to Use TCP RFC 1337 on IPv4 Interfaces + + ocil:ssg-sysctl_net_ipv4_tcp_rfc1337_action:testaction:1 + + + + Install iptables Package + + ocil:ssg-package_iptables_installed_action:testaction:1 + + + + Configure LDAP Client to Use TLS For All Transactions + + ocil:ssg-ldap_client_start_tls_action:testaction:1 + + + + Disable the cron_can_relabel SELinux Boolean + + ocil:ssg-sebool_cron_can_relabel_action:testaction:1 + + + + Ensure Sudo Logfile Exists - sudo logfile + + ocil:ssg-sudo_custom_logfile_action:testaction:1 + + + + Disable the swift_can_network SELinux Boolean + + ocil:ssg-sebool_swift_can_network_action:testaction:1 + + + + Record Events that Modify the System's Discretionary Access Controls - removexattr + + ocil:ssg-audit_rules_dac_modification_removexattr_action:testaction:1 + + + + Disable the secure_mode SELinux Boolean + + ocil:ssg-sebool_secure_mode_action:testaction:1 + + + + Ensure IPv6 is disabled through kernel boot parameter + + ocil:ssg-grub2_ipv6_disable_argument_action:testaction:1 + + + + Require modules to be validly signed + + ocil:ssg-kernel_config_module_sig_force_action:testaction:1 + + + + Assign Expiration Date to Emergency Accounts + + ocil:ssg-account_emergency_expire_date_action:testaction:1 + + + + Record Events that Modify User/Group Information - /etc/gshadow + + ocil:ssg-audit_rules_usergroup_modification_gshadow_action:testaction:1 + + + + Enforce Spectre v2 mitigation + + ocil:ssg-grub2_spectre_v2_argument_action:testaction:1 + + + + Disable the squid_use_tproxy SELinux Boolean + + ocil:ssg-sebool_squid_use_tproxy_action:testaction:1 + + + + Enable rsyslog Service + + ocil:ssg-service_rsyslog_enabled_action:testaction:1 + + + + Disable the ssh_sysadm_login SELinux Boolean + + ocil:ssg-sebool_ssh_sysadm_login_action:testaction:1 + + + + Disable the httpd_can_connect_ftp SELinux Boolean + + ocil:ssg-sebool_httpd_can_connect_ftp_action:testaction:1 + + + + Disable All GNOME3 Thumbnailers + + ocil:ssg-dconf_gnome_disable_thumbnailers_action:testaction:1 + + + + Record Successful Permission Changes to Files - lsetxattr + + ocil:ssg-audit_rules_successful_file_modification_lsetxattr_action:testaction:1 + + + + Disable the ftpd_use_fusefs SELinux Boolean + + ocil:ssg-sebool_ftpd_use_fusefs_action:testaction:1 + + + + Resolve information before writing to audit logs + + ocil:ssg-auditd_log_format_action:testaction:1 + + + + Require Client SMB Packet Signing, if using smbclient + + ocil:ssg-require_smb_client_signing_action:testaction:1 + + + + All User Files and Directories In The Home Directory Must Have a Valid Owner + + ocil:ssg-accounts_users_home_files_ownership_action:testaction:1 + + + + Limit Password Reuse: system-auth + + ocil:ssg-accounts_password_pam_pwhistory_remember_system_auth_action:testaction:1 + + + + Disable the mpd_use_nfs SELinux Boolean + + ocil:ssg-sebool_mpd_use_nfs_action:testaction:1 + + + + Configure auditd space_left on Low Disk Space + + ocil:ssg-auditd_data_retention_space_left_action:testaction:1 + + + + Record Successful Ownership Changes to Files - fchown + + ocil:ssg-audit_rules_successful_file_modification_fchown_action:testaction:1 + + + + Disable named Service + + ocil:ssg-service_named_disabled_action:testaction:1 + + + + Disable IA32 emulation + + ocil:ssg-kernel_config_ia32_emulation_action:testaction:1 + + + + Disable Kernel Parameter for Accepting ICMP Redirects by Default on IPv6 Interfaces + + ocil:ssg-sysctl_net_ipv6_conf_default_accept_redirects_action:testaction:1 + + + + Record Events that Modify the System's Mandatory Access Controls + + ocil:ssg-audit_rules_mac_modification_action:testaction:1 + + + + Disable Compression Or Set Compression to delayed + + ocil:ssg-sshd_disable_compression_action:testaction:1 + + + + Disable the httpd_use_fusefs SELinux Boolean + + ocil:ssg-sebool_httpd_use_fusefs_action:testaction:1 + + + + Verify that system commands files are group owned by root or a system account + + ocil:ssg-file_groupownership_system_commands_dirs_action:testaction:1 + + + + Disable the httpd_setrlimit SELinux Boolean + + ocil:ssg-sebool_httpd_setrlimit_action:testaction:1 + + + + Set Permissions on All Configuration Files Inside /etc/httpd/conf/ + + ocil:ssg-file_permissions_httpd_server_conf_files_action:testaction:1 + + + + Uninstall DHCP Server Package + + ocil:ssg-package_dhcp_removed_action:testaction:1 + + + + Record Unsuccessful Permission Changes to Files - chmod + + ocil:ssg-audit_rules_unsuccessful_file_modification_chmod_action:testaction:1 + + + + Set SSH Idle Timeout Interval + + ocil:ssg-sshd_set_idle_timeout_action:testaction:1 + + + + Record Successful Access Attempts to Files - open_by_handle_at + + ocil:ssg-audit_rules_successful_file_modification_open_by_handle_at_action:testaction:1 + + + + Disable the virt_read_qemu_ga_data SELinux Boolean + + ocil:ssg-sebool_virt_read_qemu_ga_data_action:testaction:1 + + + + Strong Stack Protector + + ocil:ssg-kernel_config_stackprotector_strong_action:testaction:1 + + + + Enable log_config_module For HTTPD Logging + + ocil:ssg-httpd_enable_log_config_action:testaction:1 + + + + Remove NIS Client + + ocil:ssg-package_ypbind_removed_action:testaction:1 + + + + Verify ufw Enabled + + ocil:ssg-service_ufw_enabled_action:testaction:1 + + + + Record Any Attempts to Run seunshare + + ocil:ssg-audit_rules_execution_seunshare_action:testaction:1 + + + + System Audit Logs Must Be Owned By Root + + ocil:ssg-file_ownership_var_log_audit_stig_action:testaction:1 + + + + Enable Kernel Parameter to Use TCP Syncookies on Network Interfaces + + ocil:ssg-sysctl_net_ipv4_tcp_syncookies_action:testaction:1 + + + + Ensure auditd Collects Information on the Use of Privileged Commands - mount + + ocil:ssg-audit_rules_privileged_commands_mount_action:testaction:1 + + + + Set Daemon Umask + + ocil:ssg-umask_for_daemons_action:testaction:1 + + + + Configure A Valid Server Certificate + + ocil:ssg-httpd_configure_valid_server_cert_action:testaction:1 + + + + Disable storing core dump + + ocil:ssg-coredump_disable_storage_action:testaction:1 + + + + Disable the virt_sandbox_use_sys_admin SELinux Boolean + + ocil:ssg-sebool_virt_sandbox_use_sys_admin_action:testaction:1 + + + + Record Events that Modify the System's Discretionary Access Controls - lchown + + ocil:ssg-audit_rules_dac_modification_lchown_action:testaction:1 + + + + Disable Network File System (nfs) + + ocil:ssg-service_nfs_disabled_action:testaction:1 + + + + All User Files and Directories In The Home Directory Must Be Group-Owned By The Primary User + + ocil:ssg-accounts_users_home_files_groupownership_action:testaction:1 + + + + Enable the staff_exec_content SELinux Boolean + + ocil:ssg-sebool_staff_exec_content_action:testaction:1 + + + + Enable the virt_sandbox_use_audit SELinux Boolean + + ocil:ssg-sebool_virt_sandbox_use_audit_action:testaction:1 + + + + Enable SLUB/SLAB allocator poisoning + + ocil:ssg-grub2_slub_debug_argument_action:testaction:1 + + + + Add nosuid Option to /var/log + + ocil:ssg-mount_option_var_log_nosuid_action:testaction:1 + + + + Enable page allocator poisoning + + ocil:ssg-grub2_page_poison_argument_action:testaction:1 + + + + Ensure There Are No Accounts With Blank or Null Passwords + + ocil:ssg-no_empty_passwords_etc_shadow_action:testaction:1 + + + + Use zero for poisoning instead of debugging value + + ocil:ssg-kernel_config_page_poisoning_zero_action:testaction:1 + + + + Uninstall abrt-plugin-rhtsupport Package + + ocil:ssg-package_abrt-plugin-rhtsupport_removed_action:testaction:1 + + + + Enable the user_exec_content SELinux Boolean + + ocil:ssg-sebool_user_exec_content_action:testaction:1 + + + + Verify Permissions on /var/log Directory + + ocil:ssg-file_permissions_var_log_action:testaction:1 + + + + Ensure auditd Collects Information on the Use of Privileged Commands - kmod + + ocil:ssg-audit_rules_privileged_commands_kmod_action:testaction:1 + + + + Disable GDM Automatic Login + + ocil:ssg-gnome_gdm_disable_automatic_login_action:testaction:1 + + + + Record Unsuccessful Permission Changes to Files - fchmodat + + ocil:ssg-audit_rules_unsuccessful_file_modification_fchmodat_action:testaction:1 + + + + Verify Permissions on shadow File + + ocil:ssg-file_permissions_etc_shadow_action:testaction:1 + + + + Prevent non-Privileged Users from Modifying Network Interfaces using nmcli + + ocil:ssg-network_nmcli_permissions_action:testaction:1 + + + + Enable the xend_run_blktap SELinux Boolean + + ocil:ssg-sebool_xend_run_blktap_action:testaction:1 + + + + Configure auditd Disk Error Action on Disk Error + + ocil:ssg-auditd_data_disk_error_action_stig_action:testaction:1 + + + + Account Lockouts Must Persist + + ocil:ssg-account_passwords_pam_faillock_dir_action:testaction:1 + + + + Harden OpenSSL Crypto Policy + + ocil:ssg-harden_openssl_crypto_policy_action:testaction:1 + + + + Disable hibernation + + ocil:ssg-kernel_config_hibernation_action:testaction:1 + + + + Ensure Default SNMP Password Is Not Used + + ocil:ssg-snmpd_not_default_password_action:testaction:1 + + + + Set SSH MaxSessions limit + + ocil:ssg-sshd_set_max_sessions_action:testaction:1 + + + + Installation of a compiler on production web server is prohibited + + ocil:ssg-httpd_no_compilers_in_prod_action:testaction:1 + + + + Install the SSSD Package + + ocil:ssg-package_sssd_installed_action:testaction:1 + + + + Shutdown System When Auditing Failures Occur + + ocil:ssg-audit_rules_system_shutdown_action:testaction:1 + + + + Record Successful Permission Changes to Files - fchmodat + + ocil:ssg-audit_rules_successful_file_modification_fchmodat_action:testaction:1 + + + + Ensure Rsyslog Authenticates Off-Loaded Audit Records + + ocil:ssg-rsyslog_encrypt_offload_actionsendstreamdriverauthmode_action:testaction:1 + + + + Verify that System Executables Have Root Ownership + + ocil:ssg-file_ownership_binary_dirs_action:testaction:1 + + + + All Interactive Users Home Directories Must Exist + + ocil:ssg-accounts_user_interactive_home_directory_exists_action:testaction:1 + + + + Disable the global_ssp SELinux Boolean + + ocil:ssg-sebool_global_ssp_action:testaction:1 + + + + Disable kexec system call + + ocil:ssg-kernel_config_kexec_action:testaction:1 + + + + Ensure the Default Umask is Set Correctly in login.defs + + ocil:ssg-accounts_umask_etc_login_defs_action:testaction:1 + + + + Limit the Number of Concurrent Login Sessions Allowed Per User + + ocil:ssg-accounts_max_concurrent_login_sessions_action:testaction:1 + + + + Disable SSH root Login with a Password (Insecure) + + ocil:ssg-sshd_disable_root_password_login_action:testaction:1 + + + + Set the UEFI Boot Loader Admin Username to a Non-Default Value + + ocil:ssg-grub2_uefi_admin_username_action:testaction:1 + + + + Add nosuid Option to Removable Media Partitions + + ocil:ssg-mount_option_nosuid_removable_partitions_action:testaction:1 + + + + Limit CPU consumption of the Perf system + + ocil:ssg-sysctl_kernel_perf_cpu_time_max_percent_action:testaction:1 + + + + Uninstall talk-server Package + + ocil:ssg-package_talk-server_removed_action:testaction:1 + + + + Audit Configuration Files Must Be Owned By Group root + + ocil:ssg-file_groupownership_audit_configuration_action:testaction:1 + + + + Disable the xserver_execmem SELinux Boolean + + ocil:ssg-sebool_xserver_execmem_action:testaction:1 + + + + Include Local Events in Audit Logs + + ocil:ssg-auditd_local_events_action:testaction:1 + + + + Disable SSH Root Login + + ocil:ssg-sshd_disable_root_login_action:testaction:1 + + + + Add nodev Option to Non-Root Local Partitions + + ocil:ssg-mount_option_nodev_nonroot_local_partitions_action:testaction:1 + + + + Enable GSSAPI Authentication + + ocil:ssg-sshd_enable_gssapi_auth_action:testaction:1 + + + + Record Unsuccessful Modification Attempts to Files - open_by_handle_at O_TRUNC_WRITE + + ocil:ssg-audit_rules_unsuccessful_file_modification_open_by_handle_at_o_trunc_write_action:testaction:1 + + + + Disable the spamassassin_can_network SELinux Boolean + + ocil:ssg-sebool_spamassassin_can_network_action:testaction:1 + + + + Add nodev Option to Removable Media Partitions + + ocil:ssg-mount_option_nodev_removable_partitions_action:testaction:1 + + + + Enable nails Service + + ocil:ssg-service_nails_enabled_action:testaction:1 + + + + Verify Permissions on /etc/audit/auditd.conf + + ocil:ssg-file_permissions_etc_audit_auditd_action:testaction:1 + + + + Verify Group Who Owns Backup passwd File + + ocil:ssg-file_groupowner_backup_etc_passwd_action:testaction:1 + + + + Configure file name of core dumps + + ocil:ssg-sysctl_kernel_core_uses_pid_action:testaction:1 + + + + Ensure Users Cannot Change GNOME3 Session Idle Settings + + ocil:ssg-dconf_gnome_session_idle_user_locks_action:testaction:1 + + + + Configure Time Service Maxpoll Interval + + ocil:ssg-chronyd_or_ntpd_set_maxpoll_action:testaction:1 + + + + Disable the cdrecord_read_content SELinux Boolean + + ocil:ssg-sebool_cdrecord_read_content_action:testaction:1 + + + + The Installed Operating System Is FIPS 140-2 Certified + + ocil:ssg-installed_OS_is_FIPS_certified_action:testaction:1 + + + + Verify Owner on cron.monthly + + ocil:ssg-file_owner_cron_monthly_action:testaction:1 + + + + Ensure invoking users password for privilege escalation when using sudo + + ocil:ssg-sudoers_validate_passwd_action:testaction:1 + + + + Add noexec Option to Removable Media Partitions + + ocil:ssg-mount_option_noexec_removable_partitions_action:testaction:1 + + + + Install the McAfee Runtime Libraries and Linux Agent + + ocil:ssg-install_mcafee_cma_rt_action:testaction:1 + + + + Record attempts to alter time through adjtimex + + ocil:ssg-audit_rules_time_adjtimex_action:testaction:1 + + + + Disable SSH Support for .rhosts Files + + ocil:ssg-sshd_disable_rhosts_action:testaction:1 + + + + Disable Advanced Configuration and Power Interface (acpid) + + ocil:ssg-service_acpid_disabled_action:testaction:1 + + + + Root Path Must Be Vendor Default + + ocil:ssg-root_path_default_action:testaction:1 + + + + Enable GNOME3 Screensaver Lock After Idle Period + + ocil:ssg-dconf_gnome_screensaver_lock_enabled_action:testaction:1 + + + + Enable Encrypted X11 Forwarding + + ocil:ssg-sshd_enable_x11_forwarding_action:testaction:1 + + + + Configure audispd Plugin To Send Logs To Remote Server + + ocil:ssg-auditd_audispd_configure_remote_server_action:testaction:1 + + + + Disable the telepathy_tcp_connect_generic_network_ports SELinux Boolean + + ocil:ssg-sebool_telepathy_tcp_connect_generic_network_ports_action:testaction:1 + + + + Ensure auditd Collects Information on the Use of Privileged Commands - su + + ocil:ssg-audit_rules_privileged_commands_su_action:testaction:1 + + + + Prevent Routing External Traffic to Local Loopback on All IPv4 Interfaces + + ocil:ssg-sysctl_net_ipv4_conf_all_route_localnet_action:testaction:1 + + + + Force opensc To Use Defined Smart Card Driver + + ocil:ssg-force_opensc_card_drivers_action:testaction:1 + + + + A private web server must be located on a separate controlled access subnet + + ocil:ssg-httpd_private_server_on_separate_subnet_action:testaction:1 + + + + Enable the secadm_exec_content SELinux Boolean + + ocil:ssg-sebool_secadm_exec_content_action:testaction:1 + + + + Harden common str/mem functions against buffer overflows + + ocil:ssg-kernel_config_fortify_source_action:testaction:1 + + + + Disable ypserv Service + + ocil:ssg-service_ypserv_disabled_action:testaction:1 + + + + Configure CA certificate for rsyslog remote logging + + ocil:ssg-rsyslog_remote_tls_cacert_action:testaction:1 + + + + Disable the virt_sandbox_use_mknod SELinux Boolean + + ocil:ssg-sebool_virt_sandbox_use_mknod_action:testaction:1 + + + + Configure session renegotiation for SSH client + + ocil:ssg-ssh_client_rekey_limit_action:testaction:1 + + + + Verify that All World-Writable Directories Have Sticky Bits Set + + ocil:ssg-dir_perms_world_writable_sticky_bits_action:testaction:1 + + + + Install policycoreutils Package + + ocil:ssg-package_policycoreutils_installed_action:testaction:1 + + + + Disable Client Dynamic DNS Updates + + ocil:ssg-network_disable_ddns_interfaces_action:testaction:1 + + + + Verify Permissions on /var/log/syslog File + + ocil:ssg-file_permissions_var_log_syslog_action:testaction:1 + + + + Add noauto Option to /boot + + ocil:ssg-mount_option_boot_noauto_action:testaction:1 + + + + System Audit Directories Must Be Group Owned By Root + + ocil:ssg-directory_group_ownership_var_log_audit_action:testaction:1 + + + + Remove the FreeRadius Server Package + + ocil:ssg-package_freeradius_removed_action:testaction:1 + + + + Disable the virt_rw_qemu_ga_data SELinux Boolean + + ocil:ssg-sebool_virt_rw_qemu_ga_data_action:testaction:1 + + + + Disable the httpd_mod_auth_ntlm_winbind SELinux Boolean + + ocil:ssg-sebool_httpd_mod_auth_ntlm_winbind_action:testaction:1 + + + + Disable Web Content Symbolic Links + + ocil:ssg-httpd_disable_content_symlinks_action:testaction:1 + + + + Disable Cyrus SASL Authentication Daemon (saslauthd) + + ocil:ssg-service_saslauthd_disabled_action:testaction:1 + + + + Ensure auditd Collects File Deletion Events by User + + ocil:ssg-audit_rules_file_deletion_events_action:testaction:1 + + + + Configure AIDE to Use FIPS 140-2 for Validating Hashes + + ocil:ssg-aide_use_fips_hashes_action:testaction:1 + + + + Scan All Uploaded Content for Malicious Software + + ocil:ssg-httpd_antivirus_scan_uploads_action:testaction:1 + + + + Assign Expiration Date to Temporary Accounts + + ocil:ssg-account_temp_expire_date_action:testaction:1 + + + + Ensure SSH LoginGraceTime is configured + + ocil:ssg-sshd_set_login_grace_time_action:testaction:1 + + + + Ensure auditd Collects Information on the Use of Privileged Commands - newgidmap + + ocil:ssg-audit_rules_privileged_commands_newgidmap_action:testaction:1 + + + + Enable the nfs_export_all_rw SELinux Boolean + + ocil:ssg-sebool_nfs_export_all_rw_action:testaction:1 + + + + Direct root Logins Not Allowed + + ocil:ssg-no_direct_root_logins_action:testaction:1 + + + + Configure auditd mail_acct Action on Low Disk Space + + ocil:ssg-auditd_data_retention_action_mail_acct_action:testaction:1 + + + + Disable the mmap_low_allowed SELinux Boolean + + ocil:ssg-sebool_mmap_low_allowed_action:testaction:1 + + + + Ensure No World-Writable Files Exist + + ocil:ssg-file_permissions_unauthorized_world_writable_action:testaction:1 + + + + Disable the sge_use_nfs SELinux Boolean + + ocil:ssg-sebool_sge_use_nfs_action:testaction:1 + + + + Install subscription-manager Package + + ocil:ssg-package_subscription-manager_installed_action:testaction:1 + + + + Enable GNOME3 Login Warning Banner + + ocil:ssg-dconf_gnome_banner_enabled_action:testaction:1 + + + + Disable the authlogin_nsswitch_use_ldap SELinux Boolean + + ocil:ssg-sebool_authlogin_nsswitch_use_ldap_action:testaction:1 + + + + Disable SSH TCP Forwarding + + ocil:ssg-sshd_disable_tcp_forwarding_action:testaction:1 + + + + Enable cron Service + + ocil:ssg-service_crond_enabled_action:testaction:1 + + + + Verify that Interactive Boot is Disabled + + ocil:ssg-grub2_disable_interactive_boot_action:testaction:1 + + + + Record Any Attempts to Run ssh-agent + + ocil:ssg-audit_rules_privileged_commands_ssh_agent_action:testaction:1 + + + + Ensure PAM Enforces Password Requirements - Enforce for Local Accounts Only + + ocil:ssg-accounts_password_pam_enforce_local_action:testaction:1 + + + + Record Events that Modify the System's Discretionary Access Controls - lremovexattr + + ocil:ssg-audit_rules_dac_modification_lremovexattr_action:testaction:1 + + + + Enable NX or XD Support in the BIOS + + ocil:ssg-bios_enable_execution_restrictions_action:testaction:1 + + + + Configure auditd flush priority + + ocil:ssg-auditd_data_retention_flush_action:testaction:1 + + + + Set Permissions on the /etc/httpd/conf/ Directory + + ocil:ssg-dir_perms_etc_httpd_conf_action:testaction:1 + + + + Verify that Shared Library Files Have Root Ownership + + ocil:ssg-file_ownership_library_dirs_action:testaction:1 + + + + Ensure SELinux Not Disabled in the kernel arguments + + ocil:ssg-coreos_enable_selinux_kernel_argument_action:testaction:1 + + + + Enable the pcscd Service + + ocil:ssg-service_pcscd_enabled_action:testaction:1 + + + + Record Successful Permission Changes to Files - fsetxattr + + ocil:ssg-audit_rules_successful_file_modification_fsetxattr_action:testaction:1 + + + + Disable the ftpd_use_nfs SELinux Boolean + + ocil:ssg-sebool_ftpd_use_nfs_action:testaction:1 + + + + Add noexec Option to /dev/shm + + ocil:ssg-mount_option_dev_shm_noexec_action:testaction:1 + + + + Record Events that Modify User/Group Information - /etc/passwd + + ocil:ssg-audit_rules_usergroup_modification_passwd_action:testaction:1 + + + + Disable the tftp_anon_write SELinux Boolean + + ocil:ssg-sebool_tftp_anon_write_action:testaction:1 + + + + Configure SSH Server to Use FIPS 140-2 Validated Ciphers: opensshserver.config + + ocil:ssg-harden_sshd_ciphers_opensshserver_conf_crypto_policy_action:testaction:1 + + + + Verify Group Who Owns cron.d + + ocil:ssg-file_groupowner_cron_d_action:testaction:1 + + + + Uninstall rsh-server Package + + ocil:ssg-package_rsh-server_removed_action:testaction:1 + + + + Ensure all zIPL boot entries are BLS compliant + + ocil:ssg-zipl_bls_entries_only_action:testaction:1 + + + + Disable the puppetmaster_use_db SELinux Boolean + + ocil:ssg-sebool_puppetmaster_use_db_action:testaction:1 + + + + Disable Bluetooth Kernel Module + + ocil:ssg-kernel_module_bluetooth_disabled_action:testaction:1 + + + + Disable Kerberos by removing host keytab + + ocil:ssg-kerberos_disable_no_keytab_action:testaction:1 + + + + fapolicyd Must be Configured to Limit Access to Users Home Folders + + ocil:ssg-fapolicyd_prevent_home_folder_access_action:testaction:1 + + + + Add nosuid Option to /opt + + ocil:ssg-mount_option_opt_nosuid_action:testaction:1 + + + + Harden SSH client Crypto Policy + + ocil:ssg-harden_ssh_client_crypto_policy_action:testaction:1 + + + + Disable the virt_use_samba SELinux Boolean + + ocil:ssg-sebool_virt_use_samba_action:testaction:1 + + + + Set number of Password Hashing Rounds - password-auth + + ocil:ssg-accounts_password_pam_unix_rounds_password_auth_action:testaction:1 + + + + Use Only FIPS 140-2 Validated Ciphers + + ocil:ssg-sshd_use_approved_ciphers_action:testaction:1 + + + + Configure Sending and Accepting Shared Media Redirects for All IPv4 Interfaces + + ocil:ssg-sysctl_net_ipv4_conf_all_shared_media_action:testaction:1 + + + + Configure Maximum Number of Autoconfigured Addresses on All IPv6 Interfaces By Default + + ocil:ssg-sysctl_net_ipv6_conf_default_max_addresses_action:testaction:1 + + + + Disable Accepting Router Advertisements on all IPv6 Interfaces by Default + + ocil:ssg-sysctl_net_ipv6_conf_default_accept_ra_action:testaction:1 + + + + Disable the ftpd_connect_db SELinux Boolean + + ocil:ssg-sebool_ftpd_connect_db_action:testaction:1 + + + + Enable seccomp to safely compute untrusted bytecode + + ocil:ssg-kernel_config_seccomp_action:testaction:1 + + + + Disable the samba_domain_controller SELinux Boolean + + ocil:ssg-sebool_samba_domain_controller_action:testaction:1 + + + + Configure the Firewalld Ports + + ocil:ssg-configure_firewalld_ports_action:testaction:1 + + + + Disable X11 Forwarding + + ocil:ssg-sshd_disable_x11_forwarding_action:testaction:1 + + + + Set GNOME3 Screensaver Lock Delay After Activation Period + + ocil:ssg-dconf_gnome_screensaver_lock_delay_action:testaction:1 + + + + Ensure All Groups on the System Have Unique Group Names + + ocil:ssg-group_unique_name_action:testaction:1 + + + + Install Smart Card Packages For Multifactor Authentication + + ocil:ssg-install_smartcard_packages_action:testaction:1 + + + + Ensure auditd Collects Information on the Use of Privileged Commands - newgrp + + ocil:ssg-audit_rules_privileged_commands_newgrp_action:testaction:1 + + + + Ensure that System Accounts Are Locked + + ocil:ssg-no_password_auth_for_systemaccounts_action:testaction:1 + + + + Disable the cron_system_cronjob_use_shares SELinux Boolean + + ocil:ssg-sebool_cron_system_cronjob_use_shares_action:testaction:1 + + + + Record Unsuccessful Permission Changes to Files - fsetxattr + + ocil:ssg-audit_rules_unsuccessful_file_modification_fsetxattr_action:testaction:1 + + + + Verify Group Who Owns passwd File + + ocil:ssg-file_groupowner_etc_passwd_action:testaction:1 + + + + Verify User Who Owns /var/log Directory + + ocil:ssg-file_owner_var_log_action:testaction:1 + + + + Disable the git_cgi_enable_homedirs SELinux Boolean + + ocil:ssg-sebool_git_cgi_enable_homedirs_action:testaction:1 + + + + Disable GNOME3 Automount running + + ocil:ssg-dconf_gnome_disable_autorun_action:testaction:1 + + + + Configure SSH Client to Use FIPS 140-2 Validated MACs: openssh.config + + ocil:ssg-harden_sshd_macs_openssh_conf_crypto_policy_action:testaction:1 + + + + Disable the zebra_write_config SELinux Boolean + + ocil:ssg-sebool_zebra_write_config_action:testaction:1 + + + + Disable System Statistics Reset Service (sysstat) + + ocil:ssg-service_sysstat_disabled_action:testaction:1 + + + + Disable the polipo_use_cifs SELinux Boolean + + ocil:ssg-sebool_polipo_use_cifs_action:testaction:1 + + + + Disable the selinuxuser_postgresql_connect_enabled SELinux Boolean + + ocil:ssg-sebool_selinuxuser_postgresql_connect_enabled_action:testaction:1 + + + + Record Successful Access Attempts to Files - creat + + ocil:ssg-audit_rules_successful_file_modification_creat_action:testaction:1 + + + + Uninstall setroubleshoot-plugins Package + + ocil:ssg-package_setroubleshoot-plugins_removed_action:testaction:1 + + + + Disable the samba_enable_home_dirs SELinux Boolean + + ocil:ssg-sebool_samba_enable_home_dirs_action:testaction:1 + + + + Ensure No Device Files are Unlabeled by SELinux + + ocil:ssg-selinux_all_devicefiles_labeled_action:testaction:1 + + + + Record Successful Access Attempts to Files - open + + ocil:ssg-audit_rules_successful_file_modification_open_action:testaction:1 + + + + Verify Group Who Owns gshadow File + + ocil:ssg-file_groupowner_etc_gshadow_action:testaction:1 + + + + Install binutils Package + + ocil:ssg-package_binutils_installed_action:testaction:1 + + + + Record Events that Modify the System's Discretionary Access Controls - chmod + + ocil:ssg-audit_rules_dac_modification_chmod_action:testaction:1 + + + + Install fapolicyd Package + + ocil:ssg-package_fapolicyd_installed_action:testaction:1 + + + + Disable the zarafa_setrlimit SELinux Boolean + + ocil:ssg-sebool_zarafa_setrlimit_action:testaction:1 + + + + Record Successful Ownership Changes to Files - chown + + ocil:ssg-audit_rules_successful_file_modification_chown_action:testaction:1 + + + + Install the Samba Common Package + + ocil:ssg-package_samba-common_installed_action:testaction:1 + + + + Limit sampling frequency of the Perf system + + ocil:ssg-sysctl_kernel_perf_event_max_sample_rate_action:testaction:1 + + + + Install the Policy Auditor (PA) Module + + ocil:ssg-install_mcafee_hbss_pa_action:testaction:1 + + + + Record Successful Delete Attempts to Files - unlink + + ocil:ssg-audit_rules_successful_file_modification_unlink_action:testaction:1 + + + + Configure Certificate Directives for LDAP Use of TLS + + ocil:ssg-ldap_client_tls_cacertpath_action:testaction:1 + + + + Disable the telepathy_connect_all_ports SELinux Boolean + + ocil:ssg-sebool_telepathy_connect_all_ports_action:testaction:1 + + + + Disable Power Settings in GNOME3 + + ocil:ssg-dconf_gnome_disable_power_settings_action:testaction:1 + + + + Disable the polipo_connect_all_unreserved SELinux Boolean + + ocil:ssg-sebool_polipo_connect_all_unreserved_action:testaction:1 + + + + Ensure /var/log Located On Separate Partition + + ocil:ssg-partition_for_var_log_action:testaction:1 + + + + Disable the selinuxuser_share_music SELinux Boolean + + ocil:ssg-sebool_selinuxuser_share_music_action:testaction:1 + + + + Require Encryption for Remote Access in GNOME3 + + ocil:ssg-dconf_gnome_remote_access_encryption_action:testaction:1 + + + + Verify Group Who Owns /etc/cron.allow file + + ocil:ssg-file_groupowner_cron_allow_action:testaction:1 + + + + Uninstall squid Package + + ocil:ssg-package_squid_removed_action:testaction:1 + + + + Disable the selinuxuser_execheap SELinux Boolean + + ocil:ssg-sebool_selinuxuser_execheap_action:testaction:1 + + + + Remove Write Permissions From Filesystem Paths And Server Scripts + + ocil:ssg-httpd_configure_script_permissions_action:testaction:1 + + + + Disable the cobbler_anon_write SELinux Boolean + + ocil:ssg-sebool_cobbler_anon_write_action:testaction:1 + + + + Authorize Human Interface Devices and USB hubs in USBGuard daemon + + ocil:ssg-usbguard_allow_hid_and_hub_action:testaction:1 + + + + Disable the deny_ptrace SELinux Boolean + + ocil:ssg-sebool_deny_ptrace_action:testaction:1 + + + + Ensure auditd Collects File Deletion Events by User - unlink + + ocil:ssg-audit_rules_file_deletion_events_unlink_action:testaction:1 + + + + Restrict usage of ptrace to descendant processes + + ocil:ssg-sysctl_kernel_yama_ptrace_scope_action:testaction:1 + + + + Disable the git_system_enable_homedirs SELinux Boolean + + ocil:ssg-sebool_git_system_enable_homedirs_action:testaction:1 + + + + Record Any Attempts to Run setfacl + + ocil:ssg-audit_rules_execution_setfacl_action:testaction:1 + + + + Prefer to use a 64-bit Operating System when supported + + ocil:ssg-prefer_64bit_os_action:testaction:1 + + + + Enable the NTP Daemon + + ocil:ssg-service_chronyd_or_ntpd_enabled_action:testaction:1 + + + + Verify Permissions on passwd File + + ocil:ssg-file_permissions_etc_passwd_action:testaction:1 + + + + Disable At Service (atd) + + ocil:ssg-service_atd_disabled_action:testaction:1 + + + + Configure AIDE to Verify Access Control Lists (ACLs) + + ocil:ssg-aide_verify_acls_action:testaction:1 + + + + Record Events that Modify the System's Discretionary Access Controls - fchownat + + ocil:ssg-audit_rules_dac_modification_fchownat_action:testaction:1 + + + + Ensure PAM Enforces Password Requirements - Minimum Digit Characters + + ocil:ssg-accounts_password_pam_dcredit_action:testaction:1 + + + + Enable Logging of All FTP Transactions + + ocil:ssg-ftp_log_transactions_action:testaction:1 + + + + Configure kernel to trust the CPU random number generator + + ocil:ssg-grub2_kernel_trust_cpu_rng_action:testaction:1 + + + + Record Successful Delete Attempts to Files - unlinkat + + ocil:ssg-audit_rules_successful_file_modification_unlinkat_action:testaction:1 + + + + Verify Group Who Owns /var/log/messages File + + ocil:ssg-file_groupowner_var_log_messages_action:testaction:1 + + + + Set number of Password Hashing Rounds - system-auth + + ocil:ssg-accounts_password_pam_unix_rounds_system_auth_action:testaction:1 + + + + Ensure SELinux Not Disabled in zIPL + + ocil:ssg-zipl_enable_selinux_action:testaction:1 + + + + Enforce usage of pam_wheel for su authentication + + ocil:ssg-use_pam_wheel_for_su_action:testaction:1 + + + + Mount Remote Filesystems with nosuid + + ocil:ssg-mount_option_nosuid_remote_filesystems_action:testaction:1 + + + + Enable checks on notifier call chains + + ocil:ssg-kernel_config_debug_notifiers_action:testaction:1 + + + + Add nodev Option to /dev/shm + + ocil:ssg-mount_option_dev_shm_nodev_action:testaction:1 + + + + Uninstall xinetd Package + + ocil:ssg-package_xinetd_removed_action:testaction:1 + + + + Disable the postgresql_selinux_transmit_client_label SELinux Boolean + + ocil:ssg-sebool_postgresql_selinux_transmit_client_label_action:testaction:1 + + + + Enable SSH Print Last Log + + ocil:ssg-sshd_print_last_log_action:testaction:1 + + + + Enable the SSSD Service + + ocil:ssg-service_sssd_enabled_action:testaction:1 + + + + Set Permissions on All Configuration Files Inside /etc/httpd/conf.d/ + + ocil:ssg-file_permissions_httpd_server_conf_d_files_action:testaction:1 + + + + Ensure All Accounts on the System Have Unique User IDs + + ocil:ssg-account_unique_id_action:testaction:1 + + + + Disable storing core dumps + + ocil:ssg-sysctl_kernel_core_pattern_action:testaction:1 + + + + Disable the GNOME3 Login Restart and Shutdown Buttons + + ocil:ssg-dconf_gnome_disable_restart_shutdown_action:testaction:1 + + + + Enable the postgresql_selinux_users_ddl SELinux Boolean + + ocil:ssg-sebool_postgresql_selinux_users_ddl_action:testaction:1 + + + + Audit Tools Must Have a Mode of 0755 or Less Permissive + + ocil:ssg-file_audit_tools_permissions_action:testaction:1 + + + + Ensure PAM Enforces Password Requirements - Prevent the Use of Dictionary Words + + ocil:ssg-accounts_password_pam_dictcheck_action:testaction:1 + + + + Disable PubkeyAuthentication Authentication + + ocil:ssg-sshd_disable_pubkey_auth_action:testaction:1 + + + + Disable the samba_load_libgfapi SELinux Boolean + + ocil:ssg-sebool_samba_load_libgfapi_action:testaction:1 + + + + Enable the auditadm_exec_content SELinux Boolean + + ocil:ssg-sebool_auditadm_exec_content_action:testaction:1 + + + + Disable Kerberos Authentication + + ocil:ssg-sshd_disable_kerb_auth_action:testaction:1 + + + + Disable the mpd_enable_homedirs SELinux Boolean + + ocil:ssg-sebool_mpd_enable_homedirs_action:testaction:1 + + + + Configure opensc Smart Card Drivers + + ocil:ssg-configure_opensc_card_drivers_action:testaction:1 + + + + Ensure LDAP client is not installed + + ocil:ssg-package_openldap-clients_removed_action:testaction:1 + + + + Ensure All Files Are Owned by a Group + + ocil:ssg-file_permissions_ungroupowned_action:testaction:1 + + + + Set Permissions on All Configuration Files Inside /etc/httpd/conf.modules.d/ + + ocil:ssg-file_permissions_httpd_server_modules_files_action:testaction:1 + + + + The web server password(s) must be entrusted to the SA or Web Manager + + ocil:ssg-httpd_entrust_passwords_action:testaction:1 + + + + Disable Kernel Parameter for Accepting ICMP Redirects by Default on IPv4 Interfaces + + ocil:ssg-sysctl_net_ipv4_conf_default_accept_redirects_action:testaction:1 + + + + Verify Group Who Owns cron.monthly + + ocil:ssg-file_groupowner_cron_monthly_action:testaction:1 + + + + Mount Remote Filesystems with noexec + + ocil:ssg-mount_option_noexec_remote_filesystems_action:testaction:1 + + + + Remove .java And .jpp Files + + ocil:ssg-httpd_limit_java_files_action:testaction:1 + + + + Disable the samba_share_nfs SELinux Boolean + + ocil:ssg-sebool_samba_share_nfs_action:testaction:1 + + + + Enable the unconfined_login SELinux Boolean + + ocil:ssg-sebool_unconfined_login_action:testaction:1 + + + + Disallow Configuration to Bypass Password Requirements for Privilege Escalation + + ocil:ssg-disallow_bypass_password_sudo_action:testaction:1 + + + + Ensure auditd Collects Information on the Use of Privileged Commands - pam_timestamp_check + + ocil:ssg-audit_rules_privileged_commands_pam_timestamp_check_action:testaction:1 + + + + Disable Kernel Parameter for IPv6 Forwarding + + ocil:ssg-sysctl_net_ipv6_conf_all_forwarding_action:testaction:1 + + + + Disable Bluetooth Service + + ocil:ssg-service_bluetooth_disabled_action:testaction:1 + + + + Disable the selinuxuser_udp_server SELinux Boolean + + ocil:ssg-sebool_selinuxuser_udp_server_action:testaction:1 + + + + Ensure Log Files Are Owned By Appropriate User + + ocil:ssg-rsyslog_files_ownership_action:testaction:1 + + + + Record Unsuccessful Ownership Changes to Files - chown + + ocil:ssg-audit_rules_unsuccessful_file_modification_chown_action:testaction:1 + + + + Set SSH Client Alive Count Max + + ocil:ssg-sshd_set_keepalive_action:testaction:1 + + + + Ensure sudo umask is appropriate - sudo umask + + ocil:ssg-sudo_add_umask_action:testaction:1 + + + + Verify Group Who Owns Crontab + + ocil:ssg-file_groupowner_crontab_action:testaction:1 + + + + Uninstall Samba Package + + ocil:ssg-package_samba_removed_action:testaction:1 + + + + Verify Permissions on /etc/at.allow file + + ocil:ssg-file_permissions_at_allow_action:testaction:1 + + + + Enable Kernel Parameter to Ignore ICMP Broadcast Echo Requests on IPv4 Interfaces + + ocil:ssg-sysctl_net_ipv4_icmp_echo_ignore_broadcasts_action:testaction:1 + + + + Ensure there are no legacy + NIS entries in /etc/shadow + + ocil:ssg-no_legacy_plus_entries_etc_shadow_action:testaction:1 + + + + Disable Accepting ICMP Redirects for All IPv6 Interfaces + + ocil:ssg-sysctl_net_ipv6_conf_all_accept_redirects_action:testaction:1 + + + + Encrypt Partitions + + ocil:ssg-encrypt_partitions_action:testaction:1 + + + + Set Lockout Time for Failed Password Attempts + + ocil:ssg-accounts_passwords_pam_faillock_unlock_time_action:testaction:1 + + + + Disable CAN Support + + ocil:ssg-kernel_module_can_disabled_action:testaction:1 + + + + Enable checks on linked list manipulation + + ocil:ssg-kernel_config_debug_list_action:testaction:1 + + + + Disallow kernel profiling by unprivileged users + + ocil:ssg-sysctl_kernel_perf_event_paranoid_action:testaction:1 + + + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + + +Run the following command to determine if the dhcpd_use_ldap SELinux boolean is disabled: +$ getsebool dhcpd_use_ldap +If properly configured, the output should show the following: +dhcpd_use_ldap --> off + Is it the case that dhcpd_use_ldap is not disabled? + + + + + +Run the following command to determine the current status of the +sshd service: +$ sudo systemctl is-active sshd +If the service is running, it should return the following: active + Is it the case that sshd service is disabled? + + + + The runtime status of the net.ipv4.conf.all.log_martians kernel parameter can be queried +by running the following command: +$ sysctl net.ipv4.conf.all.log_martians +1. + + Is it the case that the correct value is not returned? + + + + To check the group ownership of /boot/grub2/grub.cfg, +run the command: +$ ls -lL /boot/grub2/grub.cfg +If properly configured, the output should indicate the following group-owner: +root + Is it the case that /boot/grub2/grub.cfg does not have a group owner of root? + + + + Verify the umask setting is configured correctly in the /etc/profile file +with the following command: +$ grep "umask" /etc/profile +umask + Is it the case that the value for the "umask" parameter is not "<sub idref="var_accounts_user_umask" />", +or the "umask" parameter is missing or is commented out? + + + + Run the following command to determine if the iprutils package is installed: +$ rpm -q iprutils + Is it the case that the package is installed? + + + + +To properly set the owner of /var/log/httpd, run the command: +$ sudo chown root /var/log/httpd + +To properly set the owner of /var/log/httpd/*, run the command: +$ sudo chown root /var/log/httpd/* + Is it the case that ? + + + + Run the following command to determine if the sendmail package is installed: +$ rpm -q sendmail + Is it the case that the package is installed? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_SLAB_FREELIST_HARDENED /boot/config.* + + For each kernel installed, a line with value "y" should be returned. + + Is it the case that the kernel was not built with the required value? + + + + To determine whether OpenSSL is wrapped by a shell function that ensures that every invocation +uses a SP800-90A compliant entropy source, +make sure that the /etc/profile.d/openssl-rand.sh file contents exactly match those +that are included in the rule's description. + Is it the case that there is no <tt>/etc/profile.d/openssl-rand.sh</tt> file, or its contents don't match those in the description? + + + + +Run the following command to determine if the git_system_use_nfs SELinux boolean is disabled: +$ getsebool git_system_use_nfs +If properly configured, the output should show the following: +git_system_use_nfs --> off + Is it the case that git_system_use_nfs is not disabled? + + + + Verify the pam_faillock.so module is present in the "/etc/pam.d/system-auth" file: + +$ sudo grep pam_faillock.so /etc/pam.d/system-auth + +auth required pam_faillock.so preauth +auth required pam_faillock.so authfail +account required pam_faillock.so + Is it the case that the pam_faillock.so module is not present in the "/etc/pam.d/system-auth" file with the "preauth" line listed before pam_unix.so? + + + + +Verify that the libuser is set to encrypt password with a FIPS 140-2 approved cryptographic hashing algorithm. + +Check the hashing algorithm that is being used to hash passwords with the following command: + +$ sudo grep -i crypt_style /etc/libuser.conf + +crypt_style = sha512 + Is it the case that crypt_style is not set to sha512? + + + + To determine if the system is configured to audit successful calls +to the open system call, run the following command: +$ sudo grep "open" /etc/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + To verify that auditing of privileged command use is configured, run the +following command: +$ sudo grep newuidmap /etc/audit/audit.rules /etc/audit/rules.d/* +It should return a relevant line in the audit rules. + Is it the case that the command does not return a line, or the line is commented out? + + + + The existence of the file /etc/hosts.equiv or a file named +.rhosts inside a user home directory indicates the presence +of an Rsh trust relationship. + Is it the case that these files exist? + + + + Verify that sshd isn't configured to ignore the system wide cryptographic policy. + +Check that the CRYPTO_POLICY variable is not set or is commented out in the +/etc/sysconfig/sshd. + +Run the following command: + +$ sudo grep CRYPTO_POLICY /etc/sysconfig/sshd + Is it the case that the CRYPTO_POLICY variable is set or is not commented out in the /etc/sysconfig/sshd? + + + + +Run the following command to determine if the haproxy_connect_any SELinux boolean is disabled: +$ getsebool haproxy_connect_any +If properly configured, the output should show the following: +haproxy_connect_any --> off + Is it the case that haproxy_connect_any is not disabled? + + + + To verify if LogFormat is configured correctly in +/etc/httpd/conf/httpd.conf, run the following command: +$ grep -i logformat /etc/httpd/conf/httpd.conf +The output should contain the following: +LogFormat "a %A %h %H %l %m %s %t %u %U \"%{Referer}i\" \"%{User-Agent}i\"" combined + Is it the case that it is not? + + + + Verify that Red Hat Enterprise Linux 8 is configured to audit the execution of the "chage" command with the following command: + +$ sudo auditctl -l | grep chage + +-a always,exit -F path=/usr/bin/chage -F perm=x -F auid>=1000 -F auid!=unset -k privileged-chage + Is it the case that the command does not return a line, or the line is commented out? + + + + To verify that web content directories should not be shared anonymously over +remote filesystems such as nfs and smb, inspect each instance +of DocumentRoot and serverRoot and verify that no entry in +/etc/fstab exists or no remote filesystem process is running for +any instance. +$ ps -ef | grep "nfs\|smb" + Is it the case that it is not? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_LEGACY_VSYSCALL_NONE /boot/config.* + + For each kernel installed, a line with value "y" should be returned. + + Is it the case that the kernel was not built with the required value? + + + + Run the following command to determine if the abrt-cli package is installed: +$ rpm -q abrt-cli + Is it the case that the package is installed? + + + + To verify that McAfee VirusScan Enterprise for Linux is installed +and running, run the following command(s): +$ sudo systemctl status nails +$ rpm -q McAfeeVSEForLinux + Is it the case that virus scanning software is not installed or running? + + + + To determine if the system is configured to audit unsuccessful calls +to the fchmod system call, run the following command: +$ sudo grep "fchmod" /etc/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + To check the group ownership of /etc/issue, +run the command: +$ ls -lL /etc/issue +If properly configured, the output should indicate the following group-owner: +root + Is it the case that /etc/issue does not have a group owner of root? + + + + +Run the following command to determine if the smbd_anon_write SELinux boolean is disabled: +$ getsebool smbd_anon_write +If properly configured, the output should show the following: +smbd_anon_write --> off + Is it the case that smbd_anon_write is not disabled? + + + + +Run the following command to determine if the httpd_anon_write SELinux boolean is disabled: +$ getsebool httpd_anon_write +If properly configured, the output should show the following: +httpd_anon_write --> off + Is it the case that httpd_anon_write is not disabled? + + + + To check that the rhsmcertd service is disabled in system boot configuration, +run the following command: +$ sudo systemctl is-enabled rhsmcertd +Output should indicate the rhsmcertd service has either not been installed, +or has been disabled at all runlevels, as shown in the example below: +$ sudo systemctl is-enabled rhsmcertd disabled + +Run the following command to verify rhsmcertd is not active (i.e. not running) through current runtime configuration: +$ sudo systemctl is-active rhsmcertd + +If the service is not running the command will return the following output: +inactive + +The service will also be masked, to check that the rhsmcertd is masked, run the following command: +$ sudo systemctl show rhsmcertd | grep "LoadState\|UnitFileState" + +If the service is masked the command will return the following outputs: + +LoadState=masked + +UnitFileState=masked + Is it the case that the "rhsmcertd" is loaded and not masked? + + + + +Run the following command to determine if the logwatch_can_network_connect_mail SELinux boolean is disabled: +$ getsebool logwatch_can_network_connect_mail +If properly configured, the output should show the following: +logwatch_can_network_connect_mail --> off + Is it the case that logwatch_can_network_connect_mail is not disabled? + + + + Inspect the file /etc/firewalld/firewalld.conf to determine +the default zone for the firewalld. It should be set to DefaultZone=drop: +$ sudo grep DefaultZone /etc/firewalld/firewalld.conf + Is it the case that the default zone is not set to DROP? + + + + +Run the following command to determine if the httpd_can_connect_mythtv SELinux boolean is disabled: +$ getsebool httpd_can_connect_mythtv +If properly configured, the output should show the following: +httpd_can_connect_mythtv --> off + Is it the case that httpd_can_connect_mythtv is not disabled? + + + + To check that the netconsole service is disabled in system boot configuration, +run the following command: +$ sudo systemctl is-enabled netconsole +Output should indicate the netconsole service has either not been installed, +or has been disabled at all runlevels, as shown in the example below: +$ sudo systemctl is-enabled netconsole disabled + +Run the following command to verify netconsole is not active (i.e. not running) through current runtime configuration: +$ sudo systemctl is-active netconsole + +If the service is not running the command will return the following output: +inactive + +The service will also be masked, to check that the netconsole is masked, run the following command: +$ sudo systemctl show netconsole | grep "LoadState\|UnitFileState" + +If the service is masked the command will return the following outputs: + +LoadState=masked + +UnitFileState=masked + Is it the case that the "netconsole" is loaded and not masked? + + + + +Run the following command to determine if the gpg_web_anon_write SELinux boolean is disabled: +$ getsebool gpg_web_anon_write +If properly configured, the output should show the following: +gpg_web_anon_write --> off + Is it the case that gpg_web_anon_write is not disabled? + + + + If the system is not configured to audit time changes, this is a finding. +If the system is 64-bit only, this is not applicable +ocil: | +To determine if the system is configured to audit calls to the +stime system call, run the following command: +$ sudo grep "stime" /etc/audit/audit.* +If the system is configured to audit this activity, it will return a line. + Is it the case that no line is returned? + + + + The file /etc/cron.deny should not exist. +This can be checked by runnig the following + +stat /etc/cron.deny + +and the output should be + +stat: cannot stat `/etc/cron.deny': No such file or directory + + Is it the case that the file /etc/cron.deny exists? + + + + +Run the following command to determine if the httpd_tty_comm SELinux boolean is disabled: +$ getsebool httpd_tty_comm +If properly configured, the output should show the following: +httpd_tty_comm --> off + Is it the case that httpd_tty_comm is not disabled? + + + + Run the following command to determine if the talk package is installed: +$ rpm -q talk + Is it the case that the package is installed? + + + + To verify that Linux Audit logging is enabled for the USBGuard daemon, +run the following command: +$ sudo grep AuditBackend /etc/usbguard/usbguard-daemon.conf +The output should be +AuditBackend=LinuxAudit + Is it the case that AuditBackend is not set to LinuxAudit? + + + + To verify that tmux is not listed as allowed shell on the system +run the following command: +$ grep 'tmux$' /etc/shells +The output should be empty. + Is it the case that tmux is listed in /etc/shells? + + + + Verify the "/etc/security/faillock.conf" file is configured to log user name information when unsuccessful logon attempts occur: + +$ sudo grep audit /etc/security/faillock.conf + +audit + Is it the case that the "audit" option is not set, is missing or commented out? + + + + To check if authentication is required for single-user mode, run the following command: +$ grep sulogin /usr/lib/systemd/system/rescue.service +The output should be similar to the following, and the line must begin with +ExecStart and /usr/lib/systemd/systemd-sulogin-shell. + ExecStart=-/usr/lib/systemd/systemd-sulogin-shell rescue + Is it the case that the output is different? + + + + Run the following command to ensure the default FORWARD policy is DROP: +grep ":FORWARD" /etc/sysconfig/iptables +The output should be similar to the following: +$ sudo grep ":FORWARD" /etc/sysconfig/iptables +:FORWARD DROP [0:0 + Is it the case that the default policy for the FORWARD chain is not set to DROP? + + + + +Run the following command to determine if the httpd_unified SELinux boolean is disabled: +$ getsebool httpd_unified +If properly configured, the output should show the following: +httpd_unified --> off + Is it the case that httpd_unified is not disabled? + + + + To check that the dovecot service is disabled in system boot configuration, +run the following command: +$ sudo systemctl is-enabled dovecot +Output should indicate the dovecot service has either not been installed, +or has been disabled at all runlevels, as shown in the example below: +$ sudo systemctl is-enabled dovecot disabled + +Run the following command to verify dovecot is not active (i.e. not running) through current runtime configuration: +$ sudo systemctl is-active dovecot + +If the service is not running the command will return the following output: +inactive + +The service will also be masked, to check that the dovecot is masked, run the following command: +$ sudo systemctl show dovecot | grep "LoadState\|UnitFileState" + +If the service is masked the command will return the following outputs: + +LoadState=masked + +UnitFileState=masked + Is it the case that the "dovecot" is loaded and not masked? + + + + Verify the system commands contained in the following directories have mode "755" or less permissive with the following command: + +$ sudo find -L /bin /sbin /usr/bin /usr/sbin /usr/libexec /usr/local/bin /usr/local/sbin -perm /022 -exec ls -l {} \; + Is it the case that any system commands are found to be group-writable or world-writable? + + + + Verify Red Hat Enterprise Linux 8 generates an audit record for unsuccessful attempts to create files using the open system call with O_CREAT flag. + +If the auditd daemon is configured to use the "augenrules" program to read audit rules during daemon startup (the default), run the following command: + +$ sudo grep -r open /etc/audit/rules.d + +If the auditd daemon is configured to use the "auditctl" utility to read audit rules during daemon startup, run the following command: + +$ sudo grep open /etc/audit/audit.rules + +The output should be the following: + +-a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + Is it the case that the command does not return a line, or the line is commented out? + + + + To verify that all user initialization files have a mode of 0740 or +less permissive, run the following command: +$ sudo find /home -type f -name '\.*' \( -perm -0002 -o -perm -0020 \) +There should be no output. + Is it the case that they are not 0740 or more permissive? + + + + +Run the following command to determine if the use_samba_home_dirs SELinux boolean is disabled: +$ getsebool use_samba_home_dirs +If properly configured, the output should show the following: +use_samba_home_dirs --> off + Is it the case that use_samba_home_dirs is not disabled? + + + + Run the following command to determine if the sudo package is installed: $ rpm -q sudo + Is it the case that the package is not installed? + + + + To ensure the MaxAuthTries parameter is set, run the following command: +$ sudo grep MaxAuthTries /etc/ssh/sshd_config +If properly configured, output should be: +MaxAuthTries + Is it the case that it is commented out or not configured properly? + + + + +Run the following command to determine if the mozilla_read_content SELinux boolean is disabled: +$ getsebool mozilla_read_content +If properly configured, the output should show the following: +mozilla_read_content --> off + Is it the case that mozilla_read_content is not disabled? + + + + To determine how the SSH daemon's PermitEmptyPasswords option is set, run the following command: + +$ sudo grep -i PermitEmptyPasswords /etc/ssh/sshd_config + +If a line indicating no is returned, then the required value is set. + + Is it the case that the required value is not set? + + + + To ensure ClientAliveInterval is set correctly, run the following command: + +$ sudo grep ClientAliveCountMax /etc/ssh/sshd_config + +If properly configured, the output should be: +ClientAliveCountMax 0 + +In this case, the SSH idle timeout occurs precisely when +the ClientAliveInterval is set. + Is it the case that it is commented out or not configured properly? + + + + Run the following command to determine if the rear package is installed: $ rpm -q rear + Is it the case that the package is not installed? + + + + To verify that a remote NTP service is configured for time synchronization, +open the following file: +/etc/ntp.conf +In the file, there should be a section similar to the following: +server ntpserver + Is it the case that this is not the case? + + + + +Run the following command to determine if the httpd_use_sasl SELinux boolean is disabled: +$ getsebool httpd_use_sasl +If properly configured, the output should show the following: +httpd_use_sasl --> off + Is it the case that httpd_use_sasl is not disabled? + + + + Verify the system-wide shared library files are group-owned by "root" with the following command: + +$ sudo find -L /lib /lib64 /usr/lib /usr/lib64 ! -group root -exec ls -l {} \; + Is it the case that any system wide shared library file is returned and is not group-owned by a required system account? + + + + Verify the operating system requires re-authentication +when using the "sudo" command to elevate privileges, run the following command: +sudo grep -ri '^Defaults.*timestamp_timeout' /etc/sudoers /etc/sudoers.d +The output should be: +/etc/sudoers:Defaults timestamp_timeout=0 or "timestamp_timeout" is set to a positive number. +If conflicting results are returned, this is a finding. + Is it the case that timestamp_timeout is not set with the appropriate value for sudo? + + + + To verify that only security updates will be automatically installed by dnf-automatic, run the following command: +$ sudo grep upgrade_type /etc/dnf/automatic.conf +The output should return the following: +upgrade_type = security + Is it the case that the upgrade_type is not set to security? + + + + Inspect /etc/audit/audisp-remote.conf and locate the following line to +determine if the system is configured to perform a correct action according to the policy: +$ sudo grep -i network_failure_action /etc/audit/audisp-remote.conf +The output should return: +network_failure_action = + Is it the case that the system is not configured to switch to single user mode for corrective action? + + + + To verify that auditing of privileged command use is configured, run the +following command: +$ sudo grep usernetctl /etc/audit/audit.rules /etc/audit/rules.d/* +It should return a relevant line in the audit rules. + Is it the case that the command does not return a line, or the line is commented out? + + + + To determine if !authenticate has not been configured for sudo, run the following command: +$ sudo grep -r \!authenticate /etc/sudoers /etc/sudoers.d/ +The command should return no output. + Is it the case that !authenticate is specified in the sudo config files? + + + + Run the following command to determine if the libpwquality package is installed: +$ rpm -q libpwquality + Is it the case that the package is not installed? + + + + The runtime status of the kernel.sysrq kernel parameter can be queried +by running the following command: +$ sysctl kernel.sysrq +0. + + Is it the case that the correct value is not returned? + + + + +Run the following command to determine if the httpd_can_connect_ldap SELinux boolean is disabled: +$ getsebool httpd_can_connect_ldap +If properly configured, the output should show the following: +httpd_can_connect_ldap --> off + Is it the case that httpd_can_connect_ldap is not disabled? + + + + +If the system is configured to prevent the loading of the firewire-core kernel module, +it will contain lines inside any file in /etc/modprobe.d or the deprecated /etc/modprobe.conf. +These lines instruct the module loading system to run another program (such as /bin/true) upon a module install event. + +These lines can also instruct the module loading system to ignore the firewire-core kernel module via blacklist keyword. + +Run the following command to search for such lines in all files in /etc/modprobe.d and the deprecated /etc/modprobe.conf: +$ grep -r firewire-core /etc/modprobe.conf /etc/modprobe.d + Is it the case that no line is returned? + + + + Verify the default target is multi-user, run the following command: +$ systemctl get-default +multi-user.target + Is it the case that the system default target is not set to "multi-user.target" and the Information System Security Officer (ISSO) lacks a documented requirement for a graphical user interface? + + + + Verify that there are no wireless interfaces configured on the system +with the following command: + +$ sudo nmcli device +The output should only contain wireless devices in unavailable state, like in the +following example: +wlp0s20f3 wifi unavailable -- + Is it the case that wireless interfaces are not active? + + + + +Run the following command to determine if the postgresql_can_rsync SELinux boolean is disabled: +$ getsebool postgresql_can_rsync +If properly configured, the output should show the following: +postgresql_can_rsync --> off + Is it the case that postgresql_can_rsync is not disabled? + + + + Run the following command to determine if the openssh-server package is installed: $ rpm -q openssh-server + Is it the case that the package is not installed? + + + + +Run the following command to determine if the httpd_use_cifs SELinux boolean is disabled: +$ getsebool httpd_use_cifs +If properly configured, the output should show the following: +httpd_use_cifs --> off + Is it the case that httpd_use_cifs is not disabled? + + + + If the system does not have SELinux enabled and enforcing a targeted policy, or if the +pam_faillock.so module is not configured for use, this requirement is not applicable. + +Verify the location of the non-default tally directory for the pam_faillock.so module with +the following command: + +$ sudo grep -w dir /etc/security/faillock.conf + +dir = /var/log/faillock + +Check the security context type of the non-default tally directory with the following command: + +$ sudo ls -Zd /var/log/faillock + +unconfined_u:object_r:faillog_t:s0 /var/log/faillock + Is it the case that the security context type of the non-default tally directory is not "faillog_t"? + + + + To verify the home directory ownership, run the following command: +# ls -ld $(awk -F: '($3>=1000)&&($7 !~ /nologin/){print $6}' /etc/passwd) + Is it the case that the user ownership is incorrect? + + + + Inspect the form of default GRUB 2 command line for the Linux operating system +in /etc/default/grub. If it includes slab_nomerge=yes, +then the parameter will be configured for newly installed kernels. +First check if the GRUB recovery is enabled: +$ sudo grep 'GRUB_DISABLE_RECOVERY' /etc/default/grub +If this option is set to true, then check that a line is output by the following command: +$ sudo grep 'GRUB_CMDLINE_LINUX_DEFAULT.*slab_nomerge=yes.*' /etc/default/grub +If the recovery is disabled, check the line with +$ sudo grep 'GRUB_CMDLINE_LINUX.*slab_nomerge=yes.*' /etc/default/grub.Moreover, command line parameters for currently installed kernels should be checked as well. +Run the following command: +$ sudo grubby --info=ALL | grep args | grep -v 'slab_nomerge=yes' +The command should not return any output. + Is it the case that merging of slabs with similar size is enabled? + + + + Verify Red Hat Enterprise Linux 8 generates an audit record for unsuccessful attempts to use the unlinkat system call. + +If the auditd daemon is configured to use the "augenrules" program to to read audit rules during daemon startup (the default), run the following command: + +$ sudo grep -r unlinkat /etc/audit/rules.d + +If the auditd daemon is configured to use the "auditctl" utility to read audit rules during daemon startup, run the following command: + +$ sudo grep unlinkat /etc/audit/audit.rules + +The output should be the following: + +-a always,exit -F arch=b32 -S unlinkat -F exit=-EPERM -F auid>=1000 -F auid!=unset -k unsuccessful-delete +-a always,exit -F arch=b64 -S unlinkat -F exit=-EPERM -F auid>=1000 -F auid!=unset -k unsuccessful-delete +-a always,exit -F arch=b32 -S unlinkat -F exit=-EACCES -F auid>=1000 -F auid!=unset -k unsuccessful-delete +-a always,exit -F arch=b64 -S unlinkat -F exit=-EACCES -F auid>=1000 -F auid!=unset -k unsuccessful-delete + Is it the case that the command does not return a line, or the line is commented out? + + + + To verify that packages comprising the available updates will be automatically installed by dnf-automatic, run the following command: +$ sudo grep apply_updates /etc/dnf/automatic.conf +The output should return the following: +apply_updates = yes + Is it the case that apply_updates is not set to yes? + + + + Run the following command to determine if the abrt package is installed: +$ rpm -q abrt + Is it the case that the package is installed? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_IPV6 /boot/config.* + + Configs with value 'n' are not explicitly set in the file, so either commented lines or no + lines should be returned. + + Is it the case that the kernel was not built with the required value? + + + + To determine if the system is configured to audit successful calls +to the openat system call, run the following command: +$ sudo grep "openat" /etc/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + To verify the system is not configured to use a boot loader on removable media, +run the following command: +$ sudo grep "set root='hd0" /boot/efi/EFI/redhat/grub.cfg +The output should return something similar to: +set root='hd0,msdos1' +usb0, cd, fd0, etc. are some examples of removeable +media which should not exist in the line: +set root='hd0,msdos1' + Is it the case that it is not? + + + + +Run the following command to determine if the httpd_use_openstack SELinux boolean is disabled: +$ getsebool httpd_use_openstack +If properly configured, the output should show the following: +httpd_use_openstack --> off + Is it the case that httpd_use_openstack is not disabled? + + + + Verify that a separate file system/partition has been created for /var with the following command: + +$ mountpoint /var + + Is it the case that "/var is not a mountpoint" is returned? + + + + Verify Red Hat Enterprise Linux 8 generates an audit record for unsuccessful attempts to use the open system call. + +If the auditd daemon is configured to use the "augenrules" program to to read audit rules during daemon startup (the default), run the following command: + +$ sudo grep -r open /etc/audit/rules.d + +If the auditd daemon is configured to use the "auditctl" utility to read audit rules during daemon startup, run the following command: + +$ sudo grep open /etc/audit/audit.rules + +The output should be the following: + +-a always,exit -F arch=b32 -S open -F exit=-EPERM -F auid>=1000 -F auid!=unset -k access +-a always,exit -F arch=b64 -S open -F exit=-EPERM -F auid>=1000 -F auid!=unset -k access +-a always,exit -F arch=b32 -S open -F exit=-EACCES -F auid>=1000 -F auid!=unset -k access +-a always,exit -F arch=b64 -S open -F exit=-EACCES -F auid>=1000 -F auid!=unset -k access + Is it the case that the command does not return a line, or the line is commented out? + + + + +Run the following command to determine if the puppetagent_manage_all_files SELinux boolean is disabled: +$ getsebool puppetagent_manage_all_files +If properly configured, the output should show the following: +puppetagent_manage_all_files --> off + Is it the case that puppetagent_manage_all_files is not disabled? + + + + Verify the USBGuard has a policy configured with the following command: + +$ sudo usbguard list-rules + +If the command does not return results or an error is returned, ask the SA to indicate how unauthorized peripherals are being blocked. + Is it the case that there is no evidence that unauthorized peripherals are being blocked before establishing a connection? + + + + Inspect /etc/audit/auditd.conf and locate the following line to +determine if the system is configured to email the administrator when +disk space is starting to run low: +$ sudo grep space_left_action /etc/audit/auditd.conf +space_left_action +Acceptable values are email, suspend, single, and halt. + Is it the case that the system is not configured to send an email to the system administrator when disk space is starting to run low? + + + + +Run the following command to determine if the xdm_sysadm_login SELinux boolean is disabled: +$ getsebool xdm_sysadm_login +If properly configured, the output should show the following: +xdm_sysadm_login --> off + Is it the case that xdm_sysadm_login is not disabled? + + + + Verify the nosuid option is configured for the /var/tmp mount point, + run the following command: + $ sudo mount | grep '\s/var/tmp\s' + . . . /var/tmp . . . nosuid . . . + + Is it the case that the "/var/tmp" file system does not have the "nosuid" option set? + + + + If the device or Red Hat Enterprise Linux 8 does not have a camera installed, this requirement is not applicable. + +This requirement is not applicable to mobile devices (smartphones and tablets), where the use of the camera is a local AO decision. + +This requirement is not applicable to dedicated VTC suites located in approved VTC locations that are centrally managed. + +For an external camera, if there is not a method for the operator to manually disconnect the camera at the end of collaborative computing sessions, this is a finding. + +For a built-in camera, the camera must be protected by a camera cover (e.g., laptop camera cover slide) when not in use. If the built-in camera is not protected with a camera cover, or is not physically disabled, this is a finding. + +If the camera is not disconnected, covered, or physically disabled, determine if it is being disabled via software with the following commands: + +Verify the operating system disables the ability to load the uvcvideo kernel module. + +$ sudo grep -r uvcvideo /etc/modprobe.d/* | grep "/bin/true" + +install uvcvideo /bin/true + Is it the case that the command does not return any output, or the line is commented out, and the collaborative computing device has not been authorized for use? + + + + +Run the following command to determine if the mcelog_client SELinux boolean is disabled: +$ getsebool mcelog_client +If properly configured, the output should show the following: +mcelog_client --> off + Is it the case that mcelog_client is not disabled? + + + + + +Run the following command to determine the current status of the +systemd-journald service: +$ sudo systemctl is-active systemd-journald +If the service is running, it should return the following: active + Is it the case that the systemd-journald service is not running? + + + + Verify the noexec option is configured for the /var/log/audit mount point, + run the following command: + $ sudo mount | grep '\s/var/log/audit\s' + . . . /var/log/audit . . . noexec . . . + + Is it the case that the "/var/log/audit" file system does not have the "noexec" option set? + + + + Check whether the minimum time period between password changes for each user account is one day or greater. + +$ sudo awk -F: '$4 < 1 {print $1 " " $4}' /etc/shadow + Is it the case that any results are returned that are not associated with a system account? + + + + Verify that the system is not accepting "rsyslog" messages from other systems unless it is documented as a log aggregation server. +Display the contents of the configuration file: +cat /etc/rsyslog.conf +$ModLoad imtcp +$InputTCPServerRun port +$ModLoad imudp +$UDPServerRun port +$ModLoad imrelp +$InputRELPServerRun port + +If any of the above modules are being loaded in the "/etc/rsyslog.conf" file, ask to see the documentation for the system being used for log aggregation. + Is it the case that rsyslog accepts remote messages and is not documented as a log aggregation system? + + + + The runtime status of the vm.mmap_min_addr kernel parameter can be queried +by running the following command: +$ sysctl vm.mmap_min_addr +65536. + + Is it the case that the correct value is not returned? + + + + + +Run the following command to determine the current status of the +fapolicyd service: +$ sudo systemctl is-active fapolicyd +If the service is running, it should return the following: active + Is it the case that the service is not enabled? + + + + To check the permissions of /etc/gshadow, +run the command: +$ ls -l /etc/gshadow +If properly configured, the output should indicate the following permissions: +---------- + Is it the case that /etc/gshadow does not have unix mode ----------? + + + + +Run the following command to determine if the httpd_enable_cgi SELinux boolean is disabled: +$ getsebool httpd_enable_cgi +If properly configured, the output should show the following: +httpd_enable_cgi --> off + Is it the case that httpd_enable_cgi is not disabled? + + + + To verify that auditing is configured for system administrator actions, run the following command: +$ sudo auditctl -l | grep "watch=/etc/sudoers\|watch=/etc/sudoers.d\|-w /etc/sudoers\|-w /etc/sudoers.d" + Is it the case that there is not output? + + + + To check the permissions of /etc/issue, +run the command: +$ ls -l /etc/issue +If properly configured, the output should indicate the following permissions: +-rw-r--r-- + Is it the case that /etc/issue does not have unix mode -rw-r--r--? + + + + To determine if NOEXEC has been configured for sudo, run the following command: +$ sudo grep -ri "^[\s]*Defaults.*\bnoexec\b.*" /etc/sudoers /etc/sudoers.d/ +The command should return a matching output. + Is it the case that noexec is not enabled in sudo? + + + + Verify that core dumps are disabled for all users, run the following command: +$ grep core /etc/security/limits.conf +* hard core 0 + Is it the case that the "core" item is missing, commented out, or the value is anything other than "0" and the need for core dumps is not documented with the Information System Security Officer (ISSO) as an operational requirement for all domains that have the "core"? + + + + +Run the following command to determine if the daemons_use_tty SELinux boolean is disabled: +$ getsebool daemons_use_tty +If properly configured, the output should show the following: +daemons_use_tty --> off + Is it the case that daemons_use_tty is not disabled? + + + + +Run the following command to determine if the cluster_manage_all_files SELinux boolean is disabled: +$ getsebool cluster_manage_all_files +If properly configured, the output should show the following: +cluster_manage_all_files --> off + Is it the case that cluster_manage_all_files is not disabled? + + + + To determine if the system is configured to audit successful calls +to the rename system call, run the following command: +$ sudo grep "rename" /etc/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + +Run the following command to determine if the selinuxuser_execmod SELinux boolean is enabled: +$ getsebool selinuxuser_execmod +If properly configured, the output should show the following: +selinuxuser_execmod --> on + Is it the case that selinuxuser_execmod is not enabled? + + + + Run the following command to determine if the sssd-ipa package is installed: $ rpm -q sssd-ipa + Is it the case that the package is not installed? + + + + Verify the noexec option is configured for the /var mount point, + run the following command: + $ sudo mount | grep '\s/var\s' + . . . /var . . . noexec . . . + + Is it the case that the "/var" file system does not have the "noexec" option set? + + + + To determine that periodic AIDE execution has been scheduled, run the following command: +$ grep aide /etc/crontab +The output should return something similar to the following: +05 4 * * * root /usr/sbin/aide --check | /bin/mail -s "$(hostname) - AIDE Integrity Check" root@localhost +The email address that the notifications are sent to can be changed by overriding +. + Is it the case that AIDE has not been configured or has not been configured to notify personnel of scan details? + + + + +Run the following command to determine if the antivirus_use_jit SELinux boolean is disabled: +$ getsebool antivirus_use_jit +If properly configured, the output should show the following: +antivirus_use_jit --> off + Is it the case that antivirus_use_jit is not disabled? + + + + Run the following command to determine if the nss-tools package is installed: $ rpm -q nss-tools + Is it the case that the package is not installed? + + + + +Run the following command to determine if the domain_fd_use SELinux boolean is enabled: +$ getsebool domain_fd_use +If properly configured, the output should show the following: +domain_fd_use --> on + Is it the case that domain_fd_use is not enabled? + + + + To ensure the splash screen is configured not to show user name, run the following command: +$ gsettings get org.gnome.desktop.screensaver show-full-name-in-top-bar +If properly configured, the output should be false. +To ensure that users cannot enable user name on the lock screen, run the following: +$ grep show-full-name-in-top-bar /etc/dconf/db/local.d/locks/* +If properly configured, the output should be /org/gnome/desktop/screensaver/show-full-name-in-top-bar + Is it the case that it is not set or configured properly? + + + + +Run the following command to determine if the cvs_read_shadow SELinux boolean is disabled: +$ getsebool cvs_read_shadow +If properly configured, the output should show the following: +cvs_read_shadow --> off + Is it the case that cvs_read_shadow is not disabled? + + + + Inspect the form of default GRUB 2 command line for the Linux operating system +in /etc/default/grub. If it includes l1tf=, +then the parameter will be configured for newly installed kernels. +First check if the GRUB recovery is enabled: +$ sudo grep 'GRUB_DISABLE_RECOVERY' /etc/default/grub +If this option is set to true, then check that a line is output by the following command: +$ sudo grep 'GRUB_CMDLINE_LINUX_DEFAULT.*l1tf=.*' /etc/default/grub +If the recovery is disabled, check the line with +$ sudo grep 'GRUB_CMDLINE_LINUX.*l1tf=.*' /etc/default/grub.Moreover, command line parameters for currently installed kernels should be checked as well. +Run the following command: +$ sudo grubby --info=ALL | grep args | grep -v 'l1tf=' +The command should not return any output. + Is it the case that l1tf mitigations are not configured appropriately? + + + + To check the permissions of /etc/cron.allow, +run the command: +$ ls -l /etc/cron.allow +If properly configured, the output should indicate the following permissions: +-rw------- + Is it the case that /etc/cron.allow does not have unix mode -rw-------? + + + + Run the following command to determine if the rng-tools package is installed: $ rpm -q rng-tools + Is it the case that the package is not installed? + + + + +Run the following command to determine if the cups_execmem SELinux boolean is disabled: +$ getsebool cups_execmem +If properly configured, the output should show the following: +cups_execmem --> off + Is it the case that cups_execmem is not disabled? + + + + +Run the following command to determine if the rsync_anon_write SELinux boolean is disabled: +$ getsebool rsync_anon_write +If properly configured, the output should show the following: +rsync_anon_write --> off + Is it the case that rsync_anon_write is not disabled? + + + + To verify the password reuse setting is compliant, run the following command: +$ grep remember /etc/pam.d/system-auth +The output should show the following at the end of the line: +remember= + Is it the case that the value of remember is not equal to or greater than the expected value? + + + + +Run the following command to determine if the pcp_bind_all_unreserved_ports SELinux boolean is disabled: +$ getsebool pcp_bind_all_unreserved_ports +If properly configured, the output should show the following: +pcp_bind_all_unreserved_ports --> off + Is it the case that pcp_bind_all_unreserved_ports is not disabled? + + + + +Run the following command to determine if the httpd_run_stickshift SELinux boolean is disabled: +$ getsebool httpd_run_stickshift +If properly configured, the output should show the following: +httpd_run_stickshift --> off + Is it the case that httpd_run_stickshift is not disabled? + + + + + +Run the following command to determine the current status of the +auditd service: +$ sudo systemctl is-active auditd +If the service is running, it should return the following: active + Is it the case that the auditd service is not running? + + + + If the system uses IPv6, this is not applicable. + +If the system is configured to prevent the usage of the ipv6 on +network interfaces, it will contain a line of the form: +net.ipv6.conf.default.disable_ipv6 = 1 +Such lines may be inside any file in the /etc/sysctl.d directory. +This permits insertion of the IPv6 kernel module (which other parts of the +system expect to be present), but otherwise keeps network interfaces +from using IPv6. Run the following command to search for such lines in all +files in /etc/sysctl.d: +$ grep -r ipv6 /etc/sysctl.d + Is it the case that the ipv6 support is disabled by default on network interfaces? + + + + To determine if NOPASSWD or !authenticate have been configured for +sudo, run the following command: +$ sudo grep -ri "nopasswd\|\!authenticate" /etc/sudoers /etc/sudoers.d/ +The command should return no output. + Is it the case that nopasswd and/or !authenticate is enabled in sudo? + + + + +Run the following command to determine if the httpd_use_gpg SELinux boolean is disabled: +$ getsebool httpd_use_gpg +If properly configured, the output should show the following: +httpd_use_gpg --> off + Is it the case that httpd_use_gpg is not disabled? + + + + Storing logs with persistent storage ensures they are available after a reboot or system crash. +Run the command below to verify that logs are being persistently stored to disk. + +grep "^\sStorage" /etc/systemd/journald.conf + +and it should return + +Storage=persistent + + Is it the case that is commented out or not configured correctly? + + + + +Run the following command to determine if the condor_tcp_network_connect SELinux boolean is disabled: +$ getsebool condor_tcp_network_connect +If properly configured, the output should show the following: +condor_tcp_network_connect --> off + Is it the case that condor_tcp_network_connect is not disabled? + + + + Verify that Red Hat Enterprise Linux 8 is configured to audit the execution of the "gpasswd" command with the following command: + +$ sudo auditctl -l | grep gpasswd + +-a always,exit -F path=/usr/bin/gpasswd -F perm=x -F auid>=1000 -F auid!=unset -k privileged-gpasswd + Is it the case that the command does not return a line, or the line is commented out? + + + + To check the ownership of /etc/group, +run the command: +$ ls -lL /etc/group +If properly configured, the output should indicate the following owner: +root + Is it the case that /etc/group does not have an owner of root? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_BUG /boot/config.* + + For each kernel installed, a line with value "y" should be returned. + + Is it the case that the kernel was not built with the required value? + + + + To verify that repo_gpgcheck is configured properly, run the following +command: +$ grep repo_gpgcheck /etc/yum.conf +The output should return something similar to: +repo_gpgcheck=1 + Is it the case that gpgcheck is not enabled or configured correctly to verify repository metadata? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_PAGE_POISONING_NO_SANITY /boot/config.* + + For each kernel installed, a line with value "y" should be returned. + + Is it the case that the kernel was not built with the required value? + + + + +Run the following command to determine if the httpd_enable_ftp_server SELinux boolean is disabled: +$ getsebool httpd_enable_ftp_server +If properly configured, the output should show the following: +httpd_enable_ftp_server --> off + Is it the case that httpd_enable_ftp_server is not disabled? + + + + +Run the following command to determine if the ftpd_connect_all_unreserved SELinux boolean is disabled: +$ getsebool ftpd_connect_all_unreserved +If properly configured, the output should show the following: +ftpd_connect_all_unreserved --> off + Is it the case that ftpd_connect_all_unreserved is not disabled? + + + + +Run the following command to determine if the postfix_local_write_mail_spool SELinux boolean is enabled: +$ getsebool postfix_local_write_mail_spool +If properly configured, the output should show the following: +postfix_local_write_mail_spool --> on + Is it the case that postfix_local_write_mail_spool is not enabled? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_SECURITY_WRITABLE_HOOKS /boot/config.* + + For each kernel installed, a line with value "y" should be returned. + + Is it the case that the kernel was not built with the required value? + + + + To verify that Audit Daemon is configured to record the hostname +in audit events, run the following command: +$ sudo grep name_format /etc/audit/auditd.conf +The output should return the following: +name_format = hostname + Is it the case that name_format isn't set to hostname? + + + + To determine if the system is configured to audit calls to the +fchown system call, run the following command: +$ sudo grep "fchown" /etc/audit/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + To preclude access to the servers root directory, ensure the following +directive is in the httpd.conf file. This entry will also stop users +from setting up .htaccess files which can override security features +configured in /etc/httpd/conf/httpd.conf. +AllowOverride none + Is it the case that it is not? + + + + To check the permissions of /etc/group, +run the command: +$ ls -l /etc/group +If properly configured, the output should indicate the following permissions: +-rw-r--r-- + Is it the case that /etc/group does not have unix mode -rw-r--r--? + + + + +Run the following command to determine if the nfsd_anon_write SELinux boolean is disabled: +$ getsebool nfsd_anon_write +If properly configured, the output should show the following: +nfsd_anon_write --> off + Is it the case that nfsd_anon_write is not disabled? + + + + The runtime status of the net.ipv4.conf.all.secure_redirects kernel parameter can be queried +by running the following command: +$ sysctl net.ipv4.conf.all.secure_redirects +0. + + Is it the case that the correct value is not returned? + + + + +Run the following command to determine if the virt_use_comm SELinux boolean is disabled: +$ getsebool virt_use_comm +If properly configured, the output should show the following: +virt_use_comm --> off + Is it the case that virt_use_comm is not disabled? + + + + To determine if the system is configured to audit unsuccessful calls +to the lchown system call, run the following command: +$ sudo grep "lchown" /etc/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + +If the system is configured to prevent the loading of the dccp kernel module, +it will contain lines inside any file in /etc/modprobe.d or the deprecated /etc/modprobe.conf. +These lines instruct the module loading system to run another program (such as /bin/true) upon a module install event. + +These lines can also instruct the module loading system to ignore the dccp kernel module via blacklist keyword. + +Run the following command to search for such lines in all files in /etc/modprobe.d and the deprecated /etc/modprobe.conf: +$ grep -r dccp /etc/modprobe.conf /etc/modprobe.d + Is it the case that no line is returned? + + + + Inspect the form of default GRUB 2 command line for the Linux operating system +in /etc/default/grub. If it includes mce=0, +then the parameter will be configured for newly installed kernels. +First check if the GRUB recovery is enabled: +$ sudo grep 'GRUB_DISABLE_RECOVERY' /etc/default/grub +If this option is set to true, then check that a line is output by the following command: +$ sudo grep 'GRUB_CMDLINE_LINUX_DEFAULT.*mce=0.*' /etc/default/grub +If the recovery is disabled, check the line with +$ sudo grep 'GRUB_CMDLINE_LINUX.*mce=0.*' /etc/default/grub.Moreover, command line parameters for currently installed kernels should be checked as well. +Run the following command: +$ sudo grubby --info=ALL | grep args | grep -v 'mce=0' +The command should not return any output. + Is it the case that MCE tolerance is not set to zero? + + + + Run the following command to determine if the net-snmp package is installed: +$ rpm -q net-snmp + Is it the case that the package is installed? + + + + Verify Red Hat Enterprise Linux 8 is configured to limit the "pwquality" retry option to . + + +Check for the use of the "pwquality" retry option in the pwquality.conf file with the following command: +$ grep retry /etc/security/pwquality.conf + Is it the case that the value of "retry" is set to "0" or greater than "<sub idref="var_password_pam_retry" />", or is missing? + + + + +Run the following command to determine if the abrt_anon_write SELinux boolean is disabled: +$ getsebool abrt_anon_write +If properly configured, the output should show the following: +abrt_anon_write --> off + Is it the case that abrt_anon_write is not disabled? + + + + +Run the following command to determine if the selinuxuser_tcp_server SELinux boolean is disabled: +$ getsebool selinuxuser_tcp_server +If properly configured, the output should show the following: +selinuxuser_tcp_server --> off + Is it the case that selinuxuser_tcp_server is not disabled? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_STRICT_MODULE_RWX /boot/config.* + + For each kernel installed, a line with value "y" should be returned. + + Is it the case that the kernel was not built with the required value? + + + + Ensure that debug-shell service is not enabled with the following command: +grep systemd\.debug-shell=1 /boot/grub2/grubenv /etc/default/grub +If the command returns a line, it means that debug-shell service is being enabled. + Is it the case that the comand returns a line? + + + + The following command will list which files on the system have permissions different from what +is expected by the RPM database: +$ rpm -Va | awk '{ if (substr($0,2,1)=="M") print $NF }' + Is it the case that there is output? + + + + To verify ExecShield is enabled on 64-bit Red Hat Enterprise Linux 8 systems, +run the following command: +$ dmesg | grep '[NX|DX]*protection' +The output should not contain 'disabled by kernel command line option'. +Inspect the form of default GRUB 2 command line for the Linux operating system +in /etc/default/grub. If it includes noexec=off, +then the parameter will be configured for newly installed kernels. +First check if the GRUB recovery is enabled: +$ sudo grep 'GRUB_DISABLE_RECOVERY' /etc/default/grub +If this option is set to true, then check that a line is output by the following command: +$ sudo grep 'GRUB_CMDLINE_LINUX_DEFAULT.*noexec=off.*' /etc/default/grub +If the recovery is disabled, check the line with +$ sudo grep 'GRUB_CMDLINE_LINUX.*noexec=off.*' /etc/default/grub.Moreover, command line parameters for currently installed kernels should be checked as well. +Run the following command: +$ sudo grubby --info=ALL | grep args | grep -v 'noexec=off' +The command should not return any output. + Is it the case that ExecShield is not supported by the hardware, is not enabled, or has been disabled by the kernel configuration.? + + + + To determine if the system is configured to audit calls to the +rename system call, run the following command: +$ sudo grep "rename" /etc/audit/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + Verify that Red Hat Enterprise Linux 8 generates an audit record for all uses of the "umount" and system call. +To determine if the system is configured to audit calls to the +"umount" system call, run the following command: +$ sudo grep "umount" /etc/audit/audit.* +If the system is configured to audit this activity, it will return a line like the following. +-a always,exit -F arch=b32 -S umount -F auid>=1000 -F auid!=unset -k privileged-umount + Is it the case that the command does not return a line, or the line is commented out? + + + + To determine if the system is configured to audit successful calls +to the fremovexattr system call, run the following command: +$ sudo grep "fremovexattr" /etc/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + To determine if the system is configured to audit calls to the +chown system call, run the following command: +$ sudo grep "chown" /etc/audit/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + +Run the following command to determine if the racoon_read_shadow SELinux boolean is disabled: +$ getsebool racoon_read_shadow +If properly configured, the output should show the following: +racoon_read_shadow --> off + Is it the case that racoon_read_shadow is not disabled? + + + + + +To determine if firewalld is configured to allow access to ssh +on port 22/tcp, run the following command(s): + + firewall-cmd --list-ports + + + firewall-cmd --list-services + +If firewalld is configured to allow access through the firewall, something similar to the following will be output: + +If it is a service: +ssh + + +If it is a port: +22/tcp + + Is it the case that sshd service is disabled by firewall? + + + + To determine if the system is configured to audit calls to the +openat system call, run the following command: +$ sudo grep "openat" /etc/audit/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + To check the ownership of /etc/cron.d, +run the command: +$ ls -lL /etc/cron.d +If properly configured, the output should indicate the following owner: +root + Is it the case that /etc/cron.d does not have an owner of root? + + + + To verify that the Dracut FIPS module is enabled, run the following command: +grep "add_dracutmodules" /etc/dracut.conf.d/40-fips.conf +The output should look like this: +add_dracutmodules+=" fips " + Is it the case that the Dracut FIPS module is not enabled? + + + + The runtime status of the kernel.kptr_restrict kernel parameter can be queried +by running the following command: +$ sysctl kernel.kptr_restrict +The output of the command should indicate either: +kernel.kptr_restrict = 1 +or: +kernel.kptr_restrict = 2 +The output of the command should not indicate: +kernel.kptr_restrict = 0 + +The preferable way how to assure the runtime compliance is to have +correct persistent configuration, and rebooting the system. + +The persistent kernel parameter configuration is performed by specifying the appropriate +assignment in any file located in the /etc/sysctl.d directory. +Verify that there is not any existing incorrect configuration by executing the following command: +$ grep -r '^\s*kernel.kptr_restrict\s*=' /etc/sysctl.conf /etc/sysctl.d +The command should not find any assignments other than: +kernel.kptr_restrict = 1 +or: +kernel.kptr_restrict = 2 + +Conflicting assignments are not allowed. + Is it the case that the kernel.kptr_restrict is not set to 1 or 2 or is configured to be 0? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_MODULE_SIG_SHA512 /boot/config.* + + For each kernel installed, a line with value "y" should be returned. + + Is it the case that the kernel was not built with the required value? + + + + +Run the following command to determine if the xguest_use_bluetooth SELinux boolean is disabled: +$ getsebool xguest_use_bluetooth +If properly configured, the output should show the following: +xguest_use_bluetooth --> off + Is it the case that xguest_use_bluetooth is not disabled? + + + + +Run the following command to determine if the polipo_session_bind_all_unreserved_ports SELinux boolean is disabled: +$ getsebool polipo_session_bind_all_unreserved_ports +If properly configured, the output should show the following: +polipo_session_bind_all_unreserved_ports --> off + Is it the case that polipo_session_bind_all_unreserved_ports is not disabled? + + + + +Run the following command to get the current configured value for secure_mode_insmod +SELinux boolean: +$ getsebool secure_mode_insmod +The expected cofiguration is . +"on" means true, and "off" means false + Is it the case that secure_mode_insmod is not set as expected? + + + + To verify that McAfee HIPS is installed, run the following command(s): +$ rpm -q MFEhiplsm + Is it the case that the HBSS HIPS module is not installed? + + + + To determine if the system is configured to audit successful calls +to the open system call, run the following command: +$ sudo grep "open" /etc/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + To check that the autofs service is disabled in system boot configuration, +run the following command: +$ sudo systemctl is-enabled autofs +Output should indicate the autofs service has either not been installed, +or has been disabled at all runlevels, as shown in the example below: +$ sudo systemctl is-enabled autofs disabled + +Run the following command to verify autofs is not active (i.e. not running) through current runtime configuration: +$ sudo systemctl is-active autofs + +If the service is not running the command will return the following output: +inactive + +The service will also be masked, to check that the autofs is masked, run the following command: +$ sudo systemctl show autofs | grep "LoadState\|UnitFileState" + +If the service is masked the command will return the following outputs: + +LoadState=masked + +UnitFileState=masked + Is it the case that the "autofs" is loaded and not masked? + + + + To ensure that system location tracking is not active, run the following command: +$ gsettings get org.gnome.system.location enabled +$ gsettings get org.gnome.clocks geolocation +If properly configured, the output should be false. +To ensure that users cannot enable system location tracking, run the following: +$ grep location /etc/dconf/db/local.d/locks/* +If properly configured, the output should be +/org/gnome/system/location/enabled and /org/gnome/clocks/geolocation. + Is it the case that geolocation is enabled and not disabled? + + + + +Run the following command to determine if the irc_use_any_tcp_ports SELinux boolean is disabled: +$ getsebool irc_use_any_tcp_ports +If properly configured, the output should show the following: +irc_use_any_tcp_ports --> off + Is it the case that irc_use_any_tcp_ports is not disabled? + + + + To obtain a listing of all users, their UIDs, and their shells, run the +command: $ awk -F: '{print $1 ":" $3 ":" $7}' /etc/passwd Identify +the system accounts from this listing. These will primarily be the accounts +with UID numbers less than UID_MIN, other than root. Value of the UID_MIN +directive is set in /etc/login.defs configuration file. In the default +configuration UID_MIN is set to 1000. + Is it the case that any system account (other than root) has a login shell? + + + + +Run the following command to determine if the cluster_use_execmem SELinux boolean is disabled: +$ getsebool cluster_use_execmem +If properly configured, the output should show the following: +cluster_use_execmem --> off + Is it the case that cluster_use_execmem is not disabled? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_DEBUG_FS /boot/config.* + + Configs with value 'n' are not explicitly set in the file, so either commented lines or no + lines should be returned. + + Is it the case that the kernel was not built with the required value? + + + + To ensure that users cannot change how long until the screensaver locks, run the following: +$ grep lock-enabled /etc/dconf/db/local.d/locks/* +If properly configured, the output for lock-enabled should be /org/gnome/desktop/screensaver/lock-enabled + Is it the case that screensaver locking is not locked? + + + + +Run the following command to determine if the fips_mode SELinux boolean is enabled: +$ getsebool fips_mode +If properly configured, the output should show the following: +fips_mode --> on + Is it the case that fips_mode is not enabled? + + + + To check that the ypbind service is disabled in system boot configuration, +run the following command: +$ sudo systemctl is-enabled ypbind +Output should indicate the ypbind service has either not been installed, +or has been disabled at all runlevels, as shown in the example below: +$ sudo systemctl is-enabled ypbind disabled + +Run the following command to verify ypbind is not active (i.e. not running) through current runtime configuration: +$ sudo systemctl is-active ypbind + +If the service is not running the command will return the following output: +inactive + +The service will also be masked, to check that the ypbind is masked, run the following command: +$ sudo systemctl show ypbind | grep "LoadState\|UnitFileState" + +If the service is masked the command will return the following outputs: + +LoadState=masked + +UnitFileState=masked + Is it the case that the "ypbind" is loaded and not masked? + + + + +Run the following command to determine if the httpd_serve_cobbler_files SELinux boolean is disabled: +$ getsebool httpd_serve_cobbler_files +If properly configured, the output should show the following: +httpd_serve_cobbler_files --> off + Is it the case that httpd_serve_cobbler_files is not disabled? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_MODIFY_LDT_SYSCALL /boot/config.* + + Configs with value 'n' are not explicitly set in the file, so either commented lines or no + lines should be returned. + + Is it the case that the kernel was not built with the required value? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_MODULE_SIG /boot/config.* + + For each kernel installed, a line with value "y" should be returned. + + Is it the case that the kernel was not built with the required value? + + + + To verify that the Audit is correctly configured according to recommended rules, check the content of the file with the following command: +cat /etc/audit/rules.d/30-ospp-v42-4-delete-failed.rules +The output has to be exactly as follows: +## Unsuccessful file delete +-a always,exit -F arch=b32 -S unlink,unlinkat,rename,renameat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete +-a always,exit -F arch=b64 -S unlink,unlinkat,rename,renameat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete +-a always,exit -F arch=b32 -S unlink,unlinkat,rename,renameat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete +-a always,exit -F arch=b64 -S unlink,unlinkat,rename,renameat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete + Is it the case that the file does not exist or the content differs? + + + + +Run the following command to determine if the unprivuser_use_svirt SELinux boolean is disabled: +$ getsebool unprivuser_use_svirt +If properly configured, the output should show the following: +unprivuser_use_svirt --> off + Is it the case that unprivuser_use_svirt is not disabled? + + + + +If the system is configured to prevent the loading of the iwlmvm kernel module, +it will contain lines inside any file in /etc/modprobe.d or the deprecated /etc/modprobe.conf. +These lines instruct the module loading system to run another program (such as /bin/true) upon a module install event. + +These lines can also instruct the module loading system to ignore the iwlmvm kernel module via blacklist keyword. + +Run the following command to search for such lines in all files in /etc/modprobe.d and the deprecated /etc/modprobe.conf: +$ grep -r iwlmvm /etc/modprobe.conf /etc/modprobe.d + Is it the case that no line is returned? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_GCC_PLUGIN_LATENT_ENTROPY /boot/config.* + + For each kernel installed, a line with value "y" should be returned. + + Is it the case that the kernel was not built with the required value? + + + + To ensure screen locking on smartcard removal is enabled, run the following command: +$ grep removal-action /etc/dconf/db/local.d/* +The output should be 'lock-screen'. +To ensure that users cannot disable screen locking on smartcard removal, run the following: +$ grep removal-action /etc/dconf/db/local.d/locks/* +If properly configured, the output should be /org/gnome/settings-daemon/peripherals/smartcard/removal-action + Is it the case that removal-action has not been configured? + + + + +Run the following command to determine if the daemons_dump_core SELinux boolean is disabled: +$ getsebool daemons_dump_core +If properly configured, the output should show the following: +daemons_dump_core --> off + Is it the case that daemons_dump_core is not disabled? + + + + To check that no password hashes are stored in +/etc/passwd, run the following command: +awk '!/\S:x|\*/ {print}' /etc/passwd +If it produces any output, then a password hash is +stored in /etc/passwd. + Is it the case that any stored hashes are found in /etc/passwd? + + + + To check that the smb service is disabled in system boot configuration, +run the following command: +$ sudo systemctl is-enabled smb +Output should indicate the smb service has either not been installed, +or has been disabled at all runlevels, as shown in the example below: +$ sudo systemctl is-enabled smb disabled + +Run the following command to verify smb is not active (i.e. not running) through current runtime configuration: +$ sudo systemctl is-active smb + +If the service is not running the command will return the following output: +inactive + +The service will also be masked, to check that the smb is masked, run the following command: +$ sudo systemctl show smb | grep "LoadState\|UnitFileState" + +If the service is masked the command will return the following outputs: + +LoadState=masked + +UnitFileState=masked + Is it the case that the "smb" is loaded and not masked? + + + + To verify the boot loader superuser password has been set, run the following command: +$ sudo grep "^[\s]*GRUB2_PASSWORD=grub\.pbkdf2\.sha512.*$" /boot/efi/EFI/redhat/user.cfg +The output should be similar to: +GRUB2_PASSWORD=grub.pbkdf2.sha512.10000.C4E08AC72FBFF7E837FD267BFAD7AEB3D42DDC +2C99F2A94DD5E2E75C2DC331B719FE55D9411745F82D1B6CFD9E927D61925F9BBDD1CFAA0080E0 +916F7AB46E0D.1302284FCCC52CD73BA3671C6C12C26FF50BA873293B24EE2A96EE3B57963E6D7 +0C83964B473EC8F93B07FE749AA6710269E904A9B08A6BBACB00A2D242AD828 + Is it the case that no password is set? + + + + To determine how the SSH daemon's StrictModes option is set, run the following command: + +$ sudo grep -i StrictModes /etc/ssh/sshd_config + +If a line indicating yes is returned, then the required value is set. + + Is it the case that the required value is not set? + + + + To check how many lowercase characters are required in a password, run the following command: +$ grep lcredit /etc/security/pwquality.conf +The lcredit parameter (as a negative number) will indicate how many special characters are required. + Is it the case that lcredit is not found or not less than or equal to the required value? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_LEGACY_PTYS /boot/config.* + + Configs with value 'n' are not explicitly set in the file, so either commented lines or no + lines should be returned. + + Is it the case that the kernel was not built with the required value? + + + + Verify that Red Hat Enterprise Linux 8 disables the use of user namespaces with the following commands: + +Note: User namespaces are used primarily for Linux containers. If containers are in use, this requirement is not applicable. + +The runtime status of the user.max_user_namespaces kernel parameter can be queried +by running the following command: +$ sysctl user.max_user_namespaces +0. + + Is it the case that the correct value is not returned? + + + + +Run the following command to determine if the zoneminder_run_sudo SELinux boolean is disabled: +$ getsebool zoneminder_run_sudo +If properly configured, the output should show the following: +zoneminder_run_sudo --> off + Is it the case that zoneminder_run_sudo is not disabled? + + + + To determine if the system is configured to audit successful calls +to the fchownat system call, run the following command: +$ sudo grep "fchownat" /etc/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + To determine if the system is configured to audit calls to the +renameat system call, run the following command: +$ sudo grep "renameat" /etc/audit/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + Run the following command to ensure that /tmp is configured as a +polyinstantiated directory: +$ sudo grep /tmp /etc/security/namespace.conf +The output should return the following: +/tmp /tmp/tmp-inst/ level root,adm + Is it the case that is not configured? + + + + The following command will list which files on the system have ownership different from what +is expected by the RPM database: +$ rpm -Va | rpm -Va --nofiledigest | awk '{ if (substr($0,6,1)=="U" || substr($0,7,1)=="G") print $NF }' + Is it the case that there is output? + + + + +Run the following command to determine if the httpd_can_sendmail SELinux boolean is disabled: +$ getsebool httpd_can_sendmail +If properly configured, the output should show the following: +httpd_can_sendmail --> off + Is it the case that httpd_can_sendmail is not disabled? + + + + The runtime status of the net.ipv4.conf.all.accept_source_route kernel parameter can be queried +by running the following command: +$ sysctl net.ipv4.conf.all.accept_source_route +0. + + Is it the case that the correct value is not returned? + + + + To check that the psacct service is disabled in system boot configuration, +run the following command: +$ sudo systemctl is-enabled psacct +Output should indicate the psacct service has either not been installed, +or has been disabled at all runlevels, as shown in the example below: +$ sudo systemctl is-enabled psacct disabled + +Run the following command to verify psacct is not active (i.e. not running) through current runtime configuration: +$ sudo systemctl is-active psacct + +If the service is not running the command will return the following output: +inactive + +The service will also be masked, to check that the psacct is masked, run the following command: +$ sudo systemctl show psacct | grep "LoadState\|UnitFileState" + +If the service is masked the command will return the following outputs: + +LoadState=masked + +UnitFileState=masked + Is it the case that the "psacct" is loaded and not masked? + + + + +Run the following command to determine if the httpd_mod_auth_pam SELinux boolean is disabled: +$ getsebool httpd_mod_auth_pam +If properly configured, the output should show the following: +httpd_mod_auth_pam --> off + Is it the case that httpd_mod_auth_pam is not disabled? + + + + Verify Red Hat Enterprise Linux 8 generates an audit record for unsuccessful attempts to create files using the openat system call with O_CREAT flag. + +If the auditd daemon is configured to use the "augenrules" program to read audit rules during daemon startup (the default), run the following command: + +$ sudo grep -r openat /etc/audit/rules.d + +If the auditd daemon is configured to use the "auditctl" utility to read audit rules during daemon startup, run the following command: + +$ sudo grep openat /etc/audit/audit.rules + +The output should be the following: + +-a always,exit -F arch=b32 -S openat -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S openat -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S openat -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S openat -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + Is it the case that the command does not return a line, or the line is commented out? + + + + Run the following command to determine if the tuned package is installed: +$ rpm -q tuned + Is it the case that the package is installed? + + + + Inspect the system to determine if intrusion detection software has been installed. +Verify this intrusion detection software is active. + Is it the case that no host-based intrusion detection tools are installed? + + + + Ensure that debug-shell service is not enabled with the following command: +sudo grep -L "^options\s+.*\bsystemd.debug-shell=1\b" /boot/loader/entries/*.conf +No line should be returned, each line returned is a boot entry that enables the debug-shell. + Is it the case that the comand returns a line? + + + + Verify the nosuid option is configured for the /dev/shm mount point, + run the following command: + $ sudo mount | grep '\s/dev/shm\s' + . . . /dev/shm . . . nosuid . . . + + Is it the case that the "/dev/shm" file system does not have the "nosuid" option set? + + + + Run the following command to determine if the bind package is installed: +$ rpm -q bind + Is it the case that the package is installed? + + + + +Run the following command to determine if the mpd_use_cifs SELinux boolean is disabled: +$ getsebool mpd_use_cifs +If properly configured, the output should show the following: +mpd_use_cifs --> off + Is it the case that mpd_use_cifs is not disabled? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_SCHED_STACK_END_CHECK /boot/config.* + + For each kernel installed, a line with value "y" should be returned. + + Is it the case that the kernel was not built with the required value? + + + + Inspect the form of default GRUB 2 command line for the Linux operating system +in /etc/default/grub. If it includes audit_backlog_limit=8192, +then the parameter will be configured for newly installed kernels. +First check if the GRUB recovery is enabled: +$ sudo grep 'GRUB_DISABLE_RECOVERY' /etc/default/grub +If this option is set to true, then check that a line is output by the following command: +$ sudo grep 'GRUB_CMDLINE_LINUX_DEFAULT.*audit_backlog_limit=8192.*' /etc/default/grub +If the recovery is disabled, check the line with +$ sudo grep 'GRUB_CMDLINE_LINUX.*audit_backlog_limit=8192.*' /etc/default/grub.Moreover, command line parameters for currently installed kernels should be checked as well. +Run the following command: +$ sudo grubby --info=ALL | grep args | grep -v 'audit_backlog_limit=8192' +The command should not return any output. + Is it the case that audit backlog limit is not configured? + + + + +Run the following command to determine if the logrotate_use_nfs SELinux boolean is disabled: +$ getsebool logrotate_use_nfs +If properly configured, the output should show the following: +logrotate_use_nfs --> off + Is it the case that logrotate_use_nfs is not disabled? + + + + To verify that the Audit is correctly configured according to recommended rules, check the content of the file with the following command: +cat /etc/audit/rules.d/10-base-config.rules +The output has to be exactly as follows: +## First rule - delete all +-D + +## Increase the buffers to survive stress events. +## Make this bigger for busy systems +-b 8192 + +## This determine how long to wait in burst of events +--backlog_wait_time 60000 + +## Set failure mode to syslog +-f 1 + Is it the case that the file does not exist or the content differs? + + + + +Run the following command to determine if the gluster_anon_write SELinux boolean is disabled: +$ getsebool gluster_anon_write +If properly configured, the output should show the following: +gluster_anon_write --> off + Is it the case that gluster_anon_write is not disabled? + + + + +Run the following command to determine if the authlogin_radius SELinux boolean is disabled: +$ getsebool authlogin_radius +If properly configured, the output should show the following: +authlogin_radius --> off + Is it the case that authlogin_radius is not disabled? + + + + To determine if the system is configured to audit calls to the +openat system call, run the following command: +$ sudo grep "openat" /etc/audit/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + +Run the following command to determine if the login_console_enabled SELinux boolean is enabled: +$ getsebool login_console_enabled +If properly configured, the output should show the following: +login_console_enabled --> on + Is it the case that login_console_enabled is not enabled? + + + + Run the following command and verify that time sources are only configure with server directive: +# grep -E "^(server|pool)" /etc/chrony.conf +A line with the appropriate server should be returned, any line returned starting with pool is a finding. + Is it the case that an authoritative remote time server is not configured or configured with pool directive? + + + + Inspect /etc/audit/auditd.conf and locate the following line to +determine if the system is configured to either suspend, switch to single user mode, +or halt when disk space has run low: +admin_space_left_action single + Is it the case that the system is not configured to switch to single user mode for corrective action? + + + + The runtime status of the kernel.randomize_va_space kernel parameter can be queried +by running the following command: +$ sysctl kernel.randomize_va_space +2. + + Is it the case that the correct value is not returned? + + + + To verify that the Audit is correctly configured according to recommended rules, check the content of the file with the following command: +cat /etc/audit/rules.d/30-ospp-v42-2-modify-success.rules +The output has to be exactly as follows: +## Successful file modifications (open for write or truncate) +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification +-a always,exit -F arch=b32 -S open -F a1&01003 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification +-a always,exit -F arch=b64 -S open -F a1&01003 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification +-a always,exit -F arch=b32 -S truncate,ftruncate -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification +-a always,exit -F arch=b64 -S truncate,ftruncate -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification + Is it the case that the file does not exist or the content differs? + + + + To ensure that remote access requires credentials, run the following command: +$ gsettings get org.gnome.Vino authentication-methods +If properly configured, the output should be false. +To ensure that users cannot disable credentials for remote access, run the following: +$ grep authentication-methods /etc/dconf/db/local.d/locks/* +If properly configured, the output should be +/org/gnome/Vino/authentication-methods + Is it the case that wireless network notification is enabled and not disabled? + + + + To determine if arguments that commands can be executed with are restricted, run the following command: +$ sudo grep -PR '^(?:\s*[^#=]+)=(?:\s*(?:\([^\)]+\))?\s*(?!\s*\()[^,\s]+(?:[ \t]+[^,\s]+)+[ \t]*,)*(\s*(?:\([^\)]+\))?\s*(?!\s*\()[^,\s]+[ \t]*(?:,|$))' /etc/sudoers /etc/sudoers.d/ +The command should return no output. + Is it the case that /etc/sudoers file contains user specifications that allow execution of commands with any arguments? + + + + +If the system is configured to prevent the loading of the tipc kernel module, +it will contain lines inside any file in /etc/modprobe.d or the deprecated /etc/modprobe.conf. +These lines instruct the module loading system to run another program (such as /bin/true) upon a module install event. + +These lines can also instruct the module loading system to ignore the tipc kernel module via blacklist keyword. + +Run the following command to search for such lines in all files in /etc/modprobe.d and the deprecated /etc/modprobe.conf: +$ grep -r tipc /etc/modprobe.conf /etc/modprobe.d + Is it the case that no line is returned? + + + + +Run the following command to determine if the samba_share_fusefs SELinux boolean is disabled: +$ getsebool samba_share_fusefs +If properly configured, the output should show the following: +samba_share_fusefs --> off + Is it the case that samba_share_fusefs is not disabled? + + + + Verify that local initialization files do not execute world-writable programs, +execute the following command: +$ sudo find /home -perm -002 -type f -name ".[^.]*" -exec ls -ld {} \; +For all files listed, check for their presence in the local +initialization files with the following command: +Note: The example will be for a system that is configured to create +users' home directories in the "/home" directory. + sudo find /home/* -maxdepth 1 -type f -name \.\* -exec grep -H <file> {} \; + Is it the case that user initialization files are executing world-writable programs? + + + + Verify that rules for unsuccessful calls of the openat syscall are in the order shown below. + + If the auditd daemon is configured to use the "augenrules" program to read audit rules during daemon startup (the default), check the order of rules below in a file with suffix ".rules" in the directory "/etc/audit/rules.d". + If the auditd daemon is configured to use the "auditctl" utility to read audit rules during daemon startup, check the order of rules below in "/etc/audit/audit.rules" file. + + -a always,exit -F arch=b32 -S openat -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b32 -S openat -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b32 -S openat -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b32 -S openat -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b32 -S openat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-access + -a always,exit -F arch=b32 -S openat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-access + + If the system is 64 bit then also add the following lines: + + -a always,exit -F arch=b64 -S openat -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b64 -S openat -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b64 -S openat -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b64 -S openat -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b64 -S openat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-access + -a always,exit -F arch=b64 -S openat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-access + Is it the case that the rules are in a different order? + + + + Verify Red Hat Enterprise Linux 8 generates audit records for all account creations, modifications, disabling, and termination events that affect "/etc/passwd with the following command: + +$ sudo auditctl -l | egrep '(/etc/shadow)' + +-w /etc/shadow -p wa -k identity + Is it the case that command does not return a line, or the line is commented out? + + + + To determine if use_pty has been configured for sudo, run the following command: +$ sudo grep -ri "^[\s]*Defaults.*\buse_pty\b.*" /etc/sudoers /etc/sudoers.d/ +The command should return a matching output. + Is it the case that use_pty is not enabled in sudo? + + + + Inspect /etc/audit/audisp-remote.conf and locate the following line to +determine if the system is configured to either send to syslog, switch to single user mode, +or halt when the disk is full: +$ sudo grep -i disk_full_action /etc/audit/audisp-remote.conf +The output should return something similar to: +disk_full_action = single +Acceptable values also include syslog and halt. + Is it the case that the system is not configured to switch to single user mode for corrective action? + + + + To check the ownership of /etc/ssh/sshd_config, +run the command: +$ ls -lL /etc/ssh/sshd_config +If properly configured, the output should indicate the following owner: +root + Is it the case that /etc/ssh/sshd_config does not have an owner of root? + + + + Verify the nosuid option is configured for the /tmp mount point, + run the following command: + $ sudo mount | grep '\s/tmp\s' + . . . /tmp . . . nosuid . . . + + Is it the case that the "/tmp" file system does not have the "nosuid" option set? + + + + Run the following command to determine if the vsftpd package is installed: +$ rpm -q vsftpd + Is it the case that the package is installed? + + + + To check that page poisoning is enabled at boot time, check all boot entries with following command: +sudo grep -L "^options\s+.*\bpage_poison=1\b" /boot/loader/entries/*.conf +No line should be returned, each line returned is a boot entry that doesn't enable page poisoning. + Is it the case that page allocator poisoning is not enabled? + + + + To verify that localpkg_gpgcheck is configured properly, run the following +command: +$ grep localpkg_gpgcheck /etc/yum.conf +The output should return something similar to: +localpkg_gpgcheck=1 + Is it the case that gpgcheck is not enabled or configured correctly to verify local packages? + + + + To check that the rsyncd service is disabled in system boot configuration, +run the following command: +$ sudo systemctl is-enabled rsyncd +Output should indicate the rsyncd service has either not been installed, +or has been disabled at all runlevels, as shown in the example below: +$ sudo systemctl is-enabled rsyncd disabled + +Run the following command to verify rsyncd is not active (i.e. not running) through current runtime configuration: +$ sudo systemctl is-active rsyncd + +If the service is not running the command will return the following output: +inactive + +The service will also be masked, to check that the rsyncd is masked, run the following command: +$ sudo systemctl show rsyncd | grep "LoadState\|UnitFileState" + +If the service is masked the command will return the following outputs: + +LoadState=masked + +UnitFileState=masked + Is it the case that the "rsyncd" is loaded and not masked? + + + + Verify the noexec option is configured for the /home mount point, + run the following command: + $ sudo mount | grep '\s/home\s' + . . . /home . . . noexec . . . + + Is it the case that the "/home" file system does not have the "noexec" option set? + + + + To check the minimum password length, run the command: +$ grep PASS_MIN_LEN /etc/login.defs +The DoD requirement is 15. + Is it the case that it is not set to the required value? + + + + To check the permissions of /etc/cron.daily, +run the command: +$ ls -l /etc/cron.daily +If properly configured, the output should indicate the following permissions: +-rwx------ + Is it the case that /etc/cron.daily does not have unix mode -rwx------? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_PAGE_TABLE_ISOLATION /boot/config.* + + For each kernel installed, a line with value "y" should be returned. + + Is it the case that the kernel was not built with the required value? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_LEGACY_VSYSCALL_EMULATE /boot/config.* + + Configs with value 'n' are not explicitly set in the file, so either commented lines or no + lines should be returned. + + Is it the case that the kernel was not built with the required value? + + + + To determine how the SSH daemon's UsePAM option is set, run the following command: + +$ sudo grep -i UsePAM /etc/ssh/sshd_config + +If a line indicating yes is returned, then the required value is set. + + Is it the case that the required value is not set? + + + + To verify that USB hubs will be authorized by the USBGuard daemon, +run the following command: +$ sudo grep allow /etc/usbguard/rules.conf +One of the output lines should be +allow with-interface match-all { 09:00:* } + Is it the case that USB devices of class 9 are not authorized? + + + + + +Run the following command to determine the current status of the +ntpd service: +$ sudo systemctl is-active ntpd +If the service is running, it should return the following: active + Is it the case that ? + + + + To verify that the Audit is correctly configured according to recommended rules, check the content of the file with the following command: +cat /etc/audit/rules.d/30-ospp-v42-6-owner-change-failed.rules +The output has to be exactly as follows: +## Unsuccessful ownership change +-a always,exit -F arch=b32 -S lchown,fchown,chown,fchownat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-owner-change +-a always,exit -F arch=b64 -S lchown,fchown,chown,fchownat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-owner-change +-a always,exit -F arch=b32 -S lchown,fchown,chown,fchownat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-owner-change +-a always,exit -F arch=b64 -S lchown,fchown,chown,fchownat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-owner-change + Is it the case that the file does not exist or the content differs? + + + + +Run the following command to determine if the use_lpd_server SELinux boolean is disabled: +$ getsebool use_lpd_server +If properly configured, the output should show the following: +use_lpd_server --> off + Is it the case that use_lpd_server is not disabled? + + + + Verify Red Hat Enterprise Linux 8 is configured to lock the root account after +unsuccessful logon attempts with the command: + + +$ grep even_deny_root /etc/security/faillock.conf +even_deny_root + Is it the case that the "even_deny_root" option is not set, is missing or commented out? + + + + +Run the following command to determine if the conman_can_network SELinux boolean is disabled: +$ getsebool conman_can_network +If properly configured, the output should show the following: +conman_can_network --> off + Is it the case that conman_can_network is not disabled? + + + + To determine if the system is configured to audit successful calls +to the removexattr system call, run the following command: +$ sudo grep "removexattr" /etc/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + To check the group ownership of /etc/group-, +run the command: +$ ls -lL /etc/group- +If properly configured, the output should indicate the following group-owner: +root + Is it the case that /etc/group- does not have a group owner of root? + + + + To determine if the system is configured to audit calls to the +open_by_handle_at system call, run the following command: +$ sudo grep "open_by_handle_at" /etc/audit/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + If the system uses IPv6, this is not applicable. + +If the system is configured to prevent the usage of the ipv6 on +network interfaces, it will contain a line of the form: +net.ipv6.conf.all.disable_ipv6 = 1 +Such lines may be inside any file in the /etc/sysctl.d directory. +This permits insertion of the IPv6 kernel module (which other parts of the +system expect to be present), but otherwise keeps all network interfaces +from using IPv6. Run the following command to search for such lines in all +files in /etc/sysctl.d: +$ grep -r ipv6 /etc/sysctl.d + Is it the case that the ipv6 support is disabled on all network interfaces? + + + + To determine if the system is configured to audit calls to the +clock_settime system call, run the following command: +$ sudo grep "clock_settime" /etc/audit/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + The runtime status of the net.ipv6.conf.default.accept_ra_pinfo kernel parameter can be queried +by running the following command: +$ sysctl net.ipv6.conf.default.accept_ra_pinfo +0. + + Is it the case that the correct value is not returned? + + + + To check if pam_pwquality.so is enabled in system-auth, run the following command: +$ grep pam_pwquality /etc/pam.d/system-auth +The output should be similar to the following: +password requisite pam_pwquality.so + Is it the case that pam_pwquality.so is not enabled in system-auth? + + + + Run the following command to determine if the audispd-plugins package is installed: $ rpm -q audispd-plugins + Is it the case that the package is not installed? + + + + To check that the quota_nld service is disabled in system boot configuration, +run the following command: +$ sudo systemctl is-enabled quota_nld +Output should indicate the quota_nld service has either not been installed, +or has been disabled at all runlevels, as shown in the example below: +$ sudo systemctl is-enabled quota_nld disabled + +Run the following command to verify quota_nld is not active (i.e. not running) through current runtime configuration: +$ sudo systemctl is-active quota_nld + +If the service is not running the command will return the following output: +inactive + +The service will also be masked, to check that the quota_nld is masked, run the following command: +$ sudo systemctl show quota_nld | grep "LoadState\|UnitFileState" + +If the service is masked the command will return the following outputs: + +LoadState=masked + +UnitFileState=masked + Is it the case that the "quota_nld" is loaded and not masked? + + + + To check the ownership of /etc/shadow, +run the command: +$ ls -lL /etc/shadow +If properly configured, the output should indicate the following owner: +root + Is it the case that /etc/shadow does not have an owner of root? + + + + +Run the following command to determine if the httpd_can_network_memcache SELinux boolean is disabled: +$ getsebool httpd_can_network_memcache +If properly configured, the output should show the following: +httpd_can_network_memcache --> off + Is it the case that httpd_can_network_memcache is not disabled? + + + + +Run the following command to determine if the use_ecryptfs_home_dirs SELinux boolean is disabled: +$ getsebool use_ecryptfs_home_dirs +If properly configured, the output should show the following: +use_ecryptfs_home_dirs --> off + Is it the case that use_ecryptfs_home_dirs is not disabled? + + + + Run the following command to determine if the psacct package is installed: $ rpm -q psacct + Is it the case that the package is not installed? + + + + To check the ownership of /etc/cron.daily, +run the command: +$ ls -lL /etc/cron.daily +If properly configured, the output should indicate the following owner: +root + Is it the case that /etc/cron.daily does not have an owner of root? + + + + Verify Red Hat Enterprise Linux 8 takes the appropriate action when an audit processing failure occurs. + +Check that Red Hat Enterprise Linux 8 takes the appropriate action when an audit processing failure occurs with the following command: + +$ sudo grep disk_error_action /etc/audit/auditd.conf + +disk_error_action = + +If the value of the "disk_error_action" option is not "SYSLOG", "SINGLE", or "HALT", or the line is commented out, ask the system administrator to indicate how the system takes appropriate action when an audit process failure occurs. + Is it the case that there is no evidence of appropriate action? + + + + +To properly set the owner of /var/log/audit, run the command: +$ sudo chown root /var/log/audit + +To properly set the owner of /var/log/audit/*, run the command: +$ sudo chown root /var/log/audit/* + Is it the case that ? + + + + The runtime status of the net.ipv4.conf.all.send_redirects kernel parameter can be queried +by running the following command: +$ sysctl net.ipv4.conf.all.send_redirects +0. + + Is it the case that the correct value is not returned? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_HARDENED_USERCOPY /boot/config.* + + For each kernel installed, a line with value "y" should be returned. + + Is it the case that the kernel was not built with the required value? + + + + Run the following command to verify that SSH client is configured to use 32 bytes of entropy: +grep SSH_USE_STRONG_RNG /etc/profile.d/cc-ssh-strong-rng.sh +The output should be: +export SSH_USE_STRONG_RNG=32 + Is it the case that SSH client is not configured to use 32 bytes of entropy or more? + + + + To verify that the DConf User profile is configured correctly, run the following +command: + +$ cat /etc/dconf/profile/user +The output should show the following: +user-db:user +system-db:local +system-db:site +system-db:distro + Is it the case that DConf User profile does not exist or is not configured correctly? + + + + Determine if there is a process for the uploading of files to the web site. +This process should include the requirement for the use of a secure encrypted +logon and secure encrypted connection. If the remote users are uploading files +without utilizing approved encryption methods, this is a finding. + Is it the case that it is not? + + + + +If the system is configured to prevent the loading of the cramfs kernel module, +it will contain lines inside any file in /etc/modprobe.d or the deprecated /etc/modprobe.conf. +These lines instruct the module loading system to run another program (such as /bin/true) upon a module install event. + +These lines can also instruct the module loading system to ignore the cramfs kernel module via blacklist keyword. + +Run the following command to search for such lines in all files in /etc/modprobe.d and the deprecated /etc/modprobe.conf: +$ grep -r cramfs /etc/modprobe.conf /etc/modprobe.d + Is it the case that no line is returned? + + + + To check how many characters must differ during a password change, run the following command: +$ sudo grep difok /etc/security/pwquality.conf +difok = + +The difok parameter will indicate how many characters must differ. + Is it the case that difok is not found or set to less than the required value? + + + + +Run the following command to determine if the virt_use_xserver SELinux boolean is disabled: +$ getsebool virt_use_xserver +If properly configured, the output should show the following: +virt_use_xserver --> off + Is it the case that virt_use_xserver is not disabled? + + + + + +Run the following command to determine the current status of the +postfix service: +$ sudo systemctl is-active postfix +If the service is running, it should return the following: active + Is it the case that the system is not a cross domain solution and the service is not enabled? + + + + +Run the following command to determine if the postgresql_selinux_unconfined_dbadm SELinux boolean is enabled: +$ getsebool postgresql_selinux_unconfined_dbadm +If properly configured, the output should show the following: +postgresql_selinux_unconfined_dbadm --> on + Is it the case that postgresql_selinux_unconfined_dbadm is not enabled? + + + + +Run the following command to determine if the gssd_read_tmp SELinux boolean is enabled: +$ getsebool gssd_read_tmp +If properly configured, the output should show the following: +gssd_read_tmp --> on + Is it the case that gssd_read_tmp is not enabled? + + + + To verify that FIPS mode is enabled properly, run the following command: +fips-mode-setup --check +The output should contain the following: +FIPS mode is enabled. +To verify that the cryptographic policy has been configured correctly, run the +following command: +$ update-crypto-policies --show +The output should return . + Is it the case that FIPS mode is not enabled? + + + + To check that the cpupower service is disabled in system boot configuration, +run the following command: +$ sudo systemctl is-enabled cpupower +Output should indicate the cpupower service has either not been installed, +or has been disabled at all runlevels, as shown in the example below: +$ sudo systemctl is-enabled cpupower disabled + +Run the following command to verify cpupower is not active (i.e. not running) through current runtime configuration: +$ sudo systemctl is-active cpupower + +If the service is not running the command will return the following output: +inactive + +The service will also be masked, to check that the cpupower is masked, run the following command: +$ sudo systemctl show cpupower | grep "LoadState\|UnitFileState" + +If the service is masked the command will return the following outputs: + +LoadState=masked + +UnitFileState=masked + Is it the case that the "cpupower" is loaded and not masked? + + + + To check how many categories of characters must be used in password during a password change, +run the following command: +$ sudo grep minclass /etc/security/pwquality.conf +The minclass parameter will indicate how many character classes must be used. If +the requirement was for the password to contain characters from different categories, +then this would appear as minclass = . + Is it the case that the value of "minclass" is set to less than "<sub idref="var_password_pam_minclass" />" or is commented out? + + + + To determine if the system is configured to audit account changes, +run the following command: +auditctl -l | egrep '(/etc/passwd|/etc/shadow|/etc/group|/etc/gshadow|/etc/security/opasswd)' +If the system is configured to watch for account changes, lines should be returned for +each file specified (and with perm=wa for each). + Is it the case that the system is not configured to audit account changes? + + + + To verify that cmdport has been set properly, perform the following: +$ grep '\bcmdport\b' /etc/chrony.conf +The output should return +cmdport 0 + Is it the case that cmdport is not set or cmdport is set to a non-zero value? + + + + The following command will discover and print world-writable directories that +are not owned by root. Run it once for each local partition PART: +$ sudo find PART -xdev -type d -perm -0002 -uid +0 -print + Is it the case that there is output? + + + + +Run the following command to determine if the spamd_enable_home_dirs SELinux boolean is enabled: +$ getsebool spamd_enable_home_dirs +If properly configured, the output should show the following: +spamd_enable_home_dirs --> on + Is it the case that spamd_enable_home_dirs is not enabled? + + + + To check the maximum value for consecutive repeating characters, run the following command: +$ sudo grep maxrepeat /etc/security/pwquality.conf + Is it the case that the value of "maxrepeat" is set to more than "<sub idref="var_password_pam_maxrepeat" />" or is commented out? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_RETPOLINE /boot/config.* + + For each kernel installed, a line with value "y" should be returned. + + Is it the case that the kernel was not built with the required value? + + + + The runtime status of the net.ipv4.conf.default.send_redirects kernel parameter can be queried +by running the following command: +$ sysctl net.ipv4.conf.default.send_redirects +0. + + Is it the case that the correct value is not returned? + + + + +Run the following command to determine if the exim_can_connect_db SELinux boolean is disabled: +$ getsebool exim_can_connect_db +If properly configured, the output should show the following: +exim_can_connect_db --> off + Is it the case that exim_can_connect_db is not disabled? + + + + In order to be sure that the databases are up-to-date, run the +dconf update +command as the administrator. + Is it the case that The system-wide dconf databases are up-to-date with regards to respective keyfiles? + + + + To verify that each web content directory has an index.html file, +run the following command: +$ sudo find `grep -i documentroot /etc/httpd/conf/httpd.conf | awk -F'"' '{print $2}'` -name index.html +The output should return an index.html file for every +DocumentRoot that is set. + Is it the case that it is not? + + + + Verify an anti-virus solution is installed on the system. The anti-virus solution may be +bundled with an approved host-based security solution. + Is it the case that there is no anti-virus solution installed on the system? + + + + +Run the following command to determine if the dbadm_read_user_files SELinux boolean is disabled: +$ getsebool dbadm_read_user_files +If properly configured, the output should show the following: +dbadm_read_user_files --> off + Is it the case that dbadm_read_user_files is not disabled? + + + + +Run the following command to determine if the webadm_manage_user_files SELinux boolean is disabled: +$ getsebool webadm_manage_user_files +If properly configured, the output should show the following: +webadm_manage_user_files --> off + Is it the case that webadm_manage_user_files is not disabled? + + + + The runtime status of the kernel.unprivileged_bpf_disabled kernel parameter can be queried +by running the following command: +$ sysctl kernel.unprivileged_bpf_disabled +1. + + Is it the case that the correct value is not returned? + + + + +Run the following command to determine if the nagios_run_sudo SELinux boolean is disabled: +$ getsebool nagios_run_sudo +If properly configured, the output should show the following: +nagios_run_sudo --> off + Is it the case that nagios_run_sudo is not disabled? + + + + +Check if SSSD allows cached authentications with the following command: + +$ sudo grep cache_credentials /etc/sssd/sssd.conf +cache_credentials = true + +If "cache_credentials" is set to "false" or is missing no further checks are required. + +To verify that SSSD expires offline credentials, run the following command: +$ sudo grep offline_credentials_expiration /etc/sssd/sssd.conf +If configured properly, output should be +offline_credentials_expiration = 1 + Is it the case that it does not exist or is not configured properly? + + + + The runtime status of the net.ipv4.conf.default.log_martians kernel parameter can be queried +by running the following command: +$ sysctl net.ipv4.conf.default.log_martians +1. + + Is it the case that the correct value is not returned? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_ARM64_SW_TTBR0_PAN /boot/config.* + + For each kernel installed, a line with value "y" should be returned. + + Is it the case that the kernel was not built with the required value? + + + + To verify that audit is configured for OSPP v4.2.1, run the following commands: +for file in "10-base-config" "11-loginuid" "30-ospp-v42" "43-module-load";do diff /etc/audit/rules.d/$file.rules /usr/share/doc/audit*/rules/$file.rules; done + +If the system is configured properly, no lines should be returned. + Is it the case that the files are not there or differ? + + + + To check if the system login banner is compliant, +run the following command: +$ cat /etc/issue + Is it the case that it does not display the required banner? + + + + +Run the following command to determine if the httpd_execmem SELinux boolean is disabled: +$ getsebool httpd_execmem +If properly configured, the output should show the following: +httpd_execmem --> off + Is it the case that httpd_execmem is not disabled? + + + + +Run the following command to determine if the polipo_session_users SELinux boolean is disabled: +$ getsebool polipo_session_users +If properly configured, the output should show the following: +polipo_session_users --> off + Is it the case that polipo_session_users is not disabled? + + + + To verify that Samba clients using mount.cifs must use packet signing, run the following command: +$ grep sec /etc/fstab +The output should show either krb5i or ntlmv2i in use. + Is it the case that it does not? + + + + To ensure smart card authentication on the login screen is enabled, run the following command: +$ grep enable-smartcard-authentication /etc/dconf/db/gdm.d/* +The output should be true. +To ensure that users cannot disable smart card authentication on the login screen, run the following: +$ grep enable-smartcard-authentication /etc/dconf/db/gdm.d/locks/* +If properly configured, the output should be /org/gnome/login-screen/enable-smartcard-authentication + Is it the case that enable-smartcard-authentication has not been configured or is disabled? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_SECCOMP_FILTER /boot/config.* + + For each kernel installed, a line with value "y" should be returned. + + Is it the case that the kernel was not built with the required value? + + + + Verify Red Hat Enterprise Linux 8 generates an audit record for unsuccessful attempts to use the ftruncate system call. + +If the auditd daemon is configured to use the "augenrules" program to to read audit rules during daemon startup (the default), run the following command: + +$ sudo grep -r ftruncate /etc/audit/rules.d + +If the auditd daemon is configured to use the "auditctl" utility to read audit rules during daemon startup, run the following command: + +$ sudo grep ftruncate /etc/audit/audit.rules + +The output should be the following: + +-a always,exit -F arch=b32 -S ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -k access +-a always,exit -F arch=b64 -S ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -k access +-a always,exit -F arch=b32 -S ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -k access +-a always,exit -F arch=b64 -S ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -k access + Is it the case that the command does not return a line, or the line is commented out? + + + + To determine if the system is configured to audit unsuccessful calls +to the setxattr system call, run the following command: +$ sudo grep "setxattr" /etc/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + Run the following command to determine if the krb5-server package is installed: $ rpm -q krb5-server + Is it the case that the package is installed? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_DEBUG_CREDENTIALS /boot/config.* + + For each kernel installed, a line with value "y" should be returned. + + Is it the case that the kernel was not built with the required value? + + + + To determine if the system is configured to audit calls to the +setxattr system call, run the following command: +$ sudo grep "setxattr" /etc/audit/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + The runtime status of the net.ipv4.conf.all.forwarding kernel parameter can be queried +by running the following command: +$ sysctl net.ipv4.conf.all.forwarding +0. +The ability to forward packets is only appropriate for routers. + Is it the case that IP forwarding value is "1" and the system is not router? + + + + +Run the following command to determine if the sanlock_use_samba SELinux boolean is disabled: +$ getsebool sanlock_use_samba +If properly configured, the output should show the following: +sanlock_use_samba --> off + Is it the case that sanlock_use_samba is not disabled? + + + + Run the following command to determine if the abrt-addon-ccpp package is installed: +$ rpm -q abrt-addon-ccpp + Is it the case that the package is installed? + + + + To verify that auditing of privileged command use is configured, run the +following command: +$ sudo grep '\bat\b' /etc/audit/audit.rules /etc/audit/rules.d/* +It should return a relevant line in the audit rules. + Is it the case that the command does not return a line, or the line is commented out? + + + + +Run the following command to determine if the mcelog_server SELinux boolean is disabled: +$ getsebool mcelog_server +If properly configured, the output should show the following: +mcelog_server --> off + Is it the case that mcelog_server is not disabled? + + + + Run the following command to determine if the rsyslog package is installed: $ rpm -q rsyslog + Is it the case that the package is not installed? + + + + To verify that timed logins are disabled, run the following command: +$ grep -Pzoi "^\[daemon]\\ntimedlogin.*" /etc/gdm/custom.conf +The output should show the following: +[daemon] +TimedLoginEnable=false + Is it the case that GDM allows a guest to login without credentials? + + + + +Run the following command to determine if the xdm_bind_vnc_tcp_port SELinux boolean is disabled: +$ getsebool xdm_bind_vnc_tcp_port +If properly configured, the output should show the following: +xdm_bind_vnc_tcp_port --> off + Is it the case that xdm_bind_vnc_tcp_port is not disabled? + + + + +Run the following command to determine if the entropyd_use_audio SELinux boolean is disabled: +$ getsebool entropyd_use_audio +If properly configured, the output should show the following: +entropyd_use_audio --> off + Is it the case that entropyd_use_audio is not disabled? + + + + +Run the following command to determine if the minidlna_read_generic_user_content SELinux boolean is disabled: +$ getsebool minidlna_read_generic_user_content +If properly configured, the output should show the following: +minidlna_read_generic_user_content --> off + Is it the case that minidlna_read_generic_user_content is not disabled? + + + + The runtime status of the net.ipv6.conf.default.accept_ra_rtr_pref kernel parameter can be queried +by running the following command: +$ sysctl net.ipv6.conf.default.accept_ra_rtr_pref +0. + + Is it the case that the correct value is not returned? + + + + The document, DoDI 8500.01, establishes the policy on the use of DoD +information systems. It requires the use of a standard Notice and Consent Banner +and standard text to be included in user agreements. The banner should be set +to the following: + Is it the case that it is not display the required banner? + + + + Run the following command to determine if the geolite2-country package is installed: +$ rpm -q geolite2-country + Is it the case that the package is installed? + + + + Verify that Red Hat Enterprise Linux 8 is configured to audit the execution of the "poweroff" command with the following command: + +$ sudo auditctl -l | grep poweroff + +-a always,exit -F path=/usr/sbin/poweroff -F perm=x -F auid>=1000 -F auid!=unset -k privileged-poweroff + Is it the case that the command does not return a line, or the line is commented out? + + + + Inspect the file /etc/sysconfig/iptables to determine +the default policy for the INPUT chain. It should be set to DROP: +$ sudo grep ":INPUT" /etc/sysconfig/iptables + Is it the case that the default policy for the INPUT chain is not set to DROP? + + + + Run the following command to determine if the tar package is installed: $ rpm -q tar + Is it the case that the package is not installed? + + + + To verify that there are no .shosts files +on the system, run the following command: +$ sudo find / -name '.shosts' + Is it the case that .shosts files exist? + + + + +Run the following command to determine if the git_system_use_cifs SELinux boolean is disabled: +$ getsebool git_system_use_cifs +If properly configured, the output should show the following: +git_system_use_cifs --> off + Is it the case that git_system_use_cifs is not disabled? + + + + Run the following command to determine if the aide package is installed: $ rpm -q aide + Is it the case that the package is not installed? + + + + +Run the following command to determine if the httpd_tmp_exec SELinux boolean is disabled: +$ getsebool httpd_tmp_exec +If properly configured, the output should show the following: +httpd_tmp_exec --> off + Is it the case that httpd_tmp_exec is not disabled? + + + + Run the following command to determine if the usbguard package is installed: $ rpm -q usbguard + Is it the case that the package is not installed? + + + + +Run the following command to determine if the ftpd_use_cifs SELinux boolean is disabled: +$ getsebool ftpd_use_cifs +If properly configured, the output should show the following: +ftpd_use_cifs --> off + Is it the case that ftpd_use_cifs is not disabled? + + + + +Run the following command to determine if the abrt_handle_event SELinux boolean is disabled: +$ getsebool abrt_handle_event +If properly configured, the output should show the following: +abrt_handle_event --> off + Is it the case that abrt_handle_event is not disabled? + + + + To check the permissions of /etc/group-, +run the command: +$ ls -l /etc/group- +If properly configured, the output should indicate the following permissions: +-rw-r--r-- + Is it the case that /etc/group- does not have unix mode -rw-r--r--? + + + + Run the following command to determine if the pcsc-lite package is installed: $ rpm -q pcsc-lite + Is it the case that the package is not installed? + + + + To verify that McAfee Endpoint Security for Linux is +running, run the following command: +$ sudo ps -ef | grep -i mfetpd + Is it the case that virus scanning software is not running? + + + + To check that the mdmonitor service is disabled in system boot configuration, +run the following command: +$ sudo systemctl is-enabled mdmonitor +Output should indicate the mdmonitor service has either not been installed, +or has been disabled at all runlevels, as shown in the example below: +$ sudo systemctl is-enabled mdmonitor disabled + +Run the following command to verify mdmonitor is not active (i.e. not running) through current runtime configuration: +$ sudo systemctl is-active mdmonitor + +If the service is not running the command will return the following output: +inactive + +The service will also be masked, to check that the mdmonitor is masked, run the following command: +$ sudo systemctl show mdmonitor | grep "LoadState\|UnitFileState" + +If the service is masked the command will return the following outputs: + +LoadState=masked + +UnitFileState=masked + Is it the case that the "mdmonitor" is loaded and not masked? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_DEBUG_WX /boot/config.* + + For each kernel installed, a line with value "y" should be returned. + + Is it the case that the kernel was not built with the required value? + + + + +Run the following command to determine if the httpd_use_nfs SELinux boolean is disabled: +$ getsebool httpd_use_nfs +If properly configured, the output should show the following: +httpd_use_nfs --> off + Is it the case that httpd_use_nfs is not disabled? + + + + Run the following command to ensure that /var/tmp is configured as a +polyinstantiated directory: +$ sudo grep /var/tmp /etc/security/namespace.conf +The output should return the following: +/var/tmp /var/tmp/tmp-inst/ level root,adm + Is it the case that is not configured? + + + + These settings can be verified by running the following: +$ gsettings get org.gnome.desktop.media-handling automount +If properly configured, the output for automount should be false. +To ensure that users cannot enable automount in GNOME3, run the following: +$ grep 'automount' /etc/dconf/db/local.d/locks/* +If properly configured, the output for automount should be /org/gnome/desktop/media-handling/automount + Is it the case that GNOME automounting is not disabled? + + + + To find the location of the AIDE database file, run the following command: +$ sudo ls -l DBDIR/database_file_name + Is it the case that there is no database file? + + + + Verify that Red Hat Enterprise Linux 8 is configured to audit the execution of the "postdrop" command with the following command: + +$ sudo auditctl -l | grep postdrop + +-a always,exit -F path=/usr/bin/postdrop -F perm=x -F auid>=1000 -F auid!=unset -k privileged-postdrop + Is it the case that the command does not return a line, or the line is commented out? + + + + +Run the following command to determine if the authlogin_yubikey SELinux boolean is disabled: +$ getsebool authlogin_yubikey +If properly configured, the output should show the following: +authlogin_yubikey --> off + Is it the case that authlogin_yubikey is not disabled? + + + + +If the system is configured to prevent the loading of the cfg80211 kernel module, +it will contain lines inside any file in /etc/modprobe.d or the deprecated /etc/modprobe.conf. +These lines instruct the module loading system to run another program (such as /bin/true) upon a module install event. + +These lines can also instruct the module loading system to ignore the cfg80211 kernel module via blacklist keyword. + +Run the following command to search for such lines in all files in /etc/modprobe.d and the deprecated /etc/modprobe.conf: +$ grep -r cfg80211 /etc/modprobe.conf /etc/modprobe.d + Is it the case that no line is returned? + + + + To check that SLUB/SLAB poisoning is enabled, check all boot entries with following command; +sudo grep -L "^options\s+.*\bslub_debug=P\b" /boot/loader/entries/*.conf +No line should be returned, each line returned is a boot entry that does not enable poisoning. + Is it the case that SLUB/SLAB poisoning is not enabled? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_VMAP_STACK /boot/config.* + + For each kernel installed, a line with value "y" should be returned. + + Is it the case that the kernel was not built with the required value? + + + + +Run the following command to determine if the httpd_can_check_spam SELinux boolean is disabled: +$ getsebool httpd_can_check_spam +If properly configured, the output should show the following: +httpd_can_check_spam --> off + Is it the case that httpd_can_check_spam is not disabled? + + + + +Run the following command to determine if the httpd_can_network_relay SELinux boolean is disabled: +$ getsebool httpd_can_network_relay +If properly configured, the output should show the following: +httpd_can_network_relay --> off + Is it the case that httpd_can_network_relay is not disabled? + + + + If FTP services are not installed, this is not applicable. + +To verify this configuration, run the following command: + +grep "banner_file" /etc/vsftpd/vsftpd.conf + + +The output should show the value of banner_file is set to /etc/issue, an example of which is shown below: + +$ sudo grep "banner_file" /etc/vsftpd/vsftpd.conf + +banner_file=/etc/issue + Is it the case that it does not? + + + + Verify that Red Hat Enterprise Linux 8 does not have unauthorized IP tunnels configured. + + +# yum list installed libreswan +libreswan.x86-64 3.20-5.el7_4 + + +If "libreswan" is installed, check to see if the "IPsec" service is active with the following command: + +# systemctl status ipsec +ipsec.service - Internet Key Exchange (IKE) Protocol Daemon for IPsec +Loaded: loaded (/usr/lib/systemd/system/ipsec.service; disabled) +Active: inactive (dead) + + +If the "IPsec" service is active, check for configured IPsec connections (conn), perform the following: +grep -rni conn /etc/ipsec.conf /etc/ipsec.d/ +Verify any returned results for organizational approval. + Is it the case that the IPSec tunnels are not approved? + + + + +Run the following command to determine if the gluster_export_all_ro SELinux boolean is disabled: +$ getsebool gluster_export_all_ro +If properly configured, the output should show the following: +gluster_export_all_ro --> off + Is it the case that gluster_export_all_ro is not disabled? + + + + To determine the status and frequency of logrotate, run the following command: +$ sudo grep logrotate /var/log/cron* +If logrotate is configured properly, output should include references to +/etc/cron.daily. + Is it the case that logrotate is not configured to run daily? + + + + +Run the following command to determine if the dbadm_manage_user_files SELinux boolean is disabled: +$ getsebool dbadm_manage_user_files +If properly configured, the output should show the following: +dbadm_manage_user_files --> off + Is it the case that dbadm_manage_user_files is not disabled? + + + + +Run the following command to determine if the openvpn_run_unconfined SELinux boolean is disabled: +$ getsebool openvpn_run_unconfined +If properly configured, the output should show the following: +openvpn_run_unconfined --> off + Is it the case that openvpn_run_unconfined is not disabled? + + + + To ensure the system is configured to mask the Ctrl-Alt-Del sequence, Check +that the ctrl-alt-del.target is masked and not active with the following +command: +sudo systemctl status ctrl-alt-del.target +The output should indicate that the target is masked and not active. It +might resemble following output: +ctrl-alt-del.target +Loaded: masked (/dev/null; bad) +Active: inactive (dead) + Is it the case that the system is configured to reboot when Ctrl-Alt-Del is pressed? + + + + +To properly set the owner of /etc/audit/, run the command: +$ sudo chown root /etc/audit/ + +To properly set the owner of /etc/audit/rules.d/, run the command: +$ sudo chown root /etc/audit/rules.d/ + Is it the case that ? + + + + To ensure the failed password attempt policy is configured correctly, run the following command: + +$ grep fail_interval /etc/security/faillock.conf +The output should show fail_interval = <interval-in-seconds> where interval-in-seconds is or greater. + Is it the case that the "fail_interval" option is not set to "<sub idref="var_accounts_passwords_pam_faillock_fail_interval" />" +or less (but not "0"), the line is commented out, or the line is missing? + + + + To determine if the system is configured to audit calls to the +open_by_handle_at system call, run the following command: +$ sudo grep "open_by_handle_at" /etc/audit/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + To check the group ownership of /boot/efi/EFI/redhat/grub.cfg, +run the command: +$ ls -lL /boot/efi/EFI/redhat/grub.cfg +If properly configured, the output should indicate the following group-owner: +root + Is it the case that /boot/efi/EFI/redhat/grub.cfg does not have a group owner of root? + + + + The runtime status of the fs.protected_symlinks kernel parameter can be queried +by running the following command: +$ sysctl fs.protected_symlinks +1. + + Is it the case that the correct value is not returned? + + + + Inspect the form of default GRUB 2 command line for the Linux operating system +in /etc/default/grub. If it includes vsyscall=none, +then the parameter will be configured for newly installed kernels. +First check if the GRUB recovery is enabled: +$ sudo grep 'GRUB_DISABLE_RECOVERY' /etc/default/grub +If this option is set to true, then check that a line is output by the following command: +$ sudo grep 'GRUB_CMDLINE_LINUX_DEFAULT.*vsyscall=none.*' /etc/default/grub +If the recovery is disabled, check the line with +$ sudo grep 'GRUB_CMDLINE_LINUX.*vsyscall=none.*' /etc/default/grub.Moreover, command line parameters for currently installed kernels should be checked as well. +Run the following command: +$ sudo grubby --info=ALL | grep args | grep -v 'vsyscall=none' +The command should not return any output. + Is it the case that vsyscalls are enabled? + + + + +Run the following command to determine if the saslauthd_read_shadow SELinux boolean is disabled: +$ getsebool saslauthd_read_shadow +If properly configured, the output should show the following: +saslauthd_read_shadow --> off + Is it the case that saslauthd_read_shadow is not disabled? + + + + To verify that the Audit is correctly configured according to recommended rules, check the content of the file with the following command: +cat /etc/audit/rules.d/30-ospp-v42-2-modify-failed.rules +The output has to be exactly as follows: +## Unsuccessful file modifications (open for write or truncate) +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b32 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b32 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification + Is it the case that the file does not exist or the content differs? + + + + To check that the cockpit service is disabled in system boot configuration, +run the following command: +$ sudo systemctl is-enabled cockpit +Output should indicate the cockpit service has either not been installed, +or has been disabled at all runlevels, as shown in the example below: +$ sudo systemctl is-enabled cockpit disabled + +Run the following command to verify cockpit is not active (i.e. not running) through current runtime configuration: +$ sudo systemctl is-active cockpit + +If the service is not running the command will return the following output: +inactive + +The service will also be masked, to check that the cockpit is masked, run the following command: +$ sudo systemctl show cockpit | grep "LoadState\|UnitFileState" + +If the service is masked the command will return the following outputs: + +LoadState=masked + +UnitFileState=masked + Is it the case that the "cockpit" is loaded and not masked? + + + + +Run the following command to determine if the sge_domain_can_network_connect SELinux boolean is disabled: +$ getsebool sge_domain_can_network_connect +If properly configured, the output should show the following: +sge_domain_can_network_connect --> off + Is it the case that sge_domain_can_network_connect is not disabled? + + + + Run the following command to determine if the libselinux package is installed: $ rpm -q libselinux + Is it the case that the package is not installed? + + + + The following command will list which files on the system +have file hashes different from what is expected by the RPM database. +$ rpm -Va --noconfig | awk '$1 ~ /..5/ && $2 != "c"' + Is it the case that there is output? + + + + +Run the following command to determine if the virt_use_execmem SELinux boolean is disabled: +$ getsebool virt_use_execmem +If properly configured, the output should show the following: +virt_use_execmem --> off + Is it the case that virt_use_execmem is not disabled? + + + + Verify Red Hat Enterprise Linux 8 generates an audit record for unsuccessful attempts to use the rename system call. + +If the auditd daemon is configured to use the "augenrules" program to to read audit rules during daemon startup (the default), run the following command: + +$ sudo grep -r rename /etc/audit/rules.d + +If the auditd daemon is configured to use the "auditctl" utility to read audit rules during daemon startup, run the following command: + +$ sudo grep rename /etc/audit/audit.rules + +The output should be the following: + +-a always,exit -F arch=b32 -S rename -F exit=-EPERM -F auid>=1000 -F auid!=unset -k unsuccessful-delete +-a always,exit -F arch=b64 -S rename -F exit=-EPERM -F auid>=1000 -F auid!=unset -k unsuccessful-delete +-a always,exit -F arch=b32 -S rename -F exit=-EACCES -F auid>=1000 -F auid!=unset -k unsuccessful-delete +-a always,exit -F arch=b64 -S rename -F exit=-EACCES -F auid>=1000 -F auid!=unset -k unsuccessful-delete + Is it the case that the command does not return a line, or the line is commented out? + + + + Verify Red Hat Enterprise Linux 8 generates an audit record for unsuccessful attempts to use the open_by_handle_at system call. + +If the auditd daemon is configured to use the "augenrules" program to to read audit rules during daemon startup (the default), run the following command: + +$ sudo grep -r open_by_handle_at /etc/audit/rules.d + +If the auditd daemon is configured to use the "auditctl" utility to read audit rules during daemon startup, run the following command: + +$ sudo grep open_by_handle_at /etc/audit/audit.rules + +The output should be the following: + +-a always,exit -F arch=b32 -S open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -k access +-a always,exit -F arch=b64 -S open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -k access +-a always,exit -F arch=b32 -S open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -k access +-a always,exit -F arch=b64 -S open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -k access + Is it the case that the command does not return a line, or the line is commented out? + + + + To ensure the gdm package group is removed, run the following command: +$ rpm -qi gdm +The output should be: +package gdm is not installed + Is it the case that gdm has not been removed? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_PROC_KCORE /boot/config.* + + Configs with value 'n' are not explicitly set in the file, so either commented lines or no + lines should be returned. + + Is it the case that the kernel was not built with the required value? + + + + Verify Red Hat Enterprise Linux 8 generates an audit record for unsuccessful attempts to create files using the open_by_handle_at system call with O_CREAT flag. + +If the auditd daemon is configured to use the "augenrules" program to read audit rules during daemon startup (the default), run the following command: + +$ sudo grep -r open_by_handle_at /etc/audit/rules.d + +If the auditd daemon is configured to use the "auditctl" utility to read audit rules during daemon startup, run the following command: + +$ sudo grep open_by_handle_at /etc/audit/audit.rules + +The output should be the following: + +-a always,exit -F arch=b32 -S open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + Is it the case that the command does not return a line, or the line is commented out? + + + + To check that the ntpdate service is disabled in system boot configuration, +run the following command: +$ sudo systemctl is-enabled ntpdate +Output should indicate the ntpdate service has either not been installed, +or has been disabled at all runlevels, as shown in the example below: +$ sudo systemctl is-enabled ntpdate disabled + +Run the following command to verify ntpdate is not active (i.e. not running) through current runtime configuration: +$ sudo systemctl is-active ntpdate + +If the service is not running the command will return the following output: +inactive + +The service will also be masked, to check that the ntpdate is masked, run the following command: +$ sudo systemctl show ntpdate | grep "LoadState\|UnitFileState" + +If the service is masked the command will return the following outputs: + +LoadState=masked + +UnitFileState=masked + Is it the case that the "ntpdate" is loaded and not masked? + + + + Verify Red Hat Enterprise Linux 8 generates audit records for all account creations, modifications, disabling, and termination events that affect "/var/log/tallylog" with the following command: + +$ sudo auditctl -l | grep /var/log/tallylog + +-w /var/log/tallylog -p wa -k logins + Is it the case that the command does not return a line, or the line is commented out? + + + + Run the following command to verify that SSH client is configured to use 32 bytes of entropy: +grep SSH_USE_STRONG_RNG /etc/profile.d/cc-ssh-strong-rng.csh +It should return the following output: +setenv SSH_USE_STRONG_RNG 32. + Is it the case that SSH client is not configured to use 32 bytes of entropy or more? + + + + The runtime status of the net.ipv6.conf.all.max_addresses kernel parameter can be queried +by running the following command: +$ sysctl net.ipv6.conf.all.max_addresses +1. + + Is it the case that the correct value is not returned? + + + + +To check that the rsh service is disabled in system boot configuration with xinetd, run the following command: +$ chkconfig rsh --list +Output should indicate the rsh service has either not been installed, or has been disabled, as shown in the example below: +$ chkconfig rsh --list + +Note: This output shows SysV services only and does not include native +systemd services. SysV configuration data might be overridden by native +systemd configuration. + +If you want to list systemd services use 'systemctl list-unit-files'. +To see services enabled on particular target use +'systemctl list-dependencies [target]'. + +rsh off + +To check that the rsh socket is disabled in system boot configuration with systemd, run the following command: +$ systemctl is-enabled rsh +Output should indicate the rsh socket has either not been installed, +or has been disabled at all runlevels, as shown in the example below: +$ sudo systemctl is-enabled rshdisabled + +Run the following command to verify rsh is not active (i.e. not running) through current runtime configuration: +$ sudo systemctl is-active rsh + +If the socket is not running the command will return the following output: +inactive + +The socket will also be masked, to check that the rsh is masked, run the following command: +$ sudo systemctl show rsh | grep "LoadState\|UnitFileState" + +If the socket is masked the command will return the following outputs: + +LoadState=masked + +UnitFileState=masked + Is it the case that service and/or socket are running? + + + + To check for serial port entries which permit root login, +run the following command: +$ sudo grep ^ttyS/[0-9] /etc/securetty +If any output is returned, then root login over serial ports is permitted. + Is it the case that root login over serial ports is permitted? + + + + To determine if the system is configured to audit calls to the +open system call, run the following command: +$ sudo grep "open" /etc/audit/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + To check that the kdump service is disabled in system boot configuration, +run the following command: +$ sudo systemctl is-enabled kdump +Output should indicate the kdump service has either not been installed, +or has been disabled at all runlevels, as shown in the example below: +$ sudo systemctl is-enabled kdump disabled + +Run the following command to verify kdump is not active (i.e. not running) through current runtime configuration: +$ sudo systemctl is-active kdump + +If the service is not running the command will return the following output: +inactive + +The service will also be masked, to check that the kdump is masked, run the following command: +$ sudo systemctl show kdump | grep "LoadState\|UnitFileState" + +If the service is masked the command will return the following outputs: + +LoadState=masked + +UnitFileState=masked + Is it the case that the "kdump" is loaded and not masked? + + + + +To check that the telnet service is disabled in system boot configuration with xinetd, run the following command: +$ chkconfig telnet --list +Output should indicate the telnet service has either not been installed, or has been disabled, as shown in the example below: +$ chkconfig telnet --list + +Note: This output shows SysV services only and does not include native +systemd services. SysV configuration data might be overridden by native +systemd configuration. + +If you want to list systemd services use 'systemctl list-unit-files'. +To see services enabled on particular target use +'systemctl list-dependencies [target]'. + +telnet off + +To check that the telnet socket is disabled in system boot configuration with systemd, run the following command: +$ systemctl is-enabled telnet +Output should indicate the telnet socket has either not been installed, +or has been disabled at all runlevels, as shown in the example below: +$ sudo systemctl is-enabled telnetdisabled + +Run the following command to verify telnet is not active (i.e. not running) through current runtime configuration: +$ sudo systemctl is-active telnet + +If the socket is not running the command will return the following output: +inactive + +The socket will also be masked, to check that the telnet is masked, run the following command: +$ sudo systemctl show telnet | grep "LoadState\|UnitFileState" + +If the socket is masked the command will return the following outputs: + +LoadState=masked + +UnitFileState=masked + Is it the case that service and/or socket are running? + + + + The rsh package can be removed with the following command: $ sudo yum erase rsh + Is it the case that ? + + + + Verify the nodev option is configured for the /home mount point, + run the following command: + $ sudo mount | grep '\s/home\s' + . . . /home . . . nodev . . . + + Is it the case that the "/home" file system does not have the "nodev" option set? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_BINFMT_MISC /boot/config.* + + Configs with value 'n' are not explicitly set in the file, so either commented lines or no + lines should be returned. + + Is it the case that the kernel was not built with the required value? + + + + Verify that Red Hat Enterprise Linux 8 is configured to audit the execution of the "usermod" command with the following command: + +$ sudo auditctl -l | grep usermod + +-a always,exit -F path=/usr/bin/usermod -F perm=x -F auid>=1000 -F auid!=unset -k privileged-usermod + Is it the case that the command does not return a line, or the line is commented out? + + + + Run the following command to determine if the openssh-clients package is installed: $ rpm -q openssh-clients + Is it the case that the package is not installed? + + + + +Run the following command to determine if the glance_api_can_network SELinux boolean is disabled: +$ getsebool glance_api_can_network +If properly configured, the output should show the following: +glance_api_can_network --> off + Is it the case that glance_api_can_network is not disabled? + + + + + +Run the following command to determine the current status of the +usbguard service: +$ sudo systemctl is-active usbguard +If the service is running, it should return the following: active + Is it the case that the service is not enabled? + + + + +If the system is configured to prevent the loading of the mac80211 kernel module, +it will contain lines inside any file in /etc/modprobe.d or the deprecated /etc/modprobe.conf. +These lines instruct the module loading system to run another program (such as /bin/true) upon a module install event. + +These lines can also instruct the module loading system to ignore the mac80211 kernel module via blacklist keyword. + +Run the following command to search for such lines in all files in /etc/modprobe.d and the deprecated /etc/modprobe.conf: +$ grep -r mac80211 /etc/modprobe.conf /etc/modprobe.d + Is it the case that no line is returned? + + + + Verify "firewalld" is configured to employ a deny-all, allow-by-exception policy for allowing connections to other systems with the following commands: + +$ sudo firewall-cmd --state + +running + +$ sudo firewall-cmd --get-active-zones + +[custom] +interfaces: ens33 + +$ sudo firewall-cmd --info-zone=[custom] | grep target + +target: DROP + Is it the case that no zones are active on the interfaces or if the target is set to a different option other than "DROP"? + + + + To determine how the SSH daemon's HostbasedAuthentication option is set, run the following command: + +$ sudo grep -i HostbasedAuthentication /etc/ssh/sshd_config + +If a line indicating no is returned, then the required value is set. + + Is it the case that the required value is not set? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_COMPAT_BRK /boot/config.* + + Configs with value 'n' are not explicitly set in the file, so either commented lines or no + lines should be returned. + + Is it the case that the kernel was not built with the required value? + + + + +Run the following command to determine if the lsmd_plugin_connect_any SELinux boolean is disabled: +$ getsebool lsmd_plugin_connect_any +If properly configured, the output should show the following: +lsmd_plugin_connect_any --> off + Is it the case that lsmd_plugin_connect_any is not disabled? + + + + To determine how the SSH daemon's LogLevel option is set, run the following command: + +$ sudo grep -i LogLevel /etc/ssh/sshd_config + +If a line indicating VERBOSE is returned, then the required value is set. + + Is it the case that the required value is not set? + + + + Run the following command to determine if the gnutls-utils package is installed: $ rpm -q gnutls-utils + Is it the case that the package is not installed? + + + + Enter the following commands: + +grep Action /etc/httpd/conf/httpd.conf +grep AddHandler /etc/httpd/conf/httpd.conf + Is it the case that either of these exist and they configure csh, or any other shell as a viewer for documents? + + + + Verify that the SA and ISSO (at a minimum) are notified when the audit storage volume is full. + +Check which action Red Hat Enterprise Linux 8 takes when the audit storage volume is full with the following command: + +$ sudo grep max_log_file_action /etc/audit/auditd.conf +max_log_file_action = + Is it the case that the value of the "max_log_file_action" option is set to "ignore", "rotate", or "suspend", or the line is commented out? + + + + To verify that the Audit is correctly configured according to recommended rules, check the content of the file with the following command: +cat /etc/audit/rules.d/30-ospp-v42-6-owner-change-success.rules +The output has to be exactly as follows: +## Successful ownership change +-a always,exit -F arch=b32 -S lchown,fchown,chown,fchownat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-owner-change +-a always,exit -F arch=b64 -S lchown,fchown,chown,fchownat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-owner-change + Is it the case that the file does not exist or the content differs? + + + + Run the following command to determine if the python3-abrt-addon package is installed: +$ rpm -q python3-abrt-addon + Is it the case that the package is installed? + + + + Storing logs remotely protects the integrity of the data from local attacks. +Run the following command to verify that journald is forwarding logs to a remote host. + +grep "^\sForwardToSyslog" /etc/systemd/journald.conf + +and it should return + +ForwardToSyslog=yes + + Is it the case that is commented out or not configured correctly? + + + + +Run the following command to determine if the ftpd_full_access SELinux boolean is disabled: +$ getsebool ftpd_full_access +If properly configured, the output should show the following: +ftpd_full_access --> off + Is it the case that ftpd_full_access is not disabled? + + + + To determine if NOPASSWD has been configured for sudo, run the following command: +$ sudo grep -ri nopasswd /etc/sudoers /etc/sudoers.d/ +The command should return no output. + Is it the case that nopasswd is specified in the sudo config files? + + + + First, check whether the password is defined in either /boot/grub2/user.cfg or +/boot/grub2/grub.cfg. +Run the following commands: +$ sudo grep '^[\s]*GRUB2_PASSWORD=grub\.pbkdf2\.sha512.*$' /boot/grub2/user.cfg +$ sudo grep '^[\s]*password_pbkdf2[\s]+.*[\s]+grub\.pbkdf2\.sha512.*$' /boot/grub2/grub.cfg + + +Second, check that a superuser is defined in /boot/grub2/grub.cfg. +$ sudo grep '^[\s]*set[\s]+superusers=("?)[a-zA-Z_]+\1$' /boot/grub2/grub.cfg + Is it the case that it does not produce any output? + + + + Run the following command to determine if the libreport-plugin-logger package is installed: +$ rpm -q libreport-plugin-logger + Is it the case that the package is installed? + + + + To check how many uppercase characters are required in a password, run the following command: +$ grep ucredit /etc/security/pwquality.conf +The ucredit parameter (as a negative number) will indicate how many uppercase characters are required. +This would appear as ucredit = -1. + Is it the case that ucredit is not found or not set to the required value? + + + + +Run the following command to determine if the use_fusefs_home_dirs SELinux boolean is disabled: +$ getsebool use_fusefs_home_dirs +If properly configured, the output should show the following: +use_fusefs_home_dirs --> off + Is it the case that use_fusefs_home_dirs is not disabled? + + + + Verify the nosuid option is configured for the /srv mount point, + run the following command: + $ sudo mount | grep '\s/srv\s' + . . . /srv . . . nosuid . . . + + Is it the case that the "/srv" file system does not have the "nosuid" option set? + + + + Verify that the system backups user data. + Is it the case that it is not? + + + + To determine if the system is configured to audit successful calls +to the ftruncate system call, run the following command: +$ sudo grep "ftruncate" /etc/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + Run the following command to determine if the libreport-plugin-rhtsupport package is installed: +$ rpm -q libreport-plugin-rhtsupport + Is it the case that the package is installed? + + + + To verify that SSSD expires known SSH host keys, run the following command: +$ sudo grep ssh_known_hosts_timeout /etc/sssd/sssd.conf +If configured properly, output should be +ssh_known_hosts_timeout = + Is it the case that it does not exist or is not configured properly? + + + + +Run the following command to determine if the cobbler_use_nfs SELinux boolean is disabled: +$ getsebool cobbler_use_nfs +If properly configured, the output should show the following: +cobbler_use_nfs --> off + Is it the case that cobbler_use_nfs is not disabled? + + + + To check the ownership of /var/log/messages, +run the command: +$ ls -lL /var/log/messages +If properly configured, the output should indicate the following owner: +root + Is it the case that /var/log/messages does not have an owner of root? + + + + To determine if the system is configured to audit successful calls +to the openat system call, run the following command: +$ sudo grep "openat" /etc/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + +Run the following command to determine if the cluster_can_network_connect SELinux boolean is disabled: +$ getsebool cluster_can_network_connect +If properly configured, the output should show the following: +cluster_can_network_connect --> off + Is it the case that cluster_can_network_connect is not disabled? + + + + To verify that smart cards are enabled in SSSD, run the following command: +$ sudo grep pam_cert_auth /etc/sssd/sssd.conf +If configured properly, output should be +pam_cert_auth = True + + +To verify that smart cards are enabled in PAM files, run the following command: +$ sudo grep -e "auth.*pam_sss\.so.*\(allow_missing_name\|try_cert_auth\)" /etc/pam.d/smartcard-auth /etc/pam.d/system-auth +If configured properly, output should be + +/etc/pam.d/smartcard-auth:auth sufficient pam_sss.so allow_missing_name +/etc/pam.d/system-auth:auth [success=done authinfo_unavail=ignore ignore=ignore default=die] pam_sss.so try_cert_auth + + Is it the case that smart cards are not enabled in SSSD? + + + + +Run the following command to determine if the collectd_tcp_network_connect SELinux boolean is disabled: +$ getsebool collectd_tcp_network_connect +If properly configured, the output should show the following: +collectd_tcp_network_connect --> off + Is it the case that collectd_tcp_network_connect is not disabled? + + + + Verify the operating system encrypts audit records off-loaded onto a different system +or media from the system being audited with the following commands: + +$ sudo grep -i '$ActionSendStreamDriverMode' /etc/rsyslog.conf /etc/rsyslog.d/*.conf + +The output should be: + +/etc/rsyslog.conf:$ActionSendStreamDriverMode 1 + Is it the case that rsyslogd ActionSendStreamDriverMode is not set to 1? + + + + Verify the audit tools are owned by "root" to prevent any unauthorized access, deletion, or modification. + +Check the owner of each audit tool by running the following command: + +$ sudo stat -c "%U %n" /sbin/auditctl /sbin/aureport /sbin/ausearch /sbin/autrace /sbin/auditd /sbin/rsyslogd /sbin/augenrules + +root /sbin/auditctl +root /sbin/aureport +root /sbin/ausearch +root /sbin/autrace +root /sbin/auditd +root /sbin/rsyslogd +root /sbin/augenrules + Is it the case that any audit tools are not owned by root? + + + + To check that audit is enabled at boot time, check all boot entries with following command: +sudo grep -L "^options\s+.*\baudit=1\b" /boot/loader/entries/*.conf +No line should be returned, each line returned is a boot entry that doesn't enable audit. + Is it the case that auditing is not enabled at boot time? + + + + Verify the system-wide shared library directories are owned by "root" with the following command: + +$ sudo find /lib /lib64 /usr/lib /usr/lib64 ! -user root -type d -exec stat -c "%n %U" '{}' \; + Is it the case that any system-wide shared library directory is not owned by root? + + + + To check that the debug-shell service is disabled in system boot configuration, +run the following command: +$ sudo systemctl is-enabled debug-shell +Output should indicate the debug-shell service has either not been installed, +or has been disabled at all runlevels, as shown in the example below: +$ sudo systemctl is-enabled debug-shell disabled + +Run the following command to verify debug-shell is not active (i.e. not running) through current runtime configuration: +$ sudo systemctl is-active debug-shell + +If the service is not running the command will return the following output: +inactive + +The service will also be masked, to check that the debug-shell is masked, run the following command: +$ sudo systemctl show debug-shell | grep "LoadState\|UnitFileState" + +If the service is masked the command will return the following outputs: + +LoadState=masked + +UnitFileState=masked + Is it the case that the "debug-shell" is loaded and not masked? + + + + +Run the following command to get the current configured value for polyinstantiation_enabled +SELinux boolean: +$ getsebool polyinstantiation_enabled +The expected cofiguration is . +"on" means true, and "off" means false + Is it the case that polyinstantiation_enabled is not set as expected? + + + + Verify that Red Hat Enterprise Linux 8 is configured to audit the execution of the "unix_update" command with the following command: + +$ sudo auditctl -l | grep unix_update + +-a always,exit -F path=/usr/bin/unix_update -F perm=x -F auid>=1000 -F auid!=unset -k privileged-unix_update + Is it the case that the command does not return a line, or the line is commented out? + + + + The runtime status of the kernel.dmesg_restrict kernel parameter can be queried +by running the following command: +$ sysctl kernel.dmesg_restrict +1. + + Is it the case that the correct value is not returned? + + + + The runtime status of the net.ipv4.conf.all.arp_filter kernel parameter can be queried +by running the following command: +$ sysctl net.ipv4.conf.all.arp_filter +. + + Is it the case that the correct value is not returned? + + + + To determine if NOPASSWD has been configured for the vdsm user for sudo, +run the following command: +$ sudo grep -ri nopasswd /etc/sudoers.d/ +The command should return output only for the vdsm user. + Is it the case that nopasswd is set for any users beyond vdsm? + + + + +Run the following command to determine if the ssh_chroot_rw_homedirs SELinux boolean is disabled: +$ getsebool ssh_chroot_rw_homedirs +If properly configured, the output should show the following: +ssh_chroot_rw_homedirs --> off + Is it the case that ssh_chroot_rw_homedirs is not disabled? + + + + To ensure sshd limits the users who can log in, run the following: +$ sudo grep AllowUsers /etc/ssh/sshd_config +If properly configured, the output should be a list of usernames allowed to log in +to this system. + Is it the case that sshd does not limit the users who can log in? + + + + Verify Red Hat Enterprise Linux 8 generates an audit record for unsuccessful attempts to use the truncate system call. + +If the auditd daemon is configured to use the "augenrules" program to to read audit rules during daemon startup (the default), run the following command: + +$ sudo grep -r truncate /etc/audit/rules.d + +If the auditd daemon is configured to use the "auditctl" utility to read audit rules during daemon startup, run the following command: + +$ sudo grep truncate /etc/audit/audit.rules + +The output should be the following: + +-a always,exit -F arch=b32 -S truncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -k access +-a always,exit -F arch=b64 -S truncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -k access +-a always,exit -F arch=b32 -S truncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -k access +-a always,exit -F arch=b64 -S truncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -k access + Is it the case that the command does not return a line, or the line is commented out? + + + + To verify that session locking after period of inactivity is configured in tmux, +run the following command: + +$ sudo grep lock-after-time /etc/tmux.conf + +The output should return the following: + +set -g lock-after-time 900 + +Then, verify that the /etc/tmux.conf file can be read by other users than root: + +$ sudo ls -al /etc/tmux.conf + Is it the case that lock-after-time is set to a value greater than 900 or zero? + + + + Verify that cron is logging to rsyslog, +run the following command: +grep -rni "cron\.\*" /etc/rsyslog.* +cron.* /var/log/cron + Is it the case that cron is not logging to rsyslog? + + + + +Run the following command to determine if the exim_manage_user_files SELinux boolean is disabled: +$ getsebool exim_manage_user_files +If properly configured, the output should show the following: +exim_manage_user_files --> off + Is it the case that exim_manage_user_files is not disabled? + + + + Run the following command to determine if the tmux package is installed: $ rpm -q tmux + Is it the case that the package is not installed? + + + + +Run the following command to determine if the virt_transition_userdomain SELinux boolean is disabled: +$ getsebool virt_transition_userdomain +If properly configured, the output should show the following: +virt_transition_userdomain --> off + Is it the case that virt_transition_userdomain is not disabled? + + + + +Run the following command to determine if the polipo_use_nfs SELinux boolean is disabled: +$ getsebool polipo_use_nfs +If properly configured, the output should show the following: +polipo_use_nfs --> off + Is it the case that polipo_use_nfs is not disabled? + + + + +Run the following command to determine if the rsync_client SELinux boolean is disabled: +$ getsebool rsync_client +If properly configured, the output should show the following: +rsync_client --> off + Is it the case that rsync_client is not disabled? + + + + +Run the following command to determine if the mount_anyfile SELinux boolean is enabled: +$ getsebool mount_anyfile +If properly configured, the output should show the following: +mount_anyfile --> on + Is it the case that mount_anyfile is not enabled? + + + + +Run the following command to determine if the httpd_dbus_avahi SELinux boolean is disabled: +$ getsebool httpd_dbus_avahi +If properly configured, the output should show the following: +httpd_dbus_avahi --> off + Is it the case that httpd_dbus_avahi is not disabled? + + + + Verify that Red Hat Enterprise Linux 8 is configured to audit the execution of the "unix_chkpwd" command with the following command: + +$ sudo auditctl -l | grep unix_chkpwd + +-a always,exit -F path=/usr/bin/unix_chkpwd -F perm=x -F auid>=1000 -F auid!=unset -k privileged-unix_chkpwd + Is it the case that the command does not return a line, or the line is commented out? + + + + +To check that the rexec service is disabled in system boot configuration with xinetd, run the following command: +$ chkconfig rexec --list +Output should indicate the rexec service has either not been installed, or has been disabled, as shown in the example below: +$ chkconfig rexec --list + +Note: This output shows SysV services only and does not include native +systemd services. SysV configuration data might be overridden by native +systemd configuration. + +If you want to list systemd services use 'systemctl list-unit-files'. +To see services enabled on particular target use +'systemctl list-dependencies [target]'. + +rexec off + +To check that the rexec socket is disabled in system boot configuration with systemd, run the following command: +$ systemctl is-enabled rexec +Output should indicate the rexec socket has either not been installed, +or has been disabled at all runlevels, as shown in the example below: +$ sudo systemctl is-enabled rexecdisabled + +Run the following command to verify rexec is not active (i.e. not running) through current runtime configuration: +$ sudo systemctl is-active rexec + +If the socket is not running the command will return the following output: +inactive + +The socket will also be masked, to check that the rexec is masked, run the following command: +$ sudo systemctl show rexec | grep "LoadState\|UnitFileState" + +If the socket is masked the command will return the following outputs: + +LoadState=masked + +UnitFileState=masked + Is it the case that service and/or socket are running? + + + + To check that the zebra service is disabled in system boot configuration, +run the following command: +$ sudo systemctl is-enabled zebra +Output should indicate the zebra service has either not been installed, +or has been disabled at all runlevels, as shown in the example below: +$ sudo systemctl is-enabled zebra disabled + +Run the following command to verify zebra is not active (i.e. not running) through current runtime configuration: +$ sudo systemctl is-active zebra + +If the service is not running the command will return the following output: +inactive + +The service will also be masked, to check that the zebra is masked, run the following command: +$ sudo systemctl show zebra | grep "LoadState\|UnitFileState" + +If the service is masked the command will return the following outputs: + +LoadState=masked + +UnitFileState=masked + Is it the case that the "zebra" is loaded and not masked? + + + + To verify the sec option is configured for all NFS mounts, run the following command: +$ grep "sec=" /etc/exports +All configured NFS exports should show the sec=krb5:krb5i:krb5p setting in parentheses. +This is not applicable if NFS is not implemented. + Is it the case that the setting is not configured, has the 'sys' option added, or does not have all Kerberos options added? + + + + To verify if SSLVerifyClient is configured correctly in +/etc/httpd/conf/httpd.conf, run the following command: +$ grep -i sslverifyclient /etc/httpd/conf/httpd.conf +The command should return the following: +SSLVerifyClient require + Is it the case that it is not? + + + + +Run the following command to determine if the daemons_use_tcp_wrapper SELinux boolean is disabled: +$ getsebool daemons_use_tcp_wrapper +If properly configured, the output should show the following: +daemons_use_tcp_wrapper --> off + Is it the case that daemons_use_tcp_wrapper is not disabled? + + + + To verify if GnuTLS uses defined DoD-approved TLS Crypto Policy, run: +$ sudo grep +'+VERS-ALL:-VERS-DTLS0.9:-VERS-SSL3.0:-VERS-TLS1.0:-VERS-TLS1.1:-VERS-DTLS1.0' +/etc/crypto-policies/back-ends/gnutls.config and verify that a match exists. + Is it the case that cryptographic policy for gnutls is not configured or is configured incorrectly? + + + + Verify that a separate file system/partition has been created for /var/tmp with the following command: + +$ mountpoint /var/tmp + + Is it the case that "/var/tmp is not a mountpoint" is returned? + + + + To determine if the system is configured to audit unsuccessful calls +to the removexattr system call, run the following command: +$ sudo grep "removexattr" /etc/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + If IPv6 is disabled, this is not applicable. + + + +Run the following command to determine the current status of the +ip6tables service: +$ sudo systemctl is-active ip6tables +If the service is running, it should return the following: active + Is it the case that ? + + + + +Run the following command to determine if the virt_use_usb SELinux boolean is disabled: +$ getsebool virt_use_usb +If properly configured, the output should show the following: +virt_use_usb --> off + Is it the case that virt_use_usb is not disabled? + + + + Verify Red Hat Enterprise Linux 8 generates audit records for all account creations, modifications, disabling, and termination events that affect "/etc/security/opasswd" with the following command: + +$ sudo auditctl -l | grep /var/run/faillock + +-w /var/run/faillock -p wa -k logins + Is it the case that the command does not return a line, or the line is commented out? + + + + +Run the following command to determine if the rsync_export_all_ro SELinux boolean is disabled: +$ getsebool rsync_export_all_ro +If properly configured, the output should show the following: +rsync_export_all_ro --> off + Is it the case that rsync_export_all_ro is not disabled? + + + + To verify if the mod_perl is installed, run the following command: +$ rpm -qa | grep mod_perl +If the mod_perl module is installed, verify that PerlSwitches -T +is enabled in /etc/httpd/conf.d/perl.conf by running the following +command: +$ grep -i "PerlSwitches -T" /etc/httpd/conf.d/perl.conf +The output should return uncommented: +PerlSwitches -T + Is it the case that it is not? + + + + +Run the following command to determine if the logging_syslogd_use_tty SELinux boolean is enabled: +$ getsebool logging_syslogd_use_tty +If properly configured, the output should show the following: +logging_syslogd_use_tty --> on + Is it the case that logging_syslogd_use_tty is not enabled? + + + + To check the screensaver rhisam use status, run the following command: +$ gsettings get org.gnome.desktop.screensaver idle-activation-enabled +If properly configured, the output should be true. +To ensure that users cannot disable the screensaver idle inactivity setting, run the following: +$ grep idle-activation-enabled /etc/dconf/db/local.d/locks/* +If properly configured, the output should be /org/gnome/desktop/screensaver/idle-activation-enabled + Is it the case that idle-activation-enabled is not enabled or configured? + + + + Inspect the mounts configured in /etc/exports. Each mount should specify a value +greater than UID_MAX and GID_MAX as defined in /etc/login.defs. + Is it the case that anonuid or anongid are not set to a value greater than UID_MAX (for anonuid) and GID_MAX (for anongid)? + + + + +Run the following command to determine if the kerberos_enabled SELinux boolean is enabled: +$ getsebool kerberos_enabled +If properly configured, the output should show the following: +kerberos_enabled --> on + Is it the case that kerberos_enabled is not enabled? + + + + +Run the following command to determine if the mcelog_foreground SELinux boolean is disabled: +$ getsebool mcelog_foreground +If properly configured, the output should show the following: +mcelog_foreground --> off + Is it the case that mcelog_foreground is not disabled? + + + + Verify the audit tools are group-owned by "root" to prevent any unauthorized access, deletion, or modification. + +Check the group-owner of each audit tool by running the following command: + +$ sudo stat -c "%G %n" /sbin/auditctl /sbin/aureport /sbin/ausearch /sbin/autrace /sbin/auditd /sbin/rsyslogd /sbin/augenrules + +root /sbin/auditctl +root /sbin/aureport +root /sbin/ausearch +root /sbin/autrace +root /sbin/auditd +root /sbin/rsyslogd +root /sbin/augenrules + Is it the case that any audit tools are not group-owned by root? + + + + Verify that Red Hat Enterprise Linux 8 is configured to audit the execution of the "userhelper" command with the following command: + +$ sudo auditctl -l | grep userhelper + +-a always,exit -F path=/usr/bin/userhelper -F perm=x -F auid>=1000 -F auid!=unset -k privileged-userhelper + Is it the case that the command does not return a line, or the line is commented out? + + + + Verify that a separate file system/partition has been created for /home with the following command: + +$ mountpoint /home + + Is it the case that "/home is not a mountpoint" is returned? + + + + Check to see if Online Certificate Status Protocol (OCSP) +is enabled and using the proper digest value on the system with the following command: +$ sudo grep certificate_verification /etc/sssd/sssd.conf /etc/sssd/conf.d/*.conf | grep -v "^#" +If configured properly, output should look like + + certificate_verification = ocsp_dgst= + + Is it the case that certificate_verification in sssd is not configured? + + + + To verify that kernel parameter 'crypto.fips_enabled' is set properly, run the following command: +sysctl crypto.fips_enabled +The output should contain the following: +crypto.fips_enabled = 1 + Is it the case that crypto.fips_enabled is not 1? + + + + To check the maximum password age, run the command: +$ grep PASS_MAX_DAYS /etc/login.defs +The profile requirement is . + Is it the case that PASS_MAX_DAYS is not set equal to or greater than the required value? + + + + Run the following command to determine if the openssh-server package is installed: $ rpm -q openssh-server + Is it the case that the package is installed? + + + + Verify the nosuid option is configured for the /home mount point, + run the following command: + $ sudo mount | grep '\s/home\s' + . . . /home . . . nosuid . . . + + Is it the case that the "/home" file system does not have the "nosuid" option set? + + + + +Run the following command to determine if the ssh_keysign SELinux boolean is disabled: +$ getsebool ssh_keysign +If properly configured, the output should show the following: +ssh_keysign --> off + Is it the case that ssh_keysign is not disabled? + + + + +Run the following command to determine if the mozilla_plugin_can_network_connect SELinux boolean is disabled: +$ getsebool mozilla_plugin_can_network_connect +If properly configured, the output should show the following: +mozilla_plugin_can_network_connect --> off + Is it the case that mozilla_plugin_can_network_connect is not disabled? + + + + To verify the operating system implements cryptography to protect the integrity of +remote ldap access sessions, run the following command: +$ sudo grep ldap_tls_cacert /etc/sssd/sssd.conf +The output should return the following with a correctly configured CA cert path: +ldap_tls_cacert /path/to/tls/ca.cert + Is it the case that the TLS CA cert is not configured? + + + + Verify that authselect is enabled by running +authselect current +If authselect is enabled on the system, the output should show the ID of the profile which is currently in use. + Is it the case that authselect is not used to manage user authentication setup on the system? + + + + +If the system is configured to prevent the loading of the iwlwifi kernel module, +it will contain lines inside any file in /etc/modprobe.d or the deprecated /etc/modprobe.conf. +These lines instruct the module loading system to run another program (such as /bin/true) upon a module install event. + +These lines can also instruct the module loading system to ignore the iwlwifi kernel module via blacklist keyword. + +Run the following command to search for such lines in all files in /etc/modprobe.d and the deprecated /etc/modprobe.conf: +$ grep -r iwlwifi /etc/modprobe.conf /etc/modprobe.d + Is it the case that no line is returned? + + + + To verify that tmux is configured to execute, +run the following command: +$ grep -A1 -B3 "case ..name. in sshd|login) exec tmux ;; esac" /etc/bashrc /etc/profile.d/* +The output should return the following: +if [ "$PS1" ]; then + parent=$(ps -o ppid= -p $$) + name=$(ps -o comm= -p $parent) + case "$name" in sshd|login) exec tmux ;; esac +fi + Is it the case that exec tmux is not present at the end of bashrc? + + + + Inspect /etc/audit/auditd.conf and locate the following line to +determine how many logs the system is configured to retain after rotation: +$ sudo grep num_logs /etc/audit/auditd.conf +num_logs = 5 + Is it the case that the system log file retention has not been properly configured? + + + + Verify that the IPSec service uses the system crypto policy. + +If the ipsec service is not installed is not applicable. + +Check to see if the "IPsec" service is active with the following command: + +$ systemctl status ipsec + +ipsec.service - Internet Key Exchange (IKE) Protocol Daemon for IPsec +Loaded: loaded (/usr/lib/systemd/system/ipsec.service; disabled) +Active: inactive (dead) + +If the "IPsec" service is active, check to see if it is using the system crypto policy with the following command: + +$ sudo grep include /etc/ipsec.conf /etc/ipsec.d/*.conf + +/etc/ipsec.conf:include /etc/crypto-policies/back-ends/libreswan.config + Is it the case that the "IPsec" service is active and the ipsec configuration file does not contain does not contain <tt>include /etc/crypto-policies/back-ends/libreswan.config</tt>? + + + + To verify that USB Human Interface Devices will be authorized by the USBGuard daemon, +run the following command: +$ sudo grep allow /etc/usbguard/rules.conf +The output lines should include +allow with-interface match-all { 03:*:* } + Is it the case that USB devices of class 3 are not authorized? + + + + To verify that Audit Daemon is configured to flush to disk after +every records, run the following command: +$ sudo grep freq /etc/audit/auditd.conf +The output should return the following: +freq = + Is it the case that freq isn't set to <sub idref="var_auditd_freq" />? + + + + +Run the following command to determine if the mplayer_execstack SELinux boolean is disabled: +$ getsebool mplayer_execstack +If properly configured, the output should show the following: +mplayer_execstack --> off + Is it the case that mplayer_execstack is not disabled? + + + + Run the following command to check the mode of the httpd log +directory: +$ ls -l /var/log/ | grep httpd +Log directory must be mode 0700 or less permissive. + Is it the case that it is more permissive? + + + + +Run the following command to determine if the fcron_crond SELinux boolean is disabled: +$ getsebool fcron_crond +If properly configured, the output should show the following: +fcron_crond --> off + Is it the case that fcron_crond is not disabled? + + + + +Run the following command to determine if the exim_read_user_files SELinux boolean is disabled: +$ getsebool exim_read_user_files +If properly configured, the output should show the following: +exim_read_user_files --> off + Is it the case that exim_read_user_files is not disabled? + + + + Check whether the maximum time period for existing passwords is restricted to days with the following commands: + +$ sudo awk -F: '$5 > 60 {print $1 " " $5}' /etc/shadow + +$ sudo awk -F: '$5 <= 0 {print $1 " " $5}' /etc/shadow + Is it the case that any results are returned that are not associated with a system account? + + + + To determine if the system is configured to audit unsuccessful calls +to the fchown system call, run the following command: +$ sudo grep "fchown" /etc/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + The runtime status of the kernel.pid_max kernel parameter can be queried +by running the following command: +$ sysctl kernel.pid_max +65536. + + Is it the case that the correct value is not returned? + + + + To check the group ownership of /etc/ssh/sshd_config, +run the command: +$ ls -lL /etc/ssh/sshd_config +If properly configured, the output should indicate the following group-owner: +root + Is it the case that /etc/ssh/sshd_config does not have a group owner of root? + + + + Verify the nosuid option is configured for the /boot mount point, + run the following command: + $ sudo mount | grep '\s/boot\s' + . . . /boot . . . nosuid . . . + + Is it the case that the "/boot" file system does not have the "nosuid" option set? + + + + Verify Red Hat Enterprise Linux 8 is configured to lock an account after +unsuccessful logon attempts with the command: + + +$ grep 'deny =' /etc/security/faillock.conf +deny = . + Is it the case that the "deny" option is not set to "<sub idref="var_accounts_passwords_pam_faillock_deny" />" +or less (but not "0"), is missing or commented out? + + + + Run the following command to determine if the dovecot package is installed: +$ rpm -q dovecot + Is it the case that the package is installed? + + + + +If the system is configured to prevent the loading of the usb-storage kernel module, +it will contain lines inside any file in /etc/modprobe.d or the deprecated /etc/modprobe.conf. +These lines instruct the module loading system to run another program (such as /bin/true) upon a module install event. + +These lines can also instruct the module loading system to ignore the usb-storage kernel module via blacklist keyword. + +Run the following command to search for such lines in all files in /etc/modprobe.d and the deprecated /etc/modprobe.conf: +$ grep -r usb-storage /etc/modprobe.conf /etc/modprobe.d + Is it the case that no line is returned? + + + + Check the root home directory for a .mozilla directory. If +one exists, ensure browsing is limited to local service administration. + Is it the case that this is not the case? + + + + To check the group ownership of /etc/cron.hourly, +run the command: +$ ls -lL /etc/cron.hourly +If properly configured, the output should indicate the following group-owner: +root + Is it the case that /etc/cron.hourly does not have a group owner of root? + + + + +Run the following command to determine if the virt_sandbox_use_netlink SELinux boolean is disabled: +$ getsebool virt_sandbox_use_netlink +If properly configured, the output should show the following: +virt_sandbox_use_netlink --> off + Is it the case that virt_sandbox_use_netlink is not disabled? + + + + +Run the following command to determine if the ftpd_anon_write SELinux boolean is disabled: +$ getsebool ftpd_anon_write +If properly configured, the output should show the following: +ftpd_anon_write --> off + Is it the case that ftpd_anon_write is not disabled? + + + + To determine if requiretty has been configured for sudo, run the following command: +$ sudo grep -ri "^[\s]*Defaults.*\brequiretty\b.*" /etc/sudoers /etc/sudoers.d/ +The command should return a matching output. + Is it the case that requiretty is not enabled in sudo? + + + + To check the permissions of /etc/gshadow-, +run the command: +$ ls -l /etc/gshadow- +If properly configured, the output should indicate the following permissions: +---------- + Is it the case that /etc/gshadow- does not have unix mode ----------? + + + + To verify that port has been set properly, perform the following: +$ grep '\bport\b' /etc/chrony.conf +The output should return +port 0 + Is it the case that port is not set or port is set to a non-zero value? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_REFCOUNT_FULL /boot/config.* + + For each kernel installed, a line with value "y" should be returned. + + Is it the case that the kernel was not built with the required value? + + + + The following command will discover and print world-writable directories that +are not group owned by a system account, given the assumption that only system +accounts have a gid lower than 1000. Run it once for each local partition PART: +$ sudo find PART -xdev -type d -perm -0002 -gid +999 -print + Is it the case that there is output? + + + + The runtime status of the kernel.modules_disabled kernel parameter can be queried +by running the following command: +$ sysctl kernel.modules_disabled +1. + + Is it the case that the correct value is not returned? + + + + To verify that cryptography policy has been configured correctly, run the +following command: +$ update-crypto-policies --show +The output should return . +Run the command to check if the policy is correctly applied: +$ update-crypto-policies --is-applied +The output should be The configured policy is applied. +Moreover, check if settings for selected crypto policy are as expected. +List all libraries for which it holds that their crypto policies do not have symbolic link in /etc/crypto-policies/back-ends. +$ ls -l /etc/crypto-policies/back-ends/ | grep '^[^l]' | tail -n +2 | awk -F' ' '{print $NF}' | awk -F'.' '{print $1}' | sort +Subsequently, check if matching libraries have drop in files in the /etc/crypto-policies/local.d directory. +$ ls /etc/crypto-policies/local.d/ | awk -F'-' '{print $1}' | uniq | sort +Outputs of two previous commands should match. + Is it the case that cryptographic policy is not configured or is configured incorrectly? + + + + To check if the system login banner is compliant, +run the following command: +$ cat /etc/motd + Is it the case that it does not display the required banner? + + + + Run the following command to check the mode of the system audit logs: +$ sudo grep -iw log_file /etc/audit/auditd.conf +log_file=/var/log/audit/audit.log +$ sudo stat -c "%n %a" /var/log/audit/* +$ sudo ls -l /var/log/audit +Audit logs must be mode 0640 or less permissive. + Is it the case that any permissions are more permissive? + + + + +Run the following command to determine if the mozilla_plugin_use_gps SELinux boolean is disabled: +$ getsebool mozilla_plugin_use_gps +If properly configured, the output should show the following: +mozilla_plugin_use_gps --> off + Is it the case that mozilla_plugin_use_gps is not disabled? + + + + Run the following command to determine if the nfs-utils package is installed: +$ rpm -q nfs-utils + Is it the case that the package is installed? + + + + +Run the following command to determine if the nfs_export_all_ro SELinux boolean is enabled: +$ getsebool nfs_export_all_ro +If properly configured, the output should show the following: +nfs_export_all_ro --> on + Is it the case that nfs_export_all_ro is not enabled? + + + + Make sure that /boot/bootmap is newer than /boot/loader/entries/*.conf +and /etc/zipl.conf: +find /boot/loader/entries/*.conf /etc/zipl.conf -newer /boot/bootmap +No line should be returned, if a line is returned /boot/bootmap is outdated and needs to be regenerated. + Is it the case that the bootmap is outdated? + + + + Run the following command to determine if the McAfeeTP package is installed: $ rpm -q McAfeeTP + Is it the case that the package is not installed? + + + + To check the group ownership of /etc/motd, +run the command: +$ ls -lL /etc/motd +If properly configured, the output should indicate the following group-owner: +root + Is it the case that /etc/motd does not have a group owner of root? + + + + Run the following command to determine if the syslog-ng-core package is installed: $ rpm -q syslog-ng-core + Is it the case that the package is not installed? + + + + +Run the following command to determine if the glance_use_fusefs SELinux boolean is disabled: +$ getsebool glance_use_fusefs +If properly configured, the output should show the following: +glance_use_fusefs --> off + Is it the case that glance_use_fusefs is not disabled? + + + + To ensure there are no read-write users, run the following command: +$ sudo grep -v "^#" /etc/snmp/snmpd.conf| grep 'rwuser' +There should be no output. + Is it the case that there are users who can write to SNMP values? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_PANIC_ON_OOPS /boot/config.* + + For each kernel installed, a line with value "y" should be returned. + + Is it the case that the kernel was not built with the required value? + + + + To ensure the screensaver is configured to be blank, run the following command: +$ gsettings get org.gnome.desktop.screensaver picture-uri +If properly configured, the output should be ''. + +To ensure that users cannot set the screensaver background, run the following: +$ grep picture-uri /etc/dconf/db/local.d/locks/* +If properly configured, the output should be /org/gnome/desktop/screensaver/picture-uri + Is it the case that it is not set or configured properly? + + + + To check the permissions of /etc/cron.d, +run the command: +$ ls -l /etc/cron.d +If properly configured, the output should indicate the following permissions: +-rwx------ + Is it the case that /etc/cron.d does not have unix mode -rwx------? + + + + Run the following command and verify remote server is configured properly: +# grep -E "^(server|pool)" /etc/chrony.conf + Is it the case that a remote time server is not configured? + + + + Inspect /etc/audit/auditd.conf and locate the following line to +determine how much data the system will retain in each audit log file: +$ sudo grep max_log_file /etc/audit/auditd.conf +max_log_file = 6 + Is it the case that the system audit data threshold has not been properly configured? + + + + Verify all local interactive users on Red Hat Enterprise Linux 8 are assigned a home +directory upon creation with the following command: +$ grep -i create_home /etc/login.defs +CREATE_HOME yes + Is it the case that the value for "CREATE_HOME" parameter is not set to "yes", the line is missing, or the line is commented out? + + + + To ensure the system is configured to ignore the Ctrl-Alt-Del setting, +enter the following command: +$ sudo grep -i ctrlaltdelburstaction /etc/systemd/system.conf +The output should return: +CtrlAltDelBurstAction=none + Is it the case that the system is configured to reboot when Ctrl-Alt-Del is pressed more than 7 times in 2 seconds.? + + + + +Run the following command to determine if the httpd_manage_ipa SELinux boolean is disabled: +$ getsebool httpd_manage_ipa +If properly configured, the output should show the following: +httpd_manage_ipa --> off + Is it the case that httpd_manage_ipa is not disabled? + + + + To verify that DHCP is not being used, examine the following file for each interface: +# /etc/sysconfig/network-scripts/ifcfg-interface +Look for the following: +BOOTPROTO=none +and the following, substituting the appropriate values based on your site's addressing scheme: +NETMASK=255.255.255.0 +IPADDR=192.168.1.2 +GATEWAY=192.168.1.1 + Is it the case that it does not? + + + + To check the ownership of /etc/cron.hourly, +run the command: +$ ls -lL /etc/cron.hourly +If properly configured, the output should indicate the following owner: +root + Is it the case that /etc/cron.hourly does not have an owner of root? + + + + +Run the following command to determine if the boinc_execmem SELinux boolean is disabled: +$ getsebool boinc_execmem +If properly configured, the output should show the following: +boinc_execmem --> off + Is it the case that boinc_execmem is not disabled? + + + + Verify that rules for unsuccessful calls of the open syscall are in the order shown below. + + If the auditd daemon is configured to use the "augenrules" program to read audit rules during daemon startup (the default), check the order of rules below in a file with suffix ".rules" in the directory "/etc/audit/rules.d". + If the auditd daemon is configured to use the "auditctl" utility to read audit rules during daemon startup, check the order of rules below in "/etc/audit/audit.rules" file. + + -a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b32 -S open -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-access + -a always,exit -F arch=b32 -S open -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-access + + If the system is 64 bit then also add the following lines: + + -a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b64 -S open -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-access + -a always,exit -F arch=b64 -S open -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-access + Is it the case that the rules are in a different order? + + + + To determine if the system is configured to audit successful calls +to the open_by_handle_at system call, run the following command: +$ sudo grep "open_by_handle_at" /etc/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + To check that the snmpd service is disabled in system boot configuration, +run the following command: +$ sudo systemctl is-enabled snmpd +Output should indicate the snmpd service has either not been installed, +or has been disabled at all runlevels, as shown in the example below: +$ sudo systemctl is-enabled snmpd disabled + +Run the following command to verify snmpd is not active (i.e. not running) through current runtime configuration: +$ sudo systemctl is-active snmpd + +If the service is not running the command will return the following output: +inactive + +The service will also be masked, to check that the snmpd is masked, run the following command: +$ sudo systemctl show snmpd | grep "LoadState\|UnitFileState" + +If the service is masked the command will return the following outputs: + +LoadState=masked + +UnitFileState=masked + Is it the case that the "snmpd" is loaded and not masked? + + + + The runtime status of the net.ipv4.conf.all.arp_ignore kernel parameter can be queried +by running the following command: +$ sysctl net.ipv4.conf.all.arp_ignore +. + + Is it the case that the correct value is not returned? + + + + Run the following command to determine if the krb5-workstation package is installed: +$ rpm -q krb5-workstation + Is it the case that the package is installed? + + + + To determine how the SSH daemon's IgnoreUserKnownHosts option is set, run the following command: + +$ sudo grep -i IgnoreUserKnownHosts /etc/ssh/sshd_config + +If a line indicating yes is returned, then the required value is set. + + Is it the case that the required value is not set? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_SECURITY_YAMA /boot/config.* + + For each kernel installed, a line with value "y" should be returned. + + Is it the case that the kernel was not built with the required value? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_MODULE_SIG_ALL /boot/config.* + + For each kernel installed, a line with value "y" should be returned. + + Is it the case that the kernel was not built with the required value? + + + + To check the permissions of /etc/cron.weekly, +run the command: +$ ls -l /etc/cron.weekly +If properly configured, the output should indicate the following permissions: +-rwx------ + Is it the case that /etc/cron.weekly does not have unix mode -rwx------? + + + + To check that all boot entries extend the backlog limit; +Check that all boot entries extend the log events queue: +sudo grep -L "^options\s+.*\baudit_backlog_limit=8192\b" /boot/loader/entries/*.conf +No line should be returned, each line returned is a boot entry that does not extend the log events queue. + Is it the case that audit backlog limit is not configured? + + + + Run the following command to determine if the quagga package is installed: +$ rpm -q quagga + Is it the case that the package is installed? + + + + To check if only local user are impacted by pam_faillock, run the following command: +$ grep local_users_only /etc/security/faillock.conf +The output should return local_users_only not commented. + Is it the case that local_users_only is not uncommented or configured correctly? + + + + To check that the abrtd service is disabled in system boot configuration, +run the following command: +$ sudo systemctl is-enabled abrtd +Output should indicate the abrtd service has either not been installed, +or has been disabled at all runlevels, as shown in the example below: +$ sudo systemctl is-enabled abrtd disabled + +Run the following command to verify abrtd is not active (i.e. not running) through current runtime configuration: +$ sudo systemctl is-active abrtd + +If the service is not running the command will return the following output: +inactive + +The service will also be masked, to check that the abrtd is masked, run the following command: +$ sudo systemctl show abrtd | grep "LoadState\|UnitFileState" + +If the service is masked the command will return the following outputs: + +LoadState=masked + +UnitFileState=masked + Is it the case that the "abrtd" is loaded and not masked? + + + + +Run the following command to determine if the cobbler_can_network_connect SELinux boolean is disabled: +$ getsebool cobbler_can_network_connect +If properly configured, the output should show the following: +cobbler_can_network_connect --> off + Is it the case that cobbler_can_network_connect is not disabled? + + + + To check the permissions of /etc/cron.hourly, +run the command: +$ ls -l /etc/cron.hourly +If properly configured, the output should indicate the following permissions: +-rwx------ + Is it the case that /etc/cron.hourly does not have unix mode -rwx------? + + + + The runtime status of the net.ipv4.conf.default.secure_redirects kernel parameter can be queried +by running the following command: +$ sysctl net.ipv4.conf.default.secure_redirects +0. + + Is it the case that the correct value is not returned? + + + + Verify that Red Hat Enterprise Linux 8 is configured to audit the execution of the "passwd" command with the following command: + +$ sudo auditctl -l | grep passwd + +-a always,exit -F path=/usr/bin/passwd -F perm=x -F auid>=1000 -F auid!=unset -k privileged-passwd + Is it the case that the command does not return a line, or the line is commented out? + + + + To check the permissions of /etc/cron.monthly, +run the command: +$ ls -l /etc/cron.monthly +If properly configured, the output should indicate the following permissions: +-rwx------ + Is it the case that /etc/cron.monthly does not have unix mode -rwx------? + + + + To verify the assigned home directory of all interactive users is group- +owned by that users primary GID, run the following command: +# ls -ld $(awk -F: '($3>=1000)&&($7 !~ /nologin/){print $6}' /etc/passwd) + Is it the case that the group ownership is incorrect? + + + + To verify that the Audit is correctly configured according to recommended rules, check the content of the file with the following command: +cat /etc/audit/rules.d/30-ospp-v42-4-delete-success.rules +The output has to be exactly as follows: +## Successful file delete +-a always,exit -F arch=b32 -S unlink,unlinkat,rename,renameat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-delete +-a always,exit -F arch=b64 -S unlink,unlinkat,rename,renameat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-delete + Is it the case that the file does not exist or the content differs? + + + + +Run the following command to determine if the logging_syslogd_run_nagios_plugins SELinux boolean is disabled: +$ getsebool logging_syslogd_run_nagios_plugins +If properly configured, the output should show the following: +logging_syslogd_run_nagios_plugins --> off + Is it the case that logging_syslogd_run_nagios_plugins is not disabled? + + + + +Run the following command to determine if the named_tcp_bind_http_port SELinux boolean is disabled: +$ getsebool named_tcp_bind_http_port +If properly configured, the output should show the following: +named_tcp_bind_http_port --> off + Is it the case that named_tcp_bind_http_port is not disabled? + + + + Run the following command to determine if the geolite2-city package is installed: +$ rpm -q geolite2-city + Is it the case that the package is installed? + + + + Run the following command to determine if the chrony package is installed: $ rpm -q chrony + Is it the case that the package is not installed? + + + + Verify the UMASK setting is not configured for interactive users, +run the following command: +$ sudo grep -ri "UMASK" /home + Is it the case that the above command returns no output, or if the umask is configured incorrectly? + + + + To check that virtual syscalls are disabled at boot time, check all boot entries with following command: +sudo grep -L "^options\s+.*\bvsyscall=none\b" /boot/loader/entries/*.conf +No line should be returned, each line returned is a boot entry that doesn't disable virtual syscalls. + Is it the case that vsyscalls are enabled? + + + + To check the permissions of /etc/ssh/*_key, +run the command: +$ ls -l /etc/ssh/*_key +If properly configured, the output should indicate the following permissions: +-rw------- + Is it the case that /etc/ssh/*_key does not have unix mode -rw-------? + + + + +Run the following command to determine if the piranha_lvs_can_network_connect SELinux boolean is disabled: +$ getsebool piranha_lvs_can_network_connect +If properly configured, the output should show the following: +piranha_lvs_can_network_connect --> off + Is it the case that piranha_lvs_can_network_connect is not disabled? + + + + To check the ownership of /boot/efi/EFI/redhat/grub.cfg, +run the command: +$ ls -lL /boot/efi/EFI/redhat/grub.cfg +If properly configured, the output should indicate the following owner: +root + Is it the case that /boot/efi/EFI/redhat/grub.cfg does not have an owner of root? + + + + To check the ownership of /etc/cron.allow, +run the command: +$ ls -lL /etc/cron.allow +If properly configured, the output should indicate the following owner: +root + Is it the case that /etc/cron.allow does not have an owner of root? + + + + To verify the sec option is configured for all NFS mounts, run the following command: +$ mount | grep "sec=" +All NFS mounts should show the sec=krb5:krb5i:krb5p setting in parentheses. +This is not applicable if NFS is not implemented. + Is it the case that the setting is not configured, has the 'sys' option added, or does not have all Kerberos options added? + + + + To verify if the OpenSSL uses defined TLS Crypto Policy, run: +$ grep -P '^(TLS\.)?MinProtocol' /etc/crypto-policies/back-ends/opensslcnf.config +and verify that the value is +TLSv1.2 + Is it the case that cryptographic policy for openssl is not configured or is configured incorrectly? + + + + To determine if the system is configured to audit calls to the +open system call, run the following command: +$ sudo grep "open" /etc/audit/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + +Run the following command to determine if the unconfined_chrome_sandbox_transition SELinux boolean is enabled: +$ getsebool unconfined_chrome_sandbox_transition +If properly configured, the output should show the following: +unconfined_chrome_sandbox_transition --> on + Is it the case that unconfined_chrome_sandbox_transition is not enabled? + + + + To check the group ownership of /var/log/syslog, +run the command: +$ ls -lL /var/log/syslog +If properly configured, the output should indicate the following group-owner: +adm + Is it the case that /var/log/syslog does not have a group owner of adm? + + + + +Run the following command to determine if the mozilla_plugin_bind_unreserved_ports SELinux boolean is disabled: +$ getsebool mozilla_plugin_bind_unreserved_ports +If properly configured, the output should show the following: +mozilla_plugin_bind_unreserved_ports --> off + Is it the case that mozilla_plugin_bind_unreserved_ports is not disabled? + + + + The runtime status of the kernel.kexec_load_disabled kernel parameter can be queried +by running the following command: +$ sysctl kernel.kexec_load_disabled +1. + + Is it the case that the correct value is not returned? + + + + To verify that the Audit is correctly configured according to recommended rules, check the content of the file with the following command: +cat /etc/audit/rules.d/30-ospp-v42-3-access-success.rules +The output has to be exactly as follows: +## Successful file access (any other opens) This has to go last. +## These next two are likely to result in a whole lot of events +-a always,exit -F arch=b32 -S open,openat,openat2,open_by_handle_at -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-access +-a always,exit -F arch=b64 -S open,openat,openat2,open_by_handle_at -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-access + Is it the case that the file does not exist or the content differs? + + + + +Run the following command to determine if the xserver_clients_write_xshm SELinux boolean is disabled: +$ getsebool xserver_clients_write_xshm +If properly configured, the output should show the following: +xserver_clients_write_xshm --> off + Is it the case that xserver_clients_write_xshm is not disabled? + + + + +Run the following command to determine if the virt_use_fusefs SELinux boolean is disabled: +$ getsebool virt_use_fusefs +If properly configured, the output should show the following: +virt_use_fusefs --> off + Is it the case that virt_use_fusefs is not disabled? + + + + To check the group ownership of /etc/cron.weekly, +run the command: +$ ls -lL /etc/cron.weekly +If properly configured, the output should indicate the following group-owner: +root + Is it the case that /etc/cron.weekly does not have a group owner of root? + + + + +Run the following command to determine if the httpd_builtin_scripting SELinux boolean is disabled: +$ getsebool httpd_builtin_scripting +If properly configured, the output should show the following: +httpd_builtin_scripting --> off + Is it the case that httpd_builtin_scripting is not disabled? + + + + Verify that Red Hat Enterprise Linux 8 is configured to audit the execution of the "reboot" command with the following command: + +$ sudo auditctl -l | grep reboot + +-a always,exit -F path=/usr/sbin/reboot -F perm=x -F auid>=1000 -F auid!=unset -k privileged-reboot + Is it the case that the command does not return a line, or the line is commented out? + + + + To verify the INACTIVE setting, run the following command: +$ grep "INACTIVE" /etc/default/useradd +The output should indicate the INACTIVE configuration option is set +to an appropriate integer as shown in the example below: +$ grep "INACTIVE" /etc/default/useradd +INACTIVE= + Is it the case that the value of INACTIVE is greater than the expected value or is -1? + + + + Run the following command to check for duplicate group names: +Check that the operating system contains no duplicate group names for interactive users by running the following command: + + cut -d : -f 3 /etc/group | uniq -d + +If output is produced, this is a finding. +Configure the operating system to contain no duplicate names for groups. +Edit the file "/etc/group" and provide each group that has a duplicate group id with a unique group id. + Is it the case that the system has duplicate group ids? + + + + +Run the following command to determine if the httpd_verify_dns SELinux boolean is disabled: +$ getsebool httpd_verify_dns +If properly configured, the output should show the following: +httpd_verify_dns --> off + Is it the case that httpd_verify_dns is not disabled? + + + + To determine if the system is configured to audit calls to the +fchmod system call, run the following command: +$ sudo grep "fchmod" /etc/audit/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + +Run the following command to determine if the httpd_can_network_connect_db SELinux boolean is disabled: +$ getsebool httpd_can_network_connect_db +If properly configured, the output should show the following: +httpd_can_network_connect_db --> off + Is it the case that httpd_can_network_connect_db is not disabled? + + + + +Run the following command to determine if the tmpreaper_use_nfs SELinux boolean is disabled: +$ getsebool tmpreaper_use_nfs +If properly configured, the output should show the following: +tmpreaper_use_nfs --> off + Is it the case that tmpreaper_use_nfs is not disabled? + + + + To determine whether yum is configured to use gpgcheck, +inspect /etc/yum.conf and ensure the following appears in the +[main] section: +gpgcheck=1 +A value of 1 indicates that gpgcheck is enabled. Absence of a +gpgcheck line or a setting of 0 indicates that it is +disabled. + Is it the case that GPG checking is not enabled? + + + + +To verify the openldap-servers package is not installed, run the +following command: +$ rpm -q openldap-servers +The output should show the following: +package openldap-servers is not installed + Is it the case that it does not? + + + + To verify if the OpenSSH server uses defined MACs in the Crypto Policy, run: +$ grep -Po '(-oMACs=\S+)' /etc/crypto-policies/back-ends/opensshserver.config +and verify that the line matches: +-oMACS=hmac-sha2-512,hmac-sha2-256 + Is it the case that Crypto Policy for OpenSSH Server is not configured correctly? + + + + To determine how the SSH daemon's X11UseLocalhost option is set, run the following command: + +$ sudo grep -i X11UseLocalhost /etc/ssh/sshd_config + +If a line indicating yes is returned, then the required value is set. + Is it the case that the display proxy is listening on wildcard address? + + + + The runtime status of the net.core.bpf_jit_harden kernel parameter can be queried +by running the following command: +$ sysctl net.core.bpf_jit_harden +2. + + Is it the case that the correct value is not returned? + + + + Ensure that CGI backup scripts are not left on the production web server. +This check is limited to CGI/interactive content and not static HTML. + +Search for backup copies of CGI scripts on the web server or ask the Web +Administrator if they keep backup copies of CGI scripts on the web server. + +Common backup file extensions are: *.bak, *.old, *.temp, *.tmp, *.backup, +*.??0. This would also apply to .jsp files. + +On Red Hat Enterprise Linux, run the following commands to find backup +scripts: +find / name "*.bak" -print +find / name "*.*" -print +find / name "*.old" -print + Is it the case that If fileos with these extensions have no relationship with web activity, +such as backup batch file for operating system utility, and they are +not accessible by the web application, this is not a finding. + +If files with these extensions are found in either the document +directory or the home directory of the web server, this is +a finding. + +If files with these extensions are stored in a repository (not in the +document root) as backups for the web server? + + + + +Run the following command to determine if the xserver_object_manager SELinux boolean is disabled: +$ getsebool xserver_object_manager +If properly configured, the output should show the following: +xserver_object_manager --> off + Is it the case that xserver_object_manager is not disabled? + + + + To check the value for maximum consecutive repeating characters, run the following command: +$ sudo grep maxclassrepeat /etc/security/pwquality.conf + Is it the case that the value of "maxclassrepeat" is set to "0", more than "<sub idref="var_password_pam_maxclassrepeat" />" or is commented out? + + + + + +Run the following command to determine the current status of the +cron service: +$ sudo systemctl is-active cron +If the service is running, it should return the following: active + Is it the case that ? + + + + Inspect /etc/default/grub for any instances of selinux=0 +in the kernel boot arguments. Presence of selinux=0 indicates +that SELinux is disabled at boot time. + Is it the case that SELinux is disabled at boot time? + + + + To ensure that the GPG key is installed, run: +$ rpm -q --queryformat "%{SUMMARY}\n" gpg-pubkey +The command should return the string below: +gpg(Red Hat, Inc. (release key 2) <security@redhat.com> + Is it the case that the Red Hat GPG Key is not installed? + + + + To check for legacy lines in /etc/passwd, run the following command: + grep '^\+' /etc/passwd +The command should not return any output. + Is it the case that the file contains legacy lines? + + + + Interview the SA or web administrator to see where the public web server +is logically located in the data center. Review the site network diagram +to see how the web server is connected to the LAN. Visually check the web +server hardware connections to see if it conforms to the site network +diagram. + Is it the case that the web server is not isolated in an accredited DoD DMZ Extension? + + + + +Run the following command to determine if the cobbler_use_cifs SELinux boolean is disabled: +$ getsebool cobbler_use_cifs +If properly configured, the output should show the following: +cobbler_use_cifs --> off + Is it the case that cobbler_use_cifs is not disabled? + + + + The runtime status of the net.ipv6.conf.default.accept_ra_defrtr kernel parameter can be queried +by running the following command: +$ sysctl net.ipv6.conf.default.accept_ra_defrtr +0. + + Is it the case that the correct value is not returned? + + + + +Run the following command to determine if the use_nfs_home_dirs SELinux boolean is disabled: +$ getsebool use_nfs_home_dirs +If properly configured, the output should show the following: +use_nfs_home_dirs --> off + Is it the case that use_nfs_home_dirs is not disabled? + + + + +Run the following command to determine if the httpd_dontaudit_search_dirs SELinux boolean is disabled: +$ getsebool httpd_dontaudit_search_dirs +If properly configured, the output should show the following: +httpd_dontaudit_search_dirs --> off + Is it the case that httpd_dontaudit_search_dirs is not disabled? + + + + +Run the following command to determine if the fenced_can_network_connect SELinux boolean is disabled: +$ getsebool fenced_can_network_connect +If properly configured, the output should show the following: +fenced_can_network_connect --> off + Is it the case that fenced_can_network_connect is not disabled? + + + + To verify the nodev option is configured for all NFS mounts, run +the following command: +$ mount | grep nfs +All NFS mounts should show the nodev setting in parentheses. This +is not applicable if NFS is not implemented. + Is it the case that the setting does not show? + + + + Verify that Red Hat Enterprise Linux 8 is configured to audit the execution of the "sudoedit" command with the following command: + +$ sudo auditctl -l | grep sudoedit + +-a always,exit -F path=/usr/bin/sudoedit -F perm=x -F auid>=1000 -F auid!=unset -k privileged-sudoedit + Is it the case that the command does not return a line, or the line is commented out? + + + + The runtime status of the net.ipv4.ip_forward kernel parameter can be queried +by running the following command: +$ sysctl net.ipv4.ip_forward +0. +The ability to forward packets is only appropriate for routers. + Is it the case that the correct value is not returned? + + + + Run the following command to determine if the iptables-services package is installed: $ rpm -q iptables-services + Is it the case that the iptables-services package is not installed? + + + + +Run the following command to determine if the selinuxuser_direct_dri_enabled SELinux boolean is disabled: +$ getsebool selinuxuser_direct_dri_enabled +If properly configured, the output should show the following: +selinuxuser_direct_dri_enabled --> off + Is it the case that selinuxuser_direct_dri_enabled is not disabled? + + + + To ensure that wireless network notification is disabled, run the following command: +$ gsettings get org.gnome.nm-applet suppress-wireless-networks-available +If properly configured, the output should be true. +To ensure that users cannot enable wireless notification, run the following: +$ grep wireless-networks-available /etc/dconf/db/local.d/locks/* +If properly configured, the output should be +/org/gnome/nm-applet/suppress-wireless-networks-available + Is it the case that wireless network notification is enabled and not disabled? + + + + To determine if the system is configured to audit changes to its network configuration, +run the following command: +auditctl -l | egrep '(/etc/issue|/etc/issue.net|/etc/hosts|/etc/sysconfig/network)' +If the system is configured to watch for network configuration changes, a line should be returned for +each file specified (and perm=wa should be indicated for each). + Is it the case that the system is not configured to audit changes of the network configuration? + + + + Verify Red Hat Enterprise Linux 8 removes all software components after updated versions have been installed. + + +$ grep clean_requirements_on_remove /etc/yum.conf +clean_requirements_on_remove=1 + Is it the case that '"clean_requirements_on_remove" is not set to "1"'? + + + + The runtime status of the net.ipv6.conf.default.accept_source_route kernel parameter can be queried +by running the following command: +$ sysctl net.ipv6.conf.default.accept_source_route +0. + + Is it the case that the correct value is not returned? + + + + To verify that the Audit is correctly configured according to recommended rules, check the content of the file with the following command: +cat /etc/audit/rules.d/43-module-load.rules +The output has to be exactly as follows: +## These rules watch for kernel module insertion. By monitoring +## the syscall, we do not need any watches on programs. +-a always,exit -F arch=b32 -S init_module,finit_module -F key=module-load +-a always,exit -F arch=b64 -S init_module,finit_module -F key=module-load +-a always,exit -F arch=b32 -S delete_module -F key=module-unload +-a always,exit -F arch=b64 -S delete_module -F key=module-unload + Is it the case that the file does not exist or the content differs? + + + + To determine if the system is configured to audit accesses to +/var/log/audit directory, run the following command: +$ sudo grep "dir=/var/log/audit" /etc/audit/audit.rules +If the system is configured to audit this activity, it will return a line. + Is it the case that no line is returned? + + + + +Run the following command to determine if the rsync_full_access SELinux boolean is disabled: +$ getsebool rsync_full_access +If properly configured, the output should show the following: +rsync_full_access --> off + Is it the case that rsync_full_access is not disabled? + + + + To verify that the Audit is correctly configured according to recommended rules, check the content of the file with the following command: +cat /etc/audit/rules.d/30-ospp-v42.rules +The output has to be exactly as follows: +## The purpose of these rules is to meet the requirements for Operating +## System Protection Profile (OSPP)v4.2. These rules depends on having +## the following rule files copied to /etc/audit/rules.d: +## +## 10-base-config.rules, 11-loginuid.rules, +## 30-ospp-v42-1-create-failed.rules, 30-ospp-v42-1-create-success.rules, +## 30-ospp-v42-2-modify-failed.rules, 30-ospp-v42-2-modify-success.rules, +## 30-ospp-v42-3-access-failed.rules, 30-ospp-v42-3-access-success.rules, +## 30-ospp-v42-4-delete-failed.rules, 30-ospp-v42-4-delete-success.rules, +## 30-ospp-v42-5-perm-change-failed.rules, +## 30-ospp-v42-5-perm-change-success.rules, +## 30-ospp-v42-6-owner-change-failed.rules, +## 30-ospp-v42-6-owner-change-success.rules +## +## original copies may be found in /usr/share/audit/sample-rules/ + + +## User add delete modify. This is covered by pam. However, someone could +## open a file and directly create or modify a user, so we'll watch passwd and +## shadow for writes +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b32 -S open -F a1&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b64 -S open -F a1&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b32 -S open -F a1&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b64 -S open -F a1&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify + +## User enable and disable. This is entirely handled by pam. + +## Group add delete modify. This is covered by pam. However, someone could +## open a file and directly create or modify a user, so we'll watch group and +## gshadow for writes +-a always,exit -F path=/etc/passwd -F perm=wa -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F path=/etc/shadow -F perm=wa -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F path=/etc/group -F perm=wa -F auid>=1000 -F auid!=unset -F key=group-modify +-a always,exit -F path=/etc/gshadow -F perm=wa -F auid>=1000 -F auid!=unset -F key=group-modify + + +## Use of special rights for config changes. This would be use of setuid +## programs that relate to user accts. This is not all setuid apps because +## requirements are only for ones that affect system configuration. +-a always,exit -F path=/usr/sbin/unix_chkpwd -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/sbin/usernetctl -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/sbin/userhelper -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/sbin/seunshare -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/mount -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/newgrp -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/newuidmap -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/gpasswd -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/newgidmap -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/umount -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/passwd -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/crontab -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/at -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes + +## Privilege escalation via su or sudo. This is entirely handled by pam. + +## Watch for configuration changes to privilege escalation. +-a always,exit -F path=/etc/sudoers -F perm=wa -F key=special-config-changes +-a always,exit -F dir=/etc/sudoers.d/ -F perm=wa -F key=special-config-changes + +## Audit log access +-a always,exit -F dir=/var/log/audit/ -F perm=r -F auid>=1000 -F auid!=unset -F key=access-audit-trail +## Attempts to Alter Process and Session Initiation Information +-a always,exit -F path=/var/run/utmp -F perm=wa -F auid>=1000 -F auid!=unset -F key=session +-a always,exit -F path=/var/log/btmp -F perm=wa -F auid>=1000 -F auid!=unset -F key=session +-a always,exit -F path=/var/log/wtmp -F perm=wa -F auid>=1000 -F auid!=unset -F key=session + +## Attempts to modify MAC controls +-a always,exit -F dir=/etc/selinux/ -F perm=wa -F auid>=1000 -F auid!=unset -F key=MAC-policy + +## Software updates. This is entirely handled by rpm. + +## System start and shutdown. This is entirely handled by systemd + +## Kernel Module loading. This is handled in 43-module-load.rules + +## Application invocation. The requirements list an user requirement +## FPT_SRP_EXT.1 Software Restriction Policies. This event is intended to +## state results from that policy. This would be handled entirely by +## that daemon. + Is it the case that the file does not exist or the content differs? + + + + The runtime status of the net.ipv6.conf.all.accept_ra_pinfo kernel parameter can be queried +by running the following command: +$ sysctl net.ipv6.conf.all.accept_ra_pinfo +0. + + Is it the case that the correct value is not returned? + + + + To determine if the system is configured to audit calls to the +fsetxattr system call, run the following command: +$ sudo grep "fsetxattr" /etc/audit/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + +Run the following command to determine if the httpd_run_preupgrade SELinux boolean is disabled: +$ getsebool httpd_run_preupgrade +If properly configured, the output should show the following: +httpd_run_preupgrade --> off + Is it the case that httpd_run_preupgrade is not disabled? + + + + To check the group ownership of /etc/group, +run the command: +$ ls -lL /etc/group +If properly configured, the output should indicate the following group-owner: +root + Is it the case that /etc/group does not have a group owner of root? + + + + To check on the age of McAfee virus definition files, run the following command: +$ sudo cd /opt/NAI/LinuxShield/engine/dat +$ sudo ls -la avvscan.dat avvnames.dat avvclean.dat + Is it the case that signatures are out of date? + + + + Verify that a separate file system/partition has been created for /tmp with the following command: + +$ mountpoint /tmp + + Is it the case that "/tmp is not a mountpoint" is returned? + + + + +Run the following command to determine if the selinuxuser_mysql_connect_enabled SELinux boolean is disabled: +$ getsebool selinuxuser_mysql_connect_enabled +If properly configured, the output should show the following: +selinuxuser_mysql_connect_enabled --> off + Is it the case that selinuxuser_mysql_connect_enabled is not disabled? + + + + To determine if ignore_dot has been configured for sudo, run the following command: +$ sudo grep -ri "^[\s]*Defaults.*\bignore_dot\b.*" /etc/sudoers /etc/sudoers.d/ +The command should return a matching output. + Is it the case that ignore_dot is not enabled in sudo? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_RANDOMIZE_MEMORY /boot/config.* + + For each kernel installed, a line with value "y" should be returned. + + Is it the case that the kernel was not built with the required value? + + + + Verify that Red Hat Enterprise Linux 8 is configured to audit the execution of the "shutdown" command with the following command: + +$ sudo auditctl -l | grep shutdown + +-a always,exit -F path=/usr/sbin/shutdown -F perm=x -F auid>=1000 -F auid!=unset -k privileged-shutdown + Is it the case that the command does not return a line, or the line is commented out? + + + + Verify the audit system prevents unauthorized changes with the following command: + +$ sudo grep "^\s*[^#]" /etc/audit/audit.rules | tail -1 +-e 2 + + Is it the case that the audit system is not set to be immutable by adding the "-e 2" option to the end of "/etc/audit/audit.rules"? + + + + To ensure the X Windows package group is removed, run the following command: + +$ rpm -qi xorg-x11-server-Xorg xorg-x11-server-common xorg-x11-server-utils xorg-x11-server-Xwayland + +For each package mentioned above you should receive following line: +package <package> is not installed + Is it the case that xorg related packages are not removed and run level is not correctly configured? + + + + To check that the avahi-daemon service is disabled in system boot configuration, +run the following command: +$ sudo systemctl is-enabled avahi-daemon +Output should indicate the avahi-daemon service has either not been installed, +or has been disabled at all runlevels, as shown in the example below: +$ sudo systemctl is-enabled avahi-daemon disabled + +Run the following command to verify avahi-daemon is not active (i.e. not running) through current runtime configuration: +$ sudo systemctl is-active avahi-daemon + +If the service is not running the command will return the following output: +inactive + +The service will also be masked, to check that the avahi-daemon is masked, run the following command: +$ sudo systemctl show avahi-daemon | grep "LoadState\|UnitFileState" + +If the service is masked the command will return the following outputs: + +LoadState=masked + +UnitFileState=masked + Is it the case that the "avahi-daemon" is loaded and not masked? + + + + +Run the following command to determine if the gluster_export_all_rw SELinux boolean is disabled: +$ getsebool gluster_export_all_rw +If properly configured, the output should show the following: +gluster_export_all_rw --> off + Is it the case that gluster_export_all_rw is not disabled? + + + + Verify that Red Hat Enterprise Linux 8 is configured to audit the execution of the "setfiles" command with the following command: + +$ sudo auditctl -l | grep setfiles + +-a always,exit -F path=/usr/sbin/setfiles -F perm=x -F auid>=1000 -F auid!=unset -k privileged-unix-update + Is it the case that the command does not return a line, or the line is commented out? + + + + To check if UsePrivilegeSeparation is enabled or set correctly, run the +following command: +$ sudo grep UsePrivilegeSeparation /etc/ssh/sshd_config +If configured properly, output should be . + Is it the case that it is commented out or is not enabled? + + + + + +Run the following command to determine the current status of the +ntp service: +$ sudo systemctl is-active ntp +If the service is running, it should return the following: active + Is it the case that ? + + + + +Run the following command to determine if the logging_syslogd_can_sendmail SELinux boolean is disabled: +$ getsebool logging_syslogd_can_sendmail +If properly configured, the output should show the following: +logging_syslogd_can_sendmail --> off + Is it the case that logging_syslogd_can_sendmail is not disabled? + + + + The runtime status of the net.ipv4.conf.default.shared_media kernel parameter can be queried +by running the following command: +$ sysctl net.ipv4.conf.default.shared_media +0. + + Is it the case that the correct value is not returned? + + + + Verify the audit log directories have a correct mode or less permissive mode. + +Find the location of the audit logs: + +$ sudo grep "^log_file" /etc/audit/auditd.conf + + + +Run the following command to check the mode of the system audit logs: + +$ sudo stat -c "%a %n" [audit_log_directory] + +Replace "[audit_log_directory]" to the correct audit log directory path, by default this location is "/var/log/audit". + + +The correct permissions are 0700 + Is it the case that audit logs have a more permissive mode? + + + + +Run the following command to determine if the mozilla_plugin_use_spice SELinux boolean is disabled: +$ getsebool mozilla_plugin_use_spice +If properly configured, the output should show the following: +mozilla_plugin_use_spice --> off + Is it the case that mozilla_plugin_use_spice is not disabled? + + + + To determine how the SSH daemon's GSSAPIAuthentication option is set, run the following command: + +$ sudo grep -i GSSAPIAuthentication /etc/ssh/sshd_config + +If a line indicating no is returned, then the required value is set. + + Is it the case that the required value is not set? + + + + Ensure that Red Hat Enterprise Linux 8 verifies correct operation of security functions. + +Check if "SELinux" is active and in "" mode with the following command: + +$ sudo getenforce + + Is it the case that SELINUX is not set to enforcing? + + + + To verify that SSSD is configured to run as user sssd, run the following command: +$ sudo grep -r '\buser\b' /etc/sssd +If configured properly, output should similar to /etc/sssd/conf.d/ospp.conf:user = sssd. +Sanity of SSSD configuration in general can be checked using $ sudo sssctl config-check + Is it the case that it does not exist or is not configured properly? + + + + +Run the following command to determine if the sanlock_use_fusefs SELinux boolean is disabled: +$ getsebool sanlock_use_fusefs +If properly configured, the output should show the following: +sanlock_use_fusefs --> off + Is it the case that sanlock_use_fusefs is not disabled? + + + + To check the ownership of /var/log/syslog, +run the command: +$ ls -lL /var/log/syslog +If properly configured, the output should indicate the following owner: +syslog + Is it the case that /var/log/syslog does not have an owner of syslog? + + + + +Run the following command to determine if the tor_bind_all_unreserved_ports SELinux boolean is disabled: +$ getsebool tor_bind_all_unreserved_ports +If properly configured, the output should show the following: +tor_bind_all_unreserved_ports --> off + Is it the case that tor_bind_all_unreserved_ports is not disabled? + + + + Run the following command to determine if the audit package is installed: $ rpm -q audit + Is it the case that the audit package is not installed? + + + + Verify Red Hat Enterprise Linux 8 generates audit records for all account creations, modifications, disabling, and termination events that affect "/etc/group" with the following command: + +$ sudo auditctl -l | egrep '(/etc/group)' + +-w /etc/group -p wa -k identity + Is it the case that the command does not return a line, or the line is commented out? + + + + + Is it the case that the package is not installed? + + + + To determine whether yum has been configured to disable +gpgcheck for any repos, inspect all files in +/etc/yum.repos.d and ensure the following does not appear in any +sections: +gpgcheck=0 +A value of 0 indicates that gpgcheck has been disabled for that repo. + Is it the case that GPG checking is disabled? + + + + To check the current idle time-out value, run the following command: +$ gsettings get org.gnome.desktop.session idle-delay +If properly configured, the output should be 'uint32 '. +To ensure that users cannot change the screensaver inactivity timeout setting, run the following: +$ grep idle-delay /etc/dconf/db/local.d/locks/* +If properly configured, the output should be /org/gnome/desktop/session/idle-delay + Is it the case that idle-delay is set to 0 or a value greater than <sub idref="inactivity_timeout_value" />? + + + + To verify the assigned home directory of all interactive user home directories +have a mode of 0750 or less permissive, run the following command: +$ sudo ls -l /home +Inspect the output for any directories with incorrect permissions. + Is it the case that they are more permissive? + + + + To determine if the system is configured to audit calls to the +open_by_handle_at system call, run the following command: +$ sudo grep "open_by_handle_at" /etc/audit/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + To determine if the system is configured to audit calls to the +open system call, run the following command: +$ sudo grep "open" /etc/audit/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + +Run the following command to determine if the tor_can_network_relay SELinux boolean is disabled: +$ getsebool tor_can_network_relay +If properly configured, the output should show the following: +tor_can_network_relay --> off + Is it the case that tor_can_network_relay is not disabled? + + + + +Run the following command to determine if the samba_run_unconfined SELinux boolean is disabled: +$ getsebool samba_run_unconfined +If properly configured, the output should show the following: +samba_run_unconfined --> off + Is it the case that samba_run_unconfined is not disabled? + + + + To verify the local initialization files of all local interactive users are group- +owned by the appropriate user, inspect the primary group of the respective +users in /etc/passwd and verify all initialization files under the +respective users home directory. Check the group owner of all local interactive users +initialization files. + Is it the case that they are not? + + + + Verify users are provided with feedback on when account accesses last occurred with the following command: + +$ sudo grep pam_lastlog /etc/pam.d/postlogin + +session required pam_lastlog.so showfailed + Is it the case that "pam_lastlog" is missing from "/etc/pam.d/postlogin" file, or the silent option is present? + + + + Find the list of alias maps used by the Postfix mail server: +$ sudo postconf alias_maps +Query the Postfix alias maps for an alias for the postmaster user: +$ sudo postmap -q postmaster hash:/etc/aliases +The output should return root. + Is it the case that the alias is not set or is not root? + + + + +Run the following command to determine if the guest_exec_content SELinux boolean is disabled: +$ getsebool guest_exec_content +If properly configured, the output should show the following: +guest_exec_content --> off + Is it the case that guest_exec_content is not disabled? + + + + +Run the following command to determine if the tmpreaper_use_samba SELinux boolean is disabled: +$ getsebool tmpreaper_use_samba +If properly configured, the output should show the following: +tmpreaper_use_samba --> off + Is it the case that tmpreaper_use_samba is not disabled? + + + + Verify that Red Hat Enterprise Linux 8 is configured to prevent unrestricted mail relaying, +run the following command: +$ sudo postconf -n smtpd_client_restrictions + Is it the case that the "smtpd_client_restrictions" parameter contains any entries other than "permit_mynetworks" and "reject"? + + + + Verify that rules for unsuccessful calls of the open_by_handle_at syscall are in the order shown below. + + If the auditd daemon is configured to use the "augenrules" program to read audit rules during daemon startup (the default), check the order of rules below in a file with suffix ".rules" in the directory "/etc/audit/rules.d". + If the auditd daemon is configured to use the "auditctl" utility to read audit rules during daemon startup, check the order of rules below in "/etc/audit/audit.rules" file. + + -a always,exit -F arch=b32 -S open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b32 -S open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b32 -S open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b32 -S open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b32 -S open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-access + -a always,exit -F arch=b32 -S open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-access + + If the system is 64 bit then also add the following lines: + + -a always,exit -F arch=b64 -S open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b64 -S open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b64 -S open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b64 -S open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b64 -S open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-access + -a always,exit -F arch=b64 -S open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-access + Is it the case that the rules are in a different order? + + + + To determine whether sudo command includes configuration files from the appropriate directory, +run the following command: +$ sudo grep -rP '^[#@]include(dir)?' /etc/sudoers /etc/sudoers.d +If only the line /etc/sudoers:#includedir /etc/sudoers.d is returned, then the drop-in include configuration is set correctly. +Any other line returned is a finding. + Is it the case that the /etc/sudoers doesn't include /etc/sudores.d or includes other directories?? + + + + To verify that CUPS printer browsing is disabled, run the following command: +$ sudo grep "Browsing\|BrowseAllow" /etc/cups/cupsd.conf +The output should return the following: +Browsing Off +BrowseAllow none + Is it the case that printer browsing is not disabled? + + + + Run the following command to ensure postfix routes mail to this system: +$ grep relayhost /etc/postfix/main.cf +If properly configured, the output should show only . + Is it the case that it is not? + + + + To check that the httpd service is disabled in system boot configuration, +run the following command: +$ sudo systemctl is-enabled httpd +Output should indicate the httpd service has either not been installed, +or has been disabled at all runlevels, as shown in the example below: +$ sudo systemctl is-enabled httpd disabled + +Run the following command to verify httpd is not active (i.e. not running) through current runtime configuration: +$ sudo systemctl is-active httpd + +If the service is not running the command will return the following output: +inactive + +The service will also be masked, to check that the httpd is masked, run the following command: +$ sudo systemctl show httpd | grep "LoadState\|UnitFileState" + +If the service is masked the command will return the following outputs: + +LoadState=masked + +UnitFileState=masked + Is it the case that the "httpd" is loaded and not masked? + + + + Run the following command to determine if the policycoreutils-python-utils package is installed: $ rpm -q policycoreutils-python-utils + Is it the case that the package is not installed? + + + + To check the permissions of /etc/ssh/*.pub, +run the command: +$ ls -l /etc/ssh/*.pub +If properly configured, the output should indicate the following permissions: +-rw-r--r-- + Is it the case that /etc/ssh/*.pub does not have unix mode -rw-r--r--? + + + + Verify the umask setting is configured correctly in the /etc/bashrc file by +running the following command: +$ sudo grep "umask" /etc/bashrc +All output must show the value of umask set as shown below: +umask + Is it the case that the above command returns no output, or the umask is configured incorrectly? + + + + +If the system is configured to prevent the loading of the sctp kernel module, +it will contain lines inside any file in /etc/modprobe.d or the deprecated /etc/modprobe.conf. +These lines instruct the module loading system to run another program (such as /bin/true) upon a module install event. + +These lines can also instruct the module loading system to ignore the sctp kernel module via blacklist keyword. + +Run the following command to search for such lines in all files in /etc/modprobe.d and the deprecated /etc/modprobe.conf: +$ grep -r sctp /etc/modprobe.conf /etc/modprobe.d + Is it the case that no line is returned? + + + + +Run the following command to determine if the mcelog_exec_scripts SELinux boolean is enabled: +$ getsebool mcelog_exec_scripts +If properly configured, the output should show the following: +mcelog_exec_scripts --> on + Is it the case that mcelog_exec_scripts is not enabled? + + + + To verify that null passwords cannot be used, run the following command: + +$ grep nullok /etc/pam.d/system-auth /etc/pam.d/password-auth + +If this produces any output, it may be possible to log into accounts +with empty passwords. Remove any instances of the nullok option to +prevent logins with empty passwords. + Is it the case that NULL passwords can be used? + + + + To determine if the system is configured to audit unsuccessful calls +to the fremovexattr system call, run the following command: +$ sudo grep "fremovexattr" /etc/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + Verify "nftables" is configured to allow rate limits on any connection to the system with the following command: + +Verify "firewalld" has "nftables" set as the default backend: + +$ sudo grep -i firewallbackend /etc/firewalld/firewalld.conf + +# FirewallBackend +FirewallBackend=nftables + Is it the case that the "nftables" is not set as the "firewallbackend"? + + + + To determine if the system is configured to audit successful calls +to the setxattr system call, run the following command: +$ sudo grep "setxattr" /etc/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + Run the following command to determine if the abrt-addon-kerneloops package is installed: +$ rpm -q abrt-addon-kerneloops + Is it the case that the package is installed? + + + + To verify /etc/system-fips exists, run the following command: +ls -l /etc/system-fips +The output should be similar to the following: +-rw-r--r--. 1 root root 36 Nov 26 11:31 /etc/system-fips + Is it the case that /etc/system-fips does not exist? + + + + If the system uses IPv6, this is not applicable. + +If the system is configured to disable the +ipv6 kernel module, it will contain a line +of the form: +options ipv6 disable=1 +Such lines may be inside any file in /etc/modprobe.d or the +deprecated/etc/modprobe.conf. This permits insertion of the IPv6 +kernel module (which other parts of the system expect to be present), but +otherwise keeps it inactive. Run the following command to search for such +lines in all files in /etc/modprobe.d and the deprecated +/etc/modprobe.conf: +$ grep -r ipv6 /etc/modprobe.conf /etc/modprobe.d + Is it the case that the ipv6 kernel module is not disabled? + + + + +Run the following command to determine if the mailman_use_fusefs SELinux boolean is disabled: +$ getsebool mailman_use_fusefs +If properly configured, the output should show the following: +mailman_use_fusefs --> off + Is it the case that mailman_use_fusefs is not disabled? + + + + +Run the following command to determine if the selinuxuser_use_ssh_chroot SELinux boolean is disabled: +$ getsebool selinuxuser_use_ssh_chroot +If properly configured, the output should show the following: +selinuxuser_use_ssh_chroot --> off + Is it the case that selinuxuser_use_ssh_chroot is not disabled? + + + + To check the permissions of /etc/motd, +run the command: +$ ls -l /etc/motd +If properly configured, the output should indicate the following permissions: +-rw-r--r-- + Is it the case that /etc/motd does not have unix mode -rw-r--r--? + + + + +Run the following command to determine if the selinuxuser_execstack SELinux boolean is disabled: +$ getsebool selinuxuser_execstack +If properly configured, the output should show the following: +selinuxuser_execstack --> off + Is it the case that selinuxuser_execstack is not disabled? + + + + Verify that Red Hat Enterprise Linux 8 is configured to audit the execution of the "postqueue" command with the following command: + +$ sudo auditctl -l | grep postqueue + +-a always,exit -F path=/usr/bin/postqueue -F perm=x -F auid>=1000 -F auid!=unset -k privileged-postqueue + Is it the case that the command does not return a line, or the line is commented out? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_SLUB_DEBUG /boot/config.* + + For each kernel installed, a line with value "y" should be returned. + + Is it the case that the kernel was not built with the required value? + + + + To check that the qpidd service is disabled in system boot configuration, +run the following command: +$ sudo systemctl is-enabled qpidd +Output should indicate the qpidd service has either not been installed, +or has been disabled at all runlevels, as shown in the example below: +$ sudo systemctl is-enabled qpidd disabled + +Run the following command to verify qpidd is not active (i.e. not running) through current runtime configuration: +$ sudo systemctl is-active qpidd + +If the service is not running the command will return the following output: +inactive + +The service will also be masked, to check that the qpidd is masked, run the following command: +$ sudo systemctl show qpidd | grep "LoadState\|UnitFileState" + +If the service is masked the command will return the following outputs: + +LoadState=masked + +UnitFileState=masked + Is it the case that the "qpidd" is loaded and not masked? + + + + +Run the following command to determine if the xguest_exec_content SELinux boolean is disabled: +$ getsebool xguest_exec_content +If properly configured, the output should show the following: +xguest_exec_content --> off + Is it the case that xguest_exec_content is not disabled? + + + + To check the password warning age, run the command: +$ grep PASS_WARN_AGE /etc/login.defs +The DoD requirement is 7. + Is it the case that it is not set to the required value? + + + + To verify whether audispd plugin off-loads audit records onto a different +system or media from the system being audited, run the following command: + +$ sudo grep -i remote_server /etc/audit/audisp-remote.conf + +The output should return something similar to where REMOTE_SYSTEM +is an IP address or hostname: +remote_server = REMOTE_SYSTEM + +Determine which partition the audit records are being written to with the +following command: + +$ sudo grep log_file /etc/audit/auditd.conf +log_file = /var/log/audit/audit.log + +Check the size of the partition that audit records are written to with the +following command and verify whether it is sufficiently large: + +$ sudo df -h /var/log/audit/ +/dev/sda2 24G 10.4G 13.6G 43% /var/log/audit + Is it the case that audispd is not sending logs to a remote system and the local partition has inadequate space? + + + + To find SUID files, run the following command: +$ sudo find / -xdev -type f -perm -4000 + Is it the case that only authorized files appear in the output of the find command? + + + + To check if authentication is required for emergency mode, run the following command: +$ grep sulogin /usr/lib/systemd/system/emergency.service +The output should be similar to the following, and the line must begin with +ExecStart and /usr/lib/systemd/systemd-sulogin-shell. + ExecStart=-/usr/lib/systemd/systemd-sulogin-shell emergency + +Then, check if the emergency target requires the emergency service: +Run the following command: +$ sudo grep Requires /usr/lib/systemd/system/emergency.target +The output should be the following: +Requires=emergency.service + +Then, check if there is no custom emergency target configured in systemd configuration. +Run the following command: +$ sudo grep -r emergency.target /etc/systemd/system/ +The output should be empty. + +Then, check if there is no custom emergency service configured in systemd configuration. +Run the following command: +$ sudo grep -r emergency.service /etc/systemd/system/ +The output should be empty. + Is it the case that the output is different? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_STACKPROTECTOR /boot/config.* + + For each kernel installed, a line with value "y" should be returned. + + Is it the case that the kernel was not built with the required value? + + + + Run the following command to determine if the postfix package is installed: $ rpm -q postfix + Is it the case that the package is not installed? + + + + To check the ownership of /etc/gshadow-, +run the command: +$ ls -lL /etc/gshadow- +If properly configured, the output should indicate the following owner: +root + Is it the case that /etc/gshadow- does not have an owner of root? + + + + +Run the following command to determine if the nagios_run_pnp4nagios SELinux boolean is disabled: +$ getsebool nagios_run_pnp4nagios +If properly configured, the output should show the following: +nagios_run_pnp4nagios --> off + Is it the case that nagios_run_pnp4nagios is not disabled? + + + + To determine if the system is configured to audit calls to the +delete_module system call, run the following command: +$ sudo grep "delete_module" /etc/audit/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + Verify the hidepid=value option is configured for the /proc mount point, + run the following command: + $ sudo mount | grep '\s/proc\s' + . . . /proc . . . hidepid=value . . . + + Is it the case that the "/proc" file system does not have the "hidepid=value" option set? + + + + Shared libraries are stored in the following directories: +/lib +/lib64 +/usr/lib +/usr/lib64 + +To find shared libraries that are group-writable or world-writable, +run the following command for each directory DIR which contains shared libraries: +$ sudo find -L DIR -perm /022 -type d + Is it the case that any of these files are group-writable or world-writable? + + + + +Run the following command to determine if the mock_enable_homedirs SELinux boolean is disabled: +$ getsebool mock_enable_homedirs +If properly configured, the output should show the following: +mock_enable_homedirs --> off + Is it the case that mock_enable_homedirs is not disabled? + + + + To ensure that XDMCP is disabled in /etc/gdm/custom.conf, run the following command: +grep -Pzo "\[xdmcp\]\nEnable=false" /etc/gdm/custom.conf +The output should return the following: + +[xdmcp] +Enable=false + + Is it the case that the Enable is not set to false or is missing in the xdmcp section of the /etc/gdm/custom.conf gdm configuration file? + + + + To verify that a remote NTP service is configured for time synchronization, +open the following file: + +/etc/chrony.conf in the case the system in question is +configured to use the chronyd as the NTP daemon (default setting) +/etc/ntp.conf in the case the system in question is configured +to use the ntpd as the NTP daemon + +In the file, there should be a section similar to the following: +server ntpserver + Is it the case that this is not the case? + + + + To check the ownership of /etc/issue, +run the command: +$ ls -lL /etc/issue +If properly configured, the output should indicate the following owner: +root + Is it the case that /etc/issue does not have an owner of root? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_ACPI_CUSTOM_METHOD /boot/config.* + + Configs with value 'n' are not explicitly set in the file, so either commented lines or no + lines should be returned. + + Is it the case that the kernel was not built with the required value? + + + + +Run the following command to determine if the openshift_use_nfs SELinux boolean is disabled: +$ getsebool openshift_use_nfs +If properly configured, the output should show the following: +openshift_use_nfs --> off + Is it the case that openshift_use_nfs is not disabled? + + + + +Run the following command to determine if the mysql_connect_any SELinux boolean is disabled: +$ getsebool mysql_connect_any +If properly configured, the output should show the following: +mysql_connect_any --> off + Is it the case that mysql_connect_any is not disabled? + + + + Inspect /etc/audit/auditd.conf and locate the following line to +determine if the system is configured correctly: +space_left PERCENTAGE% + Is it the case that the system is not configured with a specific percentage to notify administrators of an issue? + + + + To verify that the Audit is correctly configured according to recommended rules, check the content of the file with the following command: +cat /etc/audit/rules.d/30-ospp-v42-1-create-success.rules +The output has to be exactly as follows: +## Successful file creation (open with O_CREAT) +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create +-a always,exit -F arch=b32 -S open -F a1&0100 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create +-a always,exit -F arch=b64 -S open -F a1&0100 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create +-a always,exit -F arch=b32 -S creat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create +-a always,exit -F arch=b64 -S creat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create + Is it the case that the file does not exist or the content differs? + + + + To check how many special characters are required in a password, run the following command: +$ grep ocredit /etc/security/pwquality.conf +The ocredit parameter (as a negative number) will indicate how many special +characters are required. + Is it the case that value of "ocredit" is a positive number or is commented out? + + + + Check that AIDE is properly configured to protect the integrity of the +audit tools by running the following command: + +# sudo cat /etc/aide.conf | grep /usr/sbin/au + +/usr/sbin/auditctl p+i+n+u+g+s+b+acl+xattrs+sha512 + +/usr/sbin/auditd p+i+n+u+g+s+b+acl+xattrs+sha512 + +/usr/sbin/ausearch p+i+n+u+g+s+b+acl+xattrs+sha512 + +/usr/sbin/aureport p+i+n+u+g+s+b+acl+xattrs+sha512 + +/usr/sbin/autrace p+i+n+u+g+s+b+acl+xattrs+sha512 + + + + +/usr/sbin/augenrules p+i+n+u+g+s+b+acl+xattrs+sha512 + + + +If AIDE is configured properly to protect the integrity of the audit tools, +all lines listed above will be returned from the command. + +If one or more lines are missing, this is a finding. + Is it the case that integrity checks of the audit tools are missing or incomplete? + + + + To check the system for the existence of any .netrc files, +run the following command: +$ sudo find /home -xdev -name .netrc + Is it the case that any .netrc files exist? + + + + To check the group ownership of /etc/cron.daily, +run the command: +$ ls -lL /etc/cron.daily +If properly configured, the output should indicate the following group-owner: +root + Is it the case that /etc/cron.daily does not have a group owner of root? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_DEBUG_SG /boot/config.* + + For each kernel installed, a line with value "y" should be returned. + + Is it the case that the kernel was not built with the required value? + + + + # grep "^OPTIONS.*-u" /etc/sysconfig/chronyd | grep -v -e '-u\s*chrony\b' +returns no output + Is it the case that chronyd is not running under chrony user account? + + + + Run the following command to determine the current status of the dnf-automatic timer: $ sudo systemctl is-active dnf-automatic.timer If the timer is running, it should return the following: active + Is it the case that the dnf-automatic.timer is not enabled? + + + + +To ensure the login warning banner text is properly set, run the following: +$ grep banner-message-text /etc/dconf/db/gdm.d/* +If properly configured, the proper banner text will appear. +To ensure the login warning banner text is locked and cannot be changed by a user, run the following: +$ grep banner-message-text /etc/dconf/db/gdm.d/locks/* +If properly configured, the output should be /org/gnome/login-screen/banner-message-text. + Is it the case that it does not? + + + + +Run the following command to determine if the privoxy_connect_any SELinux boolean is disabled: +$ getsebool privoxy_connect_any +If properly configured, the output should show the following: +privoxy_connect_any --> off + Is it the case that privoxy_connect_any is not disabled? + + + + To determine if the system is configured to audit successful calls +to the truncate system call, run the following command: +$ sudo grep "truncate" /etc/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + Run the following command to determine if the abrt-plugin-sosreport package is installed: +$ rpm -q abrt-plugin-sosreport + Is it the case that the package is installed? + + + + To determine if the system is configured to audit calls to the +openat system call, run the following command: +$ sudo grep "openat" /etc/audit/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + +Run the following command to determine if the pcp_read_generic_logs SELinux boolean is disabled: +$ getsebool pcp_read_generic_logs +If properly configured, the output should show the following: +pcp_read_generic_logs --> off + Is it the case that pcp_read_generic_logs is not disabled? + + + + +Run the following command to determine if the glance_use_execmem SELinux boolean is disabled: +$ getsebool glance_use_execmem +If properly configured, the output should show the following: +glance_use_execmem --> off + Is it the case that glance_use_execmem is not disabled? + + + + Determine if "sudoers" file restricts sudo access run the following commands: +$ sudo grep -PR '^\s*ALL\s+ALL\=\(ALL\)\s+ALL\s*$' /etc/sudoers /etc/sudoers.d/* +$ sudo grep -PR '^\s*ALL\s+ALL\=\(ALL\:ALL\)\s+ALL\s*$' /etc/sudoers /etc/sudoers.d/* + Is it the case that either of the commands returned a line? + + + + To determine if the system is configured to audit successful calls +to the chmod system call, run the following command: +$ sudo grep "chmod" /etc/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + Verify that a separate file system/partition has been created for /boot with the following command: + +$ mountpoint /boot + + Is it the case that "/boot is not a mountpoint" is returned? + + + + To check if pam_namespace.so is required for user login, run the following command: +$ grep pam_namespace.so /etc/pam.d/login +The output should return the following uncommented: +session required pam_namespace.so + Is it the case that pam_namespace.so is not required or is commented out? + + + + The runtime status of the net.ipv4.conf.all.rp_filter parameter can be queried +by running the following command: +$ sysctl net.ipv4.conf.all.rp_filter +The output of the command should indicate either: +net.ipv4.conf.all.rp_filter = 1 +or: +net.ipv4.conf.all.rp_filter = 2 +The output of the command should not indicate: +net.ipv4.conf.all.rp_filter = 0 + +The preferable way how to assure the runtime compliance is to have +correct persistent configuration, and rebooting the system. + +The persistent sysctl parameter configuration is performed by specifying the appropriate +assignment in any file located in the /etc/sysctl.d directory. +Verify that there is not any existing incorrect configuration by executing the following command: +$ grep -r '^\s*net.ipv4.conf.all.rp_filter\s*=' /etc/sysctl.conf /etc/sysctl.d +The command should not find any assignments other than: +net.ipv4.conf.all.rp_filter = 1 +or: +net.ipv4.conf.all.rp_filter = 2 + +Conflicting assignments are not allowed. + Is it the case that the net.ipv4.conf.all.rp_filter is not set to 1 or 2 or is configured to be 0? + + + + Verify the nodev option is configured for the /boot mount point, + run the following command: + $ sudo mount | grep '\s/boot\s' + . . . /boot . . . nodev . . . + + Is it the case that the "/boot" file system does not have the "nodev" option set? + + + + To ensure that users cannot disable the screensaver idle inactivity setting, run the following: +$ grep idle-activation-enabled /etc/dconf/db/local.d/locks/* +If properly configured, the output should be /org/gnome/desktop/screensaver/idle-activation-enabled + Is it the case that idle-activation-enabled is not locked? + + + + Verify that Red Hat Enterprise Linux 8 is configured to audit the execution of the "sudo" command with the following command: + +$ sudo auditctl -l | grep sudo + +-a always,exit -F path=/usr/bin/sudo -F perm=x -F auid>=1000 -F auid!=unset -k privileged-sudo + Is it the case that the command does not return a line, or the line is commented out? + + + + +Run the following command to determine if the gitosis_can_sendmail SELinux boolean is disabled: +$ getsebool gitosis_can_sendmail +If properly configured, the output should show the following: +gitosis_can_sendmail --> off + Is it the case that gitosis_can_sendmail is not disabled? + + + + +Run the following command to determine if the httpd_can_network_connect SELinux boolean is disabled: +$ getsebool httpd_can_network_connect +If properly configured, the output should show the following: +httpd_can_network_connect --> off + Is it the case that httpd_can_network_connect is not disabled? + + + + To check the ownership of /etc/shadow-, +run the command: +$ ls -lL /etc/shadow- +If properly configured, the output should indicate the following owner: +root + Is it the case that /etc/shadow- does not have an owner of root? + + + + Verify that Red Hat Enterprise Linux 8 is configured to audit the execution of the "semanage" command with the following command: + +$ sudo auditctl -l | grep semanage + +-a always,exit -F path=/usr/sbin/semanage -F perm=x -F auid>=1000 -F auid!=unset -k privileged-unix-update + Is it the case that the command does not return a line, or the line is commented out? + + + + +Run the following command to determine if the sanlock_use_nfs SELinux boolean is disabled: +$ getsebool sanlock_use_nfs +If properly configured, the output should show the following: +sanlock_use_nfs --> off + Is it the case that sanlock_use_nfs is not disabled? + + + + To verify all files and directories contained in interactive user home +directory, excluding local initialization files, have a mode of 0750, +run the following command: +$ sudo ls -lLR /home/USER + Is it the case that home directory files or folders have incorrect permissions? + + + + +If the system is configured to prevent the loading of the rds kernel module, +it will contain lines inside any file in /etc/modprobe.d or the deprecated /etc/modprobe.conf. +These lines instruct the module loading system to run another program (such as /bin/true) upon a module install event. + +These lines can also instruct the module loading system to ignore the rds kernel module via blacklist keyword. + +Run the following command to search for such lines in all files in /etc/modprobe.d and the deprecated /etc/modprobe.conf: +$ grep -r rds /etc/modprobe.conf /etc/modprobe.d + Is it the case that no line is returned? + + + + Inspect the password section of /etc/pam.d/system-auth +and ensure that the pam_unix.so module is configured to use the argument +sha512: + +$ sudo grep "^password.*pam_unix\.so.*sha512" /etc/pam.d/system-auth + +password sufficient pam_unix.so sha512 + Is it the case that "sha512" is missing, or is commented out? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_MODULE_SIG_KEY /boot/config.* + + For each kernel installed, a line with value "" should be returned. + + Is it the case that the kernel was not built with the required value? + + + + To verify that vlock is configured as a locking mechanism in tmux, run the following command: + +$ sudo grep lock-command /etc/tmux.conf + +The output should return the following: + +set -g lock-command vlock + +Then, verify that the /etc/tmux.conf file can be read by other users than root: + +$ sudo ls -al /etc/tmux.conf + Is it the case that lock-command is not set? + + + + Verify RHEL 9 generates audit records for all account creations, modifications, disabling, and termination events that affect "/etc/sudoers.d/" with the following command: + +$ sudo auditctl -l | grep/etc/sudoers.d + +-w /etc/sudoers.d/ -p wa -k identity + Is it the case that the command does not return a line, or the line is commented out? + + + + If TFTP is not installed, this is not applicable. To determine if TFTP is +installed, run the following command: +$ rpm -qa | grep tftp + +Verify tftp is configured by with the -s option by +running the following command: +grep "server_args" /etc/xinetd.d/tftp +server_args = -s + Is it the case that "server_args" line does not have a "-s" option, and a subdirectory is not assigned? + + + + To check the group ownership of /etc/at.allow, +run the command: +$ ls -lL /etc/at.allow +If properly configured, the output should indicate the following group-owner: +root + Is it the case that /etc/at.allow does not have a group owner of root? + + + + To determine if the system is configured to audit calls to the +open system call, run the following command: +$ sudo grep "open" /etc/audit/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + Review the web site to determine if HTTP and HTTPs are used in accordance with +well known ports (e.g., 80 and 443) or those ports and services as registered +and approved for use by the DoD PPSM. + + + +To configure firewalld to allow http access, run the following command(s): +firewall-cmd --permanent --add-service=http + +Then run the following command to load the newly created rule(s): +firewall-cmd --reload + + + +To configure firewalld to allow https access, run the following command(s): +firewall-cmd --permanent --add-service=https + +Then run the following command to load the newly created rule(s): +firewall-cmd --reload + Is it the case that it is not? + + + + To determine if the system is configured to audit unsuccessful calls +to the lremovexattr system call, run the following command: +$ sudo grep "lremovexattr" /etc/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + Verify Red Hat Enterprise Linux 8 takes the appropriate action when the audit storage volume is full. + +Check that Red Hat Enterprise Linux 8 takes the appropriate action when the audit storage volume is full with the following command: + +$ sudo grep disk_full_action /etc/audit/auditd.conf + +disk_full_action = + +If the value of the "disk_full_action" option is not "SYSLOG", "SINGLE", or "HALT", or the line is commented out, ask the system administrator to indicate how the system takes appropriate action when an audit storage volume is full. + Is it the case that there is no evidence of appropriate action? + + + + Verify that Red Hat Enterprise Linux 8 is configured to audit the execution of the "init" command with the following command: + +$ sudo auditctl -l | grep init + +-a always,exit -F path=/usr/sbin/init -F perm=x -F auid>=1000 -F auid!=unset -k privileged-init + Is it the case that the command does not return a line, or the line is commented out? + + + + To check the permissions of /boot/efi/EFI/redhat/grub.cfg, run the command: +$ sudo ls -lL /boot/efi/EFI/redhat/grub.cfg +If properly configured, the output should indicate the following +permissions: -rwx------ + Is it the case that it does not? + + + + Verify the noexec option is configured for the /tmp mount point, + run the following command: + $ sudo mount | grep '\s/tmp\s' + . . . /tmp . . . noexec . . . + + Is it the case that the "/tmp" file system does not have the "noexec" option set? + + + + To ensure the system is configured to ignore the Ctrl-Alt-Del sequence, +run the following command: +$ gsettings get org.gnome.settings-daemon.plugins.media-keys logout +$ grep logout /etc/dconf/db/local.d/locks/* +If properly configured, the output should be +/org/gnome/settings-daemon/plugins/media-keys/logout + Is it the case that GNOME3 is configured to reboot when Ctrl-Alt-Del is pressed? + + + + To check if RekeyLimit is set correctly, run the +following command: + +$ sudo grep RekeyLimit /etc/ssh/sshd_config + +If configured properly, output should be +RekeyLimit + Is it the case that it is commented out or is not set? + + + + To ensure the user home directory is not group-writable or world-readable, run the following: +# ls -ld /home/USER + Is it the case that the user home directory is group-writable or world-readable? + + + + +Run the following command to determine if the nis_enabled SELinux boolean is disabled: +$ getsebool nis_enabled +If properly configured, the output should show the following: +nis_enabled --> off + Is it the case that nis_enabled is not disabled? + + + + + +Run the following command to determine the current status of the +iptables service: +$ sudo systemctl is-active iptables +If the service is running, it should return the following: active + Is it the case that ? + + + + +Run the following command to determine if the neutron_can_network SELinux boolean is disabled: +$ getsebool neutron_can_network +If properly configured, the output should show the following: +neutron_can_network --> off + Is it the case that neutron_can_network is not disabled? + + + + Run the following command to determine if the dnf-automatic package is installed: $ rpm -q dnf-automatic + Is it the case that the package is not installed? + + + + Verify the nodev option is configured for the /tmp mount point, + run the following command: + $ sudo mount | grep '\s/tmp\s' + . . . /tmp . . . nodev . . . + + Is it the case that the "/tmp" file system does not have the "nodev" option set? + + + + +Run the following command to determine if the httpd_can_connect_zabbix SELinux boolean is disabled: +$ getsebool httpd_can_connect_zabbix +If properly configured, the output should show the following: +httpd_can_connect_zabbix --> off + Is it the case that httpd_can_connect_zabbix is not disabled? + + + + Verify Red Hat Enterprise Linux 8 generates an audit record for unsuccessful attempts to modify files using the open system call with O_TRUNC_WRITE flag. + +If the auditd daemon is configured to use the "augenrules" program to read audit rules during daemon startup (the default), run the following command: + +$ sudo grep -r open /etc/audit/rules.d + +If the auditd daemon is configured to use the "auditctl" utility to read audit rules during daemon startup, run the following command: + +$ sudo grep open /etc/audit/audit.rules + +The output should be the following: + +-a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + Is it the case that the command does not return a line, or the line is commented out? + + + + Verify the nodev option is configured for the /var mount point, + run the following command: + $ sudo mount | grep '\s/var\s' + . . . /var . . . nodev . . . + + Is it the case that the "/var" file system does not have the "nodev" option set? + + + + +Run the following command to determine if the openvpn_enable_homedirs SELinux boolean is disabled: +$ getsebool openvpn_enable_homedirs +If properly configured, the output should show the following: +openvpn_enable_homedirs --> off + Is it the case that openvpn_enable_homedirs is not disabled? + + + + The runtime status of the net.ipv4.conf.all.accept_redirects kernel parameter can be queried +by running the following command: +$ sysctl net.ipv4.conf.all.accept_redirects +0. + + Is it the case that the correct value is not returned? + + + + To check that the rdisc service is disabled in system boot configuration, +run the following command: +$ sudo systemctl is-enabled rdisc +Output should indicate the rdisc service has either not been installed, +or has been disabled at all runlevels, as shown in the example below: +$ sudo systemctl is-enabled rdisc disabled + +Run the following command to verify rdisc is not active (i.e. not running) through current runtime configuration: +$ sudo systemctl is-active rdisc + +If the service is not running the command will return the following output: +inactive + +The service will also be masked, to check that the rdisc is masked, run the following command: +$ sudo systemctl show rdisc | grep "LoadState\|UnitFileState" + +If the service is masked the command will return the following outputs: + +LoadState=masked + +UnitFileState=masked + Is it the case that the "rdisc" is loaded and not masked? + + + + Inspect the form of default GRUB 2 command line for the Linux operating system +in /etc/default/grub. If it includes iommu=force, +then the parameter will be configured for newly installed kernels. +First check if the GRUB recovery is enabled: +$ sudo grep 'GRUB_DISABLE_RECOVERY' /etc/default/grub +If this option is set to true, then check that a line is output by the following command: +$ sudo grep 'GRUB_CMDLINE_LINUX_DEFAULT.*iommu=force.*' /etc/default/grub +If the recovery is disabled, check the line with +$ sudo grep 'GRUB_CMDLINE_LINUX.*iommu=force.*' /etc/default/grub.Moreover, command line parameters for currently installed kernels should be checked as well. +Run the following command: +$ sudo grubby --info=ALL | grep args | grep -v 'iommu=force' +The command should not return any output. + Is it the case that I/OMMU is not activated? + + + + The runtime status of the net.ipv6.conf.all.accept_ra_rtr_pref kernel parameter can be queried +by running the following command: +$ sysctl net.ipv6.conf.all.accept_ra_rtr_pref +0. + + Is it the case that the correct value is not returned? + + + + To verify if root user is required to use complex passwords, run the following command: +$ grep enforce_for_root /etc/security/pwquality.conf +The output should return enforce_for_root uncommented. + Is it the case that enforce_for_root is commented or not present? + + + + Run the following command to determine if the ntp package is installed: $ rpm -q ntp + Is it the case that the package is not installed? + + + + Run the following command to ensure the TMOUT value is configured for all users +on the system: + +$ sudo grep TMOUT /etc/profile /etc/profile.d/*.sh + +The output should return the following: +TMOUT= + Is it the case that TMOUT is not set or its value is greater than expected setting? + + + + + +Run the following command to determine the current status of the +rngd service: +$ sudo systemctl is-active rngd +If the service is running, it should return the following: active + Is it the case that the "rngd" service is disabled, masked, or not started.? + + + + +Run the following command to determine if the sysadm_exec_content SELinux boolean is enabled: +$ getsebool sysadm_exec_content +If properly configured, the output should show the following: +sysadm_exec_content --> on + Is it the case that sysadm_exec_content is not enabled? + + + + Find the list of alias maps used by the Postfix mail server: +$ sudo postconf alias_maps +Query the Postfix alias maps for an alias for the root user: +$ sudo postmap -q root hash:/etc/aliases +The output should return an alias. + Is it the case that the alias is not set? + + + + To ensure the X Windows package group is removed, run the following command: +$ rpm -qi xorg-x11-server-common +The output should be: +package xorg-x11-server-common is not installed + Is it the case that the X Windows package group or xorg-x11-server-common has not be removed? + + + + System executables are stored in the following directories by default: +/bin +/sbin +/usr/bin +/usr/local/bin +/usr/local/sbin +/usr/sbin +For each of these directories, run the following command to find files +not owned by root: +$ sudo find -L DIR/ ! -user root -type d -exec chown root {} \; + Is it the case that any system executables directories are found to not be owned by root? + + + + +Run the following command to determine if the git_session_users SELinux boolean is disabled: +$ getsebool git_session_users +If properly configured, the output should show the following: +git_session_users --> off + Is it the case that git_session_users is not disabled? + + + + +Run the following command to determine if the httpd_run_ipa SELinux boolean is disabled: +$ getsebool httpd_run_ipa +If properly configured, the output should show the following: +httpd_run_ipa --> off + Is it the case that httpd_run_ipa is not disabled? + + + + Inspect the password section of /etc/pam.d/password-auth +and ensure that the pam_unix.so module includes the argument +sha512: +$ grep sha512 /etc/pam.d/password-auth + Is it the case that it does not? + + + + To verify that the Audit is correctly configured according to recommended rules, check the content of the file with the following command: +cat /etc/audit/rules.d/30-ospp-v42-1-create-failed.rules +The output has to be exactly as follows: +## Unsuccessful file creation (open with O_CREAT) +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b32 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b64 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b32 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b64 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create + Is it the case that the file does not exist or the content differs? + + + + To verify the LDAP client backend demands a valid certificate from the server in +remote LDAP access sessions, run the following command: +$ sudo grep ldap_tls_reqcert /etc/sssd/sssd.conf +The output should return the following: +ldap_tls_reqcert = demand + Is it the case that the TLS reqcert is not set to demand? + + + + +If the system is configured to prevent the loading of the atm kernel module, +it will contain lines inside any file in /etc/modprobe.d or the deprecated /etc/modprobe.conf. +These lines instruct the module loading system to run another program (such as /bin/true) upon a module install event. + +These lines can also instruct the module loading system to ignore the atm kernel module via blacklist keyword. + +Run the following command to search for such lines in all files in /etc/modprobe.d and the deprecated /etc/modprobe.conf: +$ grep -r atm /etc/modprobe.conf /etc/modprobe.d + Is it the case that no line is returned? + + + + To check if MaxStartups is configured, run the following command: +$ sudo grep MaxStartups /etc/ssh/sshd_config +If configured, this command should output the configuration. + Is it the case that maxstartups is not configured? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_RANDOMIZE_BASE /boot/config.* + + For each kernel installed, a line with value "y" should be returned. + + Is it the case that the kernel was not built with the required value? + + + + To determine if the system is configured to audit unsuccessful calls +to the lsetxattr system call, run the following command: +$ sudo grep "lsetxattr" /etc/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + +Run the following command to determine if the httpd_dbus_sssd SELinux boolean is disabled: +$ getsebool httpd_dbus_sssd +If properly configured, the output should show the following: +httpd_dbus_sssd --> off + Is it the case that httpd_dbus_sssd is not disabled? + + + + System executables are stored in the following directories by default: +/bin +/sbin +/usr/bin +/usr/sbin +/usr/local/bin +/usr/local/sbin +To find system executables directories that are group-writable or +world-writable, run the following command for each directory DIR +which contains system executables: +$ sudo find -L DIR -perm /022 -type d + Is it the case that any of these files are group-writable or world-writable? + + + + To verify that the Audit is correctly configured according to recommended rules, check the content of the file with the following command: +cat /etc/audit/rules.d/30-ospp-v42-3-access-failed.rules +The output has to be exactly as follows: +## Unsuccessful file access (any other opens) This has to go last. +-a always,exit -F arch=b32 -S open,openat,openat2,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-access +-a always,exit -F arch=b64 -S open,openat,openat2,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-access +-a always,exit -F arch=b32 -S open,openat,openat2,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-access +-a always,exit -F arch=b64 -S open,openat,openat2,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-access + Is it the case that the file does not exist or the content differs? + + + + To check which SSH protocol version is allowed, check version of +openssh-server with following command: +$ rpm -qi openssh-server | grep Version +Versions equal to or higher than 7.4 have deprecated the RhostsRSAAuthentication option. +If version is lower than 7.4, run the following command to check configuration: +To determine how the SSH daemon's RhostsRSAAuthentication option is set, run the following command: + +$ sudo grep -i RhostsRSAAuthentication /etc/ssh/sshd_config + +If a line indicating no is returned, then the required value is set. + + Is it the case that the required value is not set? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_BUG_ON_DATA_CORRUPTION /boot/config.* + + For each kernel installed, a line with value "y" should be returned. + + Is it the case that the kernel was not built with the required value? + + + + Verify Red Hat Enterprise Linux 8 generates an audit record for unsuccessful attempts to use the openat system call. + +If the auditd daemon is configured to use the "augenrules" program to to read audit rules during daemon startup (the default), run the following command: + +$ sudo grep -r openat /etc/audit/rules.d + +If the auditd daemon is configured to use the "auditctl" utility to read audit rules during daemon startup, run the following command: + +$ sudo grep openat /etc/audit/audit.rules + +The output should be the following: + +-a always,exit -F arch=b32 -S openat -F exit=-EPERM -F auid>=1000 -F auid!=unset -k access +-a always,exit -F arch=b64 -S openat -F exit=-EPERM -F auid>=1000 -F auid!=unset -k access +-a always,exit -F arch=b32 -S openat -F exit=-EACCES -F auid>=1000 -F auid!=unset -k access +-a always,exit -F arch=b64 -S openat -F exit=-EACCES -F auid>=1000 -F auid!=unset -k access + Is it the case that the command does not return a line, or the line is commented out? + + + + Make sure that the kernel is not disabling SMEP with the following +commands. +grep -q nosmep /boot/config-`uname -r` +If the command returns a line, it means that SMEP is being disabled. + Is it the case that the kernel is configured to disable SMEP? + + + + The runtime status of the fs.protected_hardlinks kernel parameter can be queried +by running the following command: +$ sysctl fs.protected_hardlinks +1. + + Is it the case that the correct value is not returned? + + + + To determine if env_reset has been configured for sudo, run the following command: +$ sudo grep -ri "^[\s]*Defaults.*\benv_reset\b.*" /etc/sudoers /etc/sudoers.d/ +The command should return a matching output. + Is it the case that env_reset is not enabled in sudo? + + + + Verify Red Hat Enterprise Linux 8 generates audit records for all account creations, modifications, disabling, and termination events that affect "/var/log/lastlog" with the following command: + +$ sudo auditctl -l | grep /var/log/lastlog + +-w /var/log/lastlog -p wa -k logins + Is it the case that the command does not return a line, or the line is commented out? + + + + Configure the public web server to not have a trusted relationship with +any system resources that is also not accessible to the public. Web +content is not to be shared via Microsoft shares or NFS mounts. + +Determine whether the public web server has a two-way trust relationship +with any private asset located within the network. Private web server +resources (e.g. drives, folders, printers, etc.) will not be directly +mapped to or shared with public web servers. + Is it the case that sharing is selected for any web folder, this is a finding. + +If private resources (e.g. drives, partitions, folders/directories, +printers, etc.) are sharedw ith the public web server? + + + + Verify Red Hat Enterprise Linux 8 takes the appropriate action when the audit files have reached maximum size. + +Check that Red Hat Enterprise Linux 8 takes the appropriate action when the audit files have reached maximum size with the following command: + +$ sudo grep max_log_file_action /etc/audit/auditd.conf + +max_log_file_action = + Is it the case that the value of the "disk_full_action" option is not "ROTATE", "SINGLE", or the line is commented out, ask the system administrator to indicate how the system takes appropriate action when an audit storage volume is full. If there is no evidence of appropriate action? + + + + To check the ownership of /etc/passwd-, +run the command: +$ ls -lL /etc/passwd- +If properly configured, the output should indicate the following owner: +root + Is it the case that /etc/passwd- does not have an owner of root? + + + + Run the following command to determine if the vim-enhanced package is installed: $ rpm -q vim-enhanced + Is it the case that the package is not installed? + + + + +Run the following command to determine if the unconfined_mozilla_plugin_transition SELinux boolean is enabled: +$ getsebool unconfined_mozilla_plugin_transition +If properly configured, the output should show the following: +unconfined_mozilla_plugin_transition --> on + Is it the case that unconfined_mozilla_plugin_transition is not enabled? + + + + To determine if the system is configured to audit calls to the +lsetxattr system call, run the following command: +$ sudo grep "lsetxattr" /etc/audit/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + +Run the following command to determine if the git_cgi_use_cifs SELinux boolean is disabled: +$ getsebool git_cgi_use_cifs +If properly configured, the output should show the following: +git_cgi_use_cifs --> off + Is it the case that git_cgi_use_cifs is not disabled? + + + + To check the ownership of /boot/grub2/grub.cfg, +run the command: +$ ls -lL /boot/grub2/grub.cfg +If properly configured, the output should indicate the following owner: +root + Is it the case that /boot/grub2/grub.cfg does not have an owner of root? + + + + +Run the following command to determine if the pppd_for_user SELinux boolean is disabled: +$ getsebool pppd_for_user +If properly configured, the output should show the following: +pppd_for_user --> off + Is it the case that pppd_for_user is not disabled? + + + + Run the following command to determine if the crypto-policies package is installed: $ rpm -q crypto-policies + Is it the case that the package is not installed? + + + + Verify that only the "root" account has a UID "0" assignment with the +following command: +$ awk -F: '$3 == 0 {print $1}' /etc/passwd +root + Is it the case that any accounts other than "root" have a UID of "0"? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_MODULE_SIG_HASH /boot/config.* + + For each kernel installed, a line with value "" should be returned. + + Is it the case that the kernel was not built with the required value? + + + + To determine if the system is configured to audit calls to the +finit_module system call, run the following command: +$ sudo grep "finit_module" /etc/audit/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + +Run the following command to determine if the selinuxuser_rw_noexattrfile SELinux boolean is disabled: +$ getsebool selinuxuser_rw_noexattrfile +If properly configured, the output should show the following: +selinuxuser_rw_noexattrfile --> off + Is it the case that selinuxuser_rw_noexattrfile is not disabled? + + + + Ensure there are no unconfined daemons running on the system, +the following command should produce no output: +$ sudo ps -eZ | grep "unconfined_service_t" + Is it the case that There are unconfined daemons running on the system? + + + + To check the group ownership of /var/log, +run the command: +$ ls -lL /var/log +If properly configured, the output should indicate the following group-owner: +root + Is it the case that /var/log does not have a group owner of root? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_SYN_COOKIES /boot/config.* + + For each kernel installed, a line with value "y" should be returned. + + Is it the case that the kernel was not built with the required value? + + + + To determine if the system is configured to audit successful calls +to the lchown system call, run the following command: +$ sudo grep "lchown" /etc/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + To determine if the system is configured to audit calls to the +open_by_handle_at system call, run the following command: +$ sudo grep "open_by_handle_at" /etc/audit/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + To determine if negation is used to define commands users are allowed to execute using sudo, run the following command: +$ sudo grep -PR '^(?:\s*[^#=]+)=(?:\s*(?:\([^\)]+\))?\s*(?!\s*\()[^,!\n][^,\n]+,)*\s*(?:\([^\)]+\))?\s*(?!\s*\()(!\S+).*' /etc/sudoers /etc/sudoers.d/ +The command should return no output. + Is it the case that /etc/sudoers file contains rules that define the set of allowed commands using negation? + + + + To verify all local initialization files for interactive users are owned by the +primary user, run the following command: +$ sudo ls -al /home/USER/.* +The user initialization files should be owned by USER. + Is it the case that they are not? + + + + To check that the portreserve service is disabled in system boot configuration, +run the following command: +$ sudo systemctl is-enabled portreserve +Output should indicate the portreserve service has either not been installed, +or has been disabled at all runlevels, as shown in the example below: +$ sudo systemctl is-enabled portreserve disabled + +Run the following command to verify portreserve is not active (i.e. not running) through current runtime configuration: +$ sudo systemctl is-active portreserve + +If the service is not running the command will return the following output: +inactive + +The service will also be masked, to check that the portreserve is masked, run the following command: +$ sudo systemctl show portreserve | grep "LoadState\|UnitFileState" + +If the service is masked the command will return the following outputs: + +LoadState=masked + +UnitFileState=masked + Is it the case that the "portreserve" is loaded and not masked? + + + + To verify interactive users on the system have a home directory assigned, +run the following command: +$ sudo awk -F":" '{print $1 ":" $6}' /etc/passwd +Inspect the output and verify that all interactive users have a home directory +defined. + Is it the case that users home directory is not defined? + + + + To check the permissions of /etc/audit/rules.d/*.rules, +run the command: +$ ls -l /etc/audit/rules.d/*.rules +If properly configured, the output should indicate the following permissions: +-rw-r----- + Is it the case that /etc/audit/rules.d/*.rules does not have unix mode -rw-r-----? + + + + +Run the following command to determine if the xen_use_nfs SELinux boolean is disabled: +$ getsebool xen_use_nfs +If properly configured, the output should show the following: +xen_use_nfs --> off + Is it the case that xen_use_nfs is not disabled? + + + + Determine where the audit logs are stored with the following command: + +$ sudo grep -iw log_file /etc/audit/auditd.conf + +log_file = /var/log/audit/audit.log + +Determine the owner of the audit log directory by using the output of the above command +(default: "/var/log/audit/"). Run the following command with the correct audit log directory +path: + +$ sudo ls -ld /var/log/audit + +drwx------ 2 root root 23 Jun 11 11:56 /var/log/audit + +The audit log directory must be owned by "root" + Is it the case that the directory is not owned by root? + + + + To verify that Audit Daemon is configured to write logs to the disk, run the +following command: +$ sudo grep write_logs /etc/audit/auditd.conf +The output should return the following: +write_logs = yes + Is it the case that write_logs isn't set to yes? + + + + The runtime status of the net.ipv4.conf.default.rp_filter kernel parameter can be queried +by running the following command: +$ sysctl net.ipv4.conf.default.rp_filter +1. + + Is it the case that the correct value is not returned? + + + + To verify that HBSS ACCM is installed, run the following command(s): +$ sudo ls /opt/McAfee/accm/bin/accm + Is it the case that the HBSS ACCM module is not installed? + + + + Verify that the interactive user account passwords are using a strong +password hash with the following command: + +$ sudo cut -d: -f2 /etc/shadow + +$6$kcOnRq/5$NUEYPuyL.wghQwWssXRcLRFiiru7f5JPV6GaJhNC2aK5F3PZpE/BCCtwrxRc/AInKMNX3CdMw11m9STiql12f/ + +Password hashes ! or * indicate inactive accounts not +available for logon and are not evaluated. + Is it the case that any interactive user password hash does not begin with "$6"? + + + + The telnet package can be removed with the following command: $ sudo yum erase telnet + Is it the case that ? + + + + Locate the directories containing the CGI scripts. These directories should be +language-specific (e.g., PERL, ASP, JS, JSP, etc.). Examine the file permissions +on the directories using the following command: +ls -l directories +Anonymous FTP users must not have access to these directories. + Is it the case that it is not? + + + + Verify that Red Hat Enterprise Linux 8 is configured to audit the execution of the "setsebool" command with the following command: + +$ sudo auditctl -l | grep setsebool + +-a always,exit -F path=/usr/sbin/setsebool -F perm=x -F auid>=1000 -F auid!=unset -k privileged + Is it the case that the command does not return a line, or the line is commented out? + + + + +Run the following command to determine if the webadm_read_user_files SELinux boolean is disabled: +$ getsebool webadm_read_user_files +If properly configured, the output should show the following: +webadm_read_user_files --> off + Is it the case that webadm_read_user_files is not disabled? + + + + To verify that there are no unauthorized local user accounts, run the following command: +$ less /etc/passwd +Inspect the results, and if unauthorized local user accounts exist, remove them by running +the following command: +$ sudo userdel unauthorized_user + Is it the case that there are unauthorized local user accounts on the system? + + + + To verify that the audit system collects unauthorized file accesses, run the following commands: +$ sudo grep EACCES /etc/audit/audit.rules +$ sudo grep EPERM /etc/audit/audit.rules + Is it the case that 32-bit and 64-bit system calls to creat, open, openat, open_by_handle_at, truncate, and ftruncate are not audited during EACCES and EPERM? + + + + +Run the following command to determine if the smartmon_3ware SELinux boolean is disabled: +$ getsebool smartmon_3ware +If properly configured, the output should show the following: +smartmon_3ware --> off + Is it the case that smartmon_3ware is not disabled? + + + + To verify that root's primary group is zero run the following command: + + grep '^root:' /etc/passwd | cut -d : -f 4 + +The command should return: + +0 + + Is it the case that root has a primary gid not equal to zero? + + + + +Run the following command to determine if the httpd_enable_homedirs SELinux boolean is disabled: +$ getsebool httpd_enable_homedirs +If properly configured, the output should show the following: +httpd_enable_homedirs --> off + Is it the case that httpd_enable_homedirs is not disabled? + + + + Verify Red Hat Enterprise Linux 8 generates an audit record for unsuccessful attempts to use the creat system call. + +If the auditd daemon is configured to use the "augenrules" program to to read audit rules during daemon startup (the default), run the following command: + +$ sudo grep -r creat /etc/audit/rules.d + +If the auditd daemon is configured to use the "auditctl" utility to read audit rules during daemon startup, run the following command: + +$ sudo grep creat /etc/audit/audit.rules + +The output should be the following: + +-a always,exit -F arch=b32 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -k access +-a always,exit -F arch=b64 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -k access +-a always,exit -F arch=b32 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -k access +-a always,exit -F arch=b64 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -k access + Is it the case that the command does not return a line, or the line is commented out? + + + + +Run the following command to determine if the domain_kernel_load_modules SELinux boolean is disabled: +$ getsebool domain_kernel_load_modules +If properly configured, the output should show the following: +domain_kernel_load_modules --> off + Is it the case that domain_kernel_load_modules is not disabled? + + + + To verify that TLS is configured properly in +/etc/httpd/conf.modules.d/ssl.conf, run the following command: +$ grep -i "sslengine\|sslprotocol" /etc/httpd/conf.d/ssl.conf +The output should return the following: + +SSLEngine on +SSLProtocol all -SSLv2 -SSLv3 -TLSv1 -TLSv1.1 + + Is it the case that it is not? + + + + Check that the symlink exists and target the correct Kerberos crypto policy, with the following command: +file /etc/krb5.conf.d/crypto-policies +If command ouput shows the following line, Kerberos is configured to use the system-wide crypto policy. +/etc/krb5.conf.d/crypto-policies: symbolic link to /etc/crypto-policies/back-ends/krb5.config + Is it the case that the symlink does not exist or points to a different target? + + + + Verify that the system is integrated with a centralized authentication mechanism +such as as Active Directory, Kerberos, Directory Server, etc. that has +automated account mechanisms in place. + Is it the case that the system is not using a centralized authentication mechanism, or it is not automated? + + + + Run the following command to determine if the gssproxy package is installed: +$ rpm -q gssproxy + Is it the case that the package is installed? + + + + Run the following command to determine if the tftp-server package is installed: +$ rpm -q tftp-server + Is it the case that the package is installed? + + + + To verify insecure file locking has been disabled, run the following command: +$ grep insecure_locks /etc/exports + Is it the case that there is output? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_SECURITY_DMESG_RESTRICT /boot/config.* + + Configs with value 'n' are not explicitly set in the file, so either commented lines or no + lines should be returned. + + Is it the case that the kernel was not built with the required value? + + + + +Run the following command to determine if the secure_mode_policyload SELinux boolean is disabled: +$ getsebool secure_mode_policyload +If properly configured, the output should show the following: +secure_mode_policyload --> off + Is it the case that secure_mode_policyload is not disabled? + + + + To verify if CustomLog is configured correctly in +/etc/httpd/conf/httpd.conf, run the following command: +$ grep -i customlog /etc/httpd/conf/httpd.conf +The output should return the following: +CustomLog "logs/access_log" combined + Is it the case that it is not? + + + + The runtime status of the net.ipv4.conf.all.accept_local kernel parameter can be queried +by running the following command: +$ sysctl net.ipv4.conf.all.accept_local +0. + + Is it the case that the correct value is not returned? + + + + To verify the audispd's syslog plugin is active, run the following command: +$ sudo grep active /etc/audit/plugins.d/syslog.conf +If the plugin is active, the output will show yes. + Is it the case that it is not activated? + + + + The runtime status of the fs.suid_dumpable kernel parameter can be queried +by running the following command: +$ sysctl fs.suid_dumpable +0. + + Is it the case that the correct value is not returned? + + + + The following command will discover and print any +files on local partitions which do not belong to a valid user. +$ df --local -P | awk {'if (NR!=1) print $6'} | sudo xargs -I '{}' find '{}' -xdev -nouser + +Either remove all files and directories from the system that do not have a +valid user, or assign a valid user to all unowned files and directories on +the system with the chown command: +$ sudo chown user file + Is it the case that files exist that are not owned by a valid user? + + + + + +Run the following command to determine the current status of the +chronyd service: +$ sudo systemctl is-active chronyd +If the service is running, it should return the following: active + Is it the case that the chronyd process is not running? + + + + To determine if the system is configured to audit successful calls +to the renameat system call, run the following command: +$ sudo grep "renameat" /etc/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + + +Run the following command to determine the current status of the +syslog-ng service: +$ sudo systemctl is-active syslog-ng +If the service is running, it should return the following: active + Is it the case that the "syslog-ng" service is disabled, masked, or not started.? + + + + To verify that auditing of privileged command use is configured, run the +following command for each local partition PART to find relevant +setuid / setgid programs: +$ sudo find PART -xdev -type f -perm -4000 -o -type f -perm -2000 2>/dev/null +Run the following command to verify entries in the audit rules for all programs +found with the previous command: +$ sudo grep path /etc/audit/audit.rules +All relevant setuid / setgid programs have a line +in the audit rules. + Is it the case that any setuid or setgid programs doesn't have a line in the audit rules? + + + + To check the permissions of /etc/crontab, +run the command: +$ ls -l /etc/crontab +If properly configured, the output should indicate the following permissions: +-rw------- + Is it the case that /etc/crontab does not have unix mode -rw-------? + + + + Run the following command to determine if the dnf-plugin-subscription-manager package is installed: $ rpm -q dnf-plugin-subscription-manager + Is it the case that the package is not installed? + + + + To verify that BIND uses the system crypto policy, check out that the BIND config file +/etc/named.conf contains the include "/etc/crypto-policies/back-ends/bind.config"; +directive: +$ sudo grep 'include "/etc/crypto-policies/back-ends/bind.config";' /etc/named.conf +Verify that the directive is at the bottom of the options section of the config file. + Is it the case that BIND is installed and the BIND config file doesn't contain the +<pre>include "/etc/crypto-policies/back-ends/bind.config";</pre> directive? + + + + To determine if the system is configured to audit calls to the +openat system call, run the following command: +$ sudo grep "openat" /etc/audit/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + Verify Red Hat Enterprise Linux 8 is configured in the password-auth file to prohibit password reuse +for a minimum of generations with the following command: + +$ grep -i remember /etc/pam.d/password-auth +password pam_pwhistory.so use_authtok remember= retry=3 + Is it the case that the line containing "pam_pwhistory.so" does not have the "remember" module argument set, +is commented out, or the value of the "remember" module argument is set to less than +"<sub idref="var_password_pam_remember" />"? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_GCC_PLUGIN_STRUCTLEAK /boot/config.* + + For each kernel installed, a line with value "y" should be returned. + + Is it the case that the kernel was not built with the required value? + + + + To verify if MaxKeepAliveRequests is configured correctly in +/etc/httpd/conf/httpd.conf, run the following command: +$ grep -i maxkeepaliverequests /etc/httpd/conf/httpd.conf +The command should return the following: +MaxKeepAliveRequests 100 + Is it the case that it is not? + + + + +Run the following command to determine if the abrt_upload_watch_anon_write SELinux boolean is disabled: +$ getsebool abrt_upload_watch_anon_write +If properly configured, the output should show the following: +abrt_upload_watch_anon_write --> off + Is it the case that abrt_upload_watch_anon_write is not disabled? + + + + +Run the following command to determine if the antivirus_can_scan_system SELinux boolean is enabled: +$ getsebool antivirus_can_scan_system +If properly configured, the output should show the following: +antivirus_can_scan_system --> on + Is it the case that antivirus_can_scan_system is not enabled? + + + + To check that the tftp service is disabled in system boot configuration, +run the following command: +$ sudo systemctl is-enabled tftp +Output should indicate the tftp service has either not been installed, +or has been disabled at all runlevels, as shown in the example below: +$ sudo systemctl is-enabled tftp disabled + +Run the following command to verify tftp is not active (i.e. not running) through current runtime configuration: +$ sudo systemctl is-active tftp + +If the service is not running the command will return the following output: +inactive + +The service will also be masked, to check that the tftp is masked, run the following command: +$ sudo systemctl show tftp | grep "LoadState\|UnitFileState" + +If the service is masked the command will return the following outputs: + +LoadState=masked + +UnitFileState=masked + Is it the case that the "tftp" is loaded and not masked? + + + + Verify the operating system encrypts audit records off-loaded onto a different system +or media from the system being audited with the following commands: + +$ sudo grep -i '$DefaultNetstreamDriver' /etc/rsyslog.conf /etc/rsyslog.d/*.conf + +The output should be: + +/etc/rsyslog.conf:$DefaultNetstreamDriver gtls + Is it the case that rsyslogd DefaultNetstreamDriver not set to gtls? + + + + Verify the nosuid option is configured for the /var mount point, + run the following command: + $ sudo mount | grep '\s/var\s' + . . . /var . . . nosuid . . . + + Is it the case that the "/var" file system does not have the "nosuid" option set? + + + + Verify the nodev option is configured for the /var/log mount point, + run the following command: + $ sudo mount | grep '\s/var/log\s' + . . . /var/log . . . nodev . . . + + Is it the case that the "/var/log" file system does not have the "nodev" option set? + + + + +Run the following command to determine if the icecast_use_any_tcp_ports SELinux boolean is disabled: +$ getsebool icecast_use_any_tcp_ports +If properly configured, the output should show the following: +icecast_use_any_tcp_ports --> off + Is it the case that icecast_use_any_tcp_ports is not disabled? + + + + To check the permissions of /var/log/messages, +run the command: +$ ls -l /var/log/messages +If properly configured, the output should indicate the following permissions: +-rw-r----- + Is it the case that /var/log/messages does not have unix mode -rw-r-----? + + + + Inspect /etc/login.defs and ensure that if eihter +SHA_CRYPT_MIN_ROUNDS or SHA_CRYPT_MAX_ROUNDS +are set, they must have the minimum value of 5000. + Is it the case that it does not? + + + + To determine if the system is configured to audit calls to the +init_module system call, run the following command: +$ sudo grep "init_module" /etc/audit/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_HARDENED_USERCOPY_FALLBACK /boot/config.* + + Configs with value 'n' are not explicitly set in the file, so either commented lines or no + lines should be returned. + + Is it the case that the kernel was not built with the required value? + + + + Only FIPS-approved MACs should be used. To verify that only FIPS-approved +MACs are in use, run the following command: +$ sudo grep -i macs /etc/ssh/sshd_config +The output should contain only those MACs which are FIPS-approved. Any use of other +ciphers or algorithms will result in the module entering the non-FIPS mode of +operation. + Is it the case that MACs option is commented out or not using FIPS-approved hash algorithms? + + + + The tftp package can be removed with the following command: $ sudo yum erase tftp + Is it the case that ? + + + + To verify that each web content directory exists on separate partitions, +run the following command: +$ grep `grep -i documentroot /etc/httpd/conf/httpd.conf | awk -F'"' '{print $2}'` /etc/fstab +Each of the corresponding DocumentRoot entries should have a +corresponding entry in /etc/fstab. + Is it the case that it is not? + + + + To determine if the system is configured to audit calls to the +mount system call, run the following command: +$ sudo grep "mount" /etc/audit/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + Verify that logging core dump backtraces is disabled, run the +following command: +$ grep ProcessSizeMax /etc/systemd/coredump.conf + Is it the case that ProcessSizeMax is not set to zero? + + + + To check that the rpcsvcgssd service is disabled in system boot configuration, +run the following command: +$ sudo systemctl is-enabled rpcsvcgssd +Output should indicate the rpcsvcgssd service has either not been installed, +or has been disabled at all runlevels, as shown in the example below: +$ sudo systemctl is-enabled rpcsvcgssd disabled + +Run the following command to verify rpcsvcgssd is not active (i.e. not running) through current runtime configuration: +$ sudo systemctl is-active rpcsvcgssd + +If the service is not running the command will return the following output: +inactive + +The service will also be masked, to check that the rpcsvcgssd is masked, run the following command: +$ sudo systemctl show rpcsvcgssd | grep "LoadState\|UnitFileState" + +If the service is masked the command will return the following outputs: + +LoadState=masked + +UnitFileState=masked + Is it the case that the "rpcsvcgssd" is loaded and not masked? + + + + Verify that there are no shosts.equiv files +on the system, run the following command: +$ find / -name shosts.equiv + Is it the case that shosts.equiv files exist? + + + + +Run the following command to determine if the httpd_ssi_exec SELinux boolean is disabled: +$ getsebool httpd_ssi_exec +If properly configured, the output should show the following: +httpd_ssi_exec --> off + Is it the case that httpd_ssi_exec is not disabled? + + + + To verify all squashing has been disabled, run the following command: +$ grep all_squash /etc/exports + Is it the case that there is output? + + + + +Run the following command to determine if the zoneminder_anon_write SELinux boolean is disabled: +$ getsebool zoneminder_anon_write +If properly configured, the output should show the following: +zoneminder_anon_write --> off + Is it the case that zoneminder_anon_write is not disabled? + + + + To verify that all interactive user initialization files executable search +path statements do not contain statements that will reference a working +directory other than the users home directory, run the following command: +$ sudo grep -r PATH /home/ +Inspect the output for any PATH is references directories outside the home directory. + Is it the case that paths contain more than local home directories? + + + + +Run the following command to determine if the fenced_can_ssh SELinux boolean is disabled: +$ getsebool fenced_can_ssh +If properly configured, the output should show the following: +fenced_can_ssh --> off + Is it the case that fenced_can_ssh is not disabled? + + + + +Run the following command to determine if the dbadm_exec_content SELinux boolean is enabled: +$ getsebool dbadm_exec_content +If properly configured, the output should show the following: +dbadm_exec_content --> on + Is it the case that dbadm_exec_content is not enabled? + + + + To check that the cups service is disabled in system boot configuration, +run the following command: +$ sudo systemctl is-enabled cups +Output should indicate the cups service has either not been installed, +or has been disabled at all runlevels, as shown in the example below: +$ sudo systemctl is-enabled cups disabled + +Run the following command to verify cups is not active (i.e. not running) through current runtime configuration: +$ sudo systemctl is-active cups + +If the service is not running the command will return the following output: +inactive + +The service will also be masked, to check that the cups is masked, run the following command: +$ sudo systemctl show cups | grep "LoadState\|UnitFileState" + +If the service is masked the command will return the following outputs: + +LoadState=masked + +UnitFileState=masked + Is it the case that the "cups" is loaded and not masked? + + + + Verify that Red Hat Enterprise Linux 8 is configured to audit the execution of the "crontab" command with the following command: + +$ sudo auditctl -l | grep crontab + +-a always,exit -F path=/usr/bin/crontab -F perm=x -F auid>=1000 -F auid!=unset -k privileged-crontab + Is it the case that the command does not return a line, or the line is commented out? + + + + The following command will discover and print world-writable directories that +are not owned by a system account, given the assumption that only system +accounts have a uid lower than 500. Run it once for each local partition PART: +$ sudo find PART -xdev -type d -perm -0002 -uid +499 -print + Is it the case that there is output? + + + + Verify the nodev option is configured for the /var/log/audit mount point, + run the following command: + $ sudo mount | grep '\s/var/log/audit\s' + . . . /var/log/audit . . . nodev . . . + + Is it the case that the "/var/log/audit" file system does not have the "nodev" option set? + + + + To determine if the system is configured to audit successful calls +to the lremovexattr system call, run the following command: +$ sudo grep "lremovexattr" /etc/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + Verify that Promiscuous mode of an interface is disabled, run the following command: +$ ip link | grep PROMISC + Is it the case that any network device is in promiscuous mode? + + + + Verify that a separate file system/partition has been created for /var/log/audit with the following command: + +$ mountpoint /var/log/audit + + Is it the case that "/var/log/audit is not a mountpoint" is returned? + + + + To determine if the system is configured to audit unsuccessful calls +to the fchownat system call, run the following command: +$ sudo grep "fchownat" /etc/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + Verify that Red Hat Enterprise Linux 8 is configured to audit the execution of the "chacl" command with the following command: + +$ sudo auditctl -l | grep chacl + +-a always,exit -F path=/usr/bin/chacl -F perm=x -F auid>=1000 -F auid!=unset -k perm_mod + Is it the case that the command does not return a line, or the line is commented out? + + + + To check if pam_pwquality.so is enabled in password-auth, run the following command: +$ grep pam_pwquality /etc/pam.d/password-auth +The output should be similar to the following: +password requisite pam_pwquality.so + Is it the case that pam_pwquality.so is not enabled in password-auth? + + + + +Run the following command to determine if the mozilla_plugin_use_bluejeans SELinux boolean is disabled: +$ getsebool mozilla_plugin_use_bluejeans +If properly configured, the output should show the following: +mozilla_plugin_use_bluejeans --> off + Is it the case that mozilla_plugin_use_bluejeans is not disabled? + + + + +Run the following command to determine if the nscd_use_shm SELinux boolean is enabled: +$ getsebool nscd_use_shm +If properly configured, the output should show the following: +nscd_use_shm --> on + Is it the case that nscd_use_shm is not enabled? + + + + +Run the following command to determine if the wine_mmap_zero_ignore SELinux boolean is disabled: +$ getsebool wine_mmap_zero_ignore +If properly configured, the output should show the following: +wine_mmap_zero_ignore --> off + Is it the case that wine_mmap_zero_ignore is not disabled? + + + + To determine if the system is configured to audit attempts to +alter time via the /etc/localtime file, run the following +command: +$ sudo auditctl -l | grep "watch=/etc/localtime" +If the system is configured to audit this activity, it will return a line. + Is it the case that the system is not configured to audit time changes? + + + + To check the permissions of /etc/shadow-, +run the command: +$ ls -l /etc/shadow- +If properly configured, the output should indicate the following permissions: +---------- + Is it the case that /etc/shadow- does not have unix mode ----------? + + + + Storing logs with compression can help avoid filling the system disk. +Run the following command to verify that journald is compressing logs. + +grep "^\sCompress" /etc/systemd/journald.conf + +and it should return + +Compress=yes + + Is it the case that is commented out or not configured correctly? + + + + Inspect the form of default GRUB 2 command line for the Linux operating system +in /etc/default/grub. If it includes spec_store_bypass_disable=, +then the parameter will be configured for newly installed kernels. +First check if the GRUB recovery is enabled: +$ sudo grep 'GRUB_DISABLE_RECOVERY' /etc/default/grub +If this option is set to true, then check that a line is output by the following command: +$ sudo grep 'GRUB_CMDLINE_LINUX_DEFAULT.*spec_store_bypass_disable=.*' /etc/default/grub +If the recovery is disabled, check the line with +$ sudo grep 'GRUB_CMDLINE_LINUX.*spec_store_bypass_disable=.*' /etc/default/grub.Moreover, command line parameters for currently installed kernels should be checked as well. +Run the following command: +$ sudo grubby --info=ALL | grep args | grep -v 'spec_store_bypass_disable=' +The command should not return any output. + Is it the case that SSB is not configured appropriately? + + + + To check that the certmonger service is disabled in system boot configuration, +run the following command: +$ sudo systemctl is-enabled certmonger +Output should indicate the certmonger service has either not been installed, +or has been disabled at all runlevels, as shown in the example below: +$ sudo systemctl is-enabled certmonger disabled + +Run the following command to verify certmonger is not active (i.e. not running) through current runtime configuration: +$ sudo systemctl is-active certmonger + +If the service is not running the command will return the following output: +inactive + +The service will also be masked, to check that the certmonger is masked, run the following command: +$ sudo systemctl show certmonger | grep "LoadState\|UnitFileState" + +If the service is masked the command will return the following outputs: + +LoadState=masked + +UnitFileState=masked + Is it the case that the "certmonger" is loaded and not masked? + + + + Run the following command to determine if the abrt-plugin-logger package is installed: +$ rpm -q abrt-plugin-logger + Is it the case that the package is installed? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_SLAB_MERGE_DEFAULT /boot/config.* + + Configs with value 'n' are not explicitly set in the file, so either commented lines or no + lines should be returned. + + Is it the case that the kernel was not built with the required value? + + + + Verify Red Hat Enterprise Linux 8 generates an audit record for unsuccessful attempts to modify files using the openat system call with O_TRUNC_WRITE flag. + +If the auditd daemon is configured to use the "augenrules" program to read audit rules during daemon startup (the default), run the following command: + +$ sudo grep -r openat /etc/audit/rules.d + +If the auditd daemon is configured to use the "auditctl" utility to read audit rules during daemon startup, run the following command: + +$ sudo grep openat /etc/audit/audit.rules + +The output should be the following: + +-a always,exit -F arch=b32 -S openat -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S openat -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S openat -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S openat -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + Is it the case that the command does not return a line, or the line is commented out? + + + + To verify that OpenSSL uses the system crypto policy, check out that the OpenSSL config file +/etc/pki/tls/openssl.cnf contains the [ crypto_policy ] section with the +.include /etc/crypto-policies/back-ends/opensslcnf.config directive: + +$ sudo grep '\.include\s* /etc/crypto-policies/back-ends/opensslcnf.config$' /etc/pki/tls/openssl.cnf. + Is it the case that the OpenSSL config file doesn't contain the whole section, +or the section doesn't contain the <pre>.include /etc/crypto-policies/back-ends/opensslcnf.config</pre> directive? + + + + To verify that SSSD is configured for PAM services, run the following command: +$ sudo grep services /etc/sssd/sssd.conf +If configured properly, output should be similar to +services = pam + Is it the case that it does not exist or 'pam' is not added to the 'services' option under the 'sssd' section? + + + + To verify if LogLevel is configured correctly in +/etc/httpd/conf/httpd.conf, run the following command: +$ grep -i loglevel /etc/httpd/conf/httpd.conf +The command should return the following: +LogLevel warn + Is it the case that it is not? + + + + Run the following command to determine if the cronie-anacron package is installed: +$ rpm -q cronie-anacron + Is it the case that the package is installed? + + + + To verify if ErrorLog is configured correctly in +/etc/httpd/conf/httpd.conf, run the following command: +$ grep -i errorlog /etc/httpd/conf/httpd.conf +The output should return the following: +ErrorLog "logs/error_log" + Is it the case that it is not? + + + + Run the following command to determine if the libcap-ng-utils package is installed: $ rpm -q libcap-ng-utils + Is it the case that the package is not installed? + + + + To verify the boot loader superuser account has been set, run the following +command: +sudo grep -A1 "superusers" /boot/grub2/grub.cfg +The output should show the following: +set superusers="superusers-account" +export superusers +where superusers-account is the actual account name different from common names like root, +admin, or administrator and different from any other existing user name. + Is it the case that superuser account is not set or is set to root, admin, administrator or any other existing user name? + + + + +Run the following command to determine if the xdm_exec_bootloader SELinux boolean is disabled: +$ getsebool xdm_exec_bootloader +If properly configured, the output should show the following: +xdm_exec_bootloader --> off + Is it the case that xdm_exec_bootloader is not disabled? + + + + +Run the following command to determine if the virt_use_sanlock SELinux boolean is disabled: +$ getsebool virt_use_sanlock +If properly configured, the output should show the following: +virt_use_sanlock --> off + Is it the case that virt_use_sanlock is not disabled? + + + + The runtime status of the net.ipv6.conf.default.autoconf kernel parameter can be queried +by running the following command: +$ sysctl net.ipv6.conf.default.autoconf +0. + + Is it the case that the correct value is not returned? + + + + +Run the following command to determine if the awstats_purge_apache_log_files SELinux boolean is disabled: +$ getsebool awstats_purge_apache_log_files +If properly configured, the output should show the following: +awstats_purge_apache_log_files --> off + Is it the case that awstats_purge_apache_log_files is not disabled? + + + + To check the group ownership of /etc/gshadow-, +run the command: +$ ls -lL /etc/gshadow- +If properly configured, the output should indicate the following group-owner: +root + Is it the case that /etc/gshadow- does not have a group owner of root? + + + + Run the following command to determine if the libreswan package is installed: $ rpm -q libreswan + Is it the case that the package is not installed? + + + + +Run the following command to determine if the pppd_can_insmod SELinux boolean is disabled: +$ getsebool pppd_can_insmod +If properly configured, the output should show the following: +pppd_can_insmod --> off + Is it the case that pppd_can_insmod is not disabled? + + + + Verify that a separate file system/partition has been created for /opt with the following command: + +$ mountpoint /opt + + Is it the case that "/opt is not a mountpoint" is returned? + + + + The runtime status of the net.ipv6.conf.all.accept_ra_defrtr kernel parameter can be queried +by running the following command: +$ sysctl net.ipv6.conf.all.accept_ra_defrtr +0. + + Is it the case that the correct value is not returned? + + + + Verify the system-wide shared library directories are group-owned by "root" with the following command: + +$ sudo find /lib /lib64 /usr/lib /usr/lib64 ! -group root -type d -exec stat -c "%n %G" '{}' \; + +If any system-wide shared library directory is returned and is not group-owned by a required system account, this is a finding. + Is it the case that any system-wide shared library directory is returned and is not group-owned by a required system account? + + + + Verify that GRUB_DISABLE_RECOVERY is set to true in /etc/default/grub to disable recovery boot. +Run the following command: + +$ sudo grep GRUB_DISABLE_RECOVERY /etc/default/grub + Is it the case that GRUB_DISABLE_RECOVERY is not set to true or is missing? + + + + Verify Red Hat Enterprise Linux 8 generates an audit record for unsuccessful attempts to use the unlink system call. + +If the auditd daemon is configured to use the "augenrules" program to to read audit rules during daemon startup (the default), run the following command: + +$ sudo grep -r unlink /etc/audit/rules.d + +If the auditd daemon is configured to use the "auditctl" utility to read audit rules during daemon startup, run the following command: + +$ sudo grep unlink /etc/audit/audit.rules + +The output should be the following: + +-a always,exit -F arch=b32 -S unlink -F exit=-EPERM -F auid>=1000 -F auid!=unset -k unsuccessful-delete +-a always,exit -F arch=b64 -S unlink -F exit=-EPERM -F auid>=1000 -F auid!=unset -k unsuccessful-delete +-a always,exit -F arch=b32 -S unlink -F exit=-EACCES -F auid>=1000 -F auid!=unset -k unsuccessful-delete +-a always,exit -F arch=b64 -S unlink -F exit=-EACCES -F auid>=1000 -F auid!=unset -k unsuccessful-delete + Is it the case that the command does not return a line, or the line is commented out? + + + + To check the ownership of /etc/passwd, +run the command: +$ ls -lL /etc/passwd +If properly configured, the output should indicate the following owner: +root + Is it the case that /etc/passwd does not have an owner of root? + + + + + +Run the following command to determine the current status of the +sshd service: +$ sudo systemctl is-active sshd +If the service is running, it should return the following: active + Is it the case that ? + + + + The runtime status of the net.ipv6.conf.all.router_solicitations kernel parameter can be queried +by running the following command: +$ sysctl net.ipv6.conf.all.router_solicitations +0. + + Is it the case that the correct value is not returned? + + + + To determine if the system is configured to audit calls to the +umount2 system call, run the following command: +$ sudo grep "umount2" /etc/audit/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + Verify Red Hat Enterprise Linux 8 security patches and updates are installed and up to date. +Updates are required to be applied with a frequency determined by organizational policy. + + +Obtain the list of available package security updates from Red Hat. The URL for updates is https://access.redhat.com/errata-search/. +It is important to note that updates provided by Red Hat may not be present on the system if the underlying packages are not installed. + + +Check that the available package security updates have been installed on the system with the following command: + +$ sudo yum history list | more + +Loaded plugins: langpacks, product-id, subscription-manager +ID | Command line | Date and time | Action(s) | Altered +------------------------------------------------------------------------------- +70 | install aide | 2020-03-05 10:58 | Install | 1 +69 | update -y | 2020-03-04 14:34 | Update | 18 EE +68 | install vlc | 2020-02-21 17:12 | Install | 21 +67 | update -y | 2020-02-21 17:04 | Update | 7 EE + + +Typical update frequency may be overridden by Information Assurance Vulnerability Alert (IAVA) notifications from CYBERCOM. + Is it the case that Red Hat Enterprise Linux 8 is in non-compliance with the organizational patching policy? + + + + Verify that Red Hat Enterprise Linux 8 is configured to audit the execution of the "umount" command with the following command: + +$ sudo auditctl -l | grep umount + +-a always,exit -F path=/usr/bin/umount -F perm=x -F auid>=1000 -F auid!=unset -k privileged-umount + Is it the case that the command does not return a line, or the line is commented out? + + + + To ensure that WIFI connections caanot be created, run the following command: +$ gsettings get org.gnome.nm-applet disable-wifi-create +If properly configured, the output should be true. +To ensure that users cannot enable WIFI connection creation, run the following: +$ grep wifi-create /etc/dconf/db/local.d/locks/* +If properly configured, the output should be +/org/gnome/nm-applet/disable-wifi-create + Is it the case that WIFI connections can be created through GNOME? + + + + +Run the following command to determine if the xdm_write_home SELinux boolean is disabled: +$ getsebool xdm_write_home +If properly configured, the output should show the following: +xdm_write_home --> off + Is it the case that xdm_write_home is not disabled? + + + + To determine that AIDE is verifying extended file attributes, run the following command: +$ grep xattrs /etc/aide.conf +Verify that the xattrs option is added to the correct ruleset. + Is it the case that the xattrs option is missing or not added to the correct ruleset? + + + + The file /etc/at.deny should not exist. +This can be checked by running the following + +stat /etc/at.deny + +and the output should be + +stat: cannot stat `/etc/at.deny': No such file or directory + + Is it the case that the file /etc/at.deny exists? + + + + To ensure write permissions are disabled for group and other + for each element in root's path, run the following command: +# ls -ld DIR + Is it the case that group or other write permissions exist? + + + + Run the following command to ensure postfix accepts mail messages from only the local system: +$ grep inet_interfaces /etc/postfix/main.cf +If properly configured, the output should show only . + Is it the case that it does not? + + + + Verify Red Hat Enterprise Linux 8 generates an audit record for unsuccessful attempts to use the renameat system call. + +If the auditd daemon is configured to use the "augenrules" program to to read audit rules during daemon startup (the default), run the following command: + +$ sudo grep -r renameat /etc/audit/rules.d + +If the auditd daemon is configured to use the "auditctl" utility to read audit rules during daemon startup, run the following command: + +$ sudo grep renameat /etc/audit/audit.rules + +The output should be the following: + +-a always,exit -F arch=b32 -S renameat -F exit=-EPERM -F auid>=1000 -F auid!=unset -k unsuccessful-delete +-a always,exit -F arch=b64 -S renameat -F exit=-EPERM -F auid>=1000 -F auid!=unset -k unsuccessful-delete +-a always,exit -F arch=b32 -S renameat -F exit=-EACCES -F auid>=1000 -F auid!=unset -k unsuccessful-delete +-a always,exit -F arch=b64 -S renameat -F exit=-EACCES -F auid>=1000 -F auid!=unset -k unsuccessful-delete + Is it the case that the command does not return a line, or the line is commented out? + + + + +Run the following command to determine if the samba_create_home_dirs SELinux boolean is disabled: +$ getsebool samba_create_home_dirs +If properly configured, the output should show the following: +samba_create_home_dirs --> off + Is it the case that samba_create_home_dirs is not disabled? + + + + +Run the following command to determine if the openvpn_can_network_connect SELinux boolean is disabled: +$ getsebool openvpn_can_network_connect +If properly configured, the output should show the following: +openvpn_can_network_connect --> off + Is it the case that openvpn_can_network_connect is not disabled? + + + + To verify that acquiring, saving, and processing core dumps is disabled, run the +following command: +$ systemctl status systemd-coredump.socket +The output should be similar to: +● systemd-coredump.socket + Loaded: masked (Reason: Unit systemd-coredump.socket is masked.) + Active: inactive (dead) ... + + Is it the case that unit systemd-coredump.socket is not masked or running? + + + + Verify the nosuid option is configured for the /boot/efi mount point, + run the following command: + $ sudo mount | grep '\s/boot/efi\s' + . . . /boot/efi . . . nosuid . . . + + Is it the case that the "/boot/efi" file system does not have the "nosuid" option set? + + + + Verify the nodev option is configured for the /var/tmp mount point, + run the following command: + $ sudo mount | grep '\s/var/tmp\s' + . . . /var/tmp . . . nodev . . . + + Is it the case that the "/var/tmp" file system does not have the "nodev" option set? + + + + +Run the following command to determine if the samba_export_all_ro SELinux boolean is disabled: +$ getsebool samba_export_all_ro +If properly configured, the output should show the following: +samba_export_all_ro --> off + Is it the case that samba_export_all_ro is not disabled? + + + + Run the following command to determine if the rsyslog-gnutls package is installed: $ rpm -q rsyslog-gnutls + Is it the case that the package is not installed? + + + + +Run the following command to determine if the cron_userdomain_transition SELinux boolean is enabled: +$ getsebool cron_userdomain_transition +If properly configured, the output should show the following: +cron_userdomain_transition --> on + Is it the case that cron_userdomain_transition is not enabled? + + + + To determine how the SSH daemon's PermitUserEnvironment option is set, run the following command: + +$ sudo grep -i PermitUserEnvironment /etc/ssh/sshd_config + +If a line indicating no is returned, then the required value is set. + + Is it the case that the required value is not set? + + + + +Run the following command to determine if the samba_export_all_rw SELinux boolean is disabled: +$ getsebool samba_export_all_rw +If properly configured, the output should show the following: +samba_export_all_rw --> off + Is it the case that samba_export_all_rw is not disabled? + + + + Inspect all instances of DocumentRoot and Alias. No +robots.txt file should exist. + Is it the case that it is not? + + + + The runtime status of the net.ipv4.conf.default.accept_source_route kernel parameter can be queried +by running the following command: +$ sysctl net.ipv4.conf.default.accept_source_route +0. + + Is it the case that the correct value is not returned? + + + + Run the following command to determine if the scap-security-guide package is installed: $ rpm -q scap-security-guide + Is it the case that the package is not installed? + + + + +Run the following command to determine if the squid_connect_any SELinux boolean is disabled: +$ getsebool squid_connect_any +If properly configured, the output should show the following: +squid_connect_any --> off + Is it the case that squid_connect_any is not disabled? + + + + +Verify that the shadow password suite configuration is set to encrypt password with a FIPS 140-2 approved cryptographic hashing algorithm. + +Check the hashing algorithm that is being used to hash passwords with the following command: + +$ sudo grep -i ENCRYPT_METHOD /etc/login.defs + +ENCRYPT_METHOD SHA512 + Is it the case that ENCRYPT_METHOD is not set to SHA512? + + + + To ensure that users cannot change session idle and lock settings, run the following: +$ grep 'lock-delay' /etc/dconf/db/local.d/locks/* +If properly configured, the output should return: +/org/gnome/desktop/screensaver/lock-delay + Is it the case that GNOME3 session settings are not locked or configured properly? + + + + + +Run the following command to determine the current status of the +firewalld service: +$ sudo systemctl is-active firewalld +If the service is running, it should return the following: active + Is it the case that the "firewalld" service is disabled, masked, or not started.? + + + + +Run the following command to determine if the ftpd_use_passive_mode SELinux boolean is disabled: +$ getsebool ftpd_use_passive_mode +If properly configured, the output should show the following: +ftpd_use_passive_mode --> off + Is it the case that ftpd_use_passive_mode is not disabled? + + + + +Run the following command to determine if the selinuxuser_ping SELinux boolean is enabled: +$ getsebool selinuxuser_ping +If properly configured, the output should show the following: +selinuxuser_ping --> on + Is it the case that selinuxuser_ping is not enabled? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_UNMAP_KERNEL_AT_EL0 /boot/config.* + + For each kernel installed, a line with value "y" should be returned. + + Is it the case that the kernel was not built with the required value? + + + + If network services are using the xinetd service, this is not applicable. + +To check that the xinetd service is disabled in system boot configuration, +run the following command: +$ sudo systemctl is-enabled xinetd +Output should indicate the xinetd service has either not been installed, +or has been disabled at all runlevels, as shown in the example below: +$ sudo systemctl is-enabled xinetd disabled + +Run the following command to verify xinetd is not active (i.e. not running) through current runtime configuration: +$ sudo systemctl is-active xinetd + +If the service is not running the command will return the following output: +inactive + +The service will also be masked, to check that the xinetd is masked, run the following command: +$ sudo systemctl show xinetd | grep "LoadState\|UnitFileState" + +If the service is masked the command will return the following outputs: + +LoadState=masked + +UnitFileState=masked + Is it the case that the "xinetd" is loaded and not masked? + + + + To determine if passwd_timeout has been configured for sudo, run the following command: +$ sudo grep -ri '^Defaults.*passwd_timeout=' /etc/sudoers /etc/sudoers.d/ +The command should return a matching output. + Is it the case that passwd_timeout is not set with the appropriate value for sudo? + + + + To check which SSH protocol version is allowed, check version of openssh-server with following command: + +$ rpm -qi openssh-server | grep Version + +Versions equal to or higher than 7.4 only allow Protocol 2. +If version is lower than 7.4, run the following command to check configuration: +$ sudo grep Protocol /etc/ssh/sshd_config +If configured properly, output should be Protocol 2 + Is it the case that it is commented out or is not set correctly to Protocol 2? + + + + Run the following command to determine if the telnet-server package is installed: +$ rpm -q telnet-server + Is it the case that the package is installed? + + + + Verify the noexec option is configured for the /var/log mount point, + run the following command: + $ sudo mount | grep '\s/var/log\s' + . . . /var/log . . . noexec . . . + + Is it the case that the "/var/log" file system does not have the "noexec" option set? + + + + +Run the following command to get the current configured value for deny_execmem +SELinux boolean: +$ getsebool deny_execmem +The expected cofiguration is . +"on" means true, and "off" means false + Is it the case that deny_execmem is not set as expected? + + + + To check that the vsftpd service is disabled in system boot configuration, +run the following command: +$ sudo systemctl is-enabled vsftpd +Output should indicate the vsftpd service has either not been installed, +or has been disabled at all runlevels, as shown in the example below: +$ sudo systemctl is-enabled vsftpd disabled + +Run the following command to verify vsftpd is not active (i.e. not running) through current runtime configuration: +$ sudo systemctl is-active vsftpd + +If the service is not running the command will return the following output: +inactive + +The service will also be masked, to check that the vsftpd is masked, run the following command: +$ sudo systemctl show vsftpd | grep "LoadState\|UnitFileState" + +If the service is masked the command will return the following outputs: + +LoadState=masked + +UnitFileState=masked + Is it the case that the "vsftpd" is loaded and not masked? + + + + Verify the FAIL_DELAY setting is configured correctly in the /etc/login.defs file by +running the following command: +$ sudo grep -i "FAIL_DELAY" /etc/login.defs +All output must show the value of FAIL_DELAY set as shown in the below: +$ sudo grep -i "FAIL_DELAY" /etc/login.defs +FAIL_DELAY + Is it the case that the above command returns no output, or FAIL_DELAY is configured less than the expected value? + + + + To check the group ownership of /etc/shadow-, +run the command: +$ ls -lL /etc/shadow- +If properly configured, the output should indicate the following group-owner: +root + Is it the case that /etc/shadow- does not have a group owner of root? + + + + To check that the squid service is disabled in system boot configuration, +run the following command: +$ sudo systemctl is-enabled squid +Output should indicate the squid service has either not been installed, +or has been disabled at all runlevels, as shown in the example below: +$ sudo systemctl is-enabled squid disabled + +Run the following command to verify squid is not active (i.e. not running) through current runtime configuration: +$ sudo systemctl is-active squid + +If the service is not running the command will return the following output: +inactive + +The service will also be masked, to check that the squid is masked, run the following command: +$ sudo systemctl show squid | grep "LoadState\|UnitFileState" + +If the service is masked the command will return the following outputs: + +LoadState=masked + +UnitFileState=masked + Is it the case that the "squid" is loaded and not masked? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_COMPAT_VDSO /boot/config.* + + Configs with value 'n' are not explicitly set in the file, so either commented lines or no + lines should be returned. + + Is it the case that the kernel was not built with the required value? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_SECURITY /boot/config.* + + For each kernel installed, a line with value "y" should be returned. + + Is it the case that the kernel was not built with the required value? + + + + To check the ownership of /etc/motd, +run the command: +$ ls -lL /etc/motd +If properly configured, the output should indicate the following owner: +root + Is it the case that /etc/motd does not have an owner of root? + + + + To determine how the SSH daemon's Banner option is set, run the following command: + +$ sudo grep -i Banner /etc/ssh/sshd_config + +If a line indicating /etc/issue.net is returned, then the required value is set. + + Is it the case that the required value is not set? + + + + To verify the audispd plugin encrypts audit records off-loaded onto a different +system or media from the system being audited, run the following command: + +$ sudo grep -i transport /etc/audit/audisp-remote.conf +The output should return the following: +transport = KRB5 + Is it the case that audispd is not encrypting audit records when sent over the network? + + + + The runtime status of the net.ipv6.conf.all.autoconf kernel parameter can be queried +by running the following command: +$ sysctl net.ipv6.conf.all.autoconf +0. + + Is it the case that the correct value is not returned? + + + + Verify that a separate file system/partition has been created for /srv with the following command: + +$ mountpoint /srv + + Is it the case that "/srv is not a mountpoint" is returned? + + + + If IPv6 is disabled, this is not applicable. + +Inspect the file /etc/sysconfig/ip6tables to determine +the default policy for the INPUT chain. It should be set to DROP: +$ sudo grep ":INPUT" /etc/sysconfig/ip6tables + Is it the case that the default policy for the INPUT chain is not set to DROP? + + + + To check the ownership of /etc/gshadow, +run the command: +$ ls -lL /etc/gshadow +If properly configured, the output should indicate the following owner: +root + Is it the case that /etc/gshadow does not have an owner of root? + + + + To verify that the installed operating system is supported, run +the following command: + +$ grep -i "red hat" /etc/redhat-release + +Red Hat Enterprise Linux 8 + Is it the case that the installed operating system is not supported? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_X86_VSYSCALL_EMULATION /boot/config.* + + Configs with value 'n' are not explicitly set in the file, so either commented lines or no + lines should be returned. + + Is it the case that the kernel was not built with the required value? + + + + To verify that SSSD's in-memory cache expires after a day, run the following command: +$ sudo grep memcache_timeout /etc/sssd/sssd.conf +If configured properly, output should be memcache_timeout = . + Is it the case that it does not exist or is not configured properly? + + + + The runtime status of the net.ipv4.conf.all.drop_gratuitous_arp kernel parameter can be queried +by running the following command: +$ sysctl net.ipv4.conf.all.drop_gratuitous_arp +1. + + Is it the case that the correct value is not returned? + + + + Verify that DNS servers have been configured properly, perform the following: +$ sudo grep nameserver /etc/resolv.conf + Is it the case that less than two lines are returned that are not commented out? + + + + To determine how the SSH daemon's Banner option is set, run the following command: + +$ sudo grep -i Banner /etc/ssh/sshd_config + +If a line indicating /etc/issue is returned, then the required value is set. + + Is it the case that the required value is not set? + + + + +Run the following command to determine if the kdumpgui_run_bootloader SELinux boolean is disabled: +$ getsebool kdumpgui_run_bootloader +If properly configured, the output should show the following: +kdumpgui_run_bootloader --> off + Is it the case that kdumpgui_run_bootloader is not disabled? + + + + +Run the following command to determine if the dhcpc_exec_iptables SELinux boolean is disabled: +$ getsebool dhcpc_exec_iptables +If properly configured, the output should show the following: +dhcpc_exec_iptables --> off + Is it the case that dhcpc_exec_iptables is not disabled? + + + + To verify that the operating system protects against or limits the effects of DoS +attacks by ensuring implementation of rate-limiting measures +on impacted network interfaces, run the following command: +# grep 'net.ipv4.tcp_invalid_ratelimit' /etc/sysctl.conf /etc/sysctl.d/* +The command should output the following line: +/etc/sysctl.conf:net.ipv4.tcp_invalid_ratelimit = +The file where the line has been found can differ, but it must be either /etc/sysctl.conf +or a file located under the /etc/sysctl.d/ directory. + Is it the case that rate limiting of duplicate TCP acknowledgments is not configured? + + + + +Run the following command to determine if the git_cgi_use_nfs SELinux boolean is disabled: +$ getsebool git_cgi_use_nfs +If properly configured, the output should show the following: +git_cgi_use_nfs --> off + Is it the case that git_cgi_use_nfs is not disabled? + + + + Inspect the form of default GRUB 2 command line for the Linux operating system +in /etc/default/grub. If it includes pti=on, +then the parameter will be configured for newly installed kernels. +First check if the GRUB recovery is enabled: +$ sudo grep 'GRUB_DISABLE_RECOVERY' /etc/default/grub +If this option is set to true, then check that a line is output by the following command: +$ sudo grep 'GRUB_CMDLINE_LINUX_DEFAULT.*pti=on.*' /etc/default/grub +If the recovery is disabled, check the line with +$ sudo grep 'GRUB_CMDLINE_LINUX.*pti=on.*' /etc/default/grub.Moreover, command line parameters for currently installed kernels should be checked as well. +Run the following command: +$ sudo grubby --info=ALL | grep args | grep -v 'pti=on' +The command should not return any output. + Is it the case that Kernel page-table isolation is not enabled? + + + + To ensure the user list is disabled, run the following command: +$ grep disable-user-list /etc/dconf/db/gdm.d/* +The output should be true. +To ensure that users cannot enable displaying the user list, run the following: +$ grep disable-user-list /etc/dconf/db/gdm.d/locks/* +If properly configured, the output should be /org/gnome/login-screen/disable-user-list + Is it the case that disable-user-list has not been configured or is not disabled? + + + + To check for virtual console entries which permit root login, run the +following command: +$ sudo grep ^vc/[0-9] /etc/securetty +If any output is returned, then root logins over virtual console devices is permitted. + Is it the case that root login over virtual console devices is permitted? + + + + To determine if the system is configured to audit calls to the +unlinkat system call, run the following command: +$ sudo grep "unlinkat" /etc/audit/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_PAGE_POISONING /boot/config.* + + For each kernel installed, a line with value "y" should be returned. + + Is it the case that the kernel was not built with the required value? + + + + Make sure that the kernel is not disabling SMAP with the following +commands. +grep -q nosmap /boot/config-`uname -r` +If the command returns a line, it means that SMAP is being disabled. + Is it the case that the kernel is configured to disable SMAP? + + + + The runtime status of the net.ipv6.conf.all.accept_source_route kernel parameter can be queried +by running the following command: +$ sysctl net.ipv6.conf.all.accept_source_route +0. + + Is it the case that the correct value is not returned? + + + + +To check that the rlogin service is disabled in system boot configuration with xinetd, run the following command: +$ chkconfig rlogin --list +Output should indicate the rlogin service has either not been installed, or has been disabled, as shown in the example below: +$ chkconfig rlogin --list + +Note: This output shows SysV services only and does not include native +systemd services. SysV configuration data might be overridden by native +systemd configuration. + +If you want to list systemd services use 'systemctl list-unit-files'. +To see services enabled on particular target use +'systemctl list-dependencies [target]'. + +rlogin off + +To check that the rlogin socket is disabled in system boot configuration with systemd, run the following command: +$ systemctl is-enabled rlogin +Output should indicate the rlogin socket has either not been installed, +or has been disabled at all runlevels, as shown in the example below: +$ sudo systemctl is-enabled rlogindisabled + +Run the following command to verify rlogin is not active (i.e. not running) through current runtime configuration: +$ sudo systemctl is-active rlogin + +If the socket is not running the command will return the following output: +inactive + +The socket will also be masked, to check that the rlogin is masked, run the following command: +$ sudo systemctl show rlogin | grep "LoadState\|UnitFileState" + +If the socket is masked the command will return the following outputs: + +LoadState=masked + +UnitFileState=masked + Is it the case that service and/or socket are running? + + + + Verify Red Hat Enterprise Linux 8 audits the execution of privileged functions. + +Check if Red Hat Enterprise Linux 8 is configured to audit the execution of the "execve" system call using the following command: + +$ sudo grep execve /etc/audit/audit.rules + +The output should be the following: + + +-a always,exit -F arch=b32 -S execve -C uid!=euid -F euid=0 -k setuid +-a always,exit -F arch=b64 -S execve -C uid!=euid -F euid=0 -k setuid +-a always,exit -F arch=b32 -S execve -C gid!=egid -F egid=0 -k setgid +-a always,exit -F arch=b64 -S execve -C gid!=egid -F egid=0 -k setgid + Is it the case that the command does not return all lines, or the lines are commented out? + + + + The runtime status of the net.ipv6.conf.default.router_solicitations kernel parameter can be queried +by running the following command: +$ sysctl net.ipv6.conf.default.router_solicitations +0. + + Is it the case that the correct value is not returned? + + + + +Run the following command to determine if the httpd_graceful_shutdown SELinux boolean is enabled: +$ getsebool httpd_graceful_shutdown +If properly configured, the output should show the following: +httpd_graceful_shutdown --> on + Is it the case that httpd_graceful_shutdown is not enabled? + + + + To verify that the Audit is correctly configured according to recommended rules, check the content of the file with the following command: +cat /etc/audit/rules.d/30-ospp-v42-5-perm-change-failed.rules +The output has to be exactly as follows: +## Unsuccessful permission change +-a always,exit -F arch=b32 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-perm-change +-a always,exit -F arch=b64 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-perm-change +-a always,exit -F arch=b32 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-perm-change +-a always,exit -F arch=b64 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-perm-change + Is it the case that the file does not exist or the content differs? + + + + To verify that the Audit is correctly configured according to recommended rules, check the content of the file with the following command: +cat /etc/audit/rules.d/30-ospp-v42-5-perm-change-success.rules +The output has to be exactly as follows: +## Successful permission change +-a always,exit -F arch=b32 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change +-a always,exit -F arch=b64 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + Is it the case that the file does not exist or the content differs? + + + + To verify Certmap is enabled in SSSD, run the following command: +$ sudo cat /etc/sssd/sssd.conf +If configured properly, output should contain section like the following + +[certmap/testing.test/rule_name] +matchrule =<SAN>.*EDIPI@mil +maprule = (userCertificate;binary={cert!bin}) +domains = testing.test + + Is it the case that Certmap is not configured in SSSD? + + + + To determine whether the SSH service is configured to use strong entropy seed, +run $ sudo grep SSH_USE_STRONG_RNG /etc/sysconfig/sshd +If a line indicating that SSH_USE_STRONG_RNG is set to 32 is returned, +then the option is set correctly. + Is it the case that the SSH_USE_STRONG_RNG is not set to 32 in /etc/sysconfig/sshd? + + + + Run the following command to determine if the ypserv package is installed: +$ rpm -q ypserv + Is it the case that the package is installed? + + + + To determine if the users are allowed to run commands as root, run the following commands: +$ sudo grep -PR '^\s*((?!root\b)[\w]+)\s*(\w+)\s*=\s*(.*,)?\s*[^\(\s]' /etc/sudoers /etc/sudoers.d/ +and +$ sudo grep -PR '^\s*((?!root\b)[\w]+)\s*(\w+)\s*=\s*(.*,)?\s*\([\w\s]*\b(root|ALL)\b[\w\s]*\)' /etc/sudoers /etc/sudoers.d/ +Both commands should return no output. + Is it the case that /etc/sudoers file contains rules that allow non-root users to run commands as root? + + + + To ensure the GUI does not allow user administratrion capabilities to all users, +run the following command: +$ gsettings get org.gnome.desktop.lockdown user-administration-disabled +If properly configured, the output should be true. +To ensure that users cannot enable user administration, run the following: +$ grep user-administration /etc/dconf/db/local.d/locks/* +If properly configured, the output should be +/org/gnome/desktop/lockdown/user-administration-disabled + Is it the case that user administration is not configured or disabled? + + + + Verify the noexec option is configured for the /boot mount point, + run the following command: + $ sudo mount | grep '\s/boot\s' + . . . /boot . . . noexec . . . + + Is it the case that the "/boot" file system does not have the "noexec" option set? + + + + +Run the following command to determine if the ksmtuned_use_cifs SELinux boolean is disabled: +$ getsebool ksmtuned_use_cifs +If properly configured, the output should show the following: +ksmtuned_use_cifs --> off + Is it the case that ksmtuned_use_cifs is not disabled? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_SLAB_FRELIST_RANDOM /boot/config.* + + For each kernel installed, a line with value "y" should be returned. + + Is it the case that the kernel was not built with the required value? + + + + To check for legacy lines in /etc/group, run the following command: + grep '^\+' /etc/group +The command should not return any output. + Is it the case that the file contains legacy lines? + + + + Verify the SELINUX on Red Hat Enterprise Linux 8 is using the policy with the following command: + +$ sestatus | grep policy + +Loaded policy name: + Is it the case that the loaded policy name is not "<sub idref="var_selinux_policy_name" />"? + + + + +Run the following command to determine if the container_connect_any SELinux boolean is disabled: +$ getsebool container_connect_any +If properly configured, the output should show the following: +container_connect_any --> off + Is it the case that container_connect_any is not disabled? + + + + +Run the following command to determine if the xguest_mount_media SELinux boolean is disabled: +$ getsebool xguest_mount_media +If properly configured, the output should show the following: +xguest_mount_media --> off + Is it the case that xguest_mount_media is not disabled? + + + + +Run the following command to determine if the httpd_can_network_connect_cobbler SELinux boolean is disabled: +$ getsebool httpd_can_network_connect_cobbler +If properly configured, the output should show the following: +httpd_can_network_connect_cobbler --> off + Is it the case that httpd_can_network_connect_cobbler is not disabled? + + + + To check the ownership of /etc/group-, +run the command: +$ ls -lL /etc/group- +If properly configured, the output should indicate the following owner: +root + Is it the case that /etc/group- does not have an owner of root? + + + + To determine if the system is configured to audit calls to the +fremovexattr system call, run the following command: +$ sudo grep "fremovexattr" /etc/audit/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + Inspect the form of default GRUB 2 command line for the Linux operating system +in /etc/default/grub. If it includes audit=1, +then the parameter will be configured for newly installed kernels. +First check if the GRUB recovery is enabled: +$ sudo grep 'GRUB_DISABLE_RECOVERY' /etc/default/grub +If this option is set to true, then check that a line is output by the following command: +$ sudo grep 'GRUB_CMDLINE_LINUX_DEFAULT.*audit=1.*' /etc/default/grub +If the recovery is disabled, check the line with +$ sudo grep 'GRUB_CMDLINE_LINUX.*audit=1.*' /etc/default/grub.Moreover, command line parameters for currently installed kernels should be checked as well. +Run the following command: +$ sudo grubby --info=ALL | grep args | grep -v 'audit=1' +The command should not return any output. + Is it the case that auditing is not enabled at boot time? + + + + +Run the following command to determine if the daemons_enable_cluster_mode SELinux boolean is disabled: +$ getsebool daemons_enable_cluster_mode +If properly configured, the output should show the following: +daemons_enable_cluster_mode --> off + Is it the case that daemons_enable_cluster_mode is not disabled? + + + + To check that the slapd service is disabled in system boot configuration, +run the following command: +$ sudo systemctl is-enabled slapd +Output should indicate the slapd service has either not been installed, +or has been disabled at all runlevels, as shown in the example below: +$ sudo systemctl is-enabled slapd disabled + +Run the following command to verify slapd is not active (i.e. not running) through current runtime configuration: +$ sudo systemctl is-active slapd + +If the service is not running the command will return the following output: +inactive + +The service will also be masked, to check that the slapd is masked, run the following command: +$ sudo systemctl show slapd | grep "LoadState\|UnitFileState" + +If the service is masked the command will return the following outputs: + +LoadState=masked + +UnitFileState=masked + Is it the case that the "slapd" is loaded and not masked? + + + + Run the following command to determine if the firewalld package is installed: $ rpm -q firewalld + Is it the case that the package is not installed? + + + + +Run the following command to determine if the httpd_sys_script_anon_write SELinux boolean is disabled: +$ getsebool httpd_sys_script_anon_write +If properly configured, the output should show the following: +httpd_sys_script_anon_write --> off + Is it the case that httpd_sys_script_anon_write is not disabled? + + + + To determine if the system is configured to audit successful calls +to the open_by_handle_at system call, run the following command: +$ sudo grep "open_by_handle_at" /etc/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + +Run the following command to determine if the tftp_home_dir SELinux boolean is disabled: +$ getsebool tftp_home_dir +If properly configured, the output should show the following: +tftp_home_dir --> off + Is it the case that tftp_home_dir is not disabled? + + + + To check that the oddjobd service is disabled in system boot configuration, +run the following command: +$ sudo systemctl is-enabled oddjobd +Output should indicate the oddjobd service has either not been installed, +or has been disabled at all runlevels, as shown in the example below: +$ sudo systemctl is-enabled oddjobd disabled + +Run the following command to verify oddjobd is not active (i.e. not running) through current runtime configuration: +$ sudo systemctl is-active oddjobd + +If the service is not running the command will return the following output: +inactive + +The service will also be masked, to check that the oddjobd is masked, run the following command: +$ sudo systemctl show oddjobd | grep "LoadState\|UnitFileState" + +If the service is masked the command will return the following outputs: + +LoadState=masked + +UnitFileState=masked + Is it the case that the "oddjobd" is loaded and not masked? + + + + To verify the operating system prevents non-privileged users from executing +privileged functions to include disabling, circumventing, or altering +implemented security safeguards/countermeasures, run the following +command: +$ sudo semanage login -l +All administrators must be mapped to the sysadm_u or staff_u +users with the appropriate domains (sysadm_t and staff_t). + +All authorized non-administrative +users must be mapped to the user_u role or the appropriate domain +(user_t). + Is it the case that non-admin users are not confined correctly? + + + + +Run the following command to determine if the irssi_use_full_network SELinux boolean is disabled: +$ getsebool irssi_use_full_network +If properly configured, the output should show the following: +irssi_use_full_network --> off + Is it the case that irssi_use_full_network is not disabled? + + + + To check the permissions of /boot/grub2/grub.cfg, run the command: +$ sudo ls -lL /boot/grub2/grub.cfg +If properly configured, the output should indicate the following +permissions: -rw------- + Is it the case that it does not? + + + + To determine if the system is configured to audit calls to the +rmdir system call, run the following command: +$ sudo grep "rmdir" /etc/audit/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + The runtime status of the kernel.panic_on_oops kernel parameter can be queried +by running the following command: +$ sysctl kernel.panic_on_oops +1. + + Is it the case that the correct value is not returned? + + + + To verify the system is not configured to use a boot loader on removable media, +run the following command: +$ sudo grep "set root='hd0" /boot/grub2/grub.cfg +The output should return something similar to: +set root='hd0,msdos1' +usb0, cd, fd0, etc. are some examples of removeable +media which should not exist in the line: +set root='hd0,msdos1' + Is it the case that it is not? + + + + To verify that the Audit is correctly configured according to recommended rules, check the content of the file with the following command: +$ sudo cat /etc/audit/rules.d/11-loginuid.rules +The output has to be exactly as follows: +## Make the loginuid immutable. This prevents tampering with the auid. +--loginuid-immutable + Is it the case that the file does not exist or the content differs? + + + + +Run the following command to determine if the xguest_connect_network SELinux boolean is disabled: +$ getsebool xguest_connect_network +If properly configured, the output should show the following: +xguest_connect_network --> off + Is it the case that xguest_connect_network is not disabled? + + + + +Run the following command to determine if the staff_use_svirt SELinux boolean is disabled: +$ getsebool staff_use_svirt +If properly configured, the output should show the following: +staff_use_svirt --> off + Is it the case that staff_use_svirt is not disabled? + + + + The runtime status of the net.ipv6.conf.all.accept_ra kernel parameter can be queried +by running the following command: +$ sysctl net.ipv6.conf.all.accept_ra +0. + + Is it the case that the correct value is not returned? + + + + Run the following command to determine if the httpd package is installed: +$ rpm -q httpd + Is it the case that the package is installed? + + + + To verify the operating system implements cryptography to protect the integrity of +remote ldap access sessions, run the following command: +$ sudo grep ldap_tls_cacertdir /etc/sssd/sssd.conf +The output should return the following with a correctly configured CA cert path: +ldap_tls_cacertdir /path/to/tls/cacert + Is it the case that the TLS CA cert is not configured? + + + + To determine how the SSH daemon's PubkeyAuthentication option is set, run the following command: + +$ sudo grep -i PubkeyAuthentication /etc/ssh/sshd_config + +If a line indicating yes is returned, then the required value is set. + + Is it the case that the required value is not set? + + + + +Run the following command to determine if the prosody_bind_http_port SELinux boolean is disabled: +$ getsebool prosody_bind_http_port +If properly configured, the output should show the following: +prosody_bind_http_port --> off + Is it the case that prosody_bind_http_port is not disabled? + + + + Run the following command to determine if the pigz package is installed: +$ rpm -q pigz + Is it the case that the package is installed? + + + + Verify the system-wide shared library files contained in the following directories have mode "755" or less permissive with the following command: + +$ sudo find -L /lib /lib64 /usr/lib /usr/lib64 -perm /022 -type f -exec ls -l {} \; + Is it the case that any system-wide shared library file is found to be group-writable or world-writable? + + + + Run the following command to determine if the openscap-scanner package is installed: $ rpm -q openscap-scanner + Is it the case that the package is not installed? + + + + To check that the dhcpd service is disabled in system boot configuration, +run the following command: +$ sudo systemctl is-enabled dhcpd +Output should indicate the dhcpd service has either not been installed, +or has been disabled at all runlevels, as shown in the example below: +$ sudo systemctl is-enabled dhcpd disabled + +Run the following command to verify dhcpd is not active (i.e. not running) through current runtime configuration: +$ sudo systemctl is-active dhcpd + +If the service is not running the command will return the following output: +inactive + +The service will also be masked, to check that the dhcpd is masked, run the following command: +$ sudo systemctl show dhcpd | grep "LoadState\|UnitFileState" + +If the service is masked the command will return the following outputs: + +LoadState=masked + +UnitFileState=masked + Is it the case that the "dhcpd" is loaded and not masked? + + + + +Run the following command to determine if the httpd_read_user_content SELinux boolean is disabled: +$ getsebool httpd_read_user_content +If properly configured, the output should show the following: +httpd_read_user_content --> off + Is it the case that httpd_read_user_content is not disabled? + + + + To ensure only SNMPv3 or newer is used, run the following command: +$ sudo grep 'rocommunity\|rwcommunity\|com2sec' /etc/snmp/snmpd.conf | grep -v "^#" +There should be no output. + Is it the case that there is output? + + + + +Run the following command to determine if the varnishd_connect_any SELinux boolean is disabled: +$ getsebool varnishd_connect_any +If properly configured, the output should show the following: +varnishd_connect_any --> off + Is it the case that varnishd_connect_any is not disabled? + + + + Verify the pam_faillock.so module is present in the "/etc/pam.d/password-auth" file: + +$ sudo grep pam_faillock.so /etc/pam.d/password-auth + +auth required pam_faillock.so preauth +auth required pam_faillock.so authfail +account required pam_faillock.so + Is it the case that the pam_faillock.so module is not present in the "/etc/pam.d/password-auth" file with the "preauth" line listed before pam_unix.so? + + + + Verify Red Hat Enterprise Linux 8 generates audit records for all account creations, modifications, disabling, and termination events that affect "/etc/security/opasswd" with the following command: + +$ sudo auditctl -l | egrep '(/etc/security/opasswd)' + +-w /etc/security/opasswd -p wa -k identity + Is it the case that the command does not return a line, or the line is commented out? + + + + If the system is not using TLS, set the ldap_id_use_start_tls option +in /etc/sssd/sssd.conf to true. + Is it the case that the 'ldap_id_use_start_tls' option is not set to 'true'? + + + + To check the minimum password age, run the command: +$ grep PASS_MIN_DAYS /etc/login.defs + Is it the case that it is not equal to or greater than the required value? + + + + +Run the following command to determine if the zabbix_can_network SELinux boolean is disabled: +$ getsebool zabbix_can_network +If properly configured, the output should show the following: +zabbix_can_network --> off + Is it the case that zabbix_can_network is not disabled? + + + + To check the group ownership of /etc/shadow, +run the command: +$ ls -lL /etc/shadow +If properly configured, the output should indicate the following group-owner: +root + Is it the case that /etc/shadow does not have a group owner of root? + + + + The runtime status of the net.ipv4.icmp_ignore_bogus_error_responses kernel parameter can be queried +by running the following command: +$ sysctl net.ipv4.icmp_ignore_bogus_error_responses +1. + + Is it the case that the correct value is not returned? + + + + To check how many characters are required in a password, run the following command: +$ grep minlen /etc/security/pwquality.conf +Your output should contain minlen = + Is it the case that minlen is not found, or not equal to or greater than the required value? + + + + +Run the following command to determine if the virt_sandbox_use_all_caps SELinux boolean is disabled: +$ getsebool virt_sandbox_use_all_caps +If properly configured, the output should show the following: +virt_sandbox_use_all_caps --> off + Is it the case that virt_sandbox_use_all_caps is not disabled? + + + + To determine how the SSH daemon's LogLevel option is set, run the following command: + +$ sudo grep -i LogLevel /etc/ssh/sshd_config + +If a line indicating INFO is returned, then the required value is set. + + Is it the case that the required value is not set? + + + + Verify the nosuid option is configured for the /var/log/audit mount point, + run the following command: + $ sudo mount | grep '\s/var/log/audit\s' + . . . /var/log/audit . . . nosuid . . . + + Is it the case that the "/var/log/audit" file system does not have the "nosuid" option set? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_PANIC_TIMEOUT /boot/config.* + + For each kernel installed, a line with value "" should be returned. + + Is it the case that the kernel was not built with the required value? + + + + Run the following command to determine if the setroubleshoot-server package is installed: +$ rpm -q setroubleshoot-server + Is it the case that the package is installed? + + + + To verify if the OpenSSH server uses defined Crypto Policy, run: +$ grep 'CRYPTO_POLICY' /etc/crypto-policies/back-ends/opensshserver.config | tail -n 1 +and verify that the line matches +CRYPTO_POLICY='-oCiphers=aes256-ctr,aes128-ctr,aes256-cbc,aes128-cbc -oMACs=hmac-sha2-512,hmac-sha2-256 -oGSSAPIKeyExchange=no -oKexAlgorithms=ecdh-sha2-nistp521,ecdh-sha2-nistp384,ecdh-sha2-nistp256,diffie-hellman-group14-sha1 -oHostKeyAlgorithms=ssh-rsa,ecdsa-sha2-nistp384,ecdsa-sha2-nistp256 -oPubkeyAcceptedKeyTypes=rsa-sha2-512,rsa-sha2-256,ssh-rsa,ecdsa-sha2-nistp384,ecdsa-sha2-nistp256' + Is it the case that Crypto Policy for OpenSSH Server is not configured according to CC requirements? + + + + Verify the noexec option is configured for the /var/tmp mount point, + run the following command: + $ sudo mount | grep '\s/var/tmp\s' + . . . /var/tmp . . . noexec . . . + + Is it the case that the "/var/tmp" file system does not have the "noexec" option set? + + + + Run the following command to determine if the opensc package is installed: $ rpm -q opensc + Is it the case that the package is not installed? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_DEVKMEM /boot/config.* + + Configs with value 'n' are not explicitly set in the file, so either commented lines or no + lines should be returned. + + Is it the case that the kernel was not built with the required value? + + + + Verify the umask setting is configured correctly in the /etc/csh.cshrc file by +running the following command: +$ sudo grep "umask" /etc/csh.cshrc +All output must show the value of umask set as shown in the below: +umask + Is it the case that the above command returns no output, or the umask is configured incorrectly? + + + + Verify that Red Hat Enterprise Linux 8 is configured to audit the execution of the "chsh" command with the following command: + +$ sudo auditctl -l | grep chsh + +-a always,exit -F path=/usr/bin/chsh -F perm=x -F auid>=1000 -F auid!=unset -k privileged-chsh + Is it the case that the command does not return a line, or the line is commented out? + + + + +Run the following command to determine if the xend_run_qemu SELinux boolean is enabled: +$ getsebool xend_run_qemu +If properly configured, the output should show the following: +xend_run_qemu --> on + Is it the case that xend_run_qemu is not enabled? + + + + To check the permissions of /boot/Sysem.map-*, +run the command: +$ ls -l /boot/Sysem.map-* +If properly configured, the output should indicate the following permissions: +-rw------- + Is it the case that ? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_DEFAULT_MMAP_MIN_ADDR /boot/config.* + + For each kernel installed, a line with value "65536" should be returned. + + Is it the case that the kernel was not built with the required value? + + + + Verify that Red Hat Enterprise Linux 8 is configured to audit the execution of the "chcon" command with the following command: + +$ sudo auditctl -l | grep chcon + +-a always,exit -F path=/usr/bin/chcon -F perm=x -F auid>=1000 -F auid!=unset -k perm_mod + Is it the case that the command does not return a line, or the line is commented out? + + + + To ensure the tally directory is configured correctly, run the following command: +$ grep 'dir =' /etc/security/faillock.conf +The output should show that dir is set to something different to "/var/run/faillock" + Is it the case that dir is not set or is set to /var/run/faillock? + + + + To check the permissions of /etc/ssh/sshd_config, +run the command: +$ ls -l /etc/ssh/sshd_config +If properly configured, the output should indicate the following permissions: +-rw------- + Is it the case that /etc/ssh/sshd_config does not have unix mode -rw-------? + + + + +Run the following command to determine if the virt_use_rawip SELinux boolean is disabled: +$ getsebool virt_use_rawip +If properly configured, the output should show the following: +virt_use_rawip --> off + Is it the case that virt_use_rawip is not disabled? + + + + To find SGID files, run the following command: +$ sudo find / -xdev -type f -perm -2000 + Is it the case that there is output? + + + + To verify all accounts have unique names, run the following command: +$ sudo getent passwd | awk -F: '{ print $1}' | uniq -d +No output should be returned. + Is it the case that a line is returned? + + + + Inspect the form of default GRUB 2 command line for the Linux operating system +in /etc/default/grub. If it includes rng_core.default_quality=, +then the parameter will be configured for newly installed kernels. +First check if the GRUB recovery is enabled: +$ sudo grep 'GRUB_DISABLE_RECOVERY' /etc/default/grub +If this option is set to true, then check that a line is output by the following command: +$ sudo grep 'GRUB_CMDLINE_LINUX_DEFAULT.*rng_core.default_quality=.*' /etc/default/grub +If the recovery is disabled, check the line with +$ sudo grep 'GRUB_CMDLINE_LINUX.*rng_core.default_quality=.*' /etc/default/grub.Moreover, command line parameters for currently installed kernels should be checked as well. +Run the following command: +$ sudo grubby --info=ALL | grep args | grep -v 'rng_core.default_quality=' +The command should not return any output. + Is it the case that trust on hardware random number generator is not configured appropriately? + + + + To ensure all GIDs referenced in /etc/passwd are defined in /etc/group, +run the following command: +$ sudo pwck -qr +There should be no output. + Is it the case that GIDs referenced in /etc/passwd are returned as not defined in /etc/group? + + + + The file permissions for all log files written by rsyslog should +be set to 600, or more restrictive. These log files are determined by the +second part of each Rule line in /etc/rsyslog.conf and typically +all appear in /var/log. To see the permissions of a given log +file, run the following command: +$ ls -l LOGFILE +The permissions should be 600, or more restrictive. + Is it the case that the permissions are not correct? + + + + To determine if the system is configured to audit calls to the +fchmodat system call, run the following command: +$ sudo grep "fchmodat" /etc/audit/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + To determine if the system is configured to audit calls to the +init_module system call, run the following command: +$ sudo grep "init_module" /etc/audit/audit.* +If the system is configured to audit this activity, it will return a line. +To determine if the system is configured to audit calls to the +delete_module system call, run the following command: +$ sudo grep "delete_module" /etc/audit/audit.* +If the system is configured to audit this activity, it will return a line. + Is it the case that no line is returned? + + + + To verify that remote access methods are logging to rsyslog, +run the following command: +grep -rE '(auth.\*|authpriv.\*|daemon.\*)' /etc/rsyslog.* +The output should contain auth.*, authpriv.*, and daemon.* +pointing to a log file. + Is it the case that remote access methods are not logging to rsyslog? + + + + To check the group ownership of /usr/bin/sudo, +run the command: +$ ls -lL /usr/bin/sudo +If properly configured, the output should indicate the following group-owner: + + Is it the case that /usr/bin/sudo does not have a group owner of <sub idref="var_sudo_dedicated_group" />? + + + + To check the ownership of /etc/crontab, +run the command: +$ ls -lL /etc/crontab +If properly configured, the output should indicate the following owner: +root + Is it the case that /etc/crontab does not have an owner of root? + + + + To verify that rsyslog's Forwarding Output Module is configured +to use TLS for logging to remote server, run the following command: +$ grep omfwd /etc/rsyslog.conf /etc/rsyslog.d/*.conf +The output should include record similar to +action(type="omfwd" protocol="tcp" Target="<remote system>" port="6514" + StreamDriver="gtls" StreamDriverMode="1" StreamDriverAuthMode="x509/name" streamdriver.CheckExtendedKeyPurpose="on") + +where the <remote system> present in the configuration line above must be a valid IP address or a host name of the remote logging server. + Is it the case that omfwd is not configured with gtls and AuthMode? + + + + Verify that Red Hat Enterprise Linux 8 is configured to audit the execution of the "ssh-keysign" command with the following command: + +$ sudo auditctl -l | grep ssh-keysign + +-a always,exit -F path=/usr/libexec/openssh/ssh-keysign -F perm=x -F auid>=1000 -F auid!=unset -k privileged-ssh-keysign + Is it the case that the command does not return a line, or the line is commented out? + + + + To verify if the OpenSSH client uses defined Cipher suite in the Crypto Policy, run: +$ grep -i ciphers /etc/crypto-policies/back-ends/openssh.config +and verify that the line matches: +Ciphers + Is it the case that Crypto Policy for OpenSSH client is not configured correctly? + + + + To ensure logs are sent to a remote host, examine the file +/etc/rsyslog.conf. +If using UDP, a line similar to the following should be present: + *.* @ +If using TCP, a line similar to the following should be present: + *.* @@ +If using RELP, a line similar to the following should be present: + *.* :omrelp: + Is it the case that no evidence that the audit logs are being off-loaded to another system or media? + + + + To determine if the system is configured to audit successful calls +to the openat system call, run the following command: +$ sudo grep "openat" /etc/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + To check the ownership of /etc/cron.weekly, +run the command: +$ ls -lL /etc/cron.weekly +If properly configured, the output should indicate the following owner: +root + Is it the case that /etc/cron.weekly does not have an owner of root? + + + + +Run the following command to determine if the git_session_bind_all_unreserved_ports SELinux boolean is disabled: +$ getsebool git_session_bind_all_unreserved_ports +If properly configured, the output should show the following: +git_session_bind_all_unreserved_ports --> off + Is it the case that git_session_bind_all_unreserved_ports is not disabled? + + + + To check that the rhnsd service is disabled in system boot configuration, +run the following command: +$ sudo systemctl is-enabled rhnsd +Output should indicate the rhnsd service has either not been installed, +or has been disabled at all runlevels, as shown in the example below: +$ sudo systemctl is-enabled rhnsd disabled + +Run the following command to verify rhnsd is not active (i.e. not running) through current runtime configuration: +$ sudo systemctl is-active rhnsd + +If the service is not running the command will return the following output: +inactive + +The service will also be masked, to check that the rhnsd is masked, run the following command: +$ sudo systemctl show rhnsd | grep "LoadState\|UnitFileState" + +If the service is masked the command will return the following outputs: + +LoadState=masked + +UnitFileState=masked + Is it the case that the "rhnsd" is loaded and not masked? + + + + Verify that Red Hat Enterprise Linux 8 is configured to audit the execution of the "restorecon" command with the following command: + +$ sudo auditctl -l | grep restorecon + +-a always,exit -F path=/usr/sbin/restorecon -F perm=x -F auid>=1000 -F auid!=unset -k privileged-restorecon + Is it the case that the command does not return a line, or the line is commented out? + + + + Verify the audit system is configured to take an appropriate action when the internal event queue is full: +$ sudo grep -i overflow_action /etc/audit/auditd.conf + +The output should contain overflow_action = syslog + +If the value of the "overflow_action" option is not set to syslog, +single, halt or the line is commented out, ask the System Administrator +to indicate how the audit logs are off-loaded to a different system or media. + Is it the case that auditd overflow action is not set correctly? + + + + +Run the following command to determine if the samba_portmapper SELinux boolean is disabled: +$ getsebool samba_portmapper +If properly configured, the output should show the following: +samba_portmapper --> off + Is it the case that samba_portmapper is not disabled? + + + + Verify that a separate file system/partition has been created for /usr with the following command: + +$ mountpoint /usr + + Is it the case that "/usr is not a mountpoint" is returned? + + + + Verify the Red Hat Enterprise Linux 8 "fapolicyd" employs a deny-all, permit-by-exception policy. + +Check that "fapolicyd" is in enforcement mode with the following command: + +$ sudo grep permissive /etc/fapolicyd/fapolicyd.conf + +permissive = 0 + +Check that fapolicyd employs a deny-all policy on system mounts with the following command: + +$ sudo tail /etc/fapolicyd/fapolicyd.rules + +allow exe=/usr/bin/python3.7 : ftype=text/x-python +deny_audit perm=any pattern=ld_so : all +deny perm=any all : all + Is it the case that fapolicyd is not running in enforcement mode with a deny-all, permit-by-exception policy? + + + + Check group owners of the system audit logs. + +First, determine where the audit log file is located. + +$ sudo grep -iw ^log_file /etc/audit/auditd.conf +log_file = /var/log/audit/audit.log + +The log_file option specifies the audit log file path. +If the log_file option isn't defined, check all files within /var/log/audit directory. + + +Then, determine the audit log group by running the following command: +$ sudo grep -P '^[ ]*log_group[ ]+=.*$' /etc/audit/auditd.conf + + +Then, check that the audit log file is owned by the correct group. +Run the following command to display the owner of the audit log file: + +$ sudo stat -c "%n %G" log_file + + +The audit log file must be owned by the log_group or by root if the log_group is not specified. + Is it the case that audit log files are owned by incorrect group? + + + + +Run the following command to determine if the logadm_exec_content SELinux boolean is enabled: +$ getsebool logadm_exec_content +If properly configured, the output should show the following: +logadm_exec_content --> on + Is it the case that logadm_exec_content is not enabled? + + + + The group-owner of all log files written by rsyslog should be . +These log files are determined by the second part of each Rule line in +/etc/rsyslog.conf and typically all appear in /var/log. +To see the group-owner of a given log file, run the following command: +$ ls -l LOGFILE + Is it the case that the group-owner is not correct? + + + + To determine if LDAP is being used for authentication, use the following +command: +$ sudo grep -i useldapauth /etc/sysconfig/authconfig +The output should return: +USELDAPAUTH=yes + Is it the case that USELDAPAUTH=yes is not configured correctly in /etc/sysconfig/authconfig? + + + + To determine if the system is configured to audit successful calls +to the fchmod system call, run the following command: +$ sudo grep "fchmod" /etc/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + To determine if the system is configured to audit calls to the +settimeofday system call, run the following command: +$ sudo grep "settimeofday" /etc/audit/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + These settings can be verified by running the following: +$ gsettings get org.gnome.desktop.media-handling automount-open +If properly configured, the output for automount-openshould be false. +To ensure that users cannot enable automount opening in GNOME3, run the following: +$ grep 'automount-open' /etc/dconf/db/local.d/locks/* +If properly configured, the output for automount-open should be /org/gnome/desktop/media-handling/automount-open + Is it the case that GNOME automounting is not disabled? + + + + The runtime status of the net.ipv4.ip_local_port_range kernel parameter can be queried +by running the following command: +$ sysctl net.ipv4.ip_local_port_range +32768 65535. + + Is it the case that the correct value is not returned? + + + + Verify Red Hat Enterprise Linux 8 generates audit records for all account creations, modifications, disabling, and termination events that affect "/etc/sudoers" with the following command: + +$ sudo auditctl -l | grep /etc/sudoers + +-w /etc/sudoers -p wa -k identity + Is it the case that the command does not return a line, or the line is commented out? + + + + Verify the operating system routinely checks the baseline configuration for unauthorized changes. + +To determine that periodic AIDE execution has been scheduled, run the following command: +$ grep aide /etc/crontab +The output should return something similar to the following: +05 4 * * * root /usr/sbin/aide --check + +NOTE: The usage of special cron times, such as @daily or @weekly, is acceptable. + Is it the case that AIDE is not configured to scan periodically? + + + + To check the permissions of /usr/bin/sudo, +run the command: +$ ls -l /usr/bin/sudo +If properly configured, the output should indicate the following permissions: +---s--x--- + Is it the case that /usr/bin/sudo does not have unix mode ---s--x---? + + + + To ensure the login screen resets after a specified number of failures, +run the following command: +$ grep allowed-failures /etc/dconf/db/gdm.d/* +The output should be 3 or less. +To ensure that users cannot change or configure the resets after a specified +number of failures on the login screen, run the following: +$ grep allowed-failures /etc/dconf/db/gdm.d/locks/* +If properly configured, the output should be /org/gnome/login-screen/allowed-failures + Is it the case that allowed-failures is not equal to or less than the expected value? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_STRICT_KERNEL_WRX /boot/config.* + + For each kernel installed, a line with value "y" should be returned. + + Is it the case that the kernel was not built with the required value? + + + + +Run the following command to determine if the virt_use_nfs SELinux boolean is disabled: +$ getsebool virt_use_nfs +If properly configured, the output should show the following: +virt_use_nfs --> off + Is it the case that virt_use_nfs is not disabled? + + + + +Run the following command to determine if the ksmtuned_use_nfs SELinux boolean is disabled: +$ getsebool ksmtuned_use_nfs +If properly configured, the output should show the following: +ksmtuned_use_nfs --> off + Is it the case that ksmtuned_use_nfs is not disabled? + + + + Verify Red Hat Enterprise Linux 8 takes the appropriate action when the audit storage volume is full. + +Check that Red Hat Enterprise Linux 8 takes the appropriate action when the audit storage volume is full with the following command: + +$ sudo grep disk_full_action /etc/audit/auditd.conf + +disk_full_action = HALT + +If the value of the "disk_full_action" option is not "SYSLOG", "SINGLE", or "HALT", or the line is commented out, ask the system administrator to indicate how the system takes appropriate action when an audit storage volume is full. + Is it the case that there is no evidence of appropriate action? + + + + Verify that Red Hat Enterprise Linux 8 is configured to audit the execution of the "pt_chown" command with the following command: + +$ sudo auditctl -l | grep pt_chown + +-a always,exit -F path=/usr/libexec/pt_chown -F perm=x -F auid>=1000 -F auid!=unset -k privileged-pt_chown + Is it the case that the command does not return a line, or the line is commented out? + + + + To check the permissions of /etc/passwd-, +run the command: +$ ls -l /etc/passwd- +If properly configured, the output should indicate the following permissions: +-rw-r--r-- + Is it the case that /etc/passwd- does not have unix mode -rw-r--r--? + + + + +Run the following command to determine if the named_write_master_zones SELinux boolean is disabled: +$ getsebool named_write_master_zones +If properly configured, the output should show the following: +named_write_master_zones --> off + Is it the case that named_write_master_zones is not disabled? + + + + The runtime status of the net.ipv4.tcp_rfc1337 kernel parameter can be queried +by running the following command: +$ sysctl net.ipv4.tcp_rfc1337 +1. + + Is it the case that the correct value is not returned? + + + + Run the following command to determine if the iptables package is installed: $ rpm -q iptables + Is it the case that the package is not installed? + + + + To ensure LDAP is configured to use TLS for all transactions, run the following command: +$ grep start_tls /etc/pam_ldap.conf +The result should contain: +ssl start_tls + Is it the case that LDAP is not in use, the line is commented out, or not configured correctly? + + + + +Run the following command to determine if the cron_can_relabel SELinux boolean is disabled: +$ getsebool cron_can_relabel +If properly configured, the output should show the following: +cron_can_relabel --> off + Is it the case that cron_can_relabel is not disabled? + + + + To determine if logfile has been configured for sudo, run the following command: +$ sudo grep -ri "^[\s]*Defaults\s*\blogfile\b.*" /etc/sudoers /etc/sudoers.d/ +The command should return a matching output. + Is it the case that logfile is not enabled in sudo? + + + + +Run the following command to determine if the swift_can_network SELinux boolean is disabled: +$ getsebool swift_can_network +If properly configured, the output should show the following: +swift_can_network --> off + Is it the case that swift_can_network is not disabled? + + + + To determine if the system is configured to audit calls to the +removexattr system call, run the following command: +$ sudo grep "removexattr" /etc/audit/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + +Run the following command to determine if the secure_mode SELinux boolean is disabled: +$ getsebool secure_mode +If properly configured, the output should show the following: +secure_mode --> off + Is it the case that secure_mode is not disabled? + + + + Inspect the form of default GRUB 2 command line for the Linux operating system +in /etc/default/grub. If it includes ipv6.disable=1, +then the parameter will be configured for newly installed kernels. +First check if the GRUB recovery is enabled: +$ sudo grep 'GRUB_DISABLE_RECOVERY' /etc/default/grub +If this option is set to true, then check that a line is output by the following command: +$ sudo grep 'GRUB_CMDLINE_LINUX_DEFAULT.*ipv6.disable=1.*' /etc/default/grub +If the recovery is disabled, check the line with +$ sudo grep 'GRUB_CMDLINE_LINUX.*ipv6.disable=1.*' /etc/default/grub.Moreover, command line parameters for currently installed kernels should be checked as well. +Run the following command: +$ sudo grubby --info=ALL | grep args | grep -v 'ipv6.disable=1' +The command should not return any output. + Is it the case that IPv6 is not disabled? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_MODULE_SIG_FORCE /boot/config.* + + For each kernel installed, a line with value "y" should be returned. + + Is it the case that the kernel was not built with the required value? + + + + Verify emergency accounts have been provisioned with an expiration date of 72 hours. + +For every emergency account, run the following command to obtain its account aging and expiration information: + +$ sudo chage -l emergency_account_name + +Verify each of these accounts has an expiration date set within 72 hours or as documented. + Is it the case that any emergency accounts have no expiration date set or do not expire within 72 hours? + + + + Verify Red Hat Enterprise Linux 8 generates audit records for all account creations, modifications, disabling, and termination events that affect "/etc/gshadow" with the following command: + +$ sudo auditctl -l | egrep '(/etc/gshadow)' + +-w /etc/gshadow -p wa -k identity + +If the command does not return a line, or the line is commented out, this is a finding. + Is it the case that the system is not configured to audit account changes? + + + + Inspect the form of default GRUB 2 command line for the Linux operating system +in /etc/default/grub. If it includes spectre_v2=on, +then the parameter will be configured for newly installed kernels. +First check if the GRUB recovery is enabled: +$ sudo grep 'GRUB_DISABLE_RECOVERY' /etc/default/grub +If this option is set to true, then check that a line is output by the following command: +$ sudo grep 'GRUB_CMDLINE_LINUX_DEFAULT.*spectre_v2=on.*' /etc/default/grub +If the recovery is disabled, check the line with +$ sudo grep 'GRUB_CMDLINE_LINUX.*spectre_v2=on.*' /etc/default/grub.Moreover, command line parameters for currently installed kernels should be checked as well. +Run the following command: +$ sudo grubby --info=ALL | grep args | grep -v 'spectre_v2=on' +The command should not return any output. + Is it the case that spectre_v2 mitigation is not enforced? + + + + +Run the following command to determine if the squid_use_tproxy SELinux boolean is disabled: +$ getsebool squid_use_tproxy +If properly configured, the output should show the following: +squid_use_tproxy --> off + Is it the case that squid_use_tproxy is not disabled? + + + + + +Run the following command to determine the current status of the +rsyslog service: +$ sudo systemctl is-active rsyslog +If the service is running, it should return the following: active + Is it the case that the "rsyslog" service is disabled, masked, or not started.? + + + + +Run the following command to determine if the ssh_sysadm_login SELinux boolean is disabled: +$ getsebool ssh_sysadm_login +If properly configured, the output should show the following: +ssh_sysadm_login --> off + Is it the case that ssh_sysadm_login is not disabled? + + + + +Run the following command to determine if the httpd_can_connect_ftp SELinux boolean is disabled: +$ getsebool httpd_can_connect_ftp +If properly configured, the output should show the following: +httpd_can_connect_ftp --> off + Is it the case that httpd_can_connect_ftp is not disabled? + + + + These settings can be verified by running the following: +$ gsettings get org.gnome.desktop.thumbnailers disable-all +If properly configured, the output should be true. +To ensure that users cannot how long until the screensaver locks, run the following: +$ grep disable-all /etc/dconf/db/local.d/locks/* +If properly configured, the output should be /org/gnome/desktop/thumbnailers/disable-all + Is it the case that GNOME thumbnailers are not disabled? + + + + To determine if the system is configured to audit successful calls +to the lsetxattr system call, run the following command: +$ sudo grep "lsetxattr" /etc/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + +Run the following command to determine if the ftpd_use_fusefs SELinux boolean is disabled: +$ getsebool ftpd_use_fusefs +If properly configured, the output should show the following: +ftpd_use_fusefs --> off + Is it the case that ftpd_use_fusefs is not disabled? + + + + To verify that Audit Daemon is configured to resolve all uid, gid, syscall, +architecture, and socket address information before writing the event to disk, +run the following command: +$ sudo grep log_format /etc/audit/auditd.conf +The output should return the following: +log_format = ENRICHED + Is it the case that log_format isn't set to ENRICHED? + + + + To verify that Samba clients running smbclient must use packet signing, run the following command: +$ grep signing /etc/samba/smb.conf +The output should show: +client signing = rhisam + Is it the case that it is not? + + + + To verify all files and directories in a local interactive user's +home directory have a valid owner, run the following command: +$ sudo ls -lLR /home/USER + Is it the case that the user ownership is incorrect? + + + + Verify Red Hat Enterprise Linux 8 is configured in the system-auth file to prohibit password reuse +for a minimum of generations with the following command: + +$ grep -i remember /etc/pam.d/system-auth +password pam_pwhistory.so use_authtok remember= retry=3 + Is it the case that the line containing "pam_pwhistory.so" does not have the "remember" module argument set, +is commented out, or the value of the "remember" module argument is set to less than +"<sub idref="var_password_pam_remember" />"? + + + + +Run the following command to determine if the mpd_use_nfs SELinux boolean is disabled: +$ getsebool mpd_use_nfs +If properly configured, the output should show the following: +mpd_use_nfs --> off + Is it the case that mpd_use_nfs is not disabled? + + + + Inspect /etc/audit/auditd.conf and locate the following line to +determine if the system is configured correctly: +space_left SIZE_in_MB + Is it the case that the system is not configured a specfic size in MB to notify administrators of an issue? + + + + To determine if the system is configured to audit successful calls +to the fchown system call, run the following command: +$ sudo grep "fchown" /etc/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + To check that the named service is disabled in system boot configuration, +run the following command: +$ sudo systemctl is-enabled named +Output should indicate the named service has either not been installed, +or has been disabled at all runlevels, as shown in the example below: +$ sudo systemctl is-enabled named disabled + +Run the following command to verify named is not active (i.e. not running) through current runtime configuration: +$ sudo systemctl is-active named + +If the service is not running the command will return the following output: +inactive + +The service will also be masked, to check that the named is masked, run the following command: +$ sudo systemctl show named | grep "LoadState\|UnitFileState" + +If the service is masked the command will return the following outputs: + +LoadState=masked + +UnitFileState=masked + Is it the case that the "named" is loaded and not masked? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_IA32_EMULATION /boot/config.* + + Configs with value 'n' are not explicitly set in the file, so either commented lines or no + lines should be returned. + + Is it the case that the kernel was not built with the required value? + + + + The runtime status of the net.ipv6.conf.default.accept_redirects kernel parameter can be queried +by running the following command: +$ sysctl net.ipv6.conf.default.accept_redirects +0. + + Is it the case that the correct value is not returned? + + + + To determine if the system is configured to audit changes to its SELinux +configuration files, run the following command: +$ sudo auditctl -l | grep "dir=/etc/selinux" +If the system is configured to watch for changes to its SELinux +configuration, a line should be returned (including +perm=wa indicating permissions that are watched). + Is it the case that the system is not configured to audit attempts to change the MAC policy? + + + + To check if compression is enabled or set correctly, run the +following command: +$ sudo grep Compression /etc/ssh/sshd_config +If configured properly, output should be no or delayed. + Is it the case that it is commented out, or is not set to no or delayed? + + + + +Run the following command to determine if the httpd_use_fusefs SELinux boolean is disabled: +$ getsebool httpd_use_fusefs +If properly configured, the output should show the following: +httpd_use_fusefs --> off + Is it the case that httpd_use_fusefs is not disabled? + + + + Verify the system commands contained in the following directories are group-owned by "root", or a required system account, with the following command: + +$ sudo find -L /bin /sbin /usr/bin /usr/sbin /usr/local/bin /usr/local/sbin ! -group root -exec ls -l {} \; + Is it the case that any system commands are returned and is not group-owned by a required system account? + + + + +Run the following command to determine if the httpd_setrlimit SELinux boolean is disabled: +$ getsebool httpd_setrlimit +If properly configured, the output should show the following: +httpd_setrlimit --> off + Is it the case that httpd_setrlimit is not disabled? + + + + To check the permissions of /etc/http/conf/*, +run the command: +$ ls -l /etc/http/conf/* +If properly configured, the output should indicate the following permissions: +-rw-r----- + Is it the case that /etc/http/conf/* does not have unix mode -rw-r-----? + + + + Run the following command to determine if the dhcp-server package is installed: +$ rpm -q dhcp-server + Is it the case that the package is installed? + + + + To determine if the system is configured to audit unsuccessful calls +to the chmod system call, run the following command: +$ sudo grep "chmod" /etc/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + Run the following command to see what the timeout interval is: +$ sudo grep ClientAliveInterval /etc/ssh/sshd_config +If properly configured, the output should be: +ClientAliveInterval + Is it the case that it is commented out or not configured properly? + + + + To determine if the system is configured to audit successful calls +to the open_by_handle_at system call, run the following command: +$ sudo grep "open_by_handle_at" /etc/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + +Run the following command to determine if the virt_read_qemu_ga_data SELinux boolean is disabled: +$ getsebool virt_read_qemu_ga_data +If properly configured, the output should show the following: +virt_read_qemu_ga_data --> off + Is it the case that virt_read_qemu_ga_data is not disabled? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_STACKPROTECTOR_STRONG /boot/config.* + + For each kernel installed, a line with value "y" should be returned. + + Is it the case that the kernel was not built with the required value? + + + + To verify that the log_config_module exists in +/etc/httpd/conf/httpd.conf, run the following command: +$ grep log_config_module /etc/httpd/conf/httpd.conf +The output should return: +<IfModule log_config_module> + Is it the case that it is not? + + + + The ypbind package can be removed with the following command: $ sudo yum erase ypbind + Is it the case that ? + + + + + +Run the following command to determine the current status of the +ufw service: +$ sudo systemctl is-active ufw +If the service is running, it should return the following: active + Is it the case that the service is not enabled? + + + + To verify that execution of the command is being audited, run the following command: +$ sudo grep "path=/usr/sbin/seunshare" /etc/audit/audit.rules /etc/audit/rules.d/* +The output should return something similar to: +-a always,exit -F path=/usr/sbin/seunshare -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + Is it the case that ? + + + + Verify the audit logs are owned by "root". First, determine where the audit logs are stored with the following command: +$ sudo grep -iw log_file /etc/audit/auditd.conf +log_file = /var/log/audit/audit.log +Using the location of the audit log file, determine if the audit log is owned by "root" using the following command: +$ sudo stat -c "%n %U" /var/log/audit/audit.log +Audit logs must be owned by user root. +If the log_file isn't defined in /etc/audit/auditd.conf, check all files in /var/log/audit/ directory instead. + Is it the case that the audit log is not owned by root? + + + + The runtime status of the net.ipv4.tcp_syncookies kernel parameter can be queried +by running the following command: +$ sysctl net.ipv4.tcp_syncookies +1. + + Is it the case that the correct value is not returned? + + + + Verify that Red Hat Enterprise Linux 8 is configured to audit the execution of the "mount" command with the following command: + +$ sudo auditctl -l | grep mount + +-a always,exit -F path=/usr/bin/mount -F perm=x -F auid>=1000 -F auid!=unset -k privileged-mount + Is it the case that the command does not return a line, or the line is commented out? + + + + To check the value of the umask, run the following command: +$ grep umask /etc/init.d/functions +The output should show . + Is it the case that it does not? + + + + Open browser window and browse to the appropriate site. Before entry to the +site, you should be presented with the server's PKI credentials. Review +these credentials for authenticity. + +For DoD, find an entry which cites: + +Issuer: +CN = +DOD CLASS 3 CA-3 +OU = PKI +OU = DoD +O = U.S. Government +C = US + + Is it the case that it is not? + + + + Verify Red Hat Enterprise Linux 8 disables storing core dumps for all users by issuing the following command: + +$ grep -i storage /etc/systemd/coredump.conf + +Storage=none + Is it the case that Storage is not set to none or is commented out and the need for core dumps is not documented with the Information System Security Officer (ISSO) as an operational requirement for all domains that have the "core" item assigned? + + + + +Run the following command to determine if the virt_sandbox_use_sys_admin SELinux boolean is disabled: +$ getsebool virt_sandbox_use_sys_admin +If properly configured, the output should show the following: +virt_sandbox_use_sys_admin --> off + Is it the case that virt_sandbox_use_sys_admin is not disabled? + + + + To determine if the system is configured to audit calls to the +lchown system call, run the following command: +$ sudo grep "lchown" /etc/audit/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + To check that the nfs-server service is disabled in system boot configuration, +run the following command: +$ sudo systemctl is-enabled nfs-server +Output should indicate the nfs-server service has either not been installed, +or has been disabled at all runlevels, as shown in the example below: +$ sudo systemctl is-enabled nfs-server disabled + +Run the following command to verify nfs-server is not active (i.e. not running) through current runtime configuration: +$ sudo systemctl is-active nfs-server + +If the service is not running the command will return the following output: +inactive + +The service will also be masked, to check that the nfs-server is masked, run the following command: +$ sudo systemctl show nfs-server | grep "LoadState\|UnitFileState" + +If the service is masked the command will return the following outputs: + +LoadState=masked + +UnitFileState=masked + Is it the case that the "nfs-server" is loaded and not masked? + + + + To verify all files and directories in interactive user home directory are +group-owned by a group the user is a member of, run the +following command: +$ sudo ls -lLR /home/USER + Is it the case that the group ownership is incorrect? + + + + +Run the following command to determine if the staff_exec_content SELinux boolean is enabled: +$ getsebool staff_exec_content +If properly configured, the output should show the following: +staff_exec_content --> on + Is it the case that staff_exec_content is not enabled? + + + + +Run the following command to determine if the virt_sandbox_use_audit SELinux boolean is enabled: +$ getsebool virt_sandbox_use_audit +If properly configured, the output should show the following: +virt_sandbox_use_audit --> on + Is it the case that virt_sandbox_use_audit is not enabled? + + + + Inspect the form of default GRUB 2 command line for the Linux operating system +in /etc/default/grub. If it includes slub_debug=, +then the parameter will be configured for newly installed kernels. +First check if the GRUB recovery is enabled: +$ sudo grep 'GRUB_DISABLE_RECOVERY' /etc/default/grub +If this option is set to true, then check that a line is output by the following command: +$ sudo grep 'GRUB_CMDLINE_LINUX_DEFAULT.*slub_debug=.*' /etc/default/grub +If the recovery is disabled, check the line with +$ sudo grep 'GRUB_CMDLINE_LINUX.*slub_debug=.*' /etc/default/grub.Moreover, command line parameters for currently installed kernels should be checked as well. +Run the following command: +$ sudo grubby --info=ALL | grep args | grep -v 'slub_debug=' +The command should not return any output. + Is it the case that SLUB/SLAB poisoning is not enabled? + + + + Verify the nosuid option is configured for the /var/log mount point, + run the following command: + $ sudo mount | grep '\s/var/log\s' + . . . /var/log . . . nosuid . . . + + Is it the case that the "/var/log" file system does not have the "nosuid" option set? + + + + Inspect the form of default GRUB 2 command line for the Linux operating system +in /etc/default/grub. If it includes page_poison=1, +then the parameter will be configured for newly installed kernels. +First check if the GRUB recovery is enabled: +$ sudo grep 'GRUB_DISABLE_RECOVERY' /etc/default/grub +If this option is set to true, then check that a line is output by the following command: +$ sudo grep 'GRUB_CMDLINE_LINUX_DEFAULT.*page_poison=1.*' /etc/default/grub +If the recovery is disabled, check the line with +$ sudo grep 'GRUB_CMDLINE_LINUX.*page_poison=1.*' /etc/default/grub.Moreover, command line parameters for currently installed kernels should be checked as well. +Run the following command: +$ sudo grubby --info=ALL | grep args | grep -v 'page_poison=1' +The command should not return any output. + Is it the case that page allocator poisoning is not enabled? + + + + To verify that null passwords cannot be used, run the following command: +$ sudo awk -F: '!$2 {print $1}' /etc/shadow +If this produces any output, it may be possible to log into accounts +with empty passwords. + Is it the case that Blank or NULL passwords can be used? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_PAGE_POISONING_ZERO /boot/config.* + + For each kernel installed, a line with value "y" should be returned. + + Is it the case that the kernel was not built with the required value? + + + + Run the following command to determine if the abrt-plugin-rhtsupport package is installed: +$ rpm -q abrt-plugin-rhtsupport + Is it the case that the package is installed? + + + + +Run the following command to determine if the user_exec_content SELinux boolean is enabled: +$ getsebool user_exec_content +If properly configured, the output should show the following: +user_exec_content --> on + Is it the case that user_exec_content is not enabled? + + + + To check the permissions of /var/log, +run the command: +$ ls -l /var/log +If properly configured, the output should indicate the following permissions: +drwxr-xr-x + Is it the case that /var/log does not have unix mode drwxr-xr-x? + + + + Verify that Red Hat Enterprise Linux 8 is configured to audit the execution of the "kmod" command with the following command: + +$ sudo auditctl -l | grep kmod + +-a always,exit -F path=/usr/bin/kmod -F perm=x -F auid>=1000 -F auid!=unset -k privileged-kmod + Is it the case that the command does not return a line, or the line is commented out? + + + + To verify that automatic logins are disabled, run the following command: +$ grep -Pzoi "^\[daemon]\\nautomaticlogin.*" /etc/gdm/custom.conf +The output should show the following: +[daemon] +AutomaticLoginEnable=false + Is it the case that GDM allows users to automatically login? + + + + To determine if the system is configured to audit unsuccessful calls +to the fchmodat system call, run the following command: +$ sudo grep "fchmodat" /etc/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + To check the permissions of /etc/shadow, +run the command: +$ ls -l /etc/shadow +If properly configured, the output should indicate the following permissions: +---------- + Is it the case that /etc/shadow does not have unix mode ----------? + + + + Using a non-privileged account, verify that users cannot modify or change +network settings with the nmcli command with the following command: +$ nmcli general permissions +The output should contain the following: +PERMISSION VALUE +org.freedesktop.NetworkManager.enable-disable-network auth +org.freedesktop.NetworkManager.enable-disable-wifi auth +org.freedesktop.NetworkManager.enable-disable-wwan auth +org.freedesktop.NetworkManager.enable-disable-wimax auth +org.freedesktop.NetworkManager.sleep-wake auth +org.freedesktop.NetworkManager.network-control auth +org.freedesktop.NetworkManager.wifi.share.protected auth +org.freedesktop.NetworkManager.wifi.share.open auth +org.freedesktop.NetworkManager.settings.modify.system auth +org.freedesktop.NetworkManager.settings.modify.own auth +org.freedesktop.NetworkManager.settings.modify.hostname auth +org.freedesktop.NetworkManager.settings.modify.global-dns auth +org.freedesktop.NetworkManager.reload auth +org.freedesktop.NetworkManager.checkpoint-rollback auth +org.freedesktop.NetworkManager.enable-disable-statistics auth +org.freedesktop.NetworkManager.enable-disable-connectivity-check auth +org.freedesktop.NetworkManager.wifi.scan auth + + Is it the case that non-privileged users can modify or change network settings? + + + + +Run the following command to determine if the xend_run_blktap SELinux boolean is enabled: +$ getsebool xend_run_blktap +If properly configured, the output should show the following: +xend_run_blktap --> on + Is it the case that xend_run_blktap is not enabled? + + + + Verify Red Hat Enterprise Linux 8 takes the appropriate action when an audit processing failure occurs. + +Check that Red Hat Enterprise Linux 8 takes the appropriate action when an audit processing failure occurs with the following command: + +$ sudo grep disk_error_action /etc/audit/auditd.conf + +disk_error_action = HALT + +If the value of the "disk_error_action" option is not "SYSLOG", "SINGLE", or "HALT", or the line is commented out, ask the system administrator to indicate how the system takes appropriate action when an audit process failure occurs. + Is it the case that there is no evidence of appropriate action? + + + + Verify the "/etc/security/faillock.conf" file is configured use a non-default faillock directory to ensure contents persist after reboot: + +$ sudo grep 'dir =' /etc/security/faillock.conf + +dir = /var/log/faillock + Is it the case that the "dir" option is not set to a non-default documented tally log directory, is missing or commented out? + + + + To verify if the OpenSSL uses defined Crypto Policy, run: +$ grep 'Ciphersuites' /etc/crypto-policies/back-ends/opensslcnf.config | tail -n 1 +and verify that the line matches +Ciphersuites = TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256 + Is it the case that Crypto Policy for OpenSSL is not configured according to CC requirements? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_HIBERNATION /boot/config.* + + Configs with value 'n' are not explicitly set in the file, so either commented lines or no + lines should be returned. + + Is it the case that the kernel was not built with the required value? + + + + To ensure the default password is not set, run the following command: +$ sudo grep -v "^#" /etc/snmp/snmpd.conf| grep -E 'public|private' +There should be no output. + Is it the case that the default SNMP passwords public and private have not been changed or removed? + + + + Run the following command to see what the max sessions number is: +$ sudo grep MaxSessions /etc/ssh/sshd_config +If properly configured, the output should be: +MaxSessions + Is it the case that MaxSessions is not configured or not configured correctly? + + + + Query the SA and the Web Manager to determine if a compiler is present on +the server. + Is it the case that the web server is part of an application suite and a comiler is needed +for installation, patching, and upgrading of the suite or if the compiler +is embedded and can't be removed without breaking the suite, document the +installation of the compiler with the ISSO/ISSM and verify that the compiler +is restricted to administrative users only. If documented and restricted to +administrative users, this is not a finding. + +If an undocumented compiler is present, and available to non-administrative +users? + + + + Run the following command to determine if the sssd package is installed: $ rpm -q sssd + Is it the case that the package is not installed? + + + + To verify that the system will shutdown when auditd fails, +run the following command: +$ sudo grep "\-f 2" /etc/audit/audit.rules +The output should contain: +-f 2 + Is it the case that the system is not configured to shutdown on auditd failures? + + + + To determine if the system is configured to audit successful calls +to the fchmodat system call, run the following command: +$ sudo grep "fchmodat" /etc/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + Verify the operating system authenticates the remote logging server for off-loading audit logs with the following command: + +$ sudo grep -i '$ActionSendStreamDriverAuthMode' /etc/rsyslog.conf /etc/rsyslog.d/*.conf +The output should be +$/etc/rsyslog.conf:$ActionSendStreamDriverAuthMode x509/name + Is it the case that $ActionSendStreamDriverAuthMode in /etc/rsyslog.conf is not set to x509/name? + + + + Verify the system commands contained in the following directories are owned by "root" with the following command: + +$ sudo find -L /bin /sbin /usr/bin /usr/sbin /usr/libexec /usr/local/bin /usr/local/sbin ! -user root -exec ls -l {} \; + Is it the case that any system commands are found to not be owned by root? + + + + To verify the assigned home directory of all interactive users on the system +exist, run the following command: +$ sudo pwck -r + Is it the case that users home directory does not exist? + + + + +Run the following command to determine if the global_ssp SELinux boolean is disabled: +$ getsebool global_ssp +If properly configured, the output should show the following: +global_ssp --> off + Is it the case that global_ssp is not disabled? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_KEXEC /boot/config.* + + Configs with value 'n' are not explicitly set in the file, so either commented lines or no + lines should be returned. + + Is it the case that the kernel was not built with the required value? + + + + Verify the UMASK setting is configured correctly in the /etc/login.defs file by +running the following command: +$ sudo grep "UMASK" /etc/login.defs +All output must show the value of UMASK set as shown in the below: +UMASK + Is it the case that the above command returns no output, or the umask is configured incorrectly? + + + + Verify Red Hat Enterprise Linux 8 limits the number of concurrent sessions to +"" for all +accounts and/or account types with the following command: +$ grep -r -s maxlogins /etc/security/limits.conf /etc/security/limits.d/*.conf +/etc/security/limits.conf:* hard maxlogins 10 +This can be set as a global domain (with the * wildcard) but may be set differently for multiple domains. + Is it the case that the "maxlogins" item is missing, commented out, or the value is set greater +than "<sub idref="var_accounts_max_concurrent_login_sessions" />" and +is not documented with the Information System Security Officer (ISSO) as an +operational requirement for all domains that have the "maxlogins" item +assigned'? + + + + To determine how the SSH daemon's PermitRootLogin option is set, run the following command: + +$ sudo grep -i PermitRootLogin /etc/ssh/sshd_config + +If a line indicating prohibit-password is returned, then the required value is set. + Is it the case that it is commented out or not configured properly? + + + + To verify the boot loader superuser account has been set, run the following +command: +sudo grep -A1 "superusers" /boot/efi/EFI/redhat/grub.cfg +The output should show the following: +set superusers="superusers-account" +export superusers +where superusers-account is the actual account name different from common names like root, +admin, or administrator and different from any other existing user name. + Is it the case that superuser account is not set or is set to an existing name or to a common name? + + + + Verify file systems that are used for removable media are mounted with the "nosuid" option with the following command: + +$ sudo more /etc/fstab + +UUID=2bc871e4-e2a3-4f29-9ece-3be60c835222 /mnt/usbflash vfat noauto,owner,ro,nosuid,nodev,noexec 0 0 + Is it the case that file system found in "/etc/fstab" refers to removable media and it does not have the "nosuid" option set? + + + + The runtime status of the kernel.perf_cpu_time_max_percent kernel parameter can be queried +by running the following command: +$ sysctl kernel.perf_cpu_time_max_percent +1. + + Is it the case that the correct value is not returned? + + + + Run the following command to determine if the talk-server package is installed: +$ rpm -q talk-server + Is it the case that the package is installed? + + + + +To properly set the group owner of /etc/audit/, run the command: +$ sudo chgrp root /etc/audit/ + +To properly set the group owner of /etc/audit/rules.d/, run the command: +$ sudo chgrp root /etc/audit/rules.d/ + Is it the case that ? + + + + +Run the following command to determine if the xserver_execmem SELinux boolean is disabled: +$ getsebool xserver_execmem +If properly configured, the output should show the following: +xserver_execmem --> off + Is it the case that xserver_execmem is not disabled? + + + + To verify that Audit Daemon is configured to include local events, run the +following command: +$ sudo grep local_events /etc/audit/auditd.conf +The output should return the following: +local_events = yes + Is it the case that local_events isn't set to yes? + + + + To determine how the SSH daemon's PermitRootLogin option is set, run the following command: + +$ sudo grep -i PermitRootLogin /etc/ssh/sshd_config + +If a line indicating no is returned, then the required value is set. + + Is it the case that the required value is not set? + + + + To verify the nodev option is configured for non-root local partitions, run the following command: +$ sudo mount | grep '^/dev\S* on /\S' | grep --invert-match 'nodev' +The output shows local non-root partitions mounted without the nodev option, and there should be no output at all. + + Is it the case that some mounts appear among output lines? + + + + To determine how the SSH daemon's GSSAPIAuthentication option is set, run the following command: + +$ sudo grep -i GSSAPIAuthentication /etc/ssh/sshd_config + +If a line indicating yes is returned, then the required value is set. + + Is it the case that the required value is not set? + + + + Verify Red Hat Enterprise Linux 8 generates an audit record for unsuccessful attempts to modify files using the open_by_handle_at system call with O_TRUNC_WRITE flag. + +If the auditd daemon is configured to use the "augenrules" program to read audit rules during daemon startup (the default), run the following command: + +$ sudo grep -r open_by_handle_at /etc/audit/rules.d + +If the auditd daemon is configured to use the "auditctl" utility to read audit rules during daemon startup, run the following command: + +$ sudo grep open_by_handle_at /etc/audit/audit.rules + +The output should be the following: + +-a always,exit -F arch=b32 -S open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + Is it the case that the command does not return a line, or the line is commented out? + + + + +Run the following command to determine if the spamassassin_can_network SELinux boolean is disabled: +$ getsebool spamassassin_can_network +If properly configured, the output should show the following: +spamassassin_can_network --> off + Is it the case that spamassassin_can_network is not disabled? + + + + Verify file systems that are used for removable media are mounted with the "nodev" option with the following command: + +$ sudo more /etc/fstab + +UUID=2bc871e4-e2a3-4f29-9ece-3be60c835222 /mnt/usbflash vfat noauto,owner,ro,nosuid,nodev,noexec 0 0 + Is it the case that a file system found in "/etc/fstab" refers to removable media and it does not have the "nodev" option set? + + + + + +Run the following command to determine the current status of the +nails service: +$ sudo systemctl is-active nails +If the service is running, it should return the following: active + Is it the case that ? + + + + To check the permissions of /etc/audit/auditd.conf, +run the command: +$ ls -l /etc/audit/auditd.conf +If properly configured, the output should indicate the following permissions: +-rw-r----- + Is it the case that /etc/audit/auditd.conf does not have unix mode -rw-r-----? + + + + To check the group ownership of /etc/passwd-, +run the command: +$ ls -lL /etc/passwd- +If properly configured, the output should indicate the following group-owner: +root + Is it the case that /etc/passwd- does not have a group owner of root? + + + + The runtime status of the kernel.core_uses_pid kernel parameter can be queried +by running the following command: +$ sysctl kernel.core_uses_pid +0. + Is it the case that the returned line does not have a value of 0? + + + + To ensure that users cannot change session idle and lock settings, run the following: +$ grep 'idle-delay' /etc/dconf/db/local.d/locks/* +If properly configured, the output should return: +/org/gnome/desktop/session/idle-delay + Is it the case that idle-delay is not locked? + + + + Verify Red Hat Enterprise Linux 8 is securely comparing internal information system clocks at a regular interval with an NTP server with the following command: +$ sudo grep maxpoll /etc/ntp.conf /etc/chrony.conf +server [ntp.server.name] iburst maxpoll . + Is it the case that "maxpoll" has not been set to the value of "<sub idref="var_time_service_set_maxpoll" />", is commented out, or is missing? + + + + +Run the following command to determine if the cdrecord_read_content SELinux boolean is disabled: +$ getsebool cdrecord_read_content +If properly configured, the output should show the following: +cdrecord_read_content --> off + Is it the case that cdrecord_read_content is not disabled? + + + + To verify that the installed operating system is supported or certified, run +the following command: + +The output should contain something similar to: +Red Hat Enterprise Linux 8 + Is it the case that the installed operating system is not FIPS 140-2 certified? + + + + To check the ownership of /etc/cron.monthly, +run the command: +$ ls -lL /etc/cron.monthly +If properly configured, the output should indicate the following owner: +root + Is it the case that /etc/cron.monthly does not have an owner of root? + + + + Run the following command to Verify that the sudoers security policy is configured to use the invoking user's password for privilege escalation: + sudo cvtsudoers -f sudoers /etc/sudoers | grep -E '^Defaults !?(rootpw|targetpw|runaspw)' +or if cvtsudoers not supported: + sudo find /etc/sudoers /etc/sudoers.d \( \! -name '*~' -a \! -name '*.*' \) -exec grep -E --with-filename '^[[:blank:]]*Defaults[[:blank:]](.*[[:blank:]])?!?\b(rootpw|targetpw|runaspw)' -- {} \; +If no results are returned, this is a finding. +If conflicting results are returned, this is a finding. +If "Defaults !targetpw" is not defined, this is a finding. +If "Defaults !rootpw" is not defined, this is a finding. +If "Defaults !runaspw" is not defined, this is a finding. + Is it the case that invoke user passwd when using sudo? + + + + To verify that binaries cannot be directly executed from removable media, run the following command: +$ grep -v noexec /etc/fstab +The resulting output will show partitions which do not have the noexec flag. Verify all partitions +in the output are not removable media. + Is it the case that removable media partitions are present? + + + + To verify that McAfee Runtime Libraries (MFErt) and Linux Agent (MFEcma) +are installed, run the following command(s): +$ rpm -q MFEcma +$ rpm -q MFErt + Is it the case that the HBSS HIPS module is not installed? + + + + To determine if the system is configured to audit calls to the +adjtimex system call, run the following command: +$ sudo grep "adjtimex" /etc/audit/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + To determine how the SSH daemon's IgnoreRhosts option is set, run the following command: + +$ sudo grep -i IgnoreRhosts /etc/ssh/sshd_config + +If a line indicating yes is returned, then the required value is set. + + Is it the case that the required value is not set? + + + + To check that the acpid service is disabled in system boot configuration, +run the following command: +$ sudo systemctl is-enabled acpid +Output should indicate the acpid service has either not been installed, +or has been disabled at all runlevels, as shown in the example below: +$ sudo systemctl is-enabled acpid disabled + +Run the following command to verify acpid is not active (i.e. not running) through current runtime configuration: +$ sudo systemctl is-active acpid + +If the service is not running the command will return the following output: +inactive + +The service will also be masked, to check that the acpid is masked, run the following command: +$ sudo systemctl show acpid | grep "LoadState\|UnitFileState" + +If the service is masked the command will return the following outputs: + +LoadState=masked + +UnitFileState=masked + Is it the case that the "acpid" is loaded and not masked? + + + + To view the root user's PATH, run the following command: +$ sudo env | grep PATH +If correctly configured, the PATH must: use vendor default settings, +have no empty entries, and have no entries beginning with a character +other than a slash (/). + Is it the case that any of these conditions are not met? + + + + To check the status of the idle screen lock activation, run the following command: + +$ gsettings get org.gnome.desktop.screensaver lock-enabled +If properly configured, the output should be true. +To ensure that users cannot change how long until the screensaver locks, run the following: +$ grep lock-enabled /etc/dconf/db/local.d/locks/* +If properly configured, the output for lock-enabled should be /org/gnome/desktop/screensaver/lock-enabled + Is it the case that screensaver locking is not enabled and/or has not been set or configured correctly? + + + + To determine how the SSH daemon's X11Forwarding option is set, run the following command: + +$ sudo grep -i X11Forwarding /etc/ssh/sshd_config + +If a line indicating yes is returned, then the required value is set. + + Is it the case that the required value is not set? + + + + To verify the audispd plugin off-loads audit records onto a different system or +media from the system being audited, run the following command: +$ sudo grep -i remote_server /etc/audit/audisp-remote.conf +The output should return something similar to +remote_server = + Is it the case that audispd is not sending logs to a remote system? + + + + +Run the following command to determine if the telepathy_tcp_connect_generic_network_ports SELinux boolean is disabled: +$ getsebool telepathy_tcp_connect_generic_network_ports +If properly configured, the output should show the following: +telepathy_tcp_connect_generic_network_ports --> off + Is it the case that telepathy_tcp_connect_generic_network_ports is not disabled? + + + + Verify that Red Hat Enterprise Linux 8 is configured to audit the execution of the "su" command with the following command: + +$ sudo auditctl -l | grep su + +-a always,exit -F path=/usr/bin/su -F perm=x -F auid>=1000 -F auid!=unset -k privileged-su + Is it the case that the command does not return a line, or the line is commented out? + + + + The runtime status of the net.ipv4.conf.all.route_localnet kernel parameter can be queried +by running the following command: +$ sysctl net.ipv4.conf.all.route_localnet +0. + + Is it the case that the correct value is not returned? + + + + To verify that is configured +as the smart card driver, run the following command: +$ grep force_card_driver /etc/opensc.conf +The output should return something similar to: +force_card_drivers = ; + Is it the case that the smart card driver is not configured correctly? + + + + Verify the site's network diagram and visually check the web server, to +ensure that the private web server is located on a separate controlled +access subnet and is not part of the public DMZ that houses the public +web servers. + +In addition, the private web server needs to be isolated via a controlled +access mechanism from the local general population lan. + Is it the case that the private web server is not on a separate controlled access subnet? + + + + +Run the following command to determine if the secadm_exec_content SELinux boolean is enabled: +$ getsebool secadm_exec_content +If properly configured, the output should show the following: +secadm_exec_content --> on + Is it the case that secadm_exec_content is not enabled? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_FORTIFY_SOURCE /boot/config.* + + For each kernel installed, a line with value "y" should be returned. + + Is it the case that the kernel was not built with the required value? + + + + To check that the ypserv service is disabled in system boot configuration, +run the following command: +$ sudo systemctl is-enabled ypserv +Output should indicate the ypserv service has either not been installed, +or has been disabled at all runlevels, as shown in the example below: +$ sudo systemctl is-enabled ypserv disabled + +Run the following command to verify ypserv is not active (i.e. not running) through current runtime configuration: +$ sudo systemctl is-active ypserv + +If the service is not running the command will return the following output: +inactive + +The service will also be masked, to check that the ypserv is masked, run the following command: +$ sudo systemctl show ypserv | grep "LoadState\|UnitFileState" + +If the service is masked the command will return the following outputs: + +LoadState=masked + +UnitFileState=masked + Is it the case that the "ypserv" is loaded and not masked? + + + + To verify that rsyslog's Forwarding Output Module has CA certificate +configured for its TLS connections to remote server, run the following command: +$ grep DefaultNetstreamDriverCAFile /etc/rsyslog.conf /etc/rsyslog.d/*.conf +The output should include record similar to +global(DefaultNetstreamDriverCAFile="/etc/pki/tls/cert.pem") +where the path to the CA file (/etc/pki/tls/cert.pem in case above) must point to the correct CA certificate. + Is it the case that CA certificate for rsyslog remote logging via TLS is not set? + + + + +Run the following command to determine if the virt_sandbox_use_mknod SELinux boolean is disabled: +$ getsebool virt_sandbox_use_mknod +If properly configured, the output should show the following: +virt_sandbox_use_mknod --> off + Is it the case that virt_sandbox_use_mknod is not disabled? + + + + To check if RekeyLimit is set correctly, run the following command: +$ sudo grep RekeyLimit /etc/ssh/ssh_config.d/*.conf +If configured properly, output should be +/etc/ssh/ssh_config.d/02-rekey-limit.conf: +RekeyLimit +Check also the main configuration file with the following command: +$ sudo grep RekeyLimit /etc/ssh/ssh_config +The command should not return any output. + Is it the case that it is commented out or is not set? + + + + To find world-writable directories that lack the sticky bit, run the following command: +$ sudo find / -type d \( -perm -0002 -a ! -perm -1000 \) -print 2>/dev/null + Is it the case that any world-writable directories are missing the sticky bit? + + + + Run the following command to determine if the policycoreutils package is installed: $ rpm -q policycoreutils + Is it the case that the policycoreutils package is not installed? + + + + To verify that clients cannot automatically update DNS records, perform the +following: +$ grep -i dhcp_hostname /etc/sysconfig/network-scripts/ifcfg-* +$ grep -rni "send host-name" /etc/dhclient.conf /etc/dhcp +The output should return no results. + Is it the case that client Dynamic DNS updates are not disabled? + + + + To check the permissions of /var/log/syslog, +run the command: +$ ls -l /var/log/syslog +If properly configured, the output should indicate the following permissions: +-rw-r----- + Is it the case that /var/log/syslog does not have unix mode -rw-r-----? + + + + Verify the noauto option is configured for the /boot mount point, + run the following command: + $ sudo mount | grep '\s/boot\s' + . . . /boot . . . noauto . . . + + Is it the case that the "/boot" file system does not have the "noauto" option set? + + + + +Determine the audit log group by running the following command: + +$ sudo grep -P '^[ ]*log_group[ ]+=.*$' /etc/audit/auditd.conf + +Then, check that all directories within the /var/log/audit directory are owned by the group specified as log_group or by root if the log_group is not specified. +Run the following command: + +$ sudo find /var/log/audit -type d -printf "%p %g\n" + +All listed directories must be owned by the log_group or by root if the log_group is not specified. + Is it the case that there is a directory owned by different group? + + + + Run the following command to determine if the freeradius package is installed: $ rpm -q freeradius + Is it the case that the package is installed? + + + + +Run the following command to determine if the virt_rw_qemu_ga_data SELinux boolean is disabled: +$ getsebool virt_rw_qemu_ga_data +If properly configured, the output should show the following: +virt_rw_qemu_ga_data --> off + Is it the case that virt_rw_qemu_ga_data is not disabled? + + + + +Run the following command to determine if the httpd_mod_auth_ntlm_winbind SELinux boolean is disabled: +$ getsebool httpd_mod_auth_ntlm_winbind +If properly configured, the output should show the following: +httpd_mod_auth_ntlm_winbind --> off + Is it the case that httpd_mod_auth_ntlm_winbind is not disabled? + + + + Inspect each <Directory> instance and verify that either +FollowSymLinks does not exist, or +Options SymLinksIfOwnerMatchDisable is configured properly. + Is it the case that it is not? + + + + To check that the saslauthd service is disabled in system boot configuration, +run the following command: +$ sudo systemctl is-enabled saslauthd +Output should indicate the saslauthd service has either not been installed, +or has been disabled at all runlevels, as shown in the example below: +$ sudo systemctl is-enabled saslauthd disabled + +Run the following command to verify saslauthd is not active (i.e. not running) through current runtime configuration: +$ sudo systemctl is-active saslauthd + +If the service is not running the command will return the following output: +inactive + +The service will also be masked, to check that the saslauthd is masked, run the following command: +$ sudo systemctl show saslauthd | grep "LoadState\|UnitFileState" + +If the service is masked the command will return the following outputs: + +LoadState=masked + +UnitFileState=masked + Is it the case that the "saslauthd" is loaded and not masked? + + + + To determine if the system is configured to audit calls to the +rmdir system call, run the following command: +$ sudo grep "rmdir" /etc/audit/audit.* +If the system is configured to audit this activity, it will return a line. +To determine if the system is configured to audit calls to the +unlink system call, run the following command: +$ sudo grep "unlink" /etc/audit/audit.* +If the system is configured to audit this activity, it will return a line. +To determine if the system is configured to audit calls to the +unlinkat system call, run the following command: +$ sudo grep "unlinkat" /etc/audit/audit.* +If the system is configured to audit this activity, it will return a line. +To determine if the system is configured to audit calls to the +rename system call, run the following command: +$ sudo grep "rename" /etc/audit/audit.* +If the system is configured to audit this activity, it will return a line. +To determine if the system is configured to audit calls to the +renameat system call, run the following command: +$ sudo grep "renameat" /etc/audit/audit.* +If the system is configured to audit this activity, it will return a line. + Is it the case that no line is returned? + + + + To determine that AIDE is configured for FIPS 140-2 file hashing, run the following command: +$ grep sha512 /etc/aide.conf +Verify that the sha512 option is added to the correct ruleset. + Is it the case that the sha512 option is missing or not added to the correct ruleset? + + + + Remote web authors should not be able to upload files to the Document Root +directory structure without virus checking and checking for malicious or mobile +code. + Is it the case that it is not? + + + + Verify that temporary accounts have been provisioned with an expiration date +of 72 hours. For every temporary account, run the following command to +obtain its account aging and expiration information: +$ sudo chage -l temporary_account_name +Verify each of these accounts has an expiration date set within 72 hours or +as documented. + Is it the case that any temporary accounts have no expiration date set or do not expire within 72 hours? + + + + To ensure LoginGraceTime is set correctly, run the following command: +$ sudo grep LoginGraceTime /etc/ssh/sshd_config +If properly configured, the output should be: +LoginGraceTime +If the option is set to a number greater than 0, then the unauthenticated session will be disconnected +after the configured number seconds. + Is it the case that it is commented out or not configured properly? + + + + To verify that auditing of privileged command use is configured, run the +following command: +$ sudo grep newgidmap /etc/audit/audit.rules /etc/audit/rules.d/* +It should return a relevant line in the audit rules. + Is it the case that the command does not return a line, or the line is commented out? + + + + +Run the following command to determine if the nfs_export_all_rw SELinux boolean is enabled: +$ getsebool nfs_export_all_rw +If properly configured, the output should show the following: +nfs_export_all_rw --> on + Is it the case that nfs_export_all_rw is not enabled? + + + + To ensure root may not directly login to the system over physical consoles, +run the following command: +cat /etc/securetty +If any output is returned, this is a finding. + Is it the case that the /etc/securetty file is not empty? + + + + Verify that the Red Hat Enterprise Linux 8 "auditd" service is configured to notify the SA and ISSO in the event of an audit processing failure. +Inspect /etc/audit/auditd.conf and locate the following line to +determine if the system is configured to send email to an +account when it needs to notify an administrator: +action_mail_acct = + Is it the case that auditd is not configured to send emails per identified actions? + + + + +Run the following command to determine if the mmap_low_allowed SELinux boolean is disabled: +$ getsebool mmap_low_allowed +If properly configured, the output should show the following: +mmap_low_allowed --> off + Is it the case that mmap_low_allowed is not disabled? + + + + To find world-writable files, run the following command: +$ sudo find / -xdev -type f -perm -002 + Is it the case that there is output? + + + + +Run the following command to determine if the sge_use_nfs SELinux boolean is disabled: +$ getsebool sge_use_nfs +If properly configured, the output should show the following: +sge_use_nfs --> off + Is it the case that sge_use_nfs is not disabled? + + + + Run the following command to determine if the subscription-manager package is installed: $ rpm -q subscription-manager + Is it the case that the package is not installed? + + + + To ensure a login warning banner is enabled, run the following: +$ grep banner-message-enable /etc/dconf/db/gdm.d/* +If properly configured, the output should be true. +To ensure a login warning banner is locked and cannot be changed by a user, run the following: +$ grep banner-message-enable /etc/dconf/db/gdm.d/locks/* +If properly configured, the output should be /org/gnome/login-screen/banner-message-enable. + Is it the case that it is not? + + + + +Run the following command to determine if the authlogin_nsswitch_use_ldap SELinux boolean is disabled: +$ getsebool authlogin_nsswitch_use_ldap +If properly configured, the output should show the following: +authlogin_nsswitch_use_ldap --> off + Is it the case that authlogin_nsswitch_use_ldap is not disabled? + + + + To determine how the SSH daemon's AllowTcpForwarding option is set, run the following command: + +$ sudo grep -i AllowTcpForwarding /etc/ssh/sshd_config + +If a line indicating no is returned, then the required value is set. + Is it the case that The AllowTcpForwarding option exists and is disabled? + + + + + +Run the following command to determine the current status of the +crond service: +$ sudo systemctl is-active crond +If the service is running, it should return the following: active + Is it the case that ? + + + + Inspect /etc/default/grub for any instances of +systemd.confirm_spawn=(1|yes|true|on) in the kernel boot arguments. +Presence of a systemd.confirm_spawn=(1|yes|true|on) indicates +that interactive boot is enabled at boot time and verify that +GRUB_DISABLE_RECOVERY=true to disable recovery boot. + Is it the case that Interactive boot is enabled at boot time? + + + + Verify that Red Hat Enterprise Linux 8 is configured to audit the execution of the "ssh-agent" command with the following command: + +$ sudo auditctl -l | grep ssh-agent + +-a always,exit -F path=/usr/bin/ssh-agent -F perm=x -F auid>=1000 -F auid!=unset -k privileged-ssh-agent + Is it the case that the command does not return a line, or the line is commented out? + + + + To verify if password complexities are only enforce on local users, run the following command: +$ grep local_users_only /etc/security/pwquality.conf +The output should return local_users_only uncommented. + Is it the case that local_users_only is not uncommented or configured correctly? + + + + To determine if the system is configured to audit calls to the +lremovexattr system call, run the following command: +$ sudo grep "lremovexattr" /etc/audit/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + Verify the NX (no-execution) bit flag is set on the system. + +Check that the no-execution bit flag is set with the following commands: + +$ sudo dmesg | grep NX + +[ 0.000000] NX (Execute Disable) protection: active + +If "dmesg" does not show "NX (Execute Disable) protection" active, check the cpuinfo settings with the following command: + +$ sudo grep flags /proc/cpuinfo +flags : fpu vme de pse tsc ms nx rdtscp lm constant_ts + +The output should contain the "nx" flag. + +Then, verify that there are no log messsages stating that NX is disabled in the system log. Run the following command: +$ sudo grep -P "^.+protection: disabled.+" /var/log/dmesg +The output should be empty. + +Then, check that NX is not disabled in the kernel command line. +$ sudo grep -P ".+noexec[0-9]*=off.+" /proc/cmdline +The output should be empty. + Is it the case that NX is disabled? + + + + Inspect /etc/audit/auditd.conf and locate the following line to +determine if the system is configured to synchronize audit event data +with the log files on the disk: +$ sudo grep flush /etc/audit/auditd.conf +flush = DATA +Acceptable values are DATA, and SYNC. The setting is +case-insensitive. + Is it the case that auditd is not configured to synchronously write audit event data to disk? + + + + To check the permissions of /etc/http/conf, +run the command: +$ ls -l /etc/http/conf +If properly configured, the output should indicate the following permissions: +-rwxr-x--- + Is it the case that ? + + + + Verify the system-wide shared library files are owned by "root" with the following command: + +$ sudo find -L /lib /lib64 /usr/lib /usr/lib64 ! -user root -exec ls -l {} \; + Is it the case that any system wide shared library file is not owned by root? + + + + Inspect /proc/cmdline for any instances of selinux=0 +in the kernel boot arguments. Presence of selinux=0 indicates +that SELinux is disabled at boot time. + +If it would be disabled anywhere, make sure to enable it via a +MachineConfig object. + Is it the case that SELinux is disabled at boot time? + + + + + +Run the following command to determine the current status of the +pcscd service: +$ sudo systemctl is-active pcscd +If the service is running, it should return the following: active + Is it the case that the pcscd service is not enabled? + + + + To determine if the system is configured to audit successful calls +to the fsetxattr system call, run the following command: +$ sudo grep "fsetxattr" /etc/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + +Run the following command to determine if the ftpd_use_nfs SELinux boolean is disabled: +$ getsebool ftpd_use_nfs +If properly configured, the output should show the following: +ftpd_use_nfs --> off + Is it the case that ftpd_use_nfs is not disabled? + + + + Verify the noexec option is configured for the /dev/shm mount point, + run the following command: + $ sudo mount | grep '\s/dev/shm\s' + . . . /dev/shm . . . noexec . . . + + Is it the case that the "/dev/shm" file system does not have the "noexec" option set? + + + + Verify Red Hat Enterprise Linux 8 generates audit records for all account creations, modifications, disabling, and termination events that affect "/etc/passwd" with the following command: + +$ sudo auditctl -l | egrep '(/etc/passwd)' + +-w /etc/passwd -p wa -k identity + Is it the case that the command does not return a line, or the line is commented out? + + + + +Run the following command to determine if the tftp_anon_write SELinux boolean is disabled: +$ getsebool tftp_anon_write +If properly configured, the output should show the following: +tftp_anon_write --> off + Is it the case that tftp_anon_write is not disabled? + + + + To verify if the OpenSSH server uses defined ciphers in the Crypto Policy, run: +$ grep -Po '(-oCiphers=\S+)' /etc/crypto-policies/back-ends/opensshserver.config +and verify that the line matches: +-oCiphers= + Is it the case that Crypto Policy for OpenSSH Server is not configured correctly? + + + + To check the group ownership of /etc/cron.d, +run the command: +$ ls -lL /etc/cron.d +If properly configured, the output should indicate the following group-owner: +root + Is it the case that /etc/cron.d does not have a group owner of root? + + + + Run the following command to determine if the rsh-server package is installed: +$ rpm -q rsh-server + Is it the case that the package is installed? + + + + Check that no boot image file is specified in /etc/zipl.conf: +grep -R "^image\s*=" /etc/zipl.conf +No line should be returned, if a line is returned non BLS compliant boot entries are configured for zIPL. + Is it the case that a non BLS boot entry is configured? + + + + +Run the following command to determine if the puppetmaster_use_db SELinux boolean is disabled: +$ getsebool puppetmaster_use_db +If properly configured, the output should show the following: +puppetmaster_use_db --> off + Is it the case that puppetmaster_use_db is not disabled? + + + + +If the system is configured to prevent the loading of the bluetooth kernel module, +it will contain lines inside any file in /etc/modprobe.d or the deprecated /etc/modprobe.conf. +These lines instruct the module loading system to run another program (such as /bin/true) upon a module install event. + +These lines can also instruct the module loading system to ignore the bluetooth kernel module via blacklist keyword. + +Run the following command to search for such lines in all files in /etc/modprobe.d and the deprecated /etc/modprobe.conf: +$ grep -r bluetooth /etc/modprobe.conf /etc/modprobe.d + Is it the case that no line is returned? + + + + Run the following command to see if there are some keytabs +that would potentially allow the use of Kerberos by system daemons. +$ ls -la /etc/*.keytab +The expected result is +ls: cannot access '/etc/*.keytab': No such file or directory + Is it the case that a keytab file is present on the system? + + + + Verify that fapolicyd on Red Hat Enterprise Linux 8 prevents ability of non-privileged users to grant other users direct access to the contents of their home directories/folders. +Run the following command: + +grep -r "deny_audit perm=chmod path=/home" /etc/fapolicyd/rules.d + Is it the case that fapolicyd allows non-privileged users to grant other users direct access to the contents of their home directories/folders? + + + + Verify the nosuid option is configured for the /opt mount point, + run the following command: + $ sudo mount | grep '\s/opt\s' + . . . /opt . . . nosuid . . . + + Is it the case that the "/opt" file system does not have the "nosuid" option set? + + + + To verify if the OpenSSH Client uses defined Crypto Policy, run: +$ cat /etc/ssh/ssh_config.d/02-ospp.conf +and verify that the line matches +Match final all +RekeyLimit 512M 1h +GSSAPIAuthentication no +Ciphers aes256-ctr,aes256-cbc,aes128-ctr,aes128-cbc +PubkeyAcceptedKeyTypes ssh-rsa,ecdsa-sha2-nistp384,ecdsa-sha2-nistp256 +MACs hmac-sha2-512,hmac-sha2-256 +KexAlgorithms ecdh-sha2-nistp521,ecdh-sha2-nistp384,ecdh-sha2-nistp256,diffie-hellman-group14-sha1 + Is it the case that Crypto Policy for OpenSSH Client is not configured according to CC requirements? + + + + +Run the following command to determine if the virt_use_samba SELinux boolean is disabled: +$ getsebool virt_use_samba +If properly configured, the output should show the following: +virt_use_samba --> off + Is it the case that virt_use_samba is not disabled? + + + + To verify the number of rounds for the password hashing algorithm is configured, run the following command: +$ sudo grep rounds /etc/pam.d/password-auth +The output should show the following match: +password sufficient pam_unix.so sha512 rounds= + Is it the case that rounds is not set to <sub idref="var_password_pam_unix_rounds" /> or is commented out? + + + + Only FIPS ciphers should be used. To verify that only FIPS-approved +ciphers are in use, run the following command: +$ sudo grep Ciphers /etc/ssh/sshd_config +The output should contain only those ciphers which are FIPS-approved. + Is it the case that FIPS ciphers are not configured or the enabled ciphers are not FIPS-approved? + + + + The runtime status of the net.ipv4.conf.all.shared_media kernel parameter can be queried +by running the following command: +$ sysctl net.ipv4.conf.all.shared_media +0. + + Is it the case that the correct value is not returned? + + + + The runtime status of the net.ipv6.conf.default.max_addresses kernel parameter can be queried +by running the following command: +$ sysctl net.ipv6.conf.default.max_addresses +1. + + Is it the case that the correct value is not returned? + + + + The runtime status of the net.ipv6.conf.default.accept_ra kernel parameter can be queried +by running the following command: +$ sysctl net.ipv6.conf.default.accept_ra +0. + + Is it the case that the correct value is not returned? + + + + +Run the following command to determine if the ftpd_connect_db SELinux boolean is disabled: +$ getsebool ftpd_connect_db +If properly configured, the output should show the following: +ftpd_connect_db --> off + Is it the case that ftpd_connect_db is not disabled? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_SECCOMP /boot/config.* + + For each kernel installed, a line with value "y" should be returned. + + Is it the case that the kernel was not built with the required value? + + + + +Run the following command to determine if the samba_domain_controller SELinux boolean is disabled: +$ getsebool samba_domain_controller +If properly configured, the output should show the following: +samba_domain_controller --> off + Is it the case that samba_domain_controller is not disabled? + + + + Inspect the list of enabled firewall ports and verify they are configured correctly by running +the following command: +$ sudo firewall-cmd --list-all + Is it the case that the firewalld rules are not configured? + + + + To determine how the SSH daemon's X11Forwarding option is set, run the following command: + +$ sudo grep -i X11Forwarding /etc/ssh/sshd_config + +If a line indicating no is returned, then the required value is set. + + Is it the case that the required value is not set? + + + + To check that the screen locks immediately when activated, run the following command: +$ gsettings get org.gnome.desktop.screensaver lock-delay +If properly configured, the output should be 'uint32 '. + Is it the case that the screensaver lock delay is missing, or is set to a value greater than <sub idref="var_screensaver_lock_delay" />? + + + + Run the following command to check for duplicate group names: +Check that the operating system contains no duplicate group names for interactive users by running the following command: + + cut -d : -f 1 /etc/group | uniq -d + +If output is produced, this is a finding. +Configure the operating system to contain no duplicate names for groups. +Edit the file "/etc/group" and provide each group that has a duplicate group name with a unique group name. + Is it the case that has duplicate group names? + + + + Check that Red Hat Enterprise Linux 8 has the packages for smart card support installed. + +Run the following command to determine if the openssl-pkcs11 package is installed: +$ rpm -q openssl-pkcs11 + Is it the case that smartcard software is not installed? + + + + Verify that Red Hat Enterprise Linux 8 is configured to audit the execution of the "newgrp" command with the following command: + +$ sudo auditctl -l | grep newgrp + +-a always,exit -F path=/usr/bin/newgrp -F perm=x -F auid>=1000 -F auid!=unset -k privileged-newgrp + Is it the case that the command does not return a line, or the line is commented out? + + + + To obtain a listing of all users and the contents of their shadow password +field, run the command: +$ sudo awk -F: '$1 !~ /^root$/ && $2 !~ /^[!*]/ {print $1 ":" $2}' /etc/shadow +Identify the system accounts from this listing. These will primarily be the accounts +with UID numbers less than UID_MIN, other than root. Value of the UID_MIN +directive is set in /etc/login.defs configuration file. In the default +configuration, UID_MIN is set to 500. + Is it the case that it is not? + + + + +Run the following command to determine if the cron_system_cronjob_use_shares SELinux boolean is disabled: +$ getsebool cron_system_cronjob_use_shares +If properly configured, the output should show the following: +cron_system_cronjob_use_shares --> off + Is it the case that cron_system_cronjob_use_shares is not disabled? + + + + To determine if the system is configured to audit unsuccessful calls +to the fsetxattr system call, run the following command: +$ sudo grep "fsetxattr" /etc/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + To check the group ownership of /etc/passwd, +run the command: +$ ls -lL /etc/passwd +If properly configured, the output should indicate the following group-owner: +root + Is it the case that /etc/passwd does not have a group owner of root? + + + + To check the ownership of /var/log, +run the command: +$ ls -lL /var/log +If properly configured, the output should indicate the following owner: +root + Is it the case that /var/log does not have an owner of root? + + + + +Run the following command to determine if the git_cgi_enable_homedirs SELinux boolean is disabled: +$ getsebool git_cgi_enable_homedirs +If properly configured, the output should show the following: +git_cgi_enable_homedirs --> off + Is it the case that git_cgi_enable_homedirs is not disabled? + + + + These settings can be verified by running the following: +$ gsettings get org.gnome.desktop.media-handling autorun-never +If properly configured, the output for autorun-nevershould be true. +To ensure that users cannot enable autorun in GNOME3, run the following: +$ grep 'autorun-never' /etc/dconf/db/local.d/locks/* +If properly configured, the output for autorun-never should be /org/gnome/desktop/media-handling/autorun-never + Is it the case that GNOME autorun is not disabled? + + + + To verify if the OpenSSH client uses defined MACs in the Crypto Policy, run: +$ grep -i macs /etc/crypto-policies/back-ends/openssh.config +and verify that the line matches: +MACs hmac-sha2-512,hmac-sha2-256 + Is it the case that Crypto Policy for OpenSSH client is not configured correctly? + + + + +Run the following command to determine if the zebra_write_config SELinux boolean is disabled: +$ getsebool zebra_write_config +If properly configured, the output should show the following: +zebra_write_config --> off + Is it the case that zebra_write_config is not disabled? + + + + To check that the sysstat service is disabled in system boot configuration, +run the following command: +$ sudo systemctl is-enabled sysstat +Output should indicate the sysstat service has either not been installed, +or has been disabled at all runlevels, as shown in the example below: +$ sudo systemctl is-enabled sysstat disabled + +Run the following command to verify sysstat is not active (i.e. not running) through current runtime configuration: +$ sudo systemctl is-active sysstat + +If the service is not running the command will return the following output: +inactive + +The service will also be masked, to check that the sysstat is masked, run the following command: +$ sudo systemctl show sysstat | grep "LoadState\|UnitFileState" + +If the service is masked the command will return the following outputs: + +LoadState=masked + +UnitFileState=masked + Is it the case that the "sysstat" is loaded and not masked? + + + + +Run the following command to determine if the polipo_use_cifs SELinux boolean is disabled: +$ getsebool polipo_use_cifs +If properly configured, the output should show the following: +polipo_use_cifs --> off + Is it the case that polipo_use_cifs is not disabled? + + + + +Run the following command to determine if the selinuxuser_postgresql_connect_enabled SELinux boolean is disabled: +$ getsebool selinuxuser_postgresql_connect_enabled +If properly configured, the output should show the following: +selinuxuser_postgresql_connect_enabled --> off + Is it the case that selinuxuser_postgresql_connect_enabled is not disabled? + + + + To determine if the system is configured to audit successful calls +to the creat system call, run the following command: +$ sudo grep "creat" /etc/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + Run the following command to determine if the setroubleshoot-plugins package is installed: +$ rpm -q setroubleshoot-plugins + Is it the case that the package is installed? + + + + +Run the following command to determine if the samba_enable_home_dirs SELinux boolean is disabled: +$ getsebool samba_enable_home_dirs +If properly configured, the output should show the following: +samba_enable_home_dirs --> off + Is it the case that samba_enable_home_dirs is not disabled? + + + + To check for incorrectly labeled device files, run following commands: +$ sudo find /dev -context *:device_t:* \( -type c -o -type b \) -printf "%p %Z\n" +$ sudo find /dev -context *:unlabeled_t:* \( -type c -o -type b \) -printf "%p %Z\n" +It should produce no output in a well-configured system. + Is it the case that there is output? + + + + To determine if the system is configured to audit successful calls +to the open system call, run the following command: +$ sudo grep "open" /etc/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + To check the group ownership of /etc/gshadow, +run the command: +$ ls -lL /etc/gshadow +If properly configured, the output should indicate the following group-owner: +root + Is it the case that /etc/gshadow does not have a group owner of root? + + + + Run the following command to determine if the binutils package is installed: $ rpm -q binutils + Is it the case that the package is not installed? + + + + To determine if the system is configured to audit calls to the +chmod system call, run the following command: +$ sudo grep "chmod" /etc/audit/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + Run the following command to determine if the fapolicyd package is installed: $ rpm -q fapolicyd + Is it the case that the fapolicyd package is not installed? + + + + +Run the following command to determine if the zarafa_setrlimit SELinux boolean is disabled: +$ getsebool zarafa_setrlimit +If properly configured, the output should show the following: +zarafa_setrlimit --> off + Is it the case that zarafa_setrlimit is not disabled? + + + + To determine if the system is configured to audit successful calls +to the chown system call, run the following command: +$ sudo grep "chown" /etc/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + Run the following command to determine if the samba-common package is installed: $ rpm -q samba-common + Is it the case that the package is not installed? + + + + The runtime status of the kernel.perf_event_max_sample_rate kernel parameter can be queried +by running the following command: +$ sysctl kernel.perf_event_max_sample_rate +1. + + Is it the case that the correct value is not returned? + + + + To verify that HBSS PA is installed, run the following command(s): +$ sudo ls /opt/McAfee/auditengine/bin/auditmanager + Is it the case that the HBSS PA module is not installed? + + + + To determine if the system is configured to audit successful calls +to the unlink system call, run the following command: +$ sudo grep "unlink" /etc/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + To ensure TLS is configured with trust certificates, run the following command: +$ grep cert /etc/nslcd.conf + Is it the case that LDAP is not in use, the line is commented out, or not configured correctly? + + + + +Run the following command to determine if the telepathy_connect_all_ports SELinux boolean is disabled: +$ getsebool telepathy_connect_all_ports +If properly configured, the output should show the following: +telepathy_connect_all_ports --> off + Is it the case that telepathy_connect_all_ports is not disabled? + + + + To ensure that the GUI power settings are not active, run the following command: +$ gsettings get org.gnome.settings-daemon.plugins.power active +If properly configured, the output should be false. +To ensure that users cannot enable the power settings, run the following: +$ grep power /etc/dconf/db/local.d/locks/* +If properly configured, the output should be +/org/gnome/settings-daemon/plugins/power/active + Is it the case that power settings are enabled and are not disabled? + + + + +Run the following command to determine if the polipo_connect_all_unreserved SELinux boolean is disabled: +$ getsebool polipo_connect_all_unreserved +If properly configured, the output should show the following: +polipo_connect_all_unreserved --> off + Is it the case that polipo_connect_all_unreserved is not disabled? + + + + Verify that a separate file system/partition has been created for /var/log with the following command: + +$ mountpoint /var/log + + Is it the case that "/var/log is not a mountpoint" is returned? + + + + +Run the following command to determine if the selinuxuser_share_music SELinux boolean is disabled: +$ getsebool selinuxuser_share_music +If properly configured, the output should show the following: +selinuxuser_share_music --> off + Is it the case that selinuxuser_share_music is not disabled? + + + + To ensure that remote access connections are encrypted, run the following command: +$ gsettings get org.gnome.Vino require-encrpytion +If properly configured, the output should be true. +To ensure that users cannot disable encrypted remote connections, run the following: +$ grep require-encryption /etc/dconf/db/local.d/locks/* +If properly configured, the output should be +/org/gnome/Vino/require-encryption + Is it the case that remote access connections are not encrypted? + + + + To check the group ownership of /etc/cron.allow, +run the command: +$ ls -lL /etc/cron.allow +If properly configured, the output should indicate the following group-owner: +root + Is it the case that /etc/cron.allow does not have a group owner of root? + + + + Run the following command to determine if the squid package is installed: +$ rpm -q squid + Is it the case that the package is installed? + + + + +Run the following command to determine if the selinuxuser_execheap SELinux boolean is disabled: +$ getsebool selinuxuser_execheap +If properly configured, the output should show the following: +selinuxuser_execheap --> off + Is it the case that selinuxuser_execheap is not disabled? + + + + Verify that the files and directories of each instance of Alias, +ScriptAlias, and ScriptAliasMatch that exist +have the correct file and directory permissions applied. + Is it the case that it is not? + + + + +Run the following command to determine if the cobbler_anon_write SELinux boolean is disabled: +$ getsebool cobbler_anon_write +If properly configured, the output should show the following: +cobbler_anon_write --> off + Is it the case that cobbler_anon_write is not disabled? + + + + To verify that USB Human Interface Devices and hubs will be authorized by the USBGuard daemon, +run the following command: +$ sudo grep allow /etc/usbguard/rules.conf +The output lines should include +allow with-interface match-all { 03:*:* 09:00:* } + Is it the case that USB devices of class 3 and 9:00 are not authorized? + + + + +Run the following command to determine if the deny_ptrace SELinux boolean is disabled: +$ getsebool deny_ptrace +If properly configured, the output should show the following: +deny_ptrace --> off + Is it the case that deny_ptrace is not disabled? + + + + To determine if the system is configured to audit calls to the +unlink system call, run the following command: +$ sudo grep "unlink" /etc/audit/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + The runtime status of the kernel.yama.ptrace_scope kernel parameter can be queried +by running the following command: +$ sysctl kernel.yama.ptrace_scope +1. + + Is it the case that the correct value is not returned? + + + + +Run the following command to determine if the git_system_enable_homedirs SELinux boolean is disabled: +$ getsebool git_system_enable_homedirs +If properly configured, the output should show the following: +git_system_enable_homedirs --> off + Is it the case that git_system_enable_homedirs is not disabled? + + + + Verify that Red Hat Enterprise Linux 8 is configured to audit the execution of the "setfacl" command with the following command: + +$ sudo auditctl -l | grep setfacl + +-a always,exit -F path=/usr/bin/setfacl -F perm=x -F auid>=1000 -F auid!=unset -k perm_mod + Is it the case that the command does not return a line, or the line is commented out? + + + + To check if the installed Operating System is 64-bit, run the following command: +$ uname -m +The output should be one of the following: x86_64, aarch64, ppc64le or s390x. +If the output is i686 or i386 the operating system is 32-bit. +Check if the installed CPU supports 64-bit operating systems by running the following command: +$ lscpu | grep "CPU op-mode" +If the output contains 64bit, the CPU supports 64-bit operating systems. + Is it the case that the installed operating sytem is 32-bit but the CPU supports operation in 64-bit? + + + + + +Run the following command to determine the current status of the +chronyd service: +$ sudo systemctl is-active chronyd +If the service is running, it should return the following: active + + +Run the following command to determine the current status of the +ntpd service: +$ sudo systemctl is-active ntpd +If the service is running, it should return the following: active + Is it the case that ? + + + + To check the permissions of /etc/passwd, +run the command: +$ ls -l /etc/passwd +If properly configured, the output should indicate the following permissions: +-rw-r--r-- + Is it the case that /etc/passwd does not have unix mode -rw-r--r--? + + + + To check that the atd service is disabled in system boot configuration, +run the following command: +$ sudo systemctl is-enabled atd +Output should indicate the atd service has either not been installed, +or has been disabled at all runlevels, as shown in the example below: +$ sudo systemctl is-enabled atd disabled + +Run the following command to verify atd is not active (i.e. not running) through current runtime configuration: +$ sudo systemctl is-active atd + +If the service is not running the command will return the following output: +inactive + +The service will also be masked, to check that the atd is masked, run the following command: +$ sudo systemctl show atd | grep "LoadState\|UnitFileState" + +If the service is masked the command will return the following outputs: + +LoadState=masked + +UnitFileState=masked + Is it the case that the "atd" is loaded and not masked? + + + + To determine that AIDE is verifying ACLs, run the following command: +$ grep acl /etc/aide.conf +Verify that the acl option is added to the correct ruleset. + Is it the case that the acl option is missing or not added to the correct ruleset? + + + + To determine if the system is configured to audit calls to the +fchownat system call, run the following command: +$ sudo grep "fchownat" /etc/audit/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + To check how many digits are required in a password, run the following command: +$ grep dcredit /etc/security/pwquality.conf +The dcredit parameter (as a negative number) will indicate how many digits are required. + Is it the case that dcredit is not found or not equal to or less than the required value? + + + + Find if logging is applied to the FTP daemon. + +Procedures: + +If vsftpd is started by xinetd the following command will indicate the xinetd.d startup file: +$ grep vsftpd /etc/xinetd.d/* +$ grep server_args vsftpd xinetd.d startup file +This will indicate the vsftpd config file used when starting through xinetd. +If the server_args line is missing or does not include the vsftpd configuration file, then the default config file (/etc/vsftpd/vsftpd.conf) is used. +$ sudo grep xferlog_enable vsftpd config file + Is it the case that xferlog_enable is missing, or is not set to yes? + + + + Make sure that the kernel is configured to trust the CPU RNG by following +commands. To check if the option was correctly configured at kernel compile +time, run the following command: +grep -q CONFIG_RANDOM_TRUST_CPU=y /boot/config-`uname -r` +If the command outputs: +CONFIG_RANDOM_TRUST_CPU=y, +it means that the option is compiled into the kernel. Make sure that the +option is not overridden through a boot parameter: +sudo grep 'kernelopts.*random\.trust_cpu=off.*' /boot/grub2/grubenv +The command should not return any output. If the option is not compiled into +the kernel, check that the option is configured through boot parameter. +Inspect the form of default GRUB 2 command line for the Linux operating system +in /etc/default/grub. If it includes random.trust_cpu=on, +then the parameter will be configured for newly installed kernels. +First check if the GRUB recovery is enabled: +$ sudo grep 'GRUB_DISABLE_RECOVERY' /etc/default/grub +If this option is set to true, then check that a line is output by the following command: +$ sudo grep 'GRUB_CMDLINE_LINUX_DEFAULT.*random.trust_cpu=on.*' /etc/default/grub +If the recovery is disabled, check the line with +$ sudo grep 'GRUB_CMDLINE_LINUX.*random.trust_cpu=on.*' /etc/default/grub.Moreover, command line parameters for currently installed kernels should be checked as well. +Run the following command: +$ sudo grubby --info=ALL | grep args | grep -v 'random.trust_cpu=on' +The command should not return any output. + Is it the case that the kernel is not configured to trust the CPU RNG? + + + + To determine if the system is configured to audit successful calls +to the unlinkat system call, run the following command: +$ sudo grep "unlinkat" /etc/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + To check the group ownership of /var/log/messages, +run the command: +$ ls -lL /var/log/messages +If properly configured, the output should indicate the following group-owner: +root + Is it the case that /var/log/messages does not have a group owner of root? + + + + To verify the number of rounds for the password hashing algorithm is configured, run the following command: +$ sudo grep rounds /etc/pam.d/system-auth +The output should show the following match: +password sufficient pam_unix.so sha512 rounds= + Is it the case that rounds is not set to <sub idref="var_password_pam_unix_rounds" /> or is commented out? + + + + To check that SELinux is not disabled at boot time; +Check that no boot entry disables selinux: +sudo grep -L "^options\s+.*\bselinux=0\b" /boot/loader/entries/*.conf +No line should be returned, each line returned is a boot entry that disables SELinux. + Is it the case that SELinux is disabled at boot time? + + + + Run the following command to check if the line is present: +grep pam_wheel /etc/pam.d/su +The output should contain the following line: +auth required pam_wheel.so use_uid + Is it the case that the line is not in the file or it is commented? + + + + To verify the nosuid option is configured for all NFS mounts, run +the following command: +$ mount | grep nfs +All NFS mounts should show the nosuid setting in parentheses. This +is not applicable if NFS is not implemented. + Is it the case that the setting does not show? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_DEBUG_NOTIFIERS /boot/config.* + + For each kernel installed, a line with value "y" should be returned. + + Is it the case that the kernel was not built with the required value? + + + + Verify the nodev option is configured for the /dev/shm mount point, + run the following command: + $ sudo mount | grep '\s/dev/shm\s' + . . . /dev/shm . . . nodev . . . + + Is it the case that the "/dev/shm" file system does not have the "nodev" option set? + + + + Run the following command to determine if the xinetd package is installed: +$ rpm -q xinetd + Is it the case that the package is installed? + + + + +Run the following command to determine if the postgresql_selinux_transmit_client_label SELinux boolean is disabled: +$ getsebool postgresql_selinux_transmit_client_label +If properly configured, the output should show the following: +postgresql_selinux_transmit_client_label --> off + Is it the case that postgresql_selinux_transmit_client_label is not disabled? + + + + To determine how the SSH daemon's PrintLastLog option is set, run the following command: + +$ sudo grep -i PrintLastLog /etc/ssh/sshd_config + +If a line indicating yes is returned, then the required value is set. + + Is it the case that the required value is not set? + + + + + +Run the following command to determine the current status of the +sssd service: +$ sudo systemctl is-active sssd +If the service is running, it should return the following: active + Is it the case that the service is not enabled? + + + + To check the permissions of /etc/http/conf.d/*, +run the command: +$ ls -l /etc/http/conf.d/* +If properly configured, the output should indicate the following permissions: +-rw-r----- + Is it the case that /etc/http/conf.d/* does not have unix mode -rw-r-----? + + + + Run the following command to check for duplicate account names: +Check that the operating system contains no duplicate UIDs for interactive users by running the following command: +# awk -F ":" 'list[$3]++{print $1, $3}' /etc/passwd +If output is produced, this is a finding. +Configure the operating system to contain no duplicate UIDs for interactive users. +Edit the file "/etc/passwd" and provide each interactive user account that has a duplicate UID with a unique UID. + Is it the case that a line is returned? + + + + The runtime status of the kernel.core_pattern kernel parameter can be queried +by running the following command: +$ sysctl kernel.core_pattern +|/bin/false. + + Is it the case that the returned line does not have a value of "|/bin/false", or a line is not +returned and the need for core dumps is not documented with the Information +System Security Officer (ISSO) as an operational requirement? + + + + To ensure disable and restart on the login screen are disabled, run the following command: +$ grep disable-restart-buttons /etc/dconf/db/gdm.d/* +The output should be true. +To ensure that users cannot enable disable and restart on the login screen, run the following: +$ grep disable-restart-buttons /etc/dconf/db/gdm.d/locks/* +If properly configured, the output should be /org/gnome/login-screen/disable-restart-buttons + Is it the case that disable-restart-buttons has not been configured or is not disabled? + + + + +Run the following command to determine if the postgresql_selinux_users_ddl SELinux boolean is enabled: +$ getsebool postgresql_selinux_users_ddl +If properly configured, the output should show the following: +postgresql_selinux_users_ddl --> on + Is it the case that postgresql_selinux_users_ddl is not enabled? + + + + Verify the audit tools are protected from unauthorized access, deletion, or modification by checking the permissive mode. + +Check the octal permission of each audit tool by running the following command: + +$ sudo stat -c "%U %n" /sbin/auditctl /sbin/aureport /sbin/ausearch /sbin/autrace /sbin/auditd /sbin/rsyslogd /sbin/augenrules + Is it the case that any of these files have more permissive permissions than 0755? + + + + To check if dictionary words are disallowed run the following command: +$ sudo grep dictcheck /etc/security/pwquality.conf /etc/pwquality.conf.d/*.conf +The dictcheck parameter should be equal to 1. The value should look like +dictcheck=1 + Is it the case that dictcheck is not found or not equal to the required value? + + + + To determine how the SSH daemon's PubkeyAuthentication option is set, run the following command: + +$ sudo grep -i PubkeyAuthentication /etc/ssh/sshd_config + +If a line indicating no is returned, then the required value is set. + + Is it the case that the required value is not set? + + + + +Run the following command to determine if the samba_load_libgfapi SELinux boolean is disabled: +$ getsebool samba_load_libgfapi +If properly configured, the output should show the following: +samba_load_libgfapi --> off + Is it the case that samba_load_libgfapi is not disabled? + + + + +Run the following command to determine if the auditadm_exec_content SELinux boolean is enabled: +$ getsebool auditadm_exec_content +If properly configured, the output should show the following: +auditadm_exec_content --> on + Is it the case that auditadm_exec_content is not enabled? + + + + To determine how the SSH daemon's KerberosAuthentication option is set, run the following command: + +$ sudo grep -i KerberosAuthentication /etc/ssh/sshd_config + +If a line indicating no is returned, then the required value is set. + + Is it the case that the required value is not set? + + + + +Run the following command to determine if the mpd_enable_homedirs SELinux boolean is disabled: +$ getsebool mpd_enable_homedirs +If properly configured, the output should show the following: +mpd_enable_homedirs --> off + Is it the case that mpd_enable_homedirs is not disabled? + + + + To verify that is configured +as the smart card driver, run the following command: +$ grep card_drivers /etc/opensc.conf +The output should return something similar to: +card_drivers = ; + Is it the case that the smart card driver is not configured correctly? + + + + Run the following command to determine if the openldap-clients package is installed: +$ rpm -q openldap-clients + Is it the case that the package is installed? + + + + The following command will discover and print any +files on local partitions which do not belong to a valid group. +$ df --local -P | awk '{if (NR!=1) print $6}' | sudo xargs -I '{}' find '{}' -xdev -nogroup + +Either remove all files and directories from the system that do not have a valid group, +or assign a valid group with the chgrp command: +$ sudo chgrp group file + Is it the case that there is output? + + + + To check the permissions of /etc/http/conf.modules.d/*, +run the command: +$ ls -l /etc/http/conf.modules.d/* +If properly configured, the output should indicate the following permissions: +-rw-r----- + Is it the case that /etc/http/conf.modules.d/* does not have unix mode -rw-r-----? + + + + The reviewed should make a note of the name of the account being used for +the web service. This information may be needed later in the SRR. There +may also be other server services running related to the web server in +support of a particular web application, these passwords must be entrusted +to the SA or Web Manager as well. + +Query the SA or Web Manager to determine if they have the web service +password(s). + +NOTE: For installations that run as a service, or without a password, +the SA or Web Manager having an Admin account on the system would meet +the intent of this check. + Is it the case that the web server password(s) are not entrusted to the SA or Web Manager? + + + + The runtime status of the net.ipv4.conf.default.accept_redirects kernel parameter can be queried +by running the following command: +$ sysctl net.ipv4.conf.default.accept_redirects +0. + + Is it the case that the correct value is not returned? + + + + To check the group ownership of /etc/cron.monthly, +run the command: +$ ls -lL /etc/cron.monthly +If properly configured, the output should indicate the following group-owner: +root + Is it the case that /etc/cron.monthly does not have a group owner of root? + + + + To verify the noexec option is configured for all NFS mounts, run the following command: +$ mount | grep nfs +All NFS mounts should show the noexec setting in parentheses. This is not applicable if NFS is +not implemented. + Is it the case that the setting does not show? + + + + To verify that no .java and .jpp files exist, run the +following command: +find / -name *.java -o -name *.jpp +The output should not return any .java or .jpp files + Is it the case that it is not? + + + + +Run the following command to determine if the samba_share_nfs SELinux boolean is disabled: +$ getsebool samba_share_nfs +If properly configured, the output should show the following: +samba_share_nfs --> off + Is it the case that samba_share_nfs is not disabled? + + + + +Run the following command to determine if the unconfined_login SELinux boolean is enabled: +$ getsebool unconfined_login +If properly configured, the output should show the following: +unconfined_login --> on + Is it the case that unconfined_login is not enabled? + + + + Verify the operating system is not configured to bypass password requirements for privilege +escalation. Check the configuration of the "/etc/pam.d/sudo" file with the following command: +$ sudo grep pam_succeed_if /etc/pam.d/sudo + Is it the case that system is configured to bypass password requirements for privilege escalation? + + + + Verify that Red Hat Enterprise Linux 8 is configured to audit the execution of the "pam_timestamp_check" command with the following command: + +$ sudo auditctl -l | grep pam_timestamp_check + +-a always,exit -F path=/usr/sbin/pam_timestamp_check -F perm=x -F auid>=1000 -F auid!=unset -k privileged-pam_timestamp_check + Is it the case that the command does not return a line, or the line is commented out? + + + + The runtime status of the net.ipv6.conf.all.forwarding kernel parameter can be queried +by running the following command: +$ sysctl net.ipv6.conf.all.forwarding +0. +The ability to forward packets is only appropriate for routers. + Is it the case that IP forwarding value is "1" and the system is not router? + + + + To check that the bluetooth service is disabled in system boot configuration, +run the following command: +$ sudo systemctl is-enabled bluetooth +Output should indicate the bluetooth service has either not been installed, +or has been disabled at all runlevels, as shown in the example below: +$ sudo systemctl is-enabled bluetooth disabled + +Run the following command to verify bluetooth is not active (i.e. not running) through current runtime configuration: +$ sudo systemctl is-active bluetooth + +If the service is not running the command will return the following output: +inactive + +The service will also be masked, to check that the bluetooth is masked, run the following command: +$ sudo systemctl show bluetooth | grep "LoadState\|UnitFileState" + +If the service is masked the command will return the following outputs: + +LoadState=masked + +UnitFileState=masked + Is it the case that the "bluetooth" is loaded and not masked? + + + + +Run the following command to determine if the selinuxuser_udp_server SELinux boolean is disabled: +$ getsebool selinuxuser_udp_server +If properly configured, the output should show the following: +selinuxuser_udp_server --> off + Is it the case that selinuxuser_udp_server is not disabled? + + + + The owner of all log files written by rsyslog should be . +These log files are determined by the second part of each Rule line in +/etc/rsyslog.conf and typically all appear in /var/log. +To see the owner of a given log file, run the following command: +$ ls -l LOGFILE + Is it the case that the owner is not correct? + + + + To determine if the system is configured to audit unsuccessful calls +to the chown system call, run the following command: +$ sudo grep "chown" /etc/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + To ensure ClientAliveInterval is set correctly, run the following command: +$ sudo grep ClientAliveCountMax /etc/ssh/sshd_config +If properly configured, the output should be: +ClientAliveCountMax +For SSH earlier than v8.2, a ClientAliveCountMax value of 0 causes an idle timeout precisely when +the ClientAliveInterval is set. Starting with v8.2, a value of 0 disables the timeout +functionality completely. +If the option is set to a number greater than 0, then the idle session will be disconnected after +ClientAliveInterval * ClientAliveCountMax seconds. + Is it the case that it is commented out or not configured properly? + + + + To determine if umask has been configured for sudo with the appropriate value, +run the following command: +$ sudo grep -ri '^Defaults.*umask=' /etc/sudoers /etc/sudoers.d/ +The command should return a matching output. + Is it the case that umask is not set with the appropriate value for sudo? + + + + To check the group ownership of /etc/crontab, +run the command: +$ ls -lL /etc/crontab +If properly configured, the output should indicate the following group-owner: +root + Is it the case that /etc/crontab does not have a group owner of root? + + + + Run the following command to determine if the samba package is installed: +$ rpm -q samba + Is it the case that the package is installed? + + + + To check the permissions of /etc/at.allow, +run the command: +$ ls -l /etc/at.allow +If properly configured, the output should indicate the following permissions: +-rw------- + Is it the case that /etc/at.allow does not have unix mode -rw-------? + + + + The runtime status of the net.ipv4.icmp_echo_ignore_broadcasts kernel parameter can be queried +by running the following command: +$ sysctl net.ipv4.icmp_echo_ignore_broadcasts +1. + + Is it the case that the correct value is not returned? + + + + To check for legacy lines in /etc/shadow, run the following command: + grep '^\+' /etc/shadow +The command should not return any output. + Is it the case that the file contains legacy lines? + + + + The runtime status of the net.ipv6.conf.all.accept_redirects kernel parameter can be queried +by running the following command: +$ sysctl net.ipv6.conf.all.accept_redirects +0. + + Is it the case that the correct value is not returned? + + + + Check the system partitions to determine if they are encrypted with the following command: +blkid + +Output will be similar to: +/dev/sda1: UUID=" ab12c3de-4f56-789a-8f33-3850cc8ce3a2 +" TYPE="crypto_LUKS" +/dev/sda2: UUID=" bc98d7ef-6g54-321h-1d24-9870de2ge1a2 +" TYPE="crypto_LUKS" + +The boot partition and pseudo-file systems, such as /proc, /sys, and tmpfs, +are not required to use disk encryption and are not a finding. + Is it the case that partitions do not have a type of crypto_LUKS? + + + + Verify Red Hat Enterprise Linux 8 is configured to lock an account until released by an administrator +after unsuccessful logon +attempts with the command: + + +$ grep 'unlock_time =' /etc/security/faillock.conf +unlock_time = + Is it the case that the "unlock_time" option is not set to "<sub idref="var_accounts_passwords_pam_faillock_unlock_time" />", +the line is missing, or commented out? + + + + +If the system is configured to prevent the loading of the can kernel module, +it will contain lines inside any file in /etc/modprobe.d or the deprecated /etc/modprobe.conf. +These lines instruct the module loading system to run another program (such as /bin/true) upon a module install event. + +These lines can also instruct the module loading system to ignore the can kernel module via blacklist keyword. + +Run the following command to search for such lines in all files in /etc/modprobe.d and the deprecated /etc/modprobe.conf: +$ grep -r can /etc/modprobe.conf /etc/modprobe.d + Is it the case that no line is returned? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_DEBUG_LIST /boot/config.* + + For each kernel installed, a line with value "y" should be returned. + + Is it the case that the kernel was not built with the required value? + + + + The runtime status of the kernel.perf_event_paranoid kernel parameter can be queried +by running the following command: +$ sysctl kernel.perf_event_paranoid +2. + + Is it the case that the correct value is not returned? + + + + + + + + + combine_ovals.py from SCAP Security Guide + ssg: [0, 1, 64], python: 3.10.4 + 5.11 + 2022-09-30T15:09:59 + + + + + Alibaba Cloud Linux 2 + + Red Hat Enterprise Linux 8 + + + The operating system installed on the system is Alibaba Cloud Linux 2 + + + + + + + + + Alibaba Cloud Linux 3 + + Red Hat Enterprise Linux 8 + + + The operating system installed on the system is Alibaba Cloud Linux 3 + + + + + + + + + CentOS 7 + + Red Hat Enterprise Linux 8 + + + The operating system installed on the system is + CentOS 7 + + + + + + + + + CentOS 8 + + Red Hat Enterprise Linux 8 + + + The operating system installed on the system is + CentOS 8 + + + + + + + + + + CentOS Stream 9 + + Red Hat Enterprise Linux 8 + + + The operating system installed on the system is + CentOS Stream 9 + + + + + + + + + + Debian + + Red Hat Enterprise Linux 8 + + The operating system installed is a Debian System + + + + + + + + + Debian Linux 10 + + Red Hat Enterprise Linux 8 + + + The operating system installed on the system is Debian 10 + + + + + + + + + Debian Linux 11 + + Red Hat Enterprise Linux 8 + + + The operating system installed on the system is Debian 11 + + + + + + + + + Debian 9 + + Red Hat Enterprise Linux 8 + + + The operating system installed on the system is Debian 9 + + + + + + + + + Installed operating system is Fedora + + Red Hat Enterprise Linux 8 + + + + + + The operating system installed on the system is Fedora + + + + + + + + + + Oracle Linux 7 + + Red Hat Enterprise Linux 8 + + + The operating system installed on the system is + Oracle Linux 7 + + + + + + + + + + + Oracle Linux 8 + + Red Hat Enterprise Linux 8 + + + The operating system installed on the system is + Oracle Linux 8 + + + + + + + + + + + Oracle Linux 9 + + Red Hat Enterprise Linux 8 + + + The operating system installed on the system is + Oracle Linux 9 + + + + + + + + + + + openSUSE + + Red Hat Enterprise Linux 8 + + The operating system installed on the system is openSUSE. + + + + + + + + + openSUSE Leap 15 + + Red Hat Enterprise Linux 8 + + + The operating system installed on the system is openSUSE Leap 15. + + + + + + + + + openSUSE Leap 42 + + Red Hat Enterprise Linux 8 + + + + + The operating system installed on the system is openSUSE Leap 42. + + + + + + + + + Installed operating system is part of the Unix family + + Red Hat Enterprise Linux 8 + + The operating system installed on the system is part of the Unix OS family + + + + + + + + Red Hat Enterprise Linux CoreOS + + Red Hat Enterprise Linux 8 + + + The operating system installed on the system is + Red Hat Enterprise Linux CoreOS release 4 + + + + + + + + + + + Red Hat Enterprise Linux 7 + + Red Hat Enterprise Linux 8 + + + The operating system installed on the system is + Red Hat Enterprise Linux 7 + + + + + + + + + + + + + + + + + + Red Hat Enterprise Linux 8 + + Red Hat Enterprise Linux 8 + + + The operating system installed on the system is + Red Hat Enterprise Linux 8 + + + + + + + + + + + + + + + Red Hat Enterprise Linux 8.0 + + Red Hat Enterprise Linux 8 + + + The operating system installed on the system is Red Hat Enterprise Linux 8.0 + + + + + + + + Red Hat Enterprise Linux 8.1 + + Red Hat Enterprise Linux 8 + + + The operating system installed on the system is Red Hat Enterprise Linux 8.1 + + + + + + + + Red Hat Enterprise Linux 8.2 + + Red Hat Enterprise Linux 8 + + + The operating system installed on the system is Red Hat Enterprise Linux 8.2 + + + + + + + + Red Hat Enterprise Linux 8.3 + + Red Hat Enterprise Linux 8 + + + The operating system installed on the system is Red Hat Enterprise Linux 8.3 + + + + + + + + Red Hat Enterprise Linux 8.4 + + Red Hat Enterprise Linux 8 + + + The operating system installed on the system is Red Hat Enterprise Linux 8.4 + + + + + + + + Red Hat Enterprise Linux 8.5 + + Red Hat Enterprise Linux 8 + + + The operating system installed on the system is Red Hat Enterprise Linux 8.5 + + + + + + + + Red Hat Enterprise Linux 8.6 + + Red Hat Enterprise Linux 8 + + + The operating system installed on the system is Red Hat Enterprise Linux 8.6 + + + + + + + + Red Hat Enterprise Linux 8.7 + + Red Hat Enterprise Linux 8 + + + The operating system installed on the system is Red Hat Enterprise Linux 8.7 + + + + + + + + Red Hat Enterprise Linux 8.8 + + Red Hat Enterprise Linux 8 + + + The operating system installed on the system is Red Hat Enterprise Linux 8.8 + + + + + + + + Red Hat Enterprise Linux 8.9 + + Red Hat Enterprise Linux 8 + + + The operating system installed on the system is Red Hat Enterprise Linux 8.9 + + + + + + + + Red Hat Enterprise Linux 8.10 + + Red Hat Enterprise Linux 8 + + + The operating system installed on the system is Red Hat Enterprise Linux 8.10 + + + + + + + + Red Hat Enterprise Linux 9 + + Red Hat Enterprise Linux 8 + + + The operating system installed on the system is + Red Hat Enterprise Linux 9 + + + + + + + + + + + + + + + Red Hat Virtualization 4 + + Red Hat Enterprise Linux 8 + + + The operating system installed on the system is + Red Hat Virtualization Host 4.4+ or Red Hat Enterprise Host. + + + + + + + + + Scientific Linux 7 + + Red Hat Enterprise Linux 8 + + + The operating system installed on the system is + Scientific Linux 7 + + + + + + + + + SUSE Linux Enterprise 12 + + Red Hat Enterprise Linux 8 + + + + The operating system installed on the system is + SUSE Linux Enterprise 12. + + + + + + + + + + + + + SUSE Linux Enterprise 15 + + Red Hat Enterprise Linux 8 + + + + The operating system installed on the system is + SUSE Linux Enterprise 15. + + + + + + + + + + + + + Ubuntu + + Red Hat Enterprise Linux 8 + + The operating system installed is an Ubuntu System + + + + + + + + + + Ubuntu 1604 + + Red Hat Enterprise Linux 8 + + + The operating system installed on the system is Ubuntu 1604 + + + + + + + + + Ubuntu 1804 + + Red Hat Enterprise Linux 8 + + + The operating system installed on the system is Ubuntu 1804 + + + + + + + + + Ubuntu 2004 + + Red Hat Enterprise Linux 8 + + + The operating system installed on the system is Ubuntu 2004 + + + + + + + + + Ubuntu 2204 + + Red Hat Enterprise Linux 8 + + + The operating system installed on the system is Ubuntu 2204 + + + + + + + + + UnionTech OS Server 20 + + Red Hat Enterprise Linux 8 + + + The operating system installed on the system is UnionTech OS Server 20 + + + + + + + + + Red Hat Virtualization 4 + + Red Hat Enterprise Linux 8 + + + The application installed installed on the system is + Red Hat Virtualization 4. + + + + + + + + + Package audit is installed + + Red Hat Enterprise Linux 8 + + Checks if package audit is installed. + + + + + + + + + Package chrony is installed + + Red Hat Enterprise Linux 8 + + Checks if package chrony is installed. + + + + + + + + + Package gdm is installed + + Red Hat Enterprise Linux 8 + + Checks if package gdm is installed. + + + + + + + + + Package grub2 is installed + + Red Hat Enterprise Linux 8 + + Checks if package grub2-common is installed. + + + + + + + + + + + + + Package libuser is installed + + Red Hat Enterprise Linux 8 + + Checks if package libuser is installed. + + + + + + + + + Package providing /etc/login.defs is installed + + Red Hat Enterprise Linux 8 + + Checks if package providing /etc/login.defs and is installed. + + + + + + + + + Package net-snmp is installed + + Red Hat Enterprise Linux 8 + + Checks if package net-snmp is installed. + + + + + + + + + Check if the system doesn't act as an oVirt host or manager + + Red Hat Enterprise Linux 8 + + Check if the system has neither ovirt-host nor ovirt-engine installed. + + + + + + + + Package nss-pam-ldapd is installed + + Red Hat Enterprise Linux 8 + + Checks if package nss-pam-ldapd is installed. + + + + + + + + + Package ntp is installed + + Red Hat Enterprise Linux 8 + + Checks if package ntp is installed. + + + + + + + + + Check if the system acts as an oVirt host or manager + + Red Hat Enterprise Linux 8 + + Check if the system has ovirt-host or ovirt-engine installed + + + + + + + + + + Package pam is installed + + Red Hat Enterprise Linux 8 + + Checks if package pam is installed. + + + + + + + + + Package polkit is installed + + Red Hat Enterprise Linux 8 + + Checks if package polkit is installed. + + + + + + + + + Package postfix is installed + + Red Hat Enterprise Linux 8 + + Checks if package postfix is installed. + + + + + + + + + Package sssd-common is installed + + Red Hat Enterprise Linux 8 + + Checks if package sssd-common is installed. + + + + + + + + + Package sudo is installed + + Red Hat Enterprise Linux 8 + + Checks if package sudo is installed. + + + + + + + + + Package systemd is installed + + Red Hat Enterprise Linux 8 + + Checks if package systemd is installed. + + + + + + + + + Package tftp-server is installed + + Red Hat Enterprise Linux 8 + + Checks if package tftp-server is installed. + + + + + + + + + Package tmux is installed + + Red Hat Enterprise Linux 8 + + Checks if package tmux is installed. + + + + + + + + + Package usbguard is installed + + Red Hat Enterprise Linux 8 + + Checks if package usbguard is installed. + + + + + + + + + WiFi interface is present + + Red Hat Enterprise Linux 8 + + Checks if any wifi interface is present. + + + + + + + + + Package yum is installed + + Red Hat Enterprise Linux 8 + + Checks if package yum is installed. + + + + + + + + + System uses zIPL + + Red Hat Enterprise Linux 8 + + Checks if system uses zIPL bootloader. + + + + + + + + + Check if the scan target is a container + + Red Hat Enterprise Linux 8 + + Check for presence of files characterizing container filesystems. + + + + + + + + + + Check if the scan target is a machine + + Red Hat Enterprise Linux 8 + + Check for absence of files characterizing container filesystems. + + + + + + + + + Partition /tmp exists + + Red Hat Enterprise Linux 8 + + + + + + + + + + Partition /var/tmp exists + + Red Hat Enterprise Linux 8 + + + + + + + + + + Kerberos server is older than 1.17-18 + + Red Hat Enterprise Linux 8 + + + Check if version of Kerberos server is lesser than 1.17-18 + + + + + + + + + Kerberos workstation is older than 1.17-18 + + Red Hat Enterprise Linux 8 + + + Check if version of Kerberos workstation is lesser than 1.17-18 + + + + + + + + + Test that the architecture is aarch64 + + Red Hat Enterprise Linux 8 + + Check that architecture of kernel in /proc/sys/kernel/osrelease is aarch64 + + + + + + + + Test for different architecture than aarch64 + + Red Hat Enterprise Linux 8 + + Check that architecture of kernel in /proc/sys/kernel/osrelease is not aarch64 + + + + + + + + Test for different architecture than s390x + + Red Hat Enterprise Linux 8 + + Check that architecture of kernel in /proc/sys/kernel/osrelease is not s390x + + + + + + + + Test that the architecture is ppc64le + + Red Hat Enterprise Linux 8 + + Check that architecture of kernel in /proc/sys/kernel/osrelease is ppc64le + + + + + + + + Test that the architecture is s390x + + Red Hat Enterprise Linux 8 + + Check that architecture of kernel in /proc/sys/kernel/osrelease is s390x + + + + + + + + SSSD is configured to use LDAP + + Red Hat Enterprise Linux 8 + + Identification provider is not set to ad within /etc/sssd/sssd.conf + + + + + + + + + Non-UEFI system boot mode check + + Red Hat Enterprise Linux 8 + + Check if System boot mode is non-UEFI. + + + + + + + + + UEFI system boot mode check + + Red Hat Enterprise Linux 8 + + Check if system boot mode is UEFI. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + alinux-release + + + alinux-release + + + centos-release + + + /etc/os-release + ^ID="(\w+)"$ + 1 + + + /etc/os-release + ^VERSION_ID="(\d)"$ + 1 + + + /etc/os-release + ^ID="(\w+)"$ + 1 + + + /etc/os-release + ^VERSION_ID="(\d)"$ + 1 + + + /etc/debian_version + + + /etc/debian_version + ^10.[0-9]+$ + 1 + + + /etc/debian_version + ^11.[0-9]+$ + 1 + + + /etc/debian_version + ^9.[0-9]+$ + 1 + + + fedora-release.* + + + /etc/system-release-cpe + ^cpe:\/o:fedoraproject:fedora:[\d]+$ + 1 + + + oraclelinux-release + + + oraclelinux-release + + + oraclelinux-release + + + openSUSE-release + + + openSUSE-release + + + openSUSE-release + + + + /etc/os-release + ^ID="(\w+)"$ + 1 + + + /etc/os-release + ^VERSION_ID="(\d)\.\d+"$ + 1 + + + + redhat-release-client + + + redhat-release-workstation + + + redhat-release-server + + + redhat-release-computenode + + + /etc/redhat-release + ^Red Hat Enterprise Linux release (\d)\.\d+$ + 1 + + + + redhat-release + + + redhat-release + + + redhat-release + + + redhat-release + + + redhat-release + + + redhat-release + + + redhat-release + + + redhat-release + + + redhat-release + + + redhat-release + + + redhat-release + + + redhat-release + + + /etc/redhat-release + ^Red Hat Enterprise Linux release (\d)\.\d+$ + 1 + + + + redhat-release + + + /etc/redhat-release + ^Red Hat Enterprise Linux release (\d)\.\d+$ + 1 + + + redhat-release-virtualization-host + + + sl-release + + + + sled-release + + + sles-release + + + SLES_SAP-release + + + + sled-release + + + sles-release + + + SLES_SAP-release + + + /etc/lsb-release + + + /etc/lsb-release + ^DISTRIB_ID=Ubuntu$ + 1 + + + /etc/lsb-release + ^DISTRIB_CODENAME=xenial$ + 1 + + + /etc/lsb-release + ^DISTRIB_CODENAME=bionic$ + 1 + + + /etc/lsb-release + ^DISTRIB_CODENAME=focal$ + 1 + + + /etc/lsb-release + ^DISTRIB_CODENAME=jammy$ + 1 + + + uos-release + + + rhvm-appliance + + + audit + + + chrony + + + gdm + + + grub2-common + + + /sys/firmware/opal + + + libuser + + + shadow-utils + + + net-snmp + + + nss-pam-ldapd + + + ntp + + + ovirt-host + + + ovirt-engine + + + pam + + + polkit + + + postfix + + + sssd-common + + + sudo + + + systemd + + + tftp-server + + + tmux + + + usbguard + + + /proc/net/wireless + + + yum + + + s390utils-base + + + /.dockerenv + + + /run/.containerenv + + + /tmp + + + /var/tmp + + + krb5-server + + + krb5-workstation + + + /proc/sys/kernel/osrelease + ^.*\.(.*)$ + 1 + + + /proc/sys/kernel/osrelease + ^.*\.(.*)$ + 1 + + + /proc/sys/kernel/osrelease + ^.*\.(.*)$ + 1 + + + /etc/sssd/sssd.conf + ^[\s]*\[domain\/[^]]*]([^\n\[\]]*\n+)+?[\s]*id_provider[ \t]*=[ \t]*((?i)ad)[ \t]*$ + 1 + + + /sys/firmware/efi + + + + + + + ^2.*$ + + + ^3.*$ + + + ^7.*$ + + + centos + + + 8 + + + centos + + + 9 + + + ^7.*$ + + + ^8.*$ + + + ^9.*$ + + + openSUSE-release + + + ^15.*$ + + + ^42.*$ + + + unix + + + rhcos + + + 4 + + + unix + + + ^7.*$ + + + ^7.*$ + + + ^7.*$ + + + ^7.*$ + + + 7 + + + unix + + + ^8.*$ + + + ^8.0*$ + + + ^8.1*$ + + + ^8.2*$ + + + ^8.3*$ + + + ^8.4*$ + + + ^8.5*$ + + + ^8.6*$ + + + ^8.7*$ + + + ^8.8*$ + + + ^8.9*$ + + + ^8.10*$ + + + 8 + + + unix + + + ^9.*$ + + + 9 + + + 0:4.4 + + + ^7.*$ + + + unix + + + ^12.*$ + + + ^12.*$ + + + ^12.*$ + + + unix + + + ^15.*$ + + + ^15.*$ + + + ^15.*$ + + + ^20.*$ + + + ^4.*$ + + + 0:1.17-18 + + + 0:1.17-18 + + + ^aarch64$ + + + ^ppc64le$ + + + ^s390x$ + + + ppc64le + + + + + \ No newline at end of file diff --git a/roles/satellite_config/files/ssg-rhel9-ds-tailoring.xml b/roles/satellite_config/files/ssg-rhel9-ds-tailoring.xml new file mode 100644 index 0000000..aaad172 --- /dev/null +++ b/roles/satellite_config/files/ssg-rhel9-ds-tailoring.xml @@ -0,0 +1,161 @@ + + + + 1 + + [DRAFT] CIS Red Hat Enterprise Linux 9 Benchmark for Level 2 - Server [CUSTOMIZED] + This is a draft profile based on its RHEL8 version for experimental purposes. +It is not based on the CIS benchmark for RHEL9, because this one was not available at time of +the release. + + + + + + + + + + + + + + + + + + + + + + + + + + + + ^[\s\n]+\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*(?:[\n]+|(?:\\n)+)[\s\n]+The[\s\n]+credentials[\s\n]+you[\s\n]+used[\s\n]+to[\s\n]+access[\s\n]+this[\s\n]+information[\s\n]+system[\s\n]+\(IS\)[\s\n]+have(?:[\n]+|(?:\\n)+)[\s\n]+been[\s\n]+authenticated\.[\s\n]+By[\s\n]+remaining[\s\n]+connected,[\s\n]+you[\s\n]+declare[\s\n]+that[\s\n]+you[\s\n]+are(?:[\n]+|(?:\\n)+)[\s\n]+the[\s\n]+authorized[\s\n]+user[\s\n]+of[\s\n]+these[\s\n]+credentials[\s\n]+and[\s\n]+agree[\s\n]+to[\s\n]+the[\s\n]+terms[\s\n]+of(?:[\n]+|(?:\\n)+)[\s\n]+use[\s\n]+of[\s\n]+this[\s\n]+IS\.(?:[\n]+|(?:\\n)+)(?:[\n]+|(?:\\n)+)[\s\n]+All[\s\n]+activities[\s\n]+performed[\s\n]+on[\s\n]+this[\s\n]+device[\s\n]+are[\s\n]+logged[\s\n]+and[\s\n]+monitored\.(?:[\n]+|(?:\\n)+)(?:[\n]+|(?:\\n)+)[\s\n]+Unauthorized[\s\n]+attempts[\s\n]+to[\s\n]+elevate[\s\n]+privilege[\s\n]+or[\s\n]+attempts[\s\n]+to[\s\n]+circumvent(?:[\n]+|(?:\\n)+)[\s\n]+system[\s\n]+security[\s\n]+will[\s\n]+result[\s\n]+in[\s\n]+civil[\s\n]+and/or[\s\n]+criminal[\s\n]+penalties\.(?:[\n]+|(?:\\n)+)[\s\n]+\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*(?:[\n]+|(?:\\n)+)$ + 365 + 900 + time.example.ca,time2.example.ca,time.nrc.ca,time.chu.nrc.ca + 900 + sssd + + + [DRAFT] CIS Red Hat Enterprise Linux 9 Benchmark for Level 1 - Server [CUSTOMIZED] + This is a draft profile based on its RHEL8 version for experimental purposes. +It is not based on the CIS benchmark for RHEL9, because this one was not available at time of +the release. + + + + + + + + + + + sssd + ^[\s\n]+\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*(?:[\n]+|(?:\\n)+)[\s\n]+The[\s\n]+credentials[\s\n]+you[\s\n]+used[\s\n]+to[\s\n]+access[\s\n]+this[\s\n]+information[\s\n]+system[\s\n]+\(IS\)[\s\n]+have(?:[\n]+|(?:\\n)+)[\s\n]+been[\s\n]+authenticated\.[\s\n]+By[\s\n]+remaining[\s\n]+connected,[\s\n]+you[\s\n]+declare[\s\n]+that[\s\n]+you[\s\n]+are(?:[\n]+|(?:\\n)+)[\s\n]+the[\s\n]+authorized[\s\n]+user[\s\n]+of[\s\n]+these[\s\n]+credentials[\s\n]+and[\s\n]+agree[\s\n]+to[\s\n]+the[\s\n]+terms[\s\n]+of(?:[\n]+|(?:\\n)+)[\s\n]+use[\s\n]+of[\s\n]+this[\s\n]+IS\.(?:[\n]+|(?:\\n)+)(?:[\n]+|(?:\\n)+)[\s\n]+All[\s\n]+activities[\s\n]+performed[\s\n]+on[\s\n]+this[\s\n]+device[\s\n]+are[\s\n]+logged[\s\n]+and[\s\n]+monitored\.(?:[\n]+|(?:\\n)+)(?:[\n]+|(?:\\n)+)[\s\n]+Unauthorized[\s\n]+attempts[\s\n]+to[\s\n]+elevate[\s\n]+privilege[\s\n]+or[\s\n]+attempts[\s\n]+to[\s\n]+circumvent(?:[\n]+|(?:\\n)+)[\s\n]+system[\s\n]+security[\s\n]+will[\s\n]+result[\s\n]+in[\s\n]+civil[\s\n]+and/or[\s\n]+criminal[\s\n]+penalties\.(?:[\n]+|(?:\\n)+)[\s\n]+\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*(?:[\n]+|(?:\\n)+)$ + 900 + required + 4 + 14 + 365 + 900 + time.example.ca,time2.example.ca,time.nrc.ca,time.chu.nrc.ca + 900 + + + [DRAFT] DISA STIG for Red Hat Enterprise Linux 9 [CUSTOMIZED] + This is a draft profile based on its RHEL8 version for experimental purposes. +It is not based on the DISA STIG for RHEL9, because this one was not available at time of +the release. + +In addition to being applicable to Red Hat Enterprise Linux 9, DISA recognizes this +configuration baseline as applicable to the operating system tier of +Red Hat technologies that are based on Red Hat Enterprise Linux 9, such as: + +- Red Hat Enterprise Linux Server +- Red Hat Enterprise Linux Workstation and Desktop +- Red Hat Enterprise Linux for HPC +- Red Hat Storage +- Red Hat Containers with a Red Hat Enterprise Linux 9 image + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + FIPS + 1G + 0 + sssd + ^[\s\n]+\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*(?:[\n]+|(?:\\n)+)[\s\n]+The[\s\n]+credentials[\s\n]+you[\s\n]+used[\s\n]+to[\s\n]+access[\s\n]+this[\s\n]+information[\s\n]+system[\s\n]+\(IS\)[\s\n]+have(?:[\n]+|(?:\\n)+)[\s\n]+been[\s\n]+authenticated\.[\s\n]+By[\s\n]+remaining[\s\n]+connected,[\s\n]+you[\s\n]+declare[\s\n]+that[\s\n]+you[\s\n]+are(?:[\n]+|(?:\\n)+)[\s\n]+the[\s\n]+authorized[\s\n]+user[\s\n]+of[\s\n]+these[\s\n]+credentials[\s\n]+and[\s\n]+agree[\s\n]+to[\s\n]+the[\s\n]+terms[\s\n]+of(?:[\n]+|(?:\\n)+)[\s\n]+use[\s\n]+of[\s\n]+this[\s\n]+IS\.(?:[\n]+|(?:\\n)+)(?:[\n]+|(?:\\n)+)[\s\n]+All[\s\n]+activities[\s\n]+performed[\s\n]+on[\s\n]+this[\s\n]+device[\s\n]+are[\s\n]+logged[\s\n]+and[\s\n]+monitored\.(?:[\n]+|(?:\\n)+)(?:[\n]+|(?:\\n)+)[\s\n]+Unauthorized[\s\n]+attempts[\s\n]+to[\s\n]+elevate[\s\n]+privilege[\s\n]+or[\s\n]+attempts[\s\n]+to[\s\n]+circumvent(?:[\n]+|(?:\\n)+)[\s\n]+system[\s\n]+security[\s\n]+will[\s\n]+result[\s\n]+in[\s\n]+civil[\s\n]+and/or[\s\n]+criminal[\s\n]+penalties\.(?:[\n]+|(?:\\n)+)[\s\n]+\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*(?:[\n]+|(?:\\n)+)$ + required + 16 + time.example.ca,time2.example.ca,time.chu.nrc.ca + 1G + + \ No newline at end of file diff --git a/roles/satellite_config/files/ssg-rhel9-ds.xml b/roles/satellite_config/files/ssg-rhel9-ds.xml new file mode 100644 index 0000000..3f86b5a --- /dev/null +++ b/roles/satellite_config/files/ssg-rhel9-ds.xml @@ -0,0 +1,342678 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + System architecture is AARCH64 + oval:ssg-proc_sys_kernel_osrelease_arch_aarch64:def:1 + + + Package audit is installed + oval:ssg-installed_env_has_audit_package:def:1 + + + Package chrony is installed + oval:ssg-installed_env_has_chrony_package:def:1 + + + Package gdm is installed + oval:ssg-installed_env_has_gdm_package:def:1 + + + Package grub2 is installed + oval:ssg-installed_env_has_grub2_package:def:1 + + + Package libuser is installed + oval:ssg-installed_env_has_libuser_package:def:1 + + + Package providing /etc/login.defs is installed + oval:ssg-installed_env_has_login_defs:def:1 + + + Bare-metal or Virtual Machine + oval:ssg-installed_env_is_a_machine:def:1 + + + Package net-snmp is installed + oval:ssg-installed_env_has_net-snmp_package:def:1 + + + System boot mode is non-UEFI + oval:ssg-system_boot_mode_is_non_uefi:def:1 + + + System architecture is not AARCH64 + oval:ssg-proc_sys_kernel_osrelease_arch_not_aarch64:def:1 + + + System architecture is not S390X + oval:ssg-proc_sys_kernel_osrelease_arch_not_s390x:def:1 + + + Package ntp is installed + oval:ssg-installed_env_has_ntp_package:def:1 + + + Package pam is installed + oval:ssg-installed_env_has_pam_package:def:1 + + + There is a /tmp partition + oval:ssg-installed_env_mounts_tmp:def:1 + + + There is a /var/tmp partition + oval:ssg-installed_env_mounts_var_tmp:def:1 + + + Package polkit is installed + oval:ssg-installed_env_has_polkit_package:def:1 + + + Package postfix is installed + oval:ssg-installed_env_has_postfix_package:def:1 + + + System architecture is ppc64le + oval:ssg-proc_sys_kernel_osrelease_arch_ppc64le:def:1 + + + System architecture is S390X + oval:ssg-proc_sys_kernel_osrelease_arch_s390x:def:1 + + + Package sssd-common is installed + oval:ssg-installed_env_has_sssd-common_package:def:1 + + + Package sudo is installed + oval:ssg-installed_env_has_sudo_package:def:1 + + + Package systemd is installed + oval:ssg-installed_env_has_systemd_package:def:1 + + + Package tftp-server is installed + oval:ssg-installed_env_has_tftp_server_package:def:1 + + + Package tmux is installed + oval:ssg-installed_env_has_tmux_package:def:1 + + + System boot mode is UEFI + oval:ssg-system_boot_mode_is_uefi:def:1 + + + Package usbguard is installed + oval:ssg-installed_env_has_usbguard_package:def:1 + + + WiFi interface is present + oval:ssg-installed_env_has_wifi_interface:def:1 + + + Package yum is installed + oval:ssg-installed_env_has_yum_package:def:1 + + + Red Hat Enterprise Linux 9 + oval:ssg-installed_OS_is_rhel9:def:1 + + + + + + draft + Guide to the Secure Configuration of Red Hat Enterprise Linux 9 + This guide presents a catalog of security-relevant +configuration settings for Red Hat Enterprise Linux 9. It is a rendering of +content structured in the eXtensible Configuration Checklist Description Format (XCCDF) +in order to support security automation. The SCAP content is +is available in the scap-security-guide package which is developed at + + https://www.open-scap.org/security-policies/scap-security-guide. + +Providing system administrators with such guidance informs them how to securely +configure systems under their control in a variety of network roles. Policy +makers and baseline creators can use this catalog of settings, with its +associated references to higher-level security control catalogs, in order to +assist them in security baseline creation. This guide is a catalog, not a +checklist, and satisfaction of every item is not likely to be possible or +sensible in many operational scenarios. However, the XCCDF format enables +granular selection and adjustment of settings, and their association with OVAL +and OCIL content provides an automated checking capability. Transformations of +this document, and its associated automated checking content, are capable of +providing baselines that meet a diverse set of policy objectives. Some example +XCCDF Profiles, which are selections of items that form checklists and +can be used as baselines, are available with this guide. They can be +processed, in an automated fashion, with tools that support the Security +Content Automation Protocol (SCAP). The DISA STIG, which provides required +settings for US Department of Defense systems, is one example of a baseline +created from this guidance. + + Do not attempt to implement any of the settings in +this guide without first testing them in a non-operational environment. The +creators of this guidance assume no responsibility whatsoever for its use by +other parties, and makes no guarantees, expressed or implied, about its +quality, reliability, or any other characteristic. + + The SCAP Security Guide Project + + https://www.open-scap.org/security-policies/scap-security-guide + + Red Hat and Red Hat Enterprise Linux are either registered +trademarks or trademarks of Red Hat, Inc. in the United States and other +countries. All other names are registered trademarks or trademarks of their +respective companies. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0.1.64 + + SCAP Security Guide Project + SCAP Security Guide Project + Frank J Cameron (CAM1244) <cameron@ctc.com> + 0x66656c6978 <0x66656c6978@users.noreply.github.com> + Håvard F. Aasen <havard.f.aasen@pfft.no> + Jack Adolph <jack.adolph@gmail.com> + Edgar Aguilar <edgar.aguilar@oracle.com> + Gabe Alford <redhatrises@gmail.com> + Firas AlShafei <firas.alshafei@us.abb.com> + Rodrigo Alvares <ralvares@redhat.com> + Christopher Anderson <cba@fedoraproject.org> + angystardust <angystardust@users.noreply.github.com> + anivan-suse <anastasija.ivanovic@suse.com> + anixon-rh <55244503+anixon-rh@users.noreply.github.com> + Ikko Ashimine <eltociear@gmail.com> + Chuck Atkins <chuck.atkins@kitware.com> + ayfantis <ayfantis@localhost.localdomain> + Ryan Ballanger <root@rballang-admin-2.fastenal.com> + Alex Baranowski <alex@euro-linux.com> + Eduardo Barretto <eduardo.barretto@canonical.com> + Molly Jo Bault <Molly.Jo.Bault@ballardtech.com> + Andrew Becker <A-Beck@users.noreply.github.com> + Gabriel Becker <ggasparb@redhat.com> + Alexander Bergmann <abergmann@suse.com> + Dale Bewley <dale@bewley.net> + Jose Luis BG <bgjoseluis@gmail.com> + binyanling <binyanling@uniontech.com> + Joseph Bisch <joseph.bisch@gmail.com> + Jeffrey Blank <blank@eclipse.ncsc.mil> + Olivier Bonhomme <ptitoliv@ptitoliv.net> + Lance Bragstad <lbragstad@gmail.com> + Ted Brunell <tbrunell@redhat.com> + Marcus Burghardt <maburgha@redhat.com> + Matthew Burket <mburket@redhat.com> + Blake Burkhart <blake.burkhart@us.af.mil> + Patrick Callahan <pmc@patrickcallahan.com> + George Campbell <gcampbell@palantir.com> + Nick Carboni <ncarboni@redhat.com> + Carlos <64919342+carlosmmatos@users.noreply.github.com> + James Cassell <james.cassell@ll.mit.edu> + Frank Caviggia <fcaviggi@ra.iad.redhat.com> + Eric Christensen <echriste@redhat.com> + Dan Clark <danclark@redhat.com> + Jayson Cofell <1051437+70k10@users.noreply.github.com> + Caleb Cooper <coopercd@ornl.gov> + Richard Maciel Costa <richard.maciel.costa@canonical.com> + Deric Crago <deric.crago@gmail.com> + crleekwc <crleekwc@gmail.com> + cyarbrough76 <42849651+cyarbrough76@users.noreply.github.com> + Maura Dailey <maura@eclipse.ncsc.mil> + Klaas Demter <demter@atix.de> + dhanushkar-wso2 <dhanushkar@wso2.com> + Andrew DiPrinzio <andrew.diprinzio@jhuapl.edu> + dom <dominique.blaze@devinci.fr> + Jean-Baptiste Donnette <jean-baptiste.donnette@epita.fr> + Marco De Donno <mdedonno1337@gmail.com> + dperrone <dperrone@redhat.com> + drax <applezip@gmail.com> + Sebastian Dunne <sdunne@redhat.com> + François Duthilleul <francoisduthilleul@gmail.com> + Greg Elin <gregelin@gitmachines.com> + eradot4027 <jrtonmac@gmail.com> + Alexis Facques <alexis.facques@mythalesgroup.io> + Leah Fisher <lfisher047@gmail.com> + Yavor Georgiev <strandjata@gmail.com> + Alijohn Ghassemlouei <alijohn@secureagc.com> + Swarup Ghosh <swghosh@redhat.com> + ghylock <ghylock@gmail.com> + Andrew Gilmore <agilmore2@gmail.com> + Joshua Glemza <jglemza@nasa.gov> + Nick Gompper <forestgomp@yahoo.com> + Loren Gordon <lorengordon@users.noreply.github.com> + Patrik Greco <sikevux@sikevux.se> + Steve Grubb <sgrubb@redhat.com> + guangyee <gyee@suse.com> + Marek Haicman <mhaicman@redhat.com> + Vern Hart <vern.hart@canonical.com> + Alex Haydock <alex@alexhaydock.co.uk> + Rebekah Hayes <rhayes@corp.rivierautilities.com> + Trey Henefield <thenefield@gmail.com> + Henning Henkel <henning.henkel@helvetia.ch> + hex2a <hex2a@users.noreply.github.com> + John Hooks <jhooks@starscream.pa.jhbcomputers.com> + Jakub Hrozek <jhrozek@redhat.com> + De Huo <De.Huo@windriver.com> + Robin Price II <robin@redhat.com> + Yasir Imam <yimam@redhat.com> + Jiri Jaburek <jjaburek@redhat.com> + Keith Jackson <keithkjackson@gmail.com> + Jeremiah Jahn <jeremiah@goodinassociates.com> + Jakub Jelen <jjelen@redhat.com> + Jessicahfy <Jessicahfy@users.noreply.github.com> + Stephan Joerrens <Stephan.Joerrens@fiduciagad.de> + Hunter Jones <hjones2199@gmail.com> + Jono <jono@ubuntu-18.localdomain> + justchris1 <justchris1@justchris1.email> + Kai Kang <kai.kang@windriver.com> + Charles Kernstock <charles.kernstock@ultra-ats.com> + Yuli Khodorkovskiy <ykhodorkovskiy@tresys.com> + Sherine Khoury <skhoury@redhat.com> + Nathan Kinder <nkinder@redhat.com> + Lee Kinser <lee.kinser@gmail.com> + Evgeny Kolesnikov <ekolesni@redhat.com> + Peter 'Pessoft' Kolínek <github@pessoft.com> + Luke Kordell <luke.t.kordell@lmco.com> + Malte Kraus <malte.kraus@suse.com> + Seth Kress <seth.kress@dsainc.com> + Felix Krohn <felix.krohn@helvetia.ch> + kspargur <kspargur@kspargur.csb> + Amit Kumar <amitkuma@redhat.com> + Fen Labalme <fen@civicactions.com> + Ade Lee <alee@redhat.com> + Christopher Lee <Crleekwc@gmail.com> + Ian Lee <lee1001@llnl.gov> + Jarrett Lee <jarrettl@umd.edu> + Joseph Lenox <joseph.lenox@collins.com> + Jan Lieskovsky <jlieskov@redhat.com> + Markus Linnala <Markus.Linnala@knowit.fi> + Flos Lonicerae <lonicerae@gmail.com> + Šimon Lukašík <slukasik@redhat.com> + Milan Lysonek <mlysonek@redhat.com> + Fredrik Lysén <fredrik@pipemore.se> + Caitlin Macleod <caitelatte@gmail.com> + Nick Maludy <nmaludy@gmail.com> + Lokesh Mandvekar <lsm5@fedoraproject.org> + Matus Marhefka <mmarhefk@redhat.com> + Jamie Lorwey Martin <jlmartin@redhat.com> + Carlos Matos <cmatos@redhat.com> + Robert McAllister <rmcallis@redhat.com> + Karen McCarron <kmccarro@redhat.com> + Michael McConachie <michael@redhat.com> + Marcus Meissner <meissner@suse.de> + Khary Mendez <kmendez@redhat.com> + Rodney Mercer <rmercer@harris.com> + mgjadoul <mgjadoul@laptomatic.auth-o-matic.corp> + Matt Micene <nzwulfin@gmail.com> + Brian Millett <bmillett@gmail.com> + Takuya Mishina <tmishina@jp.ibm.com> + Mixer9 <35545791+Mixer9@users.noreply.github.com> + mmosel <mmosel@kde.example.com> + Zbynek Moravec <zmoravec@redhat.com> + Kazuo Moriwaka <moriwaka@users.noreply.github.com> + Michael Moseley <michael@eclipse.ncsc.mil> + Renaud Métrich <rmetrich@redhat.com> + Joe Nall <joe@nall.com> + Neiloy <neiloy@redhat.com> + Axel Nennker <axel@nennker.de> + Michele Newman <mnewman@redhat.com> + Sean O'Keeffe <seanokeeffe797@gmail.com> + Jiri Odehnal <jodehnal@redhat.com> + Ilya Okomin <ilya.okomin@oracle.com> + Kaustubh Padegaonkar <theTuxRacer@gmail.com> + Michael Palmiotto <mpalmiotto@tresys.com> + Eryx Paredes <eryxp@lyft.com> + Max R.D. Parmer <maxp@trystero.is> + Arnaud Patard <apatard@hupstream.com> + Jan Pazdziora <jpazdziora@redhat.com> + pcactr <paul.c.arnold4.ctr@mail.mil> + Kenneth Peeples <kennethwpeeples@gmail.com> + Nathan Peters <Nathaniel.Peters@ca.com> + Frank Lin PIAT <fpiat@klabs.be> + Stefan Pietsch <mail.ipv4v6+gh@gmail.com> + piggyvenus <piggyvenus@gmail.com> + Vojtech Polasek <vpolasek@redhat.com> + Orion Poplawski <orion@nwra.com> + Nick Poyant <npoyant@redhat.com> + Martin Preisler <mpreisle@redhat.com> + Wesley Ceraso Prudencio <wcerasop@redhat.com> + Raphael Sanchez Prudencio <rsprudencio@redhat.com> + T.O. Radzy Radzykewycz <radzy@windriver.com> + Kenyon Ralph <kenyon@kenyonralph.com> + Mike Ralph <mralph@redhat.com> + Federico Ramirez <federico.r.ramirez@oracle.com> + rchikov <rumen.chikov@suse.com> + Rick Renshaw <Richard_Renshaw@xtoenergy.com> + Chris Reynolds <c.reynolds82@gmail.com> + rhayes <rhayes@rivierautilities.com> + Pat Riehecky <riehecky@fnal.gov> + rlucente-se-jboss <rlucente@redhat.com> + Juan Antonio Osorio Robles <juan.osoriorobles@eu.equinix.com> + Matt Rogers <mrogers@redhat.com> + Jesse Roland <jesse.roland@onyxpoint.com> + Joshua Roys <roysjosh@gmail.com> + rrenshaw <bofh69@yahoo.com> + Chris Ruffalo <chris.ruffalo@gmail.com> + rumch-se <77793453+rumch-se@users.noreply.github.com> + Ray Shaw (Cont ARL/CISD) rvshaw <rvshaw@esme.arl.army.mil> + Earl Sampson <ESampson@suse.com> + sampsone <esampson@suse.com> + Willy Santos <wsantos@redhat.com> + Nagarjuna Sarvepalli <snagarju@redhat.com> + Anderson Sasaki <33833274+ansasaki@users.noreply.github.com> + Gautam Satish <gautams@hpe.com> + Watson Sato <wsato@redhat.com> + Satoru SATOH <satoru.satoh@gmail.com> + Alexander Scheel <ascheel@redhat.com> + Bryan Schneiders <pschneiders@trisept.com> + shaneboulden <shane.boulden@gmail.com> + Vincent Shen <47534281+Vincent056@users.noreply.github.com> + Dhriti Shikhar <dhriti.shikhar.rokz@gmail.com> + Spencer Shimko <sshimko@tresys.com> + Mark Shoger <mshoger@redhat.com> + THOBY Simon <Simon.THOBY@viveris.fr> + Thomas Sjögren <konstruktoid@users.noreply.github.com> + Francisco Slavin <fslavin@tresys.com> + David Smith <dsmith@eclipse.ncsc.mil> + Kevin Spargur <kspargur@redhat.com> + Kenneth Stailey <kstailey.lists@gmail.com> + Leland Steinke <leland.j.steinke.ctr@mail.mil> + Justin Stephenson <jstephen@redhat.com> + Brian Stinson <brian@bstinson.com> + Jake Stookey <jakestookey@gmail.com> + Jonathan Sturges <jsturges@redhat.com> + Ian Tewksbury <itewk@redhat.com> + Philippe Thierry <phil@reseau-libre.net> + Derek Thurston <thegrit@gmail.com> + tianzhenjia <jiatianzhen@cmss.chinamobile.com> + Greg Tinsley <gtinsley@redhat.com> + Paul Tittle <ptittle@cmf.nrl.navy.mil> + tom <tom@localhost.localdomain> + tomas.hudik <tomas.hudik@embedit.cz> + Jeb Trayer <jeb.d.trayer@uscg.mil> + TrilokGeer <tgeer@redhat.com> + Viktors Trubovics <viktors.trubovics@suse.com> + Nico Truzzolino <nico.truzzolino@gmx.de> + Brian Turek <brian.turek@gmail.com> + Matěj Týč <matyc@redhat.com> + VadimDor <29509093+VadimDor@users.noreply.github.com> + Trevor Vaughan <tvaughan@onyxpoint.com> + vtrubovics <82443408+vtrubovics@users.noreply.github.com> + Samuel Warren <swarren@redhat.com> + wcushen <54533890+wcushen@users.noreply.github.com> + Shawn Wells <shawn@shawndwells.io> + Daniel E. White <linuxdan@users.noreply.github.com> + Bernhard M. Wiedemann <bwiedemann@suse.de> + Roy Williams <roywilli@roywilli.redhat.com> + Willumpie <willumpie@xs4all.nl> + Rob Wilmoth <rwilmoth@redhat.com> + Lucas Yamanishi <lucas.yamanishi@onyxpoint.com> + Xirui Yang <xirui.yang@oracle.com> + yarunachalam <yarunachalam@suse.com> + Guang Yee <guang.yee@suse.com> + Achilleas John Yfantis <ayfantis@redhat.com> + YiLin.Li <YiLin.Li@linux.alibaba.com> + YuQing <yyq0391@163.com> + Kevin Zimmerman <kevin.zimmerman@kitware.com> + Luigi Mario Zuccarelli <luzuccar@redhat.com> + Jan Černý <jcerny@redhat.com> + Michal Šrubař <msrubar@redhat.com> + https://github.com/ComplianceAsCode/content/releases/latest + + + ANSSI-BP-028 (enhanced) + This profile contains configurations that align to ANSSI-BP-028 at the enhanced hardening level. + +ANSSI is the French National Information Security Agency, and stands for Agence nationale de la sécurité des systèmes d'information. +ANSSI-BP-028 is a configuration recommendation for GNU/Linux systems. + +A copy of the ANSSI-BP-028 can be found at the ANSSI website: +https://www.ssi.gouv.fr/administration/guide/recommandations-de-securite-relatives-a-un-systeme-gnulinux/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ANSSI-BP-028 (high) + This profile contains configurations that align to ANSSI-BP-028 at the high hardening level. + +ANSSI is the French National Information Security Agency, and stands for Agence nationale de la sécurité des systèmes d'information. +ANSSI-BP-028 is a configuration recommendation for GNU/Linux systems. + +A copy of the ANSSI-BP-028 can be found at the ANSSI website: +https://www.ssi.gouv.fr/administration/guide/recommandations-de-securite-relatives-a-un-systeme-gnulinux/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ANSSI-BP-028 (intermediary) + This profile contains configurations that align to ANSSI-BP-028 at the intermediary hardening level. + +ANSSI is the French National Information Security Agency, and stands for Agence nationale de la sécurité des systèmes d'information. +ANSSI-BP-028 is a configuration recommendation for GNU/Linux systems. + +A copy of the ANSSI-BP-028 can be found at the ANSSI website: +https://www.ssi.gouv.fr/administration/guide/recommandations-de-securite-relatives-a-un-systeme-gnulinux/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ANSSI-BP-028 (minimal) + This profile contains configurations that align to ANSSI-BP-028 at the minimal hardening level. + +ANSSI is the French National Information Security Agency, and stands for Agence nationale de la sécurité des systèmes d'information. +ANSSI-BP-028 is a configuration recommendation for GNU/Linux systems. + +A copy of the ANSSI-BP-028 can be found at the ANSSI website: +https://www.ssi.gouv.fr/administration/guide/recommandations-de-securite-relatives-a-un-systeme-gnulinux/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + [DRAFT] CIS Red Hat Enterprise Linux 9 Benchmark for Level 2 - Server + This is a draft profile based on its RHEL8 version for experimental purposes. +It is not based on the CIS benchmark for RHEL9, because this one was not available at time of +the release. + https://www.cisecurity.org/benchmark/red_hat_linux/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + [DRAFT] CIS Red Hat Enterprise Linux 9 Benchmark for Level 1 - Server + This is a draft profile based on its RHEL8 version for experimental purposes. +It is not based on the CIS benchmark for RHEL9, because this one was not available at time of +the release. + https://www.cisecurity.org/benchmark/red_hat_linux/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + [DRAFT] CIS Red Hat Enterprise Linux 9 Benchmark for Level 1 - Workstation + This is a draft profile based on its RHEL8 version for experimental purposes. +It is not based on the CIS benchmark for RHEL9, because this one was not available at time of +the release. + https://www.cisecurity.org/benchmark/red_hat_linux/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + [DRAFT] CIS Red Hat Enterprise Linux 9 Benchmark for Level 2 - Workstation + This is a draft profile based on its RHEL8 version for experimental purposes. +It is not based on the CIS benchmark for RHEL9, because this one was not available at time of +the release. + https://www.cisecurity.org/benchmark/red_hat_linux/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + [DRAFT] Unclassified Information in Non-federal Information Systems and Organizations (NIST 800-171) + From NIST 800-171, Section 2.2: +Security requirements for protecting the confidentiality of CUI in nonfederal +information systems and organizations have a well-defined structure that +consists of: + +(i) a basic security requirements section; +(ii) a derived security requirements section. + +The basic security requirements are obtained from FIPS Publication 200, which +provides the high-level and fundamental security requirements for federal +information and information systems. The derived security requirements, which +supplement the basic security requirements, are taken from the security controls +in NIST Special Publication 800-53. + +This profile configures Red Hat Enterprise Linux 9 to the NIST Special +Publication 800-53 controls identified for securing Controlled Unclassified +Information (CUI)." + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Australian Cyber Security Centre (ACSC) Essential Eight + This profile contains configuration checks for Red Hat Enterprise Linux 9 +that align to the Australian Cyber Security Centre (ACSC) Essential Eight. + +A copy of the Essential Eight in Linux Environments guide can be found at the +ACSC website: + +https://www.cyber.gov.au/acsc/view-all-content/publications/hardening-linux-workstations-and-servers + https://www.cyber.gov.au/acsc/view-all-content/publications/hardening-linux-workstations-and-servers + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Health Insurance Portability and Accountability Act (HIPAA) + The HIPAA Security Rule establishes U.S. national standards to protect individuals’ +electronic personal health information that is created, received, used, or +maintained by a covered entity. The Security Rule requires appropriate +administrative, physical and technical safeguards to ensure the +confidentiality, integrity, and security of electronic protected health +information. + +This profile configures Red Hat Enterprise Linux 9 to the HIPAA Security +Rule identified for securing of electronic protected health information. +Use of this profile in no way guarantees or makes claims against legal compliance against the HIPAA Security Rule(s). + https://www.hhs.gov/hipaa/for-professionals/index.html + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Australian Cyber Security Centre (ACSC) ISM Official + This profile contains configuration checks for Red Hat Enterprise Linux 9 +that align to the Australian Cyber Security Centre (ACSC) Information Security Manual (ISM) +with the applicability marking of OFFICIAL. + +The ISM uses a risk-based approach to cyber security. This profile provides a guide to aligning +Red Hat Enterprise Linux security controls with the ISM, which can be used to select controls +specific to an organisation's security posture and risk profile. + +A copy of the ISM can be found at the ACSC website: + +https://www.cyber.gov.au/ism + https://www.cyber.gov.au/ism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Protection Profile for General Purpose Operating Systems + This profile is part of Red Hat Enterprise Linux 9 Common Criteria Guidance +documentation for Target of Evaluation based on Protection Profile for +General Purpose Operating Systems (OSPP) version 4.2.1 and Functional +Package for SSH version 1.0. + +Where appropriate, CNSSI 1253 or DoD-specific values are used for +configuration, based on Configuration Annex to the OSPP. + https://www.niap-ccevs.org/Profile/Info.cfm?PPID=442&id=442 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + PCI-DSS v3.2.1 Control Baseline for Red Hat Enterprise Linux 9 + Ensures PCI-DSS v3.2.1 security configuration settings are applied. + https://www.pcisecuritystandards.org/documents/PCI_DSS_v3-2-1.pdf + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + [DRAFT] DISA STIG for Red Hat Enterprise Linux 9 + This is a draft profile based on its RHEL8 version for experimental purposes. +It is not based on the DISA STIG for RHEL9, because this one was not available at time of +the release. + +In addition to being applicable to Red Hat Enterprise Linux 9, DISA recognizes this +configuration baseline as applicable to the operating system tier of +Red Hat technologies that are based on Red Hat Enterprise Linux 9, such as: + +- Red Hat Enterprise Linux Server +- Red Hat Enterprise Linux Workstation and Desktop +- Red Hat Enterprise Linux for HPC +- Red Hat Storage +- Red Hat Containers with a Red Hat Enterprise Linux 9 image + https://public.cyber.mil/stigs/downloads/?_dl_facet_stigs=operating-systems%2Cunix-linux + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + [DRAFT] DISA STIG with GUI for Red Hat Enterprise Linux 9 + This is a draft profile based on its RHEL8 version for experimental purposes. +It is not based on the DISA STIG for RHEL9, because this one was not available at time of +the release. + +In addition to being applicable to Red Hat Enterprise Linux 9, DISA recognizes this +configuration baseline as applicable to the operating system tier of +Red Hat technologies that are based on Red Hat Enterprise Linux 9, such as: + +- Red Hat Enterprise Linux Server +- Red Hat Enterprise Linux Workstation and Desktop +- Red Hat Enterprise Linux for HPC +- Red Hat Storage +- Red Hat Containers with a Red Hat Enterprise Linux 9 image + +Warning: The installation and use of a Graphical User Interface (GUI) +increases your attack vector and decreases your overall security posture. If +your Information Systems Security Officer (ISSO) lacks a documented operational +requirement for a graphical user interface, please consider using the +standard DISA STIG for Red Hat Enterprise Linux 9 profile. + https://public.cyber.mil/stigs/downloads/?_dl_facet_stigs=operating-systems%2Cunix-linux + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + System Settings + Contains rules that check correct system settings. + + Installing and Maintaining Software + The following sections contain information on +security-relevant choices during the initial operating system +installation process and the setup of software +updates. + + Prefer to use a 64-bit Operating System when supported + Prefer installation of 64-bit operating systems when the CPU supports it. + There is no remediation besides installing a 64-bit operating system. + BP28(R10) + Use of a 64-bit operating system offers a few advantages, like a larger address space range for +Address Space Layout Randomization (ASLR) and systematic presence of No eXecute and Execute Disable (NX/XD) protection bits. + CCE-90839-2 + + + + + + + + + System and Software Integrity + System and software integrity can be gained by installing antivirus, increasing +system encryption strength with FIPS, verifying installed software, enabling SELinux, +installing an Intrusion Prevention System, etc. However, installing or enabling integrity +checking tools cannot prevent intrusions, but they can detect that an intrusion +may have occurred. Requirements for integrity checking may be highly dependent on +the environment in which the system will be used. Snapshot-based approaches such +as AIDE may induce considerable overhead in the presence of frequent software updates. + + Software Integrity Checking + Both the AIDE (Advanced Intrusion Detection Environment) +software and the RPM package management system provide +mechanisms for verifying the integrity of installed software. +AIDE uses snapshots of file metadata (such as hashes) and compares these +to current system files in order to detect changes. + +The RPM package management system can conduct integrity +checks by comparing information in its metadata database with +files installed on the system. + + Integrity Scan Notification Email Address + Specify the email address for designated personnel if baseline +configurations are changed in an unauthorized manner. + root@localhost + + + Verify Integrity with RPM + The RPM package management system includes the ability +to verify the integrity of installed packages by comparing the +installed files with information about the files taken from the +package metadata stored in the RPM database. Although an attacker +could corrupt the RPM database (analogous to attacking the AIDE +database as described above), this check can still reveal +modification of important files. To list which files on the system differ from what is expected by the RPM database: +$ rpm -qVa +See the man page for rpm to see a complete explanation of each column. + + Verify File Hashes with RPM + Without cryptographic integrity protections, system +executables and files can be altered by unauthorized users without +detection. +The RPM package management system can check the hashes of +installed software packages, including many that are important to system +security. +To verify that the cryptographic hash of system files and commands matches vendor +values, run the following command to list which files on the system +have hashes that differ from what is expected by the RPM database: +$ rpm -Va --noconfig | grep '^..5' +A "c" in the second column indicates that a file is a configuration file, which +may appropriately be expected to change. If the file was not expected to +change, investigate the cause of the change using audit logs or other means. +The package can then be reinstalled to restore the file. +Run the following command to determine which package owns the file: +$ rpm -qf FILENAME +The package can be reinstalled from a dnf repository using the command: +$ sudo dnf reinstall PACKAGENAME +Alternatively, the package can be reinstalled from trusted media using the command: +$ sudo rpm -Uvh PACKAGENAME + 11 + 2 + 3 + 9 + 5.10.4.1 + APO01.06 + BAI03.05 + BAI06.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS06.02 + 3.3.8 + 3.4.1 + CCI-000366 + CCI-001749 + 164.308(a)(1)(ii)(D) + 164.312(b) + 164.312(c)(1) + 164.312(c)(2) + 164.312(e)(2)(i) + 4.3.4.3.2 + 4.3.4.3.3 + 4.3.4.4.4 + SR 3.1 + SR 3.3 + SR 3.4 + SR 3.8 + SR 7.6 + A.11.2.4 + A.12.1.2 + A.12.2.1 + A.12.5.1 + A.12.6.2 + A.14.1.2 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + CM-6(d) + CM-6(c) + SI-7 + SI-7(1) + SI-7(6) + AU-9(3) + PR.DS-6 + PR.DS-8 + PR.IP-1 + Req-11.5 + SRG-OS-000480-GPOS-00227 + The hashes of important files like system executables should match the +information given by the RPM database. Executables with erroneous hashes could +be a sign of nefarious activity on the system. + CCE-90841-8 + +# Find which files have incorrect hash (not in /etc, because of the system related config files) and then get files names +files_with_incorrect_hash="$(rpm -Va --noconfig | grep -E '^..5' | awk '{print $NF}' )" + +# From files names get package names and change newline to space, because rpm writes each package to new line +packages_to_reinstall="$(rpm -qf $files_with_incorrect_hash | tr '\n' ' ')" + +dnf reinstall -y $packages_to_reinstall + + + + + + + + + + Verify and Correct Ownership with RPM + The RPM package management system can check file ownership +permissions of installed software packages, including many that are +important to system security. After locating a file with incorrect +permissions, which can be found with +rpm -Va | awk '{ if (substr($0,6,1)=="U" || substr($0,7,1)=="G") print $NF }' +run the following command to determine which package owns it: +$ rpm -qf FILENAME +Next, run the following command to reset its permissions to +the correct values: +$ sudo rpm --setugids PACKAGENAME + Profiles may require that specific files be owned by root while the default owner defined +by the vendor is different. +Such files will be reported as a finding and need to be evaluated according to your policy +and deployment environment. + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + 6 + 9 + 5.10.4.1 + APO01.06 + APO11.04 + BAI03.05 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.04 + DSS05.07 + DSS06.02 + MEA02.01 + 3.3.8 + 3.4.1 + CCI-001494 + CCI-001496 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.7.3 + 4.3.4.3.2 + 4.3.4.3.3 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.1 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 5.2 + SR 7.6 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.12.1.2 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.5.1 + A.12.6.2 + A.12.7.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R4.2 + CIP-003-8 R6 + CIP-007-3 R4 + CIP-007-3 R4.1 + CIP-007-3 R4.2 + CM-6(d) + CM-6(c) + SI-7 + SI-7(1) + SI-7(6) + AU-9(3) + PR.AC-4 + PR.DS-5 + PR.IP-1 + PR.PT-1 + Req-11.5 + SRG-OS-000256-GPOS-00097 + SRG-OS-000257-GPOS-00098 + SRG-OS-000278-GPOS-00108 + Ownership of binaries and configuration files that is incorrect +could allow an unauthorized user to gain privileges that they should +not have. The ownership set by the vendor should be maintained. Any +deviations from this baseline should be investigated. + CCE-90842-6 + - name: Read list of files with incorrect ownership + command: rpm -Va --nodeps --nosignature --nofiledigest --nosize --nomtime --nordev + --nocaps --nolinkto --nomode + args: + warn: false + register: files_with_incorrect_ownership + failed_when: files_with_incorrect_ownership.rc > 1 + changed_when: false + check_mode: false + tags: + - CCE-90842-6 + - CJIS-5.10.4.1 + - NIST-800-171-3.3.8 + - NIST-800-171-3.4.1 + - NIST-800-53-AU-9(3) + - NIST-800-53-CM-6(c) + - NIST-800-53-CM-6(d) + - NIST-800-53-SI-7 + - NIST-800-53-SI-7(1) + - NIST-800-53-SI-7(6) + - PCI-DSS-Req-11.5 + - high_complexity + - high_severity + - medium_disruption + - no_reboot_needed + - restrict_strategy + - rpm_verify_ownership + +- name: Create list of packages + command: rpm -qf "{{ item }}" + args: + warn: false + with_items: '{{ files_with_incorrect_ownership.stdout_lines | map(''regex_findall'', + ''^[.]+[U|G]+.* (\/.*)'', ''\1'') | map(''join'') | select(''match'', ''(\/.*)'') + | list | unique }}' + register: list_of_packages + changed_when: false + check_mode: false + when: (files_with_incorrect_ownership.stdout_lines | length > 0) + tags: + - CCE-90842-6 + - CJIS-5.10.4.1 + - NIST-800-171-3.3.8 + - NIST-800-171-3.4.1 + - NIST-800-53-AU-9(3) + - NIST-800-53-CM-6(c) + - NIST-800-53-CM-6(d) + - NIST-800-53-SI-7 + - NIST-800-53-SI-7(1) + - NIST-800-53-SI-7(6) + - PCI-DSS-Req-11.5 + - high_complexity + - high_severity + - medium_disruption + - no_reboot_needed + - restrict_strategy + - rpm_verify_ownership + +- name: Correct file ownership with RPM + command: rpm --quiet --setugids '{{ item }}' + args: + warn: false + with_items: '{{ list_of_packages.results | map(attribute=''stdout_lines'') | list + | unique }}' + when: (files_with_incorrect_ownership.stdout_lines | length > 0) + tags: + - CCE-90842-6 + - CJIS-5.10.4.1 + - NIST-800-171-3.3.8 + - NIST-800-171-3.4.1 + - NIST-800-53-AU-9(3) + - NIST-800-53-CM-6(c) + - NIST-800-53-CM-6(d) + - NIST-800-53-SI-7 + - NIST-800-53-SI-7(1) + - NIST-800-53-SI-7(6) + - PCI-DSS-Req-11.5 + - high_complexity + - high_severity + - medium_disruption + - no_reboot_needed + - restrict_strategy + - rpm_verify_ownership + + +# Declare array to hold set of RPM packages we need to correct permissions for +declare -A SETPERMS_RPM_DICT + +# Create a list of files on the system having permissions different from what +# is expected by the RPM database +readarray -t FILES_WITH_INCORRECT_PERMS < <(rpm -Va --nofiledigest | awk '{ if (substr($0,6,1)=="U" || substr($0,7,1)=="G") print $NF }') + +for FILE_PATH in "${FILES_WITH_INCORRECT_PERMS[@]}" +do + RPM_PACKAGE=$(rpm -qf "$FILE_PATH") + # Use an associative array to store packages as it's keys, not having to care about duplicates. + SETPERMS_RPM_DICT["$RPM_PACKAGE"]=1 +done + +# For each of the RPM packages left in the list -- reset its permissions to the +# correct values +for RPM_PACKAGE in "${!SETPERMS_RPM_DICT[@]}" +do + rpm --setugids "${RPM_PACKAGE}" +done + + + + + + + + + + Verify and Correct File Permissions with RPM + The RPM package management system can check file access permissions +of installed software packages, including many that are important +to system security. +Verify that the file permissions of system files +and commands match vendor values. Check the file permissions +with the following command: +$ sudo rpm -Va | awk '{ if (substr($0,2,1)=="M") print $NF }' +Output indicates files that do not match vendor defaults. +After locating a file with incorrect permissions, +run the following command to determine which package owns it: +$ rpm -qf FILENAME + +Next, run the following command to reset its permissions to +the correct values: +$ sudo rpm --setperms PACKAGENAME + Profiles may require that specific files have stricter file permissions than defined by the +vendor. +Such files will be reported as a finding and need to be evaluated according to your policy +and deployment environment. + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + 6 + 9 + 5.10.4.1 + APO01.06 + APO11.04 + BAI03.05 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.04 + DSS05.07 + DSS06.02 + MEA02.01 + 3.3.8 + 3.4.1 + CCI-001493 + CCI-001494 + CCI-001495 + CCI-001496 + 164.308(a)(1)(ii)(D) + 164.312(b) + 164.312(c)(1) + 164.312(c)(2) + 164.312(e)(2)(i) + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.7.3 + 4.3.4.3.2 + 4.3.4.3.3 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.1 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 5.2 + SR 7.6 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.12.1.2 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.5.1 + A.12.6.2 + A.12.7.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R4.2 + CIP-003-8 R6 + CIP-007-3 R4 + CIP-007-3 R4.1 + CIP-007-3 R4.2 + CM-6(d) + CM-6(c) + SI-7 + SI-7(1) + SI-7(6) + AU-9(3) + CM-6(a) + PR.AC-4 + PR.DS-5 + PR.IP-1 + PR.PT-1 + Req-11.5 + SRG-OS-000256-GPOS-00097 + SRG-OS-000257-GPOS-00098 + SRG-OS-000258-GPOS-00099 + SRG-OS-000278-GPOS-00108 + Permissions on system binaries and configuration files that are too generous +could allow an unauthorized user to gain privileges that they should not have. +The permissions set by the vendor should be maintained. Any deviations from +this baseline should be investigated. + CCE-90840-0 + - name: Read list of files with incorrect permissions + command: rpm -Va --nodeps --nosignature --nofiledigest --nosize --nomtime --nordev + --nocaps --nolinkto --nouser --nogroup + args: + warn: false + register: files_with_incorrect_permissions + failed_when: files_with_incorrect_permissions.rc > 1 + changed_when: false + check_mode: false + tags: + - CCE-90840-0 + - CJIS-5.10.4.1 + - NIST-800-171-3.3.8 + - NIST-800-171-3.4.1 + - NIST-800-53-AU-9(3) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-6(c) + - NIST-800-53-CM-6(d) + - NIST-800-53-SI-7 + - NIST-800-53-SI-7(1) + - NIST-800-53-SI-7(6) + - PCI-DSS-Req-11.5 + - high_complexity + - high_severity + - medium_disruption + - no_reboot_needed + - restrict_strategy + - rpm_verify_permissions + +- name: Create list of packages + command: rpm -qf "{{ item }}" + args: + warn: false + with_items: '{{ files_with_incorrect_permissions.stdout_lines | map(''regex_findall'', + ''^[.]+[M]+.* (\/.*)'', ''\1'') | map(''join'') | select(''match'', ''(\/.*)'') + | list | unique }}' + register: list_of_packages + changed_when: false + check_mode: false + when: (files_with_incorrect_permissions.stdout_lines | length > 0) + tags: + - CCE-90840-0 + - CJIS-5.10.4.1 + - NIST-800-171-3.3.8 + - NIST-800-171-3.4.1 + - NIST-800-53-AU-9(3) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-6(c) + - NIST-800-53-CM-6(d) + - NIST-800-53-SI-7 + - NIST-800-53-SI-7(1) + - NIST-800-53-SI-7(6) + - PCI-DSS-Req-11.5 + - high_complexity + - high_severity + - medium_disruption + - no_reboot_needed + - restrict_strategy + - rpm_verify_permissions + +- name: Correct file permissions with RPM + command: rpm --setperms '{{ item }}' + args: + warn: false + with_items: '{{ list_of_packages.results | map(attribute=''stdout_lines'') | list + | unique }}' + when: (files_with_incorrect_permissions.stdout_lines | length > 0) + tags: + - CCE-90840-0 + - CJIS-5.10.4.1 + - NIST-800-171-3.3.8 + - NIST-800-171-3.4.1 + - NIST-800-53-AU-9(3) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-6(c) + - NIST-800-53-CM-6(d) + - NIST-800-53-SI-7 + - NIST-800-53-SI-7(1) + - NIST-800-53-SI-7(6) + - PCI-DSS-Req-11.5 + - high_complexity + - high_severity + - medium_disruption + - no_reboot_needed + - restrict_strategy + - rpm_verify_permissions + + +# Declare array to hold set of RPM packages we need to correct permissions for +declare -A SETPERMS_RPM_DICT + +# Create a list of files on the system having permissions different from what +# is expected by the RPM database +readarray -t FILES_WITH_INCORRECT_PERMS < <(rpm -Va --nofiledigest | awk '{ if (substr($0,2,1)=="M") print $NF }') + +for FILE_PATH in "${FILES_WITH_INCORRECT_PERMS[@]}" +do + # NOTE: some files maybe controlled by more then one package + readarray -t RPM_PACKAGES < <(rpm -qf "${FILE_PATH}") + for RPM_PACKAGE in "${RPM_PACKAGES[@]}" + do + # Use an associative array to store packages as it's keys, not having to care about duplicates. + SETPERMS_RPM_DICT["$RPM_PACKAGE"]=1 + done +done + +# For each of the RPM packages left in the list -- reset its permissions to the +# correct values +for RPM_PACKAGE in "${!SETPERMS_RPM_DICT[@]}" +do + rpm --restore "${RPM_PACKAGE}" +done + + + + + + + + + + + Verify Integrity with AIDE + AIDE conducts integrity checks by comparing information about +files with previously-gathered information. Ideally, the AIDE database is +created immediately after initial system configuration, and then again after any +software update. AIDE is highly configurable, with further configuration +information located in /usr/share/doc/aide-VERSION. + + + Install AIDE + The aide package can be installed with the following command: + +$ sudo dnf install aide + BP28(R51) + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 2 + 3 + 5 + 7 + 8 + 9 + 5.10.1.3 + APO01.06 + BAI01.06 + BAI02.01 + BAI03.05 + BAI06.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.03 + DSS03.05 + DSS04.07 + DSS05.02 + DSS05.03 + DSS05.05 + DSS05.07 + DSS06.02 + DSS06.06 + CCI-002696 + CCI-002699 + CCI-001744 + 4.3.4.3.2 + 4.3.4.3.3 + 4.3.4.4.4 + SR 3.1 + SR 3.3 + SR 3.4 + SR 3.8 + SR 4.1 + SR 6.2 + SR 7.6 + 1034 + 1288 + 1341 + 1417 + A.11.2.4 + A.12.1.2 + A.12.2.1 + A.12.4.1 + A.12.5.1 + A.12.6.2 + A.14.1.2 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.14.2.7 + A.15.2.1 + A.8.2.3 + CM-6(a) + DE.CM-1 + DE.CM-7 + PR.DS-1 + PR.DS-6 + PR.DS-8 + PR.IP-1 + PR.IP-3 + Req-11.5 + SRG-OS-000363-GPOS-00150 + SRG-OS-000445-GPOS-00199 + The AIDE package must be installed if it is to be available for integrity checking. + CCE-90843-4 + +package --add=aide + + include install_aide + +class install_aide { + package { 'aide': + ensure => 'installed', + } +} + + - name: Ensure aide is installed + package: + name: aide + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-90843-4 + - CJIS-5.10.1.3 + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-11.5 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_aide_installed + + +[[packages]] +name = "aide" +version = "*" + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if ! rpm -q --quiet "aide" ; then + dnf install -y "aide" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Build and Test AIDE Database + Run the following command to generate a new database: + +$ sudo /usr/sbin/aide --init + +By default, the database will be written to the file + +/var/lib/aide/aide.db.new.gz. + +Storing the database, the configuration file /etc/aide.conf, and the binary +/usr/sbin/aide +(or hashes of these files), in a secure location (such as on read-only media) provides additional assurance about their integrity. +The newly-generated database can be installed as follows: + +$ sudo cp /var/lib/aide/aide.db.new.gz /var/lib/aide/aide.db.gz + +To initiate a manual check, run the following command: +$ sudo /usr/sbin/aide --check +If this check produces any unexpected output, investigate. + BP28(R51) + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 2 + 3 + 5 + 7 + 8 + 9 + 5.10.1.3 + APO01.06 + BAI01.06 + BAI02.01 + BAI03.05 + BAI06.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.03 + DSS03.05 + DSS04.07 + DSS05.02 + DSS05.03 + DSS05.05 + DSS05.07 + DSS06.02 + DSS06.06 + 4.3.4.3.2 + 4.3.4.3.3 + 4.3.4.4.4 + SR 3.1 + SR 3.3 + SR 3.4 + SR 3.8 + SR 4.1 + SR 6.2 + SR 7.6 + A.11.2.4 + A.12.1.2 + A.12.2.1 + A.12.4.1 + A.12.5.1 + A.12.6.2 + A.14.1.2 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.14.2.7 + A.15.2.1 + A.8.2.3 + CM-6(a) + DE.CM-1 + DE.CM-7 + PR.DS-1 + PR.DS-6 + PR.DS-8 + PR.IP-1 + PR.IP-3 + Req-11.5 + For AIDE to be effective, an initial database of "known-good" information about files +must be captured and it should be able to be verified against the installed files. + CCE-83438-2 + - name: Ensure AIDE is installed + package: + name: '{{ item }}' + state: present + with_items: + - aide + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83438-2 + - CJIS-5.10.1.3 + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-11.5 + - aide_build_database + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Build and Test AIDE Database + command: /usr/sbin/aide --init + changed_when: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83438-2 + - CJIS-5.10.1.3 + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-11.5 + - aide_build_database + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Check whether the stock AIDE Database exists + stat: + path: /var/lib/aide/aide.db.new.gz + register: aide_database_stat + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83438-2 + - CJIS-5.10.1.3 + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-11.5 + - aide_build_database + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Stage AIDE Database + copy: + src: /var/lib/aide/aide.db.new.gz + dest: /var/lib/aide/aide.db.gz + backup: true + remote_src: true + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - (aide_database_stat.stat.exists is defined and aide_database_stat.stat.exists) + tags: + - CCE-83438-2 + - CJIS-5.10.1.3 + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-11.5 + - aide_build_database + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if ! rpm -q --quiet "aide" ; then + dnf install -y "aide" +fi + +/usr/sbin/aide --init +/bin/cp -p /var/lib/aide/aide.db.new.gz /var/lib/aide/aide.db.gz + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure AIDE to Verify the Audit Tools + The operating system file integrity tool must be configured to protect the integrity of the audit tools. + CCI-001496 + AU-9(3) + AU-9(3).1 + SRG-OS-000278-GPOS-00108 + Protecting the integrity of the tools used for auditing purposes is a +critical step toward ensuring the integrity of audit information. Audit +information includes all information (e.g., audit records, audit settings, +and audit reports) needed to successfully audit information system +activity. + +Audit tools include but are not limited to vendor-provided and open-source +audit tools needed to successfully view and manipulate audit information +system activity and records. Audit tools include custom queries and report +generators. + +It is not uncommon for attackers to replace the audit tools or inject code +into the existing tools to provide the capability to hide or erase system +activity from the audit logs. + +To address this risk, audit tools must be cryptographically signed to +provide the capability to identify when the audit tools have been modified, +manipulated, or replaced. An example is a checksum hash of the file or +files. + CCE-87757-1 + - name: Ensure aide is installed + package: + name: '{{ item }}' + state: present + with_items: + - aide + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-87757-1 + - NIST-800-53-AU-9(3) + - NIST-800-53-AU-9(3).1 + - aide_check_audit_tools + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Set audit_tools fact + set_fact: + audit_tools: + - /usr/sbin/auditctl + - /usr/sbin/auditd + - /usr/sbin/augenrules + - /usr/sbin/aureport + - /usr/sbin/ausearch + - /usr/sbin/autrace + - /usr/sbin/rsyslogd + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-87757-1 + - NIST-800-53-AU-9(3) + - NIST-800-53-AU-9(3).1 + - aide_check_audit_tools + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Ensure existing AIDE configuration for audit tools are correct + lineinfile: + path: /etc/aide.conf + regexp: ^{{ item }}\s + line: '{{ item }} p+i+n+u+g+s+b+acl+xattrs+sha512 ' + with_items: '{{ audit_tools }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-87757-1 + - NIST-800-53-AU-9(3) + - NIST-800-53-AU-9(3).1 + - aide_check_audit_tools + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Configure AIDE to properly protect audit tools + lineinfile: + path: /etc/aide.conf + line: '{{ item }} p+i+n+u+g+s+b+acl+xattrs+sha512 ' + with_items: '{{ audit_tools }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-87757-1 + - NIST-800-53-AU-9(3) + - NIST-800-53-AU-9(3).1 + - aide_check_audit_tools + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if ! rpm -q --quiet "aide" ; then + dnf install -y "aide" +fi + + + + + + + + + + +if grep -i '^.*/usr/sbin/auditctl.*$' /etc/aide.conf; then +sed -i "s#.*/usr/sbin/auditctl.*#/usr/sbin/auditctl p+i+n+u+g+s+b+acl+xattrs+sha512 +#" /etc/aide.conf +else +echo "/usr/sbin/auditctl p+i+n+u+g+s+b+acl+xattrs+sha512 +" >> /etc/aide.conf +fi + +if grep -i '^.*/usr/sbin/auditd.*$' /etc/aide.conf; then +sed -i "s#.*/usr/sbin/auditd.*#/usr/sbin/auditd p+i+n+u+g+s+b+acl+xattrs+sha512 +#" /etc/aide.conf +else +echo "/usr/sbin/auditd p+i+n+u+g+s+b+acl+xattrs+sha512 +" >> /etc/aide.conf +fi + +if grep -i '^.*/usr/sbin/ausearch.*$' /etc/aide.conf; then +sed -i "s#.*/usr/sbin/ausearch.*#/usr/sbin/ausearch p+i+n+u+g+s+b+acl+xattrs+sha512 +#" /etc/aide.conf +else +echo "/usr/sbin/ausearch p+i+n+u+g+s+b+acl+xattrs+sha512 +" >> /etc/aide.conf +fi + +if grep -i '^.*/usr/sbin/aureport.*$' /etc/aide.conf; then +sed -i "s#.*/usr/sbin/aureport.*#/usr/sbin/aureport p+i+n+u+g+s+b+acl+xattrs+sha512 +#" /etc/aide.conf +else +echo "/usr/sbin/aureport p+i+n+u+g+s+b+acl+xattrs+sha512 +" >> /etc/aide.conf +fi + +if grep -i '^.*/usr/sbin/autrace.*$' /etc/aide.conf; then +sed -i "s#.*/usr/sbin/autrace.*#/usr/sbin/autrace p+i+n+u+g+s+b+acl+xattrs+sha512 +#" /etc/aide.conf +else +echo "/usr/sbin/autrace p+i+n+u+g+s+b+acl+xattrs+sha512 +" >> /etc/aide.conf +fi + +if grep -i '^.*/usr/sbin/augenrules.*$' /etc/aide.conf; then +sed -i "s#.*/usr/sbin/augenrules.*#/usr/sbin/augenrules p+i+n+u+g+s+b+acl+xattrs+sha512 +#" /etc/aide.conf +else +echo "/usr/sbin/augenrules p+i+n+u+g+s+b+acl+xattrs+sha512 +" >> /etc/aide.conf +fi + +if grep -i '^.*/usr/sbin/rsyslogd.*$' /etc/aide.conf; then +sed -i "s#.*/usr/sbin/rsyslogd.*#/usr/sbin/rsyslogd p+i+n+u+g+s+b+acl+xattrs+sha512 +#" /etc/aide.conf +else +echo "/usr/sbin/rsyslogd p+i+n+u+g+s+b+acl+xattrs+sha512 +" >> /etc/aide.conf +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure Periodic Execution of AIDE + At a minimum, AIDE should be configured to run a weekly scan. +To implement a daily execution of AIDE at 4:05am using cron, add the following line to /etc/crontab: +05 4 * * * root /usr/sbin/aide --check +To implement a weekly execution of AIDE at 4:05am using cron, add the following line to /etc/crontab: +05 4 * * 0 root /usr/sbin/aide --check +AIDE can be executed periodically through other means; this is merely one example. +The usage of cron's special time codes, such as @daily and +@weekly is acceptable. + BP28(R51) + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 2 + 3 + 5 + 7 + 8 + 9 + 5.10.1.3 + APO01.06 + BAI01.06 + BAI02.01 + BAI03.05 + BAI06.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.03 + DSS03.05 + DSS04.07 + DSS05.02 + DSS05.03 + DSS05.05 + DSS05.07 + DSS06.02 + DSS06.06 + CCI-001744 + CCI-002699 + CCI-002702 + 4.3.4.3.2 + 4.3.4.3.3 + 4.3.4.4.4 + SR 3.1 + SR 3.3 + SR 3.4 + SR 3.8 + SR 4.1 + SR 6.2 + SR 7.6 + A.11.2.4 + A.12.1.2 + A.12.2.1 + A.12.4.1 + A.12.5.1 + A.12.6.2 + A.14.1.2 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.14.2.7 + A.15.2.1 + A.8.2.3 + SI-7 + SI-7(1) + CM-6(a) + DE.CM-1 + DE.CM-7 + PR.DS-1 + PR.DS-6 + PR.DS-8 + PR.IP-1 + PR.IP-3 + Req-11.5 + SRG-OS-000363-GPOS-00150 + SRG-OS-000446-GPOS-00200 + SRG-OS-000447-GPOS-00201 + By default, AIDE does not install itself for periodic execution. Periodically +running AIDE is necessary to reveal unexpected changes in installed files. + +Unauthorized changes to the baseline configuration could make the system vulnerable +to various attacks or allow unauthorized access to the operating system. Changes to +operating system configurations can have unintended side effects, some of which may +be relevant to security. + +Detecting such changes and providing an automated response can help avoid unintended, +negative consequences that could ultimately affect the security state of the operating +system. The operating system's Information Management Officer (IMO)/Information System +Security Officer (ISSO) and System Administrators (SAs) must be notified via email and/or +monitoring system trap when there is an unauthorized modification of a configuration item. + CCE-83437-4 + - name: Ensure AIDE is installed + package: + name: '{{ item }}' + state: present + with_items: + - aide + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83437-4 + - CJIS-5.10.1.3 + - NIST-800-53-CM-6(a) + - NIST-800-53-SI-7 + - NIST-800-53-SI-7(1) + - PCI-DSS-Req-11.5 + - aide_periodic_cron_checking + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Set cron package name - RedHat + set_fact: + cron_pkg_name: cronie + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ansible_os_family == "RedHat" or ansible_os_family == "Suse" + tags: + - CCE-83437-4 + - CJIS-5.10.1.3 + - NIST-800-53-CM-6(a) + - NIST-800-53-SI-7 + - NIST-800-53-SI-7(1) + - PCI-DSS-Req-11.5 + - aide_periodic_cron_checking + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Set cron package name - Debian + set_fact: + cron_pkg_name: cron + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ansible_os_family == "Debian" + tags: + - CCE-83437-4 + - CJIS-5.10.1.3 + - NIST-800-53-CM-6(a) + - NIST-800-53-SI-7 + - NIST-800-53-SI-7(1) + - PCI-DSS-Req-11.5 + - aide_periodic_cron_checking + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Install cron + package: + name: '{{ cron_pkg_name }}' + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83437-4 + - CJIS-5.10.1.3 + - NIST-800-53-CM-6(a) + - NIST-800-53-SI-7 + - NIST-800-53-SI-7(1) + - PCI-DSS-Req-11.5 + - aide_periodic_cron_checking + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Configure Periodic Execution of AIDE + cron: + name: run AIDE check + minute: 5 + hour: 4 + weekday: 0 + user: root + job: /usr/sbin/aide --check + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83437-4 + - CJIS-5.10.1.3 + - NIST-800-53-CM-6(a) + - NIST-800-53-SI-7 + - NIST-800-53-SI-7(1) + - PCI-DSS-Req-11.5 + - aide_periodic_cron_checking + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if ! rpm -q --quiet "aide" ; then + dnf install -y "aide" +fi + +if ! grep -q "/usr/sbin/aide --check" /etc/crontab ; then + echo "05 4 * * * root /usr/sbin/aide --check" >> /etc/crontab +else + sed -i '\!^.* --check.*$!d' /etc/crontab + echo "05 4 * * * root /usr/sbin/aide --check" >> /etc/crontab +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure Notification of Post-AIDE Scan Details + AIDE should notify appropriate personnel of the details of a scan after the scan has been run. +If AIDE has already been configured for periodic execution in /etc/crontab, append the +following line to the existing AIDE line: + | /bin/mail -s "$(hostname) - AIDE Integrity Check" root@localhost +Otherwise, add the following line to /etc/crontab: +05 4 * * * root /usr/sbin/aide --check | /bin/mail -s "$(hostname) - AIDE Integrity Check" root@localhost +AIDE can be executed periodically through other means; this is merely one example. + BP28(R51) + 1 + 11 + 12 + 13 + 15 + 16 + 2 + 3 + 5 + 7 + 8 + 9 + BAI01.06 + BAI06.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.03 + DSS03.05 + DSS05.02 + DSS05.05 + DSS05.07 + CCI-001744 + CCI-002699 + CCI-002702 + 4.3.4.3.2 + 4.3.4.3.3 + SR 6.2 + SR 7.6 + A.12.1.2 + A.12.4.1 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.14.2.7 + A.15.2.1 + CM-6(a) + CM-3(5) + DE.CM-1 + DE.CM-7 + PR.IP-1 + PR.IP-3 + SRG-OS-000363-GPOS-00150 + SRG-OS-000446-GPOS-00200 + SRG-OS-000447-GPOS-00201 + Unauthorized changes to the baseline configuration could make the system vulnerable +to various attacks or allow unauthorized access to the operating system. Changes to +operating system configurations can have unintended side effects, some of which may +be relevant to security. + +Detecting such changes and providing an automated response can help avoid unintended, +negative consequences that could ultimately affect the security state of the operating +system. The operating system's Information Management Officer (IMO)/Information System +Security Officer (ISSO) and System Administrators (SAs) must be notified via email and/or +monitoring system trap when there is an unauthorized modification of a configuration item. + CCE-90844-2 + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if ! rpm -q --quiet "aide" ; then + dnf install -y "aide" +fi +var_aide_scan_notification_email='' + + +CRONTAB=/etc/crontab +CRONDIRS='/etc/cron.d /etc/cron.daily /etc/cron.weekly /etc/cron.monthly' + +# NOTE: on some platforms, /etc/crontab may not exist +if [ -f /etc/crontab ]; then + CRONTAB_EXIST=/etc/crontab +fi + +if [ -f /var/spool/cron/root ]; then + VARSPOOL=/var/spool/cron/root +fi + +if ! grep -qR '^.*/usr/sbin/aide\s*\-\-check.*|.*\/bin\/mail\s*-s\s*".*"\s*.*@.*$' $CRONTAB_EXIST $VARSPOOL $CRONDIRS; then + echo "0 5 * * * root /usr/sbin/aide --check | /bin/mail -s \"\$(hostname) - AIDE Integrity Check\" $var_aide_scan_notification_email" >> $CRONTAB +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure AIDE to Use FIPS 140-2 for Validating Hashes + By default, the sha512 option is added to the NORMAL ruleset in AIDE. +If using a custom ruleset or the sha512 option is missing, add sha512 +to the appropriate ruleset. +For example, add sha512 to the following line in /etc/aide.conf: +NORMAL = FIPSR+sha512 +AIDE rules can be configured in multiple ways; this is merely one example that is already +configured by default. + System Crypto Modules must be provided by a vendor that undergoes +FIPS-140 certifications. +FIPS-140 is applicable to all Federal agencies that use +cryptographic-based security systems to protect sensitive information +in computer and telecommunication systems (including voice systems) as +defined in Section 5131 of the Information Technology Management Reform +Act of 1996, Public Law 104-106. This standard shall be used in +designing and implementing cryptographic modules that Federal +departments and agencies operate or are operated for them under +contract. See https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.140-2.pdf +To meet this, the system has to have cryptographic software provided by +a vendor that has undergone this certification. This means providing +documentation, test results, design information, and independent third +party review by an accredited lab. While open source software is +capable of meeting this, it does not meet FIPS-140 unless the vendor +submits to this process. + 2 + 3 + APO01.06 + BAI03.05 + BAI06.01 + DSS06.02 + 3.13.11 + CCI-000366 + 4.3.4.4.4 + SR 3.1 + SR 3.3 + SR 3.4 + SR 3.8 + A.11.2.4 + A.12.2.1 + A.12.5.1 + A.14.1.2 + A.14.1.3 + A.14.2.4 + SI-7 + SI-7(1) + CM-6(a) + PR.DS-6 + PR.DS-8 + SRG-OS-000480-GPOS-00227 + File integrity tools use cryptographic hashes for verifying file contents and directories +have not been altered. These hashes must be FIPS 140-2 approved cryptographic hashes. + CCE-88939-4 + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if ! rpm -q --quiet "aide" ; then + dnf install -y "aide" +fi + +aide_conf="/etc/aide.conf" +forbidden_hashes=(sha1 rmd160 sha256 whirlpool tiger haval gost crc32) + +groups=$(LC_ALL=C grep "^[A-Z][A-Za-z_]*" $aide_conf | cut -f1 -d ' ' | tr -d ' ' | sort -u) + +for group in $groups +do + config=$(grep "^$group\s*=" $aide_conf | cut -f2 -d '=' | tr -d ' ') + + if ! [[ $config = *sha512* ]] + then + config=$config"+sha512" + fi + + for hash in "${forbidden_hashes[@]}" + do + config=$(echo $config | sed "s/$hash//") + done + + config=$(echo $config | sed "s/^\+*//") + config=$(echo $config | sed "s/\+\++/+/") + config=$(echo $config | sed "s/\+$//") + + sed -i "s/^$group\s*=.*/$group = $config/g" $aide_conf +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure AIDE to Verify Access Control Lists (ACLs) + By default, the acl option is added to the FIPSR ruleset in AIDE. +If using a custom ruleset or the acl option is missing, add acl +to the appropriate ruleset. +For example, add acl to the following line in /etc/aide.conf: +FIPSR = p+i+n+u+g+s+m+c+acl+selinux+xattrs+sha256 +AIDE rules can be configured in multiple ways; this is merely one example that is already +configured by default. + +The remediation provided with this rule adds acl to all rule sets available in +/etc/aide.conf + BP28(R51) + 2 + 3 + APO01.06 + BAI03.05 + BAI06.01 + DSS06.02 + CCI-000366 + 4.3.4.4.4 + SR 3.1 + SR 3.3 + SR 3.4 + SR 3.8 + A.11.2.4 + A.12.2.1 + A.12.5.1 + A.14.1.2 + A.14.1.3 + A.14.2.4 + SI-7 + SI-7(1) + CM-6(a) + PR.DS-6 + PR.DS-8 + SRG-OS-000480-GPOS-00227 + ACLs can provide permissions beyond those permitted through the file mode and must be +verified by the file integrity tools. + CCE-90837-6 + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if ! rpm -q --quiet "aide" ; then + dnf install -y "aide" +fi + +aide_conf="/etc/aide.conf" + +groups=$(LC_ALL=C grep "^[A-Z][A-Za-z_]*" $aide_conf | grep -v "^ALLXTRAHASHES" | cut -f1 -d '=' | tr -d ' ' | sort -u) + +for group in $groups +do + config=$(grep "^$group\s*=" $aide_conf | cut -f2 -d '=' | tr -d ' ') + + if ! [[ $config = *acl* ]] + then + if [[ -z $config ]] + then + config="acl" + else + config=$config"+acl" + fi + fi + sed -i "s/^$group\s*=.*/$group = $config/g" $aide_conf +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure AIDE to Verify Extended Attributes + By default, the xattrs option is added to the FIPSR ruleset in AIDE. +If using a custom ruleset or the xattrs option is missing, add xattrs +to the appropriate ruleset. +For example, add xattrs to the following line in /etc/aide.conf: +FIPSR = p+i+n+u+g+s+m+c+acl+selinux+xattrs+sha256 +AIDE rules can be configured in multiple ways; this is merely one example that is already +configured by default. + +The remediation provided with this rule adds xattrs to all rule sets available in +/etc/aide.conf + BP28(R51) + 2 + 3 + APO01.06 + BAI03.05 + BAI06.01 + DSS06.02 + CCI-000366 + 4.3.4.4.4 + SR 3.1 + SR 3.3 + SR 3.4 + SR 3.8 + A.11.2.4 + A.12.2.1 + A.12.5.1 + A.14.1.2 + A.14.1.3 + A.14.2.4 + SI-7 + SI-7(1) + CM-6(a) + PR.DS-6 + PR.DS-8 + SRG-OS-000480-GPOS-00227 + Extended attributes in file systems are used to contain arbitrary data and file metadata +with security implications. + CCE-83439-0 + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if ! rpm -q --quiet "aide" ; then + dnf install -y "aide" +fi + +aide_conf="/etc/aide.conf" + +groups=$(LC_ALL=C grep "^[A-Z][A-Za-z_]*" $aide_conf | grep -v "^ALLXTRAHASHES" | cut -f1 -d '=' | tr -d ' ' | sort -u) + +for group in $groups +do + config=$(grep "^$group\s*=" $aide_conf | cut -f2 -d '=' | tr -d ' ') + + if ! [[ $config = *xattrs* ]] + then + if [[ -z $config ]] + then + config="xattrs" + else + config=$config"+xattrs" + fi + fi + sed -i "s/^$group\s*=.*/$group = $config/g" $aide_conf +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Audit Tools Must Be Group-owned by Root + Red Hat Enterprise Linux 9 systems providing tools to interface with audit information will leverage user permissions and roles identifying the user accessing the tools, and the corresponding rights the user enjoys, to make access decisions regarding the access to audit tools. + +Audit tools include, but are not limited to, vendor-provided and open source audit tools needed to successfully view and manipulate audit information system activity and records. Audit tools include custom queries and report generators. + +Audit tools must have the correct group owner. + CCI-001493 + CCI-001494 + CCI-001495 + AU-9 + SRG-OS-000256-GPOS-00097 + SRG-OS-000257-GPOS-00098 + SRG-OS-000258-GPOS-00099 + Protecting audit information also includes identifying and protecting the tools used to view and manipulate log data. +Therefore, protecting audit tools is necessary to prevent unauthorized operations on audit information. + CCE-86240-9 + - name: Test for existence /sbin/auditctl + stat: + path: /sbin/auditctl + register: file_exists + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86240-9 + - NIST-800-53-AU-9 + - configure_strategy + - file_audit_tools_group_ownership + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure group owner 0 on /sbin/auditctl + file: + path: /sbin/auditctl + group: '0' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-86240-9 + - NIST-800-53-AU-9 + - configure_strategy + - file_audit_tools_group_ownership + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Test for existence /sbin/aureport + stat: + path: /sbin/aureport + register: file_exists + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86240-9 + - NIST-800-53-AU-9 + - configure_strategy + - file_audit_tools_group_ownership + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure group owner 0 on /sbin/aureport + file: + path: /sbin/aureport + group: '0' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-86240-9 + - NIST-800-53-AU-9 + - configure_strategy + - file_audit_tools_group_ownership + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Test for existence /sbin/ausearch + stat: + path: /sbin/ausearch + register: file_exists + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86240-9 + - NIST-800-53-AU-9 + - configure_strategy + - file_audit_tools_group_ownership + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure group owner 0 on /sbin/ausearch + file: + path: /sbin/ausearch + group: '0' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-86240-9 + - NIST-800-53-AU-9 + - configure_strategy + - file_audit_tools_group_ownership + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Test for existence /sbin/autrace + stat: + path: /sbin/autrace + register: file_exists + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86240-9 + - NIST-800-53-AU-9 + - configure_strategy + - file_audit_tools_group_ownership + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure group owner 0 on /sbin/autrace + file: + path: /sbin/autrace + group: '0' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-86240-9 + - NIST-800-53-AU-9 + - configure_strategy + - file_audit_tools_group_ownership + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Test for existence /sbin/auditd + stat: + path: /sbin/auditd + register: file_exists + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86240-9 + - NIST-800-53-AU-9 + - configure_strategy + - file_audit_tools_group_ownership + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure group owner 0 on /sbin/auditd + file: + path: /sbin/auditd + group: '0' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-86240-9 + - NIST-800-53-AU-9 + - configure_strategy + - file_audit_tools_group_ownership + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Test for existence /sbin/rsyslogd + stat: + path: /sbin/rsyslogd + register: file_exists + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86240-9 + - NIST-800-53-AU-9 + - configure_strategy + - file_audit_tools_group_ownership + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure group owner 0 on /sbin/rsyslogd + file: + path: /sbin/rsyslogd + group: '0' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-86240-9 + - NIST-800-53-AU-9 + - configure_strategy + - file_audit_tools_group_ownership + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Test for existence /sbin/augenrules + stat: + path: /sbin/augenrules + register: file_exists + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86240-9 + - NIST-800-53-AU-9 + - configure_strategy + - file_audit_tools_group_ownership + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure group owner 0 on /sbin/augenrules + file: + path: /sbin/augenrules + group: '0' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-86240-9 + - NIST-800-53-AU-9 + - configure_strategy + - file_audit_tools_group_ownership + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +chgrp 0 /sbin/auditctl + +chgrp 0 /sbin/aureport + +chgrp 0 /sbin/ausearch + +chgrp 0 /sbin/autrace + +chgrp 0 /sbin/auditd + +chgrp 0 /sbin/rsyslogd + +chgrp 0 /sbin/augenrules + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Audit Tools Must Be Owned by Root + Red Hat Enterprise Linux 9 systems providing tools to interface with audit information will leverage user permissions and roles identifying the user accessing the tools, and the corresponding rights the user enjoys, to make access decisions regarding the access to audit tools. + +Audit tools include, but are not limited to, vendor-provided and open source audit tools needed to successfully view and manipulate audit information system activity and records. Audit tools include custom queries and report generators. + +Audit tools must have the correct owner. + CCI-001493 + CCI-001494 + CCI-001495 + AU-9 + SRG-OS-000256-GPOS-00097 + SRG-OS-000257-GPOS-00098 + SRG-OS-000258-GPOS-00099 + Protecting audit information also includes identifying and protecting the tools used to view and manipulate log data. +Therefore, protecting audit tools is necessary to prevent unauthorized operations on audit information. + CCE-86263-1 + - name: Test for existence /sbin/auditctl + stat: + path: /sbin/auditctl + register: file_exists + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86263-1 + - NIST-800-53-AU-9 + - configure_strategy + - file_audit_tools_ownership + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure owner 0 on /sbin/auditctl + file: + path: /sbin/auditctl + owner: '0' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-86263-1 + - NIST-800-53-AU-9 + - configure_strategy + - file_audit_tools_ownership + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Test for existence /sbin/aureport + stat: + path: /sbin/aureport + register: file_exists + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86263-1 + - NIST-800-53-AU-9 + - configure_strategy + - file_audit_tools_ownership + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure owner 0 on /sbin/aureport + file: + path: /sbin/aureport + owner: '0' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-86263-1 + - NIST-800-53-AU-9 + - configure_strategy + - file_audit_tools_ownership + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Test for existence /sbin/ausearch + stat: + path: /sbin/ausearch + register: file_exists + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86263-1 + - NIST-800-53-AU-9 + - configure_strategy + - file_audit_tools_ownership + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure owner 0 on /sbin/ausearch + file: + path: /sbin/ausearch + owner: '0' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-86263-1 + - NIST-800-53-AU-9 + - configure_strategy + - file_audit_tools_ownership + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Test for existence /sbin/autrace + stat: + path: /sbin/autrace + register: file_exists + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86263-1 + - NIST-800-53-AU-9 + - configure_strategy + - file_audit_tools_ownership + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure owner 0 on /sbin/autrace + file: + path: /sbin/autrace + owner: '0' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-86263-1 + - NIST-800-53-AU-9 + - configure_strategy + - file_audit_tools_ownership + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Test for existence /sbin/auditd + stat: + path: /sbin/auditd + register: file_exists + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86263-1 + - NIST-800-53-AU-9 + - configure_strategy + - file_audit_tools_ownership + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure owner 0 on /sbin/auditd + file: + path: /sbin/auditd + owner: '0' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-86263-1 + - NIST-800-53-AU-9 + - configure_strategy + - file_audit_tools_ownership + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Test for existence /sbin/rsyslogd + stat: + path: /sbin/rsyslogd + register: file_exists + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86263-1 + - NIST-800-53-AU-9 + - configure_strategy + - file_audit_tools_ownership + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure owner 0 on /sbin/rsyslogd + file: + path: /sbin/rsyslogd + owner: '0' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-86263-1 + - NIST-800-53-AU-9 + - configure_strategy + - file_audit_tools_ownership + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Test for existence /sbin/augenrules + stat: + path: /sbin/augenrules + register: file_exists + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86263-1 + - NIST-800-53-AU-9 + - configure_strategy + - file_audit_tools_ownership + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure owner 0 on /sbin/augenrules + file: + path: /sbin/augenrules + owner: '0' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-86263-1 + - NIST-800-53-AU-9 + - configure_strategy + - file_audit_tools_ownership + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +chown 0 /sbin/auditctl + +chown 0 /sbin/aureport + +chown 0 /sbin/ausearch + +chown 0 /sbin/autrace + +chown 0 /sbin/auditd + +chown 0 /sbin/rsyslogd + +chown 0 /sbin/augenrules + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Audit Tools Must Have a Mode of 0755 or Less Permissive + Red Hat Enterprise Linux 9 systems providing tools to interface with audit information will leverage user permissions and roles identifying the user accessing the tools, and the corresponding rights the user enjoys, to make access decisions regarding the access to audit tools. + +Audit tools include, but are not limited to, vendor-provided and open source audit tools needed to successfully view and manipulate audit information system activity and records. Audit tools include custom queries and report generators. + +Audit tools must have a mode of 0755 or less permissive. + CCI-001493 + AU-9 + SRG-OS-000256-GPOS-00097 + SRG-OS-000257-GPOS-00098 + SRG-OS-000258-GPOS-00099 + Protecting audit information also includes identifying and protecting the tools used to view and manipulate log data. +Therefore, protecting audit tools is necessary to prevent unauthorized operations on audit information. + CCE-86228-4 + - name: Test for existence /sbin/auditctl + stat: + path: /sbin/auditctl + register: file_exists + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86228-4 + - NIST-800-53-AU-9 + - configure_strategy + - file_audit_tools_permissions + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure permission u-s,g-ws,o-wt on /sbin/auditctl + file: + path: /sbin/auditctl + mode: u-s,g-ws,o-wt + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-86228-4 + - NIST-800-53-AU-9 + - configure_strategy + - file_audit_tools_permissions + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Test for existence /sbin/aureport + stat: + path: /sbin/aureport + register: file_exists + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86228-4 + - NIST-800-53-AU-9 + - configure_strategy + - file_audit_tools_permissions + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure permission u-s,g-ws,o-wt on /sbin/aureport + file: + path: /sbin/aureport + mode: u-s,g-ws,o-wt + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-86228-4 + - NIST-800-53-AU-9 + - configure_strategy + - file_audit_tools_permissions + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Test for existence /sbin/ausearch + stat: + path: /sbin/ausearch + register: file_exists + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86228-4 + - NIST-800-53-AU-9 + - configure_strategy + - file_audit_tools_permissions + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure permission u-s,g-ws,o-wt on /sbin/ausearch + file: + path: /sbin/ausearch + mode: u-s,g-ws,o-wt + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-86228-4 + - NIST-800-53-AU-9 + - configure_strategy + - file_audit_tools_permissions + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Test for existence /sbin/autrace + stat: + path: /sbin/autrace + register: file_exists + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86228-4 + - NIST-800-53-AU-9 + - configure_strategy + - file_audit_tools_permissions + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure permission u-s,g-ws,o-wt on /sbin/autrace + file: + path: /sbin/autrace + mode: u-s,g-ws,o-wt + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-86228-4 + - NIST-800-53-AU-9 + - configure_strategy + - file_audit_tools_permissions + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Test for existence /sbin/auditd + stat: + path: /sbin/auditd + register: file_exists + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86228-4 + - NIST-800-53-AU-9 + - configure_strategy + - file_audit_tools_permissions + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure permission u-s,g-ws,o-wt on /sbin/auditd + file: + path: /sbin/auditd + mode: u-s,g-ws,o-wt + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-86228-4 + - NIST-800-53-AU-9 + - configure_strategy + - file_audit_tools_permissions + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Test for existence /sbin/rsyslogd + stat: + path: /sbin/rsyslogd + register: file_exists + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86228-4 + - NIST-800-53-AU-9 + - configure_strategy + - file_audit_tools_permissions + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure permission u-s,g-ws,o-wt on /sbin/rsyslogd + file: + path: /sbin/rsyslogd + mode: u-s,g-ws,o-wt + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-86228-4 + - NIST-800-53-AU-9 + - configure_strategy + - file_audit_tools_permissions + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Test for existence /sbin/augenrules + stat: + path: /sbin/augenrules + register: file_exists + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86228-4 + - NIST-800-53-AU-9 + - configure_strategy + - file_audit_tools_permissions + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure permission u-s,g-ws,o-wt on /sbin/augenrules + file: + path: /sbin/augenrules + mode: u-s,g-ws,o-wt + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-86228-4 + - NIST-800-53-AU-9 + - configure_strategy + - file_audit_tools_permissions + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +chmod u-s,g-ws,o-wt /sbin/auditctl + +chmod u-s,g-ws,o-wt /sbin/aureport + +chmod u-s,g-ws,o-wt /sbin/ausearch + +chmod u-s,g-ws,o-wt /sbin/autrace + +chmod u-s,g-ws,o-wt /sbin/auditd + +chmod u-s,g-ws,o-wt /sbin/rsyslogd + +chmod u-s,g-ws,o-wt /sbin/augenrules + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + + Federal Information Processing Standard (FIPS) + The Federal Information Processing Standard (FIPS) is a computer security standard which +is developed by the U.S. Government and industry working groups to validate the quality +of cryptographic modules. The FIPS standard provides four security levels to ensure +adequate coverage of different industries, implementation of cryptographic modules, and +organizational sizes and requirements. + +FIPS 140-2 is the current standard for validating that mechanisms used to access cryptographic modules +utilize authentication that meets industry and government requirements. For government systems, this allows +Security Levels 1, 2, 3, or 4 for use on Red Hat Enterprise Linux 9. + +See http://csrc.nist.gov/publications/PubsFIPS.html for more information. + + + Enable Dracut FIPS Module + To enable FIPS mode, run the following command: +fips-mode-setup --enable +To enable FIPS, the system requires that the fips module is added in dracut configuration. +Check if /etc/dracut.conf.d/40-fips.conf contain add_dracutmodules+=" fips " + The system needs to be rebooted for these changes to take effect. + System Crypto Modules must be provided by a vendor that undergoes FIPS-140 certifications. +FIPS-140 is applicable to all Federal agencies that use cryptographic-based security +systems to protect sensitive information in computer and telecommunication systems +(including voice systems) as defined in Section 5131 of the Information Technology +Management Reform Act of 1996, Public Law 104-106. This standard shall be used in designing +and implementing cryptographic modules that Federal departments and agencies operate or are +operated for them under contract. +See https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.140-2.pdf +To meet this, the system has to have cryptographic software provided by a vendor that has +undergone this certification. This means providing documentation, test results, design +information, and independent third party review by an accredited lab. While open source +software is capable of meeting this, it does not meet FIPS-140 unless the vendor submits to +this process. + CCI-000068 + CCI-000803 + CCI-002450 + 1446 + CIP-003-8 R4.2 + CIP-007-3 R5.1 + SC-12(2) + SC-12(3) + IA-7 + SC-13 + CM-6(a) + SC-12 + FCS_RBG_EXT.1 + SRG-OS-000478-GPOS-00223 + SRG-OS-000120-VMM-000600 + SRG-OS-000478-VMM-001980 + SRG-OS-000396-VMM-001590 + Use of weak or untested encryption algorithms undermines the purposes of utilizing encryption to +protect data. The operating system must implement cryptographic modules adhering to the higher +standards approved by the federal government since this provides assurance they have been tested +and validated. + CCE-86547-7 + - name: Check to see the current status of FIPS mode + command: /usr/bin/fips-mode-setup --check + register: is_fips_enabled + changed_when: false + failed_when: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86547-7 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-7 + - NIST-800-53-SC-12 + - NIST-800-53-SC-12(2) + - NIST-800-53-SC-12(3) + - NIST-800-53-SC-13 + - enable_dracut_fips_module + - high_severity + - medium_complexity + - medium_disruption + - reboot_required + - restrict_strategy + +- name: Enable FIPS mode + command: /usr/bin/fips-mode-setup --enable + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - is_fips_enabled.stdout.find('FIPS mode is enabled.') == -1 + tags: + - CCE-86547-7 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-7 + - NIST-800-53-SC-12 + - NIST-800-53-SC-12(2) + - NIST-800-53-SC-12(3) + - NIST-800-53-SC-13 + - enable_dracut_fips_module + - high_severity + - medium_complexity + - medium_disruption + - reboot_required + - restrict_strategy + +- name: Enable Dracut FIPS Module + lineinfile: + path: /etc/dracut.conf.d/40-fips.conf + line: add_dracutmodules+=" fips " + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86547-7 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-7 + - NIST-800-53-SC-12 + - NIST-800-53-SC-12(2) + - NIST-800-53-SC-12(3) + - NIST-800-53-SC-13 + - enable_dracut_fips_module + - high_severity + - medium_complexity + - medium_disruption + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +fips-mode-setup --enable +FIPS_CONF="/etc/dracut.conf.d/40-fips.conf" +if ! grep "^add_dracutmodules+=\" fips \"" $FIPS_CONF; then + echo "add_dracutmodules+=\" fips \"" >> $FIPS_CONF +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Enable FIPS Mode + To enable FIPS mode, run the following command: +fips-mode-setup --enable + +The fips-mode-setup command will configure the system in +FIPS mode by automatically configuring the following: +Setting the kernel FIPS mode flag (/proc/sys/crypto/fips_enabled) to 1Creating /etc/system-fipsSetting the system crypto policy in /etc/crypto-policies/config to Loading the Dracut fips module + The system needs to be rebooted for these changes to take effect. + This rule DOES NOT CHECK if the components of the operating system are FIPS certified. +You can find the list of FIPS certified modules at +https://csrc.nist.gov/projects/cryptographic-module-validation-program/validated-modules/search. +This rule checks if the system is running in FIPS mode. See the rule description for more information about what it means. + CCI-000068 + CCI-000803 + CCI-002450 + 1446 + CIP-003-8 R4.2 + CIP-007-3 R5.1 + CM-3(6) + SC-12(2) + SC-12(3) + IA-7 + SC-13 + CM-6(a) + SC-12 + FCS_COP.1(1) + FCS_COP.1(2) + FCS_COP.1(3) + FCS_COP.1(4) + FCS_CKM.1 + FCS_CKM.2 + FCS_TLSC_EXT.1 + FCS_RBG_EXT.1 + SRG-OS-000478-GPOS-00223 + SRG-OS-000396-GPOS-00176 + SRG-OS-000120-VMM-000600 + SRG-OS-000478-VMM-001980 + SRG-OS-000396-VMM-001590 + Use of weak or untested encryption algorithms undermines the purposes of utilizing encryption to +protect data. The operating system must implement cryptographic modules adhering to the higher +standards approved by the federal government since this provides assurance they have been tested +and validated. + + CCE-88742-2 + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_system_crypto_policy='' + + +fips-mode-setup --enable + +stderr_of_call=$(update-crypto-policies --set ${var_system_crypto_policy} 2>&1 > /dev/null) +rc=$? + +if test "$rc" = 127; then + echo "$stderr_of_call" >&2 + echo "Make sure that the script is installed on the remediated system." >&2 + echo "See output of the 'dnf provides update-crypto-policies' command" >&2 + echo "to see what package to (re)install" >&2 + + false # end with an error code +elif test "$rc" != 0; then + echo "Error invoking the update-crypto-policies script: $stderr_of_call" >&2 + false # end with an error code +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Ensure '/etc/system-fips' exists + On a system where FIPS mode is enabled, /etc/system-fips must exist. +To enable FIPS mode, run the following command: +fips-mode-setup --enable + The system needs to be rebooted for these changes to take effect. + System Crypto Modules must be provided by a vendor that undergoes +FIPS-140 certifications. +FIPS-140 is applicable to all Federal agencies that use +cryptographic-based security systems to protect sensitive information +in computer and telecommunication systems (including voice systems) as +defined in Section 5131 of the Information Technology Management Reform +Act of 1996, Public Law 104-106. This standard shall be used in +designing and implementing cryptographic modules that Federal +departments and agencies operate or are operated for them under +contract. See https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.140-2.pdf +To meet this, the system has to have cryptographic software provided by +a vendor that has undergone this certification. This means providing +documentation, test results, design information, and independent third +party review by an accredited lab. While open source software is +capable of meeting this, it does not meet FIPS-140 unless the vendor +submits to this process. + CCI-000068 + CCI-000803 + CCI-002450 + CIP-003-8 R4.2 + CIP-007-3 R5.1 + SC-12(2) + SC-12(3) + IA-7 + SC-13 + CM-6(a) + SC-12 + SRG-OS-000120-VMM-000600 + SRG-OS-000478-VMM-001980 + SRG-OS-000396-VMM-001590 + Use of weak or untested encryption algorithms undermines the purposes of utilizing encryption to +protect data. The operating system must implement cryptographic modules adhering to the higher +standards approved by the federal government since this provides assurance they have been tested +and validated. + + + + + + + + + Set kernel parameter 'crypto.fips_enabled' to 1 + System running in FIPS mode is indicated by kernel parameter +'crypto.fips_enabled'. This parameter should be set to 1 in FIPS mode. +To enable FIPS mode, run the following command: +fips-mode-setup --enable + +To enable strict FIPS compliance, the fips=1 kernel option needs to be added to the kernel boot +parameters during system installation so key generation is done with FIPS-approved algorithms +and continuous monitoring tests in place. + The system needs to be rebooted for these changes to take effect. + System Crypto Modules must be provided by a vendor that undergoes FIPS-140 certifications. +FIPS-140 is applicable to all Federal agencies that use cryptographic-based security +systems to protect sensitive information in computer and telecommunication systems +(including voice systems) as defined in Section 5131 of the Information Technology +Management Reform Act of 1996, Public Law 104-106. This standard shall be used in designing +and implementing cryptographic modules that Federal departments and agencies operate or are +operated for them under contract. +See https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.140-2.pdf +To meet this, the system has to have cryptographic software provided by a vendor that has +undergone this certification. This means providing documentation, test results, design +information, and independent third party review by an accredited lab. While open source +software is capable of meeting this, it does not meet FIPS-140 unless the vendor submits to +this process. + CCI-000068 + CCI-000803 + CCI-000877 + CCI-001453 + CCI-002418 + CCI-002450 + CCI-002890 + CCI-003123 + CIP-003-8 R4.2 + CIP-007-3 R5.1 + SC-12(2) + SC-12(3) + IA-7 + SC-13 + CM-6(a) + SC-12 + SRG-OS-000033-GPOS-00014 + SRG-OS-000125-GPOS-00065 + SRG-OS-000250-GPOS-00093 + SRG-OS-000393-GPOS-00173 + SRG-OS-000394-GPOS-00174 + SRG-OS-000396-GPOS-00176 + SRG-OS-000423-GPOS-00187 + SRG-OS-000478-GPOS-00223 + SRG-OS-000120-VMM-000600 + SRG-OS-000478-VMM-001980 + SRG-OS-000396-VMM-001590 + Use of weak or untested encryption algorithms undermines the purposes of utilizing encryption to +protect data. The operating system must implement cryptographic modules adhering to the higher +standards approved by the federal government since this provides assurance they have been tested +and validated. + + CCE-83441-6 + + + + + + + + + + System Cryptographic Policies + Linux has the capability to centrally configure cryptographic polices. The command +update-crypto-policies is used to set the policy applicable for the various +cryptographic back-ends, such as SSL/TLS libraries. The configured cryptographic +policies will be the default policy used by these backends unless the application +user configures them otherwise. When the system has been configured to use the +centralized cryptographic policies, the administrator is assured that any application +that utilizes the supported backends will follow a policy that adheres to the +configured profile. + +Currently the supported backends are: +GnuTLS libraryOpenSSL libraryNSS libraryOpenJDKLibkrb5BINDOpenSSH +Applications and languages which rely on any of these backends will follow the +system policies as well. Examples are apache httpd, nginx, php, and others. + + SSH client RekeyLimit - size + Specify the size component of the rekey limit. This limit signifies amount +of data. After this amount of data is transferred through the connection, +the session key is renegotiated. The number is followed by K, M or G for +kilobytes, megabytes or gigabytes. Note that the RekeyLimit can be also +configured according to elapsed time. + 512M + 512M + 1G + + + SSH client RekeyLimit - time + Specify the time component of the rekey limit. The session key is +renegotiated after the defined amount of time passes. The number is followed +by units such as H or M for hours or minutes. Note that the RekeyLimit can +be also configured according to amount of transfered data. + 1h + 1h + + + The system-provided crypto policies + Specify the crypto policy for the system. + DEFAULT + DEFAULT + DEFAULT:NO-SHA1 + FIPS + FIPS:OSPP + LEGACY + FUTURE + NEXT + + + Install crypto-policies package + The crypto-policies package can be installed with the following command: + +$ sudo dnf install crypto-policies + FCS_COP.1(1) + FCS_COP.1(2) + FCS_COP.1(3) + FCS_COP.1(4) + FCS_CKM.1 + FCS_CKM.2 + FCS_TLSC_EXT.1 + SRG-OS-000396-GPOS-00176 + SRG-OS-000393-GPOS-00173 + SRG-OS-000394-GPOS-00174 + Centralized cryptographic policies simplify applying secure ciphers across an operating system and +the applications that run on that operating system. Use of weak or untested encryption algorithms +undermines the purposes of utilizing encryption to protect data. + CCE-83442-4 + +package --add=crypto-policies + + include install_crypto-policies + +class install_crypto-policies { + package { 'crypto-policies': + ensure => 'installed', + } +} + + - name: Ensure crypto-policies is installed + package: + name: crypto-policies + state: present + tags: + - CCE-83442-4 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_crypto-policies_installed + + +[[packages]] +name = "crypto-policies" +version = "*" + + +if ! rpm -q --quiet "crypto-policies" ; then + dnf install -y "crypto-policies" +fi + + + + + + + + + + Configure BIND to use System Crypto Policy + Crypto Policies provide a centralized control over crypto algorithms usage of many packages. +BIND is supported by crypto policy, but the BIND configuration may be +set up to ignore it. + +To check that Crypto Policies settings are configured correctly, ensure that the /etc/named.conf +includes the appropriate configuration: +In the options section of /etc/named.conf, make sure that the following line +is not commented out or superseded by later includes: +include "/etc/crypto-policies/back-ends/bind.config"; + CIP-003-8 R4.2 + CIP-007-3 R5.1 + SC-13 + SC-12(2) + SC-12(3) + SRG-OS-000423-GPOS-00187 + SRG-OS-000426-GPOS-00190 + Overriding the system crypto policy makes the behavior of the BIND service violate expectations, +and makes system configuration more fragmented. + CCE-83451-5 + +function remediate_bind_crypto_policy() { + CONFIG_FILE="/etc/named.conf" + if test -f "$CONFIG_FILE"; then + sed -i 's|options {|&\n\tinclude "/etc/crypto-policies/back-ends/bind.config";|' "$CONFIG_FILE" + return 0 + else + echo "Aborting remediation as '$CONFIG_FILE' was not even found." >&2 + return 1 + fi +} + +remediate_bind_crypto_policy + + + + + + + + + + Configure System Cryptography Policy + To configure the system cryptography policy to use ciphers only from the +policy, run the following command: +$ sudo update-crypto-policies --set +The rule checks if settings for selected crypto policy are configured as expected. Configuration files in the /etc/crypto-policies/back-ends are either symlinks to correct files provided by Crypto-policies package or they are regular files in case crypto policy customizations are applied. +Crypto policies may be customized by crypto policy modules, in which case it is delimited from the base policy using a colon. + The system needs to be rebooted for these changes to take effect. + System Crypto Modules must be provided by a vendor that undergoes +FIPS-140 certifications. +FIPS-140 is applicable to all Federal agencies that use +cryptographic-based security systems to protect sensitive information +in computer and telecommunication systems (including voice systems) as +defined in Section 5131 of the Information Technology Management Reform +Act of 1996, Public Law 104-106. This standard shall be used in +designing and implementing cryptographic modules that Federal +departments and agencies operate or are operated for them under +contract. See https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.140-2.pdf +To meet this, the system has to have cryptographic software provided by +a vendor that has undergone this certification. This means providing +documentation, test results, design information, and independent third +party review by an accredited lab. While open source software is +capable of meeting this, it does not meet FIPS-140 unless the vendor +submits to this process. + 164.308(a)(4)(i) + 164.308(b)(1) + 164.308(b)(3) + 164.312(e)(1) + 164.312(e)(2)(ii) + 1446 + CIP-003-8 R4.2 + CIP-007-3 R5.1 + CIP-007-3 R7.1 + AC-17(a) + AC-17(2) + CM-6(a) + MA-4(6) + SC-13 + SC-12(2) + SC-12(3) + FCS_COP.1(1) + FCS_COP.1(2) + FCS_COP.1(3) + FCS_COP.1(4) + FCS_CKM.1 + FCS_CKM.2 + FCS_TLSC_EXT.1 + SRG-OS-000396-GPOS-00176 + SRG-OS-000393-GPOS-00173 + SRG-OS-000394-GPOS-00174 + Centralized cryptographic policies simplify applying secure ciphers across an operating system and +the applications that run on that operating system. Use of weak or untested encryption algorithms +undermines the purposes of utilizing encryption to protect data. + CCE-83450-7 + - name: XCCDF Value var_system_crypto_policy # promote to variable + set_fact: + var_system_crypto_policy: !!str + tags: + - always + +- name: Configure System Cryptography Policy + lineinfile: + path: /etc/crypto-policies/config + regexp: ^(?!#)(\S+)$ + line: '{{ var_system_crypto_policy }}' + create: true + tags: + - CCE-83450-7 + - NIST-800-53-AC-17(2) + - NIST-800-53-AC-17(a) + - NIST-800-53-CM-6(a) + - NIST-800-53-MA-4(6) + - NIST-800-53-SC-12(2) + - NIST-800-53-SC-12(3) + - NIST-800-53-SC-13 + - configure_crypto_policy + - high_severity + - low_complexity + - low_disruption + - no_reboot_needed + - restrict_strategy + +- name: Verify that Crypto Policy is Set (runtime) + command: /usr/bin/update-crypto-policies --set {{ var_system_crypto_policy }} + tags: + - CCE-83450-7 + - NIST-800-53-AC-17(2) + - NIST-800-53-AC-17(a) + - NIST-800-53-CM-6(a) + - NIST-800-53-MA-4(6) + - NIST-800-53-SC-12(2) + - NIST-800-53-SC-12(3) + - NIST-800-53-SC-13 + - configure_crypto_policy + - high_severity + - low_complexity + - low_disruption + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + systemd: + units: + - name: configure-crypto-policy.service + enabled: true + contents: | + [Unit] + Before=kubelet.service + [Service] + Type=oneshot + ExecStart=update-crypto-policies --set {{.var_system_crypto_policy}} + RemainAfterExit=yes + [Install] + WantedBy=multi-user.target + + +var_system_crypto_policy='' + + +stderr_of_call=$(update-crypto-policies --set ${var_system_crypto_policy} 2>&1 > /dev/null) +rc=$? + +if test "$rc" = 127; then + echo "$stderr_of_call" >&2 + echo "Make sure that the script is installed on the remediated system." >&2 + echo "See output of the 'dnf provides update-crypto-policies' command" >&2 + echo "to see what package to (re)install" >&2 + + false # end with an error code +elif test "$rc" != 0; then + echo "Error invoking the update-crypto-policies script: $stderr_of_call" >&2 + false # end with an error code +fi + + + + + + + + + + + Configure GnuTLS library to use DoD-approved TLS Encryption + Crypto Policies provide a centralized control over crypto algorithms usage of many packages. +GnuTLS is supported by system crypto policy, but the GnuTLS configuration may be +set up to ignore it. + +To check that Crypto Policies settings are configured correctly, ensure that +/etc/crypto-policies/back-ends/gnutls.config contains the following +line and is not commented out: ++VERS-ALL:-VERS-DTLS0.9:-VERS-SSL3.0:-VERS-TLS1.0:-VERS-TLS1.1:-VERS-DTLS1.0 + CCI-001453 + AC-17(2) + SRG-OS-000250-GPOS-00093 + SRG-OS-000423-GPOS-00187 + Overriding the system crypto policy makes the behavior of the GnuTLS +library violate expectations, and makes system configuration more +fragmented. + CCE-86860-4 + - name: 'Configure GnuTLS library to use DoD-approved TLS Encryption: set_fact' + set_fact: + path: /etc/crypto-policies/back-ends/gnutls.config + correct_value: +VERS-ALL:-VERS-DTLS0.9:-VERS-SSL3.0:-VERS-TLS1.0:-VERS-TLS1.1:-VERS-DTLS1.0 + lineinfile_reg: \+VERS-ALL:-VERS-DTLS0\.9:-VERS-SSL3\.0:-VERS-TLS1\.0:-VERS-TLS1\.1:-VERS-DTLS1\.0 + tags: + - CCE-86860-4 + - NIST-800-53-AC-17(2) + - configure_gnutls_tls_crypto_policy + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: 'Configure GnuTLS library to use DoD-approved TLS Encryption: stat' + stat: + path: '{{ path }}' + follow: true + register: gnutls_file + tags: + - CCE-86860-4 + - NIST-800-53-AC-17(2) + - configure_gnutls_tls_crypto_policy + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: 'Configure GnuTLS library to use DoD-approved TLS Encryption: Add' + lineinfile: + path: '{{ path }}' + regexp: '{{ lineinfile_reg }}' + line: '{{ correct_value }}' + create: true + when: not gnutls_file.stat.exists or gnutls_file.stat.size <= correct_value|length + tags: + - CCE-86860-4 + - NIST-800-53-AC-17(2) + - configure_gnutls_tls_crypto_policy + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Configure GnuTLS library to use DoD-approved TLS Encryption + block: + + - name: 'Configure GnuTLS library to use DoD-approved TLS Encryption: Existing value + check' + lineinfile: + path: '{{ path }}' + create: false + regexp: '{{ lineinfile_reg }}' + state: absent + check_mode: true + changed_when: false + register: gnutls + + - name: 'Configure GnuTLS library to use DoD-approved TLS Encryption: Update' + replace: + path: '{{ path }}' + regexp: (\+VERS-ALL(?::-VERS-[A-Z]+\d\.\d)+) + replace: '{{ correct_value }}' + when: gnutls.found is defined and gnutls.found != 1 + when: gnutls_file.stat.exists and gnutls_file.stat.size > correct_value|length + tags: + - CCE-86860-4 + - NIST-800-53-AC-17(2) + - configure_gnutls_tls_crypto_policy + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + +CONF_FILE=/etc/crypto-policies/back-ends/gnutls.config +correct_value='+VERS-ALL:-VERS-DTLS0.9:-VERS-SSL3.0:-VERS-TLS1.0:-VERS-TLS1.1:-VERS-DTLS1.0' + +grep -q ${correct_value} ${CONF_FILE} + +if [[ $? -ne 0 ]]; then + # We need to get the existing value, using PCRE to maintain same regex + existing_value=$(grep -Po '(\+VERS-ALL(?::-VERS-[A-Z]+\d\.\d)+)' ${CONF_FILE}) + + if [[ ! -z ${existing_value} ]]; then + # replace existing_value with correct_value + sed -i "s/${existing_value}/${correct_value}/g" ${CONF_FILE} + else + # ***NOTE*** # + # This probably means this file is not here or it's been modified + # unintentionally. + # ********** # + # echo correct_value to end + echo ${correct_value} >> ${CONF_FILE} + fi +fi + + + + + + + + + + Configure Kerberos to use System Crypto Policy + Crypto Policies provide a centralized control over crypto algorithms usage of many packages. +Kerberos is supported by crypto policy, but it's configuration may be +set up to ignore it. +To check that Crypto Policies settings for Kerberos are configured correctly, examine that there is a symlink at +/etc/krb5.conf.d/crypto-policies targeting /etc/cypto-policies/back-ends/krb5.config. +If the symlink exists, Kerberos is configured to use the system-wide crypto policy settings. + 0418 + 1055 + 1402 + CIP-003-8 R4.2 + CIP-007-3 R5.1 + SC-13 + SC-12(2) + SC-12(3) + SRG-OS-000120-GPOS-00061 + Overriding the system crypto policy makes the behavior of Kerberos violate expectations, +and makes system configuration more fragmented. + CCE-83449-9 + - name: Configure Kerberos to use System Crypto Policy + file: + src: /etc/crypto-policies/back-ends/krb5.config + path: /etc/krb5.conf.d/crypto-policies + state: link + tags: + - CCE-83449-9 + - NIST-800-53-SC-12(2) + - NIST-800-53-SC-12(3) + - NIST-800-53-SC-13 + - configure_kerberos_crypto_policy + - configure_strategy + - high_severity + - low_complexity + - low_disruption + - reboot_required + + +rm -f /etc/krb5.conf.d/crypto-policies +ln -s /etc/crypto-policies/back-ends/krb5.config /etc/krb5.conf.d/crypto-policies + + + + + + + + + + Configure Libreswan to use System Crypto Policy + Crypto Policies provide a centralized control over crypto algorithms usage of many packages. +Libreswan is supported by system crypto policy, but the Libreswan configuration may be +set up to ignore it. + +To check that Crypto Policies settings are configured correctly, ensure that the /etc/ipsec.conf +includes the appropriate configuration file. +In /etc/ipsec.conf, make sure that the following line +is not commented out or superseded by later includes: +include /etc/crypto-policies/back-ends/libreswan.config + CIP-003-8 R4.2 + CIP-007-3 R5.1 + CM-6(a) + MA-4(6) + SC-13 + SC-12(2) + SC-12(3) + FCS_IPSEC_EXT.1.4 + FCS_IPSEC_EXT.1.6 + SRG-OS-000033-GPOS-00014 + Overriding the system crypto policy makes the behavior of the Libreswan +service violate expectations, and makes system configuration more +fragmented. + CCE-83446-5 + - name: Configure Libreswan to use System Crypto Policy + lineinfile: + path: /etc/ipsec.conf + line: include /etc/crypto-policies/back-ends/libreswan.config + create: true + tags: + - CCE-83446-5 + - NIST-800-53-CM-6(a) + - NIST-800-53-MA-4(6) + - NIST-800-53-SC-12(2) + - NIST-800-53-SC-12(3) + - NIST-800-53-SC-13 + - configure_libreswan_crypto_policy + - high_severity + - low_complexity + - low_disruption + - no_reboot_needed + - restrict_strategy + + +function remediate_libreswan_crypto_policy() { + CONFIG_FILE="/etc/ipsec.conf" + if ! grep -qP "^\s*include\s+/etc/crypto-policies/back-ends/libreswan.config\s*(?:#.*)?$" "$CONFIG_FILE" ; then + # the file might not end with a new line + echo -e '\ninclude /etc/crypto-policies/back-ends/libreswan.config' >> "$CONFIG_FILE" + fi + return 0 +} + +remediate_libreswan_crypto_policy + + + + + + + + + + Configure OpenSSL library to use System Crypto Policy + Crypto Policies provide a centralized control over crypto algorithms usage of many packages. +OpenSSL is supported by crypto policy, but the OpenSSL configuration may be +set up to ignore it. +To check that Crypto Policies settings are configured correctly, you have to examine the OpenSSL config file +available under /etc/pki/tls/openssl.cnf. +This file has the ini format, and it enables crypto policy support +if there is a [ crypto_policy ] section that contains the .include = /etc/crypto-policies/back-ends/opensslcnf.config directive. + CCI-001453 + CIP-003-8 R4.2 + CIP-007-3 R5.1 + CIP-007-3 R7.1 + AC-17(a) + AC-17(2) + CM-6(a) + MA-4(6) + SC-13 + SC-12(2) + SC-12(3) + SRG-OS-000250-GPOS-00093 + Overriding the system crypto policy makes the behavior of the Java runtime violates expectations, +and makes system configuration more fragmented. + CCE-83452-3 + - name: Test for crypto_policy group + command: grep '^\s*\[\s*crypto_policy\s*]' /etc/pki/tls/openssl.cnf + register: test_crypto_policy_group + ignore_errors: true + changed_when: false + check_mode: false + tags: + - CCE-83452-3 + - NIST-800-53-AC-17(2) + - NIST-800-53-AC-17(a) + - NIST-800-53-CM-6(a) + - NIST-800-53-MA-4(6) + - NIST-800-53-SC-12(2) + - NIST-800-53-SC-12(3) + - NIST-800-53-SC-13 + - configure_openssl_crypto_policy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Add .include for opensslcnf.config to crypto_policy section + lineinfile: + create: true + insertafter: ^\s*\[\s*crypto_policy\s*]\s* + line: .include = /etc/crypto-policies/back-ends/opensslcnf.config + path: /etc/pki/tls/openssl.cnf + when: + - test_crypto_policy_group.stdout is defined + - test_crypto_policy_group.stdout | length > 0 + tags: + - CCE-83452-3 + - NIST-800-53-AC-17(2) + - NIST-800-53-AC-17(a) + - NIST-800-53-CM-6(a) + - NIST-800-53-MA-4(6) + - NIST-800-53-SC-12(2) + - NIST-800-53-SC-12(3) + - NIST-800-53-SC-13 + - configure_openssl_crypto_policy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Add crypto_policy group and set include opensslcnf.config + lineinfile: + create: true + line: |- + [crypto_policy] + .include = /etc/crypto-policies/back-ends/opensslcnf.config + path: /etc/pki/tls/openssl.cnf + when: + - test_crypto_policy_group.stdout is defined + - test_crypto_policy_group.stdout | length < 1 + tags: + - CCE-83452-3 + - NIST-800-53-AC-17(2) + - NIST-800-53-AC-17(a) + - NIST-800-53-CM-6(a) + - NIST-800-53-MA-4(6) + - NIST-800-53-SC-12(2) + - NIST-800-53-SC-12(3) + - NIST-800-53-SC-13 + - configure_openssl_crypto_policy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + + +OPENSSL_CRYPTO_POLICY_SECTION='[ crypto_policy ]' +OPENSSL_CRYPTO_POLICY_SECTION_REGEX='\[\s*crypto_policy\s*\]' +OPENSSL_CRYPTO_POLICY_INCLUSION='.include = /etc/crypto-policies/back-ends/opensslcnf.config' +OPENSSL_CRYPTO_POLICY_INCLUSION_REGEX='^\s*\.include\s*(?:=\s*)?/etc/crypto-policies/back-ends/opensslcnf.config$' + + + + + +function remediate_openssl_crypto_policy() { + CONFIG_FILE=/etc/pki/tls/openssl.cnf + if test -f "$CONFIG_FILE"; then + if ! grep -q "^\\s*$OPENSSL_CRYPTO_POLICY_SECTION_REGEX" "$CONFIG_FILE"; then + printf '\n%s\n\n%s' "$OPENSSL_CRYPTO_POLICY_SECTION" "$OPENSSL_CRYPTO_POLICY_INCLUSION" >> "$CONFIG_FILE" + return 0 + elif ! grep -q "^\\s*$OPENSSL_CRYPTO_POLICY_INCLUSION_REGEX" "$CONFIG_FILE"; then + sed -i "s|$OPENSSL_CRYPTO_POLICY_SECTION_REGEX|&\\n\\n$OPENSSL_CRYPTO_POLICY_INCLUSION\\n|" "$CONFIG_FILE" + return 0 + fi + else + echo "Aborting remediation as '$CONFIG_FILE' was not even found." >&2 + return 1 + fi +} + +remediate_openssl_crypto_policy + + + + + + + + + + Configure OpenSSL library to use TLS Encryption + Crypto Policies are means of enforcing certain cryptographic settings for +selected applications including OpenSSL. OpenSSL is by default configured to +modify its configuration based on currently configured Crypto Policy. +Editing the Crypto Policy back-end is not recommended. + +Check the crypto-policies(7) man page and choose a policy that configures TLS +protocol to version 1.2 or higher, for example DEFAULT, FUTURE or FIPS policy. +Or create and apply a custom policy that restricts minimum TLS version to 1.2. + +For example for versions prior to crypto-policies-20210617-1.gitc776d3e.el8.noarch +this is expected: + +$ sudo grep -i MinProtocol /etc/crypto-policies/back-ends/opensslcnf.config + +MinProtocol = TLSv1.2 + + +Or for version crypto-policies-20210617-1.gitc776d3e.el8.noarch and newer this is +expected: + +$ sudo grep -i MinProtocol /etc/crypto-policies/back-ends/opensslcnf.config + +TLS.MinProtocol = TLSv1.2 +DTLS.MinProtocol = DTLSv1.2 + This rule doesn't come with a remediation, automatically changing the crypto-policies may be too disruptive. +Ensure the variable xccdf_org.ssgproject.content_value_var_system_crypto_policy is set to a +Crypto Policy that satisfies OpenSSL minimum TLS protocol version 1.2. Custom policies may be applied too. + CCI-001453 + AC-17(2) + SRG-OS-000125-GPOS-00065 + SRG-OS-000250-GPOS-00093 + SRG-OS-000393-GPOS-00173 + SRG-OS-000394-GPOS-00174 + Without cryptographic integrity protections, information can be altered by +unauthorized users without detection. + CCE-83448-1 + + + + + + + + + Configure SSH to use System Crypto Policy + Crypto Policies provide a centralized control over crypto algorithms usage of many packages. +SSH is supported by crypto policy, but the SSH configuration may be +set up to ignore it. +To check that Crypto Policies settings are configured correctly, ensure that +the CRYPTO_POLICY variable is either commented or not set at all +in the /etc/sysconfig/sshd. + CCI-001453 + 164.308(a)(4)(i) + 164.308(b)(1) + 164.308(b)(3) + 164.312(e)(1) + 164.312(e)(2)(ii) + CIP-003-8 R4.2 + CIP-007-3 R5.1 + CIP-007-3 R7.1 + AC-17(a) + AC-17(2) + CM-6(a) + MA-4(6) + SC-13 + FCS_SSH_EXT.1 + FCS_SSHS_EXT.1 + FCS_SSHC_EXT.1 + SRG-OS-000250-GPOS-00093 + Overriding the system crypto policy makes the behavior of the SSH service violate expectations, +and makes system configuration more fragmented. + CCE-83445-7 + - name: Configure SSH to use System Crypto Policy + lineinfile: + dest: /etc/sysconfig/sshd + state: absent + regexp: ^\s*(?i)CRYPTO_POLICY.*$ + tags: + - CCE-83445-7 + - NIST-800-53-AC-17(2) + - NIST-800-53-AC-17(a) + - NIST-800-53-CM-6(a) + - NIST-800-53-MA-4(6) + - NIST-800-53-SC-13 + - configure_ssh_crypto_policy + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + + +SSH_CONF="/etc/sysconfig/sshd" + +sed -i "/^\s*CRYPTO_POLICY.*$/Id" $SSH_CONF + + + + + + + + + + Harden SSH client Crypto Policy + Crypto Policies are means of enforcing certain cryptographic settings for selected applications including OpenSSH client. +To override the system wide crypto policy for Openssh client, place a file in the /etc/ssh/ssh_config.d/ so that it is loaded before the 05-redhat.conf. In this case it is file named 02-ospp.conf containing parameters which need to be changed with respect to the crypto policy. +This rule checks if the file exists and if it contains required parameters and values which modify the Crypto Policy. +During the parsing process, as soon as Openssh client parses some configuration option and its value, it remembers it and ignores any subsequent overrides. The customization mechanism provided by crypto policies appends eventual customizations at the end of the system wide crypto policy. Therefore, if the crypto policy customization overrides some parameter which is already configured in the system wide crypto policy, the SSH client will not honor that customized parameter. + CIP-003-8 R4.2 + CIP-007-3 R5.1 + CIP-007-3 R7.1 + AC-17(a) + AC-17(2) + CM-6(a) + MA-4(6) + SC-13 + FCS_SSHC_EXT.1 + SRG-OS-000033-GPOS-00014 + SRG-OS-000250-GPOS-00093 + SRG-OS-000393-GPOS-00173 + SRG-OS-000394-GPOS-00174 + The Common Criteria requirements specify how certain parameters for OpenSSH Client are configured. Particular parameters are RekeyLimit, GSSAPIAuthentication, Ciphers, PubkeyAcceptedKeyTypes, MACs and KexAlgorithms. Currently particular requirements specified by CC are stricter compared to any existing Crypto Policy. + CCE-86230-0 + +#the file starts with 02 so that it is loaded before the 05-redhat.conf which activates configuration provided by system vide crypto policy +file="/etc/ssh/ssh_config.d/02-ospp.conf" +echo -e "Match final all\n\ +RekeyLimit 512M 1h\n\ +GSSAPIAuthentication no\n\ +Ciphers aes256-ctr,aes256-cbc,aes128-ctr,aes128-cbc\n\ +PubkeyAcceptedKeyTypes ssh-rsa,ecdsa-sha2-nistp384,ecdsa-sha2-nistp256\n\ +MACs hmac-sha2-512,hmac-sha2-256\n\ +KexAlgorithms ecdh-sha2-nistp521,ecdh-sha2-nistp384,ecdh-sha2-nistp256,diffie-hellman-group14-sha1\n" > "$file" + + + + + + + + + + Configure SSH Client to Use FIPS 140-2 Validated Ciphers: openssh.config + Crypto Policies provide a centralized control over crypto algorithms usage of many packages. +OpenSSH is supported by system crypto policy, but the OpenSSH configuration may be +set up incorrectly. + +To check that Crypto Policies settings for ciphers are configured correctly, ensure that +/etc/crypto-policies/back-ends/openssh.config contains the following +line and is not commented out: +Ciphers + The system needs to be rebooted for these changes to take effect. + System Crypto Modules must be provided by a vendor that undergoes +FIPS-140 certifications. +FIPS-140 is applicable to all Federal agencies that use +cryptographic-based security systems to protect sensitive information +in computer and telecommunication systems (including voice systems) as +defined in Section 5131 of the Information Technology Management Reform +Act of 1996, Public Law 104-106. This standard shall be used in +designing and implementing cryptographic modules that Federal +departments and agencies operate or are operated for them under +contract. See https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.140-2.pdf +To meet this, the system has to have cryptographic software provided by +a vendor that has undergone this certification. This means providing +documentation, test results, design information, and independent third +party review by an accredited lab. While open source software is +capable of meeting this, it does not meet FIPS-140 unless the vendor +submits to this process. + CCI-000068 + CCI-000877 + CCI-001453 + CCI-002418 + CCI-002890 + CCI-003123 + AC-17(2) + SRG-OS-000033-GPOS-00014 + SRG-OS-000125-GPOS-00065 + SRG-OS-000250-GPOS-00093 + SRG-OS-000393-GPOS-00173 + SRG-OS-000394-GPOS-00174 + SRG-OS-000423-GPOS-00187 + Overriding the system crypto policy makes the behavior of the OpenSSH client +violate expectations, and makes system configuration more fragmented. By +specifying a cipher list with the order of ciphers being in a “strongest to +weakest” orientation, the system will automatically attempt to use the +strongest cipher for securing SSH connections. + CCE-90125-6 + + + + + + + + + + Configure SSH Server to Use FIPS 140-2 Validated Ciphers: opensshserver.config + Crypto Policies provide a centralized control over crypto algorithms usage of many packages. +OpenSSH is supported by system crypto policy, but the OpenSSH configuration may be +set up incorrectly. + +To check that Crypto Policies settings for ciphers are configured correctly, ensure that +/etc/crypto-policies/back-ends/opensshserver.config contains the following +text and is not commented out: +-oCiphers= + The system needs to be rebooted for these changes to take effect. + System Crypto Modules must be provided by a vendor that undergoes +FIPS-140 certifications. +FIPS-140 is applicable to all Federal agencies that use +cryptographic-based security systems to protect sensitive information +in computer and telecommunication systems (including voice systems) as +defined in Section 5131 of the Information Technology Management Reform +Act of 1996, Public Law 104-106. This standard shall be used in +designing and implementing cryptographic modules that Federal +departments and agencies operate or are operated for them under +contract. See https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.140-2.pdf +To meet this, the system has to have cryptographic software provided by +a vendor that has undergone this certification. This means providing +documentation, test results, design information, and independent third +party review by an accredited lab. While open source software is +capable of meeting this, it does not meet FIPS-140 unless the vendor +submits to this process. + CCI-000877 + CCI-001453 + AC-17(2) + SRG-OS-000125-GPOS-00065 + SRG-OS-000250-GPOS-00093 + Overriding the system crypto policy makes the behavior of the OpenSSH server +violate expectations, and makes system configuration more fragmented. By +specifying a cipher list with the order of ciphers being in a “strongest to +weakest” orientation, the system will automatically attempt to use the +strongest cipher for securing SSH connections. + CCE-87332-3 + + + + + + + + + + Configure SSH Client to Use FIPS 140-2 Validated MACs: openssh.config + Crypto Policies provide a centralized control over crypto algorithms usage of many packages. +OpenSSH is supported by system crypto policy, but the OpenSSH configuration may be +set up incorrectly. + +To check that Crypto Policies settings are configured correctly, ensure that +/etc/crypto-policies/back-ends/openssh.config contains the following +line and is not commented out: +MACs hmac-sha2-512,hmac-sha2-256 + The system needs to be rebooted for these changes to take effect. + System Crypto Modules must be provided by a vendor that undergoes +FIPS-140 certifications. +FIPS-140 is applicable to all Federal agencies that use +cryptographic-based security systems to protect sensitive information +in computer and telecommunication systems (including voice systems) as +defined in Section 5131 of the Information Technology Management Reform +Act of 1996, Public Law 104-106. This standard shall be used in +designing and implementing cryptographic modules that Federal +departments and agencies operate or are operated for them under +contract. See https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.140-2.pdf +To meet this, the system has to have cryptographic software provided by +a vendor that has undergone this certification. This means providing +documentation, test results, design information, and independent third +party review by an accredited lab. While open source software is +capable of meeting this, it does not meet FIPS-140 unless the vendor +submits to this process. + CCI-000877 + CCI-001453 + AC-17(2) + SRG-OS-000125-GPOS-00065 + SRG-OS-000250-GPOS-00093 + Overriding the system crypto policy makes the behavior of the OpenSSH +client violate expectations, and makes system configuration more +fragmented. + CCE-86208-6 + + + + + + + + + + Configure SSH Server to Use FIPS 140-2 Validated MACs: opensshserver.config + Crypto Policies provide a centralized control over crypto algorithms usage of many packages. +OpenSSH is supported by system crypto policy, but the OpenSSH configuration may be +set up incorrectly. + +To check that Crypto Policies settings are configured correctly, ensure that +/etc/crypto-policies/back-ends/opensshserver.config contains the following +text and is not commented out: +-oMACS=hmac-sha2-512,hmac-sha2-256 + The system needs to be rebooted for these changes to take effect. + System Crypto Modules must be provided by a vendor that undergoes +FIPS-140 certifications. +FIPS-140 is applicable to all Federal agencies that use +cryptographic-based security systems to protect sensitive information +in computer and telecommunication systems (including voice systems) as +defined in Section 5131 of the Information Technology Management Reform +Act of 1996, Public Law 104-106. This standard shall be used in +designing and implementing cryptographic modules that Federal +departments and agencies operate or are operated for them under +contract. See https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.140-2.pdf +To meet this, the system has to have cryptographic software provided by +a vendor that has undergone this certification. This means providing +documentation, test results, design information, and independent third +party review by an accredited lab. While open source software is +capable of meeting this, it does not meet FIPS-140 unless the vendor +submits to this process. + CCI-000877 + CCI-001453 + AC-17(2) + SRG-OS-000125-GPOS-00065 + SRG-OS-000250-GPOS-00093 + Overriding the system crypto policy makes the behavior of the OpenSSH +server violate expectations, and makes system configuration more +fragmented. + CCE-87567-4 + + + + + + + + + + + Operating System Vendor Support and Certification + The assurance of a vendor to provide operating system support and maintenance +for their product is an important criterion to ensure product stability and +security over the life of the product. A certified product that follows the +necessary standards and government certification requirements guarantees that +known software vulnerabilities will be remediated, and proper guidance for +protecting and securing the operating system will be given. + + The Installed Operating System Is FIPS 140-2 Certified + To enable processing of sensitive information the operating system must +provide certified cryptographic modules compliant with FIPS 140-2 +standard. + There is no remediation besides switching to a different operating system. + System Crypto Modules must be provided by a vendor that undergoes +FIPS-140 certifications. +FIPS-140 is applicable to all Federal agencies that use +cryptographic-based security systems to protect sensitive information +in computer and telecommunication systems (including voice systems) as +defined in Section 5131 of the Information Technology Management Reform +Act of 1996, Public Law 104-106. This standard shall be used in +designing and implementing cryptographic modules that Federal +departments and agencies operate or are operated for them under +contract. See https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.140-2.pdf +To meet this, the system has to have cryptographic software provided by +a vendor that has undergone this certification. This means providing +documentation, test results, design information, and independent third +party review by an accredited lab. While open source software is +capable of meeting this, it does not meet FIPS-140 unless the vendor +submits to this process. + CCI-000803 + CCI-002450 + CIP-003-8 R4.2 + CIP-007-3 R5.1 + SC-12(2) + SC-12(3) + IA-7 + SC-13 + CM-6(a) + SC-12 + SRG-OS-000120-VMM-000600 + SRG-OS-000478-VMM-001980 + SRG-OS-000396-VMM-001590 + The Federal Information Processing Standard (FIPS) Publication 140-2, (FIPS +PUB 140-2) is a computer security standard. The standard specifies security +requirements for cryptographic modules used to protect sensitive +unclassified information. Refer to the full FIPS 140-2 standard at + + http://csrc.nist.gov/publications/fips/fips140-2/fips1402.pdf +for further details on the requirements. +FIPS 140-2 validation is required by U.S. law when information systems use +cryptography to protect sensitive government information. In order to +achieve FIPS 140-2 certification, cryptographic modules are subject to +extensive testing by independent laboratories, accredited by National +Institute of Standards and Technology (NIST). + + + + + + + + + The Installed Operating System Is Vendor Supported + The installed operating system must be maintained by a vendor. + +Red Hat Enterprise Linux is supported by Red Hat, Inc. As the Red Hat Enterprise +Linux vendor, Red Hat, Inc. is responsible for providing security patches. + There is no remediation besides switching to a different operating system. + 18 + 20 + 4 + APO12.01 + APO12.02 + APO12.03 + APO12.04 + BAI03.10 + DSS05.01 + DSS05.02 + CCI-000366 + 4.2.3 + 4.2.3.12 + 4.2.3.7 + 4.2.3.9 + A.12.6.1 + A.14.2.3 + A.16.1.3 + A.18.2.2 + A.18.2.3 + CM-6(a) + MA-6 + SA-13(a) + ID.RA-1 + PR.IP-12 + SRG-OS-000480-GPOS-00227 + An operating system is considered "supported" if the vendor continues to +provide security patches for the product. With an unsupported release, it +will not be possible to resolve any security issue discovered in the system +software. + CCE-83453-1 + + + + + + + + + + Endpoint Protection Software + Endpoint protection security software that is not provided or supported + +by Red Hat can be installed to provide complementary or duplicative + +security capabilities to those provided by the base platform. Add-on +software may not be appropriate for some specialized systems. + + Configure Backups of User Data + The operating system must conduct backups of user data contained +in the operating system. The operating system provides utilities for +automating backups of user data. Commercial and open-source products +are also available. + Operating system backup is a critical step in maintaining data assurance and +availability. User-level information is data generated by information system +and/or application users. Backups shall be consistent with organizational +recovery time and recovery point objectives. + + + + + + Install Intrusion Detection Software + The base Red Hat Enterprise Linux 9 platform already includes a sophisticated auditing system that +can detect intruder activity, as well as SELinux, which provides host-based +intrusion prevention capabilities by confining privileged programs and user +sessions which may become compromised. + In DoD environments, supplemental intrusion detection and antivirus tools, +such as the McAfee Host-based Security System, are available to integrate with +existing infrastructure. Per DISA guidance, when these supplemental tools interfere +with proper functioning of SELinux, SELinux takes precedence. Should further +clarification be required, DISA contact information is published publicly at +https://public.cyber.mil/stigs/ + 1 + 12 + 13 + 14 + 15 + 16 + 18 + 7 + 8 + 9 + APO01.06 + APO13.01 + DSS01.03 + DSS01.05 + DSS03.05 + DSS05.02 + DSS05.04 + DSS05.07 + DSS06.02 + CCI-001263 + 4.3.3.4 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.2 + SR 7.1 + SR 7.6 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-6(a) + DE.CM-1 + PR.AC-5 + PR.DS-5 + PR.PT-4 + Req-11.4 + Host-based intrusion detection tools provide a system-level defense when an +intruder gains access to a system or network. + + + CCE-88837-0 + + + + + + + + + McAfee Endpoint Security Software + In DoD environments, McAfee Host-based Security System (HBSS) and +VirusScan Enterprise for Linux (VSEL) is required to be installed on all systems. + + The age of McAfee defintion file before requiring updating + Specify the amount of time (in seconds) before McAfee definition files need to be +updated. + 2592000 + 86400 + 604800 + 2592000 + + + McAfee Endpoint Security for Linux (ENSL) + McAfee Endpoint Security for Linux (ENSL) is a suite of software applications +used to monitor, detect, and defend computer networks and systems. + + + Install McAfee Endpoint Security for Linux (ENSL) + Install McAfee Endpoint Security for Linux antivirus software +which is provided for DoD systems and uses signatures to search for the +presence of viruses on the filesystem. + +The McAfeeTP package can be installed with the following command: + +$ sudo dnf install McAfeeTP + Due to McAfee Endpoint Security for Linux (ENSL) being 3rd party software, +automated remediation is not available for this configuration check. + CCI-001233 + SI-2(2) + SRG-OS-000191-GPOS-00080 + Virus scanning software can be used to detect if a system has been compromised by +computer viruses, as well as to limit their spread to other systems. + CCE-86236-7 + + + + + + + + + Ensure McAfee Endpoint Security for Linux (ENSL) is running + Install McAfee Endpoint Security for Linux antivirus software +which is provided for DoD systems and uses signatures to search for the +presence of viruses on the filesystem. + Due to McAfee Endpoint Security for Linux (ENSL) being 3rd party software, +automated remediation is not available for this configuration check. + CCI-001233 + SI-2(2) + SRG-OS-000191-GPOS-00080 + Virus scanning software can be used to detect if a system has been compromised by +computer viruses, as well as to limit their spread to other systems. + CCE-88806-5 + + + + + + + + + + McAfee Host-Based Intrusion Detection Software (HBSS) + McAfee Host-based Security System (HBSS) is a suite of software applications +used to monitor, detect, and defend computer networks and systems. + + Install the Host Intrusion Prevention System (HIPS) Module + Install the McAfee Host Intrusion Prevention System (HIPS) Module if it is absolutely +necessary. If SELinux is enabled, do not install or enable this module. + Installing and enabling this module conflicts with SELinux. +Per DoD/DISA guidance, SELinux takes precedence over this module. + Due to McAfee HIPS being 3rd party software, automated +remediation is not available for this configuration check. + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 18 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + APO01.06 + APO07.06 + APO08.04 + APO10.05 + APO11.06 + APO12.01 + APO12.02 + APO12.03 + APO12.04 + APO12.06 + APO13.01 + APO13.02 + BAI08.02 + BAI08.04 + DSS01.03 + DSS01.05 + DSS02.04 + DSS02.05 + DSS02.07 + DSS03.01 + DSS03.04 + DSS03.05 + DSS04.05 + DSS05.01 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + DSS06.01 + DSS06.02 + MEA03.03 + MEA03.04 + CCI-000366 + CCI-001233 + CCI-001263 + 4.2.3 + 4.2.3.12 + 4.2.3.7 + 4.2.3.9 + 4.3.3.4 + 4.3.4.5.2 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.3.4.5.9 + 4.4.3.2 + 4.4.3.3 + 4.4.3.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.4 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.3 + SR 3.5 + SR 3.8 + SR 3.9 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.12.1.1 + A.12.1.2 + A.12.4.1 + A.12.4.3 + A.12.5.1 + A.12.6.1 + A.12.6.2 + A.13.1.1 + A.13.1.2 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.14.2.7 + A.14.2.8 + A.15.2.1 + A.16.1.1 + A.16.1.2 + A.16.1.3 + A.16.1.4 + A.16.1.5 + A.16.1.6 + A.16.1.7 + A.18.1.4 + A.18.2.2 + A.18.2.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + Clause 16.1.2 + Clause 7.4 + CM-6(a) + DE.AE-1 + DE.AE-2 + DE.AE-3 + DE.AE-4 + DE.CM-1 + DE.CM-5 + DE.CM-6 + DE.CM-7 + DE.DP-2 + DE.DP-3 + DE.DP-4 + DE.DP-5 + ID.RA-1 + PR.AC-5 + PR.DS-5 + PR.IP-8 + PR.PT-4 + RS.AN-1 + RS.CO-3 + Req-11.4 + SRG-OS-000191-GPOS-00080 + SRG-OS-000196 + SRG-OS-000480-GPOS-00227 + Without a host-based intrusion detection tool, there is no system-level defense +when an intruder gains access to a system or network. Additionally, a host-based +intrusion prevention tool can provide methods to immediately lock out detected +intrusion attempts. + + CCE-89466-7 + +[[packages]] +name = "MFEhiplsm" +version = "*" + + + + + + + + + + + + + + Disk Partitioning + To ensure separation and protection of data, there +are top-level system directories which should be placed on their +own physical partition or logical volume. The installer's default +partitioning scheme creates separate logical volumes for +/, /boot, and swap. +If starting with any of the default layouts, check the box to +\"Review and modify partitioning.\" This allows for the easy creation +of additional logical volumes inside the volume group already +created, though it may require making /'s logical volume smaller to +create space. In general, using logical volumes is preferable to +using partitions because they can be more easily adjusted +later.If creating a custom layout, create the partitions mentioned in +the previous paragraph (which the installer will require anyway), +as well as separate ones described in the following sections. +If a system has already been installed, and the default +partitioning +scheme was used, it is possible but nontrivial to +modify it to create separate logical volumes for the directories +listed above. The Logical Volume Manager (LVM) makes this possible. +See the LVM HOWTO at + http://tldp.org/HOWTO/LVM-HOWTO/ +for more detailed information on LVM. + + Encrypt Partitions + Red Hat Enterprise Linux 9 natively supports partition encryption through the +Linux Unified Key Setup-on-disk-format (LUKS) technology. The easiest way to +encrypt a partition is during installation time. + +For manual installations, select the Encrypt checkbox during +partition creation to encrypt the partition. When this +option is selected the system will prompt for a passphrase to use in +decrypting the partition. The passphrase will subsequently need to be entered manually +every time the system boots. + + +For automated/unattended installations, it is possible to use Kickstart by adding +the --encrypted and --passphrase= options to the definition of each partition to be +encrypted. For example, the following line would encrypt the root partition: +part / --fstype=ext4 --size=100 --onpart=hda1 --encrypted --passphrase=PASSPHRASE +Any PASSPHRASE is stored in the Kickstart in plaintext, and the Kickstart +must then be protected accordingly. +Omitting the --passphrase= option from the partition definition will cause the +installer to pause and interactively ask for the passphrase during installation. + +By default, the Anaconda installer uses aes-xts-plain64 cipher +with a minimum 512 bit key size which should be compatible with FIPS enabled. + + +Detailed information on encrypting partitions using LUKS or LUKS ciphers can be found on +the Red Hat Enterprise Linux 9 Documentation web site: + + + https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/8/html/security_hardening/encrypting-block-devices-using-luks_security-hardening +. + 13 + 14 + APO01.06 + BAI02.01 + BAI06.01 + DSS04.07 + DSS05.03 + DSS05.04 + DSS05.07 + DSS06.02 + DSS06.06 + 3.13.16 + CCI-001199 + CCI-002475 + CCI-002476 + 164.308(a)(1)(ii)(D) + 164.308(b)(1) + 164.310(d) + 164.312(a)(1) + 164.312(a)(2)(iii) + 164.312(a)(2)(iv) + 164.312(b) + 164.312(c) + 164.314(b)(2)(i) + 164.312(d) + SR 3.4 + SR 4.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R4.2 + CIP-007-3 R5.1 + CM-6(a) + SC-28 + SC-28(1) + SC-13 + AU-9(3) + PR.DS-1 + PR.DS-5 + SRG-OS-000405-GPOS-00184 + SRG-OS-000185-GPOS-00079 + SRG-OS-000404-GPOS-00183 + SRG-OS-000404-VMM-001650 + SRG-OS-000405-VMM-001660 + The risk of a system's physical compromise, particularly mobile systems such as +laptops, places its data at risk of compromise. Encrypting this data mitigates +the risk of its loss if the system is lost. + + CCE-90849-1 + + + + + + Ensure /home Located On Separate Partition + If user home directories will be stored locally, create a separate partition +for /home at installation time (or migrate it later using LVM). If +/home will be mounted from another system such as an NFS server, then +creating a separate partition is not necessary at installation time, and the +mountpoint can instead be configured later. + BP28(R12) + 12 + 15 + 8 + APO13.01 + DSS05.02 + CCI-000366 + CCI-001208 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + A.13.1.1 + A.13.2.1 + A.14.1.3 + CM-6(a) + SC-5(2) + PR.PT-4 + SRG-OS-000480-GPOS-00227 + Ensuring that /home is mounted on its own partition enables the +setting of more restrictive mount options, and also helps ensure that +users cannot trivially fill partitions used for log or audit data storage. + + CCE-83468-9 + +part /home + + +[[customizations.filesystem]] +mountpoint = "/home" +size = 1073741824 + + + + + + + + + + Ensure /srv Located On Separate Partition + If a file server (FTP, TFTP...) is hosted locally, create a separate partition +for /srv at installation time (or migrate it later using LVM). If +/srv will be mounted from another system such as an NFS server, then +creating a separate partition is not necessary at installation time, and the +mountpoint can instead be configured later. + BP28(R12) + Srv deserves files for local network file server such as FTP. Ensuring +that /srv is mounted on its own partition enables the setting of +more restrictive mount options, and also helps ensure that +users cannot trivially fill partitions used for log or audit data storage. + + CCE-90846-7 + +part /srv + + +[[customizations.filesystem]] +mountpoint = "/srv" +size = 1073741824 + + + + + + + + + + Ensure /tmp Located On Separate Partition + The /tmp directory is a world-writable directory used +for temporary file storage. Ensure it has its own partition or +logical volume at installation time, or migrate it using LVM. + BP28(R12) + 12 + 15 + 8 + APO13.01 + DSS05.02 + CCI-000366 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + A.13.1.1 + A.13.2.1 + A.14.1.3 + CM-6(a) + SC-5(2) + PR.PT-4 + SRG-OS-000480-GPOS-00227 + The /tmp partition is used as temporary storage by many programs. +Placing /tmp in its own partition enables the setting of more +restrictive mount options, which can help protect programs which use it. + + CCE-90845-9 + +part /tmp + + +[[customizations.filesystem]] +mountpoint = "/tmp" +size = 1073741824 + + + + + + + + + + Ensure /var Located On Separate Partition + The /var directory is used by daemons and other system +services to store frequently-changing data. Ensure that /var has its own partition +or logical volume at installation time, or migrate it using LVM. + BP28(R12) + 12 + 15 + 8 + APO13.01 + DSS05.02 + CCI-000366 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + A.13.1.1 + A.13.2.1 + A.14.1.3 + CM-6(a) + SC-5(2) + PR.PT-4 + SRG-OS-000480-GPOS-00227 + SRG-OS-000341-VMM-001220 + Ensuring that /var is mounted on its own partition enables the +setting of more restrictive mount options. This helps protect +system services such as daemons or other programs which use it. +It is not uncommon for the /var directory to contain +world-writable directories installed by other software packages. + + CCE-83466-3 + +part /var + + +[[customizations.filesystem]] +mountpoint = "/var" +size = 3221225472 + + + + + + + + + + Ensure /var/log Located On Separate Partition + System logs are stored in the /var/log directory. + +Ensure that /var/log has its own partition or logical +volume at installation time, or migrate it using LVM. + BP28(R12) + BP28(R47) + 1 + 12 + 14 + 15 + 16 + 3 + 5 + 6 + 8 + APO11.04 + APO13.01 + BAI03.05 + DSS05.02 + DSS05.04 + DSS05.07 + MEA02.01 + CCI-000366 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + CIP-007-3 R6.5 + CM-6(a) + AU-4 + SC-5(2) + PR.PT-1 + PR.PT-4 + SRG-OS-000480-GPOS-00227 + Placing /var/log in its own partition +enables better separation between log files +and other files in /var/. + + CCE-90848-3 + +part /var/log + + +[[customizations.filesystem]] +mountpoint = "/var/log" +size = 5368709120 + + + + + + + + + + Ensure /var/log/audit Located On Separate Partition + Audit logs are stored in the /var/log/audit directory. + +Ensure that /var/log/audit has its own partition or logical +volume at installation time, or migrate it using LVM. +Make absolutely certain that it is large enough to store all +audit logs that will be created by the auditing daemon. + BP28(R43) + 1 + 12 + 13 + 14 + 15 + 16 + 2 + 3 + 5 + 6 + 8 + APO11.04 + APO13.01 + BAI03.05 + BAI04.04 + DSS05.02 + DSS05.04 + DSS05.07 + MEA02.01 + CCI-000366 + CCI-001849 + 164.312(a)(2)(ii) + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.2 + SR 7.6 + A.12.1.3 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.17.2.1 + CIP-007-3 R6.5 + CM-6(a) + AU-4 + SC-5(2) + PR.DS-4 + PR.PT-1 + PR.PT-4 + FMT_SMF_EXT.1 + SRG-OS-000341-GPOS-00132 + SRG-OS-000480-GPOS-00227 + SRG-OS-000341-VMM-001220 + Placing /var/log/audit in its own partition +enables better separation between audit files +and other files, and helps ensure that +auditing cannot be halted due to the partition running out +of space. + + CCE-90847-5 + +part /var/log/audit + + +[[customizations.filesystem]] +mountpoint = "/var/log/audit" +size = 10737418240 + + + + + + + + + + Ensure /var/tmp Located On Separate Partition + The /var/tmp directory is a world-writable directory used +for temporary file storage. Ensure it has its own partition or +logical volume at installation time, or migrate it using LVM. + BP28(R12) + SRG-OS-000480-GPOS-00227 + The /var/tmp partition is used as temporary storage by many programs. +Placing /var/tmp in its own partition enables the setting of more +restrictive mount options, which can help protect programs which use it. + + CCE-83487-9 + +part /var/tmp + + +[[customizations.filesystem]] +mountpoint = "/var/tmp" +size = 1073741824 + + + + + + + + + + + GNOME Desktop Environment + GNOME is a graphical desktop environment bundled with many Linux distributions that +allow users to easily interact with the operating system graphically rather than +textually. The GNOME Graphical Display Manager (GDM) provides login, logout, and user +switching contexts as well as display server management. + +GNOME is developed by the GNOME Project and is considered the default + +Red Hat Graphical environment. + + +For more information on GNOME and the GNOME Project, see https://www.gnome.org. + + + Remove the GDM Package Group + +By removing the gdm package, the system no longer has GNOME installed + +installed. If X Windows is not installed then the system cannot boot into graphical user mode. +This prevents the system from being accidentally or maliciously booted into a graphical.target +mode. To do so, run the following command: + +$ sudo yum remove gdm + CM-7(a) + CM-7(b) + CM-6(a) + SRG-OS-000480-GPOS-00227 + Unnecessary service packages must not be installed to decrease the attack surface of the system. +A graphical environment is unnecessary for certain types of systems including a virtualization +hypervisor. + CCE-83549-6 + +package --remove=gdm + + include remove_gdm + +class remove_gdm { + package { 'gdm': + ensure => 'purged', + } +} + + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83549-6 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_gdm_removed + +- name: Ensure gdm is removed + package: + name: gdm + state: absent + when: '"gdm" in ansible_facts.packages' + tags: + - CCE-83549-6 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_gdm_removed + + # Remediation is applicable only in certain platforms +if rpm --quiet -q gdm; then + +# CAUTION: This remediation script will remove gdm +# from the system, and may remove any packages +# that depend on gdm. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "gdm" ; then + + dnf remove -y "gdm" + +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Make sure that the dconf databases are up-to-date with regards to respective keyfiles + By default, DConf uses a binary database as a data backend. +The system-level database is compiled from keyfiles in the /etc/dconf/db/ +directory by the dconf update command. More specifically, content present +in the following directories: +/etc/dconf/db/distro.d +/etc/dconf/db/local.d + 164.308(a)(1)(ii)(B) + 164.308(a)(5)(ii)(A) + SRG-OS-000480-GPOS-00227 + Unlike text-based keyfiles, the binary database is impossible to check by OVAL. +Therefore, in order to evaluate dconf configuration, both have to be true at the same time - +configuration files have to be compliant, and the database needs to be more recent than those keyfiles, +which gives confidence that it reflects them. + + CCE-87295-2 + # Remediation is applicable only in certain platforms +if rpm --quiet -q gdm && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +dconf update + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure GNOME3 DConf User Profile + By default, DConf provides a standard user profile. This profile contains a list +of DConf configuration databases. The user profile and database always take the +highest priority. As such the DConf User profile should always exist and be +configured correctly. + + +To make sure that the user profile is configured correctly, the /etc/dconf/profile/user +should be set as follows: +user-db:user +system-db:local +system-db:site +system-db:distro + + Failure to have a functional DConf profile prevents GNOME3 configuration settings +from being enforced for all users and allows various security risks. + + CCE-88767-9 + + + + + + + + + Configure GNOME Login Screen + In the default GNOME desktop, the login is displayed after system boot +and can display user accounts, allow users to reboot the system, and allow users to +login automatically and/or with a guest account. The login screen should be configured +to prevent such behavior. + + +For more information about enforcing preferences in the GNOME3 environment using the DConf +configuration system, see https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/7/html/desktop_migration_and_administration_guide/> and the man page dconf(1). + + Disable the GNOME3 Login Restart and Shutdown Buttons + In the default graphical environment, users logging directly into the +system are greeted with a login screen that allows any user, known or +unknown, the ability the ability to shutdown or restart the system. This +functionality should be disabled by setting +disable-restart-buttons to true. + +To disable, add or edit disable-restart-buttons to +/etc/dconf/db/distro.d/00-security-settings. For example: +[org/gnome/login-screen] +disable-restart-buttons=true +Once the setting has been added, add a lock to +/etc/dconf/db/distro.d/locks/00-security-settings-lock to prevent +user modification. For example: +/org/gnome/login-screen/disable-restart-buttons +After the settings have been set, run dconf update. + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + 3.1.2 + CCI-000366 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-6(a) + AC-6(1) + CM-7(b) + PR.AC-4 + PR.DS-5 + SRG-OS-000480-GPOS-00227 + A user who is at the console can reboot the system at the login screen. If restart or shutdown buttons +are pressed at the login screen, this can create the risk of short-term loss of availability of systems +due to reboot. + + CCE-86315-9 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-86315-9 + - NIST-800-171-3.1.2 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(b) + - dconf_gnome_disable_restart_shutdown + - high_severity + - low_complexity + - medium_disruption + - no_reboot_needed + - unknown_strategy + +- name: Disable the GNOME3 Login Restart and Shutdown Buttons + ini_file: + dest: /etc/dconf/db/distro.d/00-security-settings + section: org/gnome/login-screen + option: disable-restart-buttons + value: 'true' + create: true + no_extra_spaces: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86315-9 + - NIST-800-171-3.1.2 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(b) + - dconf_gnome_disable_restart_shutdown + - high_severity + - low_complexity + - medium_disruption + - no_reboot_needed + - unknown_strategy + +- name: Prevent user modification of GNOME disablement of Login Restart and Shutdown + Buttons + lineinfile: + path: /etc/dconf/db/distro.d/locks/00-security-settings-lock + regexp: ^/org/gnome/login-screen/disable-restart-buttons + line: /org/gnome/login-screen/disable-restart-buttons + create: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86315-9 + - NIST-800-171-3.1.2 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(b) + - dconf_gnome_disable_restart_shutdown + - high_severity + - low_complexity + - medium_disruption + - no_reboot_needed + - unknown_strategy + +- name: Dconf Update + command: dconf update + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86315-9 + - NIST-800-171-3.1.2 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(b) + - dconf_gnome_disable_restart_shutdown + - high_severity + - low_complexity + - medium_disruption + - no_reboot_needed + - unknown_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q gdm && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +# Check for setting in any of the DConf db directories +# If files contain ibus or distro, ignore them. +# The assignment assumes that individual filenames don't contain : +readarray -t SETTINGSFILES < <(grep -r "\\[org/gnome/login-screen\\]" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +DCONFFILE="/etc/dconf/db/distro.d/00-security-settings" +DBDIR="/etc/dconf/db/distro.d" + +mkdir -p "${DBDIR}" + +if [ "${#SETTINGSFILES[@]}" -eq 0 ] +then + [ ! -z ${DCONFFILE} ] || echo "" >> ${DCONFFILE} + printf '%s\n' "[org/gnome/login-screen]" >> ${DCONFFILE} + printf '%s=%s\n' "disable-restart-buttons" "true" >> ${DCONFFILE} +else + escaped_value="$(sed -e 's/\\/\\\\/g' <<< "true")" + if grep -q "^\\s*disable-restart-buttons\\s*=" "${SETTINGSFILES[@]}" + then + + sed -i "s/\\s*disable-restart-buttons\\s*=\\s*.*/disable-restart-buttons=${escaped_value}/g" "${SETTINGSFILES[@]}" + else + sed -i "\\|\\[org/gnome/login-screen\\]|a\\disable-restart-buttons=${escaped_value}" "${SETTINGSFILES[@]}" + fi +fi + +dconf update +# Check for setting in any of the DConf db directories +LOCKFILES=$(grep -r "^/org/gnome/login-screen/disable-restart-buttons$" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +LOCKSFOLDER="/etc/dconf/db/distro.d/locks" + +mkdir -p "${LOCKSFOLDER}" + +if [[ -z "${LOCKFILES}" ]] +then + echo "/org/gnome/login-screen/disable-restart-buttons" >> "/etc/dconf/db/distro.d/locks/00-security-settings-lock" +fi + +dconf update + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable the GNOME3 Login User List + In the default graphical environment, users logging directly into the +system are greeted with a login screen that displays all known users. +This functionality should be disabled by setting disable-user-list +to true. + +To disable, add or edit disable-user-list to +/etc/dconf/db/distro.d/00-security-settings. For example: +[org/gnome/login-screen] +disable-user-list=true +Once the setting has been added, add a lock to +/etc/dconf/db/distro.d/locks/00-security-settings-lock to prevent +user modification. For example: +/org/gnome/login-screen/disable-user-list +After the settings have been set, run dconf update. + CM-6(a) + AC-23 + SRG-OS-000480-GPOS-00227 + Leaving the user list enabled is a security risk since it allows anyone +with physical access to the system to quickly enumerate known user accounts +without logging in. + + CCE-88285-2 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-88285-2 + - NIST-800-53-AC-23 + - NIST-800-53-CM-6(a) + - dconf_gnome_disable_user_list + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Disable the GNOME3 Login User List + ini_file: + dest: /etc/dconf/db/distro.d/00-security-settings + section: org/gnome/login-screen + option: disable-user-list + value: 'true' + no_extra_spaces: true + create: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-88285-2 + - NIST-800-53-AC-23 + - NIST-800-53-CM-6(a) + - dconf_gnome_disable_user_list + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Prevent user modification of GNOME3 disablement of Login User List + lineinfile: + path: /etc/dconf/db/distro.d/locks/00-security-settings-lock + regexp: ^/org/gnome/login-screen/disable-user-list$ + line: /org/gnome/login-screen/disable-user-list + create: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-88285-2 + - NIST-800-53-AC-23 + - NIST-800-53-CM-6(a) + - dconf_gnome_disable_user_list + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Dconf Update + command: dconf update + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-88285-2 + - NIST-800-53-AC-23 + - NIST-800-53-CM-6(a) + - dconf_gnome_disable_user_list + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q gdm && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +# Check for setting in any of the DConf db directories +# If files contain ibus or distro, ignore them. +# The assignment assumes that individual filenames don't contain : +readarray -t SETTINGSFILES < <(grep -r "\\[org/gnome/login-screen\\]" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +DCONFFILE="/etc/dconf/db/distro.d/00-security-settings" +DBDIR="/etc/dconf/db/distro.d" + +mkdir -p "${DBDIR}" + +if [ "${#SETTINGSFILES[@]}" -eq 0 ] +then + [ ! -z ${DCONFFILE} ] || echo "" >> ${DCONFFILE} + printf '%s\n' "[org/gnome/login-screen]" >> ${DCONFFILE} + printf '%s=%s\n' "disable-user-list" "true" >> ${DCONFFILE} +else + escaped_value="$(sed -e 's/\\/\\\\/g' <<< "true")" + if grep -q "^\\s*disable-user-list\\s*=" "${SETTINGSFILES[@]}" + then + + sed -i "s/\\s*disable-user-list\\s*=\\s*.*/disable-user-list=${escaped_value}/g" "${SETTINGSFILES[@]}" + else + sed -i "\\|\\[org/gnome/login-screen\\]|a\\disable-user-list=${escaped_value}" "${SETTINGSFILES[@]}" + fi +fi + +dconf update +# Check for setting in any of the DConf db directories +LOCKFILES=$(grep -r "^/org/gnome/login-screen/disable-user-list$" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +LOCKSFOLDER="/etc/dconf/db/distro.d/locks" + +mkdir -p "${LOCKSFOLDER}" + +if [[ -z "${LOCKFILES}" ]] +then + echo "/org/gnome/login-screen/disable-user-list" >> "/etc/dconf/db/distro.d/locks/00-security-settings-lock" +fi + +dconf update + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Enable the GNOME3 Login Smartcard Authentication + In the default graphical environment, smart card authentication +can be enabled on the login screen by setting enable-smartcard-authentication +to true. + +To enable, add or edit enable-smartcard-authentication to +/etc/dconf/db/distro.d/00-security-settings. For example: +[org/gnome/login-screen] +enable-smartcard-authentication=true +Once the setting has been added, add a lock to +/etc/dconf/db/distro.d/locks/00-security-settings-lock to prevent user modification. +For example: +/org/gnome/login-screen/enable-smartcard-authentication +After the settings have been set, run dconf update. + CCI-000765 + CCI-000766 + CCI-000767 + CCI-000768 + CCI-000771 + CCI-000772 + CCI-000884 + CCI-001948 + CCI-001954 + IA-2(3) + IA-2(4) + IA-2(8) + IA-2(9) + IA-2(11) + Req-8.3 + SRG-OS-000375-GPOS-00160 + SRG-OS-000376-GPOS-00161 + SRG-OS-000377-GPOS-00162 + Smart card login provides two-factor authentication stronger than +that provided by a username and password combination. Smart cards leverage PKI +(public key infrastructure) in order to provide and verify credentials. + + CCE-86580-8 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-86580-8 + - NIST-800-53-IA-2(11) + - NIST-800-53-IA-2(3) + - NIST-800-53-IA-2(4) + - NIST-800-53-IA-2(8) + - NIST-800-53-IA-2(9) + - PCI-DSS-Req-8.3 + - dconf_gnome_enable_smartcard_auth + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Enable the GNOME3 Login Smartcard Authentication + ini_file: + dest: /etc/dconf/db/distro.d/00-security-settings + section: org/gnome/login-screen + option: enable-smartcard-authentication + value: 'true' + create: true + no_extra_spaces: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86580-8 + - NIST-800-53-IA-2(11) + - NIST-800-53-IA-2(3) + - NIST-800-53-IA-2(4) + - NIST-800-53-IA-2(8) + - NIST-800-53-IA-2(9) + - PCI-DSS-Req-8.3 + - dconf_gnome_enable_smartcard_auth + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Prevent user modification of GNOME3 disablement of Smartcard Authentication + lineinfile: + path: /etc/dconf/db/distro.d/locks/00-security-settings-lock + regexp: ^/org/gnome/login-screen/enable-smartcard-authentication$ + line: /org/gnome/login-screen/enable-smartcard-authentication + create: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86580-8 + - NIST-800-53-IA-2(11) + - NIST-800-53-IA-2(3) + - NIST-800-53-IA-2(4) + - NIST-800-53-IA-2(8) + - NIST-800-53-IA-2(9) + - PCI-DSS-Req-8.3 + - dconf_gnome_enable_smartcard_auth + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Dconf Update + command: dconf update + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86580-8 + - NIST-800-53-IA-2(11) + - NIST-800-53-IA-2(3) + - NIST-800-53-IA-2(4) + - NIST-800-53-IA-2(8) + - NIST-800-53-IA-2(9) + - PCI-DSS-Req-8.3 + - dconf_gnome_enable_smartcard_auth + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q gdm && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +# Check for setting in any of the DConf db directories +# If files contain ibus or distro, ignore them. +# The assignment assumes that individual filenames don't contain : +readarray -t SETTINGSFILES < <(grep -r "\\[org/gnome/login-screen\\]" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +DCONFFILE="/etc/dconf/db/distro.d/00-security-settings" +DBDIR="/etc/dconf/db/distro.d" + +mkdir -p "${DBDIR}" + +if [ "${#SETTINGSFILES[@]}" -eq 0 ] +then + [ ! -z ${DCONFFILE} ] || echo "" >> ${DCONFFILE} + printf '%s\n' "[org/gnome/login-screen]" >> ${DCONFFILE} + printf '%s=%s\n' "enable-smartcard-authentication" "true" >> ${DCONFFILE} +else + escaped_value="$(sed -e 's/\\/\\\\/g' <<< "true")" + if grep -q "^\\s*enable-smartcard-authentication\\s*=" "${SETTINGSFILES[@]}" + then + + sed -i "s/\\s*enable-smartcard-authentication\\s*=\\s*.*/enable-smartcard-authentication=${escaped_value}/g" "${SETTINGSFILES[@]}" + else + sed -i "\\|\\[org/gnome/login-screen\\]|a\\enable-smartcard-authentication=${escaped_value}" "${SETTINGSFILES[@]}" + fi +fi + +dconf update +# Check for setting in any of the DConf db directories +LOCKFILES=$(grep -r "^/org/gnome/login-screen/enable-smartcard-authentication$" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +LOCKSFOLDER="/etc/dconf/db/distro.d/locks" + +mkdir -p "${LOCKSFOLDER}" + +if [[ -z "${LOCKFILES}" ]] +then + echo "/org/gnome/login-screen/enable-smartcard-authentication" >> "/etc/dconf/db/distro.d/locks/00-security-settings-lock" +fi + +dconf update + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Enable the GNOME3 Screen Locking On Smartcard Removal + In the default graphical environment, screen locking on smartcard removal +can be enabled by setting removal-action +to 'lock-screen'. + +To enable, add or edit removal-action to +/etc/dconf/db/local.d/00-security-settings. For example: +[org/gnome/settings-daemon/peripherals/smartcard] +removal-action='lock-screen' +Once the setting has been added, add a lock to +/etc/dconf/db/local.d/locks/00-security-settings-lock to prevent user modification. +For example: +/org/gnome/settings-daemon/peripherals/smartcard/removal-action +After the settings have been set, run dconf update. + CCI-000056 + CCI-000058 + SRG-OS-000028-GPOS-00009 + SRG-OS-000030-GPOS-00011 + Locking the screen automatically when removing the smartcard can +prevent undesired access to system. + + CCE-86452-0 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-86452-0 + - dconf_gnome_lock_screen_on_smartcard_removal + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Detect if removal-action can be found on /etc/dconf/db/local.d/ + find: + path: /etc/dconf/db/local.d/ + contains: ^\s*removal-action + register: dconf_gnome_lock_screen_on_smartcard_removal_config_files + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86452-0 + - dconf_gnome_lock_screen_on_smartcard_removal + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Configure removal-action - default file + ini_file: + dest: /etc/dconf/db/local.d//00-security-settings + section: org/gnome/settings-daemon/peripherals/smartcard + option: removal-action + value: '''lock-screen''' + create: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - dconf_gnome_lock_screen_on_smartcard_removal_config_files is defined and dconf_gnome_lock_screen_on_smartcard_removal_config_files.matched + == 0 + tags: + - CCE-86452-0 + - dconf_gnome_lock_screen_on_smartcard_removal + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Configure removal-action - existing files + ini_file: + dest: '{{ item.path }}' + section: org/gnome/settings-daemon/peripherals/smartcard + option: removal-action + value: '''lock-screen''' + create: true + with_items: '{{ dconf_gnome_lock_screen_on_smartcard_removal_config_files.files + }}' + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - dconf_gnome_lock_screen_on_smartcard_removal_config_files is defined and dconf_gnome_lock_screen_on_smartcard_removal_config_files.matched + > 0 + tags: + - CCE-86452-0 + - dconf_gnome_lock_screen_on_smartcard_removal + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Detect if lock for removal-action can be found on /etc/dconf/db/local.d/ + find: + path: /etc/dconf/db/local.d/locks + contains: ^\s*removal-action + register: dconf_gnome_lock_screen_on_smartcard_removal_lock_files + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86452-0 + - dconf_gnome_lock_screen_on_smartcard_removal + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Prevent user modification removal-action - default file + lineinfile: + path: /etc/dconf/db/local.d/locks/00-security-settings-lock + regexp: ^/org/gnome/settings-daemon/peripherals/smartcard/removal-action$ + line: /org/gnome/settings-daemon/peripherals/smartcard/removal-action + create: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - dconf_gnome_lock_screen_on_smartcard_removal_lock_files is defined and dconf_gnome_lock_screen_on_smartcard_removal_lock_files.matched + == 0 + tags: + - CCE-86452-0 + - dconf_gnome_lock_screen_on_smartcard_removal + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Prevent user modification removal-action - existing files + lineinfile: + path: '{{ item.path }}' + regexp: ^/org/gnome/settings-daemon/peripherals/smartcard/removal-action$ + line: /org/gnome/settings-daemon/peripherals/smartcard/removal-action + create: true + with_items: '{{ dconf_gnome_lock_screen_on_smartcard_removal_lock_files.files }}' + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - dconf_gnome_lock_screen_on_smartcard_removal_lock_files is defined and dconf_gnome_lock_screen_on_smartcard_removal_lock_files.matched + > 0 + tags: + - CCE-86452-0 + - dconf_gnome_lock_screen_on_smartcard_removal + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Dconf Update - removal-action + command: dconf update + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86452-0 + - dconf_gnome_lock_screen_on_smartcard_removal + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q gdm && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +# Check for setting in any of the DConf db directories +# If files contain ibus or distro, ignore them. +# The assignment assumes that individual filenames don't contain : +readarray -t SETTINGSFILES < <(grep -r "\\[org/gnome/settings-daemon/peripherals/smartcard\\]" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +DCONFFILE="/etc/dconf/db/local.d/00-security-settings" +DBDIR="/etc/dconf/db/local.d" + +mkdir -p "${DBDIR}" + +if [ "${#SETTINGSFILES[@]}" -eq 0 ] +then + [ ! -z ${DCONFFILE} ] || echo "" >> ${DCONFFILE} + printf '%s\n' "[org/gnome/settings-daemon/peripherals/smartcard]" >> ${DCONFFILE} + printf '%s=%s\n' "removal-action" "'lock-screen'" >> ${DCONFFILE} +else + escaped_value="$(sed -e 's/\\/\\\\/g' <<< "'lock-screen'")" + if grep -q "^\\s*removal-action\\s*=" "${SETTINGSFILES[@]}" + then + + sed -i "s/\\s*removal-action\\s*=\\s*.*/removal-action=${escaped_value}/g" "${SETTINGSFILES[@]}" + else + sed -i "\\|\\[org/gnome/settings-daemon/peripherals/smartcard\\]|a\\removal-action=${escaped_value}" "${SETTINGSFILES[@]}" + fi +fi + +dconf update +# Check for setting in any of the DConf db directories +LOCKFILES=$(grep -r "^/org/gnome/settings-daemon/peripherals/smartcard/removal-action$" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +LOCKSFOLDER="/etc/dconf/db/local.d/locks" + +mkdir -p "${LOCKSFOLDER}" + +if [[ -z "${LOCKFILES}" ]] +then + echo "/org/gnome/settings-daemon/peripherals/smartcard/removal-action" >> "/etc/dconf/db/local.d/locks/00-security-settings-lock" +fi + +dconf update + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Set the GNOME3 Login Number of Failures + In the default graphical environment, the GNOME3 login +screen and be configured to restart the authentication process after +a configured number of attempts. This can be configured by setting +allowed-failures to 3 or less. + +To enable, add or edit allowed-failures to +/etc/dconf/db/distro.d/00-security-settings. For example: +[org/gnome/login-screen] +allowed-failures=3 +Once the setting has been added, add a lock to +/etc/dconf/db/distro.d/locks/00-security-settings-lock to prevent user modification. +For example: +/org/gnome/login-screen/allowed-failures +After the settings have been set, run dconf update. + 3.1.8 + FMT_MOF_EXT.1 + Setting the password retry prompts that are permitted on a per-session basis to a low value +requires some software, such as SSH, to re-connect. This can slow down and +draw additional attention to some types of password-guessing attacks. + + CCE-87638-3 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-87638-3 + - NIST-800-171-3.1.8 + - dconf_gnome_login_retries + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Enable the GNOME3 Login Number of Failures + ini_file: + dest: /etc/dconf/db/distro.d/00-security-settings + section: org/gnome/login-screen + option: allowed-failures + value: '3' + create: true + no_extra_spaces: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-87638-3 + - NIST-800-171-3.1.8 + - dconf_gnome_login_retries + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Prevent user modification of GNOME3 Login Number of Failures + lineinfile: + path: /etc/dconf/db/distro.d/locks/00-security-settings-lock + regexp: ^/org/gnome/login-screen/allowed-failures$ + line: /org/gnome/login-screen/allowed-failures + create: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-87638-3 + - NIST-800-171-3.1.8 + - dconf_gnome_login_retries + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Dconf Update + command: dconf update + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-87638-3 + - NIST-800-171-3.1.8 + - dconf_gnome_login_retries + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q gdm && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +# Check for setting in any of the DConf db directories +# If files contain ibus or distro, ignore them. +# The assignment assumes that individual filenames don't contain : +readarray -t SETTINGSFILES < <(grep -r "\\[org/gnome/login-screen\\]" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +DCONFFILE="/etc/dconf/db/distro.d/00-security-settings" +DBDIR="/etc/dconf/db/distro.d" + +mkdir -p "${DBDIR}" + +if [ "${#SETTINGSFILES[@]}" -eq 0 ] +then + [ ! -z ${DCONFFILE} ] || echo "" >> ${DCONFFILE} + printf '%s\n' "[org/gnome/login-screen]" >> ${DCONFFILE} + printf '%s=%s\n' "allowed-failures" "3" >> ${DCONFFILE} +else + escaped_value="$(sed -e 's/\\/\\\\/g' <<< "3")" + if grep -q "^\\s*allowed-failures\\s*=" "${SETTINGSFILES[@]}" + then + + sed -i "s/\\s*allowed-failures\\s*=\\s*.*/allowed-failures=${escaped_value}/g" "${SETTINGSFILES[@]}" + else + sed -i "\\|\\[org/gnome/login-screen\\]|a\\allowed-failures=${escaped_value}" "${SETTINGSFILES[@]}" + fi +fi + +dconf update +# Check for setting in any of the DConf db directories +LOCKFILES=$(grep -r "^/org/gnome/login-screen/allowed-failures$" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +LOCKSFOLDER="/etc/dconf/db/distro.d/locks" + +mkdir -p "${LOCKSFOLDER}" + +if [[ -z "${LOCKFILES}" ]] +then + echo "/org/gnome/login-screen/allowed-failures" >> "/etc/dconf/db/distro.d/locks/00-security-settings-lock" +fi + +dconf update + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable GDM Automatic Login + The GNOME Display Manager (GDM) can allow users to automatically login without +user interaction or credentials. User should always be required to authenticate themselves +to the system that they are authorized to use. To disable user ability to automatically +login to the system, set the AutomaticLoginEnable to false in the +[daemon] section in /etc/gdm/custom.conf. For example: +[daemon] +AutomaticLoginEnable=false + 11 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + 3.1.1 + CCI-000366 + 4.3.4.3.2 + 4.3.4.3.3 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + CM-6(a) + AC-6(1) + CM-7(b) + PR.IP-1 + FIA_UAU.1 + SRG-OS-000480-GPOS-00229 + Failure to restrict system access to authenticated users negatively impacts operating +system security. + + CCE-89663-9 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-89663-9 + - NIST-800-171-3.1.1 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(b) + - gnome_gdm_disable_automatic_login + - high_severity + - low_complexity + - medium_disruption + - no_reboot_needed + - unknown_strategy + +- name: Disable GDM Automatic Login + ini_file: + dest: /etc/gdm/custom.conf + section: daemon + option: AutomaticLoginEnable + value: 'false' + no_extra_spaces: true + create: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-89663-9 + - NIST-800-171-3.1.1 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(b) + - gnome_gdm_disable_automatic_login + - high_severity + - low_complexity + - medium_disruption + - no_reboot_needed + - unknown_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q gdm && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +if rpm --quiet -q gdm +then + if ! grep -q "^AutomaticLoginEnable=" /etc/gdm/custom.conf + then + sed -i "/^\[daemon\]/a \ + AutomaticLoginEnable=False" /etc/gdm/custom.conf + else + sed -i "s/^AutomaticLoginEnable=.*/AutomaticLoginEnable=False/g" /etc/gdm/custom.conf + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable XDMCP in GDM + XDMCP is an unencrypted protocol, and therefore, presents a security risk, see e.g. +XDMCP Gnome docs. + +To disable XDMCP support in Gnome, set Enable to false under the [xdmcp] configuration section in /etc/gdm/custom.conf. For example: + +[xdmcp] +Enable=false + + XDMCP provides unencrypted remote access through the Gnome Display Manager (GDM) which does +not provide for the confidentiality and integrity of user passwords or the +remote session. If a privileged user were to login using XDMCP, the +privileged user password could be compromised due to typed XEvents +and keystrokes will traversing over the network in clear text. + - name: Gather the package facts + package_facts: + manager: auto + tags: + - gnome_gdm_disable_xdmcp + - high_severity + - low_complexity + - medium_disruption + - no_reboot_needed + - unknown_strategy + +- name: Disable XDMCP in GDM + ini_file: + path: /etc/gdm/custom.conf + section: xdmcp + option: Enable + value: 'false' + create: true + mode: 420 + when: '"gdm" in ansible_facts.packages' + tags: + - gnome_gdm_disable_xdmcp + - high_severity + - low_complexity + - medium_disruption + - no_reboot_needed + - unknown_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q gdm; then + +# Try find '[xdmcp]' and 'Enable' in '/etc/gdm/custom.conf', if it exists, set +# to 'false', if it isn't here, add it, if '[xdmcp]' doesn't exist, add it there +if grep -qzosP '[[:space:]]*\[xdmcp]([^\n\[]*\n+)+?[[:space:]]*Enable' '/etc/gdm/custom.conf'; then + + sed -i 's/Enable[^(\n)]*/Enable=false/' '/etc/gdm/custom.conf' +elif grep -qs '[[:space:]]*\[xdmcp]' '/etc/gdm/custom.conf'; then + sed -i '/[[:space:]]*\[xdmcp]/a Enable=false' '/etc/gdm/custom.conf' +else + if test -d "/etc/gdm"; then + printf '%s\n' '[xdmcp]' 'Enable=false' >> '/etc/gdm/custom.conf' + else + echo "Config file directory '/etc/gdm' doesnt exist, not remediating, assuming non-applicability." >&2 + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + GNOME Media Settings + GNOME media settings that apply to the graphical interface. + + Disable GNOME3 Automounting + The system's default desktop environment, GNOME3, will mount +devices and removable media (such as DVDs, CDs and USB flash drives) whenever +they are inserted into the system. To disable automount within GNOME3, add or set +automount to false in /etc/dconf/db/local.d/00-security-settings. +For example: +[org/gnome/desktop/media-handling] +automount=false +Once the settings have been added, add a lock to +/etc/dconf/db/local.d/locks/00-security-settings-lock to prevent user modification. +For example: +/org/gnome/desktop/media-handling/automount +After the settings have been set, run dconf update. + 12 + 16 + APO13.01 + DSS01.04 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + DSS06.03 + 3.1.7 + CCI-000366 + CCI-000778 + CCI-001958 + 4.3.3.2.2 + 4.3.3.5.2 + 4.3.3.6.6 + 4.3.3.7.2 + 4.3.3.7.4 + SR 1.1 + SR 1.13 + SR 1.2 + SR 1.4 + SR 1.5 + SR 1.9 + SR 2.1 + SR 2.6 + A.11.2.6 + A.13.1.1 + A.13.2.1 + A.6.2.1 + A.6.2.2 + A.7.1.1 + A.9.2.1 + CM-7(a) + CM-7(b) + CM-6(a) + PR.AC-3 + PR.AC-6 + SRG-OS-000114-GPOS-00059 + SRG-OS-000378-GPOS-00163 + SRG-OS-000480-GPOS-00227 + Disabling automatic mounting in GNOME3 can prevent +the introduction of malware via removable media. +It will, however, also prevent desktop users from legitimate use +of removable media. + + CCE-87734-0 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-87734-0 + - NIST-800-171-3.1.7 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - dconf_gnome_disable_automount + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Disable GNOME3 Automounting - automount + ini_file: + dest: /etc/dconf/db/local.d/00-security-settings + section: org/gnome/desktop/media-handling + option: automount + value: 'false' + create: true + no_extra_spaces: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-87734-0 + - NIST-800-171-3.1.7 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - dconf_gnome_disable_automount + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Prevent user modification of GNOME3 Automounting - automount + lineinfile: + path: /etc/dconf/db/local.d/locks/00-security-settings-lock + regexp: ^/org/gnome/desktop/media-handling/automount$ + line: /org/gnome/desktop/media-handling/automount + create: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-87734-0 + - NIST-800-171-3.1.7 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - dconf_gnome_disable_automount + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Dconf Update + command: dconf update + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-87734-0 + - NIST-800-171-3.1.7 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - dconf_gnome_disable_automount + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q gdm && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +# Check for setting in any of the DConf db directories +# If files contain ibus or distro, ignore them. +# The assignment assumes that individual filenames don't contain : +readarray -t SETTINGSFILES < <(grep -r "\\[org/gnome/desktop/media-handling\\]" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +DCONFFILE="/etc/dconf/db/local.d/00-security-settings" +DBDIR="/etc/dconf/db/local.d" + +mkdir -p "${DBDIR}" + +if [ "${#SETTINGSFILES[@]}" -eq 0 ] +then + [ ! -z ${DCONFFILE} ] || echo "" >> ${DCONFFILE} + printf '%s\n' "[org/gnome/desktop/media-handling]" >> ${DCONFFILE} + printf '%s=%s\n' "automount" "false" >> ${DCONFFILE} +else + escaped_value="$(sed -e 's/\\/\\\\/g' <<< "false")" + if grep -q "^\\s*automount\\s*=" "${SETTINGSFILES[@]}" + then + + sed -i "s/\\s*automount\\s*=\\s*.*/automount=${escaped_value}/g" "${SETTINGSFILES[@]}" + else + sed -i "\\|\\[org/gnome/desktop/media-handling\\]|a\\automount=${escaped_value}" "${SETTINGSFILES[@]}" + fi +fi + +dconf update +# Check for setting in any of the DConf db directories +LOCKFILES=$(grep -r "^/org/gnome/desktop/media-handling/automount$" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +LOCKSFOLDER="/etc/dconf/db/local.d/locks" + +mkdir -p "${LOCKSFOLDER}" + +if [[ -z "${LOCKFILES}" ]] +then + echo "/org/gnome/desktop/media-handling/automount" >> "/etc/dconf/db/local.d/locks/00-security-settings-lock" +fi + +dconf update + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable GNOME3 Automount Opening + The system's default desktop environment, GNOME3, will mount +devices and removable media (such as DVDs, CDs and USB flash drives) whenever +they are inserted into the system. To disable automount-open within GNOME3, add or set +automount-open to false in /etc/dconf/db/local.d/00-security-settings. +For example: +[org/gnome/desktop/media-handling] +automount-open=false +Once the settings have been added, add a lock to +/etc/dconf/db/local.d/locks/00-security-settings-lock to prevent user modification. +For example: +/org/gnome/desktop/media-handling/automount-open +After the settings have been set, run dconf update. + 12 + 16 + APO13.01 + DSS01.04 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + DSS06.03 + 3.1.7 + CCI-000366 + CCI-000778 + CCI-001958 + 4.3.3.2.2 + 4.3.3.5.2 + 4.3.3.6.6 + 4.3.3.7.2 + 4.3.3.7.4 + SR 1.1 + SR 1.13 + SR 1.2 + SR 1.4 + SR 1.5 + SR 1.9 + SR 2.1 + SR 2.6 + A.11.2.6 + A.13.1.1 + A.13.2.1 + A.6.2.1 + A.6.2.2 + A.7.1.1 + A.9.2.1 + CM-7(a) + CM-7(b) + CM-6(a) + PR.AC-3 + PR.AC-6 + SRG-OS-000114-GPOS-00059 + SRG-OS-000378-GPOS-00163 + SRG-OS-000480-GPOS-00227 + Automatically mounting file systems permits easy introduction of unknown devices, thereby facilitating malicious activity. +Disabling automatic mounting in GNOME3 can prevent +the introduction of malware via removable media. +It will, however, also prevent desktop users from legitimate use +of removable media. + + CCE-90128-0 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-90128-0 + - NIST-800-171-3.1.7 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - dconf_gnome_disable_automount_open + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Disable GNOME3 Automounting - automount-open + ini_file: + dest: /etc/dconf/db/local.d/00-security-settings + section: org/gnome/desktop/media-handling + option: automount-open + value: 'false' + create: true + no_extra_spaces: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-90128-0 + - NIST-800-171-3.1.7 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - dconf_gnome_disable_automount_open + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Prevent user modification of GNOME3 Automounting - automount-open + lineinfile: + path: /etc/dconf/db/local.d/locks/00-security-settings-lock + regexp: ^/org/gnome/desktop/media-handling/automount-open$ + line: /org/gnome/desktop/media-handling/automount-open + create: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-90128-0 + - NIST-800-171-3.1.7 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - dconf_gnome_disable_automount_open + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Dconf Update + command: dconf update + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-90128-0 + - NIST-800-171-3.1.7 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - dconf_gnome_disable_automount_open + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q gdm && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +# Check for setting in any of the DConf db directories +# If files contain ibus or distro, ignore them. +# The assignment assumes that individual filenames don't contain : +readarray -t SETTINGSFILES < <(grep -r "\\[org/gnome/desktop/media-handling\\]" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +DCONFFILE="/etc/dconf/db/local.d/00-security-settings" +DBDIR="/etc/dconf/db/local.d" + +mkdir -p "${DBDIR}" + +if [ "${#SETTINGSFILES[@]}" -eq 0 ] +then + [ ! -z ${DCONFFILE} ] || echo "" >> ${DCONFFILE} + printf '%s\n' "[org/gnome/desktop/media-handling]" >> ${DCONFFILE} + printf '%s=%s\n' "automount-open" "false" >> ${DCONFFILE} +else + escaped_value="$(sed -e 's/\\/\\\\/g' <<< "false")" + if grep -q "^\\s*automount-open\\s*=" "${SETTINGSFILES[@]}" + then + + sed -i "s/\\s*automount-open\\s*=\\s*.*/automount-open=${escaped_value}/g" "${SETTINGSFILES[@]}" + else + sed -i "\\|\\[org/gnome/desktop/media-handling\\]|a\\automount-open=${escaped_value}" "${SETTINGSFILES[@]}" + fi +fi + +dconf update +# Check for setting in any of the DConf db directories +LOCKFILES=$(grep -r "^/org/gnome/desktop/media-handling/automount-open$" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +LOCKSFOLDER="/etc/dconf/db/local.d/locks" + +mkdir -p "${LOCKSFOLDER}" + +if [[ -z "${LOCKFILES}" ]] +then + echo "/org/gnome/desktop/media-handling/automount-open" >> "/etc/dconf/db/local.d/locks/00-security-settings-lock" +fi + +dconf update + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable GNOME3 Automount running + The system's default desktop environment, GNOME3, will mount +devices and removable media (such as DVDs, CDs and USB flash drives) whenever +they are inserted into the system. To disable autorun-never within GNOME3, add or set +autorun-never to true in /etc/dconf/db/local.d/00-security-settings. +For example: +[org/gnome/desktop/media-handling] +autorun-never=true +Once the settings have been added, add a lock to +/etc/dconf/db/local.d/locks/00-security-settings-lock to prevent user modification. +For example: +/org/gnome/desktop/media-handling/autorun-never +After the settings have been set, run dconf update. + 12 + 16 + APO13.01 + DSS01.04 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + DSS06.03 + 3.1.7 + CCI-000366 + CCI-000778 + CCI-001958 + 4.3.3.2.2 + 4.3.3.5.2 + 4.3.3.6.6 + 4.3.3.7.2 + 4.3.3.7.4 + SR 1.1 + SR 1.13 + SR 1.2 + SR 1.4 + SR 1.5 + SR 1.9 + SR 2.1 + SR 2.6 + A.11.2.6 + A.13.1.1 + A.13.2.1 + A.6.2.1 + A.6.2.2 + A.7.1.1 + A.9.2.1 + CM-7(a) + CM-7(b) + CM-6(a) + PR.AC-3 + PR.AC-6 + SRG-OS-000114-GPOS-00059 + SRG-OS-000378-GPOS-00163 + SRG-OS-000480-GPOS-00227 + Automatically mounting file systems permits easy introduction of unknown devices, thereby facilitating malicious activity. +Disabling automatic mount running in GNOME3 can prevent +the introduction of malware via removable media. +It will, however, also prevent desktop users from legitimate use +of removable media. + + CCE-90257-7 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-90257-7 + - NIST-800-171-3.1.7 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - dconf_gnome_disable_autorun + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Disable GNOME3 Automounting - autorun-never + ini_file: + dest: /etc/dconf/db/local.d/00-security-settings + section: org/gnome/desktop/media-handling + option: autorun-never + value: 'true' + create: true + no_extra_spaces: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-90257-7 + - NIST-800-171-3.1.7 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - dconf_gnome_disable_autorun + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Prevent user modification of GNOME3 Automounting - autorun-never + lineinfile: + path: /etc/dconf/db/local.d/locks/00-security-settings-lock + regexp: ^/org/gnome/desktop/media-handling/autorun-never$ + line: /org/gnome/desktop/media-handling/autorun-never + create: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-90257-7 + - NIST-800-171-3.1.7 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - dconf_gnome_disable_autorun + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Dconf Update + command: dconf update + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-90257-7 + - NIST-800-171-3.1.7 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - dconf_gnome_disable_autorun + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q gdm && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +# Check for setting in any of the DConf db directories +# If files contain ibus or distro, ignore them. +# The assignment assumes that individual filenames don't contain : +readarray -t SETTINGSFILES < <(grep -r "\\[org/gnome/desktop/media-handling\\]" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +DCONFFILE="/etc/dconf/db/local.d/00-security-settings" +DBDIR="/etc/dconf/db/local.d" + +mkdir -p "${DBDIR}" + +if [ "${#SETTINGSFILES[@]}" -eq 0 ] +then + [ ! -z ${DCONFFILE} ] || echo "" >> ${DCONFFILE} + printf '%s\n' "[org/gnome/desktop/media-handling]" >> ${DCONFFILE} + printf '%s=%s\n' "autorun-never" "true" >> ${DCONFFILE} +else + escaped_value="$(sed -e 's/\\/\\\\/g' <<< "true")" + if grep -q "^\\s*autorun-never\\s*=" "${SETTINGSFILES[@]}" + then + + sed -i "s/\\s*autorun-never\\s*=\\s*.*/autorun-never=${escaped_value}/g" "${SETTINGSFILES[@]}" + else + sed -i "\\|\\[org/gnome/desktop/media-handling\\]|a\\autorun-never=${escaped_value}" "${SETTINGSFILES[@]}" + fi +fi + +dconf update +# Check for setting in any of the DConf db directories +LOCKFILES=$(grep -r "^/org/gnome/desktop/media-handling/autorun-never$" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +LOCKSFOLDER="/etc/dconf/db/local.d/locks" + +mkdir -p "${LOCKSFOLDER}" + +if [[ -z "${LOCKFILES}" ]] +then + echo "/org/gnome/desktop/media-handling/autorun-never" >> "/etc/dconf/db/local.d/locks/00-security-settings-lock" +fi + +dconf update + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable All GNOME3 Thumbnailers + The system's default desktop environment, GNOME3, uses +a number of different thumbnailer programs to generate thumbnails +for any new or modified content in an opened folder. To disable the +execution of these thumbnail applications, add or set disable-all +to true in /etc/dconf/db/local.d/00-security-settings. +For example: +[org/gnome/desktop/thumbnailers] +disable-all=true +Once the settings have been added, add a lock to +/etc/dconf/db/local.d/locks/00-security-settings-lock to prevent user modification. +For example: +/org/gnome/desktop/thumbnailers/disable-all +After the settings have been set, run dconf update. +This effectively prevents an attacker from gaining access to a +system through a flaw in GNOME3's Nautilus thumbnail creators. + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + PR.PT-3 + An attacker with knowledge of a flaw in a GNOME3 thumbnailer application could craft a malicious +file to exploit this flaw. Assuming the attacker could place the malicious file on the local filesystem +(via a web upload for example) and assuming a user browses the same location using Nautilus, the +malicious file would exploit the thumbnailer with the potential for malicious code execution. It +is best to disable these thumbnailer applications unless they are explicitly required. + + CCE-88714-1 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-88714-1 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - dconf_gnome_disable_thumbnailers + - low_complexity + - medium_disruption + - no_reboot_needed + - unknown_severity + - unknown_strategy + +- name: Disable All GNOME3 Thumbnailers + ini_file: + dest: /etc/dconf/db/local.d/00-security-settings + section: org/gnome/desktop/thumbnailers + option: disable-all + value: 'true' + create: true + no_extra_spaces: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-88714-1 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - dconf_gnome_disable_thumbnailers + - low_complexity + - medium_disruption + - no_reboot_needed + - unknown_severity + - unknown_strategy + +- name: Prevent user modification of GNOME3 Thumbnailers + lineinfile: + path: /etc/dconf/db/local.d/locks/00-security-settings-lock + regexp: ^/org/gnome/desktop/thumbnailers/disable-all$ + line: /org/gnome/desktop/thumbnailers/disable-all + create: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-88714-1 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - dconf_gnome_disable_thumbnailers + - low_complexity + - medium_disruption + - no_reboot_needed + - unknown_severity + - unknown_strategy + +- name: Dconf Update + command: dconf update + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-88714-1 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - dconf_gnome_disable_thumbnailers + - low_complexity + - medium_disruption + - no_reboot_needed + - unknown_severity + - unknown_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q gdm && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +# Check for setting in any of the DConf db directories +# If files contain ibus or distro, ignore them. +# The assignment assumes that individual filenames don't contain : +readarray -t SETTINGSFILES < <(grep -r "\\[org/gnome/desktop/thumbnailers\\]" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +DCONFFILE="/etc/dconf/db/local.d/00-security-settings" +DBDIR="/etc/dconf/db/local.d" + +mkdir -p "${DBDIR}" + +if [ "${#SETTINGSFILES[@]}" -eq 0 ] +then + [ ! -z ${DCONFFILE} ] || echo "" >> ${DCONFFILE} + printf '%s\n' "[org/gnome/desktop/thumbnailers]" >> ${DCONFFILE} + printf '%s=%s\n' "disable-all" "true" >> ${DCONFFILE} +else + escaped_value="$(sed -e 's/\\/\\\\/g' <<< "true")" + if grep -q "^\\s*disable-all\\s*=" "${SETTINGSFILES[@]}" + then + + sed -i "s/\\s*disable-all\\s*=\\s*.*/disable-all=${escaped_value}/g" "${SETTINGSFILES[@]}" + else + sed -i "\\|\\[org/gnome/desktop/thumbnailers\\]|a\\disable-all=${escaped_value}" "${SETTINGSFILES[@]}" + fi +fi + +dconf update +# Check for setting in any of the DConf db directories +LOCKFILES=$(grep -r "^/org/gnome/desktop/thumbnailers/disable-all$" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +LOCKSFOLDER="/etc/dconf/db/local.d/locks" + +mkdir -p "${LOCKSFOLDER}" + +if [[ -z "${LOCKFILES}" ]] +then + echo "/org/gnome/desktop/thumbnailers/disable-all" >> "/etc/dconf/db/local.d/locks/00-security-settings-lock" +fi + +dconf update + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + GNOME Network Settings + GNOME network settings that apply to the graphical interface. + + Disable WIFI Network Connection Creation in GNOME3 + GNOME allows users to create ad-hoc wireless connections through the +NetworkManager applet. Wireless connections should be disabled by +adding or setting disable-wifi-create to true in +/etc/dconf/db/local.d/00-security-settings. For example: +[org/gnome/nm-applet] +disable-wifi-create=true + +Once the settings have been added, add a lock to +/etc/dconf/db/local.d/locks/00-security-settings-lock to prevent user modification. +For example: +/org/gnome/nm-applet/disable-wifi-create +After the settings have been set, run dconf update. + 3.1.16 + Wireless network connections should not be allowed to be configured by general +users on a given system as it could open the system to backdoor attacks. + + CCE-86409-0 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-86409-0 + - NIST-800-171-3.1.16 + - dconf_gnome_disable_wifi_create + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Disable WiFi Network Connection Creation in GNOME3 + ini_file: + dest: /etc/dconf/db/local.d/00-security-settings + section: org/gnome/nm-applet + option: disable-wifi-create + value: 'true' + create: true + no_extra_spaces: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86409-0 + - NIST-800-171-3.1.16 + - dconf_gnome_disable_wifi_create + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Prevent user modification of GNOME3 disablement of WiFi + lineinfile: + path: /etc/dconf/db/local.d/locks/00-security-settings-lock + regexp: ^/org/gnome/nm-applet/disable-wifi-create$ + line: /org/gnome/nm-applet/disable-wifi-create + create: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86409-0 + - NIST-800-171-3.1.16 + - dconf_gnome_disable_wifi_create + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Dconf Update + command: dconf update + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86409-0 + - NIST-800-171-3.1.16 + - dconf_gnome_disable_wifi_create + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q gdm && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +# Check for setting in any of the DConf db directories +# If files contain ibus or distro, ignore them. +# The assignment assumes that individual filenames don't contain : +readarray -t SETTINGSFILES < <(grep -r "\\[org/gnome/nm-applet\\]" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +DCONFFILE="/etc/dconf/db/local.d/00-security-settings" +DBDIR="/etc/dconf/db/local.d" + +mkdir -p "${DBDIR}" + +if [ "${#SETTINGSFILES[@]}" -eq 0 ] +then + [ ! -z ${DCONFFILE} ] || echo "" >> ${DCONFFILE} + printf '%s\n' "[org/gnome/nm-applet]" >> ${DCONFFILE} + printf '%s=%s\n' "disable-wifi-create" "true" >> ${DCONFFILE} +else + escaped_value="$(sed -e 's/\\/\\\\/g' <<< "true")" + if grep -q "^\\s*disable-wifi-create\\s*=" "${SETTINGSFILES[@]}" + then + + sed -i "s/\\s*disable-wifi-create\\s*=\\s*.*/disable-wifi-create=${escaped_value}/g" "${SETTINGSFILES[@]}" + else + sed -i "\\|\\[org/gnome/nm-applet\\]|a\\disable-wifi-create=${escaped_value}" "${SETTINGSFILES[@]}" + fi +fi + +dconf update +# Check for setting in any of the DConf db directories +LOCKFILES=$(grep -r "^/org/gnome/nm-applet/disable-wifi-create$" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +LOCKSFOLDER="/etc/dconf/db/local.d/locks" + +mkdir -p "${LOCKSFOLDER}" + +if [[ -z "${LOCKFILES}" ]] +then + echo "/org/gnome/nm-applet/disable-wifi-create" >> "/etc/dconf/db/local.d/locks/00-security-settings-lock" +fi + +dconf update + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable WIFI Network Notification in GNOME3 + By default, GNOME disables WIFI notification. This should be permanently set +so that users do not connect to a wireless network when the system finds one. +While useful for mobile devices, this setting should be disabled for all other systems. +To configure the system to disable the WIFI notication, add or set +suppress-wireless-networks-available to true in +/etc/dconf/db/local.d/00-security-settings. For example: +[org/gnome/nm-applet] +suppress-wireless-networks-available=true + +Once the settings have been added, add a lock to +/etc/dconf/db/local.d/locks/00-security-settings-lock to prevent user modification. +For example: +/org/gnome/nm-applet/suppress-wireless-networks-available +After the settings have been set, run dconf update. + 3.1.16 + Wireless network connections should not be allowed to be configured by general +users on a given system as it could open the system to backdoor attacks. + + CCE-87894-2 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-87894-2 + - NIST-800-171-3.1.16 + - dconf_gnome_disable_wifi_notification + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Disable WiFi Network Notification in GNOME3 + ini_file: + dest: /etc/dconf/db/local.d/00-security-settings + section: org/gnome/nm-applet + option: suppress-wireless-networks-available + value: 'true' + create: true + no_extra_spaces: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-87894-2 + - NIST-800-171-3.1.16 + - dconf_gnome_disable_wifi_notification + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Prevent user modification of GNOME3 disablement of WiFi + lineinfile: + path: /etc/dconf/db/local.d/locks/00-security-settings-lock + regexp: ^/org/gnome/nm-applet/suppress-wireless-networks-available$ + line: /org/gnome/nm-applet/suppress-wireless-networks-available + create: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-87894-2 + - NIST-800-171-3.1.16 + - dconf_gnome_disable_wifi_notification + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Dconf Update + command: dconf update + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-87894-2 + - NIST-800-171-3.1.16 + - dconf_gnome_disable_wifi_notification + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q gdm && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +# Check for setting in any of the DConf db directories +# If files contain ibus or distro, ignore them. +# The assignment assumes that individual filenames don't contain : +readarray -t SETTINGSFILES < <(grep -r "\\[org/gnome/nm-applet\\]" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +DCONFFILE="/etc/dconf/db/local.d/00-security-settings" +DBDIR="/etc/dconf/db/local.d" + +mkdir -p "${DBDIR}" + +if [ "${#SETTINGSFILES[@]}" -eq 0 ] +then + [ ! -z ${DCONFFILE} ] || echo "" >> ${DCONFFILE} + printf '%s\n' "[org/gnome/nm-applet]" >> ${DCONFFILE} + printf '%s=%s\n' "suppress-wireless-networks-available" "true" >> ${DCONFFILE} +else + escaped_value="$(sed -e 's/\\/\\\\/g' <<< "true")" + if grep -q "^\\s*suppress-wireless-networks-available\\s*=" "${SETTINGSFILES[@]}" + then + + sed -i "s/\\s*suppress-wireless-networks-available\\s*=\\s*.*/suppress-wireless-networks-available=${escaped_value}/g" "${SETTINGSFILES[@]}" + else + sed -i "\\|\\[org/gnome/nm-applet\\]|a\\suppress-wireless-networks-available=${escaped_value}" "${SETTINGSFILES[@]}" + fi +fi + +dconf update +# Check for setting in any of the DConf db directories +LOCKFILES=$(grep -r "^/org/gnome/nm-applet/suppress-wireless-networks-available$" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +LOCKSFOLDER="/etc/dconf/db/local.d/locks" + +mkdir -p "${LOCKSFOLDER}" + +if [[ -z "${LOCKFILES}" ]] +then + echo "/org/gnome/nm-applet/suppress-wireless-networks-available" >> "/etc/dconf/db/local.d/locks/00-security-settings-lock" +fi + +dconf update + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + GNOME Remote Access Settings + GNOME remote access settings that apply to the graphical interface. + + Require Credential Prompting for Remote Access in GNOME3 + By default, GNOME does not require credentials when using Vino for +remote access. To configure the system to require remote credentials, add or set +authentication-methods to ['vnc'] in +/etc/dconf/db/local.d/00-security-settings. For example: +[org/gnome/Vino] +authentication-methods=['vnc'] + +Once the settings have been added, add a lock to +/etc/dconf/db/local.d/locks/00-security-settings-lock to prevent user modification. +For example: +/org/gnome/Vino/authentication-methods +After the settings have been set, run dconf update. + 3.1.12 + 164.308(a)(4)(i) + 164.308(b)(1) + 164.308(b)(3) + 164.310(b) + 164.312(e)(1) + 164.312(e)(2)(ii) + Username and password prompting is required for remote access. Otherwise, non-authorized +and nefarious users can access the system freely. + + CCE-87524-5 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-87524-5 + - NIST-800-171-3.1.12 + - dconf_gnome_remote_access_credential_prompt + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Require Credential Prompting for Remote Access in GNOME3 + ini_file: + dest: /etc/dconf/db/local.d/00-security-settings + section: org/gnome/Vino + option: authentication-methods + value: '[''vnc'']' + create: true + no_extra_spaces: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-87524-5 + - NIST-800-171-3.1.12 + - dconf_gnome_remote_access_credential_prompt + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Prevent user modification of GNOME3 Credential Prompting for Remote Access + lineinfile: + path: /etc/dconf/db/local.d/locks/00-security-settings-lock + regexp: ^/org/gnome/Vino/authentication-methods$ + line: /org/gnome/Vino/authentication-methods + create: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-87524-5 + - NIST-800-171-3.1.12 + - dconf_gnome_remote_access_credential_prompt + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Dconf Update + command: dconf update + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-87524-5 + - NIST-800-171-3.1.12 + - dconf_gnome_remote_access_credential_prompt + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q gdm && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +# Check for setting in any of the DConf db directories +# If files contain ibus or distro, ignore them. +# The assignment assumes that individual filenames don't contain : +readarray -t SETTINGSFILES < <(grep -r "\\[org/gnome/Vino\\]" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +DCONFFILE="/etc/dconf/db/local.d/00-security-settings" +DBDIR="/etc/dconf/db/local.d" + +mkdir -p "${DBDIR}" + +if [ "${#SETTINGSFILES[@]}" -eq 0 ] +then + [ ! -z ${DCONFFILE} ] || echo "" >> ${DCONFFILE} + printf '%s\n' "[org/gnome/Vino]" >> ${DCONFFILE} + printf '%s=%s\n' "authentication-methods" "['vnc']" >> ${DCONFFILE} +else + escaped_value="$(sed -e 's/\\/\\\\/g' <<< "['vnc']")" + if grep -q "^\\s*authentication-methods\\s*=" "${SETTINGSFILES[@]}" + then + + sed -i "s/\\s*authentication-methods\\s*=\\s*.*/authentication-methods=${escaped_value}/g" "${SETTINGSFILES[@]}" + else + sed -i "\\|\\[org/gnome/Vino\\]|a\\authentication-methods=${escaped_value}" "${SETTINGSFILES[@]}" + fi +fi + +dconf update +# Check for setting in any of the DConf db directories +LOCKFILES=$(grep -r "^/org/gnome/Vino/authentication-methods$" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +LOCKSFOLDER="/etc/dconf/db/local.d/locks" + +mkdir -p "${LOCKSFOLDER}" + +if [[ -z "${LOCKFILES}" ]] +then + echo "/org/gnome/Vino/authentication-methods" >> "/etc/dconf/db/local.d/locks/00-security-settings-lock" +fi + +dconf update + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Require Encryption for Remote Access in GNOME3 + By default, GNOME requires encryption when using Vino for remote access. +To prevent remote access encryption from being disabled, add or set +require-encryption to true in +/etc/dconf/db/local.d/00-security-settings. For example: +[org/gnome/Vino] +require-encryption=true + +Once the settings have been added, add a lock to +/etc/dconf/db/local.d/locks/00-security-settings-lock to prevent user modification. +For example: +/org/gnome/Vino/require-encryption +After the settings have been set, run dconf update. + 1 + 11 + 12 + 13 + 15 + 16 + 18 + 20 + 3 + 4 + 6 + 9 + BAI03.08 + BAI07.04 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS03.01 + 3.1.13 + CCI-000366 + 164.308(a)(4)(i) + 164.308(b)(1) + 164.308(b)(3) + 164.310(b) + 164.312(e)(1) + 164.312(e)(2)(ii) + 4.3.4.3.2 + 4.3.4.3.3 + 4.4.3.3 + SR 7.6 + A.12.1.1 + A.12.1.2 + A.12.1.4 + A.12.5.1 + A.12.6.2 + A.13.1.1 + A.13.1.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + CM-6(a) + AC-17(a) + AC-17(2) + DE.AE-1 + PR.DS-7 + PR.IP-1 + SRG-OS-000480-GPOS-00227 + Open X displays allow an attacker to capture keystrokes and to execute commands +remotely. + + CCE-88756-2 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-88756-2 + - NIST-800-171-3.1.13 + - NIST-800-53-AC-17(2) + - NIST-800-53-AC-17(a) + - NIST-800-53-CM-6(a) + - dconf_gnome_remote_access_encryption + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Require Encryption for Remote Access in GNOME3 + ini_file: + dest: /etc/dconf/db/local.d/00-security-settings + section: org/gnome/Vino + option: require-encryption + value: 'true' + create: true + no_extra_spaces: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-88756-2 + - NIST-800-171-3.1.13 + - NIST-800-53-AC-17(2) + - NIST-800-53-AC-17(a) + - NIST-800-53-CM-6(a) + - dconf_gnome_remote_access_encryption + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Prevent user modification of GNOME3 Encryption for Remote Access + lineinfile: + path: /etc/dconf/db/local.d/locks/00-security-settings-lock + regexp: ^/org/gnome/Vino/require-encryption$ + line: /org/gnome/Vino/require-encryption + create: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-88756-2 + - NIST-800-171-3.1.13 + - NIST-800-53-AC-17(2) + - NIST-800-53-AC-17(a) + - NIST-800-53-CM-6(a) + - dconf_gnome_remote_access_encryption + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Dconf Update + command: dconf update + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-88756-2 + - NIST-800-171-3.1.13 + - NIST-800-53-AC-17(2) + - NIST-800-53-AC-17(a) + - NIST-800-53-CM-6(a) + - dconf_gnome_remote_access_encryption + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q gdm && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +# Check for setting in any of the DConf db directories +# If files contain ibus or distro, ignore them. +# The assignment assumes that individual filenames don't contain : +readarray -t SETTINGSFILES < <(grep -r "\\[org/gnome/Vino\\]" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +DCONFFILE="/etc/dconf/db/local.d/00-security-settings" +DBDIR="/etc/dconf/db/local.d" + +mkdir -p "${DBDIR}" + +if [ "${#SETTINGSFILES[@]}" -eq 0 ] +then + [ ! -z ${DCONFFILE} ] || echo "" >> ${DCONFFILE} + printf '%s\n' "[org/gnome/Vino]" >> ${DCONFFILE} + printf '%s=%s\n' "require-encryption" "true" >> ${DCONFFILE} +else + escaped_value="$(sed -e 's/\\/\\\\/g' <<< "true")" + if grep -q "^\\s*require-encryption\\s*=" "${SETTINGSFILES[@]}" + then + + sed -i "s/\\s*require-encryption\\s*=\\s*.*/require-encryption=${escaped_value}/g" "${SETTINGSFILES[@]}" + else + sed -i "\\|\\[org/gnome/Vino\\]|a\\require-encryption=${escaped_value}" "${SETTINGSFILES[@]}" + fi +fi + +dconf update +# Check for setting in any of the DConf db directories +LOCKFILES=$(grep -r "^/org/gnome/Vino/require-encryption$" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +LOCKSFOLDER="/etc/dconf/db/local.d/locks" + +mkdir -p "${LOCKSFOLDER}" + +if [[ -z "${LOCKFILES}" ]] +then + echo "/org/gnome/Vino/require-encryption" >> "/etc/dconf/db/local.d/locks/00-security-settings-lock" +fi + +dconf update + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Configure GNOME Screen Locking + In the default GNOME3 desktop, the screen can be locked +by selecting the user name in the far right corner of the main panel and +selecting Lock. + +The following sections detail commands to enforce idle activation of the screensaver, +screen locking, a blank-screen screensaver, and an idle activation time. + +Because users should be trained to lock the screen when they +step away from the computer, the automatic locking feature is only +meant as a backup. + +The root account can be screen-locked; however, the root account should +never be used to log into an X Windows environment and should only +be used to for direct login via console in emergency circumstances. + +For more information about enforcing preferences in the GNOME3 environment using the DConf +configuration system, see http://wiki.gnome.org/dconf and +the man page dconf(1). + + Screensaver Inactivity timeout + Choose allowed duration (in seconds) of inactive graphical sessions + 600 + 900 + 1800 + 300 + 900 + + + Screensaver Lock Delay + Choose allowed duration (in seconds) after a screensaver becomes active before displaying an authentication prompt + 10 + 5 + 0 + 0 + + + Enable GNOME3 Screensaver Idle Activation + To activate the screensaver in the GNOME3 desktop after a period of inactivity, +add or set idle-activation-enabled to true in +/etc/dconf/db/local.d/00-security-settings. For example: +[org/gnome/desktop/screensaver] +idle-activation-enabled=true +Once the setting has been added, add a lock to +/etc/dconf/db/local.d/locks/00-security-settings-lock to prevent user modification. +For example: +/org/gnome/desktop/screensaver/idle-activation-enabled +After the settings have been set, run dconf update. + 1 + 12 + 15 + 16 + 5.5.5 + DSS05.04 + DSS05.10 + DSS06.10 + 3.1.10 + CCI-000057 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + A.18.1.4 + A.9.2.1 + A.9.2.4 + A.9.3.1 + A.9.4.2 + A.9.4.3 + CM-6(a) + AC-11(a) + PR.AC-7 + FMT_MOF_EXT.1 + Req-8.1.8 + SRG-OS-000029-GPOS-00010 + A session time-out lock is a temporary action taken when a user stops work and moves away from the immediate +physical vicinity of the information system but does not logout because of the temporary nature of the absence. +Rather than relying on the user to manually lock their operating system session prior to vacating the vicinity, +GNOME desktops can be configured to identify when a user's session has idled and take action to initiate the +session lock. + +Enabling idle activation of the screensaver ensures the screensaver will +be activated after the idle delay. Applications requiring continuous, +real-time screen display (such as network management products) require the +login session does not have administrator rights and the display station is located in a +controlled-access area. + + CCE-87755-5 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-87755-5 + - CJIS-5.5.5 + - NIST-800-171-3.1.10 + - NIST-800-53-AC-11(a) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.1.8 + - dconf_gnome_screensaver_idle_activation_enabled + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Enable GNOME3 Screensaver Idle Activation + ini_file: + dest: /etc/dconf/db/local.d/00-security-settings + section: org/gnome/desktop/screensaver + option: idle-activation-enabled + value: 'true' + create: true + no_extra_spaces: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-87755-5 + - CJIS-5.5.5 + - NIST-800-171-3.1.10 + - NIST-800-53-AC-11(a) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.1.8 + - dconf_gnome_screensaver_idle_activation_enabled + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Prevent user modification of GNOME idle-activation-enabled + lineinfile: + path: /etc/dconf/db/local.d/locks/00-security-settings-lock + regexp: ^/org/gnome/desktop/screensaver/idle-activation-enabled$ + line: /org/gnome/desktop/screensaver/idle-activation-enabled + create: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-87755-5 + - CJIS-5.5.5 + - NIST-800-171-3.1.10 + - NIST-800-53-AC-11(a) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.1.8 + - dconf_gnome_screensaver_idle_activation_enabled + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Dconf Update + command: dconf update + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-87755-5 + - CJIS-5.5.5 + - NIST-800-171-3.1.10 + - NIST-800-53-AC-11(a) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.1.8 + - dconf_gnome_screensaver_idle_activation_enabled + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q gdm && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +# Check for setting in any of the DConf db directories +# If files contain ibus or distro, ignore them. +# The assignment assumes that individual filenames don't contain : +readarray -t SETTINGSFILES < <(grep -r "\\[org/gnome/desktop/screensaver\\]" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +DCONFFILE="/etc/dconf/db/local.d/00-security-settings" +DBDIR="/etc/dconf/db/local.d" + +mkdir -p "${DBDIR}" + +if [ "${#SETTINGSFILES[@]}" -eq 0 ] +then + [ ! -z ${DCONFFILE} ] || echo "" >> ${DCONFFILE} + printf '%s\n' "[org/gnome/desktop/screensaver]" >> ${DCONFFILE} + printf '%s=%s\n' "idle-activation-enabled" "true" >> ${DCONFFILE} +else + escaped_value="$(sed -e 's/\\/\\\\/g' <<< "true")" + if grep -q "^\\s*idle-activation-enabled\\s*=" "${SETTINGSFILES[@]}" + then + + sed -i "s/\\s*idle-activation-enabled\\s*=\\s*.*/idle-activation-enabled=${escaped_value}/g" "${SETTINGSFILES[@]}" + else + sed -i "\\|\\[org/gnome/desktop/screensaver\\]|a\\idle-activation-enabled=${escaped_value}" "${SETTINGSFILES[@]}" + fi +fi + +dconf update +# Check for setting in any of the DConf db directories +LOCKFILES=$(grep -r "^/org/gnome/desktop/screensaver/idle-activation-enabled$" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +LOCKSFOLDER="/etc/dconf/db/local.d/locks" + +mkdir -p "${LOCKSFOLDER}" + +if [[ -z "${LOCKFILES}" ]] +then + echo "/org/gnome/desktop/screensaver/idle-activation-enabled" >> "/etc/dconf/db/local.d/locks/00-security-settings-lock" +fi + +dconf update + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure Users Cannot Change GNOME3 Screensaver Idle Activation + If not already configured, ensure that users cannot change GNOME3 screensaver lock settings +by adding /org/gnome/desktop/screensaver/idle-activation-enabled +to /etc/dconf/db/local.d/00-security-settings. +For example: +/org/gnome/desktop/screensaver/idle-activation-enabled +After the settings have been set, run dconf update. + 1 + 12 + 15 + 16 + 5.5.5 + DSS05.04 + DSS05.10 + DSS06.10 + 3.1.10 + CCI-000057 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + A.18.1.4 + A.9.2.1 + A.9.2.4 + A.9.3.1 + A.9.4.2 + A.9.4.3 + CM-6(a) + PR.AC-7 + FMT_MOF_EXT.1 + Req-8.1.8 + SRG-OS-000029-GPOS-00010 + A session lock is a temporary action taken when a user stops work and moves away from the immediate physical vicinity +of the information system but does not want to logout because of the temporary nature of the absense. + CCE-86819-0 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-86819-0 + - CJIS-5.5.5 + - NIST-800-171-3.1.10 + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.1.8 + - dconf_gnome_screensaver_idle_activation_locked + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Prevent user modification of GNOME Screensaver idle-activation-enabled + lineinfile: + path: /etc/dconf/db/local.d/locks/00-security-settings-lock + regexp: ^/org/gnome/desktop/screensaver/idle-activation-enabled$ + line: /org/gnome/desktop/screensaver/idle-activation-enabled + create: true + when: '"gdm" in ansible_facts.packages' + tags: + - CCE-86819-0 + - CJIS-5.5.5 + - NIST-800-171-3.1.10 + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.1.8 + - dconf_gnome_screensaver_idle_activation_locked + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Dconf Update + command: dconf update + when: '"gdm" in ansible_facts.packages' + tags: + - CCE-86819-0 + - CJIS-5.5.5 + - NIST-800-171-3.1.10 + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.1.8 + - dconf_gnome_screensaver_idle_activation_locked + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q gdm; then + +# Check for setting in any of the DConf db directories +LOCKFILES=$(grep -r "^/org/gnome/desktop/screensaver/idle-activation-enabled$" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +LOCKSFOLDER="/etc/dconf/db/local.d/locks" + +mkdir -p "${LOCKSFOLDER}" + +if [[ -z "${LOCKFILES}" ]] +then + echo "/org/gnome/desktop/screensaver/idle-activation-enabled" >> "/etc/dconf/db/local.d/locks/00-security-settings-lock" +fi + +dconf update + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Set GNOME3 Screensaver Inactivity Timeout + The idle time-out value for inactivity in the GNOME3 desktop is configured via the idle-delay +setting must be set under an appropriate configuration file(s) in the /etc/dconf/db/local.d directory +and locked in /etc/dconf/db/local.d/locks directory to prevent user modification. + +For example, to configure the system for a 15 minute delay, add the following to +/etc/dconf/db/local.d/00-security-settings: +[org/gnome/desktop/session] +idle-delay=uint32 900 + 1 + 12 + 15 + 16 + 5.5.5 + DSS05.04 + DSS05.10 + DSS06.10 + 3.1.10 + CCI-000057 + CCI-000060 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + A.18.1.4 + A.9.2.1 + A.9.2.4 + A.9.3.1 + A.9.4.2 + A.9.4.3 + AC-11(a) + CM-6(a) + PR.AC-7 + FMT_MOF_EXT.1 + Req-8.1.8 + SRG-OS-000029-GPOS-00010 + SRG-OS-000031-GPOS-00012 + A session time-out lock is a temporary action taken when a user stops work and moves away from +the immediate physical vicinity of the information system but does not logout because of the +temporary nature of the absence. Rather than relying on the user to manually lock their operating +system session prior to vacating the vicinity, GNOME3 can be configured to identify when +a user's session has idled and take action to initiate a session lock. + + CCE-86510-5 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-86510-5 + - CJIS-5.5.5 + - NIST-800-171-3.1.10 + - NIST-800-53-AC-11(a) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.1.8 + - dconf_gnome_screensaver_idle_delay + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy +- name: XCCDF Value inactivity_timeout_value # promote to variable + set_fact: + inactivity_timeout_value: !!str + tags: + - always + +- name: Set GNOME3 Screensaver Inactivity Timeout + ini_file: + dest: /etc/dconf/db/local.d/00-security-settings + section: org/gnome/desktop/session + option: idle-delay + value: uint32 {{ inactivity_timeout_value }} + create: true + no_extra_spaces: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86510-5 + - CJIS-5.5.5 + - NIST-800-171-3.1.10 + - NIST-800-53-AC-11(a) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.1.8 + - dconf_gnome_screensaver_idle_delay + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Dconf Update + command: dconf update + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86510-5 + - CJIS-5.5.5 + - NIST-800-171-3.1.10 + - NIST-800-53-AC-11(a) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.1.8 + - dconf_gnome_screensaver_idle_delay + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q gdm && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +inactivity_timeout_value='' + + +# Check for setting in any of the DConf db directories +# If files contain ibus or distro, ignore them. +# The assignment assumes that individual filenames don't contain : +readarray -t SETTINGSFILES < <(grep -r "\\[org/gnome/desktop/session\\]" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +DCONFFILE="/etc/dconf/db/local.d/00-security-settings" +DBDIR="/etc/dconf/db/local.d" + +mkdir -p "${DBDIR}" + +if [ "${#SETTINGSFILES[@]}" -eq 0 ] +then + [ ! -z ${DCONFFILE} ] || echo "" >> ${DCONFFILE} + printf '%s\n' "[org/gnome/desktop/session]" >> ${DCONFFILE} + printf '%s=%s\n' "idle-delay" "uint32 ${inactivity_timeout_value}" >> ${DCONFFILE} +else + escaped_value="$(sed -e 's/\\/\\\\/g' <<< "uint32 ${inactivity_timeout_value}")" + if grep -q "^\\s*idle-delay\\s*=" "${SETTINGSFILES[@]}" + then + + sed -i "s/\\s*idle-delay\\s*=\\s*.*/idle-delay=${escaped_value}/g" "${SETTINGSFILES[@]}" + else + sed -i "\\|\\[org/gnome/desktop/session\\]|a\\idle-delay=${escaped_value}" "${SETTINGSFILES[@]}" + fi +fi + +dconf update + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Set GNOME3 Screensaver Lock Delay After Activation Period + To activate the locking delay of the screensaver in the GNOME3 desktop when +the screensaver is activated, add or set lock-delay to uint32 in +/etc/dconf/db/local.d/00-security-settings. For example: +[org/gnome/desktop/screensaver] +lock-delay=uint32 + +After the settings have been set, run dconf update. + 1 + 12 + 15 + 16 + DSS05.04 + DSS05.10 + DSS06.10 + 3.1.10 + CCI-000056 + CCI-000057 + CCI-000060 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + A.18.1.4 + A.9.2.1 + A.9.2.4 + A.9.3.1 + A.9.4.2 + A.9.4.3 + AC-11(a) + CM-6(a) + PR.AC-7 + FMT_MOF_EXT.1 + Req-8.1.8 + SRG-OS-000029-GPOS-00010 + SRG-OS-000031-GPOS-00012 + A session lock is a temporary action taken when a user stops work and moves away from the immediate physical vicinity +of the information system but does not want to logout because of the temporary nature of the absense. + + CCE-86954-5 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-86954-5 + - NIST-800-171-3.1.10 + - NIST-800-53-AC-11(a) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.1.8 + - dconf_gnome_screensaver_lock_delay + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy +- name: XCCDF Value var_screensaver_lock_delay # promote to variable + set_fact: + var_screensaver_lock_delay: !!str + tags: + - always + +- name: Set GNOME3 Screensaver Lock Delay After Activation Period + ini_file: + dest: /etc/dconf/db/local.d/00-security-settings + section: org/gnome/desktop/screensaver + option: lock-delay + value: uint32 {{ var_screensaver_lock_delay }} + create: true + no_extra_spaces: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86954-5 + - NIST-800-171-3.1.10 + - NIST-800-53-AC-11(a) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.1.8 + - dconf_gnome_screensaver_lock_delay + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Dconf Update + command: dconf update + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86954-5 + - NIST-800-171-3.1.10 + - NIST-800-53-AC-11(a) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.1.8 + - dconf_gnome_screensaver_lock_delay + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q gdm && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +var_screensaver_lock_delay='' + + +# Check for setting in any of the DConf db directories +# If files contain ibus or distro, ignore them. +# The assignment assumes that individual filenames don't contain : +readarray -t SETTINGSFILES < <(grep -r "\\[org/gnome/desktop/screensaver\\]" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +DCONFFILE="/etc/dconf/db/local.d/00-security-settings" +DBDIR="/etc/dconf/db/local.d" + +mkdir -p "${DBDIR}" + +if [ "${#SETTINGSFILES[@]}" -eq 0 ] +then + [ ! -z ${DCONFFILE} ] || echo "" >> ${DCONFFILE} + printf '%s\n' "[org/gnome/desktop/screensaver]" >> ${DCONFFILE} + printf '%s=%s\n' "lock-delay" "uint32 ${var_screensaver_lock_delay}" >> ${DCONFFILE} +else + escaped_value="$(sed -e 's/\\/\\\\/g' <<< "uint32 ${var_screensaver_lock_delay}")" + if grep -q "^\\s*lock-delay\\s*=" "${SETTINGSFILES[@]}" + then + + sed -i "s/\\s*lock-delay\\s*=\\s*.*/lock-delay=${escaped_value}/g" "${SETTINGSFILES[@]}" + else + sed -i "\\|\\[org/gnome/desktop/screensaver\\]|a\\lock-delay=${escaped_value}" "${SETTINGSFILES[@]}" + fi +fi + +dconf update + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable GNOME3 Screensaver Lock After Idle Period + +To activate locking of the screensaver in the GNOME3 desktop when it is activated, +add or set lock-enabled to true in +/etc/dconf/db/local.d/00-security-settings. For example: +[org/gnome/desktop/screensaver] +lock-enabled=true + +Once the settings have been added, add a lock to +/etc/dconf/db/local.d/locks/00-security-settings-lock to prevent user modification. +For example: +/org/gnome/desktop/screensaver/lock-enabled +After the settings have been set, run dconf update. + 1 + 12 + 15 + 16 + 5.5.5 + DSS05.04 + DSS05.10 + DSS06.10 + 3.1.10 + CCI-000056 + CCI-000058 + CCI-000060 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + A.18.1.4 + A.9.2.1 + A.9.2.4 + A.9.3.1 + A.9.4.2 + A.9.4.3 + CM-6(a) + PR.AC-7 + FMT_MOF_EXT.1 + Req-8.1.8 + SRG-OS-000028-GPOS-00009 + SRG-OS-000030-GPOS-00011 + A session lock is a temporary action taken when a user stops work and moves away from the immediate physical vicinity +of the information system but does not want to logout because of the temporary nature of the absense. + + CCE-89302-4 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-89302-4 + - CJIS-5.5.5 + - NIST-800-171-3.1.10 + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.1.8 + - dconf_gnome_screensaver_lock_enabled + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Dconf Update + command: dconf update + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ansible_distribution == 'SLES' + tags: + - CCE-89302-4 + - CJIS-5.5.5 + - NIST-800-171-3.1.10 + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.1.8 + - dconf_gnome_screensaver_lock_enabled + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Enable GNOME3 Screensaver Lock After Idle Period + ini_file: + dest: /etc/dconf/db/local.d/00-security-settings + section: org/gnome/desktop/screensaver + option: lock-enabled + value: 'true' + create: true + no_extra_spaces: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ansible_distribution != 'SLES' + tags: + - CCE-89302-4 + - CJIS-5.5.5 + - NIST-800-171-3.1.10 + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.1.8 + - dconf_gnome_screensaver_lock_enabled + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Prevent user modification of GNOME lock-enabled + lineinfile: + path: /etc/dconf/db/local.d/locks/00-security-settings-lock + regexp: ^/org/gnome/desktop/screensaver/lock-enabled$ + line: /org/gnome/desktop/screensaver/lock-enabled + create: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ansible_distribution != 'SLES' + tags: + - CCE-89302-4 + - CJIS-5.5.5 + - NIST-800-171-3.1.10 + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.1.8 + - dconf_gnome_screensaver_lock_enabled + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Enable GNOME3 Screensaver Lock After Idle Period + ini_file: + dest: /etc/dconf/db/local.d/00-security-settings + section: org/gnome/desktop/lockdown + option: disable-lock-screen + value: 'false' + create: true + no_extra_spaces: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ansible_distribution == 'SLES' + tags: + - CCE-89302-4 + - CJIS-5.5.5 + - NIST-800-171-3.1.10 + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.1.8 + - dconf_gnome_screensaver_lock_enabled + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Prevent user modification of GNOME disable-lock-screen + lineinfile: + path: /etc/dconf/db/local.d/locks/00-security-settings-lock + regexp: ^/org/gnome/desktop/lockdown/disable-lock-screen$ + line: /org/gnome/desktop/lockdown/disable-lock-screen + create: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ansible_distribution == 'SLES' + tags: + - CCE-89302-4 + - CJIS-5.5.5 + - NIST-800-171-3.1.10 + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.1.8 + - dconf_gnome_screensaver_lock_enabled + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Check GNOME3 screenserver disable-lock-screen false + command: gsettings get org.gnome.desktop.lockdown disable-lock-screen + register: cmd_out + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ansible_distribution == 'SLES' + tags: + - CCE-89302-4 + - CJIS-5.5.5 + - NIST-800-171-3.1.10 + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.1.8 + - dconf_gnome_screensaver_lock_enabled + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Update GNOME3 screenserver disable-lock-screen false + command: gsettings set org.gnome.desktop.lockdown disable-lock-screen false + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ansible_distribution == 'SLES' + tags: + - CCE-89302-4 + - CJIS-5.5.5 + - NIST-800-171-3.1.10 + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.1.8 + - dconf_gnome_screensaver_lock_enabled + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Dconf Update + command: dconf update + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-89302-4 + - CJIS-5.5.5 + - NIST-800-171-3.1.10 + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.1.8 + - dconf_gnome_screensaver_lock_enabled + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q gdm && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +# Check for setting in any of the DConf db directories +# If files contain ibus or distro, ignore them. +# The assignment assumes that individual filenames don't contain : +readarray -t SETTINGSFILES < <(grep -r "\\[org/gnome/desktop/screensaver\\]" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +DCONFFILE="/etc/dconf/db/local.d/00-security-settings" +DBDIR="/etc/dconf/db/local.d" + +mkdir -p "${DBDIR}" + +if [ "${#SETTINGSFILES[@]}" -eq 0 ] +then + [ ! -z ${DCONFFILE} ] || echo "" >> ${DCONFFILE} + printf '%s\n' "[org/gnome/desktop/screensaver]" >> ${DCONFFILE} + printf '%s=%s\n' "lock-enabled" "true" >> ${DCONFFILE} +else + escaped_value="$(sed -e 's/\\/\\\\/g' <<< "true")" + if grep -q "^\\s*lock-enabled\\s*=" "${SETTINGSFILES[@]}" + then + + sed -i "s/\\s*lock-enabled\\s*=\\s*.*/lock-enabled=${escaped_value}/g" "${SETTINGSFILES[@]}" + else + sed -i "\\|\\[org/gnome/desktop/screensaver\\]|a\\lock-enabled=${escaped_value}" "${SETTINGSFILES[@]}" + fi +fi + +dconf update +# Check for setting in any of the DConf db directories +LOCKFILES=$(grep -r "^/org/gnome/desktop/screensaver/lock-enabled$" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +LOCKSFOLDER="/etc/dconf/db/local.d/locks" + +mkdir -p "${LOCKSFOLDER}" + +if [[ -z "${LOCKFILES}" ]] +then + echo "/org/gnome/desktop/screensaver/lock-enabled" >> "/etc/dconf/db/local.d/locks/00-security-settings-lock" +fi + +dconf update + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure Users Cannot Change GNOME3 Screensaver Lock After Idle Period + If not already configured, ensure that users cannot change GNOME3 screensaver lock settings +by adding /org/gnome/desktop/screensaver/lock-enabled +to /etc/dconf/db/local.d/00-security-settings. +For example: +/org/gnome/desktop/screensaver/lock-enabled +After the settings have been set, run dconf update. + 1 + 12 + 15 + 16 + 5.5.5 + DSS05.04 + DSS05.10 + DSS06.10 + 3.1.10 + CCI-000056 + CCI-000057 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + A.18.1.4 + A.9.2.1 + A.9.2.4 + A.9.3.1 + A.9.4.2 + A.9.4.3 + CM-6(a) + PR.AC-7 + FMT_MOF_EXT.1 + Req-8.1.8 + SRG-OS-000028-GPOS-00009 + SRG-OS-000030-GPOS-00011 + A session lock is a temporary action taken when a user stops work and moves away from the immediate physical vicinity +of the information system but does not want to logout because of the temporary nature of the absense. + CCE-90150-4 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-90150-4 + - CJIS-5.5.5 + - NIST-800-171-3.1.10 + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.1.8 + - dconf_gnome_screensaver_lock_locked + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Prevent user modification of GNOME Screensaver lock-enabled + lineinfile: + path: /etc/dconf/db/local.d/locks/00-security-settings-lock + regexp: ^/org/gnome/desktop/screensaver/lock-enabled$ + line: /org/gnome/desktop/screensaver/lock-enabled + create: true + when: '"gdm" in ansible_facts.packages' + tags: + - CCE-90150-4 + - CJIS-5.5.5 + - NIST-800-171-3.1.10 + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.1.8 + - dconf_gnome_screensaver_lock_locked + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Dconf Update + command: dconf update + when: '"gdm" in ansible_facts.packages' + tags: + - CCE-90150-4 + - CJIS-5.5.5 + - NIST-800-171-3.1.10 + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.1.8 + - dconf_gnome_screensaver_lock_locked + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q gdm; then + +# Check for setting in any of the DConf db directories +LOCKFILES=$(grep -r "^/org/gnome/desktop/screensaver/lock-enabled$" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +LOCKSFOLDER="/etc/dconf/db/local.d/locks" + +mkdir -p "${LOCKSFOLDER}" + +if [[ -z "${LOCKFILES}" ]] +then + echo "/org/gnome/desktop/screensaver/lock-enabled" >> "/etc/dconf/db/local.d/locks/00-security-settings-lock" +fi + +dconf update + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Implement Blank Screensaver + + + +To set the screensaver mode in the GNOME3 desktop to a blank screen, +add or set picture-uri to string '' in +/etc/dconf/db/local.d/00-security-settings. For example: +[org/gnome/desktop/screensaver] +picture-uri=string '' + +Once the settings have been added, add a lock to +/etc/dconf/db/local.d/locks/00-security-settings-lock to prevent user modification. +For example: +/org/gnome/desktop/screensaver/picture-uri +After the settings have been set, run dconf update. + 1 + 12 + 15 + 16 + 5.5.5 + DSS05.04 + DSS05.10 + DSS06.10 + 3.1.10 + CCI-000060 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + A.18.1.4 + A.9.2.1 + A.9.2.4 + A.9.3.1 + A.9.4.2 + A.9.4.3 + AC-11(1) + CM-6(a) + AC-11(1).1 + PR.AC-7 + FMT_MOF_EXT.1 + Req-8.1.8 + SRG-OS-000031-GPOS-00012 + Setting the screensaver mode to blank-only conceals the +contents of the display from passersby. + + CCE-88733-1 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-88733-1 + - CJIS-5.5.5 + - NIST-800-171-3.1.10 + - NIST-800-53-AC-11(1) + - NIST-800-53-AC-11(1).1 + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.1.8 + - dconf_gnome_screensaver_mode_blank + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Implement Blank Screensaver + ini_file: + dest: /etc/dconf/db/local.d/00-security-settings + section: org/gnome/desktop/screensaver + option: picture-uri + value: string '' + create: true + no_extra_spaces: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-88733-1 + - CJIS-5.5.5 + - NIST-800-171-3.1.10 + - NIST-800-53-AC-11(1) + - NIST-800-53-AC-11(1).1 + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.1.8 + - dconf_gnome_screensaver_mode_blank + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Prevent user modification of GNOME picture-uri + lineinfile: + path: /etc/dconf/db/local.d/locks/00-security-settings-lock + regexp: ^/org/gnome/desktop/screensaver/picture-uri$ + line: /org/gnome/desktop/screensaver/picture-uri + create: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-88733-1 + - CJIS-5.5.5 + - NIST-800-171-3.1.10 + - NIST-800-53-AC-11(1) + - NIST-800-53-AC-11(1).1 + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.1.8 + - dconf_gnome_screensaver_mode_blank + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Dconf Update + command: dconf update + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-88733-1 + - CJIS-5.5.5 + - NIST-800-171-3.1.10 + - NIST-800-53-AC-11(1) + - NIST-800-53-AC-11(1).1 + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.1.8 + - dconf_gnome_screensaver_mode_blank + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q gdm && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +# Check for setting in any of the DConf db directories +# If files contain ibus or distro, ignore them. +# The assignment assumes that individual filenames don't contain : +readarray -t SETTINGSFILES < <(grep -r "\\[org/gnome/desktop/screensaver\\]" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +DCONFFILE="/etc/dconf/db/local.d/00-security-settings" +DBDIR="/etc/dconf/db/local.d" + +mkdir -p "${DBDIR}" + +if [ "${#SETTINGSFILES[@]}" -eq 0 ] +then + [ ! -z ${DCONFFILE} ] || echo "" >> ${DCONFFILE} + printf '%s\n' "[org/gnome/desktop/screensaver]" >> ${DCONFFILE} + printf '%s=%s\n' "picture-uri" "string ''" >> ${DCONFFILE} +else + escaped_value="$(sed -e 's/\\/\\\\/g' <<< "string ''")" + if grep -q "^\\s*picture-uri\\s*=" "${SETTINGSFILES[@]}" + then + + sed -i "s/\\s*picture-uri\\s*=\\s*.*/picture-uri=${escaped_value}/g" "${SETTINGSFILES[@]}" + else + sed -i "\\|\\[org/gnome/desktop/screensaver\\]|a\\picture-uri=${escaped_value}" "${SETTINGSFILES[@]}" + fi +fi + +dconf update +# Check for setting in any of the DConf db directories +LOCKFILES=$(grep -r "^/org/gnome/desktop/screensaver/picture-uri$" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +LOCKSFOLDER="/etc/dconf/db/local.d/locks" + +mkdir -p "${LOCKSFOLDER}" + +if [[ -z "${LOCKFILES}" ]] +then + echo "/org/gnome/desktop/screensaver/picture-uri" >> "/etc/dconf/db/local.d/locks/00-security-settings-lock" +fi + +dconf update + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable Full User Name on Splash Shield + By default when the screen is locked, the splash shield will show the user's +full name. This should be disabled to prevent casual observers from seeing +who has access to the system. This can be disabled by adding or setting +show-full-name-in-top-bar to false in +/etc/dconf/db/local.d/00-security-settings. For example: +[org/gnome/desktop/screensaver] +show-full-name-in-top-bar=false + +Once the settings have been added, add a lock to +/etc/dconf/db/local.d/locks/00-security-settings-lock to prevent user modification. +For example: +/org/gnome/desktop/screensaver/show-full-name-in-top-bar +After the settings have been set, run dconf update. + FMT_MOF_EXT.1 + Setting the splash screen to not reveal the logged in user's name +conceals who has access to the system from passersby. + + CCE-87468-5 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-87468-5 + - dconf_gnome_screensaver_user_info + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Disable Full Username on Splash Screen + ini_file: + dest: /etc/dconf/db/local.d/00-security-settings + section: org/gnome/desktop/screensaver + option: show-full-name-in-top-bar + value: 'false' + create: true + no_extra_spaces: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-87468-5 + - dconf_gnome_screensaver_user_info + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Prevent user modification of GNOME show-full-name-in-top-bar + lineinfile: + path: /etc/dconf/db/local.d/locks/00-security-settings-lock + regexp: ^/org/gnome/desktop/screensaver/show-full-name-in-top-bar$ + line: /org/gnome/desktop/screensaver/show-full-name-in-top-bar + create: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-87468-5 + - dconf_gnome_screensaver_user_info + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Dconf Update + command: dconf update + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-87468-5 + - dconf_gnome_screensaver_user_info + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q gdm && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +# Check for setting in any of the DConf db directories +# If files contain ibus or distro, ignore them. +# The assignment assumes that individual filenames don't contain : +readarray -t SETTINGSFILES < <(grep -r "\\[org/gnome/desktop/screensaver\\]" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +DCONFFILE="/etc/dconf/db/local.d/00-security-settings" +DBDIR="/etc/dconf/db/local.d" + +mkdir -p "${DBDIR}" + +if [ "${#SETTINGSFILES[@]}" -eq 0 ] +then + [ ! -z ${DCONFFILE} ] || echo "" >> ${DCONFFILE} + printf '%s\n' "[org/gnome/desktop/screensaver]" >> ${DCONFFILE} + printf '%s=%s\n' "show-full-name-in-top-bar" "false" >> ${DCONFFILE} +else + escaped_value="$(sed -e 's/\\/\\\\/g' <<< "false")" + if grep -q "^\\s*show-full-name-in-top-bar\\s*=" "${SETTINGSFILES[@]}" + then + + sed -i "s/\\s*show-full-name-in-top-bar\\s*=\\s*.*/show-full-name-in-top-bar=${escaped_value}/g" "${SETTINGSFILES[@]}" + else + sed -i "\\|\\[org/gnome/desktop/screensaver\\]|a\\show-full-name-in-top-bar=${escaped_value}" "${SETTINGSFILES[@]}" + fi +fi + +dconf update +# Check for setting in any of the DConf db directories +LOCKFILES=$(grep -r "^/org/gnome/desktop/screensaver/show-full-name-in-top-bar$" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +LOCKSFOLDER="/etc/dconf/db/local.d/locks" + +mkdir -p "${LOCKSFOLDER}" + +if [[ -z "${LOCKFILES}" ]] +then + echo "/org/gnome/desktop/screensaver/show-full-name-in-top-bar" >> "/etc/dconf/db/local.d/locks/00-security-settings-lock" +fi + +dconf update + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure Users Cannot Change GNOME3 Screensaver Settings + If not already configured, ensure that users cannot change GNOME3 screensaver lock settings +by adding /org/gnome/desktop/screensaver/lock-delay +to /etc/dconf/db/local.d/locks/00-security-settings-lock to prevent user modification. +For example: +/org/gnome/desktop/screensaver/lock-delay +After the settings have been set, run dconf update. + 1 + 12 + 15 + 16 + DSS05.04 + DSS05.10 + DSS06.10 + 3.1.10 + CCI-000057 + CCI-000060 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + A.18.1.4 + A.9.2.1 + A.9.2.4 + A.9.3.1 + A.9.4.2 + A.9.4.3 + CM-6(a) + PR.AC-7 + FMT_MOF_EXT.1 + SRG-OS-000029-GPOS-00010 + SRG-OS-000031-GPOS-00012 + A session time-out lock is a temporary action taken when a user stops work and moves away from the immediate +physical vicinity of the information system but does not logout because of the temporary nature of the absence. +Rather than relying on the user to manually lock their operating system session prior to vacating the vicinity, +GNOME desktops can be configured to identify when a user's session has idled and take action to initiate the +session lock. As such, users should not be allowed to change session settings. + + CCE-87491-7 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-87491-7 + - NIST-800-171-3.1.10 + - NIST-800-53-CM-6(a) + - dconf_gnome_screensaver_user_locks + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Prevent user modification of GNOME lock-delay + lineinfile: + path: /etc/dconf/db/local.d/locks/00-security-settings-lock + regexp: ^/org/gnome/desktop/screensaver/lock-delay$ + line: /org/gnome/desktop/screensaver/lock-delay + create: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-87491-7 + - NIST-800-171-3.1.10 + - NIST-800-53-CM-6(a) + - dconf_gnome_screensaver_user_locks + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Dconf Update + command: dconf update + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-87491-7 + - NIST-800-171-3.1.10 + - NIST-800-53-CM-6(a) + - dconf_gnome_screensaver_user_locks + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q gdm && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +# Check for setting in any of the DConf db directories +LOCKFILES=$(grep -r "^/org/gnome/desktop/screensaver/lock-delay$" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +LOCKSFOLDER="/etc/dconf/db/local.d/locks" + +mkdir -p "${LOCKSFOLDER}" + +if [[ -z "${LOCKFILES}" ]] +then + echo "/org/gnome/desktop/screensaver/lock-delay" >> "/etc/dconf/db/local.d/locks/00-security-settings-lock" +fi + +dconf update + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure Users Cannot Change GNOME3 Session Idle Settings + If not already configured, ensure that users cannot change GNOME3 session idle settings +by adding /org/gnome/desktop/session/idle-delay +to /etc/dconf/db/local.d/locks/00-security-settings-lock to prevent user modification. +For example: +/org/gnome/desktop/session/idle-delay +After the settings have been set, run dconf update. + 1 + 12 + 15 + 16 + DSS05.04 + DSS05.10 + DSS06.10 + 3.1.10 + CCI-000057 + CCI-000060 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + A.18.1.4 + A.9.2.1 + A.9.2.4 + A.9.3.1 + A.9.4.2 + A.9.4.3 + CM-6(a) + PR.AC-7 + FMT_MOF_EXT.1 + SRG-OS-000029-GPOS-00010 + SRG-OS-000031-GPOS-00012 + A session time-out lock is a temporary action taken when a user stops work and moves away from the immediate +physical vicinity of the information system but does not logout because of the temporary nature of the absence. +Rather than relying on the user to manually lock their operating system session prior to vacating the vicinity, +GNOME desktops can be configured to identify when a user's session has idled and take action to initiate the +session lock. As such, users should not be allowed to change session settings. + + CCE-85971-0 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-85971-0 + - NIST-800-171-3.1.10 + - NIST-800-53-CM-6(a) + - dconf_gnome_session_idle_user_locks + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Prevent user modification of GNOME Session idle-delay + lineinfile: + path: /etc/dconf/db/local.d/locks/00-security-settings-lock + regexp: ^/org/gnome/desktop/session/idle-delay$ + line: /org/gnome/desktop/session/idle-delay + create: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-85971-0 + - NIST-800-171-3.1.10 + - NIST-800-53-CM-6(a) + - dconf_gnome_session_idle_user_locks + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Dconf Update + command: dconf update + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-85971-0 + - NIST-800-171-3.1.10 + - NIST-800-53-CM-6(a) + - dconf_gnome_session_idle_user_locks + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q gdm && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +# Check for setting in any of the DConf db directories +LOCKFILES=$(grep -r "^/org/gnome/desktop/session/idle-delay$" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +LOCKSFOLDER="/etc/dconf/db/local.d/locks" + +mkdir -p "${LOCKSFOLDER}" + +if [[ -z "${LOCKFILES}" ]] +then + echo "/org/gnome/desktop/session/idle-delay" >> "/etc/dconf/db/local.d/locks/00-security-settings-lock" +fi + +dconf update + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + GNOME System Settings + GNOME provides configuration and functionality to a graphical desktop environment +that changes grahical configurations or allow a user to perform +actions that users normally would not be able to do in non-graphical mode such as +remote access configuration, power policies, Geo-location, etc. +Configuring such settings in GNOME will prevent accidential graphical configuration +changes by users from taking place. + + Disable Ctrl-Alt-Del Reboot Key Sequence in GNOME3 + By default, GNOME will reboot the system if the +Ctrl-Alt-Del key sequence is pressed. + +To configure the system to ignore the Ctrl-Alt-Del key sequence +from the Graphical User Interface (GUI) instead of rebooting the system, +add or set logout to '' in +/etc/dconf/db/local.d/00-security-settings. For example: +[org/gnome/settings-daemon/plugins/media-keys] +logout='' +Once the settings have been added, add a lock to +/etc/dconf/db/local.d/locks/00-security-settings-lock to prevent +user modification. For example: +/org/gnome/settings-daemon/plugins/media-keys/logout +After the settings have been set, run dconf update. + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + 3.1.2 + CCI-000366 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-6(a) + AC-6(1) + CM-7(b) + PR.AC-4 + PR.DS-5 + SRG-OS-000480-GPOS-00227 + A locally logged-in user who presses Ctrl-Alt-Del, when at the console, +can reboot the system. If accidentally pressed, as could happen in +the case of mixed OS environment, this can create the risk of short-term +loss of availability of systems due to unintentional reboot. + + CCE-88653-1 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-88653-1 + - NIST-800-171-3.1.2 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(b) + - dconf_gnome_disable_ctrlaltdel_reboot + - high_severity + - low_complexity + - medium_disruption + - no_reboot_needed + - unknown_strategy + +- name: Disable Ctrl-Alt-Del Reboot Key Sequence in GNOME3 + ini_file: + dest: /etc/dconf/db/local.d/00-security-settings + section: org/gnome/settings-daemon/plugins/media-keys + option: logout + value: '''''' + create: true + no_extra_spaces: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-88653-1 + - NIST-800-171-3.1.2 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(b) + - dconf_gnome_disable_ctrlaltdel_reboot + - high_severity + - low_complexity + - medium_disruption + - no_reboot_needed + - unknown_strategy + +- name: Prevent user modification of GNOME disablement of Ctrl-Alt-Del + lineinfile: + path: /etc/dconf/db/local.d/locks/00-security-settings-lock + regexp: ^/org/gnome/settings-daemon/plugins/media-keys/logout$ + line: /org/gnome/settings-daemon/plugins/media-keys/logout + create: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-88653-1 + - NIST-800-171-3.1.2 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(b) + - dconf_gnome_disable_ctrlaltdel_reboot + - high_severity + - low_complexity + - medium_disruption + - no_reboot_needed + - unknown_strategy + +- name: Dconf Update + command: dconf update + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-88653-1 + - NIST-800-171-3.1.2 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(b) + - dconf_gnome_disable_ctrlaltdel_reboot + - high_severity + - low_complexity + - medium_disruption + - no_reboot_needed + - unknown_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q gdm && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +# Check for setting in any of the DConf db directories +# If files contain ibus or distro, ignore them. +# The assignment assumes that individual filenames don't contain : +readarray -t SETTINGSFILES < <(grep -r "\\[org/gnome/settings-daemon/plugins/media-keys\\]" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +DCONFFILE="/etc/dconf/db/local.d/00-security-settings" +DBDIR="/etc/dconf/db/local.d" + +mkdir -p "${DBDIR}" + +if [ "${#SETTINGSFILES[@]}" -eq 0 ] +then + [ ! -z ${DCONFFILE} ] || echo "" >> ${DCONFFILE} + printf '%s\n' "[org/gnome/settings-daemon/plugins/media-keys]" >> ${DCONFFILE} + printf '%s=%s\n' "logout" "''" >> ${DCONFFILE} +else + escaped_value="$(sed -e 's/\\/\\\\/g' <<< "''")" + if grep -q "^\\s*logout\\s*=" "${SETTINGSFILES[@]}" + then + + sed -i "s/\\s*logout\\s*=\\s*.*/logout=${escaped_value}/g" "${SETTINGSFILES[@]}" + else + sed -i "\\|\\[org/gnome/settings-daemon/plugins/media-keys\\]|a\\logout=${escaped_value}" "${SETTINGSFILES[@]}" + fi +fi + +dconf update +# Check for setting in any of the DConf db directories +LOCKFILES=$(grep -r "^/org/gnome/settings-daemon/plugins/media-keys/logout$" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +LOCKSFOLDER="/etc/dconf/db/local.d/locks" + +mkdir -p "${LOCKSFOLDER}" + +if [[ -z "${LOCKFILES}" ]] +then + echo "/org/gnome/settings-daemon/plugins/media-keys/logout" >> "/etc/dconf/db/local.d/locks/00-security-settings-lock" +fi + +dconf update + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable Geolocation in GNOME3 + GNOME allows the clock and applications to track and access +location information. This setting should be disabled as applications +should not track system location. To configure the system to disable +location tracking, add or set enabled to false in +/etc/dconf/db/local.d/00-security-settings. For example: +[org/gnome/system/location] +enabled=false +To configure the clock to disable location tracking, add or set +geolocation to false in +/etc/dconf/db/local.d/00-security-settings. For example: +[org/gnome/clocks] +geolocation=false +Once the settings have been added, add a lock to +/etc/dconf/db/local.d/locks/00-security-settings-lock to prevent +user modification. For example: +/org/gnome/system/location/enabled +/org/gnome/clocks/geolocation +After the settings have been set, run dconf update. + Power settings should not be enabled on systems that are not mobile devices. +Enabling power settings on non-mobile devices could have unintended processing +consequences on standard systems. + + CCE-85903-3 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-85903-3 + - dconf_gnome_disable_geolocation + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Disable Geolocation in GNOME3 - location tracking + ini_file: + dest: /etc/dconf/db/local.d/00-security-settings + section: org/gnome/system/location + option: enabled + value: 'false' + create: true + no_extra_spaces: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-85903-3 + - dconf_gnome_disable_geolocation + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Disable Geolocation in GNOME3 - clock location tracking + ini_file: + dest: /etc/dconf/db/local.d/00-security-settings + section: org/gnome/clocks + option: gelocation + value: 'false' + create: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-85903-3 + - dconf_gnome_disable_geolocation + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Prevent user modification of GNOME geolocation - location tracking + lineinfile: + path: /etc/dconf/db/local.d/locks/00-security-settings-lock + regexp: ^/org/gnome/system/location/enabled$ + line: /org/gnome/system/location/enabled + create: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-85903-3 + - dconf_gnome_disable_geolocation + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Prevent user modification of GNOME geolocation - clock location tracking + lineinfile: + path: /etc/dconf/db/local.d/locks/00-security-settings-lock + regexp: ^/org/gnome/clocks/geolocation$ + line: /org/gnome/clocks/geolocation + create: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-85903-3 + - dconf_gnome_disable_geolocation + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Dconf Update + command: dconf update + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-85903-3 + - dconf_gnome_disable_geolocation + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q gdm && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +# Check for setting in any of the DConf db directories +# If files contain ibus or distro, ignore them. +# The assignment assumes that individual filenames don't contain : +readarray -t SETTINGSFILES < <(grep -r "\\[org/gnome/system/location\\]" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +DCONFFILE="/etc/dconf/db/local.d/00-security-settings" +DBDIR="/etc/dconf/db/local.d" + +mkdir -p "${DBDIR}" + +if [ "${#SETTINGSFILES[@]}" -eq 0 ] +then + [ ! -z ${DCONFFILE} ] || echo "" >> ${DCONFFILE} + printf '%s\n' "[org/gnome/system/location]" >> ${DCONFFILE} + printf '%s=%s\n' "enabled" "false" >> ${DCONFFILE} +else + escaped_value="$(sed -e 's/\\/\\\\/g' <<< "false")" + if grep -q "^\\s*enabled\\s*=" "${SETTINGSFILES[@]}" + then + + sed -i "s/\\s*enabled\\s*=\\s*.*/enabled=${escaped_value}/g" "${SETTINGSFILES[@]}" + else + sed -i "\\|\\[org/gnome/system/location\\]|a\\enabled=${escaped_value}" "${SETTINGSFILES[@]}" + fi +fi + +dconf update +# Check for setting in any of the DConf db directories +# If files contain ibus or distro, ignore them. +# The assignment assumes that individual filenames don't contain : +readarray -t SETTINGSFILES < <(grep -r "\\[org/gnome/clocks\\]" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +DCONFFILE="/etc/dconf/db/local.d/00-security-settings" +DBDIR="/etc/dconf/db/local.d" + +mkdir -p "${DBDIR}" + +if [ "${#SETTINGSFILES[@]}" -eq 0 ] +then + [ ! -z ${DCONFFILE} ] || echo "" >> ${DCONFFILE} + printf '%s\n' "[org/gnome/clocks]" >> ${DCONFFILE} + printf '%s=%s\n' "geolocation" "false" >> ${DCONFFILE} +else + escaped_value="$(sed -e 's/\\/\\\\/g' <<< "false")" + if grep -q "^\\s*geolocation\\s*=" "${SETTINGSFILES[@]}" + then + + sed -i "s/\\s*geolocation\\s*=\\s*.*/geolocation=${escaped_value}/g" "${SETTINGSFILES[@]}" + else + sed -i "\\|\\[org/gnome/clocks\\]|a\\geolocation=${escaped_value}" "${SETTINGSFILES[@]}" + fi +fi + +dconf update +# Check for setting in any of the DConf db directories +LOCKFILES=$(grep -r "^/org/gnome/system/location/enabled$" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +LOCKSFOLDER="/etc/dconf/db/local.d/locks" + +mkdir -p "${LOCKSFOLDER}" + +if [[ -z "${LOCKFILES}" ]] +then + echo "/org/gnome/system/location/enabled" >> "/etc/dconf/db/local.d/locks/00-security-settings-lock" +fi + +dconf update +# Check for setting in any of the DConf db directories +LOCKFILES=$(grep -r "^/org/gnome/clocks/geolocation$" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +LOCKSFOLDER="/etc/dconf/db/local.d/locks" + +mkdir -p "${LOCKSFOLDER}" + +if [[ -z "${LOCKFILES}" ]] +then + echo "/org/gnome/clocks/geolocation" >> "/etc/dconf/db/local.d/locks/00-security-settings-lock" +fi + +dconf update + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable Power Settings in GNOME3 + By default, GNOME enables a power profile designed for mobile devices +with battery usage. While useful for mobile devices, this setting should be disabled +for all other systems. To configure the system to disable the power setting, add or set +active to false in +/etc/dconf/db/local.d/00-security-settings. For example: +[org/gnome/settings-daemon/plugins/power] +active=false + +Once the settings have been added, add a lock to +/etc/dconf/db/local.d/locks/00-security-settings-lock to prevent user modification. +For example: +/org/gnome/settings-daemon/plugins/power +After the settings have been set, run dconf update. + Power settings should not be enabled on systems that are not mobile devices. +Enabling power settings on non-mobile devices could have unintended processing +consequences on standard systems. + + CCE-87338-0 + + + + + + + + + Disable User Administration in GNOME3 + By default, GNOME will allow all users to have some administratrion +capability. This should be disabled so that non-administrative users are not making +configuration changes. To configure the system to disable user administration +capability in the Graphical User Interface (GUI), add or set +user-administration-disabled to true in +/etc/dconf/db/local.d/00-security-settings. For example: +[org/gnome/desktop/lockdown] +user-administration-disabled=true + +Once the settings have been added, add a lock to +/etc/dconf/db/local.d/locks/00-security-settings-lock to prevent user modification. +For example: +/org/gnome/desktop/lockdown/user-administration-disabled +After the settings have been set, run dconf update. + 3.1.5 + FMT_MOD_EXT.1 + Allowing all users to have some administratrive capabilities to the system through +the Graphical User Interface (GUI) when they would not have them otherwise could allow +unintended configuration changes as well as a nefarious user the capability to make system +changes such as adding new accounts, etc. + + CCE-88185-4 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-88185-4 + - NIST-800-171-3.1.5 + - dconf_gnome_disable_user_admin + - high_severity + - low_complexity + - medium_disruption + - no_reboot_needed + - unknown_strategy + +- name: Detect if user-administration-disabled can be found on /etc/dconf/db/local.d/ + find: + path: /etc/dconf/db/local.d/ + contains: ^\s*user-administration-disabled + register: dconf_gnome_disable_user_admin_config_files + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-88185-4 + - NIST-800-171-3.1.5 + - dconf_gnome_disable_user_admin + - high_severity + - low_complexity + - medium_disruption + - no_reboot_needed + - unknown_strategy + +- name: Configure user-administration-disabled - default file + ini_file: + dest: /etc/dconf/db/local.d//00-security-settings + section: org/gnome/desktop/lockdown + option: user-administration-disabled + value: 'true' + create: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - dconf_gnome_disable_user_admin_config_files is defined and dconf_gnome_disable_user_admin_config_files.matched + == 0 + tags: + - CCE-88185-4 + - NIST-800-171-3.1.5 + - dconf_gnome_disable_user_admin + - high_severity + - low_complexity + - medium_disruption + - no_reboot_needed + - unknown_strategy + +- name: Configure user-administration-disabled - existing files + ini_file: + dest: '{{ item.path }}' + section: org/gnome/desktop/lockdown + option: user-administration-disabled + value: 'true' + create: true + with_items: '{{ dconf_gnome_disable_user_admin_config_files.files }}' + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - dconf_gnome_disable_user_admin_config_files is defined and dconf_gnome_disable_user_admin_config_files.matched + > 0 + tags: + - CCE-88185-4 + - NIST-800-171-3.1.5 + - dconf_gnome_disable_user_admin + - high_severity + - low_complexity + - medium_disruption + - no_reboot_needed + - unknown_strategy + +- name: Detect if lock for user-administration-disabled can be found on /etc/dconf/db/local.d/ + find: + path: /etc/dconf/db/local.d/locks + contains: ^\s*user-administration-disabled + register: dconf_gnome_disable_user_admin_lock_files + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-88185-4 + - NIST-800-171-3.1.5 + - dconf_gnome_disable_user_admin + - high_severity + - low_complexity + - medium_disruption + - no_reboot_needed + - unknown_strategy + +- name: Prevent user modification user-administration-disabled - default file + lineinfile: + path: /etc/dconf/db/local.d/locks/00-security-settings-lock + regexp: ^/org/gnome/desktop/lockdown/user-administration-disabled$ + line: /org/gnome/desktop/lockdown/user-administration-disabled + create: true + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - dconf_gnome_disable_user_admin_lock_files is defined and dconf_gnome_disable_user_admin_lock_files.matched + == 0 + tags: + - CCE-88185-4 + - NIST-800-171-3.1.5 + - dconf_gnome_disable_user_admin + - high_severity + - low_complexity + - medium_disruption + - no_reboot_needed + - unknown_strategy + +- name: Prevent user modification user-administration-disabled - existing files + lineinfile: + path: '{{ item.path }}' + regexp: ^/org/gnome/desktop/lockdown/user-administration-disabled$ + line: /org/gnome/desktop/lockdown/user-administration-disabled + create: true + with_items: '{{ dconf_gnome_disable_user_admin_lock_files.files }}' + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - dconf_gnome_disable_user_admin_lock_files is defined and dconf_gnome_disable_user_admin_lock_files.matched + > 0 + tags: + - CCE-88185-4 + - NIST-800-171-3.1.5 + - dconf_gnome_disable_user_admin + - high_severity + - low_complexity + - medium_disruption + - no_reboot_needed + - unknown_strategy + +- name: Dconf Update - user-administration-disabled + command: dconf update + when: + - '"gdm" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-88185-4 + - NIST-800-171-3.1.5 + - dconf_gnome_disable_user_admin + - high_severity + - low_complexity + - medium_disruption + - no_reboot_needed + - unknown_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q gdm && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +# Check for setting in any of the DConf db directories +# If files contain ibus or distro, ignore them. +# The assignment assumes that individual filenames don't contain : +readarray -t SETTINGSFILES < <(grep -r "\\[org/gnome/desktop/lockdown\\]" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +DCONFFILE="/etc/dconf/db/local.d/00-security-settings" +DBDIR="/etc/dconf/db/local.d" + +mkdir -p "${DBDIR}" + +if [ "${#SETTINGSFILES[@]}" -eq 0 ] +then + [ ! -z ${DCONFFILE} ] || echo "" >> ${DCONFFILE} + printf '%s\n' "[org/gnome/desktop/lockdown]" >> ${DCONFFILE} + printf '%s=%s\n' "user-administration-disabled" "true" >> ${DCONFFILE} +else + escaped_value="$(sed -e 's/\\/\\\\/g' <<< "true")" + if grep -q "^\\s*user-administration-disabled\\s*=" "${SETTINGSFILES[@]}" + then + + sed -i "s/\\s*user-administration-disabled\\s*=\\s*.*/user-administration-disabled=${escaped_value}/g" "${SETTINGSFILES[@]}" + else + sed -i "\\|\\[org/gnome/desktop/lockdown\\]|a\\user-administration-disabled=${escaped_value}" "${SETTINGSFILES[@]}" + fi +fi + +dconf update +# Check for setting in any of the DConf db directories +LOCKFILES=$(grep -r "^/org/gnome/desktop/lockdown/user-administration-disabled$" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +LOCKSFOLDER="/etc/dconf/db/local.d/locks" + +mkdir -p "${LOCKSFOLDER}" + +if [[ -z "${LOCKFILES}" ]] +then + echo "/org/gnome/desktop/lockdown/user-administration-disabled" >> "/etc/dconf/db/local.d/locks/00-security-settings-lock" +fi + +dconf update + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + + SAP Specific Requirement + SAP (Systems, Applications and Products in Data Processing) is enterprise +software to manage business operations and customer relations. The +following section contains SAP specific requirement that is not part +of standard or common OS setting. + + + Sudo + Sudo, which stands for "su 'do'", provides the ability to delegate authority +to certain users, groups of users, or system administrators. When configured for system +users and/or groups, Sudo can allow a user or group to execute privileged commands +that normally only root is allowed to execute. + +For more information on Sudo and addition Sudo configuration options, see +https://www.sudo.ws. + + Group name dedicated to the use of sudo + Specify the name of the group that should own /usr/bin/sudo. + root + sudogrp + + + Sudo - logfile value + Specify the sudo logfile to use. The default value used here matches the example +location from CIS, which uses /var/log/sudo.log. + /var/log/sudo.log + /var/log/sudo.log + + + Sudo - passwd_timeout value + Defines the number of minutes before the sudo password prompt times out. +Defining 0 means no timeout. The default timeout value is 5 minutes. + 5 + 0 + 1 + 2 + 3 + 5 + + + Sudo - timestamp_timeout value + Defines the number of minutes that can elapse before sudo will ask for a passwd again. +If set to a value less than 0 the user's time stamp will never expire. Defining 0 means always prompt for a +password. The default timeout value is 5 minutes. + 5 + 0 + 1 + 2 + 3 + 5 + 15 + + + Sudo - umask value + Specify the sudo umask to use. The actual umask value that is used is the union +of the user's umask and the sudo umask. +The default sudo umask is 0022. This guarantess sudo never lowers the umask when +running a command. + 0022 + 0022 + 0027 + + + Install sudo Package + The sudo package can be installed with the following command: + +$ sudo dnf install sudo + BP28(R19) + 1382 + 1384 + 1386 + CM-6(a) + FMT_MOF_EXT.1 + SRG-OS-000324-GPOS-00125 + sudo is a program designed to allow a system administrator to give +limited root privileges to users and log root activity. The basic philosophy +is to give as few privileges as possible but still allow system users to +get their work done. + + CCE-83523-1 + +package --add=sudo + + include install_sudo + +class install_sudo { + package { 'sudo': + ensure => 'installed', + } +} + + - name: Ensure sudo is installed + package: + name: sudo + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83523-1 + - NIST-800-53-CM-6(a) + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_sudo_installed + + +[[packages]] +name = "sudo" +version = "*" + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if ! rpm -q --quiet "sudo" ; then + dnf install -y "sudo" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure Privileged Escalated Commands Cannot Execute Other Commands - sudo NOEXEC + The sudo NOEXEC tag, when specified, prevents user executed +commands from executing other commands, like a shell for example. +This should be enabled by making sure that the NOEXEC tag exists in +/etc/sudoers configuration file or any sudo configuration snippets +in /etc/sudoers.d/. + BP28(R58) + Restricting the capability of sudo allowed commands to execute sub-commands +prevents users from running programs with privileges they wouldn't have otherwise. + CCE-83537-1 + - name: Ensure noexec is enabled in /etc/sudoers + lineinfile: + path: /etc/sudoers + regexp: ^[\s]*Defaults.*\bnoexec\b.*$ + line: Defaults noexec + validate: /usr/sbin/visudo -cf %s + tags: + - CCE-83537-1 + - high_severity + - low_complexity + - low_disruption + - no_reboot_needed + - restrict_strategy + - sudo_add_noexec + + +if /usr/sbin/visudo -qcf /etc/sudoers; then + cp /etc/sudoers /etc/sudoers.bak + if ! grep -P '^[\s]*Defaults[\s]*\bnoexec\b.*$' /etc/sudoers; then + # sudoers file doesn't define Option noexec + echo "Defaults noexec" >> /etc/sudoers + fi + + # Check validity of sudoers and cleanup bak + if /usr/sbin/visudo -qcf /etc/sudoers; then + rm -f /etc/sudoers.bak + else + echo "Fail to validate remediated /etc/sudoers, reverting to original file." + mv /etc/sudoers.bak /etc/sudoers + false + fi +else + echo "Skipping remediation, /etc/sudoers failed to validate" + false +fi + + + + + + + + + + Ensure Only Users Logged In To Real tty Can Execute Sudo - sudo requiretty + The sudo requiretty tag, when specified, will only execute sudo +commands from users logged in to a real tty. +This should be enabled by making sure that the requiretty tag exists in +/etc/sudoers configuration file or any sudo configuration snippets +in /etc/sudoers.d/. + BP28(R58) + Restricting the use cases in which a user is allowed to execute sudo commands +reduces the attack surface. + CCE-83539-7 + - name: Ensure requiretty is enabled in /etc/sudoers + lineinfile: + path: /etc/sudoers + regexp: ^[\s]*Defaults.*\brequiretty\b.*$ + line: Defaults requiretty + validate: /usr/sbin/visudo -cf %s + tags: + - CCE-83539-7 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sudo_add_requiretty + + +if /usr/sbin/visudo -qcf /etc/sudoers; then + cp /etc/sudoers /etc/sudoers.bak + if ! grep -P '^[\s]*Defaults[\s]*\brequiretty\b.*$' /etc/sudoers; then + # sudoers file doesn't define Option requiretty + echo "Defaults requiretty" >> /etc/sudoers + fi + + # Check validity of sudoers and cleanup bak + if /usr/sbin/visudo -qcf /etc/sudoers; then + rm -f /etc/sudoers.bak + else + echo "Fail to validate remediated /etc/sudoers, reverting to original file." + mv /etc/sudoers.bak /etc/sudoers + false + fi +else + echo "Skipping remediation, /etc/sudoers failed to validate" + false +fi + + + + + + + + + + Ensure Only Users Logged In To Real tty Can Execute Sudo - sudo use_pty + The sudo use_pty tag, when specified, will only execute sudo +commands from users logged in to a real tty. +This should be enabled by making sure that the use_pty tag exists in +/etc/sudoers configuration file or any sudo configuration snippets +in /etc/sudoers.d/. + BP28(R58) + Requiring that sudo commands be run in a pseudo-terminal can prevent an attacker from retaining +access to the user's terminal after the main program has finished executing. + CCE-83538-9 + - name: Ensure use_pty is enabled in /etc/sudoers + lineinfile: + path: /etc/sudoers + regexp: ^[\s]*Defaults.*\buse_pty\b.*$ + line: Defaults use_pty + validate: /usr/sbin/visudo -cf %s + tags: + - CCE-83538-9 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sudo_add_use_pty + + +if /usr/sbin/visudo -qcf /etc/sudoers; then + cp /etc/sudoers /etc/sudoers.bak + if ! grep -P '^[\s]*Defaults[\s]*\buse_pty\b.*$' /etc/sudoers; then + # sudoers file doesn't define Option use_pty + echo "Defaults use_pty" >> /etc/sudoers + fi + + # Check validity of sudoers and cleanup bak + if /usr/sbin/visudo -qcf /etc/sudoers; then + rm -f /etc/sudoers.bak + else + echo "Fail to validate remediated /etc/sudoers, reverting to original file." + mv /etc/sudoers.bak /etc/sudoers + false + fi +else + echo "Skipping remediation, /etc/sudoers failed to validate" + false +fi + + + + + + + + + + Ensure Sudo Logfile Exists - sudo logfile + A custom log sudo file can be configured with the 'logfile' tag. This rule configures +a sudo custom logfile at the default location suggested by CIS, which uses +/var/log/sudo.log. + A sudo log file simplifies auditing of sudo commands. + CCE-83527-2 + - name: XCCDF Value var_sudo_logfile # promote to variable + set_fact: + var_sudo_logfile: !!str + tags: + - always + +- name: Ensure logfile is enabled with the appropriate value in /etc/sudoers + lineinfile: + path: /etc/sudoers + regexp: ^[\s]*Defaults\s(.*)\blogfile=[-]?.+\b(.*)$ + line: Defaults \1logfile={{ var_sudo_logfile }}\2 + validate: /usr/sbin/visudo -cf %s + backrefs: true + register: edit_sudoers_logfile_option + tags: + - CCE-83527-2 + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - restrict_strategy + - sudo_custom_logfile + +- name: Enable logfile option with appropriate value in /etc/sudoers + lineinfile: + path: /etc/sudoers + line: Defaults logfile={{ var_sudo_logfile }} + validate: /usr/sbin/visudo -cf %s + when: edit_sudoers_logfile_option is defined and not edit_sudoers_logfile_option.changed + tags: + - CCE-83527-2 + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - restrict_strategy + - sudo_custom_logfile + + + +var_sudo_logfile='' + + +if /usr/sbin/visudo -qcf /etc/sudoers; then + cp /etc/sudoers /etc/sudoers.bak + if ! grep -P '^[\s]*Defaults[\s]*\blogfile=("(?:\\"|\\\\|[^"\\\n])*"\B|[^"](?:(?:\\,|\\"|\\ |\\\\|[^", \\\n])*)\b)\b.*$' /etc/sudoers; then + # sudoers file doesn't define Option logfile + echo "Defaults logfile=${var_sudo_logfile}" >> /etc/sudoers + else + # sudoers file defines Option logfile, remediate if appropriate value is not set + if ! grep -P "^[\s]*Defaults.*\blogfile=${var_sudo_logfile}\b.*$" /etc/sudoers; then + + escaped_variable=${var_sudo_logfile//$'/'/$'\/'} + sed -Ei "s/(^[\s]*Defaults.*\blogfile=)[-]?.+(\b.*$)/\1$escaped_variable\2/" /etc/sudoers + fi + fi + + # Check validity of sudoers and cleanup bak + if /usr/sbin/visudo -qcf /etc/sudoers; then + rm -f /etc/sudoers.bak + else + echo "Fail to validate remediated /etc/sudoers, reverting to original file." + mv /etc/sudoers.bak /etc/sudoers + false + fi +else + echo "Skipping remediation, /etc/sudoers failed to validate" + false +fi + + + + + + + + + + + Ensure Users Re-Authenticate for Privilege Escalation - sudo !authenticate + The sudo !authenticate option, when specified, allows a user to execute commands using +sudo without having to authenticate. This should be disabled by making sure that the +!authenticate option does not exist in /etc/sudoers configuration file or +any sudo configuration snippets in /etc/sudoers.d/. + BP28(R5) + BP28(R59) + 1 + 12 + 15 + 16 + 5 + DSS05.04 + DSS05.10 + DSS06.03 + DSS06.10 + CCI-002038 + 4.3.3.5.1 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + A.18.1.4 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.2 + A.9.4.3 + IA-11 + CM-6(a) + PR.AC-1 + PR.AC-7 + SRG-OS-000373-GPOS-00156 + SRG-OS-000373-GPOS-00157 + SRG-OS-000373-GPOS-00158 + SRG-OS-000373-VMM-001470 + SRG-OS-000373-VMM-001480 + SRG-OS-000373-VMM-001490 + Without re-authentication, users may access resources or perform tasks for which they +do not have authorization. + +When operating systems provide the capability to escalate a functional capability, it +is critical that the user re-authenticate. + CCE-83544-7 + - name: Find /etc/sudoers.d/ files + find: + paths: + - /etc/sudoers.d/ + register: sudoers + tags: + - CCE-83544-7 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-11 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sudo_remove_no_authenticate + +- name: Remove lines containing !authenticate from sudoers files + replace: + regexp: (^(?!#).*[\s]+\!authenticate.*$) + replace: '# \g<1>' + path: '{{ item.path }}' + validate: /usr/sbin/visudo -cf %s + with_items: + - path: /etc/sudoers + - '{{ sudoers.files }}' + tags: + - CCE-83544-7 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-11 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sudo_remove_no_authenticate + + +for f in /etc/sudoers /etc/sudoers.d/* ; do + if [ ! -e "$f" ] ; then + continue + fi + matching_list=$(grep -P '^(?!#).*[\s]+\!authenticate.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + # comment out "!authenticate" matches to preserve user data + sed -i "s/^${entry}$/# &/g" $f + done <<< "$matching_list" + + /usr/sbin/visudo -cf $f &> /dev/null || echo "Fail to validate $f with visudo" + fi +done + + + + + + + + + + Ensure Users Re-Authenticate for Privilege Escalation - sudo NOPASSWD + The sudo NOPASSWD tag, when specified, allows a user to execute +commands using sudo without having to authenticate. This should be disabled +by making sure that the NOPASSWD tag does not exist in +/etc/sudoers configuration file or any sudo configuration snippets +in /etc/sudoers.d/. + BP28(R5) + BP28(R59) + 1 + 12 + 15 + 16 + 5 + DSS05.04 + DSS05.10 + DSS06.03 + DSS06.10 + CCI-002038 + 4.3.3.5.1 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + A.18.1.4 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.2 + A.9.4.3 + IA-11 + CM-6(a) + PR.AC-1 + PR.AC-7 + SRG-OS-000373-GPOS-00156 + SRG-OS-000373-GPOS-00157 + SRG-OS-000373-GPOS-00158 + SRG-OS-000373-VMM-001470 + SRG-OS-000373-VMM-001480 + SRG-OS-000373-VMM-001490 + Without re-authentication, users may access resources or perform tasks for which they +do not have authorization. + +When operating systems provide the capability to escalate a functional capability, it +is critical that the user re-authenticate. + CCE-83536-3 + - name: Find /etc/sudoers.d/ files + find: + paths: + - /etc/sudoers.d/ + register: sudoers + tags: + - CCE-83536-3 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-11 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sudo_remove_nopasswd + +- name: Remove lines containing NOPASSWD from sudoers files + replace: + regexp: (^(?!#).*[\s]+NOPASSWD[\s]*\:.*$) + replace: '# \g<1>' + path: '{{ item.path }}' + validate: /usr/sbin/visudo -cf %s + with_items: + - path: /etc/sudoers + - '{{ sudoers.files }}' + tags: + - CCE-83536-3 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-11 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sudo_remove_nopasswd + + +for f in /etc/sudoers /etc/sudoers.d/* ; do + if [ ! -e "$f" ] ; then + continue + fi + matching_list=$(grep -P '^(?!#).*[\s]+NOPASSWD[\s]*\:.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + # comment out "NOPASSWD" matches to preserve user data + sed -i "s/^${entry}$/# &/g" $f + done <<< "$matching_list" + + /usr/sbin/visudo -cf $f &> /dev/null || echo "Fail to validate $f with visudo" + fi +done + + + + + + + + + + Ensure Users Re-Authenticate for Privilege Escalation - sudo + The sudo NOPASSWD and !authenticate option, when +specified, allows a user to execute commands using sudo without having to +authenticate. This should be disabled by making sure that +NOPASSWD and/or !authenticate do not exist in +/etc/sudoers configuration file or any sudo configuration snippets +in /etc/sudoers.d/." + 1 + 12 + 15 + 16 + 5 + DSS05.04 + DSS05.10 + DSS06.03 + DSS06.10 + CCI-002038 + 4.3.3.5.1 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + A.18.1.4 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.2 + A.9.4.3 + IA-11 + CM-6(a) + PR.AC-1 + PR.AC-7 + SRG-OS-000373-GPOS-00156 + Without re-authentication, users may access resources or perform tasks for which they +do not have authorization. + +When operating systems provide the capability to escalate a functional capability, it +is critical that the user re-authenticate. + CCE-83543-9 + - name: Find /etc/sudoers.d/ files + find: + paths: + - /etc/sudoers.d/ + register: sudoers + tags: + - CCE-83543-9 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-11 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sudo_require_authentication + +- name: Remove lines containing NOPASSWD from sudoers files + replace: + regexp: (^(?!#).*[\s]+NOPASSWD[\s]*\:.*$) + replace: '# \g<1>' + path: '{{ item.path }}' + validate: /usr/sbin/visudo -cf %s + with_items: + - path: /etc/sudoers + - '{{ sudoers.files }}' + tags: + - CCE-83543-9 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-11 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sudo_require_authentication + +- name: Find /etc/sudoers.d/ files + find: + paths: + - /etc/sudoers.d/ + register: sudoers + tags: + - CCE-83543-9 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-11 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sudo_require_authentication + +- name: Remove lines containing !authenticate from sudoers files + replace: + regexp: (^(?!#).*[\s]+\!authenticate.*$) + replace: '# \g<1>' + path: '{{ item.path }}' + validate: /usr/sbin/visudo -cf %s + with_items: + - path: /etc/sudoers + - '{{ sudoers.files }}' + tags: + - CCE-83543-9 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-11 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sudo_require_authentication + + +for f in /etc/sudoers /etc/sudoers.d/* ; do + if [ ! -e "$f" ] ; then + continue + fi + matching_list=$(grep -P '^(?!#).*[\s]+NOPASSWD[\s]*\:.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + # comment out "NOPASSWD" matches to preserve user data + sed -i "s/^${entry}$/# &/g" $f + done <<< "$matching_list" + + /usr/sbin/visudo -cf $f &> /dev/null || echo "Fail to validate $f with visudo" + fi +done + +for f in /etc/sudoers /etc/sudoers.d/* ; do + if [ ! -e "$f" ] ; then + continue + fi + matching_list=$(grep -P '^(?!#).*[\s]+\!authenticate.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + # comment out "!authenticate" matches to preserve user data + sed -i "s/^${entry}$/# &/g" $f + done <<< "$matching_list" + + /usr/sbin/visudo -cf $f &> /dev/null || echo "Fail to validate $f with visudo" + fi +done + + + + + + + + + + The operating system must require Re-Authentication when using the sudo command. Ensure sudo timestamp_timeout is appropriate - sudo timestamp_timeout + The sudo timestamp_timeout tag sets the amount of time sudo password prompt waits. +The default timestamp_timeout value is 5 minutes. +The timestamp_timeout should be configured by making sure that the +timestamp_timeout tag exists in +/etc/sudoers configuration file or any sudo configuration snippets +in /etc/sudoers.d/. +If the value is set to an integer less than 0, the user's time stamp will not expire +and the user will not have to re-authenticate for privileged actions until the user's session is terminated. + CCI-002038 + IA-11 + SRG-OS-000373-GPOS-00156 + SRG-OS-000373-GPOS-00157 + SRG-OS-000373-GPOS-00158 + Without re-authentication, users may access resources or perform tasks for which they +do not have authorization. + +When operating systems provide the capability to escalate a functional capability, it +is critical that the user re-authenticate. + CCE-90029-0 + - name: XCCDF Value var_sudo_timestamp_timeout # promote to variable + set_fact: + var_sudo_timestamp_timeout: !!str + tags: + - always + +- name: Find out if /etc/sudoers.d/* files contain 'Defaults timestamp_timeout' to + be deduplicated + find: + path: /etc/sudoers.d + patterns: '*' + contains: ^[\s]*Defaults\s.*\btimestamp_timeout=.* + register: sudoers_d_defaults_timestamp_timeout + tags: + - CCE-90029-0 + - NIST-800-53-IA-11 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sudo_require_reauthentication + +- name: Remove found occurrences of 'Defaults timestamp_timeout' from /etc/sudoers.d/* + files + lineinfile: + path: '{{ item.path }}' + regexp: ^[\s]*Defaults\s.*\btimestamp_timeout=.* + state: absent + with_items: '{{ sudoers_d_defaults_timestamp_timeout.files }}' + tags: + - CCE-90029-0 + - NIST-800-53-IA-11 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sudo_require_reauthentication + +- name: Ensure timestamp_timeout is enabled with the appropriate value in /etc/sudoers + lineinfile: + path: /etc/sudoers + regexp: ^[\s]*Defaults\s(.*)\btimestamp_timeout=[-]?\w+\b(.*)$ + line: Defaults \1timestamp_timeout={{ var_sudo_timestamp_timeout }}\2 + validate: /usr/sbin/visudo -cf %s + backrefs: true + register: edit_sudoers_timestamp_timeout_option + tags: + - CCE-90029-0 + - NIST-800-53-IA-11 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sudo_require_reauthentication + +- name: Enable timestamp_timeout option with appropriate value in /etc/sudoers + lineinfile: + path: /etc/sudoers + line: Defaults timestamp_timeout={{ var_sudo_timestamp_timeout }} + validate: /usr/sbin/visudo -cf %s + when: edit_sudoers_timestamp_timeout_option is defined and not edit_sudoers_timestamp_timeout_option.changed + tags: + - CCE-90029-0 + - NIST-800-53-IA-11 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sudo_require_reauthentication + + + +var_sudo_timestamp_timeout='' + + +if grep -x '^[\s]*Defaults.*\btimestamp_timeout=.*' /etc/sudoers.d/*; then + find /etc/sudoers.d/ -type f -exec sed -i "/^[\s]*Defaults.*\btimestamp_timeout=.*/d" {} \; +fi + +if /usr/sbin/visudo -qcf /etc/sudoers; then + cp /etc/sudoers /etc/sudoers.bak + if ! grep -P '^[\s]*Defaults.*\btimestamp_timeout=[-]?\w+\b\b.*$' /etc/sudoers; then + # sudoers file doesn't define Option timestamp_timeout + echo "Defaults timestamp_timeout=${var_sudo_timestamp_timeout}" >> /etc/sudoers + else + # sudoers file defines Option timestamp_timeout, remediate if appropriate value is not set + if ! grep -P "^[\s]*Defaults.*\btimestamp_timeout=${var_sudo_timestamp_timeout}\b.*$" /etc/sudoers; then + + sed -Ei "s/(^[\s]*Defaults.*\btimestamp_timeout=)[-]?\w+(\b.*$)/\1${var_sudo_timestamp_timeout}\2/" /etc/sudoers + fi + fi + + # Check validity of sudoers and cleanup bak + if /usr/sbin/visudo -qcf /etc/sudoers; then + rm -f /etc/sudoers.bak + else + echo "Fail to validate remediated /etc/sudoers, reverting to original file." + mv /etc/sudoers.bak /etc/sudoers + false + fi +else + echo "Skipping remediation, /etc/sudoers failed to validate" + false +fi + + + + + + + + + + The operating system must restrict privilege elevation to authorized personnel + The sudo command allows a user to execute programs with elevated +(administrator) privileges. It prompts the user for their password +and confirms your request to execute a command by checking a file, +called sudoers. +Restrict privileged actions by removing the following entries from the sudoers file: +ALL ALL=(ALL) ALL +ALL ALL=(ALL:ALL) ALL + This rule doesn't come with a remediation, as the exact requirement allows exceptions, +and removing lines from the sudoers file can make the system non-administrable. + CCI-000366 + CM-6(b) + CM-6(iv) + SRG-OS-000480-GPOS-00227 + If the "sudoers" file is not configured correctly, any user defined +on the system can initiate privileged actions on the target system. + + CCE-83525-6 + + + + + + + + + Only the VDSM User Can Use sudo NOPASSWD + The sudo NOPASSWD tag, when specified, allows a user to execute commands using sudo without having to authenticate. Only the vdsm user should have this capability in any sudo configuration snippets in /etc/sudoers.d/. + Without re-authentication, users may access resources or perform tasks for which they +do not have authorization. + +When operating systems provide the capability to escalate a functional capability, it +is critical that the user re-authenticate. + CCE-83528-0 + + + + + + + + + Ensure sudo only includes the default configuration directory + Administrators can configure authorized sudo users via drop-in files, and it is possible to include +other directories and configuration files from the file currently being parsed. + +Make sure that /etc/sudoers only includes drop-in configuration files from /etc/sudoers.d, +or that no drop-in file is included. +Either the /etc/sudoers should contain only one #includedir directive pointing to +/etc/sudoers.d, and no file in /etc/sudoers.d/ should include other files or directories; +Or the /etc/sudoers should not contain any #include, +@include, #includedir or @includedir directives. +Note that the '#' character doesn't denote a comment in the configuration file. + CCI-000366 + SRG-OS-000480-GPOS-00227 + Some sudo configurtion options allow users to run programs without re-authenticating. +Use of these configuration options makes it easier for one compromised accound to be used to +compromise other accounts. + CCE-86477-7 + - name: Check for duplicate values + lineinfile: + path: /etc/sudoers + create: false + regexp: ^#includedir.*$ + state: absent + check_mode: true + changed_when: false + register: dupes + tags: + - CCE-86477-7 + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sudoers_default_includedir + +- name: Deduplicate values from /etc/sudoers + lineinfile: + path: /etc/sudoers + create: false + regexp: ^#includedir.*$ + state: absent + when: dupes.found is defined and dupes.found > 1 + tags: + - CCE-86477-7 + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sudoers_default_includedir + +- name: Insert correct line into /etc/sudoers + lineinfile: + path: /etc/sudoers + create: false + regexp: ^#includedir.*$ + line: '#includedir /etc/sudoers.d' + state: present + tags: + - CCE-86477-7 + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sudoers_default_includedir + +- name: Ensure sudoers doesn't include other non-default file + lineinfile: + path: /etc/sudoers + create: false + regexp: ^[#@]include[\s]+.*$ + state: absent + tags: + - CCE-86477-7 + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sudoers_default_includedir + +- name: Ensure sudoers doesn't have non-default includedir + lineinfile: + path: /etc/sudoers + create: false + regexp: ^@includedir[\s]+.*$ + state: absent + tags: + - CCE-86477-7 + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sudoers_default_includedir + +- name: Find out if /etc/sudoers.d/* files contain file or directory includes + find: + path: /etc/sudoers.d + patterns: '*' + contains: ^[#@]include(dir)?\s.*$ + register: sudoers_d_includes + tags: + - CCE-86477-7 + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sudoers_default_includedir + +- name: Remove found occurrences of file and directory includes from /etc/sudoers.d/* + files + lineinfile: + path: '{{ item.path }}' + regexp: ^[#@]include(dir)?\s.*$ + state: absent + with_items: '{{ sudoers_d_includes.files }}' + tags: + - CCE-86477-7 + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sudoers_default_includedir + + +sudoers_config_file="/etc/sudoers" +sudoers_config_dir="/etc/sudoers.d" +sudoers_includedir_count=$(grep -c "#includedir" "$sudoers_config_file") +if [ "$sudoers_includedir_count" -gt 1 ]; then + sed -i "/#includedir/d" "$sudoers_config_file" + echo "#includedir /etc/sudoers.d" >> "$sudoers_config_file" +elif [ "$sudoers_includedir_count" -eq 0 ]; then + echo "#includedir /etc/sudoers.d" >> "$sudoers_config_file" +else + if ! grep -q "^#includedir /etc/sudoers.d" "$sudoers_config_file"; then + sed -i "s|^#includedir.*|#includedir /etc/sudoers.d|g" "$sudoers_config_file" + fi +fi + +sed -Ei "/^#include\s/d; /^@includedir\s/d" "$sudoers_config_file" + +if grep -Pr "^[#@]include(dir)?\s" "$sudoers_config_dir" ; then + sed -Ei "/^[#@]include(dir)?\s/d" "$sudoers_config_dir"/* +fi + + + + + + + + + + Explicit arguments in sudo specifications + All commands in the sudoers file must strictly specify the arguments allowed to be used for a given user. +If the command is supposed to be executed only without arguments, pass "" as an argument in the corresponding user specification. + This rule doesn't come with a remediation, as absence of arguments in the user spec doesn't mean that the command is intended to be executed with no arguments. + The rule can produce false findings when an argument contains a comma - sudoers syntax allows comma escaping using backslash, but the check doesn't support that. For example, root ALL=(ALL) echo 1\,2 allows root to execute echo 1,2, but the check would interpret it as two commands echo 1\ and 2. + BP28(R63) + Any argument can modify quite significantly the behavior of a program, whether regarding the +realized operation (read, write, delete, etc.) or accessed resources (path in a file system tree). To +avoid any possibility of misuse of a command by a user, the ambiguities must be removed at the +level of its specification. + +For example, on some systems, the kernel messages are only accessible by root. +If a user nevertheless must have the privileges to read them, the argument of the dmesg command has to be restricted +in order to prevent the user from flushing the buffer through the -c option: + +user ALL = dmesg "" + + + CCE-83545-4 + + + + + + + + + Don't define allowed commands in sudoers by means of exclusion + Policies applied by sudo through the sudoers file should not involve negation. + +Each user specification in the sudoers file contains a comma-delimited list of command specifications. +The definition can make use glob patterns, as well as of negations. +Indirect definition of those commands by means of exclusion of a set of commands is trivial to bypass, so it is not allowed to use such constructs. + This rule doesn't come with a remediation, as negations indicate design issues with the sudoers user specifications design. Just removing negations doesn't increase the security - you typically have to rethink the definition of allowed commands to fix the issue. + BP28(R61) + Specifying access right using negation is inefficient and can be easily circumvented. +For example, it is expected that a specification like +# To avoid absolutely , this rule can be easily circumvented! +user ALL = ALL ,!/ bin/sh + prevents the execution of the shell +but that’s not the case: just copy the binary /bin/sh to a different name to make it executable +again through the rule keyword ALL. + + CCE-83524-9 + + + + + + + + + Don't target root user in the sudoers file + The targeted users of a user specification should be, as much as possible, non privileged users (i.e.: non-root). + +User specifications have to explicitly list the runas spec (i.e. the list of target users that can be impersonated), and ALL or root should not be used. + This rule doesn't come with a remediation, as the exact requirement allows exceptions, and removing lines from the sudoers file can make the system non-administrable. + BP28(R60) + It is common that the command to be executed does not require superuser rights (editing a file +whose the owner is not root, sending a signal to an unprivileged process,etc.). In order to limit +any attempt of privilege escalation through a command, it is better to apply normal user rights. + + CCE-83531-4 + + + + + + + + + Ensure invoking users password for privilege escalation when using sudo + The sudoers security policy requires that users authenticate themselves before they can use sudo. +When sudoers requires authentication, it validates the invoking user's credentials. +The expected output for: + sudo cvtsudoers -f sudoers /etc/sudoers | grep -E '^Defaults !?(rootpw|targetpw|runaspw)$' + Defaults !targetpw + Defaults !rootpw + Defaults !runaspw +or if cvtsudoers not supported: + sudo find /etc/sudoers /etc/sudoers.d \( \! -name '*~' -a \! -name '*.*' \) -exec grep -E --with-filename '^[[:blank:]]*Defaults[[:blank:]](.*[[:blank:]])?!?\b(rootpw|targetpw|runaspw)' -- {} \; + /etc/sudoers:Defaults !targetpw + /etc/sudoers:Defaults !rootpw + /etc/sudoers:Defaults !runaspw + CCI-000366 + CCI-002227 + CM-6(b) + CM-6.1(iv) + SRG-OS-000480-GPOS-00227 + If the rootpw, targetpw, or runaspw flags are defined and not disabled, by default the operating system will prompt +the invoking user for the "root" user password. + CCE-83529-8 + - name: Find out if /etc/sudoers.d/* files contain Defaults targetpw to be deduplicated + find: + path: /etc/sudoers.d + patterns: '*' + contains: ^Defaults targetpw$ + register: sudoers_d_defaults + tags: + - CCE-83529-8 + - NIST-800-53-CM-6(b) + - NIST-800-53-CM-6.1(iv) + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sudoers_validate_passwd + +- name: Remove found occurrences of Defaults targetpw from /etc/sudoers.d/* files + lineinfile: + path: '{{ item.path }}' + regexp: ^Defaults targetpw$ + state: absent + with_items: '{{ sudoers_d_defaults.files }}' + tags: + - CCE-83529-8 + - NIST-800-53-CM-6(b) + - NIST-800-53-CM-6.1(iv) + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sudoers_validate_passwd + +- name: Find out if /etc/sudoers.d/* files contain Defaults rootpw to be deduplicated + find: + path: /etc/sudoers.d + patterns: '*' + contains: ^Defaults rootpw$ + register: sudoers_d_defaults + tags: + - CCE-83529-8 + - NIST-800-53-CM-6(b) + - NIST-800-53-CM-6.1(iv) + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sudoers_validate_passwd + +- name: Remove found occurrences of Defaults rootpw from /etc/sudoers.d/* files + lineinfile: + path: '{{ item.path }}' + regexp: ^Defaults rootpw$ + state: absent + with_items: '{{ sudoers_d_defaults.files }}' + tags: + - CCE-83529-8 + - NIST-800-53-CM-6(b) + - NIST-800-53-CM-6.1(iv) + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sudoers_validate_passwd + +- name: Find out if /etc/sudoers.d/* files contain Defaults runaspw to be deduplicated + find: + path: /etc/sudoers.d + patterns: '*' + contains: ^Defaults runaspw$ + register: sudoers_d_defaults + tags: + - CCE-83529-8 + - NIST-800-53-CM-6(b) + - NIST-800-53-CM-6.1(iv) + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sudoers_validate_passwd + +- name: Remove found occurrences of Defaults runaspw from /etc/sudoers.d/* files + lineinfile: + path: '{{ item.path }}' + regexp: ^Defaults runaspw$ + state: absent + with_items: '{{ sudoers_d_defaults.files }}' + tags: + - CCE-83529-8 + - NIST-800-53-CM-6(b) + - NIST-800-53-CM-6.1(iv) + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sudoers_validate_passwd + +- name: Remove any ocurrences of Defaults targetpw in /etc/sudoers + lineinfile: + path: /etc/sudoers + regexp: ^Defaults targetpw$ + validate: /usr/sbin/visudo -cf %s + state: absent + register: sudoers_file_defaults + tags: + - CCE-83529-8 + - NIST-800-53-CM-6(b) + - NIST-800-53-CM-6.1(iv) + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sudoers_validate_passwd + +- name: Remove any ocurrences of Defaults rootpw in /etc/sudoers + lineinfile: + path: /etc/sudoers + regexp: ^Defaults rootpw$ + validate: /usr/sbin/visudo -cf %s + state: absent + register: sudoers_file_defaults + tags: + - CCE-83529-8 + - NIST-800-53-CM-6(b) + - NIST-800-53-CM-6.1(iv) + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sudoers_validate_passwd + +- name: Remove any ocurrences of Defaults runaspw in /etc/sudoers + lineinfile: + path: /etc/sudoers + regexp: ^Defaults runaspw$ + validate: /usr/sbin/visudo -cf %s + state: absent + register: sudoers_file_defaults + tags: + - CCE-83529-8 + - NIST-800-53-CM-6(b) + - NIST-800-53-CM-6.1(iv) + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sudoers_validate_passwd + +- name: Check for duplicate values + lineinfile: + path: /etc/sudoers + create: false + regexp: ^Defaults !targetpw$ + state: absent + check_mode: true + changed_when: false + register: dupes + tags: + - CCE-83529-8 + - NIST-800-53-CM-6(b) + - NIST-800-53-CM-6.1(iv) + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sudoers_validate_passwd + +- name: Deduplicate values from /etc/sudoers + lineinfile: + path: /etc/sudoers + create: false + regexp: ^Defaults !targetpw$ + state: absent + when: dupes.found is defined and dupes.found > 1 + tags: + - CCE-83529-8 + - NIST-800-53-CM-6(b) + - NIST-800-53-CM-6.1(iv) + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sudoers_validate_passwd + +- name: Insert correct line into /etc/sudoers + lineinfile: + path: /etc/sudoers + create: false + regexp: ^Defaults !targetpw$ + line: Defaults !targetpw + state: present + tags: + - CCE-83529-8 + - NIST-800-53-CM-6(b) + - NIST-800-53-CM-6.1(iv) + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sudoers_validate_passwd + +- name: Check for duplicate values + lineinfile: + path: /etc/sudoers + create: false + regexp: ^Defaults !rootpw$ + state: absent + check_mode: true + changed_when: false + register: dupes + tags: + - CCE-83529-8 + - NIST-800-53-CM-6(b) + - NIST-800-53-CM-6.1(iv) + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sudoers_validate_passwd + +- name: Deduplicate values from /etc/sudoers + lineinfile: + path: /etc/sudoers + create: false + regexp: ^Defaults !rootpw$ + state: absent + when: dupes.found is defined and dupes.found > 1 + tags: + - CCE-83529-8 + - NIST-800-53-CM-6(b) + - NIST-800-53-CM-6.1(iv) + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sudoers_validate_passwd + +- name: Insert correct line into /etc/sudoers + lineinfile: + path: /etc/sudoers + create: false + regexp: ^Defaults !rootpw$ + line: Defaults !rootpw + state: present + tags: + - CCE-83529-8 + - NIST-800-53-CM-6(b) + - NIST-800-53-CM-6.1(iv) + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sudoers_validate_passwd + +- name: Check for duplicate values + lineinfile: + path: /etc/sudoers + create: false + regexp: ^Defaults !runaspw$ + state: absent + check_mode: true + changed_when: false + register: dupes + tags: + - CCE-83529-8 + - NIST-800-53-CM-6(b) + - NIST-800-53-CM-6.1(iv) + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sudoers_validate_passwd + +- name: Deduplicate values from /etc/sudoers + lineinfile: + path: /etc/sudoers + create: false + regexp: ^Defaults !runaspw$ + state: absent + when: dupes.found is defined and dupes.found > 1 + tags: + - CCE-83529-8 + - NIST-800-53-CM-6(b) + - NIST-800-53-CM-6.1(iv) + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sudoers_validate_passwd + +- name: Insert correct line into /etc/sudoers + lineinfile: + path: /etc/sudoers + create: false + regexp: ^Defaults !runaspw$ + line: Defaults !runaspw + state: present + tags: + - CCE-83529-8 + - NIST-800-53-CM-6(b) + - NIST-800-53-CM-6.1(iv) + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sudoers_validate_passwd + + if grep -x '^Defaults targetpw$' /etc/sudoers; then + sed -i "/Defaults targetpw/d" /etc/sudoers \; +fi +if grep -x '^Defaults targetpw$' /etc/sudoers.d/*; then + find /etc/sudoers.d/ -type f -exec sed -i "/Defaults targetpw/d" {} \; +fi +if grep -x '^Defaults rootpw$' /etc/sudoers; then + sed -i "/Defaults rootpw/d" /etc/sudoers \; +fi +if grep -x '^Defaults rootpw$' /etc/sudoers.d/*; then + find /etc/sudoers.d/ -type f -exec sed -i "/Defaults rootpw/d" {} \; +fi +if grep -x '^Defaults runaspw$' /etc/sudoers; then + sed -i "/Defaults runaspw/d" /etc/sudoers \; +fi +if grep -x '^Defaults runaspw$' /etc/sudoers.d/*; then + find /etc/sudoers.d/ -type f -exec sed -i "/Defaults runaspw/d" {} \; +fi + +if [ -e "/etc/sudoers" ] ; then + + LC_ALL=C sed -i "/Defaults !targetpw/d" "/etc/sudoers" +else + touch "/etc/sudoers" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/sudoers" + +cp "/etc/sudoers" "/etc/sudoers.bak" +# Insert at the end of the file +printf '%s\n' "Defaults !targetpw" >> "/etc/sudoers" +# Clean up after ourselves. +rm "/etc/sudoers.bak" +if [ -e "/etc/sudoers" ] ; then + + LC_ALL=C sed -i "/Defaults !rootpw/d" "/etc/sudoers" +else + touch "/etc/sudoers" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/sudoers" + +cp "/etc/sudoers" "/etc/sudoers.bak" +# Insert at the end of the file +printf '%s\n' "Defaults !rootpw" >> "/etc/sudoers" +# Clean up after ourselves. +rm "/etc/sudoers.bak" +if [ -e "/etc/sudoers" ] ; then + + LC_ALL=C sed -i "/Defaults !runaspw/d" "/etc/sudoers" +else + touch "/etc/sudoers" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/sudoers" + +cp "/etc/sudoers" "/etc/sudoers.bak" +# Insert at the end of the file +printf '%s\n' "Defaults !runaspw" >> "/etc/sudoers" +# Clean up after ourselves. +rm "/etc/sudoers.bak" + + + + + + + + + + + System Tooling / Utilities + The following checks evaluate the system for recommended base packages -- both for installation +and removal. + + Ensure gnutls-utils is installed + The gnutls-utils package can be installed with the following command: + +$ sudo dnf install gnutls-utils + FIA_X509_EXT.1 + FIA_X509_EXT.2 + SRG-OS-000480-GPOS-00227 + GnuTLS is a secure communications library implementing the SSL, TLS and DTLS +protocols and technologies around them. It provides a simple C language +application programming interface (API) to access the secure communications +protocols as well as APIs to parse and write X.509, PKCS #12, OpenPGP and +other required structures. +This package contains command line TLS client and server and certificate +manipulation tools. + CCE-83494-5 + +package --add=gnutls-utils + + include install_gnutls-utils + +class install_gnutls-utils { + package { 'gnutls-utils': + ensure => 'installed', + } +} + + - name: Ensure gnutls-utils is installed + package: + name: gnutls-utils + state: present + tags: + - CCE-83494-5 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_gnutls-utils_installed + + +[[packages]] +name = "gnutls-utils" +version = "*" + + +if ! rpm -q --quiet "gnutls-utils" ; then + dnf install -y "gnutls-utils" +fi + + + + + + + + + + Ensure nss-tools is installed + The nss-tools package can be installed with the following command: + +$ sudo dnf install nss-tools + FMT_SMF_EXT.1 + SRG-OS-000480-GPOS-00227 + Network Security Services (NSS) is a set of libraries designed to +support cross-platform development of security-enabled client and +server applications. Install the nss-tools package +to install command-line tools to manipulate the NSS certificate +and key database. + CCE-89706-6 + +package --add=nss-tools + + include install_nss-tools + +class install_nss-tools { + package { 'nss-tools': + ensure => 'installed', + } +} + + - name: Ensure nss-tools is installed + package: + name: nss-tools + state: present + tags: + - CCE-89706-6 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_nss-tools_installed + + +[[packages]] +name = "nss-tools" +version = "*" + + +if ! rpm -q --quiet "nss-tools" ; then + dnf install -y "nss-tools" +fi + + + + + + + + + + Install openscap-scanner Package + The openscap-scanner package can be installed with the following command: + +$ sudo dnf install openscap-scanner + AGD_PRE.1 + AGD_OPE.1 + SRG-OS-000480-GPOS-00227 + SRG-OS-000191-GPOS-00080 + openscap-scanner contains the oscap command line tool. This tool is a +configuration and vulnerability scanner, capable of performing compliance checking using +SCAP content. + CCE-83502-5 + +package --add=openscap-scanner + + include install_openscap-scanner + +class install_openscap-scanner { + package { 'openscap-scanner': + ensure => 'installed', + } +} + + - name: Ensure openscap-scanner is installed + package: + name: openscap-scanner + state: present + tags: + - CCE-83502-5 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_openscap-scanner_installed + + +[[packages]] +name = "openscap-scanner" +version = "*" + + +if ! rpm -q --quiet "openscap-scanner" ; then + dnf install -y "openscap-scanner" +fi + + + + + + + + + + Install rear Package + The rear package can be installed with the following command: + +$ sudo dnf install rear + rear contains the Relax-and-Recover (ReaR) utility. ReaR produces a bootable +image of a system and restores from backup using this image. + + CCE-83503-3 + +package --add=rear + + include install_rear + +class install_rear { + package { 'rear': + ensure => 'installed', + } +} + + - name: Ensure rear is installed + package: + name: rear + state: present + when: ansible_architecture != "aarch64" + tags: + - CCE-83503-3 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_rear_installed + + +[[packages]] +name = "rear" +version = "*" + + # Remediation is applicable only in certain platforms +if ! grep -q aarch64 /proc/sys/kernel/osrelease; then + +if ! rpm -q --quiet "rear" ; then + dnf install -y "rear" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Install rng-tools Package + The rng-tools package can be installed with the following command: + +$ sudo dnf install rng-tools + CCI-000366 + SRG-OS-000480-GPOS-00227 + rng-tools provides hardware random number generator tools, +such as those used in the formation of x509/PKI certificates. + CCE-83504-1 + +package --add=rng-tools + + include install_rng-tools + +class install_rng-tools { + package { 'rng-tools': + ensure => 'installed', + } +} + + - name: Ensure rng-tools is installed + package: + name: rng-tools + state: present + tags: + - CCE-83504-1 + - enable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - package_rng-tools_installed + + +[[packages]] +name = "rng-tools" +version = "*" + + +if ! rpm -q --quiet "rng-tools" ; then + dnf install -y "rng-tools" +fi + + + + + + + + + + Install scap-security-guide Package + The scap-security-guide package can be installed with the following command: + +$ sudo dnf install scap-security-guide + AGD_PRE.1 + AGD_OPE.1 + SRG-OS-000480-GPOS-00227 + The scap-security-guide package provides a guide for configuration of the system +from the final system's security point of view. The guidance is specified in the Security +Content Automation Protocol (SCAP) format and constitutes a catalog of practical hardening +advice, linked to government requirements where applicable. The SCAP Security Guide project +bridges the gap between generalized policy requirements and specific implementation guidelines. +A system administrator can use the oscap CLI tool from the openscap-scanner +package, or the SCAP Workbench GUI tool from the scap-workbench package, to verify +that the system conforms to provided guidelines. Refer to the scap-security-guide(8) manual +page for futher information. + CCE-83505-8 + +package --add=scap-security-guide + + include install_scap-security-guide + +class install_scap-security-guide { + package { 'scap-security-guide': + ensure => 'installed', + } +} + + - name: Ensure scap-security-guide is installed + package: + name: scap-security-guide + state: present + tags: + - CCE-83505-8 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_scap-security-guide_installed + + +[[packages]] +name = "scap-security-guide" +version = "*" + + +if ! rpm -q --quiet "scap-security-guide" ; then + dnf install -y "scap-security-guide" +fi + + + + + + + + + + Install subscription-manager Package + The subscription-manager package can be installed with the following command: + +$ sudo dnf install subscription-manager + 0940 + 1144 + 1467 + 1472 + 1483 + 1493 + 1494 + 1495 + FPT_TUD_EXT.1 + FPT_TUD_EXT.2 + SRG-OS-000366-GPOS-00153 + Red Hat Subscription Manager is a local service which tracks installed products +and subscriptions on a local system to help manage subscription assignments. +It communicates with the backend subscription service (the Customer Portal +or an on-premise server such as Subscription Asset Manager) and works with +content management tools such as . + + +The package provides, among other things, plugins +to interact with repositories and subscriptions +from the Red Hat entitlement platform - the subscription-manager and +product-id plugins. + CCE-83506-6 + +package --add=subscription-manager + + include install_subscription-manager + +class install_subscription-manager { + package { 'subscription-manager': + ensure => 'installed', + } +} + + - name: Ensure subscription-manager is installed + package: + name: subscription-manager + state: present + tags: + - CCE-83506-6 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_subscription-manager_installed + + +[[packages]] +name = "subscription-manager" +version = "*" + + +if ! rpm -q --quiet "subscription-manager" ; then + dnf install -y "subscription-manager" +fi + + + + + + + + + + Uninstall geolite2-city Package + The geolite2-city package can be removed with the following command: + +$ sudo dnf erase geolite2-city + geolite2-city is part of the GeoLite2 database packages, offering geolocation databases and tooling. + +package --remove=geolite2-city + + include remove_geolite2-city + +class remove_geolite2-city { + package { 'geolite2-city': + ensure => 'purged', + } +} + + - name: Ensure geolite2-city is removed + package: + name: geolite2-city + state: absent + tags: + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - package_geolite2-city_removed + + +# CAUTION: This remediation script will remove geolite2-city +# from the system, and may remove any packages +# that depend on geolite2-city. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "geolite2-city" ; then + + dnf remove -y "geolite2-city" + +fi + + + + + + + + + + Uninstall geolite2-country Package + The geolite2-country package can be removed with the following command: + +$ sudo dnf erase geolite2-country + geolite2-country is part of the GeoLite2 database packages, offering geolocation databases and tooling. + +package --remove=geolite2-country + + include remove_geolite2-country + +class remove_geolite2-country { + package { 'geolite2-country': + ensure => 'purged', + } +} + + - name: Ensure geolite2-country is removed + package: + name: geolite2-country + state: absent + tags: + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - package_geolite2-country_removed + + +# CAUTION: This remediation script will remove geolite2-country +# from the system, and may remove any packages +# that depend on geolite2-country. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "geolite2-country" ; then + + dnf remove -y "geolite2-country" + +fi + + + + + + + + + + Uninstall gssproxy Package + The gssproxy package can be removed with the following command: + +$ sudo dnf erase gssproxy + CCI-000381 + CCI-000366 + SRG-OS-000095-GPOS-00049 + SRG-OS-000480-GPOS-00227 + gssproxy is a proxy for GSS API credential handling. + CCE-83516-5 + include remove_gssproxy + +class remove_gssproxy { + package { 'gssproxy': + ensure => 'purged', + } +} + + - name: Ensure gssproxy is removed + package: + name: gssproxy + state: absent + tags: + - CCE-83516-5 + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_gssproxy_removed + + +# CAUTION: This remediation script will remove gssproxy +# from the system, and may remove any packages +# that depend on gssproxy. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "gssproxy" ; then + + dnf remove -y "gssproxy" + +fi + + + + + + + + + + Uninstall iprutils Package + The iprutils package can be removed with the following command: + +$ sudo dnf erase iprutils + CCI-000366 + SRG-OS-000095-GPOS-00049 + SRG-OS-000480-GPOS-00227 + iprutils provides a suite of utlilities to manage and configure SCSI devices +supported by the ipr SCSI storage device driver. + CCE-83519-9 + +package --remove=iprutils + + include remove_iprutils + +class remove_iprutils { + package { 'iprutils': + ensure => 'purged', + } +} + + - name: Ensure iprutils is removed + package: + name: iprutils + state: absent + tags: + - CCE-83519-9 + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_iprutils_removed + + +# CAUTION: This remediation script will remove iprutils +# from the system, and may remove any packages +# that depend on iprutils. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "iprutils" ; then + + dnf remove -y "iprutils" + +fi + + + + + + + + + + Uninstall krb5-workstation Package + The krb5-workstation package can be removed with the following command: + +$ sudo dnf erase krb5-workstation + CCI-000803 + SRG-OS-000095-GPOS-00049 + SRG-OS-000120-GPOS-00061 + Kerberos is a network authentication system. The krb5-workstation package contains the basic +Kerberos programs (kinit, klist, kdestroy, kpasswd). + CCE-83520-7 + +package --remove=krb5-workstation + + include remove_krb5-workstation + +class remove_krb5-workstation { + package { 'krb5-workstation': + ensure => 'purged', + } +} + + - name: Ensure krb5-workstation is removed + package: + name: krb5-workstation + state: absent + tags: + - CCE-83520-7 + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_krb5-workstation_removed + + +# CAUTION: This remediation script will remove krb5-workstation +# from the system, and may remove any packages +# that depend on krb5-workstation. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "krb5-workstation" ; then + + dnf remove -y "krb5-workstation" + +fi + + + + + + + + + + Uninstall tuned Package + The tuned package can be removed with the following command: + +$ sudo dnf erase tuned + CCI-000366 + SRG-OS-000095-GPOS-00049 + SRG-OS-000480-GPOS-00227 + tuned contains a daemon that tunes the system settings dynamically. +It does so by monitoring the usage of several system components periodically. Based +on that information, components will then be put into lower or higher power savings +modes to adapt to the current usage. + CCE-83521-5 + +package --remove=tuned + + include remove_tuned + +class remove_tuned { + package { 'tuned': + ensure => 'purged', + } +} + + - name: Ensure tuned is removed + package: + name: tuned + state: absent + tags: + - CCE-83521-5 + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_tuned_removed + + +# CAUTION: This remediation script will remove tuned +# from the system, and may remove any packages +# that depend on tuned. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "tuned" ; then + + dnf remove -y "tuned" + +fi + + + + + + + + + + + Updating Software + The dnf command line tool is used to install and +update software packages. The system also provides a graphical +software update tool in the System menu, in the Administration submenu, +called Software Update. + +Red Hat Enterprise Linux 9 systems contain an installed software catalog called +the RPM database, which records metadata of installed packages. Consistently using +dnf or the graphical Software Update for all software installation +allows for insight into the current inventory of installed software on the system. + + + Install dnf-automatic Package + The dnf-automatic package can be installed with the following command: + +$ sudo dnf install dnf-automatic + BP28(R8) + SRG-OS-000191-GPOS-00080 + dnf-automatic is an alternative command line interface (CLI) +to dnf upgrade suitable for automatic, regular execution. + CCE-83454-9 + +package --add=dnf-automatic + + include install_dnf-automatic + +class install_dnf-automatic { + package { 'dnf-automatic': + ensure => 'installed', + } +} + + - name: Ensure dnf-automatic is installed + package: + name: dnf-automatic + state: present + tags: + - CCE-83454-9 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_dnf-automatic_installed + + +[[packages]] +name = "dnf-automatic" +version = "*" + + +if ! rpm -q --quiet "dnf-automatic" ; then + dnf install -y "dnf-automatic" +fi + + + + + + + + + + Ensure dnf Removes Previous Package Versions + dnf should be configured to remove previous software components after +new versions have been installed. To configure dnf to remove the + +previous software components after updating, set the clean_requirements_on_remove + + +to 1 in /etc/dnf/dnf.conf. + 18 + 20 + 4 + APO12.01 + APO12.02 + APO12.03 + APO12.04 + BAI03.10 + DSS05.01 + DSS05.02 + 3.4.8 + CCI-002617 + 4.2.3 + 4.2.3.12 + 4.2.3.7 + 4.2.3.9 + A.12.6.1 + A.14.2.3 + A.16.1.3 + A.18.2.2 + A.18.2.3 + SI-2(6) + CM-11(a) + CM-11(b) + CM-6(a) + ID.RA-1 + PR.IP-12 + SRG-OS-000437-GPOS-00194 + SRG-OS-000437-VMM-001760 + Previous versions of software components that are not removed from the information +system after updates have been installed may be exploited by some adversaries. + + CCE-83458-0 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83458-0 + - NIST-800-171-3.4.8 + - NIST-800-53-CM-11(a) + - NIST-800-53-CM-11(b) + - NIST-800-53-CM-6(a) + - NIST-800-53-SI-2(6) + - clean_components_post_updating + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - restrict_strategy + +- name: Ensure DNF Removes Previous Package Versions + lineinfile: + dest: /etc/dnf/dnf.conf + regexp: ^#?clean_requirements_on_remove + line: clean_requirements_on_remove=1 + insertafter: \[main\] + create: true + when: '"yum" in ansible_facts.packages' + tags: + - CCE-83458-0 + - NIST-800-171-3.4.8 + - NIST-800-53-CM-11(a) + - NIST-800-53-CM-11(b) + - NIST-800-53-CM-6(a) + - NIST-800-53-SI-2(6) + - clean_components_post_updating + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q yum; then + +if grep --silent ^clean_requirements_on_remove /etc/dnf/dnf.conf ; then + sed -i "s/^clean_requirements_on_remove.*/clean_requirements_on_remove=1/g" /etc/dnf/dnf.conf +else + echo -e "\n# Set clean_requirements_on_remove to 1 per security requirements" >> /etc/dnf/dnf.conf + echo "clean_requirements_on_remove=1" >> /etc/dnf/dnf.conf +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure dnf-automatic to Install Available Updates Automatically + To ensure that the packages comprising the available updates will be automatically installed by dnf-automatic, set apply_updates to yes under [commands] section in /etc/dnf/automatic.conf. + BP28(R8) + 0940 + 1144 + 1467 + 1472 + 1483 + 1493 + 1494 + 1495 + SI-2(5) + CM-6(a) + SI-2(c) + FMT_SMF_EXT.1 + SRG-OS-000191-GPOS-00080 + Installing software updates is a fundamental mitigation against +the exploitation of publicly-known vulnerabilities. If the most +recent security patches and updates are not installed, unauthorized +users may take advantage of weaknesses in the unpatched software. The +lack of prompt attention to patching could result in a system compromise. +The automated installation of updates ensures that recent security patches +are applied in a timely manner. + CCE-83456-4 + +found=false + +# set value in all files if they contain section or key +for f in $(echo -n "/etc/dnf/automatic.conf"); do + if [ ! -e "$f" ]; then + continue + fi + + # find key in section and change value + if grep -qzosP "[[:space:]]*\[commands\]([^\n\[]*\n+)+?[[:space:]]*apply_updates" "$f"; then + sed -i "s/apply_updates[^(\n)]*/apply_updates = yes/" "$f" + found=true + + # find section and add key = value to it + elif grep -qs "[[:space:]]*\[commands\]" "$f"; then + sed -i "/[[:space:]]*\[commands\]/a apply_updates = yes" "$f" + found=true + fi +done + +# if section not in any file, append section with key = value to FIRST file in files parameter +if ! $found ; then + file=$(echo "/etc/dnf/automatic.conf" | cut -f1 -d ' ') + mkdir -p "$(dirname "$file")" + echo -e "[commands]\napply_updates = yes" >> "$file" +fi + + + + + + + + + + Configure dnf-automatic to Install Only Security Updates + To configure dnf-automatic to install only security updates +automatically, set upgrade_type to security under +[commands] section in /etc/dnf/automatic.conf. + BP28(R8) + SI-2(5) + CM-6(a) + SI-2(c) + FMT_SMF_EXT.1 + SRG-OS-000191-GPOS-00080 + By default, dnf-automatic installs all available updates. +Reducing the amount of updated packages only to updates that were +issued as a part of a security advisory increases the system stability. + CCE-83461-4 + +found=false + +# set value in all files if they contain section or key +for f in $(echo -n "/etc/dnf/automatic.conf"); do + if [ ! -e "$f" ]; then + continue + fi + + # find key in section and change value + if grep -qzosP "[[:space:]]*\[commands\]([^\n\[]*\n+)+?[[:space:]]*upgrade_type" "$f"; then + sed -i "s/upgrade_type[^(\n)]*/upgrade_type = security/" "$f" + found=true + + # find section and add key = value to it + elif grep -qs "[[:space:]]*\[commands\]" "$f"; then + sed -i "/[[:space:]]*\[commands\]/a upgrade_type = security" "$f" + found=true + fi +done + +# if section not in any file, append section with key = value to FIRST file in files parameter +if ! $found ; then + file=$(echo "/etc/dnf/automatic.conf" | cut -f1 -d ' ') + mkdir -p "$(dirname "$file")" + echo -e "[commands]\nupgrade_type = security" >> "$file" +fi + + + + + + + + + + Ensure gpgcheck Enabled In Main dnf Configuration + The gpgcheck option controls whether +RPM packages' signatures are always checked prior to installation. +To configure dnf to check package signatures before installing +them, ensure the following line appears in /etc/dnf/dnf.conf in +the [main] section: +gpgcheck=1 + BP28(R15) + 11 + 2 + 3 + 9 + 5.10.4.1 + APO01.06 + BAI03.05 + BAI06.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS06.02 + 3.4.8 + CCI-001749 + 164.308(a)(1)(ii)(D) + 164.312(b) + 164.312(c)(1) + 164.312(c)(2) + 164.312(e)(2)(i) + 4.3.4.3.2 + 4.3.4.3.3 + 4.3.4.4.4 + SR 3.1 + SR 3.3 + SR 3.4 + SR 3.8 + SR 7.6 + A.11.2.4 + A.12.1.2 + A.12.2.1 + A.12.5.1 + A.12.6.2 + A.14.1.2 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + CM-5(3) + SI-7 + SC-12 + SC-12(3) + CM-6(a) + SA-12 + SA-12(10) + CM-11(a) + CM-11(b) + PR.DS-6 + PR.DS-8 + PR.IP-1 + FPT_TUD_EXT.1 + FPT_TUD_EXT.2 + Req-6.2 + SRG-OS-000366-GPOS-00153 + SRG-OS-000366-VMM-001430 + SRG-OS-000370-VMM-001460 + SRG-OS-000404-VMM-001650 + Changes to any software components can have significant effects on the +overall security of the operating system. This requirement ensures the +software has not been tampered with and that it has been provided by a +trusted vendor. + +Accordingly, patches, service packs, device drivers, or operating system +components must be signed with a certificate recognized and approved by the +organization. +Verifying the authenticity of the software prior to installation +validates the integrity of the patch or upgrade received from a vendor. +This ensures the software has not been tampered with and that it has been +provided by a trusted vendor. Self-signed certificates are disallowed by +this requirement. Certificates used to verify the software must be from an +approved Certificate Authority (CA). + + CCE-83457-2 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83457-2 + - CJIS-5.10.4.1 + - NIST-800-171-3.4.8 + - NIST-800-53-CM-11(a) + - NIST-800-53-CM-11(b) + - NIST-800-53-CM-5(3) + - NIST-800-53-CM-6(a) + - NIST-800-53-SA-12 + - NIST-800-53-SA-12(10) + - NIST-800-53-SC-12 + - NIST-800-53-SC-12(3) + - NIST-800-53-SI-7 + - PCI-DSS-Req-6.2 + - configure_strategy + - ensure_gpgcheck_globally_activated + - high_severity + - low_complexity + - medium_disruption + - no_reboot_needed + +- name: Ensure GPG check is globally activated + ini_file: + dest: /etc/dnf/dnf.conf + section: main + option: gpgcheck + value: 1 + no_extra_spaces: true + create: false + when: '"yum" in ansible_facts.packages' + tags: + - CCE-83457-2 + - CJIS-5.10.4.1 + - NIST-800-171-3.4.8 + - NIST-800-53-CM-11(a) + - NIST-800-53-CM-11(b) + - NIST-800-53-CM-5(3) + - NIST-800-53-CM-6(a) + - NIST-800-53-SA-12 + - NIST-800-53-SA-12(10) + - NIST-800-53-SC-12 + - NIST-800-53-SC-12(3) + - NIST-800-53-SI-7 + - PCI-DSS-Req-6.2 + - configure_strategy + - ensure_gpgcheck_globally_activated + - high_severity + - low_complexity + - medium_disruption + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if rpm --quiet -q yum; then + +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/dnf/dnf.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^gpgcheck") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "1" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^gpgcheck\\>" "/etc/dnf/dnf.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^gpgcheck\\>.*/$escaped_formatted_output/gi" "/etc/dnf/dnf.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-83457-2" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/dnf/dnf.conf" >> "/etc/dnf/dnf.conf" + printf '%s\n' "$formatted_output" >> "/etc/dnf/dnf.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure gpgcheck Enabled for Local Packages + dnf should be configured to verify the signature(s) of local packages +prior to installation. To configure dnf to verify signatures of local +packages, set the localpkg_gpgcheck to 1 in /etc/dnf/dnf.conf. + BP28(R15) + 11 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + 3.4.8 + CCI-001749 + 164.308(a)(1)(ii)(D) + 164.312(b) + 164.312(c)(1) + 164.312(c)(2) + 164.312(e)(2)(i) + 4.3.4.3.2 + 4.3.4.3.3 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + CM-11(a) + CM-11(b) + CM-6(a) + CM-5(3) + SA-12 + SA-12(10) + PR.IP-1 + FPT_TUD_EXT.1 + FPT_TUD_EXT.2 + SRG-OS-000366-GPOS-00153 + SRG-OS-000366-VMM-001430 + SRG-OS-000370-VMM-001460 + SRG-OS-000404-VMM-001650 + Changes to any software components can have significant effects to the overall security +of the operating system. This requirement ensures the software has not been tampered and +has been provided by a trusted vendor. + +Accordingly, patches, service packs, device drivers, or operating system components must +be signed with a certificate recognized and approved by the organization. + + CCE-83463-0 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83463-0 + - NIST-800-171-3.4.8 + - NIST-800-53-CM-11(a) + - NIST-800-53-CM-11(b) + - NIST-800-53-CM-5(3) + - NIST-800-53-CM-6(a) + - NIST-800-53-SA-12 + - NIST-800-53-SA-12(10) + - ensure_gpgcheck_local_packages + - high_severity + - low_complexity + - medium_disruption + - no_reboot_needed + - unknown_strategy + +- name: Ensure GPG check Enabled for Local Packages (dnf) + block: + + - name: Check stats of dnf + stat: + path: /etc/dnf/dnf.conf + register: pkg + + - name: Check if config file of dnf is a symlink + ansible.builtin.set_fact: + pkg_config_file_symlink: '{{ pkg.stat.lnk_target if pkg.stat.lnk_target is match("^/.*") + else "/etc/dnf/dnf.conf" | dirname ~ "/" ~ pkg.stat.lnk_target }}' + when: pkg.stat.lnk_target is defined + + - name: Ensure GPG check Enabled for Local Packages (dnf) + ini_file: + dest: '{{ pkg_config_file_symlink | default("/etc/dnf/dnf.conf") }}' + section: main + option: localpkg_gpgcheck + value: 1 + no_extra_spaces: true + create: true + when: '"yum" in ansible_facts.packages' + tags: + - CCE-83463-0 + - NIST-800-171-3.4.8 + - NIST-800-53-CM-11(a) + - NIST-800-53-CM-11(b) + - NIST-800-53-CM-5(3) + - NIST-800-53-CM-6(a) + - NIST-800-53-SA-12 + - NIST-800-53-SA-12(10) + - ensure_gpgcheck_local_packages + - high_severity + - low_complexity + - medium_disruption + - no_reboot_needed + - unknown_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q yum; then + +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/dnf/dnf.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^localpkg_gpgcheck") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "1" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^localpkg_gpgcheck\\>" "/etc/dnf/dnf.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^localpkg_gpgcheck\\>.*/$escaped_formatted_output/gi" "/etc/dnf/dnf.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-83463-0" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/dnf/dnf.conf" >> "/etc/dnf/dnf.conf" + printf '%s\n' "$formatted_output" >> "/etc/dnf/dnf.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure gpgcheck Enabled for All dnf Package Repositories + To ensure signature checking is not disabled for +any repos, remove any lines from files in /etc/yum.repos.d of the form: +gpgcheck=0 + BP28(R15) + 11 + 2 + 3 + 9 + 5.10.4.1 + APO01.06 + BAI03.05 + BAI06.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS06.02 + 3.4.8 + CCI-001749 + 164.308(a)(1)(ii)(D) + 164.312(b) + 164.312(c)(1) + 164.312(c)(2) + 164.312(e)(2)(i) + 4.3.4.3.2 + 4.3.4.3.3 + 4.3.4.4.4 + SR 3.1 + SR 3.3 + SR 3.4 + SR 3.8 + SR 7.6 + A.11.2.4 + A.12.1.2 + A.12.2.1 + A.12.5.1 + A.12.6.2 + A.14.1.2 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + CM-5(3) + SI-7 + SC-12 + SC-12(3) + CM-6(a) + SA-12 + SA-12(10) + CM-11(a) + CM-11(b) + PR.DS-6 + PR.DS-8 + PR.IP-1 + FPT_TUD_EXT.1 + FPT_TUD_EXT.2 + Req-6.2 + SRG-OS-000366-GPOS-00153 + SRG-OS-000366-VMM-001430 + SRG-OS-000370-VMM-001460 + SRG-OS-000404-VMM-001650 + Verifying the authenticity of the software prior to installation validates +the integrity of the patch or upgrade received from a vendor. This ensures +the software has not been tampered with and that it has been provided by a +trusted vendor. Self-signed certificates are disallowed by this +requirement. Certificates used to verify the software must be from an +approved Certificate Authority (CA)." + CCE-83464-8 + - name: Grep for dnf repo section names + shell: | + set -o pipefail + grep -HEr '^\[.+\]' -r /etc/yum.repos.d/ + register: repo_grep_results + ignore_errors: true + changed_when: false + tags: + - CCE-83464-8 + - CJIS-5.10.4.1 + - NIST-800-171-3.4.8 + - NIST-800-53-CM-11(a) + - NIST-800-53-CM-11(b) + - NIST-800-53-CM-5(3) + - NIST-800-53-CM-6(a) + - NIST-800-53-SA-12 + - NIST-800-53-SA-12(10) + - NIST-800-53-SC-12 + - NIST-800-53-SC-12(3) + - NIST-800-53-SI-7 + - PCI-DSS-Req-6.2 + - enable_strategy + - ensure_gpgcheck_never_disabled + - high_severity + - low_complexity + - medium_disruption + - no_reboot_needed + +- name: Set gpgcheck=1 for each dnf repo + ini_file: + path: '{{ item[0] }}' + section: '{{ item[1] }}' + option: gpgcheck + value: '1' + no_extra_spaces: true + loop: '{{ repo_grep_results.stdout | regex_findall( ''(.+\.repo):\[(.+)\]\n?'' ) + }}' + tags: + - CCE-83464-8 + - CJIS-5.10.4.1 + - NIST-800-171-3.4.8 + - NIST-800-53-CM-11(a) + - NIST-800-53-CM-11(b) + - NIST-800-53-CM-5(3) + - NIST-800-53-CM-6(a) + - NIST-800-53-SA-12 + - NIST-800-53-SA-12(10) + - NIST-800-53-SC-12 + - NIST-800-53-SC-12(3) + - NIST-800-53-SI-7 + - PCI-DSS-Req-6.2 + - enable_strategy + - ensure_gpgcheck_never_disabled + - high_severity + - low_complexity + - medium_disruption + - no_reboot_needed + + +sed -i 's/gpgcheck\s*=.*/gpgcheck=1/g' /etc/yum.repos.d/* + + + + + + + + + + Ensure Red Hat GPG Key Installed + To ensure the system can cryptographically verify base software packages +come from Red Hat (and to connect to the Red Hat Network to receive them), +the Red Hat GPG key must properly be installed. To install the Red Hat GPG +key, run: +$ sudo subscription-manager register + +If the system is not connected to the Internet or an RHN Satellite, then +install the Red Hat GPG key from trusted media such as the Red Hat +installation CD-ROM or DVD. Assuming the disc is mounted in +/media/cdrom, use the following command as the root user to import +it into the keyring: +$ sudo rpm --import /media/cdrom/RPM-GPG-KEY + +Alternatively, the key may be pre-loaded during the RHEL installation. In +such cases, the key can be installed by running the following command: +sudo rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release + BP28(R15) + 11 + 2 + 3 + 9 + 5.10.4.1 + APO01.06 + BAI03.05 + BAI06.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS06.02 + 3.4.8 + CCI-001749 + 164.308(a)(1)(ii)(D) + 164.312(b) + 164.312(c)(1) + 164.312(c)(2) + 164.312(e)(2)(i) + 4.3.4.3.2 + 4.3.4.3.3 + 4.3.4.4.4 + SR 3.1 + SR 3.3 + SR 3.4 + SR 3.8 + SR 7.6 + A.11.2.4 + A.12.1.2 + A.12.2.1 + A.12.5.1 + A.12.6.2 + A.14.1.2 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + CIP-003-8 R4.2 + CIP-003-8 R6 + CIP-007-3 R4 + CIP-007-3 R4.1 + CIP-007-3 R4.2 + CIP-007-3 R5.1 + CM-5(3) + SI-7 + SC-12 + SC-12(3) + CM-6(a) + PR.DS-6 + PR.DS-8 + PR.IP-1 + FPT_TUD_EXT.1 + FPT_TUD_EXT.2 + Req-6.2 + SRG-OS-000366-GPOS-00153 + SRG-OS-000366-VMM-001430 + SRG-OS-000370-VMM-001460 + SRG-OS-000404-VMM-001650 + Changes to software components can have significant effects on the overall +security of the operating system. This requirement ensures the software has +not been tampered with and that it has been provided by a trusted vendor. +The Red Hat GPG key is necessary to cryptographically verify packages are +from Red Hat. + CCE-84180-9 + - name: Read permission of GPG key directory + stat: + path: /etc/pki/rpm-gpg/ + register: gpg_key_directory_permission + check_mode: false + tags: + - CCE-84180-9 + - CJIS-5.10.4.1 + - NIST-800-171-3.4.8 + - NIST-800-53-CM-5(3) + - NIST-800-53-CM-6(a) + - NIST-800-53-SC-12 + - NIST-800-53-SC-12(3) + - NIST-800-53-SI-7 + - PCI-DSS-Req-6.2 + - ensure_redhat_gpgkey_installed + - high_severity + - medium_complexity + - medium_disruption + - no_reboot_needed + - restrict_strategy + +- name: Read signatures in GPG key + command: gpg --show-keys --with-fingerprint --with-colons "/etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release" + args: + warn: false + changed_when: false + register: gpg_fingerprints + check_mode: false + tags: + - CCE-84180-9 + - CJIS-5.10.4.1 + - NIST-800-171-3.4.8 + - NIST-800-53-CM-5(3) + - NIST-800-53-CM-6(a) + - NIST-800-53-SC-12 + - NIST-800-53-SC-12(3) + - NIST-800-53-SI-7 + - PCI-DSS-Req-6.2 + - ensure_redhat_gpgkey_installed + - high_severity + - medium_complexity + - medium_disruption + - no_reboot_needed + - restrict_strategy + +- name: Set Fact - Installed GPG Fingerprints + set_fact: + gpg_installed_fingerprints: |- + {{ gpg_fingerprints.stdout | regex_findall('^pub.* + (?:^fpr[:]*)([0-9A-Fa-f]*)', '\1') | list }} + tags: + - CCE-84180-9 + - CJIS-5.10.4.1 + - NIST-800-171-3.4.8 + - NIST-800-53-CM-5(3) + - NIST-800-53-CM-6(a) + - NIST-800-53-SC-12 + - NIST-800-53-SC-12(3) + - NIST-800-53-SI-7 + - PCI-DSS-Req-6.2 + - ensure_redhat_gpgkey_installed + - high_severity + - medium_complexity + - medium_disruption + - no_reboot_needed + - restrict_strategy + +- name: Set Fact - Valid fingerprints + set_fact: + gpg_valid_fingerprints: ("567E347AD0044ADE55BA8A5F199E2F91FD431D51" "7E4624258C406535D56D6F135054E4A45A6340B3") + tags: + - CCE-84180-9 + - CJIS-5.10.4.1 + - NIST-800-171-3.4.8 + - NIST-800-53-CM-5(3) + - NIST-800-53-CM-6(a) + - NIST-800-53-SC-12 + - NIST-800-53-SC-12(3) + - NIST-800-53-SI-7 + - PCI-DSS-Req-6.2 + - ensure_redhat_gpgkey_installed + - high_severity + - medium_complexity + - medium_disruption + - no_reboot_needed + - restrict_strategy + +- name: Import RedHat GPG key + rpm_key: + state: present + key: /etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release + when: + - gpg_key_directory_permission.stat.mode <= '0755' + - (gpg_installed_fingerprints | difference(gpg_valid_fingerprints)) | length == + 0 + - gpg_installed_fingerprints | length > 0 + - ansible_distribution == "RedHat" + tags: + - CCE-84180-9 + - CJIS-5.10.4.1 + - NIST-800-171-3.4.8 + - NIST-800-53-CM-5(3) + - NIST-800-53-CM-6(a) + - NIST-800-53-SC-12 + - NIST-800-53-SC-12(3) + - NIST-800-53-SI-7 + - PCI-DSS-Req-6.2 + - ensure_redhat_gpgkey_installed + - high_severity + - medium_complexity + - medium_disruption + - no_reboot_needed + - restrict_strategy + + # The two fingerprints below are retrieved from https://access.redhat.com/security/team/key +readonly REDHAT_RELEASE_FINGERPRINT="567E347AD0044ADE55BA8A5F199E2F91FD431D51" +readonly REDHAT_AUXILIARY_FINGERPRINT="7E4624258C406535D56D6F135054E4A45A6340B3" + +# Location of the key we would like to import (once it's integrity verified) +readonly REDHAT_RELEASE_KEY="/etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release" + +RPM_GPG_DIR_PERMS=$(stat -c %a "$(dirname "$REDHAT_RELEASE_KEY")") + +# Verify /etc/pki/rpm-gpg directory permissions are safe +if [ "${RPM_GPG_DIR_PERMS}" -le "755" ] +then + # If they are safe, try to obtain fingerprints from the key file + # (to ensure there won't be e.g. CRC error). + + readarray -t GPG_OUT < <(gpg --show-keys --with-fingerprint --with-colons "$REDHAT_RELEASE_KEY" | grep -A1 "^pub" | grep "^fpr" | cut -d ":" -f 10) + + GPG_RESULT=$? + # No CRC error, safe to proceed + if [ "${GPG_RESULT}" -eq "0" ] + then + echo "${GPG_OUT[*]}" | grep -vE "${REDHAT_RELEASE_FINGERPRINT}|${REDHAT_AUXILIARY_FINGERPRINT}" || { + # If $REDHAT_RELEASE_KEY file doesn't contain any keys with unknown fingerprint, import it + rpm --import "${REDHAT_RELEASE_KEY}" + } + fi +fi + + + + + + + + + + Ensure Software Patches Installed + +NOTE: U.S. Defense systems are required to be patched within 30 days or sooner as local policy +dictates. + The OVAL feed of Red Hat Enterprise Linux 9 is not a XML file, which may not be understood by all scanners. + BP28(R08) + 18 + 20 + 4 + 5.10.4.1 + APO12.01 + APO12.02 + APO12.03 + APO12.04 + BAI03.10 + DSS05.01 + DSS05.02 + CCI-000366 + CCI-001227 + 4.2.3 + 4.2.3.12 + 4.2.3.7 + 4.2.3.9 + A.12.6.1 + A.14.2.3 + A.16.1.3 + A.18.2.2 + A.18.2.3 + SI-2(5) + SI-2(c) + CM-6(a) + ID.RA-1 + PR.IP-12 + FMT_MOF_EXT.1 + Req-6.2 + SRG-OS-000480-GPOS-00227 + SRG-OS-000480-VMM-002000 + Installing software updates is a fundamental mitigation against +the exploitation of publicly-known vulnerabilities. If the most +recent security patches and updates are not installed, unauthorized +users may take advantage of weaknesses in the unpatched software. The +lack of prompt attention to patching could result in a system compromise. + CCE-84185-8 + - name: Security patches are up to date + package: + name: '*' + state: latest + tags: + - CCE-84185-8 + - CJIS-5.10.4.1 + - NIST-800-53-CM-6(a) + - NIST-800-53-SI-2(5) + - NIST-800-53-SI-2(c) + - PCI-DSS-Req-6.2 + - high_disruption + - low_complexity + - medium_severity + - patch_strategy + - reboot_required + - security_patches_up_to_date + - skip_ansible_lint + + + +yum -y update + + + + + + + Enable dnf-automatic Timer + +The dnf-automatic timer can be enabled with the following command: +$ sudo systemctl enable dnf-automatic.timer + BP28(R8) + SI-2(5) + CM-6(a) + SI-2(c) + FMT_SMF_EXT.1 + SRG-OS-000191-GPOS-00080 + The dnf-automatic is an alternative command line interface (CLI) to dnf upgrade with specific facilities to make it suitable to be executed automatically and regularly from systemd timers, cron jobs and similar. +The tool is controlled by dnf-automatic.timer SystemD timer. + CCE-83459-8 + - name: Enable timer dnf-automatic + block: + + - name: Gather the package facts + package_facts: + manager: auto + + - name: Enable timer dnf-automatic + systemd: + name: dnf-automatic.timer + enabled: 'yes' + state: started + when: + - '"dnf-automatic" in ansible_facts.packages' + tags: + - CCE-83459-8 + - NIST-800-53-CM-6(a) + - NIST-800-53-SI-2(5) + - NIST-800-53-SI-2(c) + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - timer_dnf-automatic_enabled + + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" start 'dnf-automatic.timer' +"$SYSTEMCTL_EXEC" enable 'dnf-automatic.timer' + + + + + + + + + + + + Account and Access Control + In traditional Unix security, if an attacker gains +shell access to a certain login account, they can perform any action +or access any file to which that account has access. Therefore, +making it more difficult for unauthorized people to gain shell +access to accounts, particularly to privileged accounts, is a +necessary part of securing a system. This section introduces +mechanisms for restricting access to accounts under +Red Hat Enterprise Linux 9. + + Authselect profile + Specify the authselect profile to select + minimal + minimal + sssd + + + Enable authselect + Configure user authentication setup to use the authselect tool. +If authselect profile is selected, the rule will enable the profile. + If the sudo authselect select command returns an error informing that the chosen +profile cannot be selected, it is probably because PAM files have already been modified by +the administrator. If this is the case, in order to not overwrite the desired changes made +by the administrator, the current PAM settings should be investigated before forcing the +selection of the chosen authselect profile. + BP28(R5) + CCI-000213 + 164.308(a)(1)(ii)(B) + 164.308(a)(7)(i) + 164.308(a)(7)(ii)(A) + 164.310(a)(1) + 164.310(a)(2)(i) + 164.310(a)(2)(ii) + 164.310(a)(2)(iii) + 164.310(b) + 164.310(c) + 164.310(d)(1) + 164.310(d)(2)(iii) + AC-3 + FIA_UAU.1 + FIA_AFL.1 + SRG-OS-000480-GPOS-00227 + Authselect is a successor to authconfig. +It is a tool to select system authentication and identity sources from a list of supported +profiles instead of letting the administrator manually build the PAM stack. + +That way, it avoids potential breakage of configuration, as it ships several tested profiles +that are well tested and supported to solve different use-cases. + CCE-89732-2 + - name: XCCDF Value var_authselect_profile # promote to variable + set_fact: + var_authselect_profile: !!str + tags: + - always + +- name: Select authselect profile + ansible.builtin.command: + cmd: authselect select "{{ var_authselect_profile }}" + ignore_errors: true + register: result_authselect_select + tags: + - CCE-89732-2 + - NIST-800-53-AC-3 + - configure_strategy + - enable_authselect + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + +- name: Verify if PAM has been altered + ansible.builtin.command: + cmd: rpm -qV pam + register: result_altered_authselect + ignore_errors: true + args: + warn: false + when: result_authselect_select is failed + tags: + - CCE-89732-2 + - NIST-800-53-AC-3 + - configure_strategy + - enable_authselect + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + +- name: Informative message based on the authselect integrity check + ansible.builtin.assert: + that: + - result_altered_authselect is success + fail_msg: + - Files in the 'pam' package have been altered, so the authselect configuration + won't be forced. + tags: + - CCE-89732-2 + - NIST-800-53-AC-3 + - configure_strategy + - enable_authselect + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + +- name: Force authselect profile select + ansible.builtin.command: + cmd: authselect select --force "{{ var_authselect_profile }}" + when: + - result_altered_authselect is success + - result_authselect_select is failed + tags: + - CCE-89732-2 + - NIST-800-53-AC-3 + - configure_strategy + - enable_authselect + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + + +var_authselect_profile='' + + +authselect select "$var_authselect_profile" + +if test "$?" -ne 0; then + if rpm --quiet --verify pam; then + authselect select --force "$var_authselect_profile" + else + echo "Files in the 'pam' package have been altered, so the authselect configuration won't be forced" >&2 + fi +fi + + + + + + + + + + Warning Banners for System Accesses + Each system should expose as little information about +itself as possible. + +System banners, which are typically displayed just before a +login prompt, give out information about the service or the host's +operating system. This might include the distribution name and the +system kernel version, and the particular version of a network +service. This information can assist intruders in gaining access to +the system as it can reveal whether the system is running +vulnerable software. Most network services can be configured to +limit what information is displayed. + +Many organizations implement security policies that require a +system banner provide notice of the system's ownership, provide +warning to unauthorized users, and remind authorized users of their +consent to monitoring. + + Login Banner Verbiage + Enter an appropriate login banner for your organization. Please note that new lines must +be expressed by the '\n' character and special characters like parentheses and quotation marks must be escaped with '\\'. + ^(Authorized[\s\n]+uses[\s\n]+only\.[\s\n]+All[\s\n]+activity[\s\n]+may[\s\n]+be[\s\n]+monitored[\s\n]+and[\s\n]+reported\.|^(?!.*(\\|fedora|rhel|sle|ubuntu)).*)$ + ^Authorized[\s\n]+uses[\s\n]+only\.[\s\n]+All[\s\n]+activity[\s\n]+may[\s\n]+be[\s\n]+monitored[\s\n]+and[\s\n]+reported\.$ + ^(You[\s\n]+are[\s\n]+accessing[\s\n]+a[\s\n]+U\.S\.[\s\n]+Government[\s\n]+\(USG\)[\s\n]+Information[\s\n]+System[\s\n]+\(IS\)[\s\n]+that[\s\n]+is[\s\n]+provided[\s\n]+for[\s\n]+USG\-authorized[\s\n]+use[\s\n]+only\.[\s\n]+By[\s\n]+using[\s\n]+this[\s\n]+IS[\s\n]+\(which[\s\n]+includes[\s\n]+any[\s\n]+device[\s\n]+attached[\s\n]+to[\s\n]+this[\s\n]+IS\)\,[\s\n]+you[\s\n]+consent[\s\n]+to[\s\n]+the[\s\n]+following[\s\n]+conditions\:(?:[\n]+|(?:\\n)+)\-The[\s\n]+USG[\s\n]+routinely[\s\n]+intercepts[\s\n]+and[\s\n]+monitors[\s\n]+communications[\s\n]+on[\s\n]+this[\s\n]+IS[\s\n]+for[\s\n]+purposes[\s\n]+including\,[\s\n]+but[\s\n]+not[\s\n]+limited[\s\n]+to\,[\s\n]+penetration[\s\n]+testing\,[\s\n]+COMSEC[\s\n]+monitoring\,[\s\n]+network[\s\n]+operations[\s\n]+and[\s\n]+defense\,[\s\n]+personnel[\s\n]+misconduct[\s\n]+\(PM\)\,[\s\n]+law[\s\n]+enforcement[\s\n]+\(LE\)\,[\s\n]+and[\s\n]+counterintelligence[\s\n]+\(CI\)[\s\n]+investigations\.(?:[\n]+|(?:\\n)+)\-At[\s\n]+any[\s\n]+time\,[\s\n]+the[\s\n]+USG[\s\n]+may[\s\n]+inspect[\s\n]+and[\s\n]+seize[\s\n]+data[\s\n]+stored[\s\n]+on[\s\n]+this[\s\n]+IS\.(?:[\n]+|(?:\\n)+)\-Communications[\s\n]+using\,[\s\n]+or[\s\n]+data[\s\n]+stored[\s\n]+on\,[\s\n]+this[\s\n]+IS[\s\n]+are[\s\n]+not[\s\n]+private\,[\s\n]+are[\s\n]+subject[\s\n]+to[\s\n]+routine[\s\n]+monitoring\,[\s\n]+interception\,[\s\n]+and[\s\n]+search\,[\s\n]+and[\s\n]+may[\s\n]+be[\s\n]+disclosed[\s\n]+or[\s\n]+used[\s\n]+for[\s\n]+any[\s\n]+USG\-authorized[\s\n]+purpose\.(?:[\n]+|(?:\\n)+)\-This[\s\n]+IS[\s\n]+includes[\s\n]+security[\s\n]+measures[\s\n]+\(e\.g\.\,[\s\n]+authentication[\s\n]+and[\s\n]+access[\s\n]+controls\)[\s\n]+to[\s\n]+protect[\s\n]+USG[\s\n]+interests\-\-not[\s\n]+for[\s\n]+your[\s\n]+personal[\s\n]+benefit[\s\n]+or[\s\n]+privacy\.(?:[\n]+|(?:\\n)+)\-Notwithstanding[\s\n]+the[\s\n]+above\,[\s\n]+using[\s\n]+this[\s\n]+IS[\s\n]+does[\s\n]+not[\s\n]+constitute[\s\n]+consent[\s\n]+to[\s\n]+PM\,[\s\n]+LE[\s\n]+or[\s\n]+CI[\s\n]+investigative[\s\n]+searching[\s\n]+or[\s\n]+monitoring[\s\n]+of[\s\n]+the[\s\n]+content[\s\n]+of[\s\n]+privileged[\s\n]+communications\,[\s\n]+or[\s\n]+work[\s\n]+product\,[\s\n]+related[\s\n]+to[\s\n]+personal[\s\n]+representation[\s\n]+or[\s\n]+services[\s\n]+by[\s\n]+attorneys\,[\s\n]+psychotherapists\,[\s\n]+or[\s\n]+clergy\,[\s\n]+and[\s\n]+their[\s\n]+assistants\.[\s\n]+Such[\s\n]+communications[\s\n]+and[\s\n]+work[\s\n]+product[\s\n]+are[\s\n]+private[\s\n]+and[\s\n]+confidential\.[\s\n]+See[\s\n]+User[\s\n]+Agreement[\s\n]+for[\s\n]+details\.|I've[\s\n]+read[\s\n]+\&[\s\n]+consent[\s\n]+to[\s\n]+terms[\s\n]+in[\s\n]+IS[\s\n]+user[\s\n]+agreem't\.)$ + ^You[\s\n]+are[\s\n]+accessing[\s\n]+a[\s\n]+U\.S\.[\s\n]+Government[\s\n]+\(USG\)[\s\n]+Information[\s\n]+System[\s\n]+\(IS\)[\s\n]+that[\s\n]+is[\s\n]+provided[\s\n]+for[\s\n]+USG\-authorized[\s\n]+use[\s\n]+only\.[\s\n]+By[\s\n]+using[\s\n]+this[\s\n]+IS[\s\n]+\(which[\s\n]+includes[\s\n]+any[\s\n]+device[\s\n]+attached[\s\n]+to[\s\n]+this[\s\n]+IS\)\,[\s\n]+you[\s\n]+consent[\s\n]+to[\s\n]+the[\s\n]+following[\s\n]+conditions\:(?:[\n]+|(?:\\n)+)\-The[\s\n]+USG[\s\n]+routinely[\s\n]+intercepts[\s\n]+and[\s\n]+monitors[\s\n]+communications[\s\n]+on[\s\n]+this[\s\n]+IS[\s\n]+for[\s\n]+purposes[\s\n]+including\,[\s\n]+but[\s\n]+not[\s\n]+limited[\s\n]+to\,[\s\n]+penetration[\s\n]+testing\,[\s\n]+COMSEC[\s\n]+monitoring\,[\s\n]+network[\s\n]+operations[\s\n]+and[\s\n]+defense\,[\s\n]+personnel[\s\n]+misconduct[\s\n]+\(PM\)\,[\s\n]+law[\s\n]+enforcement[\s\n]+\(LE\)\,[\s\n]+and[\s\n]+counterintelligence[\s\n]+\(CI\)[\s\n]+investigations\.(?:[\n]+|(?:\\n)+)\-At[\s\n]+any[\s\n]+time\,[\s\n]+the[\s\n]+USG[\s\n]+may[\s\n]+inspect[\s\n]+and[\s\n]+seize[\s\n]+data[\s\n]+stored[\s\n]+on[\s\n]+this[\s\n]+IS\.(?:[\n]+|(?:\\n)+)\-Communications[\s\n]+using\,[\s\n]+or[\s\n]+data[\s\n]+stored[\s\n]+on\,[\s\n]+this[\s\n]+IS[\s\n]+are[\s\n]+not[\s\n]+private\,[\s\n]+are[\s\n]+subject[\s\n]+to[\s\n]+routine[\s\n]+monitoring\,[\s\n]+interception\,[\s\n]+and[\s\n]+search\,[\s\n]+and[\s\n]+may[\s\n]+be[\s\n]+disclosed[\s\n]+or[\s\n]+used[\s\n]+for[\s\n]+any[\s\n]+USG\-authorized[\s\n]+purpose\.(?:[\n]+|(?:\\n)+)\-This[\s\n]+IS[\s\n]+includes[\s\n]+security[\s\n]+measures[\s\n]+\(e\.g\.\,[\s\n]+authentication[\s\n]+and[\s\n]+access[\s\n]+controls\)[\s\n]+to[\s\n]+protect[\s\n]+USG[\s\n]+interests\-\-not[\s\n]+for[\s\n]+your[\s\n]+personal[\s\n]+benefit[\s\n]+or[\s\n]+privacy\.(?:[\n]+|(?:\\n)+)\-Notwithstanding[\s\n]+the[\s\n]+above\,[\s\n]+using[\s\n]+this[\s\n]+IS[\s\n]+does[\s\n]+not[\s\n]+constitute[\s\n]+consent[\s\n]+to[\s\n]+PM\,[\s\n]+LE[\s\n]+or[\s\n]+CI[\s\n]+investigative[\s\n]+searching[\s\n]+or[\s\n]+monitoring[\s\n]+of[\s\n]+the[\s\n]+content[\s\n]+of[\s\n]+privileged[\s\n]+communications\,[\s\n]+or[\s\n]+work[\s\n]+product\,[\s\n]+related[\s\n]+to[\s\n]+personal[\s\n]+representation[\s\n]+or[\s\n]+services[\s\n]+by[\s\n]+attorneys\,[\s\n]+psychotherapists\,[\s\n]+or[\s\n]+clergy\,[\s\n]+and[\s\n]+their[\s\n]+assistants\.[\s\n]+Such[\s\n]+communications[\s\n]+and[\s\n]+work[\s\n]+product[\s\n]+are[\s\n]+private[\s\n]+and[\s\n]+confidential\.[\s\n]+See[\s\n]+User[\s\n]+Agreement[\s\n]+for[\s\n]+details\.$ + ^I've[\s\n]+read[\s\n]+\&[\s\n]+consent[\s\n]+to[\s\n]+terms[\s\n]+in[\s\n]+IS[\s\n]+user[\s\n]+agreem't\.$ + ^Use[\s\n]+of[\s\n]+this[\s\n]+or[\s\n]+any[\s\n]+other[\s\n]+DoD[\s\n]+interest[\s\n]+computer[\s\n]+system[\s\n]+constitutes[\s\n]+consent[\s\n]+to[\s\n]+monitoring[\s\n]+at[\s\n]+all[\s\n]+times\.[\s\n]+This[\s\n]+is[\s\n]+a[\s\n]+DoD[\s\n]+interest[\s\n]+computer[\s\n]+system\.[\s\n]+All[\s\n]+DoD[\s\n]+interest[\s\n]+computer[\s\n]+systems[\s\n]+and[\s\n]+related[\s\n]+equipment[\s\n]+are[\s\n]+intended[\s\n]+for[\s\n]+the[\s\n]+communication\,[\s\n]+transmission\,[\s\n]+processing\,[\s\n]+and[\s\n]+storage[\s\n]+of[\s\n]+official[\s\n]+U\.S\.[\s\n]+Government[\s\n]+or[\s\n]+other[\s\n]+authorized[\s\n]+information[\s\n]+only\.[\s\n]+All[\s\n]+DoD[\s\n]+interest[\s\n]+computer[\s\n]+systems[\s\n]+are[\s\n]+subject[\s\n]+to[\s\n]+monitoring[\s\n]+at[\s\n]+all[\s\n]+times[\s\n]+to[\s\n]+ensure[\s\n]+proper[\s\n]+functioning[\s\n]+of[\s\n]+equipment[\s\n]+and[\s\n]+systems[\s\n]+including[\s\n]+security[\s\n]+devices[\s\n]+and[\s\n]+systems\,[\s\n]+to[\s\n]+prevent[\s\n]+unauthorized[\s\n]+use[\s\n]+and[\s\n]+violations[\s\n]+of[\s\n]+statutes[\s\n]+and[\s\n]+security[\s\n]+regulations\,[\s\n]+to[\s\n]+deter[\s\n]+criminal[\s\n]+activity\,[\s\n]+and[\s\n]+for[\s\n]+other[\s\n]+similar[\s\n]+purposes\.[\s\n]+Any[\s\n]+user[\s\n]+of[\s\n]+a[\s\n]+DoD[\s\n]+interest[\s\n]+computer[\s\n]+system[\s\n]+should[\s\n]+be[\s\n]+aware[\s\n]+that[\s\n]+any[\s\n]+information[\s\n]+placed[\s\n]+in[\s\n]+the[\s\n]+system[\s\n]+is[\s\n]+subject[\s\n]+to[\s\n]+monitoring[\s\n]+and[\s\n]+is[\s\n]+not[\s\n]+subject[\s\n]+to[\s\n]+any[\s\n]+expectation[\s\n]+of[\s\n]+privacy\.[\s\n]+If[\s\n]+monitoring[\s\n]+of[\s\n]+this[\s\n]+or[\s\n]+any[\s\n]+other[\s\n]+DoD[\s\n]+interest[\s\n]+computer[\s\n]+system[\s\n]+reveals[\s\n]+possible[\s\n]+evidence[\s\n]+of[\s\n]+violation[\s\n]+of[\s\n]+criminal[\s\n]+statutes\,[\s\n]+this[\s\n]+evidence[\s\n]+and[\s\n]+any[\s\n]+other[\s\n]+related[\s\n]+information\,[\s\n]+including[\s\n]+identification[\s\n]+information[\s\n]+about[\s\n]+the[\s\n]+user\,[\s\n]+may[\s\n]+be[\s\n]+provided[\s\n]+to[\s\n]+law[\s\n]+enforcement[\s\n]+officials\.[\s\n]+If[\s\n]+monitoring[\s\n]+of[\s\n]+this[\s\n]+or[\s\n]+any[\s\n]+other[\s\n]+DoD[\s\n]+interest[\s\n]+computer[\s\n]+systems[\s\n]+reveals[\s\n]+violations[\s\n]+of[\s\n]+security[\s\n]+regulations[\s\n]+or[\s\n]+unauthorized[\s\n]+use\,[\s\n]+employees[\s\n]+who[\s\n]+violate[\s\n]+security[\s\n]+regulations[\s\n]+or[\s\n]+make[\s\n]+unauthorized[\s\n]+use[\s\n]+of[\s\n]+DoD[\s\n]+interest[\s\n]+computer[\s\n]+systems[\s\n]+are[\s\n]+subject[\s\n]+to[\s\n]+appropriate[\s\n]+disciplinary[\s\n]+action\.[\s\n]+Use[\s\n]+of[\s\n]+this[\s\n]+or[\s\n]+any[\s\n]+other[\s\n]+DoD[\s\n]+interest[\s\n]+computer[\s\n]+system[\s\n]+constitutes[\s\n]+consent[\s\n]+to[\s\n]+monitoring[\s\n]+at[\s\n]+all[\s\n]+times\.$ + ^\-\-[\s\n]+WARNING[\s\n]+\-\-[\s\n]+This[\s\n]+system[\s\n]+is[\s\n]+for[\s\n]+the[\s\n]+use[\s\n]+of[\s\n]+authorized[\s\n]+users[\s\n]+only\.[\s\n]+Individuals[\s\n]+using[\s\n]+this[\s\n]+computer[\s\n]+system[\s\n]+without[\s\n]+authority[\s\n]+or[\s\n]+in[\s\n]+excess[\s\n]+of[\s\n]+their[\s\n]+authority[\s\n]+are[\s\n]+subject[\s\n]+to[\s\n]+having[\s\n]+all[\s\n]+their[\s\n]+activities[\s\n]+on[\s\n]+this[\s\n]+system[\s\n]+monitored[\s\n]+and[\s\n]+recorded[\s\n]+by[\s\n]+system[\s\n]+personnel\.[\s\n]+Anyone[\s\n]+using[\s\n]+this[\s\n]+system[\s\n]+expressly[\s\n]+consents[\s\n]+to[\s\n]+such[\s\n]+monitoring[\s\n]+and[\s\n]+is[\s\n]+advised[\s\n]+that[\s\n]+if[\s\n]+such[\s\n]+monitoring[\s\n]+reveals[\s\n]+possible[\s\n]+evidence[\s\n]+of[\s\n]+criminal[\s\n]+activity[\s\n]+system[\s\n]+personal[\s\n]+may[\s\n]+provide[\s\n]+the[\s\n]+evidence[\s\n]+of[\s\n]+such[\s\n]+monitoring[\s\n]+to[\s\n]+law[\s\n]+enforcement[\s\n]+officials\.$ + ^Authorized[\s\n]+uses[\s\n]+only\.[\s\n]+All[\s\n]+activity[\s\n]+may[\s\n]+be[\s\n]+monitored[\s\n]+and[\s\n]+reported\.$ + + + Modify the System Login Banner + +To configure the system login banner edit /etc/issue. Replace the +default text with a message compliant with the local site policy or a legal +disclaimer. + + +The DoD required text is either: + +You are accessing a U.S. Government (USG) Information System (IS) that +is provided for USG-authorized use only. By using this IS (which includes +any device attached to this IS), you consent to the following conditions: +-The USG routinely intercepts and monitors communications on this IS +for purposes including, but not limited to, penetration testing, COMSEC +monitoring, network operations and defense, personnel misconduct (PM), law +enforcement (LE), and counterintelligence (CI) investigations. +-At any time, the USG may inspect and seize data stored on this IS. +-Communications using, or data stored on, this IS are not private, +are subject to routine monitoring, interception, and search, and may be +disclosed or used for any USG-authorized purpose. +-This IS includes security measures (e.g., authentication and access +controls) to protect USG interests -- not for your personal benefit or +privacy. +-Notwithstanding the above, using this IS does not constitute consent +to PM, LE or CI investigative searching or monitoring of the content of +privileged communications, or work product, related to personal +representation or services by attorneys, psychotherapists, or clergy, and +their assistants. Such communications and work product are private and +confidential. See User Agreement for details. + +OR: + +I've read & consent to terms in IS user agreem't. + 1 + 12 + 15 + 16 + DSS05.04 + DSS05.10 + DSS06.10 + 3.1.9 + CCI-000048 + CCI-000050 + CCI-001384 + CCI-001385 + CCI-001386 + CCI-001387 + CCI-001388 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + A.18.1.4 + A.9.2.1 + A.9.2.4 + A.9.3.1 + A.9.4.2 + A.9.4.3 + AC-8(a) + AC-8(c) + PR.AC-7 + FMT_MOF_EXT.1 + SRG-OS-000023-GPOS-00006 + SRG-OS-000228-GPOS-00088 + SRG-OS-000023-VMM-000060 + SRG-OS-000024-VMM-000070 + Display of a standardized and approved use notification before granting +access to the operating system ensures privacy and security notification +verbiage used is consistent with applicable federal laws, Executive Orders, +directives, policies, regulations, standards, and guidance. + +System use notifications are required only for access via login interfaces +with human users and are not required when such human interfaces do not +exist. + + CCE-83557-9 + + + + + + + + + + + + Modify the System Message of the Day Banner + To configure the system message banner edit /etc/motd. Replace the +default text with a message compliant with the local site policy or a legal +disclaimer. + +The DoD required text is either: + +You are accessing a U.S. Government (USG) Information System (IS) that +is provided for USG-authorized use only. By using this IS (which includes +any device attached to this IS), you consent to the following conditions: +-The USG routinely intercepts and monitors communications on this IS +for purposes including, but not limited to, penetration testing, COMSEC +monitoring, network operations and defense, personnel misconduct (PM), law +enforcement (LE), and counterintelligence (CI) investigations. +-At any time, the USG may inspect and seize data stored on this IS. +-Communications using, or data stored on, this IS are not private, +are subject to routine monitoring, interception, and search, and may be +disclosed or used for any USG-authorized purpose. +-This IS includes security measures (e.g., authentication and access +controls) to protect USG interests -- not for your personal benefit or +privacy. +-Notwithstanding the above, using this IS does not constitute consent +to PM, LE or CI investigative searching or monitoring of the content of +privileged communications, or work product, related to personal +representation or services by attorneys, psychotherapists, or clergy, and +their assistants. Such communications and work product are private and +confidential. See User Agreement for details. + +OR: + +I've read & consent to terms in IS user agreem't. + Display of a standardized and approved use notification before granting +access to the operating system ensures privacy and security notification +verbiage used is consistent with applicable federal laws, Executive Orders, +directives, policies, regulations, standards, and guidance. + +System use notifications are required only for access via login interfaces +with human users and are not required when such human interfaces do not +exist. + + CCE-83559-5 + + + + + + + + + + + + Verify permissions on System Login Banner + +To properly set the permissions of /etc/issue, run the command: +$ sudo chmod 0644 /etc/issue + Display of a standardized and approved use notification before granting +access to the operating system ensures privacy and security notification +verbiage used is consistent with applicable federal laws, Executive Orders, +directives, policies, regulations, standards, and guidance. +Proper permissions will ensure that only root user can modify the banner. + CCE-83551-2 + - name: Test for existence /etc/issue + stat: + path: /etc/issue + register: file_exists + tags: + - CCE-83551-2 + - configure_strategy + - file_permissions_etc_issue + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure permission u-xs,g-xws,o-xwt on /etc/issue + file: + path: /etc/issue + mode: u-xs,g-xws,o-xwt + when: file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-83551-2 + - configure_strategy + - file_permissions_etc_issue + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + + + + +chmod u-xs,g-xws,o-xwt /etc/issue + + + + + + + + + + Verify permissions on Message of the Day Banner + +To properly set the permissions of /etc/motd, run the command: +$ sudo chmod 0644 /etc/motd + Display of a standardized and approved use notification before granting +access to the operating system ensures privacy and security notification +verbiage used is consistent with applicable federal laws, Executive Orders, +directives, policies, regulations, standards, and guidance. +Proper permissions will ensure that only root user can modify the banner. + CCE-83554-6 + - name: Test for existence /etc/motd + stat: + path: /etc/motd + register: file_exists + tags: + - CCE-83554-6 + - configure_strategy + - file_permissions_etc_motd + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure permission u-xs,g-xws,o-xwt on /etc/motd + file: + path: /etc/motd + mode: u-xs,g-xws,o-xwt + when: file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-83554-6 + - configure_strategy + - file_permissions_etc_motd + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + + + + +chmod u-xs,g-xws,o-xwt /etc/motd + + + + + + + + + + Implement a GUI Warning Banner + In the default graphical environment, users logging +directly into the system are greeted with a login screen provided +by the GNOME Display Manager (GDM). The warning banner should be +displayed in this graphical environment for these users. +The following sections describe how to configure the GDM login +banner. + + + Enable GNOME3 Login Warning Banner + In the default graphical environment, displaying a login warning banner +in the GNOME Display Manager's login screen can be enabled on the login +screen by setting banner-message-enable to true. + +To enable, add or edit banner-message-enable to +/etc/dconf/db/distro.d/00-security-settings. For example: +[org/gnome/login-screen] +banner-message-enable=true +Once the setting has been added, add a lock to +/etc/dconf/db/distro.d/locks/00-security-settings-lock to prevent user modification. +For example: +/org/gnome/login-screen/banner-message-enable +After the settings have been set, run dconf update. +The banner text must also be set. + 1 + 12 + 15 + 16 + DSS05.04 + DSS05.10 + DSS06.10 + 3.1.9 + CCI-000048 + CCI-000050 + CCI-001384 + CCI-001385 + CCI-001386 + CCI-001387 + CCI-001388 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + A.18.1.4 + A.9.2.1 + A.9.2.4 + A.9.3.1 + A.9.4.2 + A.9.4.3 + AC-8(a) + AC-8(b) + AC-8(c) + PR.AC-7 + FMT_MOF_EXT.1 + SRG-OS-000023-GPOS-00006 + SRG-OS-000228-GPOS-00088 + Display of a standardized and approved use notification before granting access to the operating system +ensures privacy and security notification verbiage used is consistent with applicable federal laws, +Executive Orders, directives, policies, regulations, standards, and guidance. + +For U.S. Government systems, system use notifications are required only for access via login interfaces +with human users and are not required when such human interfaces do not exist. + CCE-87599-7 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-87599-7 + - NIST-800-171-3.1.9 + - NIST-800-53-AC-8(a) + - NIST-800-53-AC-8(b) + - NIST-800-53-AC-8(c) + - dconf_gnome_banner_enabled + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Enable GNOME3 Login Warning Banner + ini_file: + dest: /etc/dconf/db/distro.d/00-security-settings + section: org/gnome/login-screen + option: banner-message-enable + value: 'true' + create: true + no_extra_spaces: true + when: '"gdm" in ansible_facts.packages' + tags: + - CCE-87599-7 + - NIST-800-171-3.1.9 + - NIST-800-53-AC-8(a) + - NIST-800-53-AC-8(b) + - NIST-800-53-AC-8(c) + - dconf_gnome_banner_enabled + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Prevent user modification of GNOME banner-message-enabled + lineinfile: + path: /etc/dconf/db/distro.d/locks/00-security-settings-lock + regexp: ^/org/gnome/login-screen/banner-message-enable$ + line: /org/gnome/login-screen/banner-message-enable + create: true + when: '"gdm" in ansible_facts.packages' + tags: + - CCE-87599-7 + - NIST-800-171-3.1.9 + - NIST-800-53-AC-8(a) + - NIST-800-53-AC-8(b) + - NIST-800-53-AC-8(c) + - dconf_gnome_banner_enabled + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Dconf Update + command: dconf update + when: '"gdm" in ansible_facts.packages' + tags: + - CCE-87599-7 + - NIST-800-171-3.1.9 + - NIST-800-53-AC-8(a) + - NIST-800-53-AC-8(b) + - NIST-800-53-AC-8(c) + - dconf_gnome_banner_enabled + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q gdm; then + +# Check for setting in any of the DConf db directories +# If files contain ibus or distro, ignore them. +# The assignment assumes that individual filenames don't contain : +readarray -t SETTINGSFILES < <(grep -r "\\[org/gnome/login-screen\\]" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +DCONFFILE="/etc/dconf/db/distro.d/00-security-settings" +DBDIR="/etc/dconf/db/distro.d" + +mkdir -p "${DBDIR}" + +if [ "${#SETTINGSFILES[@]}" -eq 0 ] +then + [ ! -z ${DCONFFILE} ] || echo "" >> ${DCONFFILE} + printf '%s\n' "[org/gnome/login-screen]" >> ${DCONFFILE} + printf '%s=%s\n' "banner-message-enable" "true" >> ${DCONFFILE} +else + escaped_value="$(sed -e 's/\\/\\\\/g' <<< "true")" + if grep -q "^\\s*banner-message-enable\\s*=" "${SETTINGSFILES[@]}" + then + + sed -i "s/\\s*banner-message-enable\\s*=\\s*.*/banner-message-enable=${escaped_value}/g" "${SETTINGSFILES[@]}" + else + sed -i "\\|\\[org/gnome/login-screen\\]|a\\banner-message-enable=${escaped_value}" "${SETTINGSFILES[@]}" + fi +fi + +dconf update +# Check for setting in any of the DConf db directories +LOCKFILES=$(grep -r "^/org/gnome/login-screen/banner-message-enable$" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +LOCKSFOLDER="/etc/dconf/db/distro.d/locks" + +mkdir -p "${LOCKSFOLDER}" + +if [[ -z "${LOCKFILES}" ]] +then + echo "/org/gnome/login-screen/banner-message-enable" >> "/etc/dconf/db/distro.d/locks/00-security-settings-lock" +fi + +dconf update + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Set the GNOME3 Login Warning Banner Text + In the default graphical environment, configuring the login warning banner text +in the GNOME Display Manager's login screen can be configured on the login +screen by setting banner-message-text to 'APPROVED_BANNER' +where APPROVED_BANNER is the approved banner for your environment. + +To enable, add or edit banner-message-text to + +/etc/dconf/db/distro.d/00-security-settings. For example: +[org/gnome/login-screen] +banner-message-text='APPROVED_BANNER' +Once the setting has been added, add a lock to +/etc/dconf/db/distro.d/locks/00-security-settings-lock to prevent user modification. +For example: +/org/gnome/login-screen/banner-message-text + +After the settings have been set, run dconf update. +When entering a warning banner that spans several lines, remember +to begin and end the string with ' and use \n for new lines. + 1 + 12 + 15 + 16 + DSS05.04 + DSS05.10 + DSS06.10 + 3.1.9 + CCI-000048 + CCI-001384 + CCI-001385 + CCI-001386 + CCI-001387 + CCI-001388 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + A.18.1.4 + A.9.2.1 + A.9.2.4 + A.9.3.1 + A.9.4.2 + A.9.4.3 + AC-8(a) + AC-8(c) + PR.AC-7 + FMT_MOF_EXT.1 + SRG-OS-000023-GPOS-00006 + SRG-OS-000228-GPOS-00088 + An appropriate warning message reinforces policy awareness during the logon +process and facilitates possible legal action against attackers. + CCE-86529-5 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-86529-5 + - NIST-800-171-3.1.9 + - NIST-800-53-AC-8(a) + - NIST-800-53-AC-8(c) + - dconf_gnome_login_banner_text + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy +- name: XCCDF Value login_banner_text # promote to variable + set_fact: + login_banner_text: !!str + tags: + - always + +- name: Set the GNOME3 Login Warning Banner Text + file: + path: /etc/dconf/db/{{ item }} + owner: root + group: root + mode: 493 + state: directory + with_items: + - distro.d + - distro.d/locks + when: '"gdm" in ansible_facts.packages' + tags: + - CCE-86529-5 + - NIST-800-171-3.1.9 + - NIST-800-53-AC-8(a) + - NIST-800-53-AC-8(c) + - dconf_gnome_login_banner_text + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Set the GNOME3 Login Warning Banner Text + file: + path: /etc/dconf/db/distro.d/{{ item }} + owner: root + group: root + mode: 420 + state: touch + with_items: + - 00-security-settings + - locks/00-security-settings-lock + when: '"gdm" in ansible_facts.packages' + tags: + - CCE-86529-5 + - NIST-800-171-3.1.9 + - NIST-800-53-AC-8(a) + - NIST-800-53-AC-8(c) + - dconf_gnome_login_banner_text + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Set the GNOME3 Login Warning Banner Text + ini_file: + dest: /etc/dconf/db/distro.d/00-security-settings + section: org/gnome/login-screen + option: banner-message-text + value: '''{{ login_banner_text | regex_replace("^\^(.*)\$$", "\1") | regex_replace("^\((.*\.)\|.*\)$", + "\1") | regex_replace("\[\\s\\n\]\+"," ") | regex_replace("\(\?:\[\\n\]\+\|\(\?:\\\\n\)\+\)", + "(n)*") | regex_replace("\\", "") | regex_replace("\(n\)\*", "\\n") }}''' + create: true + no_extra_spaces: true + when: '"gdm" in ansible_facts.packages' + tags: + - CCE-86529-5 + - NIST-800-171-3.1.9 + - NIST-800-53-AC-8(a) + - NIST-800-53-AC-8(c) + - dconf_gnome_login_banner_text + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Prevent user modification of the GNOME3 Login Warning Banner Text + lineinfile: + path: /etc/dconf/db/distro.d/locks/00-security-settings-lock + regexp: ^/org/gnome/login-screen/banner-message-text$ + line: /org/gnome/login-screen/banner-message-text + create: true + state: present + when: '"gdm" in ansible_facts.packages' + tags: + - CCE-86529-5 + - NIST-800-171-3.1.9 + - NIST-800-53-AC-8(a) + - NIST-800-53-AC-8(c) + - dconf_gnome_login_banner_text + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + +- name: Dconf Update + command: dconf update + when: '"gdm" in ansible_facts.packages' + tags: + - CCE-86529-5 + - NIST-800-171-3.1.9 + - NIST-800-53-AC-8(a) + - NIST-800-53-AC-8(c) + - dconf_gnome_login_banner_text + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q gdm; then + +login_banner_text='' + + +# Multiple regexes transform the banner regex into a usable banner +# 0 - Remove anchors around the banner text +login_banner_text=$(echo "$login_banner_text" | sed 's/^\^\(.*\)\$$/\1/g') +# 1 - Keep only the first banners if there are multiple +# (dod_banners contains the long and short banner) +login_banner_text=$(echo "$login_banner_text" | sed 's/^(\(.*\.\)|.*)$/\1/g') +# 2 - Add spaces ' '. (Transforms regex for "space or newline" into a " ") +login_banner_text=$(echo "$login_banner_text" | sed 's/\[\\s\\n\]+/ /g') +# 3 - Adds newline "tokens". (Transforms "(?:\[\\n\]+|(?:\\n)+)" into "(n)*") +login_banner_text=$(echo "$login_banner_text" | sed 's/(?:\[\\n\]+|(?:\\\\n)+)/(n)*/g') +# 4 - Remove any leftover backslash. (From any parethesis in the banner, for example). +login_banner_text=$(echo "$login_banner_text" | sed 's/\\//g') +# 5 - Removes the newline "token." (Transforms them into newline escape sequences "\n"). +# ( Needs to be done after 4, otherwise the escapce sequence will become just "n". +login_banner_text=$(echo "$login_banner_text" | sed 's/(n)\*/\\n/g') + +# Check for setting in any of the DConf db directories +# If files contain ibus or distro, ignore them. +# The assignment assumes that individual filenames don't contain : +readarray -t SETTINGSFILES < <(grep -r "\\[org/gnome/login-screen\\]" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +DCONFFILE="/etc/dconf/db/distro.d/00-security-settings" +DBDIR="/etc/dconf/db/distro.d" + +mkdir -p "${DBDIR}" + +if [ "${#SETTINGSFILES[@]}" -eq 0 ] +then + [ ! -z ${DCONFFILE} ] || echo "" >> ${DCONFFILE} + printf '%s\n' "[org/gnome/login-screen]" >> ${DCONFFILE} + printf '%s=%s\n' "banner-message-text" "'${login_banner_text}'" >> ${DCONFFILE} +else + escaped_value="$(sed -e 's/\\/\\\\/g' <<< "'${login_banner_text}'")" + if grep -q "^\\s*banner-message-text\\s*=" "${SETTINGSFILES[@]}" + then + + sed -i "s/\\s*banner-message-text\\s*=\\s*.*/banner-message-text=${escaped_value}/g" "${SETTINGSFILES[@]}" + else + sed -i "\\|\\[org/gnome/login-screen\\]|a\\banner-message-text=${escaped_value}" "${SETTINGSFILES[@]}" + fi +fi + +dconf update +# Check for setting in any of the DConf db directories +LOCKFILES=$(grep -r "^/org/gnome/login-screen/banner-message-text$" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1) +LOCKSFOLDER="/etc/dconf/db/distro.d/locks" + +mkdir -p "${LOCKSFOLDER}" + +if [[ -z "${LOCKFILES}" ]] +then + echo "/org/gnome/login-screen/banner-message-text" >> "/etc/dconf/db/distro.d/locks/00-security-settings-lock" +fi + +dconf update + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + + + Protect Accounts by Configuring PAM + PAM, or Pluggable Authentication Modules, is a system +which implements modular authentication for Linux programs. PAM provides +a flexible and configurable architecture for authentication, and it should be configured +to minimize exposure to unnecessary risk. This section contains +guidance on how to accomplish that. + +PAM is implemented as a set of shared objects which are +loaded and invoked whenever an application wishes to authenticate a +user. Typically, the application must be running as root in order +to take advantage of PAM, because PAM's modules often need to be able +to access sensitive stores of account information, such as /etc/shadow. +Traditional privileged network listeners +(e.g. sshd) or SUID programs (e.g. sudo) already meet this +requirement. An SUID root application, userhelper, is provided so +that programs which are not SUID or privileged themselves can still +take advantage of PAM. + +PAM looks in the directory /etc/pam.d for +application-specific configuration information. For instance, if +the program login attempts to authenticate a user, then PAM's +libraries follow the instructions in the file /etc/pam.d/login +to determine what actions should be taken. + +One very important file in /etc/pam.d is +/etc/pam.d/system-auth. This file, which is included by +many other PAM configuration files, defines 'default' system authentication +measures. Modifying this file is a good way to make far-reaching +authentication changes, for instance when implementing a +centralized authentication service. + Be careful when making changes to PAM's configuration files. +The syntax for these files is complex, and modifications can +have unexpected consequences. The default configurations shipped +with applications should be sufficient for most users. + Running authconfig or system-config-authentication +will re-write the PAM configuration files, destroying any manually +made changes and replacing them with a series of system defaults. +One reference to the configuration file syntax can be found at + +https://fossies.org/linux/Linux-PAM-docs/doc/sag/Linux-PAM_SAG.pdf. + + Password Hashing algorithm + Specify the system default encryption algorithm for encrypting passwords. +Defines the value set as ENCRYPT_METHOD in /etc/login.defs. + SHA512 + SHA512 + SHA256 + + + remember + The last n passwords for each user are saved in +/etc/security/opasswd in order to force password change history and +keep the user from alternating between the same password too +frequently. + 0 + 10 + 24 + 2 + 4 + 5 + 5 + + + Install pam_pwquality Package + +The libpwquality package can be installed with the following command: + +$ sudo dnf install libpwquality + CCI-000366 + SRG-OS-000480-GPOS-00225 + Use of a complex password helps to increase the time and resources required +to compromise the password. Password complexity, or strength, is a measure +of the effectiveness of a password in resisting attempts at guessing and +brute-force attacks. "pwquality" enforces complex password construction +configuration and has the ability to limit brute-force attacks on the system. + +package --add=libpwquality + + include install_libpwquality + +class install_libpwquality { + package { 'libpwquality': + ensure => 'installed', + } +} + + - name: Ensure libpwquality is installed + package: + name: libpwquality + state: present + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_pam_pwquality_installed + + +[[packages]] +name = "libpwquality" +version = "*" + + +if ! rpm -q --quiet "libpwquality" ; then + dnf install -y "libpwquality" +fi + + + + + + + + + + Disallow Configuration to Bypass Password Requirements for Privilege Escalation + Verify the operating system is not configured to bypass password requirements for privilege +escalation. Check the configuration of the "/etc/pam.d/sudo" file with the following command: +$ sudo grep pam_succeed_if /etc/pam.d/sudo +If any occurrences of "pam_succeed_if" is returned from the command, this is a finding. + CCI-002038 + IA-11 + SRG-OS-000373-GPOS-00156 + SRG-OS-000373-GPOS-00157 + SRG-OS-000373-GPOS-00158 + Without re-authentication, users may access resources or perform tasks for which they do not +have authorization. When operating systems provide the capability to escalate a functional +capability, it is critical the user re-authenticate. + + CCE-85967-8 + + + + + + + + + Ensure PAM Displays Last Logon/Access Notification + To configure the system to notify users of last logon/access +using pam_lastlog, add or correct the pam_lastlog +settings in +/etc/pam.d/postlogin to read as follows: +session required pam_lastlog.so showfailed +And make sure that the silent option is not set for +pam_lastlog module. + 1 + 12 + 15 + 16 + 5.5.2 + DSS05.04 + DSS05.10 + DSS06.10 + CCI-000366 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + 0582 + 0584 + 05885 + 0586 + 0846 + 0957 + A.18.1.4 + A.9.2.1 + A.9.2.4 + A.9.3.1 + A.9.4.2 + A.9.4.3 + AC-9(1) + CM-6(a) + PR.AC-7 + Req-10.2.4 + SRG-OS-000480-GPOS-00227 + Users need to be aware of activity that occurs regarding +their account. Providing users with information regarding the number +of unsuccessful attempts that were made to login to their account +allows the user to determine if any unauthorized activity has occurred +and gives them an opportunity to notify administrators. + + CCE-83560-3 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83560-3 + - CJIS-5.5.2 + - NIST-800-53-AC-9(1) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.4 + - configure_strategy + - display_login_attempts + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + +- name: Ensure PAM Displays Last Logon/Access Notification - Check if /etc/pam.d/postlogin + file is present + ansible.builtin.stat: + path: /etc/pam.d/postlogin + register: result_pam_file_present + when: '"pam" in ansible_facts.packages' + tags: + - CCE-83560-3 + - CJIS-5.5.2 + - NIST-800-53-AC-9(1) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.4 + - configure_strategy + - display_login_attempts + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + +- name: Ensure PAM Displays Last Logon/Access Notification - Check the proper remediation + for the system + block: + + - name: Ensure PAM Displays Last Logon/Access Notification - Define the PAM file + to be edited as a local fact + ansible.builtin.set_fact: + pam_file_path: /etc/pam.d/postlogin + + - name: Ensure PAM Displays Last Logon/Access Notification - Check if system relies + on authselect + ansible.builtin.stat: + path: /usr/bin/authselect + register: result_authselect_present + + - name: Ensure PAM Displays Last Logon/Access Notification - Remediate using authselect + block: + + - name: Ensure PAM Displays Last Logon/Access Notification - Check integrity of + authselect current profile + ansible.builtin.command: + cmd: authselect check + register: result_authselect_check_cmd + changed_when: false + ignore_errors: true + + - name: Ensure PAM Displays Last Logon/Access Notification - Informative message + based on the authselect integrity check result + ansible.builtin.assert: + that: + - result_authselect_check_cmd is success + fail_msg: + - authselect integrity check failed. Remediation aborted! + - This remediation could not be applied because an authselect profile was + not selected or the selected profile is not intact. + - It is not recommended to manually edit the PAM files when authselect tool + is available. + - In cases where the default authselect profile does not cover a specific + demand, a custom authselect profile is recommended. + success_msg: + - authselect integrity check passed + + - name: Ensure PAM Displays Last Logon/Access Notification - Get authselect current + profile + ansible.builtin.shell: + cmd: authselect current -r | awk '{ print $1 }' + register: result_authselect_profile + changed_when: false + when: + - result_authselect_check_cmd is success + + - name: Ensure PAM Displays Last Logon/Access Notification - Define the current + authselect profile as a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: '{{ result_authselect_profile.stdout }}' + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is match("custom/") + + - name: Ensure PAM Displays Last Logon/Access Notification - Define the new authselect + custom profile as a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: custom/hardening + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is not match("custom/") + + - name: Ensure PAM Displays Last Logon/Access Notification - Get authselect current + features to also enable them in the custom profile + ansible.builtin.shell: + cmd: authselect current | tail -n+3 | awk '{ print $2 }' + register: result_authselect_features + changed_when: false + when: + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + + - name: Ensure PAM Displays Last Logon/Access Notification - Check if any custom + profile with the same name was already created + ansible.builtin.stat: + path: /etc/authselect/{{ authselect_custom_profile }} + register: result_authselect_custom_profile_present + changed_when: false + when: + - authselect_current_profile is not match("custom/") + + - name: Ensure PAM Displays Last Logon/Access Notification - Create an authselect + custom profile based on the current profile + ansible.builtin.command: + cmd: authselect create-profile hardening -b {{ authselect_current_profile + }} + when: + - result_authselect_check_cmd is success + - authselect_current_profile is not match("custom/") + - not result_authselect_custom_profile_present.stat.exists + + - name: Ensure PAM Displays Last Logon/Access Notification - Ensure authselect + changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=before-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Ensure PAM Displays Last Logon/Access Notification - Ensure the authselect + custom profile is selected + ansible.builtin.command: + cmd: authselect select {{ authselect_custom_profile }} + register: result_pam_authselect_select_profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Ensure PAM Displays Last Logon/Access Notification - Restore the authselect + features in the custom profile + ansible.builtin.command: + cmd: authselect enable-feature {{ item }} + loop: '{{ result_authselect_features.stdout_lines }}' + register: result_pam_authselect_restore_features + when: + - result_authselect_profile is not skipped + - result_authselect_features is not skipped + - result_pam_authselect_select_profile is not skipped + + - name: Ensure PAM Displays Last Logon/Access Notification - Ensure authselect + changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=after-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - result_pam_authselect_restore_features is not skipped + + - name: Ensure PAM Displays Last Logon/Access Notification - Change the PAM file + to be edited according to the custom authselect profile + ansible.builtin.set_fact: + pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path + | basename }} + when: + - result_authselect_present.stat.exists + + - name: Ensure PAM Displays Last Logon/Access Notification - Check if expected PAM + module line is present in {{ pam_file_path }} + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + regexp: ^\s*session\s+required\s+pam_lastlog.so\s*.* + state: absent + check_mode: true + changed_when: false + register: result_pam_line_present + + - name: Ensure PAM Displays Last Logon/Access Notification - Include or update the + PAM module line in {{ pam_file_path }} + block: + + - name: Ensure PAM Displays Last Logon/Access Notification - Check if required + PAM module line is present in {{ pam_file_path }} with different control + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + regexp: ^\s*session\s+.*\s+pam_lastlog.so\s* + state: absent + check_mode: true + changed_when: false + register: result_pam_line_other_control_present + + - name: Ensure PAM Displays Last Logon/Access Notification - Ensure the correct + control for the required PAM module line in {{ pam_file_path }} + ansible.builtin.replace: + dest: '{{ pam_file_path }}' + regexp: ^(\s*session\s+).*(\bpam_lastlog.so.*) + replace: \1required \2 + register: result_pam_module_edit + when: + - result_pam_line_other_control_present.found == 1 + + - name: Ensure PAM Displays Last Logon/Access Notification - Ensure the required + PAM module line is included in {{ pam_file_path }} + ansible.builtin.lineinfile: + dest: '{{ pam_file_path }}' + insertafter: BOF + line: session required pam_lastlog.so + register: result_pam_module_add + when: + - result_pam_line_other_control_present.found == 0 or result_pam_line_other_control_present.found + > 1 + + - name: Ensure PAM Displays Last Logon/Access Notification - Ensure authselect + changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: | + result_authselect_present is defined and result_authselect_present.stat.exists and ((result_pam_module_add is defined and result_pam_module_add.changed) or (result_pam_module_edit is defined and result_pam_module_edit.changed)) + when: + - result_pam_line_present.found is defined + - result_pam_line_present.found == 0 + + - name: Ensure PAM Displays Last Logon/Access Notification - Check if the required + PAM module option is present in {{ pam_file_path }} + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + regexp: ^\s*session\s+required\s+pam_lastlog.so\s*.*\sshowfailed\b + state: absent + check_mode: true + changed_when: false + register: result_pam_module_showfailed_option_present + + - name: Ensure PAM Displays Last Logon/Access Notification - Ensure the "showfailed" + PAM option for "pam_lastlog.so" is included in {{ pam_file_path }} + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + backrefs: true + regexp: ^(\s*session\s+required\s+pam_lastlog.so.*) + line: \1 showfailed + state: present + register: result_pam_showfailed_add + when: + - result_pam_module_showfailed_option_present.found == 0 + + - name: Ensure PAM Displays Last Logon/Access Notification - Ensure authselect changes + are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: + - result_authselect_present.stat.exists + - (result_pam_showfailed_add is defined and result_pam_showfailed_add.changed) + or (result_pam_showfailed_edit is defined and result_pam_showfailed_edit.changed) + when: + - '"pam" in ansible_facts.packages' + - result_pam_file_present.stat.exists + tags: + - CCE-83560-3 + - CJIS-5.5.2 + - NIST-800-53-AC-9(1) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.4 + - configure_strategy + - display_login_attempts + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + +- name: Ensure PAM Displays Last Logon/Access Notification - Check if /etc/pam.d/postlogin + file is present + ansible.builtin.stat: + path: /etc/pam.d/postlogin + register: result_pam_file_present + when: '"pam" in ansible_facts.packages' + tags: + - CCE-83560-3 + - CJIS-5.5.2 + - NIST-800-53-AC-9(1) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.4 + - configure_strategy + - display_login_attempts + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + +- name: Ensure PAM Displays Last Logon/Access Notification - Check the proper remediation + for the system + block: + + - name: Ensure PAM Displays Last Logon/Access Notification - Define the PAM file + to be edited as a local fact + ansible.builtin.set_fact: + pam_file_path: /etc/pam.d/postlogin + + - name: Ensure PAM Displays Last Logon/Access Notification - Check if system relies + on authselect + ansible.builtin.stat: + path: /usr/bin/authselect + register: result_authselect_present + + - name: Ensure PAM Displays Last Logon/Access Notification - Remediate using authselect + block: + + - name: Ensure PAM Displays Last Logon/Access Notification - Check integrity of + authselect current profile + ansible.builtin.command: + cmd: authselect check + register: result_authselect_check_cmd + changed_when: false + ignore_errors: true + + - name: Ensure PAM Displays Last Logon/Access Notification - Informative message + based on the authselect integrity check result + ansible.builtin.assert: + that: + - result_authselect_check_cmd is success + fail_msg: + - authselect integrity check failed. Remediation aborted! + - This remediation could not be applied because an authselect profile was + not selected or the selected profile is not intact. + - It is not recommended to manually edit the PAM files when authselect tool + is available. + - In cases where the default authselect profile does not cover a specific + demand, a custom authselect profile is recommended. + success_msg: + - authselect integrity check passed + + - name: Ensure PAM Displays Last Logon/Access Notification - Get authselect current + profile + ansible.builtin.shell: + cmd: authselect current -r | awk '{ print $1 }' + register: result_authselect_profile + changed_when: false + when: + - result_authselect_check_cmd is success + + - name: Ensure PAM Displays Last Logon/Access Notification - Define the current + authselect profile as a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: '{{ result_authselect_profile.stdout }}' + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is match("custom/") + + - name: Ensure PAM Displays Last Logon/Access Notification - Define the new authselect + custom profile as a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: custom/hardening + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is not match("custom/") + + - name: Ensure PAM Displays Last Logon/Access Notification - Get authselect current + features to also enable them in the custom profile + ansible.builtin.shell: + cmd: authselect current | tail -n+3 | awk '{ print $2 }' + register: result_authselect_features + changed_when: false + when: + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + + - name: Ensure PAM Displays Last Logon/Access Notification - Check if any custom + profile with the same name was already created + ansible.builtin.stat: + path: /etc/authselect/{{ authselect_custom_profile }} + register: result_authselect_custom_profile_present + changed_when: false + when: + - authselect_current_profile is not match("custom/") + + - name: Ensure PAM Displays Last Logon/Access Notification - Create an authselect + custom profile based on the current profile + ansible.builtin.command: + cmd: authselect create-profile hardening -b {{ authselect_current_profile + }} + when: + - result_authselect_check_cmd is success + - authselect_current_profile is not match("custom/") + - not result_authselect_custom_profile_present.stat.exists + + - name: Ensure PAM Displays Last Logon/Access Notification - Ensure authselect + changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=before-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Ensure PAM Displays Last Logon/Access Notification - Ensure the authselect + custom profile is selected + ansible.builtin.command: + cmd: authselect select {{ authselect_custom_profile }} + register: result_pam_authselect_select_profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Ensure PAM Displays Last Logon/Access Notification - Restore the authselect + features in the custom profile + ansible.builtin.command: + cmd: authselect enable-feature {{ item }} + loop: '{{ result_authselect_features.stdout_lines }}' + register: result_pam_authselect_restore_features + when: + - result_authselect_profile is not skipped + - result_authselect_features is not skipped + - result_pam_authselect_select_profile is not skipped + + - name: Ensure PAM Displays Last Logon/Access Notification - Ensure authselect + changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=after-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - result_pam_authselect_restore_features is not skipped + + - name: Ensure PAM Displays Last Logon/Access Notification - Change the PAM file + to be edited according to the custom authselect profile + ansible.builtin.set_fact: + pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path + | basename }} + when: + - result_authselect_present.stat.exists + + - name: Ensure PAM Displays Last Logon/Access Notification - Ensure the "silent" + option from "pam_lastlog.so" is not present in {{ pam_file_path }} + ansible.builtin.replace: + dest: '{{ pam_file_path }}' + regexp: (.*session.*pam_lastlog.so.*)\bsilent\b=?[0-9a-zA-Z]*(.*) + replace: \1\2 + register: result_pam_option_removal + + - name: Ensure PAM Displays Last Logon/Access Notification - Ensure authselect changes + are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: + - result_authselect_present.stat.exists + - result_pam_option_removal is changed + when: + - '"pam" in ansible_facts.packages' + - result_pam_file_present.stat.exists + tags: + - CCE-83560-3 + - CJIS-5.5.2 + - NIST-800-53-AC-9(1) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.4 + - configure_strategy + - display_login_attempts + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if rpm --quiet -q pam; then + +if [ -e "/etc/pam.d/postlogin" ] ; then + PAM_FILE_PATH="/etc/pam.d/postlogin" + if [ -f /usr/bin/authselect ]; then + if ! authselect check; then + echo " + authselect integrity check failed. Remediation aborted! + This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact. + It is not recommended to manually edit the PAM files when authselect tool is available. + In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended." + exit 1 + fi + CURRENT_PROFILE=$(authselect current -r | awk '{ print $1 }') + # If not already in use, a custom profile is created preserving the enabled features. + if [[ ! $CURRENT_PROFILE == custom/* ]]; then + ENABLED_FEATURES=$(authselect current | tail -n+3 | awk '{ print $2 }') + authselect create-profile hardening -b $CURRENT_PROFILE + CURRENT_PROFILE="custom/hardening" + + authselect apply-changes -b --backup=before-hardening-custom-profile + authselect select $CURRENT_PROFILE + for feature in $ENABLED_FEATURES; do + authselect enable-feature $feature; + done + + authselect apply-changes -b --backup=after-hardening-custom-profile + fi + PAM_FILE_NAME=$(basename "/etc/pam.d/postlogin") + PAM_FILE_PATH="/etc/authselect/$CURRENT_PROFILE/$PAM_FILE_NAME" + + authselect apply-changes -b + fi + if ! grep -qP '^\s*session\s+'"required"'\s+pam_lastlog.so\s*.*' "$PAM_FILE_PATH"; then + # Line matching group + control + module was not found. Check group + module. + if [ "$(grep -cP '^\s*session\s+.*\s+pam_lastlog.so\s*' "$PAM_FILE_PATH")" -eq 1 ]; then + # The control is updated only if one single line matches. + sed -i -E --follow-symlinks 's/^(\s*session\s+).*(\bpam_lastlog.so.*)/\1'"required"' \2/' "$PAM_FILE_PATH" + else + sed -i --follow-symlinks '1i session '"required"' pam_lastlog.so' "$PAM_FILE_PATH" + fi + fi + # Check the option + if ! grep -qP '^\s*session\s+'"required"'\s+pam_lastlog.so\s*.*\sshowfailed\b' "$PAM_FILE_PATH"; then + sed -i -E --follow-symlinks '/\s*session\s+'"required"'\s+pam_lastlog.so.*/ s/$/ showfailed/' "$PAM_FILE_PATH" + fi + if [ -f /usr/bin/authselect ]; then + + authselect apply-changes -b + fi +else + echo "/etc/pam.d/postlogin was not found" >&2 +fi +if [ -e "/etc/pam.d/postlogin" ] ; then + PAM_FILE_PATH="/etc/pam.d/postlogin" + if [ -f /usr/bin/authselect ]; then + if ! authselect check; then + echo " + authselect integrity check failed. Remediation aborted! + This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact. + It is not recommended to manually edit the PAM files when authselect tool is available. + In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended." + exit 1 + fi + CURRENT_PROFILE=$(authselect current -r | awk '{ print $1 }') + # If not already in use, a custom profile is created preserving the enabled features. + if [[ ! $CURRENT_PROFILE == custom/* ]]; then + ENABLED_FEATURES=$(authselect current | tail -n+3 | awk '{ print $2 }') + authselect create-profile hardening -b $CURRENT_PROFILE + CURRENT_PROFILE="custom/hardening" + + authselect apply-changes -b --backup=before-hardening-custom-profile + authselect select $CURRENT_PROFILE + for feature in $ENABLED_FEATURES; do + authselect enable-feature $feature; + done + + authselect apply-changes -b --backup=after-hardening-custom-profile + fi + PAM_FILE_NAME=$(basename "/etc/pam.d/postlogin") + PAM_FILE_PATH="/etc/authselect/$CURRENT_PROFILE/$PAM_FILE_NAME" + + authselect apply-changes -b + fi + +if grep -qP '^\s*session\s.*\bpam_lastlog.so\s.*\bsilent\b' "$PAM_FILE_PATH"; then + sed -i -E --follow-symlinks 's/(.*session.*pam_lastlog.so.*)\bsilent\b=?[[:alnum:]]*(.*)/\1\2/g' "$PAM_FILE_PATH" +fi + if [ -f /usr/bin/authselect ]; then + + authselect apply-changes -b + fi +else + echo "/etc/pam.d/postlogin was not found" >&2 +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Set Lockouts for Failed Password Attempts + The pam_faillock PAM module provides the capability to +lock out user accounts after a number of failed login attempts. Its +documentation is available in +/usr/share/doc/pam-VERSION/txts/README.pam_faillock. + + Locking out user accounts presents the +risk of a denial-of-service attack. The lockout policy +must weigh whether the risk of such a +denial-of-service attack outweighs the benefits of thwarting +password guessing attacks. + + fail_deny + Number of failed login attempts before account lockout + 10 + 3 + 5 + 6 + 3 + + + faillock directory + The directory where the user files with the failure records are kept + /var/log/faillock + /var/log/faillock + + + fail_interval + Interval for counting failed login attempts before account lockout + 100000000 + 1800 + 3600 + 86400 + 900 + 900 + + + fail_unlock_time + Seconds before automatic unlocking or permanently locking after excessive failed logins + 1800 + 3600 + 600 + 604800 + 86400 + 900 + 0 + 0 + + + faildelay_delay + Delay next login attempt after a failed login + 0 + 4000000 + 4000000 + + + pwhistory_remember + Prevent password re-use using password history lookup + 0 + 5 + 6 + 7 + 8 + 9 + 5 + + + PAM pwhistory remember - control flag + 'Specify the control flag required for password remember requirement. If multiple +values are allowed write them separated by commas as in "required,requisite", +for remediations the first value will be taken' + required + user + requisite + sufficient + binding + required,requisite + requisite + + + tally2 + Number of failed login attempts + 1 + 2 + 3 + 4 + 5 + 3 + + + Configure the Use of the pam_faillock.so Module in the /etc/pam.d/password-auth File. + The pam_faillock.so module must be loaded in preauth in /etc/pam.d/password-auth. + CCI-000044 + AC-7 (a) + SRG-OS-000021-GPOS-00005 + If the pam_faillock.so module is not loaded the system will not correctly lockout accounts to prevent +password guessing attacks. + CCE-86932-1 + + + + + + Configure the Use of the pam_faillock.so Module in the /etc/pam.d/system-auth File. + The pam_faillock.so module must be loaded in preauth in /etc/pam.d/system-auth. + CCI-000044 + AC-7 (a) + SRG-OS-000021-GPOS-00005 + If the pam_faillock.so module is not loaded the system will not correctly lockout accounts to prevent +password guessing attacks. + CCE-86917-2 + + + + + + An SELinux Context must be configured for the pam_faillock.so records directory + The dir configuration option in PAM pam_faillock.so module defines where the lockout +records is stored. The configured directory must have the correct SELinux context. + CCI-000044 + AC-7 (a) + SRG-OS-000021-GPOS-00005 + Not having the correct SELinux context on the pam_faillock.so records directory may lead to +unauthorized access to the directory. + + CCE-86249-0 + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +#!/bin/bash + +FAILLOCK_CONF_FILES="/etc/security/faillock.conf /etc/pam.d/system-auth /etc/pam.d/password-auth" +faillock_dirs=$(grep -oP "^\s*(?:auth.*pam_faillock.so.*)?dir\s*=\s*(\S+)" $FAILLOCK_CONF_FILES \ + | sed -r 's/.*=\s*(\S+)/\1/') + +if [ -n "$faillock_dirs" ]; then + for dir in $faillock_dirs; do + if ! semanage fcontext -a -t faillog_t "$dir(/.*)?"; then + semanage fcontext -m -t faillog_t "$dir(/.*)?" + fi + if [ ! -e $dir ]; then + mkdir -p $dir + fi + restorecon -R -v $dir + done +else +echo " +The pam_faillock.so dir option is not set in the system. +If this is not expected, make sure pam_faillock.so is properly configured." +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Account Lockouts Must Be Logged + PAM faillock locks an account due to excessive password failures, this event must be logged. + CCI-000044 + AC-7 (a) + SRG-OS-000021-GPOS-00005 + Without auditing of these events it may be harder or impossible to identify what an attacker did after an attack. + CCE-86108-8 + - name: Account Lockouts Must Be Logged - Check if system relies on authselect tool + ansible.builtin.stat: + path: /usr/bin/authselect + register: result_authselect_present + tags: + - CCE-86108-8 + - NIST-800-53-AC-7 (a) + - account_passwords_pam_faillock_audit + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Account Lockouts Must Be Logged - Remediation where authselect tool is present + block: + + - name: Account Lockouts Must Be Logged - Check integrity of authselect current + profile + ansible.builtin.command: + cmd: authselect check + register: result_authselect_check_cmd + changed_when: false + ignore_errors: true + + - name: Account Lockouts Must Be Logged - Informative message based on the authselect + integrity check result + ansible.builtin.assert: + that: + - result_authselect_check_cmd is success + fail_msg: + - authselect integrity check failed. Remediation aborted! + - This remediation could not be applied because an authselect profile was not + selected or the selected profile is not intact. + - It is not recommended to manually edit the PAM files when authselect tool + is available. + - In cases where the default authselect profile does not cover a specific demand, + a custom authselect profile is recommended. + success_msg: + - authselect integrity check passed + + - name: Account Lockouts Must Be Logged - Get authselect current features + ansible.builtin.shell: + cmd: authselect current | tail -n+3 | awk '{ print $2 }' + register: result_authselect_features + changed_when: false + when: + - result_authselect_check_cmd is success + + - name: Account Lockouts Must Be Logged - Ensure "with-faillock" feature is enabled + using authselect tool + ansible.builtin.command: + cmd: authselect enable-feature with-faillock + register: result_authselect_enable_feature_cmd + when: + - result_authselect_check_cmd is success + - result_authselect_features.stdout is not search("with-faillock") + + - name: Account Lockouts Must Be Logged - Ensure authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: + - result_authselect_enable_feature_cmd is not skipped + - result_authselect_enable_feature_cmd is success + when: result_authselect_present.stat.exists + tags: + - CCE-86108-8 + - NIST-800-53-AC-7 (a) + - account_passwords_pam_faillock_audit + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Account Lockouts Must Be Logged - Remediation where authselect tool is not + present + block: + + - name: Account Lockouts Must Be Logged - Check if pam_faillock.so is already enabled + ansible.builtin.lineinfile: + path: /etc/pam.d/system-auth + regexp: .*auth.*pam_faillock\.so (preauth|authfail) + state: absent + check_mode: true + changed_when: false + register: result_pam_faillock_is_enabled + + - name: Account Lockouts Must Be Logged - Enable pam_faillock.so preauth editing + PAM files + ansible.builtin.lineinfile: + path: '{{ item }}' + line: auth required pam_faillock.so preauth + insertbefore: ^auth.*sufficient.*pam_unix\.so.* + state: present + loop: + - /etc/pam.d/system-auth + - /etc/pam.d/password-auth + when: + - result_pam_faillock_is_enabled.found == 0 + + - name: Account Lockouts Must Be Logged - Enable pam_faillock.so authfail editing + PAM files + ansible.builtin.lineinfile: + path: '{{ item }}' + line: auth required pam_faillock.so authfail + insertbefore: ^auth.*required.*pam_deny\.so.* + state: present + loop: + - /etc/pam.d/system-auth + - /etc/pam.d/password-auth + when: + - result_pam_faillock_is_enabled.found == 0 + + - name: Account Lockouts Must Be Logged - Enable pam_faillock.so account section + editing PAM files + ansible.builtin.lineinfile: + path: '{{ item }}' + line: account required pam_faillock.so + insertbefore: ^account.*required.*pam_unix\.so.* + state: present + loop: + - /etc/pam.d/system-auth + - /etc/pam.d/password-auth + when: + - result_pam_faillock_is_enabled.found == 0 + when: not result_authselect_present.stat.exists + tags: + - CCE-86108-8 + - NIST-800-53-AC-7 (a) + - account_passwords_pam_faillock_audit + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Account Lockouts Must Be Logged - Check the presence of /etc/security/faillock.conf + file + ansible.builtin.stat: + path: /etc/security/faillock.conf + register: result_faillock_conf_check + tags: + - CCE-86108-8 + - NIST-800-53-AC-7 (a) + - account_passwords_pam_faillock_audit + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Account Lockouts Must Be Logged - Ensure the pam_faillock.so audit parameter + in /etc/security/faillock.conf + ansible.builtin.lineinfile: + path: /etc/security/faillock.conf + regexp: ^\s*audit + line: audit + state: present + when: result_faillock_conf_check.stat.exists + tags: + - CCE-86108-8 + - NIST-800-53-AC-7 (a) + - account_passwords_pam_faillock_audit + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Account Lockouts Must Be Logged - Ensure the pam_faillock.so audit parameter + not in PAM files + block: + + - name: Account Lockouts Must Be Logged - Check if /etc/pam.d/system-auth file is + present + ansible.builtin.stat: + path: /etc/pam.d/system-auth + register: result_pam_file_present + + - name: Account Lockouts Must Be Logged - Check the proper remediation for the system + block: + + - name: Account Lockouts Must Be Logged - Define the PAM file to be edited as + a local fact + ansible.builtin.set_fact: + pam_file_path: /etc/pam.d/system-auth + + - name: Account Lockouts Must Be Logged - Check if system relies on authselect + ansible.builtin.stat: + path: /usr/bin/authselect + register: result_authselect_present + + - name: Account Lockouts Must Be Logged - Remediate using authselect + block: + + - name: Account Lockouts Must Be Logged - Check integrity of authselect current + profile + ansible.builtin.command: + cmd: authselect check + register: result_authselect_check_cmd + changed_when: false + ignore_errors: true + + - name: Account Lockouts Must Be Logged - Informative message based on the authselect + integrity check result + ansible.builtin.assert: + that: + - result_authselect_check_cmd is success + fail_msg: + - authselect integrity check failed. Remediation aborted! + - This remediation could not be applied because an authselect profile was + not selected or the selected profile is not intact. + - It is not recommended to manually edit the PAM files when authselect tool + is available. + - In cases where the default authselect profile does not cover a specific + demand, a custom authselect profile is recommended. + success_msg: + - authselect integrity check passed + + - name: Account Lockouts Must Be Logged - Get authselect current profile + ansible.builtin.shell: + cmd: authselect current -r | awk '{ print $1 }' + register: result_authselect_profile + changed_when: false + when: + - result_authselect_check_cmd is success + + - name: Account Lockouts Must Be Logged - Define the current authselect profile + as a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: '{{ result_authselect_profile.stdout }}' + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is match("custom/") + + - name: Account Lockouts Must Be Logged - Define the new authselect custom profile + as a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: custom/hardening + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is not match("custom/") + + - name: Account Lockouts Must Be Logged - Get authselect current features to + also enable them in the custom profile + ansible.builtin.shell: + cmd: authselect current | tail -n+3 | awk '{ print $2 }' + register: result_authselect_features + changed_when: false + when: + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + + - name: Account Lockouts Must Be Logged - Check if any custom profile with the + same name was already created + ansible.builtin.stat: + path: /etc/authselect/{{ authselect_custom_profile }} + register: result_authselect_custom_profile_present + changed_when: false + when: + - authselect_current_profile is not match("custom/") + + - name: Account Lockouts Must Be Logged - Create an authselect custom profile + based on the current profile + ansible.builtin.command: + cmd: authselect create-profile hardening -b {{ authselect_current_profile + }} + when: + - result_authselect_check_cmd is success + - authselect_current_profile is not match("custom/") + - not result_authselect_custom_profile_present.stat.exists + + - name: Account Lockouts Must Be Logged - Ensure authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=before-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Account Lockouts Must Be Logged - Ensure the authselect custom profile + is selected + ansible.builtin.command: + cmd: authselect select {{ authselect_custom_profile }} + register: result_pam_authselect_select_profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Account Lockouts Must Be Logged - Restore the authselect features in + the custom profile + ansible.builtin.command: + cmd: authselect enable-feature {{ item }} + loop: '{{ result_authselect_features.stdout_lines }}' + register: result_pam_authselect_restore_features + when: + - result_authselect_profile is not skipped + - result_authselect_features is not skipped + - result_pam_authselect_select_profile is not skipped + + - name: Account Lockouts Must Be Logged - Ensure authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=after-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - result_pam_authselect_restore_features is not skipped + + - name: Account Lockouts Must Be Logged - Change the PAM file to be edited according + to the custom authselect profile + ansible.builtin.set_fact: + pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path + | basename }} + when: + - result_authselect_present.stat.exists + + - name: Account Lockouts Must Be Logged - Ensure the "audit" option from "pam_faillock.so" + is not present in {{ pam_file_path }} + ansible.builtin.replace: + dest: '{{ pam_file_path }}' + regexp: (.*auth.*pam_faillock.so.*)\baudit\b=?[0-9a-zA-Z]*(.*) + replace: \1\2 + register: result_pam_option_removal + + - name: Account Lockouts Must Be Logged - Ensure authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: + - result_authselect_present.stat.exists + - result_pam_option_removal is changed + when: + - result_pam_file_present.stat.exists + + - name: Account Lockouts Must Be Logged - Check if /etc/pam.d/password-auth file + is present + ansible.builtin.stat: + path: /etc/pam.d/password-auth + register: result_pam_file_present + + - name: Account Lockouts Must Be Logged - Check the proper remediation for the system + block: + + - name: Account Lockouts Must Be Logged - Define the PAM file to be edited as + a local fact + ansible.builtin.set_fact: + pam_file_path: /etc/pam.d/password-auth + + - name: Account Lockouts Must Be Logged - Check if system relies on authselect + ansible.builtin.stat: + path: /usr/bin/authselect + register: result_authselect_present + + - name: Account Lockouts Must Be Logged - Remediate using authselect + block: + + - name: Account Lockouts Must Be Logged - Check integrity of authselect current + profile + ansible.builtin.command: + cmd: authselect check + register: result_authselect_check_cmd + changed_when: false + ignore_errors: true + + - name: Account Lockouts Must Be Logged - Informative message based on the authselect + integrity check result + ansible.builtin.assert: + that: + - result_authselect_check_cmd is success + fail_msg: + - authselect integrity check failed. Remediation aborted! + - This remediation could not be applied because an authselect profile was + not selected or the selected profile is not intact. + - It is not recommended to manually edit the PAM files when authselect tool + is available. + - In cases where the default authselect profile does not cover a specific + demand, a custom authselect profile is recommended. + success_msg: + - authselect integrity check passed + + - name: Account Lockouts Must Be Logged - Get authselect current profile + ansible.builtin.shell: + cmd: authselect current -r | awk '{ print $1 }' + register: result_authselect_profile + changed_when: false + when: + - result_authselect_check_cmd is success + + - name: Account Lockouts Must Be Logged - Define the current authselect profile + as a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: '{{ result_authselect_profile.stdout }}' + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is match("custom/") + + - name: Account Lockouts Must Be Logged - Define the new authselect custom profile + as a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: custom/hardening + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is not match("custom/") + + - name: Account Lockouts Must Be Logged - Get authselect current features to + also enable them in the custom profile + ansible.builtin.shell: + cmd: authselect current | tail -n+3 | awk '{ print $2 }' + register: result_authselect_features + changed_when: false + when: + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + + - name: Account Lockouts Must Be Logged - Check if any custom profile with the + same name was already created + ansible.builtin.stat: + path: /etc/authselect/{{ authselect_custom_profile }} + register: result_authselect_custom_profile_present + changed_when: false + when: + - authselect_current_profile is not match("custom/") + + - name: Account Lockouts Must Be Logged - Create an authselect custom profile + based on the current profile + ansible.builtin.command: + cmd: authselect create-profile hardening -b {{ authselect_current_profile + }} + when: + - result_authselect_check_cmd is success + - authselect_current_profile is not match("custom/") + - not result_authselect_custom_profile_present.stat.exists + + - name: Account Lockouts Must Be Logged - Ensure authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=before-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Account Lockouts Must Be Logged - Ensure the authselect custom profile + is selected + ansible.builtin.command: + cmd: authselect select {{ authselect_custom_profile }} + register: result_pam_authselect_select_profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Account Lockouts Must Be Logged - Restore the authselect features in + the custom profile + ansible.builtin.command: + cmd: authselect enable-feature {{ item }} + loop: '{{ result_authselect_features.stdout_lines }}' + register: result_pam_authselect_restore_features + when: + - result_authselect_profile is not skipped + - result_authselect_features is not skipped + - result_pam_authselect_select_profile is not skipped + + - name: Account Lockouts Must Be Logged - Ensure authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=after-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - result_pam_authselect_restore_features is not skipped + + - name: Account Lockouts Must Be Logged - Change the PAM file to be edited according + to the custom authselect profile + ansible.builtin.set_fact: + pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path + | basename }} + when: + - result_authselect_present.stat.exists + + - name: Account Lockouts Must Be Logged - Ensure the "audit" option from "pam_faillock.so" + is not present in {{ pam_file_path }} + ansible.builtin.replace: + dest: '{{ pam_file_path }}' + regexp: (.*auth.*pam_faillock.so.*)\baudit\b=?[0-9a-zA-Z]*(.*) + replace: \1\2 + register: result_pam_option_removal + + - name: Account Lockouts Must Be Logged - Ensure authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: + - result_authselect_present.stat.exists + - result_pam_option_removal is changed + when: + - result_pam_file_present.stat.exists + when: result_faillock_conf_check.stat.exists + tags: + - CCE-86108-8 + - NIST-800-53-AC-7 (a) + - account_passwords_pam_faillock_audit + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Account Lockouts Must Be Logged - Ensure the pam_faillock.so audit parameter + in PAM files + block: + + - name: Account Lockouts Must Be Logged - Check if pam_faillock.so audit parameter + is already enabled in pam files + ansible.builtin.lineinfile: + path: /etc/pam.d/system-auth + regexp: .*auth.*pam_faillock\.so (preauth|authfail).*audit + state: absent + check_mode: true + changed_when: false + register: result_pam_faillock_audit_parameter_is_present + + - name: Account Lockouts Must Be Logged - Ensure the inclusion of pam_faillock.so + preauth audit parameter in auth section + ansible.builtin.lineinfile: + path: '{{ item }}' + backrefs: true + regexp: (^\s*auth\s+)([\w\[].*\b)(\s+pam_faillock.so preauth.*) + line: \1required\3 audit + state: present + loop: + - /etc/pam.d/system-auth + - /etc/pam.d/password-auth + when: + - result_pam_faillock_audit_parameter_is_present.found == 0 + when: not result_faillock_conf_check.stat.exists + tags: + - CCE-86108-8 + - NIST-800-53-AC-7 (a) + - account_passwords_pam_faillock_audit + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + +if [ -f /usr/bin/authselect ]; then + if ! authselect check; then +echo " +authselect integrity check failed. Remediation aborted! +This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact. +It is not recommended to manually edit the PAM files when authselect tool is available. +In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended." +exit 1 +fi +authselect enable-feature with-faillock + +authselect apply-changes -b +else + AUTH_FILES=("/etc/pam.d/system-auth" "/etc/pam.d/password-auth") +for pam_file in "${AUTH_FILES[@]}" +do + if ! grep -qE '^\s*auth\s+required\s+pam_faillock\.so\s+(preauth silent|authfail).*$' "$pam_file" ; then + sed -i --follow-symlinks '/^auth.*sufficient.*pam_unix\.so.*/i auth required pam_faillock.so preauth silent' "$pam_file" + sed -i --follow-symlinks '/^auth.*required.*pam_deny\.so.*/i auth required pam_faillock.so authfail' "$pam_file" + sed -i --follow-symlinks '/^account.*required.*pam_unix\.so.*/i account required pam_faillock.so' "$pam_file" + fi + sed -Ei 's/(auth.*)(\[default=die\])(.*pam_faillock\.so)/\1required \3/g' "$pam_file" +done +fi +AUTH_FILES=("/etc/pam.d/system-auth" "/etc/pam.d/password-auth") +FAILLOCK_CONF="/etc/security/faillock.conf" +if [ -f $FAILLOCK_CONF ]; then + regex="^\s*audit" + line="audit" + if ! grep -q $regex $FAILLOCK_CONF; then + echo $line >> $FAILLOCK_CONF + fi + for pam_file in "${AUTH_FILES[@]}" + do + if [ -e "$pam_file" ] ; then + PAM_FILE_PATH="$pam_file" + if [ -f /usr/bin/authselect ]; then + if ! authselect check; then + echo " + authselect integrity check failed. Remediation aborted! + This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact. + It is not recommended to manually edit the PAM files when authselect tool is available. + In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended." + exit 1 + fi + CURRENT_PROFILE=$(authselect current -r | awk '{ print $1 }') + # If not already in use, a custom profile is created preserving the enabled features. + if [[ ! $CURRENT_PROFILE == custom/* ]]; then + ENABLED_FEATURES=$(authselect current | tail -n+3 | awk '{ print $2 }') + authselect create-profile hardening -b $CURRENT_PROFILE + CURRENT_PROFILE="custom/hardening" + + authselect apply-changes -b --backup=before-hardening-custom-profile + authselect select $CURRENT_PROFILE + for feature in $ENABLED_FEATURES; do + authselect enable-feature $feature; + done + + authselect apply-changes -b --backup=after-hardening-custom-profile + fi + PAM_FILE_NAME=$(basename "$pam_file") + PAM_FILE_PATH="/etc/authselect/$CURRENT_PROFILE/$PAM_FILE_NAME" + + authselect apply-changes -b + fi + + if grep -qP '^\s*auth\s.*\bpam_faillock.so\s.*\baudit\b' "$PAM_FILE_PATH"; then + sed -i -E --follow-symlinks 's/(.*auth.*pam_faillock.so.*)\baudit\b=?[[:alnum:]]*(.*)/\1\2/g' "$PAM_FILE_PATH" + fi + if [ -f /usr/bin/authselect ]; then + + authselect apply-changes -b + fi + else + echo "$pam_file was not found" >&2 + fi + done +else + for pam_file in "${AUTH_FILES[@]}" + do + if ! grep -qE '^\s*auth.*pam_faillock\.so (preauth|authfail).*audit' "$pam_file"; then + sed -i --follow-symlinks '/^auth.*required.*pam_faillock\.so.*preauth.*silent.*/ s/$/ audit/' "$pam_file" + fi + done +fi + + + + + + + + + + Account Lockouts Must Persist + By setting a `dir` in the faillock configuration account lockouts will persist across reboots. + CCI-000044 + AC-7 (a) + SRG-OS-000021-GPOS-00005 + Having lockouts persist across reboots ensures that account is only unlocked by an administrator. +If the lockouts did not persist across reboots an attack could simply reboot the system to continue brute force attacks against the accounts on the system. + + CCE-86080-9 + + + + + + Limit Password Reuse: password-auth + Do not allow users to reuse recent passwords. This can be accomplished by using the +remember option for the pam_pwhistory PAM module. + +In the file /etc/pam.d/password-auth, make sure the parameter +remember is present and it has a value equal to or greater than +. For example: +password control_flag pam_pwhistory.so ...existing_options... remember= use_authtok +control_flag should be one of the next values: + If the system relies on authselect tool to manage PAM settings, the remediation +will also use authselect tool. However, if any manual modification was made in +PAM files, the authselect integrity check will fail and the remediation will be +aborted in order to preserve intentional changes. In this case, an informative message will +be shown in the remediation report. + 1 + 12 + 15 + 16 + 5 + 5.6.2.1.1 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.03 + DSS06.10 + 3.5.8 + CCI-000200 + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.2 + 4.3.3.7.4 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + A.18.1.4 + A.7.1.1 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.2 + A.9.4.3 + IA-5(f) + IA-5(1)(e) + PR.AC-1 + PR.AC-6 + PR.AC-7 + Req-8.2.5 + SRG-OS-000077-GPOS-00045 + SRG-OS-000077-VMM-000440 + Preventing re-use of previous passwords helps ensure that a compromised password is not re-used by a user. + + CCE-86354-8 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-86354-8 + - CJIS-5.6.2.1.1 + - NIST-800-171-3.5.8 + - NIST-800-53-IA-5(1)(e) + - NIST-800-53-IA-5(f) + - PCI-DSS-Req-8.2.5 + - accounts_password_pam_pwhistory_remember_password_auth + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed +- name: XCCDF Value var_password_pam_remember # promote to variable + set_fact: + var_password_pam_remember: !!str + tags: + - always +- name: XCCDF Value var_password_pam_remember_control_flag # promote to variable + set_fact: + var_password_pam_remember_control_flag: !!str + tags: + - always + +- name: 'Limit Password Reuse: password-auth - Check if /etc/pam.d/password-auth file + is present' + ansible.builtin.stat: + path: /etc/pam.d/password-auth + register: result_pam_file_present + when: '"pam" in ansible_facts.packages' + tags: + - CCE-86354-8 + - CJIS-5.6.2.1.1 + - NIST-800-171-3.5.8 + - NIST-800-53-IA-5(1)(e) + - NIST-800-53-IA-5(f) + - PCI-DSS-Req-8.2.5 + - accounts_password_pam_pwhistory_remember_password_auth + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + +- name: 'Limit Password Reuse: password-auth - Check the proper remediation for the + system' + block: + + - name: 'Limit Password Reuse: password-auth - Define the PAM file to be edited + as a local fact' + ansible.builtin.set_fact: + pam_file_path: /etc/pam.d/password-auth + + - name: 'Limit Password Reuse: password-auth - Check if system relies on authselect' + ansible.builtin.stat: + path: /usr/bin/authselect + register: result_authselect_present + + - name: 'Limit Password Reuse: password-auth - Remediate using authselect' + block: + + - name: 'Limit Password Reuse: password-auth - Check integrity of authselect current + profile' + ansible.builtin.command: + cmd: authselect check + register: result_authselect_check_cmd + changed_when: false + ignore_errors: true + + - name: 'Limit Password Reuse: password-auth - Informative message based on the + authselect integrity check result' + ansible.builtin.assert: + that: + - result_authselect_check_cmd is success + fail_msg: + - authselect integrity check failed. Remediation aborted! + - This remediation could not be applied because an authselect profile was + not selected or the selected profile is not intact. + - It is not recommended to manually edit the PAM files when authselect tool + is available. + - In cases where the default authselect profile does not cover a specific + demand, a custom authselect profile is recommended. + success_msg: + - authselect integrity check passed + + - name: 'Limit Password Reuse: password-auth - Get authselect current profile' + ansible.builtin.shell: + cmd: authselect current -r | awk '{ print $1 }' + register: result_authselect_profile + changed_when: false + when: + - result_authselect_check_cmd is success + + - name: 'Limit Password Reuse: password-auth - Define the current authselect profile + as a local fact' + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: '{{ result_authselect_profile.stdout }}' + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is match("custom/") + + - name: 'Limit Password Reuse: password-auth - Define the new authselect custom + profile as a local fact' + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: custom/hardening + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is not match("custom/") + + - name: 'Limit Password Reuse: password-auth - Get authselect current features + to also enable them in the custom profile' + ansible.builtin.shell: + cmd: authselect current | tail -n+3 | awk '{ print $2 }' + register: result_authselect_features + changed_when: false + when: + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + + - name: 'Limit Password Reuse: password-auth - Check if any custom profile with + the same name was already created' + ansible.builtin.stat: + path: /etc/authselect/{{ authselect_custom_profile }} + register: result_authselect_custom_profile_present + changed_when: false + when: + - authselect_current_profile is not match("custom/") + + - name: 'Limit Password Reuse: password-auth - Create an authselect custom profile + based on the current profile' + ansible.builtin.command: + cmd: authselect create-profile hardening -b {{ authselect_current_profile + }} + when: + - result_authselect_check_cmd is success + - authselect_current_profile is not match("custom/") + - not result_authselect_custom_profile_present.stat.exists + + - name: 'Limit Password Reuse: password-auth - Ensure authselect changes are applied' + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=before-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: 'Limit Password Reuse: password-auth - Ensure the authselect custom profile + is selected' + ansible.builtin.command: + cmd: authselect select {{ authselect_custom_profile }} + register: result_pam_authselect_select_profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: 'Limit Password Reuse: password-auth - Restore the authselect features + in the custom profile' + ansible.builtin.command: + cmd: authselect enable-feature {{ item }} + loop: '{{ result_authselect_features.stdout_lines }}' + register: result_pam_authselect_restore_features + when: + - result_authselect_profile is not skipped + - result_authselect_features is not skipped + - result_pam_authselect_select_profile is not skipped + + - name: 'Limit Password Reuse: password-auth - Ensure authselect changes are applied' + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=after-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - result_pam_authselect_restore_features is not skipped + + - name: 'Limit Password Reuse: password-auth - Change the PAM file to be edited + according to the custom authselect profile' + ansible.builtin.set_fact: + pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path + | basename }} + when: + - result_authselect_present.stat.exists + + - name: 'Limit Password Reuse: password-auth - Check if expected PAM module line + is present in {{ pam_file_path }}' + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + regexp: ^\s*password\s+{{ var_password_pam_remember_control_flag.split(",")[0] + }}\s+pam_pwhistory.so\s*.* + state: absent + check_mode: true + changed_when: false + register: result_pam_line_present + + - name: 'Limit Password Reuse: password-auth - Include or update the PAM module + line in {{ pam_file_path }}' + block: + + - name: 'Limit Password Reuse: password-auth - Check if required PAM module line + is present in {{ pam_file_path }} with different control' + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + regexp: ^\s*password\s+.*\s+pam_pwhistory.so\s* + state: absent + check_mode: true + changed_when: false + register: result_pam_line_other_control_present + + - name: 'Limit Password Reuse: password-auth - Ensure the correct control for + the required PAM module line in {{ pam_file_path }}' + ansible.builtin.replace: + dest: '{{ pam_file_path }}' + regexp: ^(\s*password\s+).*(\bpam_pwhistory.so.*) + replace: \1{{ var_password_pam_remember_control_flag.split(",")[0] }} \2 + register: result_pam_module_edit + when: + - result_pam_line_other_control_present.found == 1 + + - name: 'Limit Password Reuse: password-auth - Ensure the required PAM module + line is included in {{ pam_file_path }}' + ansible.builtin.lineinfile: + dest: '{{ pam_file_path }}' + insertafter: ^password.*requisite.*pam_pwquality\.so + line: password {{ var_password_pam_remember_control_flag.split(",")[0] + }} pam_pwhistory.so + register: result_pam_module_add + when: + - result_pam_line_other_control_present.found == 0 or result_pam_line_other_control_present.found + > 1 + + - name: 'Limit Password Reuse: password-auth - Ensure authselect changes are applied' + ansible.builtin.command: + cmd: authselect apply-changes -b + when: | + result_authselect_present is defined and result_authselect_present.stat.exists and ((result_pam_module_add is defined and result_pam_module_add.changed) or (result_pam_module_edit is defined and result_pam_module_edit.changed)) + when: + - result_pam_line_present.found is defined + - result_pam_line_present.found == 0 + + - name: 'Limit Password Reuse: password-auth - Check if the required PAM module + option is present in {{ pam_file_path }}' + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + regexp: ^\s*password\s+{{ var_password_pam_remember_control_flag.split(",")[0] + }}\s+pam_pwhistory.so\s*.*\sremember\b + state: absent + check_mode: true + changed_when: false + register: result_pam_module_remember_option_present + + - name: 'Limit Password Reuse: password-auth - Ensure the "remember" PAM option + for "pam_pwhistory.so" is included in {{ pam_file_path }}' + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + backrefs: true + regexp: ^(\s*password\s+{{ var_password_pam_remember_control_flag.split(",")[0] + }}\s+pam_pwhistory.so.*) + line: \1 remember={{ var_password_pam_remember }} + state: present + register: result_pam_remember_add + when: + - result_pam_module_remember_option_present.found == 0 + + - name: 'Limit Password Reuse: password-auth - Ensure the required value for "remember" + PAM option from "pam_pwhistory.so" in {{ pam_file_path }}' + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + backrefs: true + regexp: ^(\s*password\s+{{ var_password_pam_remember_control_flag.split(",")[0] + }}\s+pam_pwhistory.so\s+.*)(remember)=[0-9a-zA-Z]+\s*(.*) + line: \1\2={{ var_password_pam_remember }} \3 + register: result_pam_remember_edit + when: + - result_pam_module_remember_option_present.found > 0 + + - name: 'Limit Password Reuse: password-auth - Ensure authselect changes are applied' + ansible.builtin.command: + cmd: authselect apply-changes -b + when: + - result_authselect_present.stat.exists + - (result_pam_remember_add is defined and result_pam_remember_add.changed) or + (result_pam_remember_edit is defined and result_pam_remember_edit.changed) + when: + - '"pam" in ansible_facts.packages' + - result_pam_file_present.stat.exists + tags: + - CCE-86354-8 + - CJIS-5.6.2.1.1 + - NIST-800-171-3.5.8 + - NIST-800-53-IA-5(1)(e) + - NIST-800-53-IA-5(f) + - PCI-DSS-Req-8.2.5 + - accounts_password_pam_pwhistory_remember_password_auth + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if rpm --quiet -q pam; then + +var_password_pam_remember='' +var_password_pam_remember_control_flag='' + + +var_password_pam_remember_control_flag="$(echo $var_password_pam_remember_control_flag | cut -d \, -f 1)" + +if [ -e "/etc/pam.d/password-auth" ] ; then + PAM_FILE_PATH="/etc/pam.d/password-auth" + if [ -f /usr/bin/authselect ]; then + if ! authselect check; then + echo " + authselect integrity check failed. Remediation aborted! + This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact. + It is not recommended to manually edit the PAM files when authselect tool is available. + In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended." + exit 1 + fi + CURRENT_PROFILE=$(authselect current -r | awk '{ print $1 }') + # If not already in use, a custom profile is created preserving the enabled features. + if [[ ! $CURRENT_PROFILE == custom/* ]]; then + ENABLED_FEATURES=$(authselect current | tail -n+3 | awk '{ print $2 }') + authselect create-profile hardening -b $CURRENT_PROFILE + CURRENT_PROFILE="custom/hardening" + + authselect apply-changes -b --backup=before-hardening-custom-profile + authselect select $CURRENT_PROFILE + for feature in $ENABLED_FEATURES; do + authselect enable-feature $feature; + done + + authselect apply-changes -b --backup=after-hardening-custom-profile + fi + PAM_FILE_NAME=$(basename "/etc/pam.d/password-auth") + PAM_FILE_PATH="/etc/authselect/$CURRENT_PROFILE/$PAM_FILE_NAME" + + authselect apply-changes -b + fi + if ! grep -qP '^\s*password\s+'"$var_password_pam_remember_control_flag"'\s+pam_pwhistory.so\s*.*' "$PAM_FILE_PATH"; then + # Line matching group + control + module was not found. Check group + module. + if [ "$(grep -cP '^\s*password\s+.*\s+pam_pwhistory.so\s*' "$PAM_FILE_PATH")" -eq 1 ]; then + # The control is updated only if one single line matches. + sed -i -E --follow-symlinks 's/^(\s*password\s+).*(\bpam_pwhistory.so.*)/\1'"$var_password_pam_remember_control_flag"' \2/' "$PAM_FILE_PATH" + else + LAST_MATCH_LINE=$(grep -nP "^password.*requisite.*pam_pwquality\.so" "$PAM_FILE_PATH" | tail -n 1 | cut -d: -f 1) + if [ ! -z $LAST_MATCH_LINE ]; then + sed -i --follow-symlinks $LAST_MATCH_LINE' a password '"$var_password_pam_remember_control_flag"' pam_pwhistory.so' "$PAM_FILE_PATH" + else + echo 'password '"$var_password_pam_remember_control_flag"' pam_pwhistory.so' >> "$PAM_FILE_PATH" + fi + fi + fi + # Check the option + if ! grep -qP '^\s*password\s+'"$var_password_pam_remember_control_flag"'\s+pam_pwhistory.so\s*.*\sremember\b' "$PAM_FILE_PATH"; then + sed -i -E --follow-symlinks '/\s*password\s+'"$var_password_pam_remember_control_flag"'\s+pam_pwhistory.so.*/ s/$/ remember='"$var_password_pam_remember"'/' "$PAM_FILE_PATH" + else + sed -i -E --follow-symlinks 's/(\s*password\s+'"$var_password_pam_remember_control_flag"'\s+pam_pwhistory.so\s+.*)('"remember"'=)[[:alnum:]]+\s*(.*)/\1\2'"$var_password_pam_remember"' \3/' "$PAM_FILE_PATH" + fi + if [ -f /usr/bin/authselect ]; then + + authselect apply-changes -b + fi +else + echo "/etc/pam.d/password-auth was not found" >&2 +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + + Limit Password Reuse: system-auth + Do not allow users to reuse recent passwords. This can be accomplished by using the +remember option for the pam_pwhistory PAM module. + +In the file /etc/pam.d/system-auth, make sure the parameter +remember is present and it has a value equal to or greater than + +For example: +password control_flag pam_pwhistory.so ...existing_options... remember= use_authtok +control_flag should be one of the next values: + If the system relies on authselect tool to manage PAM settings, the remediation +will also use authselect tool. However, if any manual modification was made in +PAM files, the authselect integrity check will fail and the remediation will be +aborted in order to preserve intentional changes. In this case, an informative message will +be shown in the remediation report. + 1 + 12 + 15 + 16 + 5 + 5.6.2.1.1 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.03 + DSS06.10 + 3.5.8 + CCI-000200 + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.2 + 4.3.3.7.4 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + A.18.1.4 + A.7.1.1 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.2 + A.9.4.3 + IA-5(f) + IA-5(1)(e) + PR.AC-1 + PR.AC-6 + PR.AC-7 + Req-8.2.5 + SRG-OS-000077-GPOS-00045 + SRG-OS-000077-VMM-000440 + Preventing re-use of previous passwords helps ensure that a compromised password is not re-used by a user. + + CCE-89176-2 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-89176-2 + - CJIS-5.6.2.1.1 + - NIST-800-171-3.5.8 + - NIST-800-53-IA-5(1)(e) + - NIST-800-53-IA-5(f) + - PCI-DSS-Req-8.2.5 + - accounts_password_pam_pwhistory_remember_system_auth + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed +- name: XCCDF Value var_password_pam_remember # promote to variable + set_fact: + var_password_pam_remember: !!str + tags: + - always +- name: XCCDF Value var_password_pam_remember_control_flag # promote to variable + set_fact: + var_password_pam_remember_control_flag: !!str + tags: + - always + +- name: 'Limit Password Reuse: system-auth - Check if /etc/pam.d/system-auth file + is present' + ansible.builtin.stat: + path: /etc/pam.d/system-auth + register: result_pam_file_present + when: '"pam" in ansible_facts.packages' + tags: + - CCE-89176-2 + - CJIS-5.6.2.1.1 + - NIST-800-171-3.5.8 + - NIST-800-53-IA-5(1)(e) + - NIST-800-53-IA-5(f) + - PCI-DSS-Req-8.2.5 + - accounts_password_pam_pwhistory_remember_system_auth + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + +- name: 'Limit Password Reuse: system-auth - Check the proper remediation for the + system' + block: + + - name: 'Limit Password Reuse: system-auth - Define the PAM file to be edited as + a local fact' + ansible.builtin.set_fact: + pam_file_path: /etc/pam.d/system-auth + + - name: 'Limit Password Reuse: system-auth - Check if system relies on authselect' + ansible.builtin.stat: + path: /usr/bin/authselect + register: result_authselect_present + + - name: 'Limit Password Reuse: system-auth - Remediate using authselect' + block: + + - name: 'Limit Password Reuse: system-auth - Check integrity of authselect current + profile' + ansible.builtin.command: + cmd: authselect check + register: result_authselect_check_cmd + changed_when: false + ignore_errors: true + + - name: 'Limit Password Reuse: system-auth - Informative message based on the + authselect integrity check result' + ansible.builtin.assert: + that: + - result_authselect_check_cmd is success + fail_msg: + - authselect integrity check failed. Remediation aborted! + - This remediation could not be applied because an authselect profile was + not selected or the selected profile is not intact. + - It is not recommended to manually edit the PAM files when authselect tool + is available. + - In cases where the default authselect profile does not cover a specific + demand, a custom authselect profile is recommended. + success_msg: + - authselect integrity check passed + + - name: 'Limit Password Reuse: system-auth - Get authselect current profile' + ansible.builtin.shell: + cmd: authselect current -r | awk '{ print $1 }' + register: result_authselect_profile + changed_when: false + when: + - result_authselect_check_cmd is success + + - name: 'Limit Password Reuse: system-auth - Define the current authselect profile + as a local fact' + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: '{{ result_authselect_profile.stdout }}' + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is match("custom/") + + - name: 'Limit Password Reuse: system-auth - Define the new authselect custom + profile as a local fact' + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: custom/hardening + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is not match("custom/") + + - name: 'Limit Password Reuse: system-auth - Get authselect current features to + also enable them in the custom profile' + ansible.builtin.shell: + cmd: authselect current | tail -n+3 | awk '{ print $2 }' + register: result_authselect_features + changed_when: false + when: + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + + - name: 'Limit Password Reuse: system-auth - Check if any custom profile with + the same name was already created' + ansible.builtin.stat: + path: /etc/authselect/{{ authselect_custom_profile }} + register: result_authselect_custom_profile_present + changed_when: false + when: + - authselect_current_profile is not match("custom/") + + - name: 'Limit Password Reuse: system-auth - Create an authselect custom profile + based on the current profile' + ansible.builtin.command: + cmd: authselect create-profile hardening -b {{ authselect_current_profile + }} + when: + - result_authselect_check_cmd is success + - authselect_current_profile is not match("custom/") + - not result_authselect_custom_profile_present.stat.exists + + - name: 'Limit Password Reuse: system-auth - Ensure authselect changes are applied' + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=before-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: 'Limit Password Reuse: system-auth - Ensure the authselect custom profile + is selected' + ansible.builtin.command: + cmd: authselect select {{ authselect_custom_profile }} + register: result_pam_authselect_select_profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: 'Limit Password Reuse: system-auth - Restore the authselect features in + the custom profile' + ansible.builtin.command: + cmd: authselect enable-feature {{ item }} + loop: '{{ result_authselect_features.stdout_lines }}' + register: result_pam_authselect_restore_features + when: + - result_authselect_profile is not skipped + - result_authselect_features is not skipped + - result_pam_authselect_select_profile is not skipped + + - name: 'Limit Password Reuse: system-auth - Ensure authselect changes are applied' + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=after-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - result_pam_authselect_restore_features is not skipped + + - name: 'Limit Password Reuse: system-auth - Change the PAM file to be edited + according to the custom authselect profile' + ansible.builtin.set_fact: + pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path + | basename }} + when: + - result_authselect_present.stat.exists + + - name: 'Limit Password Reuse: system-auth - Check if expected PAM module line is + present in {{ pam_file_path }}' + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + regexp: ^\s*password\s+{{ var_password_pam_remember_control_flag.split(",")[0] + }}\s+pam_pwhistory.so\s*.* + state: absent + check_mode: true + changed_when: false + register: result_pam_line_present + + - name: 'Limit Password Reuse: system-auth - Include or update the PAM module line + in {{ pam_file_path }}' + block: + + - name: 'Limit Password Reuse: system-auth - Check if required PAM module line + is present in {{ pam_file_path }} with different control' + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + regexp: ^\s*password\s+.*\s+pam_pwhistory.so\s* + state: absent + check_mode: true + changed_when: false + register: result_pam_line_other_control_present + + - name: 'Limit Password Reuse: system-auth - Ensure the correct control for the + required PAM module line in {{ pam_file_path }}' + ansible.builtin.replace: + dest: '{{ pam_file_path }}' + regexp: ^(\s*password\s+).*(\bpam_pwhistory.so.*) + replace: \1{{ var_password_pam_remember_control_flag.split(",")[0] }} \2 + register: result_pam_module_edit + when: + - result_pam_line_other_control_present.found == 1 + + - name: 'Limit Password Reuse: system-auth - Ensure the required PAM module line + is included in {{ pam_file_path }}' + ansible.builtin.lineinfile: + dest: '{{ pam_file_path }}' + insertafter: ^password.*requisite.*pam_pwquality\.so + line: password {{ var_password_pam_remember_control_flag.split(",")[0] + }} pam_pwhistory.so + register: result_pam_module_add + when: + - result_pam_line_other_control_present.found == 0 or result_pam_line_other_control_present.found + > 1 + + - name: 'Limit Password Reuse: system-auth - Ensure authselect changes are applied' + ansible.builtin.command: + cmd: authselect apply-changes -b + when: | + result_authselect_present is defined and result_authselect_present.stat.exists and ((result_pam_module_add is defined and result_pam_module_add.changed) or (result_pam_module_edit is defined and result_pam_module_edit.changed)) + when: + - result_pam_line_present.found is defined + - result_pam_line_present.found == 0 + + - name: 'Limit Password Reuse: system-auth - Check if the required PAM module option + is present in {{ pam_file_path }}' + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + regexp: ^\s*password\s+{{ var_password_pam_remember_control_flag.split(",")[0] + }}\s+pam_pwhistory.so\s*.*\sremember\b + state: absent + check_mode: true + changed_when: false + register: result_pam_module_remember_option_present + + - name: 'Limit Password Reuse: system-auth - Ensure the "remember" PAM option for + "pam_pwhistory.so" is included in {{ pam_file_path }}' + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + backrefs: true + regexp: ^(\s*password\s+{{ var_password_pam_remember_control_flag.split(",")[0] + }}\s+pam_pwhistory.so.*) + line: \1 remember={{ var_password_pam_remember }} + state: present + register: result_pam_remember_add + when: + - result_pam_module_remember_option_present.found == 0 + + - name: 'Limit Password Reuse: system-auth - Ensure the required value for "remember" + PAM option from "pam_pwhistory.so" in {{ pam_file_path }}' + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + backrefs: true + regexp: ^(\s*password\s+{{ var_password_pam_remember_control_flag.split(",")[0] + }}\s+pam_pwhistory.so\s+.*)(remember)=[0-9a-zA-Z]+\s*(.*) + line: \1\2={{ var_password_pam_remember }} \3 + register: result_pam_remember_edit + when: + - result_pam_module_remember_option_present.found > 0 + + - name: 'Limit Password Reuse: system-auth - Ensure authselect changes are applied' + ansible.builtin.command: + cmd: authselect apply-changes -b + when: + - result_authselect_present.stat.exists + - (result_pam_remember_add is defined and result_pam_remember_add.changed) or + (result_pam_remember_edit is defined and result_pam_remember_edit.changed) + when: + - '"pam" in ansible_facts.packages' + - result_pam_file_present.stat.exists + tags: + - CCE-89176-2 + - CJIS-5.6.2.1.1 + - NIST-800-171-3.5.8 + - NIST-800-53-IA-5(1)(e) + - NIST-800-53-IA-5(f) + - PCI-DSS-Req-8.2.5 + - accounts_password_pam_pwhistory_remember_system_auth + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if rpm --quiet -q pam; then + +var_password_pam_remember='' +var_password_pam_remember_control_flag='' + + +var_password_pam_remember_control_flag="$(echo $var_password_pam_remember_control_flag | cut -d \, -f 1)" + +if [ -e "/etc/pam.d/system-auth" ] ; then + PAM_FILE_PATH="/etc/pam.d/system-auth" + if [ -f /usr/bin/authselect ]; then + if ! authselect check; then + echo " + authselect integrity check failed. Remediation aborted! + This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact. + It is not recommended to manually edit the PAM files when authselect tool is available. + In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended." + exit 1 + fi + CURRENT_PROFILE=$(authselect current -r | awk '{ print $1 }') + # If not already in use, a custom profile is created preserving the enabled features. + if [[ ! $CURRENT_PROFILE == custom/* ]]; then + ENABLED_FEATURES=$(authselect current | tail -n+3 | awk '{ print $2 }') + authselect create-profile hardening -b $CURRENT_PROFILE + CURRENT_PROFILE="custom/hardening" + + authselect apply-changes -b --backup=before-hardening-custom-profile + authselect select $CURRENT_PROFILE + for feature in $ENABLED_FEATURES; do + authselect enable-feature $feature; + done + + authselect apply-changes -b --backup=after-hardening-custom-profile + fi + PAM_FILE_NAME=$(basename "/etc/pam.d/system-auth") + PAM_FILE_PATH="/etc/authselect/$CURRENT_PROFILE/$PAM_FILE_NAME" + + authselect apply-changes -b + fi + if ! grep -qP '^\s*password\s+'"$var_password_pam_remember_control_flag"'\s+pam_pwhistory.so\s*.*' "$PAM_FILE_PATH"; then + # Line matching group + control + module was not found. Check group + module. + if [ "$(grep -cP '^\s*password\s+.*\s+pam_pwhistory.so\s*' "$PAM_FILE_PATH")" -eq 1 ]; then + # The control is updated only if one single line matches. + sed -i -E --follow-symlinks 's/^(\s*password\s+).*(\bpam_pwhistory.so.*)/\1'"$var_password_pam_remember_control_flag"' \2/' "$PAM_FILE_PATH" + else + LAST_MATCH_LINE=$(grep -nP "^password.*requisite.*pam_pwquality\.so" "$PAM_FILE_PATH" | tail -n 1 | cut -d: -f 1) + if [ ! -z $LAST_MATCH_LINE ]; then + sed -i --follow-symlinks $LAST_MATCH_LINE' a password '"$var_password_pam_remember_control_flag"' pam_pwhistory.so' "$PAM_FILE_PATH" + else + echo 'password '"$var_password_pam_remember_control_flag"' pam_pwhistory.so' >> "$PAM_FILE_PATH" + fi + fi + fi + # Check the option + if ! grep -qP '^\s*password\s+'"$var_password_pam_remember_control_flag"'\s+pam_pwhistory.so\s*.*\sremember\b' "$PAM_FILE_PATH"; then + sed -i -E --follow-symlinks '/\s*password\s+'"$var_password_pam_remember_control_flag"'\s+pam_pwhistory.so.*/ s/$/ remember='"$var_password_pam_remember"'/' "$PAM_FILE_PATH" + else + sed -i -E --follow-symlinks 's/(\s*password\s+'"$var_password_pam_remember_control_flag"'\s+pam_pwhistory.so\s+.*)('"remember"'=)[[:alnum:]]+\s*(.*)/\1\2'"$var_password_pam_remember"' \3/' "$PAM_FILE_PATH" + fi + if [ -f /usr/bin/authselect ]; then + + authselect apply-changes -b + fi +else + echo "/etc/pam.d/system-auth was not found" >&2 +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + + Limit Password Reuse + Do not allow users to reuse recent passwords. This can be accomplished by using the +remember option for the pam_unix or pam_pwhistory PAM modules. + If the system relies on authselect tool to manage PAM settings, the remediation +will also use authselect tool. However, if any manual modification was made in +PAM files, the authselect integrity check will fail and the remediation will be +aborted in order to preserve intentional changes. In this case, an informative message will +be shown in the remediation report. + BP28(R18) + 1 + 12 + 15 + 16 + 5 + 5.6.2.1.1 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.03 + DSS06.10 + 3.5.8 + CCI-000200 + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.2 + 4.3.3.7.4 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + A.18.1.4 + A.7.1.1 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.2 + A.9.4.3 + IA-5(f) + IA-5(1)(e) + PR.AC-1 + PR.AC-6 + PR.AC-7 + Req-8.2.5 + SRG-OS-000077-GPOS-00045 + SRG-OS-000077-VMM-000440 + Preventing re-use of previous passwords helps ensure that a compromised password is not re-used by a user. + + CCE-83584-3 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83584-3 + - CJIS-5.6.2.1.1 + - NIST-800-171-3.5.8 + - NIST-800-53-IA-5(1)(e) + - NIST-800-53-IA-5(f) + - PCI-DSS-Req-8.2.5 + - accounts_password_pam_unix_remember + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed +- name: XCCDF Value var_password_pam_unix_remember # promote to variable + set_fact: + var_password_pam_unix_remember: !!str + tags: + - always + +- name: Limit Password Reuse - Check if /etc/pam.d/system-auth file is present + ansible.builtin.stat: + path: /etc/pam.d/system-auth + register: result_pam_file_present + when: '"pam" in ansible_facts.packages' + tags: + - CCE-83584-3 + - CJIS-5.6.2.1.1 + - NIST-800-171-3.5.8 + - NIST-800-53-IA-5(1)(e) + - NIST-800-53-IA-5(f) + - PCI-DSS-Req-8.2.5 + - accounts_password_pam_unix_remember + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + +- name: Limit Password Reuse - Check the proper remediation for the system + block: + + - name: Limit Password Reuse - Define the PAM file to be edited as a local fact + ansible.builtin.set_fact: + pam_file_path: /etc/pam.d/system-auth + + - name: Limit Password Reuse - Check if system relies on authselect + ansible.builtin.stat: + path: /usr/bin/authselect + register: result_authselect_present + + - name: Limit Password Reuse - Remediate using authselect + block: + + - name: Limit Password Reuse - Check integrity of authselect current profile + ansible.builtin.command: + cmd: authselect check + register: result_authselect_check_cmd + changed_when: false + ignore_errors: true + + - name: Limit Password Reuse - Informative message based on the authselect integrity + check result + ansible.builtin.assert: + that: + - result_authselect_check_cmd is success + fail_msg: + - authselect integrity check failed. Remediation aborted! + - This remediation could not be applied because an authselect profile was + not selected or the selected profile is not intact. + - It is not recommended to manually edit the PAM files when authselect tool + is available. + - In cases where the default authselect profile does not cover a specific + demand, a custom authselect profile is recommended. + success_msg: + - authselect integrity check passed + + - name: Limit Password Reuse - Get authselect current profile + ansible.builtin.shell: + cmd: authselect current -r | awk '{ print $1 }' + register: result_authselect_profile + changed_when: false + when: + - result_authselect_check_cmd is success + + - name: Limit Password Reuse - Define the current authselect profile as a local + fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: '{{ result_authselect_profile.stdout }}' + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is match("custom/") + + - name: Limit Password Reuse - Define the new authselect custom profile as a local + fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: custom/hardening + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is not match("custom/") + + - name: Limit Password Reuse - Get authselect current features to also enable + them in the custom profile + ansible.builtin.shell: + cmd: authselect current | tail -n+3 | awk '{ print $2 }' + register: result_authselect_features + changed_when: false + when: + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + + - name: Limit Password Reuse - Check if any custom profile with the same name + was already created + ansible.builtin.stat: + path: /etc/authselect/{{ authselect_custom_profile }} + register: result_authselect_custom_profile_present + changed_when: false + when: + - authselect_current_profile is not match("custom/") + + - name: Limit Password Reuse - Create an authselect custom profile based on the + current profile + ansible.builtin.command: + cmd: authselect create-profile hardening -b {{ authselect_current_profile + }} + when: + - result_authselect_check_cmd is success + - authselect_current_profile is not match("custom/") + - not result_authselect_custom_profile_present.stat.exists + + - name: Limit Password Reuse - Ensure authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=before-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Limit Password Reuse - Ensure the authselect custom profile is selected + ansible.builtin.command: + cmd: authselect select {{ authselect_custom_profile }} + register: result_pam_authselect_select_profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Limit Password Reuse - Restore the authselect features in the custom profile + ansible.builtin.command: + cmd: authselect enable-feature {{ item }} + loop: '{{ result_authselect_features.stdout_lines }}' + register: result_pam_authselect_restore_features + when: + - result_authselect_profile is not skipped + - result_authselect_features is not skipped + - result_pam_authselect_select_profile is not skipped + + - name: Limit Password Reuse - Ensure authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=after-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - result_pam_authselect_restore_features is not skipped + + - name: Limit Password Reuse - Change the PAM file to be edited according to the + custom authselect profile + ansible.builtin.set_fact: + pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path + | basename }} + when: + - result_authselect_present.stat.exists + + - name: Limit Password Reuse - Check if expected PAM module line is present in {{ + pam_file_path }} + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + regexp: ^\s*password\s+requisite\s+pam_pwhistory.so\s*.* + state: absent + check_mode: true + changed_when: false + register: result_pam_line_present + + - name: Limit Password Reuse - Include or update the PAM module line in {{ pam_file_path + }} + block: + + - name: Limit Password Reuse - Check if required PAM module line is present in + {{ pam_file_path }} with different control + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + regexp: ^\s*password\s+.*\s+pam_pwhistory.so\s* + state: absent + check_mode: true + changed_when: false + register: result_pam_line_other_control_present + + - name: Limit Password Reuse - Ensure the correct control for the required PAM + module line in {{ pam_file_path }} + ansible.builtin.replace: + dest: '{{ pam_file_path }}' + regexp: ^(\s*password\s+).*(\bpam_pwhistory.so.*) + replace: \1requisite \2 + register: result_pam_module_edit + when: + - result_pam_line_other_control_present.found == 1 + + - name: Limit Password Reuse - Ensure the required PAM module line is included + in {{ pam_file_path }} + ansible.builtin.lineinfile: + dest: '{{ pam_file_path }}' + insertafter: ^password.*requisite.*pam_pwquality\.so + line: password requisite pam_pwhistory.so + register: result_pam_module_add + when: + - result_pam_line_other_control_present.found == 0 or result_pam_line_other_control_present.found + > 1 + + - name: Limit Password Reuse - Ensure authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: | + result_authselect_present is defined and result_authselect_present.stat.exists and ((result_pam_module_add is defined and result_pam_module_add.changed) or (result_pam_module_edit is defined and result_pam_module_edit.changed)) + when: + - result_pam_line_present.found is defined + - result_pam_line_present.found == 0 + + - name: Limit Password Reuse - Check if the required PAM module option is present + in {{ pam_file_path }} + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + regexp: ^\s*password\s+requisite\s+pam_pwhistory.so\s*.*\sremember\b + state: absent + check_mode: true + changed_when: false + register: result_pam_module_remember_option_present + + - name: Limit Password Reuse - Ensure the "remember" PAM option for "pam_pwhistory.so" + is included in {{ pam_file_path }} + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + backrefs: true + regexp: ^(\s*password\s+requisite\s+pam_pwhistory.so.*) + line: \1 remember={{ var_password_pam_unix_remember }} + state: present + register: result_pam_remember_add + when: + - result_pam_module_remember_option_present.found == 0 + + - name: Limit Password Reuse - Ensure the required value for "remember" PAM option + from "pam_pwhistory.so" in {{ pam_file_path }} + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + backrefs: true + regexp: ^(\s*password\s+requisite\s+pam_pwhistory.so\s+.*)(remember)=[0-9a-zA-Z]+\s*(.*) + line: \1\2={{ var_password_pam_unix_remember }} \3 + register: result_pam_remember_edit + when: + - result_pam_module_remember_option_present.found > 0 + + - name: Limit Password Reuse - Ensure authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: + - result_authselect_present.stat.exists + - (result_pam_remember_add is defined and result_pam_remember_add.changed) or + (result_pam_remember_edit is defined and result_pam_remember_edit.changed) + when: + - '"pam" in ansible_facts.packages' + - result_pam_file_present.stat.exists + tags: + - CCE-83584-3 + - CJIS-5.6.2.1.1 + - NIST-800-171-3.5.8 + - NIST-800-53-IA-5(1)(e) + - NIST-800-53-IA-5(f) + - PCI-DSS-Req-8.2.5 + - accounts_password_pam_unix_remember + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + +- name: Limit Password Reuse - Check if /etc/pam.d/password-auth file is present + ansible.builtin.stat: + path: /etc/pam.d/password-auth + register: result_pam_file_present + when: '"pam" in ansible_facts.packages' + tags: + - CCE-83584-3 + - CJIS-5.6.2.1.1 + - NIST-800-171-3.5.8 + - NIST-800-53-IA-5(1)(e) + - NIST-800-53-IA-5(f) + - PCI-DSS-Req-8.2.5 + - accounts_password_pam_unix_remember + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + +- name: Limit Password Reuse - Check the proper remediation for the system + block: + + - name: Limit Password Reuse - Define the PAM file to be edited as a local fact + ansible.builtin.set_fact: + pam_file_path: /etc/pam.d/password-auth + + - name: Limit Password Reuse - Check if system relies on authselect + ansible.builtin.stat: + path: /usr/bin/authselect + register: result_authselect_present + + - name: Limit Password Reuse - Remediate using authselect + block: + + - name: Limit Password Reuse - Check integrity of authselect current profile + ansible.builtin.command: + cmd: authselect check + register: result_authselect_check_cmd + changed_when: false + ignore_errors: true + + - name: Limit Password Reuse - Informative message based on the authselect integrity + check result + ansible.builtin.assert: + that: + - result_authselect_check_cmd is success + fail_msg: + - authselect integrity check failed. Remediation aborted! + - This remediation could not be applied because an authselect profile was + not selected or the selected profile is not intact. + - It is not recommended to manually edit the PAM files when authselect tool + is available. + - In cases where the default authselect profile does not cover a specific + demand, a custom authselect profile is recommended. + success_msg: + - authselect integrity check passed + + - name: Limit Password Reuse - Get authselect current profile + ansible.builtin.shell: + cmd: authselect current -r | awk '{ print $1 }' + register: result_authselect_profile + changed_when: false + when: + - result_authselect_check_cmd is success + + - name: Limit Password Reuse - Define the current authselect profile as a local + fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: '{{ result_authselect_profile.stdout }}' + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is match("custom/") + + - name: Limit Password Reuse - Define the new authselect custom profile as a local + fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: custom/hardening + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is not match("custom/") + + - name: Limit Password Reuse - Get authselect current features to also enable + them in the custom profile + ansible.builtin.shell: + cmd: authselect current | tail -n+3 | awk '{ print $2 }' + register: result_authselect_features + changed_when: false + when: + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + + - name: Limit Password Reuse - Check if any custom profile with the same name + was already created + ansible.builtin.stat: + path: /etc/authselect/{{ authselect_custom_profile }} + register: result_authselect_custom_profile_present + changed_when: false + when: + - authselect_current_profile is not match("custom/") + + - name: Limit Password Reuse - Create an authselect custom profile based on the + current profile + ansible.builtin.command: + cmd: authselect create-profile hardening -b {{ authselect_current_profile + }} + when: + - result_authselect_check_cmd is success + - authselect_current_profile is not match("custom/") + - not result_authselect_custom_profile_present.stat.exists + + - name: Limit Password Reuse - Ensure authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=before-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Limit Password Reuse - Ensure the authselect custom profile is selected + ansible.builtin.command: + cmd: authselect select {{ authselect_custom_profile }} + register: result_pam_authselect_select_profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Limit Password Reuse - Restore the authselect features in the custom profile + ansible.builtin.command: + cmd: authselect enable-feature {{ item }} + loop: '{{ result_authselect_features.stdout_lines }}' + register: result_pam_authselect_restore_features + when: + - result_authselect_profile is not skipped + - result_authselect_features is not skipped + - result_pam_authselect_select_profile is not skipped + + - name: Limit Password Reuse - Ensure authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=after-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - result_pam_authselect_restore_features is not skipped + + - name: Limit Password Reuse - Change the PAM file to be edited according to the + custom authselect profile + ansible.builtin.set_fact: + pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path + | basename }} + when: + - result_authselect_present.stat.exists + + - name: Limit Password Reuse - Check if expected PAM module line is present in {{ + pam_file_path }} + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + regexp: ^\s*password\s+requisite\s+pam_pwhistory.so\s*.* + state: absent + check_mode: true + changed_when: false + register: result_pam_line_present + + - name: Limit Password Reuse - Include or update the PAM module line in {{ pam_file_path + }} + block: + + - name: Limit Password Reuse - Check if required PAM module line is present in + {{ pam_file_path }} with different control + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + regexp: ^\s*password\s+.*\s+pam_pwhistory.so\s* + state: absent + check_mode: true + changed_when: false + register: result_pam_line_other_control_present + + - name: Limit Password Reuse - Ensure the correct control for the required PAM + module line in {{ pam_file_path }} + ansible.builtin.replace: + dest: '{{ pam_file_path }}' + regexp: ^(\s*password\s+).*(\bpam_pwhistory.so.*) + replace: \1requisite \2 + register: result_pam_module_edit + when: + - result_pam_line_other_control_present.found == 1 + + - name: Limit Password Reuse - Ensure the required PAM module line is included + in {{ pam_file_path }} + ansible.builtin.lineinfile: + dest: '{{ pam_file_path }}' + insertafter: ^password.*requisite.*pam_pwquality\.so + line: password requisite pam_pwhistory.so + register: result_pam_module_add + when: + - result_pam_line_other_control_present.found == 0 or result_pam_line_other_control_present.found + > 1 + + - name: Limit Password Reuse - Ensure authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: | + result_authselect_present is defined and result_authselect_present.stat.exists and ((result_pam_module_add is defined and result_pam_module_add.changed) or (result_pam_module_edit is defined and result_pam_module_edit.changed)) + when: + - result_pam_line_present.found is defined + - result_pam_line_present.found == 0 + + - name: Limit Password Reuse - Check if the required PAM module option is present + in {{ pam_file_path }} + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + regexp: ^\s*password\s+requisite\s+pam_pwhistory.so\s*.*\sremember\b + state: absent + check_mode: true + changed_when: false + register: result_pam_module_remember_option_present + + - name: Limit Password Reuse - Ensure the "remember" PAM option for "pam_pwhistory.so" + is included in {{ pam_file_path }} + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + backrefs: true + regexp: ^(\s*password\s+requisite\s+pam_pwhistory.so.*) + line: \1 remember={{ var_password_pam_unix_remember }} + state: present + register: result_pam_remember_add + when: + - result_pam_module_remember_option_present.found == 0 + + - name: Limit Password Reuse - Ensure the required value for "remember" PAM option + from "pam_pwhistory.so" in {{ pam_file_path }} + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + backrefs: true + regexp: ^(\s*password\s+requisite\s+pam_pwhistory.so\s+.*)(remember)=[0-9a-zA-Z]+\s*(.*) + line: \1\2={{ var_password_pam_unix_remember }} \3 + register: result_pam_remember_edit + when: + - result_pam_module_remember_option_present.found > 0 + + - name: Limit Password Reuse - Ensure authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: + - result_authselect_present.stat.exists + - (result_pam_remember_add is defined and result_pam_remember_add.changed) or + (result_pam_remember_edit is defined and result_pam_remember_edit.changed) + when: + - '"pam" in ansible_facts.packages' + - result_pam_file_present.stat.exists + tags: + - CCE-83584-3 + - CJIS-5.6.2.1.1 + - NIST-800-171-3.5.8 + - NIST-800-53-IA-5(1)(e) + - NIST-800-53-IA-5(f) + - PCI-DSS-Req-8.2.5 + - accounts_password_pam_unix_remember + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if rpm --quiet -q pam; then + +var_password_pam_unix_remember='' + + + +if [ -e "/etc/pam.d/system-auth" ] ; then + PAM_FILE_PATH="/etc/pam.d/system-auth" + if [ -f /usr/bin/authselect ]; then + if ! authselect check; then + echo " + authselect integrity check failed. Remediation aborted! + This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact. + It is not recommended to manually edit the PAM files when authselect tool is available. + In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended." + exit 1 + fi + CURRENT_PROFILE=$(authselect current -r | awk '{ print $1 }') + # If not already in use, a custom profile is created preserving the enabled features. + if [[ ! $CURRENT_PROFILE == custom/* ]]; then + ENABLED_FEATURES=$(authselect current | tail -n+3 | awk '{ print $2 }') + authselect create-profile hardening -b $CURRENT_PROFILE + CURRENT_PROFILE="custom/hardening" + + authselect apply-changes -b --backup=before-hardening-custom-profile + authselect select $CURRENT_PROFILE + for feature in $ENABLED_FEATURES; do + authselect enable-feature $feature; + done + + authselect apply-changes -b --backup=after-hardening-custom-profile + fi + PAM_FILE_NAME=$(basename "/etc/pam.d/system-auth") + PAM_FILE_PATH="/etc/authselect/$CURRENT_PROFILE/$PAM_FILE_NAME" + + authselect apply-changes -b + fi + if ! grep -qP '^\s*password\s+'"requisite"'\s+pam_pwhistory.so\s*.*' "$PAM_FILE_PATH"; then + # Line matching group + control + module was not found. Check group + module. + if [ "$(grep -cP '^\s*password\s+.*\s+pam_pwhistory.so\s*' "$PAM_FILE_PATH")" -eq 1 ]; then + # The control is updated only if one single line matches. + sed -i -E --follow-symlinks 's/^(\s*password\s+).*(\bpam_pwhistory.so.*)/\1'"requisite"' \2/' "$PAM_FILE_PATH" + else + LAST_MATCH_LINE=$(grep -nP "^password.*requisite.*pam_pwquality\.so" "$PAM_FILE_PATH" | tail -n 1 | cut -d: -f 1) + if [ ! -z $LAST_MATCH_LINE ]; then + sed -i --follow-symlinks $LAST_MATCH_LINE' a password '"requisite"' pam_pwhistory.so' "$PAM_FILE_PATH" + else + echo 'password '"requisite"' pam_pwhistory.so' >> "$PAM_FILE_PATH" + fi + fi + fi + # Check the option + if ! grep -qP '^\s*password\s+'"requisite"'\s+pam_pwhistory.so\s*.*\sremember\b' "$PAM_FILE_PATH"; then + sed -i -E --follow-symlinks '/\s*password\s+'"requisite"'\s+pam_pwhistory.so.*/ s/$/ remember='"$var_password_pam_unix_remember"'/' "$PAM_FILE_PATH" + else + sed -i -E --follow-symlinks 's/(\s*password\s+'"requisite"'\s+pam_pwhistory.so\s+.*)('"remember"'=)[[:alnum:]]+\s*(.*)/\1\2'"$var_password_pam_unix_remember"' \3/' "$PAM_FILE_PATH" + fi + if [ -f /usr/bin/authselect ]; then + + authselect apply-changes -b + fi +else + echo "/etc/pam.d/system-auth was not found" >&2 +fi + +if [ -e "/etc/pam.d/password-auth" ] ; then + PAM_FILE_PATH="/etc/pam.d/password-auth" + if [ -f /usr/bin/authselect ]; then + if ! authselect check; then + echo " + authselect integrity check failed. Remediation aborted! + This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact. + It is not recommended to manually edit the PAM files when authselect tool is available. + In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended." + exit 1 + fi + CURRENT_PROFILE=$(authselect current -r | awk '{ print $1 }') + # If not already in use, a custom profile is created preserving the enabled features. + if [[ ! $CURRENT_PROFILE == custom/* ]]; then + ENABLED_FEATURES=$(authselect current | tail -n+3 | awk '{ print $2 }') + authselect create-profile hardening -b $CURRENT_PROFILE + CURRENT_PROFILE="custom/hardening" + + authselect apply-changes -b --backup=before-hardening-custom-profile + authselect select $CURRENT_PROFILE + for feature in $ENABLED_FEATURES; do + authselect enable-feature $feature; + done + + authselect apply-changes -b --backup=after-hardening-custom-profile + fi + PAM_FILE_NAME=$(basename "/etc/pam.d/password-auth") + PAM_FILE_PATH="/etc/authselect/$CURRENT_PROFILE/$PAM_FILE_NAME" + + authselect apply-changes -b + fi + if ! grep -qP '^\s*password\s+'"requisite"'\s+pam_pwhistory.so\s*.*' "$PAM_FILE_PATH"; then + # Line matching group + control + module was not found. Check group + module. + if [ "$(grep -cP '^\s*password\s+.*\s+pam_pwhistory.so\s*' "$PAM_FILE_PATH")" -eq 1 ]; then + # The control is updated only if one single line matches. + sed -i -E --follow-symlinks 's/^(\s*password\s+).*(\bpam_pwhistory.so.*)/\1'"requisite"' \2/' "$PAM_FILE_PATH" + else + LAST_MATCH_LINE=$(grep -nP "^password.*requisite.*pam_pwquality\.so" "$PAM_FILE_PATH" | tail -n 1 | cut -d: -f 1) + if [ ! -z $LAST_MATCH_LINE ]; then + sed -i --follow-symlinks $LAST_MATCH_LINE' a password '"requisite"' pam_pwhistory.so' "$PAM_FILE_PATH" + else + echo 'password '"requisite"' pam_pwhistory.so' >> "$PAM_FILE_PATH" + fi + fi + fi + # Check the option + if ! grep -qP '^\s*password\s+'"requisite"'\s+pam_pwhistory.so\s*.*\sremember\b' "$PAM_FILE_PATH"; then + sed -i -E --follow-symlinks '/\s*password\s+'"requisite"'\s+pam_pwhistory.so.*/ s/$/ remember='"$var_password_pam_unix_remember"'/' "$PAM_FILE_PATH" + else + sed -i -E --follow-symlinks 's/(\s*password\s+'"requisite"'\s+pam_pwhistory.so\s+.*)('"remember"'=)[[:alnum:]]+\s*(.*)/\1\2'"$var_password_pam_unix_remember"' \3/' "$PAM_FILE_PATH" + fi + if [ -f /usr/bin/authselect ]; then + + authselect apply-changes -b + fi +else + echo "/etc/pam.d/password-auth was not found" >&2 +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Lock Accounts After Failed Password Attempts + This rule configures the system to lock out accounts after a number of incorrect login attempts +using pam_faillock.so. + +pam_faillock.so module requires multiple entries in pam files. These entries must be carefully +defined to work as expected. In order to avoid errors when manually editing these files, it is +recommended to use the appropriate tools, such as authselect or authconfig, +depending on the OS version. + If the system relies on authselect tool to manage PAM settings, the remediation +will also use authselect tool. However, if any manual modification was made in +PAM files, the authselect integrity check will fail and the remediation will be +aborted in order to preserve intentional changes. In this case, an informative message will +be shown in the remediation report. +If the system supports the /etc/security/faillock.conf file, the pam_faillock +parameters should be defined in faillock.conf file. + BP28(R18) + 1 + 12 + 15 + 16 + 5.5.3 + DSS05.04 + DSS05.10 + DSS06.10 + 3.1.8 + CCI-000044 + CCI-002236 + CCI-002237 + CCI-002238 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + 0421 + 0422 + 0431 + 0974 + 1173 + 1401 + 1504 + 1505 + 1546 + 1557 + 1558 + 1559 + 1560 + 1561 + A.18.1.4 + A.9.2.1 + A.9.2.4 + A.9.3.1 + A.9.4.2 + A.9.4.3 + CM-6(a) + AC-7(a) + PR.AC-7 + FIA_AFL.1 + Req-8.1.6 + SRG-OS-000329-GPOS-00128 + SRG-OS-000021-GPOS-00005 + SRG-OS-000021-VMM-000050 + By limiting the number of failed logon attempts, the risk of unauthorized system access via +user password guessing, also known as brute-forcing, is reduced. Limits are imposed by locking +the account. + + CCE-83587-6 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83587-6 + - CJIS-5.5.3 + - NIST-800-171-3.1.8 + - NIST-800-53-AC-7(a) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.1.6 + - accounts_passwords_pam_faillock_deny + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Lock Accounts After Failed Password Attempts - Check if system relies on authselect + tool + ansible.builtin.stat: + path: /usr/bin/authselect + register: result_authselect_present + when: '"pam" in ansible_facts.packages' + tags: + - CCE-83587-6 + - CJIS-5.5.3 + - NIST-800-171-3.1.8 + - NIST-800-53-AC-7(a) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.1.6 + - accounts_passwords_pam_faillock_deny + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Lock Accounts After Failed Password Attempts - Remediation where authselect + tool is present + block: + + - name: Lock Accounts After Failed Password Attempts - Check integrity of authselect + current profile + ansible.builtin.command: + cmd: authselect check + register: result_authselect_check_cmd + changed_when: false + ignore_errors: true + + - name: Lock Accounts After Failed Password Attempts - Informative message based + on the authselect integrity check result + ansible.builtin.assert: + that: + - result_authselect_check_cmd is success + fail_msg: + - authselect integrity check failed. Remediation aborted! + - This remediation could not be applied because an authselect profile was not + selected or the selected profile is not intact. + - It is not recommended to manually edit the PAM files when authselect tool + is available. + - In cases where the default authselect profile does not cover a specific demand, + a custom authselect profile is recommended. + success_msg: + - authselect integrity check passed + + - name: Lock Accounts After Failed Password Attempts - Get authselect current features + ansible.builtin.shell: + cmd: authselect current | tail -n+3 | awk '{ print $2 }' + register: result_authselect_features + changed_when: false + when: + - result_authselect_check_cmd is success + + - name: Lock Accounts After Failed Password Attempts - Ensure "with-faillock" feature + is enabled using authselect tool + ansible.builtin.command: + cmd: authselect enable-feature with-faillock + register: result_authselect_enable_feature_cmd + when: + - result_authselect_check_cmd is success + - result_authselect_features.stdout is not search("with-faillock") + + - name: Lock Accounts After Failed Password Attempts - Ensure authselect changes + are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: + - result_authselect_enable_feature_cmd is not skipped + - result_authselect_enable_feature_cmd is success + when: + - '"pam" in ansible_facts.packages' + - result_authselect_present.stat.exists + tags: + - CCE-83587-6 + - CJIS-5.5.3 + - NIST-800-171-3.1.8 + - NIST-800-53-AC-7(a) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.1.6 + - accounts_passwords_pam_faillock_deny + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Lock Accounts After Failed Password Attempts - Remediation where authselect + tool is not present + block: + + - name: Lock Accounts After Failed Password Attempts - Check if pam_faillock.so + is already enabled + ansible.builtin.lineinfile: + path: /etc/pam.d/system-auth + regexp: .*auth.*pam_faillock\.so (preauth|authfail) + state: absent + check_mode: true + changed_when: false + register: result_pam_faillock_is_enabled + + - name: Lock Accounts After Failed Password Attempts - Enable pam_faillock.so preauth + editing PAM files + ansible.builtin.lineinfile: + path: '{{ item }}' + line: auth required pam_faillock.so preauth + insertbefore: ^auth.*sufficient.*pam_unix\.so.* + state: present + loop: + - /etc/pam.d/system-auth + - /etc/pam.d/password-auth + when: + - result_pam_faillock_is_enabled.found == 0 + + - name: Lock Accounts After Failed Password Attempts - Enable pam_faillock.so authfail + editing PAM files + ansible.builtin.lineinfile: + path: '{{ item }}' + line: auth required pam_faillock.so authfail + insertbefore: ^auth.*required.*pam_deny\.so.* + state: present + loop: + - /etc/pam.d/system-auth + - /etc/pam.d/password-auth + when: + - result_pam_faillock_is_enabled.found == 0 + + - name: Lock Accounts After Failed Password Attempts - Enable pam_faillock.so account + section editing PAM files + ansible.builtin.lineinfile: + path: '{{ item }}' + line: account required pam_faillock.so + insertbefore: ^account.*required.*pam_unix\.so.* + state: present + loop: + - /etc/pam.d/system-auth + - /etc/pam.d/password-auth + when: + - result_pam_faillock_is_enabled.found == 0 + when: + - '"pam" in ansible_facts.packages' + - not result_authselect_present.stat.exists + tags: + - CCE-83587-6 + - CJIS-5.5.3 + - NIST-800-171-3.1.8 + - NIST-800-53-AC-7(a) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.1.6 + - accounts_passwords_pam_faillock_deny + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy +- name: XCCDF Value var_accounts_passwords_pam_faillock_deny # promote to variable + set_fact: + var_accounts_passwords_pam_faillock_deny: !!str + tags: + - always + +- name: Lock Accounts After Failed Password Attempts - Check the presence of /etc/security/faillock.conf + file + ansible.builtin.stat: + path: /etc/security/faillock.conf + register: result_faillock_conf_check + when: '"pam" in ansible_facts.packages' + tags: + - CCE-83587-6 + - CJIS-5.5.3 + - NIST-800-171-3.1.8 + - NIST-800-53-AC-7(a) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.1.6 + - accounts_passwords_pam_faillock_deny + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Lock Accounts After Failed Password Attempts - Ensure the pam_faillock.so + deny parameter in /etc/security/faillock.conf + ansible.builtin.lineinfile: + path: /etc/security/faillock.conf + regexp: ^\s*deny\s*= + line: deny = {{ var_accounts_passwords_pam_faillock_deny }} + state: present + when: + - '"pam" in ansible_facts.packages' + - result_faillock_conf_check.stat.exists + tags: + - CCE-83587-6 + - CJIS-5.5.3 + - NIST-800-171-3.1.8 + - NIST-800-53-AC-7(a) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.1.6 + - accounts_passwords_pam_faillock_deny + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Lock Accounts After Failed Password Attempts - Ensure the pam_faillock.so + deny parameter not in PAM files + block: + + - name: Lock Accounts After Failed Password Attempts - Check if /etc/pam.d/system-auth + file is present + ansible.builtin.stat: + path: /etc/pam.d/system-auth + register: result_pam_file_present + + - name: Lock Accounts After Failed Password Attempts - Check the proper remediation + for the system + block: + + - name: Lock Accounts After Failed Password Attempts - Define the PAM file to + be edited as a local fact + ansible.builtin.set_fact: + pam_file_path: /etc/pam.d/system-auth + + - name: Lock Accounts After Failed Password Attempts - Check if system relies + on authselect + ansible.builtin.stat: + path: /usr/bin/authselect + register: result_authselect_present + + - name: Lock Accounts After Failed Password Attempts - Remediate using authselect + block: + + - name: Lock Accounts After Failed Password Attempts - Check integrity of authselect + current profile + ansible.builtin.command: + cmd: authselect check + register: result_authselect_check_cmd + changed_when: false + ignore_errors: true + + - name: Lock Accounts After Failed Password Attempts - Informative message based + on the authselect integrity check result + ansible.builtin.assert: + that: + - result_authselect_check_cmd is success + fail_msg: + - authselect integrity check failed. Remediation aborted! + - This remediation could not be applied because an authselect profile was + not selected or the selected profile is not intact. + - It is not recommended to manually edit the PAM files when authselect tool + is available. + - In cases where the default authselect profile does not cover a specific + demand, a custom authselect profile is recommended. + success_msg: + - authselect integrity check passed + + - name: Lock Accounts After Failed Password Attempts - Get authselect current + profile + ansible.builtin.shell: + cmd: authselect current -r | awk '{ print $1 }' + register: result_authselect_profile + changed_when: false + when: + - result_authselect_check_cmd is success + + - name: Lock Accounts After Failed Password Attempts - Define the current authselect + profile as a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: '{{ result_authselect_profile.stdout }}' + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is match("custom/") + + - name: Lock Accounts After Failed Password Attempts - Define the new authselect + custom profile as a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: custom/hardening + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is not match("custom/") + + - name: Lock Accounts After Failed Password Attempts - Get authselect current + features to also enable them in the custom profile + ansible.builtin.shell: + cmd: authselect current | tail -n+3 | awk '{ print $2 }' + register: result_authselect_features + changed_when: false + when: + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + + - name: Lock Accounts After Failed Password Attempts - Check if any custom profile + with the same name was already created + ansible.builtin.stat: + path: /etc/authselect/{{ authselect_custom_profile }} + register: result_authselect_custom_profile_present + changed_when: false + when: + - authselect_current_profile is not match("custom/") + + - name: Lock Accounts After Failed Password Attempts - Create an authselect + custom profile based on the current profile + ansible.builtin.command: + cmd: authselect create-profile hardening -b {{ authselect_current_profile + }} + when: + - result_authselect_check_cmd is success + - authselect_current_profile is not match("custom/") + - not result_authselect_custom_profile_present.stat.exists + + - name: Lock Accounts After Failed Password Attempts - Ensure authselect changes + are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=before-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Lock Accounts After Failed Password Attempts - Ensure the authselect + custom profile is selected + ansible.builtin.command: + cmd: authselect select {{ authselect_custom_profile }} + register: result_pam_authselect_select_profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Lock Accounts After Failed Password Attempts - Restore the authselect + features in the custom profile + ansible.builtin.command: + cmd: authselect enable-feature {{ item }} + loop: '{{ result_authselect_features.stdout_lines }}' + register: result_pam_authselect_restore_features + when: + - result_authselect_profile is not skipped + - result_authselect_features is not skipped + - result_pam_authselect_select_profile is not skipped + + - name: Lock Accounts After Failed Password Attempts - Ensure authselect changes + are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=after-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - result_pam_authselect_restore_features is not skipped + + - name: Lock Accounts After Failed Password Attempts - Change the PAM file to + be edited according to the custom authselect profile + ansible.builtin.set_fact: + pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path + | basename }} + when: + - result_authselect_present.stat.exists + + - name: Lock Accounts After Failed Password Attempts - Ensure the "deny" option + from "pam_faillock.so" is not present in {{ pam_file_path }} + ansible.builtin.replace: + dest: '{{ pam_file_path }}' + regexp: (.*auth.*pam_faillock.so.*)\bdeny\b=?[0-9a-zA-Z]*(.*) + replace: \1\2 + register: result_pam_option_removal + + - name: Lock Accounts After Failed Password Attempts - Ensure authselect changes + are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: + - result_authselect_present.stat.exists + - result_pam_option_removal is changed + when: + - result_pam_file_present.stat.exists + + - name: Lock Accounts After Failed Password Attempts - Check if /etc/pam.d/password-auth + file is present + ansible.builtin.stat: + path: /etc/pam.d/password-auth + register: result_pam_file_present + + - name: Lock Accounts After Failed Password Attempts - Check the proper remediation + for the system + block: + + - name: Lock Accounts After Failed Password Attempts - Define the PAM file to + be edited as a local fact + ansible.builtin.set_fact: + pam_file_path: /etc/pam.d/password-auth + + - name: Lock Accounts After Failed Password Attempts - Check if system relies + on authselect + ansible.builtin.stat: + path: /usr/bin/authselect + register: result_authselect_present + + - name: Lock Accounts After Failed Password Attempts - Remediate using authselect + block: + + - name: Lock Accounts After Failed Password Attempts - Check integrity of authselect + current profile + ansible.builtin.command: + cmd: authselect check + register: result_authselect_check_cmd + changed_when: false + ignore_errors: true + + - name: Lock Accounts After Failed Password Attempts - Informative message based + on the authselect integrity check result + ansible.builtin.assert: + that: + - result_authselect_check_cmd is success + fail_msg: + - authselect integrity check failed. Remediation aborted! + - This remediation could not be applied because an authselect profile was + not selected or the selected profile is not intact. + - It is not recommended to manually edit the PAM files when authselect tool + is available. + - In cases where the default authselect profile does not cover a specific + demand, a custom authselect profile is recommended. + success_msg: + - authselect integrity check passed + + - name: Lock Accounts After Failed Password Attempts - Get authselect current + profile + ansible.builtin.shell: + cmd: authselect current -r | awk '{ print $1 }' + register: result_authselect_profile + changed_when: false + when: + - result_authselect_check_cmd is success + + - name: Lock Accounts After Failed Password Attempts - Define the current authselect + profile as a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: '{{ result_authselect_profile.stdout }}' + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is match("custom/") + + - name: Lock Accounts After Failed Password Attempts - Define the new authselect + custom profile as a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: custom/hardening + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is not match("custom/") + + - name: Lock Accounts After Failed Password Attempts - Get authselect current + features to also enable them in the custom profile + ansible.builtin.shell: + cmd: authselect current | tail -n+3 | awk '{ print $2 }' + register: result_authselect_features + changed_when: false + when: + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + + - name: Lock Accounts After Failed Password Attempts - Check if any custom profile + with the same name was already created + ansible.builtin.stat: + path: /etc/authselect/{{ authselect_custom_profile }} + register: result_authselect_custom_profile_present + changed_when: false + when: + - authselect_current_profile is not match("custom/") + + - name: Lock Accounts After Failed Password Attempts - Create an authselect + custom profile based on the current profile + ansible.builtin.command: + cmd: authselect create-profile hardening -b {{ authselect_current_profile + }} + when: + - result_authselect_check_cmd is success + - authselect_current_profile is not match("custom/") + - not result_authselect_custom_profile_present.stat.exists + + - name: Lock Accounts After Failed Password Attempts - Ensure authselect changes + are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=before-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Lock Accounts After Failed Password Attempts - Ensure the authselect + custom profile is selected + ansible.builtin.command: + cmd: authselect select {{ authselect_custom_profile }} + register: result_pam_authselect_select_profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Lock Accounts After Failed Password Attempts - Restore the authselect + features in the custom profile + ansible.builtin.command: + cmd: authselect enable-feature {{ item }} + loop: '{{ result_authselect_features.stdout_lines }}' + register: result_pam_authselect_restore_features + when: + - result_authselect_profile is not skipped + - result_authselect_features is not skipped + - result_pam_authselect_select_profile is not skipped + + - name: Lock Accounts After Failed Password Attempts - Ensure authselect changes + are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=after-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - result_pam_authselect_restore_features is not skipped + + - name: Lock Accounts After Failed Password Attempts - Change the PAM file to + be edited according to the custom authselect profile + ansible.builtin.set_fact: + pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path + | basename }} + when: + - result_authselect_present.stat.exists + + - name: Lock Accounts After Failed Password Attempts - Ensure the "deny" option + from "pam_faillock.so" is not present in {{ pam_file_path }} + ansible.builtin.replace: + dest: '{{ pam_file_path }}' + regexp: (.*auth.*pam_faillock.so.*)\bdeny\b=?[0-9a-zA-Z]*(.*) + replace: \1\2 + register: result_pam_option_removal + + - name: Lock Accounts After Failed Password Attempts - Ensure authselect changes + are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: + - result_authselect_present.stat.exists + - result_pam_option_removal is changed + when: + - result_pam_file_present.stat.exists + when: + - '"pam" in ansible_facts.packages' + - result_faillock_conf_check.stat.exists + tags: + - CCE-83587-6 + - CJIS-5.5.3 + - NIST-800-171-3.1.8 + - NIST-800-53-AC-7(a) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.1.6 + - accounts_passwords_pam_faillock_deny + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Lock Accounts After Failed Password Attempts - Ensure the pam_faillock.so + deny parameter in PAM files + block: + + - name: Lock Accounts After Failed Password Attempts - Check if pam_faillock.so + deny parameter is already enabled in pam files + ansible.builtin.lineinfile: + path: /etc/pam.d/system-auth + regexp: .*auth.*pam_faillock\.so (preauth|authfail).*deny + state: absent + check_mode: true + changed_when: false + register: result_pam_faillock_deny_parameter_is_present + + - name: Lock Accounts After Failed Password Attempts - Ensure the inclusion of pam_faillock.so + preauth deny parameter in auth section + ansible.builtin.lineinfile: + path: '{{ item }}' + backrefs: true + regexp: (^\s*auth\s+)([\w\[].*\b)(\s+pam_faillock.so preauth.*) + line: \1required\3 deny={{ var_accounts_passwords_pam_faillock_deny }} + state: present + loop: + - /etc/pam.d/system-auth + - /etc/pam.d/password-auth + when: + - result_pam_faillock_deny_parameter_is_present.found == 0 + + - name: Lock Accounts After Failed Password Attempts - Ensure the inclusion of pam_faillock.so + authfail deny parameter in auth section + ansible.builtin.lineinfile: + path: '{{ item }}' + backrefs: true + regexp: (^\s*auth\s+)([\w\[].*\b)(\s+pam_faillock.so authfail.*) + line: \1required\3 deny={{ var_accounts_passwords_pam_faillock_deny }} + state: present + loop: + - /etc/pam.d/system-auth + - /etc/pam.d/password-auth + when: + - result_pam_faillock_deny_parameter_is_present.found == 0 + + - name: Lock Accounts After Failed Password Attempts - Ensure the desired value + for pam_faillock.so preauth deny parameter in auth section + ansible.builtin.lineinfile: + path: '{{ item }}' + backrefs: true + regexp: (^\s*auth\s+)([\w\[].*\b)(\s+pam_faillock.so preauth.*)(deny)=[0-9]+(.*) + line: \1required\3\4={{ var_accounts_passwords_pam_faillock_deny }}\5 + state: present + loop: + - /etc/pam.d/system-auth + - /etc/pam.d/password-auth + when: + - result_pam_faillock_deny_parameter_is_present.found > 0 + + - name: Lock Accounts After Failed Password Attempts - Ensure the desired value + for pam_faillock.so authfail deny parameter in auth section + ansible.builtin.lineinfile: + path: '{{ item }}' + backrefs: true + regexp: (^\s*auth\s+)([\w\[].*\b)(\s+pam_faillock.so authfail.*)(deny)=[0-9]+(.*) + line: \1required\3\4={{ var_accounts_passwords_pam_faillock_deny }}\5 + state: present + loop: + - /etc/pam.d/system-auth + - /etc/pam.d/password-auth + when: + - result_pam_faillock_deny_parameter_is_present.found > 0 + when: + - '"pam" in ansible_facts.packages' + - not result_faillock_conf_check.stat.exists + tags: + - CCE-83587-6 + - CJIS-5.5.3 + - NIST-800-171-3.1.8 + - NIST-800-53-AC-7(a) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.1.6 + - accounts_passwords_pam_faillock_deny + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q pam; then + +var_accounts_passwords_pam_faillock_deny='' + + +if [ -f /usr/bin/authselect ]; then + if ! authselect check; then +echo " +authselect integrity check failed. Remediation aborted! +This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact. +It is not recommended to manually edit the PAM files when authselect tool is available. +In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended." +exit 1 +fi +authselect enable-feature with-faillock + +authselect apply-changes -b +else + AUTH_FILES=("/etc/pam.d/system-auth" "/etc/pam.d/password-auth") +for pam_file in "${AUTH_FILES[@]}" +do + if ! grep -qE '^\s*auth\s+required\s+pam_faillock\.so\s+(preauth silent|authfail).*$' "$pam_file" ; then + sed -i --follow-symlinks '/^auth.*sufficient.*pam_unix\.so.*/i auth required pam_faillock.so preauth silent' "$pam_file" + sed -i --follow-symlinks '/^auth.*required.*pam_deny\.so.*/i auth required pam_faillock.so authfail' "$pam_file" + sed -i --follow-symlinks '/^account.*required.*pam_unix\.so.*/i account required pam_faillock.so' "$pam_file" + fi + sed -Ei 's/(auth.*)(\[default=die\])(.*pam_faillock\.so)/\1required \3/g' "$pam_file" +done +fi +AUTH_FILES=("/etc/pam.d/system-auth" "/etc/pam.d/password-auth") +FAILLOCK_CONF="/etc/security/faillock.conf" +if [ -f $FAILLOCK_CONF ]; then + regex="^\s*deny\s*=" + line="deny = $var_accounts_passwords_pam_faillock_deny" + if ! grep -q $regex $FAILLOCK_CONF; then + echo $line >> $FAILLOCK_CONF + else + sed -i --follow-symlinks 's|^\s*\(deny\s*=\s*\)\(\S\+\)|\1'"$var_accounts_passwords_pam_faillock_deny"'|g' $FAILLOCK_CONF + fi + for pam_file in "${AUTH_FILES[@]}" + do + if [ -e "$pam_file" ] ; then + PAM_FILE_PATH="$pam_file" + if [ -f /usr/bin/authselect ]; then + if ! authselect check; then + echo " + authselect integrity check failed. Remediation aborted! + This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact. + It is not recommended to manually edit the PAM files when authselect tool is available. + In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended." + exit 1 + fi + CURRENT_PROFILE=$(authselect current -r | awk '{ print $1 }') + # If not already in use, a custom profile is created preserving the enabled features. + if [[ ! $CURRENT_PROFILE == custom/* ]]; then + ENABLED_FEATURES=$(authselect current | tail -n+3 | awk '{ print $2 }') + authselect create-profile hardening -b $CURRENT_PROFILE + CURRENT_PROFILE="custom/hardening" + + authselect apply-changes -b --backup=before-hardening-custom-profile + authselect select $CURRENT_PROFILE + for feature in $ENABLED_FEATURES; do + authselect enable-feature $feature; + done + + authselect apply-changes -b --backup=after-hardening-custom-profile + fi + PAM_FILE_NAME=$(basename "$pam_file") + PAM_FILE_PATH="/etc/authselect/$CURRENT_PROFILE/$PAM_FILE_NAME" + + authselect apply-changes -b + fi + + if grep -qP '^\s*auth\s.*\bpam_faillock.so\s.*\bdeny\b' "$PAM_FILE_PATH"; then + sed -i -E --follow-symlinks 's/(.*auth.*pam_faillock.so.*)\bdeny\b=?[[:alnum:]]*(.*)/\1\2/g' "$PAM_FILE_PATH" + fi + if [ -f /usr/bin/authselect ]; then + + authselect apply-changes -b + fi + else + echo "$pam_file was not found" >&2 + fi + done +else + for pam_file in "${AUTH_FILES[@]}" + do + if ! grep -qE '^\s*auth.*pam_faillock\.so (preauth|authfail).*deny' "$pam_file"; then + sed -i --follow-symlinks '/^auth.*required.*pam_faillock\.so.*preauth.*silent.*/ s/$/ deny='"$var_accounts_passwords_pam_faillock_deny"'/' "$pam_file" + sed -i --follow-symlinks '/^auth.*required.*pam_faillock\.so.*authfail.*/ s/$/ deny='"$var_accounts_passwords_pam_faillock_deny"'/' "$pam_file" + else + sed -i --follow-symlinks 's/\(^auth.*required.*pam_faillock\.so.*preauth.*silent.*\)\('"deny"'=\)[0-9]\+\(.*\)/\1\2'"$var_accounts_passwords_pam_faillock_deny"'\3/' "$pam_file" + sed -i --follow-symlinks 's/\(^auth.*required.*pam_faillock\.so.*authfail.*\)\('"deny"'=\)[0-9]\+\(.*\)/\1\2'"$var_accounts_passwords_pam_faillock_deny"'\3/' "$pam_file" + fi + done +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Configure the root Account for Failed Password Attempts + This rule configures the system to lock out the root account after a number of +incorrect login attempts using pam_faillock.so. + +pam_faillock.so module requires multiple entries in pam files. These entries must be carefully +defined to work as expected. In order to avoid errors when manually editing these files, it is +recommended to use the appropriate tools, such as authselect or authconfig, +depending on the OS version. + If the system relies on authselect tool to manage PAM settings, the remediation +will also use authselect tool. However, if any manual modification was made in +PAM files, the authselect integrity check will fail and the remediation will be +aborted in order to preserve intentional changes. In this case, an informative message will +be shown in the remediation report. +If the system supports the /etc/security/faillock.conf file, the pam_faillock +parameters should be defined in faillock.conf file. + BP28(R18) + 1 + 12 + 15 + 16 + DSS05.04 + DSS05.10 + DSS06.10 + CCI-002238 + CCI-000044 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + 0421 + 0422 + 0431 + 0974 + 1173 + 1401 + 1504 + 1505 + 1546 + 1557 + 1558 + 1559 + 1560 + 1561 + A.18.1.4 + A.9.2.1 + A.9.2.4 + A.9.3.1 + A.9.4.2 + A.9.4.3 + CM-6(a) + AC-7(b) + IA-5(c) + PR.AC-7 + FMT_MOF_EXT.1 + SRG-OS-000329-GPOS-00128 + SRG-OS-000021-GPOS-00005 + By limiting the number of failed logon attempts, the risk of unauthorized system access via +user password guessing, also known as brute-forcing, is reduced. Limits are imposed by locking +the account. + + CCE-83589-2 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83589-2 + - NIST-800-53-AC-7(b) + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(c) + - accounts_passwords_pam_faillock_deny_root + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Configure the root Account for Failed Password Attempts - Check if system + relies on authselect tool + ansible.builtin.stat: + path: /usr/bin/authselect + register: result_authselect_present + when: '"pam" in ansible_facts.packages' + tags: + - CCE-83589-2 + - NIST-800-53-AC-7(b) + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(c) + - accounts_passwords_pam_faillock_deny_root + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Configure the root Account for Failed Password Attempts - Remediation where + authselect tool is present + block: + + - name: Configure the root Account for Failed Password Attempts - Check integrity + of authselect current profile + ansible.builtin.command: + cmd: authselect check + register: result_authselect_check_cmd + changed_when: false + ignore_errors: true + + - name: Configure the root Account for Failed Password Attempts - Informative message + based on the authselect integrity check result + ansible.builtin.assert: + that: + - result_authselect_check_cmd is success + fail_msg: + - authselect integrity check failed. Remediation aborted! + - This remediation could not be applied because an authselect profile was not + selected or the selected profile is not intact. + - It is not recommended to manually edit the PAM files when authselect tool + is available. + - In cases where the default authselect profile does not cover a specific demand, + a custom authselect profile is recommended. + success_msg: + - authselect integrity check passed + + - name: Configure the root Account for Failed Password Attempts - Get authselect + current features + ansible.builtin.shell: + cmd: authselect current | tail -n+3 | awk '{ print $2 }' + register: result_authselect_features + changed_when: false + when: + - result_authselect_check_cmd is success + + - name: Configure the root Account for Failed Password Attempts - Ensure "with-faillock" + feature is enabled using authselect tool + ansible.builtin.command: + cmd: authselect enable-feature with-faillock + register: result_authselect_enable_feature_cmd + when: + - result_authselect_check_cmd is success + - result_authselect_features.stdout is not search("with-faillock") + + - name: Configure the root Account for Failed Password Attempts - Ensure authselect + changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: + - result_authselect_enable_feature_cmd is not skipped + - result_authselect_enable_feature_cmd is success + when: + - '"pam" in ansible_facts.packages' + - result_authselect_present.stat.exists + tags: + - CCE-83589-2 + - NIST-800-53-AC-7(b) + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(c) + - accounts_passwords_pam_faillock_deny_root + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Configure the root Account for Failed Password Attempts - Remediation where + authselect tool is not present + block: + + - name: Configure the root Account for Failed Password Attempts - Check if pam_faillock.so + is already enabled + ansible.builtin.lineinfile: + path: /etc/pam.d/system-auth + regexp: .*auth.*pam_faillock\.so (preauth|authfail) + state: absent + check_mode: true + changed_when: false + register: result_pam_faillock_is_enabled + + - name: Configure the root Account for Failed Password Attempts - Enable pam_faillock.so + preauth editing PAM files + ansible.builtin.lineinfile: + path: '{{ item }}' + line: auth required pam_faillock.so preauth + insertbefore: ^auth.*sufficient.*pam_unix\.so.* + state: present + loop: + - /etc/pam.d/system-auth + - /etc/pam.d/password-auth + when: + - result_pam_faillock_is_enabled.found == 0 + + - name: Configure the root Account for Failed Password Attempts - Enable pam_faillock.so + authfail editing PAM files + ansible.builtin.lineinfile: + path: '{{ item }}' + line: auth required pam_faillock.so authfail + insertbefore: ^auth.*required.*pam_deny\.so.* + state: present + loop: + - /etc/pam.d/system-auth + - /etc/pam.d/password-auth + when: + - result_pam_faillock_is_enabled.found == 0 + + - name: Configure the root Account for Failed Password Attempts - Enable pam_faillock.so + account section editing PAM files + ansible.builtin.lineinfile: + path: '{{ item }}' + line: account required pam_faillock.so + insertbefore: ^account.*required.*pam_unix\.so.* + state: present + loop: + - /etc/pam.d/system-auth + - /etc/pam.d/password-auth + when: + - result_pam_faillock_is_enabled.found == 0 + when: + - '"pam" in ansible_facts.packages' + - not result_authselect_present.stat.exists + tags: + - CCE-83589-2 + - NIST-800-53-AC-7(b) + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(c) + - accounts_passwords_pam_faillock_deny_root + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Configure the root Account for Failed Password Attempts - Check the presence + of /etc/security/faillock.conf file + ansible.builtin.stat: + path: /etc/security/faillock.conf + register: result_faillock_conf_check + when: '"pam" in ansible_facts.packages' + tags: + - CCE-83589-2 + - NIST-800-53-AC-7(b) + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(c) + - accounts_passwords_pam_faillock_deny_root + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Configure the root Account for Failed Password Attempts - Ensure the pam_faillock.so + even_deny_root parameter in /etc/security/faillock.conf + ansible.builtin.lineinfile: + path: /etc/security/faillock.conf + regexp: ^\s*even_deny_root + line: even_deny_root + state: present + when: + - '"pam" in ansible_facts.packages' + - result_faillock_conf_check.stat.exists + tags: + - CCE-83589-2 + - NIST-800-53-AC-7(b) + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(c) + - accounts_passwords_pam_faillock_deny_root + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Configure the root Account for Failed Password Attempts - Ensure the pam_faillock.so + even_deny_root parameter not in PAM files + block: + + - name: Configure the root Account for Failed Password Attempts - Check if /etc/pam.d/system-auth + file is present + ansible.builtin.stat: + path: /etc/pam.d/system-auth + register: result_pam_file_present + + - name: Configure the root Account for Failed Password Attempts - Check the proper + remediation for the system + block: + + - name: Configure the root Account for Failed Password Attempts - Define the PAM + file to be edited as a local fact + ansible.builtin.set_fact: + pam_file_path: /etc/pam.d/system-auth + + - name: Configure the root Account for Failed Password Attempts - Check if system + relies on authselect + ansible.builtin.stat: + path: /usr/bin/authselect + register: result_authselect_present + + - name: Configure the root Account for Failed Password Attempts - Remediate using + authselect + block: + + - name: Configure the root Account for Failed Password Attempts - Check integrity + of authselect current profile + ansible.builtin.command: + cmd: authselect check + register: result_authselect_check_cmd + changed_when: false + ignore_errors: true + + - name: Configure the root Account for Failed Password Attempts - Informative + message based on the authselect integrity check result + ansible.builtin.assert: + that: + - result_authselect_check_cmd is success + fail_msg: + - authselect integrity check failed. Remediation aborted! + - This remediation could not be applied because an authselect profile was + not selected or the selected profile is not intact. + - It is not recommended to manually edit the PAM files when authselect tool + is available. + - In cases where the default authselect profile does not cover a specific + demand, a custom authselect profile is recommended. + success_msg: + - authselect integrity check passed + + - name: Configure the root Account for Failed Password Attempts - Get authselect + current profile + ansible.builtin.shell: + cmd: authselect current -r | awk '{ print $1 }' + register: result_authselect_profile + changed_when: false + when: + - result_authselect_check_cmd is success + + - name: Configure the root Account for Failed Password Attempts - Define the + current authselect profile as a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: '{{ result_authselect_profile.stdout }}' + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is match("custom/") + + - name: Configure the root Account for Failed Password Attempts - Define the + new authselect custom profile as a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: custom/hardening + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is not match("custom/") + + - name: Configure the root Account for Failed Password Attempts - Get authselect + current features to also enable them in the custom profile + ansible.builtin.shell: + cmd: authselect current | tail -n+3 | awk '{ print $2 }' + register: result_authselect_features + changed_when: false + when: + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + + - name: Configure the root Account for Failed Password Attempts - Check if any + custom profile with the same name was already created + ansible.builtin.stat: + path: /etc/authselect/{{ authselect_custom_profile }} + register: result_authselect_custom_profile_present + changed_when: false + when: + - authselect_current_profile is not match("custom/") + + - name: Configure the root Account for Failed Password Attempts - Create an + authselect custom profile based on the current profile + ansible.builtin.command: + cmd: authselect create-profile hardening -b {{ authselect_current_profile + }} + when: + - result_authselect_check_cmd is success + - authselect_current_profile is not match("custom/") + - not result_authselect_custom_profile_present.stat.exists + + - name: Configure the root Account for Failed Password Attempts - Ensure authselect + changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=before-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Configure the root Account for Failed Password Attempts - Ensure the + authselect custom profile is selected + ansible.builtin.command: + cmd: authselect select {{ authselect_custom_profile }} + register: result_pam_authselect_select_profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Configure the root Account for Failed Password Attempts - Restore the + authselect features in the custom profile + ansible.builtin.command: + cmd: authselect enable-feature {{ item }} + loop: '{{ result_authselect_features.stdout_lines }}' + register: result_pam_authselect_restore_features + when: + - result_authselect_profile is not skipped + - result_authselect_features is not skipped + - result_pam_authselect_select_profile is not skipped + + - name: Configure the root Account for Failed Password Attempts - Ensure authselect + changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=after-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - result_pam_authselect_restore_features is not skipped + + - name: Configure the root Account for Failed Password Attempts - Change the + PAM file to be edited according to the custom authselect profile + ansible.builtin.set_fact: + pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path + | basename }} + when: + - result_authselect_present.stat.exists + + - name: Configure the root Account for Failed Password Attempts - Ensure the "even_deny_root" + option from "pam_faillock.so" is not present in {{ pam_file_path }} + ansible.builtin.replace: + dest: '{{ pam_file_path }}' + regexp: (.*auth.*pam_faillock.so.*)\beven_deny_root\b=?[0-9a-zA-Z]*(.*) + replace: \1\2 + register: result_pam_option_removal + + - name: Configure the root Account for Failed Password Attempts - Ensure authselect + changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: + - result_authselect_present.stat.exists + - result_pam_option_removal is changed + when: + - result_pam_file_present.stat.exists + + - name: Configure the root Account for Failed Password Attempts - Check if /etc/pam.d/password-auth + file is present + ansible.builtin.stat: + path: /etc/pam.d/password-auth + register: result_pam_file_present + + - name: Configure the root Account for Failed Password Attempts - Check the proper + remediation for the system + block: + + - name: Configure the root Account for Failed Password Attempts - Define the PAM + file to be edited as a local fact + ansible.builtin.set_fact: + pam_file_path: /etc/pam.d/password-auth + + - name: Configure the root Account for Failed Password Attempts - Check if system + relies on authselect + ansible.builtin.stat: + path: /usr/bin/authselect + register: result_authselect_present + + - name: Configure the root Account for Failed Password Attempts - Remediate using + authselect + block: + + - name: Configure the root Account for Failed Password Attempts - Check integrity + of authselect current profile + ansible.builtin.command: + cmd: authselect check + register: result_authselect_check_cmd + changed_when: false + ignore_errors: true + + - name: Configure the root Account for Failed Password Attempts - Informative + message based on the authselect integrity check result + ansible.builtin.assert: + that: + - result_authselect_check_cmd is success + fail_msg: + - authselect integrity check failed. Remediation aborted! + - This remediation could not be applied because an authselect profile was + not selected or the selected profile is not intact. + - It is not recommended to manually edit the PAM files when authselect tool + is available. + - In cases where the default authselect profile does not cover a specific + demand, a custom authselect profile is recommended. + success_msg: + - authselect integrity check passed + + - name: Configure the root Account for Failed Password Attempts - Get authselect + current profile + ansible.builtin.shell: + cmd: authselect current -r | awk '{ print $1 }' + register: result_authselect_profile + changed_when: false + when: + - result_authselect_check_cmd is success + + - name: Configure the root Account for Failed Password Attempts - Define the + current authselect profile as a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: '{{ result_authselect_profile.stdout }}' + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is match("custom/") + + - name: Configure the root Account for Failed Password Attempts - Define the + new authselect custom profile as a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: custom/hardening + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is not match("custom/") + + - name: Configure the root Account for Failed Password Attempts - Get authselect + current features to also enable them in the custom profile + ansible.builtin.shell: + cmd: authselect current | tail -n+3 | awk '{ print $2 }' + register: result_authselect_features + changed_when: false + when: + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + + - name: Configure the root Account for Failed Password Attempts - Check if any + custom profile with the same name was already created + ansible.builtin.stat: + path: /etc/authselect/{{ authselect_custom_profile }} + register: result_authselect_custom_profile_present + changed_when: false + when: + - authselect_current_profile is not match("custom/") + + - name: Configure the root Account for Failed Password Attempts - Create an + authselect custom profile based on the current profile + ansible.builtin.command: + cmd: authselect create-profile hardening -b {{ authselect_current_profile + }} + when: + - result_authselect_check_cmd is success + - authselect_current_profile is not match("custom/") + - not result_authselect_custom_profile_present.stat.exists + + - name: Configure the root Account for Failed Password Attempts - Ensure authselect + changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=before-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Configure the root Account for Failed Password Attempts - Ensure the + authselect custom profile is selected + ansible.builtin.command: + cmd: authselect select {{ authselect_custom_profile }} + register: result_pam_authselect_select_profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Configure the root Account for Failed Password Attempts - Restore the + authselect features in the custom profile + ansible.builtin.command: + cmd: authselect enable-feature {{ item }} + loop: '{{ result_authselect_features.stdout_lines }}' + register: result_pam_authselect_restore_features + when: + - result_authselect_profile is not skipped + - result_authselect_features is not skipped + - result_pam_authselect_select_profile is not skipped + + - name: Configure the root Account for Failed Password Attempts - Ensure authselect + changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=after-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - result_pam_authselect_restore_features is not skipped + + - name: Configure the root Account for Failed Password Attempts - Change the + PAM file to be edited according to the custom authselect profile + ansible.builtin.set_fact: + pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path + | basename }} + when: + - result_authselect_present.stat.exists + + - name: Configure the root Account for Failed Password Attempts - Ensure the "even_deny_root" + option from "pam_faillock.so" is not present in {{ pam_file_path }} + ansible.builtin.replace: + dest: '{{ pam_file_path }}' + regexp: (.*auth.*pam_faillock.so.*)\beven_deny_root\b=?[0-9a-zA-Z]*(.*) + replace: \1\2 + register: result_pam_option_removal + + - name: Configure the root Account for Failed Password Attempts - Ensure authselect + changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: + - result_authselect_present.stat.exists + - result_pam_option_removal is changed + when: + - result_pam_file_present.stat.exists + when: + - '"pam" in ansible_facts.packages' + - result_faillock_conf_check.stat.exists + tags: + - CCE-83589-2 + - NIST-800-53-AC-7(b) + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(c) + - accounts_passwords_pam_faillock_deny_root + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Configure the root Account for Failed Password Attempts - Ensure the pam_faillock.so + even_deny_root parameter in PAM files + block: + + - name: Configure the root Account for Failed Password Attempts - Check if pam_faillock.so + even_deny_root parameter is already enabled in pam files + ansible.builtin.lineinfile: + path: /etc/pam.d/system-auth + regexp: .*auth.*pam_faillock\.so (preauth|authfail).*even_deny_root + state: absent + check_mode: true + changed_when: false + register: result_pam_faillock_even_deny_root_parameter_is_present + + - name: Configure the root Account for Failed Password Attempts - Ensure the inclusion + of pam_faillock.so preauth even_deny_root parameter in auth section + ansible.builtin.lineinfile: + path: '{{ item }}' + backrefs: true + regexp: (^\s*auth\s+)([\w\[].*\b)(\s+pam_faillock.so preauth.*) + line: \1required\3 even_deny_root + state: present + loop: + - /etc/pam.d/system-auth + - /etc/pam.d/password-auth + when: + - result_pam_faillock_even_deny_root_parameter_is_present.found == 0 + + - name: Configure the root Account for Failed Password Attempts - Ensure the inclusion + of pam_faillock.so authfail even_deny_root parameter in auth section + ansible.builtin.lineinfile: + path: '{{ item }}' + backrefs: true + regexp: (^\s*auth\s+)([\w\[].*\b)(\s+pam_faillock.so authfail.*) + line: \1required\3 even_deny_root + state: present + loop: + - /etc/pam.d/system-auth + - /etc/pam.d/password-auth + when: + - result_pam_faillock_even_deny_root_parameter_is_present.found == 0 + when: + - '"pam" in ansible_facts.packages' + - not result_faillock_conf_check.stat.exists + tags: + - CCE-83589-2 + - NIST-800-53-AC-7(b) + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(c) + - accounts_passwords_pam_faillock_deny_root + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q pam; then + +if [ -f /usr/bin/authselect ]; then + if ! authselect check; then +echo " +authselect integrity check failed. Remediation aborted! +This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact. +It is not recommended to manually edit the PAM files when authselect tool is available. +In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended." +exit 1 +fi +authselect enable-feature with-faillock + +authselect apply-changes -b +else + AUTH_FILES=("/etc/pam.d/system-auth" "/etc/pam.d/password-auth") +for pam_file in "${AUTH_FILES[@]}" +do + if ! grep -qE '^\s*auth\s+required\s+pam_faillock\.so\s+(preauth silent|authfail).*$' "$pam_file" ; then + sed -i --follow-symlinks '/^auth.*sufficient.*pam_unix\.so.*/i auth required pam_faillock.so preauth silent' "$pam_file" + sed -i --follow-symlinks '/^auth.*required.*pam_deny\.so.*/i auth required pam_faillock.so authfail' "$pam_file" + sed -i --follow-symlinks '/^account.*required.*pam_unix\.so.*/i account required pam_faillock.so' "$pam_file" + fi + sed -Ei 's/(auth.*)(\[default=die\])(.*pam_faillock\.so)/\1required \3/g' "$pam_file" +done +fi +AUTH_FILES=("/etc/pam.d/system-auth" "/etc/pam.d/password-auth") +FAILLOCK_CONF="/etc/security/faillock.conf" +if [ -f $FAILLOCK_CONF ]; then + regex="^\s*even_deny_root" + line="even_deny_root" + if ! grep -q $regex $FAILLOCK_CONF; then + echo $line >> $FAILLOCK_CONF + fi + for pam_file in "${AUTH_FILES[@]}" + do + if [ -e "$pam_file" ] ; then + PAM_FILE_PATH="$pam_file" + if [ -f /usr/bin/authselect ]; then + if ! authselect check; then + echo " + authselect integrity check failed. Remediation aborted! + This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact. + It is not recommended to manually edit the PAM files when authselect tool is available. + In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended." + exit 1 + fi + CURRENT_PROFILE=$(authselect current -r | awk '{ print $1 }') + # If not already in use, a custom profile is created preserving the enabled features. + if [[ ! $CURRENT_PROFILE == custom/* ]]; then + ENABLED_FEATURES=$(authselect current | tail -n+3 | awk '{ print $2 }') + authselect create-profile hardening -b $CURRENT_PROFILE + CURRENT_PROFILE="custom/hardening" + + authselect apply-changes -b --backup=before-hardening-custom-profile + authselect select $CURRENT_PROFILE + for feature in $ENABLED_FEATURES; do + authselect enable-feature $feature; + done + + authselect apply-changes -b --backup=after-hardening-custom-profile + fi + PAM_FILE_NAME=$(basename "$pam_file") + PAM_FILE_PATH="/etc/authselect/$CURRENT_PROFILE/$PAM_FILE_NAME" + + authselect apply-changes -b + fi + + if grep -qP '^\s*auth\s.*\bpam_faillock.so\s.*\beven_deny_root\b' "$PAM_FILE_PATH"; then + sed -i -E --follow-symlinks 's/(.*auth.*pam_faillock.so.*)\beven_deny_root\b=?[[:alnum:]]*(.*)/\1\2/g' "$PAM_FILE_PATH" + fi + if [ -f /usr/bin/authselect ]; then + + authselect apply-changes -b + fi + else + echo "$pam_file was not found" >&2 + fi + done +else + for pam_file in "${AUTH_FILES[@]}" + do + if ! grep -qE '^\s*auth.*pam_faillock\.so (preauth|authfail).*even_deny_root' "$pam_file"; then + sed -i --follow-symlinks '/^auth.*required.*pam_faillock\.so.*preauth.*silent.*/ s/$/ even_deny_root/' "$pam_file" + sed -i --follow-symlinks '/^auth.*required.*pam_faillock\.so.*authfail.*/ s/$/ even_deny_root/' "$pam_file" + fi + done +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Lock Accounts Must Persist + This rule ensures that the system lock out accounts using pam_faillock.so persist +after system reboot. From "Pam_Faillock" man pages: +Note that the default directory that "pam_faillock" uses is usually cleared on system +boot so the access will be reenabled after system reboot. If that is undesirable, a different +tally directory must be set with the "dir" option. + +pam_faillock.so module requires multiple entries in pam files. These entries must be carefully +defined to work as expected. In order to avoid errors when manually editing these files, it is +recommended to use the appropriate tools, such as authselect or authconfig, +depending on the OS version. + If the system relies on authselect tool to manage PAM settings, the remediation +will also use authselect tool. However, if any manual modification was made in +PAM files, the authselect integrity check will fail and the remediation will be +aborted in order to preserve intentional changes. In this case, an informative message will +be shown in the remediation report. +If the system supports the /etc/security/faillock.conf file, the pam_faillock +parameters should be defined in faillock.conf file. + CCI-000044 + CCI-002238 + AC-7(b) + AC-7(a) + AC-7.1(ii) + SRG-OS-000021-GPOS-00005 + SRG-OS-000329-GPOS-00128 + Locking out user accounts after a number of incorrect attempts prevents direct password +guessing attacks. In combination with the silent option, user enumeration attacks +are also mitigated. + + - name: Gather the package facts + package_facts: + manager: auto + tags: + - NIST-800-53-AC-7(a) + - NIST-800-53-AC-7(b) + - NIST-800-53-AC-7.1(ii) + - accounts_passwords_pam_faillock_dir + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Lock Accounts Must Persist - Check if system relies on authselect tool + ansible.builtin.stat: + path: /usr/bin/authselect + register: result_authselect_present + when: '"pam" in ansible_facts.packages' + tags: + - NIST-800-53-AC-7(a) + - NIST-800-53-AC-7(b) + - NIST-800-53-AC-7.1(ii) + - accounts_passwords_pam_faillock_dir + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Lock Accounts Must Persist - Remediation where authselect tool is present + block: + + - name: Lock Accounts Must Persist - Check integrity of authselect current profile + ansible.builtin.command: + cmd: authselect check + register: result_authselect_check_cmd + changed_when: false + ignore_errors: true + + - name: Lock Accounts Must Persist - Informative message based on the authselect + integrity check result + ansible.builtin.assert: + that: + - result_authselect_check_cmd is success + fail_msg: + - authselect integrity check failed. Remediation aborted! + - This remediation could not be applied because an authselect profile was not + selected or the selected profile is not intact. + - It is not recommended to manually edit the PAM files when authselect tool + is available. + - In cases where the default authselect profile does not cover a specific demand, + a custom authselect profile is recommended. + success_msg: + - authselect integrity check passed + + - name: Lock Accounts Must Persist - Get authselect current features + ansible.builtin.shell: + cmd: authselect current | tail -n+3 | awk '{ print $2 }' + register: result_authselect_features + changed_when: false + when: + - result_authselect_check_cmd is success + + - name: Lock Accounts Must Persist - Ensure "with-faillock" feature is enabled using + authselect tool + ansible.builtin.command: + cmd: authselect enable-feature with-faillock + register: result_authselect_enable_feature_cmd + when: + - result_authselect_check_cmd is success + - result_authselect_features.stdout is not search("with-faillock") + + - name: Lock Accounts Must Persist - Ensure authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: + - result_authselect_enable_feature_cmd is not skipped + - result_authselect_enable_feature_cmd is success + when: + - '"pam" in ansible_facts.packages' + - result_authselect_present.stat.exists + tags: + - NIST-800-53-AC-7(a) + - NIST-800-53-AC-7(b) + - NIST-800-53-AC-7.1(ii) + - accounts_passwords_pam_faillock_dir + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Lock Accounts Must Persist - Remediation where authselect tool is not present + block: + + - name: Lock Accounts Must Persist - Check if pam_faillock.so is already enabled + ansible.builtin.lineinfile: + path: /etc/pam.d/system-auth + regexp: .*auth.*pam_faillock\.so (preauth|authfail) + state: absent + check_mode: true + changed_when: false + register: result_pam_faillock_is_enabled + + - name: Lock Accounts Must Persist - Enable pam_faillock.so preauth editing PAM + files + ansible.builtin.lineinfile: + path: '{{ item }}' + line: auth required pam_faillock.so preauth + insertbefore: ^auth.*sufficient.*pam_unix\.so.* + state: present + loop: + - /etc/pam.d/system-auth + - /etc/pam.d/password-auth + when: + - result_pam_faillock_is_enabled.found == 0 + + - name: Lock Accounts Must Persist - Enable pam_faillock.so authfail editing PAM + files + ansible.builtin.lineinfile: + path: '{{ item }}' + line: auth required pam_faillock.so authfail + insertbefore: ^auth.*required.*pam_deny\.so.* + state: present + loop: + - /etc/pam.d/system-auth + - /etc/pam.d/password-auth + when: + - result_pam_faillock_is_enabled.found == 0 + + - name: Lock Accounts Must Persist - Enable pam_faillock.so account section editing + PAM files + ansible.builtin.lineinfile: + path: '{{ item }}' + line: account required pam_faillock.so + insertbefore: ^account.*required.*pam_unix\.so.* + state: present + loop: + - /etc/pam.d/system-auth + - /etc/pam.d/password-auth + when: + - result_pam_faillock_is_enabled.found == 0 + when: + - '"pam" in ansible_facts.packages' + - not result_authselect_present.stat.exists + tags: + - NIST-800-53-AC-7(a) + - NIST-800-53-AC-7(b) + - NIST-800-53-AC-7.1(ii) + - accounts_passwords_pam_faillock_dir + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed +- name: XCCDF Value var_accounts_passwords_pam_faillock_dir # promote to variable + set_fact: + var_accounts_passwords_pam_faillock_dir: !!str + tags: + - always + +- name: Lock Accounts Must Persist - Check the presence of /etc/security/faillock.conf + file + ansible.builtin.stat: + path: /etc/security/faillock.conf + register: result_faillock_conf_check + when: '"pam" in ansible_facts.packages' + tags: + - NIST-800-53-AC-7(a) + - NIST-800-53-AC-7(b) + - NIST-800-53-AC-7.1(ii) + - accounts_passwords_pam_faillock_dir + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Lock Accounts Must Persist - Ensure the pam_faillock.so dir parameter in /etc/security/faillock.conf + ansible.builtin.lineinfile: + path: /etc/security/faillock.conf + regexp: ^\s*dir\s*= + line: dir = {{ var_accounts_passwords_pam_faillock_dir }} + state: present + when: + - '"pam" in ansible_facts.packages' + - result_faillock_conf_check.stat.exists + tags: + - NIST-800-53-AC-7(a) + - NIST-800-53-AC-7(b) + - NIST-800-53-AC-7.1(ii) + - accounts_passwords_pam_faillock_dir + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Lock Accounts Must Persist - Ensure the pam_faillock.so dir parameter not + in PAM files + block: + + - name: Lock Accounts Must Persist - Check if /etc/pam.d/system-auth file is present + ansible.builtin.stat: + path: /etc/pam.d/system-auth + register: result_pam_file_present + + - name: Lock Accounts Must Persist - Check the proper remediation for the system + block: + + - name: Lock Accounts Must Persist - Define the PAM file to be edited as a local + fact + ansible.builtin.set_fact: + pam_file_path: /etc/pam.d/system-auth + + - name: Lock Accounts Must Persist - Check if system relies on authselect + ansible.builtin.stat: + path: /usr/bin/authselect + register: result_authselect_present + + - name: Lock Accounts Must Persist - Remediate using authselect + block: + + - name: Lock Accounts Must Persist - Check integrity of authselect current profile + ansible.builtin.command: + cmd: authselect check + register: result_authselect_check_cmd + changed_when: false + ignore_errors: true + + - name: Lock Accounts Must Persist - Informative message based on the authselect + integrity check result + ansible.builtin.assert: + that: + - result_authselect_check_cmd is success + fail_msg: + - authselect integrity check failed. Remediation aborted! + - This remediation could not be applied because an authselect profile was + not selected or the selected profile is not intact. + - It is not recommended to manually edit the PAM files when authselect tool + is available. + - In cases where the default authselect profile does not cover a specific + demand, a custom authselect profile is recommended. + success_msg: + - authselect integrity check passed + + - name: Lock Accounts Must Persist - Get authselect current profile + ansible.builtin.shell: + cmd: authselect current -r | awk '{ print $1 }' + register: result_authselect_profile + changed_when: false + when: + - result_authselect_check_cmd is success + + - name: Lock Accounts Must Persist - Define the current authselect profile as + a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: '{{ result_authselect_profile.stdout }}' + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is match("custom/") + + - name: Lock Accounts Must Persist - Define the new authselect custom profile + as a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: custom/hardening + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is not match("custom/") + + - name: Lock Accounts Must Persist - Get authselect current features to also + enable them in the custom profile + ansible.builtin.shell: + cmd: authselect current | tail -n+3 | awk '{ print $2 }' + register: result_authselect_features + changed_when: false + when: + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + + - name: Lock Accounts Must Persist - Check if any custom profile with the same + name was already created + ansible.builtin.stat: + path: /etc/authselect/{{ authselect_custom_profile }} + register: result_authselect_custom_profile_present + changed_when: false + when: + - authselect_current_profile is not match("custom/") + + - name: Lock Accounts Must Persist - Create an authselect custom profile based + on the current profile + ansible.builtin.command: + cmd: authselect create-profile hardening -b {{ authselect_current_profile + }} + when: + - result_authselect_check_cmd is success + - authselect_current_profile is not match("custom/") + - not result_authselect_custom_profile_present.stat.exists + + - name: Lock Accounts Must Persist - Ensure authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=before-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Lock Accounts Must Persist - Ensure the authselect custom profile is + selected + ansible.builtin.command: + cmd: authselect select {{ authselect_custom_profile }} + register: result_pam_authselect_select_profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Lock Accounts Must Persist - Restore the authselect features in the + custom profile + ansible.builtin.command: + cmd: authselect enable-feature {{ item }} + loop: '{{ result_authselect_features.stdout_lines }}' + register: result_pam_authselect_restore_features + when: + - result_authselect_profile is not skipped + - result_authselect_features is not skipped + - result_pam_authselect_select_profile is not skipped + + - name: Lock Accounts Must Persist - Ensure authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=after-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - result_pam_authselect_restore_features is not skipped + + - name: Lock Accounts Must Persist - Change the PAM file to be edited according + to the custom authselect profile + ansible.builtin.set_fact: + pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path + | basename }} + when: + - result_authselect_present.stat.exists + + - name: Lock Accounts Must Persist - Ensure the "dir" option from "pam_faillock.so" + is not present in {{ pam_file_path }} + ansible.builtin.replace: + dest: '{{ pam_file_path }}' + regexp: (.*auth.*pam_faillock.so.*)\bdir\b=?[0-9a-zA-Z]*(.*) + replace: \1\2 + register: result_pam_option_removal + + - name: Lock Accounts Must Persist - Ensure authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: + - result_authselect_present.stat.exists + - result_pam_option_removal is changed + when: + - result_pam_file_present.stat.exists + + - name: Lock Accounts Must Persist - Check if /etc/pam.d/password-auth file is present + ansible.builtin.stat: + path: /etc/pam.d/password-auth + register: result_pam_file_present + + - name: Lock Accounts Must Persist - Check the proper remediation for the system + block: + + - name: Lock Accounts Must Persist - Define the PAM file to be edited as a local + fact + ansible.builtin.set_fact: + pam_file_path: /etc/pam.d/password-auth + + - name: Lock Accounts Must Persist - Check if system relies on authselect + ansible.builtin.stat: + path: /usr/bin/authselect + register: result_authselect_present + + - name: Lock Accounts Must Persist - Remediate using authselect + block: + + - name: Lock Accounts Must Persist - Check integrity of authselect current profile + ansible.builtin.command: + cmd: authselect check + register: result_authselect_check_cmd + changed_when: false + ignore_errors: true + + - name: Lock Accounts Must Persist - Informative message based on the authselect + integrity check result + ansible.builtin.assert: + that: + - result_authselect_check_cmd is success + fail_msg: + - authselect integrity check failed. Remediation aborted! + - This remediation could not be applied because an authselect profile was + not selected or the selected profile is not intact. + - It is not recommended to manually edit the PAM files when authselect tool + is available. + - In cases where the default authselect profile does not cover a specific + demand, a custom authselect profile is recommended. + success_msg: + - authselect integrity check passed + + - name: Lock Accounts Must Persist - Get authselect current profile + ansible.builtin.shell: + cmd: authselect current -r | awk '{ print $1 }' + register: result_authselect_profile + changed_when: false + when: + - result_authselect_check_cmd is success + + - name: Lock Accounts Must Persist - Define the current authselect profile as + a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: '{{ result_authselect_profile.stdout }}' + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is match("custom/") + + - name: Lock Accounts Must Persist - Define the new authselect custom profile + as a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: custom/hardening + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is not match("custom/") + + - name: Lock Accounts Must Persist - Get authselect current features to also + enable them in the custom profile + ansible.builtin.shell: + cmd: authselect current | tail -n+3 | awk '{ print $2 }' + register: result_authselect_features + changed_when: false + when: + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + + - name: Lock Accounts Must Persist - Check if any custom profile with the same + name was already created + ansible.builtin.stat: + path: /etc/authselect/{{ authselect_custom_profile }} + register: result_authselect_custom_profile_present + changed_when: false + when: + - authselect_current_profile is not match("custom/") + + - name: Lock Accounts Must Persist - Create an authselect custom profile based + on the current profile + ansible.builtin.command: + cmd: authselect create-profile hardening -b {{ authselect_current_profile + }} + when: + - result_authselect_check_cmd is success + - authselect_current_profile is not match("custom/") + - not result_authselect_custom_profile_present.stat.exists + + - name: Lock Accounts Must Persist - Ensure authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=before-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Lock Accounts Must Persist - Ensure the authselect custom profile is + selected + ansible.builtin.command: + cmd: authselect select {{ authselect_custom_profile }} + register: result_pam_authselect_select_profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Lock Accounts Must Persist - Restore the authselect features in the + custom profile + ansible.builtin.command: + cmd: authselect enable-feature {{ item }} + loop: '{{ result_authselect_features.stdout_lines }}' + register: result_pam_authselect_restore_features + when: + - result_authselect_profile is not skipped + - result_authselect_features is not skipped + - result_pam_authselect_select_profile is not skipped + + - name: Lock Accounts Must Persist - Ensure authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=after-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - result_pam_authselect_restore_features is not skipped + + - name: Lock Accounts Must Persist - Change the PAM file to be edited according + to the custom authselect profile + ansible.builtin.set_fact: + pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path + | basename }} + when: + - result_authselect_present.stat.exists + + - name: Lock Accounts Must Persist - Ensure the "dir" option from "pam_faillock.so" + is not present in {{ pam_file_path }} + ansible.builtin.replace: + dest: '{{ pam_file_path }}' + regexp: (.*auth.*pam_faillock.so.*)\bdir\b=?[0-9a-zA-Z]*(.*) + replace: \1\2 + register: result_pam_option_removal + + - name: Lock Accounts Must Persist - Ensure authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: + - result_authselect_present.stat.exists + - result_pam_option_removal is changed + when: + - result_pam_file_present.stat.exists + when: + - '"pam" in ansible_facts.packages' + - result_faillock_conf_check.stat.exists + tags: + - NIST-800-53-AC-7(a) + - NIST-800-53-AC-7(b) + - NIST-800-53-AC-7.1(ii) + - accounts_passwords_pam_faillock_dir + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Lock Accounts Must Persist - Ensure the pam_faillock.so dir parameter in PAM + files + block: + + - name: Lock Accounts Must Persist - Check if pam_faillock.so dir parameter is already + enabled in pam files + ansible.builtin.lineinfile: + path: /etc/pam.d/system-auth + regexp: .*auth.*pam_faillock\.so (preauth|authfail).*dir + state: absent + check_mode: true + changed_when: false + register: result_pam_faillock_dir_parameter_is_present + + - name: Lock Accounts Must Persist - Ensure the inclusion of pam_faillock.so preauth + dir parameter in auth section + ansible.builtin.lineinfile: + path: '{{ item }}' + backrefs: true + regexp: (^\s*auth\s+)([\w\[].*\b)(\s+pam_faillock.so preauth.*) + line: \1required\3 dir={{ var_accounts_passwords_pam_faillock_dir }} + state: present + loop: + - /etc/pam.d/system-auth + - /etc/pam.d/password-auth + when: + - result_pam_faillock_dir_parameter_is_present.found == 0 + + - name: Lock Accounts Must Persist - Ensure the inclusion of pam_faillock.so authfail + dir parameter in auth section + ansible.builtin.lineinfile: + path: '{{ item }}' + backrefs: true + regexp: (^\s*auth\s+)([\w\[].*\b)(\s+pam_faillock.so authfail.*) + line: \1required\3 dir={{ var_accounts_passwords_pam_faillock_dir }} + state: present + loop: + - /etc/pam.d/system-auth + - /etc/pam.d/password-auth + when: + - result_pam_faillock_dir_parameter_is_present.found == 0 + + - name: Lock Accounts Must Persist - Ensure the desired value for pam_faillock.so + preauth dir parameter in auth section + ansible.builtin.lineinfile: + path: '{{ item }}' + backrefs: true + regexp: (^\s*auth\s+)([\w\[].*\b)(\s+pam_faillock.so preauth.*)(dir)=[0-9]+(.*) + line: \1required\3\4={{ var_accounts_passwords_pam_faillock_dir }}\5 + state: present + loop: + - /etc/pam.d/system-auth + - /etc/pam.d/password-auth + when: + - result_pam_faillock_dir_parameter_is_present.found > 0 + + - name: Lock Accounts Must Persist - Ensure the desired value for pam_faillock.so + authfail dir parameter in auth section + ansible.builtin.lineinfile: + path: '{{ item }}' + backrefs: true + regexp: (^\s*auth\s+)([\w\[].*\b)(\s+pam_faillock.so authfail.*)(dir)=[0-9]+(.*) + line: \1required\3\4={{ var_accounts_passwords_pam_faillock_dir }}\5 + state: present + loop: + - /etc/pam.d/system-auth + - /etc/pam.d/password-auth + when: + - result_pam_faillock_dir_parameter_is_present.found > 0 + when: + - '"pam" in ansible_facts.packages' + - not result_faillock_conf_check.stat.exists + tags: + - NIST-800-53-AC-7(a) + - NIST-800-53-AC-7(b) + - NIST-800-53-AC-7.1(ii) + - accounts_passwords_pam_faillock_dir + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if rpm --quiet -q pam; then + +if [ -f /usr/bin/authselect ]; then + if ! authselect check; then +echo " +authselect integrity check failed. Remediation aborted! +This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact. +It is not recommended to manually edit the PAM files when authselect tool is available. +In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended." +exit 1 +fi +authselect enable-feature with-faillock + +authselect apply-changes -b +else + AUTH_FILES=("/etc/pam.d/system-auth" "/etc/pam.d/password-auth") +for pam_file in "${AUTH_FILES[@]}" +do + if ! grep -qE '^\s*auth\s+required\s+pam_faillock\.so\s+(preauth silent|authfail).*$' "$pam_file" ; then + sed -i --follow-symlinks '/^auth.*sufficient.*pam_unix\.so.*/i auth required pam_faillock.so preauth silent' "$pam_file" + sed -i --follow-symlinks '/^auth.*required.*pam_deny\.so.*/i auth required pam_faillock.so authfail' "$pam_file" + sed -i --follow-symlinks '/^account.*required.*pam_unix\.so.*/i account required pam_faillock.so' "$pam_file" + fi + sed -Ei 's/(auth.*)(\[default=die\])(.*pam_faillock\.so)/\1required \3/g' "$pam_file" +done +fi + +AUTH_FILES=("/etc/pam.d/system-auth" "/etc/pam.d/password-auth") +FAILLOCK_CONF="/etc/security/faillock.conf" +if [ -f $FAILLOCK_CONF ]; then + regex="^\s*dir\s*=" + line="dir = /var/log/faillock" + if ! grep -q $regex $FAILLOCK_CONF; then + echo $line >> $FAILLOCK_CONF + else + sed -i --follow-symlinks 's|^\s*\(dir\s*=\s*\)\(\S\+\)|\1'"/var/log/faillock"'|g' $FAILLOCK_CONF + fi + for pam_file in "${AUTH_FILES[@]}" + do + if [ -e "$pam_file" ] ; then + PAM_FILE_PATH="$pam_file" + if [ -f /usr/bin/authselect ]; then + if ! authselect check; then + echo " + authselect integrity check failed. Remediation aborted! + This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact. + It is not recommended to manually edit the PAM files when authselect tool is available. + In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended." + exit 1 + fi + CURRENT_PROFILE=$(authselect current -r | awk '{ print $1 }') + # If not already in use, a custom profile is created preserving the enabled features. + if [[ ! $CURRENT_PROFILE == custom/* ]]; then + ENABLED_FEATURES=$(authselect current | tail -n+3 | awk '{ print $2 }') + authselect create-profile hardening -b $CURRENT_PROFILE + CURRENT_PROFILE="custom/hardening" + + authselect apply-changes -b --backup=before-hardening-custom-profile + authselect select $CURRENT_PROFILE + for feature in $ENABLED_FEATURES; do + authselect enable-feature $feature; + done + + authselect apply-changes -b --backup=after-hardening-custom-profile + fi + PAM_FILE_NAME=$(basename "$pam_file") + PAM_FILE_PATH="/etc/authselect/$CURRENT_PROFILE/$PAM_FILE_NAME" + + authselect apply-changes -b + fi + + if grep -qP '^\s*auth\s.*\bpam_faillock.so\s.*\bdir\b' "$PAM_FILE_PATH"; then + sed -i -E --follow-symlinks 's/(.*auth.*pam_faillock.so.*)\bdir\b=?[[:alnum:]]*(.*)/\1\2/g' "$PAM_FILE_PATH" + fi + if [ -f /usr/bin/authselect ]; then + + authselect apply-changes -b + fi + else + echo "$pam_file was not found" >&2 + fi + done +else + for pam_file in "${AUTH_FILES[@]}" + do + if ! grep -qE '^\s*auth.*pam_faillock\.so (preauth|authfail).*dir' "$pam_file"; then + sed -i --follow-symlinks '/^auth.*required.*pam_faillock\.so.*preauth.*silent.*/ s/$/ dir='"/var/log/faillock"'/' "$pam_file" + sed -i --follow-symlinks '/^auth.*required.*pam_faillock\.so.*authfail.*/ s/$/ dir='"/var/log/faillock"'/' "$pam_file" + else + sed -i --follow-symlinks 's/\(^auth.*required.*pam_faillock\.so.*preauth.*silent.*\)\('"dir"'=\)[0-9]\+\(.*\)/\1\2'"/var/log/faillock"'\3/' "$pam_file" + sed -i --follow-symlinks 's/\(^auth.*required.*pam_faillock\.so.*authfail.*\)\('"dir"'=\)[0-9]\+\(.*\)/\1\2'"/var/log/faillock"'\3/' "$pam_file" + fi + done +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Enforce pam_faillock for Local Accounts Only + The pam_faillock module's local_users_only parameter controls requirements for +enforcing failed lockout attempts only for local user accounts and ignoring centralized user +account management failed attempt configurations. + If the system relies on authselect tool to manage PAM settings, the remediation +will also use authselect tool. However, if any manual modification was made in +PAM files, the authselect integrity check will fail and the remediation will be +aborted in order to preserve intentional changes. In this case, an informative message will +be shown in the remediation report. +If the system supports the /etc/security/faillock.conf file, the pam_faillock +parameters should be defined in faillock.conf file. + Using this rule bypasses pam_faillock's functionality and should be used in cases +where centralized management such as LDAP or Active Directory is in use. + CCI-000015 + AC-2(1) + SRG-OS-000001-GPOS-00001 + The operating system must provide automated mechanisms for supporting account management +functions. Enterprise environments make application account management challenging and +complex. A manual process for account management functions adds the risk of a potential +oversight or other error. Locking out remote accounts may cause unintentional DoS. + + - name: Gather the package facts + package_facts: + manager: auto + tags: + - NIST-800-53-AC-2(1) + - accounts_passwords_pam_faillock_enforce_local + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Enforce pam_faillock for Local Accounts Only - Check if system relies on authselect + tool + ansible.builtin.stat: + path: /usr/bin/authselect + register: result_authselect_present + when: '"pam" in ansible_facts.packages' + tags: + - NIST-800-53-AC-2(1) + - accounts_passwords_pam_faillock_enforce_local + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Enforce pam_faillock for Local Accounts Only - Remediation where authselect + tool is present + block: + + - name: Enforce pam_faillock for Local Accounts Only - Check integrity of authselect + current profile + ansible.builtin.command: + cmd: authselect check + register: result_authselect_check_cmd + changed_when: false + ignore_errors: true + + - name: Enforce pam_faillock for Local Accounts Only - Informative message based + on the authselect integrity check result + ansible.builtin.assert: + that: + - result_authselect_check_cmd is success + fail_msg: + - authselect integrity check failed. Remediation aborted! + - This remediation could not be applied because an authselect profile was not + selected or the selected profile is not intact. + - It is not recommended to manually edit the PAM files when authselect tool + is available. + - In cases where the default authselect profile does not cover a specific demand, + a custom authselect profile is recommended. + success_msg: + - authselect integrity check passed + + - name: Enforce pam_faillock for Local Accounts Only - Get authselect current features + ansible.builtin.shell: + cmd: authselect current | tail -n+3 | awk '{ print $2 }' + register: result_authselect_features + changed_when: false + when: + - result_authselect_check_cmd is success + + - name: Enforce pam_faillock for Local Accounts Only - Ensure "with-faillock" feature + is enabled using authselect tool + ansible.builtin.command: + cmd: authselect enable-feature with-faillock + register: result_authselect_enable_feature_cmd + when: + - result_authselect_check_cmd is success + - result_authselect_features.stdout is not search("with-faillock") + + - name: Enforce pam_faillock for Local Accounts Only - Ensure authselect changes + are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: + - result_authselect_enable_feature_cmd is not skipped + - result_authselect_enable_feature_cmd is success + when: + - '"pam" in ansible_facts.packages' + - result_authselect_present.stat.exists + tags: + - NIST-800-53-AC-2(1) + - accounts_passwords_pam_faillock_enforce_local + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Enforce pam_faillock for Local Accounts Only - Remediation where authselect + tool is not present + block: + + - name: Enforce pam_faillock for Local Accounts Only - Check if pam_faillock.so + is already enabled + ansible.builtin.lineinfile: + path: /etc/pam.d/system-auth + regexp: .*auth.*pam_faillock\.so (preauth|authfail) + state: absent + check_mode: true + changed_when: false + register: result_pam_faillock_is_enabled + + - name: Enforce pam_faillock for Local Accounts Only - Enable pam_faillock.so preauth + editing PAM files + ansible.builtin.lineinfile: + path: '{{ item }}' + line: auth required pam_faillock.so preauth + insertbefore: ^auth.*sufficient.*pam_unix\.so.* + state: present + loop: + - /etc/pam.d/system-auth + - /etc/pam.d/password-auth + when: + - result_pam_faillock_is_enabled.found == 0 + + - name: Enforce pam_faillock for Local Accounts Only - Enable pam_faillock.so authfail + editing PAM files + ansible.builtin.lineinfile: + path: '{{ item }}' + line: auth required pam_faillock.so authfail + insertbefore: ^auth.*required.*pam_deny\.so.* + state: present + loop: + - /etc/pam.d/system-auth + - /etc/pam.d/password-auth + when: + - result_pam_faillock_is_enabled.found == 0 + + - name: Enforce pam_faillock for Local Accounts Only - Enable pam_faillock.so account + section editing PAM files + ansible.builtin.lineinfile: + path: '{{ item }}' + line: account required pam_faillock.so + insertbefore: ^account.*required.*pam_unix\.so.* + state: present + loop: + - /etc/pam.d/system-auth + - /etc/pam.d/password-auth + when: + - result_pam_faillock_is_enabled.found == 0 + when: + - '"pam" in ansible_facts.packages' + - not result_authselect_present.stat.exists + tags: + - NIST-800-53-AC-2(1) + - accounts_passwords_pam_faillock_enforce_local + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Enforce pam_faillock for Local Accounts Only - Check the presence of /etc/security/faillock.conf + file + ansible.builtin.stat: + path: /etc/security/faillock.conf + register: result_faillock_conf_check + when: '"pam" in ansible_facts.packages' + tags: + - NIST-800-53-AC-2(1) + - accounts_passwords_pam_faillock_enforce_local + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Enforce pam_faillock for Local Accounts Only - Ensure the pam_faillock.so + local_users_only parameter in /etc/security/faillock.conf + ansible.builtin.lineinfile: + path: /etc/security/faillock.conf + regexp: ^\s*local_users_only + line: local_users_only + state: present + when: + - '"pam" in ansible_facts.packages' + - result_faillock_conf_check.stat.exists + tags: + - NIST-800-53-AC-2(1) + - accounts_passwords_pam_faillock_enforce_local + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Enforce pam_faillock for Local Accounts Only - Ensure the pam_faillock.so + local_users_only parameter not in PAM files + block: + + - name: Enforce pam_faillock for Local Accounts Only - Check if /etc/pam.d/system-auth + file is present + ansible.builtin.stat: + path: /etc/pam.d/system-auth + register: result_pam_file_present + + - name: Enforce pam_faillock for Local Accounts Only - Check the proper remediation + for the system + block: + + - name: Enforce pam_faillock for Local Accounts Only - Define the PAM file to + be edited as a local fact + ansible.builtin.set_fact: + pam_file_path: /etc/pam.d/system-auth + + - name: Enforce pam_faillock for Local Accounts Only - Check if system relies + on authselect + ansible.builtin.stat: + path: /usr/bin/authselect + register: result_authselect_present + + - name: Enforce pam_faillock for Local Accounts Only - Remediate using authselect + block: + + - name: Enforce pam_faillock for Local Accounts Only - Check integrity of authselect + current profile + ansible.builtin.command: + cmd: authselect check + register: result_authselect_check_cmd + changed_when: false + ignore_errors: true + + - name: Enforce pam_faillock for Local Accounts Only - Informative message based + on the authselect integrity check result + ansible.builtin.assert: + that: + - result_authselect_check_cmd is success + fail_msg: + - authselect integrity check failed. Remediation aborted! + - This remediation could not be applied because an authselect profile was + not selected or the selected profile is not intact. + - It is not recommended to manually edit the PAM files when authselect tool + is available. + - In cases where the default authselect profile does not cover a specific + demand, a custom authselect profile is recommended. + success_msg: + - authselect integrity check passed + + - name: Enforce pam_faillock for Local Accounts Only - Get authselect current + profile + ansible.builtin.shell: + cmd: authselect current -r | awk '{ print $1 }' + register: result_authselect_profile + changed_when: false + when: + - result_authselect_check_cmd is success + + - name: Enforce pam_faillock for Local Accounts Only - Define the current authselect + profile as a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: '{{ result_authselect_profile.stdout }}' + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is match("custom/") + + - name: Enforce pam_faillock for Local Accounts Only - Define the new authselect + custom profile as a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: custom/hardening + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is not match("custom/") + + - name: Enforce pam_faillock for Local Accounts Only - Get authselect current + features to also enable them in the custom profile + ansible.builtin.shell: + cmd: authselect current | tail -n+3 | awk '{ print $2 }' + register: result_authselect_features + changed_when: false + when: + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + + - name: Enforce pam_faillock for Local Accounts Only - Check if any custom profile + with the same name was already created + ansible.builtin.stat: + path: /etc/authselect/{{ authselect_custom_profile }} + register: result_authselect_custom_profile_present + changed_when: false + when: + - authselect_current_profile is not match("custom/") + + - name: Enforce pam_faillock for Local Accounts Only - Create an authselect + custom profile based on the current profile + ansible.builtin.command: + cmd: authselect create-profile hardening -b {{ authselect_current_profile + }} + when: + - result_authselect_check_cmd is success + - authselect_current_profile is not match("custom/") + - not result_authselect_custom_profile_present.stat.exists + + - name: Enforce pam_faillock for Local Accounts Only - Ensure authselect changes + are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=before-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Enforce pam_faillock for Local Accounts Only - Ensure the authselect + custom profile is selected + ansible.builtin.command: + cmd: authselect select {{ authselect_custom_profile }} + register: result_pam_authselect_select_profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Enforce pam_faillock for Local Accounts Only - Restore the authselect + features in the custom profile + ansible.builtin.command: + cmd: authselect enable-feature {{ item }} + loop: '{{ result_authselect_features.stdout_lines }}' + register: result_pam_authselect_restore_features + when: + - result_authselect_profile is not skipped + - result_authselect_features is not skipped + - result_pam_authselect_select_profile is not skipped + + - name: Enforce pam_faillock for Local Accounts Only - Ensure authselect changes + are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=after-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - result_pam_authselect_restore_features is not skipped + + - name: Enforce pam_faillock for Local Accounts Only - Change the PAM file to + be edited according to the custom authselect profile + ansible.builtin.set_fact: + pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path + | basename }} + when: + - result_authselect_present.stat.exists + + - name: Enforce pam_faillock for Local Accounts Only - Ensure the "local_users_only" + option from "pam_faillock.so" is not present in {{ pam_file_path }} + ansible.builtin.replace: + dest: '{{ pam_file_path }}' + regexp: (.*auth.*pam_faillock.so.*)\blocal_users_only\b=?[0-9a-zA-Z]*(.*) + replace: \1\2 + register: result_pam_option_removal + + - name: Enforce pam_faillock for Local Accounts Only - Ensure authselect changes + are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: + - result_authselect_present.stat.exists + - result_pam_option_removal is changed + when: + - result_pam_file_present.stat.exists + + - name: Enforce pam_faillock for Local Accounts Only - Check if /etc/pam.d/password-auth + file is present + ansible.builtin.stat: + path: /etc/pam.d/password-auth + register: result_pam_file_present + + - name: Enforce pam_faillock for Local Accounts Only - Check the proper remediation + for the system + block: + + - name: Enforce pam_faillock for Local Accounts Only - Define the PAM file to + be edited as a local fact + ansible.builtin.set_fact: + pam_file_path: /etc/pam.d/password-auth + + - name: Enforce pam_faillock for Local Accounts Only - Check if system relies + on authselect + ansible.builtin.stat: + path: /usr/bin/authselect + register: result_authselect_present + + - name: Enforce pam_faillock for Local Accounts Only - Remediate using authselect + block: + + - name: Enforce pam_faillock for Local Accounts Only - Check integrity of authselect + current profile + ansible.builtin.command: + cmd: authselect check + register: result_authselect_check_cmd + changed_when: false + ignore_errors: true + + - name: Enforce pam_faillock for Local Accounts Only - Informative message based + on the authselect integrity check result + ansible.builtin.assert: + that: + - result_authselect_check_cmd is success + fail_msg: + - authselect integrity check failed. Remediation aborted! + - This remediation could not be applied because an authselect profile was + not selected or the selected profile is not intact. + - It is not recommended to manually edit the PAM files when authselect tool + is available. + - In cases where the default authselect profile does not cover a specific + demand, a custom authselect profile is recommended. + success_msg: + - authselect integrity check passed + + - name: Enforce pam_faillock for Local Accounts Only - Get authselect current + profile + ansible.builtin.shell: + cmd: authselect current -r | awk '{ print $1 }' + register: result_authselect_profile + changed_when: false + when: + - result_authselect_check_cmd is success + + - name: Enforce pam_faillock for Local Accounts Only - Define the current authselect + profile as a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: '{{ result_authselect_profile.stdout }}' + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is match("custom/") + + - name: Enforce pam_faillock for Local Accounts Only - Define the new authselect + custom profile as a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: custom/hardening + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is not match("custom/") + + - name: Enforce pam_faillock for Local Accounts Only - Get authselect current + features to also enable them in the custom profile + ansible.builtin.shell: + cmd: authselect current | tail -n+3 | awk '{ print $2 }' + register: result_authselect_features + changed_when: false + when: + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + + - name: Enforce pam_faillock for Local Accounts Only - Check if any custom profile + with the same name was already created + ansible.builtin.stat: + path: /etc/authselect/{{ authselect_custom_profile }} + register: result_authselect_custom_profile_present + changed_when: false + when: + - authselect_current_profile is not match("custom/") + + - name: Enforce pam_faillock for Local Accounts Only - Create an authselect + custom profile based on the current profile + ansible.builtin.command: + cmd: authselect create-profile hardening -b {{ authselect_current_profile + }} + when: + - result_authselect_check_cmd is success + - authselect_current_profile is not match("custom/") + - not result_authselect_custom_profile_present.stat.exists + + - name: Enforce pam_faillock for Local Accounts Only - Ensure authselect changes + are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=before-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Enforce pam_faillock for Local Accounts Only - Ensure the authselect + custom profile is selected + ansible.builtin.command: + cmd: authselect select {{ authselect_custom_profile }} + register: result_pam_authselect_select_profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Enforce pam_faillock for Local Accounts Only - Restore the authselect + features in the custom profile + ansible.builtin.command: + cmd: authselect enable-feature {{ item }} + loop: '{{ result_authselect_features.stdout_lines }}' + register: result_pam_authselect_restore_features + when: + - result_authselect_profile is not skipped + - result_authselect_features is not skipped + - result_pam_authselect_select_profile is not skipped + + - name: Enforce pam_faillock for Local Accounts Only - Ensure authselect changes + are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=after-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - result_pam_authselect_restore_features is not skipped + + - name: Enforce pam_faillock for Local Accounts Only - Change the PAM file to + be edited according to the custom authselect profile + ansible.builtin.set_fact: + pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path + | basename }} + when: + - result_authselect_present.stat.exists + + - name: Enforce pam_faillock for Local Accounts Only - Ensure the "local_users_only" + option from "pam_faillock.so" is not present in {{ pam_file_path }} + ansible.builtin.replace: + dest: '{{ pam_file_path }}' + regexp: (.*auth.*pam_faillock.so.*)\blocal_users_only\b=?[0-9a-zA-Z]*(.*) + replace: \1\2 + register: result_pam_option_removal + + - name: Enforce pam_faillock for Local Accounts Only - Ensure authselect changes + are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: + - result_authselect_present.stat.exists + - result_pam_option_removal is changed + when: + - result_pam_file_present.stat.exists + when: + - '"pam" in ansible_facts.packages' + - result_faillock_conf_check.stat.exists + tags: + - NIST-800-53-AC-2(1) + - accounts_passwords_pam_faillock_enforce_local + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Enforce pam_faillock for Local Accounts Only - Ensure the pam_faillock.so + local_users_only parameter in PAM files + block: + + - name: Enforce pam_faillock for Local Accounts Only - Check if pam_faillock.so + local_users_only parameter is already enabled in pam files + ansible.builtin.lineinfile: + path: /etc/pam.d/system-auth + regexp: .*auth.*pam_faillock\.so (preauth|authfail).*local_users_only + state: absent + check_mode: true + changed_when: false + register: result_pam_faillock_local_users_only_parameter_is_present + + - name: Enforce pam_faillock for Local Accounts Only - Ensure the inclusion of pam_faillock.so + preauth local_users_only parameter in auth section + ansible.builtin.lineinfile: + path: '{{ item }}' + backrefs: true + regexp: (^\s*auth\s+)([\w\[].*\b)(\s+pam_faillock.so preauth.*) + line: \1required\3 local_users_only + state: present + loop: + - /etc/pam.d/system-auth + - /etc/pam.d/password-auth + when: + - result_pam_faillock_local_users_only_parameter_is_present.found == 0 + + - name: Enforce pam_faillock for Local Accounts Only - Ensure the inclusion of pam_faillock.so + authfail local_users_only parameter in auth section + ansible.builtin.lineinfile: + path: '{{ item }}' + backrefs: true + regexp: (^\s*auth\s+)([\w\[].*\b)(\s+pam_faillock.so authfail.*) + line: \1required\3 local_users_only + state: present + loop: + - /etc/pam.d/system-auth + - /etc/pam.d/password-auth + when: + - result_pam_faillock_local_users_only_parameter_is_present.found == 0 + when: + - '"pam" in ansible_facts.packages' + - not result_faillock_conf_check.stat.exists + tags: + - NIST-800-53-AC-2(1) + - accounts_passwords_pam_faillock_enforce_local + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q pam; then + +if [ -f /usr/bin/authselect ]; then + if ! authselect check; then +echo " +authselect integrity check failed. Remediation aborted! +This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact. +It is not recommended to manually edit the PAM files when authselect tool is available. +In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended." +exit 1 +fi +authselect enable-feature with-faillock + +authselect apply-changes -b +else + AUTH_FILES=("/etc/pam.d/system-auth" "/etc/pam.d/password-auth") +for pam_file in "${AUTH_FILES[@]}" +do + if ! grep -qE '^\s*auth\s+required\s+pam_faillock\.so\s+(preauth silent|authfail).*$' "$pam_file" ; then + sed -i --follow-symlinks '/^auth.*sufficient.*pam_unix\.so.*/i auth required pam_faillock.so preauth silent' "$pam_file" + sed -i --follow-symlinks '/^auth.*required.*pam_deny\.so.*/i auth required pam_faillock.so authfail' "$pam_file" + sed -i --follow-symlinks '/^account.*required.*pam_unix\.so.*/i account required pam_faillock.so' "$pam_file" + fi + sed -Ei 's/(auth.*)(\[default=die\])(.*pam_faillock\.so)/\1required \3/g' "$pam_file" +done +fi +AUTH_FILES=("/etc/pam.d/system-auth" "/etc/pam.d/password-auth") +FAILLOCK_CONF="/etc/security/faillock.conf" +if [ -f $FAILLOCK_CONF ]; then + regex="^\s*local_users_only" + line="local_users_only" + if ! grep -q $regex $FAILLOCK_CONF; then + echo $line >> $FAILLOCK_CONF + fi + for pam_file in "${AUTH_FILES[@]}" + do + if [ -e "$pam_file" ] ; then + PAM_FILE_PATH="$pam_file" + if [ -f /usr/bin/authselect ]; then + if ! authselect check; then + echo " + authselect integrity check failed. Remediation aborted! + This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact. + It is not recommended to manually edit the PAM files when authselect tool is available. + In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended." + exit 1 + fi + CURRENT_PROFILE=$(authselect current -r | awk '{ print $1 }') + # If not already in use, a custom profile is created preserving the enabled features. + if [[ ! $CURRENT_PROFILE == custom/* ]]; then + ENABLED_FEATURES=$(authselect current | tail -n+3 | awk '{ print $2 }') + authselect create-profile hardening -b $CURRENT_PROFILE + CURRENT_PROFILE="custom/hardening" + + authselect apply-changes -b --backup=before-hardening-custom-profile + authselect select $CURRENT_PROFILE + for feature in $ENABLED_FEATURES; do + authselect enable-feature $feature; + done + + authselect apply-changes -b --backup=after-hardening-custom-profile + fi + PAM_FILE_NAME=$(basename "$pam_file") + PAM_FILE_PATH="/etc/authselect/$CURRENT_PROFILE/$PAM_FILE_NAME" + + authselect apply-changes -b + fi + + if grep -qP '^\s*auth\s.*\bpam_faillock.so\s.*\blocal_users_only\b' "$PAM_FILE_PATH"; then + sed -i -E --follow-symlinks 's/(.*auth.*pam_faillock.so.*)\blocal_users_only\b=?[[:alnum:]]*(.*)/\1\2/g' "$PAM_FILE_PATH" + fi + if [ -f /usr/bin/authselect ]; then + + authselect apply-changes -b + fi + else + echo "$pam_file was not found" >&2 + fi + done +else + for pam_file in "${AUTH_FILES[@]}" + do + if ! grep -qE '^\s*auth.*pam_faillock\.so (preauth|authfail).*local_users_only' "$pam_file"; then + sed -i --follow-symlinks '/^auth.*required.*pam_faillock\.so.*preauth.*silent.*/ s/$/ local_users_only/' "$pam_file" + sed -i --follow-symlinks '/^auth.*required.*pam_faillock\.so.*authfail.*/ s/$/ local_users_only/' "$pam_file" + fi + done +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Set Interval For Counting Failed Password Attempts + Utilizing pam_faillock.so, the fail_interval directive configures the system +to lock out an account after a number of incorrect login attempts within a specified time +period. + If the system relies on authselect tool to manage PAM settings, the remediation +will also use authselect tool. However, if any manual modification was made in +PAM files, the authselect integrity check will fail and the remediation will be +aborted in order to preserve intentional changes. In this case, an informative message will +be shown in the remediation report. +If the system supports the /etc/security/faillock.conf file, the pam_faillock +parameters should be defined in faillock.conf file. + BP28(R18) + 1 + 12 + 15 + 16 + DSS05.04 + DSS05.10 + DSS06.10 + CCI-000044 + CCI-002236 + CCI-002237 + CCI-002238 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + 0421 + 0422 + 0431 + 0974 + 1173 + 1401 + 1504 + 1505 + 1546 + 1557 + 1558 + 1559 + 1560 + 1561 + A.18.1.4 + A.9.2.1 + A.9.2.4 + A.9.3.1 + A.9.4.2 + A.9.4.3 + CM-6(a) + AC-7(a) + PR.AC-7 + FIA_AFL.1 + SRG-OS-000329-GPOS-00128 + SRG-OS-000021-GPOS-00005 + SRG-OS-000021-VMM-000050 + By limiting the number of failed logon attempts the risk of unauthorized system +access via user password guessing, otherwise known as brute-forcing, is reduced. +Limits are imposed by locking the account. + + CCE-83583-5 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83583-5 + - NIST-800-53-AC-7(a) + - NIST-800-53-CM-6(a) + - accounts_passwords_pam_faillock_interval + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Set Interval For Counting Failed Password Attempts - Check if system relies + on authselect tool + ansible.builtin.stat: + path: /usr/bin/authselect + register: result_authselect_present + when: '"pam" in ansible_facts.packages' + tags: + - CCE-83583-5 + - NIST-800-53-AC-7(a) + - NIST-800-53-CM-6(a) + - accounts_passwords_pam_faillock_interval + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Set Interval For Counting Failed Password Attempts - Remediation where authselect + tool is present + block: + + - name: Set Interval For Counting Failed Password Attempts - Check integrity of + authselect current profile + ansible.builtin.command: + cmd: authselect check + register: result_authselect_check_cmd + changed_when: false + ignore_errors: true + + - name: Set Interval For Counting Failed Password Attempts - Informative message + based on the authselect integrity check result + ansible.builtin.assert: + that: + - result_authselect_check_cmd is success + fail_msg: + - authselect integrity check failed. Remediation aborted! + - This remediation could not be applied because an authselect profile was not + selected or the selected profile is not intact. + - It is not recommended to manually edit the PAM files when authselect tool + is available. + - In cases where the default authselect profile does not cover a specific demand, + a custom authselect profile is recommended. + success_msg: + - authselect integrity check passed + + - name: Set Interval For Counting Failed Password Attempts - Get authselect current + features + ansible.builtin.shell: + cmd: authselect current | tail -n+3 | awk '{ print $2 }' + register: result_authselect_features + changed_when: false + when: + - result_authselect_check_cmd is success + + - name: Set Interval For Counting Failed Password Attempts - Ensure "with-faillock" + feature is enabled using authselect tool + ansible.builtin.command: + cmd: authselect enable-feature with-faillock + register: result_authselect_enable_feature_cmd + when: + - result_authselect_check_cmd is success + - result_authselect_features.stdout is not search("with-faillock") + + - name: Set Interval For Counting Failed Password Attempts - Ensure authselect changes + are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: + - result_authselect_enable_feature_cmd is not skipped + - result_authselect_enable_feature_cmd is success + when: + - '"pam" in ansible_facts.packages' + - result_authselect_present.stat.exists + tags: + - CCE-83583-5 + - NIST-800-53-AC-7(a) + - NIST-800-53-CM-6(a) + - accounts_passwords_pam_faillock_interval + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Set Interval For Counting Failed Password Attempts - Remediation where authselect + tool is not present + block: + + - name: Set Interval For Counting Failed Password Attempts - Check if pam_faillock.so + is already enabled + ansible.builtin.lineinfile: + path: /etc/pam.d/system-auth + regexp: .*auth.*pam_faillock\.so (preauth|authfail) + state: absent + check_mode: true + changed_when: false + register: result_pam_faillock_is_enabled + + - name: Set Interval For Counting Failed Password Attempts - Enable pam_faillock.so + preauth editing PAM files + ansible.builtin.lineinfile: + path: '{{ item }}' + line: auth required pam_faillock.so preauth + insertbefore: ^auth.*sufficient.*pam_unix\.so.* + state: present + loop: + - /etc/pam.d/system-auth + - /etc/pam.d/password-auth + when: + - result_pam_faillock_is_enabled.found == 0 + + - name: Set Interval For Counting Failed Password Attempts - Enable pam_faillock.so + authfail editing PAM files + ansible.builtin.lineinfile: + path: '{{ item }}' + line: auth required pam_faillock.so authfail + insertbefore: ^auth.*required.*pam_deny\.so.* + state: present + loop: + - /etc/pam.d/system-auth + - /etc/pam.d/password-auth + when: + - result_pam_faillock_is_enabled.found == 0 + + - name: Set Interval For Counting Failed Password Attempts - Enable pam_faillock.so + account section editing PAM files + ansible.builtin.lineinfile: + path: '{{ item }}' + line: account required pam_faillock.so + insertbefore: ^account.*required.*pam_unix\.so.* + state: present + loop: + - /etc/pam.d/system-auth + - /etc/pam.d/password-auth + when: + - result_pam_faillock_is_enabled.found == 0 + when: + - '"pam" in ansible_facts.packages' + - not result_authselect_present.stat.exists + tags: + - CCE-83583-5 + - NIST-800-53-AC-7(a) + - NIST-800-53-CM-6(a) + - accounts_passwords_pam_faillock_interval + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy +- name: XCCDF Value var_accounts_passwords_pam_faillock_fail_interval # promote to variable + set_fact: + var_accounts_passwords_pam_faillock_fail_interval: !!str + tags: + - always + +- name: Set Interval For Counting Failed Password Attempts - Check the presence of + /etc/security/faillock.conf file + ansible.builtin.stat: + path: /etc/security/faillock.conf + register: result_faillock_conf_check + when: '"pam" in ansible_facts.packages' + tags: + - CCE-83583-5 + - NIST-800-53-AC-7(a) + - NIST-800-53-CM-6(a) + - accounts_passwords_pam_faillock_interval + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Set Interval For Counting Failed Password Attempts - Ensure the pam_faillock.so + fail_interval parameter in /etc/security/faillock.conf + ansible.builtin.lineinfile: + path: /etc/security/faillock.conf + regexp: ^\s*fail_interval\s*= + line: fail_interval = {{ var_accounts_passwords_pam_faillock_fail_interval }} + state: present + when: + - '"pam" in ansible_facts.packages' + - result_faillock_conf_check.stat.exists + tags: + - CCE-83583-5 + - NIST-800-53-AC-7(a) + - NIST-800-53-CM-6(a) + - accounts_passwords_pam_faillock_interval + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Set Interval For Counting Failed Password Attempts - Ensure the pam_faillock.so + fail_interval parameter not in PAM files + block: + + - name: Set Interval For Counting Failed Password Attempts - Check if /etc/pam.d/system-auth + file is present + ansible.builtin.stat: + path: /etc/pam.d/system-auth + register: result_pam_file_present + + - name: Set Interval For Counting Failed Password Attempts - Check the proper remediation + for the system + block: + + - name: Set Interval For Counting Failed Password Attempts - Define the PAM file + to be edited as a local fact + ansible.builtin.set_fact: + pam_file_path: /etc/pam.d/system-auth + + - name: Set Interval For Counting Failed Password Attempts - Check if system relies + on authselect + ansible.builtin.stat: + path: /usr/bin/authselect + register: result_authselect_present + + - name: Set Interval For Counting Failed Password Attempts - Remediate using authselect + block: + + - name: Set Interval For Counting Failed Password Attempts - Check integrity + of authselect current profile + ansible.builtin.command: + cmd: authselect check + register: result_authselect_check_cmd + changed_when: false + ignore_errors: true + + - name: Set Interval For Counting Failed Password Attempts - Informative message + based on the authselect integrity check result + ansible.builtin.assert: + that: + - result_authselect_check_cmd is success + fail_msg: + - authselect integrity check failed. Remediation aborted! + - This remediation could not be applied because an authselect profile was + not selected or the selected profile is not intact. + - It is not recommended to manually edit the PAM files when authselect tool + is available. + - In cases where the default authselect profile does not cover a specific + demand, a custom authselect profile is recommended. + success_msg: + - authselect integrity check passed + + - name: Set Interval For Counting Failed Password Attempts - Get authselect + current profile + ansible.builtin.shell: + cmd: authselect current -r | awk '{ print $1 }' + register: result_authselect_profile + changed_when: false + when: + - result_authselect_check_cmd is success + + - name: Set Interval For Counting Failed Password Attempts - Define the current + authselect profile as a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: '{{ result_authselect_profile.stdout }}' + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is match("custom/") + + - name: Set Interval For Counting Failed Password Attempts - Define the new + authselect custom profile as a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: custom/hardening + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is not match("custom/") + + - name: Set Interval For Counting Failed Password Attempts - Get authselect + current features to also enable them in the custom profile + ansible.builtin.shell: + cmd: authselect current | tail -n+3 | awk '{ print $2 }' + register: result_authselect_features + changed_when: false + when: + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + + - name: Set Interval For Counting Failed Password Attempts - Check if any custom + profile with the same name was already created + ansible.builtin.stat: + path: /etc/authselect/{{ authselect_custom_profile }} + register: result_authselect_custom_profile_present + changed_when: false + when: + - authselect_current_profile is not match("custom/") + + - name: Set Interval For Counting Failed Password Attempts - Create an authselect + custom profile based on the current profile + ansible.builtin.command: + cmd: authselect create-profile hardening -b {{ authselect_current_profile + }} + when: + - result_authselect_check_cmd is success + - authselect_current_profile is not match("custom/") + - not result_authselect_custom_profile_present.stat.exists + + - name: Set Interval For Counting Failed Password Attempts - Ensure authselect + changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=before-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Set Interval For Counting Failed Password Attempts - Ensure the authselect + custom profile is selected + ansible.builtin.command: + cmd: authselect select {{ authselect_custom_profile }} + register: result_pam_authselect_select_profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Set Interval For Counting Failed Password Attempts - Restore the authselect + features in the custom profile + ansible.builtin.command: + cmd: authselect enable-feature {{ item }} + loop: '{{ result_authselect_features.stdout_lines }}' + register: result_pam_authselect_restore_features + when: + - result_authselect_profile is not skipped + - result_authselect_features is not skipped + - result_pam_authselect_select_profile is not skipped + + - name: Set Interval For Counting Failed Password Attempts - Ensure authselect + changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=after-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - result_pam_authselect_restore_features is not skipped + + - name: Set Interval For Counting Failed Password Attempts - Change the PAM + file to be edited according to the custom authselect profile + ansible.builtin.set_fact: + pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path + | basename }} + when: + - result_authselect_present.stat.exists + + - name: Set Interval For Counting Failed Password Attempts - Ensure the "fail_interval" + option from "pam_faillock.so" is not present in {{ pam_file_path }} + ansible.builtin.replace: + dest: '{{ pam_file_path }}' + regexp: (.*auth.*pam_faillock.so.*)\bfail_interval\b=?[0-9a-zA-Z]*(.*) + replace: \1\2 + register: result_pam_option_removal + + - name: Set Interval For Counting Failed Password Attempts - Ensure authselect + changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: + - result_authselect_present.stat.exists + - result_pam_option_removal is changed + when: + - result_pam_file_present.stat.exists + + - name: Set Interval For Counting Failed Password Attempts - Check if /etc/pam.d/password-auth + file is present + ansible.builtin.stat: + path: /etc/pam.d/password-auth + register: result_pam_file_present + + - name: Set Interval For Counting Failed Password Attempts - Check the proper remediation + for the system + block: + + - name: Set Interval For Counting Failed Password Attempts - Define the PAM file + to be edited as a local fact + ansible.builtin.set_fact: + pam_file_path: /etc/pam.d/password-auth + + - name: Set Interval For Counting Failed Password Attempts - Check if system relies + on authselect + ansible.builtin.stat: + path: /usr/bin/authselect + register: result_authselect_present + + - name: Set Interval For Counting Failed Password Attempts - Remediate using authselect + block: + + - name: Set Interval For Counting Failed Password Attempts - Check integrity + of authselect current profile + ansible.builtin.command: + cmd: authselect check + register: result_authselect_check_cmd + changed_when: false + ignore_errors: true + + - name: Set Interval For Counting Failed Password Attempts - Informative message + based on the authselect integrity check result + ansible.builtin.assert: + that: + - result_authselect_check_cmd is success + fail_msg: + - authselect integrity check failed. Remediation aborted! + - This remediation could not be applied because an authselect profile was + not selected or the selected profile is not intact. + - It is not recommended to manually edit the PAM files when authselect tool + is available. + - In cases where the default authselect profile does not cover a specific + demand, a custom authselect profile is recommended. + success_msg: + - authselect integrity check passed + + - name: Set Interval For Counting Failed Password Attempts - Get authselect + current profile + ansible.builtin.shell: + cmd: authselect current -r | awk '{ print $1 }' + register: result_authselect_profile + changed_when: false + when: + - result_authselect_check_cmd is success + + - name: Set Interval For Counting Failed Password Attempts - Define the current + authselect profile as a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: '{{ result_authselect_profile.stdout }}' + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is match("custom/") + + - name: Set Interval For Counting Failed Password Attempts - Define the new + authselect custom profile as a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: custom/hardening + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is not match("custom/") + + - name: Set Interval For Counting Failed Password Attempts - Get authselect + current features to also enable them in the custom profile + ansible.builtin.shell: + cmd: authselect current | tail -n+3 | awk '{ print $2 }' + register: result_authselect_features + changed_when: false + when: + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + + - name: Set Interval For Counting Failed Password Attempts - Check if any custom + profile with the same name was already created + ansible.builtin.stat: + path: /etc/authselect/{{ authselect_custom_profile }} + register: result_authselect_custom_profile_present + changed_when: false + when: + - authselect_current_profile is not match("custom/") + + - name: Set Interval For Counting Failed Password Attempts - Create an authselect + custom profile based on the current profile + ansible.builtin.command: + cmd: authselect create-profile hardening -b {{ authselect_current_profile + }} + when: + - result_authselect_check_cmd is success + - authselect_current_profile is not match("custom/") + - not result_authselect_custom_profile_present.stat.exists + + - name: Set Interval For Counting Failed Password Attempts - Ensure authselect + changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=before-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Set Interval For Counting Failed Password Attempts - Ensure the authselect + custom profile is selected + ansible.builtin.command: + cmd: authselect select {{ authselect_custom_profile }} + register: result_pam_authselect_select_profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Set Interval For Counting Failed Password Attempts - Restore the authselect + features in the custom profile + ansible.builtin.command: + cmd: authselect enable-feature {{ item }} + loop: '{{ result_authselect_features.stdout_lines }}' + register: result_pam_authselect_restore_features + when: + - result_authselect_profile is not skipped + - result_authselect_features is not skipped + - result_pam_authselect_select_profile is not skipped + + - name: Set Interval For Counting Failed Password Attempts - Ensure authselect + changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=after-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - result_pam_authselect_restore_features is not skipped + + - name: Set Interval For Counting Failed Password Attempts - Change the PAM + file to be edited according to the custom authselect profile + ansible.builtin.set_fact: + pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path + | basename }} + when: + - result_authselect_present.stat.exists + + - name: Set Interval For Counting Failed Password Attempts - Ensure the "fail_interval" + option from "pam_faillock.so" is not present in {{ pam_file_path }} + ansible.builtin.replace: + dest: '{{ pam_file_path }}' + regexp: (.*auth.*pam_faillock.so.*)\bfail_interval\b=?[0-9a-zA-Z]*(.*) + replace: \1\2 + register: result_pam_option_removal + + - name: Set Interval For Counting Failed Password Attempts - Ensure authselect + changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: + - result_authselect_present.stat.exists + - result_pam_option_removal is changed + when: + - result_pam_file_present.stat.exists + when: + - '"pam" in ansible_facts.packages' + - result_faillock_conf_check.stat.exists + tags: + - CCE-83583-5 + - NIST-800-53-AC-7(a) + - NIST-800-53-CM-6(a) + - accounts_passwords_pam_faillock_interval + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Set Interval For Counting Failed Password Attempts - Ensure the pam_faillock.so + fail_interval parameter in PAM files + block: + + - name: Set Interval For Counting Failed Password Attempts - Check if pam_faillock.so + fail_interval parameter is already enabled in pam files + ansible.builtin.lineinfile: + path: /etc/pam.d/system-auth + regexp: .*auth.*pam_faillock\.so (preauth|authfail).*fail_interval + state: absent + check_mode: true + changed_when: false + register: result_pam_faillock_fail_interval_parameter_is_present + + - name: Set Interval For Counting Failed Password Attempts - Ensure the inclusion + of pam_faillock.so preauth fail_interval parameter in auth section + ansible.builtin.lineinfile: + path: '{{ item }}' + backrefs: true + regexp: (^\s*auth\s+)([\w\[].*\b)(\s+pam_faillock.so preauth.*) + line: \1required\3 fail_interval={{ var_accounts_passwords_pam_faillock_fail_interval + }} + state: present + loop: + - /etc/pam.d/system-auth + - /etc/pam.d/password-auth + when: + - result_pam_faillock_fail_interval_parameter_is_present.found == 0 + + - name: Set Interval For Counting Failed Password Attempts - Ensure the inclusion + of pam_faillock.so authfail fail_interval parameter in auth section + ansible.builtin.lineinfile: + path: '{{ item }}' + backrefs: true + regexp: (^\s*auth\s+)([\w\[].*\b)(\s+pam_faillock.so authfail.*) + line: \1required\3 fail_interval={{ var_accounts_passwords_pam_faillock_fail_interval + }} + state: present + loop: + - /etc/pam.d/system-auth + - /etc/pam.d/password-auth + when: + - result_pam_faillock_fail_interval_parameter_is_present.found == 0 + + - name: Set Interval For Counting Failed Password Attempts - Ensure the desired + value for pam_faillock.so preauth fail_interval parameter in auth section + ansible.builtin.lineinfile: + path: '{{ item }}' + backrefs: true + regexp: (^\s*auth\s+)([\w\[].*\b)(\s+pam_faillock.so preauth.*)(fail_interval)=[0-9]+(.*) + line: \1required\3\4={{ var_accounts_passwords_pam_faillock_fail_interval }}\5 + state: present + loop: + - /etc/pam.d/system-auth + - /etc/pam.d/password-auth + when: + - result_pam_faillock_fail_interval_parameter_is_present.found > 0 + + - name: Set Interval For Counting Failed Password Attempts - Ensure the desired + value for pam_faillock.so authfail fail_interval parameter in auth section + ansible.builtin.lineinfile: + path: '{{ item }}' + backrefs: true + regexp: (^\s*auth\s+)([\w\[].*\b)(\s+pam_faillock.so authfail.*)(fail_interval)=[0-9]+(.*) + line: \1required\3\4={{ var_accounts_passwords_pam_faillock_fail_interval }}\5 + state: present + loop: + - /etc/pam.d/system-auth + - /etc/pam.d/password-auth + when: + - result_pam_faillock_fail_interval_parameter_is_present.found > 0 + when: + - '"pam" in ansible_facts.packages' + - not result_faillock_conf_check.stat.exists + tags: + - CCE-83583-5 + - NIST-800-53-AC-7(a) + - NIST-800-53-CM-6(a) + - accounts_passwords_pam_faillock_interval + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q pam; then + +var_accounts_passwords_pam_faillock_fail_interval='' + + +if [ -f /usr/bin/authselect ]; then + if ! authselect check; then +echo " +authselect integrity check failed. Remediation aborted! +This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact. +It is not recommended to manually edit the PAM files when authselect tool is available. +In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended." +exit 1 +fi +authselect enable-feature with-faillock + +authselect apply-changes -b +else + AUTH_FILES=("/etc/pam.d/system-auth" "/etc/pam.d/password-auth") +for pam_file in "${AUTH_FILES[@]}" +do + if ! grep -qE '^\s*auth\s+required\s+pam_faillock\.so\s+(preauth silent|authfail).*$' "$pam_file" ; then + sed -i --follow-symlinks '/^auth.*sufficient.*pam_unix\.so.*/i auth required pam_faillock.so preauth silent' "$pam_file" + sed -i --follow-symlinks '/^auth.*required.*pam_deny\.so.*/i auth required pam_faillock.so authfail' "$pam_file" + sed -i --follow-symlinks '/^account.*required.*pam_unix\.so.*/i account required pam_faillock.so' "$pam_file" + fi + sed -Ei 's/(auth.*)(\[default=die\])(.*pam_faillock\.so)/\1required \3/g' "$pam_file" +done +fi +AUTH_FILES=("/etc/pam.d/system-auth" "/etc/pam.d/password-auth") +FAILLOCK_CONF="/etc/security/faillock.conf" +if [ -f $FAILLOCK_CONF ]; then + regex="^\s*fail_interval\s*=" + line="fail_interval = $var_accounts_passwords_pam_faillock_fail_interval" + if ! grep -q $regex $FAILLOCK_CONF; then + echo $line >> $FAILLOCK_CONF + else + sed -i --follow-symlinks 's|^\s*\(fail_interval\s*=\s*\)\(\S\+\)|\1'"$var_accounts_passwords_pam_faillock_fail_interval"'|g' $FAILLOCK_CONF + fi + for pam_file in "${AUTH_FILES[@]}" + do + if [ -e "$pam_file" ] ; then + PAM_FILE_PATH="$pam_file" + if [ -f /usr/bin/authselect ]; then + if ! authselect check; then + echo " + authselect integrity check failed. Remediation aborted! + This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact. + It is not recommended to manually edit the PAM files when authselect tool is available. + In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended." + exit 1 + fi + CURRENT_PROFILE=$(authselect current -r | awk '{ print $1 }') + # If not already in use, a custom profile is created preserving the enabled features. + if [[ ! $CURRENT_PROFILE == custom/* ]]; then + ENABLED_FEATURES=$(authselect current | tail -n+3 | awk '{ print $2 }') + authselect create-profile hardening -b $CURRENT_PROFILE + CURRENT_PROFILE="custom/hardening" + + authselect apply-changes -b --backup=before-hardening-custom-profile + authselect select $CURRENT_PROFILE + for feature in $ENABLED_FEATURES; do + authselect enable-feature $feature; + done + + authselect apply-changes -b --backup=after-hardening-custom-profile + fi + PAM_FILE_NAME=$(basename "$pam_file") + PAM_FILE_PATH="/etc/authselect/$CURRENT_PROFILE/$PAM_FILE_NAME" + + authselect apply-changes -b + fi + + if grep -qP '^\s*auth\s.*\bpam_faillock.so\s.*\bfail_interval\b' "$PAM_FILE_PATH"; then + sed -i -E --follow-symlinks 's/(.*auth.*pam_faillock.so.*)\bfail_interval\b=?[[:alnum:]]*(.*)/\1\2/g' "$PAM_FILE_PATH" + fi + if [ -f /usr/bin/authselect ]; then + + authselect apply-changes -b + fi + else + echo "$pam_file was not found" >&2 + fi + done +else + for pam_file in "${AUTH_FILES[@]}" + do + if ! grep -qE '^\s*auth.*pam_faillock\.so (preauth|authfail).*fail_interval' "$pam_file"; then + sed -i --follow-symlinks '/^auth.*required.*pam_faillock\.so.*preauth.*silent.*/ s/$/ fail_interval='"$var_accounts_passwords_pam_faillock_fail_interval"'/' "$pam_file" + sed -i --follow-symlinks '/^auth.*required.*pam_faillock\.so.*authfail.*/ s/$/ fail_interval='"$var_accounts_passwords_pam_faillock_fail_interval"'/' "$pam_file" + else + sed -i --follow-symlinks 's/\(^auth.*required.*pam_faillock\.so.*preauth.*silent.*\)\('"fail_interval"'=\)[0-9]\+\(.*\)/\1\2'"$var_accounts_passwords_pam_faillock_fail_interval"'\3/' "$pam_file" + sed -i --follow-symlinks 's/\(^auth.*required.*pam_faillock\.so.*authfail.*\)\('"fail_interval"'=\)[0-9]\+\(.*\)/\1\2'"$var_accounts_passwords_pam_faillock_fail_interval"'\3/' "$pam_file" + fi + done +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Set Lockout Time for Failed Password Attempts + This rule configures the system to lock out accounts during a specified time period after a +number of incorrect login attempts using pam_faillock.so. + +pam_faillock.so module requires multiple entries in pam files. These entries must be carefully +defined to work as expected. In order to avoid any errors when manually editing these files, +it is recommended to use the appropriate tools, such as authselect or authconfig, +depending on the OS version. + +If unlock_time is set to 0, manual intervention by an administrator is required +to unlock a user. This should be done using the faillock tool. + If the system supports the new /etc/security/faillock.conf file but the +pam_faillock.so parameters are defined directly in /etc/pam.d/system-auth and +/etc/pam.d/password-auth, the remediation will migrate the unlock_time parameter +to /etc/security/faillock.conf to ensure compatibility with authselect tool. +The parameters deny and fail_interval, if used, also have to be migrated +by their respective remediation. + If the system relies on authselect tool to manage PAM settings, the remediation +will also use authselect tool. However, if any manual modification was made in +PAM files, the authselect integrity check will fail and the remediation will be +aborted in order to preserve intentional changes. In this case, an informative message will +be shown in the remediation report. +If the system supports the /etc/security/faillock.conf file, the pam_faillock +parameters should be defined in faillock.conf file. + BP28(R18) + 1 + 12 + 15 + 16 + 5.5.3 + DSS05.04 + DSS05.10 + DSS06.10 + 3.1.8 + CCI-000044 + CCI-002236 + CCI-002237 + CCI-002238 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + 0421 + 0422 + 0431 + 0974 + 1173 + 1401 + 1504 + 1505 + 1546 + 1557 + 1558 + 1559 + 1560 + 1561 + A.18.1.4 + A.9.2.1 + A.9.2.4 + A.9.3.1 + A.9.4.2 + A.9.4.3 + CM-6(a) + AC-7(b) + PR.AC-7 + FIA_AFL.1 + Req-8.1.7 + SRG-OS-000329-GPOS-00128 + SRG-OS-000021-GPOS-00005 + SRG-OS-000329-VMM-001180 + By limiting the number of failed logon attempts the risk of unauthorized system +access via user password guessing, otherwise known as brute-forcing, is reduced. +Limits are imposed by locking the account. + + CCE-83588-4 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83588-4 + - CJIS-5.5.3 + - NIST-800-171-3.1.8 + - NIST-800-53-AC-7(b) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.1.7 + - accounts_passwords_pam_faillock_unlock_time + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Set Lockout Time for Failed Password Attempts - Check if system relies on + authselect tool + ansible.builtin.stat: + path: /usr/bin/authselect + register: result_authselect_present + when: '"pam" in ansible_facts.packages' + tags: + - CCE-83588-4 + - CJIS-5.5.3 + - NIST-800-171-3.1.8 + - NIST-800-53-AC-7(b) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.1.7 + - accounts_passwords_pam_faillock_unlock_time + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Set Lockout Time for Failed Password Attempts - Remediation where authselect + tool is present + block: + + - name: Set Lockout Time for Failed Password Attempts - Check integrity of authselect + current profile + ansible.builtin.command: + cmd: authselect check + register: result_authselect_check_cmd + changed_when: false + ignore_errors: true + + - name: Set Lockout Time for Failed Password Attempts - Informative message based + on the authselect integrity check result + ansible.builtin.assert: + that: + - result_authselect_check_cmd is success + fail_msg: + - authselect integrity check failed. Remediation aborted! + - This remediation could not be applied because an authselect profile was not + selected or the selected profile is not intact. + - It is not recommended to manually edit the PAM files when authselect tool + is available. + - In cases where the default authselect profile does not cover a specific demand, + a custom authselect profile is recommended. + success_msg: + - authselect integrity check passed + + - name: Set Lockout Time for Failed Password Attempts - Get authselect current features + ansible.builtin.shell: + cmd: authselect current | tail -n+3 | awk '{ print $2 }' + register: result_authselect_features + changed_when: false + when: + - result_authselect_check_cmd is success + + - name: Set Lockout Time for Failed Password Attempts - Ensure "with-faillock" feature + is enabled using authselect tool + ansible.builtin.command: + cmd: authselect enable-feature with-faillock + register: result_authselect_enable_feature_cmd + when: + - result_authselect_check_cmd is success + - result_authselect_features.stdout is not search("with-faillock") + + - name: Set Lockout Time for Failed Password Attempts - Ensure authselect changes + are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: + - result_authselect_enable_feature_cmd is not skipped + - result_authselect_enable_feature_cmd is success + when: + - '"pam" in ansible_facts.packages' + - result_authselect_present.stat.exists + tags: + - CCE-83588-4 + - CJIS-5.5.3 + - NIST-800-171-3.1.8 + - NIST-800-53-AC-7(b) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.1.7 + - accounts_passwords_pam_faillock_unlock_time + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Set Lockout Time for Failed Password Attempts - Remediation where authselect + tool is not present + block: + + - name: Set Lockout Time for Failed Password Attempts - Check if pam_faillock.so + is already enabled + ansible.builtin.lineinfile: + path: /etc/pam.d/system-auth + regexp: .*auth.*pam_faillock\.so (preauth|authfail) + state: absent + check_mode: true + changed_when: false + register: result_pam_faillock_is_enabled + + - name: Set Lockout Time for Failed Password Attempts - Enable pam_faillock.so preauth + editing PAM files + ansible.builtin.lineinfile: + path: '{{ item }}' + line: auth required pam_faillock.so preauth + insertbefore: ^auth.*sufficient.*pam_unix\.so.* + state: present + loop: + - /etc/pam.d/system-auth + - /etc/pam.d/password-auth + when: + - result_pam_faillock_is_enabled.found == 0 + + - name: Set Lockout Time for Failed Password Attempts - Enable pam_faillock.so authfail + editing PAM files + ansible.builtin.lineinfile: + path: '{{ item }}' + line: auth required pam_faillock.so authfail + insertbefore: ^auth.*required.*pam_deny\.so.* + state: present + loop: + - /etc/pam.d/system-auth + - /etc/pam.d/password-auth + when: + - result_pam_faillock_is_enabled.found == 0 + + - name: Set Lockout Time for Failed Password Attempts - Enable pam_faillock.so account + section editing PAM files + ansible.builtin.lineinfile: + path: '{{ item }}' + line: account required pam_faillock.so + insertbefore: ^account.*required.*pam_unix\.so.* + state: present + loop: + - /etc/pam.d/system-auth + - /etc/pam.d/password-auth + when: + - result_pam_faillock_is_enabled.found == 0 + when: + - '"pam" in ansible_facts.packages' + - not result_authselect_present.stat.exists + tags: + - CCE-83588-4 + - CJIS-5.5.3 + - NIST-800-171-3.1.8 + - NIST-800-53-AC-7(b) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.1.7 + - accounts_passwords_pam_faillock_unlock_time + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy +- name: XCCDF Value var_accounts_passwords_pam_faillock_unlock_time # promote to variable + set_fact: + var_accounts_passwords_pam_faillock_unlock_time: !!str + tags: + - always + +- name: Set Lockout Time for Failed Password Attempts - Check the presence of /etc/security/faillock.conf + file + ansible.builtin.stat: + path: /etc/security/faillock.conf + register: result_faillock_conf_check + when: '"pam" in ansible_facts.packages' + tags: + - CCE-83588-4 + - CJIS-5.5.3 + - NIST-800-171-3.1.8 + - NIST-800-53-AC-7(b) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.1.7 + - accounts_passwords_pam_faillock_unlock_time + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Set Lockout Time for Failed Password Attempts - Ensure the pam_faillock.so + unlock_time parameter in /etc/security/faillock.conf + ansible.builtin.lineinfile: + path: /etc/security/faillock.conf + regexp: ^\s*unlock_time\s*= + line: unlock_time = {{ var_accounts_passwords_pam_faillock_unlock_time }} + state: present + when: + - '"pam" in ansible_facts.packages' + - result_faillock_conf_check.stat.exists + tags: + - CCE-83588-4 + - CJIS-5.5.3 + - NIST-800-171-3.1.8 + - NIST-800-53-AC-7(b) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.1.7 + - accounts_passwords_pam_faillock_unlock_time + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Set Lockout Time for Failed Password Attempts - Ensure the pam_faillock.so + unlock_time parameter not in PAM files + block: + + - name: Set Lockout Time for Failed Password Attempts - Check if /etc/pam.d/system-auth + file is present + ansible.builtin.stat: + path: /etc/pam.d/system-auth + register: result_pam_file_present + + - name: Set Lockout Time for Failed Password Attempts - Check the proper remediation + for the system + block: + + - name: Set Lockout Time for Failed Password Attempts - Define the PAM file to + be edited as a local fact + ansible.builtin.set_fact: + pam_file_path: /etc/pam.d/system-auth + + - name: Set Lockout Time for Failed Password Attempts - Check if system relies + on authselect + ansible.builtin.stat: + path: /usr/bin/authselect + register: result_authselect_present + + - name: Set Lockout Time for Failed Password Attempts - Remediate using authselect + block: + + - name: Set Lockout Time for Failed Password Attempts - Check integrity of authselect + current profile + ansible.builtin.command: + cmd: authselect check + register: result_authselect_check_cmd + changed_when: false + ignore_errors: true + + - name: Set Lockout Time for Failed Password Attempts - Informative message + based on the authselect integrity check result + ansible.builtin.assert: + that: + - result_authselect_check_cmd is success + fail_msg: + - authselect integrity check failed. Remediation aborted! + - This remediation could not be applied because an authselect profile was + not selected or the selected profile is not intact. + - It is not recommended to manually edit the PAM files when authselect tool + is available. + - In cases where the default authselect profile does not cover a specific + demand, a custom authselect profile is recommended. + success_msg: + - authselect integrity check passed + + - name: Set Lockout Time for Failed Password Attempts - Get authselect current + profile + ansible.builtin.shell: + cmd: authselect current -r | awk '{ print $1 }' + register: result_authselect_profile + changed_when: false + when: + - result_authselect_check_cmd is success + + - name: Set Lockout Time for Failed Password Attempts - Define the current authselect + profile as a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: '{{ result_authselect_profile.stdout }}' + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is match("custom/") + + - name: Set Lockout Time for Failed Password Attempts - Define the new authselect + custom profile as a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: custom/hardening + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is not match("custom/") + + - name: Set Lockout Time for Failed Password Attempts - Get authselect current + features to also enable them in the custom profile + ansible.builtin.shell: + cmd: authselect current | tail -n+3 | awk '{ print $2 }' + register: result_authselect_features + changed_when: false + when: + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + + - name: Set Lockout Time for Failed Password Attempts - Check if any custom + profile with the same name was already created + ansible.builtin.stat: + path: /etc/authselect/{{ authselect_custom_profile }} + register: result_authselect_custom_profile_present + changed_when: false + when: + - authselect_current_profile is not match("custom/") + + - name: Set Lockout Time for Failed Password Attempts - Create an authselect + custom profile based on the current profile + ansible.builtin.command: + cmd: authselect create-profile hardening -b {{ authselect_current_profile + }} + when: + - result_authselect_check_cmd is success + - authselect_current_profile is not match("custom/") + - not result_authselect_custom_profile_present.stat.exists + + - name: Set Lockout Time for Failed Password Attempts - Ensure authselect changes + are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=before-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Set Lockout Time for Failed Password Attempts - Ensure the authselect + custom profile is selected + ansible.builtin.command: + cmd: authselect select {{ authselect_custom_profile }} + register: result_pam_authselect_select_profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Set Lockout Time for Failed Password Attempts - Restore the authselect + features in the custom profile + ansible.builtin.command: + cmd: authselect enable-feature {{ item }} + loop: '{{ result_authselect_features.stdout_lines }}' + register: result_pam_authselect_restore_features + when: + - result_authselect_profile is not skipped + - result_authselect_features is not skipped + - result_pam_authselect_select_profile is not skipped + + - name: Set Lockout Time for Failed Password Attempts - Ensure authselect changes + are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=after-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - result_pam_authselect_restore_features is not skipped + + - name: Set Lockout Time for Failed Password Attempts - Change the PAM file + to be edited according to the custom authselect profile + ansible.builtin.set_fact: + pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path + | basename }} + when: + - result_authselect_present.stat.exists + + - name: Set Lockout Time for Failed Password Attempts - Ensure the "unlock_time" + option from "pam_faillock.so" is not present in {{ pam_file_path }} + ansible.builtin.replace: + dest: '{{ pam_file_path }}' + regexp: (.*auth.*pam_faillock.so.*)\bunlock_time\b=?[0-9a-zA-Z]*(.*) + replace: \1\2 + register: result_pam_option_removal + + - name: Set Lockout Time for Failed Password Attempts - Ensure authselect changes + are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: + - result_authselect_present.stat.exists + - result_pam_option_removal is changed + when: + - result_pam_file_present.stat.exists + + - name: Set Lockout Time for Failed Password Attempts - Check if /etc/pam.d/password-auth + file is present + ansible.builtin.stat: + path: /etc/pam.d/password-auth + register: result_pam_file_present + + - name: Set Lockout Time for Failed Password Attempts - Check the proper remediation + for the system + block: + + - name: Set Lockout Time for Failed Password Attempts - Define the PAM file to + be edited as a local fact + ansible.builtin.set_fact: + pam_file_path: /etc/pam.d/password-auth + + - name: Set Lockout Time for Failed Password Attempts - Check if system relies + on authselect + ansible.builtin.stat: + path: /usr/bin/authselect + register: result_authselect_present + + - name: Set Lockout Time for Failed Password Attempts - Remediate using authselect + block: + + - name: Set Lockout Time for Failed Password Attempts - Check integrity of authselect + current profile + ansible.builtin.command: + cmd: authselect check + register: result_authselect_check_cmd + changed_when: false + ignore_errors: true + + - name: Set Lockout Time for Failed Password Attempts - Informative message + based on the authselect integrity check result + ansible.builtin.assert: + that: + - result_authselect_check_cmd is success + fail_msg: + - authselect integrity check failed. Remediation aborted! + - This remediation could not be applied because an authselect profile was + not selected or the selected profile is not intact. + - It is not recommended to manually edit the PAM files when authselect tool + is available. + - In cases where the default authselect profile does not cover a specific + demand, a custom authselect profile is recommended. + success_msg: + - authselect integrity check passed + + - name: Set Lockout Time for Failed Password Attempts - Get authselect current + profile + ansible.builtin.shell: + cmd: authselect current -r | awk '{ print $1 }' + register: result_authselect_profile + changed_when: false + when: + - result_authselect_check_cmd is success + + - name: Set Lockout Time for Failed Password Attempts - Define the current authselect + profile as a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: '{{ result_authselect_profile.stdout }}' + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is match("custom/") + + - name: Set Lockout Time for Failed Password Attempts - Define the new authselect + custom profile as a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: custom/hardening + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is not match("custom/") + + - name: Set Lockout Time for Failed Password Attempts - Get authselect current + features to also enable them in the custom profile + ansible.builtin.shell: + cmd: authselect current | tail -n+3 | awk '{ print $2 }' + register: result_authselect_features + changed_when: false + when: + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + + - name: Set Lockout Time for Failed Password Attempts - Check if any custom + profile with the same name was already created + ansible.builtin.stat: + path: /etc/authselect/{{ authselect_custom_profile }} + register: result_authselect_custom_profile_present + changed_when: false + when: + - authselect_current_profile is not match("custom/") + + - name: Set Lockout Time for Failed Password Attempts - Create an authselect + custom profile based on the current profile + ansible.builtin.command: + cmd: authselect create-profile hardening -b {{ authselect_current_profile + }} + when: + - result_authselect_check_cmd is success + - authselect_current_profile is not match("custom/") + - not result_authselect_custom_profile_present.stat.exists + + - name: Set Lockout Time for Failed Password Attempts - Ensure authselect changes + are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=before-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Set Lockout Time for Failed Password Attempts - Ensure the authselect + custom profile is selected + ansible.builtin.command: + cmd: authselect select {{ authselect_custom_profile }} + register: result_pam_authselect_select_profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Set Lockout Time for Failed Password Attempts - Restore the authselect + features in the custom profile + ansible.builtin.command: + cmd: authselect enable-feature {{ item }} + loop: '{{ result_authselect_features.stdout_lines }}' + register: result_pam_authselect_restore_features + when: + - result_authselect_profile is not skipped + - result_authselect_features is not skipped + - result_pam_authselect_select_profile is not skipped + + - name: Set Lockout Time for Failed Password Attempts - Ensure authselect changes + are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=after-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - result_pam_authselect_restore_features is not skipped + + - name: Set Lockout Time for Failed Password Attempts - Change the PAM file + to be edited according to the custom authselect profile + ansible.builtin.set_fact: + pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path + | basename }} + when: + - result_authselect_present.stat.exists + + - name: Set Lockout Time for Failed Password Attempts - Ensure the "unlock_time" + option from "pam_faillock.so" is not present in {{ pam_file_path }} + ansible.builtin.replace: + dest: '{{ pam_file_path }}' + regexp: (.*auth.*pam_faillock.so.*)\bunlock_time\b=?[0-9a-zA-Z]*(.*) + replace: \1\2 + register: result_pam_option_removal + + - name: Set Lockout Time for Failed Password Attempts - Ensure authselect changes + are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: + - result_authselect_present.stat.exists + - result_pam_option_removal is changed + when: + - result_pam_file_present.stat.exists + when: + - '"pam" in ansible_facts.packages' + - result_faillock_conf_check.stat.exists + tags: + - CCE-83588-4 + - CJIS-5.5.3 + - NIST-800-171-3.1.8 + - NIST-800-53-AC-7(b) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.1.7 + - accounts_passwords_pam_faillock_unlock_time + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Set Lockout Time for Failed Password Attempts - Ensure the pam_faillock.so + unlock_time parameter in PAM files + block: + + - name: Set Lockout Time for Failed Password Attempts - Check if pam_faillock.so + unlock_time parameter is already enabled in pam files + ansible.builtin.lineinfile: + path: /etc/pam.d/system-auth + regexp: .*auth.*pam_faillock\.so (preauth|authfail).*unlock_time + state: absent + check_mode: true + changed_when: false + register: result_pam_faillock_unlock_time_parameter_is_present + + - name: Set Lockout Time for Failed Password Attempts - Ensure the inclusion of + pam_faillock.so preauth unlock_time parameter in auth section + ansible.builtin.lineinfile: + path: '{{ item }}' + backrefs: true + regexp: (^\s*auth\s+)([\w\[].*\b)(\s+pam_faillock.so preauth.*) + line: \1required\3 unlock_time={{ var_accounts_passwords_pam_faillock_unlock_time + }} + state: present + loop: + - /etc/pam.d/system-auth + - /etc/pam.d/password-auth + when: + - result_pam_faillock_unlock_time_parameter_is_present.found == 0 + + - name: Set Lockout Time for Failed Password Attempts - Ensure the inclusion of + pam_faillock.so authfail unlock_time parameter in auth section + ansible.builtin.lineinfile: + path: '{{ item }}' + backrefs: true + regexp: (^\s*auth\s+)([\w\[].*\b)(\s+pam_faillock.so authfail.*) + line: \1required\3 unlock_time={{ var_accounts_passwords_pam_faillock_unlock_time + }} + state: present + loop: + - /etc/pam.d/system-auth + - /etc/pam.d/password-auth + when: + - result_pam_faillock_unlock_time_parameter_is_present.found == 0 + + - name: Set Lockout Time for Failed Password Attempts - Ensure the desired value + for pam_faillock.so preauth unlock_time parameter in auth section + ansible.builtin.lineinfile: + path: '{{ item }}' + backrefs: true + regexp: (^\s*auth\s+)([\w\[].*\b)(\s+pam_faillock.so preauth.*)(unlock_time)=[0-9]+(.*) + line: \1required\3\4={{ var_accounts_passwords_pam_faillock_unlock_time }}\5 + state: present + loop: + - /etc/pam.d/system-auth + - /etc/pam.d/password-auth + when: + - result_pam_faillock_unlock_time_parameter_is_present.found > 0 + + - name: Set Lockout Time for Failed Password Attempts - Ensure the desired value + for pam_faillock.so authfail unlock_time parameter in auth section + ansible.builtin.lineinfile: + path: '{{ item }}' + backrefs: true + regexp: (^\s*auth\s+)([\w\[].*\b)(\s+pam_faillock.so authfail.*)(unlock_time)=[0-9]+(.*) + line: \1required\3\4={{ var_accounts_passwords_pam_faillock_unlock_time }}\5 + state: present + loop: + - /etc/pam.d/system-auth + - /etc/pam.d/password-auth + when: + - result_pam_faillock_unlock_time_parameter_is_present.found > 0 + when: + - '"pam" in ansible_facts.packages' + - not result_faillock_conf_check.stat.exists + tags: + - CCE-83588-4 + - CJIS-5.5.3 + - NIST-800-171-3.1.8 + - NIST-800-53-AC-7(b) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.1.7 + - accounts_passwords_pam_faillock_unlock_time + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q pam; then + +var_accounts_passwords_pam_faillock_unlock_time='' + + +if [ -f /usr/bin/authselect ]; then + if ! authselect check; then +echo " +authselect integrity check failed. Remediation aborted! +This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact. +It is not recommended to manually edit the PAM files when authselect tool is available. +In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended." +exit 1 +fi +authselect enable-feature with-faillock + +authselect apply-changes -b +else + AUTH_FILES=("/etc/pam.d/system-auth" "/etc/pam.d/password-auth") +for pam_file in "${AUTH_FILES[@]}" +do + if ! grep -qE '^\s*auth\s+required\s+pam_faillock\.so\s+(preauth silent|authfail).*$' "$pam_file" ; then + sed -i --follow-symlinks '/^auth.*sufficient.*pam_unix\.so.*/i auth required pam_faillock.so preauth silent' "$pam_file" + sed -i --follow-symlinks '/^auth.*required.*pam_deny\.so.*/i auth required pam_faillock.so authfail' "$pam_file" + sed -i --follow-symlinks '/^account.*required.*pam_unix\.so.*/i account required pam_faillock.so' "$pam_file" + fi + sed -Ei 's/(auth.*)(\[default=die\])(.*pam_faillock\.so)/\1required \3/g' "$pam_file" +done +fi +AUTH_FILES=("/etc/pam.d/system-auth" "/etc/pam.d/password-auth") +FAILLOCK_CONF="/etc/security/faillock.conf" +if [ -f $FAILLOCK_CONF ]; then + regex="^\s*unlock_time\s*=" + line="unlock_time = $var_accounts_passwords_pam_faillock_unlock_time" + if ! grep -q $regex $FAILLOCK_CONF; then + echo $line >> $FAILLOCK_CONF + else + sed -i --follow-symlinks 's|^\s*\(unlock_time\s*=\s*\)\(\S\+\)|\1'"$var_accounts_passwords_pam_faillock_unlock_time"'|g' $FAILLOCK_CONF + fi + for pam_file in "${AUTH_FILES[@]}" + do + if [ -e "$pam_file" ] ; then + PAM_FILE_PATH="$pam_file" + if [ -f /usr/bin/authselect ]; then + if ! authselect check; then + echo " + authselect integrity check failed. Remediation aborted! + This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact. + It is not recommended to manually edit the PAM files when authselect tool is available. + In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended." + exit 1 + fi + CURRENT_PROFILE=$(authselect current -r | awk '{ print $1 }') + # If not already in use, a custom profile is created preserving the enabled features. + if [[ ! $CURRENT_PROFILE == custom/* ]]; then + ENABLED_FEATURES=$(authselect current | tail -n+3 | awk '{ print $2 }') + authselect create-profile hardening -b $CURRENT_PROFILE + CURRENT_PROFILE="custom/hardening" + + authselect apply-changes -b --backup=before-hardening-custom-profile + authselect select $CURRENT_PROFILE + for feature in $ENABLED_FEATURES; do + authselect enable-feature $feature; + done + + authselect apply-changes -b --backup=after-hardening-custom-profile + fi + PAM_FILE_NAME=$(basename "$pam_file") + PAM_FILE_PATH="/etc/authselect/$CURRENT_PROFILE/$PAM_FILE_NAME" + + authselect apply-changes -b + fi + + if grep -qP '^\s*auth\s.*\bpam_faillock.so\s.*\bunlock_time\b' "$PAM_FILE_PATH"; then + sed -i -E --follow-symlinks 's/(.*auth.*pam_faillock.so.*)\bunlock_time\b=?[[:alnum:]]*(.*)/\1\2/g' "$PAM_FILE_PATH" + fi + if [ -f /usr/bin/authselect ]; then + + authselect apply-changes -b + fi + else + echo "$pam_file was not found" >&2 + fi + done +else + for pam_file in "${AUTH_FILES[@]}" + do + if ! grep -qE '^\s*auth.*pam_faillock\.so (preauth|authfail).*unlock_time' "$pam_file"; then + sed -i --follow-symlinks '/^auth.*required.*pam_faillock\.so.*preauth.*silent.*/ s/$/ unlock_time='"$var_accounts_passwords_pam_faillock_unlock_time"'/' "$pam_file" + sed -i --follow-symlinks '/^auth.*required.*pam_faillock\.so.*authfail.*/ s/$/ unlock_time='"$var_accounts_passwords_pam_faillock_unlock_time"'/' "$pam_file" + else + sed -i --follow-symlinks 's/\(^auth.*required.*pam_faillock\.so.*preauth.*silent.*\)\('"unlock_time"'=\)[0-9]\+\(.*\)/\1\2'"$var_accounts_passwords_pam_faillock_unlock_time"'\3/' "$pam_file" + sed -i --follow-symlinks 's/\(^auth.*required.*pam_faillock\.so.*authfail.*\)\('"unlock_time"'=\)[0-9]\+\(.*\)/\1\2'"$var_accounts_passwords_pam_faillock_unlock_time"'\3/' "$pam_file" + fi + done +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + + Set Password Quality Requirements + The default pam_pwquality PAM module provides strength +checking for passwords. It performs a number of checks, such as +making sure passwords are not similar to dictionary words, are of +at least a certain length, are not the previous password reversed, +and are not simply a change of case from the previous password. It +can also require passwords to be in certain character classes. The +pam_pwquality module is the preferred way of configuring +password requirements. + +The man pages pam_pwquality(8) +provide information on the capabilities and configuration of +each. + + Set Password Quality Requirements, if using +pam_cracklib + The pam_cracklib PAM module can be configured to meet +requirements for a variety of policies. + +For example, to configure pam_cracklib to require at least one uppercase +character, lowercase character, digit, and other (special) +character, locate the following line in /etc/pam.d/system-auth: +password requisite pam_cracklib.so try_first_pass retry=3 +and then alter it to read: +password required pam_cracklib.so try_first_pass retry=3 maxrepeat=3 minlen=14 dcredit=-1 ucredit=-1 ocredit=-1 lcredit=-1 difok=4 +If no such line exists, add one as the first line of the password section in /etc/pam.d/system-auth. +The arguments can be modified to ensure compliance with +your organization's security policy. Discussion of each parameter follows. + Note that the password quality requirements are not enforced for the +root account for some reason. + + + Set Password Quality Requirements with pam_pwquality + The pam_pwquality PAM module can be configured to meet +requirements for a variety of policies. + +For example, to configure pam_pwquality to require at least one uppercase +character, lowercase character, digit, and other (special) +character, make sure that pam_pwquality exists in /etc/pam.d/system-auth: +password requisite pam_pwquality.so try_first_pass local_users_only retry=3 authtok_type= +If no such line exists, add one as the first line of the password section in /etc/pam.d/system-auth. +Next, modify the settings in /etc/security/pwquality.conf to match the following: +difok = 4 +minlen = 14 +dcredit = -1 +ucredit = -1 +lcredit = -1 +ocredit = -1 +maxrepeat = 3 +The arguments can be modified to ensure compliance with +your organization's security policy. Discussion of each parameter follows. + + dcredit + Minimum number of digits in password + 0 + -1 + -2 + -1 + + + dictcheck + Prevent the use of dictionary words for passwords. + 1 + 1 + + + difok + Minimum number of characters not present in old +password + 15 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 8 + + + lcredit + Minimum number of lower case in password + 0 + -1 + -2 + -1 + + + maxclassrepeat + Maximum Number of Consecutive Repeating Characters in a Password From the Same Character Class + 1 + 2 + 3 + 4 + 4 + + + maxrepeat + Maximum Number of Consecutive Repeating Characters in a Password + 1 + 2 + 3 + 3 + + + minclass + Minimum number of categories of characters that must exist in a password + 1 + 2 + 3 + 4 + 3 + + + minlen + Minimum number of characters in password + 10 + 12 + 14 + 15 + 18 + 20 + 6 + 7 + 8 + 15 + + + ocredit + Minimum number of other (special characters) in +password + 0 + -1 + -2 + -1 + + + retry + Number of retry attempts before erroring out + 1 + 2 + 3 + 4 + 5 + 3 + + + ucredit + Minimum number of upper case in password + 0 + -1 + -2 + -1 + + + Ensure PAM Enforces Password Requirements - Minimum Digit Characters + The pam_pwquality module's dcredit parameter controls requirements for +usage of digits in a password. When set to a negative number, any password will be required to +contain that many digits. When set to a positive number, pam_pwquality will grant +1 additional +length credit for each digit. Modify the dcredit setting in +/etc/security/pwquality.conf to require the use of a digit in passwords. + BP28(R18) + 1 + 12 + 15 + 16 + 5 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.03 + DSS06.10 + CCI-000194 + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.2 + 4.3.3.7.4 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + 0421 + 0422 + 0431 + 0974 + 1173 + 1401 + 1504 + 1505 + 1546 + 1557 + 1558 + 1559 + 1560 + 1561 + A.18.1.4 + A.7.1.1 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.2 + A.9.4.3 + IA-5(c) + IA-5(1)(a) + CM-6(a) + IA-5(4) + PR.AC-1 + PR.AC-6 + PR.AC-7 + FMT_SMF_EXT.1 + Req-8.2.3 + SRG-OS-000071-GPOS-00039 + SRG-OS-000071-VMM-000380 + Use of a complex password helps to increase the time and resources required +to compromise the password. Password complexity, or strength, is a measure of +the effectiveness of a password in resisting attempts at guessing and brute-force +attacks. + +Password complexity is one factor of several that determines how long it takes +to crack a password. The more complex the password, the greater the number of +possible combinations that need to be tested before the password is compromised. +Requiring digits makes password guessing attacks more difficult by ensuring a larger +search space. + + CCE-83566-0 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83566-0 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(a) + - NIST-800-53-IA-5(4) + - NIST-800-53-IA-5(c) + - PCI-DSS-Req-8.2.3 + - accounts_password_pam_dcredit + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy +- name: XCCDF Value var_password_pam_dcredit # promote to variable + set_fact: + var_password_pam_dcredit: !!str + tags: + - always + +- name: Ensure PAM variable dcredit is set accordingly + lineinfile: + create: true + dest: /etc/security/pwquality.conf + regexp: ^#?\s*dcredit + line: dcredit = {{ var_password_pam_dcredit }} + when: '"pam" in ansible_facts.packages' + tags: + - CCE-83566-0 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(a) + - NIST-800-53-IA-5(4) + - NIST-800-53-IA-5(c) + - PCI-DSS-Req-8.2.3 + - accounts_password_pam_dcredit + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q pam; then + +var_password_pam_dcredit='' + + +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/security/pwquality.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^dcredit") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$var_password_pam_dcredit" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^dcredit\\>" "/etc/security/pwquality.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^dcredit\\>.*/$escaped_formatted_output/gi" "/etc/security/pwquality.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-83566-0" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/security/pwquality.conf" >> "/etc/security/pwquality.conf" + printf '%s\n' "$formatted_output" >> "/etc/security/pwquality.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Ensure PAM Enforces Password Requirements - Prevent the Use of Dictionary Words + The pam_pwquality module's dictcheck check if passwords contains dictionary words. When +dictcheck is set to 1 passwords will be checked for dictionary words. + CCI-000366 + IA-5(c) + IA-5(1)(a) + CM-6(a) + IA-5(4) + SRG-OS-000480-GPOS-00225 + Use of a complex password helps to increase the time and resources required to compromise the password. +Password complexity, or strength, is a measure of the effectiveness of a password in resisting attempts at +guessing and brute-force attacks. + +Password complexity is one factor of several that determines how long it takes to crack a password. The more +complex the password, the greater the number of possible combinations that need to be tested before the +password is compromised. + +Passwords with dictionary words may be more vulnerable to password-guessing attacks. + + CCE-88413-0 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-88413-0 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(a) + - NIST-800-53-IA-5(4) + - NIST-800-53-IA-5(c) + - accounts_password_pam_dictcheck + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy +- name: XCCDF Value var_password_pam_dictcheck # promote to variable + set_fact: + var_password_pam_dictcheck: !!str + tags: + - always + +- name: Ensure PAM variable dictcheck is set accordingly + lineinfile: + create: true + dest: /etc/security/pwquality.conf + regexp: ^#?\s*dictcheck + line: dictcheck = {{ var_password_pam_dictcheck }} + when: '"pam" in ansible_facts.packages' + tags: + - CCE-88413-0 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(a) + - NIST-800-53-IA-5(4) + - NIST-800-53-IA-5(c) + - accounts_password_pam_dictcheck + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q pam; then + +var_password_pam_dictcheck='' + + +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/security/pwquality.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^dictcheck") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$var_password_pam_dictcheck" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^dictcheck\\>" "/etc/security/pwquality.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^dictcheck\\>.*/$escaped_formatted_output/gi" "/etc/security/pwquality.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-88413-0" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/security/pwquality.conf" >> "/etc/security/pwquality.conf" + printf '%s\n' "$formatted_output" >> "/etc/security/pwquality.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Ensure PAM Enforces Password Requirements - Minimum Different Characters + The pam_pwquality module's difok parameter sets the number of characters +in a password that must not be present in and old password during a password change. + +Modify the difok setting in /etc/security/pwquality.conf +to equal to require differing characters +when changing passwords. + 1 + 12 + 15 + 16 + 5 + 5.6.2.1.1 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.03 + DSS06.10 + CCI-000195 + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.2 + 4.3.3.7.4 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + A.18.1.4 + A.7.1.1 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.2 + A.9.4.3 + IA-5(c) + IA-5(1)(b) + CM-6(a) + IA-5(4) + PR.AC-1 + PR.AC-6 + PR.AC-7 + SRG-OS-000072-GPOS-00040 + SRG-OS-000072-VMM-000390 + Use of a complex password helps to increase the time and resources +required to compromise the password. Password complexity, or strength, +is a measure of the effectiveness of a password in resisting attempts +at guessing and brute–force attacks. + +Password complexity is one factor of several that determines how long +it takes to crack a password. The more complex the password, the +greater the number of possible combinations that need to be tested +before the password is compromised. + +Requiring a minimum number of different characters during password changes ensures that +newly changed passwords should not resemble previously compromised ones. +Note that passwords which are changed on compromised systems will still be compromised, however. + + CCE-83564-5 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83564-5 + - CJIS-5.6.2.1.1 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(b) + - NIST-800-53-IA-5(4) + - NIST-800-53-IA-5(c) + - accounts_password_pam_difok + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy +- name: XCCDF Value var_password_pam_difok # promote to variable + set_fact: + var_password_pam_difok: !!str + tags: + - always + +- name: Ensure PAM variable difok is set accordingly + lineinfile: + create: true + dest: /etc/security/pwquality.conf + regexp: ^#?\s*difok + line: difok = {{ var_password_pam_difok }} + when: '"pam" in ansible_facts.packages' + tags: + - CCE-83564-5 + - CJIS-5.6.2.1.1 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(b) + - NIST-800-53-IA-5(4) + - NIST-800-53-IA-5(c) + - accounts_password_pam_difok + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q pam; then + +var_password_pam_difok='' + + +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/security/pwquality.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^difok") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$var_password_pam_difok" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^difok\\>" "/etc/security/pwquality.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^difok\\>.*/$escaped_formatted_output/gi" "/etc/security/pwquality.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-83564-5" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/security/pwquality.conf" >> "/etc/security/pwquality.conf" + printf '%s\n' "$formatted_output" >> "/etc/security/pwquality.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Ensure PAM Enforces Password Requirements - Enforce for Local Accounts Only + The pam_pwquality module's local_users_only parameter controls requirements for +enforcing password complexity by pam_pwquality only for local user accounts and ignoring +centralized user account management password complexity configurations. Enable the local_users_only +setting in /etc/security/pwquality.conf to require password complexity enforcement +for only local user accounts. + Using this rule bypasses pam_faillock's functionality and should be used in cases +where centralized management such as LDAP or Active Directory is in use. + CCI-000015 + AC-2(1) + SRG-OS-000001-GPOS-00001 + The operating system must provide automated mechanisms for supporting account management +functions. Enterprise environments make application account management challenging and +complex. A manual process for account management functions adds the risk of a potential +oversight or other error. + + - name: Gather the package facts + package_facts: + manager: auto + tags: + - NIST-800-53-AC-2(1) + - accounts_password_pam_enforce_local + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Ensure PAM Enforces Password Requirements - Enforce for Local Accounts Only + lineinfile: + path: /etc/security/pwquality.conf + create: true + line: local_users_only + state: present + when: '"pam" in ansible_facts.packages' + tags: + - NIST-800-53-AC-2(1) + - accounts_password_pam_enforce_local + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q pam; then + +if [ -e "/etc/security/pwquality.conf" ] ; then + + LC_ALL=C sed -i "/^\s*local_users_only/Id" "/etc/security/pwquality.conf" +else + touch "/etc/security/pwquality.conf" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/security/pwquality.conf" + +cp "/etc/security/pwquality.conf" "/etc/security/pwquality.conf.bak" +# Insert at the end of the file +printf '%s\n' "local_users_only" >> "/etc/security/pwquality.conf" +# Clean up after ourselves. +rm "/etc/security/pwquality.conf.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure PAM Enforces Password Requirements - Enforce for root User + The pam_pwquality module's enforce_for_root parameter controls requirements for +enforcing password complexity for the root user. Enable the enforce_for_root +setting in /etc/security/pwquality.conf to require the root user +to use complex passwords. + CCI-000194 + CCI-000193 + CCI-001619 + CCI-000205 + CCI-000195 + CCI-000192 + CCI-000366 + IA-5(c) + IA-5(1)(a) + CM-6(a) + IA-5(4) + SRG-OS-000072-GPOS-00040 + SRG-OS-000071-GPOS-00039 + SRG-OS-000070-GPOS-00038 + SRG-OS-000266-GPOS-00101 + SRG-OS-000078-GPOS-00046 + SRG-OS-000480-GPOS-00225 + SRG-OS-000069-GPOS-00037 + Use of a complex password helps to increase the time and resources required to compromise +the password. Password complexity, or strength, is a measure of the effectiveness of a +password in resisting attempts at guessing and brute-force attacks. + +Password complexity is one factor of several that determines how long it takes to crack a +password. The more complex the password, the greater the number of possible combinations +that need to be tested before the password is compromised. + + CCE-86356-3 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-86356-3 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(a) + - NIST-800-53-IA-5(4) + - NIST-800-53-IA-5(c) + - accounts_password_pam_enforce_root + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Ensure PAM Enforces Password Requirements - Enforce for root User + lineinfile: + path: /etc/security/pwquality.conf + create: true + line: enforce_for_root + state: present + when: '"pam" in ansible_facts.packages' + tags: + - CCE-86356-3 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(a) + - NIST-800-53-IA-5(4) + - NIST-800-53-IA-5(c) + - accounts_password_pam_enforce_root + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q pam; then + +if [ -e "/etc/security/pwquality.conf" ] ; then + + LC_ALL=C sed -i "/^\s*enforce_for_root/Id" "/etc/security/pwquality.conf" +else + touch "/etc/security/pwquality.conf" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/security/pwquality.conf" + +cp "/etc/security/pwquality.conf" "/etc/security/pwquality.conf.bak" +# Insert at the end of the file +printf '%s\n' "enforce_for_root" >> "/etc/security/pwquality.conf" +# Clean up after ourselves. +rm "/etc/security/pwquality.conf.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure PAM Enforces Password Requirements - Minimum Lowercase Characters + The pam_pwquality module's lcredit parameter controls requirements for +usage of lowercase letters in a password. When set to a negative number, any password will be required to +contain that many lowercase characters. When set to a positive number, pam_pwquality will grant +1 additional +length credit for each lowercase character. Modify the lcredit setting in +/etc/security/pwquality.conf to require the use of a lowercase character in passwords. + BP28(R18) + 1 + 12 + 15 + 16 + 5 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.03 + DSS06.10 + CCI-000193 + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.2 + 4.3.3.7.4 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + 0421 + 0422 + 0431 + 0974 + 1173 + 1401 + 1504 + 1505 + 1546 + 1557 + 1558 + 1559 + 1560 + 1561 + A.18.1.4 + A.7.1.1 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.2 + A.9.4.3 + IA-5(c) + IA-5(1)(a) + CM-6(a) + IA-5(4) + PR.AC-1 + PR.AC-6 + PR.AC-7 + FMT_SMF_EXT.1 + Req-8.2.3 + SRG-OS-000070-GPOS-00038 + SRG-OS-000070-VMM-000370 + Use of a complex password helps to increase the time and resources required +to compromise the password. Password complexity, or strength, is a measure of +the effectiveness of a password in resisting attempts at guessing and brute-force +attacks. + +Password complexity is one factor of several that determines how long it takes +to crack a password. The more complex the password, the greater the number of +possble combinations that need to be tested before the password is compromised. +Requiring a minimum number of lowercase characters makes password guessing attacks +more difficult by ensuring a larger search space. + + CCE-83570-2 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83570-2 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(a) + - NIST-800-53-IA-5(4) + - NIST-800-53-IA-5(c) + - PCI-DSS-Req-8.2.3 + - accounts_password_pam_lcredit + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy +- name: XCCDF Value var_password_pam_lcredit # promote to variable + set_fact: + var_password_pam_lcredit: !!str + tags: + - always + +- name: Ensure PAM variable lcredit is set accordingly + lineinfile: + create: true + dest: /etc/security/pwquality.conf + regexp: ^#?\s*lcredit + line: lcredit = {{ var_password_pam_lcredit }} + when: '"pam" in ansible_facts.packages' + tags: + - CCE-83570-2 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(a) + - NIST-800-53-IA-5(4) + - NIST-800-53-IA-5(c) + - PCI-DSS-Req-8.2.3 + - accounts_password_pam_lcredit + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q pam; then + +var_password_pam_lcredit='' + + +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/security/pwquality.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^lcredit") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$var_password_pam_lcredit" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^lcredit\\>" "/etc/security/pwquality.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^lcredit\\>.*/$escaped_formatted_output/gi" "/etc/security/pwquality.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-83570-2" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/security/pwquality.conf" >> "/etc/security/pwquality.conf" + printf '%s\n' "$formatted_output" >> "/etc/security/pwquality.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Ensure PAM Enforces Password Requirements - Maximum Consecutive Repeating Characters from Same Character Class + The pam_pwquality module's maxclassrepeat parameter controls requirements for +consecutive repeating characters from the same character class. When set to a positive number, it will reject passwords +which contain more than that number of consecutive characters from the same character class. Modify the +maxclassrepeat setting in /etc/security/pwquality.conf to equal +to prevent a run of ( + 1) or more identical characters. + 1 + 12 + 15 + 16 + 5 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.03 + DSS06.10 + CCI-000195 + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.2 + 4.3.3.7.4 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + A.18.1.4 + A.7.1.1 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.2 + A.9.4.3 + IA-5(c) + IA-5(1)(a) + CM-6(a) + IA-5(4) + PR.AC-1 + PR.AC-6 + PR.AC-7 + SRG-OS-000072-GPOS-00040 + Use of a complex password helps to increase the time and resources required to compromise the password. +Password complexity, or strength, is a measure of the effectiveness of a password in resisting +attempts at guessing and brute-force attacks. + +Password complexity is one factor of several that determines how long it takes to crack a password. The +more complex a password, the greater the number of possible combinations that need to be tested before the +password is compromised. + + CCE-83575-1 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83575-1 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(a) + - NIST-800-53-IA-5(4) + - NIST-800-53-IA-5(c) + - accounts_password_pam_maxclassrepeat + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy +- name: XCCDF Value var_password_pam_maxclassrepeat # promote to variable + set_fact: + var_password_pam_maxclassrepeat: !!str + tags: + - always + +- name: Ensure PAM variable maxclassrepeat is set accordingly + lineinfile: + create: true + dest: /etc/security/pwquality.conf + regexp: ^#?\s*maxclassrepeat + line: maxclassrepeat = {{ var_password_pam_maxclassrepeat }} + when: '"pam" in ansible_facts.packages' + tags: + - CCE-83575-1 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(a) + - NIST-800-53-IA-5(4) + - NIST-800-53-IA-5(c) + - accounts_password_pam_maxclassrepeat + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q pam; then + +var_password_pam_maxclassrepeat='' + + +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/security/pwquality.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^maxclassrepeat") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$var_password_pam_maxclassrepeat" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^maxclassrepeat\\>" "/etc/security/pwquality.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^maxclassrepeat\\>.*/$escaped_formatted_output/gi" "/etc/security/pwquality.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-83575-1" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/security/pwquality.conf" >> "/etc/security/pwquality.conf" + printf '%s\n' "$formatted_output" >> "/etc/security/pwquality.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Set Password Maximum Consecutive Repeating Characters + The pam_pwquality module's maxrepeat parameter controls requirements for +consecutive repeating characters. When set to a positive number, it will reject passwords +which contain more than that number of consecutive characters. Modify the maxrepeat setting +in /etc/security/pwquality.conf to equal to prevent a +run of ( + 1) or more identical characters. + 1 + 12 + 15 + 16 + 5 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.03 + DSS06.10 + CCI-000195 + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.2 + 4.3.3.7.4 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + A.18.1.4 + A.7.1.1 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.2 + A.9.4.3 + IA-5(c) + CM-6(a) + IA-5(4) + PR.AC-1 + PR.AC-6 + PR.AC-7 + SRG-OS-000072-GPOS-00040 + Use of a complex password helps to increase the time and resources required to compromise the password. +Password complexity, or strength, is a measure of the effectiveness of a password in resisting attempts at +guessing and brute-force attacks. + +Password complexity is one factor of several that determines how long it takes to crack a password. The more +complex the password, the greater the number of possible combinations that need to be tested before the +password is compromised. + +Passwords with excessive repeating characters may be more vulnerable to password-guessing attacks. + + CCE-83567-8 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83567-8 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(4) + - NIST-800-53-IA-5(c) + - accounts_password_pam_maxrepeat + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy +- name: XCCDF Value var_password_pam_maxrepeat # promote to variable + set_fact: + var_password_pam_maxrepeat: !!str + tags: + - always + +- name: Ensure PAM variable maxrepeat is set accordingly + lineinfile: + create: true + dest: /etc/security/pwquality.conf + regexp: ^#?\s*maxrepeat + line: maxrepeat = {{ var_password_pam_maxrepeat }} + when: '"pam" in ansible_facts.packages' + tags: + - CCE-83567-8 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(4) + - NIST-800-53-IA-5(c) + - accounts_password_pam_maxrepeat + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q pam; then + +var_password_pam_maxrepeat='' + + +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/security/pwquality.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^maxrepeat") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$var_password_pam_maxrepeat" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^maxrepeat\\>" "/etc/security/pwquality.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^maxrepeat\\>.*/$escaped_formatted_output/gi" "/etc/security/pwquality.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-83567-8" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/security/pwquality.conf" >> "/etc/security/pwquality.conf" + printf '%s\n' "$formatted_output" >> "/etc/security/pwquality.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Ensure PAM Enforces Password Requirements - Minimum Different Categories + The pam_pwquality module's minclass parameter controls +requirements for usage of different character classes, or types, of character +that must exist in a password before it is considered valid. For example, +setting this value to three (3) requires that any password must have characters +from at least three different categories in order to be approved. The default +value is zero (0), meaning there are no required classes. There are four +categories available: + +* Upper-case characters +* Lower-case characters +* Digits +* Special characters (for example, punctuation) + +Modify the minclass setting in /etc/security/pwquality.conf entry +to require +differing categories of characters when changing passwords. + 1 + 12 + 15 + 16 + 5 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.03 + DSS06.10 + CCI-000195 + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.2 + 4.3.3.7.4 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + 0421 + 0422 + 0431 + 0974 + 1173 + 1401 + 1504 + 1505 + 1546 + 1557 + 1558 + 1559 + 1560 + 1561 + A.18.1.4 + A.7.1.1 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.2 + A.9.4.3 + IA-5(c) + IA-5(1)(a) + CM-6(a) + IA-5(4) + PR.AC-1 + PR.AC-6 + PR.AC-7 + SRG-OS-000072-GPOS-00040 + Use of a complex password helps to increase the time and resources required to compromise the password. +Password complexity, or strength, is a measure of the effectiveness of a password in resisting attempts +at guessing and brute-force attacks. + +Password complexity is one factor of several that determines how long it takes to crack a password. The +more complex the password, the greater the number of possible combinations that need to be tested before +the password is compromised. + +Requiring a minimum number of character categories makes password guessing attacks more difficult +by ensuring a larger search space. + + CCE-83563-7 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83563-7 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(a) + - NIST-800-53-IA-5(4) + - NIST-800-53-IA-5(c) + - accounts_password_pam_minclass + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy +- name: XCCDF Value var_password_pam_minclass # promote to variable + set_fact: + var_password_pam_minclass: !!str + tags: + - always + +- name: Ensure PAM variable minclass is set accordingly + lineinfile: + create: true + dest: /etc/security/pwquality.conf + regexp: ^#?\s*minclass + line: minclass = {{ var_password_pam_minclass }} + when: '"pam" in ansible_facts.packages' + tags: + - CCE-83563-7 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(a) + - NIST-800-53-IA-5(4) + - NIST-800-53-IA-5(c) + - accounts_password_pam_minclass + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q pam; then + +var_password_pam_minclass='' + + +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/security/pwquality.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^minclass") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$var_password_pam_minclass" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^minclass\\>" "/etc/security/pwquality.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^minclass\\>.*/$escaped_formatted_output/gi" "/etc/security/pwquality.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-83563-7" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/security/pwquality.conf" >> "/etc/security/pwquality.conf" + printf '%s\n' "$formatted_output" >> "/etc/security/pwquality.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Ensure PAM Enforces Password Requirements - Minimum Length + The pam_pwquality module's minlen parameter controls requirements for +minimum characters required in a password. Add minlen= +after pam_pwquality to set minimum password length requirements. + BP28(R18) + 1 + 12 + 15 + 16 + 5 + 5.6.2.1.1 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.03 + DSS06.10 + CCI-000205 + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.2 + 4.3.3.7.4 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + 0421 + 0422 + 0431 + 0974 + 1173 + 1401 + 1504 + 1505 + 1546 + 1557 + 1558 + 1559 + 1560 + 1561 + A.18.1.4 + A.7.1.1 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.2 + A.9.4.3 + IA-5(c) + IA-5(1)(a) + CM-6(a) + IA-5(4) + PR.AC-1 + PR.AC-6 + PR.AC-7 + FMT_SMF_EXT.1 + Req-8.2.3 + SRG-OS-000078-GPOS-00046 + SRG-OS-000072-VMM-000390 + SRG-OS-000078-VMM-000450 + The shorter the password, the lower the number of possible combinations +that need to be tested before the password is compromised. + +Password complexity, or strength, is a measure of the effectiveness of a +password in resisting attempts at guessing and brute-force attacks. +Password length is one factor of several that helps to determine strength +and how long it takes to crack a password. Use of more characters in a password +helps to exponentially increase the time and/or resources required to +compromise the password. + + CCE-83579-3 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83579-3 + - CJIS-5.6.2.1.1 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(a) + - NIST-800-53-IA-5(4) + - NIST-800-53-IA-5(c) + - PCI-DSS-Req-8.2.3 + - accounts_password_pam_minlen + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy +- name: XCCDF Value var_password_pam_minlen # promote to variable + set_fact: + var_password_pam_minlen: !!str + tags: + - always + +- name: Ensure PAM variable minlen is set accordingly + lineinfile: + create: true + dest: /etc/security/pwquality.conf + regexp: ^#?\s*minlen + line: minlen = {{ var_password_pam_minlen }} + when: '"pam" in ansible_facts.packages' + tags: + - CCE-83579-3 + - CJIS-5.6.2.1.1 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(a) + - NIST-800-53-IA-5(4) + - NIST-800-53-IA-5(c) + - PCI-DSS-Req-8.2.3 + - accounts_password_pam_minlen + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q pam; then + +var_password_pam_minlen='' + + +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/security/pwquality.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^minlen") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$var_password_pam_minlen" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^minlen\\>" "/etc/security/pwquality.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^minlen\\>.*/$escaped_formatted_output/gi" "/etc/security/pwquality.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-83579-3" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/security/pwquality.conf" >> "/etc/security/pwquality.conf" + printf '%s\n' "$formatted_output" >> "/etc/security/pwquality.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Ensure PAM Enforces Password Requirements - Minimum Special Characters + The pam_pwquality module's ocredit= parameter controls requirements for +usage of special (or "other") characters in a password. When set to a negative number, +any password will be required to contain that many special characters. +When set to a positive number, pam_pwquality will grant +1 +additional length credit for each special character. Modify the ocredit setting +in /etc/security/pwquality.conf to equal +to require use of a special character in passwords. + BP28(R18) + 1 + 12 + 15 + 16 + 5 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.03 + DSS06.10 + CCI-001619 + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.2 + 4.3.3.7.4 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + 0421 + 0422 + 0431 + 0974 + 1173 + 1401 + 1504 + 1505 + 1546 + 1557 + 1558 + 1559 + 1560 + 1561 + A.18.1.4 + A.7.1.1 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.2 + A.9.4.3 + IA-5(c) + IA-5(1)(a) + CM-6(a) + IA-5(4) + PR.AC-1 + PR.AC-6 + PR.AC-7 + FMT_SMF_EXT.1 + SRG-OS-000266-GPOS-00101 + SRG-OS-000266-VMM-000940 + Use of a complex password helps to increase the time and resources required +to compromise the password. Password complexity, or strength, is a measure of +the effectiveness of a password in resisting attempts at guessing and brute-force +attacks. + +Password complexity is one factor of several that determines how long it takes +to crack a password. The more complex the password, the greater the number of +possible combinations that need to be tested before the password is compromised. +Requiring a minimum number of special characters makes password guessing attacks +more difficult by ensuring a larger search space. + + CCE-83565-2 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83565-2 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(a) + - NIST-800-53-IA-5(4) + - NIST-800-53-IA-5(c) + - accounts_password_pam_ocredit + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy +- name: XCCDF Value var_password_pam_ocredit # promote to variable + set_fact: + var_password_pam_ocredit: !!str + tags: + - always + +- name: Ensure PAM variable ocredit is set accordingly + lineinfile: + create: true + dest: /etc/security/pwquality.conf + regexp: ^#?\s*ocredit + line: ocredit = {{ var_password_pam_ocredit }} + when: '"pam" in ansible_facts.packages' + tags: + - CCE-83565-2 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(a) + - NIST-800-53-IA-5(4) + - NIST-800-53-IA-5(c) + - accounts_password_pam_ocredit + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q pam; then + +var_password_pam_ocredit='' + + +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/security/pwquality.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^ocredit") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$var_password_pam_ocredit" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^ocredit\\>" "/etc/security/pwquality.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^ocredit\\>.*/$escaped_formatted_output/gi" "/etc/security/pwquality.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-83565-2" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/security/pwquality.conf" >> "/etc/security/pwquality.conf" + printf '%s\n' "$formatted_output" >> "/etc/security/pwquality.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Ensure PAM password complexity module is enabled in password-auth + To enable PAM password complexity in password-auth file: +Edit the password section in +/etc/pam.d/password-auth to show +password requisite pam_pwquality.so. + CCI-000366 + SRG-OS-000069-GPOS-00037 + SRG-OS-000070-GPOS-00038 + SRG-OS-000480-GPOS-00227 + Enabling PAM password complexity permits to enforce strong passwords and consequently +makes the system less prone to dictionary attacks. + + CCE-85878-7 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-85878-7 + - accounts_password_pam_pwquality_password_auth + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure PAM password complexity module is enabled in password-auth - Check + if /etc/pam.d/password-auth file is present + ansible.builtin.stat: + path: /etc/pam.d/password-auth + register: result_pam_file_present + when: '"pam" in ansible_facts.packages' + tags: + - CCE-85878-7 + - accounts_password_pam_pwquality_password_auth + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure PAM password complexity module is enabled in password-auth - Check + the proper remediation for the system + block: + + - name: Ensure PAM password complexity module is enabled in password-auth - Define + the PAM file to be edited as a local fact + ansible.builtin.set_fact: + pam_file_path: /etc/pam.d/password-auth + + - name: Ensure PAM password complexity module is enabled in password-auth - Check + if system relies on authselect + ansible.builtin.stat: + path: /usr/bin/authselect + register: result_authselect_present + + - name: Ensure PAM password complexity module is enabled in password-auth - Remediate + using authselect + block: + + - name: Ensure PAM password complexity module is enabled in password-auth - Check + integrity of authselect current profile + ansible.builtin.command: + cmd: authselect check + register: result_authselect_check_cmd + changed_when: false + ignore_errors: true + + - name: Ensure PAM password complexity module is enabled in password-auth - Informative + message based on the authselect integrity check result + ansible.builtin.assert: + that: + - result_authselect_check_cmd is success + fail_msg: + - authselect integrity check failed. Remediation aborted! + - This remediation could not be applied because an authselect profile was + not selected or the selected profile is not intact. + - It is not recommended to manually edit the PAM files when authselect tool + is available. + - In cases where the default authselect profile does not cover a specific + demand, a custom authselect profile is recommended. + success_msg: + - authselect integrity check passed + + - name: Ensure PAM password complexity module is enabled in password-auth - Get + authselect current profile + ansible.builtin.shell: + cmd: authselect current -r | awk '{ print $1 }' + register: result_authselect_profile + changed_when: false + when: + - result_authselect_check_cmd is success + + - name: Ensure PAM password complexity module is enabled in password-auth - Define + the current authselect profile as a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: '{{ result_authselect_profile.stdout }}' + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is match("custom/") + + - name: Ensure PAM password complexity module is enabled in password-auth - Define + the new authselect custom profile as a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: custom/hardening + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is not match("custom/") + + - name: Ensure PAM password complexity module is enabled in password-auth - Get + authselect current features to also enable them in the custom profile + ansible.builtin.shell: + cmd: authselect current | tail -n+3 | awk '{ print $2 }' + register: result_authselect_features + changed_when: false + when: + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + + - name: Ensure PAM password complexity module is enabled in password-auth - Check + if any custom profile with the same name was already created + ansible.builtin.stat: + path: /etc/authselect/{{ authselect_custom_profile }} + register: result_authselect_custom_profile_present + changed_when: false + when: + - authselect_current_profile is not match("custom/") + + - name: Ensure PAM password complexity module is enabled in password-auth - Create + an authselect custom profile based on the current profile + ansible.builtin.command: + cmd: authselect create-profile hardening -b {{ authselect_current_profile + }} + when: + - result_authselect_check_cmd is success + - authselect_current_profile is not match("custom/") + - not result_authselect_custom_profile_present.stat.exists + + - name: Ensure PAM password complexity module is enabled in password-auth - Ensure + authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=before-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Ensure PAM password complexity module is enabled in password-auth - Ensure + the authselect custom profile is selected + ansible.builtin.command: + cmd: authselect select {{ authselect_custom_profile }} + register: result_pam_authselect_select_profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Ensure PAM password complexity module is enabled in password-auth - Restore + the authselect features in the custom profile + ansible.builtin.command: + cmd: authselect enable-feature {{ item }} + loop: '{{ result_authselect_features.stdout_lines }}' + register: result_pam_authselect_restore_features + when: + - result_authselect_profile is not skipped + - result_authselect_features is not skipped + - result_pam_authselect_select_profile is not skipped + + - name: Ensure PAM password complexity module is enabled in password-auth - Ensure + authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=after-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - result_pam_authselect_restore_features is not skipped + + - name: Ensure PAM password complexity module is enabled in password-auth - Change + the PAM file to be edited according to the custom authselect profile + ansible.builtin.set_fact: + pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path + | basename }} + when: + - result_authselect_present.stat.exists + + - name: Ensure PAM password complexity module is enabled in password-auth - Check + if expected PAM module line is present in {{ pam_file_path }} + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + regexp: ^\s*password\s+requisite\s+pam_pwquality.so\s*.* + state: absent + check_mode: true + changed_when: false + register: result_pam_line_present + + - name: Ensure PAM password complexity module is enabled in password-auth - Include + or update the PAM module line in {{ pam_file_path }} + block: + + - name: Ensure PAM password complexity module is enabled in password-auth - Check + if required PAM module line is present in {{ pam_file_path }} with different + control + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + regexp: ^\s*password\s+.*\s+pam_pwquality.so\s* + state: absent + check_mode: true + changed_when: false + register: result_pam_line_other_control_present + + - name: Ensure PAM password complexity module is enabled in password-auth - Ensure + the correct control for the required PAM module line in {{ pam_file_path }} + ansible.builtin.replace: + dest: '{{ pam_file_path }}' + regexp: ^(\s*password\s+).*(\bpam_pwquality.so.*) + replace: \1requisite \2 + register: result_pam_module_edit + when: + - result_pam_line_other_control_present.found == 1 + + - name: Ensure PAM password complexity module is enabled in password-auth - Ensure + the required PAM module line is included in {{ pam_file_path }} + ansible.builtin.lineinfile: + dest: '{{ pam_file_path }}' + insertafter: ^account.*required.*pam_permit\.so + line: password requisite pam_pwquality.so + register: result_pam_module_add + when: + - result_pam_line_other_control_present.found == 0 or result_pam_line_other_control_present.found + > 1 + + - name: Ensure PAM password complexity module is enabled in password-auth - Ensure + authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: | + result_authselect_present is defined and result_authselect_present.stat.exists and ((result_pam_module_add is defined and result_pam_module_add.changed) or (result_pam_module_edit is defined and result_pam_module_edit.changed)) + when: + - result_pam_line_present.found is defined + - result_pam_line_present.found == 0 + + - name: Ensure PAM password complexity module is enabled in password-auth - Ensure + authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: + - result_authselect_present.stat.exists + - (result_pam__add is defined and result_pam__add.changed) or (result_pam__edit + is defined and result_pam__edit.changed) + when: + - '"pam" in ansible_facts.packages' + - result_pam_file_present.stat.exists + tags: + - CCE-85878-7 + - accounts_password_pam_pwquality_password_auth + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if rpm --quiet -q pam; then + +if [ -e "/etc/pam.d/password-auth" ] ; then + PAM_FILE_PATH="/etc/pam.d/password-auth" + if [ -f /usr/bin/authselect ]; then + if ! authselect check; then + echo " + authselect integrity check failed. Remediation aborted! + This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact. + It is not recommended to manually edit the PAM files when authselect tool is available. + In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended." + exit 1 + fi + CURRENT_PROFILE=$(authselect current -r | awk '{ print $1 }') + # If not already in use, a custom profile is created preserving the enabled features. + if [[ ! $CURRENT_PROFILE == custom/* ]]; then + ENABLED_FEATURES=$(authselect current | tail -n+3 | awk '{ print $2 }') + authselect create-profile hardening -b $CURRENT_PROFILE + CURRENT_PROFILE="custom/hardening" + + authselect apply-changes -b --backup=before-hardening-custom-profile + authselect select $CURRENT_PROFILE + for feature in $ENABLED_FEATURES; do + authselect enable-feature $feature; + done + + authselect apply-changes -b --backup=after-hardening-custom-profile + fi + PAM_FILE_NAME=$(basename "/etc/pam.d/password-auth") + PAM_FILE_PATH="/etc/authselect/$CURRENT_PROFILE/$PAM_FILE_NAME" + + authselect apply-changes -b + fi + if ! grep -qP '^\s*password\s+'"requisite"'\s+pam_pwquality.so\s*.*' "$PAM_FILE_PATH"; then + # Line matching group + control + module was not found. Check group + module. + if [ "$(grep -cP '^\s*password\s+.*\s+pam_pwquality.so\s*' "$PAM_FILE_PATH")" -eq 1 ]; then + # The control is updated only if one single line matches. + sed -i -E --follow-symlinks 's/^(\s*password\s+).*(\bpam_pwquality.so.*)/\1'"requisite"' \2/' "$PAM_FILE_PATH" + else + LAST_MATCH_LINE=$(grep -nP "^account.*required.*pam_permit\.so" "$PAM_FILE_PATH" | tail -n 1 | cut -d: -f 1) + if [ ! -z $LAST_MATCH_LINE ]; then + sed -i --follow-symlinks $LAST_MATCH_LINE' a password '"requisite"' pam_pwquality.so' "$PAM_FILE_PATH" + else + echo 'password '"requisite"' pam_pwquality.so' >> "$PAM_FILE_PATH" + fi + fi +fi + if [ -f /usr/bin/authselect ]; then + + authselect apply-changes -b + fi +else + echo "/etc/pam.d/password-auth was not found" >&2 +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure PAM password complexity module is enabled in system-auth + To enable PAM password complexity in system-auth file: +Edit the password section in +/etc/pam.d/system-auth to show +password requisite pam_pwquality.so. + CCI-000366 + SRG-OS-000480-GPOS-00227 + Enabling PAM password complexity permits to enforce strong passwords and consequently +makes the system less prone to dictionary attacks. + + CCE-85873-8 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-85873-8 + - accounts_password_pam_pwquality_system_auth + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure PAM password complexity module is enabled in system-auth - Check if + /etc/pam.d/system-auth file is present + ansible.builtin.stat: + path: /etc/pam.d/system-auth + register: result_pam_file_present + when: '"pam" in ansible_facts.packages' + tags: + - CCE-85873-8 + - accounts_password_pam_pwquality_system_auth + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure PAM password complexity module is enabled in system-auth - Check the + proper remediation for the system + block: + + - name: Ensure PAM password complexity module is enabled in system-auth - Define + the PAM file to be edited as a local fact + ansible.builtin.set_fact: + pam_file_path: /etc/pam.d/system-auth + + - name: Ensure PAM password complexity module is enabled in system-auth - Check + if system relies on authselect + ansible.builtin.stat: + path: /usr/bin/authselect + register: result_authselect_present + + - name: Ensure PAM password complexity module is enabled in system-auth - Remediate + using authselect + block: + + - name: Ensure PAM password complexity module is enabled in system-auth - Check + integrity of authselect current profile + ansible.builtin.command: + cmd: authselect check + register: result_authselect_check_cmd + changed_when: false + ignore_errors: true + + - name: Ensure PAM password complexity module is enabled in system-auth - Informative + message based on the authselect integrity check result + ansible.builtin.assert: + that: + - result_authselect_check_cmd is success + fail_msg: + - authselect integrity check failed. Remediation aborted! + - This remediation could not be applied because an authselect profile was + not selected or the selected profile is not intact. + - It is not recommended to manually edit the PAM files when authselect tool + is available. + - In cases where the default authselect profile does not cover a specific + demand, a custom authselect profile is recommended. + success_msg: + - authselect integrity check passed + + - name: Ensure PAM password complexity module is enabled in system-auth - Get + authselect current profile + ansible.builtin.shell: + cmd: authselect current -r | awk '{ print $1 }' + register: result_authselect_profile + changed_when: false + when: + - result_authselect_check_cmd is success + + - name: Ensure PAM password complexity module is enabled in system-auth - Define + the current authselect profile as a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: '{{ result_authselect_profile.stdout }}' + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is match("custom/") + + - name: Ensure PAM password complexity module is enabled in system-auth - Define + the new authselect custom profile as a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: custom/hardening + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is not match("custom/") + + - name: Ensure PAM password complexity module is enabled in system-auth - Get + authselect current features to also enable them in the custom profile + ansible.builtin.shell: + cmd: authselect current | tail -n+3 | awk '{ print $2 }' + register: result_authselect_features + changed_when: false + when: + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + + - name: Ensure PAM password complexity module is enabled in system-auth - Check + if any custom profile with the same name was already created + ansible.builtin.stat: + path: /etc/authselect/{{ authselect_custom_profile }} + register: result_authselect_custom_profile_present + changed_when: false + when: + - authselect_current_profile is not match("custom/") + + - name: Ensure PAM password complexity module is enabled in system-auth - Create + an authselect custom profile based on the current profile + ansible.builtin.command: + cmd: authselect create-profile hardening -b {{ authselect_current_profile + }} + when: + - result_authselect_check_cmd is success + - authselect_current_profile is not match("custom/") + - not result_authselect_custom_profile_present.stat.exists + + - name: Ensure PAM password complexity module is enabled in system-auth - Ensure + authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=before-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Ensure PAM password complexity module is enabled in system-auth - Ensure + the authselect custom profile is selected + ansible.builtin.command: + cmd: authselect select {{ authselect_custom_profile }} + register: result_pam_authselect_select_profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Ensure PAM password complexity module is enabled in system-auth - Restore + the authselect features in the custom profile + ansible.builtin.command: + cmd: authselect enable-feature {{ item }} + loop: '{{ result_authselect_features.stdout_lines }}' + register: result_pam_authselect_restore_features + when: + - result_authselect_profile is not skipped + - result_authselect_features is not skipped + - result_pam_authselect_select_profile is not skipped + + - name: Ensure PAM password complexity module is enabled in system-auth - Ensure + authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=after-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - result_pam_authselect_restore_features is not skipped + + - name: Ensure PAM password complexity module is enabled in system-auth - Change + the PAM file to be edited according to the custom authselect profile + ansible.builtin.set_fact: + pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path + | basename }} + when: + - result_authselect_present.stat.exists + + - name: Ensure PAM password complexity module is enabled in system-auth - Check + if expected PAM module line is present in {{ pam_file_path }} + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + regexp: ^\s*password\s+requisite\s+pam_pwquality.so\s*.* + state: absent + check_mode: true + changed_when: false + register: result_pam_line_present + + - name: Ensure PAM password complexity module is enabled in system-auth - Include + or update the PAM module line in {{ pam_file_path }} + block: + + - name: Ensure PAM password complexity module is enabled in system-auth - Check + if required PAM module line is present in {{ pam_file_path }} with different + control + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + regexp: ^\s*password\s+.*\s+pam_pwquality.so\s* + state: absent + check_mode: true + changed_when: false + register: result_pam_line_other_control_present + + - name: Ensure PAM password complexity module is enabled in system-auth - Ensure + the correct control for the required PAM module line in {{ pam_file_path }} + ansible.builtin.replace: + dest: '{{ pam_file_path }}' + regexp: ^(\s*password\s+).*(\bpam_pwquality.so.*) + replace: \1requisite \2 + register: result_pam_module_edit + when: + - result_pam_line_other_control_present.found == 1 + + - name: Ensure PAM password complexity module is enabled in system-auth - Ensure + the required PAM module line is included in {{ pam_file_path }} + ansible.builtin.lineinfile: + dest: '{{ pam_file_path }}' + insertafter: ^account.*required.*pam_permit\.so + line: password requisite pam_pwquality.so + register: result_pam_module_add + when: + - result_pam_line_other_control_present.found == 0 or result_pam_line_other_control_present.found + > 1 + + - name: Ensure PAM password complexity module is enabled in system-auth - Ensure + authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: | + result_authselect_present is defined and result_authselect_present.stat.exists and ((result_pam_module_add is defined and result_pam_module_add.changed) or (result_pam_module_edit is defined and result_pam_module_edit.changed)) + when: + - result_pam_line_present.found is defined + - result_pam_line_present.found == 0 + + - name: Ensure PAM password complexity module is enabled in system-auth - Ensure + authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: + - result_authselect_present.stat.exists + - (result_pam__add is defined and result_pam__add.changed) or (result_pam__edit + is defined and result_pam__edit.changed) + when: + - '"pam" in ansible_facts.packages' + - result_pam_file_present.stat.exists + tags: + - CCE-85873-8 + - accounts_password_pam_pwquality_system_auth + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if rpm --quiet -q pam; then + +if [ -e "/etc/pam.d/system-auth" ] ; then + PAM_FILE_PATH="/etc/pam.d/system-auth" + if [ -f /usr/bin/authselect ]; then + if ! authselect check; then + echo " + authselect integrity check failed. Remediation aborted! + This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact. + It is not recommended to manually edit the PAM files when authselect tool is available. + In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended." + exit 1 + fi + CURRENT_PROFILE=$(authselect current -r | awk '{ print $1 }') + # If not already in use, a custom profile is created preserving the enabled features. + if [[ ! $CURRENT_PROFILE == custom/* ]]; then + ENABLED_FEATURES=$(authselect current | tail -n+3 | awk '{ print $2 }') + authselect create-profile hardening -b $CURRENT_PROFILE + CURRENT_PROFILE="custom/hardening" + + authselect apply-changes -b --backup=before-hardening-custom-profile + authselect select $CURRENT_PROFILE + for feature in $ENABLED_FEATURES; do + authselect enable-feature $feature; + done + + authselect apply-changes -b --backup=after-hardening-custom-profile + fi + PAM_FILE_NAME=$(basename "/etc/pam.d/system-auth") + PAM_FILE_PATH="/etc/authselect/$CURRENT_PROFILE/$PAM_FILE_NAME" + + authselect apply-changes -b + fi + if ! grep -qP '^\s*password\s+'"requisite"'\s+pam_pwquality.so\s*.*' "$PAM_FILE_PATH"; then + # Line matching group + control + module was not found. Check group + module. + if [ "$(grep -cP '^\s*password\s+.*\s+pam_pwquality.so\s*' "$PAM_FILE_PATH")" -eq 1 ]; then + # The control is updated only if one single line matches. + sed -i -E --follow-symlinks 's/^(\s*password\s+).*(\bpam_pwquality.so.*)/\1'"requisite"' \2/' "$PAM_FILE_PATH" + else + LAST_MATCH_LINE=$(grep -nP "^account.*required.*pam_permit\.so" "$PAM_FILE_PATH" | tail -n 1 | cut -d: -f 1) + if [ ! -z $LAST_MATCH_LINE ]; then + sed -i --follow-symlinks $LAST_MATCH_LINE' a password '"requisite"' pam_pwquality.so' "$PAM_FILE_PATH" + else + echo 'password '"requisite"' pam_pwquality.so' >> "$PAM_FILE_PATH" + fi + fi +fi + if [ -f /usr/bin/authselect ]; then + + authselect apply-changes -b + fi +else + echo "/etc/pam.d/system-auth was not found" >&2 +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure PAM Enforces Password Requirements - Authentication Retry Prompts Permitted Per-Session + To configure the number of retry prompts that are permitted per-session: + +Edit the /etc/security/pwquality.conf to include + +retry=, or a lower value if site +policy is more restrictive. The DoD requirement is a maximum of 3 prompts +per session. + 1 + 11 + 12 + 15 + 16 + 3 + 5 + 9 + 5.5.3 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.03 + DSS06.10 + CCI-000192 + CCI-000366 + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.2 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.18.1.4 + A.7.1.1 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.2 + A.9.4.3 + CM-6(a) + AC-7(a) + IA-5(4) + PR.AC-1 + PR.AC-6 + PR.AC-7 + PR.IP-1 + FMT_MOF_EXT.1 + SRG-OS-000069-GPOS-00037 + SRG-OS-000480-GPOS-00227 + Setting the password retry prompts that are permitted on a per-session basis to a low value +requires some software, such as SSH, to re-connect. This can slow down and +draw additional attention to some types of password-guessing attacks. Note that this +is different from account lockout, which is provided by the pam_faillock module. + + CCE-83569-4 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83569-4 + - CJIS-5.5.3 + - NIST-800-53-AC-7(a) + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(4) + - accounts_password_pam_retry + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed +- name: XCCDF Value var_password_pam_retry # promote to variable + set_fact: + var_password_pam_retry: !!str + tags: + - always + +- name: Ensure PAM variable retry is set accordingly + ansible.builtin.lineinfile: + create: true + dest: /etc/security/pwquality.conf + regexp: ^\s*retry + line: retry = {{ var_password_pam_retry }} + when: '"pam" in ansible_facts.packages' + tags: + - CCE-83569-4 + - CJIS-5.5.3 + - NIST-800-53-AC-7(a) + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(4) + - accounts_password_pam_retry + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts Permitted + Per-Session - Check if /etc/pam.d/password-auth file is present + ansible.builtin.stat: + path: /etc/pam.d/password-auth + register: result_pam_file_present + when: '"pam" in ansible_facts.packages' + tags: + - CCE-83569-4 + - CJIS-5.5.3 + - NIST-800-53-AC-7(a) + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(4) + - accounts_password_pam_retry + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts Permitted + Per-Session - Check the proper remediation for the system + block: + + - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts + Permitted Per-Session - Define the PAM file to be edited as a local fact + ansible.builtin.set_fact: + pam_file_path: /etc/pam.d/password-auth + + - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts + Permitted Per-Session - Check if system relies on authselect + ansible.builtin.stat: + path: /usr/bin/authselect + register: result_authselect_present + + - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts + Permitted Per-Session - Remediate using authselect + block: + + - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts + Permitted Per-Session - Check integrity of authselect current profile + ansible.builtin.command: + cmd: authselect check + register: result_authselect_check_cmd + changed_when: false + ignore_errors: true + + - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts + Permitted Per-Session - Informative message based on the authselect integrity + check result + ansible.builtin.assert: + that: + - result_authselect_check_cmd is success + fail_msg: + - authselect integrity check failed. Remediation aborted! + - This remediation could not be applied because an authselect profile was + not selected or the selected profile is not intact. + - It is not recommended to manually edit the PAM files when authselect tool + is available. + - In cases where the default authselect profile does not cover a specific + demand, a custom authselect profile is recommended. + success_msg: + - authselect integrity check passed + + - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts + Permitted Per-Session - Get authselect current profile + ansible.builtin.shell: + cmd: authselect current -r | awk '{ print $1 }' + register: result_authselect_profile + changed_when: false + when: + - result_authselect_check_cmd is success + + - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts + Permitted Per-Session - Define the current authselect profile as a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: '{{ result_authselect_profile.stdout }}' + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is match("custom/") + + - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts + Permitted Per-Session - Define the new authselect custom profile as a local + fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: custom/hardening + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is not match("custom/") + + - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts + Permitted Per-Session - Get authselect current features to also enable them + in the custom profile + ansible.builtin.shell: + cmd: authselect current | tail -n+3 | awk '{ print $2 }' + register: result_authselect_features + changed_when: false + when: + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + + - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts + Permitted Per-Session - Check if any custom profile with the same name was + already created + ansible.builtin.stat: + path: /etc/authselect/{{ authselect_custom_profile }} + register: result_authselect_custom_profile_present + changed_when: false + when: + - authselect_current_profile is not match("custom/") + + - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts + Permitted Per-Session - Create an authselect custom profile based on the current + profile + ansible.builtin.command: + cmd: authselect create-profile hardening -b {{ authselect_current_profile + }} + when: + - result_authselect_check_cmd is success + - authselect_current_profile is not match("custom/") + - not result_authselect_custom_profile_present.stat.exists + + - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts + Permitted Per-Session - Ensure authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=before-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts + Permitted Per-Session - Ensure the authselect custom profile is selected + ansible.builtin.command: + cmd: authselect select {{ authselect_custom_profile }} + register: result_pam_authselect_select_profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts + Permitted Per-Session - Restore the authselect features in the custom profile + ansible.builtin.command: + cmd: authselect enable-feature {{ item }} + loop: '{{ result_authselect_features.stdout_lines }}' + register: result_pam_authselect_restore_features + when: + - result_authselect_profile is not skipped + - result_authselect_features is not skipped + - result_pam_authselect_select_profile is not skipped + + - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts + Permitted Per-Session - Ensure authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=after-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - result_pam_authselect_restore_features is not skipped + + - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts + Permitted Per-Session - Change the PAM file to be edited according to the + custom authselect profile + ansible.builtin.set_fact: + pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path + | basename }} + when: + - result_authselect_present.stat.exists + + - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts + Permitted Per-Session - Ensure the "retry" option from "pam_pwquality.so" is + not present in {{ pam_file_path }} + ansible.builtin.replace: + dest: '{{ pam_file_path }}' + regexp: (.*password.*.*.*pam_pwquality.so.*)\bretry\b=?[0-9a-zA-Z]*(.*) + replace: \1\2 + register: result_pam_option_removal + + - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts + Permitted Per-Session - Ensure authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: + - result_authselect_present.stat.exists + - result_pam_option_removal is changed + when: + - '"pam" in ansible_facts.packages' + - result_pam_file_present.stat.exists + tags: + - CCE-83569-4 + - CJIS-5.5.3 + - NIST-800-53-AC-7(a) + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(4) + - accounts_password_pam_retry + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts Permitted + Per-Session - Check if /etc/pam.d/system-auth file is present + ansible.builtin.stat: + path: /etc/pam.d/system-auth + register: result_pam_file_present + when: '"pam" in ansible_facts.packages' + tags: + - CCE-83569-4 + - CJIS-5.5.3 + - NIST-800-53-AC-7(a) + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(4) + - accounts_password_pam_retry + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts Permitted + Per-Session - Check the proper remediation for the system + block: + + - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts + Permitted Per-Session - Define the PAM file to be edited as a local fact + ansible.builtin.set_fact: + pam_file_path: /etc/pam.d/system-auth + + - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts + Permitted Per-Session - Check if system relies on authselect + ansible.builtin.stat: + path: /usr/bin/authselect + register: result_authselect_present + + - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts + Permitted Per-Session - Remediate using authselect + block: + + - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts + Permitted Per-Session - Check integrity of authselect current profile + ansible.builtin.command: + cmd: authselect check + register: result_authselect_check_cmd + changed_when: false + ignore_errors: true + + - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts + Permitted Per-Session - Informative message based on the authselect integrity + check result + ansible.builtin.assert: + that: + - result_authselect_check_cmd is success + fail_msg: + - authselect integrity check failed. Remediation aborted! + - This remediation could not be applied because an authselect profile was + not selected or the selected profile is not intact. + - It is not recommended to manually edit the PAM files when authselect tool + is available. + - In cases where the default authselect profile does not cover a specific + demand, a custom authselect profile is recommended. + success_msg: + - authselect integrity check passed + + - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts + Permitted Per-Session - Get authselect current profile + ansible.builtin.shell: + cmd: authselect current -r | awk '{ print $1 }' + register: result_authselect_profile + changed_when: false + when: + - result_authselect_check_cmd is success + + - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts + Permitted Per-Session - Define the current authselect profile as a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: '{{ result_authselect_profile.stdout }}' + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is match("custom/") + + - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts + Permitted Per-Session - Define the new authselect custom profile as a local + fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: custom/hardening + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is not match("custom/") + + - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts + Permitted Per-Session - Get authselect current features to also enable them + in the custom profile + ansible.builtin.shell: + cmd: authselect current | tail -n+3 | awk '{ print $2 }' + register: result_authselect_features + changed_when: false + when: + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + + - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts + Permitted Per-Session - Check if any custom profile with the same name was + already created + ansible.builtin.stat: + path: /etc/authselect/{{ authselect_custom_profile }} + register: result_authselect_custom_profile_present + changed_when: false + when: + - authselect_current_profile is not match("custom/") + + - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts + Permitted Per-Session - Create an authselect custom profile based on the current + profile + ansible.builtin.command: + cmd: authselect create-profile hardening -b {{ authselect_current_profile + }} + when: + - result_authselect_check_cmd is success + - authselect_current_profile is not match("custom/") + - not result_authselect_custom_profile_present.stat.exists + + - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts + Permitted Per-Session - Ensure authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=before-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts + Permitted Per-Session - Ensure the authselect custom profile is selected + ansible.builtin.command: + cmd: authselect select {{ authselect_custom_profile }} + register: result_pam_authselect_select_profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts + Permitted Per-Session - Restore the authselect features in the custom profile + ansible.builtin.command: + cmd: authselect enable-feature {{ item }} + loop: '{{ result_authselect_features.stdout_lines }}' + register: result_pam_authselect_restore_features + when: + - result_authselect_profile is not skipped + - result_authselect_features is not skipped + - result_pam_authselect_select_profile is not skipped + + - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts + Permitted Per-Session - Ensure authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=after-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - result_pam_authselect_restore_features is not skipped + + - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts + Permitted Per-Session - Change the PAM file to be edited according to the + custom authselect profile + ansible.builtin.set_fact: + pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path + | basename }} + when: + - result_authselect_present.stat.exists + + - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts + Permitted Per-Session - Ensure the "retry" option from "pam_pwquality.so" is + not present in {{ pam_file_path }} + ansible.builtin.replace: + dest: '{{ pam_file_path }}' + regexp: (.*password.*.*.*pam_pwquality.so.*)\bretry\b=?[0-9a-zA-Z]*(.*) + replace: \1\2 + register: result_pam_option_removal + + - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts + Permitted Per-Session - Ensure authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: + - result_authselect_present.stat.exists + - result_pam_option_removal is changed + when: + - '"pam" in ansible_facts.packages' + - result_pam_file_present.stat.exists + tags: + - CCE-83569-4 + - CJIS-5.5.3 + - NIST-800-53-AC-7(a) + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(4) + - accounts_password_pam_retry + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if rpm --quiet -q pam; then + +var_password_pam_retry='' + + +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/security/pwquality.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^retry") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$var_password_pam_retry" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^retry\\>" "/etc/security/pwquality.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^retry\\>.*/$escaped_formatted_output/gi" "/etc/security/pwquality.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-83569-4" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/security/pwquality.conf" >> "/etc/security/pwquality.conf" + printf '%s\n' "$formatted_output" >> "/etc/security/pwquality.conf" +fi + + if [ -e "/etc/pam.d/password-auth" ] ; then + PAM_FILE_PATH="/etc/pam.d/password-auth" + if [ -f /usr/bin/authselect ]; then + if ! authselect check; then + echo " + authselect integrity check failed. Remediation aborted! + This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact. + It is not recommended to manually edit the PAM files when authselect tool is available. + In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended." + exit 1 + fi + CURRENT_PROFILE=$(authselect current -r | awk '{ print $1 }') + # If not already in use, a custom profile is created preserving the enabled features. + if [[ ! $CURRENT_PROFILE == custom/* ]]; then + ENABLED_FEATURES=$(authselect current | tail -n+3 | awk '{ print $2 }') + authselect create-profile hardening -b $CURRENT_PROFILE + CURRENT_PROFILE="custom/hardening" + + authselect apply-changes -b --backup=before-hardening-custom-profile + authselect select $CURRENT_PROFILE + for feature in $ENABLED_FEATURES; do + authselect enable-feature $feature; + done + + authselect apply-changes -b --backup=after-hardening-custom-profile + fi + PAM_FILE_NAME=$(basename "/etc/pam.d/password-auth") + PAM_FILE_PATH="/etc/authselect/$CURRENT_PROFILE/$PAM_FILE_NAME" + + authselect apply-changes -b + fi + +if grep -qP '^\s*password\s+'".*"'\s+pam_pwquality.so\s.*\bretry\b' "$PAM_FILE_PATH"; then + sed -i -E --follow-symlinks 's/(.*password.*'".*"'.*pam_pwquality.so.*)\sretry=?[[:alnum:]]*(.*)/\1\2/g' "$PAM_FILE_PATH" +fi + if [ -f /usr/bin/authselect ]; then + + authselect apply-changes -b + fi +else + echo "/etc/pam.d/password-auth was not found" >&2 +fi + + if [ -e "/etc/pam.d/system-auth" ] ; then + PAM_FILE_PATH="/etc/pam.d/system-auth" + if [ -f /usr/bin/authselect ]; then + if ! authselect check; then + echo " + authselect integrity check failed. Remediation aborted! + This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact. + It is not recommended to manually edit the PAM files when authselect tool is available. + In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended." + exit 1 + fi + CURRENT_PROFILE=$(authselect current -r | awk '{ print $1 }') + # If not already in use, a custom profile is created preserving the enabled features. + if [[ ! $CURRENT_PROFILE == custom/* ]]; then + ENABLED_FEATURES=$(authselect current | tail -n+3 | awk '{ print $2 }') + authselect create-profile hardening -b $CURRENT_PROFILE + CURRENT_PROFILE="custom/hardening" + + authselect apply-changes -b --backup=before-hardening-custom-profile + authselect select $CURRENT_PROFILE + for feature in $ENABLED_FEATURES; do + authselect enable-feature $feature; + done + + authselect apply-changes -b --backup=after-hardening-custom-profile + fi + PAM_FILE_NAME=$(basename "/etc/pam.d/system-auth") + PAM_FILE_PATH="/etc/authselect/$CURRENT_PROFILE/$PAM_FILE_NAME" + + authselect apply-changes -b + fi + +if grep -qP '^\s*password\s+'".*"'\s+pam_pwquality.so\s.*\bretry\b' "$PAM_FILE_PATH"; then + sed -i -E --follow-symlinks 's/(.*password.*'".*"'.*pam_pwquality.so.*)\sretry=?[[:alnum:]]*(.*)/\1\2/g' "$PAM_FILE_PATH" +fi + if [ -f /usr/bin/authselect ]; then + + authselect apply-changes -b + fi +else + echo "/etc/pam.d/system-auth was not found" >&2 +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Ensure PAM Enforces Password Requirements - Minimum Uppercase Characters + The pam_pwquality module's ucredit= parameter controls requirements for +usage of uppercase letters in a password. When set to a negative number, any password will be required to +contain that many uppercase characters. When set to a positive number, pam_pwquality will grant +1 additional +length credit for each uppercase character. Modify the ucredit setting in +/etc/security/pwquality.conf to require the use of an uppercase character in passwords. + BP28(R18) + 1 + 12 + 15 + 16 + 5 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.03 + DSS06.10 + CCI-000192 + CCI-000193 + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.2 + 4.3.3.7.4 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + 0421 + 0422 + 0431 + 0974 + 1173 + 1401 + 1504 + 1505 + 1546 + 1557 + 1558 + 1559 + 1560 + 1561 + A.18.1.4 + A.7.1.1 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.2 + A.9.4.3 + IA-5(c) + IA-5(1)(a) + CM-6(a) + IA-5(4) + PR.AC-1 + PR.AC-6 + PR.AC-7 + FMT_SMF_EXT.1 + Req-8.2.3 + SRG-OS-000069-GPOS-00037 + SRG-OS-000070-GPOS-00038 + SRG-OS-000069-VMM-000360 + Use of a complex password helps to increase the time and resources required to compromise the password. +Password complexity, or strength, is a measure of the effectiveness of a password in resisting attempts +at guessing and brute-force attacks. + +Password complexity is one factor of several that determines how long it takes to crack a password. The more +complex the password, the greater the number of possible combinations that need to be tested before +the password is compromised. + + CCE-83568-6 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83568-6 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(a) + - NIST-800-53-IA-5(4) + - NIST-800-53-IA-5(c) + - PCI-DSS-Req-8.2.3 + - accounts_password_pam_ucredit + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy +- name: XCCDF Value var_password_pam_ucredit # promote to variable + set_fact: + var_password_pam_ucredit: !!str + tags: + - always + +- name: Ensure PAM variable ucredit is set accordingly + lineinfile: + create: true + dest: /etc/security/pwquality.conf + regexp: ^#?\s*ucredit + line: ucredit = {{ var_password_pam_ucredit }} + when: '"pam" in ansible_facts.packages' + tags: + - CCE-83568-6 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(a) + - NIST-800-53-IA-5(4) + - NIST-800-53-IA-5(c) + - PCI-DSS-Req-8.2.3 + - accounts_password_pam_ucredit + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q pam; then + +var_password_pam_ucredit='' + + +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/security/pwquality.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^ucredit") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$var_password_pam_ucredit" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^ucredit\\>" "/etc/security/pwquality.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^ucredit\\>.*/$escaped_formatted_output/gi" "/etc/security/pwquality.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-83568-6" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/security/pwquality.conf" >> "/etc/security/pwquality.conf" + printf '%s\n' "$formatted_output" >> "/etc/security/pwquality.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + + + Set Password Hashing Algorithm + The system's default algorithm for storing password hashes in +/etc/shadow is SHA-512. This can be configured in several +locations. + + Set Password Hashing Algorithm in /etc/libuser.conf + In /etc/libuser.conf, add or correct the following line in its +[defaults] section to ensure the system will use the SHA-512 +algorithm for password hashing: +crypt_style = sha512 + 1 + 12 + 15 + 16 + 5 + 5.6.2.2 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.03 + DSS06.10 + 3.13.11 + CCI-000196 + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.2 + 4.3.3.7.4 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + 0418 + 1055 + 1402 + A.18.1.4 + A.7.1.1 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.2 + A.9.4.3 + IA-5(c) + IA-5(1)(c) + CM-6(a) + PR.AC-1 + PR.AC-6 + PR.AC-7 + Req-8.2.1 + SRG-OS-000073-GPOS-00041 + SRG-OS-000480-VMM-002000 + Passwords need to be protected at all times, and encryption is the standard +method for protecting passwords. If passwords are not encrypted, they can +be plainly read (i.e., clear text) and easily compromised. Passwords that +are encrypted with a weak algorithm are no more protected than if they are +kepy in plain text. + +This setting ensures user and group account administration utilities are +configured to store only encrypted representations of passwords. +Additionally, the crypt_style configuration option ensures the use +of a strong hashing algorithm that makes password cracking attacks more +difficult. + + CCE-88865-1 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-88865-1 + - CJIS-5.6.2.2 + - NIST-800-171-3.13.11 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(c) + - NIST-800-53-IA-5(c) + - PCI-DSS-Req-8.2.1 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - set_password_hashing_algorithm_libuserconf + +- name: Set Password Hashing Algorithm in /etc/libuser.conf + lineinfile: + dest: /etc/libuser.conf + insertafter: ^\s*\[defaults] + regexp: ^#?crypt_style + line: crypt_style = sha512 + state: present + create: true + when: '"libuser" in ansible_facts.packages' + tags: + - CCE-88865-1 + - CJIS-5.6.2.2 + - NIST-800-171-3.13.11 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(c) + - NIST-800-53-IA-5(c) + - PCI-DSS-Req-8.2.1 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - set_password_hashing_algorithm_libuserconf + + # Remediation is applicable only in certain platforms +if rpm --quiet -q libuser; then + +LIBUSER_CONF="/etc/libuser.conf" +CRYPT_STYLE_REGEX='[[:space:]]*\[defaults](.*(\n)+)+?[[:space:]]*crypt_style[[:space:]]*' + +# Try find crypt_style in [defaults] section. If it is here, then change algorithm to sha512. +# If it isn't here, then add it to [defaults] section. +if grep -qzosP $CRYPT_STYLE_REGEX $LIBUSER_CONF ; then + sed -i "s/\(crypt_style[[:space:]]*=[[:space:]]*\).*/\1sha512/g" $LIBUSER_CONF +elif grep -qs "\[defaults]" $LIBUSER_CONF ; then + sed -i "/[[:space:]]*\[defaults]/a crypt_style = sha512" $LIBUSER_CONF +else + echo -e "[defaults]\ncrypt_style = sha512" >> $LIBUSER_CONF +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Set Password Hashing Algorithm in /etc/login.defs + In /etc/login.defs, add or correct the following line to ensure +the system will use SHA-512 as the hashing algorithm: +ENCRYPT_METHOD SHA512 + BP28(R32) + 1 + 12 + 15 + 16 + 5 + 5.6.2.2 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.03 + DSS06.10 + 3.13.11 + CCI-000196 + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.2 + 4.3.3.7.4 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + 0418 + 1055 + 1402 + A.18.1.4 + A.7.1.1 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.2 + A.9.4.3 + IA-5(c) + IA-5(1)(c) + CM-6(a) + PR.AC-1 + PR.AC-6 + PR.AC-7 + Req-8.2.1 + SRG-OS-000073-GPOS-00041 + Passwords need to be protected at all times, and encryption is the standard method for protecting passwords. +If passwords are not encrypted, they can be plainly read (i.e., clear text) and easily compromised. Passwords +that are encrypted with a weak algorithm are no more protected than if they are kept in plain text. + +Using a stronger hashing algorithm makes password cracking attacks more difficult. + + CCE-90590-1 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-90590-1 + - CJIS-5.6.2.2 + - NIST-800-171-3.13.11 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(c) + - NIST-800-53-IA-5(c) + - PCI-DSS-Req-8.2.1 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - set_password_hashing_algorithm_logindefs +- name: XCCDF Value var_password_hashing_algorithm # promote to variable + set_fact: + var_password_hashing_algorithm: !!str + tags: + - always + +- name: Set Password Hashing Algorithm in /etc/login.defs + lineinfile: + dest: /etc/login.defs + regexp: ^#?ENCRYPT_METHOD + line: ENCRYPT_METHOD {{ var_password_hashing_algorithm }} + state: present + create: true + when: '"shadow-utils" in ansible_facts.packages' + tags: + - CCE-90590-1 + - CJIS-5.6.2.2 + - NIST-800-171-3.13.11 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(c) + - NIST-800-53-IA-5(c) + - PCI-DSS-Req-8.2.1 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - set_password_hashing_algorithm_logindefs + + # Remediation is applicable only in certain platforms +if rpm --quiet -q shadow-utils; then + +var_password_hashing_algorithm='' + + +if grep --silent ^ENCRYPT_METHOD /etc/login.defs ; then + sed -i "s/^ENCRYPT_METHOD .*/ENCRYPT_METHOD $var_password_hashing_algorithm/g" /etc/login.defs +else + echo "" >> /etc/login.defs + echo "ENCRYPT_METHOD $var_password_hashing_algorithm" >> /etc/login.defs +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Set PAM''s Password Hashing Algorithm - password-auth + The PAM system service can be configured to only store encrypted +representations of passwords. In +/etc/pam.d/password-auth, +the +password section of the file controls which PAM modules execute +during a password change. Set the pam_unix.so module in the +password section to include the argument sha512, as shown +below: + +password sufficient pam_unix.so sha512 other arguments... + +This will help ensure when local users change their passwords, hashes for +the new passwords will be generated using the SHA-512 algorithm. This is +the default. + BP28(R32) + 1 + 12 + 15 + 16 + 5 + 5.6.2.2 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.03 + DSS06.10 + 3.13.11 + CCI-000196 + CCI-000803 + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.2 + 4.3.3.7.4 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + 0418 + 1055 + 1402 + A.18.1.4 + A.7.1.1 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.2 + A.9.4.3 + IA-5(c) + IA-5(1)(c) + CM-6(a) + PR.AC-1 + PR.AC-6 + PR.AC-7 + Req-8.2.1 + SRG-OS-000073-GPOS-00041 + SRG-OS-000120-GPOS-00061 + SRG-OS-000480-VMM-002000 + Passwords need to be protected at all times, and encryption is the standard +method for protecting passwords. If passwords are not encrypted, they can +be plainly read (i.e., clear text) and easily compromised. Passwords that +are encrypted with a weak algorithm are no more protected than if they are +kepy in plain text. + +This setting ensures user and group account administration utilities are +configured to store only encrypted representations of passwords. +Additionally, the crypt_style configuration option ensures the use +of a strong hashing algorithm that makes password cracking attacks more +difficult. + + CCE-85946-2 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-85946-2 + - CJIS-5.6.2.2 + - NIST-800-171-3.13.11 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(c) + - NIST-800-53-IA-5(c) + - PCI-DSS-Req-8.2.1 + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - set_password_hashing_algorithm_passwordauth + +- name: Set PAM's Password Hashing Algorithm - password-auth - Check if /etc/pam.d/password-auth + file is present + ansible.builtin.stat: + path: /etc/pam.d/password-auth + register: result_pam_file_present + when: '"pam" in ansible_facts.packages' + tags: + - CCE-85946-2 + - CJIS-5.6.2.2 + - NIST-800-171-3.13.11 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(c) + - NIST-800-53-IA-5(c) + - PCI-DSS-Req-8.2.1 + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - set_password_hashing_algorithm_passwordauth + +- name: Set PAM's Password Hashing Algorithm - password-auth - Check the proper remediation + for the system + block: + + - name: Set PAM's Password Hashing Algorithm - password-auth - Define the PAM file + to be edited as a local fact + ansible.builtin.set_fact: + pam_file_path: /etc/pam.d/password-auth + + - name: Set PAM's Password Hashing Algorithm - password-auth - Check if system relies + on authselect + ansible.builtin.stat: + path: /usr/bin/authselect + register: result_authselect_present + + - name: Set PAM's Password Hashing Algorithm - password-auth - Remediate using authselect + block: + + - name: Set PAM's Password Hashing Algorithm - password-auth - Check integrity + of authselect current profile + ansible.builtin.command: + cmd: authselect check + register: result_authselect_check_cmd + changed_when: false + ignore_errors: true + + - name: Set PAM's Password Hashing Algorithm - password-auth - Informative message + based on the authselect integrity check result + ansible.builtin.assert: + that: + - result_authselect_check_cmd is success + fail_msg: + - authselect integrity check failed. Remediation aborted! + - This remediation could not be applied because an authselect profile was + not selected or the selected profile is not intact. + - It is not recommended to manually edit the PAM files when authselect tool + is available. + - In cases where the default authselect profile does not cover a specific + demand, a custom authselect profile is recommended. + success_msg: + - authselect integrity check passed + + - name: Set PAM's Password Hashing Algorithm - password-auth - Get authselect + current profile + ansible.builtin.shell: + cmd: authselect current -r | awk '{ print $1 }' + register: result_authselect_profile + changed_when: false + when: + - result_authselect_check_cmd is success + + - name: Set PAM's Password Hashing Algorithm - password-auth - Define the current + authselect profile as a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: '{{ result_authselect_profile.stdout }}' + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is match("custom/") + + - name: Set PAM's Password Hashing Algorithm - password-auth - Define the new + authselect custom profile as a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: custom/hardening + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is not match("custom/") + + - name: Set PAM's Password Hashing Algorithm - password-auth - Get authselect + current features to also enable them in the custom profile + ansible.builtin.shell: + cmd: authselect current | tail -n+3 | awk '{ print $2 }' + register: result_authselect_features + changed_when: false + when: + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + + - name: Set PAM's Password Hashing Algorithm - password-auth - Check if any custom + profile with the same name was already created + ansible.builtin.stat: + path: /etc/authselect/{{ authselect_custom_profile }} + register: result_authselect_custom_profile_present + changed_when: false + when: + - authselect_current_profile is not match("custom/") + + - name: Set PAM's Password Hashing Algorithm - password-auth - Create an authselect + custom profile based on the current profile + ansible.builtin.command: + cmd: authselect create-profile hardening -b {{ authselect_current_profile + }} + when: + - result_authselect_check_cmd is success + - authselect_current_profile is not match("custom/") + - not result_authselect_custom_profile_present.stat.exists + + - name: Set PAM's Password Hashing Algorithm - password-auth - Ensure authselect + changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=before-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Set PAM's Password Hashing Algorithm - password-auth - Ensure the authselect + custom profile is selected + ansible.builtin.command: + cmd: authselect select {{ authselect_custom_profile }} + register: result_pam_authselect_select_profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Set PAM's Password Hashing Algorithm - password-auth - Restore the authselect + features in the custom profile + ansible.builtin.command: + cmd: authselect enable-feature {{ item }} + loop: '{{ result_authselect_features.stdout_lines }}' + register: result_pam_authselect_restore_features + when: + - result_authselect_profile is not skipped + - result_authselect_features is not skipped + - result_pam_authselect_select_profile is not skipped + + - name: Set PAM's Password Hashing Algorithm - password-auth - Ensure authselect + changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=after-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - result_pam_authselect_restore_features is not skipped + + - name: Set PAM's Password Hashing Algorithm - password-auth - Change the PAM + file to be edited according to the custom authselect profile + ansible.builtin.set_fact: + pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path + | basename }} + when: + - result_authselect_present.stat.exists + + - name: Set PAM's Password Hashing Algorithm - password-auth - Check if expected + PAM module line is present in {{ pam_file_path }} + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + regexp: ^\s*password\s+sufficient\s+pam_unix.so\s*.* + state: absent + check_mode: true + changed_when: false + register: result_pam_line_present + + - name: Set PAM's Password Hashing Algorithm - password-auth - Include or update + the PAM module line in {{ pam_file_path }} + block: + + - name: Set PAM's Password Hashing Algorithm - password-auth - Check if required + PAM module line is present in {{ pam_file_path }} with different control + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + regexp: ^\s*password\s+.*\s+pam_unix.so\s* + state: absent + check_mode: true + changed_when: false + register: result_pam_line_other_control_present + + - name: Set PAM's Password Hashing Algorithm - password-auth - Ensure the correct + control for the required PAM module line in {{ pam_file_path }} + ansible.builtin.replace: + dest: '{{ pam_file_path }}' + regexp: ^(\s*password\s+).*(\bpam_unix.so.*) + replace: \1sufficient \2 + register: result_pam_module_edit + when: + - result_pam_line_other_control_present.found == 1 + + - name: Set PAM's Password Hashing Algorithm - password-auth - Ensure the required + PAM module line is included in {{ pam_file_path }} + ansible.builtin.lineinfile: + dest: '{{ pam_file_path }}' + line: password sufficient pam_unix.so + register: result_pam_module_add + when: + - result_pam_line_other_control_present.found == 0 or result_pam_line_other_control_present.found + > 1 + + - name: Set PAM's Password Hashing Algorithm - password-auth - Ensure authselect + changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: | + result_authselect_present is defined and result_authselect_present.stat.exists and ((result_pam_module_add is defined and result_pam_module_add.changed) or (result_pam_module_edit is defined and result_pam_module_edit.changed)) + when: + - result_pam_line_present.found is defined + - result_pam_line_present.found == 0 + + - name: Set PAM's Password Hashing Algorithm - password-auth - Check if the required + PAM module option is present in {{ pam_file_path }} + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + regexp: ^\s*password\s+sufficient\s+pam_unix.so\s*.*\ssha512\b + state: absent + check_mode: true + changed_when: false + register: result_pam_module_sha512_option_present + + - name: Set PAM's Password Hashing Algorithm - password-auth - Ensure the "sha512" + PAM option for "pam_unix.so" is included in {{ pam_file_path }} + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + backrefs: true + regexp: ^(\s*password\s+sufficient\s+pam_unix.so.*) + line: \1 sha512 + state: present + register: result_pam_sha512_add + when: + - result_pam_module_sha512_option_present.found == 0 + + - name: Set PAM's Password Hashing Algorithm - password-auth - Ensure authselect + changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: + - result_authselect_present.stat.exists + - (result_pam_sha512_add is defined and result_pam_sha512_add.changed) or (result_pam_sha512_edit + is defined and result_pam_sha512_edit.changed) + when: + - '"pam" in ansible_facts.packages' + - result_pam_file_present.stat.exists + tags: + - CCE-85946-2 + - CJIS-5.6.2.2 + - NIST-800-171-3.13.11 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(c) + - NIST-800-53-IA-5(c) + - PCI-DSS-Req-8.2.1 + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - set_password_hashing_algorithm_passwordauth + + # Remediation is applicable only in certain platforms +if rpm --quiet -q pam; then + +if [ -e "/etc/pam.d/password-auth" ] ; then + PAM_FILE_PATH="/etc/pam.d/password-auth" + if [ -f /usr/bin/authselect ]; then + if ! authselect check; then + echo " + authselect integrity check failed. Remediation aborted! + This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact. + It is not recommended to manually edit the PAM files when authselect tool is available. + In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended." + exit 1 + fi + CURRENT_PROFILE=$(authselect current -r | awk '{ print $1 }') + # If not already in use, a custom profile is created preserving the enabled features. + if [[ ! $CURRENT_PROFILE == custom/* ]]; then + ENABLED_FEATURES=$(authselect current | tail -n+3 | awk '{ print $2 }') + authselect create-profile hardening -b $CURRENT_PROFILE + CURRENT_PROFILE="custom/hardening" + + authselect apply-changes -b --backup=before-hardening-custom-profile + authselect select $CURRENT_PROFILE + for feature in $ENABLED_FEATURES; do + authselect enable-feature $feature; + done + + authselect apply-changes -b --backup=after-hardening-custom-profile + fi + PAM_FILE_NAME=$(basename "/etc/pam.d/password-auth") + PAM_FILE_PATH="/etc/authselect/$CURRENT_PROFILE/$PAM_FILE_NAME" + + authselect apply-changes -b + fi + if ! grep -qP '^\s*password\s+'"sufficient"'\s+pam_unix.so\s*.*' "$PAM_FILE_PATH"; then + # Line matching group + control + module was not found. Check group + module. + if [ "$(grep -cP '^\s*password\s+.*\s+pam_unix.so\s*' "$PAM_FILE_PATH")" -eq 1 ]; then + # The control is updated only if one single line matches. + sed -i -E --follow-symlinks 's/^(\s*password\s+).*(\bpam_unix.so.*)/\1'"sufficient"' \2/' "$PAM_FILE_PATH" + else + echo 'password '"sufficient"' pam_unix.so' >> "$PAM_FILE_PATH" + fi + fi + # Check the option + if ! grep -qP '^\s*password\s+'"sufficient"'\s+pam_unix.so\s*.*\ssha512\b' "$PAM_FILE_PATH"; then + sed -i -E --follow-symlinks '/\s*password\s+'"sufficient"'\s+pam_unix.so.*/ s/$/ sha512/' "$PAM_FILE_PATH" + fi + if [ -f /usr/bin/authselect ]; then + + authselect apply-changes -b + fi +else + echo "/etc/pam.d/password-auth was not found" >&2 +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Set PAM''s Password Hashing Algorithm + The PAM system service can be configured to only store encrypted +representations of passwords. In "/etc/pam.d/system-auth", the +password section of the file controls which PAM modules execute +during a password change. Set the pam_unix.so module in the +password section to include the argument sha512, as shown +below: + + +password sufficient pam_unix.so sha512 other arguments... + + +This will help ensure when local users change their passwords, hashes for +the new passwords will be generated using the SHA-512 algorithm. This is +the default. + BP28(R32) + 1 + 12 + 15 + 16 + 5 + 5.6.2.2 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.03 + DSS06.10 + 3.13.11 + CCI-000196 + CCI-000803 + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.2 + 4.3.3.7.4 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + 0418 + 1055 + 1402 + A.18.1.4 + A.7.1.1 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.2 + A.9.4.3 + IA-5(c) + IA-5(1)(c) + CM-6(a) + PR.AC-1 + PR.AC-6 + PR.AC-7 + Req-8.2.1 + SRG-OS-000073-GPOS-00041 + SRG-OS-000120-GPOS-00061 + SRG-OS-000480-VMM-002000 + Passwords need to be protected at all times, and encryption is the standard +method for protecting passwords. If passwords are not encrypted, they can +be plainly read (i.e., clear text) and easily compromised. Passwords that +are encrypted with a weak algorithm are no more protected than if they are +kepy in plain text. + +This setting ensures user and group account administration utilities are +configured to store only encrypted representations of passwords. +Additionally, the crypt_style configuration option ensures the use +of a strong hashing algorithm that makes password cracking attacks more +difficult. + + CCE-83581-9 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83581-9 + - CJIS-5.6.2.2 + - NIST-800-171-3.13.11 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(c) + - NIST-800-53-IA-5(c) + - PCI-DSS-Req-8.2.1 + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - set_password_hashing_algorithm_systemauth + +- name: Set PAM's Password Hashing Algorithm - Check if /etc/pam.d/system-auth file + is present + ansible.builtin.stat: + path: /etc/pam.d/system-auth + register: result_pam_file_present + when: '"pam" in ansible_facts.packages' + tags: + - CCE-83581-9 + - CJIS-5.6.2.2 + - NIST-800-171-3.13.11 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(c) + - NIST-800-53-IA-5(c) + - PCI-DSS-Req-8.2.1 + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - set_password_hashing_algorithm_systemauth + +- name: Set PAM's Password Hashing Algorithm - Check the proper remediation for the + system + block: + + - name: Set PAM's Password Hashing Algorithm - Define the PAM file to be edited + as a local fact + ansible.builtin.set_fact: + pam_file_path: /etc/pam.d/system-auth + + - name: Set PAM's Password Hashing Algorithm - Check if system relies on authselect + ansible.builtin.stat: + path: /usr/bin/authselect + register: result_authselect_present + + - name: Set PAM's Password Hashing Algorithm - Remediate using authselect + block: + + - name: Set PAM's Password Hashing Algorithm - Check integrity of authselect current + profile + ansible.builtin.command: + cmd: authselect check + register: result_authselect_check_cmd + changed_when: false + ignore_errors: true + + - name: Set PAM's Password Hashing Algorithm - Informative message based on the + authselect integrity check result + ansible.builtin.assert: + that: + - result_authselect_check_cmd is success + fail_msg: + - authselect integrity check failed. Remediation aborted! + - This remediation could not be applied because an authselect profile was + not selected or the selected profile is not intact. + - It is not recommended to manually edit the PAM files when authselect tool + is available. + - In cases where the default authselect profile does not cover a specific + demand, a custom authselect profile is recommended. + success_msg: + - authselect integrity check passed + + - name: Set PAM's Password Hashing Algorithm - Get authselect current profile + ansible.builtin.shell: + cmd: authselect current -r | awk '{ print $1 }' + register: result_authselect_profile + changed_when: false + when: + - result_authselect_check_cmd is success + + - name: Set PAM's Password Hashing Algorithm - Define the current authselect profile + as a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: '{{ result_authselect_profile.stdout }}' + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is match("custom/") + + - name: Set PAM's Password Hashing Algorithm - Define the new authselect custom + profile as a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: custom/hardening + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is not match("custom/") + + - name: Set PAM's Password Hashing Algorithm - Get authselect current features + to also enable them in the custom profile + ansible.builtin.shell: + cmd: authselect current | tail -n+3 | awk '{ print $2 }' + register: result_authselect_features + changed_when: false + when: + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + + - name: Set PAM's Password Hashing Algorithm - Check if any custom profile with + the same name was already created + ansible.builtin.stat: + path: /etc/authselect/{{ authselect_custom_profile }} + register: result_authselect_custom_profile_present + changed_when: false + when: + - authselect_current_profile is not match("custom/") + + - name: Set PAM's Password Hashing Algorithm - Create an authselect custom profile + based on the current profile + ansible.builtin.command: + cmd: authselect create-profile hardening -b {{ authselect_current_profile + }} + when: + - result_authselect_check_cmd is success + - authselect_current_profile is not match("custom/") + - not result_authselect_custom_profile_present.stat.exists + + - name: Set PAM's Password Hashing Algorithm - Ensure authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=before-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Set PAM's Password Hashing Algorithm - Ensure the authselect custom profile + is selected + ansible.builtin.command: + cmd: authselect select {{ authselect_custom_profile }} + register: result_pam_authselect_select_profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Set PAM's Password Hashing Algorithm - Restore the authselect features + in the custom profile + ansible.builtin.command: + cmd: authselect enable-feature {{ item }} + loop: '{{ result_authselect_features.stdout_lines }}' + register: result_pam_authselect_restore_features + when: + - result_authselect_profile is not skipped + - result_authselect_features is not skipped + - result_pam_authselect_select_profile is not skipped + + - name: Set PAM's Password Hashing Algorithm - Ensure authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=after-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - result_pam_authselect_restore_features is not skipped + + - name: Set PAM's Password Hashing Algorithm - Change the PAM file to be edited + according to the custom authselect profile + ansible.builtin.set_fact: + pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path + | basename }} + when: + - result_authselect_present.stat.exists + + - name: Set PAM's Password Hashing Algorithm - Check if expected PAM module line + is present in {{ pam_file_path }} + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + regexp: ^\s*password\s+sufficient\s+pam_unix.so\s*.* + state: absent + check_mode: true + changed_when: false + register: result_pam_line_present + + - name: Set PAM's Password Hashing Algorithm - Include or update the PAM module + line in {{ pam_file_path }} + block: + + - name: Set PAM's Password Hashing Algorithm - Check if required PAM module line + is present in {{ pam_file_path }} with different control + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + regexp: ^\s*password\s+.*\s+pam_unix.so\s* + state: absent + check_mode: true + changed_when: false + register: result_pam_line_other_control_present + + - name: Set PAM's Password Hashing Algorithm - Ensure the correct control for + the required PAM module line in {{ pam_file_path }} + ansible.builtin.replace: + dest: '{{ pam_file_path }}' + regexp: ^(\s*password\s+).*(\bpam_unix.so.*) + replace: \1sufficient \2 + register: result_pam_module_edit + when: + - result_pam_line_other_control_present.found == 1 + + - name: Set PAM's Password Hashing Algorithm - Ensure the required PAM module + line is included in {{ pam_file_path }} + ansible.builtin.lineinfile: + dest: '{{ pam_file_path }}' + line: password sufficient pam_unix.so + register: result_pam_module_add + when: + - result_pam_line_other_control_present.found == 0 or result_pam_line_other_control_present.found + > 1 + + - name: Set PAM's Password Hashing Algorithm - Ensure authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: | + result_authselect_present is defined and result_authselect_present.stat.exists and ((result_pam_module_add is defined and result_pam_module_add.changed) or (result_pam_module_edit is defined and result_pam_module_edit.changed)) + when: + - result_pam_line_present.found is defined + - result_pam_line_present.found == 0 + + - name: Set PAM's Password Hashing Algorithm - Check if the required PAM module + option is present in {{ pam_file_path }} + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + regexp: ^\s*password\s+sufficient\s+pam_unix.so\s*.*\ssha512\b + state: absent + check_mode: true + changed_when: false + register: result_pam_module_sha512_option_present + + - name: Set PAM's Password Hashing Algorithm - Ensure the "sha512" PAM option for + "pam_unix.so" is included in {{ pam_file_path }} + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + backrefs: true + regexp: ^(\s*password\s+sufficient\s+pam_unix.so.*) + line: \1 sha512 + state: present + register: result_pam_sha512_add + when: + - result_pam_module_sha512_option_present.found == 0 + + - name: Set PAM's Password Hashing Algorithm - Ensure authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: + - result_authselect_present.stat.exists + - (result_pam_sha512_add is defined and result_pam_sha512_add.changed) or (result_pam_sha512_edit + is defined and result_pam_sha512_edit.changed) + when: + - '"pam" in ansible_facts.packages' + - result_pam_file_present.stat.exists + tags: + - CCE-83581-9 + - CJIS-5.6.2.2 + - NIST-800-171-3.13.11 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(c) + - NIST-800-53-IA-5(c) + - PCI-DSS-Req-8.2.1 + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - set_password_hashing_algorithm_systemauth + + # Remediation is applicable only in certain platforms +if rpm --quiet -q pam; then + +if [ -e "/etc/pam.d/system-auth" ] ; then + PAM_FILE_PATH="/etc/pam.d/system-auth" + if [ -f /usr/bin/authselect ]; then + if ! authselect check; then + echo " + authselect integrity check failed. Remediation aborted! + This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact. + It is not recommended to manually edit the PAM files when authselect tool is available. + In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended." + exit 1 + fi + CURRENT_PROFILE=$(authselect current -r | awk '{ print $1 }') + # If not already in use, a custom profile is created preserving the enabled features. + if [[ ! $CURRENT_PROFILE == custom/* ]]; then + ENABLED_FEATURES=$(authselect current | tail -n+3 | awk '{ print $2 }') + authselect create-profile hardening -b $CURRENT_PROFILE + CURRENT_PROFILE="custom/hardening" + + authselect apply-changes -b --backup=before-hardening-custom-profile + authselect select $CURRENT_PROFILE + for feature in $ENABLED_FEATURES; do + authselect enable-feature $feature; + done + + authselect apply-changes -b --backup=after-hardening-custom-profile + fi + PAM_FILE_NAME=$(basename "/etc/pam.d/system-auth") + PAM_FILE_PATH="/etc/authselect/$CURRENT_PROFILE/$PAM_FILE_NAME" + + authselect apply-changes -b + fi + if ! grep -qP '^\s*password\s+'"sufficient"'\s+pam_unix.so\s*.*' "$PAM_FILE_PATH"; then + # Line matching group + control + module was not found. Check group + module. + if [ "$(grep -cP '^\s*password\s+.*\s+pam_unix.so\s*' "$PAM_FILE_PATH")" -eq 1 ]; then + # The control is updated only if one single line matches. + sed -i -E --follow-symlinks 's/^(\s*password\s+).*(\bpam_unix.so.*)/\1'"sufficient"' \2/' "$PAM_FILE_PATH" + else + echo 'password '"sufficient"' pam_unix.so' >> "$PAM_FILE_PATH" + fi + fi + # Check the option + if ! grep -qP '^\s*password\s+'"sufficient"'\s+pam_unix.so\s*.*\ssha512\b' "$PAM_FILE_PATH"; then + sed -i -E --follow-symlinks '/\s*password\s+'"sufficient"'\s+pam_unix.so.*/ s/$/ sha512/' "$PAM_FILE_PATH" + fi + if [ -f /usr/bin/authselect ]; then + + authselect apply-changes -b + fi +else + echo "/etc/pam.d/system-auth was not found" >&2 +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Set Password Hashing Rounds in /etc/login.defs + In /etc/login.defs, ensure SHA_CRYPT_MIN_ROUNDS and +SHA_CRYPT_MAX_ROUNDS has the minimum value of 5000. +For example: +SHA_CRYPT_MIN_ROUNDS 5000 +SHA_CRYPT_MAX_ROUNDS 5000 +Notice that if neither are set, they already have the default value of 5000. +If either is set, they must have the minimum value of 5000. + CCI-000196 + CCI-000803 + SRG-OS-000073-GPOS-00041 + SRG-OS-000120-GPOS-00061 + Passwords need to be protected at all times, and encryption is the standard +method for protecting passwords. If passwords are not encrypted, they can +be plainly read (i.e., clear text) and easily compromised. Passwords +that are encrypted with a weak algorithm are no more protected than if +they are kept in plain text. + +Using more hashing rounds makes password cracking attacks more difficult. + CCE-89708-2 + + + + + + + + + + + Protect Physical Console Access + It is impossible to fully protect a system from an +attacker with physical access, so securing the space in which the +system is located should be considered a necessary step. However, +there are some steps which, if taken, make it more difficult for an +attacker to quickly or undetectably modify a system from its +console. + + Disable debug-shell SystemD Service + SystemD's debug-shell service is intended to +diagnose SystemD related boot issues with various systemctl +commands. Once enabled and following a system reboot, the root shell +will be available on tty9 which is access by pressing +CTRL-ALT-F9. The debug-shell service should only be used +for SystemD related issues and should otherwise be disabled. + +By default, the debug-shell SystemD service is already disabled. + +The debug-shell service can be disabled with the following command: +$ sudo systemctl mask --now debug-shell.service + 3.4.5 + CCI-000366 + 164.308(a)(1)(ii)(B) + 164.308(a)(7)(i) + 164.308(a)(7)(ii)(A) + 164.310(a)(1) + 164.310(a)(2)(i) + 164.310(a)(2)(ii) + 164.310(a)(2)(iii) + 164.310(b) + 164.310(c) + 164.310(d)(1) + 164.310(d)(2)(iii) + CM-6 + FIA_UAU.1 + SRG-OS-000324-GPOS-00125 + SRG-OS-000480-GPOS-00227 + This prevents attackers with physical access from trivially bypassing security +on the machine through valid troubleshooting configurations and gaining root +access when the system is rebooted. + + CCE-90724-6 + include disable_debug-shell + +class disable_debug-shell { + service {'debug-shell': + enable => false, + ensure => 'stopped', + } +} + + - name: Disable service debug-shell + block: + + - name: Disable service debug-shell + systemd: + name: debug-shell.service + enabled: 'no' + state: stopped + masked: 'yes' + ignore_errors: 'yes' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-90724-6 + - NIST-800-171-3.4.5 + - NIST-800-53-CM-6 + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_debug-shell_disabled + +- name: Unit Socket Exists - debug-shell.socket + command: systemctl list-unit-files debug-shell.socket + args: + warn: false + register: socket_file_exists + changed_when: false + ignore_errors: true + check_mode: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-90724-6 + - NIST-800-171-3.4.5 + - NIST-800-53-CM-6 + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_debug-shell_disabled + +- name: Disable socket debug-shell + systemd: + name: debug-shell.socket + enabled: 'no' + state: stopped + masked: 'yes' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"debug-shell.socket" in socket_file_exists.stdout_lines[1]' + tags: + - CCE-90724-6 + - NIST-800-171-3.4.5 + - NIST-800-53-CM-6 + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_debug-shell_disabled + + +[customizations.services] +disabled = ["debug-shell"] + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + systemd: + units: + - enabled: false + name: debug-shell.service + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" stop 'debug-shell.service' +"$SYSTEMCTL_EXEC" disable 'debug-shell.service' +"$SYSTEMCTL_EXEC" mask 'debug-shell.service' +# Disable socket activation if we have a unit file for it +if "$SYSTEMCTL_EXEC" list-unit-files | grep -q '^debug-shell.socket'; then + "$SYSTEMCTL_EXEC" stop 'debug-shell.socket' + "$SYSTEMCTL_EXEC" mask 'debug-shell.socket' +fi +# The service may not be running because it has been started and failed, +# so let's reset the state so OVAL checks pass. +# Service should be 'inactive', not 'failed' after reboot though. +"$SYSTEMCTL_EXEC" reset-failed 'debug-shell.service' || true + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable Ctrl-Alt-Del Burst Action + By default, SystemD will reboot the system if the Ctrl-Alt-Del +key sequence is pressed Ctrl-Alt-Delete more than 7 times in 2 seconds. + +To configure the system to ignore the CtrlAltDelBurstAction + +setting, add or modify the following to /etc/systemd/system.conf: +CtrlAltDelBurstAction=none + Disabling the Ctrl-Alt-Del key sequence +in /etc/init/control-alt-delete.conf DOES NOT disable the Ctrl-Alt-Del +key sequence if running in runlevel 6 (e.g. in GNOME, KDE, etc.)! The +Ctrl-Alt-Del key sequence will only be disabled if running in +the non-graphical runlevel 3. + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + 3.4.5 + CCI-000366 + 164.308(a)(1)(ii)(B) + 164.308(a)(7)(i) + 164.308(a)(7)(ii)(A) + 164.310(a)(1) + 164.310(a)(2)(i) + 164.310(a)(2)(ii) + 164.310(a)(2)(iii) + 164.310(b) + 164.310(c) + 164.310(d)(1) + 164.310(d)(2)(iii) + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-6(a) + AC-6(1) + CM-6(a) + PR.AC-4 + PR.DS-5 + FAU_GEN.1.2 + SRG-OS-000324-GPOS-00125 + SRG-OS-000480-GPOS-00227 + A locally logged-in user who presses Ctrl-Alt-Del, when at the console, +can reboot the system. If accidentally pressed, as could happen in +the case of mixed OS environment, this can create the risk of short-term +loss of availability of systems due to unintentional reboot. + + CCE-90308-8 + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,CtrlAltDelBurstAction%3Dnone + mode: 0644 + path: /etc/systemd/system.conf.d/disable_ctrlaltdelete_burstaction.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if rpm --quiet -q systemd; then + +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/systemd/system.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^CtrlAltDelBurstAction=") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s=%s" "$stripped_key" "none" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^CtrlAltDelBurstAction=\\>" "/etc/systemd/system.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^CtrlAltDelBurstAction=\\>.*/$escaped_formatted_output/gi" "/etc/systemd/system.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-90308-8" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/systemd/system.conf" >> "/etc/systemd/system.conf" + printf '%s\n' "$formatted_output" >> "/etc/systemd/system.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable Ctrl-Alt-Del Reboot Activation + By default, SystemD will reboot the system if the Ctrl-Alt-Del +key sequence is pressed. + +To configure the system to ignore the Ctrl-Alt-Del key sequence from the + +command line instead of rebooting the system, do either of the following: +ln -sf /dev/null /etc/systemd/system/ctrl-alt-del.target +or +systemctl mask ctrl-alt-del.target + +Do not simply delete the /usr/lib/systemd/system/ctrl-alt-del.service file, +as this file may be restored during future system updates. + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + 3.4.5 + CCI-000366 + 164.308(a)(1)(ii)(B) + 164.308(a)(7)(i) + 164.308(a)(7)(ii)(A) + 164.310(a)(1) + 164.310(a)(2)(i) + 164.310(a)(2)(ii) + 164.310(a)(2)(iii) + 164.310(b) + 164.310(c) + 164.310(d)(1) + 164.310(d)(2)(iii) + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + FAU_GEN.1.2 + SRG-OS-000324-GPOS-00125 + SRG-OS-000480-GPOS-00227 + A locally logged-in user who presses Ctrl-Alt-Del, when at the console, +can reboot the system. If accidentally pressed, as could happen in +the case of mixed OS environment, this can create the risk of short-term +loss of availability of systems due to unintentional reboot. + + CCE-86667-3 + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + systemd: + units: + - name: ctrl-alt-del.target + mask: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +systemctl disable --now ctrl-alt-del.target +systemctl mask --now ctrl-alt-del.target + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Verify that Interactive Boot is Disabled + Red Hat Enterprise Linux 9 systems support an "interactive boot" option that can +be used to prevent services from being started. On a Red Hat Enterprise Linux 9 +system, interactive boot can be enabled by providing a 1, +yes, true, or on value to the +systemd.confirm_spawn kernel argument in /etc/default/grub. +Remove any instance of systemd.confirm_spawn=(1|yes|true|on) from +the kernel arguments in that file to disable interactive boot. +Recovery booting must also be disabled. Confirm that +GRUB_DISABLE_RECOVERY=true is set in /etc/default/grub. +It is also required to change the runtime configuration, run: + +/sbin/grubby --update-kernel=ALL --remove-args="systemd.confirm_spawn" + +grub2-mkconfig -o /boot/grub2/grub.cfg + 11 + 12 + 14 + 15 + 16 + 18 + 3 + 5 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + DSS06.03 + DSS06.06 + 3.1.2 + 3.4.5 + CCI-000213 + 164.308(a)(1)(ii)(B) + 164.308(a)(7)(i) + 164.308(a)(7)(ii)(A) + 164.310(a)(1) + 164.310(a)(2)(i) + 164.310(a)(2)(ii) + 164.310(a)(2)(iii) + 164.310(b) + 164.310(c) + 164.310(d)(1) + 164.310(d)(2)(iii) + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + A.6.1.2 + A.7.1.1 + A.9.1.2 + A.9.2.1 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + SC-2(1) + CM-6(a) + PR.AC-4 + PR.AC-6 + PR.PT-3 + FIA_UAU.1 + SRG-OS-000480-GPOS-00227 + Using interactive or recovery boot, the console user could disable auditing, firewalls, +or other services, weakening system security. + + CCE-87114-5 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-87114-5 + - NIST-800-171-3.1.2 + - NIST-800-171-3.4.5 + - NIST-800-53-CM-6(a) + - NIST-800-53-SC-2(1) + - grub2_disable_interactive_boot + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Verify GRUB_DISABLE_RECOVERY=true + lineinfile: + path: /etc/default/grub + regexp: ^GRUB_DISABLE_RECOVERY=.* + line: GRUB_DISABLE_RECOVERY=true + state: present + when: '"grub2-common" in ansible_facts.packages' + tags: + - CCE-87114-5 + - NIST-800-171-3.1.2 + - NIST-800-171-3.4.5 + - NIST-800-53-CM-6(a) + - NIST-800-53-SC-2(1) + - grub2_disable_interactive_boot + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Verify that Interactive Boot is Disabled in /etc/default/grub + replace: + dest: /etc/default/grub + regexp: systemd.confirm_spawn(=(1|yes|true|on)|\b) + replace: systemd.confirm_spawn=no + when: '"grub2-common" in ansible_facts.packages' + tags: + - CCE-87114-5 + - NIST-800-171-3.1.2 + - NIST-800-171-3.4.5 + - NIST-800-53-CM-6(a) + - NIST-800-53-SC-2(1) + - grub2_disable_interactive_boot + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Verify that Interactive Boot is Disabled (runtime) + command: /sbin/grubby --update-kernel=ALL --remove-args="systemd.confirm_spawn" + when: '"grub2-common" in ansible_facts.packages' + tags: + - CCE-87114-5 + - NIST-800-171-3.1.2 + - NIST-800-171-3.4.5 + - NIST-800-53-CM-6(a) + - NIST-800-53-SC-2(1) + - grub2_disable_interactive_boot + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Regen grub.cfg handle updated GRUB_DISABLE_RECOVERY and confirm_spawn + command: grub2-mkconfig -o /boot/grub2/grub.cfg + when: '"grub2-common" in ansible_facts.packages' + tags: + - CCE-87114-5 + - NIST-800-171-3.1.2 + - NIST-800-171-3.4.5 + - NIST-800-53-CM-6(a) + - NIST-800-53-SC-2(1) + - grub2_disable_interactive_boot + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q grub2-common; then + +# Verify that Interactive Boot is Disabled in /etc/default/grub +CONFIRM_SPAWN_YES="systemd.confirm_spawn\(=\(1\|yes\|true\|on\)\|\b\)" +CONFIRM_SPAWN_NO="systemd.confirm_spawn=no" + +if grep -q "\(GRUB_CMDLINE_LINUX\|GRUB_CMDLINE_LINUX_DEFAULT\)" /etc/default/grub +then + sed -i "s/${CONFIRM_SPAWN_YES}/${CONFIRM_SPAWN_NO}/" /etc/default/grub +fi + +# make sure GRUB_DISABLE_RECOVERY=true +if grep -q '^GRUB_DISABLE_RECOVERY=.*' '/etc/default/grub' ; then + # modify the GRUB command-line if an GRUB_DISABLE_RECOVERY= arg already exists + sed -i 's/GRUB_DISABLE_RECOVERY=.*/GRUB_DISABLE_RECOVERY=true/' /etc/default/grub +else + # no GRUB_DISABLE_RECOVERY=arg is present, append it to file + echo "GRUB_DISABLE_RECOVERY=true" >> '/etc/default/grub' +fi + + + +# Remove 'systemd.confirm_spawn' kernel argument also from runtime settings +/sbin/grubby --update-kernel=ALL --remove-args="systemd.confirm_spawn" + + +#Regen grub.cfg handle updated GRUB_DISABLE_RECOVERY and confirm_spawn +grub2-mkconfig -o /boot/grub2/grub.cfg + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Require Authentication for Emergency Systemd Target + Emergency mode is intended as a system recovery +method, providing a single user root access to the system +during a failed boot sequence. + +By default, Emergency mode is protected by requiring a password and is set +in /usr/lib/systemd/system/emergency.service. + 1 + 11 + 12 + 14 + 15 + 16 + 18 + 3 + 5 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.03 + DSS06.06 + DSS06.10 + 3.1.1 + 3.4.5 + CCI-000213 + 164.308(a)(1)(ii)(B) + 164.308(a)(7)(i) + 164.308(a)(7)(ii)(A) + 164.310(a)(1) + 164.310(a)(2)(i) + 164.310(a)(2)(ii) + 164.310(a)(2)(iii) + 164.310(b) + 164.310(c) + 164.310(d)(1) + 164.310(d)(2)(iii) + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + 0421 + 0422 + 0431 + 0974 + 1173 + 1401 + 1504 + 1505 + 1546 + 1557 + 1558 + 1559 + 1560 + 1561 + A.18.1.4 + A.6.1.2 + A.7.1.1 + A.9.1.2 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.1 + A.9.4.2 + A.9.4.3 + A.9.4.4 + A.9.4.5 + IA-2 + AC-3 + CM-6(a) + PR.AC-1 + PR.AC-4 + PR.AC-6 + PR.AC-7 + PR.PT-3 + FIA_UAU.1 + SRG-OS-000080-GPOS-00048 + This prevents attackers with physical access from trivially bypassing security +on the machine and gaining root access. Such accesses are further prevented +by configuring the bootloader password. + + CCE-83592-6 + - name: require emergency mode password + lineinfile: + create: true + dest: /usr/lib/systemd/system/emergency.service + regexp: ^#?ExecStart= + line: ExecStart=-/usr/lib/systemd/systemd-sulogin-shell emergency + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83592-6 + - NIST-800-171-3.1.1 + - NIST-800-171-3.4.5 + - NIST-800-53-AC-3 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-2 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - require_emergency_target_auth + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +service_file="/usr/lib/systemd/system/emergency.service" + +sulogin="/usr/lib/systemd/systemd-sulogin-shell emergency" + +if grep "^ExecStart=.*" "$service_file" ; then + sed -i "s%^ExecStart=.*%ExecStart=-$sulogin%" "$service_file" +else + echo "ExecStart=-$sulogin" >> "$service_file" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Require Authentication for Single User Mode + Single-user mode is intended as a system recovery +method, providing a single user root access to the system by +providing a boot option at startup. + +By default, single-user mode is protected by requiring a password and is set +in /usr/lib/systemd/system/rescue.service. + 1 + 11 + 12 + 14 + 15 + 16 + 18 + 3 + 5 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.03 + DSS06.06 + DSS06.10 + 3.1.1 + 3.4.5 + CCI-000213 + 164.308(a)(1)(ii)(B) + 164.308(a)(7)(i) + 164.308(a)(7)(ii)(A) + 164.310(a)(1) + 164.310(a)(2)(i) + 164.310(a)(2)(ii) + 164.310(a)(2)(iii) + 164.310(b) + 164.310(c) + 164.310(d)(1) + 164.310(d)(2)(iii) + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + 0421 + 0422 + 0431 + 0974 + 1173 + 1401 + 1504 + 1505 + 1546 + 1557 + 1558 + 1559 + 1560 + 1561 + A.18.1.4 + A.6.1.2 + A.7.1.1 + A.9.1.2 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.1 + A.9.4.2 + A.9.4.3 + A.9.4.4 + A.9.4.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.2.3 + CIP-004-6 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.2 + CIP-007-3 R5.2 + CIP-007-3 R5.3.1 + CIP-007-3 R5.3.2 + CIP-007-3 R5.3.3 + IA-2 + AC-3 + CM-6(a) + PR.AC-1 + PR.AC-4 + PR.AC-6 + PR.AC-7 + PR.PT-3 + FIA_UAU.1 + SRG-OS-000080-GPOS-00048 + This prevents attackers with physical access from trivially bypassing security +on the machine and gaining root access. Such accesses are further prevented +by configuring the bootloader password. + + CCE-83594-2 + - name: require single user mode password + lineinfile: + create: true + dest: /usr/lib/systemd/system/rescue.service + regexp: ^#?ExecStart= + line: ExecStart=-/usr/lib/systemd/systemd-sulogin-shell rescue + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83594-2 + - NIST-800-171-3.1.1 + - NIST-800-171-3.4.5 + - NIST-800-53-AC-3 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-2 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - require_singleuser_auth + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +service_file="/usr/lib/systemd/system/rescue.service" + +sulogin="/usr/lib/systemd/systemd-sulogin-shell rescue" + +if grep "^ExecStart=.*" "$service_file" ; then + sed -i "s%^ExecStart=.*%ExecStart=-$sulogin%" "$service_file" +else + echo "ExecStart=-$sulogin" >> "$service_file" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure Screen Locking + When a user must temporarily leave an account +logged-in, screen locking should be employed to prevent passersby +from abusing the account. User education and training is +particularly important for screen locking to be effective, and policies +can be implemented to reinforce this. + +Automatic screen locking is only meant as a safeguard for +those cases where a user forgot to lock the screen. + + Configure Console Screen Locking + A console screen locking mechanism is a temporary action taken when a user +stops work and moves away from the immediate physical vicinity of the +information system but does not logout because of the temporary nature of +the absence. Rather than relying on the user to manually lock their +operation system session prior to vacating the vicinity, operating systems +need to be able to identify when a user's session has idled and take action +to initiate the session lock. + + Install the tmux Package + To enable console screen locking, install the tmux package. +A session lock is a temporary action taken when a user stops work and moves away from the immediate physical vicinity of the information system but does not want to log out because of the temporary nature of the absence. +The session lock is implemented at the point where session activity can be determined. +Rather than be forced to wait for a period of time to expire before the user session can be locked, Red Hat Enterprise Linux 9 needs to provide users with the ability to manually invoke a session lock so users can secure their session if it is necessary to temporarily vacate the immediate physical vicinity. +Instruct users to begin new terminal sessions with the following command: +$ tmux +The console can now be locked with the following key combination: +ctrl+b :lock-session + 1 + 12 + 15 + 16 + DSS05.04 + DSS05.10 + DSS06.10 + 3.1.10 + CCI-000058 + CCI-000056 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + A.18.1.4 + A.9.2.1 + A.9.2.4 + A.9.3.1 + A.9.4.2 + A.9.4.3 + CM-6(a) + PR.AC-7 + FMT_SMF_EXT.1 + FMT_MOF_EXT.1 + FTA_SSL.1 + SRG-OS-000030-GPOS-00011 + SRG-OS-000028-GPOS-00009 + SRG-OS-000030-VMM-000110 + A session time-out lock is a temporary action taken when a user stops work and moves away from the immediate +physical vicinity of the information system but does not logout because of the temporary nature of the absence. +Rather than relying on the user to manually lock their operation system session prior to vacating the vicinity, +operating systems need to be able to identify when a user's session has idled and take action to initiate the +session lock. + +The tmux package allows for a session lock to be implemented and configured. + + CCE-83599-1 + +package --add=tmux + + include install_tmux + +class install_tmux { + package { 'tmux': + ensure => 'installed', + } +} + + - name: Ensure tmux is installed + package: + name: tmux + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83599-1 + - NIST-800-171-3.1.10 + - NIST-800-53-CM-6(a) + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_tmux_installed + + +[[packages]] +name = "tmux" +version = "*" + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if ! rpm -q --quiet "tmux" ; then + dnf install -y "tmux" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Support session locking with tmux + The tmux terminal multiplexer is used to implement +automatic session locking. It should be started from +/etc/bashrc or drop-in files within /etc/profile.d/. + CCI-000056 + CCI-000058 + FMT_SMF_EXT.1 + FMT_MOF_EXT.1 + FTA_SSL.1 + SRG-OS-000031-GPOS-00012 + SRG-OS-000028-GPOS-00009 + SRG-OS-000030-GPOS-00011 + Unlike bash itself, the tmux terminal multiplexer +provides a mechanism to lock sessions after period of inactivity. +A session lock is a temporary action taken when a user stops work and moves away from the +immediate physical vicinity of the information system but does not want to +log out because of the temporary nature of the absence. + + CCE-90586-9 + # Remediation is applicable only in certain platforms +if rpm --quiet -q tmux; then + +if ! grep -x ' case "$name" in sshd|login) exec tmux ;; esac' /etc/bashrc; then + cat >> /etc/profile.d/tmux.sh <<'EOF' +if [ "$PS1" ]; then + parent=$(ps -o ppid= -p $$) + name=$(ps -o comm= -p $parent) + case "$name" in sshd|login) exec tmux ;; esac +fi +EOF + chmod 0644 /etc/profile.d/tmux.sh +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure tmux to lock session after inactivity + To enable console screen locking in tmux terminal multiplexer +after a period of inactivity, +the lock-after-time option has to be set to a value greater than 0 and less than +or equal to 900 in /etc/tmux.conf. + CCI-000057 + CCI-000060 + FMT_SMF_EXT.1 + FMT_MOF_EXT.1 + FTA_SSL.1 + SRG-OS-000029-GPOS-00010 + SRG-OS-000031-GPOS-00012 + Locking the session after a period of inactivity limits the +potential exposure if the session is left unattended. + + CCE-89876-7 + - name: Configure tmux to lock session after inactivity + block: + + - name: Check for duplicate values + lineinfile: + path: /etc/tmux.conf + create: false + regexp: ^\s*set -g lock-after-time\s+ + mode: '0644' + state: absent + check_mode: true + changed_when: false + register: dupes + + - name: Deduplicate values from /etc/tmux.conf + lineinfile: + path: /etc/tmux.conf + create: false + regexp: ^\s*set -g lock-after-time\s+ + mode: '0644' + state: absent + when: dupes.found is defined and dupes.found > 1 + + - name: Insert correct line to /etc/tmux.conf + lineinfile: + path: /etc/tmux.conf + create: true + regexp: ^\s*set -g lock-after-time\s+ + mode: '0644' + line: set -g lock-after-time 900 + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-89876-7 + - configure_tmux_lock_after_time + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +tmux_conf="/etc/tmux.conf" + +if grep -qP '^\s*set\s+-g\s+lock-after-time' "$tmux_conf" ; then + sed -i 's/^\s*set\s\+-g\s\+lock-after-time.*$/set -g lock-after-time 900/' "$tmux_conf" +else + echo "set -g lock-after-time 900" >> "$tmux_conf" +fi +chmod 0644 "$tmux_conf" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure the tmux Lock Command + To enable console screen locking in tmux terminal multiplexer, +the vlock command must be configured to be used as a locking +mechanism. +Add the following line to /etc/tmux.conf: +set -g lock-command vlock. +The console can now be locked with the following key combination: +ctrl+b :lock-session + CCI-000056 + CCI-000058 + AC-11(a) + AC-11(b) + CM-6(a) + FMT_SMF_EXT.1 + FMT_MOF_EXT.1 + FTA_SSL.1 + SRG-OS-000028-GPOS-00009 + SRG-OS-000030-GPOS-00011 + SRG-OS-000028-VMM-000090 + SRG-OS-000030-VMM-000110 + The tmux package allows for a session lock to be implemented and configured. +However, the session lock is implemented by an external command. The tmux +default configuration does not contain an effective session lock. + + CCE-90171-0 + - name: Configure the tmux Lock Command + block: + + - name: Check for duplicate values + lineinfile: + path: /etc/tmux.conf + create: false + regexp: ^\s*set -g lock-command\s+ + mode: '0644' + state: absent + check_mode: true + changed_when: false + register: dupes + + - name: Deduplicate values from /etc/tmux.conf + lineinfile: + path: /etc/tmux.conf + create: false + regexp: ^\s*set -g lock-command\s+ + mode: '0644' + state: absent + when: dupes.found is defined and dupes.found > 1 + + - name: Insert correct line to /etc/tmux.conf + lineinfile: + path: /etc/tmux.conf + create: true + regexp: ^\s*set -g lock-command\s+ + mode: '0644' + line: set -g lock-command vlock + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-90171-0 + - NIST-800-53-AC-11(a) + - NIST-800-53-AC-11(b) + - NIST-800-53-CM-6(a) + - configure_tmux_lock_command + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +tmux_conf="/etc/tmux.conf" + +if grep -qP '^\s*set\s+-g\s+lock-command' "$tmux_conf" ; then + sed -i 's/^\s*set\s\+-g\s\+lock-command.*$/set -g lock-command vlock/' "$tmux_conf" +else + echo "set -g lock-command vlock" >> "$tmux_conf" +fi +chmod 0644 "$tmux_conf" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Prevent user from disabling the screen lock + The tmux terminal multiplexer is used to implement +automatic session locking. It should not be listed in +/etc/shells. + CCI-000056 + CCI-000058 + CM-6 + FMT_SMF_EXT.1 + FMT_MOF_EXT.1 + FTA_SSL.1 + SRG-OS-000324-GPOS-00125 + SRG-OS-000028-GPOS-00009 + SRG-OS-000030-GPOS-00011 + Not listing tmux among permitted shells +prevents malicious program running as user +from lowering security by disabling the screen lock. + + CCE-89538-3 + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,/bin/sh%0A/bin/bash%0A/usr/bin/sh%0A/usr/bin/bash%0A + mode: 0644 + path: /etc/shells + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if grep -q 'tmux\s*$' /etc/shells ; then + sed -i '/tmux\s*$/d' /etc/shells +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Hardware Tokens for Authentication + The use of hardware tokens such as smart cards for system login +provides stronger, two-factor authentication than using a username and password. + +In Red Hat Enterprise Linux servers and workstations, hardware token login + +is not enabled by default and must be enabled in the system settings. + + + OpenSC Smart Card Drivers + Choose the Smart Card Driver in use by your organization. +For DoD, choose the cac driver. +If your driver is not listed and you don't want to use the +default driver, use the other option and +manually specify your driver. + default + acos5 + akis + asepcos + atrust-acos + authentic + belpic + cac + cardos + coolkey + cyberflex + dnie + entersafe + epass2003 + flex + gemsafeV1 + gids + gpk + iasecc + incrypto34 + isoApplet + itacns + jpki + MaskTech + mcrd + muscle + myeid + npa + oberthur + openpgp + None + PIV-II + rutoken_ecp + rutoken + sc-hsm + setcos + starcos + tcos + westcos + + + Install the opensc Package For Multifactor Authentication + +The opensc package can be installed with the following command: + +$ sudo dnf install opensc + CCI-001954 + CCI-001953 + 1382 + 1384 + 1386 + CM-6(a) + SRG-OS-000375-GPOS-00160 + SRG-OS-000376-GPOS-00161 + SRG-OS-000376-VMM-001520 + Using an authentication device, such as a CAC or token that is separate from +the information system, ensures that even if the information system is +compromised, that compromise will not affect credentials stored on the +authentication device. + +Multifactor solutions that require devices separate from +information systems gaining access include, for example, hardware tokens +providing time-based or challenge-response authenticators and smart cards such +as the U.S. Government Personal Identity Verification card and the DoD Common +Access Card. + CCE-83595-9 + +package --add=opensc + + include install_opensc + +class install_opensc { + package { 'opensc': + ensure => 'installed', + } +} + + - name: Ensure opensc is installed + package: + name: opensc + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83595-9 + - NIST-800-53-CM-6(a) + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_opensc_installed + + +[[packages]] +name = "opensc" +version = "*" + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if ! rpm -q --quiet "opensc" ; then + dnf install -y "opensc" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Install the pcsc-lite package + The pcsc-lite package can be installed with the following command: + +$ sudo dnf install pcsc-lite + CCI-001954 + 1382 + 1384 + 1386 + CM-6(a) + SRG-OS-000375-GPOS-00160 + SRG-OS-000377-VMM-001530 + The pcsc-lite package must be installed if it is to be available for +multifactor authentication using smartcards. + CCE-86280-5 + +package --add=pcsc-lite + + include install_pcsc-lite + +class install_pcsc-lite { + package { 'pcsc-lite': + ensure => 'installed', + } +} + + - name: Ensure pcsc-lite is installed + package: + name: pcsc-lite + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86280-5 + - NIST-800-53-CM-6(a) + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_pcsc-lite_installed + + +[[packages]] +name = "pcsc-lite" +version = "*" + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if ! rpm -q --quiet "pcsc-lite" ; then + dnf install -y "pcsc-lite" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Install Smart Card Packages For Multifactor Authentication + Configure the operating system to implement multifactor authentication by +installing the required package with the following command: + +The openssl-pkcs11 package can be installed with the following command: + +$ sudo dnf install openssl-pkcs11 + CCI-000765 + CCI-001948 + CCI-001953 + CCI-001954 + CM-6(a) + SRG-OS-000105-GPOS-00052 + SRG-OS-000375-GPOS-00160 + SRG-OS-000375-GPOS-00161 + SRG-OS-000377-GPOS-00162 + Using an authentication device, such as a CAC or token that is separate from +the information system, ensures that even if the information system is +compromised, that compromise will not affect credentials stored on the +authentication device. + +Multifactor solutions that require devices separate from +information systems gaining access include, for example, hardware tokens +providing time-based or challenge-response authenticators and smart cards such +as the U.S. Government Personal Identity Verification card and the DoD Common +Access Card. + + CCE-83596-7 + +package --add=openssl-pkcs11 + + include install_openssl-pkcs11 + +class install_openssl-pkcs11 { + package { 'openssl-pkcs11': + ensure => 'installed', + } +} + + - name: Ensure openssl-pkcs11 is installed + package: + name: openssl-pkcs11 + state: present + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ansible_architecture != "s390x" + tags: + - CCE-83596-7 + - NIST-800-53-CM-6(a) + - enable_strategy + - install_smartcard_packages + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + +[[packages]] +name = "openssl-pkcs11" +version = "*" + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && { ! grep -q s390x /proc/sys/kernel/osrelease; }; then + +if ! rpm -q --quiet "openssl-pkcs11" ; then + dnf install -y "openssl-pkcs11" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Enable the pcscd Service + +The pcscd service can be enabled with the following command: +$ sudo systemctl enable pcscd.service + CCI-001954 + 1382 + 1384 + 1386 + IA-2(1) + IA-2(2) + IA-2(3) + IA-2(4) + IA-2(6) + IA-2(7) + IA-2(11) + CM-6(a) + SRG-OS-000375-GPOS-00160 + SRG-OS-000377-VMM-001530 + Using an authentication device, such as a CAC or token that is separate from +the information system, ensures that even if the information system is +compromised, that compromise will not affect credentials stored on the +authentication device. + +Multifactor solutions that require devices separate from +information systems gaining access include, for example, hardware tokens +providing time-based or challenge-response authenticators and smart cards such +as the U.S. Government Personal Identity Verification card and the DoD Common +Access Card. + CCE-87907-2 + include enable_pcscd + +class enable_pcscd { + service {'pcscd': + enable => true, + ensure => 'running', + } +} + + - name: Enable service pcscd + block: + + - name: Gather the package facts + package_facts: + manager: auto + + - name: Start service pcscd + service: + name: pcscd + state: started + masked: 'no' + when: + - '"pcsc-lite" in ansible_facts.packages' + + - name: Enable service pcscd + ansible.builtin.command: + cmd: systemctl enable pcscd + when: + - '"pcsc-lite" in ansible_facts.packages' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-87907-2 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-2(1) + - NIST-800-53-IA-2(11) + - NIST-800-53-IA-2(2) + - NIST-800-53-IA-2(3) + - NIST-800-53-IA-2(4) + - NIST-800-53-IA-2(6) + - NIST-800-53-IA-2(7) + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_pcscd_enabled + + +[customizations.services] +enabled = ["pcscd"] + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" unmask 'pcscd.service' +"$SYSTEMCTL_EXEC" start 'pcscd.service' +"$SYSTEMCTL_EXEC" enable 'pcscd.service' + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure opensc Smart Card Drivers + The OpenSC smart card tool can auto-detect smart card drivers; however, +setting the smart card drivers in use by your organization helps to prevent +users from using unauthorized smart cards. The default smart card driver for this +profile is . +To configure the OpenSC driver, edit the /etc/opensc.conf +and add the following line into the file in the app default block, +so it will look like: + + +app default { + ... + card_drivers = ; +} + + 1 + 12 + 15 + 16 + 5 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.03 + DSS06.10 + CCI-000765 + CCI-000766 + CCI-000767 + CCI-000768 + CCI-000771 + CCI-000772 + CCI-000884 + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.2 + 4.3.3.7.4 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + 1382 + 1384 + 1386 + A.18.1.4 + A.7.1.1 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.2 + A.9.4.3 + IA-2(1) + IA-2(2) + IA-2(3) + IA-2(4) + IA-2(6) + IA-2(7) + IA-2(11) + CM-6(a) + PR.AC-1 + PR.AC-6 + PR.AC-7 + Req-8.3 + SRG-OS-000104-GPOS-00051 + SRG-OS-000106-GPOS-00053 + SRG-OS-000107-GPOS-00054 + SRG-OS-000109-GPOS-00056 + SRG-OS-000108-GPOS-00055 + SRG-OS-000108-GPOS-00057 + SRG-OS-000108-GPOS-00058 + SRG-OS-000376-VMM-001520 + Smart card login provides two-factor authentication stronger than +that provided by a username and password combination. Smart cards leverage PKI +(public key infrastructure) in order to provide and verify credentials. +Configuring the smart card driver in use by your organization helps to prevent +users from using unauthorized smart cards. + CCE-89122-6 + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_smartcard_drivers='' + + +OPENSC_TOOL="/usr/bin/opensc-tool" + +if [ -f "${OPENSC_TOOL}" ]; then + ${OPENSC_TOOL} -S app:default:card_drivers:$var_smartcard_drivers +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Force opensc To Use Defined Smart Card Driver + The OpenSC smart card middleware can auto-detect smart card drivers; however by +forcing the smart card driver in use by your organization, opensc will no longer +autodetect or use other drivers unless specified. This helps to prevent +users from using unauthorized smart cards. The default smart card driver for this +profile is . +To force the OpenSC driver, edit the /etc/opensc.conf. +Look for a line similar to: +# force_card_driver = customcos; +and change it to: +force_card_driver = ; + 1 + 12 + 15 + 16 + 5 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.03 + DSS06.10 + CCI-000765 + CCI-000766 + CCI-000767 + CCI-000768 + CCI-000771 + CCI-000772 + CCI-000884 + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.2 + 4.3.3.7.4 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + 1382 + 1384 + 1386 + A.18.1.4 + A.7.1.1 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.2 + A.9.4.3 + IA-2(1) + IA-2(2) + IA-2(3) + IA-2(4) + IA-2(6) + IA-2(7) + IA-2(11) + CM-6(a) + PR.AC-1 + PR.AC-6 + PR.AC-7 + Req-8.3 + SRG-OS-000104-GPOS-00051 + SRG-OS-000106-GPOS-00053 + SRG-OS-000107-GPOS-00054 + SRG-OS-000109-GPOS-00056 + SRG-OS-000108-GPOS-00055 + SRG-OS-000108-GPOS-00057 + SRG-OS-000108-GPOS-00058 + SRG-OS-000376-VMM-001520 + Smart card login provides two-factor authentication stronger than +that provided by a username and password combination. Smart cards leverage PKI +(public key infrastructure) in order to provide and verify credentials. +Forcing the smart card driver in use by your organization helps to prevent +users from using unauthorized smart cards. + CCE-89151-5 + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_smartcard_drivers='' + + +OPENSC_TOOL="/usr/bin/opensc-tool" + +if [ -f "${OPENSC_TOOL}" ]; then + ${OPENSC_TOOL} -S app:default:force_card_driver:$var_smartcard_drivers +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + + + + Protect Accounts by Restricting Password-Based Login + Conventionally, Unix shell accounts are accessed by +providing a username and password to a login program, which tests +these values for correctness using the /etc/passwd and +/etc/shadow files. Password-based login is vulnerable to +guessing of weak passwords, and to sniffing and man-in-the-middle +attacks against passwords entered over a network or at an insecure +console. Therefore, mechanisms for accessing accounts by entering +usernames and passwords should be restricted to those which are +operationally necessary. + + Accounts Authorized Local Users on the Operating System + List the user accounts that are authorized locally on the operating system. This list +includes both users requried by the operating system and by the installed applications. +Depending on the Operating System distribution, version, software groups and applications, +the user list is different and can be customized with scap-workbench. +OVAL regular expression is used for the user list. +The list starts with '^' and ends with '$' so that it matches exactly the +username, not any string that includes the username. Users are separated with '|'. +For example, three users: bin, oracle and sapadm are allowed, then the list is +^(bin|oracle|sapadm)$. The user root is the only user that is hard coded +in OVAL that is always allowed on the operating system. + ^(abrt|adm|avahi|bin|chrony|clevis|cockpit-ws|cockpit-wsinstance|colord|daemon|dbus|dnsmasq|flatpak|ftp|games|gdm|geoclue|gluster|gnome-initial-setup|halt|libstoragemgmt|lp|mail|nfsnobody|nobody|ntp|operator|oprofile|oracle|pcp|pegasus|pipewire|polkitd|postfix|pulse|qemu|radvd|rngd|root|rpc|rpcuser|rtkit|saned|saslauth|setroubleshoot|shutdown|sshd|sssd|sync|systemd-bus-proxy|systemd-coredump|systemd-network|systemd-resolve|tcpdump|tss|unbound|usbmuxd$|uuidd)$ + ^(abrt|adm|avahi|bin|chrony|clevis|cockpit-ws|cockpit-wsinstance|colord|daemon|dbus|dnsmasq|flatpak|ftp|games|gdm|geoclue|gluster|gnome-initial-setup|halt|libstoragemgmt|lp|mail|nfsnobody|nobody|ntp|operator|oprofile|oracle|pcp|pegasus|pipewire|polkitd|postfix|pulse|qemu|radvd|rngd|root|rpc|rpcuser|rtkit|saned|saslauth|setroubleshoot|shutdown|sshd|sssd|sync|systemd-bus-proxy|systemd-coredump|systemd-network|systemd-resolve|tcpdump|tss|unbound|usbmuxd$|uuidd)$ + ^(root|bin|daemon|adm|lp|sync|shutdown|halt|mail|operator|games|ftp|nobody|pegasus|systemd-bus-proxy|systemd-network|dbus|polkitd|abrt|unbound|tss|libstoragemgmt|rpc|colord|usbmuxd$|pcp|saslauth|geoclue|setroubleshoot|rtkit|chrony|qemu|radvd|rpcuser|nfsnobody|pulse|gdm|gnome-initial-setup|postfix|avahi|ntp|sshd|tcpdump|oprofile|uuidd)$ + ^(root|bin|daemon|adm|lp|sync|shutdown|halt|mail|operator|games|ftp|nobody|pegasus|systemd-bus-proxy|systemd-network|dbus|polkitd|abrt|unbound|tss|libstoragemgmt|rpc|colord|usbmuxd$|pcp|saslauth|geoclue|setroubleshoot|rtkit|chrony|qemu|radvd|rpcuser|nfsnobody|pulse|gdm|gnome-initial-setup|postfix|avahi|ntp|sshd|tcpdump|oprofile|uuidd|systemd-resolve|systemd-coredump|sssd|rngd)$ + ^(root|bin|daemon|adm|lp|sync|shutdown|halt|mail|operator|games|ftp|nobody|pegasus|systemd-bus-proxy|systemd-network|dbus|polkitd|abrt|unbound|tss|libstoragemgmt|rpc|colord|usbmuxd$|pcp|saslauth|geoclue|setroubleshoot|rtkit|chrony|qemu|radvd|rpcuser|nfsnobody|pulse|gdm|gnome-initial-setup|postfix|avahi|ntp|sshd|tcpdump|oprofile|uuidd|systemd-resolve|systemd-coredump|sssd|rngd)$ + ^(root|bin|daemon|adm|lp|sync|shutdown|halt|mail|operator|games|ftp|nobody|pegasus|systemd-bus-proxy|systemd-network|dbus|polkitd|abrt|unbound|tss|libstoragemgmt|rpc|colord|usbmuxd$|pcp|saslauth|geoclue|setroubleshoot|rtkit|chrony|qemu|radvd|rpcuser|nfsnobody|pulse|gdm|gnome-initial-setup|postfix|avahi|ntp|sshd|tcpdump|oprofile|uuidd|systemd-resolve|systemd-coredump|sssd|rngd|man|systemd-timesync|scard|hacluster|statd|at|dockremap|vnc)$ + ^(root|bin|daemon|adm|lp|sync|shutdown|halt|mail|operator|games|ftp|nobody|pegasus|systemd-bus-proxy|systemd-network|dbus|polkitd|abrt|unbound|tss|libstoragemgmt|rpc|colord|usbmuxd$|pcp|saslauth|geoclue|setroubleshoot|rtkit|chrony|qemu|radvd|rpcuser|nfsnobody|pulse|gdm|gnome-initial-setup|postfix|avahi|ntp|sshd|tcpdump|oprofile|uuidd|systemd-resolve|systemd-coredump|sssd|rngd|man|systemd-timesync|scard|hacluster|statd|at|dockremap|vnc|messagebus|nscd)$ + + + Ensure All Accounts on the System Have Unique User IDs + Change user IDs (UIDs), or delete accounts, so each has a unique name. + Automatic remediation of this control is not available due to unique requirements of each +system. + CCI-000135 + CCI-000764 + CCI-000804 + SRG-OS-000104-GPOS-00051 + SRG-OS-000121-GPOS-00062 + SRG-OS-000042-GPOS-00020 + To assure accountability and prevent unauthenticated access, interactive users must be identified and authenticated to prevent potential misuse and compromise of the system. + + CCE-88493-2 + + + + + + + + + Only Authorized Local User Accounts Exist on Operating System + Enterprise Application tends to use the server or virtual machine exclusively. +Besides the default operating system user, there should be only authorized local +users required by the installed software groups and applications that exist on +the operating system. The authorized user list can be customized in the refine +value variable var_accounts_authorized_local_users_regex. +OVAL regular expression is used for the user list. +Configure the system so all accounts on the system are assigned to an active system, +application, or user account. Remove accounts that do not support approved system +activities or that allow for a normal user to perform administrative-level actions. +To remove unauthorized system accounts, use the following command: +$ sudo userdel unauthorized_user + Automatic remediation of this control is not available due to the unique +requirements of each system. + CCI-000366 + SRG-OS-000480-GPOS-00227 + Accounts providing no operational purpose provide additional opportunities for +system compromise. Unnecessary accounts include user accounts for individuals not +requiring access to the system and application accounts for applications not installed +on the system. + CCE-88048-4 + + + + + + + + + + Ensure All Groups on the System Have Unique Group ID + Change the group name or delete groups, so each has a unique id. + Automatic remediation of this control is not available due to the unique requirements of each system. + CCI-000764 + SRG-OS-000104-GPOS-00051 + To assure accountability and prevent unauthenticated access, groups must be identified uniquely to prevent potential misuse and compromise of the system. + CCE-86043-7 + + + + + + + + + Set Account Expiration Parameters + Accounts can be configured to be automatically disabled +after a certain time period, +meaning that they will require administrator interaction to become usable again. +Expiration of accounts after inactivity can be set for all accounts by default +and also on a per-account basis, such as for accounts that are known to be temporary. +To configure automatic expiration of an account following +the expiration of its password (that is, after the password has expired and not been changed), +run the following command, substituting NUM_DAYS and USER appropriately: +$ sudo chage -I NUM_DAYS USER +Accounts, such as temporary accounts, can also be configured to expire on an explicitly-set date with the +-E option. +The file /etc/default/useradd controls +default settings for all newly-created accounts created with the system's +normal command line utilities. + This will only apply to newly created accounts + + number of days after the last login of the user when the user will be locked out + 'This option is specific for the auth or account phase. It specifies the number of days after +the last login of the user when the user will be locked out by the pam_lastlog module.' + 0 + 180 + 30 + 35 + 40 + 60 + 90 + 35 + + + number of days after a password expires until the account is permanently disabled + The number of days to wait after a password expires, until the account will be permanently disabled. + 0 + 180 + 30 + 35 + 40 + 60 + 90 + 35 + + + Set Account Expiration Following Inactivity + To specify the number of days after a password expires (which +signifies inactivity) until an account is permanently disabled, add or correct +the following line in /etc/default/useradd: +INACTIVE= +If a password is currently on the verge of expiration, then + +day(s) remain(s) until the account is automatically +disabled. However, if the password will not expire for another 60 days, then 60 +days plus day(s) could +elapse until the account would be automatically disabled. See the +useradd man page for more information. + 1 + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + 7 + 8 + 5.6.2.1.1 + DSS01.03 + DSS03.05 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.03 + DSS06.10 + 3.5.6 + CCI-000017 + CCI-000795 + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 6.2 + A.12.4.1 + A.12.4.3 + A.18.1.4 + A.6.1.2 + A.7.1.1 + A.9.1.2 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.1 + A.9.4.2 + A.9.4.3 + A.9.4.4 + A.9.4.5 + CIP-004-6 R2.2.2 + CIP-004-6 R2.2.3 + CIP-007-3 R.1.3 + CIP-007-3 R5 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.3 + CIP-007-3 R5.2.1 + CIP-007-3 R5.2.3 + IA-4(e) + AC-2(3) + CM-6(a) + DE.CM-1 + DE.CM-3 + PR.AC-1 + PR.AC-4 + PR.AC-6 + PR.AC-7 + Req-8.1.4 + SRG-OS-000118-GPOS-00060 + SRG-OS-000003-VMM-000030 + SRG-OS-000118-VMM-000590 + Inactive identifiers pose a risk to systems and applications because attackers may exploit an inactive identifier and potentially obtain undetected access to the system. +Disabling inactive accounts ensures that accounts which may not have been responsibly removed are not available to attackers who may have compromised their credentials. +Owners of inactive accounts will not notice if unauthorized access to their user account has been obtained. + + CCE-83627-0 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83627-0 + - CJIS-5.6.2.1.1 + - NIST-800-171-3.5.6 + - NIST-800-53-AC-2(3) + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-4(e) + - PCI-DSS-Req-8.1.4 + - account_disable_post_pw_expiration + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy +- name: XCCDF Value var_account_disable_post_pw_expiration # promote to variable + set_fact: + var_account_disable_post_pw_expiration: !!str + tags: + - always + +- name: Set Account Expiration Following Inactivity + lineinfile: + create: true + dest: /etc/default/useradd + regexp: ^INACTIVE + line: INACTIVE={{ var_account_disable_post_pw_expiration }} + when: '"shadow-utils" in ansible_facts.packages' + tags: + - CCE-83627-0 + - CJIS-5.6.2.1.1 + - NIST-800-171-3.5.6 + - NIST-800-53-AC-2(3) + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-4(e) + - PCI-DSS-Req-8.1.4 + - account_disable_post_pw_expiration + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q shadow-utils; then + +var_account_disable_post_pw_expiration='' + + +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/default/useradd"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^INACTIVE") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s=%s" "$stripped_key" "$var_account_disable_post_pw_expiration" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^INACTIVE\\>" "/etc/default/useradd"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^INACTIVE\\>.*/$escaped_formatted_output/gi" "/etc/default/useradd" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-83627-0" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/default/useradd" >> "/etc/default/useradd" + printf '%s\n' "$formatted_output" >> "/etc/default/useradd" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Assign Expiration Date to Emergency Accounts + Emergency accounts are privileged accounts established in response to +crisis situations where the need for rapid account activation is required. +In the event emergency accounts are required, configure the system to +terminate them after a documented time period. For every emergency account, +run the following command to set an expiration date on it, substituting +ACCOUNT_NAME and YYYY-MM-DD +appropriately: +$ sudo chage -E YYYY-MM-DD ACCOUNT_NAME +YYYY-MM-DD indicates the documented expiration date for the +account. For U.S. Government systems, the operating system must be +configured to automatically terminate these types of accounts after a +period of 72 hours. + Due to the unique requirements of each sysetem, automated +remediation is not available for this configuration check. + 1 + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + 7 + 8 + DSS01.03 + DSS03.05 + DSS05.04 + DSS05.05 + DSS05.07 + DSS06.03 + CCI-000016 + CCI-001682 + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + SR 1.1 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 6.2 + A.12.4.1 + A.12.4.3 + A.6.1.2 + A.7.1.1 + A.9.1.2 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.1 + A.9.4.2 + A.9.4.3 + A.9.4.4 + A.9.4.5 + AC-2(2) + AC-2(3) + CM-6(a) + DE.CM-1 + DE.CM-3 + PR.AC-1 + PR.AC-4 + PR.AC-6 + SRG-OS-000123-GPOS-00064 + SRG-OS-000002-GPOS-00002 + SRG-OS-000002-VMM-000020 + SRG-OS-000123-VMM-000620 + If emergency user accounts remain active when no longer needed or for +an excessive period, these accounts may be used to gain unauthorized access. +To mitigate this risk, automated termination of all emergency accounts +must be set upon account creation. + + CCE-90560-4 + + + + + + Assign Expiration Date to Temporary Accounts + Temporary accounts are established as part of normal account activation +procedures when there is a need for short-term accounts. In the event +temporary accounts are required, configure the system to +terminate them after a documented time period. For every temporary account, run the following command to set an expiration date on +it, substituting USER and YYYY-MM-DD +appropriately: +$ sudo chage -E YYYY-MM-DD USER +YYYY-MM-DD indicates the documented expiration date for the +account. For U.S. Government systems, the operating system must be +configured to automatically terminate these types of accounts after a +period of 72 hours. + 1 + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + 7 + 8 + DSS01.03 + DSS03.05 + DSS05.04 + DSS05.05 + DSS05.07 + DSS06.03 + CCI-000016 + CCI-001682 + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + SR 1.1 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 6.2 + A.12.4.1 + A.12.4.3 + A.6.1.2 + A.7.1.1 + A.9.1.2 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.1 + A.9.4.2 + A.9.4.3 + A.9.4.4 + A.9.4.5 + AC-2(2) + AC-2(3) + CM-6(a) + DE.CM-1 + DE.CM-3 + PR.AC-1 + PR.AC-4 + PR.AC-6 + SRG-OS-000123-GPOS-00064 + SRG-OS-000002-GPOS-00002 + SRG-OS-000002-VMM-000020 + SRG-OS-000123-VMM-000620 + If temporary user accounts remain active when no longer needed or for +an excessive period, these accounts may be used to gain unauthorized access. +To mitigate this risk, automated termination of all temporary accounts +must be set upon account creation. + + CCE-90096-9 + + + + + + Ensure All Accounts on the System Have Unique Names + Ensure accounts on the system have unique names. + +To ensure all accounts have unique names, run the following command: +$ sudo getent passwd | awk -F: '{ print $1}' | uniq -d +If a username is returned, change or delete the username. + 5.5.2 + CCI-000770 + CCI-000804 + Req-8.1.1 + Unique usernames allow for accountability on the system. + CCE-83628-8 + + + + + + + + + Use Centralized and Automated Authentication + Implement an automated system for managing user accounts that minimizes the +risk of errors, either intentional or deliberate. This system +should integrate with an existing enterprise user management system, such as +one based on Identity Management tools such as Active Directory, Kerberos, +Directory Server, etc. + A comprehensive account management process that includes automation helps to +ensure the accounts designated as requiring attention are consistently and +promptly addressed. Enterprise environments make user account management +challenging and complex. A user management process requiring administrators to +manually address account management functions adds risk of potential +oversight. + + + + + + + Set Password Expiration Parameters + The file /etc/login.defs controls several +password-related settings. Programs such as passwd, +su, and +login consult /etc/login.defs to determine +behavior with regard to password aging, expiration warnings, +and length. See the man page login.defs(5) for more information. + +Users should be forced to change their passwords, in order to +decrease the utility of compromised passwords. However, the need to +change passwords often should be balanced against the risk that +users will reuse or write down passwords if forced to change them +too often. Forcing password changes every 90-360 days, depending on +the environment, is recommended. Set the appropriate value as +PASS_MAX_DAYS and apply it to existing accounts with the +-M flag. + +The PASS_MIN_DAYS (-m) setting prevents password +changes for 7 days after the first change, to discourage password +cycling. If you use this setting, train users to contact an administrator +for an emergency password change in case a new password becomes +compromised. The PASS_WARN_AGE (-W) setting gives +users 7 days of warnings at login time that their passwords are about to expire. + +For example, for each existing human user USER, expiration parameters +could be adjusted to a 180 day maximum password age, 7 day minimum password +age, and 7 day warning period with the following command: +$ sudo chage -M 180 -m 7 -W 7 USER + + maximum password age + Maximum age of password in days + 365 + 120 + 180 + 60 + 90 + 60 + + + minimum password age + Minimum age of password in days + 0 + 1 + 2 + 5 + 7 + 7 + + + minimum password length + Minimum number of characters in password + This will only check new passwords + 10 + 12 + 14 + 15 + 18 + 20 + 6 + 8 + 15 + + + warning days before password expires + The number of days' warning given before a password expires. + This will only apply to newly created accounts + 0 + 14 + 7 + 7 + + + Set Password Maximum Age + To specify password maximum age for new accounts, +edit the file /etc/login.defs +and add or correct the following line: +PASS_MAX_DAYS +A value of 180 days is sufficient for many environments. +The DoD requirement is 60. +The profile requirement is . + BP28(R18) + 1 + 12 + 15 + 16 + 5 + 5.6.2.1 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.03 + DSS06.10 + 3.5.6 + CCI-000199 + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.2 + 4.3.3.7.4 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + 0418 + 1055 + 1402 + A.18.1.4 + A.7.1.1 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.2 + A.9.4.3 + IA-5(f) + IA-5(1)(d) + CM-6(a) + PR.AC-1 + PR.AC-6 + PR.AC-7 + Req-8.2.4 + SRG-OS-000076-GPOS-00044 + Any password, no matter how complex, can eventually be cracked. Therefore, passwords +need to be changed periodically. If the operating system does not limit the lifetime +of passwords and force users to change their passwords, there is the risk that the +operating system passwords could be compromised. + +Setting the password maximum age ensures users are required to +periodically change their passwords. Requiring shorter password lifetimes +increases the risk of users writing down the password in a convenient +location subject to physical compromise. + + CCE-83606-4 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83606-4 + - CJIS-5.6.2.1 + - NIST-800-171-3.5.6 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(d) + - NIST-800-53-IA-5(f) + - PCI-DSS-Req-8.2.4 + - accounts_maximum_age_login_defs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy +- name: XCCDF Value var_accounts_maximum_age_login_defs # promote to variable + set_fact: + var_accounts_maximum_age_login_defs: !!str + tags: + - always + +- name: Set Password Maximum Age + lineinfile: + create: true + dest: /etc/login.defs + regexp: ^#?PASS_MAX_DAYS + line: PASS_MAX_DAYS {{ var_accounts_maximum_age_login_defs }} + when: '"shadow-utils" in ansible_facts.packages' + tags: + - CCE-83606-4 + - CJIS-5.6.2.1 + - NIST-800-171-3.5.6 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(d) + - NIST-800-53-IA-5(f) + - PCI-DSS-Req-8.2.4 + - accounts_maximum_age_login_defs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q shadow-utils; then + +var_accounts_maximum_age_login_defs='' + + +grep -q ^PASS_MAX_DAYS /etc/login.defs && \ + sed -i "s/PASS_MAX_DAYS.*/PASS_MAX_DAYS $var_accounts_maximum_age_login_defs/g" /etc/login.defs +if ! [ $? -eq 0 ]; then + echo "PASS_MAX_DAYS $var_accounts_maximum_age_login_defs" >> /etc/login.defs +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Set Password Minimum Age + To specify password minimum age for new accounts, +edit the file /etc/login.defs +and add or correct the following line: +PASS_MIN_DAYS +A value of 1 day is considered sufficient for many +environments. The DoD requirement is 1. +The profile requirement is . + 1 + 12 + 15 + 16 + 5 + 5.6.2.1.1 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.03 + DSS06.10 + 3.5.8 + CCI-000198 + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.2 + 4.3.3.7.4 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + 0418 + 1055 + 1402 + A.18.1.4 + A.7.1.1 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.2 + A.9.4.3 + IA-5(f) + IA-5(1)(d) + CM-6(a) + PR.AC-1 + PR.AC-6 + PR.AC-7 + SRG-OS-000075-GPOS-00043 + Enforcing a minimum password lifetime helps to prevent repeated password +changes to defeat the password reuse or history enforcement requirement. If +users are allowed to immediately and continually change their password, +then the password could be repeatedly changed in a short period of time to +defeat the organization's policy regarding password reuse. + +Setting the minimum password age protects against users cycling back to a +favorite password after satisfying the password reuse requirement. + + CCE-83610-6 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83610-6 + - CJIS-5.6.2.1.1 + - NIST-800-171-3.5.8 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(d) + - NIST-800-53-IA-5(f) + - accounts_minimum_age_login_defs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy +- name: XCCDF Value var_accounts_minimum_age_login_defs # promote to variable + set_fact: + var_accounts_minimum_age_login_defs: !!str + tags: + - always + +- name: Set Password Minimum Age + lineinfile: + create: true + dest: /etc/login.defs + regexp: ^#?PASS_MIN_DAYS + line: PASS_MIN_DAYS {{ var_accounts_minimum_age_login_defs }} + when: '"shadow-utils" in ansible_facts.packages' + tags: + - CCE-83610-6 + - CJIS-5.6.2.1.1 + - NIST-800-171-3.5.8 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(d) + - NIST-800-53-IA-5(f) + - accounts_minimum_age_login_defs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q shadow-utils; then + +var_accounts_minimum_age_login_defs='' + + +grep -q ^PASS_MIN_DAYS /etc/login.defs && \ + sed -i "s/PASS_MIN_DAYS.*/PASS_MIN_DAYS $var_accounts_minimum_age_login_defs/g" /etc/login.defs +if ! [ $? -eq 0 ]; then + echo "PASS_MIN_DAYS $var_accounts_minimum_age_login_defs" >> /etc/login.defs +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Set Password Minimum Length in login.defs + To specify password length requirements for new accounts, edit the file +/etc/login.defs and add or correct the following line: +PASS_MIN_LEN + +The DoD requirement is 15. +The FISMA requirement is 12. +The profile requirement is +. +If a program consults /etc/login.defs and also another PAM module +(such as pam_pwquality) during a password change operation, then +the most restrictive must be satisfied. See PAM section for more +information about enforcing password quality requirements. + BP28(R18) + 1 + 12 + 15 + 16 + 5 + 5.6.2.1 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.03 + DSS06.10 + 3.5.7 + CCI-000205 + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.2 + 4.3.3.7.4 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + 0421 + 0422 + 0431 + 0974 + 1173 + 1401 + 1504 + 1505 + 1546 + 1557 + 1558 + 1559 + 1560 + 1561 + A.18.1.4 + A.7.1.1 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.2 + A.9.4.3 + IA-5(f) + IA-5(1)(a) + CM-6(a) + PR.AC-1 + PR.AC-6 + PR.AC-7 + SRG-OS-000078-GPOS-00046 + Requiring a minimum password length makes password +cracking attacks more difficult by ensuring a larger +search space. However, any security benefit from an onerous requirement +must be carefully weighed against usability problems, support costs, or counterproductive +behavior that may result. + + CCE-83608-0 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83608-0 + - CJIS-5.6.2.1 + - NIST-800-171-3.5.7 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(a) + - NIST-800-53-IA-5(f) + - accounts_password_minlen_login_defs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy +- name: XCCDF Value var_accounts_password_minlen_login_defs # promote to variable + set_fact: + var_accounts_password_minlen_login_defs: !!str + tags: + - always + +- name: Set Password Minimum Length in login.defs + lineinfile: + dest: /etc/login.defs + regexp: ^PASS_MIN_LEN *[0-9]* + state: present + line: PASS_MIN_LEN {{ var_accounts_password_minlen_login_defs }} + create: true + when: '"shadow-utils" in ansible_facts.packages' + tags: + - CCE-83608-0 + - CJIS-5.6.2.1 + - NIST-800-171-3.5.7 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(a) + - NIST-800-53-IA-5(f) + - accounts_password_minlen_login_defs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q shadow-utils; then + +var_accounts_password_minlen_login_defs='' + + +grep -q ^PASS_MIN_LEN /etc/login.defs && \ +sed -i "s/PASS_MIN_LEN.*/PASS_MIN_LEN\t$var_accounts_password_minlen_login_defs/g" /etc/login.defs +if ! [ $? -eq 0 ] +then + echo -e "PASS_MIN_LEN\t$var_accounts_password_minlen_login_defs" >> /etc/login.defs +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Set Existing Passwords Maximum Age + Configure non-compliant accounts to enforce a -day maximum password lifetime +restriction by running the following command: +$ sudo chage -M USER + CCI-000199 + IA-5(f) + IA-5(1)(d) + CM-6(a) + SRG-OS-000076-GPOS-00044 + SRG-OS-000076-VMM-000430 + Any password, no matter how complex, can eventually be cracked. Therefore, +passwords need to be changed periodically. If the operating system does +not limit the lifetime of passwords and force users to change their +passwords, there is the risk that the operating system passwords could be +compromised. + CCE-86031-2 + - name: XCCDF Value var_accounts_maximum_age_login_defs # promote to variable + set_fact: + var_accounts_maximum_age_login_defs: !!str + tags: + - always + +- name: Collect users with not correct maximum time period between password changes + ansible.builtin.command: | + awk -F: '$5 > {{ var_accounts_maximum_age_login_defs }} || $5 == "" {print $1}' /etc/shadow + register: user_names + tags: + - CCE-86031-2 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(d) + - NIST-800-53-IA-5(f) + - accounts_password_set_max_life_existing + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Change the maximum time period between password changes + ansible.builtin.user: + user: '{{ item }}' + password_expire_max: '{{ var_accounts_maximum_age_login_defs }}' + with_items: '{{ user_names.stdout_lines }}' + when: user_names.stdout_lines | length > 0 + tags: + - CCE-86031-2 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(d) + - NIST-800-53-IA-5(f) + - accounts_password_set_max_life_existing + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + +var_accounts_maximum_age_login_defs='' + + +while IFS= read -r i; do + chage -M $var_accounts_maximum_age_login_defs $i +done < <(awk -v var="$var_accounts_maximum_age_login_defs" -F: '$5 > var || $5 == "" {print $1}' /etc/shadow) + + + + + + + + + + + + Set Existing Passwords Minimum Age + Configure non-compliant accounts to enforce a 24 hours/1 day minimum password +lifetime by running the following command: +$ sudo chage -m 1 USER + CCI-000198 + IA-5(f) + IA-5(1)(d) + CM-6(a) + SRG-OS-000075-GPOS-00043 + SRG-OS-000075-VMM000420 + Enforcing a minimum password lifetime helps to prevent repeated password +changes to defeat the password reuse or history enforcement requirement. If +users are allowed to immediately and continually change their password, the +password could be repeatedly changed in a short period of time to defeat the +organization's policy regarding password reuse. + CCE-89069-9 + +var_accounts_minimum_age_login_defs='' + + +while IFS= read -r i; do + passwd -n $var_accounts_minimum_age_login_defs $i +done < <(awk -v var="$var_accounts_minimum_age_login_defs" -F: '$4 < var || $4 == "" {print $1}' /etc/shadow) + + + + + + + + + + + + Set Password Warning Age + To specify how many days prior to password +expiration that a warning will be issued to users, +edit the file /etc/login.defs and add or correct + the following line: +PASS_WARN_AGE +The DoD requirement is 7. +The profile requirement is . + 1 + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + 7 + 8 + DSS01.03 + DSS03.05 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.03 + DSS06.10 + 3.5.8 + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 6.2 + 0418 + 1055 + 1402 + A.12.4.1 + A.12.4.3 + A.18.1.4 + A.6.1.2 + A.7.1.1 + A.9.1.2 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.1 + A.9.4.2 + A.9.4.3 + A.9.4.4 + A.9.4.5 + IA-5(f) + IA-5(1)(d) + CM-6(a) + DE.CM-1 + DE.CM-3 + PR.AC-1 + PR.AC-4 + PR.AC-6 + PR.AC-7 + Setting the password warning age enables users to +make the change at a practical time. + + CCE-83609-8 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83609-8 + - NIST-800-171-3.5.8 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(d) + - NIST-800-53-IA-5(f) + - accounts_password_warn_age_login_defs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy +- name: XCCDF Value var_accounts_password_warn_age_login_defs # promote to variable + set_fact: + var_accounts_password_warn_age_login_defs: !!str + tags: + - always + +- name: Set Password Warning Age + lineinfile: + dest: /etc/login.defs + regexp: ^PASS_WARN_AGE *[0-9]* + state: present + line: PASS_WARN_AGE {{ var_accounts_password_warn_age_login_defs }} + create: true + when: '"shadow-utils" in ansible_facts.packages' + tags: + - CCE-83609-8 + - NIST-800-171-3.5.8 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(d) + - NIST-800-53-IA-5(f) + - accounts_password_warn_age_login_defs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q shadow-utils; then + +var_accounts_password_warn_age_login_defs='' + + +grep -q ^PASS_WARN_AGE /etc/login.defs && \ +sed -i "s/PASS_WARN_AGE.*/PASS_WARN_AGE\t$var_accounts_password_warn_age_login_defs/g" /etc/login.defs +if ! [ $? -eq 0 ] +then + echo -e "PASS_WARN_AGE\t$var_accounts_password_warn_age_login_defs" >> /etc/login.defs +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + + Verify Proper Storage and Existence of Password +Hashes + By default, password hashes for local accounts are stored +in the second field (colon-separated) in +/etc/shadow. This file should be readable only by +processes running with root credentials, preventing users from +casually accessing others' password hashes and attempting +to crack them. +However, it remains possible to misconfigure the system +and store password hashes +in world-readable files such as /etc/passwd, or +to even store passwords themselves in plaintext on the system. +Using system-provided tools for password change/creation +should allow administrators to avoid such misconfiguration. + + Password Hashing algorithm + Specify the number of SHA rounds for the system password encryption algorithm. +Defines the value set in /etc/pam.d/system-auth and /etc/pam.d/password-auth + 5000 + 5000 + 65536 + + + Verify All Account Password Hashes are Shadowed + If any password hashes are stored in /etc/passwd (in the second field, +instead of an x or *), the cause of this misconfiguration should be +investigated. The account should have its password reset and the hash should be +properly stored, or the account should be deleted entirely. + 1 + 12 + 15 + 16 + 5 + 5.5.2 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.03 + DSS06.10 + 3.5.10 + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.2 + 4.3.3.7.4 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + 1410 + A.18.1.4 + A.7.1.1 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.2 + A.9.4.3 + IA-5(h) + CM-6(a) + PR.AC-1 + PR.AC-6 + PR.AC-7 + Req-8.2.1 + The hashes for all user account passwords should be stored in +the file /etc/shadow and never in /etc/passwd, +which is readable by all users. + + CCE-83618-9 + + + + + + + + + Verify All Account Password Hashes are Shadowed with SHA512 + Verify the operating system requires the shadow password suite +configuration be set to encrypt interactive user passwords using a strong +cryptographic hash. +Check that the interactive user account passwords are using a strong +password hash with the following command: +$ sudo cut -d: -f2 /etc/shadow +$6$kcOnRq/5$NUEYPuyL.wghQwWssXRcLRFiiru7f5JPV6GaJhNC2aK5F3PZpE/BCCtwrxRc/AInKMNX3CdMw11m9STiql12f/ +Password hashes ! or * indicate inactive accounts not +available for logon and are not evaluated. +If any interactive user password hash does not begin with $6, +this is a finding. + CCI-000196 + CCI-000803 + IA-5(1)(c) + IA-5(1).1(v) + IA-7 + IA-7.1 + SRG-OS-000073-GPOS-00041 + SRG-OS-000120-GPOS-00061 + Passwords need to be protected at all times, and encryption is the standard method for +protecting passwords. If passwords are not encrypted, they can be plainly read +(i.e., clear text) and easily compromised. + CCE-89983-1 + + + + + + + + + Set number of Password Hashing Rounds - password-auth + Configure the number or rounds for the password hashing algorithm. This can be +accomplished by using the rounds option for the pam_unix PAM module. + +In file /etc/pam.d/password-auth append rounds= +to the pam_unix.so entry, as shown below: +password sufficient pam_unix.so ...existing_options... rounds= +The system's default number of rounds is 5000. + Setting a high number of hashing rounds makes it more difficult to brute force the password, +but requires more CPU resources to authenticate users. + BP28(R32) + CCI-000196 + SRG-OS-000073-GPOS-00041 + Using a higher number of rounds makes password cracking attacks more difficult. + + CCE-83615-5 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83615-5 + - accounts_password_pam_unix_rounds_password_auth + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed +- name: XCCDF Value var_password_pam_unix_rounds # promote to variable + set_fact: + var_password_pam_unix_rounds: !!str + tags: + - always + +- name: Set number of Password Hashing Rounds - password-auth - Check if /etc/pam.d/password-auth + file is present + ansible.builtin.stat: + path: /etc/pam.d/password-auth + register: result_pam_file_present + when: '"pam" in ansible_facts.packages' + tags: + - CCE-83615-5 + - accounts_password_pam_unix_rounds_password_auth + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + +- name: Set number of Password Hashing Rounds - password-auth - Check the proper remediation + for the system + block: + + - name: Set number of Password Hashing Rounds - password-auth - Define the PAM file + to be edited as a local fact + ansible.builtin.set_fact: + pam_file_path: /etc/pam.d/password-auth + + - name: Set number of Password Hashing Rounds - password-auth - Check if system + relies on authselect + ansible.builtin.stat: + path: /usr/bin/authselect + register: result_authselect_present + + - name: Set number of Password Hashing Rounds - password-auth - Remediate using + authselect + block: + + - name: Set number of Password Hashing Rounds - password-auth - Check integrity + of authselect current profile + ansible.builtin.command: + cmd: authselect check + register: result_authselect_check_cmd + changed_when: false + ignore_errors: true + + - name: Set number of Password Hashing Rounds - password-auth - Informative message + based on the authselect integrity check result + ansible.builtin.assert: + that: + - result_authselect_check_cmd is success + fail_msg: + - authselect integrity check failed. Remediation aborted! + - This remediation could not be applied because an authselect profile was + not selected or the selected profile is not intact. + - It is not recommended to manually edit the PAM files when authselect tool + is available. + - In cases where the default authselect profile does not cover a specific + demand, a custom authselect profile is recommended. + success_msg: + - authselect integrity check passed + + - name: Set number of Password Hashing Rounds - password-auth - Get authselect + current profile + ansible.builtin.shell: + cmd: authselect current -r | awk '{ print $1 }' + register: result_authselect_profile + changed_when: false + when: + - result_authselect_check_cmd is success + + - name: Set number of Password Hashing Rounds - password-auth - Define the current + authselect profile as a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: '{{ result_authselect_profile.stdout }}' + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is match("custom/") + + - name: Set number of Password Hashing Rounds - password-auth - Define the new + authselect custom profile as a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: custom/hardening + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is not match("custom/") + + - name: Set number of Password Hashing Rounds - password-auth - Get authselect + current features to also enable them in the custom profile + ansible.builtin.shell: + cmd: authselect current | tail -n+3 | awk '{ print $2 }' + register: result_authselect_features + changed_when: false + when: + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + + - name: Set number of Password Hashing Rounds - password-auth - Check if any custom + profile with the same name was already created + ansible.builtin.stat: + path: /etc/authselect/{{ authselect_custom_profile }} + register: result_authselect_custom_profile_present + changed_when: false + when: + - authselect_current_profile is not match("custom/") + + - name: Set number of Password Hashing Rounds - password-auth - Create an authselect + custom profile based on the current profile + ansible.builtin.command: + cmd: authselect create-profile hardening -b {{ authselect_current_profile + }} + when: + - result_authselect_check_cmd is success + - authselect_current_profile is not match("custom/") + - not result_authselect_custom_profile_present.stat.exists + + - name: Set number of Password Hashing Rounds - password-auth - Ensure authselect + changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=before-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Set number of Password Hashing Rounds - password-auth - Ensure the authselect + custom profile is selected + ansible.builtin.command: + cmd: authselect select {{ authselect_custom_profile }} + register: result_pam_authselect_select_profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Set number of Password Hashing Rounds - password-auth - Restore the authselect + features in the custom profile + ansible.builtin.command: + cmd: authselect enable-feature {{ item }} + loop: '{{ result_authselect_features.stdout_lines }}' + register: result_pam_authselect_restore_features + when: + - result_authselect_profile is not skipped + - result_authselect_features is not skipped + - result_pam_authselect_select_profile is not skipped + + - name: Set number of Password Hashing Rounds - password-auth - Ensure authselect + changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=after-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - result_pam_authselect_restore_features is not skipped + + - name: Set number of Password Hashing Rounds - password-auth - Change the PAM + file to be edited according to the custom authselect profile + ansible.builtin.set_fact: + pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path + | basename }} + when: + - result_authselect_present.stat.exists + + - name: Set number of Password Hashing Rounds - password-auth - Check if expected + PAM module line is present in {{ pam_file_path }} + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + regexp: ^\s*password\s+sufficient\s+pam_unix.so\s*.* + state: absent + check_mode: true + changed_when: false + register: result_pam_line_present + + - name: Set number of Password Hashing Rounds - password-auth - Include or update + the PAM module line in {{ pam_file_path }} + block: + + - name: Set number of Password Hashing Rounds - password-auth - Check if required + PAM module line is present in {{ pam_file_path }} with different control + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + regexp: ^\s*password\s+.*\s+pam_unix.so\s* + state: absent + check_mode: true + changed_when: false + register: result_pam_line_other_control_present + + - name: Set number of Password Hashing Rounds - password-auth - Ensure the correct + control for the required PAM module line in {{ pam_file_path }} + ansible.builtin.replace: + dest: '{{ pam_file_path }}' + regexp: ^(\s*password\s+).*(\bpam_unix.so.*) + replace: \1sufficient \2 + register: result_pam_module_edit + when: + - result_pam_line_other_control_present.found == 1 + + - name: Set number of Password Hashing Rounds - password-auth - Ensure the required + PAM module line is included in {{ pam_file_path }} + ansible.builtin.lineinfile: + dest: '{{ pam_file_path }}' + line: password sufficient pam_unix.so + register: result_pam_module_add + when: + - result_pam_line_other_control_present.found == 0 or result_pam_line_other_control_present.found + > 1 + + - name: Set number of Password Hashing Rounds - password-auth - Ensure authselect + changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: | + result_authselect_present is defined and result_authselect_present.stat.exists and ((result_pam_module_add is defined and result_pam_module_add.changed) or (result_pam_module_edit is defined and result_pam_module_edit.changed)) + when: + - result_pam_line_present.found is defined + - result_pam_line_present.found == 0 + + - name: Set number of Password Hashing Rounds - password-auth - Check if the required + PAM module option is present in {{ pam_file_path }} + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + regexp: ^\s*password\s+sufficient\s+pam_unix.so\s*.*\srounds\b + state: absent + check_mode: true + changed_when: false + register: result_pam_module_rounds_option_present + + - name: Set number of Password Hashing Rounds - password-auth - Ensure the "rounds" + PAM option for "pam_unix.so" is included in {{ pam_file_path }} + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + backrefs: true + regexp: ^(\s*password\s+sufficient\s+pam_unix.so.*) + line: \1 rounds={{ var_password_pam_unix_rounds }} + state: present + register: result_pam_rounds_add + when: + - result_pam_module_rounds_option_present.found == 0 + + - name: Set number of Password Hashing Rounds - password-auth - Ensure the required + value for "rounds" PAM option from "pam_unix.so" in {{ pam_file_path }} + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + backrefs: true + regexp: ^(\s*password\s+sufficient\s+pam_unix.so\s+.*)(rounds)=[0-9a-zA-Z]+\s*(.*) + line: \1\2={{ var_password_pam_unix_rounds }} \3 + register: result_pam_rounds_edit + when: + - result_pam_module_rounds_option_present.found > 0 + + - name: Set number of Password Hashing Rounds - password-auth - Ensure authselect + changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: + - result_authselect_present.stat.exists + - (result_pam_rounds_add is defined and result_pam_rounds_add.changed) or (result_pam_rounds_edit + is defined and result_pam_rounds_edit.changed) + when: + - '"pam" in ansible_facts.packages' + - result_pam_file_present.stat.exists + tags: + - CCE-83615-5 + - accounts_password_pam_unix_rounds_password_auth + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if rpm --quiet -q pam; then + +var_password_pam_unix_rounds='' + + + +if [ -e "/etc/pam.d/password-auth" ] ; then + PAM_FILE_PATH="/etc/pam.d/password-auth" + if [ -f /usr/bin/authselect ]; then + if ! authselect check; then + echo " + authselect integrity check failed. Remediation aborted! + This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact. + It is not recommended to manually edit the PAM files when authselect tool is available. + In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended." + exit 1 + fi + CURRENT_PROFILE=$(authselect current -r | awk '{ print $1 }') + # If not already in use, a custom profile is created preserving the enabled features. + if [[ ! $CURRENT_PROFILE == custom/* ]]; then + ENABLED_FEATURES=$(authselect current | tail -n+3 | awk '{ print $2 }') + authselect create-profile hardening -b $CURRENT_PROFILE + CURRENT_PROFILE="custom/hardening" + + authselect apply-changes -b --backup=before-hardening-custom-profile + authselect select $CURRENT_PROFILE + for feature in $ENABLED_FEATURES; do + authselect enable-feature $feature; + done + + authselect apply-changes -b --backup=after-hardening-custom-profile + fi + PAM_FILE_NAME=$(basename "/etc/pam.d/password-auth") + PAM_FILE_PATH="/etc/authselect/$CURRENT_PROFILE/$PAM_FILE_NAME" + + authselect apply-changes -b + fi + if ! grep -qP '^\s*password\s+'"sufficient"'\s+pam_unix.so\s*.*' "$PAM_FILE_PATH"; then + # Line matching group + control + module was not found. Check group + module. + if [ "$(grep -cP '^\s*password\s+.*\s+pam_unix.so\s*' "$PAM_FILE_PATH")" -eq 1 ]; then + # The control is updated only if one single line matches. + sed -i -E --follow-symlinks 's/^(\s*password\s+).*(\bpam_unix.so.*)/\1'"sufficient"' \2/' "$PAM_FILE_PATH" + else + echo 'password '"sufficient"' pam_unix.so' >> "$PAM_FILE_PATH" + fi + fi + # Check the option + if ! grep -qP '^\s*password\s+'"sufficient"'\s+pam_unix.so\s*.*\srounds\b' "$PAM_FILE_PATH"; then + sed -i -E --follow-symlinks '/\s*password\s+'"sufficient"'\s+pam_unix.so.*/ s/$/ rounds='"$var_password_pam_unix_rounds"'/' "$PAM_FILE_PATH" + else + sed -i -E --follow-symlinks 's/(\s*password\s+'"sufficient"'\s+pam_unix.so\s+.*)('"rounds"'=)[[:alnum:]]+\s*(.*)/\1\2'"$var_password_pam_unix_rounds"' \3/' "$PAM_FILE_PATH" + fi + if [ -f /usr/bin/authselect ]; then + + authselect apply-changes -b + fi +else + echo "/etc/pam.d/password-auth was not found" >&2 +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Set number of Password Hashing Rounds - system-auth + Configure the number or rounds for the password hashing algorithm. This can be +accomplished by using the rounds option for the pam_unix PAM module. + +In file /etc/pam.d/system-auth append rounds= +to the pam_unix.so entry, as shown below: +password sufficient pam_unix.so ...existing_options... rounds= +The system's default number of rounds is 5000. + Setting a high number of hashing rounds makes it more difficult to brute force the password, +but requires more CPU resources to authenticate users. + BP28(R32) + CCI-000196 + SRG-OS-000073-GPOS-00041 + Using a higher number of rounds makes password cracking attacks more difficult. + + CCE-83621-3 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83621-3 + - accounts_password_pam_unix_rounds_system_auth + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed +- name: XCCDF Value var_password_pam_unix_rounds # promote to variable + set_fact: + var_password_pam_unix_rounds: !!str + tags: + - always + +- name: Set number of Password Hashing Rounds - system-auth - Check if /etc/pam.d/system-auth + file is present + ansible.builtin.stat: + path: /etc/pam.d/system-auth + register: result_pam_file_present + when: '"pam" in ansible_facts.packages' + tags: + - CCE-83621-3 + - accounts_password_pam_unix_rounds_system_auth + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + +- name: Set number of Password Hashing Rounds - system-auth - Check the proper remediation + for the system + block: + + - name: Set number of Password Hashing Rounds - system-auth - Define the PAM file + to be edited as a local fact + ansible.builtin.set_fact: + pam_file_path: /etc/pam.d/system-auth + + - name: Set number of Password Hashing Rounds - system-auth - Check if system relies + on authselect + ansible.builtin.stat: + path: /usr/bin/authselect + register: result_authselect_present + + - name: Set number of Password Hashing Rounds - system-auth - Remediate using authselect + block: + + - name: Set number of Password Hashing Rounds - system-auth - Check integrity + of authselect current profile + ansible.builtin.command: + cmd: authselect check + register: result_authselect_check_cmd + changed_when: false + ignore_errors: true + + - name: Set number of Password Hashing Rounds - system-auth - Informative message + based on the authselect integrity check result + ansible.builtin.assert: + that: + - result_authselect_check_cmd is success + fail_msg: + - authselect integrity check failed. Remediation aborted! + - This remediation could not be applied because an authselect profile was + not selected or the selected profile is not intact. + - It is not recommended to manually edit the PAM files when authselect tool + is available. + - In cases where the default authselect profile does not cover a specific + demand, a custom authselect profile is recommended. + success_msg: + - authselect integrity check passed + + - name: Set number of Password Hashing Rounds - system-auth - Get authselect current + profile + ansible.builtin.shell: + cmd: authselect current -r | awk '{ print $1 }' + register: result_authselect_profile + changed_when: false + when: + - result_authselect_check_cmd is success + + - name: Set number of Password Hashing Rounds - system-auth - Define the current + authselect profile as a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: '{{ result_authselect_profile.stdout }}' + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is match("custom/") + + - name: Set number of Password Hashing Rounds - system-auth - Define the new authselect + custom profile as a local fact + ansible.builtin.set_fact: + authselect_current_profile: '{{ result_authselect_profile.stdout }}' + authselect_custom_profile: custom/hardening + when: + - result_authselect_profile is not skipped + - result_authselect_profile.stdout is not match("custom/") + + - name: Set number of Password Hashing Rounds - system-auth - Get authselect current + features to also enable them in the custom profile + ansible.builtin.shell: + cmd: authselect current | tail -n+3 | awk '{ print $2 }' + register: result_authselect_features + changed_when: false + when: + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + + - name: Set number of Password Hashing Rounds - system-auth - Check if any custom + profile with the same name was already created + ansible.builtin.stat: + path: /etc/authselect/{{ authselect_custom_profile }} + register: result_authselect_custom_profile_present + changed_when: false + when: + - authselect_current_profile is not match("custom/") + + - name: Set number of Password Hashing Rounds - system-auth - Create an authselect + custom profile based on the current profile + ansible.builtin.command: + cmd: authselect create-profile hardening -b {{ authselect_current_profile + }} + when: + - result_authselect_check_cmd is success + - authselect_current_profile is not match("custom/") + - not result_authselect_custom_profile_present.stat.exists + + - name: Set number of Password Hashing Rounds - system-auth - Ensure authselect + changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=before-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Set number of Password Hashing Rounds - system-auth - Ensure the authselect + custom profile is selected + ansible.builtin.command: + cmd: authselect select {{ authselect_custom_profile }} + register: result_pam_authselect_select_profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - authselect_current_profile is not match("custom/") + - authselect_custom_profile is not match(authselect_current_profile) + + - name: Set number of Password Hashing Rounds - system-auth - Restore the authselect + features in the custom profile + ansible.builtin.command: + cmd: authselect enable-feature {{ item }} + loop: '{{ result_authselect_features.stdout_lines }}' + register: result_pam_authselect_restore_features + when: + - result_authselect_profile is not skipped + - result_authselect_features is not skipped + - result_pam_authselect_select_profile is not skipped + + - name: Set number of Password Hashing Rounds - system-auth - Ensure authselect + changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b --backup=after-hardening-custom-profile + when: + - result_authselect_check_cmd is success + - result_authselect_profile is not skipped + - result_pam_authselect_restore_features is not skipped + + - name: Set number of Password Hashing Rounds - system-auth - Change the PAM file + to be edited according to the custom authselect profile + ansible.builtin.set_fact: + pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path + | basename }} + when: + - result_authselect_present.stat.exists + + - name: Set number of Password Hashing Rounds - system-auth - Check if expected + PAM module line is present in {{ pam_file_path }} + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + regexp: ^\s*password\s+sufficient\s+pam_unix.so\s*.* + state: absent + check_mode: true + changed_when: false + register: result_pam_line_present + + - name: Set number of Password Hashing Rounds - system-auth - Include or update + the PAM module line in {{ pam_file_path }} + block: + + - name: Set number of Password Hashing Rounds - system-auth - Check if required + PAM module line is present in {{ pam_file_path }} with different control + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + regexp: ^\s*password\s+.*\s+pam_unix.so\s* + state: absent + check_mode: true + changed_when: false + register: result_pam_line_other_control_present + + - name: Set number of Password Hashing Rounds - system-auth - Ensure the correct + control for the required PAM module line in {{ pam_file_path }} + ansible.builtin.replace: + dest: '{{ pam_file_path }}' + regexp: ^(\s*password\s+).*(\bpam_unix.so.*) + replace: \1sufficient \2 + register: result_pam_module_edit + when: + - result_pam_line_other_control_present.found == 1 + + - name: Set number of Password Hashing Rounds - system-auth - Ensure the required + PAM module line is included in {{ pam_file_path }} + ansible.builtin.lineinfile: + dest: '{{ pam_file_path }}' + line: password sufficient pam_unix.so + register: result_pam_module_add + when: + - result_pam_line_other_control_present.found == 0 or result_pam_line_other_control_present.found + > 1 + + - name: Set number of Password Hashing Rounds - system-auth - Ensure authselect + changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: | + result_authselect_present is defined and result_authselect_present.stat.exists and ((result_pam_module_add is defined and result_pam_module_add.changed) or (result_pam_module_edit is defined and result_pam_module_edit.changed)) + when: + - result_pam_line_present.found is defined + - result_pam_line_present.found == 0 + + - name: Set number of Password Hashing Rounds - system-auth - Check if the required + PAM module option is present in {{ pam_file_path }} + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + regexp: ^\s*password\s+sufficient\s+pam_unix.so\s*.*\srounds\b + state: absent + check_mode: true + changed_when: false + register: result_pam_module_rounds_option_present + + - name: Set number of Password Hashing Rounds - system-auth - Ensure the "rounds" + PAM option for "pam_unix.so" is included in {{ pam_file_path }} + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + backrefs: true + regexp: ^(\s*password\s+sufficient\s+pam_unix.so.*) + line: \1 rounds={{ var_password_pam_unix_rounds }} + state: present + register: result_pam_rounds_add + when: + - result_pam_module_rounds_option_present.found == 0 + + - name: Set number of Password Hashing Rounds - system-auth - Ensure the required + value for "rounds" PAM option from "pam_unix.so" in {{ pam_file_path }} + ansible.builtin.lineinfile: + path: '{{ pam_file_path }}' + backrefs: true + regexp: ^(\s*password\s+sufficient\s+pam_unix.so\s+.*)(rounds)=[0-9a-zA-Z]+\s*(.*) + line: \1\2={{ var_password_pam_unix_rounds }} \3 + register: result_pam_rounds_edit + when: + - result_pam_module_rounds_option_present.found > 0 + + - name: Set number of Password Hashing Rounds - system-auth - Ensure authselect + changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: + - result_authselect_present.stat.exists + - (result_pam_rounds_add is defined and result_pam_rounds_add.changed) or (result_pam_rounds_edit + is defined and result_pam_rounds_edit.changed) + when: + - '"pam" in ansible_facts.packages' + - result_pam_file_present.stat.exists + tags: + - CCE-83621-3 + - accounts_password_pam_unix_rounds_system_auth + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if rpm --quiet -q pam; then + +var_password_pam_unix_rounds='' + + +if [ -e "/etc/pam.d/system-auth" ] ; then + PAM_FILE_PATH="/etc/pam.d/system-auth" + if [ -f /usr/bin/authselect ]; then + if ! authselect check; then + echo " + authselect integrity check failed. Remediation aborted! + This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact. + It is not recommended to manually edit the PAM files when authselect tool is available. + In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended." + exit 1 + fi + CURRENT_PROFILE=$(authselect current -r | awk '{ print $1 }') + # If not already in use, a custom profile is created preserving the enabled features. + if [[ ! $CURRENT_PROFILE == custom/* ]]; then + ENABLED_FEATURES=$(authselect current | tail -n+3 | awk '{ print $2 }') + authselect create-profile hardening -b $CURRENT_PROFILE + CURRENT_PROFILE="custom/hardening" + + authselect apply-changes -b --backup=before-hardening-custom-profile + authselect select $CURRENT_PROFILE + for feature in $ENABLED_FEATURES; do + authselect enable-feature $feature; + done + + authselect apply-changes -b --backup=after-hardening-custom-profile + fi + PAM_FILE_NAME=$(basename "/etc/pam.d/system-auth") + PAM_FILE_PATH="/etc/authselect/$CURRENT_PROFILE/$PAM_FILE_NAME" + + authselect apply-changes -b + fi + if ! grep -qP '^\s*password\s+'"sufficient"'\s+pam_unix.so\s*.*' "$PAM_FILE_PATH"; then + # Line matching group + control + module was not found. Check group + module. + if [ "$(grep -cP '^\s*password\s+.*\s+pam_unix.so\s*' "$PAM_FILE_PATH")" -eq 1 ]; then + # The control is updated only if one single line matches. + sed -i -E --follow-symlinks 's/^(\s*password\s+).*(\bpam_unix.so.*)/\1'"sufficient"' \2/' "$PAM_FILE_PATH" + else + echo 'password '"sufficient"' pam_unix.so' >> "$PAM_FILE_PATH" + fi + fi + # Check the option + if ! grep -qP '^\s*password\s+'"sufficient"'\s+pam_unix.so\s*.*\srounds\b' "$PAM_FILE_PATH"; then + sed -i -E --follow-symlinks '/\s*password\s+'"sufficient"'\s+pam_unix.so.*/ s/$/ rounds='"$var_password_pam_unix_rounds"'/' "$PAM_FILE_PATH" + else + sed -i -E --follow-symlinks 's/(\s*password\s+'"sufficient"'\s+pam_unix.so\s+.*)('"rounds"'=)[[:alnum:]]+\s*(.*)/\1\2'"$var_password_pam_unix_rounds"' \3/' "$PAM_FILE_PATH" + fi + if [ -f /usr/bin/authselect ]; then + + authselect apply-changes -b + fi +else + echo "/etc/pam.d/system-auth was not found" >&2 +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + All GIDs referenced in /etc/passwd must be defined in /etc/group + Add a group to the system for each GID referenced without a corresponding group. + 1 + 12 + 15 + 16 + 5 + 5.5.2 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.03 + DSS06.10 + CCI-000764 + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.2 + 4.3.3.7.4 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + A.18.1.4 + A.7.1.1 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.2 + A.9.4.3 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.2.3 + CIP-004-6 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.2 + CIP-007-3 R5.2 + CIP-007-3 R5.3.1 + CIP-007-3 R5.3.2 + CIP-007-3 R5.3.3 + IA-2 + CM-6(a) + PR.AC-1 + PR.AC-6 + PR.AC-7 + Req-8.5.a + SRG-OS-000104-GPOS-00051 + If a user is assigned the Group Identifier (GID) of a group not existing on the system, and a group +with the Group Identifier (GID) is subsequently created, the user may have unintended rights to +any files associated with the group. + CCE-83613-0 + + + + + + + + + Prevent Login to Accounts With Empty Password + If an account is configured for password authentication +but does not have an assigned password, it may be possible to log +into the account without authentication. Remove any instances of the +nullok in + +/etc/pam.d/system-auth and +/etc/pam.d/password-auth + +to prevent logins with empty passwords. + If the system relies on authselect tool to manage PAM settings, the remediation +will also use authselect tool. However, if any manual modification was made in +PAM files, the authselect integrity check will fail and the remediation will be +aborted in order to preserve intentional changes. In this case, an informative message will +be shown in the remediation report. +Note that this rule is not applicable for systems running within a +container. Having user with empty password within a container is not +considered a risk, because it should not be possible to directly login into +a container anyway. + 1 + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + 5.5.2 + APO01.06 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.02 + DSS06.03 + DSS06.10 + 3.1.1 + 3.1.5 + CCI-000366 + 164.308(a)(1)(ii)(B) + 164.308(a)(7)(i) + 164.308(a)(7)(ii)(A) + 164.310(a)(1) + 164.310(a)(2)(i) + 164.310(a)(2)(ii) + 164.310(a)(2)(iii) + 164.310(b) + 164.310(c) + 164.310(d)(1) + 164.310(d)(2)(iii) + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.18.1.4 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.1 + A.9.4.2 + A.9.4.3 + A.9.4.4 + A.9.4.5 + IA-5(1)(a) + IA-5(c) + CM-6(a) + PR.AC-1 + PR.AC-4 + PR.AC-6 + PR.AC-7 + PR.DS-5 + FIA_UAU.1 + Req-8.2.3 + SRG-OS-000480-GPOS-00227 + If an account has an empty password, anyone could log in and +run commands with the privileges of that account. Accounts with +empty passwords should never be used in operational environments. + + CCE-83611-4 + - name: Prevent Login to Accounts With Empty Password - Check if system relies on + authselect + ansible.builtin.stat: + path: /usr/bin/authselect + register: result_authselect_present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83611-4 + - CJIS-5.5.2 + - NIST-800-171-3.1.1 + - NIST-800-171-3.1.5 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(a) + - NIST-800-53-IA-5(c) + - PCI-DSS-Req-8.2.3 + - configure_strategy + - high_severity + - low_complexity + - medium_disruption + - no_empty_passwords + - no_reboot_needed + +- name: Prevent Login to Accounts With Empty Password - Remediate using authselect + block: + + - name: Prevent Login to Accounts With Empty Password - Check integrity of authselect + current profile + ansible.builtin.command: + cmd: authselect check + register: result_authselect_check_cmd + changed_when: false + ignore_errors: true + + - name: Prevent Login to Accounts With Empty Password - Informative message based + on the authselect integrity check result + ansible.builtin.assert: + that: + - result_authselect_check_cmd is success + fail_msg: + - authselect integrity check failed. Remediation aborted! + - This remediation could not be applied because an authselect profile was not + selected or the selected profile is not intact. + - It is not recommended to manually edit the PAM files when authselect tool + is available. + - In cases where the default authselect profile does not cover a specific demand, + a custom authselect profile is recommended. + success_msg: + - authselect integrity check passed + + - name: Prevent Login to Accounts With Empty Password - Get authselect current features + ansible.builtin.shell: + cmd: authselect current | tail -n+3 | awk '{ print $2 }' + register: result_authselect_features + changed_when: false + when: + - result_authselect_check_cmd is success + + - name: Prevent Login to Accounts With Empty Password - Ensure "without-nullok" + feature is enabled using authselect tool + ansible.builtin.command: + cmd: authselect enable-feature without-nullok + register: result_authselect_enable_feature_cmd + when: + - result_authselect_check_cmd is success + - result_authselect_features.stdout is not search("without-nullok") + + - name: Prevent Login to Accounts With Empty Password - Ensure authselect changes + are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: + - result_authselect_enable_feature_cmd is not skipped + - result_authselect_enable_feature_cmd is success + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - result_authselect_present.stat.exists + tags: + - CCE-83611-4 + - CJIS-5.5.2 + - NIST-800-171-3.1.1 + - NIST-800-171-3.1.5 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(a) + - NIST-800-53-IA-5(c) + - PCI-DSS-Req-8.2.3 + - configure_strategy + - high_severity + - low_complexity + - medium_disruption + - no_empty_passwords + - no_reboot_needed + +- name: Prevent Login to Accounts With Empty Password - Remediate directly editing + PAM files + ansible.builtin.replace: + dest: '{{ item }}' + regexp: nullok + loop: + - /etc/pam.d/system-auth + - /etc/pam.d/password-auth + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - not result_authselect_present.stat.exists + tags: + - CCE-83611-4 + - CJIS-5.5.2 + - NIST-800-171-3.1.1 + - NIST-800-171-3.1.5 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(a) + - NIST-800-53-IA-5(c) + - PCI-DSS-Req-8.2.3 + - configure_strategy + - high_severity + - low_complexity + - medium_disruption + - no_empty_passwords + - no_reboot_needed + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,%23%20Generated%20by%20authselect%20on%20Sat%20Oct%2027%2014%3A59%3A36%202018%0A%23%20Do%20not%20modify%20this%20file%20manually.%0A%0Aauth%20%20%20%20%20%20%20%20required%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_env.so%0Aauth%20%20%20%20%20%20%20%20required%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_faildelay.so%20delay%3D2000000%0Aauth%20%20%20%20%20%20%20%20sufficient%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_fprintd.so%0Aauth%20%20%20%20%20%20%20%20%5Bdefault%3D1%20ignore%3Dignore%20success%3Dok%5D%20%20%20%20%20%20%20%20%20pam_succeed_if.so%20uid%20%3E%3D%201000%20quiet%0Aauth%20%20%20%20%20%20%20%20%5Bdefault%3D1%20ignore%3Dignore%20success%3Dok%5D%20%20%20%20%20%20%20%20%20pam_localuser.so%0Aauth%20%20%20%20%20%20%20%20sufficient%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_unix.so%20try_first_pass%0Aauth%20%20%20%20%20%20%20%20requisite%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_succeed_if.so%20uid%20%3E%3D%201000%20quiet_success%0Aauth%20%20%20%20%20%20%20%20sufficient%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_sss.so%20forward_pass%0Aauth%20%20%20%20%20%20%20%20required%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_deny.so%0A%0Aaccount%20%20%20%20%20required%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_unix.so%0Aaccount%20%20%20%20%20sufficient%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_localuser.so%0Aaccount%20%20%20%20%20sufficient%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_succeed_if.so%20uid%20%3C%201000%20quiet%0Aaccount%20%20%20%20%20%5Bdefault%3Dbad%20success%3Dok%20user_unknown%3Dignore%5D%20pam_sss.so%0Aaccount%20%20%20%20%20required%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_permit.so%0A%0Apassword%20%20%20%20requisite%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_pwquality.so%20try_first_pass%20local_users_only%0Apassword%20%20%20%20sufficient%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_unix.so%20sha512%20shadow%20try_first_pass%20use_authtok%0Apassword%20%20%20%20sufficient%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_sss.so%20use_authtok%0Apassword%20%20%20%20required%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_deny.so%0A%0Asession%20%20%20%20%20user%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_keyinit.so%20revoke%0Asession%20%20%20%20%20required%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_limits.so%0A-session%20%20%20%20user%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_systemd.so%0Asession%20%20%20%20%20%5Bsuccess%3D1%20default%3Dignore%5D%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_succeed_if.so%20service%20in%20crond%20quiet%20use_uid%0Asession%20%20%20%20%20required%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_unix.so%0Asession%20%20%20%20%20user%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_sss.so%0A + mode: 0644 + path: /etc/pam.d/password-auth + overwrite: true + - contents: + source: data:,%23%20Generated%20by%20authselect%20on%20Sat%20Oct%2027%2014%3A59%3A36%202018%0A%23%20Do%20not%20modify%20this%20file%20manually.%0A%0Aauth%20%20%20%20%20%20%20%20required%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_env.so%0Aauth%20%20%20%20%20%20%20%20required%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_faildelay.so%20delay%3D2000000%0Aauth%20%20%20%20%20%20%20%20sufficient%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_fprintd.so%0Aauth%20%20%20%20%20%20%20%20%5Bdefault%3D1%20ignore%3Dignore%20success%3Dok%5D%20%20%20%20%20%20%20%20%20pam_succeed_if.so%20uid%20%3E%3D%201000%20quiet%0Aauth%20%20%20%20%20%20%20%20%5Bdefault%3D1%20ignore%3Dignore%20success%3Dok%5D%20%20%20%20%20%20%20%20%20pam_localuser.so%0Aauth%20%20%20%20%20%20%20%20sufficient%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_unix.so%20try_first_pass%0Aauth%20%20%20%20%20%20%20%20requisite%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_succeed_if.so%20uid%20%3E%3D%201000%20quiet_success%0Aauth%20%20%20%20%20%20%20%20sufficient%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_sss.so%20forward_pass%0Aauth%20%20%20%20%20%20%20%20required%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_deny.so%0A%0Aaccount%20%20%20%20%20required%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_unix.so%0Aaccount%20%20%20%20%20sufficient%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_localuser.so%0Aaccount%20%20%20%20%20sufficient%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_succeed_if.so%20uid%20%3C%201000%20quiet%0Aaccount%20%20%20%20%20%5Bdefault%3Dbad%20success%3Dok%20user_unknown%3Dignore%5D%20pam_sss.so%0Aaccount%20%20%20%20%20required%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_permit.so%0A%0Apassword%20%20%20%20requisite%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_pwquality.so%20try_first_pass%20local_users_only%0Apassword%20%20%20%20sufficient%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_unix.so%20sha512%20shadow%20try_first_pass%20use_authtok%0Apassword%20%20%20%20sufficient%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_sss.so%20use_authtok%0Apassword%20%20%20%20required%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_deny.so%0A%0Asession%20%20%20%20%20user%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_keyinit.so%20revoke%0Asession%20%20%20%20%20required%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_limits.so%0A-session%20%20%20%20user%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_systemd.so%0Asession%20%20%20%20%20%5Bsuccess%3D1%20default%3Dignore%5D%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_succeed_if.so%20service%20in%20crond%20quiet%20use_uid%0Asession%20%20%20%20%20required%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_unix.so%0Asession%20%20%20%20%20user%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_sss.so%0A + mode: 0644 + path: /etc/pam.d/system-auth + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if [ -f /usr/bin/authselect ]; then + if ! authselect check; then +echo " +authselect integrity check failed. Remediation aborted! +This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact. +It is not recommended to manually edit the PAM files when authselect tool is available. +In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended." +exit 1 +fi +authselect enable-feature without-nullok + +authselect apply-changes -b +else + +if grep -qP '^\s*auth\s+'"sufficient"'\s+pam_unix.so\s.*\bnullok\b' "/etc/pam.d/system-auth"; then + sed -i -E --follow-symlinks 's/(.*auth.*'"sufficient"'.*pam_unix.so.*)\snullok=?[[:alnum:]]*(.*)/\1\2/g' "/etc/pam.d/system-auth" +fi + +if grep -qP '^\s*password\s+'"sufficient"'\s+pam_unix.so\s.*\bnullok\b' "/etc/pam.d/system-auth"; then + sed -i -E --follow-symlinks 's/(.*password.*'"sufficient"'.*pam_unix.so.*)\snullok=?[[:alnum:]]*(.*)/\1\2/g' "/etc/pam.d/system-auth" +fi + +if grep -qP '^\s*auth\s+'"sufficient"'\s+pam_unix.so\s.*\bnullok\b' "/etc/pam.d/password-auth"; then + sed -i -E --follow-symlinks 's/(.*auth.*'"sufficient"'.*pam_unix.so.*)\snullok=?[[:alnum:]]*(.*)/\1\2/g' "/etc/pam.d/password-auth" +fi + +if grep -qP '^\s*password\s+'"sufficient"'\s+pam_unix.so\s.*\bnullok\b' "/etc/pam.d/password-auth"; then + sed -i -E --follow-symlinks 's/(.*password.*'"sufficient"'.*pam_unix.so.*)\snullok=?[[:alnum:]]*(.*)/\1\2/g' "/etc/pam.d/password-auth" +fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure There Are No Accounts With Blank or Null Passwords + Check the "/etc/shadow" file for blank passwords with the +following command: +$ sudo awk -F: '!$2 {print $1}' /etc/shadow +If the command returns any results, this is a finding. +Configure all accounts on the system to have a password or lock +the account with the following commands: +Perform a password reset: +$ sudo passwd [username] +Lock an account: +$ sudo passwd -l [username] + Note that this rule is not applicable for systems running within a container. Having user with empty password within a container is not considered a risk, because it should not be possible to directly login into a container anyway. + CCI-000366 + CM-6(b) + CM-6.1(iv) + SRG-OS-000480-GPOS-00227 + If an account has an empty password, anyone could log in and +run commands with the privileges of that account. Accounts with +empty passwords should never be used in operational environments. + + CCE-85972-8 + - name: Collect users with no password + command: | + awk -F: '!$2 {print $1}' /etc/shadow + register: users_nopasswd + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-85972-8 + - NIST-800-53-CM-6(b) + - NIST-800-53-CM-6.1(iv) + - high_severity + - low_complexity + - low_disruption + - no_empty_passwords_etc_shadow + - no_reboot_needed + - restrict_strategy + +- name: Lock users with no password + command: | + passwd -l {{ item }} + with_items: '{{ users_nopasswd.stdout_lines }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - users_nopasswd.stdout_lines | length > 0 + tags: + - CCE-85972-8 + - NIST-800-53-CM-6(b) + - NIST-800-53-CM-6.1(iv) + - high_severity + - low_complexity + - low_disruption + - no_empty_passwords_etc_shadow + - no_reboot_needed + - restrict_strategy + + + + + + + + + + Ensure there are no legacy + NIS entries in /etc/group + The + character in /etc/group file marks a place where +entries from a network information service (NIS) should be directly inserted. + Using this method to include entries into /etc/group is considered legacy +and should be avoided. These entries may provide a way for an attacker +to gain access to the system. + CCE-83616-3 + +if grep -q '^\+' /etc/group; then +# backup old file to /etc/group- + cp /etc/group /etc/group- + sed -i '/^\+.*$/d' /etc/group +fi + + + + + + + + + + Ensure there are no legacy + NIS entries in /etc/passwd + The + character in /etc/passwd file marks a place where +entries from a network information service (NIS) should be directly inserted. + Using this method to include entries into /etc/passwd is considered legacy +and should be avoided. These entries may provide a way for an attacker +to gain access to the system. + CCE-83620-5 + +if grep -q '^\+' /etc/passwd; then +# backup old file to /etc/passwd- + cp /etc/passwd /etc/passwd- + sed -i '/^\+.*$/d' /etc/passwd +fi + + + + + + + + + + Ensure there are no legacy + NIS entries in /etc/shadow + The + character in /etc/shadow file marks a place where +entries from a network information service (NIS) should be directly inserted. + Using this method to include entries into /etc/shadow is considered legacy +and should be avoided. These entries may provide a way for an attacker +to gain access to the system. + CCE-83612-2 + +if grep -q '^\+' /etc/shadow; then +# backup old file to /etc/shadow- + cp /etc/shadow /etc/shadow- + sed -i '/^\+.*$/d' /etc/shadow +fi + + + + + + + + + + Verify No netrc Files Exist + The .netrc files contain login information +used to auto-login into FTP servers and reside in the user's home +directory. These files may contain unencrypted passwords to +remote FTP servers making them susceptible to access by unauthorized +users and should not be used. Any .netrc files should be removed. + 1 + 11 + 12 + 14 + 15 + 16 + 18 + 3 + 5 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.03 + DSS06.06 + DSS06.10 + CCI-000196 + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + A.18.1.4 + A.6.1.2 + A.7.1.1 + A.9.1.2 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.1 + A.9.4.2 + A.9.4.3 + A.9.4.4 + A.9.4.5 + CIP-003-8 R1.3 + CIP-003-8 R3 + CIP-003-8 R3.1 + CIP-003-8 R3.2 + CIP-003-8 R3.3 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.2.3 + CIP-004-6 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.2 + CIP-007-3 R5.2 + CIP-007-3 R5.3.1 + CIP-007-3 R5.3.2 + CIP-007-3 R5.3.3 + IA-5(h) + IA-5(1)(c) + CM-6(a) + IA-5(7) + PR.AC-1 + PR.AC-4 + PR.AC-6 + PR.AC-7 + PR.PT-3 + Unencrypted passwords for remote FTP servers may be stored in .netrc +files. + CCE-83617-1 + + + + + + + + + + Restrict Root Logins + Direct root logins should be allowed only for emergency use. +In normal situations, the administrator should access the system +via a unique unprivileged account, and then use su or sudo to execute +privileged commands. Discouraging administrators from accessing the +root account directly ensures an audit trail in organizations with +multiple administrators. Locking down the channels through which +root can connect directly also reduces opportunities for +password-guessing against the root account. The login program +uses the file /etc/securetty to determine which interfaces +should allow root logins. + +The virtual devices /dev/console +and /dev/tty* represent the system consoles (accessible via +the Ctrl-Alt-F1 through Ctrl-Alt-F6 keyboard sequences on a default +installation). The default securetty file also contains /dev/vc/*. +These are likely to be deprecated in most environments, but may be retained +for compatibility. Root should also be prohibited from connecting +via network protocols. Other sections of this document +include guidance describing how to prevent root from logging in via SSH. + + Verify Only Root Has UID 0 + If any account other than root has a UID of 0, this misconfiguration should +be investigated and the accounts other than root should be removed or have +their UID changed. + +If the account is associated with system commands or applications the UID +should be changed to one greater than "0" but less than "1000." +Otherwise assign a UID greater than "1000" that has not already been +assigned. + 1 + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.02 + DSS06.03 + DSS06.10 + 3.1.1 + 3.1.5 + CCI-000366 + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.18.1.4 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.1 + A.9.4.2 + A.9.4.3 + A.9.4.4 + A.9.4.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.2.3 + CIP-004-6 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.2 + CIP-007-3 R5.2 + CIP-007-3 R5.3.1 + CIP-007-3 R5.3.2 + CIP-007-3 R5.3.3 + IA-2 + AC-6(5) + IA-4(b) + PR.AC-1 + PR.AC-4 + PR.AC-6 + PR.AC-7 + PR.DS-5 + SRG-OS-000480-GPOS-00227 + An account has root authority if it has a UID of 0. Multiple accounts +with a UID of 0 afford more opportunity for potential intruders to +guess a password for a privileged account. Proper configuration of +sudo is recommended to afford multiple system administrators +access to root privileges in an accountable manner. + CCE-83624-7 + awk -F: '$3 == 0 && $1 != "root" { print $1 }' /etc/passwd | xargs --no-run-if-empty --max-lines=1 passwd -l + + + + + + + + + + Verify Root Has A Primary GID 0 + The root user should have a primary group of 0. + To help ensure that root-owned files are not inadvertently exposed to other users. + CCE-86298-7 + + + + + + + + + Direct root Logins Not Allowed + To further limit access to the root account, administrators +can disable root logins at the console by editing the /etc/securetty file. +This file lists all devices the root user is allowed to login to. If the file does +not exist at all, the root user can login through any communication device on the +system, whether via the console or via a raw network interface. This is dangerous +as user can login to the system as root via Telnet, which sends the password in +plain text over the network. By default, Red Hat Enterprise Linux 9's +/etc/securetty file only allows the root user to login at the console +physically attached to the system. To prevent root from logging in, remove the +contents of this file. To prevent direct root logins, remove the contents of this +file by typing the following command: + +$ sudo echo > /etc/securetty + + This rule only checks the /etc/securetty file existence and its content. +If you need to restrict user access using the /etc/securetty file, make sure +the pam_securetty.so PAM module is properly enabled in relevant PAM files. + BP28(R19) + 1 + 12 + 15 + 16 + 5 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.03 + DSS06.10 + 3.1.1 + 3.1.6 + 164.308(a)(1)(ii)(B) + 164.308(a)(7)(i) + 164.308(a)(7)(ii)(A) + 164.310(a)(1) + 164.310(a)(2)(i) + 164.310(a)(2)(ii) + 164.310(a)(2)(iii) + 164.310(b) + 164.310(c) + 164.310(d)(1) + 164.310(d)(2)(iii) + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.2 + 4.3.3.7.4 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + A.18.1.4 + A.7.1.1 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.2 + A.9.4.3 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.2.3 + CIP-004-6 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.2 + CIP-007-3 R5.2 + CIP-007-3 R5.3.1 + CIP-007-3 R5.3.2 + CIP-007-3 R5.3.3 + IA-2 + CM-6(a) + PR.AC-1 + PR.AC-6 + PR.AC-7 + Disabling direct root logins ensures proper accountability and multifactor +authentication to privileged accounts. Users will first login, then escalate +to privileged (root) access via su / sudo. This is required for FISMA Low +and FISMA Moderate systems. + + CCE-83625-4 + - name: Direct root Logins Not Allowed + copy: + dest: /etc/securetty + content: '' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83625-4 + - NIST-800-171-3.1.1 + - NIST-800-171-3.1.6 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-2 + - low_complexity + - low_disruption + - medium_severity + - no_direct_root_logins + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:, + mode: 0600 + path: /etc/securetty + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +echo > /etc/securetty + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure that System Accounts Are Locked + Some accounts are not associated with a human user of the system, and exist to +perform some administrative function. An attacker should not be able to log into +these accounts. + +System accounts are those user accounts with a user ID +less than UID_MIN, where value of the UID_MIN directive is set in +/etc/login.defs configuration file. In the default configuration UID_MIN is set +to 500, thus system accounts are those user accounts with a user ID less than +500. If any system account SYSACCT (other than root) has an unlocked password, +disable it with the command: +$ sudo passwd -l SYSACCT + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + AC-6 + CM-6(a) + Disabling authentication for default system accounts makes it more difficult +for attackers to make use of them to compromise a system.false + + + + + + Ensure that System Accounts Do Not Run a Shell Upon Login + Some accounts are not associated with a human user of the system, and exist to +perform some administrative function. Should an attacker be able to log into +these accounts, they should not be granted access to a shell. + +The login shell for each local account is stored in the last field of each line +in /etc/passwd. System accounts are those user accounts with a user ID +less than UID_MIN, where value of UID_MIN directive is set in +/etc/login.defs configuration file. In the default configuration UID_MIN is set +to 1000, thus system accounts are those user accounts with a user ID less than +1000. The user ID is stored in the third field. If any system account +SYSACCT (other than root) has a login shell, disable it with the +command: $ sudo usermod -s /sbin/nologin SYSACCT + Do not perform the steps in this section on the root account. Doing so might +cause the system to become inaccessible. + 1 + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + 7 + 8 + DSS01.03 + DSS03.05 + DSS05.04 + DSS05.05 + DSS05.07 + DSS06.03 + CCI-000366 + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + SR 1.1 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 6.2 + 1491 + A.12.4.1 + A.12.4.3 + A.6.1.2 + A.7.1.1 + A.9.1.2 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.1 + A.9.4.2 + A.9.4.3 + A.9.4.4 + A.9.4.5 + AC-6 + CM-6(a) + CM-6(b) + CM-6.1(iv) + DE.CM-1 + DE.CM-3 + PR.AC-1 + PR.AC-4 + PR.AC-6 + SRG-OS-000480-GPOS-00227 + Ensuring shells are not given to system accounts upon login makes it more +difficult for attackers to make use of system accounts. + CCE-83623-9 + + + + + + + + + Restrict Serial Port Root Logins + To restrict root logins on serial ports, +ensure lines of this form do not appear in /etc/securetty: +ttyS0 +ttyS1 + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + 3.1.1 + 3.1.5 + CCI-000770 + 164.308(a)(1)(ii)(B) + 164.308(a)(7)(i) + 164.308(a)(7)(ii)(A) + 164.310(a)(1) + 164.310(a)(2)(i) + 164.310(a)(2)(ii) + 164.310(a)(2)(iii) + 164.310(b) + 164.310(c) + 164.310(d)(1) + 164.310(d)(2)(iii) + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + AC-6 + CM-6(a) + PR.AC-4 + PR.DS-5 + Preventing direct root login to serial port interfaces +helps ensure accountability for actions taken on the systems +using the root account. + CCE-83622-1 + - name: Restrict Serial Port Root Logins + lineinfile: + dest: /etc/securetty + regexp: ttyS[0-9] + state: absent + tags: + - CCE-83622-1 + - NIST-800-171-3.1.1 + - NIST-800-171-3.1.5 + - NIST-800-53-AC-6 + - NIST-800-53-CM-6(a) + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_serial_port_logins + - restrict_strategy + + sed -i '/ttyS/d' /etc/securetty + + + + + + + + + + Restrict Virtual Console Root Logins + To restrict root logins through the (deprecated) virtual console devices, +ensure lines of this form do not appear in /etc/securetty: +vc/1 +vc/2 +vc/3 +vc/4 + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + 3.1.1 + 3.1.5 + CCI-000770 + 164.308(a)(1)(ii)(B) + 164.308(a)(7)(i) + 164.308(a)(7)(ii)(A) + 164.310(a)(1) + 164.310(a)(2)(i) + 164.310(a)(2)(ii) + 164.310(a)(2)(iii) + 164.310(b) + 164.310(c) + 164.310(d)(1) + 164.310(d)(2)(iii) + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + AC-6 + CM-6(a) + PR.AC-4 + PR.DS-5 + SRG-OS-000324-GPOS-00125 + Preventing direct root login to virtual console devices +helps ensure accountability for actions taken on the system +using the root account. + CCE-83626-2 + - name: Restrict Virtual Console Root Logins + lineinfile: + dest: /etc/securetty + regexp: ^vc + state: absent + tags: + - CCE-83626-2 + - NIST-800-171-3.1.1 + - NIST-800-171-3.1.5 + - NIST-800-53-AC-6 + - NIST-800-53-CM-6(a) + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - securetty_root_login_console_only + + sed -i '/^vc\//d' /etc/securetty + + + + + + + + + + Enforce usage of pam_wheel for su authentication + To ensure that only users who are members of the wheel group can +run commands with altered privileges through the su command, make +sure that the following line exists in the file /etc/pam.d/su: +auth required pam_wheel.so use_uid + FMT_SMF_EXT.1.1 + SRG-OS-000373-GPOS-00156 + SRG-OS-000312-GPOS-00123 + The su program allows to run commands with a substitute user and +group ID. It is commonly used to run commands as the root user. Limiting +access to such command is considered a good security practice. + CCE-90085-2 + - name: restrict usage of su command only to members of wheel group + replace: + path: /etc/pam.d/su + regexp: ^[\s]*#[\s]*auth[\s]+required[\s]+pam_wheel\.so[\s]+use_uid$ + replace: auth required pam_wheel.so use_uid + tags: + - CCE-90085-2 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - use_pam_wheel_for_su + + +# uncomment the option if commented + sed '/^[[:space:]]*#[[:space:]]*auth[[:space:]]\+required[[:space:]]\+pam_wheel\.so[[:space:]]\+use_uid$/s/^[[:space:]]*#//' -i /etc/pam.d/su + + + + + + + + + + + + Secure Session Configuration Files for Login Accounts + When a user logs into a Unix account, the system +configures the user's session by reading a number of files. Many of +these files are located in the user's home directory, and may have +weak permissions as a result of user error or misconfiguration. If +an attacker can modify or even read certain types of account +configuration information, they can often gain full access to the +affected user's account. Therefore, it is important to test and +correct configuration file permissions for interactive accounts, +particularly those of privileged users such as root or system +administrators. + + Maximum login attempts delay + Maximum time in seconds between fail login attempts before re-prompting. + 1 + 2 + 3 + 4 + 5 + 4 + + + Maximum concurrent login sessions + Maximum number of concurrent sessions by a user + 1 + 10 + 15 + 20 + 3 + 5 + 1 + + + Account Inactivity Timeout (seconds) + In an interactive shell, the value is interpreted as the +number of seconds to wait for input after issuing the primary prompt. +Bash terminates after waiting for that number of seconds if input does +not arrive. + 1800 + 600 + 900 + 300 + 600 + + + Interactive users initialization files + 'A regular expression describing a list of file names +for files that are sourced at login time for interactive users' + (\.bashrc|\.zshrc|\.cshrc|\.profile|\.bash_login|\.bash_profile) + + + Ensure Home Directories are Created for New Users + All local interactive user accounts, upon creation, should be assigned a home directory. + +Configure the operating system to assign home directories to all new local interactive users by setting the CREATE_HOME +parameter in /etc/login.defs to yes as follows: + +CREATE_HOME yes + CCI-000366 + SRG-OS-000480-GPOS-00227 + If local interactive users are not assigned a valid home directory, there is no place +for the storage and control of files they should own. + + CCE-88983-2 + + + + + + + + + Ensure the Logon Failure Delay is Set Correctly in login.defs + To ensure the logon failure delay controlled by /etc/login.defs is set properly, +add or correct the FAIL_DELAY setting in /etc/login.defs to read as follows: +FAIL_DELAY + 11 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + CCI-000366 + 4.3.4.3.2 + 4.3.4.3.3 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + AC-7(b) + CM-6(a) + PR.IP-1 + SRG-OS-000480-GPOS-00226 + Increasing the time between a failed authentication attempt and re-prompting to +enter credentials helps to slow a single-threaded brute force attack. + + CCE-83635-3 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83635-3 + - NIST-800-53-AC-7(b) + - NIST-800-53-CM-6(a) + - accounts_logon_fail_delay + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy +- name: XCCDF Value var_accounts_fail_delay # promote to variable + set_fact: + var_accounts_fail_delay: !!str + tags: + - always + +- name: Set accounts logon fail delay + lineinfile: + dest: /etc/login.defs + regexp: ^FAIL_DELAY + line: FAIL_DELAY {{ var_accounts_fail_delay }} + create: true + when: '"shadow-utils" in ansible_facts.packages' + tags: + - CCE-83635-3 + - NIST-800-53-AC-7(b) + - NIST-800-53-CM-6(a) + - accounts_logon_fail_delay + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q shadow-utils; then + +var_accounts_fail_delay='' + + +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/login.defs"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^FAIL_DELAY") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s %s" "$stripped_key" "$var_accounts_fail_delay" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^FAIL_DELAY\\>" "/etc/login.defs"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^FAIL_DELAY\\>.*/$escaped_formatted_output/gi" "/etc/login.defs" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-83635-3" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/login.defs" >> "/etc/login.defs" + printf '%s\n' "$formatted_output" >> "/etc/login.defs" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Limit the Number of Concurrent Login Sessions Allowed Per User + Limiting the number of allowed users and sessions per user can limit risks related to Denial of +Service attacks. This addresses concurrent sessions for a single account and does not address +concurrent sessions by a single user via multiple accounts. To set the number of concurrent +sessions per user add the following line in /etc/security/limits.conf or +a file under /etc/security/limits.d/: +* hard maxlogins + 14 + 15 + 18 + 9 + 5.5.2.2 + DSS01.05 + DSS05.02 + CCI-000054 + 4.3.3.4 + SR 3.1 + SR 3.8 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.14.1.2 + A.14.1.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.2 + AC-10 + CM-6(a) + PR.AC-5 + SRG-OS-000027-GPOS-00008 + SRG-OS-000027-VMM-000080 + Limiting simultaneous user logins can insulate the system from denial of service +problems caused by excessive logins. Automated login processes operating improperly or +maliciously may result in an exceptional number of simultaneous login sessions. + + CCE-83641-1 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83641-1 + - CJIS-5.5.2.2 + - NIST-800-53-AC-10 + - NIST-800-53-CM-6(a) + - accounts_max_concurrent_login_sessions + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - restrict_strategy +- name: XCCDF Value var_accounts_max_concurrent_login_sessions # promote to variable + set_fact: + var_accounts_max_concurrent_login_sessions: !!str + tags: + - always + +- name: Find /etc/security/limits.d files containing maxlogins configuration + find: + paths: /etc/security/limits.d + contains: ^[\s]*\*[\s]+(?:(?:hard)|(?:-))[\s]+maxlogins + patterns: '*.conf' + register: maxlogins + when: '"pam" in ansible_facts.packages' + tags: + - CCE-83641-1 + - CJIS-5.5.2.2 + - NIST-800-53-AC-10 + - NIST-800-53-CM-6(a) + - accounts_max_concurrent_login_sessions + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - restrict_strategy + +- name: Limit the Number of Concurrent Login Sessions Allowed Per User in files from + limits.d + replace: + dest: '{{ item.path }}' + regexp: ^#?\*.*maxlogins.* + replace: '* hard maxlogins {{ var_accounts_max_concurrent_login_sessions + }}' + with_items: + - '{{ maxlogins.files }}' + when: '"pam" in ansible_facts.packages' + tags: + - CCE-83641-1 + - CJIS-5.5.2.2 + - NIST-800-53-AC-10 + - NIST-800-53-CM-6(a) + - accounts_max_concurrent_login_sessions + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - restrict_strategy + +- name: Limit the Number of Concurrent Login Sessions Allowed Per User + lineinfile: + state: present + dest: /etc/security/limits.conf + insertbefore: ^# End of file + regexp: ^#?\*.*maxlogins + line: '* hard maxlogins {{ var_accounts_max_concurrent_login_sessions + }}' + create: true + when: + - '"pam" in ansible_facts.packages' + - maxlogins.matched == 0 + tags: + - CCE-83641-1 + - CJIS-5.5.2.2 + - NIST-800-53-AC-10 + - NIST-800-53-CM-6(a) + - accounts_max_concurrent_login_sessions + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q pam; then + +var_accounts_max_concurrent_login_sessions='' + + +if grep -q '^[^#]*\<maxlogins\>' /etc/security/limits.d/*.conf; then + sed -i "/^[^#]*\<maxlogins\>/ s/maxlogins.*/maxlogins $var_accounts_max_concurrent_login_sessions/" /etc/security/limits.d/*.conf +elif grep -q '^[^#]*\<maxlogins\>' /etc/security/limits.conf; then + sed -i "/^[^#]*\<maxlogins\>/ s/maxlogins.*/maxlogins $var_accounts_max_concurrent_login_sessions/" /etc/security/limits.conf +else + echo "* hard maxlogins $var_accounts_max_concurrent_login_sessions" >> /etc/security/limits.conf +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Configure Polyinstantiation of /tmp Directories + To configure polyinstantiated /tmp directories, first create the parent directories +which will hold the polyinstantiation child directories. Use the following command: +$ sudo mkdir --mode 000 /tmp/tmp-inst +Then, add the following entry to /etc/security/namespace.conf: +/tmp /tmp/tmp-inst/ level root,adm + BP28(R39) + Polyinstantiation of temporary directories is a proactive security measure +which reduces chances of attacks that are made possible by /tmp +directories being world-writable. + CCE-90827-7 + if ! [ -d /tmp/tmp-inst ] ; then + mkdir --mode 000 /tmp/tmp-inst +fi +chmod 000 /tmp/tmp-inst +chcon --reference=/tmp /tmp/tmp-inst + +if ! grep -Eq '^\s*/tmp\s+/tmp/tmp-inst/\s+level\s+root,adm$' /etc/security/namespace.conf ; then + if grep -Eq '^\s*/tmp\s+' /etc/security/namespace.conf ; then + sed -i '/^\s*\/tmp/d' /etc/security/namespace.conf + fi + echo "/tmp /tmp/tmp-inst/ level root,adm" >> /etc/security/namespace.conf +fi + + + + + + + + + + Configure Polyinstantiation of /var/tmp Directories + To configure polyinstantiated /tmp directories, first create the parent directories +which will hold the polyinstantiation child directories. Use the following command: +$ sudo mkdir --mode 000 /var/tmp/tmp-inst +Then, add the following entry to /etc/security/namespace.conf: +/var/tmp /var/tmp/tmp-inst/ level root,adm + BP28(R39) + Polyinstantiation of temporary directories is a proactive security measure +which reduces chances of attacks that are made possible by /var/tmp +directories being world-writable. + CCE-83642-9 + if ! [ -d /tmp-inst ] ; then + mkdir --mode 000 /var/tmp/tmp-inst +fi +chmod 000 /var/tmp/tmp-inst +chcon --reference=/var/tmp/ /var/tmp/tmp-inst + +if ! grep -Eq '^\s*/var/tmp\s+/var/tmp/tmp-inst/\s+level\s+root,adm$' /etc/security/namespace.conf ; then + if grep -Eq '^\s*/var/tmp\s+' /etc/security/namespace.conf ; then + sed -i '/^\s*\/var\/tmp/d' /etc/security/namespace.conf + fi + echo "/var/tmp /var/tmp/tmp-inst/ level root,adm" >> /etc/security/namespace.conf +fi + + + + + + + + + + Set Interactive Session Timeout + Setting the TMOUT option in /etc/profile ensures that +all user sessions will terminate based on inactivity. +The value of TMOUT should be exported and read only. +The TMOUT + +setting in a file loaded by /etc/profile, e.g. +/etc/profile.d/tmout.sh should read as follows: +declare -xr TMOUT= + BP28(R29) + 1 + 12 + 15 + 16 + DSS05.04 + DSS05.10 + DSS06.10 + 3.1.11 + CCI-000057 + CCI-001133 + CCI-002361 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + A.18.1.4 + A.9.2.1 + A.9.2.4 + A.9.3.1 + A.9.4.2 + A.9.4.3 + CIP-004-6 R2.2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.2 + CIP-007-3 R5.3.1 + CIP-007-3 R5.3.2 + CIP-007-3 R5.3.3 + AC-12 + SC-10 + AC-2(5) + CM-6(a) + PR.AC-7 + FMT_MOF_EXT.1 + SRG-OS-000163-GPOS-00072 + SRG-OS-000029-GPOS-00010 + SRG-OS-000163-VMM-000700 + SRG-OS-000279-VMM-001010 + Terminating an idle session within a short time period reduces +the window of opportunity for unauthorized personnel to take control of a +management session enabled on the console or console port that has been +left unattended. + + CCE-83633-8 + - name: XCCDF Value var_accounts_tmout # promote to variable + set_fact: + var_accounts_tmout: !!str + tags: + - always + +- name: Check for duplicate values + lineinfile: + path: /etc/profile.d/tmout.sh + create: false + regexp: TMOUT= + state: absent + check_mode: true + changed_when: false + register: dupes + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83633-8 + - NIST-800-171-3.1.11 + - NIST-800-53-AC-12 + - NIST-800-53-AC-2(5) + - NIST-800-53-CM-6(a) + - NIST-800-53-SC-10 + - accounts_tmout + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Deduplicate values from /etc/profile.d/tmout.sh + lineinfile: + path: /etc/profile.d/tmout.sh + create: false + regexp: TMOUT= + state: absent + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - dupes.found is defined and dupes.found > 1 + tags: + - CCE-83633-8 + - NIST-800-171-3.1.11 + - NIST-800-53-AC-12 + - NIST-800-53-AC-2(5) + - NIST-800-53-CM-6(a) + - NIST-800-53-SC-10 + - accounts_tmout + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Insert correct line into /etc/profile.d/tmout.sh + lineinfile: + path: /etc/profile.d/tmout.sh + create: true + regexp: TMOUT= + line: declare -xr TMOUT={{ var_accounts_tmout }} + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83633-8 + - NIST-800-171-3.1.11 + - NIST-800-53-AC-12 + - NIST-800-53-AC-2(5) + - NIST-800-53-CM-6(a) + - NIST-800-53-SC-10 + - accounts_tmout + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_accounts_tmout='' + + +# if 0, no occurence of tmout found, if 1, occurence found +tmout_found=0 + +for f in /etc/profile /etc/profile.d/*.sh; do + if grep --silent '^\s*TMOUT' $f; then + sed -i -E "s/^(\s*)TMOUT\s*=\s*(\w|\$)*(.*)$/declare -xr TMOUT=$var_accounts_tmout\3/g" $f + tmout_found=1 + fi +done + +if [ $tmout_found -eq 0 ]; then + echo -e "\n# Set TMOUT to $var_accounts_tmout per security requirements" >> /etc/profile.d/tmout.sh + echo "declare -xr TMOUT=$var_accounts_tmout" >> /etc/profile.d/tmout.sh +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + User Initialization Files Must Not Run World-Writable Programs + Set the mode on files being executed by the user initialization files with the +following command: +$ sudo chmod o-w FILE + CCI-000366 + SRG-OS-000480-GPOS-00227 + If user start-up files execute world-writable programs, especially in +unprotected directories, they could be maliciously modified to destroy user +files or otherwise compromise the system at the user level. If the system is +compromised at the user level, it is easier to elevate privileges to eventually +compromise the system at the root and network level. + CCE-87451-1 + +readarray -t world_writable_files < <(find / -xdev -type f -perm -0002 2> /dev/null) +readarray -t interactive_home_dirs < <(awk -F':' '{ if ($3 >= 1000 && $3 != 65534) print $6 }' /etc/passwd) + +for world_writable in "${world_writable_files[@]}"; do + for homedir in "${interactive_home_dirs[@]}"; do + if grep -q -d skip "$world_writable" "$homedir"/.*; then + chmod o-w $world_writable + break + fi + done +done + + + + + + + + + + + Ensure that Users Path Contains Only Local Directories + Ensure that all interactive user initialization files executable search +path statements do not contain statements that will reference a working +directory other than the users home directory. + CCI-000366 + SRG-OS-000480-GPOS-00227 + The executable search path (typically the PATH environment variable) contains a +list of directories for the shell to search to find executables. If this path +includes the current working directory (other than the users home directory), +executables in these directories may be executed instead of system commands. +This variable is formatted as a colon-separated list of directories. If there is +an empty entry, such as a leading or trailing colon or two consecutive colons, +this is interpreted as the current working directory. If deviations from the +default system search path for the local interactive user are required, they +must be documented with the Information System Security Officer (ISSO). + CCE-87487-5 + + + + + + All Interactive Users Must Have A Home Directory Defined + Assign home directories to all interactive users that currently do not +have a home directory assigned. + +This rule checks if the home directory is properly defined in a folder which has +at least one parent folder, like "user" in "/home/user" or "/remote/users/user". +Therefore, this rule will report a finding for home directories like /users, +/tmp or /. + CCI-000366 + SRG-OS-000480-GPOS-00227 + If local interactive users are not assigned a valid home directory, there is no +place for the storage and control of files they should own. + CCE-88964-2 + - name: Get all local users from /etc/passwd + ansible.builtin.getent: + database: passwd + split: ':' + tags: + - CCE-88964-2 + - accounts_user_interactive_home_directory_defined + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Create local_users variable from the getent output + ansible.builtin.set_fact: + local_users: '{{ ansible_facts.getent_passwd|dict2items }}' + tags: + - CCE-88964-2 + - accounts_user_interactive_home_directory_defined + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Ensure interactive users have an exclusive home directory defined + ansible.builtin.user: + name: '{{ item.key }}' + home: /home/{{ item.key }} + create_home: false + loop: '{{ local_users }}' + when: + - item.value[2]|int >= 1000 + - item.value[2]|int != 65534 + - not item.value[4] | regex_search('^\/\w*\/\w{1,}') + tags: + - CCE-88964-2 + - accounts_user_interactive_home_directory_defined + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + +for user in $(awk -F':' '{ if ($4 >= 1000 && $4 != 65534) print $1 }' /etc/passwd); do + # This follows the same logic of evaluation of home directories as used in OVAL. + if ! grep -q $user /etc/passwd | cut -d: -f6 | grep '^\/\w*\/\w\{1,\}'; then + sed -i "s/\($user:x:[0-9]*:[0-9]*:.*:\).*\(:.*\)$/\1\/home\/$user\2/g" /etc/passwd; + fi +done + + + + + + + + + + All Interactive Users Home Directories Must Exist + Create home directories to all interactive users that currently do not +have a home directory assigned. Use the following commands to create the user +home directory assigned in /etc/passwd: +$ sudo mkdir /home/USER + CCI-000366 + SRG-OS-000480-GPOS-00227 + If a local interactive user has a home directory defined that does not exist, +the user may be given access to the / directory as the current working directory +upon logon. This could create a Denial of Service because the user would not be +able to access their logon configuration files, and it may give them visibility +to system files they normally would not be able to access. + CCE-83639-5 + - name: Get all local users from /etc/passwd + ansible.builtin.getent: + database: passwd + split: ':' + tags: + - CCE-83639-5 + - accounts_user_interactive_home_directory_exists + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Create local_users variable from the getent output + ansible.builtin.set_fact: + local_users: '{{ ansible_facts.getent_passwd|dict2items }}' + tags: + - CCE-83639-5 + - accounts_user_interactive_home_directory_exists + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Ensure interactive users have a home directory exists + ansible.builtin.user: + name: '{{ item.key }}' + create_home: true + loop: '{{ local_users }}' + when: + - item.value[2]|int >= 1000 + - item.value[2]|int != 65534 + tags: + - CCE-83639-5 + - accounts_user_interactive_home_directory_exists + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + +for user in $(awk -F':' '{ if ($3 >= 1000 && $3 != 65534) print $1}' /etc/passwd); do + mkhomedir_helper $user 0077; +done + + + + + + + + + + All Interactive User Home Directories Must Be Group-Owned By The Primary User + Change the group owner of interactive users home directory to the +group found in /etc/passwd. To change the group owner of +interactive users home directory, use the following command: +$ sudo chgrp USER_GROUP /home/USER + +This rule ensures every home directory related to an interactive user is +group-owned by an interactive user. It also ensures that interactive users +are group-owners of one and only one home directory. + Due to OVAL limitation, this rule can report a false negative in a +specific situation where two interactive users swap the group-ownership +of their respective home directories. + CCI-000366 + SRG-OS-000480-GPOS-00227 + If the Group Identifier (GID) of a local interactive users home directory is +not the same as the primary GID of the user, this would allow unauthorized +access to the users files, and users that share the same group may not be +able to access files that they legitimately should. + CCE-83629-6 + - name: Get all local users from /etc/passwd + ansible.builtin.getent: + database: passwd + split: ':' + tags: + - CCE-83629-6 + - file_groupownership_home_directories + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Create local_users variable from the getent output + ansible.builtin.set_fact: + local_users: '{{ ansible_facts.getent_passwd|dict2items }}' + tags: + - CCE-83629-6 + - file_groupownership_home_directories + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Test for existence of home directories to avoid creating them, but only fixing + group ownership + ansible.builtin.stat: + path: '{{ item.value[4] }}' + register: path_exists + loop: '{{ local_users }}' + when: + - item.value[2]|int >= 1000 + - item.value[2]|int != 65534 + tags: + - CCE-83629-6 + - file_groupownership_home_directories + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Ensure interactive local users are the group-owners of their respective home + directories + ansible.builtin.file: + path: '{{ item.0.value[4] }}' + group: '{{ item.0.value[2] }}' + loop: '{{ local_users|zip(path_exists.results)|list }}' + when: item.1.stat is defined and item.1.stat.exists + tags: + - CCE-83629-6 + - file_groupownership_home_directories + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + +awk -F':' '{ if ($4 >= 1000 && $4 != 65534) system("chgrp -f " $4" "$6) }' /etc/passwd + + + + + + + + + + Ensure All User Initialization Files Have Mode 0740 Or Less Permissive + Set the mode of the user initialization files to 0740 with the +following command: +$ sudo chmod 0740 /home/USER/.INIT_FILE + CCI-000366 + SRG-OS-000480-GPOS-00227 + Local initialization files are used to configure the user's shell environment +upon logon. Malicious modification of these files could compromise accounts upon +logon. + CCE-83637-9 + + + + + + All Interactive User Home Directories Must Have mode 0750 Or Less Permissive + Change the mode of interactive users home directories to 0750. To +change the mode of interactive users home directory, use the +following command: +$ sudo chmod 0750 /home/USER + CCI-000366 + SRG-OS-000480-GPOS-00227 + Excessive permissions on local interactive user home directories may allow +unauthorized access to user files by other users. + CCE-83634-6 + - name: Get all local users from /etc/passwd + ansible.builtin.getent: + database: passwd + split: ':' + tags: + - CCE-83634-6 + - file_permissions_home_directories + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Create local_users variable from the getent output + ansible.builtin.set_fact: + local_users: '{{ ansible_facts.getent_passwd|dict2items }}' + tags: + - CCE-83634-6 + - file_permissions_home_directories + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Test for existence home directories to avoid creating them. + ansible.builtin.stat: + path: '{{ item.value[4] }}' + register: path_exists + loop: '{{ local_users }}' + when: + - item.value[1]|int >= 1000 + - item.value[1]|int != 65534 + tags: + - CCE-83634-6 + - file_permissions_home_directories + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Ensure interactive local users have proper permissions on their respective + home directories + ansible.builtin.file: + path: '{{ item.0.value[4] }}' + mode: u-s,g-w-s,o=- + follow: false + recurse: false + loop: '{{ local_users|zip(path_exists.results)|list }}' + when: item.1.stat is defined and item.1.stat.exists + tags: + - CCE-83634-6 + - file_permissions_home_directories + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + +for home_dir in $(awk -F':' '{ if ($3 >= 1000 && $3 != 65534) print $6 }' /etc/passwd); do + # Only update the permissions when necessary. This will avoid changing the inode timestamp when + # the permission is already defined as expected, therefore not impacting in possible integrity + # check systems that also check inodes timestamps. + find "$home_dir" -maxdepth 0 -perm /7027 -exec chmod u-s,g-w-s,o=- {} \; +done + + + + + + + + + + Ensure that User Home Directories are not Group-Writable or World-Readable + For each human user of the system, view the +permissions of the user's home directory: +# ls -ld /home/USER +Ensure that the directory is not group-writable and that it +is not world-readable. If necessary, repair the permissions: +# chmod g-w /home/USER +# chmod o-rwx /home/USER + This action may involve modifying user home directories. +Notify your user community, and solicit input if appropriate, +before making this type of change. + This rule is deprecated in favor of the file_permissions_home_directories rule. +Please consider replacing this rule in your files as it is not expected to receive +updates as of version 0.1.62. + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + CCI-000225 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-6(a) + AC-6(1) + CM-6(a) + PR.AC-4 + PR.DS-5 + User home directories contain many configuration files which +affect the behavior of a user's account. No user should ever have +write permission to another user's home directory. Group shared +directories can be configured in sub-directories or elsewhere in the +filesystem if they are needed. Typically, user home directories +should not be world-readable, as it would disclose file names +to other users. If a subset of users need read access +to one another's home directories, this can be provided using +groups or ACLs. + CCE-83638-7 + - name: Get all local users from /etc/passwd + ansible.builtin.getent: + database: passwd + split: ':' + tags: + - CCE-83638-7 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-6(a) + - file_permissions_home_dirs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Create local_users variable from the getent output + ansible.builtin.set_fact: + local_users: '{{ ansible_facts.getent_passwd|dict2items }}' + tags: + - CCE-83638-7 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-6(a) + - file_permissions_home_dirs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Test for existence home directories to avoid creating them. + ansible.builtin.stat: + path: '{{ item.value[4] }}' + register: path_exists + loop: '{{ local_users }}' + when: + - item.value[1]|int >= 1000 + - item.value[1]|int != 65534 + tags: + - CCE-83638-7 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-6(a) + - file_permissions_home_dirs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Ensure interactive local users have proper permissions on their respective + home directories + ansible.builtin.file: + path: '{{ item.0.value[4] }}' + mode: u-s,g-w-s,o=- + follow: false + recurse: false + loop: '{{ local_users|zip(path_exists.results)|list }}' + when: item.1.stat is defined and item.1.stat.exists + tags: + - CCE-83638-7 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-6(a) + - file_permissions_home_dirs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + +for home_dir in $(awk -F':' '{ if ($3 >= 1000 && $3 != 65534) print $6 }' /etc/passwd); do + # Only update the permissions when necessary. This will avoid changing the inode timestamp when + # the permission is already defined as expected, therefore not impacting in possible integrity + # check systems that also check inodes timestamps. + find "$home_dir" -maxdepth 0 -perm /7027 -exec chmod u-s,g-w-s,o=- {} \; +done + + + + + + + + + + Ensure that No Dangerous Directories Exist in Root's Path + The active path of the root account can be obtained by +starting a new root shell and running: +# echo $PATH +This will produce a colon-separated list of +directories in the path. + +Certain path elements could be considered dangerous, as they could lead +to root executing unknown or +untrusted programs, which could contain malicious +code. +Since root may sometimes work inside +untrusted directories, the . character, which represents the +current directory, should never be in the root path, nor should any +directory which can be written to by an unprivileged or +semi-privileged (system) user. + +It is a good practice for administrators to always execute +privileged commands by typing the full path to the +command. + + Ensure that Root's Path Does Not Include World or Group-Writable Directories + For each element in root's path, run: +# ls -ld DIR +and ensure that write permissions are disabled for group and +other. + 11 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + CCI-000366 + 4.3.4.3.2 + 4.3.4.3.3 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + CM-6(a) + CM-6(a) + PR.IP-1 + Such entries increase the risk that root could +execute code provided by unprivileged users, +and potentially malicious code. + CCE-83643-7 + - name: Print error message if user is not root + fail: + msg: Root account required to read root $PATH + when: ansible_env.USER != "root" + ignore_errors: true + tags: + - CCE-83643-7 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-6(a) + - accounts_root_path_dirs_no_write + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Get root paths which are not symbolic links + stat: + path: '{{ item }}' + changed_when: false + failed_when: false + register: root_paths + with_items: '{{ ansible_env.PATH.split('':'') }}' + when: ansible_env.USER == "root" + tags: + - CCE-83643-7 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-6(a) + - accounts_root_path_dirs_no_write + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Disable writability to root directories + file: + path: '{{ item.item }}' + mode: g-w,o-w + with_items: '{{ root_paths.results }}' + when: + - root_paths.results is defined + - item.stat.exists + - not item.stat.islnk + tags: + - CCE-83643-7 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-6(a) + - accounts_root_path_dirs_no_write + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + + + + + + + + + Ensure that Root's Path Does Not Include Relative Paths or Null Directories + Ensure that none of the directories in root's path is equal to a single +. character, or +that it contains any instances that lead to relative path traversal, such as +.. or beginning a path without the slash (/) character. +Also ensure that there are no "empty" elements in the path, such as in these examples: +PATH=:/bin +PATH=/bin: +PATH=/bin::/sbin +These empty elements have the same effect as a single . character. + 11 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + CCI-000366 + 4.3.4.3.2 + 4.3.4.3.3 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + CM-6(a) + CM-6(a) + PR.IP-1 + Including these entries increases the risk that root could +execute code from an untrusted location. + CCE-88059-1 + + + + + + + Ensure that Users Have Sensible Umask Values + The umask setting controls the default permissions +for the creation of new files. +With a default umask setting of 077, files and directories +created by users will not be readable by any other user on the +system. Users who wish to make specific files group- or +world-readable can accomplish this by using the chmod command. +Additionally, users can make all their files readable to their +group by default by setting a umask of 027 in their shell +configuration files. If default per-user groups exist (that is, if +every user has a default group whose name is the same as that +user's username and whose only member is the user), then it may +even be safe for users to select a umask of 007, making it very +easy to intentionally share files with groups of which the user is +a member. + + + Sensible umask + Enter default user umask + 007 + 022 + 027 + 077 + 027 + + + Ensure the Default Bash Umask is Set Correctly + To ensure the default umask for users of the Bash shell is set properly, +add or correct the umask setting in /etc/bashrc to read +as follows: +umask + BP28(R35) + 18 + APO13.01 + BAI03.01 + BAI03.02 + BAI03.03 + CCI-000366 + 4.3.4.3.3 + A.14.1.1 + A.14.2.1 + A.14.2.5 + A.6.1.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + AC-6(1) + CM-6(a) + PR.IP-2 + SRG-OS-000480-GPOS-00228 + SRG-OS-000480-GPOS-00227 + The umask value influences the permissions assigned to files when they are created. +A misconfigured umask value could result in files with excessive permissions that can be read or +written to by unauthorized users. + CCE-83644-5 + - name: XCCDF Value var_accounts_user_umask # promote to variable + set_fact: + var_accounts_user_umask: !!str + tags: + - always + +- name: Replace user umask in /etc/bashrc + replace: + path: /etc/bashrc + regexp: umask.* + replace: umask {{ var_accounts_user_umask }} + register: umask_replace + tags: + - CCE-83644-5 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - accounts_umask_etc_bashrc + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Append user umask in /etc/bashrc + lineinfile: + create: true + path: /etc/bashrc + line: umask {{ var_accounts_user_umask }} + when: umask_replace is not changed + tags: + - CCE-83644-5 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - accounts_umask_etc_bashrc + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + +var_accounts_user_umask='' + + + + + + +grep -q "^\s*umask" /etc/bashrc && \ + sed -i -E -e "s/^(\s*umask).*/\1 $var_accounts_user_umask/g" /etc/bashrc +if ! [ $? -eq 0 ]; then + echo "umask $var_accounts_user_umask" >> /etc/bashrc +fi + + + + + + + + + + + Ensure the Default C Shell Umask is Set Correctly + To ensure the default umask for users of the C shell is set properly, +add or correct the umask setting in /etc/csh.cshrc to read as follows: +umask + 18 + APO13.01 + BAI03.01 + BAI03.02 + BAI03.03 + CCI-000366 + 4.3.4.3.3 + A.14.1.1 + A.14.2.1 + A.14.2.5 + A.6.1.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + AC-6(1) + CM-6(a) + PR.IP-2 + SRG-OS-000480-GPOS-00228 + SRG-OS-000480-GPOS-00227 + The umask value influences the permissions assigned to files when they are created. +A misconfigured umask value could result in files with excessive permissions that can be read or +written to by unauthorized users. + CCE-87721-7 + - name: XCCDF Value var_accounts_user_umask # promote to variable + set_fact: + var_accounts_user_umask: !!str + tags: + - always + +- name: Replace user umask in /etc/csh.cshrc + replace: + path: /etc/csh.cshrc + regexp: umask.* + replace: umask {{ var_accounts_user_umask }} + register: umask_replace + tags: + - CCE-87721-7 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - accounts_umask_etc_csh_cshrc + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Append user umask in /etc/csh.cshrc + lineinfile: + create: true + path: /etc/csh.cshrc + line: umask {{ var_accounts_user_umask }} + when: umask_replace is not changed + tags: + - CCE-87721-7 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - accounts_umask_etc_csh_cshrc + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + +var_accounts_user_umask='' + + +grep -q "^\s*umask" /etc/csh.cshrc && \ + sed -i -E -e "s/^(\s*umask).*/\1 $var_accounts_user_umask/g" /etc/csh.cshrc +if ! [ $? -eq 0 ]; then + echo "umask $var_accounts_user_umask" >> /etc/csh.cshrc +fi + + + + + + + + + + + Ensure the Default Umask is Set Correctly in login.defs + To ensure the default umask controlled by /etc/login.defs is set properly, +add or correct the UMASK setting in /etc/login.defs to read as follows: +UMASK + BP28(R35) + 11 + 18 + 3 + 9 + APO13.01 + BAI03.01 + BAI03.02 + BAI03.03 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + CCI-000366 + 4.3.4.3.2 + 4.3.4.3.3 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.1.1 + A.14.2.1 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.14.2.5 + A.6.1.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + AC-6(1) + CM-6(a) + PR.IP-1 + PR.IP-2 + SRG-OS-000480-GPOS-00228 + The umask value influences the permissions assigned to files when they are created. +A misconfigured umask value could result in files with excessive permissions that can be read and +written to by unauthorized users. + + CCE-83647-8 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83647-8 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - accounts_umask_etc_login_defs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy +- name: XCCDF Value var_accounts_user_umask # promote to variable + set_fact: + var_accounts_user_umask: !!str + tags: + - always + +- name: Ensure the Default UMASK is Set Correctly + replace: + path: /etc/login.defs + regexp: ^UMASK + replace: UMASK {{ var_accounts_user_umask }} + register: umask_replace + when: '"shadow-utils" in ansible_facts.packages' + tags: + - CCE-83647-8 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - accounts_umask_etc_login_defs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Ensure the Default UMASK is Appended Correctly + lineinfile: + create: true + path: /etc/login.defs + line: UMASK {{ var_accounts_user_umask }} + when: + - '"shadow-utils" in ansible_facts.packages' + - umask_replace is not changed + tags: + - CCE-83647-8 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - accounts_umask_etc_login_defs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q shadow-utils; then + +var_accounts_user_umask='' + + +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/login.defs"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^UMASK") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s %s" "$stripped_key" "$var_accounts_user_umask" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^UMASK\\>" "/etc/login.defs"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^UMASK\\>.*/$escaped_formatted_output/gi" "/etc/login.defs" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-83647-8" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/login.defs" >> "/etc/login.defs" + printf '%s\n' "$formatted_output" >> "/etc/login.defs" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Ensure the Default Umask is Set Correctly in /etc/profile + To ensure the default umask controlled by /etc/profile is set properly, +add or correct the umask setting in /etc/profile to read as follows: +umask + BP28(R35) + 18 + APO13.01 + BAI03.01 + BAI03.02 + BAI03.03 + CCI-000366 + 4.3.4.3.3 + A.14.1.1 + A.14.2.1 + A.14.2.5 + A.6.1.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + AC-6(1) + CM-6(a) + PR.IP-2 + SRG-OS-000480-GPOS-00228 + SRG-OS-000480-GPOS-00227 + The umask value influences the permissions assigned to files when they are created. +A misconfigured umask value could result in files with excessive permissions that can be read or +written to by unauthorized users. + CCE-90828-5 + - name: XCCDF Value var_accounts_user_umask # promote to variable + set_fact: + var_accounts_user_umask: !!str + tags: + - always + +- name: Check if umask is already set + ansible.builtin.lineinfile: + path: /etc/profile + regexp: (^[\s]*umask)\s+(\d+) + state: absent + check_mode: true + changed_when: false + register: result_umask_is_set + tags: + - CCE-90828-5 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - accounts_umask_etc_profile + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Replace user umask in /etc/profile + ansible.builtin.replace: + path: /etc/profile + regexp: ^(\s*)umask\s+\d+ + replace: \1umask {{ var_accounts_user_umask }} + tags: + - CCE-90828-5 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - accounts_umask_etc_profile + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Append user umask in /etc/profile + ansible.builtin.lineinfile: + create: true + path: /etc/profile + line: umask {{ var_accounts_user_umask }} + when: result_umask_is_set.found == 0 + tags: + - CCE-90828-5 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - accounts_umask_etc_profile + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + +var_accounts_user_umask='' + + +grep -qE '^[^#]*umask' /etc/profile && \ + sed -i "s/umask.*/umask $var_accounts_user_umask/g" /etc/profile +if ! [ $? -eq 0 ]; then + echo "umask $var_accounts_user_umask" >> /etc/profile +fi + + + + + + + + + + + Ensure the Default Umask is Set Correctly For Interactive Users + Remove the UMASK environment variable from all interactive users initialization files. + CCI-000366 + CCI-001814 + SRG-OS-000480-GPOS-00227 + SRG-OS-000480-GPOS-00228 + The umask controls the default access mode assigned to newly created files. A +umask of 077 limits new files to mode 700 or less permissive. Although umask can +be represented as a four-digit number, the first digit representing special +access modes is typically ignored or required to be 0. This requirement +applies to the globally configured system defaults and the local interactive +user defaults for each account on the system. + CCE-90365-8 + - name: Ensure interactive local users are the owners of their respective initialization + files + ansible.builtin.shell: + cmd: |- + for dir in $(awk -F':' '{ if ($3 >= 1000 && $3 != 65534) print $6}' /etc/passwd); do + for file in $(find $dir -maxdepth 1 -type f -name ".*"); do + sed -i 's/^\([\s]*umask\s*\)/#\1/g' $file + done + done + tags: + - CCE-90365-8 + - accounts_umask_interactive_users + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + +while IFS= read -r dir; do + while IFS= read -r -d '' file; do + sed -i 's/^\([\s]*umask\s*\)/#\1/g' "$file" + done < <(find $dir -maxdepth 1 -type f -name ".*" -print0) +done < <(awk -F':' '{ if ($3 >= 1000 && $3 != 65534) print $6}' /etc/passwd) + + + + + + + + + + + + + System Accounting with auditd + The audit service provides substantial capabilities +for recording system activities. By default, the service audits about +SELinux AVC denials and certain types of security-relevant events +such as system logins, account modifications, and authentication +events performed by programs such as sudo. +Under its default configuration, auditd has modest disk space +requirements, and should not noticeably impact system performance. + +NOTE: The Linux Audit daemon auditd can be configured to use +the augenrules program to read audit rules files (*.rules) +located in /etc/audit/rules.d location and compile them to create +the resulting form of the /etc/audit/audit.rules configuration file +during the daemon startup (default configuration). Alternatively, the auditd +daemon can use the auditctl utility to read audit rules from the +/etc/audit/audit.rules configuration file during daemon startup, +and load them into the kernel. The expected behavior is configured via the +appropriate ExecStartPost directive setting in the +/usr/lib/systemd/system/auditd.service configuration file. +To instruct the auditd daemon to use the augenrules program +to read audit rules (default configuration), use the following setting: + ExecStartPost=-/sbin/augenrules --load +in the /usr/lib/systemd/system/auditd.service configuration file. +In order to instruct the auditd daemon to use the auditctl +utility to read audit rules, use the following setting: + ExecStartPost=-/sbin/auditctl -R /etc/audit/audit.rules +in the /usr/lib/systemd/system/auditd.service configuration file. +Refer to [Service] section of the /usr/lib/systemd/system/auditd.service +configuration file for further details. + +Government networks often have substantial auditing +requirements and auditd can be configured to meet these +requirements. +Examining some example audit records demonstrates how the Linux audit system +satisfies common requirements. +The following example from Red Hat Enterprise Linux 7 Documentation available at +https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/7/html-single/selinux_users_and_administrators_guide/index#sect-Security-Enhanced_Linux-Fixing_Problems-Raw_Audit_Messages +shows the substantial amount of information captured in a +two typical "raw" audit messages, followed by a breakdown of the most important +fields. In this example the message is SELinux-related and reports an AVC +denial (and the associated system call) that occurred when the Apache HTTP +Server attempted to access the /var/www/html/file1 file (labeled with +the samba_share_t type): +type=AVC msg=audit(1226874073.147:96): avc: denied { getattr } for pid=2465 comm="httpd" +path="/var/www/html/file1" dev=dm-0 ino=284133 scontext=unconfined_u:system_r:httpd_t:s0 +tcontext=unconfined_u:object_r:samba_share_t:s0 tclass=file + +type=SYSCALL msg=audit(1226874073.147:96): arch=40000003 syscall=196 success=no exit=-13 +a0=b98df198 a1=bfec85dc a2=54dff4 a3=2008171 items=0 ppid=2463 pid=2465 auid=502 uid=48 +gid=48 euid=48 suid=48 fsuid=48 egid=48 sgid=48 fsgid=48 tty=(none) ses=6 comm="httpd" +exe="/usr/sbin/httpd" subj=unconfined_u:system_r:httpd_t:s0 key=(null) + +msg=audit(1226874073.147:96)The number in parentheses is the unformatted time stamp (Epoch time) +for the event, which can be converted to standard time by using the +date command. +{ getattr }The item in braces indicates the permission that was denied. getattr +indicates the source process was trying to read the target file's status information. +This occurs before reading files. This action is denied due to the file being +accessed having the wrong label. Commonly seen permissions include getattr, +read, and write.comm="httpd"The executable that launched the process. The full path of the executable is +found in the exe= section of the system call (SYSCALL) message, +which in this case, is exe="/usr/sbin/httpd". +path="/var/www/html/file1"The path to the object (target) the process attempted to access. +scontext="unconfined_u:system_r:httpd_t:s0"The SELinux context of the process that attempted the denied action. In +this case, it is the SELinux context of the Apache HTTP Server, which is running +in the httpd_t domain. +tcontext="unconfined_u:object_r:samba_share_t:s0"The SELinux context of the object (target) the process attempted to access. +In this case, it is the SELinux context of file1. Note: the samba_share_t +type is not accessible to processes running in the httpd_t domain. From the system call (SYSCALL) message, two items are of interest: +success=no: indicates whether the denial (AVC) was enforced or not. +success=no indicates the system call was not successful (SELinux denied +access). success=yes indicates the system call was successful - this can +be seen for permissive domains or unconfined domains, such as initrc_t +and kernel_t. +exe="/usr/sbin/httpd": the full path to the executable that launched +the process, which in this case, is exe="/usr/sbin/httpd". + + + + + Install audispd-plugins Package + The audispd-plugins package can be installed with the following command: + +$ sudo dnf install audispd-plugins + FMT_SMF_EXT.1 + SRG-OS-000342-GPOS-00133 + audispd-plugins provides plugins for the real-time interface to the +audit subsystem, audispd. These plugins can do things like relay events +to remote machines or analyze events for suspicious behavior. + CCE-83648-6 + +package --add=audispd-plugins + + include install_audispd-plugins + +class install_audispd-plugins { + package { 'audispd-plugins': + ensure => 'installed', + } +} + + - name: Ensure audispd-plugins is installed + package: + name: audispd-plugins + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83648-6 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_audispd-plugins_installed + + +[[packages]] +name = "audispd-plugins" +version = "*" + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if ! rpm -q --quiet "audispd-plugins" ; then + dnf install -y "audispd-plugins" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure the default plugins for the audit dispatcher are Installed + The audit-audispd-plugins package should be installed. + CCI-001851 + SRG-OS-000342-GPOS-00133 + Information stored in one location is vulnerable to accidental or incidental deletion or alteration. Off-loading is a common process in information systems with limited audit storage capacity. + CCE-89457-6 + +package --add=audit-audispd-plugins + + include install_audit-audispd-plugins + +class install_audit-audispd-plugins { + package { 'audit-audispd-plugins': + ensure => 'installed', + } +} + + - name: Ensure audit-audispd-plugins is installed + package: + name: audit-audispd-plugins + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-89457-6 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_audit-audispd-plugins_installed + + +[[packages]] +name = "audit-audispd-plugins" +version = "*" + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if ! rpm -q --quiet "audit-audispd-plugins" ; then + dnf install -y "audit-audispd-plugins" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure the audit Subsystem is Installed + The audit package should be installed. + BP28(R50) + CCI-000130 + CCI-000131 + CCI-000132 + CCI-000133 + CCI-000134 + CCI-000135 + CCI-000154 + CCI-000158 + CCI-000172 + CCI-001464 + CCI-001487 + CCI-001814 + CCI-001875 + CCI-001876 + CCI-001877 + CCI-001878 + CCI-001879 + CCI-001880 + CCI-001881 + CCI-001882 + CCI-001889 + CCI-001914 + CCI-002884 + CCI-000169 + CIP-004-6 R3.3 + CIP-007-3 R6.5 + AC-7(a) + AU-7(1) + AU-7(2) + AU-14 + AU-12(2) + AU-2(a) + CM-6(a) + FAU_GEN.1 + SRG-OS-000062-GPOS-00031 + SRG-OS-000037-GPOS-00015 + SRG-OS-000038-GPOS-00016 + SRG-OS-000039-GPOS-00017 + SRG-OS-000040-GPOS-00018 + SRG-OS-000041-GPOS-00019 + SRG-OS-000042-GPOS-00021 + SRG-OS-000051-GPOS-00024 + SRG-OS-000054-GPOS-00025 + SRG-OS-000122-GPOS-00063 + SRG-OS-000254-GPOS-00095 + SRG-OS-000255-GPOS-00096 + SRG-OS-000337-GPOS-00129 + SRG-OS-000348-GPOS-00136 + SRG-OS-000349-GPOS-00137 + SRG-OS-000350-GPOS-00138 + SRG-OS-000351-GPOS-00139 + SRG-OS-000352-GPOS-00140 + SRG-OS-000353-GPOS-00141 + SRG-OS-000354-GPOS-00142 + SRG-OS-000358-GPOS-00145 + SRG-OS-000365-GPOS-00152 + SRG-OS-000392-GPOS-00172 + SRG-OS-000475-GPOS-00220 + The auditd service is an access monitoring and accounting daemon, watching system calls to audit any access, in comparison with potential local access control policy such as SELinux policy. + CCE-83649-4 + +package --add=audit + + include install_audit + +class install_audit { + package { 'audit': + ensure => 'installed', + } +} + + - name: Ensure audit is installed + package: + name: audit + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83649-4 + - NIST-800-53-AC-7(a) + - NIST-800-53-AU-12(2) + - NIST-800-53-AU-14 + - NIST-800-53-AU-2(a) + - NIST-800-53-AU-7(1) + - NIST-800-53-AU-7(2) + - NIST-800-53-CM-6(a) + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_audit_installed + + +[[packages]] +name = "audit" +version = "*" + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if ! rpm -q --quiet "audit" ; then + dnf install -y "audit" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Enable auditd Service + The auditd service is an essential userspace component of +the Linux Auditing System, as it is responsible for writing audit records to +disk. + +The auditd service can be enabled with the following command: +$ sudo systemctl enable auditd.service + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 5.4.1.1 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.3.1 + 3.3.2 + 3.3.6 + CCI-000126 + CCI-000130 + CCI-000131 + CCI-000132 + CCI-000133 + CCI-000134 + CCI-000135 + CCI-000154 + CCI-000158 + CCI-000172 + CCI-000366 + CCI-001464 + CCI-001487 + CCI-001814 + CCI-001875 + CCI-001876 + CCI-001877 + CCI-002884 + CCI-001878 + CCI-001879 + CCI-001880 + CCI-001881 + CCI-001882 + CCI-001889 + CCI-001914 + CCI-000169 + 164.308(a)(1)(ii)(D) + 164.308(a)(5)(ii)(C) + 164.310(a)(2)(iv) + 164.310(d)(2)(iii) + 164.312(b) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + CIP-004-6 R3.3 + CIP-007-3 R6.5 + AC-2(g) + AU-3 + AU-10 + AU-2(d) + AU-12(c) + AU-14(1) + AC-6(9) + CM-6(a) + SI-4(23) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1 + Req-10.1 + SRG-OS-000062-GPOS-00031 + SRG-OS-000037-GPOS-00015 + SRG-OS-000038-GPOS-00016 + SRG-OS-000039-GPOS-00017 + SRG-OS-000040-GPOS-00018 + SRG-OS-000041-GPOS-00019 + SRG-OS-000042-GPOS-00021 + SRG-OS-000051-GPOS-00024 + SRG-OS-000054-GPOS-00025 + SRG-OS-000122-GPOS-00063 + SRG-OS-000254-GPOS-00095 + SRG-OS-000255-GPOS-00096 + SRG-OS-000337-GPOS-00129 + SRG-OS-000348-GPOS-00136 + SRG-OS-000349-GPOS-00137 + SRG-OS-000350-GPOS-00138 + SRG-OS-000351-GPOS-00139 + SRG-OS-000352-GPOS-00140 + SRG-OS-000353-GPOS-00141 + SRG-OS-000354-GPOS-00142 + SRG-OS-000358-GPOS-00145 + SRG-OS-000365-GPOS-00152 + SRG-OS-000392-GPOS-00172 + SRG-OS-000475-GPOS-00220 + SRG-OS-000037-VMM-000150 + SRG-OS-000063-VMM-000310 + SRG-OS-000038-VMM-000160 + SRG-OS-000039-VMM-000170 + SRG-OS-000040-VMM-000180 + SRG-OS-000041-VMM-000190 + Without establishing what type of events occurred, it would be difficult +to establish, correlate, and investigate the events leading up to an outage or attack. +Ensuring the auditd service is active ensures audit records +generated by the kernel are appropriately recorded. + +Additionally, a properly configured audit subsystem ensures that actions of +individual system users can be uniquely traced to those users so they +can be held accountable for their actions. + + + CCE-90829-3 + include enable_auditd + +class enable_auditd { + service {'auditd': + enable => true, + ensure => 'running', + } +} + + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-90829-3 + - CJIS-5.4.1.1 + - NIST-800-171-3.3.1 + - NIST-800-171-3.3.2 + - NIST-800-171-3.3.6 + - NIST-800-53-AC-2(g) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-10 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-14(1) + - NIST-800-53-AU-2(d) + - NIST-800-53-AU-3 + - NIST-800-53-CM-6(a) + - NIST-800-53-SI-4(23) + - PCI-DSS-Req-10.1 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_auditd_enabled + +- name: Enable service auditd + block: + + - name: Gather the package facts + package_facts: + manager: auto + + - name: Enable service auditd + service: + name: auditd + enabled: 'yes' + state: started + masked: 'no' + when: + - '"audit" in ansible_facts.packages' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"audit" in ansible_facts.packages' + tags: + - CCE-90829-3 + - CJIS-5.4.1.1 + - NIST-800-171-3.3.1 + - NIST-800-171-3.3.2 + - NIST-800-171-3.3.6 + - NIST-800-53-AC-2(g) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-10 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-14(1) + - NIST-800-53-AU-2(d) + - NIST-800-53-AU-3 + - NIST-800-53-CM-6(a) + - NIST-800-53-SI-4(23) + - PCI-DSS-Req-10.1 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_auditd_enabled + + +[customizations.services] +enabled = ["auditd"] + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + systemd: + units: + - name: auditd.service + enabled: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && { rpm --quiet -q audit; }; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" unmask 'auditd.service' +"$SYSTEMCTL_EXEC" start 'auditd.service' +"$SYSTEMCTL_EXEC" enable 'auditd.service' + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Enable Auditing for Processes Which Start Prior to the Audit Daemon + To ensure all processes can be audited, even those which start +prior to the audit daemon, add the argument audit=1 to the default +GRUB 2 command line for the Linux operating system. +To ensure that audit=1 is added as a kernel command line +argument to newly installed kernels, add audit=1 to the +default Grub2 command line for Linux operating systems. Modify the line within +/etc/default/grub as shown below: +GRUB_CMDLINE_LINUX="... audit=1 ..." +Run the following command to update command line for already installed kernels:# grubby --update-kernel=ALL --args="audit=1" + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 3 + 4 + 5 + 6 + 7 + 8 + 5.4.1.1 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.3.1 + CCI-001464 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(5)(ii)(C) + 164.310(a)(2)(iv) + 164.310(d)(2)(iii) + 164.312(b) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AC-17(1) + AU-14(1) + AU-10 + CM-6(a) + IR-5(1) + DE.AE-3 + DE.AE-5 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1 + Req-10.3 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + SRG-OS-000473-GPOS-00218 + SRG-OS-000254-GPOS-00095 + SRG-OS-000254-VMM-000880 + Each process on the system carries an "auditable" flag which indicates whether +its activities can be audited. Although auditd takes care of enabling +this for all processes which launch after it does, adding the kernel argument +ensures it is set for every process during boot. + + CCE-83651-0 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83651-0 + - CJIS-5.4.1.1 + - NIST-800-171-3.3.1 + - NIST-800-53-AC-17(1) + - NIST-800-53-AU-10 + - NIST-800-53-AU-14(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-IR-5(1) + - PCI-DSS-Req-10.3 + - grub2_audit_argument + - low_disruption + - medium_complexity + - medium_severity + - reboot_required + - restrict_strategy + +- name: Update grub defaults and the bootloader menu + command: /sbin/grubby --update-kernel=ALL --args="audit=1" + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"grub2-common" in ansible_facts.packages' + tags: + - CCE-83651-0 + - CJIS-5.4.1.1 + - NIST-800-171-3.3.1 + - NIST-800-53-AC-17(1) + - NIST-800-53-AU-10 + - NIST-800-53-AU-14(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-IR-5(1) + - PCI-DSS-Req-10.3 + - grub2_audit_argument + - low_disruption + - medium_complexity + - medium_severity + - reboot_required + - restrict_strategy + + [customizations.kernel] +append = "audit=1" + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && { rpm --quiet -q grub2-common; }; then + +grubby --update-kernel=ALL --args=audit=1 + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Extend Audit Backlog Limit for the Audit Daemon + To improve the kernel capacity to queue all log events, even those which occurred +prior to the audit daemon, add the argument audit_backlog_limit=8192 to the default +GRUB 2 command line for the Linux operating system. +To ensure that audit_backlog_limit=8192 is added as a kernel command line +argument to newly installed kernels, add audit_backlog_limit=8192 to the +default Grub2 command line for Linux operating systems. Modify the line within +/etc/default/grub as shown below: +GRUB_CMDLINE_LINUX="... audit_backlog_limit=8192 ..." +Run the following command to update command line for already installed kernels:# grubby --update-kernel=ALL --args="audit_backlog_limit=8192" + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-001849 + CCI-002884 + CM-6(a) + FAU_STG.1 + FAU_STG.3 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000254-GPOS-00095 + SRG-OS-000341-GPOS-00132 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + audit_backlog_limit sets the queue length for audit events awaiting transfer +to the audit daemon. Until the audit daemon is up and running, all log messages +are stored in this queue. If the queue is overrun during boot process, the action +defined by audit failure flag is taken. + + CCE-83652-8 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83652-8 + - NIST-800-53-CM-6(a) + - grub2_audit_backlog_limit_argument + - low_disruption + - low_severity + - medium_complexity + - reboot_required + - restrict_strategy + +- name: Update grub defaults and the bootloader menu + command: /sbin/grubby --update-kernel=ALL --args="audit_backlog_limit=8192" + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"grub2-common" in ansible_facts.packages' + tags: + - CCE-83652-8 + - NIST-800-53-CM-6(a) + - grub2_audit_backlog_limit_argument + - low_disruption + - low_severity + - medium_complexity + - reboot_required + - restrict_strategy + + [customizations.kernel] +append = "audit_backlog_limit=8192" + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && { rpm --quiet -q grub2-common; }; then + +grubby --update-kernel=ALL --args=audit_backlog_limit=8192 + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure auditd Rules for Comprehensive Auditing + The auditd program can perform comprehensive +monitoring of system activity. This section describes recommended +configuration settings for comprehensive auditing, but a full +description of the auditing system's capabilities is beyond the +scope of this guide. The mailing list linux-audit@redhat.com exists +to facilitate community discussion of the auditing system. + +The audit subsystem supports extensive collection of events, including: + +Tracing of arbitrary system calls (identified by name or number) +on entry or exit.Filtering by PID, UID, call success, system call argument (with +some limitations), etc.Monitoring of specific files for modifications to the file's +contents or metadata. + +Auditing rules at startup are controlled by the file /etc/audit/audit.rules. +Add rules to it to meet the auditing requirements for your organization. +Each line in /etc/audit/audit.rules represents a series of arguments +that can be passed to auditctl and can be individually tested +during runtime. See documentation in /usr/share/doc/audit-VERSION and +in the related man pages for more details. + +If copying any example audit rulesets from /usr/share/doc/audit-VERSION, +be sure to comment out the +lines containing arch= which are not appropriate for your system's +architecture. Then review and understand the following rules, +ensuring rules are activated as needed for the appropriate +architecture. + +After reviewing all the rules, reading the following sections, and +editing as needed, the new rules can be activated as follows: +$ sudo service auditd restart + + + Record Events that Modify User/Group Information via open syscall - /etc/group + The audit system should collect write events to /etc/group file for all users and root. +If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: +-a always,exit -F arch=b32 -S open -F a1&03 -F path=/etc/group -F auid>=1000 -F auid!=unset -F key=modify +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S open -F a1&03 -F path=/etc/group -F auid>=1000 -F auid!=unset -F key=modify +If the system is 64 bit then also add the following line: +-a always,exit -F arch=b64 -S open -F a1&03 -F path=/etc/group -F auid>=1000 -F auid!=unset -F key=modify + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping system calls related +to the same event is more efficient. See the following example: +-a always,exit -F arch=b32 -S open -F a1&03 -F path=/etc/group -F auid>=1000 -F auid!=unset -F key=modify + CIP-004-6 R2.2.2 + CIP-004-6 R2.2.3 + CIP-007-3 R.1.3 + CIP-007-3 R5 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.3 + CIP-007-3 R5.2.1 + CIP-007-3 R5.2.3 + AC-2(4) + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + FAU_GEN.1.1.c + Creation of groups through direct edition of /etc/group could be an indicator of malicious activity on a system. +Auditing these events could serve as evidence of potential system compromise. + - name: Gather the package facts + package_facts: + manager: auto + tags: + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_group_open + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit open tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_group_open + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for open for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - open + syscall_grouping: [] + + - name: Check existence of open in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a1&03 -F path=/etc/group -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/modify.rules + set_fact: audit_file="/etc/audit/rules.d/modify.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F a1&03 -F path=/etc/group -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F a1&03 -F path=/etc/group + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - open + syscall_grouping: [] + + - name: Check existence of open in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a1&03 -F path=/etc/group -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F a1&03 -F path=/etc/group -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F a1&03 -F path=/etc/group + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_group_open + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for open for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - open + syscall_grouping: [] + + - name: Check existence of open in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a1&03 -F path=/etc/group -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/modify.rules + set_fact: audit_file="/etc/audit/rules.d/modify.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F a1&03 -F path=/etc/group -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F a1&03 -F path=/etc/group + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - open + syscall_grouping: [] + + - name: Check existence of open in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a1&03 -F path=/etc/group -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F a1&03 -F path=/etc/group -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F a1&03 -F path=/etc/group + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_group_open + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F a1&03 -F path=/etc/group" + AUID_FILTERS="-F auid>=1000 -F auid!=unset" + SYSCALL="open" + KEY="user-modify" + SYSCALL_GROUPING="" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Events that Modify User/Group Information via open_by_handle_at syscall - /etc/group + The audit system should collect write events to /etc/group file for all group and root. +If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: +-a always,exit -F arch=b32 -S open_by_handle_at -F a2&03 -F path=/etc/group -F auid>=1000 -F auid!=unset -F key=modify +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S open_by_handle_at -F a2&03 -F path=/etc/group -F auid>=1000 -F auid!=unset -F key=modify +If the system is 64 bit then also add the following line: +-a always,exit -F arch=b64 -S open_by_handle_at -F a2&03 -F path=/etc/group -F auid>=1000 -F auid!=unset -F key=modify + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping system calls related +to the same event is more efficient. See the following example: +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&03 -F path=/etc/group -F auid>=1000 -F auid!=unset -F key=modify + CIP-004-6 R2.2.2 + CIP-004-6 R2.2.3 + CIP-007-3 R.1.3 + CIP-007-3 R5 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.3 + CIP-007-3 R5.2.1 + CIP-007-3 R5.2.3 + AC-2(4) + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + FAU_GEN.1.1.c + Creation of groups through direct edition of /etc/group could be an indicator of malicious activity on a system. +Auditing these events could serve as evidence of potential system compromise. + - name: Gather the package facts + package_facts: + manager: auto + tags: + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_group_open_by_handle_at + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit open_by_handle_at tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_group_open_by_handle_at + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for open_by_handle_at for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - open_by_handle_at + syscall_grouping: [] + + - name: Check existence of open_by_handle_at in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a2&03 -F path=/etc/group -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/modify.rules + set_fact: audit_file="/etc/audit/rules.d/modify.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F a2&03 -F path=/etc/group -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F a2&03 -F path=/etc/group + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - open_by_handle_at + syscall_grouping: [] + + - name: Check existence of open_by_handle_at in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a2&03 -F path=/etc/group -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F a2&03 -F path=/etc/group -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F a2&03 -F path=/etc/group + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_group_open_by_handle_at + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for open_by_handle_at for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - open_by_handle_at + syscall_grouping: [] + + - name: Check existence of open_by_handle_at in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a2&03 -F path=/etc/group -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/modify.rules + set_fact: audit_file="/etc/audit/rules.d/modify.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F a2&03 -F path=/etc/group -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F a2&03 -F path=/etc/group + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - open_by_handle_at + syscall_grouping: [] + + - name: Check existence of open_by_handle_at in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a2&03 -F path=/etc/group -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F a2&03 -F path=/etc/group -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F a2&03 -F path=/etc/group + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_group_open_by_handle_at + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F a2&03 -F path=/etc/group" + AUID_FILTERS="-F auid>=1000 -F auid!=unset" + SYSCALL="open_by_handle_at" + KEY="user-modify" + SYSCALL_GROUPING="" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Events that Modify User/Group Information via openat syscall - /etc/group + The audit system should collect write events to /etc/group file for all users and root. +If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: +-a always,exit -F arch=b32 -S openat -F a2&03 -F path=/etc/group -F auid>=1000 -F auid!=unset -F key=modify +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S openat -F a2&03 -F path=/etc/group -F auid>=1000 -F auid!=unset -F key=modify +If the system is 64 bit then also add the following line: +-a always,exit -F arch=b64 -S openat -F a2&03 -F path=/etc/group -F auid>=1000 -F auid!=unset -F key=modify + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping system calls related +to the same event is more efficient. See the following example: +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&03 -F path=/etc/group -F auid>=1000 -F auid!=unset -F key=modify + CIP-004-6 R2.2.2 + CIP-004-6 R2.2.3 + CIP-007-3 R.1.3 + CIP-007-3 R5 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.3 + CIP-007-3 R5.2.1 + CIP-007-3 R5.2.3 + AC-2(4) + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + FAU_GEN.1.1.c + Creation of groups through direct edition of /etc/group could be an indicator of malicious activity on a system. +Auditing these events could serve as evidence of potential system compromise. + - name: Gather the package facts + package_facts: + manager: auto + tags: + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_group_openat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit openat tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_group_openat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for openat for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - openat + syscall_grouping: [] + + - name: Check existence of openat in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a2&03 -F path=/etc/group -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/modify.rules + set_fact: audit_file="/etc/audit/rules.d/modify.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F a2&03 -F path=/etc/group -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F a2&03 -F path=/etc/group + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - openat + syscall_grouping: [] + + - name: Check existence of openat in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a2&03 -F path=/etc/group -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F a2&03 -F path=/etc/group -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F a2&03 -F path=/etc/group + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_group_openat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for openat for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - openat + syscall_grouping: [] + + - name: Check existence of openat in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a2&03 -F path=/etc/group -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/modify.rules + set_fact: audit_file="/etc/audit/rules.d/modify.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F a2&03 -F path=/etc/group -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F a2&03 -F path=/etc/group + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - openat + syscall_grouping: [] + + - name: Check existence of openat in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a2&03 -F path=/etc/group -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F a2&03 -F path=/etc/group -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F a2&03 -F path=/etc/group + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_group_openat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F a2&03 -F path=/etc/group" + AUID_FILTERS="-F auid>=1000 -F auid!=unset" + SYSCALL="openat" + KEY="user-modify" + SYSCALL_GROUPING="" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Events that Modify User/Group Information via open syscall - /etc/gshadow + The audit system should collect write events to /etc/gshadow file for all users and root. +If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: +-a always,exit -F arch=b32 -S open -F a1&03 -F path=/etc/gshadow -F auid>=1000 -F auid!=unset -F key=user-modify +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S open -F a1&03 -F path=/etc/gshadow -F auid>=1000 -F auid!=unset -F key=user-modify +If the system is 64 bit then also add the following line: +-a always,exit -F arch=b64 -S open -F a1&03 -F path=/etc/gshadow -F auid>=1000 -F auid!=unset -F key=user-modify + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping system calls related +to the same event is more efficient. See the following example: +-a always,exit -F arch=b32 -S open -F a1&03 -F path=/etc/gshadow -F auid>=1000 -F auid!=unset -F key=user-modify + CIP-004-6 R2.2.2 + CIP-004-6 R2.2.3 + CIP-007-3 R.1.3 + CIP-007-3 R5 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.3 + CIP-007-3 R5.2.1 + CIP-007-3 R5.2.3 + AC-2(4) + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + FAU_GEN.1.1.c + Creation of users through direct edition of /etc/gshadow could be an indicator of malicious activity on a system. +Auditing these events could serve as evidence of potential system compromise. + - name: Gather the package facts + package_facts: + manager: auto + tags: + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_gshadow_open + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit open tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_gshadow_open + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for open for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - open + syscall_grouping: [] + + - name: Check existence of open in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a1&03 -F path=/etc/gshadow -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/modify.rules + set_fact: audit_file="/etc/audit/rules.d/modify.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F a1&03 -F path=/etc/gshadow -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F a1&03 -F path=/etc/gshadow + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - open + syscall_grouping: [] + + - name: Check existence of open in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a1&03 -F path=/etc/gshadow -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F a1&03 -F path=/etc/gshadow -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F a1&03 -F path=/etc/gshadow + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_gshadow_open + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for open for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - open + syscall_grouping: [] + + - name: Check existence of open in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a1&03 -F path=/etc/gshadow -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/modify.rules + set_fact: audit_file="/etc/audit/rules.d/modify.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F a1&03 -F path=/etc/gshadow -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F a1&03 -F path=/etc/gshadow + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - open + syscall_grouping: [] + + - name: Check existence of open in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a1&03 -F path=/etc/gshadow -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F a1&03 -F path=/etc/gshadow -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F a1&03 -F path=/etc/gshadow + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_gshadow_open + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F a1&03 -F path=/etc/gshadow" + AUID_FILTERS="-F auid>=1000 -F auid!=unset" + SYSCALL="open" + KEY="user-modify" + SYSCALL_GROUPING="" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Events that Modify User/Group Information via open_by_handle_at syscall - /etc/gshadow + The audit system should collect write events to /etc/gshadow file for all users and root. +If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: +-a always,exit -F arch=b32 -S open_by_handle_at -F a2&03 -F path=/etc/gshadow -F auid>=1000 -F auid!=unset -F key=user-modify +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S open_by_handle_at -F a2&03 -F path=/etc/gshadow -F auid>=1000 -F auid!=unset -F key=user-modify +If the system is 64 bit then also add the following line: +-a always,exit -F arch=b64 -S open_by_handle_at -F a2&03 -F path=/etc/gshadow -F auid>=1000 -F auid!=unset -F key=user-modify + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping system calls related +to the same event is more efficient. See the following example: +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&03 -F path=/etc/gshadow -F auid>=1000 -F auid!=unset -F key=user-modify + CIP-004-6 R2.2.2 + CIP-004-6 R2.2.3 + CIP-007-3 R.1.3 + CIP-007-3 R5 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.3 + CIP-007-3 R5.2.1 + CIP-007-3 R5.2.3 + AC-2(4) + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + FAU_GEN.1.1.c + Creation of users through direct edition of /etc/gshadow could be an indicator of malicious activity on a system. +Auditing these events could serve as evidence of potential system compromise. + - name: Gather the package facts + package_facts: + manager: auto + tags: + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_gshadow_open_by_handle_at + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit open_by_handle_at tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_gshadow_open_by_handle_at + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for open_by_handle_at for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - open_by_handle_at + syscall_grouping: [] + + - name: Check existence of open_by_handle_at in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a2&03 -F path=/etc/gshadow -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/modify.rules + set_fact: audit_file="/etc/audit/rules.d/modify.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F a2&03 -F path=/etc/gshadow -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F a2&03 -F path=/etc/gshadow + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - open_by_handle_at + syscall_grouping: [] + + - name: Check existence of open_by_handle_at in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a2&03 -F path=/etc/gshadow -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F a2&03 -F path=/etc/gshadow -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F a2&03 -F path=/etc/gshadow + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_gshadow_open_by_handle_at + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for open_by_handle_at for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - open_by_handle_at + syscall_grouping: [] + + - name: Check existence of open_by_handle_at in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a2&03 -F path=/etc/gshadow -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/modify.rules + set_fact: audit_file="/etc/audit/rules.d/modify.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F a2&03 -F path=/etc/gshadow -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F a2&03 -F path=/etc/gshadow + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - open_by_handle_at + syscall_grouping: [] + + - name: Check existence of open_by_handle_at in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a2&03 -F path=/etc/gshadow -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F a2&03 -F path=/etc/gshadow -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F a2&03 -F path=/etc/gshadow + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_gshadow_open_by_handle_at + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F a2&03 -F path=/etc/gshadow" + AUID_FILTERS="-F auid>=1000 -F auid!=unset" + SYSCALL="open_by_handle_at" + KEY="user-modify" + SYSCALL_GROUPING="" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Events that Modify User/Group Information via openat syscall - /etc/gshadow + The audit system should collect write events to /etc/gshadow file for all users and root. +If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: +-a always,exit -F arch=b32 -S openat -F a2&03 -F path=/etc/gshadow -F auid>=1000 -F auid!=unset -F key=user-modify +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S openat -F a2&03 -F path=/etc/gshadow -F auid>=1000 -F auid!=unset -F key=user-modify +If the system is 64 bit then also add the following line: +-a always,exit -F arch=b64 -S openat -F a2&03 -F path=/etc/gshadow -F auid>=1000 -F auid!=unset -F key=user-modify + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping system calls related +to the same event is more efficient. See the following example: +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&03 -F path=/etc/gshadow -F auid>=1000 -F auid!=unset -F key=user-modify + CIP-004-6 R2.2.2 + CIP-004-6 R2.2.3 + CIP-007-3 R.1.3 + CIP-007-3 R5 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.3 + CIP-007-3 R5.2.1 + CIP-007-3 R5.2.3 + AC-2(4) + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + FAU_GEN.1.1.c + Creation of users through direct edition of /etc/gshadow could be an indicator of malicious activity on a system. +Auditing these events could serve as evidence of potential system compromise. + - name: Gather the package facts + package_facts: + manager: auto + tags: + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_gshadow_openat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit openat tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_gshadow_openat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for openat for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - openat + syscall_grouping: [] + + - name: Check existence of openat in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a2&03 -F path=/etc/gshadow -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/modify.rules + set_fact: audit_file="/etc/audit/rules.d/modify.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F a2&03 -F path=/etc/gshadow -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F a2&03 -F path=/etc/gshadow + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - openat + syscall_grouping: [] + + - name: Check existence of openat in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a2&03 -F path=/etc/gshadow -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F a2&03 -F path=/etc/gshadow -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F a2&03 -F path=/etc/gshadow + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_gshadow_openat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for openat for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - openat + syscall_grouping: [] + + - name: Check existence of openat in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a2&03 -F path=/etc/gshadow -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/modify.rules + set_fact: audit_file="/etc/audit/rules.d/modify.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F a2&03 -F path=/etc/gshadow -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F a2&03 -F path=/etc/gshadow + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - openat + syscall_grouping: [] + + - name: Check existence of openat in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a2&03 -F path=/etc/gshadow -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F a2&03 -F path=/etc/gshadow -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F a2&03 -F path=/etc/gshadow + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_gshadow_openat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F a2&03 -F path=/etc/gshadow" + AUID_FILTERS="-F auid>=1000 -F auid!=unset" + SYSCALL="openat" + KEY="user-modify" + SYSCALL_GROUPING="" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Events that Modify User/Group Information via open syscall - /etc/passwd + The audit system should collect write events to /etc/passwd file for all users and root. +If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: +-a always,exit -F arch=b32 -S open -F a1&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=modify +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S open -F a1&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=modify +If the system is 64 bit then also add the following line: +-a always,exit -F arch=b64 -S open -F a1&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=modify + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping system calls related +to the same event is more efficient. See the following example: +-a always,exit -F arch=b32 -S open -F a1&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=modify + CIP-004-6 R2.2.2 + CIP-004-6 R2.2.3 + CIP-007-3 R.1.3 + CIP-007-3 R5 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.3 + CIP-007-3 R5.2.1 + CIP-007-3 R5.2.3 + AC-2(4) + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + FAU_GEN.1.1.c + Creation of users through direct edition of /etc/passwd could be an indicator of malicious activity on a system. +Auditing these events could serve as evidence of potential system compromise. + - name: Gather the package facts + package_facts: + manager: auto + tags: + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_passwd_open + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit open tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_passwd_open + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for open for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - open + syscall_grouping: [] + + - name: Check existence of open in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a1&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/modify.rules + set_fact: audit_file="/etc/audit/rules.d/modify.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F a1&03 -F path=/etc/passwd -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F a1&03 -F path=/etc/passwd + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - open + syscall_grouping: [] + + - name: Check existence of open in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a1&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F a1&03 -F path=/etc/passwd -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F a1&03 -F path=/etc/passwd + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_passwd_open + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for open for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - open + syscall_grouping: [] + + - name: Check existence of open in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a1&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/modify.rules + set_fact: audit_file="/etc/audit/rules.d/modify.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F a1&03 -F path=/etc/passwd -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F a1&03 -F path=/etc/passwd + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - open + syscall_grouping: [] + + - name: Check existence of open in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a1&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F a1&03 -F path=/etc/passwd -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F a1&03 -F path=/etc/passwd + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_passwd_open + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F a1&03 -F path=/etc/passwd" + AUID_FILTERS="-F auid>=1000 -F auid!=unset" + SYSCALL="open" + KEY="user-modify" + SYSCALL_GROUPING="" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Events that Modify User/Group Information via open_by_handle_at syscall - /etc/passwd + The audit system should collect write events to /etc/passwd file for all users and root. +If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: +-a always,exit -F arch=b32 -S open_by_handle_at -F a2&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=modify +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S open_by_handle_at -F a2&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=modify +If the system is 64 bit then also add the following line: +-a always,exit -F arch=b64 -S open_by_handle_at -F a2&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=modify + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping system calls related +to the same event is more efficient. See the following example: +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=modify + CIP-004-6 R2.2.2 + CIP-004-6 R2.2.3 + CIP-007-3 R.1.3 + CIP-007-3 R5 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.3 + CIP-007-3 R5.2.1 + CIP-007-3 R5.2.3 + AC-2(4) + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + FAU_GEN.1.1.c + Creation of users through direct edition of /etc/passwd could be an indicator of malicious activity on a system. +Auditing these events could serve as evidence of potential system compromise. + - name: Gather the package facts + package_facts: + manager: auto + tags: + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_passwd_open_by_handle_at + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit open_by_handle_at tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_passwd_open_by_handle_at + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for open_by_handle_at for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - open_by_handle_at + syscall_grouping: [] + + - name: Check existence of open_by_handle_at in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a2&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/modify.rules + set_fact: audit_file="/etc/audit/rules.d/modify.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F a2&03 -F path=/etc/passwd -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F a2&03 -F path=/etc/passwd + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - open_by_handle_at + syscall_grouping: [] + + - name: Check existence of open_by_handle_at in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a2&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F a2&03 -F path=/etc/passwd -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F a2&03 -F path=/etc/passwd + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_passwd_open_by_handle_at + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for open_by_handle_at for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - open_by_handle_at + syscall_grouping: [] + + - name: Check existence of open_by_handle_at in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a2&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/modify.rules + set_fact: audit_file="/etc/audit/rules.d/modify.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F a2&03 -F path=/etc/passwd -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F a2&03 -F path=/etc/passwd + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - open_by_handle_at + syscall_grouping: [] + + - name: Check existence of open_by_handle_at in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a2&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F a2&03 -F path=/etc/passwd -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F a2&03 -F path=/etc/passwd + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_passwd_open_by_handle_at + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F a2&03 -F path=/etc/passwd" + AUID_FILTERS="-F auid>=1000 -F auid!=unset" + SYSCALL="open_by_handle_at" + KEY="user-modify" + SYSCALL_GROUPING="" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Events that Modify User/Group Information via openat syscall - /etc/passwd + The audit system should collect write events to /etc/passwd file for all users and root. +If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: +-a always,exit -F arch=b32 -S openat -F a2&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=modify +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S openat -F a2&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=modify +If the system is 64 bit then also add the following line: +-a always,exit -F arch=b64 -S openat -F a2&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=modify + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping system calls related +to the same event is more efficient. See the following example: +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=modify + CIP-004-6 R2.2.2 + CIP-004-6 R2.2.3 + CIP-007-3 R.1.3 + CIP-007-3 R5 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.3 + CIP-007-3 R5.2.1 + CIP-007-3 R5.2.3 + AC-2(4) + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + FAU_GEN.1.1.c + Creation of users through direct edition of /etc/passwd could be an indicator of malicious activity on a system. +Auditing these events could serve as evidence of potential system compromise. + - name: Gather the package facts + package_facts: + manager: auto + tags: + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_passwd_openat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit openat tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_passwd_openat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for openat for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - openat + syscall_grouping: [] + + - name: Check existence of openat in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a2&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/modify.rules + set_fact: audit_file="/etc/audit/rules.d/modify.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F a2&03 -F path=/etc/passwd -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F a2&03 -F path=/etc/passwd + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - openat + syscall_grouping: [] + + - name: Check existence of openat in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a2&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F a2&03 -F path=/etc/passwd -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F a2&03 -F path=/etc/passwd + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_passwd_openat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for openat for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - openat + syscall_grouping: [] + + - name: Check existence of openat in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a2&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/modify.rules + set_fact: audit_file="/etc/audit/rules.d/modify.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F a2&03 -F path=/etc/passwd -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F a2&03 -F path=/etc/passwd + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - openat + syscall_grouping: [] + + - name: Check existence of openat in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a2&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F a2&03 -F path=/etc/passwd -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F a2&03 -F path=/etc/passwd + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_passwd_openat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F a2&03 -F path=/etc/passwd" + AUID_FILTERS="-F auid>=1000 -F auid!=unset" + SYSCALL="openat" + KEY="user-modify" + SYSCALL_GROUPING="" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Events that Modify User/Group Information via open syscall - /etc/shadow + The audit system should collect write events to /etc/shadow file for all users and root. +If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: +-a always,exit -F arch=b32 -S open -F a1&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S open -F a1&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify +If the system is 64 bit then also add the following line: +-a always,exit -F arch=b64 -S open -F a1&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping system calls related +to the same event is more efficient. See the following example: +-a always,exit -F arch=b32 -S open,openat,open_by_handle_at -F a1&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify + CIP-004-6 R2.2.2 + CIP-004-6 R2.2.3 + CIP-007-3 R.1.3 + CIP-007-3 R5 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.3 + CIP-007-3 R5.2.1 + CIP-007-3 R5.2.3 + AC-2(4) + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + FAU_GEN.1.1.c + Creation of users through direct edition of /etc/shadow could be an indicator of malicious activity on a system. +Auditing these events could serve as evidence of potential system compromise. + - name: Gather the package facts + package_facts: + manager: auto + tags: + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_shadow_open + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit open tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_shadow_open + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for open for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - open + syscall_grouping: [] + + - name: Check existence of open in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a1&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/modify.rules + set_fact: audit_file="/etc/audit/rules.d/modify.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F a1&03 -F path=/etc/shadow -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F a1&03 -F path=/etc/shadow + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - open + syscall_grouping: [] + + - name: Check existence of open in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a1&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F a1&03 -F path=/etc/shadow -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F a1&03 -F path=/etc/shadow + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_shadow_open + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for open for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - open + syscall_grouping: [] + + - name: Check existence of open in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a1&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/modify.rules + set_fact: audit_file="/etc/audit/rules.d/modify.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F a1&03 -F path=/etc/shadow -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F a1&03 -F path=/etc/shadow + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - open + syscall_grouping: [] + + - name: Check existence of open in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a1&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F a1&03 -F path=/etc/shadow -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F a1&03 -F path=/etc/shadow + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_shadow_open + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F a1&03 -F path=/etc/shadow" + AUID_FILTERS="-F auid>=1000 -F auid!=unset" + SYSCALL="open" + KEY="user-modify" + SYSCALL_GROUPING="" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Events that Modify User/Group Information via open_by_handle_at syscall - /etc/shadow + The audit system should collect write events to /etc/shadow file for all users and root. +If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: +-a always,exit -F arch=b32 -S open_by_handle_at -F a2&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S open_by_handle_at -F a2&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify +If the system is 64 bit then also add the following line: +-a always,exit -F arch=b64 -S open_by_handle_at -F a2&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping system calls related +to the same event is more efficient. See the following example: +-a always,exit -F arch=b32 -S open,openat,open_by_handle_at -F a2&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify + CIP-004-6 R2.2.2 + CIP-004-6 R2.2.3 + CIP-007-3 R.1.3 + CIP-007-3 R5 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.3 + CIP-007-3 R5.2.1 + CIP-007-3 R5.2.3 + AC-2(4) + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + FAU_GEN.1.1.c + Creation of users through direct edition of /etc/shadow could be an indicator of malicious activity on a system. +Auditing these events could serve as evidence of potential system compromise. + - name: Gather the package facts + package_facts: + manager: auto + tags: + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_shadow_open_by_handle_at + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit open_by_handle_at tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_shadow_open_by_handle_at + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for open_by_handle_at for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - open_by_handle_at + syscall_grouping: [] + + - name: Check existence of open_by_handle_at in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a2&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/modify.rules + set_fact: audit_file="/etc/audit/rules.d/modify.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F a2&03 -F path=/etc/shadow -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F a2&03 -F path=/etc/shadow + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - open_by_handle_at + syscall_grouping: [] + + - name: Check existence of open_by_handle_at in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a2&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F a2&03 -F path=/etc/shadow -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F a2&03 -F path=/etc/shadow + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_shadow_open_by_handle_at + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for open_by_handle_at for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - open_by_handle_at + syscall_grouping: [] + + - name: Check existence of open_by_handle_at in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a2&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/modify.rules + set_fact: audit_file="/etc/audit/rules.d/modify.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F a2&03 -F path=/etc/shadow -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F a2&03 -F path=/etc/shadow + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - open_by_handle_at + syscall_grouping: [] + + - name: Check existence of open_by_handle_at in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a2&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F a2&03 -F path=/etc/shadow -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F a2&03 -F path=/etc/shadow + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_shadow_open_by_handle_at + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F a2&03 -F path=/etc/shadow" + AUID_FILTERS="-F auid>=1000 -F auid!=unset" + SYSCALL="open_by_handle_at" + KEY="user-modify" + SYSCALL_GROUPING="" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Events that Modify User/Group Information via openat syscall - /etc/shadow + The audit system should collect write events to /etc/shadow file for all users and root. +If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: +-a always,exit -F arch=b32 -S openat -F a2&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S openat -F a2&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify +If the system is 64 bit then also add the following line: +-a always,exit -F arch=b64 -S openat -F a2&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping system calls related +to the same event is more efficient. See the following example: +-a always,exit -F arch=b32 -S open,openat,open_by_handle_at -F a2&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify + CIP-004-6 R2.2.2 + CIP-004-6 R2.2.3 + CIP-007-3 R.1.3 + CIP-007-3 R5 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.3 + CIP-007-3 R5.2.1 + CIP-007-3 R5.2.3 + AC-2(4) + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + FAU_GEN.1.1.c + Creation of users through direct edition of /etc/shadow could be an indicator of malicious activity on a system. +Auditing these events could serve as evidence of potential system compromise. + - name: Gather the package facts + package_facts: + manager: auto + tags: + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_shadow_openat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit openat tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_shadow_openat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for openat for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - openat + syscall_grouping: [] + + - name: Check existence of openat in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a2&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/modify.rules + set_fact: audit_file="/etc/audit/rules.d/modify.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F a2&03 -F path=/etc/shadow -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F a2&03 -F path=/etc/shadow + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - openat + syscall_grouping: [] + + - name: Check existence of openat in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a2&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F a2&03 -F path=/etc/shadow -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F a2&03 -F path=/etc/shadow + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_shadow_openat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for openat for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - openat + syscall_grouping: [] + + - name: Check existence of openat in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a2&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/modify.rules + set_fact: audit_file="/etc/audit/rules.d/modify.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F a2&03 -F path=/etc/shadow -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F a2&03 -F path=/etc/shadow + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - openat + syscall_grouping: [] + + - name: Check existence of openat in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a2&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F a2&03 -F path=/etc/shadow -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F a2&03 -F path=/etc/shadow + -F auid>=1000 -F auid!=unset -F key=modify + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_etc_shadow_openat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F a2&03 -F path=/etc/shadow" + AUID_FILTERS="-F auid>=1000 -F auid!=unset" + SYSCALL="openat" + KEY="user-modify" + SYSCALL_GROUPING="" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Make the auditd Configuration Immutable + If the auditd daemon is configured to use the +augenrules program to read audit rules during daemon startup (the +default), add the following line to a file with suffix .rules in the +directory /etc/audit/rules.d in order to make the auditd configuration +immutable: +-e 2 +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following line to +/etc/audit/audit.rules file in order to make the auditd configuration +immutable: +-e 2 +With this setting, a reboot will be required to change any audit rules. + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 18 + 19 + 3 + 4 + 5 + 6 + 7 + 8 + 5.4.1.1 + APO01.06 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + BAI03.05 + BAI08.02 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS05.04 + DSS05.07 + DSS06.02 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.3.1 + 3.4.3 + CCI-000162 + CCI-000163 + CCI-000164 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.310(a)(2)(iv) + 164.312(d) + 164.310(d)(2)(iii) + 164.312(b) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.7.3 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.1 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 5.2 + SR 6.1 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + AC-6(9) + CM-6(a) + DE.AE-3 + DE.AE-5 + ID.SC-4 + PR.AC-4 + PR.DS-5 + PR.PT-1 + RS.AN-1 + RS.AN-4 + Req-10.5.2 + SRG-OS-000057-GPOS-00027 + SRG-OS-000058-GPOS-00028 + SRG-OS-000059-GPOS-00029 + Making the audit configuration immutable prevents accidental as +well as malicious modification of the audit rules, although it may be +problematic if legitimate changes are needed during system +operation. + CCE-83716-1 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83716-1 + - CJIS-5.4.1.1 + - NIST-800-171-3.3.1 + - NIST-800-171-3.4.3 + - NIST-800-53-AC-6(9) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.2 + - audit_rules_immutable + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Collect all files from /etc/audit/rules.d with .rules extension + find: + paths: /etc/audit/rules.d/ + patterns: '*.rules' + register: find_rules_d + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83716-1 + - CJIS-5.4.1.1 + - NIST-800-171-3.3.1 + - NIST-800-171-3.4.3 + - NIST-800-53-AC-6(9) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.2 + - audit_rules_immutable + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Remove the -e option from all Audit config files + lineinfile: + path: '{{ item }}' + regexp: ^\s*(?:-e)\s+.*$ + state: absent + loop: '{{ find_rules_d.files | map(attribute=''path'') | list + [''/etc/audit/audit.rules''] + }}' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83716-1 + - CJIS-5.4.1.1 + - NIST-800-171-3.3.1 + - NIST-800-171-3.4.3 + - NIST-800-53-AC-6(9) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.2 + - audit_rules_immutable + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Add Audit -e option into /etc/audit/rules.d/immutable.rules and /etc/audit/audit.rules + lineinfile: + path: '{{ item }}' + create: true + line: -e 2 + mode: o-rwx + loop: + - /etc/audit/audit.rules + - /etc/audit/rules.d/immutable.rules + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83716-1 + - CJIS-5.4.1.1 + - NIST-800-171-3.3.1 + - NIST-800-171-3.4.3 + - NIST-800-53-AC-6(9) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.2 + - audit_rules_immutable + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,-e%202%0A + mode: 0600 + path: /etc/audit/rules.d/90-immutable.rules + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# Traverse all of: +# +# /etc/audit/audit.rules, (for auditctl case) +# /etc/audit/rules.d/*.rules (for augenrules case) +# +# files to check if '-e .*' setting is present in that '*.rules' file already. +# If found, delete such occurrence since auditctl(8) manual page instructs the +# '-e 2' rule should be placed as the last rule in the configuration +find /etc/audit /etc/audit/rules.d -maxdepth 1 -type f -name '*.rules' -exec sed -i '/-e[[:space:]]\+.*/d' {} ';' + +# Append '-e 2' requirement at the end of both: +# * /etc/audit/audit.rules file (for auditctl case) +# * /etc/audit/rules.d/immutable.rules (for augenrules case) + +for AUDIT_FILE in "/etc/audit/audit.rules" "/etc/audit/rules.d/immutable.rules" +do + echo '' >> $AUDIT_FILE + echo '# Set the audit.rules configuration immutable per security requirements' >> $AUDIT_FILE + echo '# Reboot is required to change audit rules once this setting is applied' >> $AUDIT_FILE + echo '-e 2' >> $AUDIT_FILE + chmod o-rwx $AUDIT_FILE +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Events that Modify the System's Mandatory Access Controls + If the auditd daemon is configured to use the +augenrules program to read audit rules during daemon startup (the +default), add the following line to a file with suffix .rules in the +directory /etc/audit/rules.d: +-w /etc/selinux/ -p wa -k MAC-policy +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following line to +/etc/audit/audit.rules file: +-w /etc/selinux/ -p wa -k MAC-policy + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 5.4.1.1 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.8 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.5.5 + The system's rhisam access policy (SELinux) should not be +arbitrarily changed by anything other than administrator action. All changes to +MAC policy should be audited. + CCE-83721-1 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83721-1 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.8 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_mac_modification + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Check if watch rule for /etc/selinux/ already exists in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: ^\s*-w\s+/etc/selinux/\s+-p\s+wa(\s|$)+ + patterns: '*.rules' + register: find_existing_watch_rules_d + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83721-1 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.8 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_mac_modification + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Search /etc/audit/rules.d for other rules with specified key MAC-policy + find: + paths: /etc/audit/rules.d + contains: ^.*(?:-F key=|-k\s+)MAC-policy$ + patterns: '*.rules' + register: find_watch_key + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched + == 0 + tags: + - CCE-83721-1 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.8 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_mac_modification + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Use /etc/audit/rules.d/MAC-policy.rules as the recipient for the rule + set_fact: + all_files: + - /etc/audit/rules.d/MAC-policy.rules + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_watch_key.matched is defined and find_watch_key.matched == 0 and find_existing_watch_rules_d.matched + is defined and find_existing_watch_rules_d.matched == 0 + tags: + - CCE-83721-1 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.8 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_mac_modification + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Use matched file as the recipient for the rule + set_fact: + all_files: + - '{{ find_watch_key.files | map(attribute=''path'') | list | first }}' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_watch_key.matched is defined and find_watch_key.matched > 0 and find_existing_watch_rules_d.matched + is defined and find_existing_watch_rules_d.matched == 0 + tags: + - CCE-83721-1 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.8 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_mac_modification + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Add watch rule for /etc/selinux/ in /etc/audit/rules.d/ + lineinfile: + path: '{{ all_files[0] }}' + line: -w /etc/selinux/ -p wa -k MAC-policy + create: true + mode: '0640' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched + == 0 + tags: + - CCE-83721-1 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.8 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_mac_modification + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Check if watch rule for /etc/selinux/ already exists in /etc/audit/audit.rules + find: + paths: /etc/audit/ + contains: ^\s*-w\s+/etc/selinux/\s+-p\s+wa(\s|$)+ + patterns: audit.rules + register: find_existing_watch_audit_rules + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83721-1 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.8 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_mac_modification + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Add watch rule for /etc/selinux/ in /etc/audit/audit.rules + lineinfile: + line: -w /etc/selinux/ -p wa -k MAC-policy + state: present + dest: /etc/audit/audit.rules + create: true + mode: '0640' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_audit_rules.matched is defined and find_existing_watch_audit_rules.matched + == 0 + tags: + - CCE-83721-1 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.8 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_mac_modification + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + --- + +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,{{ -w%20/etc/selinux/%20-p%20wa%20-k%20MAC-policy%0A }} + mode: 0600 + path: /etc/audit/rules.d/75-etcselinux-wa-MAC-policy.rules + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + + +# If the audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# into the list of files to be inspected +files_to_inspect+=('/etc/audit/audit.rules') + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/etc/selinux/" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/selinux/ $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/etc/selinux/$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /etc/selinux/ -p wa -k MAC-policy" >> "$audit_rules_file" + fi +done +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + +# If the audit is 'augenrules', then check if rule is already defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection. +# If rule isn't defined, add '/etc/audit/rules.d/MAC-policy.rules' to list of files for inspection. +readarray -t matches < <(grep -HP "[\s]*-w[\s]+/etc/selinux/" /etc/audit/rules.d/*.rules) + +# For each of the matched entries +for match in "${matches[@]}" +do + # Extract filepath from the match + rulesd_audit_file=$(echo $match | cut -f1 -d ':') + # Append that path into list of files for inspection + files_to_inspect+=("$rulesd_audit_file") +done +# Case when particular audit rule isn't defined yet +if [ "${#files_to_inspect[@]}" -eq "0" ] +then + # Append '/etc/audit/rules.d/MAC-policy.rules' into list of files for inspection + key_rule_file="/etc/audit/rules.d/MAC-policy.rules" + # If the MAC-policy.rules file doesn't exist yet, create it with correct permissions + if [ ! -e "$key_rule_file" ] + then + touch "$key_rule_file" + chmod 0640 "$key_rule_file" + fi + files_to_inspect+=("$key_rule_file") +fi + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/etc/selinux/" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/selinux/ $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/etc/selinux/$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /etc/selinux/ -p wa -k MAC-policy" >> "$audit_rules_file" + fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure auditd Collects Information on Exporting to Media (successful) + At a minimum, the audit system should collect media exportation +events for all users and root. If the auditd daemon is configured to +use the augenrules program to read audit rules during daemon startup +(the default), add the following line to a file with suffix .rules in +the directory /etc/audit/rules.d, setting ARCH to either b32 or b64 as +appropriate for your system: +-a always,exit -F arch=ARCH -S mount -F auid>=1000 -F auid!=unset -F key=export +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following line to +/etc/audit/audit.rules file, setting ARCH to either b32 or b64 as +appropriate for your system: +-a always,exit -F arch=ARCH -S mount -F auid>=1000 -F auid!=unset -F key=export + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 5.4.1.1 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + Req-10.2.7 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + The unauthorized exportation of data to external media could result in an information leak +where classified information, Privacy Act information, and intellectual property could be lost. An audit +trail should be created each time a filesystem is mounted to help identify and guard against information +loss. + CCE-83735-1 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83735-1 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.7 + - audit_rules_media_export + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit mount tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83735-1 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.7 + - audit_rules_media_export + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for mount for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - mount + syscall_grouping: [] + + - name: Check existence of mount in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules + set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - mount + syscall_grouping: [] + + - name: Check existence of mount in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83735-1 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.7 + - audit_rules_media_export + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for mount for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - mount + syscall_grouping: [] + + - name: Check existence of mount in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules + set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - mount + syscall_grouping: [] + + - name: Check existence of mount in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-83735-1 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.7 + - audit_rules_media_export + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="" + AUID_FILTERS="-F auid>=1000 -F auid!=unset" + SYSCALL="mount" + KEY="perm_mod" + SYSCALL_GROUPING="" + + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Events that Modify the System's Network Environment + If the auditd daemon is configured to use the +augenrules program to read audit rules during daemon startup (the +default), add the following lines to a file with suffix .rules in the +directory /etc/audit/rules.d, setting ARCH to either b32 or b64 as +appropriate for your system: +-a always,exit -F arch=ARCH -S sethostname,setdomainname -F key=audit_rules_networkconfig_modification +-w /etc/issue -p wa -k audit_rules_networkconfig_modification +-w /etc/issue.net -p wa -k audit_rules_networkconfig_modification +-w /etc/hosts -p wa -k audit_rules_networkconfig_modification +-w /etc/sysconfig/network -p wa -k audit_rules_networkconfig_modification +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file, setting ARCH to either b32 or b64 as +appropriate for your system: +-a always,exit -F arch=ARCH -S sethostname,setdomainname -F key=audit_rules_networkconfig_modification +-w /etc/issue -p wa -k audit_rules_networkconfig_modification +-w /etc/issue.net -p wa -k audit_rules_networkconfig_modification +-w /etc/hosts -p wa -k audit_rules_networkconfig_modification +-w /etc/sysconfig/network -p wa -k audit_rules_networkconfig_modification + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 5.4.1.1 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + Req-10.5.5 + The network environment should not be modified by anything other +than administrator action. Any change to network parameters should be +audited. + CCE-83706-2 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83706-2 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_networkconfig_modification + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Set architecture for audit tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83706-2 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_networkconfig_modification + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Remediate audit rules for network configuration for x86 + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - sethostname + - setdomainname + syscall_grouping: + - sethostname + - setdomainname + + - name: Check existence of sethostname, setdomainname in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/audit_rules_networkconfig_modification.rules + set_fact: audit_file="/etc/audit/rules.d/audit_rules_networkconfig_modification.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F key=audit_rules_networkconfig_modification + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - sethostname + - setdomainname + syscall_grouping: + - sethostname + - setdomainname + + - name: Check existence of sethostname, setdomainname in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F key=audit_rules_networkconfig_modification + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83706-2 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_networkconfig_modification + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Remediate audit rules for network configuration for x86_64 + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - sethostname + - setdomainname + syscall_grouping: + - sethostname + - setdomainname + + - name: Check existence of sethostname, setdomainname in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/audit_rules_networkconfig_modification.rules + set_fact: audit_file="/etc/audit/rules.d/audit_rules_networkconfig_modification.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F key=audit_rules_networkconfig_modification + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - sethostname + - setdomainname + syscall_grouping: + - sethostname + - setdomainname + + - name: Check existence of sethostname, setdomainname in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F key=audit_rules_networkconfig_modification + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-83706-2 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_networkconfig_modification + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Check if watch rule for /etc/issue already exists in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: ^\s*-w\s+/etc/issue\s+-p\s+wa(\s|$)+ + patterns: '*.rules' + register: find_existing_watch_rules_d + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83706-2 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_networkconfig_modification + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Search /etc/audit/rules.d for other rules with specified key audit_rules_networkconfig_modification + find: + paths: /etc/audit/rules.d + contains: ^.*(?:-F key=|-k\s+)audit_rules_networkconfig_modification$ + patterns: '*.rules' + register: find_watch_key + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched + == 0 + tags: + - CCE-83706-2 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_networkconfig_modification + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Use /etc/audit/rules.d/audit_rules_networkconfig_modification.rules as the + recipient for the rule + set_fact: + all_files: + - /etc/audit/rules.d/audit_rules_networkconfig_modification.rules + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_watch_key.matched is defined and find_watch_key.matched == 0 and find_existing_watch_rules_d.matched + is defined and find_existing_watch_rules_d.matched == 0 + tags: + - CCE-83706-2 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_networkconfig_modification + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Use matched file as the recipient for the rule + set_fact: + all_files: + - '{{ find_watch_key.files | map(attribute=''path'') | list | first }}' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_watch_key.matched is defined and find_watch_key.matched > 0 and find_existing_watch_rules_d.matched + is defined and find_existing_watch_rules_d.matched == 0 + tags: + - CCE-83706-2 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_networkconfig_modification + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Add watch rule for /etc/issue in /etc/audit/rules.d/ + lineinfile: + path: '{{ all_files[0] }}' + line: -w /etc/issue -p wa -k audit_rules_networkconfig_modification + create: true + mode: '0640' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched + == 0 + tags: + - CCE-83706-2 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_networkconfig_modification + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Check if watch rule for /etc/issue already exists in /etc/audit/audit.rules + find: + paths: /etc/audit/ + contains: ^\s*-w\s+/etc/issue\s+-p\s+wa(\s|$)+ + patterns: audit.rules + register: find_existing_watch_audit_rules + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83706-2 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_networkconfig_modification + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Add watch rule for /etc/issue in /etc/audit/audit.rules + lineinfile: + line: -w /etc/issue -p wa -k audit_rules_networkconfig_modification + state: present + dest: /etc/audit/audit.rules + create: true + mode: '0640' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_audit_rules.matched is defined and find_existing_watch_audit_rules.matched + == 0 + tags: + - CCE-83706-2 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_networkconfig_modification + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Check if watch rule for /etc/issue.net already exists in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: ^\s*-w\s+/etc/issue.net\s+-p\s+wa(\s|$)+ + patterns: '*.rules' + register: find_existing_watch_rules_d + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83706-2 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_networkconfig_modification + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Search /etc/audit/rules.d for other rules with specified key audit_rules_networkconfig_modification + find: + paths: /etc/audit/rules.d + contains: ^.*(?:-F key=|-k\s+)audit_rules_networkconfig_modification$ + patterns: '*.rules' + register: find_watch_key + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched + == 0 + tags: + - CCE-83706-2 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_networkconfig_modification + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Use /etc/audit/rules.d/audit_rules_networkconfig_modification.rules as the + recipient for the rule + set_fact: + all_files: + - /etc/audit/rules.d/audit_rules_networkconfig_modification.rules + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_watch_key.matched is defined and find_watch_key.matched == 0 and find_existing_watch_rules_d.matched + is defined and find_existing_watch_rules_d.matched == 0 + tags: + - CCE-83706-2 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_networkconfig_modification + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Use matched file as the recipient for the rule + set_fact: + all_files: + - '{{ find_watch_key.files | map(attribute=''path'') | list | first }}' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_watch_key.matched is defined and find_watch_key.matched > 0 and find_existing_watch_rules_d.matched + is defined and find_existing_watch_rules_d.matched == 0 + tags: + - CCE-83706-2 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_networkconfig_modification + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Add watch rule for /etc/issue.net in /etc/audit/rules.d/ + lineinfile: + path: '{{ all_files[0] }}' + line: -w /etc/issue.net -p wa -k audit_rules_networkconfig_modification + create: true + mode: '0640' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched + == 0 + tags: + - CCE-83706-2 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_networkconfig_modification + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Check if watch rule for /etc/issue.net already exists in /etc/audit/audit.rules + find: + paths: /etc/audit/ + contains: ^\s*-w\s+/etc/issue.net\s+-p\s+wa(\s|$)+ + patterns: audit.rules + register: find_existing_watch_audit_rules + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83706-2 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_networkconfig_modification + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Add watch rule for /etc/issue.net in /etc/audit/audit.rules + lineinfile: + line: -w /etc/issue.net -p wa -k audit_rules_networkconfig_modification + state: present + dest: /etc/audit/audit.rules + create: true + mode: '0640' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_audit_rules.matched is defined and find_existing_watch_audit_rules.matched + == 0 + tags: + - CCE-83706-2 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_networkconfig_modification + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Check if watch rule for /etc/hosts already exists in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: ^\s*-w\s+/etc/hosts\s+-p\s+wa(\s|$)+ + patterns: '*.rules' + register: find_existing_watch_rules_d + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83706-2 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_networkconfig_modification + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Search /etc/audit/rules.d for other rules with specified key audit_rules_networkconfig_modification + find: + paths: /etc/audit/rules.d + contains: ^.*(?:-F key=|-k\s+)audit_rules_networkconfig_modification$ + patterns: '*.rules' + register: find_watch_key + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched + == 0 + tags: + - CCE-83706-2 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_networkconfig_modification + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Use /etc/audit/rules.d/audit_rules_networkconfig_modification.rules as the + recipient for the rule + set_fact: + all_files: + - /etc/audit/rules.d/audit_rules_networkconfig_modification.rules + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_watch_key.matched is defined and find_watch_key.matched == 0 and find_existing_watch_rules_d.matched + is defined and find_existing_watch_rules_d.matched == 0 + tags: + - CCE-83706-2 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_networkconfig_modification + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Use matched file as the recipient for the rule + set_fact: + all_files: + - '{{ find_watch_key.files | map(attribute=''path'') | list | first }}' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_watch_key.matched is defined and find_watch_key.matched > 0 and find_existing_watch_rules_d.matched + is defined and find_existing_watch_rules_d.matched == 0 + tags: + - CCE-83706-2 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_networkconfig_modification + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Add watch rule for /etc/hosts in /etc/audit/rules.d/ + lineinfile: + path: '{{ all_files[0] }}' + line: -w /etc/hosts -p wa -k audit_rules_networkconfig_modification + create: true + mode: '0640' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched + == 0 + tags: + - CCE-83706-2 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_networkconfig_modification + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Check if watch rule for /etc/hosts already exists in /etc/audit/audit.rules + find: + paths: /etc/audit/ + contains: ^\s*-w\s+/etc/hosts\s+-p\s+wa(\s|$)+ + patterns: audit.rules + register: find_existing_watch_audit_rules + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83706-2 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_networkconfig_modification + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Add watch rule for /etc/hosts in /etc/audit/audit.rules + lineinfile: + line: -w /etc/hosts -p wa -k audit_rules_networkconfig_modification + state: present + dest: /etc/audit/audit.rules + create: true + mode: '0640' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_audit_rules.matched is defined and find_existing_watch_audit_rules.matched + == 0 + tags: + - CCE-83706-2 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_networkconfig_modification + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Check if watch rule for /etc/sysconfig/network already exists in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: ^\s*-w\s+/etc/sysconfig/network\s+-p\s+wa(\s|$)+ + patterns: '*.rules' + register: find_existing_watch_rules_d + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83706-2 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_networkconfig_modification + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Search /etc/audit/rules.d for other rules with specified key audit_rules_networkconfig_modification + find: + paths: /etc/audit/rules.d + contains: ^.*(?:-F key=|-k\s+)audit_rules_networkconfig_modification$ + patterns: '*.rules' + register: find_watch_key + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched + == 0 + tags: + - CCE-83706-2 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_networkconfig_modification + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Use /etc/audit/rules.d/audit_rules_networkconfig_modification.rules as the + recipient for the rule + set_fact: + all_files: + - /etc/audit/rules.d/audit_rules_networkconfig_modification.rules + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_watch_key.matched is defined and find_watch_key.matched == 0 and find_existing_watch_rules_d.matched + is defined and find_existing_watch_rules_d.matched == 0 + tags: + - CCE-83706-2 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_networkconfig_modification + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Use matched file as the recipient for the rule + set_fact: + all_files: + - '{{ find_watch_key.files | map(attribute=''path'') | list | first }}' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_watch_key.matched is defined and find_watch_key.matched > 0 and find_existing_watch_rules_d.matched + is defined and find_existing_watch_rules_d.matched == 0 + tags: + - CCE-83706-2 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_networkconfig_modification + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Add watch rule for /etc/sysconfig/network in /etc/audit/rules.d/ + lineinfile: + path: '{{ all_files[0] }}' + line: -w /etc/sysconfig/network -p wa -k audit_rules_networkconfig_modification + create: true + mode: '0640' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched + == 0 + tags: + - CCE-83706-2 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_networkconfig_modification + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Check if watch rule for /etc/sysconfig/network already exists in /etc/audit/audit.rules + find: + paths: /etc/audit/ + contains: ^\s*-w\s+/etc/sysconfig/network\s+-p\s+wa(\s|$)+ + patterns: audit.rules + register: find_existing_watch_audit_rules + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83706-2 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_networkconfig_modification + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Add watch rule for /etc/sysconfig/network in /etc/audit/audit.rules + lineinfile: + line: -w /etc/sysconfig/network -p wa -k audit_rules_networkconfig_modification + state: present + dest: /etc/audit/audit.rules + create: true + mode: '0640' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_audit_rules.matched is defined and find_existing_watch_audit_rules.matched + == 0 + tags: + - CCE-83706-2 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_networkconfig_modification + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="" + AUID_FILTERS="" + SYSCALL="sethostname setdomainname" + KEY="audit_rules_networkconfig_modification" + SYSCALL_GROUPING="sethostname setdomainname" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +# Then perform the remediations for the watch rules +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + + +# If the audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# into the list of files to be inspected +files_to_inspect+=('/etc/audit/audit.rules') + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/etc/issue" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/issue $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/etc/issue$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /etc/issue -p wa -k audit_rules_networkconfig_modification" >> "$audit_rules_file" + fi +done +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + +# If the audit is 'augenrules', then check if rule is already defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection. +# If rule isn't defined, add '/etc/audit/rules.d/audit_rules_networkconfig_modification.rules' to list of files for inspection. +readarray -t matches < <(grep -HP "[\s]*-w[\s]+/etc/issue" /etc/audit/rules.d/*.rules) + +# For each of the matched entries +for match in "${matches[@]}" +do + # Extract filepath from the match + rulesd_audit_file=$(echo $match | cut -f1 -d ':') + # Append that path into list of files for inspection + files_to_inspect+=("$rulesd_audit_file") +done +# Case when particular audit rule isn't defined yet +if [ "${#files_to_inspect[@]}" -eq "0" ] +then + # Append '/etc/audit/rules.d/audit_rules_networkconfig_modification.rules' into list of files for inspection + key_rule_file="/etc/audit/rules.d/audit_rules_networkconfig_modification.rules" + # If the audit_rules_networkconfig_modification.rules file doesn't exist yet, create it with correct permissions + if [ ! -e "$key_rule_file" ] + then + touch "$key_rule_file" + chmod 0640 "$key_rule_file" + fi + files_to_inspect+=("$key_rule_file") +fi + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/etc/issue" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/issue $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/etc/issue$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /etc/issue -p wa -k audit_rules_networkconfig_modification" >> "$audit_rules_file" + fi +done +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + + +# If the audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# into the list of files to be inspected +files_to_inspect+=('/etc/audit/audit.rules') + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/etc/issue.net" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/issue.net $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/etc/issue.net$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /etc/issue.net -p wa -k audit_rules_networkconfig_modification" >> "$audit_rules_file" + fi +done +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + +# If the audit is 'augenrules', then check if rule is already defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection. +# If rule isn't defined, add '/etc/audit/rules.d/audit_rules_networkconfig_modification.rules' to list of files for inspection. +readarray -t matches < <(grep -HP "[\s]*-w[\s]+/etc/issue.net" /etc/audit/rules.d/*.rules) + +# For each of the matched entries +for match in "${matches[@]}" +do + # Extract filepath from the match + rulesd_audit_file=$(echo $match | cut -f1 -d ':') + # Append that path into list of files for inspection + files_to_inspect+=("$rulesd_audit_file") +done +# Case when particular audit rule isn't defined yet +if [ "${#files_to_inspect[@]}" -eq "0" ] +then + # Append '/etc/audit/rules.d/audit_rules_networkconfig_modification.rules' into list of files for inspection + key_rule_file="/etc/audit/rules.d/audit_rules_networkconfig_modification.rules" + # If the audit_rules_networkconfig_modification.rules file doesn't exist yet, create it with correct permissions + if [ ! -e "$key_rule_file" ] + then + touch "$key_rule_file" + chmod 0640 "$key_rule_file" + fi + files_to_inspect+=("$key_rule_file") +fi + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/etc/issue.net" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/issue.net $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/etc/issue.net$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /etc/issue.net -p wa -k audit_rules_networkconfig_modification" >> "$audit_rules_file" + fi +done +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + + +# If the audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# into the list of files to be inspected +files_to_inspect+=('/etc/audit/audit.rules') + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/etc/hosts" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/hosts $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/etc/hosts$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /etc/hosts -p wa -k audit_rules_networkconfig_modification" >> "$audit_rules_file" + fi +done +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + +# If the audit is 'augenrules', then check if rule is already defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection. +# If rule isn't defined, add '/etc/audit/rules.d/audit_rules_networkconfig_modification.rules' to list of files for inspection. +readarray -t matches < <(grep -HP "[\s]*-w[\s]+/etc/hosts" /etc/audit/rules.d/*.rules) + +# For each of the matched entries +for match in "${matches[@]}" +do + # Extract filepath from the match + rulesd_audit_file=$(echo $match | cut -f1 -d ':') + # Append that path into list of files for inspection + files_to_inspect+=("$rulesd_audit_file") +done +# Case when particular audit rule isn't defined yet +if [ "${#files_to_inspect[@]}" -eq "0" ] +then + # Append '/etc/audit/rules.d/audit_rules_networkconfig_modification.rules' into list of files for inspection + key_rule_file="/etc/audit/rules.d/audit_rules_networkconfig_modification.rules" + # If the audit_rules_networkconfig_modification.rules file doesn't exist yet, create it with correct permissions + if [ ! -e "$key_rule_file" ] + then + touch "$key_rule_file" + chmod 0640 "$key_rule_file" + fi + files_to_inspect+=("$key_rule_file") +fi + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/etc/hosts" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/hosts $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/etc/hosts$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /etc/hosts -p wa -k audit_rules_networkconfig_modification" >> "$audit_rules_file" + fi +done +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + + +# If the audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# into the list of files to be inspected +files_to_inspect+=('/etc/audit/audit.rules') + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/etc/sysconfig/network" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/sysconfig/network $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/etc/sysconfig/network$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /etc/sysconfig/network -p wa -k audit_rules_networkconfig_modification" >> "$audit_rules_file" + fi +done +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + +# If the audit is 'augenrules', then check if rule is already defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection. +# If rule isn't defined, add '/etc/audit/rules.d/audit_rules_networkconfig_modification.rules' to list of files for inspection. +readarray -t matches < <(grep -HP "[\s]*-w[\s]+/etc/sysconfig/network" /etc/audit/rules.d/*.rules) + +# For each of the matched entries +for match in "${matches[@]}" +do + # Extract filepath from the match + rulesd_audit_file=$(echo $match | cut -f1 -d ':') + # Append that path into list of files for inspection + files_to_inspect+=("$rulesd_audit_file") +done +# Case when particular audit rule isn't defined yet +if [ "${#files_to_inspect[@]}" -eq "0" ] +then + # Append '/etc/audit/rules.d/audit_rules_networkconfig_modification.rules' into list of files for inspection + key_rule_file="/etc/audit/rules.d/audit_rules_networkconfig_modification.rules" + # If the audit_rules_networkconfig_modification.rules file doesn't exist yet, create it with correct permissions + if [ ! -e "$key_rule_file" ] + then + touch "$key_rule_file" + chmod 0640 "$key_rule_file" + fi + files_to_inspect+=("$key_rule_file") +fi + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/etc/sysconfig/network" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/sysconfig/network $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/etc/sysconfig/network$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /etc/sysconfig/network -p wa -k audit_rules_networkconfig_modification" >> "$audit_rules_file" + fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Attempts to Alter Process and Session Initiation Information + The audit system already collects process information for all +users and root. If the auditd daemon is configured to use the +augenrules program to read audit rules during daemon startup (the +default), add the following lines to a file with suffix .rules in the +directory /etc/audit/rules.d in order to watch for attempted manual +edits of files involved in storing such process information: +-w /var/run/utmp -p wa -k session +-w /var/log/btmp -p wa -k session +-w /var/log/wtmp -p wa -k session +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file in order to watch for attempted manual +edits of files involved in storing such process information: +-w /var/run/utmp -p wa -k session +-w /var/log/btmp -p wa -k session +-w /var/log/wtmp -p wa -k session + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 5.4.1.1 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + 0582 + 0584 + 05885 + 0586 + 0846 + 0957 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.2.3 + Manual editing of these files may indicate nefarious activity, such +as an attacker attempting to remove evidence of an intrusion. + CCE-83713-8 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83713-8 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.3 + - audit_rules_session_events + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Check if watch rule for /var/run/utmp already exists in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: ^\s*-w\s+/var/run/utmp\s+-p\s+wa(\s|$)+ + patterns: '*.rules' + register: find_existing_watch_rules_d + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83713-8 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.3 + - audit_rules_session_events + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Search /etc/audit/rules.d for other rules with specified key session + find: + paths: /etc/audit/rules.d + contains: ^.*(?:-F key=|-k\s+)session$ + patterns: '*.rules' + register: find_watch_key + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched + == 0 + tags: + - CCE-83713-8 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.3 + - audit_rules_session_events + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Use /etc/audit/rules.d/session.rules as the recipient for the rule + set_fact: + all_files: + - /etc/audit/rules.d/session.rules + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_watch_key.matched is defined and find_watch_key.matched == 0 and find_existing_watch_rules_d.matched + is defined and find_existing_watch_rules_d.matched == 0 + tags: + - CCE-83713-8 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.3 + - audit_rules_session_events + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Use matched file as the recipient for the rule + set_fact: + all_files: + - '{{ find_watch_key.files | map(attribute=''path'') | list | first }}' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_watch_key.matched is defined and find_watch_key.matched > 0 and find_existing_watch_rules_d.matched + is defined and find_existing_watch_rules_d.matched == 0 + tags: + - CCE-83713-8 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.3 + - audit_rules_session_events + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Add watch rule for /var/run/utmp in /etc/audit/rules.d/ + lineinfile: + path: '{{ all_files[0] }}' + line: -w /var/run/utmp -p wa -k session + create: true + mode: '0640' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched + == 0 + tags: + - CCE-83713-8 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.3 + - audit_rules_session_events + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Check if watch rule for /var/run/utmp already exists in /etc/audit/audit.rules + find: + paths: /etc/audit/ + contains: ^\s*-w\s+/var/run/utmp\s+-p\s+wa(\s|$)+ + patterns: audit.rules + register: find_existing_watch_audit_rules + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83713-8 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.3 + - audit_rules_session_events + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Add watch rule for /var/run/utmp in /etc/audit/audit.rules + lineinfile: + line: -w /var/run/utmp -p wa -k session + state: present + dest: /etc/audit/audit.rules + create: true + mode: '0640' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_audit_rules.matched is defined and find_existing_watch_audit_rules.matched + == 0 + tags: + - CCE-83713-8 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.3 + - audit_rules_session_events + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Check if watch rule for /var/log/btmp already exists in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: ^\s*-w\s+/var/log/btmp\s+-p\s+wa(\s|$)+ + patterns: '*.rules' + register: find_existing_watch_rules_d + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83713-8 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.3 + - audit_rules_session_events + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Search /etc/audit/rules.d for other rules with specified key session + find: + paths: /etc/audit/rules.d + contains: ^.*(?:-F key=|-k\s+)session$ + patterns: '*.rules' + register: find_watch_key + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched + == 0 + tags: + - CCE-83713-8 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.3 + - audit_rules_session_events + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Use /etc/audit/rules.d/session.rules as the recipient for the rule + set_fact: + all_files: + - /etc/audit/rules.d/session.rules + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_watch_key.matched is defined and find_watch_key.matched == 0 and find_existing_watch_rules_d.matched + is defined and find_existing_watch_rules_d.matched == 0 + tags: + - CCE-83713-8 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.3 + - audit_rules_session_events + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Use matched file as the recipient for the rule + set_fact: + all_files: + - '{{ find_watch_key.files | map(attribute=''path'') | list | first }}' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_watch_key.matched is defined and find_watch_key.matched > 0 and find_existing_watch_rules_d.matched + is defined and find_existing_watch_rules_d.matched == 0 + tags: + - CCE-83713-8 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.3 + - audit_rules_session_events + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Add watch rule for /var/log/btmp in /etc/audit/rules.d/ + lineinfile: + path: '{{ all_files[0] }}' + line: -w /var/log/btmp -p wa -k session + create: true + mode: '0640' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched + == 0 + tags: + - CCE-83713-8 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.3 + - audit_rules_session_events + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Check if watch rule for /var/log/btmp already exists in /etc/audit/audit.rules + find: + paths: /etc/audit/ + contains: ^\s*-w\s+/var/log/btmp\s+-p\s+wa(\s|$)+ + patterns: audit.rules + register: find_existing_watch_audit_rules + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83713-8 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.3 + - audit_rules_session_events + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Add watch rule for /var/log/btmp in /etc/audit/audit.rules + lineinfile: + line: -w /var/log/btmp -p wa -k session + state: present + dest: /etc/audit/audit.rules + create: true + mode: '0640' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_audit_rules.matched is defined and find_existing_watch_audit_rules.matched + == 0 + tags: + - CCE-83713-8 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.3 + - audit_rules_session_events + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Check if watch rule for /var/log/wtmp already exists in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: ^\s*-w\s+/var/log/wtmp\s+-p\s+wa(\s|$)+ + patterns: '*.rules' + register: find_existing_watch_rules_d + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83713-8 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.3 + - audit_rules_session_events + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Search /etc/audit/rules.d for other rules with specified key session + find: + paths: /etc/audit/rules.d + contains: ^.*(?:-F key=|-k\s+)session$ + patterns: '*.rules' + register: find_watch_key + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched + == 0 + tags: + - CCE-83713-8 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.3 + - audit_rules_session_events + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Use /etc/audit/rules.d/session.rules as the recipient for the rule + set_fact: + all_files: + - /etc/audit/rules.d/session.rules + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_watch_key.matched is defined and find_watch_key.matched == 0 and find_existing_watch_rules_d.matched + is defined and find_existing_watch_rules_d.matched == 0 + tags: + - CCE-83713-8 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.3 + - audit_rules_session_events + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Use matched file as the recipient for the rule + set_fact: + all_files: + - '{{ find_watch_key.files | map(attribute=''path'') | list | first }}' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_watch_key.matched is defined and find_watch_key.matched > 0 and find_existing_watch_rules_d.matched + is defined and find_existing_watch_rules_d.matched == 0 + tags: + - CCE-83713-8 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.3 + - audit_rules_session_events + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Add watch rule for /var/log/wtmp in /etc/audit/rules.d/ + lineinfile: + path: '{{ all_files[0] }}' + line: -w /var/log/wtmp -p wa -k session + create: true + mode: '0640' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched + == 0 + tags: + - CCE-83713-8 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.3 + - audit_rules_session_events + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Check if watch rule for /var/log/wtmp already exists in /etc/audit/audit.rules + find: + paths: /etc/audit/ + contains: ^\s*-w\s+/var/log/wtmp\s+-p\s+wa(\s|$)+ + patterns: audit.rules + register: find_existing_watch_audit_rules + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83713-8 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.3 + - audit_rules_session_events + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Add watch rule for /var/log/wtmp in /etc/audit/audit.rules + lineinfile: + line: -w /var/log/wtmp -p wa -k session + state: present + dest: /etc/audit/audit.rules + create: true + mode: '0640' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_audit_rules.matched is defined and find_existing_watch_audit_rules.matched + == 0 + tags: + - CCE-83713-8 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.3 + - audit_rules_session_events + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + --- + + +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,{{ %0A-w%20/var/run/utmp%20-p%20wa%20-k%20session%0A-w%20/var/log/btmp%20-p%20wa%20-k%20session%0A-w%20/var/log/wtmp%20-p%20wa%20-k%20session%0A }} + mode: 0600 + path: /etc/audit/rules.d/75-audit-session-events.rules + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + + +# If the audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# into the list of files to be inspected +files_to_inspect+=('/etc/audit/audit.rules') + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/var/run/utmp" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/var/run/utmp $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/var/run/utmp$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /var/run/utmp -p wa -k session" >> "$audit_rules_file" + fi +done +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + +# If the audit is 'augenrules', then check if rule is already defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection. +# If rule isn't defined, add '/etc/audit/rules.d/session.rules' to list of files for inspection. +readarray -t matches < <(grep -HP "[\s]*-w[\s]+/var/run/utmp" /etc/audit/rules.d/*.rules) + +# For each of the matched entries +for match in "${matches[@]}" +do + # Extract filepath from the match + rulesd_audit_file=$(echo $match | cut -f1 -d ':') + # Append that path into list of files for inspection + files_to_inspect+=("$rulesd_audit_file") +done +# Case when particular audit rule isn't defined yet +if [ "${#files_to_inspect[@]}" -eq "0" ] +then + # Append '/etc/audit/rules.d/session.rules' into list of files for inspection + key_rule_file="/etc/audit/rules.d/session.rules" + # If the session.rules file doesn't exist yet, create it with correct permissions + if [ ! -e "$key_rule_file" ] + then + touch "$key_rule_file" + chmod 0640 "$key_rule_file" + fi + files_to_inspect+=("$key_rule_file") +fi + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/var/run/utmp" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/var/run/utmp $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/var/run/utmp$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /var/run/utmp -p wa -k session" >> "$audit_rules_file" + fi +done +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + + +# If the audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# into the list of files to be inspected +files_to_inspect+=('/etc/audit/audit.rules') + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/var/log/btmp" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/var/log/btmp $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/var/log/btmp$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /var/log/btmp -p wa -k session" >> "$audit_rules_file" + fi +done +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + +# If the audit is 'augenrules', then check if rule is already defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection. +# If rule isn't defined, add '/etc/audit/rules.d/session.rules' to list of files for inspection. +readarray -t matches < <(grep -HP "[\s]*-w[\s]+/var/log/btmp" /etc/audit/rules.d/*.rules) + +# For each of the matched entries +for match in "${matches[@]}" +do + # Extract filepath from the match + rulesd_audit_file=$(echo $match | cut -f1 -d ':') + # Append that path into list of files for inspection + files_to_inspect+=("$rulesd_audit_file") +done +# Case when particular audit rule isn't defined yet +if [ "${#files_to_inspect[@]}" -eq "0" ] +then + # Append '/etc/audit/rules.d/session.rules' into list of files for inspection + key_rule_file="/etc/audit/rules.d/session.rules" + # If the session.rules file doesn't exist yet, create it with correct permissions + if [ ! -e "$key_rule_file" ] + then + touch "$key_rule_file" + chmod 0640 "$key_rule_file" + fi + files_to_inspect+=("$key_rule_file") +fi + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/var/log/btmp" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/var/log/btmp $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/var/log/btmp$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /var/log/btmp -p wa -k session" >> "$audit_rules_file" + fi +done +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + + +# If the audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# into the list of files to be inspected +files_to_inspect+=('/etc/audit/audit.rules') + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/var/log/wtmp" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/var/log/wtmp $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/var/log/wtmp$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /var/log/wtmp -p wa -k session" >> "$audit_rules_file" + fi +done +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + +# If the audit is 'augenrules', then check if rule is already defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection. +# If rule isn't defined, add '/etc/audit/rules.d/session.rules' to list of files for inspection. +readarray -t matches < <(grep -HP "[\s]*-w[\s]+/var/log/wtmp" /etc/audit/rules.d/*.rules) + +# For each of the matched entries +for match in "${matches[@]}" +do + # Extract filepath from the match + rulesd_audit_file=$(echo $match | cut -f1 -d ':') + # Append that path into list of files for inspection + files_to_inspect+=("$rulesd_audit_file") +done +# Case when particular audit rule isn't defined yet +if [ "${#files_to_inspect[@]}" -eq "0" ] +then + # Append '/etc/audit/rules.d/session.rules' into list of files for inspection + key_rule_file="/etc/audit/rules.d/session.rules" + # If the session.rules file doesn't exist yet, create it with correct permissions + if [ ! -e "$key_rule_file" ] + then + touch "$key_rule_file" + chmod 0640 "$key_rule_file" + fi + files_to_inspect+=("$key_rule_file") +fi + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/var/log/wtmp" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/var/log/wtmp $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/var/log/wtmp$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /var/log/wtmp -p wa -k session" >> "$audit_rules_file" + fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + Ensure auditd Collects System Administrator Actions - /etc/sudoers + At a minimum, the audit system should collect administrator actions +for all users and root. If the auditd daemon is configured to use the +augenrules program to read audit rules during daemon startup (the default), +add the following line to a file with suffix .rules in the directory +/etc/audit/rules.d: +-w /etc/sudoers -p wa -k actions +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following line to +/etc/audit/audit.rules file: +-w /etc/sudoers -p wa -k actions + CCI-000018 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-001403 + CCI-001404 + CCI-002130 + CCI-002132 + CCI-002884 + SRG-OS-000004-GPOS-00004 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000304-GPOS-00121 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000470-GPOS-00214 + SRG-OS-000471-GPOS-00215 + SRG-OS-000239-GPOS-00089 + SRG-OS-000240-GPOS-00090 + SRG-OS-000241-GPOS-00091 + SRG-OS-000303-GPOS-00120 + SRG-OS-000466-GPOS-00210 + SRG-OS-000476-GPOS-00221 + The actions taken by system administrators should be audited to keep a record +of what was executed on the system, as well as, for accountability purposes. +Editing the sudoers file may be sign of an attacker trying to +establish persistent methods to a system, auditing the editing of the sudoers +files mitigates this risk. + CCE-90176-9 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-90176-9 + - audit_rules_sudoers + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Check if watch rule for /etc/sudoers already exists in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: ^\s*-w\s+/etc/sudoers\s+-p\s+wa(\s|$)+ + patterns: '*.rules' + register: find_existing_watch_rules_d + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-90176-9 + - audit_rules_sudoers + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Search /etc/audit/rules.d for other rules with specified key actions + find: + paths: /etc/audit/rules.d + contains: ^.*(?:-F key=|-k\s+)actions$ + patterns: '*.rules' + register: find_watch_key + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched + == 0 + tags: + - CCE-90176-9 + - audit_rules_sudoers + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Use /etc/audit/rules.d/actions.rules as the recipient for the rule + set_fact: + all_files: + - /etc/audit/rules.d/actions.rules + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_watch_key.matched is defined and find_watch_key.matched == 0 and find_existing_watch_rules_d.matched + is defined and find_existing_watch_rules_d.matched == 0 + tags: + - CCE-90176-9 + - audit_rules_sudoers + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Use matched file as the recipient for the rule + set_fact: + all_files: + - '{{ find_watch_key.files | map(attribute=''path'') | list | first }}' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_watch_key.matched is defined and find_watch_key.matched > 0 and find_existing_watch_rules_d.matched + is defined and find_existing_watch_rules_d.matched == 0 + tags: + - CCE-90176-9 + - audit_rules_sudoers + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Add watch rule for /etc/sudoers in /etc/audit/rules.d/ + lineinfile: + path: '{{ all_files[0] }}' + line: -w /etc/sudoers -p wa -k actions + create: true + mode: '0640' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched + == 0 + tags: + - CCE-90176-9 + - audit_rules_sudoers + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Check if watch rule for /etc/sudoers already exists in /etc/audit/audit.rules + find: + paths: /etc/audit/ + contains: ^\s*-w\s+/etc/sudoers\s+-p\s+wa(\s|$)+ + patterns: audit.rules + register: find_existing_watch_audit_rules + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-90176-9 + - audit_rules_sudoers + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Add watch rule for /etc/sudoers in /etc/audit/audit.rules + lineinfile: + line: -w /etc/sudoers -p wa -k actions + state: present + dest: /etc/audit/audit.rules + create: true + mode: '0640' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_audit_rules.matched is defined and find_existing_watch_audit_rules.matched + == 0 + tags: + - CCE-90176-9 + - audit_rules_sudoers + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + + +# If the audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# into the list of files to be inspected +files_to_inspect+=('/etc/audit/audit.rules') + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/etc/sudoers" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/sudoers $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/etc/sudoers$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /etc/sudoers -p wa -k actions" >> "$audit_rules_file" + fi +done +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + +# If the audit is 'augenrules', then check if rule is already defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection. +# If rule isn't defined, add '/etc/audit/rules.d/actions.rules' to list of files for inspection. +readarray -t matches < <(grep -HP "[\s]*-w[\s]+/etc/sudoers" /etc/audit/rules.d/*.rules) + +# For each of the matched entries +for match in "${matches[@]}" +do + # Extract filepath from the match + rulesd_audit_file=$(echo $match | cut -f1 -d ':') + # Append that path into list of files for inspection + files_to_inspect+=("$rulesd_audit_file") +done +# Case when particular audit rule isn't defined yet +if [ "${#files_to_inspect[@]}" -eq "0" ] +then + # Append '/etc/audit/rules.d/actions.rules' into list of files for inspection + key_rule_file="/etc/audit/rules.d/actions.rules" + # If the actions.rules file doesn't exist yet, create it with correct permissions + if [ ! -e "$key_rule_file" ] + then + touch "$key_rule_file" + chmod 0640 "$key_rule_file" + fi + files_to_inspect+=("$key_rule_file") +fi + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/etc/sudoers" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/sudoers $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/etc/sudoers$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /etc/sudoers -p wa -k actions" >> "$audit_rules_file" + fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure auditd Collects System Administrator Actions - /etc/sudoers.d/ + At a minimum, the audit system should collect administrator actions +for all users and root. If the auditd daemon is configured to use the +augenrules program to read audit rules during daemon startup (the default), +add the following line to a file with suffix .rules in the directory +/etc/audit/rules.d: +-w /etc/sudoers.d/ -p wa -k actions +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following line to +/etc/audit/audit.rules file: +-w /etc/sudoers.d/ -p wa -k actions + CCI-000018 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-001403 + CCI-001404 + CCI-002130 + CCI-002132 + CCI-002884 + SRG-OS-000004-GPOS-00004 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000304-GPOS-00121 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000470-GPOS-00214 + SRG-OS-000471-GPOS-00215 + SRG-OS-000239-GPOS-00089 + SRG-OS-000240-GPOS-00090 + SRG-OS-000241-GPOS-00091 + SRG-OS-000303-GPOS-00120 + SRG-OS-000466-GPOS-00210 + SRG-OS-000476-GPOS-00221 + The actions taken by system administrators should be audited to keep a record +of what was executed on the system, as well as, for accountability purposes. +Editing the sudoers file may be sign of an attacker trying to +establish persistent methods to a system, auditing the editing of the sudoers +files mitigates this risk. + CCE-89498-0 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-89498-0 + - audit_rules_sudoers_d + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Check if watch rule for /etc/sudoers.d/ already exists in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: ^\s*-w\s+/etc/sudoers.d/\s+-p\s+wa(\s|$)+ + patterns: '*.rules' + register: find_existing_watch_rules_d + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-89498-0 + - audit_rules_sudoers_d + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Search /etc/audit/rules.d for other rules with specified key actions + find: + paths: /etc/audit/rules.d + contains: ^.*(?:-F key=|-k\s+)actions$ + patterns: '*.rules' + register: find_watch_key + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched + == 0 + tags: + - CCE-89498-0 + - audit_rules_sudoers_d + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Use /etc/audit/rules.d/actions.rules as the recipient for the rule + set_fact: + all_files: + - /etc/audit/rules.d/actions.rules + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_watch_key.matched is defined and find_watch_key.matched == 0 and find_existing_watch_rules_d.matched + is defined and find_existing_watch_rules_d.matched == 0 + tags: + - CCE-89498-0 + - audit_rules_sudoers_d + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Use matched file as the recipient for the rule + set_fact: + all_files: + - '{{ find_watch_key.files | map(attribute=''path'') | list | first }}' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_watch_key.matched is defined and find_watch_key.matched > 0 and find_existing_watch_rules_d.matched + is defined and find_existing_watch_rules_d.matched == 0 + tags: + - CCE-89498-0 + - audit_rules_sudoers_d + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Add watch rule for /etc/sudoers.d/ in /etc/audit/rules.d/ + lineinfile: + path: '{{ all_files[0] }}' + line: -w /etc/sudoers.d/ -p wa -k actions + create: true + mode: '0640' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched + == 0 + tags: + - CCE-89498-0 + - audit_rules_sudoers_d + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Check if watch rule for /etc/sudoers.d/ already exists in /etc/audit/audit.rules + find: + paths: /etc/audit/ + contains: ^\s*-w\s+/etc/sudoers.d/\s+-p\s+wa(\s|$)+ + patterns: audit.rules + register: find_existing_watch_audit_rules + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-89498-0 + - audit_rules_sudoers_d + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Add watch rule for /etc/sudoers.d/ in /etc/audit/audit.rules + lineinfile: + line: -w /etc/sudoers.d/ -p wa -k actions + state: present + dest: /etc/audit/audit.rules + create: true + mode: '0640' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_audit_rules.matched is defined and find_existing_watch_audit_rules.matched + == 0 + tags: + - CCE-89498-0 + - audit_rules_sudoers_d + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + + +# If the audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# into the list of files to be inspected +files_to_inspect+=('/etc/audit/audit.rules') + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/etc/sudoers.d/" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/sudoers.d/ $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/etc/sudoers.d/$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /etc/sudoers.d/ -p wa -k actions" >> "$audit_rules_file" + fi +done +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + +# If the audit is 'augenrules', then check if rule is already defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection. +# If rule isn't defined, add '/etc/audit/rules.d/actions.rules' to list of files for inspection. +readarray -t matches < <(grep -HP "[\s]*-w[\s]+/etc/sudoers.d/" /etc/audit/rules.d/*.rules) + +# For each of the matched entries +for match in "${matches[@]}" +do + # Extract filepath from the match + rulesd_audit_file=$(echo $match | cut -f1 -d ':') + # Append that path into list of files for inspection + files_to_inspect+=("$rulesd_audit_file") +done +# Case when particular audit rule isn't defined yet +if [ "${#files_to_inspect[@]}" -eq "0" ] +then + # Append '/etc/audit/rules.d/actions.rules' into list of files for inspection + key_rule_file="/etc/audit/rules.d/actions.rules" + # If the actions.rules file doesn't exist yet, create it with correct permissions + if [ ! -e "$key_rule_file" ] + then + touch "$key_rule_file" + chmod 0640 "$key_rule_file" + fi + files_to_inspect+=("$key_rule_file") +fi + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/etc/sudoers.d/" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/sudoers.d/ $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/etc/sudoers.d/$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /etc/sudoers.d/ -p wa -k actions" >> "$audit_rules_file" + fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Events When Privileged Executables Are Run + Verify the system generates an audit record when privileged functions are executed. + +If audit is using the "auditctl" tool to load the rules, run the following command: + +$ sudo grep execve /etc/audit/audit.rules + +If audit is using the "augenrules" tool to load the rules, run the following command: + +$ sudo grep -r execve /etc/audit/rules.d + + +-a always,exit -F arch=b32 -S execve -C uid!=euid -F euid=0 -k setuid +-a always,exit -F arch=b64 -S execve -C uid!=euid -F euid=0 -k setuid +-a always,exit -F arch=b32 -S execve -C gid!=egid -F egid=0 -k setgid +-a always,exit -F arch=b64 -S execve -C gid!=egid -F egid=0 -k setgid + + +If both the "b32" and "b64" audit rules for "SUID" files are not defined, this is a finding. +If both the "b32" and "b64" audit rules for "SGID" files are not defined, this is a finding. + Note that these rules can be configured in a +number of ways while still achieving the desired effect. + CCI-001814 + CCI-001882 + CCI-001889 + CCI-001880 + CCI-001881 + CCI-001878 + CCI-001879 + CCI-001875 + CCI-001877 + CCI-001914 + CCI-002233 + CCI-002234 + CM-5(1) + AU-7(a) + AU-7(b) + AU-8(b) + AU-12(3) + AC-6(9) + SRG-OS-000326-GPOS-00126 + SRG-OS-000327-GPOS-00127 + Misuse of privileged functions, either intentionally or unintentionally by +authorized users, or by unauthorized external entities that have +compromised information system accounts, is a serious and ongoing concern +and can have significant adverse impacts on organizations. Auditing the use +of privileged functions is one way to detect such misuse and identify the +risk from insider threats and the advanced persistent threat. + + CCE-86402-5 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-86402-5 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(3) + - NIST-800-53-AU-7(a) + - NIST-800-53-AU-7(b) + - NIST-800-53-AU-8(b) + - NIST-800-53-CM-5(1) + - audit_rules_suid_privilege_function + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Service facts + service_facts: null + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86402-5 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(3) + - NIST-800-53-AU-7(a) + - NIST-800-53-AU-7(b) + - NIST-800-53-AU-8(b) + - NIST-800-53-CM-5(1) + - audit_rules_suid_privilege_function + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Check the rules script being used + command: grep '^ExecStartPost' /usr/lib/systemd/system/auditd.service + register: check_rules_scripts_result + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86402-5 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(3) + - NIST-800-53-AU-7(a) + - NIST-800-53-AU-7(b) + - NIST-800-53-AU-8(b) + - NIST-800-53-CM-5(1) + - audit_rules_suid_privilege_function + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Set suid_audit_rules fact + set_fact: + suid_audit_rules: + - -a always,exit -F arch=b32 -S execve -C gid!=egid -F egid=0 -k setgid + - -a always,exit -F arch=b64 -S execve -C gid!=egid -F egid=0 -k setgid + - -a always,exit -F arch=b32 -S execve -C uid!=euid -F euid=0 -k setuid + - -a always,exit -F arch=b64 -S execve -C uid!=euid -F euid=0 -k setuid + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86402-5 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(3) + - NIST-800-53-AU-7(a) + - NIST-800-53-AU-7(b) + - NIST-800-53-AU-8(b) + - NIST-800-53-CM-5(1) + - audit_rules_suid_privilege_function + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Update /etc/audit/rules.d/privileged.rules to audit privileged functions + lineinfile: + path: /etc/audit/rules.d/privileged.rules + line: '{{ item }}' + create: true + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"auditd.service" in ansible_facts.services' + - '"augenrules" in check_rules_scripts_result.stdout' + register: augenrules_audit_rules_privilege_function_update_result + with_items: '{{ suid_audit_rules }}' + tags: + - CCE-86402-5 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(3) + - NIST-800-53-AU-7(a) + - NIST-800-53-AU-7(b) + - NIST-800-53-AU-8(b) + - NIST-800-53-CM-5(1) + - audit_rules_suid_privilege_function + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Update Update /etc/audit/audit.rules to audit privileged functions + lineinfile: + path: /etc/audit/audit.rules + line: '{{ item }}' + create: true + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"auditd.service" in ansible_facts.services' + - '"auditctl" in check_rules_scripts_result.stdout' + register: auditctl_audit_rules_privilege_function_update_result + with_items: '{{ suid_audit_rules }}' + tags: + - CCE-86402-5 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(3) + - NIST-800-53-AU-7(a) + - NIST-800-53-AU-7(b) + - NIST-800-53-AU-8(b) + - NIST-800-53-CM-5(1) + - audit_rules_suid_privilege_function + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Restart Auditd + command: /usr/sbin/service auditd restart + args: + warn: false + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - (augenrules_audit_rules_privilege_function_update_result.changed or auditctl_audit_rules_privilege_function_update_result.changed) + - ansible_facts.services["auditd.service"].state == "running" + tags: + - CCE-86402-5 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(3) + - NIST-800-53-AU-7(a) + - NIST-800-53-AU-7(b) + - NIST-800-53-AU-8(b) + - NIST-800-53-CM-5(1) + - audit_rules_suid_privilege_function + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + + OTHER_FILTERS="-C uid!=euid -F euid=0" + + AUID_FILTERS="" + SYSCALL="execve" + KEY="setuid" + SYSCALL_GROUPING="" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + + OTHER_FILTERS="-C gid!=egid -F egid=0" + + AUID_FILTERS="" + SYSCALL="execve" + KEY="setgid" + SYSCALL_GROUPING="" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure auditd Collects System Administrator Actions + At a minimum, the audit system should collect administrator actions +for all users and root. If the auditd daemon is configured to use the +augenrules program to read audit rules during daemon startup (the default), +add the following line to a file with suffix .rules in the directory +/etc/audit/rules.d: +-w /etc/sudoers -p wa -k actions +-w /etc/sudoers.d/ -p wa -k actions +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following line to +/etc/audit/audit.rules file: +-w /etc/sudoers -p wa -k actions +-w /etc/sudoers.d/ -p wa -k actions + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 18 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 5.4.1.1 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + DSS06.03 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000126 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.2.2 + 4.3.3.3.9 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.1 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.1.2 + A.6.2.1 + A.6.2.2 + A.7.1.1 + A.9.1.2 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.1 + A.9.4.2 + A.9.4.3 + A.9.4.4 + A.9.4.5 + AC-2(7)(b) + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-1 + PR.AC-3 + PR.AC-4 + PR.AC-6 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.2.2 + Req-10.2.5.b + SRG-OS-000004-GPOS-00004 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000304-GPOS-00121 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000470-GPOS-00214 + SRG-OS-000471-GPOS-00215 + SRG-OS-000239-GPOS-00089 + SRG-OS-000240-GPOS-00090 + SRG-OS-000241-GPOS-00091 + SRG-OS-000303-GPOS-00120 + SRG-OS-000304-GPOS-00121 + SRG-OS-000466-GPOS-00210 + SRG-OS-000476-GPOS-00221 + SRG-OS-000462-VMM-001840 + SRG-OS-000471-VMM-001910 + The actions taken by system administrators should be audited to keep a record +of what was executed on the system, as well as, for accountability purposes. + CCE-83729-4 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83729-4 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(7)(b) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.2 + - PCI-DSS-Req-10.2.5.b + - audit_rules_sysadmin_actions + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Check if watch rule for /etc/sudoers already exists in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: ^\s*-w\s+/etc/sudoers\s+-p\s+wa(\s|$)+ + patterns: '*.rules' + register: find_existing_watch_rules_d + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83729-4 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(7)(b) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.2 + - PCI-DSS-Req-10.2.5.b + - audit_rules_sysadmin_actions + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Search /etc/audit/rules.d for other rules with specified key actions + find: + paths: /etc/audit/rules.d + contains: ^.*(?:-F key=|-k\s+)actions$ + patterns: '*.rules' + register: find_watch_key + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched + == 0 + tags: + - CCE-83729-4 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(7)(b) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.2 + - PCI-DSS-Req-10.2.5.b + - audit_rules_sysadmin_actions + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Use /etc/audit/rules.d/actions.rules as the recipient for the rule + set_fact: + all_files: + - /etc/audit/rules.d/actions.rules + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_watch_key.matched is defined and find_watch_key.matched == 0 and find_existing_watch_rules_d.matched + is defined and find_existing_watch_rules_d.matched == 0 + tags: + - CCE-83729-4 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(7)(b) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.2 + - PCI-DSS-Req-10.2.5.b + - audit_rules_sysadmin_actions + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Use matched file as the recipient for the rule + set_fact: + all_files: + - '{{ find_watch_key.files | map(attribute=''path'') | list | first }}' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_watch_key.matched is defined and find_watch_key.matched > 0 and find_existing_watch_rules_d.matched + is defined and find_existing_watch_rules_d.matched == 0 + tags: + - CCE-83729-4 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(7)(b) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.2 + - PCI-DSS-Req-10.2.5.b + - audit_rules_sysadmin_actions + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Add watch rule for /etc/sudoers in /etc/audit/rules.d/ + lineinfile: + path: '{{ all_files[0] }}' + line: -w /etc/sudoers -p wa -k actions + create: true + mode: '0640' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched + == 0 + tags: + - CCE-83729-4 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(7)(b) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.2 + - PCI-DSS-Req-10.2.5.b + - audit_rules_sysadmin_actions + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Check if watch rule for /etc/sudoers already exists in /etc/audit/audit.rules + find: + paths: /etc/audit/ + contains: ^\s*-w\s+/etc/sudoers\s+-p\s+wa(\s|$)+ + patterns: audit.rules + register: find_existing_watch_audit_rules + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83729-4 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(7)(b) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.2 + - PCI-DSS-Req-10.2.5.b + - audit_rules_sysadmin_actions + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Add watch rule for /etc/sudoers in /etc/audit/audit.rules + lineinfile: + line: -w /etc/sudoers -p wa -k actions + state: present + dest: /etc/audit/audit.rules + create: true + mode: '0640' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_audit_rules.matched is defined and find_existing_watch_audit_rules.matched + == 0 + tags: + - CCE-83729-4 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(7)(b) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.2 + - PCI-DSS-Req-10.2.5.b + - audit_rules_sysadmin_actions + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Check if watch rule for /etc/sudoers.d/ already exists in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: ^\s*-w\s+/etc/sudoers.d/\s+-p\s+wa(\s|$)+ + patterns: '*.rules' + register: find_existing_watch_rules_d + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83729-4 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(7)(b) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.2 + - PCI-DSS-Req-10.2.5.b + - audit_rules_sysadmin_actions + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Search /etc/audit/rules.d for other rules with specified key actions + find: + paths: /etc/audit/rules.d + contains: ^.*(?:-F key=|-k\s+)actions$ + patterns: '*.rules' + register: find_watch_key + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched + == 0 + tags: + - CCE-83729-4 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(7)(b) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.2 + - PCI-DSS-Req-10.2.5.b + - audit_rules_sysadmin_actions + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Use /etc/audit/rules.d/actions.rules as the recipient for the rule + set_fact: + all_files: + - /etc/audit/rules.d/actions.rules + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_watch_key.matched is defined and find_watch_key.matched == 0 and find_existing_watch_rules_d.matched + is defined and find_existing_watch_rules_d.matched == 0 + tags: + - CCE-83729-4 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(7)(b) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.2 + - PCI-DSS-Req-10.2.5.b + - audit_rules_sysadmin_actions + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Use matched file as the recipient for the rule + set_fact: + all_files: + - '{{ find_watch_key.files | map(attribute=''path'') | list | first }}' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_watch_key.matched is defined and find_watch_key.matched > 0 and find_existing_watch_rules_d.matched + is defined and find_existing_watch_rules_d.matched == 0 + tags: + - CCE-83729-4 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(7)(b) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.2 + - PCI-DSS-Req-10.2.5.b + - audit_rules_sysadmin_actions + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Add watch rule for /etc/sudoers.d/ in /etc/audit/rules.d/ + lineinfile: + path: '{{ all_files[0] }}' + line: -w /etc/sudoers.d/ -p wa -k actions + create: true + mode: '0640' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched + == 0 + tags: + - CCE-83729-4 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(7)(b) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.2 + - PCI-DSS-Req-10.2.5.b + - audit_rules_sysadmin_actions + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Check if watch rule for /etc/sudoers.d/ already exists in /etc/audit/audit.rules + find: + paths: /etc/audit/ + contains: ^\s*-w\s+/etc/sudoers.d/\s+-p\s+wa(\s|$)+ + patterns: audit.rules + register: find_existing_watch_audit_rules + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83729-4 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(7)(b) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.2 + - PCI-DSS-Req-10.2.5.b + - audit_rules_sysadmin_actions + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Add watch rule for /etc/sudoers.d/ in /etc/audit/audit.rules + lineinfile: + line: -w /etc/sudoers.d/ -p wa -k actions + state: present + dest: /etc/audit/audit.rules + create: true + mode: '0640' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_audit_rules.matched is defined and find_existing_watch_audit_rules.matched + == 0 + tags: + - CCE-83729-4 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(7)(b) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.2 + - PCI-DSS-Req-10.2.5.b + - audit_rules_sysadmin_actions + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,{{ -w%20/etc/sudoers.d/%20-p%20wa%20-k%20actions%0A-w%20/etc/sudoers%20-p%20wa%20-k%20actions%0A }} + mode: 0600 + path: /etc/audit/rules.d/75-audit-sysadmin-actions.rules + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + + +# If the audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# into the list of files to be inspected +files_to_inspect+=('/etc/audit/audit.rules') + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/etc/sudoers" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/sudoers $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/etc/sudoers$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /etc/sudoers -p wa -k actions" >> "$audit_rules_file" + fi +done +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + +# If the audit is 'augenrules', then check if rule is already defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection. +# If rule isn't defined, add '/etc/audit/rules.d/actions.rules' to list of files for inspection. +readarray -t matches < <(grep -HP "[\s]*-w[\s]+/etc/sudoers" /etc/audit/rules.d/*.rules) + +# For each of the matched entries +for match in "${matches[@]}" +do + # Extract filepath from the match + rulesd_audit_file=$(echo $match | cut -f1 -d ':') + # Append that path into list of files for inspection + files_to_inspect+=("$rulesd_audit_file") +done +# Case when particular audit rule isn't defined yet +if [ "${#files_to_inspect[@]}" -eq "0" ] +then + # Append '/etc/audit/rules.d/actions.rules' into list of files for inspection + key_rule_file="/etc/audit/rules.d/actions.rules" + # If the actions.rules file doesn't exist yet, create it with correct permissions + if [ ! -e "$key_rule_file" ] + then + touch "$key_rule_file" + chmod 0640 "$key_rule_file" + fi + files_to_inspect+=("$key_rule_file") +fi + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/etc/sudoers" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/sudoers $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/etc/sudoers$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /etc/sudoers -p wa -k actions" >> "$audit_rules_file" + fi +done + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + + +# If the audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# into the list of files to be inspected +files_to_inspect+=('/etc/audit/audit.rules') + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/etc/sudoers.d/" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/sudoers.d/ $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/etc/sudoers.d/$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /etc/sudoers.d/ -p wa -k actions" >> "$audit_rules_file" + fi +done +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + +# If the audit is 'augenrules', then check if rule is already defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection. +# If rule isn't defined, add '/etc/audit/rules.d/actions.rules' to list of files for inspection. +readarray -t matches < <(grep -HP "[\s]*-w[\s]+/etc/sudoers.d/" /etc/audit/rules.d/*.rules) + +# For each of the matched entries +for match in "${matches[@]}" +do + # Extract filepath from the match + rulesd_audit_file=$(echo $match | cut -f1 -d ':') + # Append that path into list of files for inspection + files_to_inspect+=("$rulesd_audit_file") +done +# Case when particular audit rule isn't defined yet +if [ "${#files_to_inspect[@]}" -eq "0" ] +then + # Append '/etc/audit/rules.d/actions.rules' into list of files for inspection + key_rule_file="/etc/audit/rules.d/actions.rules" + # If the actions.rules file doesn't exist yet, create it with correct permissions + if [ ! -e "$key_rule_file" ] + then + touch "$key_rule_file" + chmod 0640 "$key_rule_file" + fi + files_to_inspect+=("$key_rule_file") +fi + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/etc/sudoers.d/" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/sudoers.d/ $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/etc/sudoers.d/$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /etc/sudoers.d/ -p wa -k actions" >> "$audit_rules_file" + fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Shutdown System When Auditing Failures Occur + If the auditd daemon is configured to use the +augenrules program to read audit rules during daemon startup (the +default), add the following line to to the bottom of a file with suffix +.rules in the directory /etc/audit/rules.d: +-f 2 +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following line to the +bottom of the /etc/audit/audit.rules file: +-f 2 + 1 + 14 + 15 + 16 + 3 + 5 + 6 + APO11.04 + BAI03.05 + DSS05.04 + DSS05.07 + MEA02.01 + 3.3.1 + 3.3.4 + CCI-000139 + CCI-000140 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + AU-5(b) + SC-24 + CM-6(a) + PR.PT-1 + SRG-OS-000046-GPOS-00022 + SRG-OS-000047-GPOS-00023 + SRG-OS-000047-VMM-000220 + It is critical for the appropriate personnel to be aware if a system +is at risk of failing to process audit logs as required. Without this +notification, the security personnel may be unaware of an impending failure of +the audit capability, and system operation may be adversely affected. + +Audit processing failures include software/hardware errors, failures in the +audit capturing mechanisms, and audit storage capacity being reached or +exceeded. + CCE-83709-6 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83709-6 + - NIST-800-171-3.3.1 + - NIST-800-171-3.3.4 + - NIST-800-53-AU-5(b) + - NIST-800-53-CM-6(a) + - NIST-800-53-SC-24 + - audit_rules_system_shutdown + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Collect all files from /etc/audit/rules.d with .rules extension + find: + paths: /etc/audit/rules.d/ + patterns: '*.rules' + register: find_rules_d + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83709-6 + - NIST-800-171-3.3.1 + - NIST-800-171-3.3.4 + - NIST-800-53-AU-5(b) + - NIST-800-53-CM-6(a) + - NIST-800-53-SC-24 + - audit_rules_system_shutdown + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Remove the -f option from all Audit config files + lineinfile: + path: '{{ item }}' + regexp: ^\s*(?:-f)\s+.*$ + state: absent + loop: '{{ find_rules_d.files | map(attribute=''path'') | list + [''/etc/audit/audit.rules''] + }}' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83709-6 + - NIST-800-171-3.3.1 + - NIST-800-171-3.3.4 + - NIST-800-53-AU-5(b) + - NIST-800-53-CM-6(a) + - NIST-800-53-SC-24 + - audit_rules_system_shutdown + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Add Audit -f option into /etc/audit/rules.d/immutable.rules and /etc/audit/audit.rules + lineinfile: + path: '{{ item }}' + create: true + line: -f 2 + loop: + - /etc/audit/audit.rules + - /etc/audit/rules.d/immutable.rules + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83709-6 + - NIST-800-171-3.3.1 + - NIST-800-171-3.3.4 + - NIST-800-53-AU-5(b) + - NIST-800-53-CM-6(a) + - NIST-800-53-SC-24 + - audit_rules_system_shutdown + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# Traverse all of: +# +# /etc/audit/audit.rules, (for auditctl case) +# /etc/audit/rules.d/*.rules (for augenrules case) +find /etc/audit /etc/audit/rules.d -maxdepth 1 -type f -name '*.rules' -exec sed -i '/-f[[:space:]]\+.*/d' {} ';' + +for AUDIT_FILE in "/etc/audit/audit.rules" "/etc/audit/rules.d/immutable.rules" +do + echo '' >> $AUDIT_FILE + echo '# Set the audit.rules configuration to halt system upon audit failure per security requirements' >> $AUDIT_FILE + echo '-f 2' >> $AUDIT_FILE +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Events that Modify User/Group Information + If the auditd daemon is configured to use the +augenrules program to read audit rules during daemon startup (the +default), add the following lines to a file with suffix .rules in the +directory /etc/audit/rules.d, in order to capture events that modify +account changes: +-w /etc/group -p wa -k audit_rules_usergroup_modification +-w /etc/passwd -p wa -k audit_rules_usergroup_modification +-w /etc/gshadow -p wa -k audit_rules_usergroup_modification +-w /etc/shadow -p wa -k audit_rules_usergroup_modification +-w /etc/security/opasswd -p wa -k audit_rules_usergroup_modification + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file, in order to capture events that modify +account changes: +-w /etc/group -p wa -k audit_rules_usergroup_modification +-w /etc/passwd -p wa -k audit_rules_usergroup_modification +-w /etc/gshadow -p wa -k audit_rules_usergroup_modification +-w /etc/shadow -p wa -k audit_rules_usergroup_modification +-w /etc/security/opasswd -p wa -k audit_rules_usergroup_modification + This rule checks for multiple syscalls related to account changes; +it was written with DISA STIG in mind. Other policies should use a +separate rule for each syscall that needs to be checked. For example: +audit_rules_usergroup_modification_groupaudit_rules_usergroup_modification_gshadowaudit_rules_usergroup_modification_passwd + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 18 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 5.4.1.1 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + DSS06.03 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000018 + CCI-000130 + CCI-000172 + CCI-001403 + CCI-002130 + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.2.2 + 4.3.3.3.9 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.1 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.1.2 + A.6.2.1 + A.6.2.2 + A.7.1.1 + A.9.1.2 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.1 + A.9.4.2 + A.9.4.3 + A.9.4.4 + A.9.4.5 + CIP-004-6 R2.2.2 + CIP-004-6 R2.2.3 + CIP-007-3 R.1.3 + CIP-007-3 R5 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.3 + CIP-007-3 R5.2.1 + CIP-007-3 R5.2.3 + AC-2(4) + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-1 + PR.AC-3 + PR.AC-4 + PR.AC-6 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + Req-10.2.5 + SRG-OS-000004-GPOS-00004 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000239-GPOS-00089 + SRG-OS-000241-GPOS-00090 + SRG-OS-000241-GPOS-00091 + SRG-OS-000303-GPOS-00120 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + SRG-OS-000476-GPOS-00221 + In addition to auditing new user and group accounts, these watches +will alert the system administrator(s) to any modifications. Any unexpected +users, groups, or modifications should be investigated for legitimacy. + CCE-83715-3 + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + + +# If the audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# into the list of files to be inspected +files_to_inspect+=('/etc/audit/audit.rules') + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/etc/group" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/group $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/etc/group$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /etc/group -p wa -k audit_rules_usergroup_modification" >> "$audit_rules_file" + fi +done +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + +# If the audit is 'augenrules', then check if rule is already defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection. +# If rule isn't defined, add '/etc/audit/rules.d/audit_rules_usergroup_modification.rules' to list of files for inspection. +readarray -t matches < <(grep -HP "[\s]*-w[\s]+/etc/group" /etc/audit/rules.d/*.rules) + +# For each of the matched entries +for match in "${matches[@]}" +do + # Extract filepath from the match + rulesd_audit_file=$(echo $match | cut -f1 -d ':') + # Append that path into list of files for inspection + files_to_inspect+=("$rulesd_audit_file") +done +# Case when particular audit rule isn't defined yet +if [ "${#files_to_inspect[@]}" -eq "0" ] +then + # Append '/etc/audit/rules.d/audit_rules_usergroup_modification.rules' into list of files for inspection + key_rule_file="/etc/audit/rules.d/audit_rules_usergroup_modification.rules" + # If the audit_rules_usergroup_modification.rules file doesn't exist yet, create it with correct permissions + if [ ! -e "$key_rule_file" ] + then + touch "$key_rule_file" + chmod 0640 "$key_rule_file" + fi + files_to_inspect+=("$key_rule_file") +fi + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/etc/group" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/group $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/etc/group$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /etc/group -p wa -k audit_rules_usergroup_modification" >> "$audit_rules_file" + fi +done +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + + +# If the audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# into the list of files to be inspected +files_to_inspect+=('/etc/audit/audit.rules') + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/etc/passwd" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/passwd $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/etc/passwd$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /etc/passwd -p wa -k audit_rules_usergroup_modification" >> "$audit_rules_file" + fi +done +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + +# If the audit is 'augenrules', then check if rule is already defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection. +# If rule isn't defined, add '/etc/audit/rules.d/audit_rules_usergroup_modification.rules' to list of files for inspection. +readarray -t matches < <(grep -HP "[\s]*-w[\s]+/etc/passwd" /etc/audit/rules.d/*.rules) + +# For each of the matched entries +for match in "${matches[@]}" +do + # Extract filepath from the match + rulesd_audit_file=$(echo $match | cut -f1 -d ':') + # Append that path into list of files for inspection + files_to_inspect+=("$rulesd_audit_file") +done +# Case when particular audit rule isn't defined yet +if [ "${#files_to_inspect[@]}" -eq "0" ] +then + # Append '/etc/audit/rules.d/audit_rules_usergroup_modification.rules' into list of files for inspection + key_rule_file="/etc/audit/rules.d/audit_rules_usergroup_modification.rules" + # If the audit_rules_usergroup_modification.rules file doesn't exist yet, create it with correct permissions + if [ ! -e "$key_rule_file" ] + then + touch "$key_rule_file" + chmod 0640 "$key_rule_file" + fi + files_to_inspect+=("$key_rule_file") +fi + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/etc/passwd" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/passwd $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/etc/passwd$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /etc/passwd -p wa -k audit_rules_usergroup_modification" >> "$audit_rules_file" + fi +done +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + + +# If the audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# into the list of files to be inspected +files_to_inspect+=('/etc/audit/audit.rules') + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/etc/gshadow" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/gshadow $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/etc/gshadow$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /etc/gshadow -p wa -k audit_rules_usergroup_modification" >> "$audit_rules_file" + fi +done +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + +# If the audit is 'augenrules', then check if rule is already defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection. +# If rule isn't defined, add '/etc/audit/rules.d/audit_rules_usergroup_modification.rules' to list of files for inspection. +readarray -t matches < <(grep -HP "[\s]*-w[\s]+/etc/gshadow" /etc/audit/rules.d/*.rules) + +# For each of the matched entries +for match in "${matches[@]}" +do + # Extract filepath from the match + rulesd_audit_file=$(echo $match | cut -f1 -d ':') + # Append that path into list of files for inspection + files_to_inspect+=("$rulesd_audit_file") +done +# Case when particular audit rule isn't defined yet +if [ "${#files_to_inspect[@]}" -eq "0" ] +then + # Append '/etc/audit/rules.d/audit_rules_usergroup_modification.rules' into list of files for inspection + key_rule_file="/etc/audit/rules.d/audit_rules_usergroup_modification.rules" + # If the audit_rules_usergroup_modification.rules file doesn't exist yet, create it with correct permissions + if [ ! -e "$key_rule_file" ] + then + touch "$key_rule_file" + chmod 0640 "$key_rule_file" + fi + files_to_inspect+=("$key_rule_file") +fi + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/etc/gshadow" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/gshadow $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/etc/gshadow$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /etc/gshadow -p wa -k audit_rules_usergroup_modification" >> "$audit_rules_file" + fi +done +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + + +# If the audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# into the list of files to be inspected +files_to_inspect+=('/etc/audit/audit.rules') + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/etc/shadow" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/shadow $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/etc/shadow$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /etc/shadow -p wa -k audit_rules_usergroup_modification" >> "$audit_rules_file" + fi +done +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + +# If the audit is 'augenrules', then check if rule is already defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection. +# If rule isn't defined, add '/etc/audit/rules.d/audit_rules_usergroup_modification.rules' to list of files for inspection. +readarray -t matches < <(grep -HP "[\s]*-w[\s]+/etc/shadow" /etc/audit/rules.d/*.rules) + +# For each of the matched entries +for match in "${matches[@]}" +do + # Extract filepath from the match + rulesd_audit_file=$(echo $match | cut -f1 -d ':') + # Append that path into list of files for inspection + files_to_inspect+=("$rulesd_audit_file") +done +# Case when particular audit rule isn't defined yet +if [ "${#files_to_inspect[@]}" -eq "0" ] +then + # Append '/etc/audit/rules.d/audit_rules_usergroup_modification.rules' into list of files for inspection + key_rule_file="/etc/audit/rules.d/audit_rules_usergroup_modification.rules" + # If the audit_rules_usergroup_modification.rules file doesn't exist yet, create it with correct permissions + if [ ! -e "$key_rule_file" ] + then + touch "$key_rule_file" + chmod 0640 "$key_rule_file" + fi + files_to_inspect+=("$key_rule_file") +fi + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/etc/shadow" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/shadow $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/etc/shadow$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /etc/shadow -p wa -k audit_rules_usergroup_modification" >> "$audit_rules_file" + fi +done +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + + +# If the audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# into the list of files to be inspected +files_to_inspect+=('/etc/audit/audit.rules') + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/etc/security/opasswd" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/security/opasswd $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/etc/security/opasswd$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /etc/security/opasswd -p wa -k audit_rules_usergroup_modification" >> "$audit_rules_file" + fi +done +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + +# If the audit is 'augenrules', then check if rule is already defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection. +# If rule isn't defined, add '/etc/audit/rules.d/audit_rules_usergroup_modification.rules' to list of files for inspection. +readarray -t matches < <(grep -HP "[\s]*-w[\s]+/etc/security/opasswd" /etc/audit/rules.d/*.rules) + +# For each of the matched entries +for match in "${matches[@]}" +do + # Extract filepath from the match + rulesd_audit_file=$(echo $match | cut -f1 -d ':') + # Append that path into list of files for inspection + files_to_inspect+=("$rulesd_audit_file") +done +# Case when particular audit rule isn't defined yet +if [ "${#files_to_inspect[@]}" -eq "0" ] +then + # Append '/etc/audit/rules.d/audit_rules_usergroup_modification.rules' into list of files for inspection + key_rule_file="/etc/audit/rules.d/audit_rules_usergroup_modification.rules" + # If the audit_rules_usergroup_modification.rules file doesn't exist yet, create it with correct permissions + if [ ! -e "$key_rule_file" ] + then + touch "$key_rule_file" + chmod 0640 "$key_rule_file" + fi + files_to_inspect+=("$key_rule_file") +fi + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/etc/security/opasswd" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/security/opasswd $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/etc/security/opasswd$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /etc/security/opasswd -p wa -k audit_rules_usergroup_modification" >> "$audit_rules_file" + fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Events that Modify User/Group Information - /etc/group + If the auditd daemon is configured to use the +augenrules program to read audit rules during daemon startup (the +default), add the following lines to a file with suffix .rules in the +directory /etc/audit/rules.d, in order to capture events that modify +account changes: + +-w /etc/group -p wa -k audit_rules_usergroup_modification + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file, in order to capture events that modify +account changes: + +-w /etc/group -p wa -k audit_rules_usergroup_modification + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 18 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 5.4.1.1 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + DSS06.03 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000018 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-001403 + CCI-001404 + CCI-001405 + CCI-001683 + CCI-001684 + CCI-001685 + CCI-001686 + CCI-002130 + CCI-002132 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.2.2 + 4.3.3.3.9 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.1 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.1.2 + A.6.2.1 + A.6.2.2 + A.7.1.1 + A.9.1.2 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.1 + A.9.4.2 + A.9.4.3 + A.9.4.4 + A.9.4.5 + CIP-004-6 R2.2.2 + CIP-004-6 R2.2.3 + CIP-007-3 R.1.3 + CIP-007-3 R5 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.3 + CIP-007-3 R5.2.1 + CIP-007-3 R5.2.3 + AC-2(4) + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-1 + PR.AC-3 + PR.AC-4 + PR.AC-6 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.2.5 + SRG-OS-000004-GPOS-00004 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000304-GPOS-00121 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000470-GPOS-00214 + SRG-OS-000471-GPOS-00215 + SRG-OS-000239-GPOS-00089 + SRG-OS-000240-GPOS-00090 + SRG-OS-000241-GPOS-00091 + SRG-OS-000303-GPOS-00120 + SRG-OS-000466-GPOS-00210 + SRG-OS-000476-GPOS-00221 + SRG-OS-000004-VMM-000040 + SRG-OS-000239-VMM-000810 + SRG-OS-000240-VMM-000820 + SRG-OS-000241-VMM-000830 + SRG-OS-000274-VMM-000960 + SRG-OS-000275-VMM-000970 + SRG-OS-000276-VMM-000980 + SRG-OS-000277-VMM-000990 + SRG-OS-000303-VMM-001090 + SRG-OS-000304-VMM-001100 + SRG-OS-000476-VMM-001960 + In addition to auditing new user and group accounts, these watches +will alert the system administrator(s) to any modifications. Any unexpected +users, groups, or modifications should be investigated for legitimacy. + CCE-83722-9 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83722-9 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.5 + - audit_rules_usergroup_modification_group + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Check if watch rule for /etc/group already exists in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: ^\s*-w\s+/etc/group\s+-p\s+wa(\s|$)+ + patterns: '*.rules' + register: find_existing_watch_rules_d + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83722-9 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.5 + - audit_rules_usergroup_modification_group + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Search /etc/audit/rules.d for other rules with specified key audit_rules_usergroup_modification + find: + paths: /etc/audit/rules.d + contains: ^.*(?:-F key=|-k\s+)audit_rules_usergroup_modification$ + patterns: '*.rules' + register: find_watch_key + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched + == 0 + tags: + - CCE-83722-9 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.5 + - audit_rules_usergroup_modification_group + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Use /etc/audit/rules.d/audit_rules_usergroup_modification.rules as the recipient + for the rule + set_fact: + all_files: + - /etc/audit/rules.d/audit_rules_usergroup_modification.rules + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_watch_key.matched is defined and find_watch_key.matched == 0 and find_existing_watch_rules_d.matched + is defined and find_existing_watch_rules_d.matched == 0 + tags: + - CCE-83722-9 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.5 + - audit_rules_usergroup_modification_group + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Use matched file as the recipient for the rule + set_fact: + all_files: + - '{{ find_watch_key.files | map(attribute=''path'') | list | first }}' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_watch_key.matched is defined and find_watch_key.matched > 0 and find_existing_watch_rules_d.matched + is defined and find_existing_watch_rules_d.matched == 0 + tags: + - CCE-83722-9 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.5 + - audit_rules_usergroup_modification_group + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Add watch rule for /etc/group in /etc/audit/rules.d/ + lineinfile: + path: '{{ all_files[0] }}' + line: -w /etc/group -p wa -k audit_rules_usergroup_modification + create: true + mode: '0640' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched + == 0 + tags: + - CCE-83722-9 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.5 + - audit_rules_usergroup_modification_group + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Check if watch rule for /etc/group already exists in /etc/audit/audit.rules + find: + paths: /etc/audit/ + contains: ^\s*-w\s+/etc/group\s+-p\s+wa(\s|$)+ + patterns: audit.rules + register: find_existing_watch_audit_rules + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83722-9 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.5 + - audit_rules_usergroup_modification_group + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Add watch rule for /etc/group in /etc/audit/audit.rules + lineinfile: + line: -w /etc/group -p wa -k audit_rules_usergroup_modification + state: present + dest: /etc/audit/audit.rules + create: true + mode: '0640' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_audit_rules.matched is defined and find_existing_watch_audit_rules.matched + == 0 + tags: + - CCE-83722-9 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.5 + - audit_rules_usergroup_modification_group + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + + +# If the audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# into the list of files to be inspected +files_to_inspect+=('/etc/audit/audit.rules') + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/etc/group" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/group $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/etc/group$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /etc/group -p wa -k audit_rules_usergroup_modification" >> "$audit_rules_file" + fi +done +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + +# If the audit is 'augenrules', then check if rule is already defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection. +# If rule isn't defined, add '/etc/audit/rules.d/audit_rules_usergroup_modification.rules' to list of files for inspection. +readarray -t matches < <(grep -HP "[\s]*-w[\s]+/etc/group" /etc/audit/rules.d/*.rules) + +# For each of the matched entries +for match in "${matches[@]}" +do + # Extract filepath from the match + rulesd_audit_file=$(echo $match | cut -f1 -d ':') + # Append that path into list of files for inspection + files_to_inspect+=("$rulesd_audit_file") +done +# Case when particular audit rule isn't defined yet +if [ "${#files_to_inspect[@]}" -eq "0" ] +then + # Append '/etc/audit/rules.d/audit_rules_usergroup_modification.rules' into list of files for inspection + key_rule_file="/etc/audit/rules.d/audit_rules_usergroup_modification.rules" + # If the audit_rules_usergroup_modification.rules file doesn't exist yet, create it with correct permissions + if [ ! -e "$key_rule_file" ] + then + touch "$key_rule_file" + chmod 0640 "$key_rule_file" + fi + files_to_inspect+=("$key_rule_file") +fi + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/etc/group" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/group $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/etc/group$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /etc/group -p wa -k audit_rules_usergroup_modification" >> "$audit_rules_file" + fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Events that Modify User/Group Information - /etc/gshadow + If the auditd daemon is configured to use the +augenrules program to read audit rules during daemon startup (the +default), add the following lines to a file with suffix .rules in the +directory /etc/audit/rules.d, in order to capture events that modify +account changes: + +-w /etc/gshadow -p wa -k audit_rules_usergroup_modification + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file, in order to capture events that modify +account changes: + +-w /etc/gshadow -p wa -k audit_rules_usergroup_modification + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 18 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 5.4.1.1 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + DSS06.03 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000018 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-001403 + CCI-001404 + CCI-001405 + CCI-001683 + CCI-001684 + CCI-001685 + CCI-001686 + CCI-002130 + CCI-002132 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.2.2 + 4.3.3.3.9 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.1 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.1.2 + A.6.2.1 + A.6.2.2 + A.7.1.1 + A.9.1.2 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.1 + A.9.4.2 + A.9.4.3 + A.9.4.4 + A.9.4.5 + CIP-004-6 R2.2.2 + CIP-004-6 R2.2.3 + CIP-007-3 R.1.3 + CIP-007-3 R5 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.3 + CIP-007-3 R5.2.1 + CIP-007-3 R5.2.3 + AC-2(4) + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-1 + PR.AC-3 + PR.AC-4 + PR.AC-6 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.2.5 + SRG-OS-000004-GPOS-00004 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000304-GPOS-00121 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000470-GPOS-00214 + SRG-OS-000471-GPOS-00215 + SRG-OS-000239-GPOS-00089 + SRG-OS-000240-GPOS-00090 + SRG-OS-000241-GPOS-00091 + SRG-OS-000303-GPOS-00120 + SRG-OS-000466-GPOS-00210 + SRG-OS-000476-GPOS-00221 + SRG-OS-000004-VMM-000040 + SRG-OS-000239-VMM-000810 + SRG-OS-000240-VMM-000820 + SRG-OS-000241-VMM-000830 + SRG-OS-000274-VMM-000960 + SRG-OS-000275-VMM-000970 + SRG-OS-000276-VMM-000980 + SRG-OS-000277-VMM-000990 + SRG-OS-000303-VMM-001090 + SRG-OS-000304-VMM-001100 + SRG-OS-000476-VMM-001960 + In addition to auditing new user and group accounts, these watches +will alert the system administrator(s) to any modifications. Any unexpected +users, groups, or modifications should be investigated for legitimacy. + CCE-83723-7 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83723-7 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.5 + - audit_rules_usergroup_modification_gshadow + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Check if watch rule for /etc/gshadow already exists in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: ^\s*-w\s+/etc/gshadow\s+-p\s+wa(\s|$)+ + patterns: '*.rules' + register: find_existing_watch_rules_d + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83723-7 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.5 + - audit_rules_usergroup_modification_gshadow + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Search /etc/audit/rules.d for other rules with specified key audit_rules_usergroup_modification + find: + paths: /etc/audit/rules.d + contains: ^.*(?:-F key=|-k\s+)audit_rules_usergroup_modification$ + patterns: '*.rules' + register: find_watch_key + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched + == 0 + tags: + - CCE-83723-7 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.5 + - audit_rules_usergroup_modification_gshadow + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Use /etc/audit/rules.d/audit_rules_usergroup_modification.rules as the recipient + for the rule + set_fact: + all_files: + - /etc/audit/rules.d/audit_rules_usergroup_modification.rules + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_watch_key.matched is defined and find_watch_key.matched == 0 and find_existing_watch_rules_d.matched + is defined and find_existing_watch_rules_d.matched == 0 + tags: + - CCE-83723-7 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.5 + - audit_rules_usergroup_modification_gshadow + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Use matched file as the recipient for the rule + set_fact: + all_files: + - '{{ find_watch_key.files | map(attribute=''path'') | list | first }}' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_watch_key.matched is defined and find_watch_key.matched > 0 and find_existing_watch_rules_d.matched + is defined and find_existing_watch_rules_d.matched == 0 + tags: + - CCE-83723-7 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.5 + - audit_rules_usergroup_modification_gshadow + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Add watch rule for /etc/gshadow in /etc/audit/rules.d/ + lineinfile: + path: '{{ all_files[0] }}' + line: -w /etc/gshadow -p wa -k audit_rules_usergroup_modification + create: true + mode: '0640' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched + == 0 + tags: + - CCE-83723-7 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.5 + - audit_rules_usergroup_modification_gshadow + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Check if watch rule for /etc/gshadow already exists in /etc/audit/audit.rules + find: + paths: /etc/audit/ + contains: ^\s*-w\s+/etc/gshadow\s+-p\s+wa(\s|$)+ + patterns: audit.rules + register: find_existing_watch_audit_rules + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83723-7 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.5 + - audit_rules_usergroup_modification_gshadow + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Add watch rule for /etc/gshadow in /etc/audit/audit.rules + lineinfile: + line: -w /etc/gshadow -p wa -k audit_rules_usergroup_modification + state: present + dest: /etc/audit/audit.rules + create: true + mode: '0640' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_audit_rules.matched is defined and find_existing_watch_audit_rules.matched + == 0 + tags: + - CCE-83723-7 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.5 + - audit_rules_usergroup_modification_gshadow + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + + +# If the audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# into the list of files to be inspected +files_to_inspect+=('/etc/audit/audit.rules') + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/etc/gshadow" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/gshadow $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/etc/gshadow$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /etc/gshadow -p wa -k audit_rules_usergroup_modification" >> "$audit_rules_file" + fi +done +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + +# If the audit is 'augenrules', then check if rule is already defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection. +# If rule isn't defined, add '/etc/audit/rules.d/audit_rules_usergroup_modification.rules' to list of files for inspection. +readarray -t matches < <(grep -HP "[\s]*-w[\s]+/etc/gshadow" /etc/audit/rules.d/*.rules) + +# For each of the matched entries +for match in "${matches[@]}" +do + # Extract filepath from the match + rulesd_audit_file=$(echo $match | cut -f1 -d ':') + # Append that path into list of files for inspection + files_to_inspect+=("$rulesd_audit_file") +done +# Case when particular audit rule isn't defined yet +if [ "${#files_to_inspect[@]}" -eq "0" ] +then + # Append '/etc/audit/rules.d/audit_rules_usergroup_modification.rules' into list of files for inspection + key_rule_file="/etc/audit/rules.d/audit_rules_usergroup_modification.rules" + # If the audit_rules_usergroup_modification.rules file doesn't exist yet, create it with correct permissions + if [ ! -e "$key_rule_file" ] + then + touch "$key_rule_file" + chmod 0640 "$key_rule_file" + fi + files_to_inspect+=("$key_rule_file") +fi + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/etc/gshadow" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/gshadow $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/etc/gshadow$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /etc/gshadow -p wa -k audit_rules_usergroup_modification" >> "$audit_rules_file" + fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Events that Modify User/Group Information - /etc/security/opasswd + If the auditd daemon is configured to use the +augenrules program to read audit rules during daemon startup (the +default), add the following lines to a file with suffix .rules in the +directory /etc/audit/rules.d, in order to capture events that modify +account changes: + +-w /etc/security/opasswd -p wa -k audit_rules_usergroup_modification + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file, in order to capture events that modify +account changes: + +-w /etc/security/opasswd -p wa -k audit_rules_usergroup_modification + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 18 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 5.4.1.1 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + DSS06.03 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000018 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-001403 + CCI-001404 + CCI-001405 + CCI-001683 + CCI-001684 + CCI-001685 + CCI-001686 + CCI-002130 + CCI-002132 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.2.2 + 4.3.3.3.9 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.1 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.1.2 + A.6.2.1 + A.6.2.2 + A.7.1.1 + A.9.1.2 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.1 + A.9.4.2 + A.9.4.3 + A.9.4.4 + A.9.4.5 + CIP-004-6 R2.2.2 + CIP-004-6 R2.2.3 + CIP-007-3 R.1.3 + CIP-007-3 R5 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.3 + CIP-007-3 R5.2.1 + CIP-007-3 R5.2.3 + AC-2(4) + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-1 + PR.AC-3 + PR.AC-4 + PR.AC-6 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.2.5 + SRG-OS-000004-GPOS-00004 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000304-GPOS-00121 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000470-GPOS-00214 + SRG-OS-000471-GPOS-00215 + SRG-OS-000239-GPOS-00089 + SRG-OS-000240-GPOS-00090 + SRG-OS-000241-GPOS-00091 + SRG-OS-000303-GPOS-00120 + SRG-OS-000466-GPOS-00210 + SRG-OS-000476-GPOS-00221 + SRG-OS-000004-VMM-000040 + SRG-OS-000239-VMM-000810 + SRG-OS-000240-VMM-000820 + SRG-OS-000241-VMM-000830 + SRG-OS-000274-VMM-000960 + SRG-OS-000275-VMM-000970 + SRG-OS-000276-VMM-000980 + SRG-OS-000277-VMM-000990 + SRG-OS-000303-VMM-001090 + SRG-OS-000304-VMM-001100 + SRG-OS-000476-VMM-001960 + In addition to auditing new user and group accounts, these watches +will alert the system administrator(s) to any modifications. Any unexpected +users, groups, or modifications should be investigated for legitimacy. + CCE-83712-0 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83712-0 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.5 + - audit_rules_usergroup_modification_opasswd + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Check if watch rule for /etc/security/opasswd already exists in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: ^\s*-w\s+/etc/security/opasswd\s+-p\s+wa(\s|$)+ + patterns: '*.rules' + register: find_existing_watch_rules_d + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83712-0 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.5 + - audit_rules_usergroup_modification_opasswd + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Search /etc/audit/rules.d for other rules with specified key audit_rules_usergroup_modification + find: + paths: /etc/audit/rules.d + contains: ^.*(?:-F key=|-k\s+)audit_rules_usergroup_modification$ + patterns: '*.rules' + register: find_watch_key + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched + == 0 + tags: + - CCE-83712-0 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.5 + - audit_rules_usergroup_modification_opasswd + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Use /etc/audit/rules.d/audit_rules_usergroup_modification.rules as the recipient + for the rule + set_fact: + all_files: + - /etc/audit/rules.d/audit_rules_usergroup_modification.rules + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_watch_key.matched is defined and find_watch_key.matched == 0 and find_existing_watch_rules_d.matched + is defined and find_existing_watch_rules_d.matched == 0 + tags: + - CCE-83712-0 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.5 + - audit_rules_usergroup_modification_opasswd + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Use matched file as the recipient for the rule + set_fact: + all_files: + - '{{ find_watch_key.files | map(attribute=''path'') | list | first }}' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_watch_key.matched is defined and find_watch_key.matched > 0 and find_existing_watch_rules_d.matched + is defined and find_existing_watch_rules_d.matched == 0 + tags: + - CCE-83712-0 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.5 + - audit_rules_usergroup_modification_opasswd + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Add watch rule for /etc/security/opasswd in /etc/audit/rules.d/ + lineinfile: + path: '{{ all_files[0] }}' + line: -w /etc/security/opasswd -p wa -k audit_rules_usergroup_modification + create: true + mode: '0640' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched + == 0 + tags: + - CCE-83712-0 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.5 + - audit_rules_usergroup_modification_opasswd + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Check if watch rule for /etc/security/opasswd already exists in /etc/audit/audit.rules + find: + paths: /etc/audit/ + contains: ^\s*-w\s+/etc/security/opasswd\s+-p\s+wa(\s|$)+ + patterns: audit.rules + register: find_existing_watch_audit_rules + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83712-0 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.5 + - audit_rules_usergroup_modification_opasswd + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Add watch rule for /etc/security/opasswd in /etc/audit/audit.rules + lineinfile: + line: -w /etc/security/opasswd -p wa -k audit_rules_usergroup_modification + state: present + dest: /etc/audit/audit.rules + create: true + mode: '0640' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_audit_rules.matched is defined and find_existing_watch_audit_rules.matched + == 0 + tags: + - CCE-83712-0 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.5 + - audit_rules_usergroup_modification_opasswd + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + + +# If the audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# into the list of files to be inspected +files_to_inspect+=('/etc/audit/audit.rules') + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/etc/security/opasswd" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/security/opasswd $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/etc/security/opasswd$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /etc/security/opasswd -p wa -k audit_rules_usergroup_modification" >> "$audit_rules_file" + fi +done +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + +# If the audit is 'augenrules', then check if rule is already defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection. +# If rule isn't defined, add '/etc/audit/rules.d/audit_rules_usergroup_modification.rules' to list of files for inspection. +readarray -t matches < <(grep -HP "[\s]*-w[\s]+/etc/security/opasswd" /etc/audit/rules.d/*.rules) + +# For each of the matched entries +for match in "${matches[@]}" +do + # Extract filepath from the match + rulesd_audit_file=$(echo $match | cut -f1 -d ':') + # Append that path into list of files for inspection + files_to_inspect+=("$rulesd_audit_file") +done +# Case when particular audit rule isn't defined yet +if [ "${#files_to_inspect[@]}" -eq "0" ] +then + # Append '/etc/audit/rules.d/audit_rules_usergroup_modification.rules' into list of files for inspection + key_rule_file="/etc/audit/rules.d/audit_rules_usergroup_modification.rules" + # If the audit_rules_usergroup_modification.rules file doesn't exist yet, create it with correct permissions + if [ ! -e "$key_rule_file" ] + then + touch "$key_rule_file" + chmod 0640 "$key_rule_file" + fi + files_to_inspect+=("$key_rule_file") +fi + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/etc/security/opasswd" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/security/opasswd $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/etc/security/opasswd$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /etc/security/opasswd -p wa -k audit_rules_usergroup_modification" >> "$audit_rules_file" + fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Events that Modify User/Group Information - /etc/passwd + If the auditd daemon is configured to use the +augenrules program to read audit rules during daemon startup (the +default), add the following lines to a file with suffix .rules in the +directory /etc/audit/rules.d, in order to capture events that modify +account changes: + +-w /etc/passwd -p wa -k audit_rules_usergroup_modification + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file, in order to capture events that modify +account changes: + +-w /etc/passwd -p wa -k audit_rules_usergroup_modification + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 18 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 5.4.1.1 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + DSS06.03 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000018 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-001403 + CCI-001404 + CCI-001405 + CCI-001683 + CCI-001684 + CCI-001685 + CCI-001686 + CCI-002130 + CCI-002132 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.2.2 + 4.3.3.3.9 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.1 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.1.2 + A.6.2.1 + A.6.2.2 + A.7.1.1 + A.9.1.2 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.1 + A.9.4.2 + A.9.4.3 + A.9.4.4 + A.9.4.5 + CIP-004-6 R2.2.2 + CIP-004-6 R2.2.3 + CIP-007-3 R.1.3 + CIP-007-3 R5 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.3 + CIP-007-3 R5.2.1 + CIP-007-3 R5.2.3 + AC-2(4) + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-1 + PR.AC-3 + PR.AC-4 + PR.AC-6 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.2.5 + SRG-OS-000004-GPOS-00004 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000304-GPOS-00121 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000470-GPOS-00214 + SRG-OS-000471-GPOS-00215 + SRG-OS-000239-GPOS-00089 + SRG-OS-000240-GPOS-00090 + SRG-OS-000241-GPOS-00091 + SRG-OS-000303-GPOS-00120 + SRG-OS-000304-GPOS-00121 + SRG-OS-000466-GPOS-00210 + SRG-OS-000476-GPOS-00221 + SRG-OS-000274-GPOS-00104 + SRG-OS-000275-GPOS-00105 + SRG-OS-000276-GPOS-00106 + SRG-OS-000277-GPOS-00107 + SRG-OS-000004-VMM-000040 + SRG-OS-000239-VMM-000810 + SRG-OS-000240-VMM-000820 + SRG-OS-000241-VMM-000830 + SRG-OS-000274-VMM-000960 + SRG-OS-000275-VMM-000970 + SRG-OS-000276-VMM-000980 + SRG-OS-000277-VMM-000990 + SRG-OS-000303-VMM-001090 + SRG-OS-000304-VMM-001100 + SRG-OS-000476-VMM-001960 + In addition to auditing new user and group accounts, these watches +will alert the system administrator(s) to any modifications. Any unexpected +users, groups, or modifications should be investigated for legitimacy. + CCE-83714-6 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83714-6 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.5 + - audit_rules_usergroup_modification_passwd + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Check if watch rule for /etc/passwd already exists in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: ^\s*-w\s+/etc/passwd\s+-p\s+wa(\s|$)+ + patterns: '*.rules' + register: find_existing_watch_rules_d + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83714-6 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.5 + - audit_rules_usergroup_modification_passwd + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Search /etc/audit/rules.d for other rules with specified key audit_rules_usergroup_modification + find: + paths: /etc/audit/rules.d + contains: ^.*(?:-F key=|-k\s+)audit_rules_usergroup_modification$ + patterns: '*.rules' + register: find_watch_key + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched + == 0 + tags: + - CCE-83714-6 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.5 + - audit_rules_usergroup_modification_passwd + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Use /etc/audit/rules.d/audit_rules_usergroup_modification.rules as the recipient + for the rule + set_fact: + all_files: + - /etc/audit/rules.d/audit_rules_usergroup_modification.rules + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_watch_key.matched is defined and find_watch_key.matched == 0 and find_existing_watch_rules_d.matched + is defined and find_existing_watch_rules_d.matched == 0 + tags: + - CCE-83714-6 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.5 + - audit_rules_usergroup_modification_passwd + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Use matched file as the recipient for the rule + set_fact: + all_files: + - '{{ find_watch_key.files | map(attribute=''path'') | list | first }}' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_watch_key.matched is defined and find_watch_key.matched > 0 and find_existing_watch_rules_d.matched + is defined and find_existing_watch_rules_d.matched == 0 + tags: + - CCE-83714-6 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.5 + - audit_rules_usergroup_modification_passwd + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Add watch rule for /etc/passwd in /etc/audit/rules.d/ + lineinfile: + path: '{{ all_files[0] }}' + line: -w /etc/passwd -p wa -k audit_rules_usergroup_modification + create: true + mode: '0640' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched + == 0 + tags: + - CCE-83714-6 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.5 + - audit_rules_usergroup_modification_passwd + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Check if watch rule for /etc/passwd already exists in /etc/audit/audit.rules + find: + paths: /etc/audit/ + contains: ^\s*-w\s+/etc/passwd\s+-p\s+wa(\s|$)+ + patterns: audit.rules + register: find_existing_watch_audit_rules + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83714-6 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.5 + - audit_rules_usergroup_modification_passwd + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Add watch rule for /etc/passwd in /etc/audit/audit.rules + lineinfile: + line: -w /etc/passwd -p wa -k audit_rules_usergroup_modification + state: present + dest: /etc/audit/audit.rules + create: true + mode: '0640' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_audit_rules.matched is defined and find_existing_watch_audit_rules.matched + == 0 + tags: + - CCE-83714-6 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.5 + - audit_rules_usergroup_modification_passwd + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + + +# If the audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# into the list of files to be inspected +files_to_inspect+=('/etc/audit/audit.rules') + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/etc/passwd" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/passwd $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/etc/passwd$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /etc/passwd -p wa -k audit_rules_usergroup_modification" >> "$audit_rules_file" + fi +done +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + +# If the audit is 'augenrules', then check if rule is already defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection. +# If rule isn't defined, add '/etc/audit/rules.d/audit_rules_usergroup_modification.rules' to list of files for inspection. +readarray -t matches < <(grep -HP "[\s]*-w[\s]+/etc/passwd" /etc/audit/rules.d/*.rules) + +# For each of the matched entries +for match in "${matches[@]}" +do + # Extract filepath from the match + rulesd_audit_file=$(echo $match | cut -f1 -d ':') + # Append that path into list of files for inspection + files_to_inspect+=("$rulesd_audit_file") +done +# Case when particular audit rule isn't defined yet +if [ "${#files_to_inspect[@]}" -eq "0" ] +then + # Append '/etc/audit/rules.d/audit_rules_usergroup_modification.rules' into list of files for inspection + key_rule_file="/etc/audit/rules.d/audit_rules_usergroup_modification.rules" + # If the audit_rules_usergroup_modification.rules file doesn't exist yet, create it with correct permissions + if [ ! -e "$key_rule_file" ] + then + touch "$key_rule_file" + chmod 0640 "$key_rule_file" + fi + files_to_inspect+=("$key_rule_file") +fi + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/etc/passwd" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/passwd $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/etc/passwd$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /etc/passwd -p wa -k audit_rules_usergroup_modification" >> "$audit_rules_file" + fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Events that Modify User/Group Information - /etc/shadow + If the auditd daemon is configured to use the +augenrules program to read audit rules during daemon startup (the +default), add the following lines to a file with suffix .rules in the +directory /etc/audit/rules.d, in order to capture events that modify +account changes: + +-w /etc/shadow -p wa -k audit_rules_usergroup_modification + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file, in order to capture events that modify +account changes: + +-w /etc/shadow -p wa -k audit_rules_usergroup_modification + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 18 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 5.4.1.1 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + DSS06.03 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000018 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-001403 + CCI-001404 + CCI-001405 + CCI-001683 + CCI-001684 + CCI-001685 + CCI-001686 + CCI-002130 + CCI-002132 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.2.2 + 4.3.3.3.9 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.1 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.1.2 + A.6.2.1 + A.6.2.2 + A.7.1.1 + A.9.1.2 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.1 + A.9.4.2 + A.9.4.3 + A.9.4.4 + A.9.4.5 + CIP-004-6 R2.2.2 + CIP-004-6 R2.2.3 + CIP-007-3 R.1.3 + CIP-007-3 R5 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.3 + CIP-007-3 R5.2.1 + CIP-007-3 R5.2.3 + AC-2(4) + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-1 + PR.AC-3 + PR.AC-4 + PR.AC-6 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.2.5 + SRG-OS-000004-GPOS-00004 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000304-GPOS-00121 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000470-GPOS-00214 + SRG-OS-000471-GPOS-00215 + SRG-OS-000239-GPOS-00089 + SRG-OS-000240-GPOS-00090 + SRG-OS-000241-GPOS-00091 + SRG-OS-000303-GPOS-00120 + SRG-OS-000466-GPOS-00210 + SRG-OS-000476-GPOS-00221 + SRG-OS-000004-VMM-000040 + SRG-OS-000239-VMM-000810 + SRG-OS-000240-VMM-000820 + SRG-OS-000241-VMM-000830 + SRG-OS-000274-VMM-000960 + SRG-OS-000275-VMM-000970 + SRG-OS-000276-VMM-000980 + SRG-OS-000277-VMM-000990 + SRG-OS-000303-VMM-001090 + SRG-OS-000304-VMM-001100 + SRG-OS-000476-VMM-001960 + In addition to auditing new user and group accounts, these watches +will alert the system administrator(s) to any modifications. Any unexpected +users, groups, or modifications should be investigated for legitimacy. + CCE-83725-2 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83725-2 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.5 + - audit_rules_usergroup_modification_shadow + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Check if watch rule for /etc/shadow already exists in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: ^\s*-w\s+/etc/shadow\s+-p\s+wa(\s|$)+ + patterns: '*.rules' + register: find_existing_watch_rules_d + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83725-2 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.5 + - audit_rules_usergroup_modification_shadow + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Search /etc/audit/rules.d for other rules with specified key audit_rules_usergroup_modification + find: + paths: /etc/audit/rules.d + contains: ^.*(?:-F key=|-k\s+)audit_rules_usergroup_modification$ + patterns: '*.rules' + register: find_watch_key + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched + == 0 + tags: + - CCE-83725-2 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.5 + - audit_rules_usergroup_modification_shadow + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Use /etc/audit/rules.d/audit_rules_usergroup_modification.rules as the recipient + for the rule + set_fact: + all_files: + - /etc/audit/rules.d/audit_rules_usergroup_modification.rules + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_watch_key.matched is defined and find_watch_key.matched == 0 and find_existing_watch_rules_d.matched + is defined and find_existing_watch_rules_d.matched == 0 + tags: + - CCE-83725-2 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.5 + - audit_rules_usergroup_modification_shadow + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Use matched file as the recipient for the rule + set_fact: + all_files: + - '{{ find_watch_key.files | map(attribute=''path'') | list | first }}' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_watch_key.matched is defined and find_watch_key.matched > 0 and find_existing_watch_rules_d.matched + is defined and find_existing_watch_rules_d.matched == 0 + tags: + - CCE-83725-2 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.5 + - audit_rules_usergroup_modification_shadow + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Add watch rule for /etc/shadow in /etc/audit/rules.d/ + lineinfile: + path: '{{ all_files[0] }}' + line: -w /etc/shadow -p wa -k audit_rules_usergroup_modification + create: true + mode: '0640' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched + == 0 + tags: + - CCE-83725-2 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.5 + - audit_rules_usergroup_modification_shadow + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Check if watch rule for /etc/shadow already exists in /etc/audit/audit.rules + find: + paths: /etc/audit/ + contains: ^\s*-w\s+/etc/shadow\s+-p\s+wa(\s|$)+ + patterns: audit.rules + register: find_existing_watch_audit_rules + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83725-2 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.5 + - audit_rules_usergroup_modification_shadow + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Add watch rule for /etc/shadow in /etc/audit/audit.rules + lineinfile: + line: -w /etc/shadow -p wa -k audit_rules_usergroup_modification + state: present + dest: /etc/audit/audit.rules + create: true + mode: '0640' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_audit_rules.matched is defined and find_existing_watch_audit_rules.matched + == 0 + tags: + - CCE-83725-2 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.5 + - audit_rules_usergroup_modification_shadow + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + + +# If the audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# into the list of files to be inspected +files_to_inspect+=('/etc/audit/audit.rules') + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/etc/shadow" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/shadow $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/etc/shadow$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /etc/shadow -p wa -k audit_rules_usergroup_modification" >> "$audit_rules_file" + fi +done +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + +# If the audit is 'augenrules', then check if rule is already defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection. +# If rule isn't defined, add '/etc/audit/rules.d/audit_rules_usergroup_modification.rules' to list of files for inspection. +readarray -t matches < <(grep -HP "[\s]*-w[\s]+/etc/shadow" /etc/audit/rules.d/*.rules) + +# For each of the matched entries +for match in "${matches[@]}" +do + # Extract filepath from the match + rulesd_audit_file=$(echo $match | cut -f1 -d ':') + # Append that path into list of files for inspection + files_to_inspect+=("$rulesd_audit_file") +done +# Case when particular audit rule isn't defined yet +if [ "${#files_to_inspect[@]}" -eq "0" ] +then + # Append '/etc/audit/rules.d/audit_rules_usergroup_modification.rules' into list of files for inspection + key_rule_file="/etc/audit/rules.d/audit_rules_usergroup_modification.rules" + # If the audit_rules_usergroup_modification.rules file doesn't exist yet, create it with correct permissions + if [ ! -e "$key_rule_file" ] + then + touch "$key_rule_file" + chmod 0640 "$key_rule_file" + fi + files_to_inspect+=("$key_rule_file") +fi + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/etc/shadow" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/shadow $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/etc/shadow$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /etc/shadow -p wa -k audit_rules_usergroup_modification" >> "$audit_rules_file" + fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Access Events to Audit Log Directory + The audit system should collect access events to read audit log directory. +The following audit rule will assure that access to audit log directory are +collected. +-a always,exit -F dir=/var/log/audit/ -F perm=r -F auid>=1000 -F auid!=unset -F key=access-audit-trail +If the auditd daemon is configured to use the augenrules +program to read audit rules during daemon startup (the default), add the +rule to a file with suffix .rules in the directory +/etc/audit/rules.d. +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the rule to +/etc/audit/audit.rules file. + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + FAU_GEN.1.1.c + Attempts to read the logs should be recorded, suspicious access to audit log files could be an indicator of malicious activity on a system. +Auditing these events could serve as evidence of potential system compromise.' + + - name: Gather the package facts + package_facts: + manager: auto + tags: + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - directory_access_var_log_audit + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Perform remediation of Audit rules for /var/log/audit + block: + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + dir=/var/log/audit/ -F perm=r -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access-audit-trail.rules + set_fact: audit_file="/etc/audit/rules.d/access-audit-trail.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F dir=/var/log/audit/ -F perm=r -F + auid>=1000 -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F dir=/var/log/audit/ -F perm=r + -F auid>=1000 -F auid!=unset -F key=access-audit-trail + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + dir=/var/log/audit/ -F perm=r -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:( + -S |,)\w+)+)( -F dir=/var/log/audit/ -F perm=r -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F dir=/var/log/audit/ -F perm=r + -F auid>=1000 -F auid!=unset -F key=access-audit-trail + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - directory_access_var_log_audit + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +ACTION_ARCH_FILTERS="-a always,exit" +OTHER_FILTERS="-F dir=/var/log/audit/ -F perm=r" +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="" +KEY="access-audit-trail" +SYSCALL_GROUPING="" +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + System Audit Directories Must Be Group Owned By Root + All audit directories must be group owned by root user. By default, the path for audit log is /var/log/audit/. + +To properly set the group owner of /var/log/audit, run the command: +$ sudo chgrp root /var/log/audit + +If log_group in /etc/audit/auditd.conf is set to a group other than the root +group account, change the group ownership of the audit directories to this specific group. + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 18 + 19 + 3 + 4 + 5 + 6 + 7 + 8 + 5.4.1.1 + APO01.06 + APO11.04 + APO12.06 + BAI03.05 + BAI08.02 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS05.04 + DSS05.07 + DSS06.02 + MEA02.01 + 3.3.1 + CCI-000162 + CCI-000163 + CCI-000164 + CCI-001314 + 4.2.3.10 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.7.3 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.1 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 5.2 + SR 6.1 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-6(a) + AC-6(1) + AU-9(4) + DE.AE-3 + DE.AE-5 + PR.AC-4 + PR.DS-5 + PR.PT-1 + RS.AN-1 + RS.AN-4 + Req-10.5.1 + SRG-OS-000057-GPOS-00027 + SRG-OS-000058-GPOS-00028 + SRG-OS-000059-GPOS-00029 + SRG-OS-000206-GPOS-00084 + Unauthorized disclosure of audit records can reveal system and configuration data to +attackers, thus compromising its confidentiality. + CCE-90516-6 + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +if LC_ALL=C grep -m 1 -q ^log_group /etc/audit/auditd.conf; then + GROUP=$(awk -F "=" '/log_group/ {print $2}' /etc/audit/auditd.conf | tr -d ' ') +else + GROUP=root +fi +if LC_ALL=C grep -iw ^log_file /etc/audit/auditd.conf; then + DIR=$(awk -F "=" '/^log_file/ {print $2}' /etc/audit/auditd.conf | tr -d ' ' | rev | cut -d"/" -f2- | rev) +else + DIR="/var/log/audit" +fi + + +find ${DIR} -type d -exec chgrp ${GROUP} {} \; + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + System Audit Directories Must Be Owned By Root + All audit directories must be owned by root user. By default, the path for audit log is /var/log/audit/. + +To properly set the owner of /var/log/audit, run the command: +$ sudo chown root /var/log/audit + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 18 + 19 + 3 + 4 + 5 + 6 + 7 + 8 + 5.4.1.1 + APO01.06 + APO11.04 + APO12.06 + BAI03.05 + BAI08.02 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS05.04 + DSS05.07 + DSS06.02 + MEA02.01 + 3.3.1 + CCI-000162 + CCI-000163 + CCI-000164 + CCI-001314 + 4.2.3.10 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.7.3 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.1 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 5.2 + SR 6.1 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-6(a) + AC-6(1) + AU-9(4) + DE.AE-3 + DE.AE-5 + PR.AC-4 + PR.DS-5 + PR.PT-1 + RS.AN-1 + RS.AN-4 + Req-10.5.1 + SRG-OS-000057-GPOS-00027 + SRG-OS-000058-GPOS-00028 + SRG-OS-000059-GPOS-00029 + SRG-OS-000206-GPOS-00084 + Unauthorized disclosure of audit records can reveal system and configuration data to +attackers, thus compromising its confidentiality. + CCE-85869-6 + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +if LC_ALL=C grep -iw ^log_file /etc/audit/auditd.conf; then + FILE=$(awk -F "=" '/^log_file/ {print $2}' /etc/audit/auditd.conf | tr -d ' ') + LOGPATH="$(dirname "$FILE")" + chown root $LOGPATH +else + chown root /var/log/audit +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + System Audit Logs Must Have Mode 0750 or Less Permissive + +If log_group in /etc/audit/auditd.conf is set to a group other than the root +group account, change the mode of the audit log files with the following command: +$ sudo chmod 0750 /var/log/audit + +Otherwise, change the mode of the audit log files with the following command: +$ sudo chmod 0700 /var/log/audit + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 18 + 19 + 3 + 4 + 5 + 6 + 7 + 8 + APO01.06 + APO11.04 + APO12.06 + BAI03.05 + BAI08.02 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS05.04 + DSS05.07 + DSS06.02 + MEA02.01 + CCI-000162 + CCI-000163 + CCI-000164 + 4.2.3.10 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.7.3 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.1 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 5.2 + SR 6.1 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.2 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-004-6 R3.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CIP-007-3 R6.5 + CM-6(a) + AC-6(1) + AU-9 + DE.AE-3 + DE.AE-5 + PR.AC-4 + PR.DS-5 + PR.PT-1 + RS.AN-1 + RS.AN-4 + SRG-OS-000057-GPOS-00027 + SRG-OS-000058-GPOS-00028 + SRG-OS-000059-GPOS-00029 + If users can write to audit logs, audit trails can be modified or destroyed. + CCE-83734-4 + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +if LC_ALL=C grep -iw ^log_file /etc/audit/auditd.conf; then + DIR=$(awk -F "=" '/^log_file/ {print $2}' /etc/audit/auditd.conf | tr -d ' ' | rev | cut -d"/" -f2- | rev) +else + DIR="/var/log/audit" +fi + + +if LC_ALL=C grep -m 1 -q ^log_group /etc/audit/auditd.conf; then + GROUP=$(awk -F "=" '/log_group/ {print $2}' /etc/audit/auditd.conf | tr -d ' ') + if ! [ "${GROUP}" == 'root' ] ; then + chmod 0750 $DIR + else + chmod 0700 $DIR + fi +else + chmod 0700 $DIR +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + System Audit Logs Must Be Group Owned By Root + All audit logs must be group owned by root user. The path for audit log can +be configured via log_file parameter in /etc/audit/auditd.conf +or, by default, the path for audit log is /var/log/audit/. + +To properly set the group owner of /var/log/audit/*, run the command: +$ sudo chgrp root /var/log/audit/* + +If log_group in /etc/audit/auditd.conf is set to a group other +than the root group account, change the group ownership of the audit logs +to this specific group. + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 18 + 19 + 3 + 4 + 5 + 6 + 7 + 8 + 5.4.1.1 + APO01.06 + APO11.04 + APO12.06 + BAI03.05 + BAI08.02 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS05.04 + DSS05.07 + DSS06.02 + MEA02.01 + 3.3.1 + CCI-000162 + CCI-000163 + CCI-000164 + CCI-001314 + 4.2.3.10 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.7.3 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.1 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 5.2 + SR 6.1 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-6(a) + AC-6(1) + AU-9(4) + DE.AE-3 + DE.AE-5 + PR.AC-4 + PR.DS-5 + PR.PT-1 + RS.AN-1 + RS.AN-4 + Req-10.5.1 + SRG-OS-000057-GPOS-00027 + SRG-OS-000058-GPOS-00028 + SRG-OS-000059-GPOS-00029 + SRG-OS-000206-GPOS-00084 + Unauthorized disclosure of audit records can reveal system and configuration data to +attackers, thus compromising its confidentiality. + CCE-89603-5 + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +if LC_ALL=C grep -iw log_file /etc/audit/auditd.conf; then + FILE=$(awk -F "=" '/^log_file/ {print $2}' /etc/audit/auditd.conf | tr -d ' ') +else + FILE="/var/log/audit/audit.log" +fi + + +if LC_ALL=C grep -m 1 -q ^log_group /etc/audit/auditd.conf; then + GROUP=$(awk -F "=" '/log_group/ {print $2}' /etc/audit/auditd.conf | tr -d ' ') + if ! [ "${GROUP}" == 'root' ]; then + chgrp ${GROUP} $FILE* + else + chgrp root $FILE* + fi +else + chgrp root $FILE* +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Audit Configuration Files Must Be Owned By Group root + All audit configuration files must be owned by group root. +chown :root /etc/audit/audit*.{rules,conf} /etc/audit/rules.d/* + CCI-000171 + SRG-OS-000063-GPOS-00032 + Without the capability to restrict which roles and individuals can +select which events are audited, unauthorized personnel may be able +to prevent the auditing of critical events. +Misconfigured audits may degrade the system's performance by +overwhelming the audit log. Misconfigured audits may also make it more +difficult to establish, correlate, and investigate the events relating +to an incident or identify those responsible for one. + - name: Gather the package facts + package_facts: + manager: auto + tags: + - configure_strategy + - file_groupownership_audit_configuration + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Find /etc/audit/ file(s) matching ^audit(\.rules|d\.conf)$ + command: find -H /etc/audit/ -maxdepth 1 -type f ! -gid 0 -regex "^audit(\.rules|d\.conf)$" + register: files_found + changed_when: false + failed_when: false + check_mode: false + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - configure_strategy + - file_groupownership_audit_configuration + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure group owner on /etc/audit/ file(s) matching ^audit(\.rules|d\.conf)$ + file: + path: '{{ item }}' + group: '0' + state: file + with_items: + - '{{ files_found.stdout_lines }}' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - configure_strategy + - file_groupownership_audit_configuration + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Find /etc/audit/rules.d/ file(s) matching ^.*\.rules$ + command: find -H /etc/audit/rules.d/ -maxdepth 1 -type f ! -gid 0 -regex "^.*\.rules$" + register: files_found + changed_when: false + failed_when: false + check_mode: false + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - configure_strategy + - file_groupownership_audit_configuration + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure group owner on /etc/audit/rules.d/ file(s) matching ^.*\.rules$ + file: + path: '{{ item }}' + group: '0' + state: file + with_items: + - '{{ files_found.stdout_lines }}' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - configure_strategy + - file_groupownership_audit_configuration + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +find /etc/audit/ -maxdepth 1 -type f ! -gid 0 -regex '^audit(\.rules|d\.conf)$' -exec chgrp 0 {} \; + + +find /etc/audit/rules.d/ -maxdepth 1 -type f ! -gid 0 -regex '^.*\.rules$' -exec chgrp 0 {} \; + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Audit Configuration Files Must Be Owned By Root + All audit configuration files must be owned by root user. + +To properly set the owner of /etc/audit/, run the command: +$ sudo chown root /etc/audit/ + +To properly set the owner of /etc/audit/rules.d/, run the command: +$ sudo chown root /etc/audit/rules.d/ + CCI-000171 + SRG-OS-000063-GPOS-00032 + Without the capability to restrict which roles and individuals can +select which events are audited, unauthorized personnel may be able +to prevent the auditing of critical events. +Misconfigured audits may degrade the system's performance by +overwhelming the audit log. Misconfigured audits may also make it more +difficult to establish, correlate, and investigate the events relating +to an incident or identify those responsible for one. + - name: Gather the package facts + package_facts: + manager: auto + tags: + - configure_strategy + - file_ownership_audit_configuration + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Find /etc/audit/ file(s) matching ^audit(\.rules|d\.conf)$ + command: find -H /etc/audit/ -maxdepth 1 -type f ! -uid 0 -regex "^audit(\.rules|d\.conf)$" + register: files_found + changed_when: false + failed_when: false + check_mode: false + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - configure_strategy + - file_ownership_audit_configuration + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure owner on /etc/audit/ file(s) matching ^audit(\.rules|d\.conf)$ + file: + path: '{{ item }}' + owner: '0' + state: file + with_items: + - '{{ files_found.stdout_lines }}' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - configure_strategy + - file_ownership_audit_configuration + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Find /etc/audit/rules.d/ file(s) matching ^.*\.rules$ + command: find -H /etc/audit/rules.d/ -maxdepth 1 -type f ! -uid 0 -regex "^.*\.rules$" + register: files_found + changed_when: false + failed_when: false + check_mode: false + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - configure_strategy + - file_ownership_audit_configuration + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure owner on /etc/audit/rules.d/ file(s) matching ^.*\.rules$ + file: + path: '{{ item }}' + owner: '0' + state: file + with_items: + - '{{ files_found.stdout_lines }}' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - configure_strategy + - file_ownership_audit_configuration + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +find /etc/audit/ -maxdepth 1 -type f ! -uid 0 -regex '^audit(\.rules|d\.conf)$' -exec chown 0 {} \; + +find /etc/audit/rules.d/ -maxdepth 1 -type f ! -uid 0 -regex '^.*\.rules$' -exec chown 0 {} \; + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + System Audit Logs Must Be Owned By Root + All audit logs must be owned by root user and group. By default, the path for audit log is /var/log/audit/. + +To properly set the owner of /var/log/audit, run the command: +$ sudo chown root /var/log/audit + +To properly set the owner of /var/log/audit/*, run the command: +$ sudo chown root /var/log/audit/* + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 18 + 19 + 3 + 4 + 5 + 6 + 7 + 8 + 5.4.1.1 + APO01.06 + APO11.04 + APO12.06 + BAI03.05 + BAI08.02 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS05.04 + DSS05.07 + DSS06.02 + MEA02.01 + 3.3.1 + CCI-000162 + CCI-000163 + CCI-000164 + CCI-001314 + 4.2.3.10 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.7.3 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.1 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 5.2 + SR 6.1 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-6(a) + AC-6(1) + AU-9(4) + DE.AE-3 + DE.AE-5 + PR.AC-4 + PR.DS-5 + PR.PT-1 + RS.AN-1 + RS.AN-4 + Req-10.5.1 + SRG-OS-000057-GPOS-00027 + SRG-OS-000058-GPOS-00028 + SRG-OS-000059-GPOS-00029 + Unauthorized disclosure of audit records can reveal system and configuration data to +attackers, thus compromising its confidentiality. + CCE-83726-0 + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +if LC_ALL=C grep -m 1 -q ^log_group /etc/audit/auditd.conf; then + GROUP=$(awk -F "=" '/log_group/ {print $2}' /etc/audit/auditd.conf | tr -d ' ') + if ! [ "${GROUP}" == 'root' ] ; then + chown root:${GROUP} /var/log/audit + chown root:${GROUP} /var/log/audit/audit.log* + else + chown root:root /var/log/audit + chown root:root /var/log/audit/audit.log* + fi +else + chown root:root /var/log/audit + chown root:root /var/log/audit/audit.log* +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + System Audit Logs Must Be Owned By Root + All audit logs must be owned by root user. The path for audit log can be +configured via log_file parameter in /etc/audit/auditd.conf +or by default, the path for audit log is /var/log/audit/. + +To properly set the owner of /var/log/audit/*, run the command: +$ sudo chown root /var/log/audit/* + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 18 + 19 + 3 + 4 + 5 + 6 + 7 + 8 + 5.4.1.1 + APO01.06 + APO11.04 + APO12.06 + BAI03.05 + BAI08.02 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS05.04 + DSS05.07 + DSS06.02 + MEA02.01 + 3.3.1 + CCI-000162 + CCI-000163 + CCI-000164 + CCI-001314 + 4.2.3.10 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.7.3 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.1 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 5.2 + SR 6.1 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-6(a) + AC-6(1) + AU-9(4) + DE.AE-3 + DE.AE-5 + PR.AC-4 + PR.DS-5 + PR.PT-1 + RS.AN-1 + RS.AN-4 + Req-10.5.1 + SRG-OS-000057-GPOS-00027 + SRG-OS-000058-GPOS-00028 + SRG-OS-000059-GPOS-00029 + SRG-OS-000206-GPOS-00084 + Unauthorized disclosure of audit records can reveal system and configuration data to +attackers, thus compromising its confidentiality. + CCE-89952-6 + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +if LC_ALL=C grep -iw log_file /etc/audit/auditd.conf; then + FILE=$(awk -F "=" '/^log_file/ {print $2}' /etc/audit/auditd.conf | tr -d ' ') + chown root $FILE* +else + chown root /var/log/audit/audit.log* +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + System Audit Logs Must Have Mode 0640 or Less Permissive + +Determine where the audit logs are stored with the following command: +$ sudo grep -iw log_file /etc/audit/auditd.conf +log_file = /var/log/audit/audit.log +Configure the audit log to be protected from unauthorized read access by setting the correct +permissive mode with the following command: +$ sudo chmod 0600 audit_log_file +By default, audit_log_file is "/var/log/audit/audit.log". + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 18 + 19 + 3 + 4 + 5 + 6 + 7 + 8 + 5.4.1.1 + APO01.06 + APO11.04 + APO12.06 + BAI03.05 + BAI08.02 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS05.04 + DSS05.07 + DSS06.02 + MEA02.01 + 3.3.1 + CCI-000162 + CCI-000163 + CCI-000164 + CCI-001314 + 4.2.3.10 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.7.3 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.1 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 5.2 + SR 6.1 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-6(a) + AC-6(1) + AU-9(4) + DE.AE-3 + DE.AE-5 + PR.AC-4 + PR.DS-5 + PR.PT-1 + RS.AN-1 + RS.AN-4 + Req-10.5 + SRG-OS-000057-GPOS-00027 + SRG-OS-000058-GPOS-00028 + SRG-OS-000059-GPOS-00029 + SRG-OS-000206-GPOS-00084 + If users can write to audit logs, audit trails can be modified or destroyed. + CCE-83720-3 + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +if LC_ALL=C grep -iw ^log_file /etc/audit/auditd.conf; then + FILE=$(awk -F "=" '/^log_file/ {print $2}' /etc/audit/auditd.conf | tr -d ' ') +else + FILE="/var/log/audit/audit.log" +fi + + +chmod 0600 $FILE + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Events that Modify the System's Discretionary Access Controls + At a minimum, the audit system should collect file permission +changes for all users and root. Note that the "-F arch=b32" lines should be +present even on a 64 bit system. These commands identify system calls for +auditing. Even if the system is 64 bit it can still execute 32 bit system +calls. Additionally, these rules can be configured in a number of ways while +still achieving the desired effect. An example of this is that the "-S" calls +could be split up and placed on separate lines, however, this is less efficient. +Add the following to /etc/audit/audit.rules: +-a always,exit -F arch=b32 -S chmod,fchmod,fchmodat -F auid>=1000 -F auid!=unset -F key=perm_mod + -a always,exit -F arch=b32 -S chown,fchown,fchownat,lchown -F auid>=1000 -F auid!=unset -F key=perm_mod + -a always,exit -F arch=b32 -S setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F auid>=1000 -F auid!=unset -F key=perm_mod +If your system is 64 bit then these lines should be duplicated and the +arch=b32 replaced with arch=b64 as follows: +-a always,exit -F arch=b64 -S chmod,fchmod,fchmodat -F auid>=1000 -F auid!=unset -F key=perm_mod + -a always,exit -F arch=b64 -S chown,fchown,fchownat,lchown -F auid>=1000 -F auid!=unset -F key=perm_mod + -a always,exit -F arch=b64 -S setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F auid>=1000 -F auid!=unset -F key=perm_mod + + Record Events that Modify the System's Discretionary Access Controls - chmod + At a minimum, the audit system should collect file permission +changes for all users and root. If the auditd daemon is configured to +use the augenrules program to read audit rules during daemon startup +(the default), add the following line to a file with suffix .rules in +the directory /etc/audit/rules.d: +-a always,exit -F arch=b32 -S chmod -F auid>=1000 -F auid!=unset -F key=perm_mod +If the system is 64 bit then also add the following line: +-a always,exit -F arch=b64 -S chmod -F auid>=1000 -F auid!=unset -F key=perm_mod +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following line to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S chmod -F auid>=1000 -F auid!=unset -F key=perm_mod +If the system is 64 bit then also add the following line: +-a always,exit -F arch=b64 -S chmod -F auid>=1000 -F auid!=unset -F key=perm_mod + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping these system +calls with others as identifying earlier in this guide is more efficient. + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 5.4.1.1 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000126 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.5.5 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + SRG-OS-000064-GPOS-00033 + SRG-OS-000466-GPOS-00210 + SRG-OS-000458-GPOS-00203 + SRG-OS-000458-VMM-001810 + SRG-OS-000474-VMM-001940 + The changing of file permissions could indicate that a user is attempting to +gain access to information that would otherwise be disallowed. Auditing DAC modifications +can facilitate the identification of patterns of abuse among both authorized and +unauthorized users. + CCE-83830-0 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83830-0 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_chmod + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit chmod tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83830-0 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_chmod + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for chmod for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - chmod + syscall_grouping: + - chmod + - fchmod + - fchmodat + + - name: Check existence of chmod in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules + set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - chmod + syscall_grouping: + - chmod + - fchmod + - fchmodat + + - name: Check existence of chmod in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83830-0 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_chmod + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for chmod for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - chmod + syscall_grouping: + - chmod + - fchmod + - fchmodat + + - name: Check existence of chmod in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules + set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - chmod + syscall_grouping: + - chmod + - fchmod + - fchmodat + + - name: Check existence of chmod in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-83830-0 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_chmod + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="" + AUID_FILTERS="-F auid>=1000 -F auid!=unset" + SYSCALL="chmod" + KEY="perm_mod" + SYSCALL_GROUPING="chmod fchmod fchmodat" + + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Events that Modify the System's Discretionary Access Controls - chown + At a minimum, the audit system should collect file permission +changes for all users and root. If the auditd daemon is configured to +use the augenrules program to read audit rules during daemon startup +(the default), add the following line to a file with suffix .rules in +the directory /etc/audit/rules.d: +-a always,exit -F arch=b32 -S chown -F auid>=1000 -F auid!=unset -F key=perm_mod +If the system is 64 bit then also add the following line: +-a always,exit -F arch=b64 -S chown -F auid>=1000 -F auid!=unset -F key=perm_mod +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following line to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S chown -F auid>=1000 -F auid!=unset -F key=perm_mod +If the system is 64 bit then also add the following line: +-a always,exit -F arch=b64 -S chown -F auid>=1000 -F auid!=unset -F key=perm_mod + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping these system +calls with others as identifying earlier in this guide is more efficient. + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 5.4.1.1 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000126 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.5.5 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + SRG-OS-000064-GPOS-00033 + SRG-OS-000466-GPOS-00210 + SRG-OS-000458-GPOS-00203 + SRG-OS-000474-GPOS-00219 + SRG-OS-000458-VMM-001810 + SRG-OS-000474-VMM-001940 + The changing of file permissions could indicate that a user is attempting to +gain access to information that would otherwise be disallowed. Auditing DAC modifications +can facilitate the identification of patterns of abuse among both authorized and +unauthorized users. + CCE-83812-8 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83812-8 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_chown + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit chown tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83812-8 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_chown + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for chown for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - chown + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of chown in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules + set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - chown + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of chown in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83812-8 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_chown + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for chown for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - chown + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of chown in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules + set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - chown + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of chown in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-83812-8 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_chown + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="" + AUID_FILTERS="-F auid>=1000 -F auid!=unset" + SYSCALL="chown" + KEY="perm_mod" + SYSCALL_GROUPING="chown fchown fchownat lchown" + + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Events that Modify the System's Discretionary Access Controls - fchmod + At a minimum, the audit system should collect file permission +changes for all users and root. If the auditd daemon is configured to +use the augenrules program to read audit rules during daemon startup +(the default), add the following line to a file with suffix .rules in +the directory /etc/audit/rules.d: +-a always,exit -F arch=b32 -S fchmod -F auid>=1000 -F auid!=unset -F key=perm_mod +If the system is 64 bit then also add the following line: +-a always,exit -F arch=b64 -S fchmod -F auid>=1000 -F auid!=unset -F key=perm_mod +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following line to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S fchmod -F auid>=1000 -F auid!=unset -F key=perm_mod +If the system is 64 bit then also add the following line: +-a always,exit -F arch=b64 -S fchmod -F auid>=1000 -F auid!=unset -F key=perm_mod + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping these system +calls with others as identifying earlier in this guide is more efficient. + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 5.4.1.1 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000126 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.5.5 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + SRG-OS-000064-GPOS-00033 + SRG-OS-000466-GPOS-00210 + SRG-OS-000458-GPOS-00203 + SRG-OS-000458-VMM-001810 + SRG-OS-000474-VMM-001940 + The changing of file permissions could indicate that a user is attempting to +gain access to information that would otherwise be disallowed. Auditing DAC modifications +can facilitate the identification of patterns of abuse among both authorized and +unauthorized users. + CCE-83832-6 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83832-6 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_fchmod + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit fchmod tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83832-6 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_fchmod + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for fchmod for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchmod + syscall_grouping: + - chmod + - fchmod + - fchmodat + + - name: Check existence of fchmod in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules + set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchmod + syscall_grouping: + - chmod + - fchmod + - fchmodat + + - name: Check existence of fchmod in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83832-6 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_fchmod + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for fchmod for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchmod + syscall_grouping: + - chmod + - fchmod + - fchmodat + + - name: Check existence of fchmod in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules + set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchmod + syscall_grouping: + - chmod + - fchmod + - fchmodat + + - name: Check existence of fchmod in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-83832-6 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_fchmod + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="" + AUID_FILTERS="-F auid>=1000 -F auid!=unset" + SYSCALL="fchmod" + KEY="perm_mod" + SYSCALL_GROUPING="chmod fchmod fchmodat" + + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Events that Modify the System's Discretionary Access Controls - fchmodat + At a minimum, the audit system should collect file permission +changes for all users and root. If the auditd daemon is configured to +use the augenrules program to read audit rules during daemon startup +(the default), add the following line to a file with suffix .rules in +the directory /etc/audit/rules.d: +-a always,exit -F arch=b32 -S fchmodat -F auid>=1000 -F auid!=unset -F key=perm_mod +If the system is 64 bit then also add the following line: +-a always,exit -F arch=b64 -S fchmodat -F auid>=1000 -F auid!=unset -F key=perm_mod +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following line to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S fchmodat -F auid>=1000 -F auid!=unset -F key=perm_mod +If the system is 64 bit then also add the following line: +-a always,exit -F arch=b64 -S fchmodat -F auid>=1000 -F auid!=unset -F key=perm_mod + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping these system +calls with others as identifying earlier in this guide is more efficient. + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 5.4.1.1 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000126 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.5.5 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + SRG-OS-000064-GPOS-00033 + SRG-OS-000466-GPOS-00210 + SRG-OS-000458-GPOS-00203 + SRG-OS-000458-VMM-001810 + SRG-OS-000474-VMM-001940 + The changing of file permissions could indicate that a user is attempting to +gain access to information that would otherwise be disallowed. Auditing DAC modifications +can facilitate the identification of patterns of abuse among both authorized and +unauthorized users. + CCE-83822-7 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83822-7 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_fchmodat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit fchmodat tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83822-7 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_fchmodat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for fchmodat for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchmodat + syscall_grouping: + - chmod + - fchmod + - fchmodat + + - name: Check existence of fchmodat in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules + set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchmodat + syscall_grouping: + - chmod + - fchmod + - fchmodat + + - name: Check existence of fchmodat in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83822-7 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_fchmodat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for fchmodat for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchmodat + syscall_grouping: + - chmod + - fchmod + - fchmodat + + - name: Check existence of fchmodat in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules + set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchmodat + syscall_grouping: + - chmod + - fchmod + - fchmodat + + - name: Check existence of fchmodat in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-83822-7 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_fchmodat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="" + AUID_FILTERS="-F auid>=1000 -F auid!=unset" + SYSCALL="fchmodat" + KEY="perm_mod" + SYSCALL_GROUPING="chmod fchmod fchmodat" + + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Events that Modify the System's Discretionary Access Controls - fchown + At a minimum, the audit system should collect file permission +changes for all users and root. If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following line to a file with suffix +.rules in the directory /etc/audit/rules.d: +-a always,exit -F arch=b32 -S fchown -F auid>=1000 -F auid!=unset -F key=perm_mod + +If the system is 64 bit then also add the following line: +-a always,exit -F arch=b64 -S fchown -F auid>=1000 -F auid!=unset -F key=perm_mod + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following line to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S fchown -F auid>=1000 -F auid!=unset -F key=perm_mod + +If the system is 64 bit then also add the following line: +-a always,exit -F arch=b64 -S fchown -F auid>=1000 -F auid!=unset -F key=perm_mod + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping these system +calls with others as identifying earlier in this guide is more efficient. + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 5.4.1.1 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000126 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.5.5 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + SRG-OS-000064-GPOS-00033 + SRG-OS-000466-GPOS-00210 + SRG-OS-000458-GPOS-00203 + SRG-OS-000474-GPOS-00219 + SRG-OS-000458-VMM-001810 + SRG-OS-000474-VMM-001940 + The changing of file permissions could indicate that a user is attempting to +gain access to information that would otherwise be disallowed. Auditing DAC modifications +can facilitate the identification of patterns of abuse among both authorized and +unauthorized users. + CCE-83829-2 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83829-2 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_fchown + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit fchown tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83829-2 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_fchown + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for fchown for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchown + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of fchown in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules + set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchown + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of fchown in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83829-2 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_fchown + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for fchown for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchown + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of fchown in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules + set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchown + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of fchown in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-83829-2 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_fchown + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="" + AUID_FILTERS="-F auid>=1000 -F auid!=unset" + SYSCALL="fchown" + KEY="perm_mod" + SYSCALL_GROUPING="chown fchown fchownat lchown" + + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Events that Modify the System's Discretionary Access Controls - fchownat + At a minimum, the audit system should collect file permission +changes for all users and root. If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following line to a file with suffix +.rules in the directory /etc/audit/rules.d: +-a always,exit -F arch=b32 -S fchownat -F auid>=1000 -F auid!=unset -F key=perm_mod +If the system is 64 bit then also add the following line: +-a always,exit -F arch=b64 -S fchownat -F auid>=1000 -F auid!=unset -F key=perm_mod +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following line to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S fchownat -F auid>=1000 -F auid!=unset -F key=perm_mod +If the system is 64 bit then also add the following line: +-a always,exit -F arch=b64 -S fchownat -F auid>=1000 -F auid!=unset -F key=perm_mod + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping these system +calls with others as identifying earlier in this guide is more efficient. + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 5.4.1.1 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000126 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.5.5 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + SRG-OS-000064-GPOS-00033 + SRG-OS-000466-GPOS-00210 + SRG-OS-000458-GPOS-00203 + SRG-OS-000474-GPOS-00219 + SRG-OS-000458-VMM-001810 + SRG-OS-000474-VMM-001940 + The changing of file permissions could indicate that a user is attempting to +gain access to information that would otherwise be disallowed. Auditing DAC modifications +can facilitate the identification of patterns of abuse among both authorized and +unauthorized users. + CCE-83831-8 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83831-8 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_fchownat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit fchownat tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83831-8 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_fchownat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for fchownat for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchownat + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of fchownat in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules + set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchownat + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of fchownat in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83831-8 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_fchownat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for fchownat for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchownat + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of fchownat in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules + set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchownat + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of fchownat in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-83831-8 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_fchownat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="" + AUID_FILTERS="-F auid>=1000 -F auid!=unset" + SYSCALL="fchownat" + KEY="perm_mod" + SYSCALL_GROUPING="chown fchown fchownat lchown" + + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Events that Modify the System's Discretionary Access Controls - fremovexattr + At a minimum, the audit system should collect file permission +changes for all users and root. + +If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following line to a file with suffix +.rules in the directory /etc/audit/rules.d: +-a always,exit -F arch=b32 -S fremovexattr -F auid>=1000 -F auid!=unset -F key=perm_mod +-a always,exit -F arch=b32 -S fremovexattr -F auid=0 -F key=perm_mod + +If the system is 64 bit then also add the following line: +-a always,exit -F arch=b64 -S fremovexattr -F auid>=1000 -F auid!=unset -F key=perm_mod +-a always,exit -F arch=b64 -S fremovexattr -F auid=0 -F key=perm_mod + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following line to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S fremovexattr -F auid>=1000 -F auid!=unset -F key=perm_mod +-a always,exit -F arch=b32 -S fremovexattr -F auid=0 -F key=perm_mod + +If the system is 64 bit then also add the following line: +-a always,exit -F arch=b64 -S fremovexattr -F auid>=1000 -F auid!=unset -F key=perm_mod +-a always,exit -F arch=b64 -S fremovexattr -F auid=0 -F key=perm_mod + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping these system +calls with others as identifying earlier in this guide is more efficient. + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 5.4.1.1 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.5.5 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000458-GPOS-00203 + SRG-OS-000462-GPOS-00206 + SRG-OS-000463-GPOS-00207 + SRG-OS-000471-GPOS-00215 + SRG-OS-000474-GPOS-00219 + SRG-OS-000466-GPOS-00210 + SRG-OS-000468-GPOS-00212 + SRG-OS-000064-GPOS-00033 + SRG-OS-000458-VMM-001810 + SRG-OS-000474-VMM-001940 + The changing of file permissions could indicate that a user is attempting to +gain access to information that would otherwise be disallowed. Auditing DAC modifications +can facilitate the identification of patterns of abuse among both authorized and +unauthorized users. + CCE-83821-9 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83821-9 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_fremovexattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit fremovexattr tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83821-9 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_fremovexattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for fremovexattr for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - fremovexattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of fremovexattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules + set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - fremovexattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of fremovexattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - fremovexattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of fremovexattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid=0 (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules + set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid=0 (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid=0 -F + key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - fremovexattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of fremovexattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid=0 (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid=0 (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid=0 -F + key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83821-9 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_fremovexattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for fremovexattr for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - fremovexattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of fremovexattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules + set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - fremovexattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of fremovexattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - fremovexattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of fremovexattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid=0 (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules + set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid=0 (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid=0 -F + key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - fremovexattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of fremovexattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid=0 (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid=0 (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid=0 -F + key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-83821-9 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_fremovexattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="" + AUID_FILTERS="-F auid>=1000 -F auid!=unset" + SYSCALL="fremovexattr" + KEY="perm_mod" + SYSCALL_GROUPING="fremovexattr lremovexattr removexattr fsetxattr lsetxattr setxattr" + + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + + + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="" + AUID_FILTERS="-F auid=0" + SYSCALL="fremovexattr" + KEY="perm_mod" + SYSCALL_GROUPING="fremovexattr lremovexattr removexattr fsetxattr lsetxattr setxattr" + + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Events that Modify the System's Discretionary Access Controls - fsetxattr + At a minimum, the audit system should collect file permission +changes for all users and root. If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following line to a file with suffix +.rules in the directory /etc/audit/rules.d: +-a always,exit -F arch=b32 -S fsetxattr -F auid>=1000 -F auid!=unset -F key=perm_mod +-a always,exit -F arch=b32 -S fsetxattr -F auid=0 -F key=perm_mod +If the system is 64 bit then also add the following line: +-a always,exit -F arch=b64 -S fsetxattr -F auid>=1000 -F auid!=unset -F key=perm_mod +-a always,exit -F arch=b64 -S fsetxattr -F auid=0 -F key=perm_mod +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following line to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S fsetxattr -F auid>=1000 -F auid!=unset -F key=perm_mod +-a always,exit -F arch=b32 -S fsetxattr -F auid=0 -F key=perm_mod +If the system is 64 bit then also add the following line: +-a always,exit -F arch=b64 -S fsetxattr -F auid>=1000 -F auid!=unset -F key=perm_mod +-a always,exit -F arch=b64 -S fsetxattr -F auid=0 -F key=perm_mod + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping these system +calls with others as identifying earlier in this guide is more efficient. + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 5.4.1.1 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000126 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.5.5 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000458-GPOS-00203 + SRG-OS-000462-GPOS-00206 + SRG-OS-000463-GPOS-00207 + SRG-OS-000466-GPOS-00210 + SRG-OS-000468-GPOS-00212 + SRG-OS-000471-GPOS-00215 + SRG-OS-000474-GPOS-00219 + SRG-OS-000064-GPOS-00033 + SRG-OS-000458-VMM-001810 + SRG-OS-000474-VMM-001940 + The changing of file permissions could indicate that a user is attempting to +gain access to information that would otherwise be disallowed. Auditing DAC modifications +can facilitate the identification of patterns of abuse among both authorized and +unauthorized users. + CCE-83817-7 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83817-7 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_fsetxattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit fsetxattr tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83817-7 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_fsetxattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for fsetxattr for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - fsetxattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of fsetxattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules + set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - fsetxattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of fsetxattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - fsetxattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of fsetxattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid=0 (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules + set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid=0 (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid=0 -F + key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - fsetxattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of fsetxattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid=0 (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid=0 (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid=0 -F + key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83817-7 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_fsetxattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for fsetxattr for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - fsetxattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of fsetxattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules + set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - fsetxattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of fsetxattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - fsetxattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of fsetxattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid=0 (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules + set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid=0 (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid=0 -F + key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - fsetxattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of fsetxattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid=0 (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid=0 (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid=0 -F + key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-83817-7 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_fsetxattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="" + AUID_FILTERS="-F auid>=1000 -F auid!=unset" + SYSCALL="fsetxattr" + KEY="perm_mod" + SYSCALL_GROUPING="fremovexattr lremovexattr removexattr fsetxattr lsetxattr setxattr" + + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + + + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="" + AUID_FILTERS="-F auid=0" + SYSCALL="fsetxattr" + KEY="perm_mod" + SYSCALL_GROUPING="fremovexattr lremovexattr removexattr fsetxattr lsetxattr setxattr" + + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Events that Modify the System's Discretionary Access Controls - lchown + At a minimum, the audit system should collect file permission +changes for all users and root. If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following line to a file with suffix +.rules in the directory /etc/audit/rules.d: +-a always,exit -F arch=b32 -S lchown -F auid>=1000 -F auid!=unset -F key=perm_mod +If the system is 64 bit then also add the following line: +-a always,exit -F arch=b64 -S lchown -F auid>=1000 -F auid!=unset -F key=perm_mod +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following line to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S lchown -F auid>=1000 -F auid!=unset -F key=perm_mod +If the system is 64 bit then also add the following line: +-a always,exit -F arch=b64 -S lchown -F auid>=1000 -F auid!=unset -F key=perm_mod + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping these system +calls with others as identifying earlier in this guide is more efficient. + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 5.4.1.1 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000126 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.5.5 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + SRG-OS-000064-GPOS-00033 + SRG-OS-000466-GPOS-00210 + SRG-OS-000458-GPOS-00203 + SRG-OS-000474-GPOS-00219 + SRG-OS-000458-VMM-001810 + SRG-OS-000474-VMM-001940 + The changing of file permissions could indicate that a user is attempting to +gain access to information that would otherwise be disallowed. Auditing DAC modifications +can facilitate the identification of patterns of abuse among both authorized and +unauthorized users. + CCE-83833-4 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83833-4 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_lchown + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit lchown tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83833-4 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_lchown + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for lchown for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - lchown + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of lchown in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules + set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - lchown + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of lchown in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83833-4 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_lchown + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for lchown for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - lchown + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of lchown in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules + set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - lchown + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of lchown in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-83833-4 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_lchown + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="" + AUID_FILTERS="-F auid>=1000 -F auid!=unset" + SYSCALL="lchown" + KEY="perm_mod" + SYSCALL_GROUPING="chown fchown fchownat lchown" + + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Events that Modify the System's Discretionary Access Controls - lremovexattr + At a minimum, the audit system should collect file permission +changes for all users and root. + +If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following line to a file with suffix +.rules in the directory /etc/audit/rules.d: +-a always,exit -F arch=b32 -S lremovexattr -F auid>=1000 -F auid!=unset -F key=perm_mod +-a always,exit -F arch=b32 -S lremovexattr -F auid=0 -F key=perm_mod + +If the system is 64 bit then also add the following line: +-a always,exit -F arch=b64 -S lremovexattr -F auid>=1000 -F auid!=unset -F key=perm_mod +-a always,exit -F arch=b64 -S lremovexattr -F auid=0 -F key=perm_mod + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following line to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S lremovexattr -F auid>=1000 -F auid!=unset -F key=perm_mod +-a always,exit -F arch=b32 -S lremovexattr -F auid=0 -F key=perm_mod + +If the system is 64 bit then also add the following line: +-a always,exit -F arch=b64 -S lremovexattr -F auid>=1000 -F auid!=unset -F key=perm_mod +-a always,exit -F arch=b64 -S lremovexattr -F auid=0 -F key=perm_mod + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping these system +calls with others as identifying earlier in this guide is more efficient. + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 5.4.1.1 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.5.5 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000458-GPOS-00203 + SRG-OS-000462-GPOS-00206 + SRG-OS-000463-GPOS-00207 + SRG-OS-000468-GPOS-00212 + SRG-OS-000471-GPOS-00215 + SRG-OS-000474-GPOS-00219 + SRG-OS-000466-GPOS-00210 + SRG-OS-000064-GPOS-00033 + SRG-OS-000458-VMM-001810 + SRG-OS-000474-VMM-001940 + The changing of file permissions could indicate that a user is attempting to +gain access to information that would otherwise be disallowed. Auditing DAC modifications +can facilitate the identification of patterns of abuse among both authorized and +unauthorized users. + CCE-83814-4 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83814-4 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_lremovexattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit lremovexattr tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83814-4 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_lremovexattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for lremovexattr for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - lremovexattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of lremovexattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules + set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - lremovexattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of lremovexattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - lremovexattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of lremovexattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid=0 (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules + set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid=0 (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid=0 -F + key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - lremovexattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of lremovexattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid=0 (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid=0 (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid=0 -F + key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83814-4 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_lremovexattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for lremovexattr for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - lremovexattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of lremovexattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules + set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - lremovexattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of lremovexattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - lremovexattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of lremovexattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid=0 (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules + set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid=0 (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid=0 -F + key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - lremovexattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of lremovexattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid=0 (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid=0 (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid=0 -F + key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-83814-4 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_lremovexattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="" + AUID_FILTERS="-F auid>=1000 -F auid!=unset" + SYSCALL="lremovexattr" + KEY="perm_mod" + SYSCALL_GROUPING="fremovexattr lremovexattr removexattr fsetxattr lsetxattr setxattr" + + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + + + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="" + AUID_FILTERS="-F auid=0" + SYSCALL="lremovexattr" + KEY="perm_mod" + SYSCALL_GROUPING="fremovexattr lremovexattr removexattr fsetxattr lsetxattr setxattr" + + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Events that Modify the System's Discretionary Access Controls - lsetxattr + At a minimum, the audit system should collect file permission +changes for all users and root. If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following line to a file with suffix +.rules in the directory /etc/audit/rules.d: +-a always,exit -F arch=b32 -S lsetxattr -F auid>=1000 -F auid!=unset -F key=perm_mod +-a always,exit -F arch=b32 -S lsetxattr -F auid=0 -F key=perm_mod +If the system is 64 bit then also add the following line: +-a always,exit -F arch=b64 -S lsetxattr -F auid>=1000 -F auid!=unset -F key=perm_mod +-a always,exit -F arch=b64 -S lsetxattr -F auid=0 -F key=perm_mod +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following line to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S lsetxattr -F auid>=1000 -F auid!=unset -F key=perm_mod +-a always,exit -F arch=b32 -S lsetxattr -F auid=0 -F key=perm_mod +If the system is 64 bit then also add the following line: +-a always,exit -F arch=b64 -S lsetxattr -F auid>=1000 -F auid!=unset -F key=perm_mod +-a always,exit -F arch=b64 -S lsetxattr -F auid=0 -F key=perm_mod + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping these system +calls with others as identifying earlier in this guide is more efficient. + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 5.4.1.1 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000126 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.5.5 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000458-GPOS-00203 + SRG-OS-000462-GPOS-00206 + SRG-OS-000463-GPOS-00207 + SRG-OS-000466-GPOS-00210 + SRG-OS-000468-GPOS-00212 + SRG-OS-000471-GPOS-00215 + SRG-OS-000474-GPOS-00219 + SRG-OS-000064-GPOS-00033 + SRG-OS-000458-VMM-001810 + SRG-OS-000474-VMM-001940 + The changing of file permissions could indicate that a user is attempting to +gain access to information that would otherwise be disallowed. Auditing DAC modifications +can facilitate the identification of patterns of abuse among both authorized and +unauthorized users. + CCE-83808-6 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83808-6 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_lsetxattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit lsetxattr tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83808-6 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_lsetxattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for lsetxattr for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - lsetxattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of lsetxattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules + set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - lsetxattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of lsetxattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - lsetxattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of lsetxattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid=0 (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules + set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid=0 (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid=0 -F + key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - lsetxattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of lsetxattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid=0 (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid=0 (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid=0 -F + key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83808-6 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_lsetxattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for lsetxattr for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - lsetxattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of lsetxattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules + set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - lsetxattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of lsetxattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - lsetxattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of lsetxattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid=0 (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules + set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid=0 (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid=0 -F + key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - lsetxattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of lsetxattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid=0 (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid=0 (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid=0 -F + key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-83808-6 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_lsetxattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="" + AUID_FILTERS="-F auid>=1000 -F auid!=unset" + SYSCALL="lsetxattr" + KEY="perm_mod" + SYSCALL_GROUPING="fremovexattr lremovexattr removexattr fsetxattr lsetxattr setxattr" + + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + + + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="" + AUID_FILTERS="-F auid=0" + SYSCALL="lsetxattr" + KEY="perm_mod" + SYSCALL_GROUPING="fremovexattr lremovexattr removexattr fsetxattr lsetxattr setxattr" + + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Events that Modify the System's Discretionary Access Controls - removexattr + At a minimum, the audit system should collect file permission +changes for all users and root. + +If the auditd daemon is configured to use the augenrules +program to read audit rules during daemon startup (the default), add the +following line to a file with suffix .rules in the directory /etc/audit/rules.d: +-a always,exit -F arch=b32 -S removexattr -F auid>=1000 -F auid!=unset -F key=perm_mod +-a always,exit -F arch=b32 -S removexattr -F auid=0 -F key=perm_mod + +If the system is 64 bit then also add the following line: +-a always,exit -F arch=b64 -S removexattr -F auid>=1000 -F auid!=unset -F key=perm_mod +-a always,exit -F arch=b64 -S removexattr -F auid=0 -F key=perm_mod + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following line to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S removexattr -F auid>=1000 -F auid!=unset -F key=perm_mod +-a always,exit -F arch=b32 -S removexattr -F auid=0 -F key=perm_mod + +If the system is 64 bit then also add the following line: +-a always,exit -F arch=b64 -S removexattr -F auid>=1000 -F auid!=unset -F key=perm_mod +-a always,exit -F arch=b64 -S removexattr -F auid=0 -F key=perm_mod + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping these system +calls with others as identifying earlier in this guide is more efficient. + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 5.4.1.1 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.5.5 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000458-GPOS-00203 + SRG-OS-000462-GPOS-00206 + SRG-OS-000463-GPOS-00207 + SRG-OS-000468-GPOS-00212 + SRG-OS-000471-GPOS-00215 + SRG-OS-000474-GPOS-00219 + SRG-OS-000466-GPOS-00210 + SRG-OS-000064-GPOS-00033 + SRG-OS-000458-VMM-001810 + SRG-OS-000474-VMM-001940 + The changing of file permissions could indicate that a user is attempting to +gain access to information that would otherwise be disallowed. Auditing DAC modifications +can facilitate the identification of patterns of abuse among both authorized and +unauthorized users. + CCE-83807-8 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83807-8 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_removexattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit removexattr tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83807-8 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_removexattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for removexattr for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - removexattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of removexattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules + set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - removexattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of removexattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - removexattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of removexattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid=0 (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules + set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid=0 (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid=0 -F + key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - removexattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of removexattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid=0 (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid=0 (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid=0 -F + key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83807-8 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_removexattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for removexattr for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - removexattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of removexattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules + set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - removexattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of removexattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - removexattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of removexattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid=0 (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules + set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid=0 (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid=0 -F + key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - removexattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of removexattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid=0 (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid=0 (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid=0 -F + key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-83807-8 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_removexattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="" + AUID_FILTERS="-F auid>=1000 -F auid!=unset" + SYSCALL="removexattr" + KEY="perm_mod" + SYSCALL_GROUPING="fremovexattr lremovexattr removexattr fsetxattr lsetxattr setxattr" + + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + + + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="" + AUID_FILTERS="-F auid=0" + SYSCALL="removexattr" + KEY="perm_mod" + SYSCALL_GROUPING="fremovexattr lremovexattr removexattr fsetxattr lsetxattr setxattr" + + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Events that Modify the System's Discretionary Access Controls - setxattr + At a minimum, the audit system should collect file permission +changes for all users and root. If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following line to a file with suffix +.rules in the directory /etc/audit/rules.d: +-a always,exit -F arch=b32 -S setxattr -F auid>=1000 -F auid!=unset -F key=perm_mod +-a always,exit -F arch=b32 -S setxattr -F auid=0 -F key=perm_mod +If the system is 64 bit then also add the following line: +-a always,exit -F arch=b64 -S setxattr -F auid>=1000 -F auid!=unset -F key=perm_mod +-a always,exit -F arch=b64 -S setxattr -F auid=0 -F key=perm_mod +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following line to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S setxattr -F auid>=1000 -F auid!=unset -F key=perm_mod +-a always,exit -F arch=b32 -S setxattr -F auid=0 -F key=perm_mod +If the system is 64 bit then also add the following line: +-a always,exit -F arch=b64 -S setxattr -F auid>=1000 -F auid!=unset -F key=perm_mod +-a always,exit -F arch=b64 -S setxattr -F auid=0 -F key=perm_mod + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping these system +calls with others as identifying earlier in this guide is more efficient. + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 5.4.1.1 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000126 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.5.5 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000466-GPOS-00210 + SRG-OS-000471-GPOS-00215 + SRG-OS-000064-GPOS-00033 + SRG-OS-000458-GPOS-00203 + SRG-OS-000458-VMM-001810 + SRG-OS-000474-VMM-001940 + The changing of file permissions could indicate that a user is attempting to +gain access to information that would otherwise be disallowed. Auditing DAC modifications +can facilitate the identification of patterns of abuse among both authorized and +unauthorized users. + CCE-83811-0 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83811-0 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_setxattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit setxattr tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83811-0 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_setxattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for setxattr for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - setxattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of setxattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules + set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - setxattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of setxattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - setxattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of setxattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid=0 (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules + set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid=0 (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid=0 -F + key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - setxattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of setxattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid=0 (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid=0 (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid=0 -F + key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83811-0 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_setxattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for setxattr for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - setxattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of setxattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules + set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - setxattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of setxattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - setxattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of setxattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid=0 (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules + set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid=0 (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid=0 -F + key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - setxattr + syscall_grouping: + - fremovexattr + - lremovexattr + - removexattr + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of setxattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid=0 (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid=0 (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid=0 -F + key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-83811-0 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.5 + - audit_rules_dac_modification_setxattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="" + AUID_FILTERS="-F auid>=1000 -F auid!=unset" + SYSCALL="setxattr" + KEY="perm_mod" + SYSCALL_GROUPING="fremovexattr lremovexattr removexattr fsetxattr lsetxattr setxattr" + + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + + + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="" + AUID_FILTERS="-F auid=0" + SYSCALL="setxattr" + KEY="perm_mod" + SYSCALL_GROUPING="fremovexattr lremovexattr removexattr fsetxattr lsetxattr setxattr" + + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Events that Modify the System's Discretionary Access Controls - umount + At a minimum, the audit system should collect file system umount +changes. If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following line to a file with suffix +.rules in the directory /etc/audit/rules.d: +-a always,exit -F arch=b32 -S umount -F auid>=1000 -F auid!=unset -F key=perm_mod +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following line to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S umount -F auid>=1000 -F auid!=unset -F key=perm_mod + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping these system +calls with others as identifying earlier in this guide is more efficient. + CCI-000130 + CCI-000169 + CCI-000172 + CCI-002884 + SRG-OS-000037-GPOS-00015 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + The changing of file permissions could indicate that a user is attempting to +gain access to information that would otherwise be disallowed. Auditing DAC modifications +can facilitate the identification of patterns of abuse among both authorized and +unauthorized users. + CCE-89272-9 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-89272-9 + - audit_rules_dac_modification_umount + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for umount for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - umount + syscall_grouping: [] + + - name: Check existence of umount in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules + set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - umount + syscall_grouping: [] + + - name: Check existence of umount in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-89272-9 + - audit_rules_dac_modification_umount + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +ACTION_ARCH_FILTERS="-a always,exit -F arch=b32" +OTHER_FILTERS="" +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="umount" +KEY="perm_mod" +SYSCALL_GROUPING="" + +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Events that Modify the System's Discretionary Access Controls - umount2 + At a minimum, the audit system should collect file system umount2 +changes. If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following line to a file with suffix +.rules in the directory /etc/audit/rules.d: +-a always,exit -F arch=b32 -S umount2 -F auid>=1000 -F auid!=unset -F key=perm_mod +If the system is 64 bit then also add the following line: +-a always,exit -F arch=b64 -S umount2 -F auid>=1000 -F auid!=unset -F key=perm_mod +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following line to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S umount2 -F auid>=1000 -F auid!=unset -F key=perm_mod +If the system is 64 bit then also add the following line: +-a always,exit -F arch=b64 -S umount2 -F auid>=1000 -F auid!=unset -F key=perm_mod + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping these system +calls with others as identifying earlier in this guide is more efficient. + CCI-000130 + CCI-000169 + CCI-000172 + CCI-002884 + SRG-OS-000037-GPOS-00015 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + The changing of file permissions could indicate that a user is attempting to +gain access to information that would otherwise be disallowed. Auditing DAC modifications +can facilitate the identification of patterns of abuse among both authorized and +unauthorized users. + CCE-88570-7 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-88570-7 + - audit_rules_dac_modification_umount2 + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit umount2 tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-88570-7 + - audit_rules_dac_modification_umount2 + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for umount2 for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - umount2 + syscall_grouping: [] + + - name: Check existence of umount2 in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules + set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - umount2 + syscall_grouping: [] + + - name: Check existence of umount2 in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-88570-7 + - audit_rules_dac_modification_umount2 + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for umount2 for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - umount2 + syscall_grouping: [] + + - name: Check existence of umount2 in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules + set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - umount2 + syscall_grouping: [] + + - name: Check existence of umount2 in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=perm_mod + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-88570-7 + - audit_rules_dac_modification_umount2 + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="" + AUID_FILTERS="-F auid>=1000 -F auid!=unset" + SYSCALL="umount2" + KEY="perm_mod" + SYSCALL_GROUPING="" + + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Record Execution Attempts to Run ACL Privileged Commands + At a minimum, the audit system should collect the execution of +ACL privileged commands for all users and root. + + Record Any Attempts to Run chacl + At a minimum, the audit system should collect any execution attempt +of the chacl command for all users and root. If the auditd +daemon is configured to use the augenrules program to read audit rules +during daemon startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: +-a always,exit -F path=/usr/bin/chacl -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F path=/usr/bin/chacl -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + SRG-OS-000466-GPOS-00210 + Without generating audit records that are specific to the security and +mission needs of the organization, it would be difficult to establish, +correlate, and investigate the events relating to an incident or identify +those responsible for one. +Audit records can be generated from various components within the +information system (e.g., module or policy filter). + CCE-87685-4 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-87685-4 + - audit_rules_execution_chacl + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Perform remediation of Audit rules for /usr/bin/chacl + block: + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/bin/chacl -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules + set_fact: audit_file="/etc/audit/rules.d/privileged.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/bin/chacl -F perm=x -F + auid>=1000 -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/chacl -F perm=x + -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/bin/chacl -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:( + -S |,)\w+)+)( -F path=/usr/bin/chacl -F perm=x -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/chacl -F perm=x + -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-87685-4 + - audit_rules_execution_chacl + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +ACTION_ARCH_FILTERS="-a always,exit" +OTHER_FILTERS="-F path=/usr/bin/chacl -F perm=x" +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="" +KEY="privileged" +SYSCALL_GROUPING="" +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Any Attempts to Run setfacl + At a minimum, the audit system should collect any execution attempt +of the setfacl command for all users and root. If the auditd +daemon is configured to use the augenrules program to read audit rules +during daemon startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: +-a always,exit -F path=/usr/bin/setfacl -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F path=/usr/bin/setfacl -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + Without generating audit records that are specific to the security and +mission needs of the organization, it would be difficult to establish, +correlate, and investigate the events relating to an incident or identify +those responsible for one. +Audit records can be generated from various components within the +information system (e.g., module or policy filter). + CCE-90482-1 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-90482-1 + - audit_rules_execution_setfacl + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Perform remediation of Audit rules for /usr/bin/setfacl + block: + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/bin/setfacl -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules + set_fact: audit_file="/etc/audit/rules.d/privileged.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/bin/setfacl -F perm=x + -F auid>=1000 -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/setfacl -F perm=x + -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/bin/setfacl -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:( + -S |,)\w+)+)( -F path=/usr/bin/setfacl -F perm=x -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/setfacl -F perm=x + -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-90482-1 + - audit_rules_execution_setfacl + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +ACTION_ARCH_FILTERS="-a always,exit" +OTHER_FILTERS="-F path=/usr/bin/setfacl -F perm=x" +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="" +KEY="privileged" +SYSCALL_GROUPING="" +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Record Execution Attempts to Run SELinux Privileged Commands + At a minimum, the audit system should collect the execution of +SELinux privileged commands for all users and root. + + Record Any Attempts to Run chcon + At a minimum, the audit system should collect any execution attempt +of the chcon command for all users and root. If the auditd +daemon is configured to use the augenrules program to read audit rules +during daemon startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: +-a always,exit -F path=/usr/bin/chcon -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F path=/usr/bin/chcon -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + 1 + 12 + 13 + 14 + 15 + 16 + 2 + 3 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + BAI03.05 + DSS01.03 + DSS03.05 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 6.1 + SR 6.2 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.14.2.7 + A.15.2.1 + A.15.2.2 + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.PT-1 + FAU_GEN.1.1.c + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000468-GPOS-00212 + SRG-OS-000471-GPOS-00215 + SRG-OS-000463-GPOS-00207 + SRG-OS-000465-GPOS-00209 + SRG-OS-000463-VMM-001850 + Misuse of privileged functions, either intentionally or unintentionally by +authorized users, or by unauthorized external entities that have compromised system accounts, +is a serious and ongoing concern and can have significant adverse impacts on organizations. +Auditing the use of privileged functions is one way to detect such misuse and identify +the risk from insider and advanced persistent threats. + +Privileged programs are subject to escalation-of-privilege attacks, +which attempt to subvert their normal role of providing some necessary but +limited capability. As such, motivation exists to monitor these programs for +unusual activity. + CCE-83748-4 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83748-4 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_execution_chcon + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Perform remediation of Audit rules for /usr/bin/chcon + block: + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/bin/chcon -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules + set_fact: audit_file="/etc/audit/rules.d/privileged.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/bin/chcon -F perm=x -F + auid>=1000 -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/chcon -F perm=x + -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/bin/chcon -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:( + -S |,)\w+)+)( -F path=/usr/bin/chcon -F perm=x -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/chcon -F perm=x + -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83748-4 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_execution_chcon + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +ACTION_ARCH_FILTERS="-a always,exit" +OTHER_FILTERS="-F path=/usr/bin/chcon -F perm=x" +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="" +KEY="privileged" +SYSCALL_GROUPING="" +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Any Attempts to Run restorecon + At a minimum, the audit system should collect any execution attempt +of the restorecon command for all users and root. If the auditd +daemon is configured to use the augenrules program to read audit rules +during daemon startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: +-a always,exit -F path=/usr/sbin/restorecon -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F path=/usr/sbin/restorecon -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + 1 + 12 + 13 + 14 + 15 + 16 + 2 + 3 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + BAI03.05 + DSS01.03 + DSS03.05 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 6.1 + SR 6.2 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.14.2.7 + A.15.2.1 + A.15.2.2 + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.PT-1 + FAU_GEN.1.1.c + SRG-OS-000392-GPOS-00172 + SRG-OS-000463-GPOS-00207 + SRG-OS-000465-GPOS-00209 + SRG-OS-000463-VMM-001850 + Misuse of privileged functions, either intentionally or unintentionally by +authorized users, or by unauthorized external entities that have compromised system accounts, +is a serious and ongoing concern and can have significant adverse impacts on organizations. +Auditing the use of privileged functions is one way to detect such misuse and identify +the risk from insider and advanced persistent threats. + +Privileged programs are subject to escalation-of-privilege attacks, +which attempt to subvert their normal role of providing some necessary but +limited capability. As such, motivation exists to monitor these programs for +unusual activity. + CCE-83749-2 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83749-2 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_execution_restorecon + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Perform remediation of Audit rules for /usr/sbin/restorecon + block: + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/sbin/restorecon -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules + set_fact: audit_file="/etc/audit/rules.d/privileged.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/sbin/restorecon -F perm=x + -F auid>=1000 -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/sbin/restorecon + -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/sbin/restorecon -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:( + -S |,)\w+)+)( -F path=/usr/sbin/restorecon -F perm=x -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/sbin/restorecon + -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83749-2 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_execution_restorecon + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +ACTION_ARCH_FILTERS="-a always,exit" +OTHER_FILTERS="-F path=/usr/sbin/restorecon -F perm=x" +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="" +KEY="privileged" +SYSCALL_GROUPING="" +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Any Attempts to Run semanage + At a minimum, the audit system should collect any execution attempt +of the semanage command for all users and root. If the auditd +daemon is configured to use the augenrules program to read audit rules +during daemon startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: +-a always,exit -F path=/usr/sbin/semanage -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F path=/usr/sbin/semanage -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + 1 + 12 + 13 + 14 + 15 + 16 + 2 + 3 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + BAI03.05 + DSS01.03 + DSS03.05 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000169 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 6.1 + SR 6.2 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.14.2.7 + A.15.2.1 + A.15.2.2 + CIP-004-6 R2.2.2 + CIP-004-6 R2.2.3 + CIP-007-3 R.1.3 + CIP-007-3 R5 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.3 + CIP-007-3 R5.2.1 + CIP-007-3 R5.2.3 + AC-2(4) + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.PT-1 + FAU_GEN.1.1.c + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + SRG-OS-000463-GPOS-00207 + SRG-OS-000465-GPOS-00209 + SRG-OS-000463-VMM-001850 + Misuse of privileged functions, either intentionally or unintentionally by +authorized users, or by unauthorized external entities that have compromised system accounts, +is a serious and ongoing concern and can have significant adverse impacts on organizations. +Auditing the use of privileged functions is one way to detect such misuse and identify +the risk from insider and advanced persistent threats. + +Privileged programs are subject to escalation-of-privilege attacks, +which attempt to subvert their normal role of providing some necessary but +limited capability. As such, motivation exists to monitor these programs for +unusual activity. + CCE-83750-0 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83750-0 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_execution_semanage + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Perform remediation of Audit rules for /usr/sbin/semanage + block: + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/sbin/semanage -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules + set_fact: audit_file="/etc/audit/rules.d/privileged.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/sbin/semanage -F perm=x + -F auid>=1000 -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/sbin/semanage -F + perm=x -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/sbin/semanage -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:( + -S |,)\w+)+)( -F path=/usr/sbin/semanage -F perm=x -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/sbin/semanage -F + perm=x -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83750-0 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_execution_semanage + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +ACTION_ARCH_FILTERS="-a always,exit" +OTHER_FILTERS="-F path=/usr/sbin/semanage -F perm=x" +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="" +KEY="privileged" +SYSCALL_GROUPING="" +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Any Attempts to Run setfiles + At a minimum, the audit system should collect any execution attempt +of the setfiles command for all users and root. If the auditd +daemon is configured to use the augenrules program to read audit rules +during daemon startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: +-a always,exit -F path=/usr/sbin/setfiles -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F path=/usr/sbin/setfiles -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + CCI-000169 + CCI-000172 + CCI-002884 + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + SRG-OS-000463-GPOS-00207 + SRG-OS-000465-GPOS-00209 + SRG-OS-000463-VMM-001850 + Misuse of privileged functions, either intentionally or unintentionally by +authorized users, or by unauthorized external entities that have compromised system accounts, +is a serious and ongoing concern and can have significant adverse impacts on organizations. +Auditing the use of privileged functions is one way to detect such misuse and identify +the risk from insider and advanced persistent threats. + +Privileged programs are subject to escalation-of-privilege attacks, +which attempt to subvert their normal role of providing some necessary but +limited capability. As such, motivation exists to monitor these programs for +unusual activity. + CCE-83736-9 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83736-9 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_execution_setfiles + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Perform remediation of Audit rules for /usr/sbin/setfiles + block: + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/sbin/setfiles -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules + set_fact: audit_file="/etc/audit/rules.d/privileged.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/sbin/setfiles -F perm=x + -F auid>=1000 -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/sbin/setfiles -F + perm=x -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/sbin/setfiles -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:( + -S |,)\w+)+)( -F path=/usr/sbin/setfiles -F perm=x -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/sbin/setfiles -F + perm=x -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83736-9 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_execution_setfiles + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +ACTION_ARCH_FILTERS="-a always,exit" +OTHER_FILTERS="-F path=/usr/sbin/setfiles -F perm=x" +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="" +KEY="privileged" +SYSCALL_GROUPING="" +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Any Attempts to Run setsebool + At a minimum, the audit system should collect any execution attempt +of the setsebool command for all users and root. If the auditd +daemon is configured to use the augenrules program to read audit rules +during daemon startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: +-a always,exit -F path=/usr/sbin/setsebool -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F path=/usr/sbin/setsebool -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + 1 + 12 + 13 + 14 + 15 + 16 + 2 + 3 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + BAI03.05 + DSS01.03 + DSS03.05 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 6.1 + SR 6.2 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.14.2.7 + A.15.2.1 + A.15.2.2 + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.PT-1 + FAU_GEN.1.1.c + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + SRG-OS-000463-GPOS-00207 + SRG-OS-000465-GPOS-00209 + SRG-OS-000463-VMM-001850 + Misuse of privileged functions, either intentionally or unintentionally by +authorized users, or by unauthorized external entities that have compromised system accounts, +is a serious and ongoing concern and can have significant adverse impacts on organizations. +Auditing the use of privileged functions is one way to detect such misuse and identify +the risk from insider and advanced persistent threats. + +Privileged programs are subject to escalation-of-privilege attacks, +which attempt to subvert their normal role of providing some necessary but +limited capability. As such, motivation exists to monitor these programs for +unusual activity. + CCE-83751-8 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83751-8 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_execution_setsebool + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Perform remediation of Audit rules for /usr/sbin/setsebool + block: + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/sbin/setsebool -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules + set_fact: audit_file="/etc/audit/rules.d/privileged.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/sbin/setsebool -F perm=x + -F auid>=1000 -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/sbin/setsebool -F + perm=x -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/sbin/setsebool -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:( + -S |,)\w+)+)( -F path=/usr/sbin/setsebool -F perm=x -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/sbin/setsebool -F + perm=x -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83751-8 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_execution_setsebool + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +ACTION_ARCH_FILTERS="-a always,exit" +OTHER_FILTERS="-F path=/usr/sbin/setsebool -F perm=x" +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="" +KEY="privileged" +SYSCALL_GROUPING="" +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Any Attempts to Run seunshare + At a minimum, the audit system should collect any execution attempt +of the seunshare command for all users and root. If the auditd +daemon is configured to use the augenrules program to read audit rules +during daemon startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: +-a always,exit -F path=/usr/sbin/seunshare -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F path=/usr/sbin/seunshare -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + CCI-000172 + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + FAU_GEN.1.1.c + SRG-OS-000463-VMM-001850 + Misuse of privileged functions, either intentionally or unintentionally by +authorized users, or by unauthorized external entities that have compromised system accounts, +is a serious and ongoing concern and can have significant adverse impacts on organizations. +Auditing the use of privileged functions is one way to detect such misuse and identify +the risk from insider and advanced persistent threats. + +Privileged programs are subject to escalation-of-privilege attacks, +which attempt to subvert their normal role of providing some necessary but +limited capability. As such, motivation exists to monitor these programs for +unusual activity. + CCE-83746-8 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83746-8 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_execution_seunshare + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Perform remediation of Audit rules for /usr/sbin/seunshare + block: + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/sbin/seunshare -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules + set_fact: audit_file="/etc/audit/rules.d/privileged.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/sbin/seunshare -F perm=x + -F auid>=1000 -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/sbin/seunshare -F + perm=x -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/sbin/seunshare -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:( + -S |,)\w+)+)( -F path=/usr/sbin/seunshare -F perm=x -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/sbin/seunshare -F + perm=x -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83746-8 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_execution_seunshare + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +ACTION_ARCH_FILTERS="-a always,exit" +OTHER_FILTERS="-F path=/usr/sbin/seunshare -F perm=x" +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="" +KEY="privileged" +SYSCALL_GROUPING="" +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Record File Deletion Events by User + At a minimum, the audit system should collect file deletion events +for all users and root. If the auditd daemon is configured to use the +augenrules program to read audit rules during daemon startup (the +default), add the following line to a file with suffix .rules in the +directory /etc/audit/rules.d, setting ARCH to either b32 or b64 as +appropriate for your system: +-a always,exit -F arch=ARCH -S rmdir,unlink,unlinkat,rename,renameat -F auid>=1000 -F auid!=unset -F key=delete +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following line to +/etc/audit/audit.rules file, setting ARCH to either b32 or b64 as +appropriate for your system: +-a always,exit -F arch=ARCH -S rmdir,unlink,unlinkat,rename,renameat -F auid>=1000 -F auid!=unset -F key=delete + + Ensure auditd Collects File Deletion Events by User + At a minimum the audit system should collect file deletion events +for all users and root. If the auditd daemon is configured to use the +augenrules program to read audit rules during daemon startup (the +default), add the following line to a file with suffix .rules in the +directory /etc/audit/rules.d, setting ARCH to either b32 or b64 as +appropriate for your system: +-a always,exit -F arch=ARCH -S rmdir,unlink,unlinkat,rename,renameat -F auid>=1000 -F auid!=unset -F key=delete +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following line to +/etc/audit/audit.rules file, setting ARCH to either b32 or b64 as +appropriate for your system: +-a always,exit -F arch=ARCH -S rmdir,unlink,unlinkat,rename -S renameat -F auid>=1000 -F auid!=unset -F key=delete + This rule checks for multiple syscalls related to file deletion; +it was written with DISA STIG in mind. Other policies should use a +separate rule for each syscall that needs to be checked. For example: +audit_rules_file_deletion_events_rmdiraudit_rules_file_deletion_events_unlinkaudit_rules_file_deletion_events_unlinkat + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 5.4.1.1 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000366 + CCI-000172 + CCI-002884 + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.2.7 + Auditing file deletions will create an audit trail for files that are removed +from the system. The audit trail could aid in system troubleshooting, as well as, detecting +malicious processes that attempt to delete log files to conceal their presence. + CCE-83752-6 + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# Perform the remediation for the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="" + AUID_FILTERS="-F auid>=1000 -F auid!=unset" + SYSCALL="rmdir unlink unlinkat rename renameat" + KEY="delete" + SYSCALL_GROUPING="rmdir unlink unlinkat rename renameat" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure auditd Collects File Deletion Events by User - rename + At a minimum, the audit system should collect file deletion events +for all users and root. If the auditd daemon is configured to use the +augenrules program to read audit rules during daemon startup (the +default), add the following line to a file with suffix .rules in the +directory /etc/audit/rules.d, setting ARCH to either b32 or b64 as +appropriate for your system: +-a always,exit -F arch=ARCH -S rename -F auid>=1000 -F auid!=unset -F key=delete +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following line to +/etc/audit/audit.rules file, setting ARCH to either b32 or b64 as +appropriate for your system: +-a always,exit -F arch=ARCH -S rename -F auid>=1000 -F auid!=unset -F key=delete + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-000366 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.4 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.1.1 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.MA-2 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.2.7 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + SRG-OS-000466-GPOS-00210 + SRG-OS-000467-GPOS-00211 + SRG-OS-000468-GPOS-00212 + SRG-OS-000466-VMM-001870 + SRG-OS-000468-VMM-001890 + Auditing file deletions will create an audit trail for files that are removed +from the system. The audit trail could aid in system troubleshooting, as well as, detecting +malicious processes that attempt to delete log files to conceal their presence. + CCE-83754-2 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83754-2 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.7 + - audit_rules_file_deletion_events_rename + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit rename tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83754-2 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.7 + - audit_rules_file_deletion_events_rename + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for rename for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - rename + syscall_grouping: + - unlink + - unlinkat + - rename + - renameat + - rmdir + + - name: Check existence of rename in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/delete.rules + set_fact: audit_file="/etc/audit/rules.d/delete.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=delete + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - rename + syscall_grouping: + - unlink + - unlinkat + - rename + - renameat + - rmdir + + - name: Check existence of rename in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=delete + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83754-2 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.7 + - audit_rules_file_deletion_events_rename + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for rename for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - rename + syscall_grouping: + - unlink + - unlinkat + - rename + - renameat + - rmdir + + - name: Check existence of rename in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/delete.rules + set_fact: audit_file="/etc/audit/rules.d/delete.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=delete + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - rename + syscall_grouping: + - unlink + - unlinkat + - rename + - renameat + - rmdir + + - name: Check existence of rename in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=delete + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-83754-2 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.7 + - audit_rules_file_deletion_events_rename + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="" + AUID_FILTERS="-F auid>=1000 -F auid!=unset" + SYSCALL="rename" + KEY="delete" + SYSCALL_GROUPING="unlink unlinkat rename renameat rmdir" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure auditd Collects File Deletion Events by User - renameat + At a minimum, the audit system should collect file deletion events +for all users and root. If the auditd daemon is configured to use the +augenrules program to read audit rules during daemon startup (the +default), add the following line to a file with suffix .rules in the +directory /etc/audit/rules.d, setting ARCH to either b32 or b64 as +appropriate for your system: +-a always,exit -F arch=ARCH -S renameat -F auid>=1000 -F auid!=unset -F key=delete +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following line to +/etc/audit/audit.rules file, setting ARCH to either b32 or b64 as +appropriate for your system: +-a always,exit -F arch=ARCH -S renameat -F auid>=1000 -F auid!=unset -F key=delete + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-000366 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.4 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.1.1 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.MA-2 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.2.7 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + SRG-OS-000466-GPOS-00210 + SRG-OS-000467-GPOS-00211 + SRG-OS-000468-GPOS-00212 + SRG-OS-000466-VMM-001870 + SRG-OS-000468-VMM-001890 + Auditing file deletions will create an audit trail for files that are removed +from the system. The audit trail could aid in system troubleshooting, as well as, detecting +malicious processes that attempt to delete log files to conceal their presence. + CCE-83756-7 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83756-7 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.7 + - audit_rules_file_deletion_events_renameat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit renameat tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83756-7 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.7 + - audit_rules_file_deletion_events_renameat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for renameat for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - renameat + syscall_grouping: + - unlink + - unlinkat + - rename + - renameat + - rmdir + + - name: Check existence of renameat in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/delete.rules + set_fact: audit_file="/etc/audit/rules.d/delete.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=delete + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - renameat + syscall_grouping: + - unlink + - unlinkat + - rename + - renameat + - rmdir + + - name: Check existence of renameat in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=delete + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83756-7 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.7 + - audit_rules_file_deletion_events_renameat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for renameat for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - renameat + syscall_grouping: + - unlink + - unlinkat + - rename + - renameat + - rmdir + + - name: Check existence of renameat in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/delete.rules + set_fact: audit_file="/etc/audit/rules.d/delete.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=delete + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - renameat + syscall_grouping: + - unlink + - unlinkat + - rename + - renameat + - rmdir + + - name: Check existence of renameat in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=delete + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-83756-7 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.7 + - audit_rules_file_deletion_events_renameat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="" + AUID_FILTERS="-F auid>=1000 -F auid!=unset" + SYSCALL="renameat" + KEY="delete" + SYSCALL_GROUPING="unlink unlinkat rename renameat rmdir" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure auditd Collects File Deletion Events by User - rmdir + At a minimum, the audit system should collect file deletion events +for all users and root. If the auditd daemon is configured to use the +augenrules program to read audit rules during daemon startup (the +default), add the following line to a file with suffix .rules in the +directory /etc/audit/rules.d, setting ARCH to either b32 or b64 as +appropriate for your system: +-a always,exit -F arch=ARCH -S rmdir -F auid>=1000 -F auid!=unset -F key=delete +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following line to +/etc/audit/audit.rules file, setting ARCH to either b32 or b64 as +appropriate for your system: +-a always,exit -F arch=ARCH -S rmdir -F auid>=1000 -F auid!=unset -F key=delete + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-000366 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.4 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.1.1 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.MA-2 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.2.7 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + SRG-OS-000466-GPOS-00210 + SRG-OS-000467-GPOS-00211 + SRG-OS-000468-GPOS-00212 + SRG-OS-000466-VMM-001870 + SRG-OS-000468-VMM-001890 + Auditing file deletions will create an audit trail for files that are removed +from the system. The audit trail could aid in system troubleshooting, as well as, detecting +malicious processes that attempt to delete log files to conceal their presence. + CCE-83758-3 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83758-3 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.7 + - audit_rules_file_deletion_events_rmdir + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit rmdir tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83758-3 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.7 + - audit_rules_file_deletion_events_rmdir + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for rmdir for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - rmdir + syscall_grouping: + - unlink + - unlinkat + - rename + - renameat + - rmdir + + - name: Check existence of rmdir in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/delete.rules + set_fact: audit_file="/etc/audit/rules.d/delete.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=delete + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - rmdir + syscall_grouping: + - unlink + - unlinkat + - rename + - renameat + - rmdir + + - name: Check existence of rmdir in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=delete + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83758-3 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.7 + - audit_rules_file_deletion_events_rmdir + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for rmdir for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - rmdir + syscall_grouping: + - unlink + - unlinkat + - rename + - renameat + - rmdir + + - name: Check existence of rmdir in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/delete.rules + set_fact: audit_file="/etc/audit/rules.d/delete.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=delete + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - rmdir + syscall_grouping: + - unlink + - unlinkat + - rename + - renameat + - rmdir + + - name: Check existence of rmdir in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=delete + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-83758-3 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.7 + - audit_rules_file_deletion_events_rmdir + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="" + AUID_FILTERS="-F auid>=1000 -F auid!=unset" + SYSCALL="rmdir" + KEY="delete" + SYSCALL_GROUPING="unlink unlinkat rename renameat rmdir" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure auditd Collects File Deletion Events by User - unlink + At a minimum, the audit system should collect file deletion events +for all users and root. If the auditd daemon is configured to use the +augenrules program to read audit rules during daemon startup (the +default), add the following line to a file with suffix .rules in the +directory /etc/audit/rules.d, setting ARCH to either b32 or b64 as +appropriate for your system: +-a always,exit -F arch=ARCH -S unlink -F auid>=1000 -F auid!=unset -F key=delete +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following line to +/etc/audit/audit.rules file, setting ARCH to either b32 or b64 as +appropriate for your system: +-a always,exit -F arch=ARCH -S unlink -F auid>=1000 -F auid!=unset -F key=delete + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-000366 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.4 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.1.1 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.MA-2 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.2.7 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + SRG-OS-000466-GPOS-00210 + SRG-OS-000467-GPOS-00211 + SRG-OS-000468-GPOS-00212 + SRG-OS-000466-VMM-001870 + SRG-OS-000468-VMM-001890 + Auditing file deletions will create an audit trail for files that are removed +from the system. The audit trail could aid in system troubleshooting, as well as, detecting +malicious processes that attempt to delete log files to conceal their presence. + CCE-83757-5 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83757-5 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.7 + - audit_rules_file_deletion_events_unlink + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit unlink tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83757-5 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.7 + - audit_rules_file_deletion_events_unlink + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for unlink for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - unlink + syscall_grouping: + - unlink + - unlinkat + - rename + - renameat + - rmdir + + - name: Check existence of unlink in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/delete.rules + set_fact: audit_file="/etc/audit/rules.d/delete.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=delete + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - unlink + syscall_grouping: + - unlink + - unlinkat + - rename + - renameat + - rmdir + + - name: Check existence of unlink in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=delete + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83757-5 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.7 + - audit_rules_file_deletion_events_unlink + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for unlink for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - unlink + syscall_grouping: + - unlink + - unlinkat + - rename + - renameat + - rmdir + + - name: Check existence of unlink in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/delete.rules + set_fact: audit_file="/etc/audit/rules.d/delete.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=delete + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - unlink + syscall_grouping: + - unlink + - unlinkat + - rename + - renameat + - rmdir + + - name: Check existence of unlink in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=delete + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-83757-5 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.7 + - audit_rules_file_deletion_events_unlink + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="" + AUID_FILTERS="-F auid>=1000 -F auid!=unset" + SYSCALL="unlink" + KEY="delete" + SYSCALL_GROUPING="unlink unlinkat rename renameat rmdir" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure auditd Collects File Deletion Events by User - unlinkat + At a minimum, the audit system should collect file deletion events +for all users and root. If the auditd daemon is configured to use the +augenrules program to read audit rules during daemon startup (the +default), add the following line to a file with suffix .rules in the +directory /etc/audit/rules.d, setting ARCH to either b32 or b64 as +appropriate for your system: +-a always,exit -F arch=ARCH -S unlinkat -F auid>=1000 -F auid!=unset -F key=delete +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following line to +/etc/audit/audit.rules file, setting ARCH to either b32 or b64 as +appropriate for your system: +-a always,exit -F arch=ARCH -S unlinkat -F auid>=1000 -F auid!=unset -F key=delete + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-000366 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.4 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.1.1 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.MA-2 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.2.7 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + SRG-OS-000466-GPOS-00210 + SRG-OS-000467-GPOS-00211 + SRG-OS-000468-GPOS-00212 + SRG-OS-000466-VMM-001870 + SRG-OS-000468-VMM-001890 + Auditing file deletions will create an audit trail for files that are removed +from the system. The audit trail could aid in system troubleshooting, as well as, detecting +malicious processes that attempt to delete log files to conceal their presence. + CCE-83755-9 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83755-9 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.7 + - audit_rules_file_deletion_events_unlinkat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit unlinkat tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83755-9 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.7 + - audit_rules_file_deletion_events_unlinkat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for unlinkat for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - unlinkat + syscall_grouping: + - unlink + - unlinkat + - rename + - renameat + - rmdir + + - name: Check existence of unlinkat in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/delete.rules + set_fact: audit_file="/etc/audit/rules.d/delete.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=delete + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - unlinkat + syscall_grouping: + - unlink + - unlinkat + - rename + - renameat + - rmdir + + - name: Check existence of unlinkat in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=delete + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83755-9 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.7 + - audit_rules_file_deletion_events_unlinkat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for unlinkat for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - unlinkat + syscall_grouping: + - unlink + - unlinkat + - rename + - renameat + - rmdir + + - name: Check existence of unlinkat in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/delete.rules + set_fact: audit_file="/etc/audit/rules.d/delete.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=delete + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - unlinkat + syscall_grouping: + - unlink + - unlinkat + - rename + - renameat + - rmdir + + - name: Check existence of unlinkat in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=delete + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-83755-9 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.7 + - audit_rules_file_deletion_events_unlinkat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="" + AUID_FILTERS="-F auid>=1000 -F auid!=unset" + SYSCALL="unlinkat" + KEY="delete" + SYSCALL_GROUPING="unlink unlinkat rename renameat rmdir" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Record Unauthorized Access Attempts Events to Files (unsuccessful) + At a minimum, the audit system should collect unauthorized file +accesses for all users and root. Note that the "-F arch=b32" lines should be +present even on a 64 bit system. These commands identify system calls for +auditing. Even if the system is 64 bit it can still execute 32 bit system +calls. Additionally, these rules can be configured in a number of ways while +still achieving the desired effect. An example of this is that the "-S" calls +could be split up and placed on separate lines, however, this is less efficient. +Add the following to /etc/audit/audit.rules: +-a always,exit -F arch=b32 -S creat,open,openat,open_by_handle_at,truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=access + -a always,exit -F arch=b32 -S creat,open,openat,open_by_handle_at,truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=access +If your system is 64 bit then these lines should be duplicated and the +arch=b32 replaced with arch=b64 as follows: +-a always,exit -F arch=b64 -S creat,open,openat,open_by_handle_at,truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=access + -a always,exit -F arch=b64 -S creat,open,openat,open_by_handle_at,truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=access + + Record Successful Permission Changes to Files - chmod + At a minimum, the audit system should collect file permission changes +for all users and root. If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: + +-a always,exit -F arch=b32 -S chmod -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S chmod -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S chmod -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S chmod -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping these system +calls with others as identifying earlier in this guide is more efficient. + File permission changes could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + + + + + + Record Successful Ownership Changes to Files - chown + At a minimum, the audit system should collect file ownership changes +for all users and root. If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: + +-a always,exit -F arch=b32 -S chown -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S chown -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S chown -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S chown -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping these system +calls with others as identifying earlier in this guide is more efficient. + File ownership attempts could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + + + + + + Record Successful Access Attempts to Files - creat + At a minimum, the audit system should collect unauthorized file +accesses for all users and root. If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: + +-a always,exit -F arch=b32 -S creat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-access + +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S creat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-access + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S creat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-access + +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S creat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-access + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping these system +calls with others as identifying earlier in this guide is more efficient. + File access attempts could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + + + + + + Record Successful Permission Changes to Files - fchmod + At a minimum, the audit system should collect file permission changes +for all users and root. If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: + +-a always,exit -F arch=b32 -S fchmod -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S fchmod -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S fchmod -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S fchmod -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping these system +calls with others as identifying earlier in this guide is more efficient. + File permission changes could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + + + + + + Record Successful Permission Changes to Files - fchmodat + At a minimum, the audit system should collect file permission changes +for all users and root. If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: + +-a always,exit -F arch=b32 -S fchmodat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S fchmodat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S fchmodat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S fchmodat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping these system +calls with others as identifying earlier in this guide is more efficient. + File permission changes could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + + + + + + Record Successful Ownership Changes to Files - fchown + At a minimum, the audit system should collect file ownership changes +for all users and root. If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: + +-a always,exit -F arch=b32 -S fchown -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S fchown -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S fchown -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S fchown -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping these system +calls with others as identifying earlier in this guide is more efficient. + File ownership attempts could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + + + + + + Record Successful Ownership Changes to Files - fchownat + At a minimum, the audit system should collect file ownership changes +for all users and root. If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: + +-a always,exit -F arch=b32 -S fchownat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S fchownat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S fchownat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S fchownat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping these system +calls with others as identifying earlier in this guide is more efficient. + File ownership attempts could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + + + + + + Record Successful Permission Changes to Files - fremovexattr + At a minimum, the audit system should collect file permission changes +for all users and root. If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: + +-a always,exit -F arch=b32 -S fremovexattr -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S fremovexattr -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S fremovexattr -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S fremovexattr -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping these system +calls with others as identifying earlier in this guide is more efficient. + File permission changes could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + + + + + + Record Successful Permission Changes to Files - fsetxattr + At a minimum, the audit system should collect file permission changes +for all users and root. If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: + +-a always,exit -F arch=b32 -S fsetxattr -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S fsetxattr -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S fsetxattr -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S fsetxattr -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping these system +calls with others as identifying earlier in this guide is more efficient. + File permission changes could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + + + + + + Record Successful Access Attempts to Files - ftruncate + At a minimum, the audit system should collect unauthorized file +accesses for all users and root. If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: + +-a always,exit -F arch=b32 -S ftruncate -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-access + +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S ftruncate -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-access + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S ftruncate -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-access + +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S ftruncate -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-access + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping these system +calls with others as identifying earlier in this guide is more efficient. + File access attempts could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + + + + + + Record Successful Ownership Changes to Files - lchown + At a minimum, the audit system should collect file ownership changes +for all users and root. If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: + +-a always,exit -F arch=b32 -S lchown -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S lchown -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S lchown -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S lchown -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping these system +calls with others as identifying earlier in this guide is more efficient. + File ownership attempts could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + + + + + + Record Successful Permission Changes to Files - lremovexattr + At a minimum, the audit system should collect file permission changes +for all users and root. If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: + +-a always,exit -F arch=b32 -S lremovexattr -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S lremovexattr -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S lremovexattr -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S lremovexattr -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping these system +calls with others as identifying earlier in this guide is more efficient. + File permission changes could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + + + + + + Record Successful Permission Changes to Files - lsetxattr + At a minimum, the audit system should collect file permission changes +for all users and root. If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: + +-a always,exit -F arch=b32 -S lsetxattr -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S lsetxattr -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S lsetxattr -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S lsetxattr -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping these system +calls with others as identifying earlier in this guide is more efficient. + File permission changes could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + + + + + + Record Successful Access Attempts to Files - open + At a minimum, the audit system should collect unauthorized file +accesses for all users and root. If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: + +-a always,exit -F arch=b32 -S open -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-access + +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S open -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-access + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S open -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-access + +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S open -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-access + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping these system +calls with others as identifying earlier in this guide is more efficient. + File access attempts could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + + + + + + Record Successful Access Attempts to Files - open_by_handle_at + At a minimum, the audit system should collect unauthorized file +accesses for all users and root. If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: + +-a always,exit -F arch=b32 -S open_by_handle_at -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-access + +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S open_by_handle_at -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-access + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S open_by_handle_at -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-access + +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S open_by_handle_at -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-access + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping these system +calls with others as identifying earlier in this guide is more efficient. + File access attempts could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + + + + + + Record Successful Creation Attempts to Files - open_by_handle_at O_CREAT + The open_by_handle_at syscall can be used to create new files +when O_CREAT flag is specified. + +The following audit rules will assure that successful attempts to create a +file via open_by_handle_at syscall are collected. + +If the auditd daemon is configured to use the augenrules +program to read audit rules during daemon startup (the default), add the +rules below to a file with suffix .rules in the directory +/etc/audit/rules.d. + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the rules below to +/etc/audit/audit.rules file. + + +-a always,exit -F arch=b32 -S open_by_handle_at -F a2&0100 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create + + +If the system is 64 bit then also add the following lines: + +-a always,exit -F arch=b64 -S open_by_handle_at -F a2&0100 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create + + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping system calls related +to the same event is more efficient. See the following example: +-a always,exit -F arch=b32 -S open_by_handle_at,open_by_handle_at -F a2&0100 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create + Successful attempts to access files could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + + + + + + Record Successful Creation Attempts to Files - open_by_handle_at O_TRUNC_WRITE + The audit system should collect detailed file access records for +all users and root. The open_by_handle_at syscall can be used to modify +files if called for write operation with the O_TRUNC_WRITE flag. + +The following audit rules will assure that successful attempts to create a +file via open_by_handle_at syscall are collected. + +If the auditd daemon is configured to use the augenrules +program to read audit rules during daemon startup (the default), add the +rules below to a file with suffix .rules in the directory +/etc/audit/rules.d. + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the rules below to +/etc/audit/audit.rules file. + + +-a always,exit -F arch=b32 -S open_by_handle_at -F a2&01003 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification + + +If the system is 64 bit then also add the following lines: + +-a always,exit -F arch=b64 -S open_by_handle_at -F a2&01003 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification + + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping system calls related +to the same event is more efficient. See the following example: +-a always,exit -F arch=b32 -S open,open_by_handle_at -F a2&01003 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification + Successful attempts to access files could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + + + + + + Record Successful Creation Attempts to Files - open O_CREAT + The open syscall can be used to create new files +when O_CREAT flag is specified. + +The following audit rules will assure that successful attempts to create a +file via open syscall are collected. + +If the auditd daemon is configured to use the augenrules +program to read audit rules during daemon startup (the default), add the +rules below to a file with suffix .rules in the directory +/etc/audit/rules.d. + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the rules below to +/etc/audit/audit.rules file. + + +-a always,exit -F arch=b32 -S open -F a2&0100 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create + + +If the system is 64 bit then also add the following lines: + +-a always,exit -F arch=b64 -S open -F a2&0100 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create + + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping system calls related +to the same event is more efficient. See the following example: +-a always,exit -F arch=b32 -S open,open -F a2&0100 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create + Successful attempts to access files could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + + + + + + Record Successful Creation Attempts to Files - open O_TRUNC_WRITE + The audit system should collect detailed file access records for +all users and root. The open syscall can be used to modify +files if called for write operation with the O_TRUNC_WRITE flag. + +The following audit rules will assure that successful attempts to create a +file via open syscall are collected. + +If the auditd daemon is configured to use the augenrules +program to read audit rules during daemon startup (the default), add the +rules below to a file with suffix .rules in the directory +/etc/audit/rules.d. + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the rules below to +/etc/audit/audit.rules file. + + +-a always,exit -F arch=b32 -S open -F a2&01003 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification + + +If the system is 64 bit then also add the following lines: + +-a always,exit -F arch=b64 -S open -F a2&01003 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification + + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping system calls related +to the same event is more efficient. See the following example: +-a always,exit -F arch=b32 -S open,openat -F a2&01003 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification + Successful attempts to access files could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + + + + + + Record Successful Access Attempts to Files - openat + At a minimum, the audit system should collect unauthorized file +accesses for all users and root. If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: + +-a always,exit -F arch=b32 -S openat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-access + +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S openat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-access + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S openat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-access + +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S openat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-access + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping these system +calls with others as identifying earlier in this guide is more efficient. + File access attempts could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + + + + + + Record Successful Creation Attempts to Files - openat O_CREAT + The openat syscall can be used to create new files +when O_CREAT flag is specified. + +The following audit rules will assure that successful attempts to create a +file via openat syscall are collected. + +If the auditd daemon is configured to use the augenrules +program to read audit rules during daemon startup (the default), add the +rules below to a file with suffix .rules in the directory +/etc/audit/rules.d. + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the rules below to +/etc/audit/audit.rules file. + + +-a always,exit -F arch=b32 -S openat -F a2&0100 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create + + +If the system is 64 bit then also add the following lines: + +-a always,exit -F arch=b64 -S openat -F a2&0100 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create + + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping system calls related +to the same event is more efficient. See the following example: +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create + Successful attempts to access files could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + + + + + + Record Successful Creation Attempts to Files - openat O_TRUNC_WRITE + The audit system should collect detailed file access records for +all users and root. The openat syscall can be used to modify +files if called for write operation with the O_TRUNC_WRITE flag. + +The following audit rules will assure that successful attempts to create a +file via openat syscall are collected. + +If the auditd daemon is configured to use the augenrules +program to read audit rules during daemon startup (the default), add the +rules below to a file with suffix .rules in the directory +/etc/audit/rules.d. + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the rules below to +/etc/audit/audit.rules file. + + +-a always,exit -F arch=b32 -S openat -F a2&01003 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification + + +If the system is 64 bit then also add the following lines: + +-a always,exit -F arch=b64 -S openat -F a2&01003 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification + + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping system calls related +to the same event is more efficient. See the following example: +-a always,exit -F arch=b32 -S open,openat -F a2&01003 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification + Successful attempts to access files could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + + + + + + Record Successful Permission Changes to Files - removexattr + At a minimum, the audit system should collect file permission changes +for all users and root. If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: + +-a always,exit -F arch=b32 -S removexattr -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S removexattr -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S removexattr -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S removexattr -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping these system +calls with others as identifying earlier in this guide is more efficient. + File permission changes could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + + + + + + Record Successful Delete Attempts to Files - rename + At a minimum, the audit system should collect file +deletion for all users and root. If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: + +-a always,exit -F arch=b32 -S rename -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-delete + +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S rename -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-delete + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S rename -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-delete + +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S rename -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-delete + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping these system +calls with others as identifying earlier in this guide is more efficient. + File deletion attempts could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + + + + + + Record Successful Delete Attempts to Files - renameat + At a minimum, the audit system should collect file +deletion for all users and root. If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: + +-a always,exit -F arch=b32 -S renameat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-delete + +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S renameat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-delete + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S renameat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-delete + +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S renameat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-delete + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping these system +calls with others as identifying earlier in this guide is more efficient. + File deletion attempts could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + + + + + + Record Successful Permission Changes to Files - setxattr + At a minimum, the audit system should collect file permission changes +for all users and root. If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: + +-a always,exit -F arch=b32 -S setxattr -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S setxattr -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S setxattr -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S setxattr -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping these system +calls with others as identifying earlier in this guide is more efficient. + File deletion attempts could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + + + + + + Record Successful Access Attempts to Files - truncate + At a minimum, the audit system should collect unauthorized file +accesses for all users and root. If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: + +-a always,exit -F arch=b32 -S truncate -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-access + +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S truncate -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-access + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S truncate -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-access + +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S truncate -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-access + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping these system +calls with others as identifying earlier in this guide is more efficient. + File access attempts could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + + + + + + Record Successful Delete Attempts to Files - unlink + At a minimum, the audit system should collect file +deletion for all users and root. If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: + +-a always,exit -F arch=b32 -S unlink -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-delete + +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S unlink -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-delete + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S unlink -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-delete + +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S unlink -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-delete + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping these system +calls with others as identifying earlier in this guide is more efficient. + File deletion attempts could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + + + + + + Record Successful Delete Attempts to Files - unlinkat + At a minimum, the audit system should collect file +deletion for all users and root. If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: + +-a always,exit -F arch=b32 -S unlinkat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-delete + +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S unlinkat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-delete + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S unlinkat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-delete + +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S unlinkat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-delete + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping these system +calls with others as identifying earlier in this guide is more efficient. + File deletion attempts could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + + + + + + Ensure auditd Collects Unauthorized Access Attempts to Files (unsuccessful) + At a minimum the audit system should collect unauthorized file +accesses for all users and root. If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: +-a always,exit -F arch=b32 -S creat,open,openat,open_by_handle_at,truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=access +-a always,exit -F arch=b32 -S creat,open,openat,open_by_handle_at,truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=access +If the system is 64 bit then also add the following lines: + +-a always,exit -F arch=b64 -S creat,open,openat,open_by_handle_at,truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=access +-a always,exit -F arch=b64 -S creat,open,openat,open_by_handle_at,truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=access +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S creat,open,openat,open_by_handle_at,truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=access +-a always,exit -F arch=b32 -S creat,open,openat,open_by_handle_at,truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=access +If the system is 64 bit then also add the following lines: + +-a always,exit -F arch=b64 -S creat,open,openat,open_by_handle_at,truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=access +-a always,exit -F arch=b64 -S creat,open,openat,open_by_handle_at,truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=access + This rule checks for multiple syscalls related to unsuccessful file modification; +it was written with DISA STIG in mind. Other policies should use a +separate rule for each syscall that needs to be checked. For example: +audit_rules_unsuccessful_file_modification_openaudit_rules_unsuccessful_file_modification_ftruncateaudit_rules_unsuccessful_file_modification_creat + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 5.4.1.1 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000172 + CCI-002884 + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + 0582 + 0584 + 05885 + 0586 + 0846 + 0957 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + Req-10.2.4 + Req-10.2.1 + Unsuccessful attempts to access files could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + CCE-83793-0 + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# Perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +for ARCH in "${RULE_ARCHS[@]}" +do + + # First fix the -EACCES requirement + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EACCES" + AUID_FILTERS="-F auid>=1000 -F auid!=unset" + SYSCALL="creat open openat open_by_handle_at truncate ftruncate" + KEY="access" + SYSCALL_GROUPING="creat open openat open_by_handle_at truncate ftruncate" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + + # Then fix the -EPERM requirement + # No need to change content of $GROUP variable - it's the same as for -EACCES case above + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EPERM" + AUID_FILTERS="-F auid>=1000 -F auid!=unset" + SYSCALL="creat open openat open_by_handle_at truncate ftruncate" + KEY="access" + SYSCALL_GROUPING="creat open openat open_by_handle_at truncate ftruncate" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Unsuccessful Permission Changes to Files - chmod + The audit system should collect unsuccessful file permission change +attempts for all users and root. +If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d. +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file. +-a always,exit -F arch=b32 -S chmod -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change +-a always,exit -F arch=b32 -S chmod -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S chmod -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change +-a always,exit -F arch=b64 -S chmod -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the audit rule checks a +system call independently of other system calls. Grouping system calls related +to the same event is more efficient. See the following example: +-a always,exit -F arch=b32 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change + CCI-000172 + AU-2(d) + AU-12(c) + CM-6(a) + SRG-OS-000458-VMM-001810 + SRG-OS-000461-VMM-001830 + Unsuccessful attempts to change permissions of files could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + - name: Gather the package facts + package_facts: + manager: auto + tags: + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_chmod + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit chmod tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_chmod + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for chmod EACCES for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - chmod + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of chmod in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - chmod + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of chmod in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_chmod + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for chmod EACCES for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - chmod + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of chmod in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - chmod + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of chmod in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_chmod + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for chmod EPERM for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - chmod + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of chmod in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - chmod + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of chmod in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_chmod + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for chmod EPERM for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - chmod + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of chmod in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - chmod + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of chmod in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_chmod + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="chmod" +KEY="access" +SYSCALL_GROUPING="chmod fchmod fchmodat fsetxattr lsetxattr setxattr" + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EACCES" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EPERM" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Unsuccessful Ownership Changes to Files - chown + The audit system should collect unsuccessful file ownership change +attempts for all users and root. +If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d. +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file. +-a always,exit -F arch=b32 -S chown -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change +-a always,exit -F arch=b32 -S chown -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S chown -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change +-a always,exit -F arch=b64 -S chown -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the audit rule checks a +system call independently of other system calls. Grouping system calls related +to the same event is more efficient. See the following example: +-a always,exit -F arch=b32 -S lchown,fchown,chown,fchownat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change + CCI-000172 + AU-2(d) + AU-12(c) + CM-6(a) + SRG-OS-000458-VMM-001810 + SRG-OS-000461-VMM-001830 + Unsuccessful attempts to change ownership of files could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + - name: Gather the package facts + package_facts: + manager: auto + tags: + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_chown + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit chown tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_chown + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for chown EACCES for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - chown + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of chown in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - chown + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of chown in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_chown + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for chown EACCES for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - chown + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of chown in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - chown + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of chown in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_chown + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for chown EPERM for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - chown + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of chown in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - chown + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of chown in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_chown + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for chown EPERM for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - chown + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of chown in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - chown + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of chown in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_chown + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="chown" +KEY="access" +SYSCALL_GROUPING="chown fchown fchownat lchown" + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EACCES" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EPERM" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Unsuccessful Access Attempts to Files - creat + At a minimum, the audit system should collect unauthorized file +accesses for all users and root. If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: +-a always,exit -F arch=b32 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=access +-a always,exit -F arch=b32 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=access +If the system is 64 bit then also add the following lines: + +-a always,exit -F arch=b64 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=access +-a always,exit -F arch=b64 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=access +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=access +-a always,exit -F arch=b32 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=access +If the system is 64 bit then also add the following lines: + +-a always,exit -F arch=b64 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=access +-a always,exit -F arch=b64 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=access + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping these system +calls with others as identifying earlier in this guide is more efficient. + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.2.4 + Req-10.2.1 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + SRG-OS-000064-GPOS-00033 + SRG-OS-000458-GPOS-00203 + SRG-OS-000461-GPOS-00205 + SRG-OS-000458-VMM-001810 + SRG-OS-000461-VMM-001830 + Unsuccessful attempts to access files could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + CCE-83786-4 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83786-4 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_creat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit creat tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83786-4 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_creat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for creat EACCES for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - creat + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of creat in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - creat + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of creat in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83786-4 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_creat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for creat EACCES for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - creat + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of creat in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - creat + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of creat in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-83786-4 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_creat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for creat EPERM for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - creat + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of creat in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - creat + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of creat in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83786-4 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_creat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for creat EPERM for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - creat + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of creat in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - creat + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of creat in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-83786-4 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_creat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="creat" +KEY="access" +SYSCALL_GROUPING="creat ftruncate truncate open openat open_by_handle_at" + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EACCES" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EPERM" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Unsuccessful Permission Changes to Files - fchmod + The audit system should collect unsuccessful file permission change +attempts for all users and root. +If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d. +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file. +-a always,exit -F arch=b32 -S fchmod -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change +-a always,exit -F arch=b32 -S fchmod -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S fchmod -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change +-a always,exit -F arch=b64 -S fchmod -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the audit rule checks a +system call independently of other system calls. Grouping system calls related +to the same event is more efficient. See the following example: +-a always,exit -F arch=b32 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change + CCI-000172 + AU-2(d) + AU-12(c) + CM-6(a) + SRG-OS-000458-VMM-001810 + SRG-OS-000461-VMM-001830 + Unsuccessful attempts to change permissions of files could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + - name: Gather the package facts + package_facts: + manager: auto + tags: + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_fchmod + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit fchmod tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_fchmod + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for fchmod EACCES for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchmod + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of fchmod in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchmod + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of fchmod in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_fchmod + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for fchmod EACCES for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchmod + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of fchmod in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchmod + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of fchmod in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_fchmod + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for fchmod EPERM for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchmod + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of fchmod in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchmod + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of fchmod in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_fchmod + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for fchmod EPERM for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchmod + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of fchmod in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchmod + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of fchmod in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_fchmod + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="fchmod" +KEY="access" +SYSCALL_GROUPING="chmod fchmod fchmodat fsetxattr lsetxattr setxattr" + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EACCES" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EPERM" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Unsuccessful Permission Changes to Files - fchmodat + The audit system should collect unsuccessful file permission change +attempts for all users and root. +If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d. +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file. +-a always,exit -F arch=b32 -S fchmodat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change +-a always,exit -F arch=b32 -S fchmodat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S fchmodat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change +-a always,exit -F arch=b64 -S fchmodat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the audit rule checks a +system call independently of other system calls. Grouping system calls related +to the same event is more efficient. See the following example: +-a always,exit -F arch=b32 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change + CCI-000172 + AU-2(d) + AU-12(c) + CM-6(a) + SRG-OS-000458-VMM-001810 + SRG-OS-000461-VMM-001830 + Unsuccessful attempts to change permissions of files could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + - name: Gather the package facts + package_facts: + manager: auto + tags: + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_fchmodat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit fchmodat tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_fchmodat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for fchmodat EACCES for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchmodat + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of fchmodat in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchmodat + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of fchmodat in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_fchmodat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for fchmodat EACCES for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchmodat + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of fchmodat in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchmodat + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of fchmodat in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_fchmodat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for fchmodat EPERM for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchmodat + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of fchmodat in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchmodat + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of fchmodat in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_fchmodat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for fchmodat EPERM for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchmodat + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of fchmodat in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchmodat + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of fchmodat in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_fchmodat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="fchmodat" +KEY="access" +SYSCALL_GROUPING="chmod fchmod fchmodat fsetxattr lsetxattr setxattr" + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EACCES" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EPERM" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Unsuccessful Ownership Changes to Files - fchown + The audit system should collect unsuccessful file ownership change +attempts for all users and root. +If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d. +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file. +-a always,exit -F arch=b32 -S fchown -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change +-a always,exit -F arch=b32 -S fchown -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S fchown -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change +-a always,exit -F arch=b64 -S fchown -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the audit rule checks a +system call independently of other system calls. Grouping system calls related +to the same event is more efficient. See the following example: +-a always,exit -F arch=b32 -S lchown,fchown,chown,fchownat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change + CCI-000172 + AU-2(d) + AU-12(c) + CM-6(a) + SRG-OS-000458-VMM-001810 + SRG-OS-000461-VMM-001830 + Unsuccessful attempts to change ownership of files could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + - name: Gather the package facts + package_facts: + manager: auto + tags: + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_fchown + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit fchown tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_fchown + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for fchown EACCES for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchown + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of fchown in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchown + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of fchown in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_fchown + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for fchown EACCES for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchown + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of fchown in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchown + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of fchown in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_fchown + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for fchown EPERM for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchown + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of fchown in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchown + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of fchown in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_fchown + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for fchown EPERM for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchown + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of fchown in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchown + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of fchown in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_fchown + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="fchown" +KEY="access" +SYSCALL_GROUPING="chown fchown fchownat lchown" + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EACCES" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EPERM" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Unsuccessful Ownership Changes to Files - fchownat + The audit system should collect unsuccessful file ownership change +attempts for all users and root. +If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d. +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file. +-a always,exit -F arch=b32 -S fchownat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change +-a always,exit -F arch=b32 -S fchownat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S fchownat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change +-a always,exit -F arch=b64 -S fchownat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the audit rule checks a +system call independently of other system calls. Grouping system calls related +to the same event is more efficient. See the following example: +-a always,exit -F arch=b32 -S lchown,fchown,chown,fchownat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change + CCI-000172 + AU-2(d) + AU-12(c) + CM-6(a) + SRG-OS-000458-VMM-001810 + SRG-OS-000461-VMM-001830 + Unsuccessful attempts to change ownership of files could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + - name: Gather the package facts + package_facts: + manager: auto + tags: + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_fchownat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit fchownat tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_fchownat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for fchownat EACCES for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchownat + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of fchownat in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchownat + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of fchownat in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_fchownat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for fchownat EACCES for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchownat + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of fchownat in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchownat + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of fchownat in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_fchownat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for fchownat EPERM for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchownat + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of fchownat in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchownat + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of fchownat in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_fchownat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for fchownat EPERM for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchownat + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of fchownat in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - fchownat + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of fchownat in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_fchownat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="fchownat" +KEY="access" +SYSCALL_GROUPING="chown fchown fchownat lchown" + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EACCES" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EPERM" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Unsuccessful Permission Changes to Files - fremovexattr + The audit system should collect unsuccessful file permission change +attempts for all users and root. +If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d. +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file. +-a always,exit -F arch=b32 -S fremovexattr -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change +-a always,exit -F arch=b32 -S fremovexattr -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S fremovexattr -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change +-a always,exit -F arch=b64 -S fremovexattr -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the audit rule checks a +system call independently of other system calls. Grouping system calls related +to the same event is more efficient. See the following example: +-a always,exit -F arch=b32 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change + CCI-000172 + AU-2(d) + AU-12(c) + CM-6(a) + SRG-OS-000458-VMM-001810 + SRG-OS-000461-VMM-001830 + Unsuccessful attempts to change permissions of files could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + - name: Gather the package facts + package_facts: + manager: auto + tags: + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_fremovexattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit fremovexattr tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_fremovexattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for fremovexattr EACCES for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - fremovexattr + syscall_grouping: [] + + - name: Check existence of fremovexattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - fremovexattr + syscall_grouping: [] + + - name: Check existence of fremovexattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_fremovexattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for fremovexattr EACCES for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - fremovexattr + syscall_grouping: [] + + - name: Check existence of fremovexattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - fremovexattr + syscall_grouping: [] + + - name: Check existence of fremovexattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_fremovexattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for fremovexattr EPERM for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - fremovexattr + syscall_grouping: [] + + - name: Check existence of fremovexattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - fremovexattr + syscall_grouping: [] + + - name: Check existence of fremovexattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_fremovexattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for fremovexattr EPERM for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - fremovexattr + syscall_grouping: [] + + - name: Check existence of fremovexattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - fremovexattr + syscall_grouping: [] + + - name: Check existence of fremovexattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_fremovexattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="fremovexattr" +KEY="access" +SYSCALL_GROUPING="" + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EACCES" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EPERM" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Unsuccessful Permission Changes to Files - fsetxattr + The audit system should collect unsuccessful file permission change +attempts for all users and root. +If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d. +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file. +-a always,exit -F arch=b32 -S fsetxattr -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change +-a always,exit -F arch=b32 -S fsetxattr -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S fsetxattr -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change +-a always,exit -F arch=b64 -S fsetxattr -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the audit rule checks a +system call independently of other system calls. Grouping system calls related +to the same event is more efficient. See the following example: +-a always,exit -F arch=b32 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change + CCI-000172 + AU-2(d) + AU-12(c) + CM-6(a) + SRG-OS-000458-VMM-001810 + SRG-OS-000461-VMM-001830 + Unsuccessful attempts to change permissions of files could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + - name: Gather the package facts + package_facts: + manager: auto + tags: + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_fsetxattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit fsetxattr tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_fsetxattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for fsetxattr EACCES for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - fsetxattr + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of fsetxattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - fsetxattr + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of fsetxattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_fsetxattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for fsetxattr EACCES for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - fsetxattr + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of fsetxattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - fsetxattr + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of fsetxattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_fsetxattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for fsetxattr EPERM for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - fsetxattr + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of fsetxattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - fsetxattr + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of fsetxattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_fsetxattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for fsetxattr EPERM for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - fsetxattr + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of fsetxattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - fsetxattr + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of fsetxattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_fsetxattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="fsetxattr" +KEY="access" +SYSCALL_GROUPING="chmod fchmod fchmodat fsetxattr lsetxattr setxattr" + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EACCES" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EPERM" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Unsuccessful Access Attempts to Files - ftruncate + At a minimum, the audit system should collect unauthorized file +accesses for all users and root. If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: +-a always,exit -F arch=b32 -S ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=access +-a always,exit -F arch=b32 -S ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=access + +If the system is 64 bit then also add the following lines: + +-a always,exit -F arch=b64 -S ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=access +-a always,exit -F arch=b64 -S ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=access + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=access +-a always,exit -F arch=b32 -S ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=access + +If the system is 64 bit then also add the following lines: + +-a always,exit -F arch=b64 -S ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=access +-a always,exit -F arch=b64 -S ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=access + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping these system +calls with others as identifying earlier in this guide is more efficient. + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.2.4 + Req-10.2.1 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + SRG-OS-000064-GPOS-00033 + SRG-OS-000458-GPOS-00203 + SRG-OS-000461-GPOS-00205 + SRG-OS-000458-VMM-001810 + SRG-OS-000461-VMM-001830 + Unsuccessful attempts to access files could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + CCE-83800-3 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83800-3 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_ftruncate + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit ftruncate tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83800-3 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_ftruncate + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for ftruncate EACCES for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - ftruncate + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of ftruncate in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - ftruncate + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of ftruncate in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83800-3 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_ftruncate + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for ftruncate EACCES for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - ftruncate + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of ftruncate in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - ftruncate + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of ftruncate in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-83800-3 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_ftruncate + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for ftruncate EPERM for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - ftruncate + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of ftruncate in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - ftruncate + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of ftruncate in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83800-3 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_ftruncate + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for ftruncate EPERM for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - ftruncate + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of ftruncate in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - ftruncate + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of ftruncate in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-83800-3 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_ftruncate + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="ftruncate" +KEY="access" +SYSCALL_GROUPING="creat ftruncate truncate open openat open_by_handle_at" + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EACCES" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EPERM" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Unsuccessful Ownership Changes to Files - lchown + The audit system should collect unsuccessful file ownership change +attempts for all users and root. + +If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d. + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file. + +-a always,exit -F arch=b32 -S lchown -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change +-a always,exit -F arch=b32 -S lchown -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change + +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S lchown -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change +-a always,exit -F arch=b64 -S lchown -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the audit rule checks a +system call independently of other system calls. Grouping system calls related +to the same event is more efficient. See the following example: +-a always,exit -F arch=b32 -S lchown,fchown,chown,fchownat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change + CCI-000172 + AU-2(d) + AU-12(c) + CM-6(a) + SRG-OS-000458-VMM-001810 + SRG-OS-000461-VMM-001830 + Unsuccessful attempts to change ownership of files could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + - name: Gather the package facts + package_facts: + manager: auto + tags: + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_lchown + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit lchown tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_lchown + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for lchown EACCES for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - lchown + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of lchown in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - lchown + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of lchown in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_lchown + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for lchown EACCES for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - lchown + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of lchown in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - lchown + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of lchown in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_lchown + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for lchown EPERM for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - lchown + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of lchown in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - lchown + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of lchown in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_lchown + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for lchown EPERM for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - lchown + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of lchown in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - lchown + syscall_grouping: + - chown + - fchown + - fchownat + - lchown + + - name: Check existence of lchown in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_lchown + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="lchown" +KEY="access" +SYSCALL_GROUPING="chown fchown fchownat lchown" + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EACCES" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EPERM" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Unsuccessful Permission Changes to Files - lremovexattr + The audit system should collect unsuccessful file permission change +attempts for all users and root. +If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d. +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file. +-a always,exit -F arch=b32 -S lremovexattr -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change +-a always,exit -F arch=b32 -S lremovexattr -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S lremovexattr -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change +-a always,exit -F arch=b64 -S lremovexattr -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the audit rule checks a +system call independently of other system calls. Grouping system calls related +to the same event is more efficient. See the following example: +-a always,exit -F arch=b32 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change + CCI-000172 + AU-2(d) + AU-12(c) + CM-6(a) + SRG-OS-000458-VMM-001810 + SRG-OS-000461-VMM-001830 + Unsuccessful attempts to change permissions of files could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + - name: Gather the package facts + package_facts: + manager: auto + tags: + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_lremovexattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit lremovexattr tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_lremovexattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for lremovexattr EACCES for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - lremovexattr + syscall_grouping: [] + + - name: Check existence of lremovexattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - lremovexattr + syscall_grouping: [] + + - name: Check existence of lremovexattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_lremovexattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for lremovexattr EACCES for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - lremovexattr + syscall_grouping: [] + + - name: Check existence of lremovexattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - lremovexattr + syscall_grouping: [] + + - name: Check existence of lremovexattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_lremovexattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for lremovexattr EPERM for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - lremovexattr + syscall_grouping: [] + + - name: Check existence of lremovexattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - lremovexattr + syscall_grouping: [] + + - name: Check existence of lremovexattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_lremovexattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for lremovexattr EPERM for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - lremovexattr + syscall_grouping: [] + + - name: Check existence of lremovexattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - lremovexattr + syscall_grouping: [] + + - name: Check existence of lremovexattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_lremovexattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="lremovexattr" +KEY="access" +SYSCALL_GROUPING="" + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EACCES" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EPERM" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Unsuccessful Permission Changes to Files - lsetxattr + The audit system should collect unsuccessful file permission change +attempts for all users and root. +If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d. +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file. +-a always,exit -F arch=b32 -S lsetxattr -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change +-a always,exit -F arch=b32 -S lsetxattr -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S lsetxattr -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change +-a always,exit -F arch=b64 -S lsetxattr -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the audit rule checks a +system call independently of other system calls. Grouping system calls related +to the same event is more efficient. See the following example: +-a always,exit -F arch=b32 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change + CCI-000172 + AU-2(d) + AU-12(c) + CM-6(a) + SRG-OS-000458-VMM-001810 + SRG-OS-000461-VMM-001830 + Unsuccessful attempts to change permissions of files could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + - name: Gather the package facts + package_facts: + manager: auto + tags: + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_lsetxattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit lsetxattr tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_lsetxattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for lsetxattr EACCES for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - lsetxattr + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of lsetxattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - lsetxattr + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of lsetxattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_lsetxattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for lsetxattr EACCES for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - lsetxattr + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of lsetxattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - lsetxattr + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of lsetxattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_lsetxattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for lsetxattr EPERM for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - lsetxattr + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of lsetxattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - lsetxattr + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of lsetxattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_lsetxattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for lsetxattr EPERM for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - lsetxattr + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of lsetxattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - lsetxattr + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of lsetxattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_lsetxattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="lsetxattr" +KEY="access" +SYSCALL_GROUPING="chmod fchmod fchmodat fsetxattr lsetxattr setxattr" + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EACCES" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EPERM" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Unsuccessful Access Attempts to Files - open + At a minimum, the audit system should collect unauthorized file +accesses for all users and root. If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: +-a always,exit -F arch=b32 -S open -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=access +-a always,exit -F arch=b32 -S open -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=access + +If the system is 64 bit then also add the following lines: + +-a always,exit -F arch=b64 -S open -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=access +-a always,exit -F arch=b64 -S open -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=access + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S open -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=access +-a always,exit -F arch=b32 -S open -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=access + +If the system is 64 bit then also add the following lines: + +-a always,exit -F arch=b64 -S open -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=access +-a always,exit -F arch=b64 -S open -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=access + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping these system +calls with others as identifying earlier in this guide is more efficient. + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.2.4 + Req-10.2.1 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + SRG-OS-000064-GPOS-00033 + SRG-OS-000458-GPOS-00203 + SRG-OS-000461-GPOS-00205 + SRG-OS-000458-VMM-001810 + SRG-OS-000461-VMM-001830 + Unsuccessful attempts to access files could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + CCE-83801-1 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83801-1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_open + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit open tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83801-1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_open + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for open EACCES for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - open + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of open in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - open + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of open in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83801-1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_open + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for open EACCES for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - open + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of open in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - open + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of open in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-83801-1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_open + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for open EPERM for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - open + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of open in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - open + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of open in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83801-1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_open + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for open EPERM for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - open + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of open in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - open + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of open in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-83801-1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_open + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="open" +KEY="access" +SYSCALL_GROUPING="creat ftruncate truncate open openat open_by_handle_at" + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EACCES" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EPERM" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Unsuccessful Access Attempts to Files - open_by_handle_at + At a minimum, the audit system should collect unauthorized file +accesses for all users and root. If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: +-a always,exit -F arch=b32 -S open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=access +-a always,exit -F arch=b32 -S open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=access +If the system is 64 bit then also add the following lines: + +-a always,exit -F arch=b64 -S open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=access +-a always,exit -F arch=b64 -S open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=access +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S open_by_handle_at,truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=access +-a always,exit -F arch=b32 -S open_by_handle_at,truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=access +If the system is 64 bit then also add the following lines: + +-a always,exit -F arch=b64 -S open_by_handle_at,truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=access +-a always,exit -F arch=b64 -S open_by_handle_at,truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=access + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping these system +calls with others as identifying earlier in this guide is more efficient. + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.2.4 + Req-10.2.1 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + SRG-OS-000064-GPOS-00033 + SRG-OS-000458-GPOS-00203 + SRG-OS-000461-GPOS-00205 + SRG-OS-000458-VMM-001810 + SRG-OS-000461-VMM-001830 + Unsuccessful attempts to access files could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + CCE-83796-3 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83796-3 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_open_by_handle_at + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit open_by_handle_at tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83796-3 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_open_by_handle_at + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for open_by_handle_at EACCES for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - open_by_handle_at + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of open_by_handle_at in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - open_by_handle_at + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of open_by_handle_at in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83796-3 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_open_by_handle_at + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for open_by_handle_at EACCES for x86_64 + platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - open_by_handle_at + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of open_by_handle_at in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - open_by_handle_at + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of open_by_handle_at in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-83796-3 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_open_by_handle_at + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for open_by_handle_at EPERM for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - open_by_handle_at + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of open_by_handle_at in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - open_by_handle_at + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of open_by_handle_at in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83796-3 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_open_by_handle_at + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for open_by_handle_at EPERM for x86_64 + platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - open_by_handle_at + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of open_by_handle_at in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - open_by_handle_at + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of open_by_handle_at in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-83796-3 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_open_by_handle_at + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="open_by_handle_at" +KEY="access" +SYSCALL_GROUPING="creat ftruncate truncate open openat open_by_handle_at" + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EACCES" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EPERM" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Unsuccessful Creation Attempts to Files - open_by_handle_at O_CREAT + The audit system should collect unauthorized file accesses for +all users and root. The open_by_handle_at syscall can be used to create new files +when O_CREAT flag is specified. + +The following auidt rules will asure that unsuccessful attempts to create a +file via open_by_handle_at syscall are collected. + +If the auditd daemon is configured to use the augenrules +program to read audit rules during daemon startup (the default), add the +rules below to a file with suffix .rules in the directory +/etc/audit/rules.d. + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the rules below to +/etc/audit/audit.rules file. + +-a always,exit -F arch=b32 -S open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + + +If the system is 64 bit then also add the following lines: + +-a always,exit -F arch=b64 -S open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping system calls related +to the same event is more efficient. See the following example: +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.2.4 + Req-10.2.1 + SRG-OS-000064-GPOS-00033 + SRG-OS-000458-GPOS-00203 + SRG-OS-000461-GPOS-00205 + SRG-OS-000392-GPOS-00172 + SRG-OS-000458-VMM-001810 + SRG-OS-000461-VMM-001830 + Unsuccessful attempts to access files could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + CCE-86899-2 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-86899-2 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_open_by_handle_at_o_creat + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Add unsuccessful file operations audit rules + blockinfile: + path: /etc/audit/rules.d/30-ospp-v42-remediation.rules + create: true + block: |- + ## This content is a section of an Audit config snapshot recommended for Red Hat Enterprise Linux 9 systems that target OSPP compliance. + ## The following content has been retreived on 2019-03-11 from: https://github.com/linux-audit/audit-userspace/blob/master/rules/30-ospp-v42.rules + + ## The purpose of these rules is to meet the requirements for Operating + ## System Protection Profile (OSPP)v4.2. These rules depends on having + ## 10-base-config.rules, 11-loginuid.rules, and 43-module-load.rules installed. + + ## Unsuccessful file creation (open with O_CREAT) + -a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b32 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b64 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b32 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b64 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + + ## Unsuccessful file modifications (open for write or truncate) + -a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b32 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b32 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + + ## Unsuccessful file access (any other opens) This has to go last. + -a always,exit -F arch=b32 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-access + -a always,exit -F arch=b64 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-access + -a always,exit -F arch=b32 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-access + -a always,exit -F arch=b64 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-access + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86899-2 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_open_by_handle_at_o_creat + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +mkdir -p "$(dirname '/etc/audit/rules.d/30-ospp-v42-remediation.rules')" +cat <<EOF > "/etc/audit/rules.d/30-ospp-v42-remediation.rules" +## This content is a section of an Audit config snapshot recommended for linux systems that target OSPP compliance. +## The following content has been retreived on 2019-03-11 from: https://github.com/linux-audit/audit-userspace/blob/master/rules/30-ospp-v42.rules + +## The purpose of these rules is to meet the requirements for Operating +## System Protection Profile (OSPP)v4.2. These rules depends on having +## 10-base-config.rules, 11-loginuid.rules, and 43-module-load.rules installed. + +## Unsuccessful file creation (open with O_CREAT) +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + +## Unsuccessful file modifications (open for write or truncate) +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + +## Unsuccessful file access (any other opens) This has to go last. +-a always,exit -F arch=b32 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-access +-a always,exit -F arch=b64 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-access +-a always,exit -F arch=b32 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-access +-a always,exit -F arch=b64 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-access +EOF + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Unsuccessful Modification Attempts to Files - open_by_handle_at O_TRUNC_WRITE + The audit system should collect detailed unauthorized file accesses for +all users and root. The open_by_handle_at syscall can be used to modify files +if called for write operation of with O_TRUNC_WRITE flag. + +The following auidt rules will asure that unsuccessful attempts to modify a +file via open_by_handle_at syscall are collected. + +If the auditd daemon is configured to use the augenrules +program to read audit rules during daemon startup (the default), add the +rules below to a file with suffix .rules in the directory +/etc/audit/rules.d. + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the rules below to +/etc/audit/audit.rules file. + +-a always,exit -F arch=b32 -S open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + + +If the system is 64 bit then also add the following lines: + +-a always,exit -F arch=b64 -S open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping system calls related +to the same event is more efficient. See the following example: +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.2.4 + Req-10.2.1 + SRG-OS-000064-GPOS-00033 + SRG-OS-000458-GPOS-00203 + SRG-OS-000461-GPOS-00205 + SRG-OS-000392-GPOS-00172 + SRG-OS-000458-VMM-001810 + SRG-OS-000461-VMM-001830 + Unsuccessful attempts to access files could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + CCE-90286-6 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-90286-6 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_open_by_handle_at_o_trunc_write + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Add unsuccessful file operations audit rules + blockinfile: + path: /etc/audit/rules.d/30-ospp-v42-remediation.rules + create: true + block: |- + ## This content is a section of an Audit config snapshot recommended for Red Hat Enterprise Linux 9 systems that target OSPP compliance. + ## The following content has been retreived on 2019-03-11 from: https://github.com/linux-audit/audit-userspace/blob/master/rules/30-ospp-v42.rules + + ## The purpose of these rules is to meet the requirements for Operating + ## System Protection Profile (OSPP)v4.2. These rules depends on having + ## 10-base-config.rules, 11-loginuid.rules, and 43-module-load.rules installed. + + ## Unsuccessful file creation (open with O_CREAT) + -a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b32 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b64 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b32 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b64 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + + ## Unsuccessful file modifications (open for write or truncate) + -a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b32 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b32 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + + ## Unsuccessful file access (any other opens) This has to go last. + -a always,exit -F arch=b32 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-access + -a always,exit -F arch=b64 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-access + -a always,exit -F arch=b32 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-access + -a always,exit -F arch=b64 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-access + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-90286-6 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_open_by_handle_at_o_trunc_write + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +mkdir -p "$(dirname '/etc/audit/rules.d/30-ospp-v42-remediation.rules')" +cat <<EOF > "/etc/audit/rules.d/30-ospp-v42-remediation.rules" +## This content is a section of an Audit config snapshot recommended for linux systems that target OSPP compliance. +## The following content has been retreived on 2019-03-11 from: https://github.com/linux-audit/audit-userspace/blob/master/rules/30-ospp-v42.rules + +## The purpose of these rules is to meet the requirements for Operating +## System Protection Profile (OSPP)v4.2. These rules depends on having +## 10-base-config.rules, 11-loginuid.rules, and 43-module-load.rules installed. + +## Unsuccessful file creation (open with O_CREAT) +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + +## Unsuccessful file modifications (open for write or truncate) +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + +## Unsuccessful file access (any other opens) This has to go last. +-a always,exit -F arch=b32 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-access +-a always,exit -F arch=b64 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-access +-a always,exit -F arch=b32 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-access +-a always,exit -F arch=b64 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-access +EOF + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure auditd Unauthorized Access Attempts To open_by_handle_at Are Ordered Correctly + The audit system should collect detailed unauthorized file +accesses for all users and root. +To correctly identify unsuccessful creation, unsuccessful modification and unsuccessful access +of files via open_by_handle_at syscall the audit rules collecting these events need to be in certain order. +The more specific rules need to come before the less specific rules. The reason for that is that more +specific rules cover a subset of events covered in the less specific rules, thus, they need to come +before to not be overshadowed by less specific rules, which match a bigger set of events. +Make sure that rules for unsuccessful calls of open_by_handle_at syscall are in the order shown below. +If the auditd daemon is configured to use the augenrules +program to read audit rules during daemon startup (the default), check the order of +rules below in a file with suffix .rules in the directory +/etc/audit/rules.d. +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, check the order of rules below in +/etc/audit/audit.rules file. + +-a always,exit -F arch=b32 -S open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-access +-a always,exit -F arch=b32 -S open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-access + +If the system is 64 bit then also add the following lines: + +-a always,exit -F arch=b64 -S open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-access +-a always,exit -F arch=b64 -S open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-access + + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.2.4 + Req-10.2.1 + SRG-OS-000064-GPOS-00033 + SRG-OS-000458-GPOS-00203 + SRG-OS-000461-GPOS-00205 + SRG-OS-000392-GPOS-00172 + SRG-OS-000458-VMM-001810 + SRG-OS-000461-VMM-001830 + The more specific rules cover a subset of events covered by the less specific rules. +By ordering them from more specific to less specific, it is assured that the less specific +rule will not catch events better recorded by the more specific rule. + CCE-89998-9 + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +mkdir -p "$(dirname '/etc/audit/rules.d/30-ospp-v42-remediation.rules')" +cat <<EOF > "/etc/audit/rules.d/30-ospp-v42-remediation.rules" +## This content is a section of an Audit config snapshot recommended for linux systems that target OSPP compliance. +## The following content has been retreived on 2019-03-11 from: https://github.com/linux-audit/audit-userspace/blob/master/rules/30-ospp-v42.rules + +## The purpose of these rules is to meet the requirements for Operating +## System Protection Profile (OSPP)v4.2. These rules depends on having +## 10-base-config.rules, 11-loginuid.rules, and 43-module-load.rules installed. + +## Unsuccessful file creation (open with O_CREAT) +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + +## Unsuccessful file modifications (open for write or truncate) +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + +## Unsuccessful file access (any other opens) This has to go last. +-a always,exit -F arch=b32 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-access +-a always,exit -F arch=b64 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-access +-a always,exit -F arch=b32 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-access +-a always,exit -F arch=b64 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-access +EOF + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Unsuccessful Creation Attempts to Files - open O_CREAT + The audit system should collect unauthorized file accesses for +all users and root. The open syscall can be used to create new files +when O_CREAT flag is specified. + +The following auidt rules will asure that unsuccessful attempts to create a +file via open syscall are collected. + +If the auditd daemon is configured to use the augenrules +program to read audit rules during daemon startup (the default), add the +rules below to a file with suffix .rules in the directory +/etc/audit/rules.d. + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the rules below to +/etc/audit/audit.rules file. + +-a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + + +If the system is 64 bit then also add the following lines: + +-a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping system calls related +to the same event is more efficient. See the following example: +-a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.2.4 + Req-10.2.1 + SRG-OS-000064-GPOS-00033 + SRG-OS-000458-GPOS-00203 + SRG-OS-000461-GPOS-00205 + SRG-OS-000392-GPOS-00172 + SRG-OS-000458-VMM-001810 + SRG-OS-000461-VMM-001830 + Unsuccessful attempts to access files could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + CCE-86173-2 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-86173-2 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_open_o_creat + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Add unsuccessful file operations audit rules + blockinfile: + path: /etc/audit/rules.d/30-ospp-v42-remediation.rules + create: true + block: |- + ## This content is a section of an Audit config snapshot recommended for Red Hat Enterprise Linux 9 systems that target OSPP compliance. + ## The following content has been retreived on 2019-03-11 from: https://github.com/linux-audit/audit-userspace/blob/master/rules/30-ospp-v42.rules + + ## The purpose of these rules is to meet the requirements for Operating + ## System Protection Profile (OSPP)v4.2. These rules depends on having + ## 10-base-config.rules, 11-loginuid.rules, and 43-module-load.rules installed. + + ## Unsuccessful file creation (open with O_CREAT) + -a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b32 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b64 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b32 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b64 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + + ## Unsuccessful file modifications (open for write or truncate) + -a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b32 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b32 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + + ## Unsuccessful file access (any other opens) This has to go last. + -a always,exit -F arch=b32 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-access + -a always,exit -F arch=b64 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-access + -a always,exit -F arch=b32 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-access + -a always,exit -F arch=b64 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-access + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86173-2 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_open_o_creat + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +mkdir -p "$(dirname '/etc/audit/rules.d/30-ospp-v42-remediation.rules')" +cat <<EOF > "/etc/audit/rules.d/30-ospp-v42-remediation.rules" +## This content is a section of an Audit config snapshot recommended for linux systems that target OSPP compliance. +## The following content has been retreived on 2019-03-11 from: https://github.com/linux-audit/audit-userspace/blob/master/rules/30-ospp-v42.rules + +## The purpose of these rules is to meet the requirements for Operating +## System Protection Profile (OSPP)v4.2. These rules depends on having +## 10-base-config.rules, 11-loginuid.rules, and 43-module-load.rules installed. + +## Unsuccessful file creation (open with O_CREAT) +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + +## Unsuccessful file modifications (open for write or truncate) +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + +## Unsuccessful file access (any other opens) This has to go last. +-a always,exit -F arch=b32 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-access +-a always,exit -F arch=b64 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-access +-a always,exit -F arch=b32 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-access +-a always,exit -F arch=b64 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-access +EOF + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Unsuccessful Modification Attempts to Files - open O_TRUNC_WRITE + The audit system should collect detailed unauthorized file accesses for +all users and root. The open syscall can be used to modify files +if called for write operation of with O_TRUNC_WRITE flag. +The following auidt rules will asure that unsuccessful attempts to modify a +file via open syscall are collected. +If the auditd daemon is configured to use the augenrules +program to read audit rules during daemon startup (the default), add the +rules below to a file with suffix .rules in the directory +/etc/audit/rules.d. +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the rules below to +/etc/audit/audit.rules file. + +-a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + +If the system is 64 bit then also add the following lines: + +-a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping system calls related +to the same event is more efficient. See the following example: +-a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.2.4 + Req-10.2.1 + SRG-OS-000064-GPOS-00033 + SRG-OS-000458-GPOS-00203 + SRG-OS-000461-GPOS-00205 + SRG-OS-000392-GPOS-00172 + SRG-OS-000458-VMM-001810 + SRG-OS-000461-VMM-001830 + Unsuccessful attempts to access files could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + CCE-90569-5 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-90569-5 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_open_o_trunc_write + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Add unsuccessful file operations audit rules + blockinfile: + path: /etc/audit/rules.d/30-ospp-v42-remediation.rules + create: true + block: |- + ## This content is a section of an Audit config snapshot recommended for Red Hat Enterprise Linux 9 systems that target OSPP compliance. + ## The following content has been retreived on 2019-03-11 from: https://github.com/linux-audit/audit-userspace/blob/master/rules/30-ospp-v42.rules + + ## The purpose of these rules is to meet the requirements for Operating + ## System Protection Profile (OSPP)v4.2. These rules depends on having + ## 10-base-config.rules, 11-loginuid.rules, and 43-module-load.rules installed. + + ## Unsuccessful file creation (open with O_CREAT) + -a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b32 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b64 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b32 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b64 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + + ## Unsuccessful file modifications (open for write or truncate) + -a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b32 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b32 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + + ## Unsuccessful file access (any other opens) This has to go last. + -a always,exit -F arch=b32 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-access + -a always,exit -F arch=b64 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-access + -a always,exit -F arch=b32 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-access + -a always,exit -F arch=b64 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-access + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-90569-5 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_open_o_trunc_write + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +mkdir -p "$(dirname '/etc/audit/rules.d/30-ospp-v42-remediation.rules')" +cat <<EOF > "/etc/audit/rules.d/30-ospp-v42-remediation.rules" +## This content is a section of an Audit config snapshot recommended for linux systems that target OSPP compliance. +## The following content has been retreived on 2019-03-11 from: https://github.com/linux-audit/audit-userspace/blob/master/rules/30-ospp-v42.rules + +## The purpose of these rules is to meet the requirements for Operating +## System Protection Profile (OSPP)v4.2. These rules depends on having +## 10-base-config.rules, 11-loginuid.rules, and 43-module-load.rules installed. + +## Unsuccessful file creation (open with O_CREAT) +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + +## Unsuccessful file modifications (open for write or truncate) +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + +## Unsuccessful file access (any other opens) This has to go last. +-a always,exit -F arch=b32 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-access +-a always,exit -F arch=b64 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-access +-a always,exit -F arch=b32 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-access +-a always,exit -F arch=b64 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-access +EOF + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure auditd Rules For Unauthorized Attempts To open Are Ordered Correctly + The audit system should collect detailed unauthorized file +accesses for all users and root. +To correctly identify unsuccessful creation, unsuccessful modification and unsuccessful access +of files via open syscall the audit rules collecting these events need to be in certain order. +The more specific rules need to come before the less specific rules. The reason for that is that more +specific rules cover a subset of events covered in the less specific rules, thus, they need to come +before to not be overshadowed by less specific rules, which match a bigger set of events. +Make sure that rules for unsuccessful calls of open syscall are in the order shown below. +If the auditd daemon is configured to use the augenrules +program to read audit rules during daemon startup (the default), check the order of +rules below in a file with suffix .rules in the directory +/etc/audit/rules.d. +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, check the order of rules below in +/etc/audit/audit.rules file. + +-a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S open -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-access +-a always,exit -F arch=b32 -S open -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-access + +If the system is 64 bit then also add the following lines: + +-a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S open -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-access +-a always,exit -F arch=b64 -S open -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-access + + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.2.4 + Req-10.2.1 + SRG-OS-000064-GPOS-00033 + SRG-OS-000458-GPOS-00203 + SRG-OS-000461-GPOS-00205 + SRG-OS-000392-GPOS-00172 + SRG-OS-000458-VMM-001810 + SRG-OS-000461-VMM-001830 + The more specific rules cover a subset of events covered by the less specific rules. +By ordering them from more specific to less specific, it is assured that the less specific +rule will not catch events better recorded by the more specific rule. + CCE-89777-7 + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +mkdir -p "$(dirname '/etc/audit/rules.d/30-ospp-v42-remediation.rules')" +cat <<EOF > "/etc/audit/rules.d/30-ospp-v42-remediation.rules" +## This content is a section of an Audit config snapshot recommended for linux systems that target OSPP compliance. +## The following content has been retreived on 2019-03-11 from: https://github.com/linux-audit/audit-userspace/blob/master/rules/30-ospp-v42.rules + +## The purpose of these rules is to meet the requirements for Operating +## System Protection Profile (OSPP)v4.2. These rules depends on having +## 10-base-config.rules, 11-loginuid.rules, and 43-module-load.rules installed. + +## Unsuccessful file creation (open with O_CREAT) +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + +## Unsuccessful file modifications (open for write or truncate) +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + +## Unsuccessful file access (any other opens) This has to go last. +-a always,exit -F arch=b32 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-access +-a always,exit -F arch=b64 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-access +-a always,exit -F arch=b32 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-access +-a always,exit -F arch=b64 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-access +EOF + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Unsuccessful Access Attempts to Files - openat + At a minimum, the audit system should collect unauthorized file +accesses for all users and root. If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: +-a always,exit -F arch=b32 -S openat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=access +-a always,exit -F arch=b32 -S openat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=access + +If the system is 64 bit then also add the following lines: + +-a always,exit -F arch=b64 -S openat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=access +-a always,exit -F arch=b64 -S openat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=access + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S openat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=access +-a always,exit -F arch=b32 -S openat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=access + +If the system is 64 bit then also add the following lines: + +-a always,exit -F arch=b64 -S openat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=access +-a always,exit -F arch=b64 -S openat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=access + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping these system +calls with others as identifying earlier in this guide is more efficient. + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.2.4 + Req-10.2.1 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + SRG-OS-000064-GPOS-00033 + SRG-OS-000458-GPOS-00203 + SRG-OS-000461-GPOS-00205 + SRG-OS-000458-VMM-001810 + SRG-OS-000461-VMM-001830 + Unsuccessful attempts to access files could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + CCE-83794-8 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83794-8 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_openat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit openat tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83794-8 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_openat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for openat EACCES for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - openat + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of openat in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - openat + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of openat in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83794-8 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_openat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for openat EACCES for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - openat + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of openat in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - openat + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of openat in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-83794-8 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_openat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for openat EPERM for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - openat + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of openat in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - openat + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of openat in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83794-8 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_openat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for openat EPERM for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - openat + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of openat in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - openat + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of openat in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-83794-8 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_openat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="openat" +KEY="access" +SYSCALL_GROUPING="creat ftruncate truncate open openat open_by_handle_at" + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EACCES" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EPERM" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Unsuccessful Creation Attempts to Files - openat O_CREAT + The audit system should collect unauthorized file accesses for +all users and root. The openat syscall can be used to create new files +when O_CREAT flag is specified. + +The following auidt rules will asure that unsuccessful attempts to create a +file via openat syscall are collected. + +If the auditd daemon is configured to use the augenrules +program to read audit rules during daemon startup (the default), add the +rules below to a file with suffix .rules in the directory +/etc/audit/rules.d. + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the rules below to +/etc/audit/audit.rules file. + +-a always,exit -F arch=b32 -S openat -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S openat -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + + +If the system is 64 bit then also add the following lines: + +-a always,exit -F arch=b64 -S openat -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S openat -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping system calls related +to the same event is more efficient. See the following example: +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.2.4 + Req-10.2.1 + SRG-OS-000064-GPOS-00033 + SRG-OS-000458-GPOS-00203 + SRG-OS-000461-GPOS-00205 + SRG-OS-000392-GPOS-00172 + SRG-OS-000458-VMM-001810 + SRG-OS-000461-VMM-001830 + Unsuccessful attempts to access files could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + CCE-86238-3 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-86238-3 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_openat_o_creat + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Add unsuccessful file operations audit rules + blockinfile: + path: /etc/audit/rules.d/30-ospp-v42-remediation.rules + create: true + block: |- + ## This content is a section of an Audit config snapshot recommended for Red Hat Enterprise Linux 9 systems that target OSPP compliance. + ## The following content has been retreived on 2019-03-11 from: https://github.com/linux-audit/audit-userspace/blob/master/rules/30-ospp-v42.rules + + ## The purpose of these rules is to meet the requirements for Operating + ## System Protection Profile (OSPP)v4.2. These rules depends on having + ## 10-base-config.rules, 11-loginuid.rules, and 43-module-load.rules installed. + + ## Unsuccessful file creation (open with O_CREAT) + -a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b32 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b64 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b32 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b64 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + + ## Unsuccessful file modifications (open for write or truncate) + -a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b32 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b32 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + + ## Unsuccessful file access (any other opens) This has to go last. + -a always,exit -F arch=b32 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-access + -a always,exit -F arch=b64 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-access + -a always,exit -F arch=b32 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-access + -a always,exit -F arch=b64 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-access + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86238-3 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_openat_o_creat + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +mkdir -p "$(dirname '/etc/audit/rules.d/30-ospp-v42-remediation.rules')" +cat <<EOF > "/etc/audit/rules.d/30-ospp-v42-remediation.rules" +## This content is a section of an Audit config snapshot recommended for linux systems that target OSPP compliance. +## The following content has been retreived on 2019-03-11 from: https://github.com/linux-audit/audit-userspace/blob/master/rules/30-ospp-v42.rules + +## The purpose of these rules is to meet the requirements for Operating +## System Protection Profile (OSPP)v4.2. These rules depends on having +## 10-base-config.rules, 11-loginuid.rules, and 43-module-load.rules installed. + +## Unsuccessful file creation (open with O_CREAT) +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + +## Unsuccessful file modifications (open for write or truncate) +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + +## Unsuccessful file access (any other opens) This has to go last. +-a always,exit -F arch=b32 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-access +-a always,exit -F arch=b64 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-access +-a always,exit -F arch=b32 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-access +-a always,exit -F arch=b64 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-access +EOF + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Unsuccessful Modification Attempts to Files - openat O_TRUNC_WRITE + The audit system should collect detailed unauthorized file accesses for +all users and root. The openat syscall can be used to modify files +if called for write operation of with O_TRUNC_WRITE flag. + +The following auidt rules will asure that unsuccessful attempts to modify a +file via openat syscall are collected. + +If the auditd daemon is configured to use the augenrules +program to read audit rules during daemon startup (the default), add the +rules below to a file with suffix .rules in the directory +/etc/audit/rules.d. + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the rules below to +/etc/audit/audit.rules file. + +-a always,exit -F arch=b32 -S openat -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S openat -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + + +If the system is 64 bit then also add the following lines: + +-a always,exit -F arch=b64 -S openat -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S openat -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping system calls related +to the same event is more efficient. See the following example: +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.2.4 + Req-10.2.1 + SRG-OS-000064-GPOS-00033 + SRG-OS-000458-GPOS-00203 + SRG-OS-000461-GPOS-00205 + SRG-OS-000392-GPOS-00172 + SRG-OS-000458-VMM-001810 + SRG-OS-000461-VMM-001830 + Unsuccessful attempts to access files could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + CCE-89488-1 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-89488-1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_openat_o_trunc_write + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Add unsuccessful file operations audit rules + blockinfile: + path: /etc/audit/rules.d/30-ospp-v42-remediation.rules + create: true + block: |- + ## This content is a section of an Audit config snapshot recommended for Red Hat Enterprise Linux 9 systems that target OSPP compliance. + ## The following content has been retreived on 2019-03-11 from: https://github.com/linux-audit/audit-userspace/blob/master/rules/30-ospp-v42.rules + + ## The purpose of these rules is to meet the requirements for Operating + ## System Protection Profile (OSPP)v4.2. These rules depends on having + ## 10-base-config.rules, 11-loginuid.rules, and 43-module-load.rules installed. + + ## Unsuccessful file creation (open with O_CREAT) + -a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b32 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b64 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b32 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b64 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + + ## Unsuccessful file modifications (open for write or truncate) + -a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b32 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b32 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + + ## Unsuccessful file access (any other opens) This has to go last. + -a always,exit -F arch=b32 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-access + -a always,exit -F arch=b64 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-access + -a always,exit -F arch=b32 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-access + -a always,exit -F arch=b64 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-access + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-89488-1 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_openat_o_trunc_write + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +mkdir -p "$(dirname '/etc/audit/rules.d/30-ospp-v42-remediation.rules')" +cat <<EOF > "/etc/audit/rules.d/30-ospp-v42-remediation.rules" +## This content is a section of an Audit config snapshot recommended for linux systems that target OSPP compliance. +## The following content has been retreived on 2019-03-11 from: https://github.com/linux-audit/audit-userspace/blob/master/rules/30-ospp-v42.rules + +## The purpose of these rules is to meet the requirements for Operating +## System Protection Profile (OSPP)v4.2. These rules depends on having +## 10-base-config.rules, 11-loginuid.rules, and 43-module-load.rules installed. + +## Unsuccessful file creation (open with O_CREAT) +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + +## Unsuccessful file modifications (open for write or truncate) +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + +## Unsuccessful file access (any other opens) This has to go last. +-a always,exit -F arch=b32 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-access +-a always,exit -F arch=b64 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-access +-a always,exit -F arch=b32 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-access +-a always,exit -F arch=b64 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-access +EOF + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure auditd Rules For Unauthorized Attempts To openat Are Ordered Correctly + The audit system should collect detailed unauthorized file +accesses for all users and root. +To correctly identify unsuccessful creation, unsuccessful modification and unsuccessful access +of files via openat syscall the audit rules collecting these events need to be in certain order. +The more specific rules need to come before the less specific rules. The reason for that is that more +specific rules cover a subset of events covered in the less specific rules, thus, they need to come +before to not be overshadowed by less specific rules, which match a bigger set of events. +Make sure that rules for unsuccessful calls of openat syscall are in the order shown below. +If the auditd daemon is configured to use the augenrules +program to read audit rules during daemon startup (the default), check the order of +rules below in a file with suffix .rules in the directory +/etc/audit/rules.d. +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, check the order of rules below in +/etc/audit/audit.rules file. + +-a always,exit -F arch=b32 -S openat -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S openat -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S openat -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S openat -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S openat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-access +-a always,exit -F arch=b32 -S openat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-access + +If the system is 64 bit then also add the following lines: + +-a always,exit -F arch=b64 -S openat -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S openat -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S openat -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S openat -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S openat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-access +-a always,exit -F arch=b64 -S openat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-access + + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.2.4 + Req-10.2.1 + SRG-OS-000064-GPOS-00033 + SRG-OS-000458-GPOS-00203 + SRG-OS-000461-GPOS-00205 + SRG-OS-000392-GPOS-00172 + SRG-OS-000458-VMM-001810 + SRG-OS-000461-VMM-001830 + The more specific rules cover a subset of events covered by the less specific rules. +By ordering them from more specific to less specific, it is assured that the less specific +rule will not catch events better recorded by the more specific rule. + CCE-90137-1 + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +mkdir -p "$(dirname '/etc/audit/rules.d/30-ospp-v42-remediation.rules')" +cat <<EOF > "/etc/audit/rules.d/30-ospp-v42-remediation.rules" +## This content is a section of an Audit config snapshot recommended for linux systems that target OSPP compliance. +## The following content has been retreived on 2019-03-11 from: https://github.com/linux-audit/audit-userspace/blob/master/rules/30-ospp-v42.rules + +## The purpose of these rules is to meet the requirements for Operating +## System Protection Profile (OSPP)v4.2. These rules depends on having +## 10-base-config.rules, 11-loginuid.rules, and 43-module-load.rules installed. + +## Unsuccessful file creation (open with O_CREAT) +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + +## Unsuccessful file modifications (open for write or truncate) +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b32 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification +-a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + +## Unsuccessful file access (any other opens) This has to go last. +-a always,exit -F arch=b32 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-access +-a always,exit -F arch=b64 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-access +-a always,exit -F arch=b32 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-access +-a always,exit -F arch=b64 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-access +EOF + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Unsuccessful Permission Changes to Files - removexattr + The audit system should collect unsuccessful file permission change +attempts for all users and root. +If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d. +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file. +-a always,exit -F arch=b32 -S removexattr -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change +-a always,exit -F arch=b32 -S removexattr -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S removexattr -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change +-a always,exit -F arch=b64 -S removexattr -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the audit rule checks a +system call independently of other system calls. Grouping system calls related +to the same event is more efficient. See the following example: +-a always,exit -F arch=b32 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change + CCI-000172 + AU-2(d) + AU-12(c) + CM-6(a) + SRG-OS-000458-VMM-001810 + SRG-OS-000461-VMM-001830 + Unsuccessful attempts to change permissions of files could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + - name: Gather the package facts + package_facts: + manager: auto + tags: + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_removexattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit removexattr tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_removexattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for removexattr EACCES for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - removexattr + syscall_grouping: [] + + - name: Check existence of removexattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - removexattr + syscall_grouping: [] + + - name: Check existence of removexattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_removexattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for removexattr EACCES for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - removexattr + syscall_grouping: [] + + - name: Check existence of removexattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - removexattr + syscall_grouping: [] + + - name: Check existence of removexattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_removexattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for removexattr EPERM for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - removexattr + syscall_grouping: [] + + - name: Check existence of removexattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - removexattr + syscall_grouping: [] + + - name: Check existence of removexattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_removexattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for removexattr EPERM for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - removexattr + syscall_grouping: [] + + - name: Check existence of removexattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - removexattr + syscall_grouping: [] + + - name: Check existence of removexattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_removexattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="removexattr" +KEY="access" +SYSCALL_GROUPING="" + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EACCES" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EPERM" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Unsuccessful Delete Attempts to Files - rename + The audit system should collect unsuccessful file deletion +attempts for all users and root. If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d. +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file. +-a always,exit -F arch=b32 -S rename -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete +-a always,exit -F arch=b32 -S rename -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete +If the system is 64 bit then also add the following lines: + +-a always,exit -F arch=b64 -S rename -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete +-a always,exit -F arch=b64 -S rename -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping system calls related +to the same event is more efficient. See the following example: +-a always,exit -F arch=b32 -S unlink,unlinkat,rename,renameat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-delete + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.2.4 + Req-10.2.1 + SRG-OS-000064-GPOS-00033 + SRG-OS-000392-GPOS-00172 + SRG-OS-000458-GPOS-00203 + SRG-OS-000461-GPOS-00205 + SRG-OS-000468-GPOS-00212 + SRG-OS-000458-VMM-001810 + SRG-OS-000461-VMM-001830 + Unsuccessful attempts to delete files could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + CCE-88011-2 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-88011-2 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_rename + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit rename tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-88011-2 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_rename + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for rename EACCES for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - rename + syscall_grouping: + - rename + - renameat + - unlink + - unlinkat + + - name: Check existence of rename in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - rename + syscall_grouping: + - rename + - renameat + - unlink + - unlinkat + + - name: Check existence of rename in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-88011-2 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_rename + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for rename EACCES for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - rename + syscall_grouping: + - rename + - renameat + - unlink + - unlinkat + + - name: Check existence of rename in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - rename + syscall_grouping: + - rename + - renameat + - unlink + - unlinkat + + - name: Check existence of rename in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-88011-2 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_rename + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for rename EPERM for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - rename + syscall_grouping: + - rename + - renameat + - unlink + - unlinkat + + - name: Check existence of rename in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - rename + syscall_grouping: + - rename + - renameat + - unlink + - unlinkat + + - name: Check existence of rename in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-88011-2 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_rename + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for rename EPERM for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - rename + syscall_grouping: + - rename + - renameat + - unlink + - unlinkat + + - name: Check existence of rename in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - rename + syscall_grouping: + - rename + - renameat + - unlink + - unlinkat + + - name: Check existence of rename in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-88011-2 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_rename + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="rename" +KEY="access" +SYSCALL_GROUPING="rename renameat unlink unlinkat" + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EACCES" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EPERM" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Unsuccessful Delete Attempts to Files - renameat + +The audit system should collect unsuccessful file deletion +attempts for all users and root. If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d. +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file. +-a always,exit -F arch=b32 -S renameat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete +-a always,exit -F arch=b32 -S renameat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete +If the system is 64 bit then also add the following lines: + +-a always,exit -F arch=b64 -S renameat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete +-a always,exit -F arch=b64 -S renameat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping system calls related +to the same event is more efficient. See the following example: + +-a always,exit -F arch=b32 -S unlink,unlinkat,rename,renameat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-delete + + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.2.4 + Req-10.2.1 + SRG-OS-000064-GPOS-00033 + SRG-OS-000392-GPOS-00172 + SRG-OS-000458-GPOS-00203 + SRG-OS-000461-GPOS-00205 + SRG-OS-000468-GPOS-00212 + SRG-OS-000458-VMM-001810 + SRG-OS-000461-VMM-001830 + Unsuccessful attempts to delete files could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + CCE-87670-6 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-87670-6 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_renameat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit renameat tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-87670-6 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_renameat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for renameat EACCES for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - renameat + syscall_grouping: + - rename + - renameat + - unlink + - unlinkat + + - name: Check existence of renameat in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - renameat + syscall_grouping: + - rename + - renameat + - unlink + - unlinkat + + - name: Check existence of renameat in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-87670-6 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_renameat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for renameat EACCES for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - renameat + syscall_grouping: + - rename + - renameat + - unlink + - unlinkat + + - name: Check existence of renameat in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - renameat + syscall_grouping: + - rename + - renameat + - unlink + - unlinkat + + - name: Check existence of renameat in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-87670-6 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_renameat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for renameat EPERM for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - renameat + syscall_grouping: + - rename + - renameat + - unlink + - unlinkat + + - name: Check existence of renameat in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - renameat + syscall_grouping: + - rename + - renameat + - unlink + - unlinkat + + - name: Check existence of renameat in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-87670-6 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_renameat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for renameat EPERM for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - renameat + syscall_grouping: + - rename + - renameat + - unlink + - unlinkat + + - name: Check existence of renameat in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - renameat + syscall_grouping: + - rename + - renameat + - unlink + - unlinkat + + - name: Check existence of renameat in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-87670-6 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_renameat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="renameat" +KEY="access" +SYSCALL_GROUPING="rename renameat unlink unlinkat" + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EACCES" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EPERM" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Unsuccessful Permission Changes to Files - setxattr + The audit system should collect unsuccessful file permission change +attempts for all users and root. +If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d. +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file. +-a always,exit -F arch=b32 -S setxattr -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change +-a always,exit -F arch=b32 -S setxattr -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change +If the system is 64 bit then also add the following lines: +-a always,exit -F arch=b64 -S setxattr -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change +-a always,exit -F arch=b64 -S setxattr -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the audit rule checks a +system call independently of other system calls. Grouping system calls related +to the same event is more efficient. See the following example: +-a always,exit -F arch=b32 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-perm-change + CCI-000172 + AU-2(d) + AU-12(c) + CM-6(a) + SRG-OS-000458-VMM-001810 + SRG-OS-000461-VMM-001830 + Unsuccessful attempts to change permissions of files could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + - name: Gather the package facts + package_facts: + manager: auto + tags: + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_setxattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit setxattr tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_setxattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for setxattr EACCES for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - setxattr + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of setxattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - setxattr + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of setxattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_setxattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for setxattr EACCES for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - setxattr + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of setxattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - setxattr + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of setxattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_setxattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for setxattr EPERM for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - setxattr + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of setxattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - setxattr + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of setxattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_setxattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for setxattr EPERM for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - setxattr + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of setxattr in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - setxattr + syscall_grouping: + - chmod + - fchmod + - fchmodat + - fsetxattr + - lsetxattr + - setxattr + + - name: Check existence of setxattr in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_unsuccessful_file_modification_setxattr + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="setxattr" +KEY="access" +SYSCALL_GROUPING="chmod fchmod fchmodat fsetxattr lsetxattr setxattr" + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EACCES" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EPERM" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Unsuccessful Access Attempts to Files - truncate + At a minimum, the audit system should collect unauthorized file +accesses for all users and root. If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: +-a always,exit -F arch=b32 -S truncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=access +-a always,exit -F arch=b32 -S truncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=access + +If the system is 64 bit then also add the following lines: + +-a always,exit -F arch=b64 -S truncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=access +-a always,exit -F arch=b64 -S truncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=access + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S truncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=access +-a always,exit -F arch=b32 -S truncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=access + +If the system is 64 bit then also add the following lines: + +-a always,exit -F arch=b64 -S truncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=access +-a always,exit -F arch=b64 -S truncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=access + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping these system +calls with others as identifying earlier in this guide is more efficient. + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.2.4 + Req-10.2.1 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + SRG-OS-000064-GPOS-00033 + SRG-OS-000458-GPOS-00203 + SRG-OS-000461-GPOS-00205 + SRG-OS-000458-VMM-001810 + SRG-OS-000461-VMM-001830 + Unsuccessful attempts to access files could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + CCE-83792-2 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83792-2 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_truncate + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit truncate tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83792-2 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_truncate + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for truncate EACCES for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - truncate + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of truncate in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - truncate + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of truncate in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83792-2 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_truncate + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for truncate EACCES for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - truncate + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of truncate in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - truncate + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of truncate in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-83792-2 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_truncate + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for truncate EPERM for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - truncate + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of truncate in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - truncate + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of truncate in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83792-2 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_truncate + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for truncate EPERM for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - truncate + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of truncate in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - truncate + syscall_grouping: + - creat + - ftruncate + - truncate + - open + - openat + - open_by_handle_at + + - name: Check existence of truncate in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-83792-2 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_truncate + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="truncate" +KEY="access" +SYSCALL_GROUPING="creat ftruncate truncate open openat open_by_handle_at" + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EACCES" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EPERM" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Unsuccessful Delete Attempts to Files - unlink + +The audit system should collect unsuccessful file deletion +attempts for all users and root. If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d. + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file. +-a always,exit -F arch=b32 -S unlink -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete +-a always,exit -F arch=b32 -S unlink -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete + +If the system is 64 bit then also add the following lines: + +-a always,exit -F arch=b64 -S unlink -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete +-a always,exit -F arch=b64 -S unlink -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping system calls related +to the same event is more efficient. See the following example: + +-a always,exit -F arch=b32 -S unlink,unlinkat,rename,renameat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-delete + + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.2.4 + Req-10.2.1 + SRG-OS-000064-GPOS-00033 + SRG-OS-000392-GPOS-00172 + SRG-OS-000458-GPOS-00203 + SRG-OS-000461-GPOS-00205 + SRG-OS-000468-GPOS-00212 + SRG-OS-000458-VMM-001810 + SRG-OS-000461-VMM-001830 + Unsuccessful attempts to delete files could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + CCE-85917-3 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-85917-3 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_unlink + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit unlink tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-85917-3 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_unlink + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for unlink EACCES for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - unlink + syscall_grouping: + - rename + - renameat + - unlink + - unlinkat + + - name: Check existence of unlink in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - unlink + syscall_grouping: + - rename + - renameat + - unlink + - unlinkat + + - name: Check existence of unlink in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-85917-3 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_unlink + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for unlink EACCES for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - unlink + syscall_grouping: + - rename + - renameat + - unlink + - unlinkat + + - name: Check existence of unlink in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - unlink + syscall_grouping: + - rename + - renameat + - unlink + - unlinkat + + - name: Check existence of unlink in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-85917-3 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_unlink + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for unlink EPERM for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - unlink + syscall_grouping: + - rename + - renameat + - unlink + - unlinkat + + - name: Check existence of unlink in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - unlink + syscall_grouping: + - rename + - renameat + - unlink + - unlinkat + + - name: Check existence of unlink in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-85917-3 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_unlink + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for unlink EPERM for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - unlink + syscall_grouping: + - rename + - renameat + - unlink + - unlinkat + + - name: Check existence of unlink in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - unlink + syscall_grouping: + - rename + - renameat + - unlink + - unlinkat + + - name: Check existence of unlink in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-85917-3 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_unlink + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="unlink" +KEY="access" +SYSCALL_GROUPING="rename renameat unlink unlinkat" + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EACCES" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EPERM" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Unsuccessful Delete Attempts to Files - unlinkat + +The audit system should collect unsuccessful file deletion +attempts for all users and root. If the auditd daemon is configured +to use the augenrules program to read audit rules during daemon +startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d. + +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file. +-a always,exit -F arch=b32 -S unlinkat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete +-a always,exit -F arch=b32 -S unlinkat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete + +If the system is 64 bit then also add the following lines: + +-a always,exit -F arch=b64 -S unlinkat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete +-a always,exit -F arch=b64 -S unlinkat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete + Note that these rules can be configured in a +number of ways while still achieving the desired effect. Here the system calls +have been placed independent of other system calls. Grouping system calls related +to the same event is more efficient. See the following example: + +-a always,exit -F arch=b32 -S unlink,unlinkat,rename,renameat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-delete + + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.2.4 + Req-10.2.1 + SRG-OS-000064-GPOS-00033 + SRG-OS-000392-GPOS-00172 + SRG-OS-000458-GPOS-00203 + SRG-OS-000461-GPOS-00205 + SRG-OS-000468-GPOS-00212 + SRG-OS-000458-VMM-001810 + SRG-OS-000461-VMM-001830 + Unsuccessful attempts to delete files could be an indicator of malicious activity on a system. Auditing +these events could serve as evidence of potential system compromise. + CCE-90754-3 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-90754-3 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_unlinkat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit unlinkat tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-90754-3 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_unlinkat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for unlinkat EACCES for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - unlinkat + syscall_grouping: + - rename + - renameat + - unlink + - unlinkat + + - name: Check existence of unlinkat in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - unlinkat + syscall_grouping: + - rename + - renameat + - unlink + - unlinkat + + - name: Check existence of unlinkat in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-90754-3 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_unlinkat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for unlinkat EACCES for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - unlinkat + syscall_grouping: + - rename + - renameat + - unlink + - unlinkat + + - name: Check existence of unlinkat in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - unlinkat + syscall_grouping: + - rename + - renameat + - unlink + - unlinkat + + - name: Check existence of unlinkat in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-90754-3 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_unlinkat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for unlinkat EPERM for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - unlinkat + syscall_grouping: + - rename + - renameat + - unlink + - unlinkat + + - name: Check existence of unlinkat in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - unlinkat + syscall_grouping: + - rename + - renameat + - unlink + - unlinkat + + - name: Check existence of unlinkat in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-90754-3 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_unlinkat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for unlinkat EPERM for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - unlinkat + syscall_grouping: + - rename + - renameat + - unlink + - unlinkat + + - name: Check existence of unlinkat in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules + set_fact: audit_file="/etc/audit/rules.d/access.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - unlinkat + syscall_grouping: + - rename + - renameat + - unlink + - unlinkat + + - name: Check existence of unlinkat in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM + -F auid>=1000 -F auid!=unset -F key=access + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-90754-3 + - NIST-800-171-3.1.7 + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.1 + - PCI-DSS-Req-10.2.4 + - audit_rules_unsuccessful_file_modification_unlinkat + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="unlinkat" +KEY="access" +SYSCALL_GROUPING="rename renameat unlink unlinkat" + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EACCES" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F exit=-EPERM" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Record Information on Kernel Modules Loading and Unloading + To capture kernel module loading and unloading events, use following lines, setting ARCH to +either b32 for 32-bit system, or having two lines for both b32 and b64 in case your system is 64-bit: + +-a always,exit -F arch=ARCH -S init_module,delete_module -F key=modules + + +Place to add the lines depends on a way auditd daemon is configured. If it is configured +to use the augenrules program (the default), add the lines to a file with suffix +.rules in the directory /etc/audit/rules.d. + +If the auditd daemon is configured to use the auditctl utility, +add the lines to file /etc/audit/audit.rules. + + Ensure auditd Collects Information on Kernel Module Loading and Unloading + To capture kernel module loading and unloading events, use following lines, setting ARCH to +either b32 for 32-bit system, or having two lines for both b32 and b64 in case your system is 64-bit: + +-a always,exit -F arch=ARCH -S init_module,finit_module,delete_module -F key=modules + + +The place to add the lines depends on a way auditd daemon is configured. If it is configured +to use the augenrules program (the default), add the lines to a file with suffix +.rules in the directory /etc/audit/rules.d. + +If the auditd daemon is configured to use the auditctl utility, +add the lines to file /etc/audit/audit.rules. + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 5.4.1.1 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000172 + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + Req-10.2.7 + The addition/removal of kernel modules can be used to alter the behavior of +the kernel and potentially introduce malicious code into kernel space. It is important +to have an audit trail of modules that have been introduced into the kernel. + CCE-83804-5 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83804-5 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.7 + - audit_rules_kernel_module_loading + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Set architecture for audit tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83804-5 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.7 + - audit_rules_kernel_module_loading + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for kernel module loading for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - init_module + - delete_module + - finit_module + syscall_grouping: + - init_module + - delete_module + - finit_module + + - name: Check existence of init_module, delete_module, finit_module in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/modules.rules + set_fact: audit_file="/etc/audit/rules.d/modules.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=modules + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - init_module + - delete_module + - finit_module + syscall_grouping: + - init_module + - delete_module + - finit_module + + - name: Check existence of init_module, delete_module, finit_module in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=modules + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83804-5 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.7 + - audit_rules_kernel_module_loading + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Perform remediation of Audit rules for kernel module loading for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - init_module + - delete_module + - finit_module + syscall_grouping: + - init_module + - delete_module + - finit_module + + - name: Check existence of init_module, delete_module, finit_module in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/modules.rules + set_fact: audit_file="/etc/audit/rules.d/modules.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=modules + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - init_module + - delete_module + - finit_module + syscall_grouping: + - init_module + - delete_module + - finit_module + + - name: Check existence of init_module, delete_module, finit_module in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=modules + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-83804-5 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.7 + - audit_rules_kernel_module_loading + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +# Note: 32-bit and 64-bit kernel syscall numbers not always line up => +# it's required on a 64-bit system to check also for the presence +# of 32-bit's equivalent of the corresponding rule. +# (See `man 7 audit.rules` for details ) +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="" + + AUID_FILTERS="-F auid>=1000 -F auid!=unset" + + SYSCALL="init_module finit_module delete_module" + KEY="modules" + SYSCALL_GROUPING="init_module finit_module delete_module" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure auditd Collects Information on Kernel Module Unloading - delete_module + To capture kernel module unloading events, use following line, setting ARCH to +either b32 for 32-bit system, or having two lines for both b32 and b64 in case your system is 64-bit: + +-a always,exit -F arch=ARCH -S delete_module -F auid>=1000 -F auid!=unset -F key=modules + + +Place to add the line depends on a way auditd daemon is configured. If it is configured +to use the augenrules program (the default), add the line to a file with suffix +.rules in the directory /etc/audit/rules.d. + +If the auditd daemon is configured to use the auditctl utility, +add the line to file /etc/audit/audit.rules. + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.2.7 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + SRG-OS-000471-GPOS-00216 + SRG-OS-000477-GPOS-00222 + SRG-OS-000477-VMM-001970 + The removal of kernel modules can be used to alter the behavior of +the kernel and potentially introduce malicious code into kernel space. It is important +to have an audit trail of modules that have been introduced into the kernel. + CCE-83802-9 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83802-9 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.7 + - audit_rules_kernel_module_loading_delete + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Set architecture for audit delete_module tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83802-9 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.7 + - audit_rules_kernel_module_loading_delete + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Perform remediation of Audit rules for delete_module for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - delete_module + syscall_grouping: [] + + - name: Check existence of delete_module in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/module-change.rules + set_fact: audit_file="/etc/audit/rules.d/module-change.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=module-change + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - delete_module + syscall_grouping: [] + + - name: Check existence of delete_module in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=module-change + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83802-9 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.7 + - audit_rules_kernel_module_loading_delete + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Perform remediation of Audit rules for delete_module for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - delete_module + syscall_grouping: [] + + - name: Check existence of delete_module in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/module-change.rules + set_fact: audit_file="/etc/audit/rules.d/module-change.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=module-change + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - delete_module + syscall_grouping: [] + + - name: Check existence of delete_module in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=module-change + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-83802-9 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.7 + - audit_rules_kernel_module_loading_delete + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,-a%20always%2Cexit%20-F%20arch%3Db32%20-S%20delete_module%20-k%20module-change%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20delete_module%20-k%20module-change%0A + mode: 0600 + path: /etc/audit/rules.d/75-kernel-module-loading-delete.rules + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +# Note: 32-bit and 64-bit kernel syscall numbers not always line up => +# it's required on a 64-bit system to check also for the presence +# of 32-bit's equivalent of the corresponding rule. +# (See `man 7 audit.rules` for details ) +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="" + + AUID_FILTERS="-F auid>=1000 -F auid!=unset" + + SYSCALL="delete_module" + KEY="modules" + SYSCALL_GROUPING="delete_module" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure auditd Collects Information on Kernel Module Loading and Unloading - finit_module + If the auditd daemon is configured to use the augenrules program +to read audit rules during daemon startup (the default), add the following lines to a file +with suffix .rules in the directory /etc/audit/rules.d to capture kernel module +loading and unloading events, setting ARCH to either b32 or b64 as appropriate for your system: + +-a always,exit -F arch=ARCH -S finit_module -F auid>=1000 -F auid!=unset -F key=modules + If the auditd daemon is configured to use the auditctl utility to read audit +rules during daemon startup, add the following lines to /etc/audit/audit.rules file +in order to capture kernel module loading and unloading events, setting ARCH to either b32 or +b64 as appropriate for your system: + +-a always,exit -F arch=ARCH -S finit_module -F auid>=1000 -F auid!=unset -F key=modules + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.2.7 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + SRG-OS-000471-GPOS-00216 + SRG-OS-000477-GPOS-00222 + SRG-OS-000477-VMM-001970 + The addition/removal of kernel modules can be used to alter the behavior of +the kernel and potentially introduce malicious code into kernel space. It is important +to have an audit trail of modules that have been introduced into the kernel. + CCE-83803-7 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83803-7 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.7 + - audit_rules_kernel_module_loading_finit + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Set architecture for audit finit_module tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83803-7 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.7 + - audit_rules_kernel_module_loading_finit + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Perform remediation of Audit rules for finit_module for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - finit_module + syscall_grouping: + - init_module + - finit_module + + - name: Check existence of finit_module in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/module-change.rules + set_fact: audit_file="/etc/audit/rules.d/module-change.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=module-change + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - finit_module + syscall_grouping: + - init_module + - finit_module + + - name: Check existence of finit_module in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=module-change + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83803-7 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.7 + - audit_rules_kernel_module_loading_finit + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Perform remediation of Audit rules for finit_module for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - finit_module + syscall_grouping: + - init_module + - finit_module + + - name: Check existence of finit_module in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/module-change.rules + set_fact: audit_file="/etc/audit/rules.d/module-change.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=module-change + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - finit_module + syscall_grouping: + - init_module + - finit_module + + - name: Check existence of finit_module in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=module-change + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-83803-7 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.7 + - audit_rules_kernel_module_loading_finit + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,-a%20always%2Cexit%20-F%20arch%3Db32%20-S%20finit_module%20-k%20module-change%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20finit_module%20-k%20module-change%0A + mode: 0600 + path: /etc/audit/rules.d/75-kernel-module-loading-finit.rules + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +# Note: 32-bit and 64-bit kernel syscall numbers not always line up => +# it's required on a 64-bit system to check also for the presence +# of 32-bit's equivalent of the corresponding rule. +# (See `man 7 audit.rules` for details ) +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="" + + AUID_FILTERS="-F auid>=1000 -F auid!=unset" + + SYSCALL="finit_module" + KEY="modules" + SYSCALL_GROUPING="init_module finit_module" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure auditd Collects Information on Kernel Module Loading - init_module + To capture kernel module loading events, use following line, setting ARCH to +either b32 for 32-bit system, or having two lines for both b32 and b64 in case your system is 64-bit: + +-a always,exit -F arch=ARCH -S init_module -F auid>=1000 -F auid!=unset -F key=modules + + +Place to add the line depends on a way auditd daemon is configured. If it is configured +to use the augenrules program (the default), add the line to a file with suffix +.rules in the directory /etc/audit/rules.d. + +If the auditd daemon is configured to use the auditctl utility, +add the line to file /etc/audit/audit.rules. + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.2.7 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + SRG-OS-000471-GPOS-00216 + SRG-OS-000477-GPOS-00222 + SRG-OS-000477-VMM-001970 + The addition of kernel modules can be used to alter the behavior of +the kernel and potentially introduce malicious code into kernel space. It is important +to have an audit trail of modules that have been introduced into the kernel. + CCE-90835-0 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-90835-0 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.7 + - audit_rules_kernel_module_loading_init + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Set architecture for audit init_module tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-90835-0 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.7 + - audit_rules_kernel_module_loading_init + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Perform remediation of Audit rules for init_module for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - init_module + syscall_grouping: + - init_module + - finit_module + + - name: Check existence of init_module in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/module-change.rules + set_fact: audit_file="/etc/audit/rules.d/module-change.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=module-change + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - init_module + syscall_grouping: + - init_module + - finit_module + + - name: Check existence of init_module in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=module-change + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-90835-0 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.7 + - audit_rules_kernel_module_loading_init + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Perform remediation of Audit rules for init_module for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - init_module + syscall_grouping: + - init_module + - finit_module + + - name: Check existence of init_module in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/module-change.rules + set_fact: audit_file="/etc/audit/rules.d/module-change.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=module-change + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - init_module + syscall_grouping: + - init_module + - finit_module + + - name: Check existence of init_module in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F + key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000 + -F auid!=unset -F key=module-change + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-90835-0 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.7 + - audit_rules_kernel_module_loading_init + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,-a%20always%2Cexit%20-F%20arch%3Db32%20-S%20init_module%20-k%20module-change%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20init_module%20-k%20module-change%0A + mode: 0600 + path: /etc/audit/rules.d/75-kernel-module-loading-init.rules + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +# Note: 32-bit and 64-bit kernel syscall numbers not always line up => +# it's required on a 64-bit system to check also for the presence +# of 32-bit's equivalent of the corresponding rule. +# (See `man 7 audit.rules` for details ) +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="" + + AUID_FILTERS="-F auid>=1000 -F auid!=unset" + + SYSCALL="init_module" + KEY="modules" + SYSCALL_GROUPING="init_module finit_module" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Record Attempts to Alter Logon and Logout Events + The audit system already collects login information for all users +and root. If the auditd daemon is configured to use the +augenrules program to read audit rules during daemon startup (the +default), add the following lines to a file with suffix .rules in the +directory /etc/audit/rules.d in order to watch for attempted manual +edits of files involved in storing logon events: +-w /var/log/tallylog -p wa -k logins +-w /var/log/faillock -p wa -k logins +-w /var/log/lastlog -p wa -k logins +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file in order to watch for unattempted manual +edits of files involved in storing logon events: +-w /var/log/tallylog -p wa -k logins +-w /var/log/faillock -p wa -k logins +-w /var/log/lastlog -p wa -k logins + + Record Attempts to Alter Logon and Logout Events + The audit system already collects login information for all users +and root. If the auditd daemon is configured to use the +augenrules program to read audit rules during daemon startup (the +default), add the following lines to a file with suffix .rules in the +directory /etc/audit/rules.d in order to watch for attempted manual +edits of files involved in storing logon events: +-w /var/log/tallylog -p wa -k logins +-w /var/log/faillock -p wa -k logins +-w /var/log/lastlog -p wa -k logins +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file in order to watch for unattempted manual +edits of files involved in storing logon events: +-w /var/log/tallylog -p wa -k logins +-w /var/log/faillock -p wa -k logins +-w /var/log/lastlog -p wa -k logins + This rule checks for multiple syscalls related to login events; +it was written with DISA STIG in mind. Other policies should use a +separate rule for each syscall that needs to be checked. For example: +audit_rules_login_events_tallylogaudit_rules_login_events_faillockaudit_rules_login_events_lastlog + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 5.4.1.1 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000172 + CCI-002884 + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + Req-10.2.3 + Manual editing of these files may indicate nefarious activity, such +as an attacker attempting to remove evidence of an intrusion. + CCE-83784-9 + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + + + + + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + + +# If the audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# into the list of files to be inspected +files_to_inspect+=('/etc/audit/audit.rules') + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/var/log/tallylog" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/var/log/tallylog $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/var/log/tallylog$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /var/log/tallylog -p wa -k logins" >> "$audit_rules_file" + fi +done +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + +# If the audit is 'augenrules', then check if rule is already defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection. +# If rule isn't defined, add '/etc/audit/rules.d/logins.rules' to list of files for inspection. +readarray -t matches < <(grep -HP "[\s]*-w[\s]+/var/log/tallylog" /etc/audit/rules.d/*.rules) + +# For each of the matched entries +for match in "${matches[@]}" +do + # Extract filepath from the match + rulesd_audit_file=$(echo $match | cut -f1 -d ':') + # Append that path into list of files for inspection + files_to_inspect+=("$rulesd_audit_file") +done +# Case when particular audit rule isn't defined yet +if [ "${#files_to_inspect[@]}" -eq "0" ] +then + # Append '/etc/audit/rules.d/logins.rules' into list of files for inspection + key_rule_file="/etc/audit/rules.d/logins.rules" + # If the logins.rules file doesn't exist yet, create it with correct permissions + if [ ! -e "$key_rule_file" ] + then + touch "$key_rule_file" + chmod 0640 "$key_rule_file" + fi + files_to_inspect+=("$key_rule_file") +fi + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/var/log/tallylog" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/var/log/tallylog $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/var/log/tallylog$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /var/log/tallylog -p wa -k logins" >> "$audit_rules_file" + fi +done + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + + +# If the audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# into the list of files to be inspected +files_to_inspect+=('/etc/audit/audit.rules') + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/var/log/faillock" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/var/log/faillock $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/var/log/faillock$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /var/log/faillock -p wa -k logins" >> "$audit_rules_file" + fi +done +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + +# If the audit is 'augenrules', then check if rule is already defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection. +# If rule isn't defined, add '/etc/audit/rules.d/logins.rules' to list of files for inspection. +readarray -t matches < <(grep -HP "[\s]*-w[\s]+/var/log/faillock" /etc/audit/rules.d/*.rules) + +# For each of the matched entries +for match in "${matches[@]}" +do + # Extract filepath from the match + rulesd_audit_file=$(echo $match | cut -f1 -d ':') + # Append that path into list of files for inspection + files_to_inspect+=("$rulesd_audit_file") +done +# Case when particular audit rule isn't defined yet +if [ "${#files_to_inspect[@]}" -eq "0" ] +then + # Append '/etc/audit/rules.d/logins.rules' into list of files for inspection + key_rule_file="/etc/audit/rules.d/logins.rules" + # If the logins.rules file doesn't exist yet, create it with correct permissions + if [ ! -e "$key_rule_file" ] + then + touch "$key_rule_file" + chmod 0640 "$key_rule_file" + fi + files_to_inspect+=("$key_rule_file") +fi + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/var/log/faillock" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/var/log/faillock $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/var/log/faillock$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /var/log/faillock -p wa -k logins" >> "$audit_rules_file" + fi +done + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + + +# If the audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# into the list of files to be inspected +files_to_inspect+=('/etc/audit/audit.rules') + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/var/log/lastlog" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/var/log/lastlog $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/var/log/lastlog$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /var/log/lastlog -p wa -k logins" >> "$audit_rules_file" + fi +done +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + +# If the audit is 'augenrules', then check if rule is already defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection. +# If rule isn't defined, add '/etc/audit/rules.d/logins.rules' to list of files for inspection. +readarray -t matches < <(grep -HP "[\s]*-w[\s]+/var/log/lastlog" /etc/audit/rules.d/*.rules) + +# For each of the matched entries +for match in "${matches[@]}" +do + # Extract filepath from the match + rulesd_audit_file=$(echo $match | cut -f1 -d ':') + # Append that path into list of files for inspection + files_to_inspect+=("$rulesd_audit_file") +done +# Case when particular audit rule isn't defined yet +if [ "${#files_to_inspect[@]}" -eq "0" ] +then + # Append '/etc/audit/rules.d/logins.rules' into list of files for inspection + key_rule_file="/etc/audit/rules.d/logins.rules" + # If the logins.rules file doesn't exist yet, create it with correct permissions + if [ ! -e "$key_rule_file" ] + then + touch "$key_rule_file" + chmod 0640 "$key_rule_file" + fi + files_to_inspect+=("$key_rule_file") +fi + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/var/log/lastlog" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/var/log/lastlog $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/var/log/lastlog$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /var/log/lastlog -p wa -k logins" >> "$audit_rules_file" + fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + Record Attempts to Alter Logon and Logout Events - faillock + The audit system already collects login information for all users +and root. If the auditd daemon is configured to use the +augenrules program to read audit rules during daemon startup (the +default), add the following lines to a file with suffix .rules in the +directory /etc/audit/rules.d in order to watch for attempted manual +edits of files involved in storing logon events: +-w /var/log/faillock -p wa -k logins +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file in order to watch for unattempted manual +edits of files involved in storing logon events: +-w /var/log/faillock -p wa -k logins + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000126 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.2.3 + SRG-OS-000392-GPOS-00172 + SRG-OS-000470-GPOS-00214 + SRG-OS-000473-GPOS-00218 + SRG-OS-000473-VMM-001930 + SRG-OS-000470-VMM-001900 + Manual editing of these files may indicate nefarious activity, such +as an attacker attempting to remove evidence of an intrusion. + CCE-83783-1 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83783-1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.3 + - audit_rules_login_events_faillock + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Check if watch rule for /var/log/faillock already exists in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: ^\s*-w\s+/var/log/faillock\s+-p\s+wa(\s|$)+ + patterns: '*.rules' + register: find_existing_watch_rules_d + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83783-1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.3 + - audit_rules_login_events_faillock + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Search /etc/audit/rules.d for other rules with specified key logins + find: + paths: /etc/audit/rules.d + contains: ^.*(?:-F key=|-k\s+)logins$ + patterns: '*.rules' + register: find_watch_key + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched + == 0 + tags: + - CCE-83783-1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.3 + - audit_rules_login_events_faillock + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Use /etc/audit/rules.d/logins.rules as the recipient for the rule + set_fact: + all_files: + - /etc/audit/rules.d/logins.rules + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_watch_key.matched is defined and find_watch_key.matched == 0 and find_existing_watch_rules_d.matched + is defined and find_existing_watch_rules_d.matched == 0 + tags: + - CCE-83783-1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.3 + - audit_rules_login_events_faillock + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Use matched file as the recipient for the rule + set_fact: + all_files: + - '{{ find_watch_key.files | map(attribute=''path'') | list | first }}' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_watch_key.matched is defined and find_watch_key.matched > 0 and find_existing_watch_rules_d.matched + is defined and find_existing_watch_rules_d.matched == 0 + tags: + - CCE-83783-1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.3 + - audit_rules_login_events_faillock + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Add watch rule for /var/log/faillock in /etc/audit/rules.d/ + lineinfile: + path: '{{ all_files[0] }}' + line: -w /var/log/faillock -p wa -k logins + create: true + mode: '0640' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched + == 0 + tags: + - CCE-83783-1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.3 + - audit_rules_login_events_faillock + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Check if watch rule for /var/log/faillock already exists in /etc/audit/audit.rules + find: + paths: /etc/audit/ + contains: ^\s*-w\s+/var/log/faillock\s+-p\s+wa(\s|$)+ + patterns: audit.rules + register: find_existing_watch_audit_rules + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83783-1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.3 + - audit_rules_login_events_faillock + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Add watch rule for /var/log/faillock in /etc/audit/audit.rules + lineinfile: + line: -w /var/log/faillock -p wa -k logins + state: present + dest: /etc/audit/audit.rules + create: true + mode: '0640' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_audit_rules.matched is defined and find_existing_watch_audit_rules.matched + == 0 + tags: + - CCE-83783-1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.3 + - audit_rules_login_events_faillock + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + + +# If the audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# into the list of files to be inspected +files_to_inspect+=('/etc/audit/audit.rules') + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/var/log/faillock" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/var/log/faillock $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/var/log/faillock$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /var/log/faillock -p wa -k logins" >> "$audit_rules_file" + fi +done +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + +# If the audit is 'augenrules', then check if rule is already defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection. +# If rule isn't defined, add '/etc/audit/rules.d/logins.rules' to list of files for inspection. +readarray -t matches < <(grep -HP "[\s]*-w[\s]+/var/log/faillock" /etc/audit/rules.d/*.rules) + +# For each of the matched entries +for match in "${matches[@]}" +do + # Extract filepath from the match + rulesd_audit_file=$(echo $match | cut -f1 -d ':') + # Append that path into list of files for inspection + files_to_inspect+=("$rulesd_audit_file") +done +# Case when particular audit rule isn't defined yet +if [ "${#files_to_inspect[@]}" -eq "0" ] +then + # Append '/etc/audit/rules.d/logins.rules' into list of files for inspection + key_rule_file="/etc/audit/rules.d/logins.rules" + # If the logins.rules file doesn't exist yet, create it with correct permissions + if [ ! -e "$key_rule_file" ] + then + touch "$key_rule_file" + chmod 0640 "$key_rule_file" + fi + files_to_inspect+=("$key_rule_file") +fi + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/var/log/faillock" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/var/log/faillock $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/var/log/faillock$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /var/log/faillock -p wa -k logins" >> "$audit_rules_file" + fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Attempts to Alter Logon and Logout Events - lastlog + The audit system already collects login information for all users +and root. If the auditd daemon is configured to use the +augenrules program to read audit rules during daemon startup (the +default), add the following lines to a file with suffix .rules in the +directory /etc/audit/rules.d in order to watch for attempted manual +edits of files involved in storing logon events: +-w /var/log/lastlog -p wa -k logins +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file in order to watch for unattempted manual +edits of files involved in storing logon events: +-w /var/log/lastlog -p wa -k logins + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000126 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.2.3 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + SRG-OS-000473-GPOS-00218 + SRG-OS-000470-GPOS-00214 + SRG-OS-000473-VMM-001930 + SRG-OS-000470-VMM-001900 + Manual editing of these files may indicate nefarious activity, such +as an attacker attempting to remove evidence of an intrusion. + CCE-83785-6 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83785-6 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.3 + - audit_rules_login_events_lastlog + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Check if watch rule for /var/log/lastlog already exists in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: ^\s*-w\s+/var/log/lastlog\s+-p\s+wa(\s|$)+ + patterns: '*.rules' + register: find_existing_watch_rules_d + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83785-6 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.3 + - audit_rules_login_events_lastlog + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Search /etc/audit/rules.d for other rules with specified key logins + find: + paths: /etc/audit/rules.d + contains: ^.*(?:-F key=|-k\s+)logins$ + patterns: '*.rules' + register: find_watch_key + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched + == 0 + tags: + - CCE-83785-6 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.3 + - audit_rules_login_events_lastlog + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Use /etc/audit/rules.d/logins.rules as the recipient for the rule + set_fact: + all_files: + - /etc/audit/rules.d/logins.rules + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_watch_key.matched is defined and find_watch_key.matched == 0 and find_existing_watch_rules_d.matched + is defined and find_existing_watch_rules_d.matched == 0 + tags: + - CCE-83785-6 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.3 + - audit_rules_login_events_lastlog + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Use matched file as the recipient for the rule + set_fact: + all_files: + - '{{ find_watch_key.files | map(attribute=''path'') | list | first }}' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_watch_key.matched is defined and find_watch_key.matched > 0 and find_existing_watch_rules_d.matched + is defined and find_existing_watch_rules_d.matched == 0 + tags: + - CCE-83785-6 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.3 + - audit_rules_login_events_lastlog + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Add watch rule for /var/log/lastlog in /etc/audit/rules.d/ + lineinfile: + path: '{{ all_files[0] }}' + line: -w /var/log/lastlog -p wa -k logins + create: true + mode: '0640' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched + == 0 + tags: + - CCE-83785-6 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.3 + - audit_rules_login_events_lastlog + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Check if watch rule for /var/log/lastlog already exists in /etc/audit/audit.rules + find: + paths: /etc/audit/ + contains: ^\s*-w\s+/var/log/lastlog\s+-p\s+wa(\s|$)+ + patterns: audit.rules + register: find_existing_watch_audit_rules + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83785-6 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.3 + - audit_rules_login_events_lastlog + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Add watch rule for /var/log/lastlog in /etc/audit/audit.rules + lineinfile: + line: -w /var/log/lastlog -p wa -k logins + state: present + dest: /etc/audit/audit.rules + create: true + mode: '0640' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_audit_rules.matched is defined and find_existing_watch_audit_rules.matched + == 0 + tags: + - CCE-83785-6 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.3 + - audit_rules_login_events_lastlog + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + + +# If the audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# into the list of files to be inspected +files_to_inspect+=('/etc/audit/audit.rules') + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/var/log/lastlog" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/var/log/lastlog $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/var/log/lastlog$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /var/log/lastlog -p wa -k logins" >> "$audit_rules_file" + fi +done +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + +# If the audit is 'augenrules', then check if rule is already defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection. +# If rule isn't defined, add '/etc/audit/rules.d/logins.rules' to list of files for inspection. +readarray -t matches < <(grep -HP "[\s]*-w[\s]+/var/log/lastlog" /etc/audit/rules.d/*.rules) + +# For each of the matched entries +for match in "${matches[@]}" +do + # Extract filepath from the match + rulesd_audit_file=$(echo $match | cut -f1 -d ':') + # Append that path into list of files for inspection + files_to_inspect+=("$rulesd_audit_file") +done +# Case when particular audit rule isn't defined yet +if [ "${#files_to_inspect[@]}" -eq "0" ] +then + # Append '/etc/audit/rules.d/logins.rules' into list of files for inspection + key_rule_file="/etc/audit/rules.d/logins.rules" + # If the logins.rules file doesn't exist yet, create it with correct permissions + if [ ! -e "$key_rule_file" ] + then + touch "$key_rule_file" + chmod 0640 "$key_rule_file" + fi + files_to_inspect+=("$key_rule_file") +fi + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/var/log/lastlog" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/var/log/lastlog $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/var/log/lastlog$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /var/log/lastlog -p wa -k logins" >> "$audit_rules_file" + fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Attempts to Alter Logon and Logout Events - tallylog + The audit system already collects login information for all users +and root. If the auditd daemon is configured to use the +augenrules program to read audit rules during daemon startup (the +default), add the following lines to a file with suffix .rules in the +directory /etc/audit/rules.d in order to watch for attempted manual +edits of files involved in storing logon events: +-w /var/log/tallylog -p wa -k logins +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file in order to watch for unattempted manual +edits of files involved in storing logon events: +-w /var/log/tallylog -p wa -k logins + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000172 + CCI-002884 + CCI-000126 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.2.3 + SRG-OS-000392-GPOS-00172 + SRG-OS-000470-GPOS-00214 + SRG-OS-000473-GPOS-00218 + SRG-OS-000473-VMM-001930 + SRG-OS-000470-VMM-001900 + Manual editing of these files may indicate nefarious activity, such +as an attacker attempting to remove evidence of an intrusion. + CCE-83782-3 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83782-3 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.3 + - audit_rules_login_events_tallylog + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Check if watch rule for /var/log/tallylog already exists in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: ^\s*-w\s+/var/log/tallylog\s+-p\s+wa(\s|$)+ + patterns: '*.rules' + register: find_existing_watch_rules_d + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83782-3 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.3 + - audit_rules_login_events_tallylog + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Search /etc/audit/rules.d for other rules with specified key logins + find: + paths: /etc/audit/rules.d + contains: ^.*(?:-F key=|-k\s+)logins$ + patterns: '*.rules' + register: find_watch_key + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched + == 0 + tags: + - CCE-83782-3 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.3 + - audit_rules_login_events_tallylog + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Use /etc/audit/rules.d/logins.rules as the recipient for the rule + set_fact: + all_files: + - /etc/audit/rules.d/logins.rules + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_watch_key.matched is defined and find_watch_key.matched == 0 and find_existing_watch_rules_d.matched + is defined and find_existing_watch_rules_d.matched == 0 + tags: + - CCE-83782-3 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.3 + - audit_rules_login_events_tallylog + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Use matched file as the recipient for the rule + set_fact: + all_files: + - '{{ find_watch_key.files | map(attribute=''path'') | list | first }}' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_watch_key.matched is defined and find_watch_key.matched > 0 and find_existing_watch_rules_d.matched + is defined and find_existing_watch_rules_d.matched == 0 + tags: + - CCE-83782-3 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.3 + - audit_rules_login_events_tallylog + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Add watch rule for /var/log/tallylog in /etc/audit/rules.d/ + lineinfile: + path: '{{ all_files[0] }}' + line: -w /var/log/tallylog -p wa -k logins + create: true + mode: '0640' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched + == 0 + tags: + - CCE-83782-3 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.3 + - audit_rules_login_events_tallylog + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Check if watch rule for /var/log/tallylog already exists in /etc/audit/audit.rules + find: + paths: /etc/audit/ + contains: ^\s*-w\s+/var/log/tallylog\s+-p\s+wa(\s|$)+ + patterns: audit.rules + register: find_existing_watch_audit_rules + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83782-3 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.3 + - audit_rules_login_events_tallylog + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Add watch rule for /var/log/tallylog in /etc/audit/audit.rules + lineinfile: + line: -w /var/log/tallylog -p wa -k logins + state: present + dest: /etc/audit/audit.rules + create: true + mode: '0640' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_audit_rules.matched is defined and find_existing_watch_audit_rules.matched + == 0 + tags: + - CCE-83782-3 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.3 + - audit_rules_login_events_tallylog + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + + +# If the audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# into the list of files to be inspected +files_to_inspect+=('/etc/audit/audit.rules') + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/var/log/tallylog" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/var/log/tallylog $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/var/log/tallylog$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /var/log/tallylog -p wa -k logins" >> "$audit_rules_file" + fi +done +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + +# If the audit is 'augenrules', then check if rule is already defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection. +# If rule isn't defined, add '/etc/audit/rules.d/logins.rules' to list of files for inspection. +readarray -t matches < <(grep -HP "[\s]*-w[\s]+/var/log/tallylog" /etc/audit/rules.d/*.rules) + +# For each of the matched entries +for match in "${matches[@]}" +do + # Extract filepath from the match + rulesd_audit_file=$(echo $match | cut -f1 -d ':') + # Append that path into list of files for inspection + files_to_inspect+=("$rulesd_audit_file") +done +# Case when particular audit rule isn't defined yet +if [ "${#files_to_inspect[@]}" -eq "0" ] +then + # Append '/etc/audit/rules.d/logins.rules' into list of files for inspection + key_rule_file="/etc/audit/rules.d/logins.rules" + # If the logins.rules file doesn't exist yet, create it with correct permissions + if [ ! -e "$key_rule_file" ] + then + touch "$key_rule_file" + chmod 0640 "$key_rule_file" + fi + files_to_inspect+=("$key_rule_file") +fi + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/var/log/tallylog" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/var/log/tallylog $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/var/log/tallylog$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /var/log/tallylog -p wa -k logins" >> "$audit_rules_file" + fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Record Information on the Use of Privileged Commands + At a minimum, the audit system should collect the execution of +privileged commands for all users and root. + + Ensure auditd Collects Information on the Use of Privileged Commands - init + At a minimum, the audit system should collect the execution of +privileged commands for all users and root. If the auditd daemon is +configured to use the augenrules program to read audit rules during +daemon startup (the default), add a line of the following form to a file with +suffix .rules in the directory /etc/audit/rules.d: +-a always,exit -F path=/usr/sbin/init -F auid>=1000 -F auid!=unset -F key=privileged +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add a line of the following +form to /etc/audit/audit.rules: +-a always,exit -F path=/usr/sbin/init -F auid>=1000 -F auid!=unset -F key=privileged + CCI-000172 + AU-12(c) + SRG-OS-000477-GPOS-00222 + Misuse of the init command may cause availability issues for the system. + CCE-85956-1 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-85956-1 + - NIST-800-53-AU-12(c) + - audit_privileged_commands_init + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Perform remediation of Audit rules for /usr/sbin/init + block: + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/sbin/init -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules + set_fact: audit_file="/etc/audit/rules.d/privileged.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/sbin/init -F perm=x -F + auid>=1000 -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/sbin/init -F perm=x + -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/sbin/init -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:( + -S |,)\w+)+)( -F path=/usr/sbin/init -F perm=x -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/sbin/init -F perm=x + -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-85956-1 + - NIST-800-53-AU-12(c) + - audit_privileged_commands_init + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +ACTION_ARCH_FILTERS="-a always,exit" +OTHER_FILTERS="-F path=/usr/sbin/init -F perm=x" +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="" +KEY="privileged" +SYSCALL_GROUPING="" +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - poweroff + At a minimum, the audit system should collect the execution of +privileged commands for all users and root. If the auditd daemon is +configured to use the augenrules program to read audit rules during +daemon startup (the default), add a line of the following form to a file with +suffix .rules in the directory /etc/audit/rules.d: +-a always,exit -F path=/usr/sbin/poweroff -F auid>=1000 -F auid!=unset -F key=privileged +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add a line of the following +form to /etc/audit/audit.rules: +-a always,exit -F path=/usr/sbin/poweroff -F auid>=1000 -F auid!=unset -F key=privileged + CCI-000172 + AU-12(c) + SRG-OS-000477-GPOS-00222 + Misuse of the poweroff command may cause availability issues for the system. + CCE-85957-9 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-85957-9 + - NIST-800-53-AU-12(c) + - audit_privileged_commands_poweroff + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Perform remediation of Audit rules for /usr/sbin/poweroff + block: + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/sbin/poweroff -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules + set_fact: audit_file="/etc/audit/rules.d/privileged.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/sbin/poweroff -F perm=x + -F auid>=1000 -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/sbin/poweroff -F + perm=x -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/sbin/poweroff -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:( + -S |,)\w+)+)( -F path=/usr/sbin/poweroff -F perm=x -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/sbin/poweroff -F + perm=x -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-85957-9 + - NIST-800-53-AU-12(c) + - audit_privileged_commands_poweroff + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +ACTION_ARCH_FILTERS="-a always,exit" +OTHER_FILTERS="-F path=/usr/sbin/poweroff -F perm=x" +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="" +KEY="privileged" +SYSCALL_GROUPING="" +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - reboot + At a minimum, the audit system should collect the execution of +privileged commands for all users and root. If the auditd daemon is +configured to use the augenrules program to read audit rules during +daemon startup (the default), add a line of the following form to a file with +suffix .rules in the directory /etc/audit/rules.d: +-a always,exit -F path=/usr/sbin/reboot -F auid>=1000 -F auid!=unset -F key=privileged +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add a line of the following +form to /etc/audit/audit.rules: +-a always,exit -F path=/usr/sbin/reboot -F auid>=1000 -F auid!=unset -F key=privileged + CCI-000172 + AU-12(c) + SRG-OS-000477-GPOS-00222 + Misuse of the reboot command may cause availability issues for the system. + CCE-85958-7 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-85958-7 + - NIST-800-53-AU-12(c) + - audit_privileged_commands_reboot + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Perform remediation of Audit rules for /usr/sbin/reboot + block: + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/sbin/reboot -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules + set_fact: audit_file="/etc/audit/rules.d/privileged.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/sbin/reboot -F perm=x + -F auid>=1000 -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/sbin/reboot -F perm=x + -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/sbin/reboot -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:( + -S |,)\w+)+)( -F path=/usr/sbin/reboot -F perm=x -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/sbin/reboot -F perm=x + -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-85958-7 + - NIST-800-53-AU-12(c) + - audit_privileged_commands_reboot + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +ACTION_ARCH_FILTERS="-a always,exit" +OTHER_FILTERS="-F path=/usr/sbin/reboot -F perm=x" +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="" +KEY="privileged" +SYSCALL_GROUPING="" +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - shutdown + At a minimum, the audit system should collect the execution of +privileged commands for all users and root. If the auditd daemon is +configured to use the augenrules program to read audit rules during +daemon startup (the default), add a line of the following form to a file with +suffix .rules in the directory /etc/audit/rules.d: +-a always,exit -F path=/usr/sbin/shutdown -F auid>=1000 -F auid!=unset -F key=privileged +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add a line of the following +form to /etc/audit/audit.rules: +-a always,exit -F path=/usr/sbin/shutdown -F auid>=1000 -F auid!=unset -F key=privileged + CCI-000172 + AU-12(c) + SRG-OS-000477-GPOS-00222 + Misuse of the shutdown command may cause availability issues for the system. + CCE-85959-5 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-85959-5 + - NIST-800-53-AU-12(c) + - audit_privileged_commands_shutdown + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Perform remediation of Audit rules for /usr/sbin/shutdown + block: + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/sbin/shutdown -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules + set_fact: audit_file="/etc/audit/rules.d/privileged.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/sbin/shutdown -F perm=x + -F auid>=1000 -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/sbin/shutdown -F + perm=x -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/sbin/shutdown -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:( + -S |,)\w+)+)( -F path=/usr/sbin/shutdown -F perm=x -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/sbin/shutdown -F + perm=x -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-85959-5 + - NIST-800-53-AU-12(c) + - audit_privileged_commands_shutdown + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +ACTION_ARCH_FILTERS="-a always,exit" +OTHER_FILTERS="-F path=/usr/sbin/shutdown -F perm=x" +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="" +KEY="privileged" +SYSCALL_GROUPING="" +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands + The audit system should collect information about usage of privileged +commands for all users and root. To find the relevant setuid / +setgid programs, run the following command for each local partition +PART: +$ sudo find PART -xdev -type f -perm -4000 -o -type f -perm -2000 2>/dev/null +If the auditd daemon is configured to use the augenrules +program to read audit rules during daemon startup (the default), add a line of +the following form to a file with suffix .rules in the directory +/etc/audit/rules.d for each setuid / setgid program on the system, +replacing the SETUID_PROG_PATH part with the full path of that setuid / +setgid program in the list: +-a always,exit -F path=SETUID_PROG_PATH -F auid>=1000 -F auid!=unset -F key=privileged +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add a line of the following +form to /etc/audit/audit.rules for each setuid / setgid program on the +system, replacing the SETUID_PROG_PATH part with the full path of that +setuid / setgid program in the list: +-a always,exit -F path=SETUID_PROG_PATH -F auid>=1000 -F auid!=unset -F key=privileged + This rule checks for multiple syscalls related to privileged commands; +it was written with DISA STIG in mind. Other policies should use a +separate rule for each syscall that needs to be checked. For example: +audit_rules_privileged_commands_suaudit_rules_privileged_commands_umountaudit_rules_privileged_commands_passwd + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 5.4.1.1 + APO08.04 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.05 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-002234 + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.5 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.3.4.5.9 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 3.9 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + 0582 + 0584 + 05885 + 0586 + 0846 + 0957 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.1 + A.16.1.2 + A.16.1.3 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.1.3 + A.6.2.1 + A.6.2.2 + CIP-004-6 R2.2.2 + CIP-004-6 R2.2.3 + CIP-007-3 R.1.3 + CIP-007-3 R5 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.3 + CIP-007-3 R5.2.1 + CIP-007-3 R5.2.3 + AC-2(4) + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + DE.AE-2 + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + DE.DP-4 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + RS.CO-2 + Req-10.2.2 + SRG-OS-000327-GPOS-00127 + SRG-OS-000471-VMM-001910 + Misuse of privileged functions, either intentionally or unintentionally by +authorized users, or by unauthorized external entities that have compromised system accounts, +is a serious and ongoing concern and can have significant adverse impacts on organizations. +Auditing the use of privileged functions is one way to detect such misuse and identify +the risk from insider and advanced persistent threats. + +Privileged programs are subject to escalation-of-privilege attacks, +which attempt to subvert their normal role of providing some necessary but +limited capability. As such, motivation exists to monitor these programs for +unusual activity. + CCE-83759-1 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83759-1 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.2 + - audit_rules_privileged_commands + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Search for privileged commands + shell: | + set -o pipefail + find / -not \( -fstype afs -o -fstype ceph -o -fstype cifs -o -fstype smb3 -o -fstype smbfs -o -fstype sshfs -o -fstype ncpfs -o -fstype ncp -o -fstype nfs -o -fstype nfs4 -o -fstype gfs -o -fstype gfs2 -o -fstype glusterfs -o -fstype gpfs -o -fstype pvfs2 -o -fstype ocfs2 -o -fstype lustre -o -fstype davfs -o -fstype fuse.sshfs \) -type f \( -perm -4000 -o -perm -2000 \) 2> /dev/null + args: + warn: false + executable: /bin/bash + check_mode: false + register: find_result + changed_when: false + failed_when: false + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83759-1 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.2 + - audit_rules_privileged_commands + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Search /etc/audit/rules.d for audit rule entries + find: + paths: /etc/audit/rules.d + recurse: false + contains: ^.*path={{ item }} .*$ + patterns: '*.rules' + with_items: + - '{{ find_result.stdout_lines }}' + register: files_result + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83759-1 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.2 + - audit_rules_privileged_commands + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Overwrites the rule in rules.d + lineinfile: + path: '{{ item.1.path }}' + line: -a always,exit -F path={{ item.0.item }} -F auid>=1000 -F auid!=unset -F + key=privileged + create: false + regexp: ^.*path={{ item.0.item }} .*$ + with_subelements: + - '{{ files_result.results }}' + - files + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83759-1 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.2 + - audit_rules_privileged_commands + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Adds the rule in rules.d + lineinfile: + path: /etc/audit/rules.d/privileged.rules + line: -a always,exit -F path={{ item.item }} -F auid>=1000 -F auid!=unset -F key=privileged + create: true + with_items: + - '{{ files_result.results }}' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - files_result.results is defined and item.matched == 0 + tags: + - CCE-83759-1 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.2 + - audit_rules_privileged_commands + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Inserts/replaces the rule in audit.rules + lineinfile: + path: /etc/audit/audit.rules + line: -a always,exit -F path={{ item.item }} -F auid>=1000 -F auid!=unset -F key=privileged + create: true + regexp: ^.*path={{ item.item }} .*$ + with_items: + - '{{ files_result.results }}' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83759-1 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.2.2 + - audit_rules_privileged_commands + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +files_to_inspect=() + +# If the audit tool is 'auditctl', then: +# * add '/etc/audit/audit.rules'to the list of files to be inspected, +# * specify '/etc/audit/audit.rules' as the output audit file, where +# missing rules should be inserted +files_to_inspect=("/etc/audit/audit.rules") +output_audit_file="/etc/audit/audit.rules" + +# Obtain the list of SUID/SGID binaries on the particular system (split by newline) +# into privileged_binaries array +privileged_binaries=() +readarray -t privileged_binaries < <(find / -not \( -fstype afs -o -fstype ceph -o -fstype cifs -o -fstype smb3 -o -fstype smbfs -o -fstype sshfs -o -fstype ncpfs -o -fstype ncp -o -fstype nfs -o -fstype nfs4 -o -fstype gfs -o -fstype gfs2 -o -fstype glusterfs -o -fstype gpfs -o -fstype pvfs2 -o -fstype ocfs2 -o -fstype lustre -o -fstype davfs -o -fstype fuse.sshfs \) -type f \( -perm -4000 -o -perm -2000 \) 2> /dev/null) + +# Keep list of SUID/SGID binaries that have been already handled within some previous iteration +sbinaries_to_skip=() + +# For each found sbinary in privileged_binaries list +for sbinary in "${privileged_binaries[@]}" +do + + # Check if this sbinary wasn't already handled in some of the previous sbinary iterations + # Return match only if whole sbinary definition matched (not in the case just prefix matched!!!) + if [[ $(sed -ne "\|${sbinary}|p" <<< "${sbinaries_to_skip[*]}") ]] + then + # If so, don't process it second time & go to process next sbinary + continue + fi + + # Reset the counter of inspected files when starting to check + # presence of existing audit rule for new sbinary + count_of_inspected_files=0 + + # Define expected rule form for this binary + expected_rule="-a always,exit -F path=${sbinary} -F auid>=1000 -F auid!=unset -F key=privileged" + + # If list of audit rules files to be inspected is empty, just add new rule and move on to next binary + if [[ ${#files_to_inspect[@]} -eq 0 ]]; then + echo "$expected_rule" >> "$output_audit_file" + continue + fi + + # Replace possible slash '/' character in sbinary definition so we could use it in sed expressions below + sbinary_esc=${sbinary//$'/'/$'\/'} + + # For each audit rules file from the list of files to be inspected + for afile in "${files_to_inspect[@]}" + do + # Search current audit rules file's content for match. Match criteria: + # * existing rule is for the same SUID/SGID binary we are currently processing (but + # can contain multiple -F path= elements covering multiple SUID/SGID binaries) + # * existing rule contains all arguments from expected rule form (though can contain + # them in arbitrary order) + + base_search=$(sed -e '/-a always,exit/!d' -e '/-F path='"${sbinary_esc}"'[^[:graph:]]/!d' \ + -e '/-F path=[^[:space:]]\+/!d' \ + -e '/-F auid>='"1000"'/!d' -e '/-F auid!=\(4294967295\|unset\)/!d' \ + -e '/-k \|-F key=/!d' "$afile") + + # Increase the count of inspected files for this sbinary + count_of_inspected_files=$((count_of_inspected_files + 1)) + + # Search current audit rules file's content for presence of rule pattern for this sbinary + if [[ $base_search ]] + then + + # Current audit rules file already contains rule for this binary => + # Store the exact form of found rule for this binary for further processing + concrete_rule=$base_search + + # Select all other SUID/SGID binaries possibly also present in the found rule + + readarray -t handled_sbinaries < <(grep -o -e "-F path=[^[:space:]]\+" <<< "$concrete_rule") + handled_sbinaries=("${handled_sbinaries[@]//-F path=/}") + + # Merge the list of such SUID/SGID binaries found in this iteration with global list ignoring duplicates + readarray -t sbinaries_to_skip < <(for i in "${sbinaries_to_skip[@]}" "${handled_sbinaries[@]}"; do echo "$i"; done | sort -du) + + # if there is a -F perm flag, remove it + if grep -q '.*-F\s\+perm=[rwxa]\+.*' <<< "$concrete_rule"; then + + # Separate concrete_rule into three sections using hash '#' + # sign as a delimiter around rule's permission section borders + # note that the trailing space after perm flag is captured because there would be + # two consecutive spaces after joining remaining parts of the rule together + concrete_rule="$(echo "$concrete_rule" | sed -n "s/\(.*\)\+\(-F perm=[rwax]\+\ \?\)\+/\1#\2#/p")" + + # Split concrete_rule into head and tail sections using hash '#' delimiter + # The second column contains the permission section, which we don't need to extract + rule_head=$(cut -d '#' -f 1 <<< "$concrete_rule") + rule_tail=$(cut -d '#' -f 3 <<< "$concrete_rule") + + # Remove permissions section from existing rule in the file + sed -i "s#${rule_head}\(.*\)${rule_tail}#${rule_head}${rule_tail}#" "$afile" + fi + # If the required audit rule for particular sbinary wasn't found yet, insert it under following conditions: + # + # * in the "auditctl" mode of operation insert particular rule each time + # (because in this mode there's only one file -- /etc/audit/audit.rules to be inspected for presence of this rule), + # + # * in the "augenrules" mode of operation insert particular rule only once and only in case we have already + # searched all of the files from /etc/audit/rules.d/*.rules location (since that audit rule can be defined + # in any of those files and if not, we want it to be inserted only once into /etc/audit/rules.d/privileged.rules file) + # + + else + # Check if this sbinary wasn't already handled in some of the previous afile iterations + # Return match only if whole sbinary definition matched (not in the case just prefix matched!!!) + if [[ ! $(sed -ne "\|${sbinary}|p" <<< "${sbinaries_to_skip[*]}") ]] + then + # Current audit rules file's content doesn't contain expected rule for this + # SUID/SGID binary yet => append it + echo "$expected_rule" >> "$output_audit_file" + fi + continue + fi + done +done +files_to_inspect=() +# If the audit tool is 'augenrules', then: +# * add '/etc/audit/rules.d/*.rules' to the list of files to be inspected +# (split by newline), +# * specify /etc/audit/rules.d/privileged.rules' as the output file, where +# missing rules should be inserted +readarray -t files_to_inspect < <(find /etc/audit/rules.d -maxdepth 1 -type f -name '*.rules' -print) +output_audit_file="/etc/audit/rules.d/privileged.rules" + +# Obtain the list of SUID/SGID binaries on the particular system (split by newline) +# into privileged_binaries array +privileged_binaries=() +readarray -t privileged_binaries < <(find / -not \( -fstype afs -o -fstype ceph -o -fstype cifs -o -fstype smb3 -o -fstype smbfs -o -fstype sshfs -o -fstype ncpfs -o -fstype ncp -o -fstype nfs -o -fstype nfs4 -o -fstype gfs -o -fstype gfs2 -o -fstype glusterfs -o -fstype gpfs -o -fstype pvfs2 -o -fstype ocfs2 -o -fstype lustre -o -fstype davfs -o -fstype fuse.sshfs \) -type f \( -perm -4000 -o -perm -2000 \) 2> /dev/null) + +# Keep list of SUID/SGID binaries that have been already handled within some previous iteration +sbinaries_to_skip=() + +# For each found sbinary in privileged_binaries list +for sbinary in "${privileged_binaries[@]}" +do + + # Check if this sbinary wasn't already handled in some of the previous sbinary iterations + # Return match only if whole sbinary definition matched (not in the case just prefix matched!!!) + if [[ $(sed -ne "\|${sbinary}|p" <<< "${sbinaries_to_skip[*]}") ]] + then + # If so, don't process it second time & go to process next sbinary + continue + fi + + # Reset the counter of inspected files when starting to check + # presence of existing audit rule for new sbinary + count_of_inspected_files=0 + + # Define expected rule form for this binary + expected_rule="-a always,exit -F path=${sbinary} -F auid>=1000 -F auid!=unset -F key=privileged" + + # If list of audit rules files to be inspected is empty, just add new rule and move on to next binary + if [[ ${#files_to_inspect[@]} -eq 0 ]]; then + echo "$expected_rule" >> "$output_audit_file" + continue + fi + + # Replace possible slash '/' character in sbinary definition so we could use it in sed expressions below + sbinary_esc=${sbinary//$'/'/$'\/'} + + # For each audit rules file from the list of files to be inspected + for afile in "${files_to_inspect[@]}" + do + # Search current audit rules file's content for match. Match criteria: + # * existing rule is for the same SUID/SGID binary we are currently processing (but + # can contain multiple -F path= elements covering multiple SUID/SGID binaries) + # * existing rule contains all arguments from expected rule form (though can contain + # them in arbitrary order) + + base_search=$(sed -e '/-a always,exit/!d' -e '/-F path='"${sbinary_esc}"'[^[:graph:]]/!d' \ + -e '/-F path=[^[:space:]]\+/!d' \ + -e '/-F auid>='"1000"'/!d' -e '/-F auid!=\(4294967295\|unset\)/!d' \ + -e '/-k \|-F key=/!d' "$afile") + + # Increase the count of inspected files for this sbinary + count_of_inspected_files=$((count_of_inspected_files + 1)) + + # Search current audit rules file's content for presence of rule pattern for this sbinary + if [[ $base_search ]] + then + + # Current audit rules file already contains rule for this binary => + # Store the exact form of found rule for this binary for further processing + concrete_rule=$base_search + + # Select all other SUID/SGID binaries possibly also present in the found rule + + readarray -t handled_sbinaries < <(grep -o -e "-F path=[^[:space:]]\+" <<< "$concrete_rule") + handled_sbinaries=("${handled_sbinaries[@]//-F path=/}") + + # Merge the list of such SUID/SGID binaries found in this iteration with global list ignoring duplicates + readarray -t sbinaries_to_skip < <(for i in "${sbinaries_to_skip[@]}" "${handled_sbinaries[@]}"; do echo "$i"; done | sort -du) + + # if there is a -F perm flag, remove it + if grep -q '.*-F\s\+perm=[rwxa]\+.*' <<< "$concrete_rule"; then + + # Separate concrete_rule into three sections using hash '#' + # sign as a delimiter around rule's permission section borders + # note that the trailing space after perm flag is captured because there would be + # two consecutive spaces after joining remaining parts of the rule together + concrete_rule="$(echo "$concrete_rule" | sed -n "s/\(.*\)\+\(-F perm=[rwax]\+\ \?\)\+/\1#\2#/p")" + + # Split concrete_rule into head and tail sections using hash '#' delimiter + # The second column contains the permission section, which we don't need to extract + rule_head=$(cut -d '#' -f 1 <<< "$concrete_rule") + rule_tail=$(cut -d '#' -f 3 <<< "$concrete_rule") + + # Remove permissions section from existing rule in the file + sed -i "s#${rule_head}\(.*\)${rule_tail}#${rule_head}${rule_tail}#" "$afile" + fi + # If the required audit rule for particular sbinary wasn't found yet, insert it under following conditions: + # + # * in the "auditctl" mode of operation insert particular rule each time + # (because in this mode there's only one file -- /etc/audit/audit.rules to be inspected for presence of this rule), + # + # * in the "augenrules" mode of operation insert particular rule only once and only in case we have already + # searched all of the files from /etc/audit/rules.d/*.rules location (since that audit rule can be defined + # in any of those files and if not, we want it to be inserted only once into /etc/audit/rules.d/privileged.rules file) + # + elif [[ $count_of_inspected_files -eq "${#files_to_inspect[@]}" ]] + then + + # Check if this sbinary wasn't already handled in some of the previous afile iterations + # Return match only if whole sbinary definition matched (not in the case just prefix matched!!!) + if [[ ! $(sed -ne "\|${sbinary}|p" <<< "${sbinaries_to_skip[*]}") ]] + then + # Current audit rules file's content doesn't contain expected rule for this + # SUID/SGID binary yet => append it + echo "$expected_rule" >> "$output_audit_file" + fi + continue + fi + done +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - at + At a minimum, the audit system should collect the execution of +privileged commands for all users and root. If the auditd daemon is +configured to use the augenrules program to read audit rules during +daemon startup (the default), add a line of the following form to a file with +suffix .rules in the directory /etc/audit/rules.d: +-a always,exit -F path=/usr/bin/at -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add a line of the following +form to /etc/audit/audit.rules: +-a always,exit -F path=/usr/bin/at -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + CCI-000172 + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + FAU_GEN.1.1.c + SRG-OS-000471-VMM-001910 + Misuse of privileged functions, either intentionally or unintentionally by +authorized users, or by unauthorized external entities that have compromised system accounts, +is a serious and ongoing concern and can have significant adverse impacts on organizations. +Auditing the use of privileged functions is one way to detect such misuse and identify +the risk from insider and advanced persistent threats. + +Privileged programs are subject to escalation-of-privilege attacks, +which attempt to subvert their normal role of providing some necessary but +limited capability. As such, motivation exists to monitor these programs for +unusual activity. + - name: Gather the package facts + package_facts: + manager: auto + tags: + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_privileged_commands_at + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Perform remediation of Audit rules for /usr/bin/at + block: + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/bin/at -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules + set_fact: audit_file="/etc/audit/rules.d/privileged.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/bin/at -F perm=x -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/at -F perm=x + -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/bin/at -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:( + -S |,)\w+)+)( -F path=/usr/bin/at -F perm=x -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/at -F perm=x + -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_privileged_commands_at + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +ACTION_ARCH_FILTERS="-a always,exit" +OTHER_FILTERS="-F path=/usr/bin/at -F perm=x" +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="" +KEY="privileged" +SYSCALL_GROUPING="" +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - chage + At a minimum, the audit system should collect the execution of +privileged commands for all users and root. If the auditd daemon is +configured to use the augenrules program to read audit rules during +daemon startup (the default), add a line of the following form to a file with +suffix .rules in the directory /etc/audit/rules.d: +-a always,exit -F path=/usr/bin/chage -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add a line of the following +form to /etc/audit/audit.rules: +-a always,exit -F path=/usr/bin/chage -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + 1 + 12 + 13 + 14 + 15 + 16 + 2 + 3 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + BAI03.05 + DSS01.03 + DSS03.05 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 6.1 + SR 6.2 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.14.2.7 + A.15.2.1 + A.15.2.2 + CIP-004-6 R2.2.2 + CIP-004-6 R2.2.3 + CIP-007-3 R.1.3 + CIP-007-3 R5 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.3 + CIP-007-3 R5.2.1 + CIP-007-3 R5.2.3 + AC-2(4) + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.PT-1 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000468-GPOS-00212 + SRG-OS-000471-GPOS-00215 + SRG-OS-000471-VMM-001910 + Misuse of privileged functions, either intentionally or unintentionally by +authorized users, or by unauthorized external entities that have compromised system accounts, +is a serious and ongoing concern and can have significant adverse impacts on organizations. +Auditing the use of privileged functions is one way to detect such misuse and identify +the risk from insider and advanced persistent threats. + +Privileged programs are subject to escalation-of-privilege attacks, +which attempt to subvert their normal role of providing some necessary but +limited capability. As such, motivation exists to monitor these programs for +unusual activity. + CCE-83765-8 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83765-8 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_privileged_commands_chage + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Perform remediation of Audit rules for /usr/bin/chage + block: + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/bin/chage -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules + set_fact: audit_file="/etc/audit/rules.d/privileged.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/bin/chage -F perm=x -F + auid>=1000 -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/chage -F perm=x + -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/bin/chage -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:( + -S |,)\w+)+)( -F path=/usr/bin/chage -F perm=x -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/chage -F perm=x + -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83765-8 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_privileged_commands_chage + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +ACTION_ARCH_FILTERS="-a always,exit" +OTHER_FILTERS="-F path=/usr/bin/chage -F perm=x" +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="" +KEY="privileged" +SYSCALL_GROUPING="" +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - chsh + At a minimum, the audit system should collect the execution of +privileged commands for all users and root. If the auditd daemon is +configured to use the augenrules program to read audit rules during +daemon startup (the default), add a line of the following form to a file with +suffix .rules in the directory /etc/audit/rules.d: +-a always,exit -F path=/usr/bin/chsh -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add a line of the following +form to /etc/audit/audit.rules: +-a always,exit -F path=/usr/bin/chsh -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + 1 + 12 + 13 + 14 + 15 + 16 + 2 + 3 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + BAI03.05 + DSS01.03 + DSS03.05 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 6.1 + SR 6.2 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.14.2.7 + A.15.2.1 + A.15.2.2 + CIP-004-6 R2.2.2 + CIP-004-6 R2.2.3 + CIP-007-3 R.1.3 + CIP-007-3 R5 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.3 + CIP-007-3 R5.2.1 + CIP-007-3 R5.2.3 + AC-2(4) + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.PT-1 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + SRG-OS-000471-VMM-001910 + Misuse of privileged functions, either intentionally or unintentionally by +authorized users, or by unauthorized external entities that have compromised system accounts, +is a serious and ongoing concern and can have significant adverse impacts on organizations. +Auditing the use of privileged functions is one way to detect such misuse and identify +the risk from insider and advanced persistent threats. + +Privileged programs are subject to escalation-of-privilege attacks, +which attempt to subvert their normal role of providing some necessary but +limited capability. As such, motivation exists to monitor these programs for +unusual activity. + CCE-83763-3 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83763-3 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_privileged_commands_chsh + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Perform remediation of Audit rules for /usr/bin/chsh + block: + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/bin/chsh -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules + set_fact: audit_file="/etc/audit/rules.d/privileged.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/bin/chsh -F perm=x -F + auid>=1000 -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/chsh -F perm=x + -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/bin/chsh -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:( + -S |,)\w+)+)( -F path=/usr/bin/chsh -F perm=x -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/chsh -F perm=x + -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83763-3 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_privileged_commands_chsh + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +ACTION_ARCH_FILTERS="-a always,exit" +OTHER_FILTERS="-F path=/usr/bin/chsh -F perm=x" +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="" +KEY="privileged" +SYSCALL_GROUPING="" +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - crontab + At a minimum, the audit system should collect the execution of +privileged commands for all users and root. If the auditd daemon is +configured to use the augenrules program to read audit rules during +daemon startup (the default), add a line of the following form to a file with +suffix .rules in the directory /etc/audit/rules.d: +-a always,exit -F path=/usr/bin/crontab -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add a line of the following +form to /etc/audit/audit.rules: +-a always,exit -F path=/usr/bin/crontab -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + 1 + 12 + 13 + 14 + 15 + 16 + 2 + 3 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + BAI03.05 + DSS01.03 + DSS03.05 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 6.1 + SR 6.2 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.14.2.7 + A.15.2.1 + A.15.2.2 + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.PT-1 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + SRG-OS-000471-VMM-001910 + Misuse of privileged functions, either intentionally or unintentionally by +authorized users, or by unauthorized external entities that have compromised system accounts, +is a serious and ongoing concern and can have significant adverse impacts on organizations. +Auditing the use of privileged functions is one way to detect such misuse and identify +the risk from insider and advanced persistent threats. + +Privileged programs are subject to escalation-of-privilege attacks, +which attempt to subvert their normal role of providing some necessary but +limited capability. As such, motivation exists to monitor these programs for +unusual activity. + CCE-83761-7 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83761-7 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_privileged_commands_crontab + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Perform remediation of Audit rules for /usr/bin/crontab + block: + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/bin/crontab -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules + set_fact: audit_file="/etc/audit/rules.d/privileged.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/bin/crontab -F perm=x + -F auid>=1000 -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/crontab -F perm=x + -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/bin/crontab -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:( + -S |,)\w+)+)( -F path=/usr/bin/crontab -F perm=x -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/crontab -F perm=x + -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83761-7 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_privileged_commands_crontab + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +ACTION_ARCH_FILTERS="-a always,exit" +OTHER_FILTERS="-F path=/usr/bin/crontab -F perm=x" +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="" +KEY="privileged" +SYSCALL_GROUPING="" +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - gpasswd + At a minimum, the audit system should collect the execution of +privileged commands for all users and root. If the auditd daemon is +configured to use the augenrules program to read audit rules during +daemon startup (the default), add a line of the following form to a file with +suffix .rules in the directory /etc/audit/rules.d: +-a always,exit -F path=/usr/bin/gpasswd -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add a line of the following +form to /etc/audit/audit.rules: +-a always,exit -F path=/usr/bin/gpasswd -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + 1 + 12 + 13 + 14 + 15 + 16 + 2 + 3 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + BAI03.05 + DSS01.03 + DSS03.05 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 6.1 + SR 6.2 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.14.2.7 + A.15.2.1 + A.15.2.2 + CIP-004-6 R2.2.2 + CIP-004-6 R2.2.3 + CIP-007-3 R.1.3 + CIP-007-3 R5 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.3 + CIP-007-3 R5.2.1 + CIP-007-3 R5.2.3 + AC-2(4) + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.PT-1 + FAU_GEN.1.1.c + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + SRG-OS-000471-VMM-001910 + Misuse of privileged functions, either intentionally or unintentionally by +authorized users, or by unauthorized external entities that have compromised system accounts, +is a serious and ongoing concern and can have significant adverse impacts on organizations. +Auditing the use of privileged functions is one way to detect such misuse and identify +the risk from insider and advanced persistent threats. + +Privileged programs are subject to escalation-of-privilege attacks, +which attempt to subvert their normal role of providing some necessary but +limited capability. As such, motivation exists to monitor these programs for +unusual activity. + CCE-83773-2 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83773-2 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_privileged_commands_gpasswd + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Perform remediation of Audit rules for /usr/bin/gpasswd + block: + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/bin/gpasswd -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules + set_fact: audit_file="/etc/audit/rules.d/privileged.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/bin/gpasswd -F perm=x + -F auid>=1000 -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/gpasswd -F perm=x + -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/bin/gpasswd -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:( + -S |,)\w+)+)( -F path=/usr/bin/gpasswd -F perm=x -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/gpasswd -F perm=x + -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83773-2 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_privileged_commands_gpasswd + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +ACTION_ARCH_FILTERS="-a always,exit" +OTHER_FILTERS="-F path=/usr/bin/gpasswd -F perm=x" +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="" +KEY="privileged" +SYSCALL_GROUPING="" +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - kmod + At a minimum, the audit system should collect the execution of +privileged commands for all users and root. If the auditd daemon is +configured to use the augenrules program to read audit rules during +daemon startup (the default), add a line of the following form to a file with +suffix .rules in the directory /etc/audit/rules.d: +-a always,exit -F path=/usr/bin/kmod -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add a line of the following +form to /etc/audit/audit.rules: +-a always,exit -F path=/usr/bin/kmod -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + AU-3 + AU-3.1 + AU-12(a) + AU-12.1(ii) + AU-12.1(iv)AU-12(c) + MA-4(1)(a) + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + SRG-OS-000471-GPOS-00216 + SRG-OS-000477-GPOS-00222 + Without generating audit records that are specific to the security and +mission needs of the organization, it would be difficult to establish, +correlate, and investigate the events relating to an incident or identify +those responsible for one. + +Audit records can be generated from various components within the +information system (e.g., module or policy filter). + + CCE-90262-7 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-90262-7 + - NIST-800-53-AU-12(a) + - NIST-800-53-AU-12.1(ii) + - NIST-800-53-AU-12.1(iv)AU-12(c) + - NIST-800-53-AU-3 + - NIST-800-53-AU-3.1 + - NIST-800-53-MA-4(1)(a) + - audit_rules_privileged_commands_kmod + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Perform remediation of Audit rules for /usr/bin/kmod + block: + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/bin/kmod -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules + set_fact: audit_file="/etc/audit/rules.d/privileged.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/bin/kmod -F perm=x -F + auid>=1000 -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/kmod -F perm=x + -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/bin/kmod -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:( + -S |,)\w+)+)( -F path=/usr/bin/kmod -F perm=x -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/kmod -F perm=x + -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-90262-7 + - NIST-800-53-AU-12(a) + - NIST-800-53-AU-12.1(ii) + - NIST-800-53-AU-12.1(iv)AU-12(c) + - NIST-800-53-AU-3 + - NIST-800-53-AU-3.1 + - NIST-800-53-MA-4(1)(a) + - audit_rules_privileged_commands_kmod + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +ACTION_ARCH_FILTERS="-a always,exit" +OTHER_FILTERS="-F path=/usr/bin/kmod -F perm=x" +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="" +KEY="privileged" +SYSCALL_GROUPING="" +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - mount + At a minimum, the audit system should collect the execution of +privileged commands for all users and root. If the auditd daemon is +configured to use the augenrules program to read audit rules during +daemon startup (the default), add a line of the following form to a file with +suffix .rules in the directory /etc/audit/rules.d: +-a always,exit -F path=/usr/bin/mount -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add a line of the following +form to /etc/audit/audit.rules: +-a always,exit -F path=/usr/bin/mount -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + FAU_GEN.1.1.c + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + SRG-OS-000471-VMM-001910 + Misuse of privileged functions, either intentionally or unintentionally by +authorized users, or by unauthorized external entities that have compromised system accounts, +is a serious and ongoing concern and can have significant adverse impacts on organizations. +Auditing the use of privileged functions is one way to detect such misuse and identify +the risk from insider and advanced persistent threats. + +Privileged programs are subject to escalation-of-privilege attacks, +which attempt to subvert their normal role of providing some necessary but +limited capability. As such, motivation exists to monitor these programs for +unusual activity. + CCE-89564-9 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-89564-9 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_privileged_commands_mount + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Perform remediation of Audit rules for /usr/bin/mount + block: + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/bin/mount -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules + set_fact: audit_file="/etc/audit/rules.d/privileged.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/bin/mount -F perm=x -F + auid>=1000 -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/mount -F perm=x + -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/bin/mount -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:( + -S |,)\w+)+)( -F path=/usr/bin/mount -F perm=x -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/mount -F perm=x + -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-89564-9 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_privileged_commands_mount + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +ACTION_ARCH_FILTERS="-a always,exit" +OTHER_FILTERS="-F path=/usr/bin/mount -F perm=x" +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="" +KEY="privileged" +SYSCALL_GROUPING="" +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - newgidmap + At a minimum, the audit system should collect the execution of +privileged commands for all users and root. If the auditd daemon is +configured to use the augenrules program to read audit rules during +daemon startup (the default), add a line of the following form to a file with +suffix .rules in the directory /etc/audit/rules.d: +-a always,exit -F path=/usr/bin/newgidmap -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add a line of the following +form to /etc/audit/audit.rules: +-a always,exit -F path=/usr/bin/newgidmap -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + CCI-000172 + CIP-004-6 R2.2.2 + CIP-004-6 R2.2.3 + CIP-007-3 R.1.3 + CIP-007-3 R5 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.3 + CIP-007-3 R5.2.1 + CIP-007-3 R5.2.3 + AC-2(4) + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + FAU_GEN.1.1.c + SRG-OS-000471-VMM-001910 + Misuse of privileged functions, either intentionally or unintentionally by +authorized users, or by unauthorized external entities that have compromised system accounts, +is a serious and ongoing concern and can have significant adverse impacts on organizations. +Auditing the use of privileged functions is one way to detect such misuse and identify +the risk from insider and advanced persistent threats. + +Privileged programs are subject to escalation-of-privilege attacks, +which attempt to subvert their normal role of providing some necessary but +limited capability. As such, motivation exists to monitor these programs for +unusual activity. + - name: Gather the package facts + package_facts: + manager: auto + tags: + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_privileged_commands_newgidmap + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Perform remediation of Audit rules for /usr/bin/newgidmap + block: + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/bin/newgidmap -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules + set_fact: audit_file="/etc/audit/rules.d/privileged.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/bin/newgidmap -F perm=x + -F auid>=1000 -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/newgidmap -F + perm=x -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/bin/newgidmap -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:( + -S |,)\w+)+)( -F path=/usr/bin/newgidmap -F perm=x -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/newgidmap -F + perm=x -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_privileged_commands_newgidmap + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +ACTION_ARCH_FILTERS="-a always,exit" +OTHER_FILTERS="-F path=/usr/bin/newgidmap -F perm=x" +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="" +KEY="privileged" +SYSCALL_GROUPING="" +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - newgrp + At a minimum, the audit system should collect the execution of +privileged commands for all users and root. If the auditd daemon is +configured to use the augenrules program to read audit rules during +daemon startup (the default), add a line of the following form to a file with +suffix .rules in the directory /etc/audit/rules.d: +-a always,exit -F path=/usr/bin/newgrp -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add a line of the following +form to /etc/audit/audit.rules: +-a always,exit -F path=/usr/bin/newgrp -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + 1 + 12 + 13 + 14 + 15 + 16 + 2 + 3 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + BAI03.05 + DSS01.03 + DSS03.05 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000130 + CCI-000169 + CCI-000135 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 6.1 + SR 6.2 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.14.2.7 + A.15.2.1 + A.15.2.2 + CIP-004-6 R2.2.2 + CIP-004-6 R2.2.3 + CIP-007-3 R.1.3 + CIP-007-3 R5 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.3 + CIP-007-3 R5.2.1 + CIP-007-3 R5.2.3 + AC-2(4) + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.PT-1 + FAU_GEN.1.1.c + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + SRG-OS-000471-VMM-001910 + Misuse of privileged functions, either intentionally or unintentionally by +authorized users, or by unauthorized external entities that have compromised system accounts, +is a serious and ongoing concern and can have significant adverse impacts on organizations. +Auditing the use of privileged functions is one way to detect such misuse and identify +the risk from insider and advanced persistent threats. + +Privileged programs are subject to escalation-of-privilege attacks, +which attempt to subvert their normal role of providing some necessary but +limited capability. As such, motivation exists to monitor these programs for +unusual activity. + CCE-83766-6 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83766-6 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_privileged_commands_newgrp + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Perform remediation of Audit rules for /usr/bin/newgrp + block: + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/bin/newgrp -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules + set_fact: audit_file="/etc/audit/rules.d/privileged.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/bin/newgrp -F perm=x -F + auid>=1000 -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/newgrp -F perm=x + -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/bin/newgrp -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:( + -S |,)\w+)+)( -F path=/usr/bin/newgrp -F perm=x -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/newgrp -F perm=x + -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83766-6 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_privileged_commands_newgrp + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +ACTION_ARCH_FILTERS="-a always,exit" +OTHER_FILTERS="-F path=/usr/bin/newgrp -F perm=x" +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="" +KEY="privileged" +SYSCALL_GROUPING="" +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - newuidmap + At a minimum, the audit system should collect the execution of +privileged commands for all users and root. If the auditd daemon is +configured to use the augenrules program to read audit rules during +daemon startup (the default), add a line of the following form to a file with +suffix .rules in the directory /etc/audit/rules.d: +-a always,exit -F path=/usr/bin/newuidmap -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add a line of the following +form to /etc/audit/audit.rules: +-a always,exit -F path=/usr/bin/newuidmap -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + CCI-000172 + CIP-004-6 R2.2.2 + CIP-004-6 R2.2.3 + CIP-007-3 R.1.3 + CIP-007-3 R5 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.3 + CIP-007-3 R5.2.1 + CIP-007-3 R5.2.3 + AC-2(4) + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + FAU_GEN.1.1.c + SRG-OS-000471-VMM-001910 + Misuse of privileged functions, either intentionally or unintentionally by +authorized users, or by unauthorized external entities that have compromised system accounts, +is a serious and ongoing concern and can have significant adverse impacts on organizations. +Auditing the use of privileged functions is one way to detect such misuse and identify +the risk from insider and advanced persistent threats. + +Privileged programs are subject to escalation-of-privilege attacks, +which attempt to subvert their normal role of providing some necessary but +limited capability. As such, motivation exists to monitor these programs for +unusual activity. + - name: Gather the package facts + package_facts: + manager: auto + tags: + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_privileged_commands_newuidmap + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Perform remediation of Audit rules for /usr/bin/newuidmap + block: + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/bin/newuidmap -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules + set_fact: audit_file="/etc/audit/rules.d/privileged.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/bin/newuidmap -F perm=x + -F auid>=1000 -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/newuidmap -F + perm=x -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/bin/newuidmap -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:( + -S |,)\w+)+)( -F path=/usr/bin/newuidmap -F perm=x -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/newuidmap -F + perm=x -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_privileged_commands_newuidmap + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +ACTION_ARCH_FILTERS="-a always,exit" +OTHER_FILTERS="-F path=/usr/bin/newuidmap -F perm=x" +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="" +KEY="privileged" +SYSCALL_GROUPING="" +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - pam_timestamp_check + At a minimum, the audit system should collect the execution of +privileged commands for all users and root. If the auditd daemon is +configured to use the augenrules program to read audit rules during +daemon startup (the default), add a line of the following form to a file with +suffix .rules in the directory /etc/audit/rules.d: +-a always,exit -F path=/usr/sbin/pam_timestamp_check +-F perm=x -F auid>=1000 -F auid!=unset -F key=privileged +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add a line of the following +form to /etc/audit/audit.rules: +-a always,exit -F path=/usr/sbin/pam_timestamp_check +-F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + 1 + 12 + 13 + 14 + 15 + 16 + 2 + 3 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + BAI03.05 + DSS01.03 + DSS03.05 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 6.1 + SR 6.2 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.14.2.7 + A.15.2.1 + A.15.2.2 + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.PT-1 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + SRG-OS-000471-VMM-001910 + Misuse of privileged functions, either intentionally or unintentionally by +authorized users, or by unauthorized external entities that have compromised system accounts, +is a serious and ongoing concern and can have significant adverse impacts on organizations. +Auditing the use of privileged functions is one way to detect such misuse and identify +the risk from insider and advanced persistent threats. + +Privileged programs are subject to escalation-of-privilege attacks, +which attempt to subvert their normal role of providing some necessary but +limited capability. As such, motivation exists to monitor these programs for +unusual activity. + CCE-83767-4 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83767-4 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_privileged_commands_pam_timestamp_check + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Perform remediation of Audit rules for /usr/sbin/pam_timestamp_check + block: + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/sbin/pam_timestamp_check -F perm=x -F auid>=1000 -F auid!=unset + (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules + set_fact: audit_file="/etc/audit/rules.d/privileged.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/sbin/pam_timestamp_check + -F perm=x -F auid>=1000 -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/sbin/pam_timestamp_check + -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/sbin/pam_timestamp_check -F perm=x -F auid>=1000 -F auid!=unset + (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:( + -S |,)\w+)+)( -F path=/usr/sbin/pam_timestamp_check -F perm=x -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/sbin/pam_timestamp_check + -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83767-4 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_privileged_commands_pam_timestamp_check + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +ACTION_ARCH_FILTERS="-a always,exit" +OTHER_FILTERS="-F path=/usr/sbin/pam_timestamp_check -F perm=x" +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="" +KEY="privileged" +SYSCALL_GROUPING="" +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - passwd + At a minimum, the audit system should collect the execution of +privileged commands for all users and root. If the auditd daemon is +configured to use the augenrules program to read audit rules during +daemon startup (the default), add a line of the following form to a file with +suffix .rules in the directory /etc/audit/rules.d: +-a always,exit -F path=/usr/bin/passwd -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add a line of the following +form to /etc/audit/audit.rules: +-a always,exit -F path=/usr/bin/passwd -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + 1 + 12 + 13 + 14 + 15 + 16 + 2 + 3 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + BAI03.05 + DSS01.03 + DSS03.05 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 6.1 + SR 6.2 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.14.2.7 + A.15.2.1 + A.15.2.2 + CIP-004-6 R2.2.2 + CIP-004-6 R2.2.3 + CIP-007-3 R.1.3 + CIP-007-3 R5 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.3 + CIP-007-3 R5.2.1 + CIP-007-3 R5.2.3 + AC-2(4) + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.PT-1 + FAU_GEN.1.1.c + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + SRG-OS-000471-VMM-001910 + Misuse of privileged functions, either intentionally or unintentionally by +authorized users, or by unauthorized external entities that have compromised system accounts, +is a serious and ongoing concern and can have significant adverse impacts on organizations. +Auditing the use of privileged functions is one way to detect such misuse and identify +the risk from insider and advanced persistent threats. + +Privileged programs are subject to escalation-of-privilege attacks, +which attempt to subvert their normal role of providing some necessary but +limited capability. As such, motivation exists to monitor these programs for +unusual activity. + CCE-83781-5 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83781-5 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_privileged_commands_passwd + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Perform remediation of Audit rules for /usr/bin/passwd + block: + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/bin/passwd -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules + set_fact: audit_file="/etc/audit/rules.d/privileged.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/bin/passwd -F perm=x -F + auid>=1000 -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/passwd -F perm=x + -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/bin/passwd -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:( + -S |,)\w+)+)( -F path=/usr/bin/passwd -F perm=x -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/passwd -F perm=x + -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83781-5 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_privileged_commands_passwd + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +ACTION_ARCH_FILTERS="-a always,exit" +OTHER_FILTERS="-F path=/usr/bin/passwd -F perm=x" +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="" +KEY="privileged" +SYSCALL_GROUPING="" +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - postdrop + At a minimum, the audit system should collect the execution of +privileged commands for all users and root. If the auditd daemon is +configured to use the augenrules program to read audit rules during +daemon startup (the default), add a line of the following form to a file with +suffix .rules in the directory /etc/audit/rules.d: +-a always,exit -F path=/usr/sbin/postdrop -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add a line of the following +form to /etc/audit/audit.rules: +-a always,exit -F path=/usr/sbin/postdrop -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + 1 + 12 + 13 + 14 + 15 + 16 + 2 + 3 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + BAI03.05 + DSS01.03 + DSS03.05 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 6.1 + SR 6.2 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.14.2.7 + A.15.2.1 + A.15.2.2 + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.PT-1 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + SRG-OS-000471-VMM-001910 + Misuse of privileged functions, either intentionally or unintentionally by +authorized users, or by unauthorized external entities that have compromised system accounts, +is a serious and ongoing concern and can have significant adverse impacts on organizations. +Auditing the use of privileged functions is one way to detect such misuse and identify +the risk from insider and advanced persistent threats. + +Privileged programs are subject to escalation-of-privilege attacks, +which attempt to subvert their normal role of providing some necessary but +limited capability. As such, motivation exists to monitor these programs for +unusual activity. + CCE-83769-0 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83769-0 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_privileged_commands_postdrop + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Perform remediation of Audit rules for /usr/sbin/postdrop + block: + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/sbin/postdrop -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules + set_fact: audit_file="/etc/audit/rules.d/privileged.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/sbin/postdrop -F perm=x + -F auid>=1000 -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/sbin/postdrop -F + perm=x -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/sbin/postdrop -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:( + -S |,)\w+)+)( -F path=/usr/sbin/postdrop -F perm=x -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/sbin/postdrop -F + perm=x -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83769-0 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_privileged_commands_postdrop + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +ACTION_ARCH_FILTERS="-a always,exit" +OTHER_FILTERS="-F path=/usr/sbin/postdrop -F perm=x" +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="" +KEY="privileged" +SYSCALL_GROUPING="" +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - postqueue + At a minimum, the audit system should collect the execution of +privileged commands for all users and root. If the auditd daemon is +configured to use the augenrules program to read audit rules during +daemon startup (the default), add a line of the following form to a file with +suffix .rules in the directory /etc/audit/rules.d: +-a always,exit -F path=/usr/sbin/postqueue -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add a line of the following +form to /etc/audit/audit.rules: +-a always,exit -F path=/usr/sbin/postqueue -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + 1 + 12 + 13 + 14 + 15 + 16 + 2 + 3 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + BAI03.05 + DSS01.03 + DSS03.05 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 6.1 + SR 6.2 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.14.2.7 + A.15.2.1 + A.15.2.2 + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.PT-1 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + SRG-OS-000471-VMM-001910 + Misuse of privileged functions, either intentionally or unintentionally by +authorized users, or by unauthorized external entities that have compromised system accounts, +is a serious and ongoing concern and can have significant adverse impacts on organizations. +Auditing the use of privileged functions is one way to detect such misuse and identify +the risk from insider and advanced persistent threats. + +Privileged programs are subject to escalation-of-privilege attacks, +which attempt to subvert their normal role of providing some necessary but +limited capability. As such, motivation exists to monitor these programs for +unusual activity. + CCE-83770-8 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83770-8 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_privileged_commands_postqueue + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Perform remediation of Audit rules for /usr/sbin/postqueue + block: + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/sbin/postqueue -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules + set_fact: audit_file="/etc/audit/rules.d/privileged.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/sbin/postqueue -F perm=x + -F auid>=1000 -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/sbin/postqueue -F + perm=x -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/sbin/postqueue -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:( + -S |,)\w+)+)( -F path=/usr/sbin/postqueue -F perm=x -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/sbin/postqueue -F + perm=x -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83770-8 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_privileged_commands_postqueue + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +ACTION_ARCH_FILTERS="-a always,exit" +OTHER_FILTERS="-F path=/usr/sbin/postqueue -F perm=x" +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="" +KEY="privileged" +SYSCALL_GROUPING="" +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - pt_chown + At a minimum, the audit system should collect the execution of +privileged commands for all users and root. If the auditd daemon is +configured to use the augenrules program to read audit rules during +daemon startup (the default), add a line of the following form to a file with +suffix .rules in the directory /etc/audit/rules.d: +-a always,exit -F path=/usr/libexec/pt_chown -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add a line of the following +form to /etc/audit/audit.rules: +-a always,exit -F path=/usr/libexec/pt_chown -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + 1 + 12 + 13 + 14 + 15 + 16 + 2 + 3 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + BAI03.05 + DSS01.03 + DSS03.05 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000135 + CCI-000172 + CCI-002884 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 6.1 + SR 6.2 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.14.2.7 + A.15.2.1 + A.15.2.2 + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.PT-1 + SRG-OS-000042-GPOS-00020 + SRG-OS-000392-GPOS-00172 + SRG-OS-000471-GPOS-00215 + SRG-OS-000471-VMM-001910 + Misuse of privileged functions, either intentionally or unintentionally by +authorized users, or by unauthorized external entities that have compromised system accounts, +is a serious and ongoing concern and can have significant adverse impacts on organizations. +Auditing the use of privileged functions is one way to detect such misuse and identify +the risk from insider and advanced persistent threats. + +Privileged programs are subject to escalation-of-privilege attacks, +which attempt to subvert their normal role of providing some necessary but +limited capability. As such, motivation exists to monitor these programs for +unusual activity. + CCE-88512-9 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-88512-9 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_privileged_commands_pt_chown + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Perform remediation of Audit rules for /usr/libexec/pt_chown + block: + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/libexec/pt_chown -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules + set_fact: audit_file="/etc/audit/rules.d/privileged.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/libexec/pt_chown -F perm=x + -F auid>=1000 -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/libexec/pt_chown + -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/libexec/pt_chown -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:( + -S |,)\w+)+)( -F path=/usr/libexec/pt_chown -F perm=x -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/libexec/pt_chown + -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-88512-9 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_privileged_commands_pt_chown + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +ACTION_ARCH_FILTERS="-a always,exit" +OTHER_FILTERS="-F path=/usr/libexec/pt_chown -F perm=x" +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="" +KEY="privileged" +SYSCALL_GROUPING="" +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Any Attempts to Run ssh-agent + At a minimum, the audit system should collect any execution attempt +of the ssh-agent command for all users and root. If the auditd +daemon is configured to use the augenrules program to read audit rules +during daemon startup (the default), add the following lines to a file with suffix +.rules in the directory /etc/audit/rules.d: +-a always,exit -F path=/usr/bin/ssh-agent -F perm=x -F auid>=1000 -F auid!=unset -k privileged-ssh-agent +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following lines to +/etc/audit/audit.rules file: +-a always,exit -F path=/usr/bin/ssh-agent -F perm=x -F auid>=1000 -F auid!=unset -k privileged-ssh-agent + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + Without generating audit records that are specific to the security and +mission needs of the organization, it would be difficult to establish, +correlate, and investigate the events relating to an incident or identify +those responsible for one. + +Audit records can be generated from various components within the +information system (e.g., module or policy filter). + CCE-90388-0 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-90388-0 + - audit_rules_privileged_commands_ssh_agent + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Perform remediation of Audit rules for /usr/bin/ssh-agent + block: + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/bin/ssh-agent -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules + set_fact: audit_file="/etc/audit/rules.d/privileged.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/bin/ssh-agent -F perm=x + -F auid>=1000 -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/ssh-agent -F + perm=x -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/bin/ssh-agent -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:( + -S |,)\w+)+)( -F path=/usr/bin/ssh-agent -F perm=x -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/ssh-agent -F + perm=x -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-90388-0 + - audit_rules_privileged_commands_ssh_agent + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +ACTION_ARCH_FILTERS="-a always,exit" +OTHER_FILTERS="-F path=/usr/bin/ssh-agent -F perm=x" +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="" +KEY="privileged" +SYSCALL_GROUPING="" +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - ssh-keysign + At a minimum, the audit system should collect the execution of +privileged commands for all users and root. If the auditd daemon is +configured to use the augenrules program to read audit rules during +daemon startup (the default), add a line of the following form to a file with +suffix .rules in the directory /etc/audit/rules.d: +-a always,exit -F path=/usr/libexec/openssh/ssh-keysign -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add a line of the following +form to /etc/audit/audit.rules: +-a always,exit -F path=/usr/libexec/openssh/ssh-keysign -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + 1 + 12 + 13 + 14 + 15 + 16 + 2 + 3 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + BAI03.05 + DSS01.03 + DSS03.05 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 6.1 + SR 6.2 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.14.2.7 + A.15.2.1 + A.15.2.2 + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.PT-1 + FAU_GEN.1.1.c + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + SRG-OS-000471-VMM-001910 + Misuse of privileged functions, either intentionally or unintentionally by +authorized users, or by unauthorized external entities that have compromised system accounts, +is a serious and ongoing concern and can have significant adverse impacts on organizations. +Auditing the use of privileged functions is one way to detect such misuse and identify +the risk from insider and advanced persistent threats. + +Privileged programs are subject to escalation-of-privilege attacks, +which attempt to subvert their normal role of providing some necessary but +limited capability. As such, motivation exists to monitor these programs for +unusual activity. + CCE-83776-5 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83776-5 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_privileged_commands_ssh_keysign + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Perform remediation of Audit rules for /usr/libexec/openssh/ssh-keysign + block: + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/libexec/openssh/ssh-keysign -F perm=x -F auid>=1000 -F auid!=unset + (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules + set_fact: audit_file="/etc/audit/rules.d/privileged.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/libexec/openssh/ssh-keysign + -F perm=x -F auid>=1000 -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/libexec/openssh/ssh-keysign + -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/libexec/openssh/ssh-keysign -F perm=x -F auid>=1000 -F auid!=unset + (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:( + -S |,)\w+)+)( -F path=/usr/libexec/openssh/ssh-keysign -F perm=x -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/libexec/openssh/ssh-keysign + -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83776-5 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_privileged_commands_ssh_keysign + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +ACTION_ARCH_FILTERS="-a always,exit" +OTHER_FILTERS="-F path=/usr/libexec/openssh/ssh-keysign -F perm=x" +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="" +KEY="privileged" +SYSCALL_GROUPING="" +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - su + At a minimum, the audit system should collect the execution of +privileged commands for all users and root. If the auditd daemon is +configured to use the augenrules program to read audit rules during +daemon startup (the default), add a line of the following form to a file with +suffix .rules in the directory /etc/audit/rules.d: +-a always,exit -F path=/usr/bin/su -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add a line of the following +form to /etc/audit/audit.rules: +-a always,exit -F path=/usr/bin/su -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + 1 + 12 + 13 + 14 + 15 + 16 + 2 + 3 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + BAI03.05 + DSS01.03 + DSS03.05 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 6.1 + SR 6.2 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.14.2.7 + A.15.2.1 + A.15.2.2 + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.PT-1 + FAU_GEN.1.1.c + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000064-GPOS-0003 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + SRG-OS-000466-GPOS-00210 + SRG-OS-000471-VMM-001910 + Misuse of privileged functions, either intentionally or unintentionally by +authorized users, or by unauthorized external entities that have compromised system accounts, +is a serious and ongoing concern and can have significant adverse impacts on organizations. +Auditing the use of privileged functions is one way to detect such misuse and identify +the risk from insider and advanced persistent threats. + +Privileged programs are subject to escalation-of-privilege attacks, +which attempt to subvert their normal role of providing some necessary but +limited capability. As such, motivation exists to monitor these programs for +unusual activity. + CCE-83771-6 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83771-6 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_privileged_commands_su + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Perform remediation of Audit rules for /usr/bin/su + block: + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/bin/su -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules + set_fact: audit_file="/etc/audit/rules.d/privileged.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/bin/su -F perm=x -F auid>=1000 + -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/su -F perm=x + -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/bin/su -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:( + -S |,)\w+)+)( -F path=/usr/bin/su -F perm=x -F auid>=1000 -F auid!=unset (?:-k + |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/su -F perm=x + -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83771-6 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_privileged_commands_su + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +ACTION_ARCH_FILTERS="-a always,exit" +OTHER_FILTERS="-F path=/usr/bin/su -F perm=x" +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="" +KEY="privileged" +SYSCALL_GROUPING="" +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - sudo + At a minimum, the audit system should collect the execution of +privileged commands for all users and root. If the auditd daemon is +configured to use the augenrules program to read audit rules during +daemon startup (the default), add a line of the following form to a file with +suffix .rules in the directory /etc/audit/rules.d: +-a always,exit -F path=/usr/bin/sudo -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add a line of the following +form to /etc/audit/audit.rules: +-a always,exit -F path=/usr/bin/sudo -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + BP28(R19) + 1 + 12 + 13 + 14 + 15 + 16 + 2 + 3 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + BAI03.05 + DSS01.03 + DSS03.05 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 6.1 + SR 6.2 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.14.2.7 + A.15.2.1 + A.15.2.2 + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.PT-1 + FAU_GEN.1.1.c + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + SRG-OS-000466-GPOS-00210 + SRG-OS-000471-VMM-001910 + Misuse of privileged functions, either intentionally or unintentionally by +authorized users, or by unauthorized external entities that have compromised system accounts, +is a serious and ongoing concern and can have significant adverse impacts on organizations. +Auditing the use of privileged functions is one way to detect such misuse and identify +the risk from insider and advanced persistent threats. + +Privileged programs are subject to escalation-of-privilege attacks, +which attempt to subvert their normal role of providing some necessary but +limited capability. As such, motivation exists to monitor these programs for +unusual activity. + CCE-83780-7 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83780-7 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_privileged_commands_sudo + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Perform remediation of Audit rules for /usr/bin/sudo + block: + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/bin/sudo -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules + set_fact: audit_file="/etc/audit/rules.d/privileged.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/bin/sudo -F perm=x -F + auid>=1000 -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/sudo -F perm=x + -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/bin/sudo -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:( + -S |,)\w+)+)( -F path=/usr/bin/sudo -F perm=x -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/sudo -F perm=x + -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83780-7 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_privileged_commands_sudo + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +ACTION_ARCH_FILTERS="-a always,exit" +OTHER_FILTERS="-F path=/usr/bin/sudo -F perm=x" +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="" +KEY="privileged" +SYSCALL_GROUPING="" +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - sudoedit + At a minimum, the audit system should collect the execution of +privileged commands for all users and root. If the auditd daemon is +configured to use the augenrules program to read audit rules during +daemon startup (the default), add a line of the following form to a file with +suffix .rules in the directory /etc/audit/rules.d: +-a always,exit -F path=/usr/bin/sudoedit -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add a line of the following +form to /etc/audit/audit.rules: +-a always,exit -F path=/usr/bin/sudoedit -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + 1 + 12 + 13 + 14 + 15 + 16 + 2 + 3 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + BAI03.05 + DSS01.03 + DSS03.05 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 6.1 + SR 6.2 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.14.2.7 + A.15.2.1 + A.15.2.2 + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.PT-1 + FAU_GEN.1.1.c + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + SRG-OS-000471-VMM-001910 + Misuse of privileged functions, either intentionally or unintentionally by +authorized users, or by unauthorized external entities that have compromised system accounts, +is a serious and ongoing concern and can have significant adverse impacts on organizations. +Auditing the use of privileged functions is one way to detect such misuse and identify +the risk from insider and advanced persistent threats. + +Privileged programs are subject to escalation-of-privilege attacks, +which attempt to subvert their normal role of providing some necessary but +limited capability. As such, motivation exists to monitor these programs for +unusual activity. + CCE-83764-1 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83764-1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_privileged_commands_sudoedit + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Perform remediation of Audit rules for /usr/bin/sudoedit + block: + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/bin/sudoedit -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules + set_fact: audit_file="/etc/audit/rules.d/privileged.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/bin/sudoedit -F perm=x + -F auid>=1000 -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/sudoedit -F + perm=x -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/bin/sudoedit -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:( + -S |,)\w+)+)( -F path=/usr/bin/sudoedit -F perm=x -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/sudoedit -F + perm=x -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83764-1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_privileged_commands_sudoedit + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +ACTION_ARCH_FILTERS="-a always,exit" +OTHER_FILTERS="-F path=/usr/bin/sudoedit -F perm=x" +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="" +KEY="privileged" +SYSCALL_GROUPING="" +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - umount + At a minimum, the audit system should collect the execution of +privileged commands for all users and root. If the auditd daemon is +configured to use the augenrules program to read audit rules during +daemon startup (the default), add a line of the following form to a file with +suffix .rules in the directory /etc/audit/rules.d: +-a always,exit -F path=/usr/bin/umount -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add a line of the following +form to /etc/audit/audit.rules: +-a always,exit -F path=/usr/bin/umount -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + 1 + 12 + 13 + 14 + 15 + 16 + 2 + 3 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + BAI03.05 + DSS01.03 + DSS03.05 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000130 + CCI-000169 + CCI-000135 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 6.1 + SR 6.2 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.14.2.7 + A.15.2.1 + A.15.2.2 + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.PT-1 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + SRG-OS-000471-VMM-001910 + Misuse of privileged functions, either intentionally or unintentionally by +authorized users, or by unauthorized external entities that have compromised system accounts, +is a serious and ongoing concern and can have significant adverse impacts on organizations. +Auditing the use of privileged functions is one way to detect such misuse and identify +the risk from insider and advanced persistent threats. + +Privileged programs are subject to escalation-of-privilege attacks, +which attempt to subvert their normal role of providing some necessary but +limited capability. As such, motivation exists to monitor these programs for +unusual activity. + CCE-83762-5 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83762-5 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_privileged_commands_umount + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Perform remediation of Audit rules for /usr/bin/umount + block: + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/bin/umount -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules + set_fact: audit_file="/etc/audit/rules.d/privileged.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/bin/umount -F perm=x -F + auid>=1000 -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/umount -F perm=x + -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/bin/umount -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:( + -S |,)\w+)+)( -F path=/usr/bin/umount -F perm=x -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/umount -F perm=x + -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83762-5 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_privileged_commands_umount + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +ACTION_ARCH_FILTERS="-a always,exit" +OTHER_FILTERS="-F path=/usr/bin/umount -F perm=x" +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="" +KEY="privileged" +SYSCALL_GROUPING="" +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - unix_chkpwd + At a minimum, the audit system should collect the execution of +privileged commands for all users and root. If the auditd daemon is +configured to use the augenrules program to read audit rules during +daemon startup (the default), add a line of the following form to a file with +suffix .rules in the directory /etc/audit/rules.d: +-a always,exit -F path=/usr/sbin/unix_chkpwd -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add a line of the following +form to /etc/audit/audit.rules: +-a always,exit -F path=/usr/sbin/unix_chkpwd -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + 1 + 12 + 13 + 14 + 15 + 16 + 2 + 3 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + BAI03.05 + DSS01.03 + DSS03.05 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 6.1 + SR 6.2 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.14.2.7 + A.15.2.1 + A.15.2.2 + CIP-004-6 R2.2.2 + CIP-004-6 R2.2.3 + CIP-007-3 R.1.3 + CIP-007-3 R5 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.3 + CIP-007-3 R5.2.1 + CIP-007-3 R5.2.3 + CIP-007-3 R6.5 + AC-2(4) + AU-2(d) + AU-3 + AU-3.1 + AU-12(a) + AU-12(c) + AU-12.1(ii) + AU-12.1(iv) + AC-6(9) + CM-6(a) + MA-4(1)(a) + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.PT-1 + FAU_GEN.1.1.c + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + SRG-OS-000471-VMM-001910 + Misuse of privileged functions, either intentionally or unintentionally by +authorized users, or by unauthorized external entities that have compromised system accounts, +is a serious and ongoing concern and can have significant adverse impacts on organizations. +Auditing the use of privileged functions is one way to detect such misuse and identify +the risk from insider and advanced persistent threats. + +Privileged programs are subject to escalation-of-privilege attacks, +which attempt to subvert their normal role of providing some necessary but +limited capability. As such, motivation exists to monitor these programs for +unusual activity. + CCE-83768-2 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83768-2 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(a) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-12.1(ii) + - NIST-800-53-AU-12.1(iv) + - NIST-800-53-AU-2(d) + - NIST-800-53-AU-3 + - NIST-800-53-AU-3.1 + - NIST-800-53-CM-6(a) + - NIST-800-53-MA-4(1)(a) + - audit_rules_privileged_commands_unix_chkpwd + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Perform remediation of Audit rules for /usr/sbin/unix_chkpwd + block: + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/sbin/unix_chkpwd -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules + set_fact: audit_file="/etc/audit/rules.d/privileged.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/sbin/unix_chkpwd -F perm=x + -F auid>=1000 -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/sbin/unix_chkpwd + -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/sbin/unix_chkpwd -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:( + -S |,)\w+)+)( -F path=/usr/sbin/unix_chkpwd -F perm=x -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/sbin/unix_chkpwd + -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83768-2 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(a) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-12.1(ii) + - NIST-800-53-AU-12.1(iv) + - NIST-800-53-AU-2(d) + - NIST-800-53-AU-3 + - NIST-800-53-AU-3.1 + - NIST-800-53-CM-6(a) + - NIST-800-53-MA-4(1)(a) + - audit_rules_privileged_commands_unix_chkpwd + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +ACTION_ARCH_FILTERS="-a always,exit" +OTHER_FILTERS="-F path=/usr/sbin/unix_chkpwd -F perm=x" +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="" +KEY="privileged" +SYSCALL_GROUPING="" +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - unix_update + At a minimum, the audit system should collect the execution of +privileged commands for all users and root. If the auditd daemon is +configured to use the augenrules program to read audit rules during +daemon startup (the default), add a line of the following form to a file with +suffix .rules in the directory /etc/audit/rules.d: +-a always,exit -F path=/usr/sbin/unix_update -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add a line of the following +form to /etc/audit/audit.rules: +-a always,exit -F path=/usr/sbin/unix_update -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000064-GPOS-00033 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + Misuse of privileged functions, either intentionally or unintentionally by +authorized users, or by unauthorized external entities that have compromised system accounts, +is a serious and ongoing concern and can have significant adverse impacts on organizations. +Auditing the use of privileged functions is one way to detect such misuse and identify +the risk from insider and advanced persistent threats. + +Privileged programs are subject to escalation-of-privilege attacks, +which attempt to subvert their normal role of providing some necessary but +limited capability. As such, motivation exists to monitor these programs for +unusual activity. + CCE-89481-6 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-89481-6 + - audit_rules_privileged_commands_unix_update + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Perform remediation of Audit rules for /usr/sbin/unix_update + block: + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/sbin/unix_update -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules + set_fact: audit_file="/etc/audit/rules.d/privileged.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/sbin/unix_update -F perm=x + -F auid>=1000 -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/sbin/unix_update + -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/sbin/unix_update -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:( + -S |,)\w+)+)( -F path=/usr/sbin/unix_update -F perm=x -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/sbin/unix_update + -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-89481-6 + - audit_rules_privileged_commands_unix_update + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +ACTION_ARCH_FILTERS="-a always,exit" +OTHER_FILTERS="-F path=/usr/sbin/unix_update -F perm=x" +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="" +KEY="privileged" +SYSCALL_GROUPING="" +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - userhelper + At a minimum, the audit system should collect the execution of +privileged commands for all users and root. If the auditd daemon is +configured to use the augenrules program to read audit rules during +daemon startup (the default), add a line of the following form to a file with +suffix .rules in the directory /etc/audit/rules.d: +-a always,exit -F path=/usr/sbin/userhelper -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add a line of the following +form to /etc/audit/audit.rules: +-a always,exit -F path=/usr/sbin/userhelper -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + 1 + 12 + 13 + 14 + 15 + 16 + 2 + 3 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + BAI03.05 + DSS01.03 + DSS03.05 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 6.1 + SR 6.2 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.14.2.7 + A.15.2.1 + A.15.2.2 + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.PT-1 + FAU_GEN.1.1.c + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + SRG-OS-000471-VMM-001910 + Misuse of privileged functions, either intentionally or unintentionally by +authorized users, or by unauthorized external entities that have compromised system accounts, +is a serious and ongoing concern and can have significant adverse impacts on organizations. +Auditing the use of privileged functions is one way to detect such misuse and identify +the risk from insider and advanced persistent threats. + +Privileged programs are subject to escalation-of-privilege attacks, +which attempt to subvert their normal role of providing some necessary but +limited capability. As such, motivation exists to monitor these programs for +unusual activity. + CCE-83760-9 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83760-9 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_privileged_commands_userhelper + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Perform remediation of Audit rules for /usr/sbin/userhelper + block: + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/sbin/userhelper -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules + set_fact: audit_file="/etc/audit/rules.d/privileged.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/sbin/userhelper -F perm=x + -F auid>=1000 -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/sbin/userhelper + -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/sbin/userhelper -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:( + -S |,)\w+)+)( -F path=/usr/sbin/userhelper -F perm=x -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/sbin/userhelper + -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83760-9 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_privileged_commands_userhelper + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +ACTION_ARCH_FILTERS="-a always,exit" +OTHER_FILTERS="-F path=/usr/sbin/userhelper -F perm=x" +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="" +KEY="privileged" +SYSCALL_GROUPING="" +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - usermod + At a minimum, the audit system should collect the execution of +privileged commands for all users and root. If the auditd daemon is +configured to use the augenrules program to read audit rules during +daemon startup (the default), add a line of the following form to a file with +suffix .rules in the directory /etc/audit/rules.d: +-a always,exit -F path=/usr/sbin/usermod -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add a line of the following +form to /etc/audit/audit.rules: +-a always,exit -F path=/usr/sbin/usermod -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + CCI-000130 + CCI-000135 + CCI-000169 + CCI-000172 + CCI-002884 + SRG-OS-000037-GPOS-00015 + SRG-OS-000042-GPOS-00020 + SRG-OS-000062-GPOS-00031 + SRG-OS-000392-GPOS-00172 + SRG-OS-000462-GPOS-00206 + SRG-OS-000471-GPOS-00215 + SRG-OS-000466-GPOS-00210 + Misuse of privileged functions, either intentionally or unintentionally by +authorized users, or by unauthorized external entities that have compromised system accounts, +is a serious and ongoing concern and can have significant adverse impacts on organizations. +Auditing the use of privileged functions is one way to detect such misuse and identify +the risk from insider and advanced persistent threats. + +Privileged programs are subject to escalation-of-privilege attacks, +which attempt to subvert their normal role of providing some necessary but +limited capability. As such, motivation exists to monitor these programs for +unusual activity. + CCE-87212-7 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-87212-7 + - audit_rules_privileged_commands_usermod + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Perform remediation of Audit rules for /usr/sbin/usermod + block: + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/sbin/usermod -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules + set_fact: audit_file="/etc/audit/rules.d/privileged.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/sbin/usermod -F perm=x + -F auid>=1000 -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/sbin/usermod -F + perm=x -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/sbin/usermod -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:( + -S |,)\w+)+)( -F path=/usr/sbin/usermod -F perm=x -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/sbin/usermod -F + perm=x -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-87212-7 + - audit_rules_privileged_commands_usermod + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +ACTION_ARCH_FILTERS="-a always,exit" +OTHER_FILTERS="-F path=/usr/sbin/usermod -F perm=x" +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="" +KEY="privileged" +SYSCALL_GROUPING="" +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - usernetctl + At a minimum, the audit system should collect the execution of +privileged commands for all users and root. If the auditd daemon is +configured to use the augenrules program to read audit rules during +daemon startup (the default), add a line of the following form to a file with +suffix .rules in the directory /etc/audit/rules.d: +-a always,exit -F path=/usr/sbin/usernetctl -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add a line of the following +form to /etc/audit/audit.rules: +-a always,exit -F path=/usr/sbin/usernetctl -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + CCI-000172 + CIP-004-6 R2.2.2 + CIP-004-6 R2.2.3 + CIP-007-3 R.1.3 + CIP-007-3 R5 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.3 + CIP-007-3 R5.2.1 + CIP-007-3 R5.2.3 + AC-2(4) + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + FAU_GEN.1.1.c + SRG-OS-000471-VMM-001910 + Misuse of privileged functions, either intentionally or unintentionally by +authorized users, or by unauthorized external entities that have compromised system accounts, +is a serious and ongoing concern and can have significant adverse impacts on organizations. +Auditing the use of privileged functions is one way to detect such misuse and identify +the risk from insider and advanced persistent threats. + +Privileged programs are subject to escalation-of-privilege attacks, +which attempt to subvert their normal role of providing some necessary but +limited capability. As such, motivation exists to monitor these programs for +unusual activity. + - name: Gather the package facts + package_facts: + manager: auto + tags: + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_privileged_commands_usernetctl + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Perform remediation of Audit rules for /usr/sbin/usernetctl + block: + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/sbin/usernetctl -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules + set_fact: audit_file="/etc/audit/rules.d/privileged.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/sbin/usernetctl -F perm=x + -F auid>=1000 -F auid!=unset (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/sbin/usernetctl + -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: [] + syscall_grouping: [] + + - name: Check existence of in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F + path=/usr/sbin/usernetctl -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:( + -S |,)\w+)+)( -F path=/usr/sbin/usernetctl -F perm=x -F auid>=1000 -F auid!=unset + (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/sbin/usernetctl + -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AC-2(4) + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - audit_rules_privileged_commands_usernetctl + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +ACTION_ARCH_FILTERS="-a always,exit" +OTHER_FILTERS="-F path=/usr/sbin/usernetctl -F perm=x" +AUID_FILTERS="-F auid>=1000 -F auid!=unset" +SYSCALL="" +KEY="privileged" +SYSCALL_GROUPING="" +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Records Events that Modify Date and Time Information + Arbitrary changes to the system time can be used to obfuscate +nefarious activities in log files, as well as to confuse network services that +are highly dependent upon an accurate system time. All changes to the system +time should be audited. + + Record attempts to alter time through adjtimex + If the auditd daemon is configured to use the +augenrules program to read audit rules during daemon startup (the +default), add the following line to a file with suffix .rules in the +directory /etc/audit/rules.d: +-a always,exit -F arch=b32 -S adjtimex -F key=audit_time_rules +If the system is 64 bit then also add the following line: +-a always,exit -F arch=b64 -S adjtimex -F key=audit_time_rules +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following line to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S adjtimex -F key=audit_time_rules +If the system is 64 bit then also add the following line: +-a always,exit -F arch=b64 -S adjtimex -F key=audit_time_rules +The -k option allows for the specification of a key in string form that can be +used for better reporting capability through ausearch and aureport. Multiple +system calls can be defined on the same line to save space if desired, but is +not required. See an example of multiple combined syscalls: +-a always,exit -F arch=b64 -S adjtimex,settimeofday -F key=audit_time_rules + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 5.4.1.1 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-001487 + CCI-000169 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + Req-10.4.2.b + Arbitrary changes to the system time can be used to obfuscate +nefarious activities in log files, as well as to confuse network services that +are highly dependent upon an accurate system time (such as sshd). All changes +to the system time should be audited. + CCE-83840-9 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83840-9 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.4.2.b + - audit_rules_time_adjtimex + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Set architecture for audit tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83840-9 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.4.2.b + - audit_rules_time_adjtimex + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Perform remediation of Audit rules for adjtimex for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - adjtimex + syscall_grouping: + - adjtimex + - settimeofday + - stime + + - name: Check existence of adjtimex in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/audit_time_rules.rules + set_fact: audit_file="/etc/audit/rules.d/audit_time_rules.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F key=audit_time_rules + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - adjtimex + syscall_grouping: + - adjtimex + - settimeofday + - stime + + - name: Check existence of adjtimex in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F key=audit_time_rules + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83840-9 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.4.2.b + - audit_rules_time_adjtimex + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Perform remediation of Audit rules for adjtimex for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - adjtimex + syscall_grouping: + - adjtimex + - settimeofday + + - name: Check existence of adjtimex in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/audit_time_rules.rules + set_fact: audit_file="/etc/audit/rules.d/audit_time_rules.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F key=audit_time_rules + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - adjtimex + syscall_grouping: + - adjtimex + - settimeofday + - stime + + - name: Check existence of adjtimex in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F key=audit_time_rules + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-83840-9 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.4.2.b + - audit_rules_time_adjtimex + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,{{ -a%20always%2Cexit%20-F%20arch%3Db64%20-S%20adjtimex%20-k%20audit_time_rules%0A-a%20always%2Cexit%20-F%20arch%3Db32%20-S%20adjtimex%20-k%20audit_time_rules%0A }} + mode: 0600 + path: /etc/audit/rules.d/75-syscall-adjtimex.rules + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +for ARCH in "${RULE_ARCHS[@]}" +do + # Create expected audit group and audit rule form for particular system call & architecture + if [ ${ARCH} = "b32" ] + then + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + # stime system call is known at 32-bit arch (see e.g "$ ausyscall i386 stime" 's output) + # so append it to the list of time group system calls to be audited + SYSCALL="adjtimex settimeofday stime" + SYSCALL_GROUPING="adjtimex settimeofday stime" + elif [ ${ARCH} = "b64" ] + then + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + # stime system call isn't known at 64-bit arch (see "$ ausyscall x86_64 stime" 's output) + # therefore don't add it to the list of time group system calls to be audited + SYSCALL="adjtimex settimeofday" + SYSCALL_GROUPING="adjtimex settimeofday" + fi + OTHER_FILTERS="" + AUID_FILTERS="" + KEY="audit_time_rules" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a + unset syscall_grouping + unset syscall_string + unset syscall + unset file_to_edit + unset rule_to_edit + unset rule_syscalls_to_edit + unset other_string + unset auid_string + unset full_rule + + # Load macro arguments into arrays + read -a syscall_a <<< $SYSCALL + read -a syscall_grouping <<< $SYSCALL_GROUPING + + # Create a list of audit *.rules files that should be inspected for presence and correctness + # of a particular audit rule. The scheme is as follows: + # + # ----------------------------------------------------------------------------------------- + # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | + # ----------------------------------------------------------------------------------------- + # auditctl | Doesn't matter | /etc/audit/audit.rules | + # ----------------------------------------------------------------------------------------- + # augenrules | Yes | /etc/audit/rules.d/*.rules | + # augenrules | No | /etc/audit/rules.d/$key.rules | + # ----------------------------------------------------------------------------------------- + # + files_to_inspect=() + + + # If audit tool is 'augenrules', then check if the audit rule is defined + # If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection + # If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection + default_file="/etc/audit/rules.d/$KEY.rules" + # As other_filters may include paths, lets use a different delimiter for it + # The "F" script expression tells sed to print the filenames where the expressions matched + readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) + # Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet + if [ ${#files_to_inspect[@]} -eq "0" ] + then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi + fi + + # After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead + skip=1 + + for audit_file in "${files_to_inspect[@]}" + do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi + done + + if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi + fi + unset syscall_a + unset syscall_grouping + unset syscall_string + unset syscall + unset file_to_edit + unset rule_to_edit + unset rule_syscalls_to_edit + unset other_string + unset auid_string + unset full_rule + + # Load macro arguments into arrays + read -a syscall_a <<< $SYSCALL + read -a syscall_grouping <<< $SYSCALL_GROUPING + + # Create a list of audit *.rules files that should be inspected for presence and correctness + # of a particular audit rule. The scheme is as follows: + # + # ----------------------------------------------------------------------------------------- + # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | + # ----------------------------------------------------------------------------------------- + # auditctl | Doesn't matter | /etc/audit/audit.rules | + # ----------------------------------------------------------------------------------------- + # augenrules | Yes | /etc/audit/rules.d/*.rules | + # augenrules | No | /etc/audit/rules.d/$key.rules | + # ----------------------------------------------------------------------------------------- + # + files_to_inspect=() + + + + # If audit tool is 'auditctl', then add '/etc/audit/audit.rules' + # file to the list of files to be inspected + default_file="/etc/audit/audit.rules" + files_to_inspect+=('/etc/audit/audit.rules' ) + + # After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead + skip=1 + + for audit_file in "${files_to_inspect[@]}" + do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi + done + + if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi + fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Attempts to Alter Time Through clock_settime + If the auditd daemon is configured to use the +augenrules program to read audit rules during daemon startup (the +default), add the following line to a file with suffix .rules in the +directory /etc/audit/rules.d: +-a always,exit -F arch=b32 -S clock_settime -F a0=0x0 -F key=time-change +If the system is 64 bit then also add the following line: +-a always,exit -F arch=b64 -S clock_settime -F a0=0x0 -F key=time-change +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following line to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S clock_settime -F a0=0x0 -F key=time-change +If the system is 64 bit then also add the following line: +-a always,exit -F arch=b64 -S clock_settime -F a0=0x0 -F key=time-change +The -k option allows for the specification of a key in string form that can +be used for better reporting capability through ausearch and aureport. +Multiple system calls can be defined on the same line to save space if +desired, but is not required. See an example of multiple combined syscalls: +-a always,exit -F arch=b64 -S adjtimex,settimeofday -F key=audit_time_rules + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 5.4.1.1 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-001487 + CCI-000169 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + Req-10.4.2.b + Arbitrary changes to the system time can be used to obfuscate +nefarious activities in log files, as well as to confuse network services that +are highly dependent upon an accurate system time (such as sshd). All changes +to the system time should be audited. + CCE-83837-5 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83837-5 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.4.2.b + - audit_rules_time_clock_settime + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Set architecture for audit tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83837-5 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.4.2.b + - audit_rules_time_clock_settime + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Perform remediation of Audit rules for clock_settime for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - clock_settime + syscall_grouping: [] + + - name: Check existence of clock_settime in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a0=0x0 (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/time-change.rules + set_fact: audit_file="/etc/audit/rules.d/time-change.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F a0=0x0 (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F a0=0x0 -F + key=time-change + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - clock_settime + syscall_grouping: [] + + - name: Check existence of clock_settime in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a0=0x0 (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F a0=0x0 (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F a0=0x0 -F + key=time-change + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83837-5 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.4.2.b + - audit_rules_time_clock_settime + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Perform remediation of Audit rules for clock_settime for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - clock_settime + syscall_grouping: [] + + - name: Check existence of clock_settime in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a0=0x0 (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/time-change.rules + set_fact: audit_file="/etc/audit/rules.d/time-change.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( -F a0=0x0 (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F a0=0x0 -F + key=time-change + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - clock_settime + syscall_grouping: [] + + - name: Check existence of clock_settime in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* -F a0=0x0 (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( -F a0=0x0 (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F a0=0x0 -F + key=time-change + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-83837-5 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.4.2.b + - audit_rules_time_clock_settime + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,{{ -a%20always%2Cexit%20-F%20arch%3Db64%20-S%20clock_settime%20-F%20a0%3D0x0%20-k%20time-change%0A-a%20always%2Cexit%20-F%20arch%3Db32%20-S%20clock_settime%20-F%20a0%3D0x0%20-k%20time-change%0A }} + mode: 0600 + path: /etc/audit/rules.d/75-syscall-clock-settime.rules + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# First perform the remediation of the syscall rule +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +for ARCH in "${RULE_ARCHS[@]}" +do + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + OTHER_FILTERS="-F a0=0x0" + AUID_FILTERS="" + SYSCALL="clock_settime" + KEY="time-change" + SYSCALL_GROUPING="" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + +# If audit tool is 'augenrules', then check if the audit rule is defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection +# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection +default_file="/etc/audit/rules.d/$KEY.rules" +# As other_filters may include paths, lets use a different delimiter for it +# The "F" script expression tells sed to print the filenames where the expressions matched +readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) +# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet +if [ ${#files_to_inspect[@]} -eq "0" ] +then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi +fi + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi + unset syscall_a +unset syscall_grouping +unset syscall_string +unset syscall +unset file_to_edit +unset rule_to_edit +unset rule_syscalls_to_edit +unset other_string +unset auid_string +unset full_rule + +# Load macro arguments into arrays +read -a syscall_a <<< $SYSCALL +read -a syscall_grouping <<< $SYSCALL_GROUPING + +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +# +files_to_inspect=() + + + +# If audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# file to the list of files to be inspected +default_file="/etc/audit/audit.rules" +files_to_inspect+=('/etc/audit/audit.rules' ) + +# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead +skip=1 + +for audit_file in "${files_to_inspect[@]}" +do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi +done + +if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record attempts to alter time through settimeofday + If the auditd daemon is configured to use the +augenrules program to read audit rules during daemon startup (the +default), add the following line to a file with suffix .rules in the +directory /etc/audit/rules.d: +-a always,exit -F arch=b32 -S settimeofday -F key=audit_time_rules +If the system is 64 bit then also add the following line: +-a always,exit -F arch=b64 -S settimeofday -F key=audit_time_rules +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following line to +/etc/audit/audit.rules file: +-a always,exit -F arch=b32 -S settimeofday -F key=audit_time_rules +If the system is 64 bit then also add the following line: +-a always,exit -F arch=b64 -S settimeofday -F key=audit_time_rules +The -k option allows for the specification of a key in string form that can be +used for better reporting capability through ausearch and aureport. Multiple +system calls can be defined on the same line to save space if desired, but is +not required. See an example of multiple combined syscalls: +-a always,exit -F arch=b64 -S adjtimex,settimeofday -F key=audit_time_rules + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 5.4.1.1 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-001487 + CCI-000169 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + Req-10.4.2.b + Arbitrary changes to the system time can be used to obfuscate +nefarious activities in log files, as well as to confuse network services that +are highly dependent upon an accurate system time (such as sshd). All changes +to the system time should be audited. + CCE-83836-7 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83836-7 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.4.2.b + - audit_rules_time_settimeofday + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Set architecture for audit tasks + set_fact: + audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }} + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83836-7 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.4.2.b + - audit_rules_time_settimeofday + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Perform remediation of Audit rules for settimeofday for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - settimeofday + syscall_grouping: + - adjtimex + - settimeofday + - stime + + - name: Check existence of settimeofday in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/audit_time_rules.rules + set_fact: audit_file="/etc/audit/rules.d/audit_time_rules.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F key=audit_time_rules + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - settimeofday + syscall_grouping: + - adjtimex + - settimeofday + - stime + + - name: Check existence of settimeofday in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F key=audit_time_rules + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83836-7 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.4.2.b + - audit_rules_time_settimeofday + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Perform remediation of Audit rules for settimeofday for x86_64 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - settimeofday + syscall_grouping: + - adjtimex + - settimeofday + - stime + + - name: Check existence of settimeofday in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/audit_time_rules.rules + set_fact: audit_file="/etc/audit/rules.d/audit_time_rules.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F key=audit_time_rules + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - settimeofday + syscall_grouping: + - adjtimex + - settimeofday + - stime + + - name: Check existence of settimeofday in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F key=audit_time_rules + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - audit_arch == "b64" + tags: + - CCE-83836-7 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.4.2.b + - audit_rules_time_settimeofday + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,{{ -a%20always%2Cexit%20-F%20arch%3Db64%20-S%20settimeofday%20-k%20audit_time_rules%0A-a%20always%2Cexit%20-F%20arch%3Db32%20-S%20settimeofday%20-k%20audit_time_rules%0A }} + mode: 0600 + path: /etc/audit/rules.d/75-syscall-settimeofday.rules + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +for ARCH in "${RULE_ARCHS[@]}" +do + # Create expected audit group and audit rule form for particular system call & architecture + if [ ${ARCH} = "b32" ] + then + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + # stime system call is known at 32-bit arch (see e.g "$ ausyscall i386 stime" 's output) + # so append it to the list of time group system calls to be audited + SYSCALL="adjtimex settimeofday stime" + SYSCALL_GROUPING="adjtimex settimeofday stime" + elif [ ${ARCH} = "b64" ] + then + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + # stime system call isn't known at 64-bit arch (see "$ ausyscall x86_64 stime" 's output) + # therefore don't add it to the list of time group system calls to be audited + SYSCALL="adjtimex settimeofday" + SYSCALL_GROUPING="adjtimex settimeofday" + fi + OTHER_FILTERS="" + AUID_FILTERS="" + KEY="audit_time_rules" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a + unset syscall_grouping + unset syscall_string + unset syscall + unset file_to_edit + unset rule_to_edit + unset rule_syscalls_to_edit + unset other_string + unset auid_string + unset full_rule + + # Load macro arguments into arrays + read -a syscall_a <<< $SYSCALL + read -a syscall_grouping <<< $SYSCALL_GROUPING + + # Create a list of audit *.rules files that should be inspected for presence and correctness + # of a particular audit rule. The scheme is as follows: + # + # ----------------------------------------------------------------------------------------- + # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | + # ----------------------------------------------------------------------------------------- + # auditctl | Doesn't matter | /etc/audit/audit.rules | + # ----------------------------------------------------------------------------------------- + # augenrules | Yes | /etc/audit/rules.d/*.rules | + # augenrules | No | /etc/audit/rules.d/$key.rules | + # ----------------------------------------------------------------------------------------- + # + files_to_inspect=() + + + # If audit tool is 'augenrules', then check if the audit rule is defined + # If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection + # If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection + default_file="/etc/audit/rules.d/$KEY.rules" + # As other_filters may include paths, lets use a different delimiter for it + # The "F" script expression tells sed to print the filenames where the expressions matched + readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) + # Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet + if [ ${#files_to_inspect[@]} -eq "0" ] + then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi + fi + + # After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead + skip=1 + + for audit_file in "${files_to_inspect[@]}" + do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi + done + + if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi + fi + unset syscall_a + unset syscall_grouping + unset syscall_string + unset syscall + unset file_to_edit + unset rule_to_edit + unset rule_syscalls_to_edit + unset other_string + unset auid_string + unset full_rule + + # Load macro arguments into arrays + read -a syscall_a <<< $SYSCALL + read -a syscall_grouping <<< $SYSCALL_GROUPING + + # Create a list of audit *.rules files that should be inspected for presence and correctness + # of a particular audit rule. The scheme is as follows: + # + # ----------------------------------------------------------------------------------------- + # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | + # ----------------------------------------------------------------------------------------- + # auditctl | Doesn't matter | /etc/audit/audit.rules | + # ----------------------------------------------------------------------------------------- + # augenrules | Yes | /etc/audit/rules.d/*.rules | + # augenrules | No | /etc/audit/rules.d/$key.rules | + # ----------------------------------------------------------------------------------------- + # + files_to_inspect=() + + + + # If audit tool is 'auditctl', then add '/etc/audit/audit.rules' + # file to the list of files to be inspected + default_file="/etc/audit/audit.rules" + files_to_inspect+=('/etc/audit/audit.rules' ) + + # After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead + skip=1 + + for audit_file in "${files_to_inspect[@]}" + do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi + done + + if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi + fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Attempts to Alter Time Through stime + If the auditd daemon is configured to use the +augenrules program to read audit rules during daemon startup (the +default), add the following line to a file with suffix .rules in the +directory /etc/audit/rules.d for both 32 bit and 64 bit systems: +-a always,exit -F arch=b32 -S stime -F key=audit_time_rules +Since the 64 bit version of the "stime" system call is not defined in the audit +lookup table, the corresponding "-F arch=b64" form of this rule is not expected +to be defined on 64 bit systems (the aforementioned "-F arch=b32" stime rule +form itself is sufficient for both 32 bit and 64 bit systems). If the +auditd daemon is configured to use the auditctl utility to +read audit rules during daemon startup, add the following line to +/etc/audit/audit.rules file for both 32 bit and 64 bit systems: +-a always,exit -F arch=b32 -S stime -F key=audit_time_rules +Since the 64 bit version of the "stime" system call is not defined in the audit +lookup table, the corresponding "-F arch=b64" form of this rule is not expected +to be defined on 64 bit systems (the aforementioned "-F arch=b32" stime rule +form itself is sufficient for both 32 bit and 64 bit systems). The -k option +allows for the specification of a key in string form that can be used for +better reporting capability through ausearch and aureport. Multiple system +calls can be defined on the same line to save space if desired, but is not +required. See an example of multiple combined system calls: +-a always,exit -F arch=b64 -S adjtimex,settimeofday -F key=audit_time_rules + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 5.4.1.1 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-001487 + CCI-000169 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + Req-10.4.2.b + Arbitrary changes to the system time can be used to obfuscate +nefarious activities in log files, as well as to confuse network services that +are highly dependent upon an accurate system time (such as sshd). All changes +to the system time should be audited. + CCE-83835-9 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83835-9 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.4.2.b + - audit_rules_time_stime + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Perform remediation of Audit rules for stime syscall for x86 platform + block: + + - name: Declare list of syscalls + set_fact: + syscalls: + - stime + syscall_grouping: + - adjtimex + - settimeofday + - stime + + - name: Check existence of stime in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* (-k\s+|-F\s+key=)\S+\s*$ + patterns: '*.rules' + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Reset syscalls found per file + set_fact: + syscalls_per_file: {} + found_paths_dict: {} + + - name: Declare syscalls found per file + set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path + :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}" + loop: '{{ find_command.results | selectattr(''matched'') | list }}' + + - name: Declare files where syscalls were found + set_fact: found_paths="{{ find_command.results | map(attribute='files') | flatten + | map(attribute='path') | list }}" + + - name: Count occurrences of syscalls in paths + set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item, + 0) }) }}" + loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'') + | list }}' + + - name: Get path with most syscalls + set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value') + | last).key }}" + when: found_paths | length >= 1 + + - name: No file with syscall found, set path to /etc/audit/rules.d/audit_time_rules.rules + set_fact: audit_file="/etc/audit/rules.d/audit_time_rules.rules" + when: found_paths | length == 0 + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file] + | join("|") }}))\b)((?:( -S |,)\w+)+)( (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F key=audit_time_rules + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + + - name: Declare list of syscalls + set_fact: + syscalls: + - stime + syscall_grouping: + - adjtimex + - settimeofday + - stime + + - name: Check existence of stime in /etc/audit/audit.rules + find: + paths: /etc/audit + contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S + |,)\w+)* (-k\s+|-F\s+key=)\S+\s*$ + patterns: audit.rules + register: find_command + loop: '{{ (syscall_grouping + syscalls) | unique }}' + + - name: Set path to /etc/audit/audit.rules + set_fact: audit_file="/etc/audit/audit.rules" + + - name: Declare found syscalls + set_fact: syscalls_found="{{ find_command.results | selectattr('matched') | map(attribute='item') + | list }}" + + - name: Declare missing syscalls + set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found) }}" + + - name: Replace the audit rule in {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | + join("|") }}))\b)((?:( -S |,)\w+)+)( (?:-k |-F key=)\w+) + line: \1\2\3{{ missing_syscalls | join("\3") }}\4 + backrefs: true + state: present + when: syscalls_found | length > 0 and missing_syscalls | length > 0 + + - name: Add the audit rule to {{ audit_file }} + lineinfile: + path: '{{ audit_file }}' + line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F key=audit_time_rules + create: true + mode: o-rwx + state: present + when: syscalls_found | length == 0 + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83835-9 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.4.2.b + - audit_rules_time_stime + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,{{ -a%20always%2Cexit%20-F%20arch%3Db64%20-S%20stime%20-k%20audit_time_rules%0A-a%20always%2Cexit%20-F%20arch%3Db32%20-S%20stime%20-k%20audit_time_rules%0A }} + mode: 0600 + path: /etc/audit/rules.d/75-syscall-stime.rules + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# Retrieve hardware architecture of the underlying system +[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") + +for ARCH in "${RULE_ARCHS[@]}" +do + # Create expected audit group and audit rule form for particular system call & architecture + if [ ${ARCH} = "b32" ] + then + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + # stime system call is known at 32-bit arch (see e.g "$ ausyscall i386 stime" 's output) + # so append it to the list of time group system calls to be audited + SYSCALL="adjtimex settimeofday stime" + SYSCALL_GROUPING="adjtimex settimeofday stime" + elif [ ${ARCH} = "b64" ] + then + ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" + # stime system call isn't known at 64-bit arch (see "$ ausyscall x86_64 stime" 's output) + # therefore don't add it to the list of time group system calls to be audited + SYSCALL="adjtimex settimeofday" + SYSCALL_GROUPING="adjtimex settimeofday" + fi + OTHER_FILTERS="" + AUID_FILTERS="" + KEY="audit_time_rules" + # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' + unset syscall_a + unset syscall_grouping + unset syscall_string + unset syscall + unset file_to_edit + unset rule_to_edit + unset rule_syscalls_to_edit + unset other_string + unset auid_string + unset full_rule + + # Load macro arguments into arrays + read -a syscall_a <<< $SYSCALL + read -a syscall_grouping <<< $SYSCALL_GROUPING + + # Create a list of audit *.rules files that should be inspected for presence and correctness + # of a particular audit rule. The scheme is as follows: + # + # ----------------------------------------------------------------------------------------- + # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | + # ----------------------------------------------------------------------------------------- + # auditctl | Doesn't matter | /etc/audit/audit.rules | + # ----------------------------------------------------------------------------------------- + # augenrules | Yes | /etc/audit/rules.d/*.rules | + # augenrules | No | /etc/audit/rules.d/$key.rules | + # ----------------------------------------------------------------------------------------- + # + files_to_inspect=() + + + # If audit tool is 'augenrules', then check if the audit rule is defined + # If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection + # If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection + default_file="/etc/audit/rules.d/$KEY.rules" + # As other_filters may include paths, lets use a different delimiter for it + # The "F" script expression tells sed to print the filenames where the expressions matched + readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) + # Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet + if [ ${#files_to_inspect[@]} -eq "0" ] + then + file_to_inspect="/etc/audit/rules.d/$KEY.rules" + files_to_inspect=("$file_to_inspect") + if [ ! -e "$file_to_inspect" ] + then + touch "$file_to_inspect" + chmod 0640 "$file_to_inspect" + fi + fi + + # After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead + skip=1 + + for audit_file in "${files_to_inspect[@]}" + do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi + done + + if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi + fi + unset syscall_a + unset syscall_grouping + unset syscall_string + unset syscall + unset file_to_edit + unset rule_to_edit + unset rule_syscalls_to_edit + unset other_string + unset auid_string + unset full_rule + + # Load macro arguments into arrays + read -a syscall_a <<< $SYSCALL + read -a syscall_grouping <<< $SYSCALL_GROUPING + + # Create a list of audit *.rules files that should be inspected for presence and correctness + # of a particular audit rule. The scheme is as follows: + # + # ----------------------------------------------------------------------------------------- + # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | + # ----------------------------------------------------------------------------------------- + # auditctl | Doesn't matter | /etc/audit/audit.rules | + # ----------------------------------------------------------------------------------------- + # augenrules | Yes | /etc/audit/rules.d/*.rules | + # augenrules | No | /etc/audit/rules.d/$key.rules | + # ----------------------------------------------------------------------------------------- + # + files_to_inspect=() + + + + # If audit tool is 'auditctl', then add '/etc/audit/audit.rules' + # file to the list of files to be inspected + default_file="/etc/audit/audit.rules" + files_to_inspect+=('/etc/audit/audit.rules' ) + + # After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead + skip=1 + + for audit_file in "${files_to_inspect[@]}" + do + # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, + # i.e, collect rules that match: + # * the action, list and arch, (2-nd argument) + # * the other filters, (3-rd argument) + # * the auid filters, (4-rd argument) + readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") + + candidate_rules=() + # Filter out rules that have more fields then required. This will remove rules more specific than the required scope + for s_rule in "${similar_rules[@]}" + do + # Strip all the options and fields we know of, + # than check if there was any field left over + extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") + grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") + done + + if [[ ${#syscall_a[@]} -ge 1 ]] + then + # Check if the syscall we want is present in any of the similar existing rules + for rule in "${candidate_rules[@]}" + do + rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) + all_syscalls_found=0 + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { + # A syscall was not found in the candidate rule + all_syscalls_found=1 + } + done + if [[ $all_syscalls_found -eq 0 ]] + then + # We found a rule with all the syscall(s) we want; skip rest of macro + skip=0 + break + fi + + # Check if this rule can be grouped with our target syscall and keep track of it + for syscall_g in "${syscall_grouping[@]}" + do + if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" + then + file_to_edit=${audit_file} + rule_to_edit=${rule} + rule_syscalls_to_edit=${rule_syscalls} + fi + done + done + else + # If there is any candidate rule, it is compliant; skip rest of macro + if [ "${#candidate_rules[@]}" -gt 0 ] + then + skip=0 + fi + fi + + if [ "$skip" -eq 0 ]; then + break + fi + done + + if [ "$skip" -ne 0 ]; then + # We checked all rules that matched the expected resemblance pattern (action, arch & auid) + # At this point we know if we need to either append the $full_rule or group + # the syscall together with an exsiting rule + + # Append the full_rule if it cannot be grouped to any other rule + if [ -z ${rule_to_edit+x} ] + then + # Build full_rule while avoid adding double spaces when other_filters is empty + if [ "${#syscall_a[@]}" -gt 0 ] + then + syscall_string="" + for syscall in "${syscall_a[@]}" + do + syscall_string+=" -S $syscall" + done + fi + other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true + auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true + full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true + echo "$full_rule" >> "$default_file" + chmod o-rwx ${default_file} + else + # Check if the syscalls are declared as a comma separated list or + # as multiple -S parameters + if grep -q -- "," <<< "${rule_syscalls_to_edit}" + then + delimiter="," + else + delimiter=" -S " + fi + new_grouped_syscalls="${rule_syscalls_to_edit}" + for syscall in "${syscall_a[@]}" + do + grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { + # A syscall was not found in the candidate rule + new_grouped_syscalls+="${delimiter}${syscall}" + } + done + + # Group the syscall in the rule + sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" + fi + fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Record Attempts to Alter the localtime File + If the auditd daemon is configured to use the +augenrules program to read audit rules during daemon startup (the default), +add the following line to a file with suffix .rules in the directory +/etc/audit/rules.d: +-w /etc/localtime -p wa -k audit_time_rules +If the auditd daemon is configured to use the auditctl +utility to read audit rules during daemon startup, add the following line to +/etc/audit/audit.rules file: +-w /etc/localtime -p wa -k audit_time_rules +The -k option allows for the specification of a key in string form that can +be used for better reporting capability through ausearch and aureport and +should always be used. + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 5.4.1.1 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI08.02 + DSS01.03 + DSS01.04 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.1.7 + CCI-001487 + CCI-000169 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.2.3.10 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.3.6.6 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.13 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.6 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.6.2.1 + A.6.2.2 + AU-2(d) + AU-12(c) + AC-6(9) + CM-6(a) + DE.AE-3 + DE.AE-5 + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.AC-3 + PR.PT-1 + PR.PT-4 + RS.AN-1 + RS.AN-4 + Req-10.4.2.b + Arbitrary changes to the system time can be used to obfuscate +nefarious activities in log files, as well as to confuse network services that +are highly dependent upon an accurate system time (such as sshd). All changes +to the system time should be audited. + CCE-83839-1 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83839-1 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.4.2.b + - audit_rules_time_watch_localtime + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Check if watch rule for /etc/localtime already exists in /etc/audit/rules.d/ + find: + paths: /etc/audit/rules.d + contains: ^\s*-w\s+/etc/localtime\s+-p\s+wa(\s|$)+ + patterns: '*.rules' + register: find_existing_watch_rules_d + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83839-1 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.4.2.b + - audit_rules_time_watch_localtime + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Search /etc/audit/rules.d for other rules with specified key audit_time_rules + find: + paths: /etc/audit/rules.d + contains: ^.*(?:-F key=|-k\s+)audit_time_rules$ + patterns: '*.rules' + register: find_watch_key + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched + == 0 + tags: + - CCE-83839-1 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.4.2.b + - audit_rules_time_watch_localtime + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Use /etc/audit/rules.d/audit_time_rules.rules as the recipient for the rule + set_fact: + all_files: + - /etc/audit/rules.d/audit_time_rules.rules + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_watch_key.matched is defined and find_watch_key.matched == 0 and find_existing_watch_rules_d.matched + is defined and find_existing_watch_rules_d.matched == 0 + tags: + - CCE-83839-1 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.4.2.b + - audit_rules_time_watch_localtime + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Use matched file as the recipient for the rule + set_fact: + all_files: + - '{{ find_watch_key.files | map(attribute=''path'') | list | first }}' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_watch_key.matched is defined and find_watch_key.matched > 0 and find_existing_watch_rules_d.matched + is defined and find_existing_watch_rules_d.matched == 0 + tags: + - CCE-83839-1 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.4.2.b + - audit_rules_time_watch_localtime + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Add watch rule for /etc/localtime in /etc/audit/rules.d/ + lineinfile: + path: '{{ all_files[0] }}' + line: -w /etc/localtime -p wa -k audit_time_rules + create: true + mode: '0640' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched + == 0 + tags: + - CCE-83839-1 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.4.2.b + - audit_rules_time_watch_localtime + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Check if watch rule for /etc/localtime already exists in /etc/audit/audit.rules + find: + paths: /etc/audit/ + contains: ^\s*-w\s+/etc/localtime\s+-p\s+wa(\s|$)+ + patterns: audit.rules + register: find_existing_watch_audit_rules + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83839-1 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.4.2.b + - audit_rules_time_watch_localtime + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Add watch rule for /etc/localtime in /etc/audit/audit.rules + lineinfile: + line: -w /etc/localtime -p wa -k audit_time_rules + state: present + dest: /etc/audit/audit.rules + create: true + mode: '0640' + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - find_existing_watch_audit_rules.matched is defined and find_existing_watch_audit_rules.matched + == 0 + tags: + - CCE-83839-1 + - CJIS-5.4.1.1 + - NIST-800-171-3.1.7 + - NIST-800-53-AC-6(9) + - NIST-800-53-AU-12(c) + - NIST-800-53-AU-2(d) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.4.2.b + - audit_rules_time_watch_localtime + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,{{ -w%20/etc/localtime%20-p%20wa%20-k%20audit_time_rules%0A }} + mode: 0600 + path: /etc/audit/rules.d/75-etclocaltime-wa-audit_time_rules.rules + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +# Perform the remediation for both possible tools: 'auditctl' and 'augenrules' +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + + +# If the audit tool is 'auditctl', then add '/etc/audit/audit.rules' +# into the list of files to be inspected +files_to_inspect+=('/etc/audit/audit.rules') + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/etc/localtime" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/localtime $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/etc/localtime$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /etc/localtime -p wa -k audit_time_rules" >> "$audit_rules_file" + fi +done +# Create a list of audit *.rules files that should be inspected for presence and correctness +# of a particular audit rule. The scheme is as follows: +# +# ----------------------------------------------------------------------------------------- +# Tool used to load audit rules | Rule already defined | Audit rules file to inspect | +# ----------------------------------------------------------------------------------------- +# auditctl | Doesn't matter | /etc/audit/audit.rules | +# ----------------------------------------------------------------------------------------- +# augenrules | Yes | /etc/audit/rules.d/*.rules | +# augenrules | No | /etc/audit/rules.d/$key.rules | +# ----------------------------------------------------------------------------------------- +files_to_inspect=() + +# If the audit is 'augenrules', then check if rule is already defined +# If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection. +# If rule isn't defined, add '/etc/audit/rules.d/audit_time_rules.rules' to list of files for inspection. +readarray -t matches < <(grep -HP "[\s]*-w[\s]+/etc/localtime" /etc/audit/rules.d/*.rules) + +# For each of the matched entries +for match in "${matches[@]}" +do + # Extract filepath from the match + rulesd_audit_file=$(echo $match | cut -f1 -d ':') + # Append that path into list of files for inspection + files_to_inspect+=("$rulesd_audit_file") +done +# Case when particular audit rule isn't defined yet +if [ "${#files_to_inspect[@]}" -eq "0" ] +then + # Append '/etc/audit/rules.d/audit_time_rules.rules' into list of files for inspection + key_rule_file="/etc/audit/rules.d/audit_time_rules.rules" + # If the audit_time_rules.rules file doesn't exist yet, create it with correct permissions + if [ ! -e "$key_rule_file" ] + then + touch "$key_rule_file" + chmod 0640 "$key_rule_file" + fi + files_to_inspect+=("$key_rule_file") +fi + +# Finally perform the inspection and possible subsequent audit rule +# correction for each of the files previously identified for inspection +for audit_rules_file in "${files_to_inspect[@]}" +do + # Check if audit watch file system object rule for given path already present + if grep -q -P -- "^[\s]*-w[\s]+/etc/localtime" "$audit_rules_file" + then + # Rule is found => verify yet if existing rule definition contains + # all of the required access type bits + + # Define BRE whitespace class shortcut + sp="[[:space:]]" + # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule + current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/localtime $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") + # Split required access bits string into characters array + # (to check bit's presence for one bit at a time) + for access_bit in $(echo "wa" | grep -o .) + do + # For each from the required access bits (e.g. 'w', 'a') check + # if they are already present in current access bits for rule. + # If not, append that bit at the end + if ! grep -q "$access_bit" <<< "$current_access_bits" + then + # Concatenate the existing mask with the missing bit + current_access_bits="$current_access_bits$access_bit" + fi + done + # Propagate the updated rule's access bits (original + the required + # ones) back into the /etc/audit/audit.rules file for that rule + sed -i "s#\($sp*-w$sp\+/etc/localtime$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" + else + # Rule isn't present yet. Append it at the end of $audit_rules_file file + # with proper key + + echo "-w /etc/localtime -p wa -k audit_time_rules" >> "$audit_rules_file" + fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + + Configure auditd Data Retention + The audit system writes data to /var/log/audit/audit.log. By default, +auditd rotates 5 logs by size (6MB), retaining a maximum of 30MB of +data in total, and refuses to write entries when the disk is too +full. This minimizes the risk of audit data filling its partition +and impacting other services. This also minimizes the risk of the audit +daemon temporarily disabling the system if it cannot write audit log (which +it can be configured to do). + +For a busy +system or a system which is thoroughly auditing system activity, the default settings +for data retention may be + insufficient. The log file size needed will depend heavily on what types +of events are being audited. First configure auditing to log all the events of +interest. Then monitor the log size manually for awhile to determine what file +size will allow you to keep the required data for the correct time period. + +Using a dedicated partition for /var/log/audit prevents the +auditd logs from disrupting system functionality if they fill, and, +more importantly, prevents other activity in /var from filling the +partition and stopping the audit trail. (The audit logs are size-limited and +therefore unlikely to grow without bound unless configured to do so.) Some +machines may have requirements that no actions occur which cannot be audited. +If this is the case, then auditd can be configured to halt the machine +if it runs out of space. Note: Since older logs are rotated, +configuring auditd this way does not prevent older logs from being +rotated away before they can be viewed. + +If your system is configured to halt when logging cannot be performed, make +sure this can never happen under normal circumstances! Ensure that +/var/log/audit is on its own partition, and that this partition is +larger than the maximum amount of data auditd will retain +normally. + + + Action for audispd to take when disk is full + The setting for disk_full_action in /etc/audisp/audisp-remote.conf + single + exec + halt + single + suspend + syslog + warn_once + stop + + + Action for audispd to take when network fails + The setting for network_failure_action in /etc/audisp/audisp-remote.conf + single + exec + halt + single + suspend + syslog + warn_once + stop + ignore + + + Remote server for audispd to send audit records + +The setting for remote_server in /etc/audisp/audisp-remote.conf + logcollector + + + Account for auditd to send email when actions occurs + The setting for action_mail_acct in /etc/audit/auditd.conf + admin + root + root + + + Action for auditd to take when disk space is low + The setting for admin_space_left_action in /etc/audit/auditd.conf + single + email + exec + halt + single + suspend + syslog + rotate + ignore + + + Action for auditd to take when disk errors + 'The setting for disk_error_action in /etc/audit/auditd.conf, if multiple +values are allowed write them separated by pipes as in "syslog|single|halt", +for remediations the first value will be taken' + single + exec + halt + single + suspend + syslog + ignore + syslog|single|halt + syslog|single|halt + + + Action for auditd to take when disk is full + 'The setting for disk_full_action in /etc/audit/auditd.conf, if multiple +values are allowed write them separated by pipes as in "syslog|single|halt", +for remediations the first value will be taken' + single + exec + halt + single + suspend + syslog + ignore + rotate + syslog|single|halt + syslog|single|halt + + + Auditd priority for flushing data to disk + The setting for flush in /etc/audit/auditd.conf + data + data + incremental + incremental_async + none + sync + + + Number of Record to Retain Before Flushing to Disk + The setting for freq in /etc/audit/auditd.conf + 50 + 100 + 50 + + + Maximum audit log file size for auditd + The setting for max_log_file in /etc/audit/auditd.conf + 1 + 10 + 20 + 5 + 6 + 6 + + + Action for auditd to take when log files reach their maximum size + The setting for max_log_file_action in /etc/audit/auditd.conf. The following options are available: +ignore - audit daemon does nothing. +syslog - audit daemon will issue a warning to syslog. +suspend - audit daemon will stop writing records to the disk. +rotate - audit daemon will rotate logs in the same convention used by logrotate. +keep_logs - similar to rotate but prevents audit logs to be overwritten. May trigger space_left_action if volume is full. + rotate + keep_logs + rotate + suspend + syslog + ignore + + + Number of log files for auditd to retain + The setting for num_logs in /etc/audit/auditd.conf + 0 + 1 + 2 + 3 + 4 + 5 + 10 + 20 + 50 + 100 + 5 + + + Size remaining in disk space before prompting space_left_action + The setting for space_left (MB) in /etc/audit/auditd.conf + 1000 + 100 + 250 + 500 + 750 + 100 + + + Action for auditd to take when disk space just starts to run low + The setting for space_left_action in /etc/audit/auditd.conf + email + email + exec + halt + single + suspend + syslog + rotate + ignore + + + The percentage remaining in disk space before prompting space_left_action + The setting for space_left as a percentage in /etc/audit/auditd.conf + 25 + 50 + 75 + 25 + + + Configure audispd Plugin To Send Logs To Remote Server + Configure the audispd plugin to off-load audit records onto a different +system or media from the system being audited. + +Set the remote_server option in /etc/audit/audisp-remote.conf +with an IP address or hostname of the system that the audispd plugin should +send audit records to. For example +remote_server = + CCI-001851 + FAU_GEN.1.1.c + SRG-OS-000342-GPOS-00133 + SRG-OS-000479-GPOS-00224 + SRG-OS-000051-VMM-000230 + SRG-OS-000058-VMM-000270 + SRG-OS-000059-VMM-000280 + SRG-OS-000479-VMM-001990 + SRG-OS-000479-VMM-001990 + Information stored in one location is vulnerable to accidental or incidental +deletion or alteration.Off-loading is a common process in information systems +with limited audit storage capacity. + CCE-89900-5 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-89900-5 + - auditd_audispd_configure_remote_server + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed +- name: XCCDF Value var_audispd_remote_server # promote to variable + set_fact: + var_audispd_remote_server: !!str + tags: + - always + +- name: Make sure that a remote server is configured for Audispd + lineinfile: + path: /etc/audit/audisp-remote.conf + line: remote_server = {{ var_audispd_remote_server }} + regexp: ^\s*remote_server\s*=.*$ + create: true + state: present + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-89900-5 + - auditd_audispd_configure_remote_server + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +var_audispd_remote_server='' + + +AUDITCONFIG=/etc/audit/audisp-remote.conf + + + +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "$AUDITCONFIG"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^remote_server") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$var_audispd_remote_server" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^remote_server\\>" "$AUDITCONFIG"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^remote_server\\>.*/$escaped_formatted_output/gi" "$AUDITCONFIG" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-89900-5" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "$AUDITCONFIG" >> "$AUDITCONFIG" + printf '%s\n' "$formatted_output" >> "$AUDITCONFIG" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Configure a Sufficiently Large Partition for Audit Logs + The Red Hat Enterprise Linux 9 operating system must allocate audit record storage +capacity to store at least one weeks worth of audit records when audit +records are not immediately sent to a central audit record storage +facility. + +The partition size needed to capture a week's worth of audit records is +based on the activity level of the system and the total storage capacity +available. In normal circumstances, 10.0 GB of storage space for audit +records will be sufficient. + +Determine which partition the audit records are being written to with the +following command: + +$ sudo grep log_file /etc/audit/auditd.conf +log_file = /var/log/audit/audit.log + +Check the size of the partition that audit records are written to with the +following command: + +$ sudo df -h /var/log/audit/ +/dev/sda2 24G 10.4G 13.6G 43% /var/log/audit + CCI-001849 + SRG-OS-000341-GPOS-00132 + SRG-OS-000342-GPOS-00133 + Information stored in one location is vulnerable to accidental or incidental +deletion or alteration. Off-loading is a common process in information +systems with limited audit storage capacity. + + CCE-88173-0 + + + + + + Configure audispd's Plugin disk_full_action When Disk Is Full + Configure the action the operating system takes if the disk the audit records +are written to becomes full. Edit the file /etc/audit/audisp-remote.conf. +Add or modify the following line, substituting ACTION appropriately: +disk_full_action = ACTION +Set this value to single to cause the system to switch to single user +mode for corrective action. Acceptable values also include syslog and +halt. For certain systems, the need for availability +outweighs the need to log all actions, and a different setting should be +determined. + CCI-001851 + AU-5(b) + AU-5(2) + AU-5(1) + AU-5(4) + CM-6(a) + SRG-OS-000342-GPOS-00133 + SRG-OS-000479-GPOS-00224 + Taking appropriate action in case of a filled audit storage volume will +minimize the possibility of losing audit records. + CCE-88477-5 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-88477-5 + - NIST-800-53-AU-5(1) + - NIST-800-53-AU-5(2) + - NIST-800-53-AU-5(4) + - NIST-800-53-AU-5(b) + - NIST-800-53-CM-6(a) + - auditd_audispd_disk_full_action + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed +- name: XCCDF Value var_audispd_disk_full_action # promote to variable + set_fact: + var_audispd_disk_full_action: !!str + tags: + - always + +- name: Make sure that disk full action is configured for Audispd + lineinfile: + path: /etc/audit/audisp-remote.conf + line: disk_full_action = {{ var_audispd_disk_full_action }} + regexp: ^\s*disk_full_action\s*=.*$ + create: true + state: present + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-88477-5 + - NIST-800-53-AU-5(1) + - NIST-800-53-AU-5(2) + - NIST-800-53-AU-5(4) + - NIST-800-53-AU-5(b) + - NIST-800-53-CM-6(a) + - auditd_audispd_disk_full_action + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +var_audispd_disk_full_action='' + + +AUDITCONFIG=/etc/audit/audisp-remote.conf + +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "$AUDITCONFIG"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^disk_full_action") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$var_audispd_disk_full_action" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^disk_full_action\\>" "$AUDITCONFIG"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^disk_full_action\\>.*/$escaped_formatted_output/gi" "$AUDITCONFIG" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-88477-5" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "$AUDITCONFIG" >> "$AUDITCONFIG" + printf '%s\n' "$formatted_output" >> "$AUDITCONFIG" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Encrypt Audit Records Sent With audispd Plugin + Configure the operating system to encrypt the transfer of off-loaded audit +records onto a different system or media from the system being audited. + +Uncomment the enable_krb5 option in /etc/audit/audisp-remote.conf, +and set it with the following line: +enable_krb5 = yes + CCI-001851 + AU-9(3) + CM-6(a) + FAU_GEN.1.1.c + SRG-OS-000342-GPOS-00133 + SRG-OS-000479-GPOS-00224 + Information stored in one location is vulnerable to accidental or incidental deletion +or alteration. Off-loading is a common process in information systems with limited +audit storage capacity. + CCE-86621-0 + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +AUDISP_REMOTE_CONFIG="/etc/audit/audisp-remote.conf" + +option="^enable_krb5" +value="yes" + + +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "$AUDISP_REMOTE_CONFIG"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "$option") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$value" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "$option\\>" "$AUDISP_REMOTE_CONFIG"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/$option\\>.*/$escaped_formatted_output/gi" "$AUDISP_REMOTE_CONFIG" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-86621-0" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "$AUDISP_REMOTE_CONFIG" >> "$AUDISP_REMOTE_CONFIG" + printf '%s\n' "$formatted_output" >> "$AUDISP_REMOTE_CONFIG" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure audispd's Plugin network_failure_action On Network Failure + Configure the action the operating system takes if there is an error sending +audit records to a remote system. Edit the file /etc/audit/audisp-remote.conf. +Add or modify the following line, substituting ACTION appropriately: +network_failure_action = ACTION +Set this value to single to cause the system to switch to single user +mode for corrective action. Acceptable values also include syslog and +halt. For certain systems, the need for availability +outweighs the need to log all actions, and a different setting should be +determined. +This profile configures the action to be . + CCI-001851 + AU-5(b) + AU-5(2) + AU-5(1) + AU-5(4) + CM-6(a) + SRG-OS-000342-GPOS-00133 + SRG-OS-000479-GPOS-00224 + Taking appropriate action when there is an error sending audit records to a +remote system will minimize the possibility of losing audit records. + CCE-90187-6 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-90187-6 + - NIST-800-53-AU-5(1) + - NIST-800-53-AU-5(2) + - NIST-800-53-AU-5(4) + - NIST-800-53-AU-5(b) + - NIST-800-53-CM-6(a) + - auditd_audispd_network_failure_action + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed +- name: XCCDF Value var_audispd_network_failure_action # promote to variable + set_fact: + var_audispd_network_failure_action: !!str + tags: + - always + +- name: Make sure that network failure action is configured for Audispd + lineinfile: + path: /etc/audit/audisp-remote.conf + line: network_failure_action = {{ var_audispd_network_failure_action }} + regexp: ^\s*network_failure_action\s*=.*$ + create: true + state: present + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-90187-6 + - NIST-800-53-AU-5(1) + - NIST-800-53-AU-5(2) + - NIST-800-53-AU-5(4) + - NIST-800-53-AU-5(b) + - NIST-800-53-CM-6(a) + - auditd_audispd_network_failure_action + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +var_audispd_network_failure_action='' + + +AUDITCONFIG=/etc/audit/audisp-remote.conf + +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "$AUDITCONFIG"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^network_failure_action") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$var_audispd_network_failure_action" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^network_failure_action\\>" "$AUDITCONFIG"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^network_failure_action\\>.*/$escaped_formatted_output/gi" "$AUDITCONFIG" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-90187-6" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "$AUDITCONFIG" >> "$AUDITCONFIG" + printf '%s\n' "$formatted_output" >> "$AUDITCONFIG" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Configure auditd to use audispd's syslog plugin + To configure the auditd service to use the +syslog plug-in of the audispd audit event multiplexor, set +the active line in /etc/audit/plugins.d/syslog.conf to yes. +Restart the auditd service: +$ sudo service auditd restart + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 3 + 4 + 5 + 6 + 7 + 8 + 5.4.1.1 + APO11.04 + APO12.06 + BAI03.05 + BAI08.02 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS05.04 + DSS05.07 + MEA02.01 + 3.3.1 + CCI-000136 + 164.308(a)(1)(ii)(D) + 164.308(a)(5)(ii)(B) + 164.308(a)(5)(ii)(C) + 164.308(a)(6)(ii) + 164.308(a)(8) + 164.310(d)(2)(iii) + 164.312(b) + 164.314(a)(2)(i)(C) + 164.314(a)(2)(iii) + 4.2.3.10 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 6.1 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.16.1.4 + A.16.1.5 + A.16.1.7 + AU-4(1) + CM-6(a) + DE.AE-3 + DE.AE-5 + PR.PT-1 + RS.AN-1 + RS.AN-4 + FAU_GEN.1.1.c + Req-10.5.3 + SRG-OS-000479-GPOS-00224 + SRG-OS-000342-GPOS-00133 + SRG-OS-000051-VMM-000230 + SRG-OS-000058-VMM-000270 + SRG-OS-000059-VMM-000280 + SRG-OS-000479-VMM-001990 + SRG-OS-000479-VMM-001990 + The auditd service does not include the ability to send audit +records to a centralized server for management directly. It does, however, +include a plug-in for audit event multiplexor (audispd) to pass audit records +to the local syslog server. + CCE-83695-7 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83695-7 + - CJIS-5.4.1.1 + - NIST-800-171-3.3.1 + - NIST-800-53-AU-4(1) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.3 + - auditd_audispd_syslog_plugin_activated + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: enable syslog plugin + lineinfile: + dest: /etc/audit/plugins.d/syslog.conf + regexp: ^active + line: active = yes + create: true + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83695-7 + - CJIS-5.4.1.1 + - NIST-800-171-3.3.1 + - NIST-800-53-AU-4(1) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.3 + - auditd_audispd_syslog_plugin_activated + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +var_syslog_active="yes" + +AUDISP_SYSLOGCONFIG=/etc/audit/plugins.d/syslog.conf + +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "$AUDISP_SYSLOGCONFIG"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^active") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$var_syslog_active" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^active\\>" "$AUDISP_SYSLOGCONFIG"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^active\\>.*/$escaped_formatted_output/gi" "$AUDISP_SYSLOGCONFIG" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-83695-7" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "$AUDISP_SYSLOGCONFIG" >> "$AUDISP_SYSLOGCONFIG" + printf '%s\n' "$formatted_output" >> "$AUDISP_SYSLOGCONFIG" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure auditd Disk Error Action on Disk Error + The auditd service can be configured to take an action +when there is a disk error. +Edit the file /etc/audit/auditd.conf. Add or modify the following line, +substituting ACTION appropriately: +disk_error_action = ACTION +Set this value to single to cause the system to switch to single-user +mode for corrective action. Acceptable values also include syslog, +exec, single, and halt. For certain systems, the need for availability +outweighs the need to log all actions, and a different setting should be +determined. Details regarding all possible values for ACTION are described in the +auditd.conf man page. + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI04.04 + BAI08.02 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS05.04 + DSS05.07 + MEA02.01 + CCI-000140 + 4.2.3.10 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 6.1 + SR 7.1 + SR 7.2 + A.12.1.3 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.17.2.1 + AU-5(b) + AU-5(2) + AU-5(1) + AU-5(4) + CM-6(a) + DE.AE-3 + DE.AE-5 + PR.DS-4 + PR.PT-1 + RS.AN-1 + RS.AN-4 + SRG-OS-000047-GPOS-00023 + Taking appropriate action in case of disk errors will minimize the possibility of +losing audit records. + CCE-83690-8 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83690-8 + - NIST-800-53-AU-5(1) + - NIST-800-53-AU-5(2) + - NIST-800-53-AU-5(4) + - NIST-800-53-AU-5(b) + - NIST-800-53-CM-6(a) + - auditd_data_disk_error_action + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy +- name: XCCDF Value var_auditd_disk_error_action # promote to variable + set_fact: + var_auditd_disk_error_action: !!str + tags: + - always + +- name: Configure auditd Disk Error Action on Disk Error + lineinfile: + dest: /etc/audit/auditd.conf + line: disk_error_action = {{ var_auditd_disk_error_action.split('|')[0] }} + regexp: ^\s*disk_error_action\s*=\s*.*$ + state: present + create: true + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83690-8 + - NIST-800-53-AU-5(1) + - NIST-800-53-AU-5(2) + - NIST-800-53-AU-5(4) + - NIST-800-53-AU-5(b) + - NIST-800-53-CM-6(a) + - auditd_data_disk_error_action + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,{{ %23%0A%23%20This%20file%20controls%20the%20configuration%20of%20the%20audit%20daemon%0A%23%0A%0Alocal_events%20%3D%20yes%0Awrite_logs%20%3D%20yes%0Alog_file%20%3D%20/var/log/audit/audit.log%0Alog_group%20%3D%20root%0Alog_format%20%3D%20ENRICHED%0Aflush%20%3D%20%7B%7B.var_auditd_flush%7D%7D%0Afreq%20%3D%2050%0Amax_log_file%20%3D%20%7B%7B.var_auditd_max_log_file%7D%7D%0Anum_logs%20%3D%20%7B%7B.var_auditd_num_logs%7D%7D%0Apriority_boost%20%3D%204%0Aname_format%20%3D%20hostname%0A%23%23name%20%3D%20mydomain%0Amax_log_file_action%20%3D%20%7B%7B.var_auditd_max_log_file_action%7D%7D%0Aspace_left%20%3D%20%7B%7B.var_auditd_space_left%7D%7D%0Aspace_left_action%20%3D%20%7B%7B.var_auditd_space_left_action%7D%7D%0Averify_email%20%3D%20yes%0Aaction_mail_acct%20%3D%20%7B%7B.var_auditd_action_mail_acct%7D%7D%0Aadmin_space_left%20%3D%2050%0Aadmin_space_left_action%20%3D%20syslog%0Adisk_full_action%20%3D%20%7B%7B.var_auditd_disk_full_action%7D%7D%0Adisk_error_action%20%3D%20%7B%7B.var_auditd_disk_error_action%7D%7D%0Ause_libwrap%20%3D%20yes%0A%23%23tcp_listen_port%20%3D%2060%0Atcp_listen_queue%20%3D%205%0Atcp_max_per_addr%20%3D%201%0A%23%23tcp_client_ports%20%3D%201024-65535%0Atcp_client_max_idle%20%3D%200%0Atransport%20%3D%20TCP%0Akrb5_principal%20%3D%20auditd%0A%23%23krb5_key_file%20%3D%20/etc/audit/audit.key%0Adistribute_network%20%3D%20no%0Aq_depth%20%3D%20400%0Aoverflow_action%20%3D%20syslog%0Amax_restarts%20%3D%2010%0Aplugin_dir%20%3D%20/etc/audit/plugins.d }} + mode: 0640 + path: /etc/audit/auditd.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +var_auditd_disk_error_action='' + + +# +# If disk_error_action present in /etc/audit/auditd.conf, change value +# to var_auditd_disk_error_action, else +# add "disk_error_action = $var_auditd_disk_error_action" to /etc/audit/auditd.conf +# +var_auditd_disk_error_action="$(echo $var_auditd_disk_error_action | cut -d \| -f 1)" + +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/audit/auditd.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^disk_error_action") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$var_auditd_disk_error_action" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^disk_error_action\\>" "/etc/audit/auditd.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^disk_error_action\\>.*/$escaped_formatted_output/gi" "/etc/audit/auditd.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-83690-8" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/audit/auditd.conf" >> "/etc/audit/auditd.conf" + printf '%s\n' "$formatted_output" >> "/etc/audit/auditd.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Configure auditd Disk Error Action on Disk Error + The auditd service can be configured to take an action +when there is a disk error. +Edit the file /etc/audit/auditd.conf. Add or modify the following line, +substituting ACTION appropriately: +disk_error_action = ACTION +Set this value to single to cause the system to switch to single-user +mode for corrective action. Acceptable values also include syslog, +exec, single, and halt. For certain systems, the need for availability +outweighs the need to log all actions, and a different setting should be +determined. Details regarding all possible values for ACTION are described in the +auditd.conf man page. + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI04.04 + BAI08.02 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS05.04 + DSS05.07 + MEA02.01 + CCI-000140 + 4.2.3.10 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 6.1 + SR 7.1 + SR 7.2 + A.12.1.3 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.17.2.1 + AU-5(b) + AU-5(2) + AU-5(1) + AU-5(4) + CM-6(a) + DE.AE-3 + DE.AE-5 + PR.DS-4 + PR.PT-1 + RS.AN-1 + RS.AN-4 + SRG-OS-000047-GPOS-00023 + Taking appropriate action in case of disk errors will minimize the possibility of +losing audit records. + CCE-88303-3 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-88303-3 + - NIST-800-53-AU-5(1) + - NIST-800-53-AU-5(2) + - NIST-800-53-AU-5(4) + - NIST-800-53-AU-5(b) + - NIST-800-53-CM-6(a) + - auditd_data_disk_error_action_stig + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy +- name: XCCDF Value var_auditd_disk_error_action # promote to variable + set_fact: + var_auditd_disk_error_action: !!str + tags: + - always + +- name: Configure auditd Disk Error Action on Disk Error + lineinfile: + dest: /etc/audit/auditd.conf + line: disk_error_action = {{ var_auditd_disk_error_action }} + regexp: ^\s*disk_error_action\s*=\s*.*$ + state: present + create: true + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-88303-3 + - NIST-800-53-AU-5(1) + - NIST-800-53-AU-5(2) + - NIST-800-53-AU-5(4) + - NIST-800-53-AU-5(b) + - NIST-800-53-CM-6(a) + - auditd_data_disk_error_action_stig + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,{{ %23%0A%23%20This%20file%20controls%20the%20configuration%20of%20the%20audit%20daemon%0A%23%0A%0Alocal_events%20%3D%20yes%0Awrite_logs%20%3D%20yes%0Alog_file%20%3D%20/var/log/audit/audit.log%0Alog_group%20%3D%20root%0Alog_format%20%3D%20ENRICHED%0Aflush%20%3D%20%7B%7B.var_auditd_flush%7D%7D%0Afreq%20%3D%2050%0Amax_log_file%20%3D%20%7B%7B.var_auditd_max_log_file%7D%7D%0Anum_logs%20%3D%20%7B%7B.var_auditd_num_logs%7D%7D%0Apriority_boost%20%3D%204%0Aname_format%20%3D%20hostname%0A%23%23name%20%3D%20mydomain%0Amax_log_file_action%20%3D%20%7B%7B.var_auditd_max_log_file_action%7D%7D%0Aspace_left%20%3D%20%7B%7B.var_auditd_space_left%7D%7D%0Aspace_left_action%20%3D%20%7B%7B.var_auditd_space_left_action%7D%7D%0Averify_email%20%3D%20yes%0Aaction_mail_acct%20%3D%20%7B%7B.var_auditd_action_mail_acct%7D%7D%0Aadmin_space_left%20%3D%2050%0Aadmin_space_left_action%20%3D%20syslog%0Adisk_full_action%20%3D%20%7B%7B.var_auditd_disk_full_action%7D%7D%0Adisk_error_action%20%3D%20%7B%7B.var_auditd_disk_error_action%7D%7D%0Ause_libwrap%20%3D%20yes%0A%23%23tcp_listen_port%20%3D%2060%0Atcp_listen_queue%20%3D%205%0Atcp_max_per_addr%20%3D%201%0A%23%23tcp_client_ports%20%3D%201024-65535%0Atcp_client_max_idle%20%3D%200%0Atransport%20%3D%20TCP%0Akrb5_principal%20%3D%20auditd%0A%23%23krb5_key_file%20%3D%20/etc/audit/audit.key%0Adistribute_network%20%3D%20no%0Aq_depth%20%3D%20400%0Aoverflow_action%20%3D%20syslog%0Amax_restarts%20%3D%2010%0Aplugin_dir%20%3D%20/etc/audit/plugins.d }} + mode: 0640 + path: /etc/audit/auditd.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +var_auditd_disk_error_action='' + + +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/audit/auditd.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^disk_error_action") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$var_auditd_disk_error_action" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^disk_error_action\\>" "/etc/audit/auditd.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^disk_error_action\\>.*/$escaped_formatted_output/gi" "/etc/audit/auditd.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-88303-3" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/audit/auditd.conf" >> "/etc/audit/auditd.conf" + printf '%s\n' "$formatted_output" >> "/etc/audit/auditd.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure auditd Disk Full Action when Disk Space Is Full + The auditd service can be configured to take an action +when disk space is running low but prior to running out of space completely. +Edit the file /etc/audit/auditd.conf. Add or modify the following line, +substituting ACTION appropriately: +disk_full_action = ACTION +Set this value to single to cause the system to switch to single-user +mode for corrective action. Acceptable values also include syslog, + +exec, + +single, and halt. For certain systems, the need for availability +outweighs the need to log all actions, and a different setting should be +determined. Details regarding all possible values for ACTION are described in the +auditd.conf man page. + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI04.04 + BAI08.02 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS05.04 + DSS05.07 + MEA02.01 + CCI-000140 + 4.2.3.10 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 6.1 + SR 7.1 + SR 7.2 + A.12.1.3 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.17.2.1 + AU-5(b) + AU-5(2) + AU-5(1) + AU-5(4) + CM-6(a) + DE.AE-3 + DE.AE-5 + PR.DS-4 + PR.PT-1 + RS.AN-1 + RS.AN-4 + SRG-OS-000047-GPOS-00023 + Taking appropriate action in case of a filled audit storage volume will minimize +the possibility of losing audit records. + CCE-83684-1 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83684-1 + - NIST-800-53-AU-5(1) + - NIST-800-53-AU-5(2) + - NIST-800-53-AU-5(4) + - NIST-800-53-AU-5(b) + - NIST-800-53-CM-6(a) + - auditd_data_disk_full_action + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy +- name: XCCDF Value var_auditd_disk_full_action # promote to variable + set_fact: + var_auditd_disk_full_action: !!str + tags: + - always + +- name: Configure auditd Disk Full Action when Disk Space Is Full + lineinfile: + dest: /etc/audit/auditd.conf + line: disk_full_action = {{ var_auditd_disk_full_action.split('|')[0] }} + regexp: ^\s*disk_full_action\s*=\s*.*$ + state: present + create: true + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83684-1 + - NIST-800-53-AU-5(1) + - NIST-800-53-AU-5(2) + - NIST-800-53-AU-5(4) + - NIST-800-53-AU-5(b) + - NIST-800-53-CM-6(a) + - auditd_data_disk_full_action + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,{{ %23%0A%23%20This%20file%20controls%20the%20configuration%20of%20the%20audit%20daemon%0A%23%0A%0Alocal_events%20%3D%20yes%0Awrite_logs%20%3D%20yes%0Alog_file%20%3D%20/var/log/audit/audit.log%0Alog_group%20%3D%20root%0Alog_format%20%3D%20ENRICHED%0Aflush%20%3D%20%7B%7B.var_auditd_flush%7D%7D%0Afreq%20%3D%2050%0Amax_log_file%20%3D%20%7B%7B.var_auditd_max_log_file%7D%7D%0Anum_logs%20%3D%20%7B%7B.var_auditd_num_logs%7D%7D%0Apriority_boost%20%3D%204%0Aname_format%20%3D%20hostname%0A%23%23name%20%3D%20mydomain%0Amax_log_file_action%20%3D%20%7B%7B.var_auditd_max_log_file_action%7D%7D%0Aspace_left%20%3D%20%7B%7B.var_auditd_space_left%7D%7D%0Aspace_left_action%20%3D%20%7B%7B.var_auditd_space_left_action%7D%7D%0Averify_email%20%3D%20yes%0Aaction_mail_acct%20%3D%20%7B%7B.var_auditd_action_mail_acct%7D%7D%0Aadmin_space_left%20%3D%2050%0Aadmin_space_left_action%20%3D%20syslog%0Adisk_full_action%20%3D%20%7B%7B.var_auditd_disk_full_action%7D%7D%0Adisk_error_action%20%3D%20%7B%7B.var_auditd_disk_error_action%7D%7D%0Ause_libwrap%20%3D%20yes%0A%23%23tcp_listen_port%20%3D%2060%0Atcp_listen_queue%20%3D%205%0Atcp_max_per_addr%20%3D%201%0A%23%23tcp_client_ports%20%3D%201024-65535%0Atcp_client_max_idle%20%3D%200%0Atransport%20%3D%20TCP%0Akrb5_principal%20%3D%20auditd%0A%23%23krb5_key_file%20%3D%20/etc/audit/audit.key%0Adistribute_network%20%3D%20no%0Aq_depth%20%3D%20400%0Aoverflow_action%20%3D%20syslog%0Amax_restarts%20%3D%2010%0Aplugin_dir%20%3D%20/etc/audit/plugins.d }} + mode: 0640 + path: /etc/audit/auditd.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +var_auditd_disk_full_action='' + + +var_auditd_disk_full_action="$(echo $var_auditd_disk_full_action | cut -d \| -f 1)" + +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/audit/auditd.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^disk_full_action") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$var_auditd_disk_full_action" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^disk_full_action\\>" "/etc/audit/auditd.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^disk_full_action\\>.*/$escaped_formatted_output/gi" "/etc/audit/auditd.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-83684-1" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/audit/auditd.conf" >> "/etc/audit/auditd.conf" + printf '%s\n' "$formatted_output" >> "/etc/audit/auditd.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Configure auditd Disk Full Action when Disk Space Is Full + The auditd service can be configured to take an action +when disk space is running low but prior to running out of space completely. +Edit the file /etc/audit/auditd.conf. Add or modify the following line, +substituting ACTION appropriately: +disk_full_action = ACTION +Set this value to single to cause the system to switch to single-user +mode for corrective action. Acceptable values also include syslog, +single, and halt. For certain systems, the need for availability +outweighs the need to log all actions, and a different setting should be +determined. Details regarding all possible values for ACTION are described in the +auditd.conf man page. + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI04.04 + BAI08.02 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS05.04 + DSS05.07 + MEA02.01 + CCI-000140 + 4.2.3.10 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 6.1 + SR 7.1 + SR 7.2 + A.12.1.3 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.17.2.1 + AU-5(b) + AU-5(2) + AU-5(1) + AU-5(4) + CM-6(a) + DE.AE-3 + DE.AE-5 + PR.DS-4 + PR.PT-1 + RS.AN-1 + RS.AN-4 + SRG-OS-000047-GPOS-00023 + Taking appropriate action in case of a filled audit storage volume will minimize +the possibility of losing audit records. + CCE-88336-3 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-88336-3 + - NIST-800-53-AU-5(1) + - NIST-800-53-AU-5(2) + - NIST-800-53-AU-5(4) + - NIST-800-53-AU-5(b) + - NIST-800-53-CM-6(a) + - auditd_data_disk_full_action_stig + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy +- name: XCCDF Value var_auditd_disk_full_action # promote to variable + set_fact: + var_auditd_disk_full_action: !!str + tags: + - always + +- name: Configure auditd Disk Full Action when Disk Space Is Full + lineinfile: + dest: /etc/audit/auditd.conf + line: disk_full_action = {{ var_auditd_disk_full_action }} + regexp: ^\s*disk_full_action\s*=\s*.*$ + state: present + create: true + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-88336-3 + - NIST-800-53-AU-5(1) + - NIST-800-53-AU-5(2) + - NIST-800-53-AU-5(4) + - NIST-800-53-AU-5(b) + - NIST-800-53-CM-6(a) + - auditd_data_disk_full_action_stig + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,{{ %23%0A%23%20This%20file%20controls%20the%20configuration%20of%20the%20audit%20daemon%0A%23%0A%0Alocal_events%20%3D%20yes%0Awrite_logs%20%3D%20yes%0Alog_file%20%3D%20/var/log/audit/audit.log%0Alog_group%20%3D%20root%0Alog_format%20%3D%20ENRICHED%0Aflush%20%3D%20%7B%7B.var_auditd_flush%7D%7D%0Afreq%20%3D%2050%0Amax_log_file%20%3D%20%7B%7B.var_auditd_max_log_file%7D%7D%0Anum_logs%20%3D%20%7B%7B.var_auditd_num_logs%7D%7D%0Apriority_boost%20%3D%204%0Aname_format%20%3D%20hostname%0A%23%23name%20%3D%20mydomain%0Amax_log_file_action%20%3D%20%7B%7B.var_auditd_max_log_file_action%7D%7D%0Aspace_left%20%3D%20%7B%7B.var_auditd_space_left%7D%7D%0Aspace_left_action%20%3D%20%7B%7B.var_auditd_space_left_action%7D%7D%0Averify_email%20%3D%20yes%0Aaction_mail_acct%20%3D%20%7B%7B.var_auditd_action_mail_acct%7D%7D%0Aadmin_space_left%20%3D%2050%0Aadmin_space_left_action%20%3D%20syslog%0Adisk_full_action%20%3D%20%7B%7B.var_auditd_disk_full_action%7D%7D%0Adisk_error_action%20%3D%20%7B%7B.var_auditd_disk_error_action%7D%7D%0Ause_libwrap%20%3D%20yes%0A%23%23tcp_listen_port%20%3D%2060%0Atcp_listen_queue%20%3D%205%0Atcp_max_per_addr%20%3D%201%0A%23%23tcp_client_ports%20%3D%201024-65535%0Atcp_client_max_idle%20%3D%200%0Atransport%20%3D%20TCP%0Akrb5_principal%20%3D%20auditd%0A%23%23krb5_key_file%20%3D%20/etc/audit/audit.key%0Adistribute_network%20%3D%20no%0Aq_depth%20%3D%20400%0Aoverflow_action%20%3D%20syslog%0Amax_restarts%20%3D%2010%0Aplugin_dir%20%3D%20/etc/audit/plugins.d }} + mode: 0640 + path: /etc/audit/auditd.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +var_auditd_disk_full_action='' + + +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/audit/auditd.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^disk_full_action") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$var_auditd_disk_full_action" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^disk_full_action\\>" "/etc/audit/auditd.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^disk_full_action\\>.*/$escaped_formatted_output/gi" "/etc/audit/auditd.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-88336-3" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/audit/auditd.conf" >> "/etc/audit/auditd.conf" + printf '%s\n' "$formatted_output" >> "/etc/audit/auditd.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure auditd mail_acct Action on Low Disk Space + The auditd service can be configured to send email to +a designated account in certain situations. Add or correct the following line +in /etc/audit/auditd.conf to ensure that administrators are notified +via email for those situations: +action_mail_acct = + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 5.4.1.1 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI04.04 + BAI08.02 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS05.04 + DSS05.07 + MEA02.01 + 3.3.1 + CCI-000139 + CCI-001855 + 164.312(a)(2)(ii) + 4.2.3.10 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 6.1 + SR 7.1 + SR 7.2 + A.12.1.3 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.17.2.1 + CIP-003-8 R1.3 + CIP-003-8 R3 + CIP-003-8 R3.1 + CIP-003-8 R3.2 + CIP-003-8 R3.3 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.2.3 + CIP-004-6 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.2 + CIP-007-3 R5.2 + CIP-007-3 R5.3.1 + CIP-007-3 R5.3.2 + CIP-007-3 R5.3.3 + IA-5(1) + AU-5(a) + AU-5(2) + CM-6(a) + DE.AE-3 + DE.AE-5 + PR.DS-4 + PR.PT-1 + RS.AN-1 + RS.AN-4 + Req-10.7.a + SRG-OS-000046-GPOS-00022 + SRG-OS-000343-GPOS-00134 + SRG-OS-000046-VMM-000210 + SRG-OS-000343-VMM-001240 + Email sent to the root account is typically aliased to the +administrators of the system, who can take appropriate action. + CCE-83698-1 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83698-1 + - CJIS-5.4.1.1 + - NIST-800-171-3.3.1 + - NIST-800-53-AU-5(2) + - NIST-800-53-AU-5(a) + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1) + - PCI-DSS-Req-10.7.a + - auditd_data_retention_action_mail_acct + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy +- name: XCCDF Value var_auditd_action_mail_acct # promote to variable + set_fact: + var_auditd_action_mail_acct: !!str + tags: + - always + +- name: Configure auditd mail_acct Action on Low Disk Space + lineinfile: + dest: /etc/audit/auditd.conf + line: action_mail_acct = {{ var_auditd_action_mail_acct }} + state: present + create: true + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83698-1 + - CJIS-5.4.1.1 + - NIST-800-171-3.3.1 + - NIST-800-53-AU-5(2) + - NIST-800-53-AU-5(a) + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1) + - PCI-DSS-Req-10.7.a + - auditd_data_retention_action_mail_acct + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +var_auditd_action_mail_acct='' + + +AUDITCONFIG=/etc/audit/auditd.conf + +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "$AUDITCONFIG"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^action_mail_acct") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$var_auditd_action_mail_acct" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^action_mail_acct\\>" "$AUDITCONFIG"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^action_mail_acct\\>.*/$escaped_formatted_output/gi" "$AUDITCONFIG" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-83698-1" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "$AUDITCONFIG" >> "$AUDITCONFIG" + printf '%s\n' "$formatted_output" >> "$AUDITCONFIG" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Configure auditd admin_space_left Action on Low Disk Space + The auditd service can be configured to take an action +when disk space is running low but prior to running out of space completely. +Edit the file /etc/audit/auditd.conf. Add or modify the following line, +substituting ACTION appropriately: +admin_space_left_action = ACTION +Set this value to single to cause the system to switch to single user +mode for corrective action. Acceptable values also include suspend and +halt. For certain systems, the need for availability +outweighs the need to log all actions, and a different setting should be +determined. Details regarding all possible values for ACTION are described in the +auditd.conf man page. + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 5.4.1.1 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI04.04 + BAI08.02 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS05.04 + DSS05.07 + MEA02.01 + 3.3.1 + CCI-000140 + CCI-001343 + CCI-001855 + 164.312(a)(2)(ii) + 4.2.3.10 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 6.1 + SR 7.1 + SR 7.2 + A.12.1.3 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.17.2.1 + AU-5(b) + AU-5(2) + AU-5(1) + AU-5(4) + CM-6(a) + DE.AE-3 + DE.AE-5 + PR.DS-4 + PR.PT-1 + RS.AN-1 + RS.AN-4 + Req-10.7 + SRG-OS-000343-GPOS-00134 + Administrators should be made aware of an inability to record +audit records. If a separate partition or logical volume of adequate size +is used, running low on space for audit records should never occur. + CCE-83700-5 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83700-5 + - CJIS-5.4.1.1 + - NIST-800-171-3.3.1 + - NIST-800-53-AU-5(1) + - NIST-800-53-AU-5(2) + - NIST-800-53-AU-5(4) + - NIST-800-53-AU-5(b) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.7 + - auditd_data_retention_admin_space_left_action + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy +- name: XCCDF Value var_auditd_admin_space_left_action # promote to variable + set_fact: + var_auditd_admin_space_left_action: !!str + tags: + - always + +- name: Configure auditd admin_space_left Action on Low Disk Space + lineinfile: + dest: /etc/audit/auditd.conf + line: admin_space_left_action = {{ var_auditd_admin_space_left_action }} + regexp: ^\s*admin_space_left_action\s*=\s*.*$ + state: present + create: true + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83700-5 + - CJIS-5.4.1.1 + - NIST-800-171-3.3.1 + - NIST-800-53-AU-5(1) + - NIST-800-53-AU-5(2) + - NIST-800-53-AU-5(4) + - NIST-800-53-AU-5(b) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.7 + - auditd_data_retention_admin_space_left_action + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,{{ %23%0A%23%20This%20file%20controls%20the%20configuration%20of%20the%20audit%20daemon%0A%23%0A%0Alocal_events%20%3D%20yes%0Awrite_logs%20%3D%20yes%0Alog_file%20%3D%20/var/log/audit/audit.log%0Alog_group%20%3D%20root%0Alog_format%20%3D%20ENRICHED%0Aflush%20%3D%20%7B%7B.var_auditd_flush%7D%7D%0Afreq%20%3D%2050%0Amax_log_file%20%3D%20%7B%7B.var_auditd_max_log_file%7D%7D%0Anum_logs%20%3D%20%7B%7B.var_auditd_num_logs%7D%7D%0Apriority_boost%20%3D%204%0Aname_format%20%3D%20hostname%0A%23%23name%20%3D%20mydomain%0Amax_log_file_action%20%3D%20%7B%7B.var_auditd_max_log_file_action%7D%7D%0Aspace_left%20%3D%20%7B%7B.var_auditd_space_left%7D%7D%0Aspace_left_action%20%3D%20%7B%7B.var_auditd_space_left_action%7D%7D%0Averify_email%20%3D%20yes%0Aaction_mail_acct%20%3D%20%7B%7B.var_auditd_action_mail_acct%7D%7D%0Aadmin_space_left%20%3D%2050%0Aadmin_space_left_action%20%3D%20syslog%0Adisk_full_action%20%3D%20%7B%7B.var_auditd_disk_full_action%7D%7D%0Adisk_error_action%20%3D%20%7B%7B.var_auditd_disk_error_action%7D%7D%0Ause_libwrap%20%3D%20yes%0A%23%23tcp_listen_port%20%3D%2060%0Atcp_listen_queue%20%3D%205%0Atcp_max_per_addr%20%3D%201%0A%23%23tcp_client_ports%20%3D%201024-65535%0Atcp_client_max_idle%20%3D%200%0Atransport%20%3D%20TCP%0Akrb5_principal%20%3D%20auditd%0A%23%23krb5_key_file%20%3D%20/etc/audit/audit.key%0Adistribute_network%20%3D%20no%0Aq_depth%20%3D%20400%0Aoverflow_action%20%3D%20syslog%0Amax_restarts%20%3D%2010%0Aplugin_dir%20%3D%20/etc/audit/plugins.d }} + mode: 0640 + path: /etc/audit/auditd.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +var_auditd_admin_space_left_action='' + + +AUDITCONFIG=/etc/audit/auditd.conf + +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "$AUDITCONFIG"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^admin_space_left_action") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$var_auditd_admin_space_left_action" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^admin_space_left_action\\>" "$AUDITCONFIG"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^admin_space_left_action\\>.*/$escaped_formatted_output/gi" "$AUDITCONFIG" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-83700-5" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "$AUDITCONFIG" >> "$AUDITCONFIG" + printf '%s\n' "$formatted_output" >> "$AUDITCONFIG" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Configure auditd flush priority + The auditd service can be configured to +synchronously write audit event data to disk. Add or correct the following +line in /etc/audit/auditd.conf to ensure that audit event data is +fully synchronized with the log files on the disk: +flush = + 1 + 12 + 13 + 14 + 15 + 16 + 2 + 3 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + BAI03.05 + DSS01.03 + DSS03.05 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + 3.3.1 + CCI-001576 + 164.308(a)(1)(ii)(D) + 164.308(a)(3)(ii)(A) + 164.308(a)(5)(ii)(C) + 164.312(a)(2)(i) + 164.312(b) + 164.312(d) + 164.312(e) + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 6.1 + SR 6.2 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.14.2.7 + A.15.2.1 + A.15.2.2 + CIP-004-6 R2.2.3 + CIP-004-6 R3.3 + CIP-007-3 R5.2 + CIP-007-3 R5.3.1 + CIP-007-3 R5.3.2 + CIP-007-3 R5.3.3 + CIP-007-3 R6.5 + AU-11 + CM-6(a) + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.PT-1 + SRG-OS-000480-GPOS-00227 + Audit data should be synchronously written to disk to ensure +log integrity. These parameters assure that all audit event data is fully +synchronized with the log files on the disk. + CCE-83685-8 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83685-8 + - NIST-800-171-3.3.1 + - NIST-800-53-AU-11 + - NIST-800-53-CM-6(a) + - auditd_data_retention_flush + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy +- name: XCCDF Value var_auditd_flush # promote to variable + set_fact: + var_auditd_flush: !!str + tags: + - always + +- name: Configure auditd Flush Priority + lineinfile: + dest: /etc/audit/auditd.conf + regexp: ^\s*flush\s*=\s*.*$ + line: flush = {{ var_auditd_flush }} + state: present + create: true + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83685-8 + - NIST-800-171-3.3.1 + - NIST-800-53-AU-11 + - NIST-800-53-CM-6(a) + - auditd_data_retention_flush + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,{{ %23%0A%23%20This%20file%20controls%20the%20configuration%20of%20the%20audit%20daemon%0A%23%0A%0Alocal_events%20%3D%20yes%0Awrite_logs%20%3D%20yes%0Alog_file%20%3D%20/var/log/audit/audit.log%0Alog_group%20%3D%20root%0Alog_format%20%3D%20ENRICHED%0Aflush%20%3D%20%7B%7B.var_auditd_flush%7D%7D%0Afreq%20%3D%2050%0Amax_log_file%20%3D%20%7B%7B.var_auditd_max_log_file%7D%7D%0Anum_logs%20%3D%20%7B%7B.var_auditd_num_logs%7D%7D%0Apriority_boost%20%3D%204%0Aname_format%20%3D%20hostname%0A%23%23name%20%3D%20mydomain%0Amax_log_file_action%20%3D%20%7B%7B.var_auditd_max_log_file_action%7D%7D%0Aspace_left%20%3D%20%7B%7B.var_auditd_space_left%7D%7D%0Aspace_left_action%20%3D%20%7B%7B.var_auditd_space_left_action%7D%7D%0Averify_email%20%3D%20yes%0Aaction_mail_acct%20%3D%20%7B%7B.var_auditd_action_mail_acct%7D%7D%0Aadmin_space_left%20%3D%2050%0Aadmin_space_left_action%20%3D%20syslog%0Adisk_full_action%20%3D%20%7B%7B.var_auditd_disk_full_action%7D%7D%0Adisk_error_action%20%3D%20%7B%7B.var_auditd_disk_error_action%7D%7D%0Ause_libwrap%20%3D%20yes%0A%23%23tcp_listen_port%20%3D%2060%0Atcp_listen_queue%20%3D%205%0Atcp_max_per_addr%20%3D%201%0A%23%23tcp_client_ports%20%3D%201024-65535%0Atcp_client_max_idle%20%3D%200%0Atransport%20%3D%20TCP%0Akrb5_principal%20%3D%20auditd%0A%23%23krb5_key_file%20%3D%20/etc/audit/audit.key%0Adistribute_network%20%3D%20no%0Aq_depth%20%3D%20400%0Aoverflow_action%20%3D%20syslog%0Amax_restarts%20%3D%2010%0Aplugin_dir%20%3D%20/etc/audit/plugins.d }} + mode: 0640 + path: /etc/audit/auditd.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +var_auditd_flush='' + + +AUDITCONFIG=/etc/audit/auditd.conf + +# if flush is present, flush param edited to var_auditd_flush +# else flush param is defined by var_auditd_flush +# +# the freq param is only used for values 'incremental' and 'incremental_async' and will be +# commented out if flush != incremental or flush != incremental_async +# +# if flush == incremental or flush == incremental_async && freq param is not defined, it +# will be defined as the package-default value of 20 + +grep -q ^flush $AUDITCONFIG && \ + sed -i 's/^flush.*/flush = '"$var_auditd_flush"'/g' $AUDITCONFIG +if ! [ $? -eq 0 ]; then + echo "flush = $var_auditd_flush" >> $AUDITCONFIG +fi + +if ! [ "$var_auditd_flush" == "incremental" ] && ! [ "$var_auditd_flush" == "incremental_async" ]; then + sed -i 's/^freq/##freq/g' $AUDITCONFIG +elif [ "$var_auditd_flush" == "incremental" ] || [ "$var_auditd_flush" == "incremental_async" ]; then + grep -q freq $AUDITCONFIG && \ + sed -i 's/^#\+freq/freq/g' $AUDITCONFIG + if ! [ $? -eq 0 ]; then + echo "freq = 20" >> $AUDITCONFIG + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Configure auditd Max Log File Size + Determine the amount of audit data (in megabytes) +which should be retained in each log file. Edit the file +/etc/audit/auditd.conf. Add or modify the following line, substituting +the correct value of for STOREMB: +max_log_file = STOREMB +Set the value to 6 (MB) or higher for general-purpose systems. +Larger values, of course, +support retention of even more audit data. + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 3 + 4 + 5 + 6 + 7 + 8 + 5.4.1.1 + APO11.04 + APO12.06 + BAI03.05 + BAI08.02 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS05.04 + DSS05.07 + MEA02.01 + 4.2.3.10 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 6.1 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.16.1.4 + A.16.1.5 + A.16.1.7 + CIP-004-6 R2.2.3 + CIP-004-6 R3.3 + CIP-007-3 R5.2 + CIP-007-3 R5.3.1 + CIP-007-3 R5.3.2 + CIP-007-3 R5.3.3 + CIP-007-3 R6.5 + AU-11 + CM-6(a) + DE.AE-3 + DE.AE-5 + PR.PT-1 + RS.AN-1 + RS.AN-4 + Req-10.7 + The total storage for audit log files must be large enough to retain +log information over the period required. This is a function of the maximum +log file size and the number of logs retained. + CCE-83683-3 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83683-3 + - CJIS-5.4.1.1 + - NIST-800-53-AU-11 + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.7 + - auditd_data_retention_max_log_file + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy +- name: XCCDF Value var_auditd_max_log_file # promote to variable + set_fact: + var_auditd_max_log_file: !!str + tags: + - always + +- name: Configure auditd Max Log File Size + lineinfile: + dest: /etc/audit/auditd.conf + regexp: ^\s*max_log_file\s*=\s*.*$ + line: max_log_file = {{ var_auditd_max_log_file }} + state: present + create: true + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83683-3 + - CJIS-5.4.1.1 + - NIST-800-53-AU-11 + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.7 + - auditd_data_retention_max_log_file + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,{{ %23%0A%23%20This%20file%20controls%20the%20configuration%20of%20the%20audit%20daemon%0A%23%0A%0Alocal_events%20%3D%20yes%0Awrite_logs%20%3D%20yes%0Alog_file%20%3D%20/var/log/audit/audit.log%0Alog_group%20%3D%20root%0Alog_format%20%3D%20ENRICHED%0Aflush%20%3D%20%7B%7B.var_auditd_flush%7D%7D%0Afreq%20%3D%2050%0Amax_log_file%20%3D%20%7B%7B.var_auditd_max_log_file%7D%7D%0Anum_logs%20%3D%20%7B%7B.var_auditd_num_logs%7D%7D%0Apriority_boost%20%3D%204%0Aname_format%20%3D%20hostname%0A%23%23name%20%3D%20mydomain%0Amax_log_file_action%20%3D%20%7B%7B.var_auditd_max_log_file_action%7D%7D%0Aspace_left%20%3D%20%7B%7B.var_auditd_space_left%7D%7D%0Aspace_left_action%20%3D%20%7B%7B.var_auditd_space_left_action%7D%7D%0Averify_email%20%3D%20yes%0Aaction_mail_acct%20%3D%20%7B%7B.var_auditd_action_mail_acct%7D%7D%0Aadmin_space_left%20%3D%2050%0Aadmin_space_left_action%20%3D%20syslog%0Adisk_full_action%20%3D%20%7B%7B.var_auditd_disk_full_action%7D%7D%0Adisk_error_action%20%3D%20%7B%7B.var_auditd_disk_error_action%7D%7D%0Ause_libwrap%20%3D%20yes%0A%23%23tcp_listen_port%20%3D%2060%0Atcp_listen_queue%20%3D%205%0Atcp_max_per_addr%20%3D%201%0A%23%23tcp_client_ports%20%3D%201024-65535%0Atcp_client_max_idle%20%3D%200%0Atransport%20%3D%20TCP%0Akrb5_principal%20%3D%20auditd%0A%23%23krb5_key_file%20%3D%20/etc/audit/audit.key%0Adistribute_network%20%3D%20no%0Aq_depth%20%3D%20400%0Aoverflow_action%20%3D%20syslog%0Amax_restarts%20%3D%2010%0Aplugin_dir%20%3D%20/etc/audit/plugins.d }} + mode: 0640 + path: /etc/audit/auditd.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +var_auditd_max_log_file='' + + +AUDITCONFIG=/etc/audit/auditd.conf + +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "$AUDITCONFIG"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^max_log_file") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$var_auditd_max_log_file" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^max_log_file\\>" "$AUDITCONFIG"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^max_log_file\\>.*/$escaped_formatted_output/gi" "$AUDITCONFIG" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-83683-3" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "$AUDITCONFIG" >> "$AUDITCONFIG" + printf '%s\n' "$formatted_output" >> "$AUDITCONFIG" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Configure auditd max_log_file_action Upon Reaching Maximum Log Size + The default action to take when the logs reach their maximum size +is to rotate the log files, discarding the oldest one. To configure the action taken +by auditd, add or correct the line in /etc/audit/auditd.conf: +max_log_file_action = ACTION +Possible values for ACTION are described in the auditd.conf man +page. These include: +ignoresyslogsuspendrotatekeep_logs +Set the ACTION to rotate to ensure log rotation +occurs. This is the default. The setting is case-insensitive. + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 5.4.1.1 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI04.04 + BAI08.02 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS05.04 + DSS05.07 + MEA02.01 + CCI-000140 + 164.312(a)(2)(ii) + 4.2.3.10 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 6.1 + SR 7.1 + SR 7.2 + A.12.1.3 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.17.2.1 + AU-5(b) + AU-5(2) + AU-5(1) + AU-5(4) + CM-6(a) + DE.AE-3 + DE.AE-5 + PR.DS-4 + PR.PT-1 + RS.AN-1 + RS.AN-4 + Req-10.7 + SRG-OS-000047-GPOS-00023 + Automatically rotating logs (by setting this to rotate) +minimizes the chances of the system unexpectedly running out of disk space by +being overwhelmed with log data. However, for systems that must never discard +log data, or which use external processes to transfer it and reclaim space, +keep_logs can be employed. + CCE-83701-3 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83701-3 + - CJIS-5.4.1.1 + - NIST-800-53-AU-5(1) + - NIST-800-53-AU-5(2) + - NIST-800-53-AU-5(4) + - NIST-800-53-AU-5(b) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.7 + - auditd_data_retention_max_log_file_action + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy +- name: XCCDF Value var_auditd_max_log_file_action # promote to variable + set_fact: + var_auditd_max_log_file_action: !!str + tags: + - always + +- name: Configure auditd max_log_file_action Upon Reaching Maximum Log Size + lineinfile: + dest: /etc/audit/auditd.conf + line: max_log_file_action = {{ var_auditd_max_log_file_action }} + regexp: ^\s*max_log_file_action\s*=\s*.*$ + state: present + create: true + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83701-3 + - CJIS-5.4.1.1 + - NIST-800-53-AU-5(1) + - NIST-800-53-AU-5(2) + - NIST-800-53-AU-5(4) + - NIST-800-53-AU-5(b) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.7 + - auditd_data_retention_max_log_file_action + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,{{ %23%0A%23%20This%20file%20controls%20the%20configuration%20of%20the%20audit%20daemon%0A%23%0A%0Alocal_events%20%3D%20yes%0Awrite_logs%20%3D%20yes%0Alog_file%20%3D%20/var/log/audit/audit.log%0Alog_group%20%3D%20root%0Alog_format%20%3D%20ENRICHED%0Aflush%20%3D%20%7B%7B.var_auditd_flush%7D%7D%0Afreq%20%3D%2050%0Amax_log_file%20%3D%20%7B%7B.var_auditd_max_log_file%7D%7D%0Anum_logs%20%3D%20%7B%7B.var_auditd_num_logs%7D%7D%0Apriority_boost%20%3D%204%0Aname_format%20%3D%20hostname%0A%23%23name%20%3D%20mydomain%0Amax_log_file_action%20%3D%20%7B%7B.var_auditd_max_log_file_action%7D%7D%0Aspace_left%20%3D%20%7B%7B.var_auditd_space_left%7D%7D%0Aspace_left_action%20%3D%20%7B%7B.var_auditd_space_left_action%7D%7D%0Averify_email%20%3D%20yes%0Aaction_mail_acct%20%3D%20%7B%7B.var_auditd_action_mail_acct%7D%7D%0Aadmin_space_left%20%3D%2050%0Aadmin_space_left_action%20%3D%20syslog%0Adisk_full_action%20%3D%20%7B%7B.var_auditd_disk_full_action%7D%7D%0Adisk_error_action%20%3D%20%7B%7B.var_auditd_disk_error_action%7D%7D%0Ause_libwrap%20%3D%20yes%0A%23%23tcp_listen_port%20%3D%2060%0Atcp_listen_queue%20%3D%205%0Atcp_max_per_addr%20%3D%201%0A%23%23tcp_client_ports%20%3D%201024-65535%0Atcp_client_max_idle%20%3D%200%0Atransport%20%3D%20TCP%0Akrb5_principal%20%3D%20auditd%0A%23%23krb5_key_file%20%3D%20/etc/audit/audit.key%0Adistribute_network%20%3D%20no%0Aq_depth%20%3D%20400%0Aoverflow_action%20%3D%20syslog%0Amax_restarts%20%3D%2010%0Aplugin_dir%20%3D%20/etc/audit/plugins.d }} + mode: 0640 + path: /etc/audit/auditd.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +var_auditd_max_log_file_action='' + + +AUDITCONFIG=/etc/audit/auditd.conf + +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "$AUDITCONFIG"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^max_log_file_action") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$var_auditd_max_log_file_action" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^max_log_file_action\\>" "$AUDITCONFIG"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^max_log_file_action\\>.*/$escaped_formatted_output/gi" "$AUDITCONFIG" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-83701-3" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "$AUDITCONFIG" >> "$AUDITCONFIG" + printf '%s\n' "$formatted_output" >> "$AUDITCONFIG" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Configure auditd max_log_file_action Upon Reaching Maximum Log Size + The default action to take when the logs reach their maximum size +is to rotate the log files, discarding the oldest one. To configure the action taken +by auditd, add or correct the line in /etc/audit/auditd.conf: +max_log_file_action = ACTION +Possible values for ACTION are described in the auditd.conf man +page. These include: +ignoresyslogsuspendrotatekeep_logs +Set the ACTION to rotate to ensure log rotation +occurs. This is the default. The setting is case-insensitive. + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI04.04 + BAI08.02 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS05.04 + DSS05.07 + MEA02.01 + CCI-000140 + 164.312(a)(2)(ii) + 4.2.3.10 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 6.1 + SR 7.1 + SR 7.2 + A.12.1.3 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.17.2.1 + AU-5(b) + AU-5(2) + AU-5(1) + AU-5(4) + CM-6(a) + DE.AE-3 + DE.AE-5 + PR.DS-4 + PR.PT-1 + RS.AN-1 + RS.AN-4 + Req-10.7 + SRG-OS-000047-GPOS-00023 + Automatically rotating logs (by setting this to rotate) +minimizes the chances of the system unexpectedly running out of disk space by +being overwhelmed with log data. However, for systems that must never discard +log data, or which use external processes to transfer it and reclaim space, +keep_logs can be employed. + CCE-88396-7 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-88396-7 + - NIST-800-53-AU-5(1) + - NIST-800-53-AU-5(2) + - NIST-800-53-AU-5(4) + - NIST-800-53-AU-5(b) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.7 + - auditd_data_retention_max_log_file_action_stig + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy +- name: XCCDF Value var_auditd_max_log_file_action # promote to variable + set_fact: + var_auditd_max_log_file_action: !!str + tags: + - always + +- name: Configure auditd max_log_file_action Upon Reaching Maximum Log Size + lineinfile: + dest: /etc/audit/auditd.conf + line: max_log_file_action = {{ var_auditd_max_log_file_action }} + regexp: ^\s*max_log_file_action\s*=\s*.*$ + state: present + create: true + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-88396-7 + - NIST-800-53-AU-5(1) + - NIST-800-53-AU-5(2) + - NIST-800-53-AU-5(4) + - NIST-800-53-AU-5(b) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.7 + - auditd_data_retention_max_log_file_action_stig + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,{{ %23%0A%23%20This%20file%20controls%20the%20configuration%20of%20the%20audit%20daemon%0A%23%0A%0Alocal_events%20%3D%20yes%0Awrite_logs%20%3D%20yes%0Alog_file%20%3D%20/var/log/audit/audit.log%0Alog_group%20%3D%20root%0Alog_format%20%3D%20ENRICHED%0Aflush%20%3D%20%7B%7B.var_auditd_flush%7D%7D%0Afreq%20%3D%2050%0Amax_log_file%20%3D%20%7B%7B.var_auditd_max_log_file%7D%7D%0Anum_logs%20%3D%20%7B%7B.var_auditd_num_logs%7D%7D%0Apriority_boost%20%3D%204%0Aname_format%20%3D%20hostname%0A%23%23name%20%3D%20mydomain%0Amax_log_file_action%20%3D%20%7B%7B.var_auditd_max_log_file_action%7D%7D%0Aspace_left%20%3D%20%7B%7B.var_auditd_space_left%7D%7D%0Aspace_left_action%20%3D%20%7B%7B.var_auditd_space_left_action%7D%7D%0Averify_email%20%3D%20yes%0Aaction_mail_acct%20%3D%20%7B%7B.var_auditd_action_mail_acct%7D%7D%0Aadmin_space_left%20%3D%2050%0Aadmin_space_left_action%20%3D%20syslog%0Adisk_full_action%20%3D%20%7B%7B.var_auditd_disk_full_action%7D%7D%0Adisk_error_action%20%3D%20%7B%7B.var_auditd_disk_error_action%7D%7D%0Ause_libwrap%20%3D%20yes%0A%23%23tcp_listen_port%20%3D%2060%0Atcp_listen_queue%20%3D%205%0Atcp_max_per_addr%20%3D%201%0A%23%23tcp_client_ports%20%3D%201024-65535%0Atcp_client_max_idle%20%3D%200%0Atransport%20%3D%20TCP%0Akrb5_principal%20%3D%20auditd%0A%23%23krb5_key_file%20%3D%20/etc/audit/audit.key%0Adistribute_network%20%3D%20no%0Aq_depth%20%3D%20400%0Aoverflow_action%20%3D%20syslog%0Amax_restarts%20%3D%2010%0Aplugin_dir%20%3D%20/etc/audit/plugins.d }} + mode: 0640 + path: /etc/audit/auditd.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +var_auditd_max_log_file_action='' + + +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/audit/auditd.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^max_log_file_action") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$var_auditd_max_log_file_action" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^max_log_file_action\\>" "/etc/audit/auditd.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^max_log_file_action\\>.*/$escaped_formatted_output/gi" "/etc/audit/auditd.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-88396-7" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/audit/auditd.conf" >> "/etc/audit/auditd.conf" + printf '%s\n' "$formatted_output" >> "/etc/audit/auditd.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure auditd Number of Logs Retained + Determine how many log files +auditd should retain when it rotates logs. +Edit the file /etc/audit/auditd.conf. Add or modify the following +line, substituting NUMLOGS with the correct value of : +num_logs = NUMLOGS +Set the value to 5 for general-purpose systems. +Note that values less than 2 result in no log rotation. + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 3 + 4 + 5 + 6 + 7 + 8 + 5.4.1.1 + APO11.04 + APO12.06 + BAI03.05 + BAI08.02 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS05.04 + DSS05.07 + MEA02.01 + 3.3.1 + 4.2.3.10 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 6.1 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.16.1.4 + A.16.1.5 + A.16.1.7 + CIP-004-6 R2.2.3 + CIP-004-6 R3.3 + CIP-007-3 R5.2 + CIP-007-3 R5.3.1 + CIP-007-3 R5.3.2 + CIP-007-3 R5.3.3 + CIP-007-3 R6.5 + AU-11 + CM-6(a) + DE.AE-3 + DE.AE-5 + PR.PT-1 + RS.AN-1 + RS.AN-4 + Req-10.7 + The total storage for audit log files must be large enough to retain +log information over the period required. This is a function of the maximum log +file size and the number of logs retained. + CCE-83688-2 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83688-2 + - CJIS-5.4.1.1 + - NIST-800-171-3.3.1 + - NIST-800-53-AU-11 + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.7 + - auditd_data_retention_num_logs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy +- name: XCCDF Value var_auditd_num_logs # promote to variable + set_fact: + var_auditd_num_logs: !!str + tags: + - always + +- name: Configure auditd Number of Logs Retained + lineinfile: + dest: /etc/audit/auditd.conf + line: num_logs = {{ var_auditd_num_logs }} + regexp: ^\s*num_logs\s*=\s*.*$ + state: present + create: true + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83688-2 + - CJIS-5.4.1.1 + - NIST-800-171-3.3.1 + - NIST-800-53-AU-11 + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.7 + - auditd_data_retention_num_logs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,{{ %23%0A%23%20This%20file%20controls%20the%20configuration%20of%20the%20audit%20daemon%0A%23%0A%0Alocal_events%20%3D%20yes%0Awrite_logs%20%3D%20yes%0Alog_file%20%3D%20/var/log/audit/audit.log%0Alog_group%20%3D%20root%0Alog_format%20%3D%20ENRICHED%0Aflush%20%3D%20%7B%7B.var_auditd_flush%7D%7D%0Afreq%20%3D%2050%0Amax_log_file%20%3D%20%7B%7B.var_auditd_max_log_file%7D%7D%0Anum_logs%20%3D%20%7B%7B.var_auditd_num_logs%7D%7D%0Apriority_boost%20%3D%204%0Aname_format%20%3D%20hostname%0A%23%23name%20%3D%20mydomain%0Amax_log_file_action%20%3D%20%7B%7B.var_auditd_max_log_file_action%7D%7D%0Aspace_left%20%3D%20%7B%7B.var_auditd_space_left%7D%7D%0Aspace_left_action%20%3D%20%7B%7B.var_auditd_space_left_action%7D%7D%0Averify_email%20%3D%20yes%0Aaction_mail_acct%20%3D%20%7B%7B.var_auditd_action_mail_acct%7D%7D%0Aadmin_space_left%20%3D%2050%0Aadmin_space_left_action%20%3D%20syslog%0Adisk_full_action%20%3D%20%7B%7B.var_auditd_disk_full_action%7D%7D%0Adisk_error_action%20%3D%20%7B%7B.var_auditd_disk_error_action%7D%7D%0Ause_libwrap%20%3D%20yes%0A%23%23tcp_listen_port%20%3D%2060%0Atcp_listen_queue%20%3D%205%0Atcp_max_per_addr%20%3D%201%0A%23%23tcp_client_ports%20%3D%201024-65535%0Atcp_client_max_idle%20%3D%200%0Atransport%20%3D%20TCP%0Akrb5_principal%20%3D%20auditd%0A%23%23krb5_key_file%20%3D%20/etc/audit/audit.key%0Adistribute_network%20%3D%20no%0Aq_depth%20%3D%20400%0Aoverflow_action%20%3D%20syslog%0Amax_restarts%20%3D%2010%0Aplugin_dir%20%3D%20/etc/audit/plugins.d }} + mode: 0640 + path: /etc/audit/auditd.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +var_auditd_num_logs='' + + +AUDITCONFIG=/etc/audit/auditd.conf + +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "$AUDITCONFIG"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^num_logs") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$var_auditd_num_logs" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^num_logs\\>" "$AUDITCONFIG"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^num_logs\\>.*/$escaped_formatted_output/gi" "$AUDITCONFIG" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-83688-2" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "$AUDITCONFIG" >> "$AUDITCONFIG" + printf '%s\n' "$formatted_output" >> "$AUDITCONFIG" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Configure auditd space_left on Low Disk Space + The auditd service can be configured to take an action +when disk space is running low but prior to running out of space completely. +Edit the file /etc/audit/auditd.conf. Add or modify the following line, +substituting SIZE_in_MB appropriately: +space_left = SIZE_in_MB +Set this value to the appropriate size in Megabytes cause the system to +notify the user of an issue. + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI04.04 + BAI08.02 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS05.04 + DSS05.07 + MEA02.01 + CCI-001855 + 4.2.3.10 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 6.1 + SR 7.1 + SR 7.2 + A.12.1.3 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.17.2.1 + AU-5(b) + AU-5(2) + AU-5(1) + AU-5(4) + CM-6(a) + DE.AE-3 + DE.AE-5 + PR.DS-4 + PR.PT-1 + RS.AN-1 + RS.AN-4 + Req-10.7 + SRG-OS-000343-GPOS-00134 + SRG-OS-000343-VMM-001240 + Notifying administrators of an impending disk space problem may allow them to +take corrective action prior to any disruption. + CCE-87414-9 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-87414-9 + - NIST-800-53-AU-5(1) + - NIST-800-53-AU-5(2) + - NIST-800-53-AU-5(4) + - NIST-800-53-AU-5(b) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.7 + - auditd_data_retention_space_left + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy +- name: XCCDF Value var_auditd_space_left # promote to variable + set_fact: + var_auditd_space_left: !!str + tags: + - always + +- name: Configure auditd space_left on Low Disk Space + lineinfile: + dest: /etc/audit/auditd.conf + line: space_left = {{ var_auditd_space_left }} + regexp: ^\s*space_left\s*=\s*.*$ + state: present + create: true + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-87414-9 + - NIST-800-53-AU-5(1) + - NIST-800-53-AU-5(2) + - NIST-800-53-AU-5(4) + - NIST-800-53-AU-5(b) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.7 + - auditd_data_retention_space_left + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,{{ %23%0A%23%20This%20file%20controls%20the%20configuration%20of%20the%20audit%20daemon%0A%23%0A%0Alocal_events%20%3D%20yes%0Awrite_logs%20%3D%20yes%0Alog_file%20%3D%20/var/log/audit/audit.log%0Alog_group%20%3D%20root%0Alog_format%20%3D%20ENRICHED%0Aflush%20%3D%20%7B%7B.var_auditd_flush%7D%7D%0Afreq%20%3D%2050%0Amax_log_file%20%3D%20%7B%7B.var_auditd_max_log_file%7D%7D%0Anum_logs%20%3D%20%7B%7B.var_auditd_num_logs%7D%7D%0Apriority_boost%20%3D%204%0Aname_format%20%3D%20hostname%0A%23%23name%20%3D%20mydomain%0Amax_log_file_action%20%3D%20%7B%7B.var_auditd_max_log_file_action%7D%7D%0Aspace_left%20%3D%20%7B%7B.var_auditd_space_left%7D%7D%0Aspace_left_action%20%3D%20%7B%7B.var_auditd_space_left_action%7D%7D%0Averify_email%20%3D%20yes%0Aaction_mail_acct%20%3D%20%7B%7B.var_auditd_action_mail_acct%7D%7D%0Aadmin_space_left%20%3D%2050%0Aadmin_space_left_action%20%3D%20syslog%0Adisk_full_action%20%3D%20%7B%7B.var_auditd_disk_full_action%7D%7D%0Adisk_error_action%20%3D%20%7B%7B.var_auditd_disk_error_action%7D%7D%0Ause_libwrap%20%3D%20yes%0A%23%23tcp_listen_port%20%3D%2060%0Atcp_listen_queue%20%3D%205%0Atcp_max_per_addr%20%3D%201%0A%23%23tcp_client_ports%20%3D%201024-65535%0Atcp_client_max_idle%20%3D%200%0Atransport%20%3D%20TCP%0Akrb5_principal%20%3D%20auditd%0A%23%23krb5_key_file%20%3D%20/etc/audit/audit.key%0Adistribute_network%20%3D%20no%0Aq_depth%20%3D%20400%0Aoverflow_action%20%3D%20syslog%0Amax_restarts%20%3D%2010%0Aplugin_dir%20%3D%20/etc/audit/plugins.d }} + mode: 0640 + path: /etc/audit/auditd.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +var_auditd_space_left='' + + +grep -q "^space_left[[:space:]]*=.*$" /etc/audit/auditd.conf && \ + sed -i "s/^space_left[[:space:]]*=.*$/space_left = $var_auditd_space_left/g" /etc/audit/auditd.conf || \ + echo "space_left = $var_auditd_space_left" >> /etc/audit/auditd.conf + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Configure auditd space_left Action on Low Disk Space + The auditd service can be configured to take an action +when disk space starts to run low. +Edit the file /etc/audit/auditd.conf. Modify the following line, +substituting ACTION appropriately: +space_left_action = ACTION +Possible values for ACTION are described in the auditd.conf man page. +These include: +syslogemailexecsuspendsinglehalt +Set this to email (instead of the default, +which is suspend) as it is more likely to get prompt attention. Acceptable values +also include suspend, single, and halt. + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 5.4.1.1 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI04.04 + BAI08.02 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS05.04 + DSS05.07 + MEA02.01 + 3.3.1 + CCI-001855 + 164.312(a)(2)(ii) + 4.2.3.10 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 6.1 + SR 7.1 + SR 7.2 + A.12.1.3 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.17.2.1 + AU-5(b) + AU-5(2) + AU-5(1) + AU-5(4) + CM-6(a) + DE.AE-3 + DE.AE-5 + PR.DS-4 + PR.PT-1 + RS.AN-1 + RS.AN-4 + Req-10.7 + SRG-OS-000343-GPOS-00134 + SRG-OS-000343-VMM-001240 + Notifying administrators of an impending disk space problem may +allow them to take corrective action prior to any disruption. + CCE-83703-9 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83703-9 + - CJIS-5.4.1.1 + - NIST-800-171-3.3.1 + - NIST-800-53-AU-5(1) + - NIST-800-53-AU-5(2) + - NIST-800-53-AU-5(4) + - NIST-800-53-AU-5(b) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.7 + - auditd_data_retention_space_left_action + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy +- name: XCCDF Value var_auditd_space_left_action # promote to variable + set_fact: + var_auditd_space_left_action: !!str + tags: + - always + +- name: Configure auditd space_left Action on Low Disk Space + lineinfile: + dest: /etc/audit/auditd.conf + line: space_left_action = {{ var_auditd_space_left_action }} + regexp: ^\s*space_left_action\s*=\s*.*$ + state: present + create: true + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83703-9 + - CJIS-5.4.1.1 + - NIST-800-171-3.3.1 + - NIST-800-53-AU-5(1) + - NIST-800-53-AU-5(2) + - NIST-800-53-AU-5(4) + - NIST-800-53-AU-5(b) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.7 + - auditd_data_retention_space_left_action + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,{{ %23%0A%23%20This%20file%20controls%20the%20configuration%20of%20the%20audit%20daemon%0A%23%0A%0Alocal_events%20%3D%20yes%0Awrite_logs%20%3D%20yes%0Alog_file%20%3D%20/var/log/audit/audit.log%0Alog_group%20%3D%20root%0Alog_format%20%3D%20ENRICHED%0Aflush%20%3D%20%7B%7B.var_auditd_flush%7D%7D%0Afreq%20%3D%2050%0Amax_log_file%20%3D%20%7B%7B.var_auditd_max_log_file%7D%7D%0Anum_logs%20%3D%20%7B%7B.var_auditd_num_logs%7D%7D%0Apriority_boost%20%3D%204%0Aname_format%20%3D%20hostname%0A%23%23name%20%3D%20mydomain%0Amax_log_file_action%20%3D%20%7B%7B.var_auditd_max_log_file_action%7D%7D%0Aspace_left%20%3D%20%7B%7B.var_auditd_space_left%7D%7D%0Aspace_left_action%20%3D%20%7B%7B.var_auditd_space_left_action%7D%7D%0Averify_email%20%3D%20yes%0Aaction_mail_acct%20%3D%20%7B%7B.var_auditd_action_mail_acct%7D%7D%0Aadmin_space_left%20%3D%2050%0Aadmin_space_left_action%20%3D%20syslog%0Adisk_full_action%20%3D%20%7B%7B.var_auditd_disk_full_action%7D%7D%0Adisk_error_action%20%3D%20%7B%7B.var_auditd_disk_error_action%7D%7D%0Ause_libwrap%20%3D%20yes%0A%23%23tcp_listen_port%20%3D%2060%0Atcp_listen_queue%20%3D%205%0Atcp_max_per_addr%20%3D%201%0A%23%23tcp_client_ports%20%3D%201024-65535%0Atcp_client_max_idle%20%3D%200%0Atransport%20%3D%20TCP%0Akrb5_principal%20%3D%20auditd%0A%23%23krb5_key_file%20%3D%20/etc/audit/audit.key%0Adistribute_network%20%3D%20no%0Aq_depth%20%3D%20400%0Aoverflow_action%20%3D%20syslog%0Amax_restarts%20%3D%2010%0Aplugin_dir%20%3D%20/etc/audit/plugins.d }} + mode: 0640 + path: /etc/audit/auditd.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +var_auditd_space_left_action='' + + +# +# If space_left_action present in /etc/audit/auditd.conf, change value +# to var_auditd_space_left_action, else +# add "space_left_action = $var_auditd_space_left_action" to /etc/audit/auditd.conf +# + +AUDITCONFIG=/etc/audit/auditd.conf + +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "$AUDITCONFIG"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^space_left_action") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$var_auditd_space_left_action" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^space_left_action\\>" "$AUDITCONFIG"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^space_left_action\\>.*/$escaped_formatted_output/gi" "$AUDITCONFIG" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-83703-9" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "$AUDITCONFIG" >> "$AUDITCONFIG" + printf '%s\n' "$formatted_output" >> "$AUDITCONFIG" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Configure auditd space_left on Low Disk Space + The auditd service can be configured to take an action +when disk space is running low but prior to running out of space completely. +Edit the file /etc/audit/auditd.conf. Add or modify the following line, +substituting PERCENTAGE appropriately: +space_left = PERCENTAGE% +Set this value to at least 25 to cause the system to +notify the user of an issue. + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 19 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + APO11.04 + APO12.06 + APO13.01 + BAI03.05 + BAI04.04 + BAI08.02 + DSS02.02 + DSS02.04 + DSS02.07 + DSS03.01 + DSS05.04 + DSS05.07 + MEA02.01 + CCI-001855 + 4.2.3.10 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.3.4.5.6 + 4.3.4.5.7 + 4.3.4.5.8 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 6.1 + SR 7.1 + SR 7.2 + A.12.1.3 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.16.1.4 + A.16.1.5 + A.16.1.7 + A.17.2.1 + AU-5(b) + AU-5(2) + AU-5(1) + AU-5(4) + CM-6(a) + DE.AE-3 + DE.AE-5 + PR.DS-4 + PR.PT-1 + RS.AN-1 + RS.AN-4 + Req-10.7 + SRG-OS-000343-GPOS-00134 + SRG-OS-000343-VMM-001240 + Notifying administrators of an impending disk space problem may allow them to +take corrective action prior to any disruption. + CCE-87746-4 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-87746-4 + - NIST-800-53-AU-5(1) + - NIST-800-53-AU-5(2) + - NIST-800-53-AU-5(4) + - NIST-800-53-AU-5(b) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.7 + - auditd_data_retention_space_left_percentage + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy +- name: XCCDF Value var_auditd_space_left_percentage # promote to variable + set_fact: + var_auditd_space_left_percentage: !!str + tags: + - always + +- name: Configure auditd space_left on Low Disk Space + lineinfile: + dest: /etc/audit/auditd.conf + line: space_left = {{ var_auditd_space_left_percentage }}% + regexp: ^\s*space_left\s*=\s*.*$ + state: present + create: true + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-87746-4 + - NIST-800-53-AU-5(1) + - NIST-800-53-AU-5(2) + - NIST-800-53-AU-5(4) + - NIST-800-53-AU-5(b) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.7 + - auditd_data_retention_space_left_percentage + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +var_auditd_space_left_percentage='' + + +grep -q "^space_left[[:space:]]*=.*$" /etc/audit/auditd.conf && \ + sed -i "s/^space_left[[:space:]]*=.*$/space_left = $var_auditd_space_left_percentage%/g" /etc/audit/auditd.conf || \ + echo "space_left = $var_auditd_space_left_percentage%" >> /etc/audit/auditd.conf + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Set number of records to cause an explicit flush to audit logs + To configure Audit daemon to issue an explicit flush to disk command +after writing records, set freq to +in /etc/audit/auditd.conf. + CM-6 + FAU_GEN.1 + SRG-OS-000051-GPOS-00024 + If option freq isn't set to , the flush to disk +may happen after higher number of records, increasing the danger +of audit loss. + CCE-83704-7 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83704-7 + - NIST-800-53-CM-6 + - auditd_freq + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Set number of records to cause an explicit flush to audit logs + block: + + - name: Check for duplicate values + lineinfile: + path: /etc/audit/auditd.conf + create: false + regexp: (?i)^\s*freq\s*=\s* + state: absent + check_mode: true + changed_when: false + register: dupes + + - name: Deduplicate values from /etc/audit/auditd.conf + lineinfile: + path: /etc/audit/auditd.conf + create: false + regexp: (?i)^\s*freq\s*=\s* + state: absent + when: dupes.found is defined and dupes.found > 1 + + - name: Insert correct line to /etc/audit/auditd.conf + lineinfile: + path: /etc/audit/auditd.conf + create: true + regexp: (?i)^\s*freq\s*=\s* + line: freq = 50 + state: present + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83704-7 + - NIST-800-53-CM-6 + - auditd_freq + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,{{ %23%0A%23%20This%20file%20controls%20the%20configuration%20of%20the%20audit%20daemon%0A%23%0A%0Alocal_events%20%3D%20yes%0Awrite_logs%20%3D%20yes%0Alog_file%20%3D%20/var/log/audit/audit.log%0Alog_group%20%3D%20root%0Alog_format%20%3D%20ENRICHED%0Aflush%20%3D%20%7B%7B.var_auditd_flush%7D%7D%0Afreq%20%3D%2050%0Amax_log_file%20%3D%20%7B%7B.var_auditd_max_log_file%7D%7D%0Anum_logs%20%3D%20%7B%7B.var_auditd_num_logs%7D%7D%0Apriority_boost%20%3D%204%0Aname_format%20%3D%20hostname%0A%23%23name%20%3D%20mydomain%0Amax_log_file_action%20%3D%20%7B%7B.var_auditd_max_log_file_action%7D%7D%0Aspace_left%20%3D%20%7B%7B.var_auditd_space_left%7D%7D%0Aspace_left_action%20%3D%20%7B%7B.var_auditd_space_left_action%7D%7D%0Averify_email%20%3D%20yes%0Aaction_mail_acct%20%3D%20%7B%7B.var_auditd_action_mail_acct%7D%7D%0Aadmin_space_left%20%3D%2050%0Aadmin_space_left_action%20%3D%20syslog%0Adisk_full_action%20%3D%20%7B%7B.var_auditd_disk_full_action%7D%7D%0Adisk_error_action%20%3D%20%7B%7B.var_auditd_disk_error_action%7D%7D%0Ause_libwrap%20%3D%20yes%0A%23%23tcp_listen_port%20%3D%2060%0Atcp_listen_queue%20%3D%205%0Atcp_max_per_addr%20%3D%201%0A%23%23tcp_client_ports%20%3D%201024-65535%0Atcp_client_max_idle%20%3D%200%0Atransport%20%3D%20TCP%0Akrb5_principal%20%3D%20auditd%0A%23%23krb5_key_file%20%3D%20/etc/audit/audit.key%0Adistribute_network%20%3D%20no%0Aq_depth%20%3D%20400%0Aoverflow_action%20%3D%20syslog%0Amax_restarts%20%3D%2010%0Aplugin_dir%20%3D%20/etc/audit/plugins.d }} + mode: 0640 + path: /etc/audit/auditd.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +if [ -e "/etc/audit/auditd.conf" ] ; then + + LC_ALL=C sed -i "/^\s*freq\s*=\s*/Id" "/etc/audit/auditd.conf" +else + touch "/etc/audit/auditd.conf" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/audit/auditd.conf" + +cp "/etc/audit/auditd.conf" "/etc/audit/auditd.conf.bak" +# Insert at the end of the file +printf '%s\n' "freq = 50" >> "/etc/audit/auditd.conf" +# Clean up after ourselves. +rm "/etc/audit/auditd.conf.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Include Local Events in Audit Logs + To configure Audit daemon to include local events in Audit logs, set +local_events to yes in /etc/audit/auditd.conf. +This is the default setting. + CCI-000366 + CM-6 + FAU_GEN.1 + SRG-OS-000062-GPOS-00031 + SRG-OS-000480-GPOS-00227 + If option local_events isn't set to yes only events from +network will be aggregated. + CCE-83682-5 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83682-5 + - NIST-800-53-CM-6 + - auditd_local_events + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Include Local Events in Audit Logs + block: + + - name: Check for duplicate values + lineinfile: + path: /etc/audit/auditd.conf + create: false + regexp: (?i)^\s*local_events\s*=\s* + state: absent + check_mode: true + changed_when: false + register: dupes + + - name: Deduplicate values from /etc/audit/auditd.conf + lineinfile: + path: /etc/audit/auditd.conf + create: false + regexp: (?i)^\s*local_events\s*=\s* + state: absent + when: dupes.found is defined and dupes.found > 1 + + - name: Insert correct line to /etc/audit/auditd.conf + lineinfile: + path: /etc/audit/auditd.conf + create: true + regexp: (?i)^\s*local_events\s*=\s* + line: local_events = yes + state: present + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83682-5 + - NIST-800-53-CM-6 + - auditd_local_events + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,{{ %23%0A%23%20This%20file%20controls%20the%20configuration%20of%20the%20audit%20daemon%0A%23%0A%0Alocal_events%20%3D%20yes%0Awrite_logs%20%3D%20yes%0Alog_file%20%3D%20/var/log/audit/audit.log%0Alog_group%20%3D%20root%0Alog_format%20%3D%20ENRICHED%0Aflush%20%3D%20%7B%7B.var_auditd_flush%7D%7D%0Afreq%20%3D%2050%0Amax_log_file%20%3D%20%7B%7B.var_auditd_max_log_file%7D%7D%0Anum_logs%20%3D%20%7B%7B.var_auditd_num_logs%7D%7D%0Apriority_boost%20%3D%204%0Aname_format%20%3D%20hostname%0A%23%23name%20%3D%20mydomain%0Amax_log_file_action%20%3D%20%7B%7B.var_auditd_max_log_file_action%7D%7D%0Aspace_left%20%3D%20%7B%7B.var_auditd_space_left%7D%7D%0Aspace_left_action%20%3D%20%7B%7B.var_auditd_space_left_action%7D%7D%0Averify_email%20%3D%20yes%0Aaction_mail_acct%20%3D%20%7B%7B.var_auditd_action_mail_acct%7D%7D%0Aadmin_space_left%20%3D%2050%0Aadmin_space_left_action%20%3D%20syslog%0Adisk_full_action%20%3D%20%7B%7B.var_auditd_disk_full_action%7D%7D%0Adisk_error_action%20%3D%20%7B%7B.var_auditd_disk_error_action%7D%7D%0Ause_libwrap%20%3D%20yes%0A%23%23tcp_listen_port%20%3D%2060%0Atcp_listen_queue%20%3D%205%0Atcp_max_per_addr%20%3D%201%0A%23%23tcp_client_ports%20%3D%201024-65535%0Atcp_client_max_idle%20%3D%200%0Atransport%20%3D%20TCP%0Akrb5_principal%20%3D%20auditd%0A%23%23krb5_key_file%20%3D%20/etc/audit/audit.key%0Adistribute_network%20%3D%20no%0Aq_depth%20%3D%20400%0Aoverflow_action%20%3D%20syslog%0Amax_restarts%20%3D%2010%0Aplugin_dir%20%3D%20/etc/audit/plugins.d }} + mode: 0640 + path: /etc/audit/auditd.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +if [ -e "/etc/audit/auditd.conf" ] ; then + + LC_ALL=C sed -i "/^\s*local_events\s*=\s*/Id" "/etc/audit/auditd.conf" +else + touch "/etc/audit/auditd.conf" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/audit/auditd.conf" + +cp "/etc/audit/auditd.conf" "/etc/audit/auditd.conf.bak" +# Insert at the end of the file +printf '%s\n' "local_events = yes" >> "/etc/audit/auditd.conf" +# Clean up after ourselves. +rm "/etc/audit/auditd.conf.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Resolve information before writing to audit logs + To configure Audit daemon to resolve all uid, gid, syscall, +architecture, and socket address information before writing the +events to disk, set log_format to ENRICHED +in /etc/audit/auditd.conf. + CCI-000366 + CM-6 + AU-3 + FAU_GEN.1.2 + SRG-OS-000255-GPOS-00096 + SRG-OS-000480-GPOS-00227 + If option log_format isn't set to ENRICHED, the +audit records will be stored in a format exactly as the kernel sends them. + CCE-83696-5 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83696-5 + - NIST-800-53-AU-3 + - NIST-800-53-CM-6 + - auditd_log_format + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Resolve information before writing to audit logs + block: + + - name: Check for duplicate values + lineinfile: + path: /etc/audit/auditd.conf + create: false + regexp: (?i)^\s*log_format\s*=\s* + state: absent + check_mode: true + changed_when: false + register: dupes + + - name: Deduplicate values from /etc/audit/auditd.conf + lineinfile: + path: /etc/audit/auditd.conf + create: false + regexp: (?i)^\s*log_format\s*=\s* + state: absent + when: dupes.found is defined and dupes.found > 1 + + - name: Insert correct line to /etc/audit/auditd.conf + lineinfile: + path: /etc/audit/auditd.conf + create: true + regexp: (?i)^\s*log_format\s*=\s* + line: log_format = ENRICHED + state: present + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83696-5 + - NIST-800-53-AU-3 + - NIST-800-53-CM-6 + - auditd_log_format + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,{{ %23%0A%23%20This%20file%20controls%20the%20configuration%20of%20the%20audit%20daemon%0A%23%0A%0Alocal_events%20%3D%20yes%0Awrite_logs%20%3D%20yes%0Alog_file%20%3D%20/var/log/audit/audit.log%0Alog_group%20%3D%20root%0Alog_format%20%3D%20ENRICHED%0Aflush%20%3D%20%7B%7B.var_auditd_flush%7D%7D%0Afreq%20%3D%2050%0Amax_log_file%20%3D%20%7B%7B.var_auditd_max_log_file%7D%7D%0Anum_logs%20%3D%20%7B%7B.var_auditd_num_logs%7D%7D%0Apriority_boost%20%3D%204%0Aname_format%20%3D%20hostname%0A%23%23name%20%3D%20mydomain%0Amax_log_file_action%20%3D%20%7B%7B.var_auditd_max_log_file_action%7D%7D%0Aspace_left%20%3D%20%7B%7B.var_auditd_space_left%7D%7D%0Aspace_left_action%20%3D%20%7B%7B.var_auditd_space_left_action%7D%7D%0Averify_email%20%3D%20yes%0Aaction_mail_acct%20%3D%20%7B%7B.var_auditd_action_mail_acct%7D%7D%0Aadmin_space_left%20%3D%2050%0Aadmin_space_left_action%20%3D%20syslog%0Adisk_full_action%20%3D%20%7B%7B.var_auditd_disk_full_action%7D%7D%0Adisk_error_action%20%3D%20%7B%7B.var_auditd_disk_error_action%7D%7D%0Ause_libwrap%20%3D%20yes%0A%23%23tcp_listen_port%20%3D%2060%0Atcp_listen_queue%20%3D%205%0Atcp_max_per_addr%20%3D%201%0A%23%23tcp_client_ports%20%3D%201024-65535%0Atcp_client_max_idle%20%3D%200%0Atransport%20%3D%20TCP%0Akrb5_principal%20%3D%20auditd%0A%23%23krb5_key_file%20%3D%20/etc/audit/audit.key%0Adistribute_network%20%3D%20no%0Aq_depth%20%3D%20400%0Aoverflow_action%20%3D%20syslog%0Amax_restarts%20%3D%2010%0Aplugin_dir%20%3D%20/etc/audit/plugins.d }} + mode: 0640 + path: /etc/audit/auditd.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +if [ -e "/etc/audit/auditd.conf" ] ; then + + LC_ALL=C sed -i "/^\s*log_format\s*=\s*/Id" "/etc/audit/auditd.conf" +else + touch "/etc/audit/auditd.conf" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/audit/auditd.conf" + +cp "/etc/audit/auditd.conf" "/etc/audit/auditd.conf.bak" +# Insert at the end of the file +printf '%s\n' "log_format = ENRICHED" >> "/etc/audit/auditd.conf" +# Clean up after ourselves. +rm "/etc/audit/auditd.conf.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Set hostname as computer node name in audit logs + To configure Audit daemon to use value returned by gethostname +syscall as computer node name in the audit events, +set name_format to hostname +in /etc/audit/auditd.conf. + CCI-001851 + CM-6 + AU-3 + FAU_GEN.1.2 + SRG-OS-000039-GPOS-00017 + SRG-OS-000342-GPOS-00133 + SRG-OS-000479-GPOS-00224 + If option name_format is left at its default value of +none, audit events from different computers may be hard +to distinguish. + CCE-83686-6 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83686-6 + - NIST-800-53-AU-3 + - NIST-800-53-CM-6 + - auditd_name_format + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Set hostname as computer node name in audit logs + block: + + - name: Check for duplicate values + lineinfile: + path: /etc/audit/auditd.conf + create: false + regexp: (?i)^\s*name_format\s*=\s* + state: absent + check_mode: true + changed_when: false + register: dupes + + - name: Deduplicate values from /etc/audit/auditd.conf + lineinfile: + path: /etc/audit/auditd.conf + create: false + regexp: (?i)^\s*name_format\s*=\s* + state: absent + when: dupes.found is defined and dupes.found > 1 + + - name: Insert correct line to /etc/audit/auditd.conf + lineinfile: + path: /etc/audit/auditd.conf + create: true + regexp: (?i)^\s*name_format\s*=\s* + line: name_format = hostname + state: present + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83686-6 + - NIST-800-53-AU-3 + - NIST-800-53-CM-6 + - auditd_name_format + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,{{ %23%0A%23%20This%20file%20controls%20the%20configuration%20of%20the%20audit%20daemon%0A%23%0A%0Alocal_events%20%3D%20yes%0Awrite_logs%20%3D%20yes%0Alog_file%20%3D%20/var/log/audit/audit.log%0Alog_group%20%3D%20root%0Alog_format%20%3D%20ENRICHED%0Aflush%20%3D%20%7B%7B.var_auditd_flush%7D%7D%0Afreq%20%3D%2050%0Amax_log_file%20%3D%20%7B%7B.var_auditd_max_log_file%7D%7D%0Anum_logs%20%3D%20%7B%7B.var_auditd_num_logs%7D%7D%0Apriority_boost%20%3D%204%0Aname_format%20%3D%20hostname%0A%23%23name%20%3D%20mydomain%0Amax_log_file_action%20%3D%20%7B%7B.var_auditd_max_log_file_action%7D%7D%0Aspace_left%20%3D%20%7B%7B.var_auditd_space_left%7D%7D%0Aspace_left_action%20%3D%20%7B%7B.var_auditd_space_left_action%7D%7D%0Averify_email%20%3D%20yes%0Aaction_mail_acct%20%3D%20%7B%7B.var_auditd_action_mail_acct%7D%7D%0Aadmin_space_left%20%3D%2050%0Aadmin_space_left_action%20%3D%20syslog%0Adisk_full_action%20%3D%20%7B%7B.var_auditd_disk_full_action%7D%7D%0Adisk_error_action%20%3D%20%7B%7B.var_auditd_disk_error_action%7D%7D%0Ause_libwrap%20%3D%20yes%0A%23%23tcp_listen_port%20%3D%2060%0Atcp_listen_queue%20%3D%205%0Atcp_max_per_addr%20%3D%201%0A%23%23tcp_client_ports%20%3D%201024-65535%0Atcp_client_max_idle%20%3D%200%0Atransport%20%3D%20TCP%0Akrb5_principal%20%3D%20auditd%0A%23%23krb5_key_file%20%3D%20/etc/audit/audit.key%0Adistribute_network%20%3D%20no%0Aq_depth%20%3D%20400%0Aoverflow_action%20%3D%20syslog%0Amax_restarts%20%3D%2010%0Aplugin_dir%20%3D%20/etc/audit/plugins.d }} + mode: 0640 + path: /etc/audit/auditd.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +if [ -e "/etc/audit/auditd.conf" ] ; then + + LC_ALL=C sed -i "/^\s*name_format\s*=\s*/Id" "/etc/audit/auditd.conf" +else + touch "/etc/audit/auditd.conf" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/audit/auditd.conf" + +cp "/etc/audit/auditd.conf" "/etc/audit/auditd.conf.bak" +# Insert at the end of the file +printf '%s\n' "name_format = hostname" >> "/etc/audit/auditd.conf" +# Clean up after ourselves. +rm "/etc/audit/auditd.conf.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Appropriate Action Must be Setup When the Internal Audit Event Queue is Full + The audit system should have an action setup in the event the internal event queue becomes full. +To setup an overflow action edit /etc/audit/auditd.conf. Set overflow_action +to one of the following values: syslog, single, halt. + CCI-001851 + AU-4(1) + SRG-OS-000342-GPOS-00133 + SRG-OS-000479-GPOS-00224 + The audit system should have an action setup in the event the internal event queue becomes full +so that no data is lost. + CCE-87901-5 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-87901-5 + - NIST-800-53-AU-4(1) + - auditd_overflow_action + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Appropriate Action Must be Setup When the Internal Audit Event Queue is Full + block: + + - name: Check for duplicate values + lineinfile: + path: /etc/audit/auditd.conf + create: false + regexp: (?i)^\s*overflow_action\s*=\s* + state: absent + check_mode: true + changed_when: false + register: dupes + + - name: Deduplicate values from /etc/audit/auditd.conf + lineinfile: + path: /etc/audit/auditd.conf + create: false + regexp: (?i)^\s*overflow_action\s*=\s* + state: absent + when: dupes.found is defined and dupes.found > 1 + + - name: Insert correct line to /etc/audit/auditd.conf + lineinfile: + path: /etc/audit/auditd.conf + create: true + regexp: (?i)^\s*overflow_action\s*=\s* + line: overflow_action = syslog + state: present + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-87901-5 + - NIST-800-53-AU-4(1) + - auditd_overflow_action + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +if [ -e "/etc/audit/auditd.conf" ] ; then + + LC_ALL=C sed -i "/^\s*overflow_action\s*=\s*/Id" "/etc/audit/auditd.conf" +else + touch "/etc/audit/auditd.conf" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/audit/auditd.conf" + +cp "/etc/audit/auditd.conf" "/etc/audit/auditd.conf.bak" +# Insert at the end of the file +printf '%s\n' "overflow_action = syslog" >> "/etc/audit/auditd.conf" +# Clean up after ourselves. +rm "/etc/audit/auditd.conf.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Write Audit Logs to the Disk + To configure Audit daemon to write Audit logs to the disk, set +write_logs to yes in /etc/audit/auditd.conf. +This is the default setting. + CM-6 + FAU_STG.1 + SRG-OS-000480-GPOS-00227 + If write_logs isn't set to yes, the Audit logs will +not be written to the disk. + CCE-83705-4 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83705-4 + - NIST-800-53-CM-6 + - auditd_write_logs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Write Audit Logs to the Disk + block: + + - name: Check for duplicate values + lineinfile: + path: /etc/audit/auditd.conf + create: false + regexp: (?i)^\s*write_logs\s*=\s* + state: absent + check_mode: true + changed_when: false + register: dupes + + - name: Deduplicate values from /etc/audit/auditd.conf + lineinfile: + path: /etc/audit/auditd.conf + create: false + regexp: (?i)^\s*write_logs\s*=\s* + state: absent + when: dupes.found is defined and dupes.found > 1 + + - name: Insert correct line to /etc/audit/auditd.conf + lineinfile: + path: /etc/audit/auditd.conf + create: true + regexp: (?i)^\s*write_logs\s*=\s* + line: write_logs = yes + state: present + when: + - '"audit" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83705-4 + - NIST-800-53-CM-6 + - auditd_write_logs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,{{ %23%0A%23%20This%20file%20controls%20the%20configuration%20of%20the%20audit%20daemon%0A%23%0A%0Alocal_events%20%3D%20yes%0Awrite_logs%20%3D%20yes%0Alog_file%20%3D%20/var/log/audit/audit.log%0Alog_group%20%3D%20root%0Alog_format%20%3D%20ENRICHED%0Aflush%20%3D%20%7B%7B.var_auditd_flush%7D%7D%0Afreq%20%3D%2050%0Amax_log_file%20%3D%20%7B%7B.var_auditd_max_log_file%7D%7D%0Anum_logs%20%3D%20%7B%7B.var_auditd_num_logs%7D%7D%0Apriority_boost%20%3D%204%0Aname_format%20%3D%20hostname%0A%23%23name%20%3D%20mydomain%0Amax_log_file_action%20%3D%20%7B%7B.var_auditd_max_log_file_action%7D%7D%0Aspace_left%20%3D%20%7B%7B.var_auditd_space_left%7D%7D%0Aspace_left_action%20%3D%20%7B%7B.var_auditd_space_left_action%7D%7D%0Averify_email%20%3D%20yes%0Aaction_mail_acct%20%3D%20%7B%7B.var_auditd_action_mail_acct%7D%7D%0Aadmin_space_left%20%3D%2050%0Aadmin_space_left_action%20%3D%20syslog%0Adisk_full_action%20%3D%20%7B%7B.var_auditd_disk_full_action%7D%7D%0Adisk_error_action%20%3D%20%7B%7B.var_auditd_disk_error_action%7D%7D%0Ause_libwrap%20%3D%20yes%0A%23%23tcp_listen_port%20%3D%2060%0Atcp_listen_queue%20%3D%205%0Atcp_max_per_addr%20%3D%201%0A%23%23tcp_client_ports%20%3D%201024-65535%0Atcp_client_max_idle%20%3D%200%0Atransport%20%3D%20TCP%0Akrb5_principal%20%3D%20auditd%0A%23%23krb5_key_file%20%3D%20/etc/audit/audit.key%0Adistribute_network%20%3D%20no%0Aq_depth%20%3D%20400%0Aoverflow_action%20%3D%20syslog%0Amax_restarts%20%3D%2010%0Aplugin_dir%20%3D%20/etc/audit/plugins.d }} + mode: 0640 + path: /etc/audit/auditd.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then + +if [ -e "/etc/audit/auditd.conf" ] ; then + + LC_ALL=C sed -i "/^\s*write_logs\s*=\s*/Id" "/etc/audit/auditd.conf" +else + touch "/etc/audit/auditd.conf" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/audit/auditd.conf" + +cp "/etc/audit/auditd.conf" "/etc/audit/auditd.conf.bak" +# Insert at the end of the file +printf '%s\n' "write_logs = yes" >> "/etc/audit/auditd.conf" +# Clean up after ourselves. +rm "/etc/audit/auditd.conf.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + System Accounting with auditd + The auditd program can perform comprehensive +monitoring of system activity. This section makes use of recommended +configuration settings for specific policies or use cases. +The rules in this section make use of rules defined in /usr/share/doc/audit-VERSION/rules. + + + Configure auditing of unsuccessful file accesses + Ensure that unsuccessful attempts to access a file are audited. + +The following rules configure audit as described above: +## Unsuccessful file access (any other opens) This has to go last. +-a always,exit -F arch=b32 -S open,openat,openat2,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-access +-a always,exit -F arch=b64 -S open,openat,openat2,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-access +-a always,exit -F arch=b32 -S open,openat,openat2,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-access +-a always,exit -F arch=b64 -S open,openat,openat2,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-access + +Load new Audit rules into kernel by running: +augenrules --load + +Note: This rule uses a special set of Audit rules to comply with OSPP 4.2.1. You may reuse this rule in different profiles. If you decide to do so, it is recommended that you inspect contents of the file closely and make sure that they are alligned with your needs. + 0582 + 0584 + 05885 + 0586 + 0846 + 0957 + AU-2(a) + FAU_GEN.1.1.c + SRG-OS-000458-GPOS-00203 + SRG-OS-000474-GPOS-00219 + SRG-OS-000475-GPOS-00220 + SRG-OS-000463-GPOS-00207 + SRG-OS-000465-GPOS-00209 + SRG-OS-000461-GPOS-00205 + Unsuccessful attempts to access a file might be signs of malicious activity happening within the system. Auditing of such activities helps in their monitoring and investigation. + + CCE-83672-6 + - name: Put contents into /etc/audit/rules.d/30-ospp-v42-3-access-failed.rules according + to policy + copy: + dest: /etc/audit/rules.d/30-ospp-v42-3-access-failed.rules + content: | + ## Unsuccessful file access (any other opens) This has to go last. + -a always,exit -F arch=b32 -S open,openat,openat2,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-access + -a always,exit -F arch=b64 -S open,openat,openat2,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-access + -a always,exit -F arch=b32 -S open,openat,openat2,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-access + -a always,exit -F arch=b64 -S open,openat,openat2,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-access + force: true + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ( not ( ansible_architecture == "aarch64" ) and not ( ansible_architecture == + "ppc64le" ) ) + tags: + - CCE-83672-6 + - NIST-800-53-AU-2(a) + - audit_access_failed + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Remove any permissions from other group + file: + path: /etc/audit/rules.d/30-ospp-v42-3-access-failed.rules + mode: o-rwx + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ( not ( ansible_architecture == "aarch64" ) and not ( ansible_architecture == + "ppc64le" ) ) + tags: + - CCE-83672-6 + - NIST-800-53-AU-2(a) + - audit_access_failed + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,%23%23%20Unsuccessful%20file%20access%20%28any%20other%20opens%29%20This%20has%20to%20go%20last.%0A-a%20always%2Cexit%20-F%20arch%3Db32%20-S%20open%2Copenat%2Copen_by_handle_at%20-F%20exit%3D-EACCES%20-F%20auid%26gt%3B%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-access%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20open%2Copenat%2Copen_by_handle_at%20-F%20exit%3D-EACCES%20-F%20auid%26gt%3B%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-access%0A-a%20always%2Cexit%20-F%20arch%3Db32%20-S%20open%2Copenat%2Copen_by_handle_at%20-F%20exit%3D-EPERM%20-F%20auid%26gt%3B%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-access%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20open%2Copenat%2Copen_by_handle_at%20-F%20exit%3D-EPERM%20-F%20auid%26gt%3B%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-access + mode: 0600 + path: /etc/audit/rules.d/30-ospp-v42-3-access-failed.rules + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && { ( ! ( grep -q aarch64 /proc/sys/kernel/osrelease ) && ! ( grep -q ppc64le /proc/sys/kernel/osrelease ) ); }; then + +cat << 'EOF' > /etc/audit/rules.d/30-ospp-v42-3-access-failed.rules +## Unsuccessful file access (any other opens) This has to go last. +-a always,exit -F arch=b32 -S open,openat,openat2,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-access +-a always,exit -F arch=b64 -S open,openat,openat2,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-access +-a always,exit -F arch=b32 -S open,openat,openat2,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-access +-a always,exit -F arch=b64 -S open,openat,openat2,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-access +EOF + +chmod o-rwx /etc/audit/rules.d/30-ospp-v42-3-access-failed.rules + +augenrules --load + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure auditing of unsuccessful file accesses (AArch64) + Ensure that unsuccessful attempts to access a file are audited. + +The following rules configure audit as described above: +## Unsuccessful file access (any other opens) This has to go last. +-a always,exit -F arch=b32 -S open,openat,openat2,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-access +-a always,exit -F arch=b64 -S openat,openat2,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-access +-a always,exit -F arch=b32 -S open,openat,openat2,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-access +-a always,exit -F arch=b64 -S openat,openat2,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-access + +Load new Audit rules into kernel by running: +augenrules --load + +Note: This rule uses a special set of Audit rules to comply with OSPP 4.2.1. You may reuse this rule in different profiles. If you decide to do so, it is recommended that you inspect contents of the file closely and make sure that they are alligned with your needs. + 0582 + 0584 + 05885 + 0586 + 0846 + 0957 + AU-2(a) + FAU_GEN.1.1.c + SRG-OS-000458-GPOS-00203 + SRG-OS-000474-GPOS-00219 + SRG-OS-000475-GPOS-00220 + SRG-OS-000463-GPOS-00207 + SRG-OS-000465-GPOS-00209 + SRG-OS-000461-GPOS-00205 + Unsuccessful attempts to access a file might be signs of malicious activity happening within the system. Auditing of such activities helps in their monitoring and investigation. + + CCE-85922-3 + - name: Put contents into /etc/audit/rules.d/30-ospp-v42-3-access-failed.rules according + to policy + copy: + dest: /etc/audit/rules.d/30-ospp-v42-3-access-failed.rules + content: | + ## Unsuccessful file access (any other opens) This has to go last. + -a always,exit -F arch=b32 -S open,openat,openat2,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-access + -a always,exit -F arch=b64 -S openat,openat2,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-access + -a always,exit -F arch=b32 -S open,openat,openat2,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-access + -a always,exit -F arch=b64 -S openat,openat2,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-access + force: true + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ansible_architecture == "aarch64" + tags: + - CCE-85922-3 + - NIST-800-53-AU-2(a) + - audit_access_failed_aarch64 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Remove any permissions from other group + file: + path: /etc/audit/rules.d/30-ospp-v42-3-access-failed.rules + mode: o-rwx + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ansible_architecture == "aarch64" + tags: + - CCE-85922-3 + - NIST-800-53-AU-2(a) + - audit_access_failed_aarch64 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,%23%23%20Unsuccessful%20file%20access%20%28any%20other%20opens%29%20This%20has%20to%20go%20last.%0A-a%20always%2Cexit%20-F%20arch%3Db32%20-S%20open%2Copenat%2Copenat2%2Copen_by_handle_at%20-F%20exit%3D-EACCES%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-access%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20openat%2Copenat2%2Copen_by_handle_at%20-F%20exit%3D-EACCES%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-access%0A-a%20always%2Cexit%20-F%20arch%3Db32%20-S%20open%2Copenat%2Copenat2%2Copen_by_handle_at%20-F%20exit%3D-EPERM%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-access%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20openat%2Copenat2%2Copen_by_handle_at%20-F%20exit%3D-EPERM%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-access%0A + mode: 0600 + path: /etc/audit/rules.d/30-ospp-v42-3-access-failed.rules + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && { grep -q aarch64 /proc/sys/kernel/osrelease; }; then + +cat << 'EOF' > /etc/audit/rules.d/30-ospp-v42-3-access-failed.rules +## Unsuccessful file access (any other opens) This has to go last. +-a always,exit -F arch=b32 -S open,openat,openat2,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-access +-a always,exit -F arch=b64 -S openat,openat2,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-access +-a always,exit -F arch=b32 -S open,openat,openat2,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-access +-a always,exit -F arch=b64 -S openat,openat2,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-access +EOF + +chmod o-rwx /etc/audit/rules.d/30-ospp-v42-3-access-failed.rules + +augenrules --load + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure auditing of unsuccessful file accesses (ppc64le) + Ensure that unsuccessful attempts to access a file are audited. + +The following rules configure audit as described above: +## Unsuccessful file access (any other opens) This has to go last. +-a always,exit -F arch=b64 -S open,openat,openat2,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-access +-a always,exit -F arch=b64 -S open,openat,openat2,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-access + +Load new Audit rules into kernel by running: +augenrules --load + +Note: This rule uses a special set of Audit rules to comply with OSPP 4.2.1. You may reuse this rule in different profiles. If you decide to do so, it is recommended that you inspect contents of the file closely and make sure that they are alligned with your needs. + 0582 + 0584 + 05885 + 0586 + 0846 + 0957 + AU-2(a) + FAU_GEN.1.1.c + SRG-OS-000458-GPOS-00203 + SRG-OS-000474-GPOS-00219 + SRG-OS-000475-GPOS-00220 + SRG-OS-000463-GPOS-00207 + SRG-OS-000465-GPOS-00209 + SRG-OS-000461-GPOS-00205 + Unsuccessful attempts to access a file might be signs of malicious activity happening within the system. Auditing of such activities helps in their monitoring and investigation. + + CCE-86001-5 + - name: Put contents into /etc/audit/rules.d/30-ospp-v42-3-access-failed.rules according + to policy + copy: + dest: /etc/audit/rules.d/30-ospp-v42-3-access-failed.rules + content: | + ## Unsuccessful file access (any other opens) This has to go last. + -a always,exit -F arch=b64 -S open,openat,openat2,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-access + -a always,exit -F arch=b64 -S open,openat,openat2,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-access + force: true + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ansible_architecture == "ppc64le" + tags: + - CCE-86001-5 + - NIST-800-53-AU-2(a) + - audit_access_failed_ppc64le + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Remove any permissions from other group + file: + path: /etc/audit/rules.d/30-ospp-v42-3-access-failed.rules + mode: o-rwx + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ansible_architecture == "ppc64le" + tags: + - CCE-86001-5 + - NIST-800-53-AU-2(a) + - audit_access_failed_ppc64le + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,%23%23%20Unsuccessful%20file%20access%20%28any%20other%20opens%29%20This%20has%20to%20go%20last.%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20open%2Copenat%2Copenat2%2Copen_by_handle_at%20-F%20exit%3D-EACCES%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-access%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20open%2Copenat%2Copenat2%2Copen_by_handle_at%20-F%20exit%3D-EPERM%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-access%0A + mode: 0600 + path: /etc/audit/rules.d/30-ospp-v42-3-access-failed.rules + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && { grep -q ppc64le /proc/sys/kernel/osrelease; }; then + +cat << 'EOF' > /etc/audit/rules.d/30-ospp-v42-3-access-failed.rules +## Unsuccessful file access (any other opens) This has to go last. +-a always,exit -F arch=b64 -S open,openat,openat2,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-access +-a always,exit -F arch=b64 -S open,openat,openat2,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-access +EOF + +chmod o-rwx /etc/audit/rules.d/30-ospp-v42-3-access-failed.rules + +augenrules --load + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure auditing of successful file accesses + Ensure that successful attempts to access a file are audited. + +The following rules configure audit as described above: +## Successful file access (any other opens) This has to go last. +## These next two are likely to result in a whole lot of events +-a always,exit -F arch=b32 -S open,openat,openat2,open_by_handle_at -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-access +-a always,exit -F arch=b64 -S open,openat,openat2,open_by_handle_at -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-access + +Load new Audit rules into kernel by running: +augenrules --load + +Note: This rule uses a special set of Audit rules to comply with OSPP 4.2.1. You may reuse this rule in different profiles. If you decide to do so, it is recommended that you inspect contents of the file closely and make sure that they are alligned with your needs. + 0582 + 0584 + 05885 + 0586 + 0846 + 0957 + AU-2(a) + FAU_GEN.1.1.c + SRG-OS-000458-GPOS-00203 + SRG-OS-000474-GPOS-00219 + SRG-OS-000475-GPOS-00220 + SRG-OS-000463-GPOS-00207 + SRG-OS-000465-GPOS-00209 + SRG-OS-000461-GPOS-00205 + Auditing of successful attempts to access a file helps in investigation of activities performed on the system. + + CCE-83653-6 + - name: Put contents into /etc/audit/rules.d/30-ospp-v42-3-access-success.rules according + to policy + copy: + dest: /etc/audit/rules.d/30-ospp-v42-3-access-success.rules + content: | + ## Successful file access (any other opens) This has to go last. + ## These next two are likely to result in a whole lot of events + -a always,exit -F arch=b32 -S open,openat,openat2,open_by_handle_at -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-access + -a always,exit -F arch=b64 -S open,openat,openat2,open_by_handle_at -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-access + force: true + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ( not ( ansible_architecture == "aarch64" ) and not ( ansible_architecture == + "ppc64le" ) ) + tags: + - CCE-83653-6 + - NIST-800-53-AU-2(a) + - audit_access_success + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Remove any permissions from other group + file: + path: /etc/audit/rules.d/30-ospp-v42-3-access-success.rules + mode: o-rwx + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ( not ( ansible_architecture == "aarch64" ) and not ( ansible_architecture == + "ppc64le" ) ) + tags: + - CCE-83653-6 + - NIST-800-53-AU-2(a) + - audit_access_success + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,%23%23%20Successful%20file%20access%20%28any%20other%20opens%29%20This%20has%20to%20go%20last.%0A%23%23%20These%20next%20two%20are%20likely%20to%20result%20in%20a%20whole%20lot%20of%20events%0A-a%20always%2Cexit%20-F%20arch%3Db32%20-S%20open%2Copenat%2Copen_by_handle_at%20-F%20success%3D1%20-F%20auid%26gt%3B%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dsuccessful-access%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20open%2Copenat%2Copen_by_handle_at%20-F%20success%3D1%20-F%20auid%26gt%3B%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dsuccessful-access + mode: 0600 + path: /etc/audit/rules.d/30-ospp-v42-3-access-success.rules + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && { ( ! ( grep -q aarch64 /proc/sys/kernel/osrelease ) && ! ( grep -q ppc64le /proc/sys/kernel/osrelease ) ); }; then + +cat << 'EOF' > /etc/audit/rules.d/30-ospp-v42-3-access-success.rules +## Successful file access (any other opens) This has to go last. +## These next two are likely to result in a whole lot of events +-a always,exit -F arch=b32 -S open,openat,openat2,open_by_handle_at -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-access +-a always,exit -F arch=b64 -S open,openat,openat2,open_by_handle_at -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-access +EOF + +chmod o-rwx /etc/audit/rules.d/30-ospp-v42-3-access-success.rules + +augenrules --load + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure auditing of successful file accesses (AArch64) + Ensure that successful attempts to access a file are audited. + +The following rules configure audit as described above: +## Successful file access (any other opens) This has to go last. +## These next two are likely to result in a whole lot of events +-a always,exit -F arch=b32 -S open,openat,openat2,open_by_handle_at -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-access +-a always,exit -F arch=b64 -S openat,openat2,open_by_handle_at -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-access + +Load new Audit rules into kernel by running: +augenrules --load + +Note: This rule uses a special set of Audit rules to comply with OSPP 4.2.1. You may reuse this rule in different profiles. If you decide to do so, it is recommended that you inspect contents of the file closely and make sure that they are alligned with your needs. + 0582 + 0584 + 05885 + 0586 + 0846 + 0957 + AU-2(a) + FAU_GEN.1.1.c + SRG-OS-000458-GPOS-00203 + SRG-OS-000474-GPOS-00219 + SRG-OS-000475-GPOS-00220 + SRG-OS-000463-GPOS-00207 + SRG-OS-000465-GPOS-00209 + SRG-OS-000461-GPOS-00205 + Auditing of successful attempts to access a file helps in investigation of activities performed on the system. + + CCE-85924-9 + - name: Put contents into /etc/audit/rules.d/30-ospp-v42-3-access-success.rules according + to policy + copy: + dest: /etc/audit/rules.d/30-ospp-v42-3-access-success.rules + content: | + ## Successful file access (any other opens) This has to go last. + ## These next two are likely to result in a whole lot of events + -a always,exit -F arch=b32 -S open,openat,openat2,open_by_handle_at -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-access + -a always,exit -F arch=b64 -S openat,openat2,open_by_handle_at -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-access + force: true + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ansible_architecture == "aarch64" + tags: + - CCE-85924-9 + - NIST-800-53-AU-2(a) + - audit_access_success_aarch64 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Remove any permissions from other group + file: + path: /etc/audit/rules.d/30-ospp-v42-3-access-success.rules + mode: o-rwx + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ansible_architecture == "aarch64" + tags: + - CCE-85924-9 + - NIST-800-53-AU-2(a) + - audit_access_success_aarch64 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,%23%23%20Successful%20file%20access%20%28any%20other%20opens%29%20This%20has%20to%20go%20last.%0A%23%23%20These%20next%20two%20are%20likely%20to%20result%20in%20a%20whole%20lot%20of%20events%0A-a%20always%2Cexit%20-F%20arch%3Db32%20-S%20open%2Copenat%2Copenat2%2Copen_by_handle_at%20-F%20success%3D1%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dsuccessful-access%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20openat%2Copenat2%2Copen_by_handle_at%20-F%20success%3D1%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dsuccessful-access%0A + mode: 0600 + path: /etc/audit/rules.d/30-ospp-v42-3-access-success.rules + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && { grep -q aarch64 /proc/sys/kernel/osrelease; }; then + +cat << 'EOF' > /etc/audit/rules.d/30-ospp-v42-3-access-success.rules +## Successful file access (any other opens) This has to go last. +## These next two are likely to result in a whole lot of events +-a always,exit -F arch=b32 -S open,openat,openat2,open_by_handle_at -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-access +-a always,exit -F arch=b64 -S openat,openat2,open_by_handle_at -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-access +EOF + +chmod o-rwx /etc/audit/rules.d/30-ospp-v42-3-access-success.rules + +augenrules --load + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure auditing of successful file accesses (ppc64le) + Ensure that successful attempts to access a file are audited. + +The following rules configure audit as described above: +## Successful file access (any other opens) This has to go last. +## These next two are likely to result in a whole lot of events +-a always,exit -F arch=b64 -S open,openat,openat2,open_by_handle_at -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-access + +Load new Audit rules into kernel by running: +augenrules --load + +Note: This rule uses a special set of Audit rules to comply with OSPP 4.2.1. You may reuse this rule in different profiles. If you decide to do so, it is recommended that you inspect contents of the file closely and make sure that they are alligned with your needs. + 0582 + 0584 + 05885 + 0586 + 0846 + 0957 + AU-2(a) + FAU_GEN.1.1.c + SRG-OS-000458-GPOS-00203 + SRG-OS-000474-GPOS-00219 + SRG-OS-000475-GPOS-00220 + SRG-OS-000463-GPOS-00207 + SRG-OS-000465-GPOS-00209 + SRG-OS-000461-GPOS-00205 + Auditing of successful attempts to access a file helps in investigation of activities performed on the system. + + CCE-85999-1 + - name: Put contents into /etc/audit/rules.d/30-ospp-v42-3-access-success.rules according + to policy + copy: + dest: /etc/audit/rules.d/30-ospp-v42-3-access-success.rules + content: | + ## Successful file access (any other opens) This has to go last. + ## These next two are likely to result in a whole lot of events + -a always,exit -F arch=b64 -S open,openat,openat2,open_by_handle_at -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-access + force: true + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ansible_architecture == "ppc64le" + tags: + - CCE-85999-1 + - NIST-800-53-AU-2(a) + - audit_access_success_ppc64le + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Remove any permissions from other group + file: + path: /etc/audit/rules.d/30-ospp-v42-3-access-success.rules + mode: o-rwx + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ansible_architecture == "ppc64le" + tags: + - CCE-85999-1 + - NIST-800-53-AU-2(a) + - audit_access_success_ppc64le + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,%23%23%20Successful%20file%20access%20%28any%20other%20opens%29%20This%20has%20to%20go%20last.%0A%23%23%20These%20next%20two%20are%20likely%20to%20result%20in%20a%20whole%20lot%20of%20events%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20open%2Copenat%2Copenat2%2Copen_by_handle_at%20-F%20success%3D1%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dsuccessful-access%0A + mode: 0600 + path: /etc/audit/rules.d/30-ospp-v42-3-access-success.rules + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && { grep -q ppc64le /proc/sys/kernel/osrelease; }; then + +cat << 'EOF' > /etc/audit/rules.d/30-ospp-v42-3-access-success.rules +## Successful file access (any other opens) This has to go last. +## These next two are likely to result in a whole lot of events +-a always,exit -F arch=b64 -S open,openat,openat2,open_by_handle_at -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-access +EOF + +chmod o-rwx /etc/audit/rules.d/30-ospp-v42-3-access-success.rules + +augenrules --load + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure basic parameters of Audit system + Perform basic configuration of Audit system. +Make sure that any previously defined rules are cleared, the auditing system is configured to handle sudden bursts of events, and in cases of failure, messages are configured to be directed to system log. + +The following rules configure audit as described above: +## First rule - delete all +-D + +## Increase the buffers to survive stress events. +## Make this bigger for busy systems +-b 8192 + +## This determine how long to wait in burst of events +--backlog_wait_time 60000 + +## Set failure mode to syslog +-f 1 + +Load new Audit rules into kernel by running: +augenrules --load + It might happen that Audit buffer configured by this rule is not large enough for certain use cases. If that is the case, the buffer size can be overridden by placing -b larger_buffer_size into a file within /etc/audit/rules.d directory, replacing larger_file_size with the desired value. The file name should start with a number higher than 10 and lower than 99. + AU-2(a) + FAU_GEN.1 + SRG-OS-000365-GPOS-00152 + SRG-OS-000475-GPOS-00220 + Without basic configurations, audit may not perform as expected. It may not be able to correctly handle events under stressful conditions, or log events in case of failure. + CCE-83670-0 + - name: Put contents into /etc/audit/rules.d/10-base-config.rules according to policy + copy: + dest: /etc/audit/rules.d/10-base-config.rules + content: |+ + ## First rule - delete all + -D + + ## Increase the buffers to survive stress events. + ## Make this bigger for busy systems + -b 8192 + + ## This determine how long to wait in burst of events + --backlog_wait_time 60000 + + ## Set failure mode to syslog + -f 1 + + force: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83670-0 + - NIST-800-53-AU-2(a) + - audit_basic_configuration + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Remove any permissions from other group + file: + path: /etc/audit/rules.d/10-base-config.rules + mode: o-rwx + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83670-0 + - NIST-800-53-AU-2(a) + - audit_basic_configuration + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,%23%23%20First%20rule%20-%20delete%20all%0A-D%0A%0A%23%23%20Increase%20the%20buffers%20to%20survive%20stress%20events.%0A%23%23%20Make%20this%20bigger%20for%20busy%20systems%0A-b%208192%0A%0A%23%23%20This%20determine%20how%20long%20to%20wait%20in%20burst%20of%20events%0A--backlog_wait_time%2060000%0A%0A%23%23%20Set%20failure%20mode%20to%20syslog%0A-f%201%0A + mode: 0600 + path: /etc/audit/rules.d/10-base-config.rules + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +cat << 'EOF' > /etc/audit/rules.d/10-base-config.rules +## First rule - delete all +-D + +## Increase the buffers to survive stress events. +## Make this bigger for busy systems +-b 8192 + +## This determine how long to wait in burst of events +--backlog_wait_time 60000 + +## Set failure mode to syslog +-f 1 + +EOF + +chmod o-rwx /etc/audit/rules.d/10-base-config.rules + +augenrules --load + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure auditing of unsuccessful file creations + Ensure that unsuccessful attempts to create a file are audited. + +The following rules configure audit as described above: +## Unsuccessful file creation (open with O_CREAT) +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b32 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b64 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b32 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b64 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create + +Load new Audit rules into kernel by running: +augenrules --load + +Note: This rule uses a special set of Audit rules to comply with OSPP 4.2.1. You may reuse this rule in different profiles. If you decide to do so, it is recommended that you inspect contents of the file closely and make sure that they are alligned with your needs. + AU-2(a) + FAU_GEN.1.1.c + SRG-OS-000458-GPOS-00203 + SRG-OS-000474-GPOS-00219 + SRG-OS-000475-GPOS-00220 + SRG-OS-000463-GPOS-00207 + SRG-OS-000465-GPOS-00209 + SRG-OS-000461-GPOS-00205 + Unsuccessful file creations might be a sign of a malicious action being performed on the system. Keeping log of such events helps in monitoring and investigation of such actions. + + CCE-83669-2 + - name: Put contents into /etc/audit/rules.d/30-ospp-v42-1-create-failed.rules according + to policy + copy: + dest: /etc/audit/rules.d/30-ospp-v42-1-create-failed.rules + content: | + ## Unsuccessful file creation (open with O_CREAT) + -a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create + -a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create + -a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create + -a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create + -a always,exit -F arch=b32 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create + -a always,exit -F arch=b64 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create + -a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create + -a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create + -a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create + -a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create + -a always,exit -F arch=b32 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create + -a always,exit -F arch=b64 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create + force: true + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ( not ( ansible_architecture == "aarch64" ) and not ( ansible_architecture == + "ppc64le" ) ) + tags: + - CCE-83669-2 + - NIST-800-53-AU-2(a) + - audit_create_failed + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Remove any permissions from other group + file: + path: /etc/audit/rules.d/30-ospp-v42-1-create-failed.rules + mode: o-rwx + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ( not ( ansible_architecture == "aarch64" ) and not ( ansible_architecture == + "ppc64le" ) ) + tags: + - CCE-83669-2 + - NIST-800-53-AU-2(a) + - audit_create_failed + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,%23%23%20Unsuccessful%20file%20creation%20%28open%20with%20O_CREAT%29%0A%23%23%20/etc/audit/rules.d/30-ospp-v42-1-create-failed.rules%0A-a%20always%2Cexit%20-F%20arch%3Db32%20-S%20openat%2Copen_by_handle_at%20-F%20a2%26amp%3B0100%20-F%20exit%3D-EACCES%20-F%20auid%26gt%3B%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-create%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20openat%2Copen_by_handle_at%20-F%20a2%26amp%3B0100%20-F%20exit%3D-EACCES%20-F%20auid%26gt%3B%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-create%0A-a%20always%2Cexit%20-F%20arch%3Db32%20-S%20open%20-F%20a1%26amp%3B0100%20-F%20exit%3D-EACCES%20-F%20auid%26gt%3B%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-create%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20open%20-F%20a1%26amp%3B0100%20-F%20exit%3D-EACCES%20-F%20auid%26gt%3B%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-create%0A-a%20always%2Cexit%20-F%20arch%3Db32%20-S%20creat%20-F%20exit%3D-EACCES%20-F%20auid%26gt%3B%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-create%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20creat%20-F%20exit%3D-EACCES%20-F%20auid%26gt%3B%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-create%0A-a%20always%2Cexit%20-F%20arch%3Db32%20-S%20openat%2Copen_by_handle_at%20-F%20a2%26amp%3B0100%20-F%20exit%3D-EPERM%20-F%20auid%26gt%3B%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-create%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20openat%2Copen_by_handle_at%20-F%20a2%26amp%3B0100%20-F%20exit%3D-EPERM%20-F%20auid%26gt%3B%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-create%0A-a%20always%2Cexit%20-F%20arch%3Db32%20-S%20open%20-F%20a1%26amp%3B0100%20-F%20exit%3D-EPERM%20-F%20auid%26gt%3B%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-create%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20open%20-F%20a1%26amp%3B0100%20-F%20exit%3D-EPERM%20-F%20auid%26gt%3B%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-create%0A-a%20always%2Cexit%20-F%20arch%3Db32%20-S%20creat%20-F%20exit%3D-EPERM%20-F%20auid%26gt%3B%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-create%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20creat%20-F%20exit%3D-EPERM%20-F%20auid%26gt%3B%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-create%0A + mode: 0600 + path: /etc/audit/rules.d/30-ospp-v42-1-create-failed.rules + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && { ( ! ( grep -q aarch64 /proc/sys/kernel/osrelease ) && ! ( grep -q ppc64le /proc/sys/kernel/osrelease ) ); }; then + +cat << 'EOF' > /etc/audit/rules.d/30-ospp-v42-1-create-failed.rules +## Unsuccessful file creation (open with O_CREAT) +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b32 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b64 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b32 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b64 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +EOF + +chmod o-rwx /etc/audit/rules.d/30-ospp-v42-1-create-failed.rules + +augenrules --load + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure auditing of unsuccessful file creations (AArch64) + Ensure that unsuccessful attempts to create a file are audited. + +The following rules configure audit as described above: +## Unsuccessful file creation (open with O_CREAT) +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b32 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b32 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create + +Load new Audit rules into kernel by running: +augenrules --load + +Note: This rule uses a special set of Audit rules to comply with OSPP 4.2.1. You may reuse this rule in different profiles. If you decide to do so, it is recommended that you inspect contents of the file closely and make sure that they are alligned with your needs. + AU-2(a) + FAU_GEN.1.1.c + SRG-OS-000458-GPOS-00203 + SRG-OS-000474-GPOS-00219 + SRG-OS-000475-GPOS-00220 + SRG-OS-000463-GPOS-00207 + SRG-OS-000465-GPOS-00209 + SRG-OS-000461-GPOS-00205 + Unsuccessful file creations might be a sign of a malicious action being performed on the system. Keeping log of such events helps in monitoring and investigation of such actions. + + CCE-85898-5 + - name: Put contents into /etc/audit/rules.d/30-ospp-v42-1-create-failed.rules according + to policy + copy: + dest: /etc/audit/rules.d/30-ospp-v42-1-create-failed.rules + content: | + ## Unsuccessful file creation (open with O_CREAT) + -a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create + -a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create + -a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create + -a always,exit -F arch=b32 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create + -a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create + -a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create + -a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create + -a always,exit -F arch=b32 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create + force: true + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ansible_architecture == "aarch64" + tags: + - CCE-85898-5 + - NIST-800-53-AU-2(a) + - audit_create_failed_aarch64 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Remove any permissions from other group + file: + path: /etc/audit/rules.d/30-ospp-v42-1-create-failed.rules + mode: o-rwx + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ansible_architecture == "aarch64" + tags: + - CCE-85898-5 + - NIST-800-53-AU-2(a) + - audit_create_failed_aarch64 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,%23%23%20Unsuccessful%20file%20creation%20%28open%20with%20O_CREAT%29%0A-a%20always%2Cexit%20-F%20arch%3Db32%20-S%20openat%2Copen_by_handle_at%20-F%20a2%26amp%3B0100%20-F%20exit%3D-EACCES%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-create%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20openat%2Copen_by_handle_at%20-F%20a2%26amp%3B0100%20-F%20exit%3D-EACCES%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-create%0A-a%20always%2Cexit%20-F%20arch%3Db32%20-S%20open%20-F%20a1%26amp%3B0100%20-F%20exit%3D-EACCES%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-create%0A-a%20always%2Cexit%20-F%20arch%3Db32%20-S%20creat%20-F%20exit%3D-EACCES%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-create%0A-a%20always%2Cexit%20-F%20arch%3Db32%20-S%20openat%2Copen_by_handle_at%20-F%20a2%26amp%3B0100%20-F%20exit%3D-EPERM%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-create%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20openat%2Copen_by_handle_at%20-F%20a2%26amp%3B0100%20-F%20exit%3D-EPERM%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-create%0A-a%20always%2Cexit%20-F%20arch%3Db32%20-S%20open%20-F%20a1%26amp%3B0100%20-F%20exit%3D-EPERM%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-create%0A-a%20always%2Cexit%20-F%20arch%3Db32%20-S%20creat%20-F%20exit%3D-EPERM%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-create%0A + mode: 0600 + path: /etc/audit/rules.d/30-ospp-v42-1-create-failed.rules + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && { grep -q aarch64 /proc/sys/kernel/osrelease; }; then + +cat << 'EOF' > /etc/audit/rules.d/30-ospp-v42-1-create-failed.rules +## Unsuccessful file creation (open with O_CREAT) +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b32 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b32 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +EOF + +chmod o-rwx /etc/audit/rules.d/30-ospp-v42-1-create-failed.rules + +augenrules --load + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure auditing of unsuccessful file creations (ppc64le) + Ensure that unsuccessful attempts to create a file are audited. + +The following rules configure audit as described above: +## Unsuccessful file creation (open with O_CREAT) +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b64 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b64 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create + +Load new Audit rules into kernel by running: +augenrules --load + +Note: This rule uses a special set of Audit rules to comply with OSPP 4.2.1. You may reuse this rule in different profiles. If you decide to do so, it is recommended that you inspect contents of the file closely and make sure that they are alligned with your needs. + AU-2(a) + FAU_GEN.1.1.c + SRG-OS-000458-GPOS-00203 + SRG-OS-000474-GPOS-00219 + SRG-OS-000475-GPOS-00220 + SRG-OS-000463-GPOS-00207 + SRG-OS-000465-GPOS-00209 + SRG-OS-000461-GPOS-00205 + Unsuccessful file creations might be a sign of a malicious action being performed on the system. Keeping log of such events helps in monitoring and investigation of such actions. + + CCE-85997-5 + - name: Put contents into /etc/audit/rules.d/30-ospp-v42-1-create-failed.rules according + to policy + copy: + dest: /etc/audit/rules.d/30-ospp-v42-1-create-failed.rules + content: | + ## Unsuccessful file creation (open with O_CREAT) + -a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create + -a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create + -a always,exit -F arch=b64 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create + -a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create + -a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create + -a always,exit -F arch=b64 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create + force: true + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ansible_architecture == "ppc64le" + tags: + - CCE-85997-5 + - NIST-800-53-AU-2(a) + - audit_create_failed_ppc64le + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Remove any permissions from other group + file: + path: /etc/audit/rules.d/30-ospp-v42-1-create-failed.rules + mode: o-rwx + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ansible_architecture == "ppc64le" + tags: + - CCE-85997-5 + - NIST-800-53-AU-2(a) + - audit_create_failed_ppc64le + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,%23%23%20Unsuccessful%20file%20creation%20%28open%20with%20O_CREAT%29%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20openat%2Copen_by_handle_at%20-F%20a2%260100%20-F%20exit%3D-EACCES%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-create%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20open%20-F%20a1%260100%20-F%20exit%3D-EACCES%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-create%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20creat%20-F%20exit%3D-EACCES%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-create%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20openat%2Copen_by_handle_at%20-F%20a2%260100%20-F%20exit%3D-EPERM%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-create%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20open%20-F%20a1%260100%20-F%20exit%3D-EPERM%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-create%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20creat%20-F%20exit%3D-EPERM%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-create%0A + mode: 0600 + path: /etc/audit/rules.d/30-ospp-v42-1-create-failed.rules + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && { grep -q ppc64le /proc/sys/kernel/osrelease; }; then + +cat << 'EOF' > /etc/audit/rules.d/30-ospp-v42-1-create-failed.rules +## Unsuccessful file creation (open with O_CREAT) +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b64 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b64 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +EOF + +chmod o-rwx /etc/audit/rules.d/30-ospp-v42-1-create-failed.rules + +augenrules --load + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure auditing of successful file creations + Ensure that successful attempts to create a file are audited. + +The following rules configure audit as described above: +## Successful file creation (open with O_CREAT) +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create +-a always,exit -F arch=b32 -S open -F a1&0100 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create +-a always,exit -F arch=b64 -S open -F a1&0100 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create +-a always,exit -F arch=b32 -S creat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create +-a always,exit -F arch=b64 -S creat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create + +Load new Audit rules into kernel by running: +augenrules --load + +Note: This rule uses a special set of Audit rules to comply with OSPP 4.2.1. You may reuse this rule in different profiles. If you decide to do so, it is recommended that you inspect contents of the file closely and make sure that they are alligned with your needs. + AU-2(a) + FAU_GEN.1.1.c + SRG-OS-000458-GPOS-00203 + SRG-OS-000474-GPOS-00219 + SRG-OS-000475-GPOS-00220 + SRG-OS-000463-GPOS-00207 + SRG-OS-000465-GPOS-00209 + SRG-OS-000461-GPOS-00205 + Auditing of successful attempts to create a file helps in investigation of actions which happened on the system. + + CCE-83668-4 + - name: Put contents into /etc/audit/rules.d/30-ospp-v42-1-create-success.rules according + to policy + copy: + dest: /etc/audit/rules.d/30-ospp-v42-1-create-success.rules + content: | + ## Successful file creation (open with O_CREAT) + -a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create + -a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create + -a always,exit -F arch=b32 -S open -F a1&0100 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create + -a always,exit -F arch=b64 -S open -F a1&0100 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create + -a always,exit -F arch=b32 -S creat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create + -a always,exit -F arch=b64 -S creat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create + force: true + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ( not ( ansible_architecture == "aarch64" ) and not ( ansible_architecture == + "ppc64le" ) ) + tags: + - CCE-83668-4 + - NIST-800-53-AU-2(a) + - audit_create_success + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Remove any permissions from other group + file: + path: /etc/audit/rules.d/30-ospp-v42-1-create-success.rules + mode: o-rwx + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ( not ( ansible_architecture == "aarch64" ) and not ( ansible_architecture == + "ppc64le" ) ) + tags: + - CCE-83668-4 + - NIST-800-53-AU-2(a) + - audit_create_success + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && { ( ! ( grep -q aarch64 /proc/sys/kernel/osrelease ) && ! ( grep -q ppc64le /proc/sys/kernel/osrelease ) ); }; then + +cat << 'EOF' > /etc/audit/rules.d/30-ospp-v42-1-create-success.rules +## Successful file creation (open with O_CREAT) +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create +-a always,exit -F arch=b32 -S open -F a1&0100 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create +-a always,exit -F arch=b64 -S open -F a1&0100 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create +-a always,exit -F arch=b32 -S creat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create +-a always,exit -F arch=b64 -S creat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create +EOF + +chmod o-rwx /etc/audit/rules.d/30-ospp-v42-1-create-success.rules + +augenrules --load + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure auditing of successful file creations (AArch64) + Ensure that successful attempts to create a file are audited. + +The following rules configure audit as described above: +## Successful file creation (open with O_CREAT) +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create +-a always,exit -F arch=b32 -S open -F a1&0100 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create +-a always,exit -F arch=b32 -S creat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create + +Load new Audit rules into kernel by running: +augenrules --load + +Note: This rule uses a special set of Audit rules to comply with OSPP 4.2.1. You may reuse this rule in different profiles. If you decide to do so, it is recommended that you inspect contents of the file closely and make sure that they are alligned with your needs. + AU-2(a) + FAU_GEN.1.1.c + SRG-OS-000458-GPOS-00203 + SRG-OS-000474-GPOS-00219 + SRG-OS-000475-GPOS-00220 + SRG-OS-000463-GPOS-00207 + SRG-OS-000465-GPOS-00209 + SRG-OS-000461-GPOS-00205 + Auditing of successful attempts to create a file helps in investigation of actions which happened on the system. + + CCE-85905-8 + - name: Put contents into /etc/audit/rules.d/30-ospp-v42-1-create-success.rules according + to policy + copy: + dest: /etc/audit/rules.d/30-ospp-v42-1-create-success.rules + content: | + ## Successful file creation (open with O_CREAT) + -a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create + -a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create + -a always,exit -F arch=b32 -S open -F a1&0100 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create + -a always,exit -F arch=b32 -S creat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create + force: true + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ansible_architecture == "aarch64" + tags: + - CCE-85905-8 + - NIST-800-53-AU-2(a) + - audit_create_success_aarch64 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Remove any permissions from other group + file: + path: /etc/audit/rules.d/30-ospp-v42-1-create-success.rules + mode: o-rwx + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ansible_architecture == "aarch64" + tags: + - CCE-85905-8 + - NIST-800-53-AU-2(a) + - audit_create_success_aarch64 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && { grep -q aarch64 /proc/sys/kernel/osrelease; }; then + +cat << 'EOF' > /etc/audit/rules.d/30-ospp-v42-1-create-success.rules +## Successful file creation (open with O_CREAT) +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create +-a always,exit -F arch=b32 -S open -F a1&0100 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create +-a always,exit -F arch=b32 -S creat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create +EOF + +chmod o-rwx /etc/audit/rules.d/30-ospp-v42-1-create-success.rules + +augenrules --load + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure auditing of successful file creations (ppc64le) + Ensure that successful attempts to create a file are audited. + +The following rules configure audit as described above: +## Successful file creation (open with O_CREAT) +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create +-a always,exit -F arch=b64 -S open -F a1&0100 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create +-a always,exit -F arch=b64 -S creat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create + +Load new Audit rules into kernel by running: +augenrules --load + +Note: This rule uses a special set of Audit rules to comply with OSPP 4.2.1. You may reuse this rule in different profiles. If you decide to do so, it is recommended that you inspect contents of the file closely and make sure that they are alligned with your needs. + AU-2(a) + FAU_GEN.1.1.c + SRG-OS-000458-GPOS-00203 + SRG-OS-000474-GPOS-00219 + SRG-OS-000475-GPOS-00220 + SRG-OS-000463-GPOS-00207 + SRG-OS-000465-GPOS-00209 + SRG-OS-000461-GPOS-00205 + Auditing of successful attempts to create a file helps in investigation of actions which happened on the system. + + CCE-85985-0 + - name: Put contents into /etc/audit/rules.d/30-ospp-v42-1-create-success.rules according + to policy + copy: + dest: /etc/audit/rules.d/30-ospp-v42-1-create-success.rules + content: | + ## Successful file creation (open with O_CREAT) + -a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create + -a always,exit -F arch=b64 -S open -F a1&0100 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create + -a always,exit -F arch=b64 -S creat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create + force: true + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ansible_architecture == "ppc64le" + tags: + - CCE-85985-0 + - NIST-800-53-AU-2(a) + - audit_create_success_ppc64le + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Remove any permissions from other group + file: + path: /etc/audit/rules.d/30-ospp-v42-1-create-success.rules + mode: o-rwx + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ansible_architecture == "ppc64le" + tags: + - CCE-85985-0 + - NIST-800-53-AU-2(a) + - audit_create_success_ppc64le + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && { grep -q ppc64le /proc/sys/kernel/osrelease; }; then + +cat << 'EOF' > /etc/audit/rules.d/30-ospp-v42-1-create-success.rules +## Successful file creation (open with O_CREAT) +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create +-a always,exit -F arch=b64 -S open -F a1&0100 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create +-a always,exit -F arch=b64 -S creat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create +EOF + +chmod o-rwx /etc/audit/rules.d/30-ospp-v42-1-create-success.rules + +augenrules --load + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure auditing of unsuccessful file deletions + Ensure that unsuccessful attempts to delete a file are audited. + +The following rules configure audit as described above: +## Unsuccessful file delete +-a always,exit -F arch=b32 -S unlink,unlinkat,rename,renameat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete +-a always,exit -F arch=b64 -S unlink,unlinkat,rename,renameat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete +-a always,exit -F arch=b32 -S unlink,unlinkat,rename,renameat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete +-a always,exit -F arch=b64 -S unlink,unlinkat,rename,renameat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete + +Load new Audit rules into kernel by running: +augenrules --load + +Note: This rule uses a special set of Audit rules to comply with OSPP 4.2.1. You may reuse this rule in different profiles. If you decide to do so, it is recommended that you inspect contents of the file closely and make sure that they are alligned with your needs. + AU-2(a) + FAU_GEN.1.1.c + SRG-OS-000458-GPOS-00203 + SRG-OS-000474-GPOS-00219 + SRG-OS-000475-GPOS-00220 + SRG-OS-000463-GPOS-00207 + SRG-OS-000465-GPOS-00209 + SRG-OS-000461-GPOS-00205 + SRG-OS-000468-GPOS-00212 + Unsuccessful attempts to delete a file might be signs of malicious activities. Auditing of such events help in monitoring and investigating of such activities. + + CCE-83667-6 + - name: Put contents into /etc/audit/rules.d/30-ospp-v42-4-delete-failed.rules according + to policy + copy: + dest: /etc/audit/rules.d/30-ospp-v42-4-delete-failed.rules + content: | + ## Unsuccessful file delete + -a always,exit -F arch=b32 -S unlink,unlinkat,rename,renameat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete + -a always,exit -F arch=b64 -S unlink,unlinkat,rename,renameat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete + -a always,exit -F arch=b32 -S unlink,unlinkat,rename,renameat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete + -a always,exit -F arch=b64 -S unlink,unlinkat,rename,renameat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete + force: true + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ( not ( ansible_architecture == "aarch64" ) and not ( ansible_architecture == + "ppc64le" ) ) + tags: + - CCE-83667-6 + - NIST-800-53-AU-2(a) + - audit_delete_failed + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Remove any permissions from other group + file: + path: /etc/audit/rules.d/30-ospp-v42-4-delete-failed.rules + mode: o-rwx + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ( not ( ansible_architecture == "aarch64" ) and not ( ansible_architecture == + "ppc64le" ) ) + tags: + - CCE-83667-6 + - NIST-800-53-AU-2(a) + - audit_delete_failed + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,%23%23%20Unsuccessful%20file%20delete%0A-a%20always%2Cexit%20-F%20arch%3Db32%20-S%20unlink%2Cunlinkat%2Crename%2Crenameat%20-F%20exit%3D-EACCES%20-F%20auid%26gt%3B%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-delete%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20unlink%2Cunlinkat%2Crename%2Crenameat%20-F%20exit%3D-EACCES%20-F%20auid%26gt%3B%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-delete%0A-a%20always%2Cexit%20-F%20arch%3Db32%20-S%20unlink%2Cunlinkat%2Crename%2Crenameat%20-F%20exit%3D-EPERM%20-F%20auid%26gt%3B%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-delete%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20unlink%2Cunlinkat%2Crename%2Crenameat%20-F%20exit%3D-EPERM%20-F%20auid%26gt%3B%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-delete + mode: 0600 + path: /etc/audit/rules.d/30-ospp-v42-4-delete-failed.rules + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && { ( ! ( grep -q aarch64 /proc/sys/kernel/osrelease ) && ! ( grep -q ppc64le /proc/sys/kernel/osrelease ) ); }; then + +cat << 'EOF' > /etc/audit/rules.d/30-ospp-v42-4-delete-failed.rules +## Unsuccessful file delete +-a always,exit -F arch=b32 -S unlink,unlinkat,rename,renameat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete +-a always,exit -F arch=b64 -S unlink,unlinkat,rename,renameat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete +-a always,exit -F arch=b32 -S unlink,unlinkat,rename,renameat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete +-a always,exit -F arch=b64 -S unlink,unlinkat,rename,renameat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete +EOF + +chmod o-rwx /etc/audit/rules.d/30-ospp-v42-4-delete-failed.rules + +augenrules --load + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure auditing of unsuccessful file deletions (AArch64) + Ensure that unsuccessful attempts to delete a file are audited. + +The following rules configure audit as described above: +## Unsuccessful file delete +-a always,exit -F arch=b32 -S unlink,unlinkat,rename,renameat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete +-a always,exit -F arch=b64 -S unlinkat,renameat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete +-a always,exit -F arch=b32 -S unlink,unlinkat,rename,renameat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete +-a always,exit -F arch=b64 -S unlinkat,renameat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete + +Load new Audit rules into kernel by running: +augenrules --load + +Note: This rule uses a special set of Audit rules to comply with OSPP 4.2.1. You may reuse this rule in different profiles. If you decide to do so, it is recommended that you inspect contents of the file closely and make sure that they are alligned with your needs. + AU-2(a) + FAU_GEN.1.1.c + SRG-OS-000458-GPOS-00203 + SRG-OS-000474-GPOS-00219 + SRG-OS-000475-GPOS-00220 + SRG-OS-000463-GPOS-00207 + SRG-OS-000465-GPOS-00209 + SRG-OS-000461-GPOS-00205 + SRG-OS-000468-GPOS-00212 + Unsuccessful attempts to delete a file might be signs of malicious activities. Auditing of such events help in monitoring and investigating of such activities. + + CCE-85937-1 + - name: Put contents into /etc/audit/rules.d/30-ospp-v42-4-delete-failed.rules according + to policy + copy: + dest: /etc/audit/rules.d/30-ospp-v42-4-delete-failed.rules + content: | + ## Unsuccessful file delete + -a always,exit -F arch=b32 -S unlink,unlinkat,rename,renameat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete + -a always,exit -F arch=b64 -S unlinkat,renameat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete + -a always,exit -F arch=b32 -S unlink,unlinkat,rename,renameat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete + -a always,exit -F arch=b64 -S unlinkat,renameat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete + force: true + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ansible_architecture == "aarch64" + tags: + - CCE-85937-1 + - NIST-800-53-AU-2(a) + - audit_delete_failed_aarch64 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Remove any permissions from other group + file: + path: /etc/audit/rules.d/30-ospp-v42-4-delete-failed.rules + mode: o-rwx + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ansible_architecture == "aarch64" + tags: + - CCE-85937-1 + - NIST-800-53-AU-2(a) + - audit_delete_failed_aarch64 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,%23%23%20Unsuccessful%20file%20delete%0A-a%20always%2Cexit%20-F%20arch%3Db32%20-S%20unlink%2Cunlinkat%2Crename%2Crenameat%20-F%20exit%3D-EACCES%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-delete%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20unlinkat%2Crenameat%20-F%20exit%3D-EACCES%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-delete%0A-a%20always%2Cexit%20-F%20arch%3Db32%20-S%20unlink%2Cunlinkat%2Crename%2Crenameat%20-F%20exit%3D-EPERM%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-delete%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20unlinkat%2Crenameat%20-F%20exit%3D-EPERM%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-delete + mode: 0600 + path: /etc/audit/rules.d/30-ospp-v42-4-delete-failed.rules + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && { grep -q aarch64 /proc/sys/kernel/osrelease; }; then + +cat << 'EOF' > /etc/audit/rules.d/30-ospp-v42-4-delete-failed.rules +## Unsuccessful file delete +-a always,exit -F arch=b32 -S unlink,unlinkat,rename,renameat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete +-a always,exit -F arch=b64 -S unlinkat,renameat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete +-a always,exit -F arch=b32 -S unlink,unlinkat,rename,renameat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete +-a always,exit -F arch=b64 -S unlinkat,renameat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete +EOF + +chmod o-rwx /etc/audit/rules.d/30-ospp-v42-4-delete-failed.rules + +augenrules --load + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure auditing of unsuccessful file deletions (ppc64le) + Ensure that unsuccessful attempts to delete a file are audited. + +The following rules configure audit as described above: +## Unsuccessful file delete +-a always,exit -F arch=b64 -S unlink,unlinkat,rename,renameat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete +-a always,exit -F arch=b64 -S unlink,unlinkat,rename,renameat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete + +Load new Audit rules into kernel by running: +augenrules --load + +Note: This rule uses a special set of Audit rules to comply with OSPP 4.2.1. You may reuse this rule in different profiles. If you decide to do so, it is recommended that you inspect contents of the file closely and make sure that they are alligned with your needs. + AU-2(a) + FAU_GEN.1.1.c + SRG-OS-000458-GPOS-00203 + SRG-OS-000474-GPOS-00219 + SRG-OS-000475-GPOS-00220 + SRG-OS-000463-GPOS-00207 + SRG-OS-000465-GPOS-00209 + SRG-OS-000461-GPOS-00205 + SRG-OS-000468-GPOS-00212 + Unsuccessful attempts to delete a file might be signs of malicious activities. Auditing of such events help in monitoring and investigating of such activities. + + CCE-90787-3 + - name: Put contents into /etc/audit/rules.d/30-ospp-v42-4-delete-failed.rules according + to policy + copy: + dest: /etc/audit/rules.d/30-ospp-v42-4-delete-failed.rules + content: | + ## Unsuccessful file delete + -a always,exit -F arch=b64 -S unlink,unlinkat,rename,renameat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete + -a always,exit -F arch=b64 -S unlink,unlinkat,rename,renameat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete + force: true + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ansible_architecture == "ppc64le" + tags: + - CCE-90787-3 + - NIST-800-53-AU-2(a) + - audit_delete_failed_ppc64le + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Remove any permissions from other group + file: + path: /etc/audit/rules.d/30-ospp-v42-4-delete-failed.rules + mode: o-rwx + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ansible_architecture == "ppc64le" + tags: + - CCE-90787-3 + - NIST-800-53-AU-2(a) + - audit_delete_failed_ppc64le + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,%23%23%20Unsuccessful%20file%20delete%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20unlink%2Cunlinkat%2Crename%2Crenameat%20-F%20exit%3D-EACCES%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-delete%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20unlink%2Cunlinkat%2Crename%2Crenameat%20-F%20exit%3D-EPERM%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-delete + mode: 0600 + path: /etc/audit/rules.d/30-ospp-v42-4-delete-failed.rules + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && { grep -q ppc64le /proc/sys/kernel/osrelease; }; then + +cat << 'EOF' > /etc/audit/rules.d/30-ospp-v42-4-delete-failed.rules +## Unsuccessful file delete +-a always,exit -F arch=b64 -S unlink,unlinkat,rename,renameat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete +-a always,exit -F arch=b64 -S unlink,unlinkat,rename,renameat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete +EOF + +chmod o-rwx /etc/audit/rules.d/30-ospp-v42-4-delete-failed.rules + +augenrules --load + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure auditing of successful file deletions + Ensure that successful attempts to delete a file are audited. + +The following rules configure audit as described above: +## Successful file delete +-a always,exit -F arch=b32 -S unlink,unlinkat,rename,renameat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-delete +-a always,exit -F arch=b64 -S unlink,unlinkat,rename,renameat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-delete + +Load new Audit rules into kernel by running: +augenrules --load + +Note: This rule uses a special set of Audit rules to comply with OSPP 4.2.1. You may reuse this rule in different profiles. If you decide to do so, it is recommended that you inspect contents of the file closely and make sure that they are alligned with your needs. + AU-2(a) + FAU_GEN.1.1.c + SRG-OS-000458-GPOS-00203 + SRG-OS-000474-GPOS-00219 + SRG-OS-000475-GPOS-00220 + SRG-OS-000463-GPOS-00207 + SRG-OS-000465-GPOS-00209 + SRG-OS-000461-GPOS-00205 + SRG-OS-000468-GPOS-00212 + Auditing of successful attempts to delete a file may help in monitoring and investigation of activities performed on the system. + + CCE-83680-9 + - name: Put contents into /etc/audit/rules.d/30-ospp-v42-4-delete-success.rules according + to policy + copy: + dest: /etc/audit/rules.d/30-ospp-v42-4-delete-success.rules + content: | + ## Successful file delete + -a always,exit -F arch=b32 -S unlink,unlinkat,rename,renameat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-delete + -a always,exit -F arch=b64 -S unlink,unlinkat,rename,renameat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-delete + force: true + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ( not ( ansible_architecture == "aarch64" ) and not ( ansible_architecture == + "ppc64le" ) ) + tags: + - CCE-83680-9 + - NIST-800-53-AU-2(a) + - audit_delete_success + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Remove any permissions from other group + file: + path: /etc/audit/rules.d/30-ospp-v42-4-delete-success.rules + mode: o-rwx + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ( not ( ansible_architecture == "aarch64" ) and not ( ansible_architecture == + "ppc64le" ) ) + tags: + - CCE-83680-9 + - NIST-800-53-AU-2(a) + - audit_delete_success + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + --- + +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,{{ %23%23%20Successful%20file%20delete%0A-a%20always%2Cexit%20-F%20arch%3Db32%20-S%20unlink%2Cunlinkat%2Crename%2Crenameat%20-F%20success%3D1%20-F%20auid%26gt%3B%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dsuccessful-delete%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20unlink%2Cunlinkat%2Crename%2Crenameat%20-F%20success%3D1%20-F%20auid%26gt%3B%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dsuccessful-delete }} + mode: 0600 + path: /etc/audit/rules.d/30-ospp-v42-4-delete-success.rules + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && { ( ! ( grep -q aarch64 /proc/sys/kernel/osrelease ) && ! ( grep -q ppc64le /proc/sys/kernel/osrelease ) ); }; then + +cat << 'EOF' > /etc/audit/rules.d/30-ospp-v42-4-delete-success.rules +## Successful file delete +-a always,exit -F arch=b32 -S unlink,unlinkat,rename,renameat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-delete +-a always,exit -F arch=b64 -S unlink,unlinkat,rename,renameat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-delete +EOF + +chmod o-rwx /etc/audit/rules.d/30-ospp-v42-4-delete-success.rules + +augenrules --load + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure auditing of successful file deletions (AArch64) + Ensure that successful attempts to delete a file are audited. + +The following rules configure audit as described above: +## Successful file delete +-a always,exit -F arch=b32 -S unlink,unlinkat,rename,renameat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-delete +-a always,exit -F arch=b64 -S unlinkat,renameat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-delete + +Load new Audit rules into kernel by running: +augenrules --load + +Note: This rule uses a special set of Audit rules to comply with OSPP 4.2.1. You may reuse this rule in different profiles. If you decide to do so, it is recommended that you inspect contents of the file closely and make sure that they are alligned with your needs. + AU-2(a) + FAU_GEN.1.1.c + SRG-OS-000458-GPOS-00203 + SRG-OS-000474-GPOS-00219 + SRG-OS-000475-GPOS-00220 + SRG-OS-000463-GPOS-00207 + SRG-OS-000465-GPOS-00209 + SRG-OS-000461-GPOS-00205 + SRG-OS-000468-GPOS-00212 + Auditing of successful attempts to delete a file may help in monitoring and investigation of activities performed on the system. + + CCE-85939-7 + - name: Put contents into /etc/audit/rules.d/30-ospp-v42-4-delete-success.rules according + to policy + copy: + dest: /etc/audit/rules.d/30-ospp-v42-4-delete-success.rules + content: | + ## Successful file delete + -a always,exit -F arch=b32 -S unlink,unlinkat,rename,renameat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-delete + -a always,exit -F arch=b64 -S unlinkat,renameat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-delete + force: true + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ansible_architecture == "aarch64" + tags: + - CCE-85939-7 + - NIST-800-53-AU-2(a) + - audit_delete_success_aarch64 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Remove any permissions from other group + file: + path: /etc/audit/rules.d/30-ospp-v42-4-delete-success.rules + mode: o-rwx + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ansible_architecture == "aarch64" + tags: + - CCE-85939-7 + - NIST-800-53-AU-2(a) + - audit_delete_success_aarch64 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + --- + +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,{{ %23%23%20Successful%20file%20delete%0A-a%20always%2Cexit%20-F%20arch%3Db32%20-S%20unlink%2Cunlinkat%2Crename%2Crenameat%20-F%20success%3D1%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dsuccessful-delete%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20unlinkat%2Crenameat%20-F%20success%3D1%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dsuccessful-delete }} + mode: 0600 + path: /etc/audit/rules.d/30-ospp-v42-4-delete-success.rules + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && { grep -q aarch64 /proc/sys/kernel/osrelease; }; then + +cat << 'EOF' > /etc/audit/rules.d/30-ospp-v42-4-delete-success.rules +## Successful file delete +-a always,exit -F arch=b32 -S unlink,unlinkat,rename,renameat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-delete +-a always,exit -F arch=b64 -S unlinkat,renameat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-delete +EOF + +chmod o-rwx /etc/audit/rules.d/30-ospp-v42-4-delete-success.rules + +augenrules --load + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure auditing of successful file deletions (ppc64le) + Ensure that successful attempts to delete a file are audited. + +The following rules configure audit as described above: +## Successful file delete +-a always,exit -F arch=b64 -S unlink,unlinkat,rename,renameat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-delete + +Load new Audit rules into kernel by running: +augenrules --load + +Note: This rule uses a special set of Audit rules to comply with OSPP 4.2.1. You may reuse this rule in different profiles. If you decide to do so, it is recommended that you inspect contents of the file closely and make sure that they are alligned with your needs. + AU-2(a) + FAU_GEN.1.1.c + SRG-OS-000458-GPOS-00203 + SRG-OS-000474-GPOS-00219 + SRG-OS-000475-GPOS-00220 + SRG-OS-000463-GPOS-00207 + SRG-OS-000465-GPOS-00209 + SRG-OS-000461-GPOS-00205 + SRG-OS-000468-GPOS-00212 + Auditing of successful attempts to delete a file may help in monitoring and investigation of activities performed on the system. + + CCE-90789-9 + - name: Put contents into /etc/audit/rules.d/30-ospp-v42-4-delete-success.rules according + to policy + copy: + dest: /etc/audit/rules.d/30-ospp-v42-4-delete-success.rules + content: | + ## Successful file delete + -a always,exit -F arch=b64 -S unlink,unlinkat,rename,renameat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-delete + force: true + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ansible_architecture == "ppc64le" + tags: + - CCE-90789-9 + - NIST-800-53-AU-2(a) + - audit_delete_success_ppc64le + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Remove any permissions from other group + file: + path: /etc/audit/rules.d/30-ospp-v42-4-delete-success.rules + mode: o-rwx + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ansible_architecture == "ppc64le" + tags: + - CCE-90789-9 + - NIST-800-53-AU-2(a) + - audit_delete_success_ppc64le + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + --- + +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,{{ %23%23%20Successful%20file%20delete%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20unlink%2Cunlinkat%2Crename%2Crenameat%20-F%20success%3D1%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dsuccessful-delete }} + mode: 0600 + path: /etc/audit/rules.d/30-ospp-v42-4-delete-success.rules + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && { grep -q ppc64le /proc/sys/kernel/osrelease; }; then + +cat << 'EOF' > /etc/audit/rules.d/30-ospp-v42-4-delete-success.rules +## Successful file delete +-a always,exit -F arch=b64 -S unlink,unlinkat,rename,renameat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-delete +EOF + +chmod o-rwx /etc/audit/rules.d/30-ospp-v42-4-delete-success.rules + +augenrules --load + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure immutable Audit login UIDs + Configure kernel to prevent modification of login UIDs once they are set. +Changing login UIDs while this configuration is enforced requires special capabilities which +are not available to unprivileged users. + +The following rules configure audit as described above: +## Make the loginuid immutable. This prevents tampering with the auid. +--loginuid-immutable + +Load new Audit rules into kernel by running: +augenrules --load + CCI-000162 + CCI-000163 + CCI-000164 + AU-2(a) + FAU_GEN.1.2 + SRG-OS-000462-GPOS-00206 + SRG-OS-000475-GPOS-00220 + SRG-OS-000057-GPOS-00027 + SRG-OS-000058-GPOS-00028 + SRG-OS-000059-GPOS-00029 + If modification of login UIDs is not prevented, they can be changed by unprivileged users and +make auditing complicated or impossible. + CCE-83673-4 + - name: Put contents into /etc/audit/rules.d/11-loginuid.rules according to policy + copy: + dest: /etc/audit/rules.d/11-loginuid.rules + content: |+ + ## Make the loginuid immutable. This prevents tampering with the auid. + --loginuid-immutable + + force: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83673-4 + - NIST-800-53-AU-2(a) + - audit_immutable_login_uids + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Remove any permissions from other group + file: + path: /etc/audit/rules.d/11-loginuid.rules + mode: o-rwx + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83673-4 + - NIST-800-53-AU-2(a) + - audit_immutable_login_uids + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,%23%23%20Make%20the%20loginuid%20immutable.%20This%20prevents%20tampering%20with%20the%20auid.%0A--loginuid-immutable + mode: 0600 + path: /etc/audit/rules.d/11-loginuid.rules + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +cat << 'EOF' > /etc/audit/rules.d/11-loginuid.rules +## Make the loginuid immutable. This prevents tampering with the auid. +--loginuid-immutable + +EOF + +chmod o-rwx /etc/audit/rules.d/11-loginuid.rules + +augenrules --load + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure auditing of unsuccessful file modifications + Ensure that unsuccessful attempts to modify a file are audited. + +The following rules configure audit as described above: +## Unsuccessful file modifications (open for write or truncate) +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b32 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b32 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification + +Load new Audit rules into kernel by running: +augenrules --load + +Note: This rule uses a special set of Audit rules to comply with OSPP 4.2.1. You may reuse this rule in different profiles. If you decide to do so, it is recommended that you inspect contents of the file closely and make sure that they are alligned with your needs. + AU-2(a) + FAU_GEN.1.1.c + SRG-OS-000458-GPOS-00203 + SRG-OS-000474-GPOS-00219 + SRG-OS-000475-GPOS-00220 + SRG-OS-000463-GPOS-00207 + SRG-OS-000465-GPOS-00209 + SRG-OS-000461-GPOS-00205 + Unsuccessful file modifications might be a sign of a malicious action being performed on the system. Auditing of such events helps in detection and investigation of such actions. + + CCE-83671-8 + - name: Put contents into /etc/audit/rules.d/30-ospp-v42-2-modify-failed.rules according + to policy + copy: + dest: /etc/audit/rules.d/30-ospp-v42-2-modify-failed.rules + content: | + ## Unsuccessful file modifications (open for write or truncate) + -a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification + -a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification + -a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification + -a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification + -a always,exit -F arch=b32 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification + -a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification + -a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification + -a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification + -a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification + -a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification + -a always,exit -F arch=b32 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification + -a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification + force: true + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ( not ( ansible_architecture == "aarch64" ) and not ( ansible_architecture == + "ppc64le" ) ) + tags: + - CCE-83671-8 + - NIST-800-53-AU-2(a) + - audit_modify_failed + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Remove any permissions from other group + file: + path: /etc/audit/rules.d/30-ospp-v42-2-modify-failed.rules + mode: o-rwx + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ( not ( ansible_architecture == "aarch64" ) and not ( ansible_architecture == + "ppc64le" ) ) + tags: + - CCE-83671-8 + - NIST-800-53-AU-2(a) + - audit_modify_failed + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,%23%23%20Unsuccessful%20file%20modifications%20%28open%20for%20write%20or%20truncate%29%0A-a%20always%2Cexit%20-F%20arch%3Db32%20-S%20openat%2Copen_by_handle_at%20-F%20a2%2601003%20-F%20exit%3D-EACCES%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-modification%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20openat%2Copen_by_handle_at%20-F%20a2%2601003%20-F%20exit%3D-EACCES%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-modification%0A-a%20always%2Cexit%20-F%20arch%3Db32%20-S%20open%20-F%20a1%2601003%20-F%20exit%3D-EACCES%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-modification%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20open%20-F%20a1%2601003%20-F%20exit%3D-EACCES%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-modification%0A-a%20always%2Cexit%20-F%20arch%3Db32%20-S%20truncate%2Cftruncate%20-F%20exit%3D-EACCES%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-modification%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20truncate%2Cftruncate%20-F%20exit%3D-EACCES%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-modification%0A-a%20always%2Cexit%20-F%20arch%3Db32%20-S%20openat%2Copen_by_handle_at%20-F%20a2%2601003%20-F%20exit%3D-EPERM%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-modification%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20openat%2Copen_by_handle_at%20-F%20a2%2601003%20-F%20exit%3D-EPERM%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-modification%0A-a%20always%2Cexit%20-F%20arch%3Db32%20-S%20open%20-F%20a1%2601003%20-F%20exit%3D-EPERM%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-modification%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20open%20-F%20a1%2601003%20-F%20exit%3D-EPERM%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-modification%0A-a%20always%2Cexit%20-F%20arch%3Db32%20-S%20truncate%2Cftruncate%20-F%20exit%3D-EPERM%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-modification%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20truncate%2Cftruncate%20-F%20exit%3D-EPERM%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-modification%0A + mode: 0600 + path: /etc/audit/rules.d/30-ospp-v42-2-modify-failed.rules + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && { ( ! ( grep -q aarch64 /proc/sys/kernel/osrelease ) && ! ( grep -q ppc64le /proc/sys/kernel/osrelease ) ); }; then + +cat << 'EOF' > /etc/audit/rules.d/30-ospp-v42-2-modify-failed.rules +## Unsuccessful file modifications (open for write or truncate) +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b32 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b32 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +EOF + +chmod o-rwx /etc/audit/rules.d/30-ospp-v42-2-modify-failed.rules + +augenrules --load + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure auditing of unsuccessful file modifications (AARch64) + Ensure that unsuccessful attempts to modify a file are audited. + +The following rules configure audit as described above: +## Unsuccessful file modifications (open for write or truncate) +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b32 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b32 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification + +Load new Audit rules into kernel by running: +augenrules --load + +Note: This rule uses a special set of Audit rules to comply with OSPP 4.2.1. You may reuse this rule in different profiles. If you decide to do so, it is recommended that you inspect contents of the file closely and make sure that they are alligned with your needs. + AU-2(a) + FAU_GEN.1.1.c + SRG-OS-000458-GPOS-00203 + SRG-OS-000474-GPOS-00219 + SRG-OS-000475-GPOS-00220 + SRG-OS-000463-GPOS-00207 + SRG-OS-000465-GPOS-00209 + SRG-OS-000461-GPOS-00205 + Unsuccessful file modifications might be a sign of a malicious action being performed on the system. Auditing of such events helps in detection and investigation of such actions. + + CCE-85907-4 + - name: Put contents into /etc/audit/rules.d/30-ospp-v42-2-modify-failed.rules according + to policy + copy: + dest: /etc/audit/rules.d/30-ospp-v42-2-modify-failed.rules + content: | + ## Unsuccessful file modifications (open for write or truncate) + -a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification + -a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification + -a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification + -a always,exit -F arch=b32 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification + -a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification + -a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification + -a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification + -a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification + -a always,exit -F arch=b32 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification + -a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification + force: true + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ansible_architecture == "aarch64" + tags: + - CCE-85907-4 + - NIST-800-53-AU-2(a) + - audit_modify_failed_aarch64 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Remove any permissions from other group + file: + path: /etc/audit/rules.d/30-ospp-v42-2-modify-failed.rules + mode: o-rwx + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ansible_architecture == "aarch64" + tags: + - CCE-85907-4 + - NIST-800-53-AU-2(a) + - audit_modify_failed_aarch64 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,%23%23%20Unsuccessful%20file%20modifications%20%28open%20for%20write%20or%20truncate%29%0A-a%20always%2Cexit%20-F%20arch%3Db32%20-S%20openat%2Copen_by_handle_at%20-F%20a2%26amp%3B01003%20-F%20exit%3D-EACCES%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-modification%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20openat%2Copen_by_handle_at%20-F%20a2%26amp%3B01003%20-F%20exit%3D-EACCES%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-modification%0A-a%20always%2Cexit%20-F%20arch%3Db32%20-S%20open%20-F%20a1%26amp%3B01003%20-F%20exit%3D-EACCES%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-modification%0A-a%20always%2Cexit%20-F%20arch%3Db32%20-S%20truncate%2Cftruncate%20-F%20exit%3D-EACCES%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-modification%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20truncate%2Cftruncate%20-F%20exit%3D-EACCES%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-modification%0A-a%20always%2Cexit%20-F%20arch%3Db32%20-S%20openat%2Copen_by_handle_at%20-F%20a2%26amp%3B01003%20-F%20exit%3D-EPERM%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-modification%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20openat%2Copen_by_handle_at%20-F%20a2%26amp%3B01003%20-F%20exit%3D-EPERM%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-modification%0A-a%20always%2Cexit%20-F%20arch%3Db32%20-S%20open%20-F%20a1%26amp%3B01003%20-F%20exit%3D-EPERM%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-modification%0A-a%20always%2Cexit%20-F%20arch%3Db32%20-S%20truncate%2Cftruncate%20-F%20exit%3D-EPERM%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-modification%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20truncate%2Cftruncate%20-F%20exit%3D-EPERM%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-modification%0A + mode: 0600 + path: /etc/audit/rules.d/30-ospp-v42-2-modify-failed.rules + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && { grep -q aarch64 /proc/sys/kernel/osrelease; }; then + +cat << 'EOF' > /etc/audit/rules.d/30-ospp-v42-2-modify-failed.rules +## Unsuccessful file modifications (open for write or truncate) +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b32 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b32 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +EOF + +chmod o-rwx /etc/audit/rules.d/30-ospp-v42-2-modify-failed.rules + +augenrules --load + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure auditing of unsuccessful file modifications (ppc64le) + Ensure that unsuccessful attempts to modify a file are audited. + +The following rules configure audit as described above: +## Unsuccessful file modifications (open for write or truncate) +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification + +Load new Audit rules into kernel by running: +augenrules --load + +Note: This rule uses a special set of Audit rules to comply with OSPP 4.2.1. You may reuse this rule in different profiles. If you decide to do so, it is recommended that you inspect contents of the file closely and make sure that they are alligned with your needs. + AU-2(a) + FAU_GEN.1.1.c + SRG-OS-000458-GPOS-00203 + SRG-OS-000474-GPOS-00219 + SRG-OS-000475-GPOS-00220 + SRG-OS-000463-GPOS-00207 + SRG-OS-000465-GPOS-00209 + SRG-OS-000461-GPOS-00205 + Unsuccessful file modifications might be a sign of a malicious action being performed on the system. Auditing of such events helps in detection and investigation of such actions. + + CCE-90790-7 + - name: Put contents into /etc/audit/rules.d/30-ospp-v42-2-modify-failed.rules according + to policy + copy: + dest: /etc/audit/rules.d/30-ospp-v42-2-modify-failed.rules + content: | + ## Unsuccessful file modifications (open for write or truncate) + -a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification + -a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification + -a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification + -a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification + -a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification + -a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification + force: true + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ansible_architecture == "ppc64le" + tags: + - CCE-90790-7 + - NIST-800-53-AU-2(a) + - audit_modify_failed_ppc64le + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Remove any permissions from other group + file: + path: /etc/audit/rules.d/30-ospp-v42-2-modify-failed.rules + mode: o-rwx + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ansible_architecture == "ppc64le" + tags: + - CCE-90790-7 + - NIST-800-53-AU-2(a) + - audit_modify_failed_ppc64le + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,%23%23%20Unsuccessful%20file%20modifications%20%28open%20for%20write%20or%20truncate%29%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20openat%2Copen_by_handle_at%20-F%20a2%2601003%20-F%20exit%3D-EACCES%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-modification%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20open%20-F%20a1%2601003%20-F%20exit%3D-EACCES%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-modification%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20truncate%2Cftruncate%20-F%20exit%3D-EACCES%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-modification%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20openat%2Copen_by_handle_at%20-F%20a2%2601003%20-F%20exit%3D-EPERM%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-modification%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20open%20-F%20a1%2601003%20-F%20exit%3D-EPERM%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-modification%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20truncate%2Cftruncate%20-F%20exit%3D-EPERM%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dunsuccessful-modification%0A + mode: 0600 + path: /etc/audit/rules.d/30-ospp-v42-2-modify-failed.rules + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && { grep -q ppc64le /proc/sys/kernel/osrelease; }; then + +cat << 'EOF' > /etc/audit/rules.d/30-ospp-v42-2-modify-failed.rules +## Unsuccessful file modifications (open for write or truncate) +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +EOF + +chmod o-rwx /etc/audit/rules.d/30-ospp-v42-2-modify-failed.rules + +augenrules --load + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure auditing of successful file modifications + Ensure that successful attempts to modify a file are audited. + +The following rules configure audit as described above: +## Successful file modifications (open for write or truncate) +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification +-a always,exit -F arch=b32 -S open -F a1&01003 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification +-a always,exit -F arch=b64 -S open -F a1&01003 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification +-a always,exit -F arch=b32 -S truncate,ftruncate -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification +-a always,exit -F arch=b64 -S truncate,ftruncate -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification + +Load new Audit rules into kernel by running: +augenrules --load + +Note: This rule uses a special set of Audit rules to comply with OSPP 4.2.1. You may reuse this rule in different profiles. If you decide to do so, it is recommended that you inspect contents of the file closely and make sure that they are alligned with your needs. + AU-2(a) + FAU_GEN.1.1.c + SRG-OS-000458-GPOS-00203 + SRG-OS-000474-GPOS-00219 + SRG-OS-000475-GPOS-00220 + SRG-OS-000463-GPOS-00207 + SRG-OS-000465-GPOS-00209 + SRG-OS-000461-GPOS-00205 + Auditing of successful attempts to modify a file helps in investigation of actions which happened on the system. + + CCE-83681-7 + - name: Put contents into /etc/audit/rules.d/30-ospp-v42-2-modify-success.rules according + to policy + copy: + dest: /etc/audit/rules.d/30-ospp-v42-2-modify-success.rules + content: | + ## Successful file modifications (open for write or truncate) + -a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification + -a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification + -a always,exit -F arch=b32 -S open -F a1&01003 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification + -a always,exit -F arch=b64 -S open -F a1&01003 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification + -a always,exit -F arch=b32 -S truncate,ftruncate -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification + -a always,exit -F arch=b64 -S truncate,ftruncate -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification + force: true + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ( not ( ansible_architecture == "aarch64" ) and not ( ansible_architecture == + "ppc64le" ) ) + tags: + - CCE-83681-7 + - NIST-800-53-AU-2(a) + - audit_modify_success + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Remove any permissions from other group + file: + path: /etc/audit/rules.d/30-ospp-v42-2-modify-success.rules + mode: o-rwx + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ( not ( ansible_architecture == "aarch64" ) and not ( ansible_architecture == + "ppc64le" ) ) + tags: + - CCE-83681-7 + - NIST-800-53-AU-2(a) + - audit_modify_success + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,%23%23%20Successful%20file%20modifications%20%28open%20for%20write%20or%20truncate%29%0A-a%20always%2Cexit%20-F%20arch%3Db32%20-S%20openat%2Copen_by_handle_at%20-F%20a2%26amp%3B01003%20-F%20success%3D1%20-F%20auid%26gt%3B%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dsuccessful-modification%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20openat%2Copen_by_handle_at%20-F%20a2%26amp%3B01003%20-F%20success%3D1%20-F%20auid%26gt%3B%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dsuccessful-modification%0A-a%20always%2Cexit%20-F%20arch%3Db32%20-S%20open%20-F%20a1%26amp%3B01003%20-F%20success%3D1%20-F%20auid%26gt%3B%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dsuccessful-modification%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20open%20-F%20a1%26amp%3B01003%20-F%20success%3D1%20-F%20auid%26gt%3B%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dsuccessful-modification%0A-a%20always%2Cexit%20-F%20arch%3Db32%20-S%20truncate%2Cftruncate%20-F%20success%3D1%20-F%20auid%26gt%3B%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dsuccessful-modification%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20truncate%2Cftruncate%20-F%20success%3D1%20-F%20auid%26gt%3B%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dsuccessful-modification%0A + mode: 0600 + path: /etc/audit/rules.d/30-ospp-v42-2-modify-success.rules + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && { ( ! ( grep -q aarch64 /proc/sys/kernel/osrelease ) && ! ( grep -q ppc64le /proc/sys/kernel/osrelease ) ); }; then + +cat << 'EOF' > /etc/audit/rules.d/30-ospp-v42-2-modify-success.rules +## Successful file modifications (open for write or truncate) +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification +-a always,exit -F arch=b32 -S open -F a1&01003 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification +-a always,exit -F arch=b64 -S open -F a1&01003 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification +-a always,exit -F arch=b32 -S truncate,ftruncate -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification +-a always,exit -F arch=b64 -S truncate,ftruncate -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification +EOF + +chmod o-rwx /etc/audit/rules.d/30-ospp-v42-2-modify-success.rules + +augenrules --load + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure auditing of successful file modifications (AArch64) + Ensure that successful attempts to modify a file are audited. + +The following rules configure audit as described above: +## Successful file modifications (open for write or truncate) +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification +-a always,exit -F arch=b32 -S open -F a1&01003 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification +-a always,exit -F arch=b32 -S truncate,ftruncate -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification +-a always,exit -F arch=b64 -S truncate,ftruncate -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification + +Load new Audit rules into kernel by running: +augenrules --load + +Note: This rule uses a special set of Audit rules to comply with OSPP 4.2.1. You may reuse this rule in different profiles. If you decide to do so, it is recommended that you inspect contents of the file closely and make sure that they are alligned with your needs. + AU-2(a) + FAU_GEN.1.1.c + SRG-OS-000458-GPOS-00203 + SRG-OS-000474-GPOS-00219 + SRG-OS-000475-GPOS-00220 + SRG-OS-000463-GPOS-00207 + SRG-OS-000465-GPOS-00209 + SRG-OS-000461-GPOS-00205 + Auditing of successful attempts to modify a file helps in investigation of actions which happened on the system. + + CCE-85909-0 + - name: Put contents into /etc/audit/rules.d/30-ospp-v42-2-modify-success.rules according + to policy + copy: + dest: /etc/audit/rules.d/30-ospp-v42-2-modify-success.rules + content: | + ## Successful file modifications (open for write or truncate) + -a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification + -a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification + -a always,exit -F arch=b32 -S open -F a1&01003 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification + -a always,exit -F arch=b32 -S truncate,ftruncate -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification + -a always,exit -F arch=b64 -S truncate,ftruncate -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification + force: true + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ansible_architecture == "aarch64" + tags: + - CCE-85909-0 + - NIST-800-53-AU-2(a) + - audit_modify_success_aarch64 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Remove any permissions from other group + file: + path: /etc/audit/rules.d/30-ospp-v42-2-modify-success.rules + mode: o-rwx + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ansible_architecture == "aarch64" + tags: + - CCE-85909-0 + - NIST-800-53-AU-2(a) + - audit_modify_success_aarch64 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,%23%23%20Successful%20file%20modifications%20%28open%20for%20write%20or%20truncate%29%0A-a%20always%2Cexit%20-F%20arch%3Db32%20-S%20openat%2Copen_by_handle_at%20-F%20a2%2601003%20-F%20success%3D1%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dsuccessful-modification%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20openat%2Copen_by_handle_at%20-F%20a2%2601003%20-F%20success%3D1%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dsuccessful-modification%0A-a%20always%2Cexit%20-F%20arch%3Db32%20-S%20open%20-F%20a1%2601003%20-F%20success%3D1%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dsuccessful-modification%0A-a%20always%2Cexit%20-F%20arch%3Db32%20-S%20truncate%2Cftruncate%20-F%20success%3D1%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dsuccessful-modification%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20truncate%2Cftruncate%20-F%20success%3D1%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dsuccessful-modification%0A + mode: 0600 + path: /etc/audit/rules.d/30-ospp-v42-2-modify-success.rules + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && { grep -q aarch64 /proc/sys/kernel/osrelease; }; then + +cat << 'EOF' > /etc/audit/rules.d/30-ospp-v42-2-modify-success.rules +## Successful file modifications (open for write or truncate) +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification +-a always,exit -F arch=b32 -S open -F a1&01003 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification +-a always,exit -F arch=b32 -S truncate,ftruncate -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification +-a always,exit -F arch=b64 -S truncate,ftruncate -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification +EOF + +chmod o-rwx /etc/audit/rules.d/30-ospp-v42-2-modify-success.rules + +augenrules --load + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure auditing of successful file modifications (ppc64le) + Ensure that successful attempts to modify a file are audited. + +The following rules configure audit as described above: +## Successful file modifications (open for write or truncate) +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification +-a always,exit -F arch=b64 -S open -F a1&01003 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification +-a always,exit -F arch=b64 -S truncate,ftruncate -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification + +Load new Audit rules into kernel by running: +augenrules --load + +Note: This rule uses a special set of Audit rules to comply with OSPP 4.2.1. You may reuse this rule in different profiles. If you decide to do so, it is recommended that you inspect contents of the file closely and make sure that they are alligned with your needs. + AU-2(a) + FAU_GEN.1.1.c + SRG-OS-000458-GPOS-00203 + SRG-OS-000474-GPOS-00219 + SRG-OS-000475-GPOS-00220 + SRG-OS-000463-GPOS-00207 + SRG-OS-000465-GPOS-00209 + SRG-OS-000461-GPOS-00205 + Auditing of successful attempts to modify a file helps in investigation of actions which happened on the system. + + CCE-90791-5 + - name: Put contents into /etc/audit/rules.d/30-ospp-v42-2-modify-success.rules according + to policy + copy: + dest: /etc/audit/rules.d/30-ospp-v42-2-modify-success.rules + content: | + ## Successful file modifications (open for write or truncate) + -a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification + -a always,exit -F arch=b64 -S open -F a1&01003 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification + -a always,exit -F arch=b64 -S truncate,ftruncate -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification + force: true + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ansible_architecture == "ppc64le" + tags: + - CCE-90791-5 + - NIST-800-53-AU-2(a) + - audit_modify_success_ppc64le + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Remove any permissions from other group + file: + path: /etc/audit/rules.d/30-ospp-v42-2-modify-success.rules + mode: o-rwx + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ansible_architecture == "ppc64le" + tags: + - CCE-90791-5 + - NIST-800-53-AU-2(a) + - audit_modify_success_ppc64le + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,%23%23%20Successful%20file%20modifications%20%28open%20for%20write%20or%20truncate%29%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20openat%2Copen_by_handle_at%20-F%20a2%2601003%20-F%20success%3D1%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dsuccessful-modification%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20open%20-F%20a1%2601003%20-F%20success%3D1%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dsuccessful-modification%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20truncate%2Cftruncate%20-F%20success%3D1%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dsuccessful-modification%0A + mode: 0600 + path: /etc/audit/rules.d/30-ospp-v42-2-modify-success.rules + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && { grep -q ppc64le /proc/sys/kernel/osrelease; }; then + +cat << 'EOF' > /etc/audit/rules.d/30-ospp-v42-2-modify-success.rules +## Successful file modifications (open for write or truncate) +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification +-a always,exit -F arch=b64 -S open -F a1&01003 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification +-a always,exit -F arch=b64 -S truncate,ftruncate -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification +EOF + +chmod o-rwx /etc/audit/rules.d/30-ospp-v42-2-modify-success.rules + +augenrules --load + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure auditing of loading and unloading of kernel modules + Ensure that loading and unloading of kernel modules is audited. + +The following rules configure audit as described above: +## These rules watch for kernel module insertion. By monitoring +## the syscall, we do not need any watches on programs. +-a always,exit -F arch=b32 -S init_module,finit_module -F key=module-load +-a always,exit -F arch=b64 -S init_module,finit_module -F key=module-load +-a always,exit -F arch=b32 -S delete_module -F key=module-unload +-a always,exit -F arch=b64 -S delete_module -F key=module-unload + +Load new Audit rules into kernel by running: +augenrules --load + AU-2(a) + FAU_GEN.1.1.c + SRG-OS-000471-GPOS-00216 + SRG-OS-000477-GPOS-00222 + SRG-OS-000475-GPOS-00220 + Loading of a malicious kernel module introduces a risk to the system, as the module has access to sensitive data and perform actions at the operating system kernel level. Having such events audited helps in monitoring and investigating of malicious activities. + + CCE-90814-5 + - name: Put contents into /etc/audit/rules.d/43-module-load.rules according to policy + copy: + dest: /etc/audit/rules.d/43-module-load.rules + content: | + ## These rules watch for kernel module insertion. By monitoring + ## the syscall, we do not need any watches on programs. + -a always,exit -F arch=b32 -S init_module,finit_module -F key=module-load + -a always,exit -F arch=b64 -S init_module,finit_module -F key=module-load + -a always,exit -F arch=b32 -S delete_module -F key=module-unload + -a always,exit -F arch=b64 -S delete_module -F key=module-unload + force: true + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - not ( ansible_architecture == "ppc64le" ) + tags: + - CCE-90814-5 + - NIST-800-53-AU-2(a) + - audit_module_load + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Remove any permissions from other group + file: + path: /etc/audit/rules.d/43-module-load.rules + mode: o-rwx + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - not ( ansible_architecture == "ppc64le" ) + tags: + - CCE-90814-5 + - NIST-800-53-AU-2(a) + - audit_module_load + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,%23%23%20These%20rules%20watch%20for%20kernel%20module%20insertion.%20By%20monitoring%0A%23%23%20the%20syscall%2C%20we%20do%20not%20need%20any%20watches%20on%20programs.%0A-a%20always%2Cexit%20-F%20arch%3Db32%20-S%20init_module%2Cfinit_module%20-F%20key%3Dmodule-load%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20init_module%2Cfinit_module%20-F%20key%3Dmodule-load%0A-a%20always%2Cexit%20-F%20arch%3Db32%20-S%20delete_module%20-F%20key%3Dmodule-unload%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20delete_module%20-F%20key%3Dmodule-unload%0A + mode: 0600 + path: /etc/audit/rules.d/43-module-load.rules + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && { ! ( grep -q ppc64le /proc/sys/kernel/osrelease ); }; then + +cat << 'EOF' > /etc/audit/rules.d/43-module-load.rules +## These rules watch for kernel module insertion. By monitoring +## the syscall, we do not need any watches on programs. +-a always,exit -F arch=b32 -S init_module,finit_module -F key=module-load +-a always,exit -F arch=b64 -S init_module,finit_module -F key=module-load +-a always,exit -F arch=b32 -S delete_module -F key=module-unload +-a always,exit -F arch=b64 -S delete_module -F key=module-unload +EOF + +chmod o-rwx /etc/audit/rules.d/43-module-load.rules + +augenrules --load + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure auditing of loading and unloading of kernel modules (ppc64le) + Ensure that loading and unloading of kernel modules is audited. + +The following rules configure audit as described above: +## These rules watch for kernel module insertion. By monitoring +## the syscall, we do not need any watches on programs. +-a always,exit -F arch=b64 -S init_module,finit_module -F key=module-load +-a always,exit -F arch=b64 -S delete_module -F key=module-unload + +Load new Audit rules into kernel by running: +augenrules --load + AU-2(a) + FAU_GEN.1.1.c + SRG-OS-000471-GPOS-00216 + SRG-OS-000477-GPOS-00222 + SRG-OS-000475-GPOS-00220 + Loading of a malicious kernel module introduces a risk to the system, as the module has access to sensitive data and perform actions at the operating system kernel level. Having such events audited helps in monitoring and investigating of malicious activities. + + CCE-90788-1 + - name: Put contents into /etc/audit/rules.d/43-module-load.rules according to policy + copy: + dest: /etc/audit/rules.d/43-module-load.rules + content: | + ## These rules watch for kernel module insertion. By monitoring + ## the syscall, we do not need any watches on programs. + -a always,exit -F arch=b64 -S init_module,finit_module -F key=module-load + -a always,exit -F arch=b64 -S delete_module -F key=module-unload + force: true + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ansible_architecture == "ppc64le" + tags: + - CCE-90788-1 + - NIST-800-53-AU-2(a) + - audit_module_load_ppc64le + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Remove any permissions from other group + file: + path: /etc/audit/rules.d/43-module-load.rules + mode: o-rwx + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ansible_architecture == "ppc64le" + tags: + - CCE-90788-1 + - NIST-800-53-AU-2(a) + - audit_module_load_ppc64le + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,%23%23%20These%20rules%20watch%20for%20kernel%20module%20insertion.%20By%20monitoring%0A%23%23%20the%20syscall%2C%20we%20do%20not%20need%20any%20watches%20on%20programs.%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20init_module%2Cfinit_module%20-F%20key%3Dmodule-load%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20delete_module%20-F%20key%3Dmodule-unload%0A + mode: 0600 + path: /etc/audit/rules.d/43-module-load.rules + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && { grep -q ppc64le /proc/sys/kernel/osrelease; }; then + +cat << 'EOF' > /etc/audit/rules.d/43-module-load.rules +## These rules watch for kernel module insertion. By monitoring +## the syscall, we do not need any watches on programs. +-a always,exit -F arch=b64 -S init_module,finit_module -F key=module-load +-a always,exit -F arch=b64 -S delete_module -F key=module-unload +EOF + +chmod o-rwx /etc/audit/rules.d/43-module-load.rules + +augenrules --load + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Perform general configuration of Audit for OSPP + Configure some basic Audit parameters specific for OSPP profile. +In particular, configure Audit to watch for direct modification of files storing system user and group information, and usage of applications with special rights which can change system configuration. +Further audited events include access to audit log it self, attempts to Alter Process and Session Initiation Information, and attempts to modify MAC controls. + +The following rules configure audit as described above: +## The purpose of these rules is to meet the requirements for Operating +## System Protection Profile (OSPP)v4.2. These rules depends on having +## the following rule files copied to /etc/audit/rules.d: +## +## 10-base-config.rules, 11-loginuid.rules, +## 30-ospp-v42-1-create-failed.rules, 30-ospp-v42-1-create-success.rules, +## 30-ospp-v42-2-modify-failed.rules, 30-ospp-v42-2-modify-success.rules, +## 30-ospp-v42-3-access-failed.rules, 30-ospp-v42-3-access-success.rules, +## 30-ospp-v42-4-delete-failed.rules, 30-ospp-v42-4-delete-success.rules, +## 30-ospp-v42-5-perm-change-failed.rules, +## 30-ospp-v42-5-perm-change-success.rules, +## 30-ospp-v42-6-owner-change-failed.rules, +## 30-ospp-v42-6-owner-change-success.rules +## +## original copies may be found in /usr/share/audit/sample-rules/ + + +## User add delete modify. This is covered by pam. However, someone could +## open a file and directly create or modify a user, so we'll watch passwd and +## shadow for writes +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b32 -S open -F a1&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b64 -S open -F a1&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b32 -S open -F a1&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b64 -S open -F a1&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify + +## User enable and disable. This is entirely handled by pam. + +## Group add delete modify. This is covered by pam. However, someone could +## open a file and directly create or modify a user, so we'll watch group and +## gshadow for writes +-a always,exit -F path=/etc/passwd -F perm=wa -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F path=/etc/shadow -F perm=wa -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F path=/etc/group -F perm=wa -F auid>=1000 -F auid!=unset -F key=group-modify +-a always,exit -F path=/etc/gshadow -F perm=wa -F auid>=1000 -F auid!=unset -F key=group-modify + + +## Use of special rights for config changes. This would be use of setuid +## programs that relate to user accts. This is not all setuid apps because +## requirements are only for ones that affect system configuration. +-a always,exit -F path=/usr/sbin/unix_chkpwd -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/sbin/usernetctl -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/sbin/userhelper -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/sbin/seunshare -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/mount -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/newgrp -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/newuidmap -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/gpasswd -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/newgidmap -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/umount -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/passwd -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/crontab -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/at -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes + +## Privilege escalation via su or sudo. This is entirely handled by pam. + +## Watch for configuration changes to privilege escalation. +-a always,exit -F path=/etc/sudoers -F perm=wa -F key=special-config-changes +-a always,exit -F dir=/etc/sudoers.d/ -F perm=wa -F key=special-config-changes + +## Audit log access +-a always,exit -F dir=/var/log/audit/ -F perm=r -F auid>=1000 -F auid!=unset -F key=access-audit-trail +## Attempts to Alter Process and Session Initiation Information +-a always,exit -F path=/var/run/utmp -F perm=wa -F auid>=1000 -F auid!=unset -F key=session +-a always,exit -F path=/var/log/btmp -F perm=wa -F auid>=1000 -F auid!=unset -F key=session +-a always,exit -F path=/var/log/wtmp -F perm=wa -F auid>=1000 -F auid!=unset -F key=session + +## Attempts to modify MAC controls +-a always,exit -F dir=/etc/selinux/ -F perm=wa -F auid>=1000 -F auid!=unset -F key=MAC-policy + +## Software updates. This is entirely handled by rpm. + +## System start and shutdown. This is entirely handled by systemd + +## Kernel Module loading. This is handled in 43-module-load.rules + +## Application invocation. The requirements list an user requirement +## FPT_SRP_EXT.1 Software Restriction Policies. This event is intended to +## state results from that policy. This would be handled entirely by +## that daemon. + +Load new Audit rules into kernel by running: +augenrules --load + +Note: This rule uses a special set of Audit rules to comply with OSPP 4.2.1. You may reuse this rule in different profiles. If you decide to do so, it is recommended that you inspect contents of the file closely and make sure that they are alligned with your needs. + AU-2(a) + FAU_GEN.1.1.c + SRG-OS-000004-GPOS-00004 + SRG-OS-000241-GPOS-00091 + SRG-OS-000476-GPOS-00221 + SRG-OS-000327-GPOS-00127 + SRG-OS-000475-GPOS-00220 + SRG-OS-000239-GPOS-00089 + SRG-OS-000274-GPOS-00104 + SRG-OS-000275-GPOS-00105 + SRG-OS-000303-GPOS-00120 + SRG-OS-000304-GPOS-00121 + Auditing of events listed in the description provides data for monitoring and investigation of potentially malicious events e.g. tampering with Audit logs, malicious access to files storing information about system users and groups etc. + + CCE-83655-1 + - name: Put contents into /etc/audit/rules.d/30-ospp-v42.rules according to policy + copy: + dest: /etc/audit/rules.d/30-ospp-v42.rules + content: |+ + ## The purpose of these rules is to meet the requirements for Operating + ## System Protection Profile (OSPP)v4.2. These rules depends on having + ## the following rule files copied to /etc/audit/rules.d: + ## + ## 10-base-config.rules, 11-loginuid.rules, + ## 30-ospp-v42-1-create-failed.rules, 30-ospp-v42-1-create-success.rules, + ## 30-ospp-v42-2-modify-failed.rules, 30-ospp-v42-2-modify-success.rules, + ## 30-ospp-v42-3-access-failed.rules, 30-ospp-v42-3-access-success.rules, + ## 30-ospp-v42-4-delete-failed.rules, 30-ospp-v42-4-delete-success.rules, + ## 30-ospp-v42-5-perm-change-failed.rules, + ## 30-ospp-v42-5-perm-change-success.rules, + ## 30-ospp-v42-6-owner-change-failed.rules, + ## 30-ospp-v42-6-owner-change-success.rules + ## + ## original copies may be found in /usr/share/audit/sample-rules/ + + + ## User add delete modify. This is covered by pam. However, someone could + ## open a file and directly create or modify a user, so we'll watch passwd and + ## shadow for writes + -a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=user-modify + -a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=user-modify + -a always,exit -F arch=b32 -S open -F a1&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=user-modify + -a always,exit -F arch=b64 -S open -F a1&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=user-modify + -a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify + -a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify + -a always,exit -F arch=b32 -S open -F a1&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify + -a always,exit -F arch=b64 -S open -F a1&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify + + ## User enable and disable. This is entirely handled by pam. + + ## Group add delete modify. This is covered by pam. However, someone could + ## open a file and directly create or modify a user, so we'll watch group and + ## gshadow for writes + -a always,exit -F path=/etc/passwd -F perm=wa -F auid>=1000 -F auid!=unset -F key=user-modify + -a always,exit -F path=/etc/shadow -F perm=wa -F auid>=1000 -F auid!=unset -F key=user-modify + -a always,exit -F path=/etc/group -F perm=wa -F auid>=1000 -F auid!=unset -F key=group-modify + -a always,exit -F path=/etc/gshadow -F perm=wa -F auid>=1000 -F auid!=unset -F key=group-modify + + + ## Use of special rights for config changes. This would be use of setuid + ## programs that relate to user accts. This is not all setuid apps because + ## requirements are only for ones that affect system configuration. + -a always,exit -F path=/usr/sbin/unix_chkpwd -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes + -a always,exit -F path=/usr/sbin/usernetctl -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes + -a always,exit -F path=/usr/sbin/userhelper -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes + -a always,exit -F path=/usr/sbin/seunshare -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes + -a always,exit -F path=/usr/bin/mount -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes + -a always,exit -F path=/usr/bin/newgrp -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes + -a always,exit -F path=/usr/bin/newuidmap -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes + -a always,exit -F path=/usr/bin/gpasswd -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes + -a always,exit -F path=/usr/bin/newgidmap -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes + -a always,exit -F path=/usr/bin/umount -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes + -a always,exit -F path=/usr/bin/passwd -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes + -a always,exit -F path=/usr/bin/crontab -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes + -a always,exit -F path=/usr/bin/at -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes + + ## Privilege escalation via su or sudo. This is entirely handled by pam. + + ## Watch for configuration changes to privilege escalation. + -a always,exit -F path=/etc/sudoers -F perm=wa -F key=special-config-changes + -a always,exit -F dir=/etc/sudoers.d/ -F perm=wa -F key=special-config-changes + + ## Audit log access + -a always,exit -F dir=/var/log/audit/ -F perm=r -F auid>=1000 -F auid!=unset -F key=access-audit-trail + ## Attempts to Alter Process and Session Initiation Information + -a always,exit -F path=/var/run/utmp -F perm=wa -F auid>=1000 -F auid!=unset -F key=session + -a always,exit -F path=/var/log/btmp -F perm=wa -F auid>=1000 -F auid!=unset -F key=session + -a always,exit -F path=/var/log/wtmp -F perm=wa -F auid>=1000 -F auid!=unset -F key=session + + ## Attempts to modify MAC controls + -a always,exit -F dir=/etc/selinux/ -F perm=wa -F auid>=1000 -F auid!=unset -F key=MAC-policy + + ## Software updates. This is entirely handled by rpm. + + ## System start and shutdown. This is entirely handled by systemd + + ## Kernel Module loading. This is handled in 43-module-load.rules + + ## Application invocation. The requirements list an user requirement + ## FPT_SRP_EXT.1 Software Restriction Policies. This event is intended to + ## state results from that policy. This would be handled entirely by + ## that daemon. + + force: true + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ( not ( ansible_architecture == "aarch64" ) and not ( ansible_architecture == + "ppc64le" ) ) + tags: + - CCE-83655-1 + - NIST-800-53-AU-2(a) + - audit_ospp_general + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Remove any permissions from other group + file: + path: /etc/audit/rules.d/30-ospp-v42.rules + mode: o-rwx + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ( not ( ansible_architecture == "aarch64" ) and not ( ansible_architecture == + "ppc64le" ) ) + tags: + - CCE-83655-1 + - NIST-800-53-AU-2(a) + - audit_ospp_general + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,%23%23%20The%20purpose%20of%20these%20rules%20is%20to%20meet%20the%20requirements%20for%20Operating%0A%23%23%20System%20Protection%20Profile%20%28OSPP%29v4.2.%20These%20rules%20depends%20on%20having%0A%23%23%20the%20following%20rule%20files%20copied%20to%20/etc/audit/rules.d%3A%0A%23%23%0A%23%23%2010-base-config.rules%2C%2011-loginuid.rules%2C%0A%23%23%2030-ospp-v42-1-create-failed.rules%2C%2030-ospp-v42-1-create-success.rules%2C%0A%23%23%2030-ospp-v42-2-modify-failed.rules%2C%2030-ospp-v42-2-modify-success.rules%2C%0A%23%23%2030-ospp-v42-3-access-failed.rules%2C%2030-ospp-v42-3-access-success.rules%2C%0A%23%23%2030-ospp-v42-4-delete-failed.rules%2C%2030-ospp-v42-4-delete-success.rules%2C%0A%23%23%2030-ospp-v42-5-perm-change-failed.rules%2C%0A%23%23%2030-ospp-v42-5-perm-change-success.rules%2C%0A%23%23%2030-ospp-v42-6-owner-change-failed.rules%2C%0A%23%23%2030-ospp-v42-6-owner-change-success.rules%0A%23%23%0A%23%23%20original%20copies%20may%20be%20found%20in%20/usr/share/audit/sample-rules/%0A%0A%0A%23%23%20User%20add%20delete%20modify.%20This%20is%20covered%20by%20pam.%20However%2C%20someone%20could%0A%23%23%20open%20a%20file%20and%20directly%20create%20or%20modify%20a%20user%2C%20so%20we%27ll%20watch%20passwd%20and%0A%23%23%20shadow%20for%20writes%0A-a%20always%2Cexit%20-F%20arch%3Db32%20-S%20openat%2Copen_by_handle_at%20-F%20a2%2603%20-F%20path%3D/etc/passwd%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Duser-modify%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20openat%2Copen_by_handle_at%20-F%20a2%2603%20-F%20path%3D/etc/passwd%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Duser-modify%0A-a%20always%2Cexit%20-F%20arch%3Db32%20-S%20open%20-F%20a1%2603%20-F%20path%3D/etc/passwd%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Duser-modify%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20open%20-F%20a1%2603%20-F%20path%3D/etc/passwd%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Duser-modify%0A-a%20always%2Cexit%20-F%20arch%3Db32%20-S%20openat%2Copen_by_handle_at%20-F%20a2%2603%20-F%20path%3D/etc/shadow%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Duser-modify%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20openat%2Copen_by_handle_at%20-F%20a2%2603%20-F%20path%3D/etc/shadow%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Duser-modify%0A-a%20always%2Cexit%20-F%20arch%3Db32%20-S%20open%20-F%20a1%2603%20-F%20path%3D/etc/shadow%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Duser-modify%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20open%20-F%20a1%2603%20-F%20path%3D/etc/shadow%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Duser-modify%0A%0A%23%23%20User%20enable%20and%20disable.%20This%20is%20entirely%20handled%20by%20pam.%0A%0A%23%23%20Group%20add%20delete%20modify.%20This%20is%20covered%20by%20pam.%20However%2C%20someone%20could%0A%23%23%20open%20a%20file%20and%20directly%20create%20or%20modify%20a%20user%2C%20so%20we%27ll%20watch%20group%20and%0A%23%23%20gshadow%20for%20writes%0A-a%20always%2Cexit%20-F%20path%3D/etc/passwd%20-F%20perm%3Dwa%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Duser-modify%0A-a%20always%2Cexit%20-F%20path%3D/etc/shadow%20-F%20perm%3Dwa%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Duser-modify%0A-a%20always%2Cexit%20-F%20path%3D/etc/group%20-F%20perm%3Dwa%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dgroup-modify%0A-a%20always%2Cexit%20-F%20path%3D/etc/gshadow%20-F%20perm%3Dwa%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dgroup-modify%0A%0A%0A%23%23%20Use%20of%20special%20rights%20for%20config%20changes.%20This%20would%20be%20use%20of%20setuid%0A%23%23%20programs%20that%20relate%20to%20user%20accts.%20This%20is%20not%20all%20setuid%20apps%20because%0A%23%23%20requirements%20are%20only%20for%20ones%20that%20affect%20system%20configuration.%0A-a%20always%2Cexit%20-F%20path%3D/usr/sbin/unix_chkpwd%20-F%20perm%3Dx%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dspecial-config-changes%0A-a%20always%2Cexit%20-F%20path%3D/usr/sbin/usernetctl%20-F%20perm%3Dx%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dspecial-config-changes%0A-a%20always%2Cexit%20-F%20path%3D/usr/sbin/userhelper%20-F%20perm%3Dx%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dspecial-config-changes%0A-a%20always%2Cexit%20-F%20path%3D/usr/sbin/seunshare%20-F%20perm%3Dx%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dspecial-config-changes%0A-a%20always%2Cexit%20-F%20path%3D/usr/bin/mount%20-F%20perm%3Dx%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dspecial-config-changes%0A-a%20always%2Cexit%20-F%20path%3D/usr/bin/newgrp%20-F%20perm%3Dx%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dspecial-config-changes%0A-a%20always%2Cexit%20-F%20path%3D/usr/bin/newuidmap%20-F%20perm%3Dx%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dspecial-config-changes%0A-a%20always%2Cexit%20-F%20path%3D/usr/bin/gpasswd%20-F%20perm%3Dx%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dspecial-config-changes%0A-a%20always%2Cexit%20-F%20path%3D/usr/bin/newgidmap%20-F%20perm%3Dx%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dspecial-config-changes%0A-a%20always%2Cexit%20-F%20path%3D/usr/bin/umount%20-F%20perm%3Dx%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dspecial-config-changes%0A-a%20always%2Cexit%20-F%20path%3D/usr/bin/passwd%20-F%20perm%3Dx%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dspecial-config-changes%0A-a%20always%2Cexit%20-F%20path%3D/usr/bin/crontab%20-F%20perm%3Dx%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dspecial-config-changes%0A-a%20always%2Cexit%20-F%20path%3D/usr/bin/at%20-F%20perm%3Dx%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dspecial-config-changes%0A%0A%23%23%20Privilege%20escalation%20via%20su%20or%20sudo.%20This%20is%20entirely%20handled%20by%20pam.%0A%0A%23%23%20Audit%20log%20access%0A-a%20always%2Cexit%20-F%20dir%3D/var/log/audit/%20-F%20perm%3Dr%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Daccess-audit-trail%0A%23%23%20Attempts%20to%20Alter%20Process%20and%20Session%20Initiation%20Information%0A-a%20always%2Cexit%20-F%20path%3D/var/run/utmp%20-F%20perm%3Dwa%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dsession%0A-a%20always%2Cexit%20-F%20path%3D/var/log/btmp%20-F%20perm%3Dwa%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dsession%0A-a%20always%2Cexit%20-F%20path%3D/var/log/wtmp%20-F%20perm%3Dwa%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dsession%0A%0A%23%23%20Attempts%20to%20modify%20MAC%20controls%0A-a%20always%2Cexit%20-F%20dir%3D/etc/selinux/%20-F%20perm%3Dwa%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3DMAC-policy%0A%0A%23%23%20Software%20updates.%20This%20is%20entirely%20handled%20by%20rpm.%0A%0A%23%23%20System%20start%20and%20shutdown.%20This%20is%20entirely%20handled%20by%20systemd%0A%0A%23%23%20Kernel%20Module%20loading.%20This%20is%20handled%20in%2043-module-load.rules%0A%0A%23%23%20Application%20invocation.%20The%20requirements%20list%20an%20user%20requirement%0A%23%23%20FPT_SRP_EXT.1%20Software%20Restriction%20Policies.%20This%20event%20is%20intended%20to%0A%23%23%20state%20results%20from%20that%20policy.%20This%20would%20be%20handled%20entirely%20by%0A%23%23%20that%20daemon.%0A + mode: 0600 + path: /etc/audit/rules.d/30-ospp-v42.rules + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && { ( ! ( grep -q aarch64 /proc/sys/kernel/osrelease ) && ! ( grep -q ppc64le /proc/sys/kernel/osrelease ) ); }; then + +cat << 'EOF' > /etc/audit/rules.d/30-ospp-v42.rules +## The purpose of these rules is to meet the requirements for Operating +## System Protection Profile (OSPP)v4.2. These rules depends on having +## the following rule files copied to /etc/audit/rules.d: +## +## 10-base-config.rules, 11-loginuid.rules, +## 30-ospp-v42-1-create-failed.rules, 30-ospp-v42-1-create-success.rules, +## 30-ospp-v42-2-modify-failed.rules, 30-ospp-v42-2-modify-success.rules, +## 30-ospp-v42-3-access-failed.rules, 30-ospp-v42-3-access-success.rules, +## 30-ospp-v42-4-delete-failed.rules, 30-ospp-v42-4-delete-success.rules, +## 30-ospp-v42-5-perm-change-failed.rules, +## 30-ospp-v42-5-perm-change-success.rules, +## 30-ospp-v42-6-owner-change-failed.rules, +## 30-ospp-v42-6-owner-change-success.rules +## +## original copies may be found in /usr/share/audit/sample-rules/ + + +## User add delete modify. This is covered by pam. However, someone could +## open a file and directly create or modify a user, so we'll watch passwd and +## shadow for writes +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b32 -S open -F a1&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b64 -S open -F a1&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b32 -S open -F a1&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b64 -S open -F a1&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify + +## User enable and disable. This is entirely handled by pam. + +## Group add delete modify. This is covered by pam. However, someone could +## open a file and directly create or modify a user, so we'll watch group and +## gshadow for writes +-a always,exit -F path=/etc/passwd -F perm=wa -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F path=/etc/shadow -F perm=wa -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F path=/etc/group -F perm=wa -F auid>=1000 -F auid!=unset -F key=group-modify +-a always,exit -F path=/etc/gshadow -F perm=wa -F auid>=1000 -F auid!=unset -F key=group-modify + + +## Use of special rights for config changes. This would be use of setuid +## programs that relate to user accts. This is not all setuid apps because +## requirements are only for ones that affect system configuration. +-a always,exit -F path=/usr/sbin/unix_chkpwd -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/sbin/usernetctl -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/sbin/userhelper -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/sbin/seunshare -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/mount -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/newgrp -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/newuidmap -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/gpasswd -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/newgidmap -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/umount -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/passwd -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/crontab -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/at -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes + +## Privilege escalation via su or sudo. This is entirely handled by pam. + +## Watch for configuration changes to privilege escalation. +-a always,exit -F path=/etc/sudoers -F perm=wa -F key=special-config-changes +-a always,exit -F dir=/etc/sudoers.d/ -F perm=wa -F key=special-config-changes + +## Audit log access +-a always,exit -F dir=/var/log/audit/ -F perm=r -F auid>=1000 -F auid!=unset -F key=access-audit-trail +## Attempts to Alter Process and Session Initiation Information +-a always,exit -F path=/var/run/utmp -F perm=wa -F auid>=1000 -F auid!=unset -F key=session +-a always,exit -F path=/var/log/btmp -F perm=wa -F auid>=1000 -F auid!=unset -F key=session +-a always,exit -F path=/var/log/wtmp -F perm=wa -F auid>=1000 -F auid!=unset -F key=session + +## Attempts to modify MAC controls +-a always,exit -F dir=/etc/selinux/ -F perm=wa -F auid>=1000 -F auid!=unset -F key=MAC-policy + +## Software updates. This is entirely handled by rpm. + +## System start and shutdown. This is entirely handled by systemd + +## Kernel Module loading. This is handled in 43-module-load.rules + +## Application invocation. The requirements list an user requirement +## FPT_SRP_EXT.1 Software Restriction Policies. This event is intended to +## state results from that policy. This would be handled entirely by +## that daemon. + +EOF + +chmod o-rwx /etc/audit/rules.d/30-ospp-v42.rules + +augenrules --load + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Perform general configuration of Audit for OSPP (AArch64) + Configure some basic Audit parameters specific for OSPP profile. +In particular, configure Audit to watch for direct modification of files storing system user and group information, and usage of applications with special rights which can change system configuration. +Further audited events include access to audit log it self, attempts to Alter Process and Session Initiation Information, and attempts to modify MAC controls. + +The following rules configure audit as described above: +## The purpose of these rules is to meet the requirements for Operating +## System Protection Profile (OSPP)v4.2. These rules depends on having +## the following rule files copied to /etc/audit/rules.d: +## +## 10-base-config.rules, 11-loginuid.rules, +## 30-ospp-v42-1-create-failed.rules, 30-ospp-v42-1-create-success.rules, +## 30-ospp-v42-2-modify-failed.rules, 30-ospp-v42-2-modify-success.rules, +## 30-ospp-v42-3-access-failed.rules, 30-ospp-v42-3-access-success.rules, +## 30-ospp-v42-4-delete-failed.rules, 30-ospp-v42-4-delete-success.rules, +## 30-ospp-v42-5-perm-change-failed.rules, +## 30-ospp-v42-5-perm-change-success.rules, +## 30-ospp-v42-6-owner-change-failed.rules, +## 30-ospp-v42-6-owner-change-success.rules +## +## original copies may be found in /usr/share/audit/sample-rules/ + + +## User add delete modify. This is covered by pam. However, someone could +## open a file and directly create or modify a user, so we'll watch passwd and +## shadow for writes +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b32 -S open -F a1&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b32 -S open -F a1&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify + +## User enable and disable. This is entirely handled by pam. + +## Group add delete modify. This is covered by pam. However, someone could +## open a file and directly create or modify a user, so we'll watch group and +## gshadow for writes +-a always,exit -F path=/etc/passwd -F perm=wa -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F path=/etc/shadow -F perm=wa -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F path=/etc/group -F perm=wa -F auid>=1000 -F auid!=unset -F key=group-modify +-a always,exit -F path=/etc/gshadow -F perm=wa -F auid>=1000 -F auid!=unset -F key=group-modify + + +## Use of special rights for config changes. This would be use of setuid +## programs that relate to user accts. This is not all setuid apps because +## requirements are only for ones that affect system configuration. +-a always,exit -F path=/usr/sbin/unix_chkpwd -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/sbin/usernetctl -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/sbin/userhelper -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/sbin/seunshare -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/mount -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/newgrp -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/newuidmap -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/gpasswd -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/newgidmap -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/umount -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/passwd -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/crontab -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/at -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes + +## Privilege escalation via su or sudo. This is entirely handled by pam. + +## Watch for configuration changes to privilege escalation. +-a always,exit -F path=/etc/sudoers -F perm=wa -F key=special-config-changes +-a always,exit -F dir=/etc/sudoers.d/ -F perm=wa -F key=special-config-changes + +## Audit log access +-a always,exit -F dir=/var/log/audit/ -F perm=r -F auid>=1000 -F auid!=unset -F key=access-audit-trail +## Attempts to Alter Process and Session Initiation Information +-a always,exit -F path=/var/run/utmp -F perm=wa -F auid>=1000 -F auid!=unset -F key=session +-a always,exit -F path=/var/log/btmp -F perm=wa -F auid>=1000 -F auid!=unset -F key=session +-a always,exit -F path=/var/log/wtmp -F perm=wa -F auid>=1000 -F auid!=unset -F key=session + +## Attempts to modify MAC controls +-a always,exit -F dir=/etc/selinux/ -F perm=wa -F auid>=1000 -F auid!=unset -F key=MAC-policy + +## Software updates. This is entirely handled by rpm. + +## System start and shutdown. This is entirely handled by systemd + +## Kernel Module loading. This is handled in 43-module-load.rules + +## Application invocation. The requirements list an user requirement +## FPT_SRP_EXT.1 Software Restriction Policies. This event is intended to +## state results from that policy. This would be handled entirely by +## that daemon. + +Load new Audit rules into kernel by running: +augenrules --load + +Note: This rule uses a special set of Audit rules to comply with OSPP 4.2.1. You may reuse this rule in different profiles. If you decide to do so, it is recommended that you inspect contents of the file closely and make sure that they are alligned with your needs. + AU-2(a) + FAU_GEN.1.1.c + SRG-OS-000004-GPOS-00004 + SRG-OS-000241-GPOS-00091 + SRG-OS-000476-GPOS-00221 + SRG-OS-000327-GPOS-00127 + SRG-OS-000475-GPOS-00220 + SRG-OS-000239-GPOS-00089 + SRG-OS-000274-GPOS-00104 + SRG-OS-000275-GPOS-00105 + SRG-OS-000303-GPOS-00120 + SRG-OS-000304-GPOS-00121 + Auditing of events listed in the description provides data for monitoring and investigation of potentially malicious events e.g. tampering with Audit logs, malicious access to files storing information about system users and groups etc. + + CCE-85893-6 + - name: Put contents into /etc/audit/rules.d/30-ospp-v42.rules according to policy + copy: + dest: /etc/audit/rules.d/30-ospp-v42.rules + content: |+ + ## The purpose of these rules is to meet the requirements for Operating + ## System Protection Profile (OSPP)v4.2. These rules depends on having + ## the following rule files copied to /etc/audit/rules.d: + ## + ## 10-base-config.rules, 11-loginuid.rules, + ## 30-ospp-v42-1-create-failed.rules, 30-ospp-v42-1-create-success.rules, + ## 30-ospp-v42-2-modify-failed.rules, 30-ospp-v42-2-modify-success.rules, + ## 30-ospp-v42-3-access-failed.rules, 30-ospp-v42-3-access-success.rules, + ## 30-ospp-v42-4-delete-failed.rules, 30-ospp-v42-4-delete-success.rules, + ## 30-ospp-v42-5-perm-change-failed.rules, + ## 30-ospp-v42-5-perm-change-success.rules, + ## 30-ospp-v42-6-owner-change-failed.rules, + ## 30-ospp-v42-6-owner-change-success.rules + ## + ## original copies may be found in /usr/share/audit/sample-rules/ + + + ## User add delete modify. This is covered by pam. However, someone could + ## open a file and directly create or modify a user, so we'll watch passwd and + ## shadow for writes + -a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=user-modify + -a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=user-modify + -a always,exit -F arch=b32 -S open -F a1&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=user-modify + -a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify + -a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify + -a always,exit -F arch=b32 -S open -F a1&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify + + ## User enable and disable. This is entirely handled by pam. + + ## Group add delete modify. This is covered by pam. However, someone could + ## open a file and directly create or modify a user, so we'll watch group and + ## gshadow for writes + -a always,exit -F path=/etc/passwd -F perm=wa -F auid>=1000 -F auid!=unset -F key=user-modify + -a always,exit -F path=/etc/shadow -F perm=wa -F auid>=1000 -F auid!=unset -F key=user-modify + -a always,exit -F path=/etc/group -F perm=wa -F auid>=1000 -F auid!=unset -F key=group-modify + -a always,exit -F path=/etc/gshadow -F perm=wa -F auid>=1000 -F auid!=unset -F key=group-modify + + + ## Use of special rights for config changes. This would be use of setuid + ## programs that relate to user accts. This is not all setuid apps because + ## requirements are only for ones that affect system configuration. + -a always,exit -F path=/usr/sbin/unix_chkpwd -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes + -a always,exit -F path=/usr/sbin/usernetctl -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes + -a always,exit -F path=/usr/sbin/userhelper -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes + -a always,exit -F path=/usr/sbin/seunshare -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes + -a always,exit -F path=/usr/bin/mount -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes + -a always,exit -F path=/usr/bin/newgrp -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes + -a always,exit -F path=/usr/bin/newuidmap -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes + -a always,exit -F path=/usr/bin/gpasswd -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes + -a always,exit -F path=/usr/bin/newgidmap -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes + -a always,exit -F path=/usr/bin/umount -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes + -a always,exit -F path=/usr/bin/passwd -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes + -a always,exit -F path=/usr/bin/crontab -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes + -a always,exit -F path=/usr/bin/at -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes + + ## Privilege escalation via su or sudo. This is entirely handled by pam. + + ## Watch for configuration changes to privilege escalation. + -a always,exit -F path=/etc/sudoers -F perm=wa -F key=special-config-changes + -a always,exit -F dir=/etc/sudoers.d/ -F perm=wa -F key=special-config-changes + + ## Audit log access + -a always,exit -F dir=/var/log/audit/ -F perm=r -F auid>=1000 -F auid!=unset -F key=access-audit-trail + ## Attempts to Alter Process and Session Initiation Information + -a always,exit -F path=/var/run/utmp -F perm=wa -F auid>=1000 -F auid!=unset -F key=session + -a always,exit -F path=/var/log/btmp -F perm=wa -F auid>=1000 -F auid!=unset -F key=session + -a always,exit -F path=/var/log/wtmp -F perm=wa -F auid>=1000 -F auid!=unset -F key=session + + ## Attempts to modify MAC controls + -a always,exit -F dir=/etc/selinux/ -F perm=wa -F auid>=1000 -F auid!=unset -F key=MAC-policy + + ## Software updates. This is entirely handled by rpm. + + ## System start and shutdown. This is entirely handled by systemd + + ## Kernel Module loading. This is handled in 43-module-load.rules + + ## Application invocation. The requirements list an user requirement + ## FPT_SRP_EXT.1 Software Restriction Policies. This event is intended to + ## state results from that policy. This would be handled entirely by + ## that daemon. + + force: true + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ansible_architecture == "aarch64" + tags: + - CCE-85893-6 + - NIST-800-53-AU-2(a) + - audit_ospp_general_aarch64 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Remove any permissions from other group + file: + path: /etc/audit/rules.d/30-ospp-v42.rules + mode: o-rwx + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ansible_architecture == "aarch64" + tags: + - CCE-85893-6 + - NIST-800-53-AU-2(a) + - audit_ospp_general_aarch64 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,## The purpose of these rules is to meet the requirements for Operating +## System Protection Profile (OSPP)v4.2. These rules depends on having +## the following rule files copied to /etc/audit/rules.d: +## +## 10-base-config.rules, 11-loginuid.rules, +## 30-ospp-v42-1-create-failed.rules, 30-ospp-v42-1-create-success.rules, +## 30-ospp-v42-2-modify-failed.rules, 30-ospp-v42-2-modify-success.rules, +## 30-ospp-v42-3-access-failed.rules, 30-ospp-v42-3-access-success.rules, +## 30-ospp-v42-4-delete-failed.rules, 30-ospp-v42-4-delete-success.rules, +## 30-ospp-v42-5-perm-change-failed.rules, +## 30-ospp-v42-5-perm-change-success.rules, +## 30-ospp-v42-6-owner-change-failed.rules, +## 30-ospp-v42-6-owner-change-success.rules +## +## original copies may be found in /usr/share/audit/sample-rules/ + + +## User add delete modify. This is covered by pam. However, someone could +## open a file and directly create or modify a user, so we'll watch passwd and +## shadow for writes +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&amp;03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&amp;03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b32 -S open -F a1&amp;03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&amp;03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&amp;03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b32 -S open -F a1&amp;03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify + +## User enable and disable. This is entirely handled by pam. + +## Group add delete modify. This is covered by pam. However, someone could +## open a file and directly create or modify a user, so we'll watch group and +## gshadow for writes +-a always,exit -F path=/etc/passwd -F perm=wa -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F path=/etc/shadow -F perm=wa -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F path=/etc/group -F perm=wa -F auid>=1000 -F auid!=unset -F key=group-modify +-a always,exit -F path=/etc/gshadow -F perm=wa -F auid>=1000 -F auid!=unset -F key=group-modify + + +## Use of special rights for config changes. This would be use of setuid +## programs that relate to user accts. This is not all setuid apps because +## requirements are only for ones that affect system configuration. +-a always,exit -F path=/usr/sbin/unix_chkpwd -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/sbin/usernetctl -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/sbin/userhelper -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/sbin/seunshare -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/mount -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/newgrp -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/newuidmap -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/gpasswd -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/newgidmap -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/umount -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/passwd -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/crontab -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/at -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes + +## Privilege escalation via su or sudo. This is entirely handled by pam. + +## Watch for configuration changes to privilege escalation. +-a always,exit -F path=/etc/sudoers -F perm=wa -F key=special-config-changes +-a always,exit -F dir=/etc/sudoers.d/ -F perm=wa -F key=special-config-changes + +## Audit log access +-a always,exit -F dir=/var/log/audit/ -F perm=r -F auid>=1000 -F auid!=unset -F key=access-audit-trail +## Attempts to Alter Process and Session Initiation Information +-a always,exit -F path=/var/run/utmp -F perm=wa -F auid>=1000 -F auid!=unset -F key=session +-a always,exit -F path=/var/log/btmp -F perm=wa -F auid>=1000 -F auid!=unset -F key=session +-a always,exit -F path=/var/log/wtmp -F perm=wa -F auid>=1000 -F auid!=unset -F key=session + +## Attempts to modify MAC controls +-a always,exit -F dir=/etc/selinux/ -F perm=wa -F auid>=1000 -F auid!=unset -F key=MAC-policy + +## Software updates. This is entirely handled by rpm. + +## System start and shutdown. This is entirely handled by systemd + +## Kernel Module loading. This is handled in 43-module-load.rules + +## Application invocation. The requirements list an user requirement +## FPT_SRP_EXT.1 Software Restriction Policies. This event is intended to +## state results from that policy. This would be handled entirely by +## that daemon.%0A + mode: 0600 + path: /etc/audit/rules.d/30-ospp-v42.rules + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && { grep -q aarch64 /proc/sys/kernel/osrelease; }; then + +cat << 'EOF' > /etc/audit/rules.d/30-ospp-v42.rules +## The purpose of these rules is to meet the requirements for Operating +## System Protection Profile (OSPP)v4.2. These rules depends on having +## the following rule files copied to /etc/audit/rules.d: +## +## 10-base-config.rules, 11-loginuid.rules, +## 30-ospp-v42-1-create-failed.rules, 30-ospp-v42-1-create-success.rules, +## 30-ospp-v42-2-modify-failed.rules, 30-ospp-v42-2-modify-success.rules, +## 30-ospp-v42-3-access-failed.rules, 30-ospp-v42-3-access-success.rules, +## 30-ospp-v42-4-delete-failed.rules, 30-ospp-v42-4-delete-success.rules, +## 30-ospp-v42-5-perm-change-failed.rules, +## 30-ospp-v42-5-perm-change-success.rules, +## 30-ospp-v42-6-owner-change-failed.rules, +## 30-ospp-v42-6-owner-change-success.rules +## +## original copies may be found in /usr/share/audit/sample-rules/ + + +## User add delete modify. This is covered by pam. However, someone could +## open a file and directly create or modify a user, so we'll watch passwd and +## shadow for writes +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b32 -S open -F a1&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b32 -S open -F a1&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify + +## User enable and disable. This is entirely handled by pam. + +## Group add delete modify. This is covered by pam. However, someone could +## open a file and directly create or modify a user, so we'll watch group and +## gshadow for writes +-a always,exit -F path=/etc/passwd -F perm=wa -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F path=/etc/shadow -F perm=wa -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F path=/etc/group -F perm=wa -F auid>=1000 -F auid!=unset -F key=group-modify +-a always,exit -F path=/etc/gshadow -F perm=wa -F auid>=1000 -F auid!=unset -F key=group-modify + + +## Use of special rights for config changes. This would be use of setuid +## programs that relate to user accts. This is not all setuid apps because +## requirements are only for ones that affect system configuration. +-a always,exit -F path=/usr/sbin/unix_chkpwd -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/sbin/usernetctl -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/sbin/userhelper -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/sbin/seunshare -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/mount -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/newgrp -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/newuidmap -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/gpasswd -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/newgidmap -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/umount -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/passwd -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/crontab -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/at -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes + +## Privilege escalation via su or sudo. This is entirely handled by pam. + +## Watch for configuration changes to privilege escalation. +-a always,exit -F path=/etc/sudoers -F perm=wa -F key=special-config-changes +-a always,exit -F dir=/etc/sudoers.d/ -F perm=wa -F key=special-config-changes + +## Audit log access +-a always,exit -F dir=/var/log/audit/ -F perm=r -F auid>=1000 -F auid!=unset -F key=access-audit-trail +## Attempts to Alter Process and Session Initiation Information +-a always,exit -F path=/var/run/utmp -F perm=wa -F auid>=1000 -F auid!=unset -F key=session +-a always,exit -F path=/var/log/btmp -F perm=wa -F auid>=1000 -F auid!=unset -F key=session +-a always,exit -F path=/var/log/wtmp -F perm=wa -F auid>=1000 -F auid!=unset -F key=session + +## Attempts to modify MAC controls +-a always,exit -F dir=/etc/selinux/ -F perm=wa -F auid>=1000 -F auid!=unset -F key=MAC-policy + +## Software updates. This is entirely handled by rpm. + +## System start and shutdown. This is entirely handled by systemd + +## Kernel Module loading. This is handled in 43-module-load.rules + +## Application invocation. The requirements list an user requirement +## FPT_SRP_EXT.1 Software Restriction Policies. This event is intended to +## state results from that policy. This would be handled entirely by +## that daemon. + +EOF + +chmod o-rwx /etc/audit/rules.d/30-ospp-v42.rules + +augenrules --load + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Perform general configuration of Audit for OSPP (ppc64le) + Configure some basic Audit parameters specific for OSPP profile. +In particular, configure Audit to watch for direct modification of files storing system user and group information, and usage of applications with special rights which can change system configuration. +Further audited events include access to audit log it self, attempts to Alter Process and Session Initiation Information, and attempts to modify MAC controls. + +The following rules configure audit as described above: +## The purpose of these rules is to meet the requirements for Operating +## System Protection Profile (OSPP)v4.2. These rules depends on having +## the following rule files copied to /etc/audit/rules.d: +## +## 10-base-config.rules, 11-loginuid.rules, +## 30-ospp-v42-1-create-failed.rules, 30-ospp-v42-1-create-success.rules, +## 30-ospp-v42-2-modify-failed.rules, 30-ospp-v42-2-modify-success.rules, +## 30-ospp-v42-3-access-failed.rules, 30-ospp-v42-3-access-success.rules, +## 30-ospp-v42-4-delete-failed.rules, 30-ospp-v42-4-delete-success.rules, +## 30-ospp-v42-5-perm-change-failed.rules, +## 30-ospp-v42-5-perm-change-success.rules, +## 30-ospp-v42-6-owner-change-failed.rules, +## 30-ospp-v42-6-owner-change-success.rules +## +## original copies may be found in /usr/share/audit/sample-rules/ + + +## User add delete modify. This is covered by pam. However, someone could +## open a file and directly create or modify a user, so we'll watch passwd and +## shadow for writes +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b64 -S open -F a1&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b64 -S open -F a1&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify + +## User enable and disable. This is entirely handled by pam. + +## Group add delete modify. This is covered by pam. However, someone could +## open a file and directly create or modify a user, so we'll watch group and +## gshadow for writes +-a always,exit -F path=/etc/passwd -F perm=wa -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F path=/etc/shadow -F perm=wa -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F path=/etc/group -F perm=wa -F auid>=1000 -F auid!=unset -F key=group-modify +-a always,exit -F path=/etc/gshadow -F perm=wa -F auid>=1000 -F auid!=unset -F key=group-modify + + +## Use of special rights for config changes. This would be use of setuid +## programs that relate to user accts. This is not all setuid apps because +## requirements are only for ones that affect system configuration. +-a always,exit -F path=/usr/sbin/unix_chkpwd -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/sbin/usernetctl -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/sbin/userhelper -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/sbin/seunshare -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/mount -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/newgrp -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/newuidmap -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/gpasswd -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/newgidmap -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/umount -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/passwd -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/crontab -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/at -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes + +## Privilege escalation via su or sudo. This is entirely handled by pam. + +## Watch for configuration changes to privilege escalation. +-a always,exit -F path=/etc/sudoers -F perm=wa -F key=special-config-changes +-a always,exit -F dir=/etc/sudoers.d/ -F perm=wa -F key=special-config-changes + +## Audit log access +-a always,exit -F dir=/var/log/audit/ -F perm=r -F auid>=1000 -F auid!=unset -F key=access-audit-trail +## Attempts to Alter Process and Session Initiation Information +-a always,exit -F path=/var/run/utmp -F perm=wa -F auid>=1000 -F auid!=unset -F key=session +-a always,exit -F path=/var/log/btmp -F perm=wa -F auid>=1000 -F auid!=unset -F key=session +-a always,exit -F path=/var/log/wtmp -F perm=wa -F auid>=1000 -F auid!=unset -F key=session + +## Attempts to modify MAC controls +-a always,exit -F dir=/etc/selinux/ -F perm=wa -F auid>=1000 -F auid!=unset -F key=MAC-policy + +## Software updates. This is entirely handled by rpm. + +## System start and shutdown. This is entirely handled by systemd + +## Kernel Module loading. This is handled in 43-module-load.rules + +## Application invocation. The requirements list an user requirement +## FPT_SRP_EXT.1 Software Restriction Policies. This event is intended to +## state results from that policy. This would be handled entirely by +## that daemon. + +Load new Audit rules into kernel by running: +augenrules --load + +Note: This rule uses a special set of Audit rules to comply with OSPP 4.2.1. You may reuse this rule in different profiles. If you decide to do so, it is recommended that you inspect contents of the file closely and make sure that they are alligned with your needs. + AU-2(a) + FAU_GEN.1.1.c + SRG-OS-000004-GPOS-00004 + SRG-OS-000241-GPOS-00091 + SRG-OS-000476-GPOS-00221 + SRG-OS-000327-GPOS-00127 + SRG-OS-000475-GPOS-00220 + SRG-OS-000239-GPOS-00089 + SRG-OS-000274-GPOS-00104 + SRG-OS-000275-GPOS-00105 + SRG-OS-000303-GPOS-00120 + SRG-OS-000304-GPOS-00121 + Auditing of events listed in the description provides data for monitoring and investigation of potentially malicious events e.g. tampering with Audit logs, malicious access to files storing information about system users and groups etc. + + CCE-90786-5 + - name: Put contents into /etc/audit/rules.d/30-ospp-v42.rules according to policy + copy: + dest: /etc/audit/rules.d/30-ospp-v42.rules + content: |+ + ## The purpose of these rules is to meet the requirements for Operating + ## System Protection Profile (OSPP)v4.2. These rules depends on having + ## the following rule files copied to /etc/audit/rules.d: + ## + ## 10-base-config.rules, 11-loginuid.rules, + ## 30-ospp-v42-1-create-failed.rules, 30-ospp-v42-1-create-success.rules, + ## 30-ospp-v42-2-modify-failed.rules, 30-ospp-v42-2-modify-success.rules, + ## 30-ospp-v42-3-access-failed.rules, 30-ospp-v42-3-access-success.rules, + ## 30-ospp-v42-4-delete-failed.rules, 30-ospp-v42-4-delete-success.rules, + ## 30-ospp-v42-5-perm-change-failed.rules, + ## 30-ospp-v42-5-perm-change-success.rules, + ## 30-ospp-v42-6-owner-change-failed.rules, + ## 30-ospp-v42-6-owner-change-success.rules + ## + ## original copies may be found in /usr/share/audit/sample-rules/ + + + ## User add delete modify. This is covered by pam. However, someone could + ## open a file and directly create or modify a user, so we'll watch passwd and + ## shadow for writes + -a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=user-modify + -a always,exit -F arch=b64 -S open -F a1&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=user-modify + -a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify + -a always,exit -F arch=b64 -S open -F a1&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify + + ## User enable and disable. This is entirely handled by pam. + + ## Group add delete modify. This is covered by pam. However, someone could + ## open a file and directly create or modify a user, so we'll watch group and + ## gshadow for writes + -a always,exit -F path=/etc/passwd -F perm=wa -F auid>=1000 -F auid!=unset -F key=user-modify + -a always,exit -F path=/etc/shadow -F perm=wa -F auid>=1000 -F auid!=unset -F key=user-modify + -a always,exit -F path=/etc/group -F perm=wa -F auid>=1000 -F auid!=unset -F key=group-modify + -a always,exit -F path=/etc/gshadow -F perm=wa -F auid>=1000 -F auid!=unset -F key=group-modify + + + ## Use of special rights for config changes. This would be use of setuid + ## programs that relate to user accts. This is not all setuid apps because + ## requirements are only for ones that affect system configuration. + -a always,exit -F path=/usr/sbin/unix_chkpwd -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes + -a always,exit -F path=/usr/sbin/usernetctl -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes + -a always,exit -F path=/usr/sbin/userhelper -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes + -a always,exit -F path=/usr/sbin/seunshare -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes + -a always,exit -F path=/usr/bin/mount -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes + -a always,exit -F path=/usr/bin/newgrp -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes + -a always,exit -F path=/usr/bin/newuidmap -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes + -a always,exit -F path=/usr/bin/gpasswd -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes + -a always,exit -F path=/usr/bin/newgidmap -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes + -a always,exit -F path=/usr/bin/umount -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes + -a always,exit -F path=/usr/bin/passwd -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes + -a always,exit -F path=/usr/bin/crontab -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes + -a always,exit -F path=/usr/bin/at -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes + + ## Privilege escalation via su or sudo. This is entirely handled by pam. + + ## Watch for configuration changes to privilege escalation. + -a always,exit -F path=/etc/sudoers -F perm=wa -F key=special-config-changes + -a always,exit -F dir=/etc/sudoers.d/ -F perm=wa -F key=special-config-changes + + ## Audit log access + -a always,exit -F dir=/var/log/audit/ -F perm=r -F auid>=1000 -F auid!=unset -F key=access-audit-trail + ## Attempts to Alter Process and Session Initiation Information + -a always,exit -F path=/var/run/utmp -F perm=wa -F auid>=1000 -F auid!=unset -F key=session + -a always,exit -F path=/var/log/btmp -F perm=wa -F auid>=1000 -F auid!=unset -F key=session + -a always,exit -F path=/var/log/wtmp -F perm=wa -F auid>=1000 -F auid!=unset -F key=session + + ## Attempts to modify MAC controls + -a always,exit -F dir=/etc/selinux/ -F perm=wa -F auid>=1000 -F auid!=unset -F key=MAC-policy + + ## Software updates. This is entirely handled by rpm. + + ## System start and shutdown. This is entirely handled by systemd + + ## Kernel Module loading. This is handled in 43-module-load.rules + + ## Application invocation. The requirements list an user requirement + ## FPT_SRP_EXT.1 Software Restriction Policies. This event is intended to + ## state results from that policy. This would be handled entirely by + ## that daemon. + + force: true + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ansible_architecture == "ppc64le" + tags: + - CCE-90786-5 + - NIST-800-53-AU-2(a) + - audit_ospp_general_ppc64le + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Remove any permissions from other group + file: + path: /etc/audit/rules.d/30-ospp-v42.rules + mode: o-rwx + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ansible_architecture == "ppc64le" + tags: + - CCE-90786-5 + - NIST-800-53-AU-2(a) + - audit_ospp_general_ppc64le + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,%23%23%20The%20purpose%20of%20these%20rules%20is%20to%20meet%20the%20requirements%20for%20Operating%0A%23%23%20System%20Protection%20Profile%20%28OSPP%29v4.2.%20These%20rules%20depends%20on%20having%0A%23%23%20the%20following%20rule%20files%20copied%20to%20%2Fetc%2Faudit%2Frules.d%3A%0A%23%23%0A%23%23%2010-base-config.rules%2C%2011-loginuid.rules%2C%0A%23%23%2030-ospp-v42-1-create-failed.rules%2C%2030-ospp-v42-1-create-success.rules%2C%0A%23%23%2030-ospp-v42-2-modify-failed.rules%2C%2030-ospp-v42-2-modify-success.rules%2C%0A%23%23%2030-ospp-v42-3-access-failed.rules%2C%2030-ospp-v42-3-access-success.rules%2C%0A%23%23%2030-ospp-v42-4-delete-failed.rules%2C%2030-ospp-v42-4-delete-success.rules%2C%0A%23%23%2030-ospp-v42-5-perm-change-failed.rules%2C%0A%23%23%2030-ospp-v42-5-perm-change-success.rules%2C%0A%23%23%2030-ospp-v42-6-owner-change-failed.rules%2C%0A%23%23%2030-ospp-v42-6-owner-change-success.rules%0A%23%23%0A%23%23%20original%20copies%20may%20be%20found%20in%20%2Fusr%2Fshare%2Faudit%2Fsample-rules%2F%0A%0A%0A%23%23%20User%20add%20delete%20modify.%20This%20is%20covered%20by%20pam.%20However%2C%20someone%20could%0A%23%23%20open%20a%20file%20and%20directly%20create%20or%20modify%20a%20user%2C%20so%20we%27ll%20watch%20passwd%20and%0A%23%23%20shadow%20for%20writes%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20openat%2Copen_by_handle_at%20-F%20a2%2603%20-F%20path%3D%2Fetc%2Fpasswd%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Duser-modify%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20open%20-F%20a1%2603%20-F%20path%3D%2Fetc%2Fpasswd%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Duser-modify%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20openat%2Copen_by_handle_at%20-F%20a2%2603%20-F%20path%3D%2Fetc%2Fshadow%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Duser-modify%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20open%20-F%20a1%2603%20-F%20path%3D%2Fetc%2Fshadow%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Duser-modify%0A%0A%23%23%20User%20enable%20and%20disable.%20This%20is%20entirely%20handled%20by%20pam.%0A%0A%23%23%20Group%20add%20delete%20modify.%20This%20is%20covered%20by%20pam.%20However%2C%20someone%20could%0A%23%23%20open%20a%20file%20and%20directly%20create%20or%20modify%20a%20user%2C%20so%20we%27ll%20watch%20group%20and%0A%23%23%20gshadow%20for%20writes%0A-a%20always%2Cexit%20-F%20path%3D%2Fetc%2Fpasswd%20-F%20perm%3Dwa%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Duser-modify%0A-a%20always%2Cexit%20-F%20path%3D%2Fetc%2Fshadow%20-F%20perm%3Dwa%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Duser-modify%0A-a%20always%2Cexit%20-F%20path%3D%2Fetc%2Fgroup%20-F%20perm%3Dwa%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dgroup-modify%0A-a%20always%2Cexit%20-F%20path%3D%2Fetc%2Fgshadow%20-F%20perm%3Dwa%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dgroup-modify%0A%0A%0A%23%23%20Use%20of%20special%20rights%20for%20config%20changes.%20This%20would%20be%20use%20of%20setuid%0A%23%23%20programs%20that%20relate%20to%20user%20accts.%20This%20is%20not%20all%20setuid%20apps%20because%0A%23%23%20requirements%20are%20only%20for%20ones%20that%20affect%20system%20configuration.%0A-a%20always%2Cexit%20-F%20path%3D%2Fusr%2Fsbin%2Funix_chkpwd%20-F%20perm%3Dx%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dspecial-config-changes%0A-a%20always%2Cexit%20-F%20path%3D%2Fusr%2Fsbin%2Fusernetctl%20-F%20perm%3Dx%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dspecial-config-changes%0A-a%20always%2Cexit%20-F%20path%3D%2Fusr%2Fsbin%2Fuserhelper%20-F%20perm%3Dx%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dspecial-config-changes%0A-a%20always%2Cexit%20-F%20path%3D%2Fusr%2Fsbin%2Fseunshare%20-F%20perm%3Dx%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dspecial-config-changes%0A-a%20always%2Cexit%20-F%20path%3D%2Fusr%2Fbin%2Fmount%20-F%20perm%3Dx%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dspecial-config-changes%0A-a%20always%2Cexit%20-F%20path%3D%2Fusr%2Fbin%2Fnewgrp%20-F%20perm%3Dx%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dspecial-config-changes%0A-a%20always%2Cexit%20-F%20path%3D%2Fusr%2Fbin%2Fnewuidmap%20-F%20perm%3Dx%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dspecial-config-changes%0A-a%20always%2Cexit%20-F%20path%3D%2Fusr%2Fbin%2Fgpasswd%20-F%20perm%3Dx%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dspecial-config-changes%0A-a%20always%2Cexit%20-F%20path%3D%2Fusr%2Fbin%2Fnewgidmap%20-F%20perm%3Dx%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dspecial-config-changes%0A-a%20always%2Cexit%20-F%20path%3D%2Fusr%2Fbin%2Fumount%20-F%20perm%3Dx%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dspecial-config-changes%0A-a%20always%2Cexit%20-F%20path%3D%2Fusr%2Fbin%2Fpasswd%20-F%20perm%3Dx%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dspecial-config-changes%0A-a%20always%2Cexit%20-F%20path%3D%2Fusr%2Fbin%2Fcrontab%20-F%20perm%3Dx%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dspecial-config-changes%0A-a%20always%2Cexit%20-F%20path%3D%2Fusr%2Fbin%2Fat%20-F%20perm%3Dx%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dspecial-config-changes%0A%0A%23%23%20Privilege%20escalation%20via%20su%20or%20sudo.%20This%20is%20entirely%20handled%20by%20pam.%0A%0A%23%23%20Watch%20for%20configuration%20changes%20to%20privilege%20escalation.%0A-a%20always%2Cexit%20-F%20path%3D%2Fetc%2Fsudoers%20-F%20perm%3Dwa%20-F%20key%3Dspecial-config-changes%0A-a%20always%2Cexit%20-F%20dir%3D%2Fetc%2Fsudoers.d%2F%20-F%20perm%3Dwa%20-F%20key%3Dspecial-config-changes%0A%0A%23%23%20Audit%20log%20access%0A-a%20always%2Cexit%20-F%20dir%3D%2Fvar%2Flog%2Faudit%2F%20-F%20perm%3Dr%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Daccess-audit-trail%0A%23%23%20Attempts%20to%20Alter%20Process%20and%20Session%20Initiation%20Information%0A-a%20always%2Cexit%20-F%20path%3D%2Fvar%2Frun%2Futmp%20-F%20perm%3Dwa%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dsession%0A-a%20always%2Cexit%20-F%20path%3D%2Fvar%2Flog%2Fbtmp%20-F%20perm%3Dwa%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dsession%0A-a%20always%2Cexit%20-F%20path%3D%2Fvar%2Flog%2Fwtmp%20-F%20perm%3Dwa%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3Dsession%0A%0A%23%23%20Attempts%20to%20modify%20MAC%20controls%0A-a%20always%2Cexit%20-F%20dir%3D%2Fetc%2Fselinux%2F%20-F%20perm%3Dwa%20-F%20auid%3E%3D1000%20-F%20auid%21%3Dunset%20-F%20key%3DMAC-policy%0A%0A%23%23%20Software%20updates.%20This%20is%20entirely%20handled%20by%20rpm.%0A%0A%23%23%20System%20start%20and%20shutdown.%20This%20is%20entirely%20handled%20by%20systemd%0A%0A%23%23%20Kernel%20Module%20loading.%20This%20is%20handled%20in%2043-module-load.rules%0A%0A%23%23%20Application%20invocation.%20The%20requirements%20list%20an%20user%20requirement%0A%23%23%20FPT_SRP_EXT.1%20Software%20Restriction%20Policies.%20This%20event%20is%20intended%20to%0A%23%23%20state%20results%20from%20that%20policy.%20This%20would%20be%20handled%20entirely%20by%0A%23%23%20that%20daemon.%0A + mode: 0600 + path: /etc/audit/rules.d/30-ospp-v42.rules + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && { grep -q ppc64le /proc/sys/kernel/osrelease; }; then + +cat << 'EOF' > /etc/audit/rules.d/30-ospp-v42.rules +## The purpose of these rules is to meet the requirements for Operating +## System Protection Profile (OSPP)v4.2. These rules depends on having +## the following rule files copied to /etc/audit/rules.d: +## +## 10-base-config.rules, 11-loginuid.rules, +## 30-ospp-v42-1-create-failed.rules, 30-ospp-v42-1-create-success.rules, +## 30-ospp-v42-2-modify-failed.rules, 30-ospp-v42-2-modify-success.rules, +## 30-ospp-v42-3-access-failed.rules, 30-ospp-v42-3-access-success.rules, +## 30-ospp-v42-4-delete-failed.rules, 30-ospp-v42-4-delete-success.rules, +## 30-ospp-v42-5-perm-change-failed.rules, +## 30-ospp-v42-5-perm-change-success.rules, +## 30-ospp-v42-6-owner-change-failed.rules, +## 30-ospp-v42-6-owner-change-success.rules +## +## original copies may be found in /usr/share/audit/sample-rules/ + + +## User add delete modify. This is covered by pam. However, someone could +## open a file and directly create or modify a user, so we'll watch passwd and +## shadow for writes +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b64 -S open -F a1&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b64 -S open -F a1&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify + +## User enable and disable. This is entirely handled by pam. + +## Group add delete modify. This is covered by pam. However, someone could +## open a file and directly create or modify a user, so we'll watch group and +## gshadow for writes +-a always,exit -F path=/etc/passwd -F perm=wa -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F path=/etc/shadow -F perm=wa -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F path=/etc/group -F perm=wa -F auid>=1000 -F auid!=unset -F key=group-modify +-a always,exit -F path=/etc/gshadow -F perm=wa -F auid>=1000 -F auid!=unset -F key=group-modify + + +## Use of special rights for config changes. This would be use of setuid +## programs that relate to user accts. This is not all setuid apps because +## requirements are only for ones that affect system configuration. +-a always,exit -F path=/usr/sbin/unix_chkpwd -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/sbin/usernetctl -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/sbin/userhelper -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/sbin/seunshare -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/mount -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/newgrp -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/newuidmap -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/gpasswd -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/newgidmap -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/umount -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/passwd -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/crontab -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/at -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes + +## Privilege escalation via su or sudo. This is entirely handled by pam. + +## Watch for configuration changes to privilege escalation. +-a always,exit -F path=/etc/sudoers -F perm=wa -F key=special-config-changes +-a always,exit -F dir=/etc/sudoers.d/ -F perm=wa -F key=special-config-changes + +## Audit log access +-a always,exit -F dir=/var/log/audit/ -F perm=r -F auid>=1000 -F auid!=unset -F key=access-audit-trail +## Attempts to Alter Process and Session Initiation Information +-a always,exit -F path=/var/run/utmp -F perm=wa -F auid>=1000 -F auid!=unset -F key=session +-a always,exit -F path=/var/log/btmp -F perm=wa -F auid>=1000 -F auid!=unset -F key=session +-a always,exit -F path=/var/log/wtmp -F perm=wa -F auid>=1000 -F auid!=unset -F key=session + +## Attempts to modify MAC controls +-a always,exit -F dir=/etc/selinux/ -F perm=wa -F auid>=1000 -F auid!=unset -F key=MAC-policy + +## Software updates. This is entirely handled by rpm. + +## System start and shutdown. This is entirely handled by systemd + +## Kernel Module loading. This is handled in 43-module-load.rules + +## Application invocation. The requirements list an user requirement +## FPT_SRP_EXT.1 Software Restriction Policies. This event is intended to +## state results from that policy. This would be handled entirely by +## that daemon. + +EOF + +chmod o-rwx /etc/audit/rules.d/30-ospp-v42.rules + +augenrules --load + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure auditing of unsuccessful ownership changes + Ensure that unsuccessful attempts to change an ownership of files or directories are audited. + +The following rules configure audit as described above: +## Unsuccessful ownership change +-a always,exit -F arch=b32 -S lchown,fchown,chown,fchownat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-owner-change +-a always,exit -F arch=b64 -S lchown,fchown,chown,fchownat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-owner-change +-a always,exit -F arch=b32 -S lchown,fchown,chown,fchownat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-owner-change +-a always,exit -F arch=b64 -S lchown,fchown,chown,fchownat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-owner-change + +Load new Audit rules into kernel by running: +augenrules --load + +Note: This rule uses a special set of Audit rules to comply with OSPP 4.2.1. You may reuse this rule in different profiles. If you decide to do so, it is recommended that you inspect contents of the file closely and make sure that they are alligned with your needs. + AU-2(a) + FAU_GEN.1.1.c + SRG-OS-000462-GPOS-00206 + SRG-OS-000463-GPOS-00207 + SRG-OS-000465-GPOS-00209 + SRG-OS-000474-GPOS-00219 + SRG-OS-000475-GPOS-00220 + SRG-OS-000466-GPOS-00210 + SRG-OS-000064-GPOS-00033 + Unsuccessful attempts to change an ownership of files or directories might be signs of a malicious activity. Having such events audited helps in monitoring and investigation of such activities. + + CCE-83675-9 + - name: Put contents into /etc/audit/rules.d/30-ospp-v42-6-owner-change-failed.rules + according to policy + copy: + dest: /etc/audit/rules.d/30-ospp-v42-6-owner-change-failed.rules + content: | + ## Unsuccessful ownership change + -a always,exit -F arch=b32 -S lchown,fchown,chown,fchownat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-owner-change + -a always,exit -F arch=b64 -S lchown,fchown,chown,fchownat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-owner-change + -a always,exit -F arch=b32 -S lchown,fchown,chown,fchownat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-owner-change + -a always,exit -F arch=b64 -S lchown,fchown,chown,fchownat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-owner-change + force: true + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ( not ( ansible_architecture == "aarch64" ) and not ( ansible_architecture == + "ppc64le" ) ) + tags: + - CCE-83675-9 + - NIST-800-53-AU-2(a) + - audit_owner_change_failed + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Remove any permissions from other group + file: + path: /etc/audit/rules.d/30-ospp-v42-6-owner-change-failed.rules + mode: o-rwx + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ( not ( ansible_architecture == "aarch64" ) and not ( ansible_architecture == + "ppc64le" ) ) + tags: + - CCE-83675-9 + - NIST-800-53-AU-2(a) + - audit_owner_change_failed + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && { ( ! ( grep -q aarch64 /proc/sys/kernel/osrelease ) && ! ( grep -q ppc64le /proc/sys/kernel/osrelease ) ); }; then + +cat << 'EOF' > /etc/audit/rules.d/30-ospp-v42-6-owner-change-failed.rules +## Unsuccessful ownership change +-a always,exit -F arch=b32 -S lchown,fchown,chown,fchownat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-owner-change +-a always,exit -F arch=b64 -S lchown,fchown,chown,fchownat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-owner-change +-a always,exit -F arch=b32 -S lchown,fchown,chown,fchownat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-owner-change +-a always,exit -F arch=b64 -S lchown,fchown,chown,fchownat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-owner-change +EOF + +chmod o-rwx /etc/audit/rules.d/30-ospp-v42-6-owner-change-failed.rules + +augenrules --load + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure auditing of unsuccessful ownership changes (AArch64) + Ensure that unsuccessful attempts to change an ownership of files or directories are audited. + +The following rules configure audit as described above: +## Unsuccessful ownership change +-a always,exit -F arch=b32 -S lchown,fchown,chown,fchownat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-owner-change +-a always,exit -F arch=b64 -S fchown,fchownat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-owner-change +-a always,exit -F arch=b32 -S lchown,fchown,chown,fchownat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-owner-change +-a always,exit -F arch=b64 -S fchown,fchownat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-owner-change + +Load new Audit rules into kernel by running: +augenrules --load + +Note: This rule uses a special set of Audit rules to comply with OSPP 4.2.1. You may reuse this rule in different profiles. If you decide to do so, it is recommended that you inspect contents of the file closely and make sure that they are alligned with your needs. + AU-2(a) + FAU_GEN.1.1.c + SRG-OS-000462-GPOS-00206 + SRG-OS-000463-GPOS-00207 + SRG-OS-000465-GPOS-00209 + SRG-OS-000474-GPOS-00219 + SRG-OS-000475-GPOS-00220 + SRG-OS-000466-GPOS-00210 + SRG-OS-000064-GPOS-00033 + Unsuccessful attempts to change an ownership of files or directories might be signs of a malicious activity. Having such events audited helps in monitoring and investigation of such activities. + + CCE-85942-1 + - name: Put contents into /etc/audit/rules.d/30-ospp-v42-6-owner-change-failed.rules + according to policy + copy: + dest: /etc/audit/rules.d/30-ospp-v42-6-owner-change-failed.rules + content: | + ## Unsuccessful ownership change + -a always,exit -F arch=b32 -S lchown,fchown,chown,fchownat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-owner-change + -a always,exit -F arch=b64 -S fchown,fchownat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-owner-change + -a always,exit -F arch=b32 -S lchown,fchown,chown,fchownat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-owner-change + -a always,exit -F arch=b64 -S fchown,fchownat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-owner-change + force: true + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ansible_architecture == "aarch64" + tags: + - CCE-85942-1 + - NIST-800-53-AU-2(a) + - audit_owner_change_failed_aarch64 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Remove any permissions from other group + file: + path: /etc/audit/rules.d/30-ospp-v42-6-owner-change-failed.rules + mode: o-rwx + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ansible_architecture == "aarch64" + tags: + - CCE-85942-1 + - NIST-800-53-AU-2(a) + - audit_owner_change_failed_aarch64 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && { grep -q aarch64 /proc/sys/kernel/osrelease; }; then + +cat << 'EOF' > /etc/audit/rules.d/30-ospp-v42-6-owner-change-failed.rules +## Unsuccessful ownership change +-a always,exit -F arch=b32 -S lchown,fchown,chown,fchownat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-owner-change +-a always,exit -F arch=b64 -S fchown,fchownat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-owner-change +-a always,exit -F arch=b32 -S lchown,fchown,chown,fchownat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-owner-change +-a always,exit -F arch=b64 -S fchown,fchownat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-owner-change +EOF + +chmod o-rwx /etc/audit/rules.d/30-ospp-v42-6-owner-change-failed.rules + +augenrules --load + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure auditing of unsuccessful ownership changes (ppc64le) + Ensure that unsuccessful attempts to change an ownership of files or directories are audited. + +The following rules configure audit as described above: +## Unsuccessful ownership change +-a always,exit -F arch=b64 -S lchown,fchown,chown,fchownat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-owner-change +-a always,exit -F arch=b64 -S lchown,fchown,chown,fchownat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-owner-change + +Load new Audit rules into kernel by running: +augenrules --load + +Note: This rule uses a special set of Audit rules to comply with OSPP 4.2.1. You may reuse this rule in different profiles. If you decide to do so, it is recommended that you inspect contents of the file closely and make sure that they are alligned with your needs. + AU-2(a) + FAU_GEN.1.1.c + SRG-OS-000462-GPOS-00206 + SRG-OS-000463-GPOS-00207 + SRG-OS-000465-GPOS-00209 + SRG-OS-000474-GPOS-00219 + SRG-OS-000475-GPOS-00220 + SRG-OS-000466-GPOS-00210 + SRG-OS-000064-GPOS-00033 + Unsuccessful attempts to change an ownership of files or directories might be signs of a malicious activity. Having such events audited helps in monitoring and investigation of such activities. + + CCE-85988-4 + - name: Put contents into /etc/audit/rules.d/30-ospp-v42-6-owner-change-failed.rules + according to policy + copy: + dest: /etc/audit/rules.d/30-ospp-v42-6-owner-change-failed.rules + content: | + ## Unsuccessful ownership change + -a always,exit -F arch=b64 -S lchown,fchown,chown,fchownat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-owner-change + -a always,exit -F arch=b64 -S lchown,fchown,chown,fchownat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-owner-change + force: true + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ansible_architecture == "ppc64le" + tags: + - CCE-85988-4 + - NIST-800-53-AU-2(a) + - audit_owner_change_failed_ppc64le + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Remove any permissions from other group + file: + path: /etc/audit/rules.d/30-ospp-v42-6-owner-change-failed.rules + mode: o-rwx + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ansible_architecture == "ppc64le" + tags: + - CCE-85988-4 + - NIST-800-53-AU-2(a) + - audit_owner_change_failed_ppc64le + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && { grep -q ppc64le /proc/sys/kernel/osrelease; }; then + +cat << 'EOF' > /etc/audit/rules.d/30-ospp-v42-6-owner-change-failed.rules +## Unsuccessful ownership change +-a always,exit -F arch=b64 -S lchown,fchown,chown,fchownat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-owner-change +-a always,exit -F arch=b64 -S lchown,fchown,chown,fchownat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-owner-change +EOF + +chmod o-rwx /etc/audit/rules.d/30-ospp-v42-6-owner-change-failed.rules + +augenrules --load + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure auditing of successful ownership changes + Ensure that successful attempts to change an ownership of files or directories are audited. + +The following rules configure audit as described above: +## Successful ownership change +-a always,exit -F arch=b32 -S lchown,fchown,chown,fchownat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-owner-change +-a always,exit -F arch=b64 -S lchown,fchown,chown,fchownat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-owner-change + +Load new Audit rules into kernel by running: +augenrules --load + +Note: This rule uses a special set of Audit rules to comply with OSPP 4.2.1. You may reuse this rule in different profiles. If you decide to do so, it is recommended that you inspect contents of the file closely and make sure that they are alligned with your needs. + AU-2(a) + FAU_GEN.1.1.c + SRG-OS-000462-GPOS-00206 + SRG-OS-000463-GPOS-00207 + SRG-OS-000465-GPOS-00209 + SRG-OS-000474-GPOS-00219 + SRG-OS-000475-GPOS-00220 + SRG-OS-000466-GPOS-00210 + SRG-OS-000064-GPOS-00033 + Auditing of successful ownership changes of files or directories helps in monitoring or investingating of activities performed on the system. + + CCE-83658-5 + - name: Put contents into /etc/audit/rules.d/30-ospp-v42-6-owner-change-success.rules + according to policy + copy: + dest: /etc/audit/rules.d/30-ospp-v42-6-owner-change-success.rules + content: | + ## Successful ownership change + -a always,exit -F arch=b32 -S lchown,fchown,chown,fchownat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-owner-change + -a always,exit -F arch=b64 -S lchown,fchown,chown,fchownat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-owner-change + force: true + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ( not ( ansible_architecture == "aarch64" ) and not ( ansible_architecture == + "ppc64le" ) ) + tags: + - CCE-83658-5 + - NIST-800-53-AU-2(a) + - audit_owner_change_success + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Remove any permissions from other group + file: + path: /etc/audit/rules.d/30-ospp-v42-6-owner-change-success.rules + mode: o-rwx + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ( not ( ansible_architecture == "aarch64" ) and not ( ansible_architecture == + "ppc64le" ) ) + tags: + - CCE-83658-5 + - NIST-800-53-AU-2(a) + - audit_owner_change_success + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && { ( ! ( grep -q aarch64 /proc/sys/kernel/osrelease ) && ! ( grep -q ppc64le /proc/sys/kernel/osrelease ) ); }; then + +cat << 'EOF' > /etc/audit/rules.d/30-ospp-v42-6-owner-change-success.rules +## Successful ownership change +-a always,exit -F arch=b32 -S lchown,fchown,chown,fchownat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-owner-change +-a always,exit -F arch=b64 -S lchown,fchown,chown,fchownat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-owner-change +EOF + +chmod o-rwx /etc/audit/rules.d/30-ospp-v42-6-owner-change-success.rules + +augenrules --load + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure auditing of successful ownership changes (AArch64) + Ensure that successful attempts to change an ownership of files or directories are audited. + +The following rules configure audit as described above: +## Successful ownership change +-a always,exit -F arch=b32 -S lchown,fchown,chown,fchownat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-owner-change +-a always,exit -F arch=b64 -S fchown,fchownat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-owner-change + +Load new Audit rules into kernel by running: +augenrules --load + +Note: This rule uses a special set of Audit rules to comply with OSPP 4.2.1. You may reuse this rule in different profiles. If you decide to do so, it is recommended that you inspect contents of the file closely and make sure that they are alligned with your needs. + AU-2(a) + FAU_GEN.1.1.c + SRG-OS-000462-GPOS-00206 + SRG-OS-000463-GPOS-00207 + SRG-OS-000465-GPOS-00209 + SRG-OS-000474-GPOS-00219 + SRG-OS-000475-GPOS-00220 + SRG-OS-000466-GPOS-00210 + SRG-OS-000064-GPOS-00033 + Auditing of successful ownership changes of files or directories helps in monitoring or investingating of activities performed on the system. + + CCE-85948-8 + - name: Put contents into /etc/audit/rules.d/30-ospp-v42-6-owner-change-success.rules + according to policy + copy: + dest: /etc/audit/rules.d/30-ospp-v42-6-owner-change-success.rules + content: | + ## Successful ownership change + -a always,exit -F arch=b32 -S lchown,fchown,chown,fchownat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-owner-change + -a always,exit -F arch=b64 -S fchown,fchownat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-owner-change + force: true + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ansible_architecture == "aarch64" + tags: + - CCE-85948-8 + - NIST-800-53-AU-2(a) + - audit_owner_change_success_aarch64 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Remove any permissions from other group + file: + path: /etc/audit/rules.d/30-ospp-v42-6-owner-change-success.rules + mode: o-rwx + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ansible_architecture == "aarch64" + tags: + - CCE-85948-8 + - NIST-800-53-AU-2(a) + - audit_owner_change_success_aarch64 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && { grep -q aarch64 /proc/sys/kernel/osrelease; }; then + +cat << 'EOF' > /etc/audit/rules.d/30-ospp-v42-6-owner-change-success.rules +## Successful ownership change +-a always,exit -F arch=b32 -S lchown,fchown,chown,fchownat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-owner-change +-a always,exit -F arch=b64 -S fchown,fchownat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-owner-change +EOF + +chmod o-rwx /etc/audit/rules.d/30-ospp-v42-6-owner-change-success.rules + +augenrules --load + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure auditing of successful ownership changes (ppc64le) + Ensure that successful attempts to change an ownership of files or directories are audited. + +The following rules configure audit as described above: +## Successful ownership change +-a always,exit -F arch=b64 -S lchown,fchown,chown,fchownat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-owner-change + +Load new Audit rules into kernel by running: +augenrules --load + +Note: This rule uses a special set of Audit rules to comply with OSPP 4.2.1. You may reuse this rule in different profiles. If you decide to do so, it is recommended that you inspect contents of the file closely and make sure that they are alligned with your needs. + AU-2(a) + FAU_GEN.1.1.c + SRG-OS-000462-GPOS-00206 + SRG-OS-000463-GPOS-00207 + SRG-OS-000465-GPOS-00209 + SRG-OS-000474-GPOS-00219 + SRG-OS-000475-GPOS-00220 + SRG-OS-000466-GPOS-00210 + SRG-OS-000064-GPOS-00033 + Auditing of successful ownership changes of files or directories helps in monitoring or investingating of activities performed on the system. + + CCE-85998-3 + - name: Put contents into /etc/audit/rules.d/30-ospp-v42-6-owner-change-success.rules + according to policy + copy: + dest: /etc/audit/rules.d/30-ospp-v42-6-owner-change-success.rules + content: | + ## Successful ownership change + -a always,exit -F arch=b64 -S lchown,fchown,chown,fchownat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-owner-change + force: true + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ansible_architecture == "ppc64le" + tags: + - CCE-85998-3 + - NIST-800-53-AU-2(a) + - audit_owner_change_success_ppc64le + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Remove any permissions from other group + file: + path: /etc/audit/rules.d/30-ospp-v42-6-owner-change-success.rules + mode: o-rwx + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ansible_architecture == "ppc64le" + tags: + - CCE-85998-3 + - NIST-800-53-AU-2(a) + - audit_owner_change_success_ppc64le + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && { grep -q ppc64le /proc/sys/kernel/osrelease; }; then + +cat << 'EOF' > /etc/audit/rules.d/30-ospp-v42-6-owner-change-success.rules +## Successful ownership change +-a always,exit -F arch=b64 -S lchown,fchown,chown,fchownat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-owner-change +EOF + +chmod o-rwx /etc/audit/rules.d/30-ospp-v42-6-owner-change-success.rules + +augenrules --load + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure auditing of unsuccessful permission changes + Ensure that unsuccessful attempts to change file or directory permissions are audited. + +The following rules configure audit as described above: +## Unsuccessful permission change +-a always,exit -F arch=b32 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-perm-change +-a always,exit -F arch=b64 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-perm-change +-a always,exit -F arch=b32 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-perm-change +-a always,exit -F arch=b64 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-perm-change + +Load new Audit rules into kernel by running: +augenrules --load + +Note: This rule uses a special set of Audit rules to comply with OSPP 4.2.1. You may reuse this rule in different profiles. If you decide to do so, it is recommended that you inspect contents of the file closely and make sure that they are alligned with your needs. + AU-2(a) + FAU_GEN.1.1.c + SRG-OS-000462-GPOS-00206 + SRG-OS-000463-GPOS-00207 + SRG-OS-000465-GPOS-00209 + SRG-OS-000474-GPOS-00219 + SRG-OS-000475-GPOS-00220 + SRG-OS-000466-GPOS-00210 + SRG-OS-000064-GPOS-00033 + Unsuccessful attempts to change permissions of files or directories might be signs of malicious activity. Having such events audited helps in monitoring and investigation of such activities. + + CCE-83676-7 + - name: Put contents into /etc/audit/rules.d/30-ospp-v42-5-perm-change-failed.rules + according to policy + copy: + dest: /etc/audit/rules.d/30-ospp-v42-5-perm-change-failed.rules + content: | + ## Unsuccessful permission change + -a always,exit -F arch=b32 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-perm-change + -a always,exit -F arch=b64 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-perm-change + -a always,exit -F arch=b32 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-perm-change + -a always,exit -F arch=b64 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-perm-change + force: true + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ( not ( ansible_architecture == "aarch64" ) and not ( ansible_architecture == + "ppc64le" ) ) + tags: + - CCE-83676-7 + - NIST-800-53-AU-2(a) + - audit_perm_change_failed + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Remove any permissions from other group + file: + path: /etc/audit/rules.d/30-ospp-v42-5-perm-change-failed.rules + mode: o-rwx + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ( not ( ansible_architecture == "aarch64" ) and not ( ansible_architecture == + "ppc64le" ) ) + tags: + - CCE-83676-7 + - NIST-800-53-AU-2(a) + - audit_perm_change_failed + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && { ( ! ( grep -q aarch64 /proc/sys/kernel/osrelease ) && ! ( grep -q ppc64le /proc/sys/kernel/osrelease ) ); }; then + +cat << 'EOF' > /etc/audit/rules.d/30-ospp-v42-5-perm-change-failed.rules +## Unsuccessful permission change +-a always,exit -F arch=b32 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-perm-change +-a always,exit -F arch=b64 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-perm-change +-a always,exit -F arch=b32 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-perm-change +-a always,exit -F arch=b64 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-perm-change +EOF + +chmod o-rwx /etc/audit/rules.d/30-ospp-v42-5-perm-change-failed.rules + +augenrules --load + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure auditing of unsuccessful permission changes (AArch64) + Ensure that unsuccessful attempts to change file or directory permissions are audited. + +The following rules configure audit as described above: +## Unsuccessful permission change +-a always,exit -F arch=b32 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-perm-change +-a always,exit -F arch=b64 -S fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-perm-change +-a always,exit -F arch=b32 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-perm-change +-a always,exit -F arch=b64 -S fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-perm-change + +Load new Audit rules into kernel by running: +augenrules --load + +Note: This rule uses a special set of Audit rules to comply with OSPP 4.2.1. You may reuse this rule in different profiles. If you decide to do so, it is recommended that you inspect contents of the file closely and make sure that they are alligned with your needs. + AU-2(a) + FAU_GEN.1.1.c + SRG-OS-000462-GPOS-00206 + SRG-OS-000463-GPOS-00207 + SRG-OS-000465-GPOS-00209 + SRG-OS-000474-GPOS-00219 + SRG-OS-000475-GPOS-00220 + SRG-OS-000466-GPOS-00210 + SRG-OS-000064-GPOS-00033 + Unsuccessful attempts to change permissions of files or directories might be signs of malicious activity. Having such events audited helps in monitoring and investigation of such activities. + + CCE-85950-4 + - name: Put contents into /etc/audit/rules.d/30-ospp-v42-5-perm-change-failed.rules + according to policy + copy: + dest: /etc/audit/rules.d/30-ospp-v42-5-perm-change-failed.rules + content: | + ## Unsuccessful permission change + -a always,exit -F arch=b32 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-perm-change + -a always,exit -F arch=b64 -S fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-perm-change + -a always,exit -F arch=b32 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-perm-change + -a always,exit -F arch=b64 -S fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-perm-change + force: true + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ansible_architecture == "aarch64" + tags: + - CCE-85950-4 + - NIST-800-53-AU-2(a) + - audit_perm_change_failed_aarch64 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Remove any permissions from other group + file: + path: /etc/audit/rules.d/30-ospp-v42-5-perm-change-failed.rules + mode: o-rwx + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ansible_architecture == "aarch64" + tags: + - CCE-85950-4 + - NIST-800-53-AU-2(a) + - audit_perm_change_failed_aarch64 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && { grep -q aarch64 /proc/sys/kernel/osrelease; }; then + +cat << 'EOF' > /etc/audit/rules.d/30-ospp-v42-5-perm-change-failed.rules +## Unsuccessful permission change +-a always,exit -F arch=b32 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-perm-change +-a always,exit -F arch=b64 -S fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-perm-change +-a always,exit -F arch=b32 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-perm-change +-a always,exit -F arch=b64 -S fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-perm-change +EOF + +chmod o-rwx /etc/audit/rules.d/30-ospp-v42-5-perm-change-failed.rules + +augenrules --load + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure auditing of unsuccessful permission changes (ppc64le) + Ensure that unsuccessful attempts to change file or directory permissions are audited. + +The following rules configure audit as described above: +## Unsuccessful permission change +-a always,exit -F arch=b64 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-perm-change +-a always,exit -F arch=b64 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-perm-change + +Load new Audit rules into kernel by running: +augenrules --load + +Note: This rule uses a special set of Audit rules to comply with OSPP 4.2.1. You may reuse this rule in different profiles. If you decide to do so, it is recommended that you inspect contents of the file closely and make sure that they are alligned with your needs. + AU-2(a) + FAU_GEN.1.1.c + SRG-OS-000462-GPOS-00206 + SRG-OS-000463-GPOS-00207 + SRG-OS-000465-GPOS-00209 + SRG-OS-000474-GPOS-00219 + SRG-OS-000475-GPOS-00220 + SRG-OS-000466-GPOS-00210 + SRG-OS-000064-GPOS-00033 + Unsuccessful attempts to change permissions of files or directories might be signs of malicious activity. Having such events audited helps in monitoring and investigation of such activities. + + CCE-86000-7 + - name: Put contents into /etc/audit/rules.d/30-ospp-v42-5-perm-change-failed.rules + according to policy + copy: + dest: /etc/audit/rules.d/30-ospp-v42-5-perm-change-failed.rules + content: | + ## Unsuccessful permission change + -a always,exit -F arch=b64 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-perm-change + -a always,exit -F arch=b64 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-perm-change + force: true + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ansible_architecture == "ppc64le" + tags: + - CCE-86000-7 + - NIST-800-53-AU-2(a) + - audit_perm_change_failed_ppc64le + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Remove any permissions from other group + file: + path: /etc/audit/rules.d/30-ospp-v42-5-perm-change-failed.rules + mode: o-rwx + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ansible_architecture == "ppc64le" + tags: + - CCE-86000-7 + - NIST-800-53-AU-2(a) + - audit_perm_change_failed_ppc64le + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && { grep -q ppc64le /proc/sys/kernel/osrelease; }; then + +cat << 'EOF' > /etc/audit/rules.d/30-ospp-v42-5-perm-change-failed.rules +## Unsuccessful permission change +-a always,exit -F arch=b64 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-perm-change +-a always,exit -F arch=b64 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-perm-change +EOF + +chmod o-rwx /etc/audit/rules.d/30-ospp-v42-5-perm-change-failed.rules + +augenrules --load + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure auditing of successful permission changes + Ensure that successful attempts to modify permissions of files or directories are audited. + +The following rules configure audit as described above: +## Successful permission change +-a always,exit -F arch=b32 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change +-a always,exit -F arch=b64 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + +Load new Audit rules into kernel by running: +augenrules --load + +Note: This rule uses a special set of Audit rules to comply with OSPP 4.2.1. You may reuse this rule in different profiles. If you decide to do so, it is recommended that you inspect contents of the file closely and make sure that they are alligned with your needs. + AU-2(a) + FAU_GEN.1.1.c + SRG-OS-000462-GPOS-00206 + SRG-OS-000463-GPOS-00207 + SRG-OS-000465-GPOS-00209 + SRG-OS-000474-GPOS-00219 + SRG-OS-000475-GPOS-00220 + SRG-OS-000466-GPOS-00210 + SRG-OS-000064-GPOS-00033 + Auditing successful file or directory permission changes helps in monitoring and investigating of activities performed on the system. + + CCE-83678-3 + - name: Put contents into /etc/audit/rules.d/30-ospp-v42-5-perm-change-success.rules + according to policy + copy: + dest: /etc/audit/rules.d/30-ospp-v42-5-perm-change-success.rules + content: | + ## Successful permission change + -a always,exit -F arch=b32 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + -a always,exit -F arch=b64 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + force: true + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ( not ( ansible_architecture == "aarch64" ) and not ( ansible_architecture == + "ppc64le" ) ) + tags: + - CCE-83678-3 + - NIST-800-53-AU-2(a) + - audit_perm_change_success + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Remove any permissions from other group + file: + path: /etc/audit/rules.d/30-ospp-v42-5-perm-change-success.rules + mode: o-rwx + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ( not ( ansible_architecture == "aarch64" ) and not ( ansible_architecture == + "ppc64le" ) ) + tags: + - CCE-83678-3 + - NIST-800-53-AU-2(a) + - audit_perm_change_success + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && { ( ! ( grep -q aarch64 /proc/sys/kernel/osrelease ) && ! ( grep -q ppc64le /proc/sys/kernel/osrelease ) ); }; then + +cat << 'EOF' > /etc/audit/rules.d/30-ospp-v42-5-perm-change-success.rules +## Successful permission change +-a always,exit -F arch=b32 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change +-a always,exit -F arch=b64 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change +EOF + +chmod o-rwx /etc/audit/rules.d/30-ospp-v42-5-perm-change-success.rules + +augenrules --load + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure auditing of successful permission changes (AArch64) + Ensure that successful attempts to modify permissions of files or directories are audited. + +The following rules configure audit as described above: +## Successful permission change +-a always,exit -F arch=b32 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change +-a always,exit -F arch=b64 -S fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + +Load new Audit rules into kernel by running: +augenrules --load + +Note: This rule uses a special set of Audit rules to comply with OSPP 4.2.1. You may reuse this rule in different profiles. If you decide to do so, it is recommended that you inspect contents of the file closely and make sure that they are alligned with your needs. + AU-2(a) + FAU_GEN.1.1.c + SRG-OS-000462-GPOS-00206 + SRG-OS-000463-GPOS-00207 + SRG-OS-000465-GPOS-00209 + SRG-OS-000474-GPOS-00219 + SRG-OS-000475-GPOS-00220 + SRG-OS-000466-GPOS-00210 + SRG-OS-000064-GPOS-00033 + Auditing successful file or directory permission changes helps in monitoring and investigating of activities performed on the system. + + CCE-85952-0 + - name: Put contents into /etc/audit/rules.d/30-ospp-v42-5-perm-change-success.rules + according to policy + copy: + dest: /etc/audit/rules.d/30-ospp-v42-5-perm-change-success.rules + content: | + ## Successful permission change + -a always,exit -F arch=b32 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + -a always,exit -F arch=b64 -S fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + force: true + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ansible_architecture == "aarch64" + tags: + - CCE-85952-0 + - NIST-800-53-AU-2(a) + - audit_perm_change_success_aarch64 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Remove any permissions from other group + file: + path: /etc/audit/rules.d/30-ospp-v42-5-perm-change-success.rules + mode: o-rwx + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ansible_architecture == "aarch64" + tags: + - CCE-85952-0 + - NIST-800-53-AU-2(a) + - audit_perm_change_success_aarch64 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && { grep -q aarch64 /proc/sys/kernel/osrelease; }; then + +cat << 'EOF' > /etc/audit/rules.d/30-ospp-v42-5-perm-change-success.rules +## Successful permission change +-a always,exit -F arch=b32 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change +-a always,exit -F arch=b64 -S fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change +EOF + +chmod o-rwx /etc/audit/rules.d/30-ospp-v42-5-perm-change-success.rules + +augenrules --load + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure auditing of successful permission changes (ppc64le) + Ensure that successful attempts to modify permissions of files or directories are audited. + +The following rules configure audit as described above: +## Successful permission change +-a always,exit -F arch=b64 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + +Load new Audit rules into kernel by running: +augenrules --load + +Note: This rule uses a special set of Audit rules to comply with OSPP 4.2.1. You may reuse this rule in different profiles. If you decide to do so, it is recommended that you inspect contents of the file closely and make sure that they are alligned with your needs. + AU-2(a) + FAU_GEN.1.1.c + SRG-OS-000462-GPOS-00206 + SRG-OS-000463-GPOS-00207 + SRG-OS-000465-GPOS-00209 + SRG-OS-000474-GPOS-00219 + SRG-OS-000475-GPOS-00220 + SRG-OS-000466-GPOS-00210 + SRG-OS-000064-GPOS-00033 + Auditing successful file or directory permission changes helps in monitoring and investigating of activities performed on the system. + + CCE-86002-3 + - name: Put contents into /etc/audit/rules.d/30-ospp-v42-5-perm-change-success.rules + according to policy + copy: + dest: /etc/audit/rules.d/30-ospp-v42-5-perm-change-success.rules + content: | + ## Successful permission change + -a always,exit -F arch=b64 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + force: true + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ansible_architecture == "ppc64le" + tags: + - CCE-86002-3 + - NIST-800-53-AU-2(a) + - audit_perm_change_success_ppc64le + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Remove any permissions from other group + file: + path: /etc/audit/rules.d/30-ospp-v42-5-perm-change-success.rules + mode: o-rwx + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ansible_architecture == "ppc64le" + tags: + - CCE-86002-3 + - NIST-800-53-AU-2(a) + - audit_perm_change_success_ppc64le + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && { grep -q ppc64le /proc/sys/kernel/osrelease; }; then + +cat << 'EOF' > /etc/audit/rules.d/30-ospp-v42-5-perm-change-success.rules +## Successful permission change +-a always,exit -F arch=b64 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change +EOF + +chmod o-rwx /etc/audit/rules.d/30-ospp-v42-5-perm-change-success.rules + +augenrules --load + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure audit according to OSPP requirements + Configure audit to meet requirements for Operating System Protection Profile (OSPP) v4.2.1. + +Audit defines groups of rules in /usr/share/doc/audit/rules to satisfy specific policies. + +To fulfill requirements for compliance with OSPP v4.2.1, the following files are necessary: +/usr/share/doc/audit/rules/10-base-config.rules/usr/share/doc/audit/rules/11-loginuid.rules/usr/share/doc/audit/rules/30-ospp-v42.rules/usr/share/doc/audit/rules/43-module-load.rules + +Copy the files from /usr/share/doc/audit/rules to /etc/audit/rules.d: + +cp /usr/share/doc/audit*/rules/{10-base-config,11-loginuid,30-ospp-v42,43-module-load}.rules /etc/audit/rules.d/ + + It might happen that Audit buffer configured by this rule is not large enough for certain use cases. If that is the case, the buffer size can be overridden by placing -b larger_buffer_size into a file within /etc/audit/rules.d directory, replacing larger_file_size with the desired value. The file name should start with a number higher than 10 and lower than 99. + NONE + FAU_GEN.1.1.c + SRG-OS-000004-GPOS-00004 + SRG-OS-000240-GPOS-00090 + SRG-OS-000241-GPOS-00091 + SRG-OS-000303-GPOS-00120 + SRG-OS-000476-GPOS-00221 + SRG-OS-000327-GPOS-00127 + SRG-OS-000064-GPOS-00033 + SRG-OS-000365-GPOS-00152 + SRG-OS-000458-GPOS-00203 + SRG-OS-000461-GPOS-00205 + SRG-OS-000462-GPOS-00206 + SRG-OS-000463-GPOS-00207 + SRG-OS-000465-GPOS-00209 + SRG-OS-000466-GPOS-00210 + SRG-OS-000468-GPOS-00212 + SRG-OS-000470-GPOS-00214 + SRG-OS-000471-GPOS-00215 + SRG-OS-000471-GPOS-00216 + SRG-OS-000472-GPOS-00217 + SRG-OS-000474-GPOS-00219 + SRG-OS-000475-GPOS-00220 + SRG-OS-000477-GPOS-00222 + The audit rules defined in /usr/share/doc/audit/rules are the recommended way to meet compliance with OSPP v4.2.1. + CCE-85991-8 + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +cp /usr/share/doc/audit*/rules/10-base-config.rules /etc/audit/rules.d +cp /usr/share/doc/audit*/rules/11-loginuid.rules /etc/audit/rules.d +cp /usr/share/doc/audit*/rules/30-ospp-v42.rules /etc/audit/rules.d +cp /usr/share/doc/audit*/rules/43-module-load.rules /etc/audit/rules.d + +augenrules --load + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + + AppArmor + Many security vulnerabilities result from bugs in trusted programs. A trusted +program runs with privileges that attackers want to possess. The program fails +to keep that trust if there is a bug in the program that allows the attacker to +acquire said privilege. + +AppArmor® is an application security solution designed specifically to apply +privilege confinement to suspect programs. AppArmor allows the administrator to +specify the domain of activities the program can perform by developing a +security profile. A security profile is a listing of files that the program may +access and the operations the program may perform. AppArmor secures +applications by enforcing good application behavior without relying on attack +signatures, so it can prevent attacks even if previously unknown +vulnerabilities are being exploited. + + + GRUB2 bootloader configuration + During the boot process, the boot loader is +responsible for starting the execution of the kernel and passing +options to it. The boot loader allows for the selection of +different kernels - possibly on different partitions or media. +The default Red Hat Enterprise Linux 9 boot loader for x86 systems is called GRUB2. +Options it can pass to the kernel include single-user mode, which +provides root access without any authentication, and the ability to +disable SELinux. To prevent local users from modifying the boot +parameters and endangering security, protect the boot loader configuration +with a password and ensure its configuration file's permissions +are set properly. + + + L1TF vulnerability mitigation + Defines the L1TF vulneratility mitigations to employ. + flush + full + full,force + flush + flush,nosmt + flush,nowarn + + + MDS vulnerability mitigation + Defines the MDS vulneratility mitigation to employ. + full + full + full,nosmt + + + Confidence level on Hardware Random Number Generator + Defines the level of trust on the hardware random number generators available in the +system and the percentage of entropy to credit. + 500 + 500 + 512 + 1000 + + + Spec Store Bypass Mitigation + This controls how the Speculative Store Bypass (SSB) vulnerability is mitigated. + prctl + on + auto + prctl + seccomp + + + Disable Recovery Booting + Red Hat Enterprise Linux 9 systems support an "recovery boot" option that can be used +to prevent services from being started. The GRUB_DISABLE_RECOVERY +configuration option in /etc/default/grub should be set to +true to disable the generation of recovery mode menu entries. It is +also required to change the runtime configuration, run: +$ sudo grubby --update-kernel=ALL + FIA_UAU.1 + Using recovery boot, the console user could disable auditing, firewalls, +or other services, weakening system security. + + CCE-85986-8 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-85986-8 + - grub2_disable_recovery + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Verify GRUB_DISABLE_RECOVERY=true + lineinfile: + path: /etc/default/grub + regexp: ^GRUB_DISABLE_RECOVERY=.* + line: GRUB_DISABLE_RECOVERY=true + state: present + when: '"grub2-common" in ansible_facts.packages' + tags: + - CCE-85986-8 + - grub2_disable_recovery + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + +- name: Update grub defaults and the bootloader menu + command: /sbin/grubby --update-kernel=ALL + when: '"grub2-common" in ansible_facts.packages' + tags: + - CCE-85986-8 + - grub2_disable_recovery + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q grub2-common; then + +if grep -q '^GRUB_DISABLE_RECOVERY=.*' '/etc/default/grub' ; then + sed -i 's/GRUB_DISABLE_RECOVERY=.*/GRUB_DISABLE_RECOVERY=true/' "/etc/default/grub" +else + echo "GRUB_DISABLE_RECOVERY=true" >> '/etc/default/grub' +fi + +grubby --update-kernel=ALL + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + IOMMU configuration directive + On x86 architecture supporting VT-d, the IOMMU manages the access control policy between the hardware devices and some + of the system critical units such as the memory. +To ensure that iommu=force is added as a kernel command line +argument to newly installed kernels, add iommu=force to the +default Grub2 command line for Linux operating systems. Modify the line within +/etc/default/grub as shown below: +GRUB_CMDLINE_LINUX="... iommu=force ..." +Run the following command to update command line for already installed kernels:# grubby --update-kernel=ALL --args="iommu=force" + Depending on the hardware, devices and operating system used, enabling IOMMU can cause hardware instabilities. Proper function and stability should be assessed before applying remediation to production systems. + BP28(R11) + On x86 architectures, activating the I/OMMU prevents the system from arbitrary accesses potentially made by + hardware devices. + + CCE-83844-1 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83844-1 + - grub2_enable_iommu_force + - low_disruption + - medium_complexity + - reboot_required + - restrict_strategy + - unknown_severity + +- name: Update grub defaults and the bootloader menu + command: /sbin/grubby --update-kernel=ALL --args="iommu=force" + when: + - '"grub2-common" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83844-1 + - grub2_enable_iommu_force + - low_disruption + - medium_complexity + - reboot_required + - restrict_strategy + - unknown_severity + + [customizations.kernel] +append = "iommu=force" + + # Remediation is applicable only in certain platforms +if rpm --quiet -q grub2-common && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +grubby --update-kernel=ALL --args=iommu=force + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure kernel to zero out memory before allocation + To configure the kernel to zero out memory before allocating it, add the +init_on_alloc=1 argument to the default GRUB 2 command line. +To ensure that init_on_alloc=1 is added as a kernel command line +argument to newly installed kernels, add init_on_alloc=1 to the +default Grub2 command line for Linux operating systems. Modify the line within +/etc/default/grub as shown below: +GRUB_CMDLINE_LINUX="... init_on_alloc=1 ..." +Run the following command to update command line for already installed kernels:# grubby --update-kernel=ALL --args="init_on_alloc=1" + When the kernel configuration option init_on_alloc is enabled, +all page allocator and slab allocator memory will be zeroed when allocated, +eliminating many kinds of "uninitialized heap memory" flaws, effectively +preventing data leaks. + + CCE-85867-0 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-85867-0 + - grub2_init_on_alloc_argument + - low_disruption + - medium_complexity + - medium_severity + - reboot_required + - restrict_strategy + +- name: Update grub defaults and the bootloader menu + command: /sbin/grubby --update-kernel=ALL --args="init_on_alloc=1" + when: + - '"grub2-common" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-85867-0 + - grub2_init_on_alloc_argument + - low_disruption + - medium_complexity + - medium_severity + - reboot_required + - restrict_strategy + + [customizations.kernel] +append = "init_on_alloc=1" + + # Remediation is applicable only in certain platforms +if rpm --quiet -q grub2-common && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +grubby --update-kernel=ALL --args=init_on_alloc=1 + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure kernel to trust the CPU random number generator + There exist two ways how to ensure that the Linux kernel trusts the CPU +hardware random number generator. If the option is configured during kernel +compilation, e.g. the option CONFIG_RANDOM_TRUST_CPU is set to +Y, make sure that it is not overridden with the boot parameter. +There must not exist the boot parameter random.trust_cpu=off. If +the option is not compiled in, make sure that random.trust_cpu=on +is configured as a boot parameter. +To ensure that random.trust_cpu=on is added as a kernel command line +argument to newly installed kernels, add random.trust_cpu=on to the +default Grub2 command line for Linux operating systems. Modify the line within +/etc/default/grub as shown below: +GRUB_CMDLINE_LINUX="... random.trust_cpu=on ..." +Run the following command to update command line for already installed kernels:# grubby --update-kernel=ALL --args="random.trust_cpu=on" + FCS_RBG_EXT.1.1 + SRG-OS-000480-GPOS-00227 + The Linux kernel offers an option which signifies if the kernel should trust +data provided by CPU hardware random number generator. Hardware random +number generators can provide random data very quickly and are used to generate random cryptographic keys. They can +be useful during boot time when other means of getting random data can be +slow because there is not yet enough entropy in the system. + + CCE-83841-7 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83841-7 + - grub2_kernel_trust_cpu_rng + - low_disruption + - medium_complexity + - medium_severity + - reboot_required + - restrict_strategy + +- name: Update grub defaults and the bootloader menu + command: /sbin/grubby --update-kernel=ALL --args="random.trust_cpu=on" + when: + - '"grub2-common" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83841-7 + - grub2_kernel_trust_cpu_rng + - low_disruption + - medium_complexity + - medium_severity + - reboot_required + - restrict_strategy + + [customizations.kernel] +append = "random.trust_cpu=on" + + # Remediation is applicable only in certain platforms +if rpm --quiet -q grub2-common && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +grubby --update-kernel=ALL --args=random.trust_cpu=on + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure L1 Terminal Fault mitigations + L1 Terminal Fault (L1TF) is a hardware vulnerability which allows unprivileged +speculative access to data which is available in the Level 1 Data Cache when +the page table entry isn't present. + +Select the appropriate mitigation by adding the argument +l1tf= to the default +GRUB 2 command line for the Linux operating system. +To ensure that l1tf= is added as a kernel command line +argument to newly installed kernels, add l1tf= to the +default Grub2 command line for Linux operating systems. Modify the line within +/etc/default/grub as shown below: +GRUB_CMDLINE_LINUX="... l1tf= ..." +Run the following command to update command line for already installed kernels:# grubby --update-kernel=ALL --args="l1tf=" + +Since Linux Kernel 4.19 you can check the L1TF vulnerability state with the +following command: +cat /sys/devices/system/cpu/vulnerabilities/l1tf + Enabling L1TF mitigations may impact performance of the system. + The L1TF vulnerability allows an attacker to bypass memory access security controls imposed +by the system or hypervisor. The L1TF vulnerability allows read access to any physical memory +location that is cached in the L1 Data Cache. + + CCE-89123-4 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-89123-4 + - grub2_l1tf_argument + - high_severity + - low_disruption + - medium_complexity + - reboot_required + - restrict_strategy +- name: XCCDF Value var_l1tf_options # promote to variable + set_fact: + var_l1tf_options: !!str + tags: + - always + +- name: Update grub defaults and the bootloader menu + command: /sbin/grubby --update-kernel=ALL --args="l1tf={{ var_l1tf_options }}" + when: + - '"grub2-common" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-89123-4 + - grub2_l1tf_argument + - high_severity + - low_disruption + - medium_complexity + - reboot_required + - restrict_strategy + + [customizations.kernel] +append = "l1tf=" + + # Remediation is applicable only in certain platforms +if rpm --quiet -q grub2-common && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +var_l1tf_options='' + + + +grubby --update-kernel=ALL --args=l1tf=$var_l1tf_options + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Force kernel panic on uncorrected MCEs + A Machine Check Exception is an error generated by the CPU itdetects an error +in itself, memory or I/O devices. +These errors may be corrected and generate a check log entry, if an error +cannot be corrected the kernel may panic or SIGBUS. + +To force the kernel to panic on any uncorrected error reported by Machine Check +set the MCE tolerance to zero by adding mce=0 +to the default GRUB 2 command line for the Linux operating system. +To ensure that mce=0 is added as a kernel command line +argument to newly installed kernels, add mce=0 to the +default Grub2 command line for Linux operating systems. Modify the line within +/etc/default/grub as shown below: +GRUB_CMDLINE_LINUX="... mce=0 ..." +Run the following command to update command line for already installed kernels:# grubby --update-kernel=ALL --args="mce=0" + Allowing uncorrected errors to result on a SIGBUS may allow an attacker to continue +trying to exploit a vulnerability such as Rowhammer. + + CCE-88098-9 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-88098-9 + - grub2_mce_argument + - low_disruption + - medium_complexity + - medium_severity + - reboot_required + - restrict_strategy + +- name: Update grub defaults and the bootloader menu + command: /sbin/grubby --update-kernel=ALL --args="mce=0" + when: + - '"grub2-common" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-88098-9 + - grub2_mce_argument + - low_disruption + - medium_complexity + - medium_severity + - reboot_required + - restrict_strategy + + [customizations.kernel] +append = "mce=0" + + # Remediation is applicable only in certain platforms +if rpm --quiet -q grub2-common && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +grubby --update-kernel=ALL --args=mce=0 + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure Microarchitectural Data Sampling mitigation + Microarchitectural Data Sampling (MDS) is a hardware vulnerability which allows unprivileged +speculative access to data which is available in various CPU internal buffers. + +When performing store, load, L1 refill operations, processors write data into temporary +microarchitectural structures (buffers), and the data in the buffer can be forwarded to load +operations as an optimization. + +Under certain conditions, data unrelated to the load operations can be speculatively +forwarded from the buffers to a disclosure gadget which allows in turn to infer the value +via a cache side channel attack. + +Select the appropriate mitigation by adding the argument +mds= to the default +GRUB 2 command line for the Linux operating system. +To ensure that mds= is added as a kernel command line +argument to newly installed kernels, add mds= to the +default Grub2 command line for Linux operating systems. Modify the line within +/etc/default/grub as shown below: +GRUB_CMDLINE_LINUX="... mds= ..." +Run the following command to update command line for already installed kernels:# grubby --update-kernel=ALL --args="mds=" + +Not all processors are affected by all variants of MDS, but the mitigation mechanism is +identical for all of them. + +Since Linux Kernel 5.2 you can check whether the system is vulnerable or mitigated with the +following command: +cat /sys/devices/system/cpu/vulnerabilities/mds + Enabling MDS mitigations will impact performance of the system, mainly by workloads with +high rates of user-kernel-user space transitions. For example, system calls, NMIs and interrupts. + The MDS vulnerability allows an attacker to sample data from internal CPU buffers. + + CCE-90456-5 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-90456-5 + - grub2_mds_argument + - low_disruption + - medium_complexity + - medium_severity + - reboot_required + - restrict_strategy +- name: XCCDF Value var_mds_options # promote to variable + set_fact: + var_mds_options: !!str + tags: + - always + +- name: Update grub defaults and the bootloader menu + command: /sbin/grubby --update-kernel=ALL --args="mds={{ var_mds_options }}" + when: + - '"grub2-common" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-90456-5 + - grub2_mds_argument + - low_disruption + - medium_complexity + - medium_severity + - reboot_required + - restrict_strategy + + [customizations.kernel] +append = "mds=" + + # Remediation is applicable only in certain platforms +if rpm --quiet -q grub2-common && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +var_mds_options='' + + + +grubby --update-kernel=ALL --args=mds=$var_mds_options + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Ensure SMAP is not disabled during boot + The SMAP is used to prevent the supervisor mode from unintentionally reading/writing into +memory pages in the user space, it is enabled by default since Linux kernel 3.7. +But it could be disabled through kernel boot parameters. + +Ensure that Supervisor Mode Access Prevention (SMAP) is not disabled by +the nosmap boot paramenter option. + +Check that the line GRUB_CMDLINE_LINUX="..." within /etc/default/grub +doesn't contain the argument nosmap. +Run the following command to update command line for already installed kernels: +# grubby --update-kernel=ALL --remove-args="nosmap" + Disabling SMAP can facilitate exploitation of vulnerabilities caused by unintended access and +manipulation of data in the user space. + + CCE-88345-4 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-88345-4 + - grub2_nosmap_argument_absent + - low_disruption + - medium_complexity + - medium_severity + - reboot_required + - restrict_strategy + +- name: Update grub defaults and the bootloader menu + command: /sbin/grubby --update-kernel=ALL --remove-args="nosmap" + when: + - '"grub2-common" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-88345-4 + - grub2_nosmap_argument_absent + - low_disruption + - medium_complexity + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q grub2-common && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +grubby --update-kernel=ALL --remove-args=nosmap + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure SMEP is not disabled during boot + The SMEP is used to prevent the supervisor mode from executing user space code, +it is enabled by default since Linux kernel 3.0. But it could be disabled through +kernel boot parameters. + +Ensure that Supervisor Mode Execution Prevention (SMEP) is not disabled by +the nosmep boot paramenter option. + +Check that the line GRUB_CMDLINE_LINUX="..." within /etc/default/grub +doesn't contain the argument nosmep. +Run the following command to update command line for already installed kernels: +# grubby --update-kernel=ALL --remove-args="nosmep" + Disabling SMEP can facilitate exploitation of certain vulnerabilities because it allows +the kernel to unintentionally execute code in less privileged memory space. + + CCE-86089-0 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-86089-0 + - grub2_nosmep_argument_absent + - low_disruption + - medium_complexity + - medium_severity + - reboot_required + - restrict_strategy + +- name: Update grub defaults and the bootloader menu + command: /sbin/grubby --update-kernel=ALL --remove-args="nosmep" + when: + - '"grub2-common" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86089-0 + - grub2_nosmep_argument_absent + - low_disruption + - medium_complexity + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q grub2-common && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +grubby --update-kernel=ALL --remove-args=nosmep + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Enable randomization of the page allocator + To enable randomization of the page allocator in the kernel, add the +page_alloc.shuffle=1 argument to the default GRUB 2 command line. +To ensure that page_alloc.shuffle=1 is added as a kernel command line +argument to newly installed kernels, add page_alloc.shuffle=1 to the +default Grub2 command line for Linux operating systems. Modify the line within +/etc/default/grub as shown below: +GRUB_CMDLINE_LINUX="... page_alloc.shuffle=1 ..." +Run the following command to update command line for already installed kernels:# grubby --update-kernel=ALL --args="page_alloc.shuffle=1" + The CONFIG_SHUFFLE_PAGE_ALLOCATOR config option is primarily +focused on improving the average utilization of a direct-mapped +memory-side-cache. Aside of this performance effect, it also reduces +predictability of page allocations in situations when the bad actor can +crash the system and somehow leverage knowledge of (page) allocation order +right after a fresh reboot, or can control the timing between a +hot-pluggable memory node (as in NUMA node) and applications allocating +memory ouf of that node. The page_alloc.shuffle=1 kernel command +line parameter then forces this functionality irrespectively of memory cache +architecture. + + CCE-85879-5 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-85879-5 + - grub2_page_alloc_shuffle_argument + - low_disruption + - medium_complexity + - medium_severity + - reboot_required + - restrict_strategy + +- name: Update grub defaults and the bootloader menu + command: /sbin/grubby --update-kernel=ALL --args="page_alloc.shuffle=1" + when: + - '"grub2-common" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-85879-5 + - grub2_page_alloc_shuffle_argument + - low_disruption + - medium_complexity + - medium_severity + - reboot_required + - restrict_strategy + + [customizations.kernel] +append = "page_alloc.shuffle=1" + + # Remediation is applicable only in certain platforms +if rpm --quiet -q grub2-common && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +grubby --update-kernel=ALL --args=page_alloc.shuffle=1 + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Enable Kernel Page-Table Isolation (KPTI) + To enable Kernel page-table isolation, +add the argument pti=on to the default +GRUB 2 command line for the Linux operating system. +To ensure that pti=on is added as a kernel command line +argument to newly installed kernels, add pti=on to the +default Grub2 command line for Linux operating systems. Modify the line within +/etc/default/grub as shown below: +GRUB_CMDLINE_LINUX="... pti=on ..." +Run the following command to update command line for already installed kernels:# grubby --update-kernel=ALL --args="pti=on" + CCI-000381 + SI-16 + SRG-OS-000433-GPOS-00193 + SRG-OS-000095-GPOS-00049 + Kernel page-table isolation is a kernel feature that mitigates +the Meltdown security vulnerability and hardens the kernel +against attempts to bypass kernel address space layout +randomization (KASLR). + + CCE-83843-3 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83843-3 + - NIST-800-53-SI-16 + - grub2_pti_argument + - low_disruption + - low_severity + - medium_complexity + - reboot_required + - restrict_strategy + +- name: Update grub defaults and the bootloader menu + command: /sbin/grubby --update-kernel=ALL --args="pti=on" + when: + - '"grub2-common" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83843-3 + - NIST-800-53-SI-16 + - grub2_pti_argument + - low_disruption + - low_severity + - medium_complexity + - reboot_required + - restrict_strategy + + [customizations.kernel] +append = "pti=on" + + # Remediation is applicable only in certain platforms +if rpm --quiet -q grub2-common && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +grubby --update-kernel=ALL --args=pti=on + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure the confidence in TPM for entropy + The TPM security chip that is available in most modern systems has a hardware RNG. +It is also used to feed the entropy pool, but generally not credited entropy. + +Use rng_core.default_quality in the kernel command line to set the trust +level on the hardware generators. The trust level defines the amount of entropy to credit. +A value of 0 tells the system not to trust the hardware random number generators +available, and doesn't credit any entropy to the pool. +A value of 1000 assigns full confidence in the generators, and credits all the +entropy it provides to the pool. + +Note that the value of rng_core.default_quality is global, affecting the trust +on all hardware random number generators. + +Select the appropriate confidence by adding the argument +rng_core.default_quality= to the default +GRUB 2 command line for the Linux operating system. +To ensure that rng_core.default_quality= is added as a kernel command line +argument to newly installed kernels, add rng_core.default_quality= to the +default Grub2 command line for Linux operating systems. Modify the line within +/etc/default/grub as shown below: +GRUB_CMDLINE_LINUX="... rng_core.default_quality= ..." +Run the following command to update command line for already installed kernels:# grubby --update-kernel=ALL --args="rng_core.default_quality=" + A system may struggle to initialize its entropy pool and end up starving. Crediting entropy +from the hardware number generators available in the system helps fill up the entropy pool. + + CCE-90567-9 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-90567-9 + - grub2_rng_core_default_quality_argument + - low_disruption + - low_severity + - medium_complexity + - reboot_required + - restrict_strategy +- name: XCCDF Value var_rng_core_default_quality # promote to variable + set_fact: + var_rng_core_default_quality: !!str + tags: + - always + +- name: Update grub defaults and the bootloader menu + command: /sbin/grubby --update-kernel=ALL --args="rng_core.default_quality={{ var_rng_core_default_quality + }}" + when: + - '"grub2-common" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-90567-9 + - grub2_rng_core_default_quality_argument + - low_disruption + - low_severity + - medium_complexity + - reboot_required + - restrict_strategy + + [customizations.kernel] +append = "rng_core.default_quality=" + + # Remediation is applicable only in certain platforms +if rpm --quiet -q grub2-common && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +var_rng_core_default_quality='' + + + +grubby --update-kernel=ALL --args=rng_core.default_quality=$var_rng_core_default_quality + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable merging of slabs with similar size + The kernel may merge similar slabs together to reduce overhead and increase +cache hotness of objects. +Disabling merging of slabs keeps the slabs separate and reduces the risk of +kernel heap overflows overwriting objects in merged caches. + +To disable merging of slabs in the Kernel add the argument slab_nomerge=yes +to the default GRUB 2 command line for the Linux operating system. +To ensure that slab_nomerge=yes is added as a kernel command line +argument to newly installed kernels, add slab_nomerge=yes to the +default Grub2 command line for Linux operating systems. Modify the line within +/etc/default/grub as shown below: +GRUB_CMDLINE_LINUX="... slab_nomerge=yes ..." +Run the following command to update command line for already installed kernels:# grubby --update-kernel=ALL --args="slab_nomerge=yes" + Disabling merge of slabs will slightly increase kernel memory utilization. + Disabling the merge of slabs of similar sizes prevents the kernel from +merging a seemingly useless but vulnerable slab with a useful and valuable slab. +This increase the risk that a heap overflow could overwrite objects from merged caches, +with unmerged caches the heap overflow would only affect the objects in the same cache. +Overall, this reduces the kernel attack surface area by isolating slabs from each other. + + CCE-87770-4 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-87770-4 + - grub2_slab_nomerge_argument + - low_disruption + - medium_complexity + - medium_severity + - reboot_required + - restrict_strategy + +- name: Update grub defaults and the bootloader menu + command: /sbin/grubby --update-kernel=ALL --args="slab_nomerge=yes" + when: + - '"grub2-common" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-87770-4 + - grub2_slab_nomerge_argument + - low_disruption + - medium_complexity + - medium_severity + - reboot_required + - restrict_strategy + + [customizations.kernel] +append = "slab_nomerge=yes" + + # Remediation is applicable only in certain platforms +if rpm --quiet -q grub2-common && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +grubby --update-kernel=ALL --args=slab_nomerge=yes + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure Speculative Store Bypass Mitigation + Certain CPUs are vulnerable to an exploit against a common wide industry wide performance +optimization known as Speculative Store Bypass (SSB). + +In such cases, recent stores to the same memory location cannot always be observed by later +loads during speculative execution. However, such stores are unlikely and thus they can be +detected prior to instruction retirement at the end of a particular speculation execution +window. + +Since Linux Kernel 4.17 you can check the SSB mitigation state with the following command: +cat /sys/devices/system/cpu/vulnerabilities/spec_store_bypass + +Select the appropriate SSB state by adding the argument +spec_store_bypass_disable= to the default +GRUB 2 command line for the Linux operating system. +To ensure that spec_store_bypass_disable= is added as a kernel command line +argument to newly installed kernels, add spec_store_bypass_disable= to the +default Grub2 command line for Linux operating systems. Modify the line within +/etc/default/grub as shown below: +GRUB_CMDLINE_LINUX="... spec_store_bypass_disable= ..." +Run the following command to update command line for already installed kernels:# grubby --update-kernel=ALL --args="spec_store_bypass_disable=" + Disabling Speculative Store Bypass may impact performance of the system. + In vulnerable processsors, the speculatively forwarded store can be used in a cache side channel +attack. An example of this is reading memory to which the attacker does not directly have access, +for example inside the sandboxed code. + + CCE-90234-6 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-90234-6 + - grub2_spec_store_bypass_disable_argument + - low_disruption + - medium_complexity + - medium_severity + - reboot_required + - restrict_strategy +- name: XCCDF Value var_spec_store_bypass_disable_options # promote to variable + set_fact: + var_spec_store_bypass_disable_options: !!str + tags: + - always + +- name: Update grub defaults and the bootloader menu + command: /sbin/grubby --update-kernel=ALL --args="spec_store_bypass_disable={{ var_spec_store_bypass_disable_options + }}" + when: + - '"grub2-common" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-90234-6 + - grub2_spec_store_bypass_disable_argument + - low_disruption + - medium_complexity + - medium_severity + - reboot_required + - restrict_strategy + + [customizations.kernel] +append = "spec_store_bypass_disable=" + + # Remediation is applicable only in certain platforms +if rpm --quiet -q grub2-common && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +var_spec_store_bypass_disable_options='' + + + +grubby --update-kernel=ALL --args=spec_store_bypass_disable=$var_spec_store_bypass_disable_options + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enforce Spectre v2 mitigation + Spectre V2 is an indirect branch poisoning attack that can lead to data leakage. +An exploit for Spectre V2 tricks the indirect branch predictor into executing +code from a future indirect branch chosen by the attacker, even if the privilege +level is different. + +Since Linux Kernel 4.15 you can check the Spectre V2 mitigation state with the following command: +cat /sys/devices/system/cpu/vulnerabilities/spectre_v2 + +Enforce the Spectre V2 mitigation by adding the argument +spectre_v2=on to the default +GRUB 2 command line for the Linux operating system. +To ensure that spectre_v2=on) is added as a kernel command line +argument to newly installed kernels, add spectre_v2=on) to the +default Grub2 command line for Linux operating systems. Modify the line within +/etc/default/grub as shown below: +GRUB_CMDLINE_LINUX="... spectre_v2=on) ..." +Run the following command to update command line for already installed kernels:# grubby --update-kernel=ALL --args="spectre_v2=on)" + The Spectre V2 vulnerability allows an attacker to read memory that he should not have +access to. + + CCE-90345-0 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-90345-0 + - grub2_spectre_v2_argument + - high_severity + - low_disruption + - medium_complexity + - reboot_required + - restrict_strategy + +- name: Update grub defaults and the bootloader menu + command: /sbin/grubby --update-kernel=ALL --args="spectre_v2=on" + when: + - '"grub2-common" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-90345-0 + - grub2_spectre_v2_argument + - high_severity + - low_disruption + - medium_complexity + - reboot_required + - restrict_strategy + + [customizations.kernel] +append = "spectre_v2=on" + + # Remediation is applicable only in certain platforms +if rpm --quiet -q grub2-common && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +grubby --update-kernel=ALL --args=spectre_v2=on + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure debug-shell service is not enabled during boot + systemd's debug-shell service is intended to +diagnose systemd related boot issues with various systemctl +commands. Once enabled and following a system reboot, the root shell +will be available on tty9 which is access by pressing +CTRL-ALT-F9. The debug-shell service should only be used +for systemd related issues and should otherwise be disabled. + +By default, the debug-shell systemd service is already disabled. + +Ensure the debug-shell is not enabled by the systemd.debug-shel=1 +boot paramenter option. + +Check that the line GRUB_CMDLINE_LINUX="..." within /etc/default/grub +doesn't contain the argument systemd.debug-shell=1. +Run the following command to update command line for already installed kernels: +# grubby --update-kernel=ALL --remove-args="systemd.debug-shell" + FIA_UAU.1 + This prevents attackers with physical access from trivially bypassing security +on the machine through valid troubleshooting configurations and gaining root +access when the system is rebooted. + + CCE-86292-0 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-86292-0 + - grub2_systemd_debug-shell_argument_absent + - low_disruption + - medium_complexity + - medium_severity + - reboot_required + - restrict_strategy + +- name: Update grub defaults and the bootloader menu + command: /sbin/grubby --update-kernel=ALL --remove-args="systemd.debug-shell" + when: + - '"grub2-common" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86292-0 + - grub2_systemd_debug-shell_argument_absent + - low_disruption + - medium_complexity + - medium_severity + - reboot_required + - restrict_strategy + + # Remediation is applicable only in certain platforms +if rpm --quiet -q grub2-common && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +grubby --update-kernel=ALL --remove-args=systemd.debug-shell + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable vsyscalls + To disable use of virtual syscalls, +add the argument vsyscall=none to the default +GRUB 2 command line for the Linux operating system. +To ensure that vsyscall=none is added as a kernel command line +argument to newly installed kernels, add vsyscall=none to the +default Grub2 command line for Linux operating systems. Modify the line within +/etc/default/grub as shown below: +GRUB_CMDLINE_LINUX="... vsyscall=none ..." +Run the following command to update command line for already installed kernels:# grubby --update-kernel=ALL --args="vsyscall=none" + CCI-001084 + CM-7(a) + FPT_ASLR_EXT.1 + SRG-OS-000480-GPOS-00227 + SRG-OS-000134-GPOS-00068 + Virtual Syscalls provide an opportunity of attack for a user who has control +of the return instruction pointer. + + CCE-83842-5 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83842-5 + - NIST-800-53-CM-7(a) + - grub2_vsyscall_argument + - low_disruption + - medium_complexity + - medium_severity + - reboot_required + - restrict_strategy + +- name: Update grub defaults and the bootloader menu + command: /sbin/grubby --update-kernel=ALL --args="vsyscall=none" + when: + - '"grub2-common" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83842-5 + - NIST-800-53-CM-7(a) + - grub2_vsyscall_argument + - low_disruption + - medium_complexity + - medium_severity + - reboot_required + - restrict_strategy + + [customizations.kernel] +append = "vsyscall=none" + + # Remediation is applicable only in certain platforms +if rpm --quiet -q grub2-common && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +grubby --update-kernel=ALL --args=vsyscall=none + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Non-UEFI GRUB2 bootloader configuration + Non-UEFI GRUB2 bootloader configuration + + + Verify /boot/grub2/grub.cfg Group Ownership + The file /boot/grub2/grub.cfg should +be group-owned by the root group to prevent +destruction or modification of the file. + +To properly set the group owner of /boot/grub2/grub.cfg, run the command: +$ sudo chgrp root /boot/grub2/grub.cfg + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + 5.5.2.2 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + 3.4.5 + CCI-000225 + 164.308(a)(1)(ii)(B) + 164.308(a)(7)(i) + 164.308(a)(7)(ii)(A) + 164.310(a)(1) + 164.310(a)(2)(i) + 164.310(a)(2)(ii) + 164.310(a)(2)(iii) + 164.310(b) + 164.310(c) + 164.310(d)(1) + 164.310(d)(2)(iii) + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + Req-7.1 + SRG-OS-000480-GPOS-00227 + The root group is a highly-privileged group. Furthermore, the group-owner of this +file should not have any access privileges anyway. + + CCE-83848-2 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83848-2 + - CJIS-5.5.2.2 + - NIST-800-171-3.4.5 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-7.1 + - configure_strategy + - file_groupowner_grub2_cfg + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Test for existence /boot/grub2/grub.cfg + stat: + path: /boot/grub2/grub.cfg + register: file_exists + when: + - '"/boot/efi" not in ansible_mounts | map(attribute="mount") | list' + - '"grub2-common" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83848-2 + - CJIS-5.5.2.2 + - NIST-800-171-3.4.5 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-7.1 + - configure_strategy + - file_groupowner_grub2_cfg + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure group owner 0 on /boot/grub2/grub.cfg + file: + path: /boot/grub2/grub.cfg + group: '0' + when: + - '"/boot/efi" not in ansible_mounts | map(attribute="mount") | list' + - '"grub2-common" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-83848-2 + - CJIS-5.5.2.2 + - NIST-800-171-3.4.5 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-7.1 + - configure_strategy + - file_groupowner_grub2_cfg + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /sys/firmware/efi ] && rpm --quiet -q grub2-common && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +chgrp 0 /boot/grub2/grub.cfg + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Verify /boot/grub2/grub.cfg User Ownership + The file /boot/grub2/grub.cfg should +be owned by the root user to prevent destruction +or modification of the file. + +To properly set the owner of /boot/grub2/grub.cfg, run the command: +$ sudo chown root /boot/grub2/grub.cfg + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + 5.5.2.2 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + 3.4.5 + CCI-000225 + 164.308(a)(1)(ii)(B) + 164.308(a)(7)(i) + 164.308(a)(7)(ii)(A) + 164.310(a)(1) + 164.310(a)(2)(i) + 164.310(a)(2)(ii) + 164.310(a)(2)(iii) + 164.310(b) + 164.310(c) + 164.310(d)(1) + 164.310(d)(2)(iii) + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + Req-7.1 + Only root should be able to modify important boot parameters. + + CCE-83845-8 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83845-8 + - CJIS-5.5.2.2 + - NIST-800-171-3.4.5 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-7.1 + - configure_strategy + - file_owner_grub2_cfg + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Test for existence /boot/grub2/grub.cfg + stat: + path: /boot/grub2/grub.cfg + register: file_exists + when: + - '"/boot/efi" not in ansible_mounts | map(attribute="mount") | list' + - '"grub2-common" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83845-8 + - CJIS-5.5.2.2 + - NIST-800-171-3.4.5 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-7.1 + - configure_strategy + - file_owner_grub2_cfg + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure owner 0 on /boot/grub2/grub.cfg + file: + path: /boot/grub2/grub.cfg + owner: '0' + when: + - '"/boot/efi" not in ansible_mounts | map(attribute="mount") | list' + - '"grub2-common" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-83845-8 + - CJIS-5.5.2.2 + - NIST-800-171-3.4.5 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-7.1 + - configure_strategy + - file_owner_grub2_cfg + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /sys/firmware/efi ] && rpm --quiet -q grub2-common && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +chown 0 /boot/grub2/grub.cfg + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Verify /boot/grub2/grub.cfg Permissions + File permissions for /boot/grub2/grub.cfg should be set to 600. + +To properly set the permissions of /boot/grub2/grub.cfg, run the command: +$ sudo chmod 600 /boot/grub2/grub.cfg + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + 3.4.5 + CCI-000225 + 164.308(a)(1)(ii)(B) + 164.308(a)(7)(i) + 164.308(a)(7)(ii)(A) + 164.310(a)(1) + 164.310(a)(2)(i) + 164.310(a)(2)(ii) + 164.310(a)(2)(iii) + 164.310(b) + 164.310(c) + 164.310(d)(1) + 164.310(d)(2)(iii) + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + Proper permissions ensure that only the root user can modify important boot +parameters. + + CCE-83846-6 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83846-6 + - NIST-800-171-3.4.5 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_permissions_grub2_cfg + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Test for existence /boot/grub2/grub.cfg + stat: + path: /boot/grub2/grub.cfg + register: file_exists + when: + - '"/boot/efi" not in ansible_mounts | map(attribute="mount") | list' + - '"grub2-common" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83846-6 + - NIST-800-171-3.4.5 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_permissions_grub2_cfg + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure permission u-xs,g-xwrs,o-xwrt on /boot/grub2/grub.cfg + file: + path: /boot/grub2/grub.cfg + mode: u-xs,g-xwrs,o-xwrt + when: + - '"/boot/efi" not in ansible_mounts | map(attribute="mount") | list' + - '"grub2-common" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-83846-6 + - NIST-800-171-3.4.5 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_permissions_grub2_cfg + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /sys/firmware/efi ] && rpm --quiet -q grub2-common && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +chmod u-xs,g-xwrs,o-xwrt /boot/grub2/grub.cfg + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Set the Boot Loader Admin Username to a Non-Default Value + The grub2 boot loader should have a superuser account and password +protection enabled to protect boot-time settings. + +To maximize the protection, select a password-protected superuser account with unique name, and modify the +/etc/grub.d/01_users configuration file to reflect the account name change. + +Do not to use common administrator account names like root, +admin, or administrator for the grub2 superuser account. + +Change the superuser to a different username (The default is 'root'). +$ sed -i 's/\(set superuser=\).*/\1"<unique user ID>"/g' /etc/grub.d/01_users + +Once the superuser account has been added, +update the +grub.cfg file by running: +grubby --update-kernel=ALL + To prevent hard-coded admin usernames, automatic remediation of this control is not available. Remediation +must be automated as a component of machine provisioning, or followed manually as outlined above. + +Also, do NOT manually add the superuser account and password to the +grub.cfg file as the grub2-mkconfig command overwrites this file. + BP28(R17) + 1 + 11 + 12 + 14 + 15 + 16 + 18 + 3 + 5 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.03 + DSS06.06 + DSS06.10 + 3.4.5 + CCI-000213 + 164.308(a)(1)(ii)(B) + 164.308(a)(7)(i) + 164.308(a)(7)(ii)(A) + 164.310(a)(1) + 164.310(a)(2)(i) + 164.310(a)(2)(ii) + 164.310(a)(2)(iii) + 164.310(b) + 164.310(c) + 164.310(d)(1) + 164.310(d)(2)(iii) + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + A.18.1.4 + A.6.1.2 + A.7.1.1 + A.9.1.2 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.1 + A.9.4.2 + A.9.4.3 + A.9.4.4 + A.9.4.5 + CM-6(a) + PR.AC-1 + PR.AC-4 + PR.AC-6 + PR.AC-7 + PR.PT-3 + FIA_UAU.1 + SRG-OS-000080-GPOS-00048 + Having a non-default grub superuser username makes password-guessing attacks less effective. + + CCE-87370-3 + + + + + + + + + Set Boot Loader Password in grub2 + The grub2 boot loader should have a superuser account and password +protection enabled to protect boot-time settings. + +Since plaintext passwords are a security risk, generate a hash for the password +by running the following command: + +# grub2-setpassword + +When prompted, enter the password that was selected. + + To prevent hard-coded passwords, automatic remediation of this control is not available. Remediation +must be automated as a component of machine provisioning, or followed manually as outlined above. + +Also, do NOT manually add the superuser account and password to the +grub.cfg file as the grub2-mkconfig command overwrites this file. + BP28(R17) + 1 + 11 + 12 + 14 + 15 + 16 + 18 + 3 + 5 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.03 + DSS06.06 + DSS06.10 + 3.4.5 + CCI-000213 + 164.308(a)(1)(ii)(B) + 164.308(a)(7)(i) + 164.308(a)(7)(ii)(A) + 164.310(a)(1) + 164.310(a)(2)(i) + 164.310(a)(2)(ii) + 164.310(a)(2)(iii) + 164.310(b) + 164.310(c) + 164.310(d)(1) + 164.310(d)(2)(iii) + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + A.18.1.4 + A.6.1.2 + A.7.1.1 + A.9.1.2 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.1 + A.9.4.2 + A.9.4.3 + A.9.4.4 + A.9.4.5 + CM-6(a) + PR.AC-1 + PR.AC-4 + PR.AC-6 + PR.AC-7 + PR.PT-3 + FIA_UAU.1 + SRG-OS-000080-GPOS-00048 + Password protection on the boot loader configuration ensures +users with physical access cannot trivially alter +important bootloader settings. These include which kernel to use, +and whether to enter single-user mode. + + CCE-83849-0 + + + + + + + + + + UEFI GRUB2 bootloader configuration + UEFI GRUB2 bootloader configuration + + + Verify the UEFI Boot Loader grub.cfg Permissions + File permissions for /boot/grub2/grub.cfg should be set to 700. + +To properly set the permissions of /boot/grub2/grub.cfg, run the command: +$ sudo chmod 700 /boot/grub2/grub.cfg + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + 3.4.5 + CCI-000225 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + Proper permissions ensure that only the root user can modify important boot +parameters. + + CCE-85925-6 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-85925-6 + - NIST-800-171-3.4.5 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_permissions_efi_grub2_cfg + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Test for existence /boot/grub2/grub.cfg + stat: + path: /boot/grub2/grub.cfg + register: file_exists + when: + - '"/boot/efi" in ansible_mounts | map(attribute="mount") | list' + - '"grub2-common" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-85925-6 + - NIST-800-171-3.4.5 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_permissions_efi_grub2_cfg + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure permission u-s,g-xwrs,o-xwrt on /boot/grub2/grub.cfg + file: + path: /boot/grub2/grub.cfg + mode: u-s,g-xwrs,o-xwrt + when: + - '"/boot/efi" in ansible_mounts | map(attribute="mount") | list' + - '"grub2-common" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-85925-6 + - NIST-800-171-3.4.5 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_permissions_efi_grub2_cfg + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ -f /sys/firmware/efi ] && rpm --quiet -q grub2-common && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +chmod u-s,g-xwrs,o-xwrt /boot/grub2/grub.cfg + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Set the UEFI Boot Loader Admin Username to a Non-Default Value + The grub2 boot loader should have a superuser account and password +protection enabled to protect boot-time settings. + +To maximize the protection, select a password-protected superuser account with unique name, and modify the +/etc/grub.d/01_users configuration file to reflect the account name change. + +It is highly suggested not to use common administrator account names like root, +admin, or administrator for the grub2 superuser account. + +Change the superuser to a different username (The default is 'root'). +$ sed -i 's/\(set superusers=\).*/\1"<unique user ID>"/g' /etc/grub.d/01_users + +Once the superuser account has been added, +update the +grub.cfg file by running: +grubby --update-kernel=ALL + To prevent hard-coded admin usernames, automatic remediation of this control is not available. Remediation +must be automated as a component of machine provisioning, or followed manually as outlined above. + +Also, do NOT manually add the superuser account and password to the +grub.cfg file as the grub2-mkconfig command overwrites this file. + BP28(R17) + 11 + 12 + 14 + 15 + 16 + 18 + 3 + 5 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + DSS06.03 + DSS06.06 + 3.4.5 + CCI-000213 + 164.308(a)(1)(ii)(B) + 164.308(a)(7)(i) + 164.308(a)(7)(ii)(A) + 164.310(a)(1) + 164.310(a)(2)(i) + 164.310(a)(2)(ii) + 164.310(a)(2)(iii) + 164.310(b) + 164.310(c) + 164.310(d)(1) + 164.310(d)(2)(iii) + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + A.6.1.2 + A.7.1.1 + A.9.1.2 + A.9.2.1 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-6(a) + PR.AC-4 + PR.AC-6 + PR.PT-3 + FIA_UAU.1 + SRG-OS-000080-GPOS-00048 + Having a non-default grub superuser username makes password-guessing attacks less effective. + + CCE-89427-9 + + + + + + + + + Set the UEFI Boot Loader Password + The grub2 boot loader should have a superuser account and password +protection enabled to protect boot-time settings. + +Since plaintext passwords are a security risk, generate a hash for the password +by running the following command: + +# grub2-setpassword + +When prompted, enter the password that was selected. + + To prevent hard-coded passwords, automatic remediation of this control is not available. Remediation +must be automated as a component of machine provisioning, or followed manually as outlined above. + +Also, do NOT manually add the superuser account and password to the +grub.cfg file as the grub2-mkconfig command overwrites this file. + BP28(R17) + 11 + 12 + 14 + 15 + 16 + 18 + 3 + 5 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + DSS06.03 + DSS06.06 + 3.4.5 + CCI-000213 + 164.308(a)(1)(ii)(B) + 164.308(a)(7)(i) + 164.308(a)(7)(ii)(A) + 164.310(a)(1) + 164.310(a)(2)(i) + 164.310(a)(2)(ii) + 164.310(a)(2)(iii) + 164.310(b) + 164.310(c) + 164.310(d)(1) + 164.310(d)(2)(iii) + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + A.6.1.2 + A.7.1.1 + A.9.1.2 + A.9.2.1 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-6(a) + PR.AC-4 + PR.AC-6 + PR.PT-3 + FIA_UAU.1 + SRG-OS-000080-GPOS-00048 + Password protection on the boot loader configuration ensures +users with physical access cannot trivially alter +important bootloader settings. These include which kernel to use, +and whether to enter single-user mode. + + CCE-88654-9 + + + + + + + + + + + zIPL bootloader configuration + During the boot process, the bootloader is +responsible for starting the execution of the kernel and passing +options to it. +The default Red Hat Enterprise Linux 9 boot loader for s390x systems is called zIPL. + + + Enable Auditing to Start Prior to the Audit Daemon in zIPL + To ensure all processes can be audited, even those which start prior to the audit daemon, +check that all boot entries in /boot/loader/entries/*.conf have audit=1 +included in its options. + +To ensure that new kernels and boot entries continue to enable audit, +add audit=1 to /etc/kernel/cmdline. + FAU_GEN.1 + Each process on the system carries an "auditable" flag which indicates whether +its activities can be audited. Although auditd takes care of enabling +this for all processes which launch after it does, adding the kernel argument +ensures it is set for every process during boot. + + CCE-84096-7 + - name: Ensure BLS boot entries options contain audit=1 + block: + + - name: 'Check how many boot entries exist ' + find: + paths: /boot/loader/entries/ + patterns: '*.conf' + register: n_entries + + - name: Check how many boot entries set audit=1 + find: + paths: /boot/loader/entries/ + contains: ^options .*audit=1.*$ + patterns: '*.conf' + register: n_entries_options + + - name: Update boot entries options + command: grubby --update-kernel=ALL --args="audit=1" + when: n_entries is defined and n_entries_options is defined and n_entries.matched + != n_entries_options.matched + + - name: Check if /etc/kernel/cmdline exists + stat: + path: /etc/kernel/cmdline + register: cmdline_stat + + - name: Check if /etc/kernel/cmdline contains audit=1 + find: + paths: /etc/kernel/ + patterns: cmdline + contains: ^.*audit=1.*$ + register: cmdline_find + + - name: Add /etc/kernel/cmdline contains audit=1 + lineinfile: + create: true + path: /etc/kernel/cmdline + line: audit=1 + when: cmdline_stat is defined and not cmdline_stat.stat.exists + + - name: Append /etc/kernel/cmdline contains audit=1 + lineinfile: + path: /etc/kernel/cmdline + backrefs: true + regexp: ^(.*)$ + line: \1 audit=1 + when: cmdline_stat is defined and cmdline_stat.stat.exists and cmdline_find is + defined and cmdline_find.matched == 0 + when: + - ansible_architecture == "s390x" + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84096-7 + - configure_strategy + - low_disruption + - medium_complexity + - medium_severity + - reboot_required + - zipl_audit_argument + + # Remediation is applicable only in certain platforms +if grep -q s390x /proc/sys/kernel/osrelease && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +# Correct BLS option using grubby, which is a thin wrapper around BLS operations +grubby --update-kernel=ALL --args="audit=1" + +# Ensure new kernels and boot entries retain the boot option +if [ ! -f /etc/kernel/cmdline ]; then + echo "audit=1" > /etc/kernel/cmdline +elif ! grep -q '^(.*\s)?audit=1(\s.*)?$' /etc/kernel/cmdline; then + + sed -Ei 's/^(.*)$/\1 audit=1/' /etc/kernel/cmdline +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Extend Audit Backlog Limit for the Audit Daemon in zIPL + To improve the kernel capacity to queue all log events, even those which start prior to the audit daemon, +check that all boot entries in /boot/loader/entries/*.conf have audit_backlog_limit=8192 +included in its options. +To ensure that new kernels and boot entries continue to extend the audit log events queue, +add audit_backlog_limit=8192 to /etc/kernel/cmdline. + FAU_STG.1 + FAU_STG.3 + audit_backlog_limit sets the queue length for audit events awaiting transfer +to the audit daemon. Until the audit daemon is up and running, all log messages +are stored in this queue. If the queue is overrun during boot process, the action +defined by audit failure flag is taken. + + CCE-84099-1 + - name: Ensure BLS boot entries options contain audit_backlog_limit=8192 + block: + + - name: 'Check how many boot entries exist ' + find: + paths: /boot/loader/entries/ + patterns: '*.conf' + register: n_entries + + - name: Check how many boot entries set audit_backlog_limit=8192 + find: + paths: /boot/loader/entries/ + contains: ^options .*audit_backlog_limit=8192.*$ + patterns: '*.conf' + register: n_entries_options + + - name: Update boot entries options + command: grubby --update-kernel=ALL --args="audit_backlog_limit=8192" + when: n_entries is defined and n_entries_options is defined and n_entries.matched + != n_entries_options.matched + + - name: Check if /etc/kernel/cmdline exists + stat: + path: /etc/kernel/cmdline + register: cmdline_stat + + - name: Check if /etc/kernel/cmdline contains audit_backlog_limit=8192 + find: + paths: /etc/kernel/ + patterns: cmdline + contains: ^.*audit_backlog_limit=8192.*$ + register: cmdline_find + + - name: Add /etc/kernel/cmdline contains audit_backlog_limit=8192 + lineinfile: + create: true + path: /etc/kernel/cmdline + line: audit_backlog_limit=8192 + when: cmdline_stat is defined and not cmdline_stat.stat.exists + + - name: Append /etc/kernel/cmdline contains audit_backlog_limit=8192 + lineinfile: + path: /etc/kernel/cmdline + backrefs: true + regexp: ^(.*)$ + line: \1 audit_backlog_limit=8192 + when: cmdline_stat is defined and cmdline_stat.stat.exists and cmdline_find is + defined and cmdline_find.matched == 0 + when: + - ansible_architecture == "s390x" + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84099-1 + - configure_strategy + - low_disruption + - medium_complexity + - medium_severity + - reboot_required + - zipl_audit_backlog_limit_argument + + # Remediation is applicable only in certain platforms +if grep -q s390x /proc/sys/kernel/osrelease && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +# Correct BLS option using grubby, which is a thin wrapper around BLS operations +grubby --update-kernel=ALL --args="audit_backlog_limit=8192" + +# Ensure new kernels and boot entries retain the boot option +if [ ! -f /etc/kernel/cmdline ]; then + echo "audit_backlog_limit=8192" > /etc/kernel/cmdline +elif ! grep -q '^(.*\s)?audit_backlog_limit=8192(\s.*)?$' /etc/kernel/cmdline; then + + sed -Ei 's/^(.*)$/\1 audit_backlog_limit=8192/' /etc/kernel/cmdline +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure all zIPL boot entries are BLS compliant + Ensure that zIPL boot entries fully adheres to Boot Loader Specification (BLS) +by checking that /etc/zipl.conf doesn't contain image = . + To prevent breakage or removal of all boot entries oconfigured in /etc/zipl.conf +automated remediation for this rule is not available. + Red Hat Enterprise Linux 9 adheres to Boot Loader Specification (BLS) and is the prefered method of +configuration. + + CCE-84092-6 + + + + + + + + + Ensure zIPL bootmap is up to date + Make sure that /boot/bootmap is up to date. +Every time a boot entry or zIPL configuration is changed /boot/bootmap needs to +be updated to reflect the changes. +Run zipl command to generate an updated /boot/bootmap. + The file /boot/bootmap contains all boot data, keeping it up to date is crucial to +boot correct kernel and options. + + CCE-84098-3 + # Remediation is applicable only in certain platforms +if grep -q s390x /proc/sys/kernel/osrelease && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +/usr/sbin/zipl + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure SELinux Not Disabled in zIPL + To ensure SELinux is not disabled at boot time, +check that no boot entry in /boot/loader/entries/*.conf has selinux=0 +included in its options. + Disabling a major host protection feature, such as SELinux, at boot time prevents +it from confining system services at boot time. Further, it increases +the chances that it will remain off during system operation. + + + + + + + Configure kernel to zero out memory before allocation in zIPL + To ensure that the kernel is configured to zero out memory before +allocation, check that all boot entries in +/boot/loader/entries/*.conf have init_on_alloc=1 +included in its options. + +To ensure that new kernels and boot entries continue to zero out memory +before allocation, add init_on_alloc=1 to /etc/kernel/cmdline. + When the kernel configuration option init_on_alloc is enabled, +all page allocator and slab allocator memory will be zeroed when allocated, +eliminating many kinds of "uninitialized heap memory" flaws, effectively +preventing data leaks. + + CCE-85868-8 + - name: Ensure BLS boot entries options contain init_on_alloc=1 + block: + + - name: 'Check how many boot entries exist ' + find: + paths: /boot/loader/entries/ + patterns: '*.conf' + register: n_entries + + - name: Check how many boot entries set init_on_alloc=1 + find: + paths: /boot/loader/entries/ + contains: ^options .*init_on_alloc=1.*$ + patterns: '*.conf' + register: n_entries_options + + - name: Update boot entries options + command: grubby --update-kernel=ALL --args="init_on_alloc=1" + when: n_entries is defined and n_entries_options is defined and n_entries.matched + != n_entries_options.matched + + - name: Check if /etc/kernel/cmdline exists + stat: + path: /etc/kernel/cmdline + register: cmdline_stat + + - name: Check if /etc/kernel/cmdline contains init_on_alloc=1 + find: + paths: /etc/kernel/ + patterns: cmdline + contains: ^.*init_on_alloc=1.*$ + register: cmdline_find + + - name: Add /etc/kernel/cmdline contains init_on_alloc=1 + lineinfile: + create: true + path: /etc/kernel/cmdline + line: init_on_alloc=1 + when: cmdline_stat is defined and not cmdline_stat.stat.exists + + - name: Append /etc/kernel/cmdline contains init_on_alloc=1 + lineinfile: + path: /etc/kernel/cmdline + backrefs: true + regexp: ^(.*)$ + line: \1 init_on_alloc=1 + when: cmdline_stat is defined and cmdline_stat.stat.exists and cmdline_find is + defined and cmdline_find.matched == 0 + when: + - ansible_architecture == "s390x" + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-85868-8 + - configure_strategy + - low_disruption + - medium_complexity + - medium_severity + - reboot_required + - zipl_init_on_alloc_argument + + # Remediation is applicable only in certain platforms +if grep -q s390x /proc/sys/kernel/osrelease && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +# Correct BLS option using grubby, which is a thin wrapper around BLS operations +grubby --update-kernel=ALL --args="init_on_alloc=1" + +# Ensure new kernels and boot entries retain the boot option +if [ ! -f /etc/kernel/cmdline ]; then + echo "init_on_alloc=1" > /etc/kernel/cmdline +elif ! grep -q '^(.*\s)?init_on_alloc=1(\s.*)?$' /etc/kernel/cmdline; then + + sed -Ei 's/^(.*)$/\1 init_on_alloc=1/' /etc/kernel/cmdline +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Enable randomization of the page allocator in zIPL + To enable the randomization of the page allocator in the kernel, check that +all boot entries in /boot/loader/entries/*.conf have +page_alloc.shuffle=1 included in its options. + +To enable randomization of the page allocator also for newly installed +kernels, add page_alloc.shuffle=1 to /etc/kernel/cmdline. + The CONFIG_SHUFFLE_PAGE_ALLOCATOR config option is primarily +focused on improving the average utilization of a direct-mapped +memory-side-cache. Aside of this performance effect, it also reduces +predictability of page allocations in situations when the bad actor can +crash the system and somehow leverage knowledge of (page) allocation order +right after a fresh reboot, or can control the timing between a +hot-pluggable memory node (as in NUMA node) and applications allocating +memory ouf of that node. The page_alloc.shuffle=1 kernel command +line parameter then forces this functionality irrespectively of memory cache +architecture. + + CCE-85880-3 + - name: Ensure BLS boot entries options contain page_alloc.shuffle=1 + block: + + - name: 'Check how many boot entries exist ' + find: + paths: /boot/loader/entries/ + patterns: '*.conf' + register: n_entries + + - name: Check how many boot entries set page_alloc.shuffle=1 + find: + paths: /boot/loader/entries/ + contains: ^options .*page_alloc.shuffle=1.*$ + patterns: '*.conf' + register: n_entries_options + + - name: Update boot entries options + command: grubby --update-kernel=ALL --args="page_alloc.shuffle=1" + when: n_entries is defined and n_entries_options is defined and n_entries.matched + != n_entries_options.matched + + - name: Check if /etc/kernel/cmdline exists + stat: + path: /etc/kernel/cmdline + register: cmdline_stat + + - name: Check if /etc/kernel/cmdline contains page_alloc.shuffle=1 + find: + paths: /etc/kernel/ + patterns: cmdline + contains: ^.*page_alloc.shuffle=1.*$ + register: cmdline_find + + - name: Add /etc/kernel/cmdline contains page_alloc.shuffle=1 + lineinfile: + create: true + path: /etc/kernel/cmdline + line: page_alloc.shuffle=1 + when: cmdline_stat is defined and not cmdline_stat.stat.exists + + - name: Append /etc/kernel/cmdline contains page_alloc.shuffle=1 + lineinfile: + path: /etc/kernel/cmdline + backrefs: true + regexp: ^(.*)$ + line: \1 page_alloc.shuffle=1 + when: cmdline_stat is defined and cmdline_stat.stat.exists and cmdline_find is + defined and cmdline_find.matched == 0 + when: + - ansible_architecture == "s390x" + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-85880-3 + - configure_strategy + - low_disruption + - medium_complexity + - medium_severity + - reboot_required + - zipl_page_alloc_shuffle_argument + + # Remediation is applicable only in certain platforms +if grep -q s390x /proc/sys/kernel/osrelease && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +# Correct BLS option using grubby, which is a thin wrapper around BLS operations +grubby --update-kernel=ALL --args="page_alloc.shuffle=1" + +# Ensure new kernels and boot entries retain the boot option +if [ ! -f /etc/kernel/cmdline ]; then + echo "page_alloc.shuffle=1" > /etc/kernel/cmdline +elif ! grep -q '^(.*\s)?page_alloc.shuffle=1(\s.*)?$' /etc/kernel/cmdline; then + + sed -Ei 's/^(.*)$/\1 page_alloc.shuffle=1/' /etc/kernel/cmdline +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Enable page allocator poisoning in zIPL + To enable poisoning of free pages, +check that all boot entries in /boot/loader/entries/*.conf have page_poison=1 +included in its options. +To ensure that new kernels and boot entries continue to enable page poisoning, +add page_poison=1 to /etc/kernel/cmdline. + Poisoning writes an arbitrary value to freed pages, so any modification or +reference to that page after being freed or before being initialized will be +detected and prevented. +This prevents many types of use-after-free vulnerabilities at little performance cost. +Also prevents leak of data and detection of corrupted memory. + + CCE-84101-5 + - name: Ensure BLS boot entries options contain page_poison=1 + block: + + - name: 'Check how many boot entries exist ' + find: + paths: /boot/loader/entries/ + patterns: '*.conf' + register: n_entries + + - name: Check how many boot entries set page_poison=1 + find: + paths: /boot/loader/entries/ + contains: ^options .*page_poison=1.*$ + patterns: '*.conf' + register: n_entries_options + + - name: Update boot entries options + command: grubby --update-kernel=ALL --args="page_poison=1" + when: n_entries is defined and n_entries_options is defined and n_entries.matched + != n_entries_options.matched + + - name: Check if /etc/kernel/cmdline exists + stat: + path: /etc/kernel/cmdline + register: cmdline_stat + + - name: Check if /etc/kernel/cmdline contains page_poison=1 + find: + paths: /etc/kernel/ + patterns: cmdline + contains: ^.*page_poison=1.*$ + register: cmdline_find + + - name: Add /etc/kernel/cmdline contains page_poison=1 + lineinfile: + create: true + path: /etc/kernel/cmdline + line: page_poison=1 + when: cmdline_stat is defined and not cmdline_stat.stat.exists + + - name: Append /etc/kernel/cmdline contains page_poison=1 + lineinfile: + path: /etc/kernel/cmdline + backrefs: true + regexp: ^(.*)$ + line: \1 page_poison=1 + when: cmdline_stat is defined and cmdline_stat.stat.exists and cmdline_find is + defined and cmdline_find.matched == 0 + when: + - ansible_architecture == "s390x" + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84101-5 + - configure_strategy + - low_disruption + - medium_complexity + - medium_severity + - reboot_required + - zipl_page_poison_argument + + # Remediation is applicable only in certain platforms +if grep -q s390x /proc/sys/kernel/osrelease && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +# Correct BLS option using grubby, which is a thin wrapper around BLS operations +grubby --update-kernel=ALL --args="page_poison=1" + +# Ensure new kernels and boot entries retain the boot option +if [ ! -f /etc/kernel/cmdline ]; then + echo "page_poison=1" > /etc/kernel/cmdline +elif ! grep -q '^(.*\s)?page_poison=1(\s.*)?$' /etc/kernel/cmdline; then + + sed -Ei 's/^(.*)$/\1 page_poison=1/' /etc/kernel/cmdline +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Enable SLUB/SLAB allocator poisoning in zIPL + To enable poisoning of SLUB/SLAB objects, +check that all boot entries in /boot/loader/entries/*.conf have slub_debug=P +included in its options. +To ensure that new kernels and boot entries continue to enable poisoning of SLUB/SLAB objects, +add slub_debug=P to /etc/kernel/cmdline. + Poisoning writes an arbitrary value to freed objects, so any modification or +reference to that object after being freed or before being initialized will be +detected and prevented. +This prevents many types of use-after-free vulnerabilities at little performance cost. +Also prevents leak of data and detection of corrupted memory. + + CCE-84094-2 + - name: Ensure BLS boot entries options contain slub_debug=P + block: + + - name: 'Check how many boot entries exist ' + find: + paths: /boot/loader/entries/ + patterns: '*.conf' + register: n_entries + + - name: Check how many boot entries set slub_debug=P + find: + paths: /boot/loader/entries/ + contains: ^options .*slub_debug=P.*$ + patterns: '*.conf' + register: n_entries_options + + - name: Update boot entries options + command: grubby --update-kernel=ALL --args="slub_debug=P" + when: n_entries is defined and n_entries_options is defined and n_entries.matched + != n_entries_options.matched + + - name: Check if /etc/kernel/cmdline exists + stat: + path: /etc/kernel/cmdline + register: cmdline_stat + + - name: Check if /etc/kernel/cmdline contains slub_debug=P + find: + paths: /etc/kernel/ + patterns: cmdline + contains: ^.*slub_debug=P.*$ + register: cmdline_find + + - name: Add /etc/kernel/cmdline contains slub_debug=P + lineinfile: + create: true + path: /etc/kernel/cmdline + line: slub_debug=P + when: cmdline_stat is defined and not cmdline_stat.stat.exists + + - name: Append /etc/kernel/cmdline contains slub_debug=P + lineinfile: + path: /etc/kernel/cmdline + backrefs: true + regexp: ^(.*)$ + line: \1 slub_debug=P + when: cmdline_stat is defined and cmdline_stat.stat.exists and cmdline_find is + defined and cmdline_find.matched == 0 + when: + - ansible_architecture == "s390x" + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84094-2 + - configure_strategy + - low_disruption + - medium_complexity + - medium_severity + - reboot_required + - zipl_slub_debug_argument + + # Remediation is applicable only in certain platforms +if grep -q s390x /proc/sys/kernel/osrelease && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +# Correct BLS option using grubby, which is a thin wrapper around BLS operations +grubby --update-kernel=ALL --args="slub_debug=P" + +# Ensure new kernels and boot entries retain the boot option +if [ ! -f /etc/kernel/cmdline ]; then + echo "slub_debug=P" > /etc/kernel/cmdline +elif ! grep -q '^(.*\s)?slub_debug=P(\s.*)?$' /etc/kernel/cmdline; then + + sed -Ei 's/^(.*)$/\1 slub_debug=P/' /etc/kernel/cmdline +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure debug-shell service is not enabled in zIPL + systemd's debug-shell service is intended to +diagnose systemd related boot issues with various systemctl +commands. Once enabled and following a system reboot, the root shell +will be available on tty9 which is access by pressing +CTRL-ALT-F9. The debug-shell service should only be used +for systemd related issues and should otherwise be disabled. + +By default, the debug-shell systemd service is already disabled. + +Ensure the debug-shell is not enabled by the systemd.debug-shel=1 +boot paramenter option. + +Check that not boot entries in /boot/loader/entries/*.conf have +systemd.debug-shell=1 included in its options. +To ensure that new kernels and boot entries don't enable the debug-shell, check +that systemd.debug-shell=1 is not present in /etc/kernel/cmdline. + FIA_UAU.1 + This prevents attackers with physical access from trivially bypassing security +on the machine through valid troubleshooting configurations and gaining root +access when the system is rebooted. + + CCE-86420-7 + - name: Ensure BLS boot entries options contain systemd.debug-shell + block: + + - name: Check how many boot entries set systemd.debug-shell + find: + paths: /boot/loader/entries/ + contains: ^options .*systemd\.debug-shell.*$ + patterns: '*.conf' + register: n_entries + + - name: Remove systemd.debug-shell from boot entries + command: grubby --update-kernel=ALL --remove-args="systemd.debug-shell" + when: n_entries is defined and n_entries.matched >= 1 + + - name: Check if /etc/kernel/cmdline exists + stat: + path: /etc/kernel/cmdline + register: cmdline_stat + + - name: Check if /etc/kernel/cmdline contains systemd.debug-shell + find: + paths: /etc/kernel/ + patterns: cmdline + contains: ^.*systemd\.debug-shell.*$ + register: cmdline_find + + - name: Remove systemd.debug-shell from /etc/kernel/cmdline + lineinfile: + path: /etc/kernel/cmdline + backrefs: true + regexp: ^(.*)\s*systemd.debug-shell\b\S*(.*)$ + line: \1\2 + when: cmdline_stat is defined and cmdline_stat.stat.exists and cmdline_find is + defined and cmdline_find.matched >= 1 + when: + - ansible_architecture == "s390x" + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86420-7 + - configure_strategy + - low_disruption + - medium_complexity + - medium_severity + - reboot_required + - zipl_systemd_debug-shell_argument_absent + + # Remediation is applicable only in certain platforms +if grep -q s390x /proc/sys/kernel/osrelease && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +# Correct BLS option using grubby, which is a thin wrapper around BLS operations +grubby --update-kernel=ALL --remove-args="systemd.debug-shell" + +# Ensure new kernels and boot entries retain the boot option +if grep -q '\bsystemd.debug-shell\b' /etc/kernel/cmdline; then + sed -Ei 's/^(.*)\s*systemd.debug-shell\b\S*(.*)/\1\2/' /etc/kernel/cmdline +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable vsyscalls in zIPL + To disable use of virtual syscalls, +check that all boot entries in /boot/loader/entries/*.conf have vsyscall=none +included in its options. +To ensure that new kernels and boot entries continue to disable virtual syscalls, +add vsyscall=none to /etc/kernel/cmdline. + FPT_ASLR_EXT.1 + Virtual Syscalls provide an opportunity of attack for a user who has control +of the return instruction pointer. + + CCE-84100-7 + - name: Ensure BLS boot entries options contain vsyscall=none + block: + + - name: 'Check how many boot entries exist ' + find: + paths: /boot/loader/entries/ + patterns: '*.conf' + register: n_entries + + - name: Check how many boot entries set vsyscall=none + find: + paths: /boot/loader/entries/ + contains: ^options .*vsyscall=none.*$ + patterns: '*.conf' + register: n_entries_options + + - name: Update boot entries options + command: grubby --update-kernel=ALL --args="vsyscall=none" + when: n_entries is defined and n_entries_options is defined and n_entries.matched + != n_entries_options.matched + + - name: Check if /etc/kernel/cmdline exists + stat: + path: /etc/kernel/cmdline + register: cmdline_stat + + - name: Check if /etc/kernel/cmdline contains vsyscall=none + find: + paths: /etc/kernel/ + patterns: cmdline + contains: ^.*vsyscall=none.*$ + register: cmdline_find + + - name: Add /etc/kernel/cmdline contains vsyscall=none + lineinfile: + create: true + path: /etc/kernel/cmdline + line: vsyscall=none + when: cmdline_stat is defined and not cmdline_stat.stat.exists + + - name: Append /etc/kernel/cmdline contains vsyscall=none + lineinfile: + path: /etc/kernel/cmdline + backrefs: true + regexp: ^(.*)$ + line: \1 vsyscall=none + when: cmdline_stat is defined and cmdline_stat.stat.exists and cmdline_find is + defined and cmdline_find.matched == 0 + when: + - ansible_architecture == "s390x" + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84100-7 + - configure_strategy + - low_disruption + - medium_complexity + - medium_severity + - reboot_required + - zipl_vsyscall_argument + + # Remediation is applicable only in certain platforms +if grep -q s390x /proc/sys/kernel/osrelease && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +# Correct BLS option using grubby, which is a thin wrapper around BLS operations +grubby --update-kernel=ALL --args="vsyscall=none" + +# Ensure new kernels and boot entries retain the boot option +if [ ! -f /etc/kernel/cmdline ]; then + echo "vsyscall=none" > /etc/kernel/cmdline +elif ! grep -q '^(.*\s)?vsyscall=none(\s.*)?$' /etc/kernel/cmdline; then + + sed -Ei 's/^(.*)$/\1 vsyscall=none/' /etc/kernel/cmdline +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Protect Random-Number Entropy Pool + The I/O operations of the Linux kernel block layer due to their inherently +unpredictable execution times have been traditionally considered as a reliable +source to contribute to random-number entropy pool of the Linux kernel. This +has changed with introduction of solid-state storage devices (SSDs) though. + + + Ensure Solid State Drives Do Not Contribute To Random-Number Entropy Pool + For each solid-state drive on the system, run: + # echo 0 > /sys/block/DRIVE/queue/add_random + In contrast to traditional electromechanical magnetic disks, containing +spinning disks and / or movable read / write heads, the solid-state storage +devices (SSDs) do not contain moving / mechanical components. Therefore the +I/O operation completion times are much more predictable for them. + + + + Kernel Configuration + Contains rules that check the kernel configuration that was used to build it. + + + Hash function for kernel module signing + The hash function to use when signing modules during kernel build process. + sha512 + sha1 + sha224 + sha256 + sha384 + sha512 + + + Key and certificate for kernel module signing + The private key and certificate to use when signing modules during kernel build process. +On systems where the OpenSSL ENGINE_pkcs11 is functional — a PKCS#11 URI as defined by RFC7512 +In the latter case, the PKCS#11 URI should reference both a certificate and a private key. + certs/signing_key.pem + certs/signing_key.pem + + + Kernel panic timeout + The time, in seconds, to wait until a reboot occurs. +If the value is 0 the system never reboots. +If the value is less than 0 the system reboots immediately. + 0 + 0 + 300 + 60 + -1 + + + Do not allow ACPI methods to be inserted/replaced at run time + This debug facility allows ACPI AML methods to be inserted and/or replaced without rebooting +the system. +This configuration is available from kernel 3.0. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_ACPI_CUSTOM_METHOD, run the following command: + grep CONFIG_ACPI_CUSTOM_METHOD /boot/config-* + + Configs with value 'n' are not explicitly set in the file, so either commented lines or no + lines should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + Enabling this feature allows arbitrary kernel memory to be written to by root (uid=0) users, +allowing them to bypass certain security measures + CCE-86779-6 + + + + + + + + + Emulate Privileged Access Never (PAN) + Enabling this option prevents the kernel from accessing user-space memory directly by pointing +TTBR0_EL1 to a reserved zeroed area and reserved ASID. +The user access routines restore the valid TTBR0_EL1 temporarily. +This configuration is available from kernel 4.10, but may be available if backported +by distros. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_ARM64_SW_TTBR0_PAN, run the following command: + grep CONFIG_ARM64_SW_TTBR0_PAN /boot/config-* + + For each kernel installed, a line with value "y" should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + The Privileged Access Never (PAN) is the ARM equivalent of the x86 Supervisor Mode Access +Prevention (SMAP), and it prevents privileged acccess to user data unless explicitly enabled. + + CCE-89060-8 + + + + + + + + + Disable kernel support for MISC binaries + Enabling CONFIG_BINFMT_MISC makes it possible to plug wrapper-driven binary formats +into the kernel. This is specially useful for programs that need an interpreter to run like +Java, Python and DOS emulators. Once you have registered such a binary class with the kernel, +you can start one of those programs simply by typing in its name at a shell prompt. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_BINFMT_MISC, run the following command: + grep CONFIG_BINFMT_MISC /boot/config-* + + Configs with value 'n' are not explicitly set in the file, so either commented lines or no + lines should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + This disables arbitrary binary format support and helps reduce attack surface. + CCE-87767-0 + + + + + + + + + Enable support for BUG() + Disabling this option eliminates support for BUG and WARN, reducing the size of your kernel +image and potentially quietly ignoring numerous fatal conditions. You should only consider +disabling this option for embedded systems with no facilities for reporting errors. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_BUG, run the following command: + grep CONFIG_BUG /boot/config-* + + For each kernel installed, a line with value "y" should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + Not setting this variable may hide a number of critical errors. + CCE-86096-5 + + + + + + + + + Trigger a kernel BUG when data corruption is detected + This option makes the kernel BUG when it encounters data corruption in kernel memory structures +when they get checked for validity. +This configuration is available from kernel 4.10. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_BUG_ON_DATA_CORRUPTION, run the following command: + grep CONFIG_BUG_ON_DATA_CORRUPTION /boot/config-* + + For each kernel installed, a line with value "y" should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + This helps detect data corruptions early and stop with a BUG() error message. + CCE-87305-9 + + + + + + + + + Disable compatibility with brk() + Enabling compatiliby with brk() allows legacy binaries to run (i.e. those linked +against libc5). But this compatibility comes at the cost of not being able to randomize +the heap placement (ASLR). + +Unless legacy binaries need to run on the system, set CONFIG_COMPAT_BRK to "n". + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_COMPAT_BRK, run the following command: + grep CONFIG_COMPAT_BRK /boot/config-* + + Configs with value 'n' are not explicitly set in the file, so either commented lines or no + lines should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + Enabling compatibility with brk() disables support for ASLR. + CCE-88963-4 + + + + + + + + + Disable the 32-bit vDSO + Certain buggy versions of glibc (2.3.3) will crash if they are presented with a 32-bit vDSO +that is not mapped at the address indicated in its segment table. +Setting CONFIG_COMPAT_VDSO to y turns off the 32-bit VDSO and works +aroud the glibc bug. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_COMPAT_VDSO, run the following command: + grep CONFIG_COMPAT_VDSO /boot/config-* + + Configs with value 'n' are not explicitly set in the file, so either commented lines or no + lines should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + Enabling VDSO compatibility hurts performance and disables ASLR. + CCE-87257-2 + + + + + + + + + Enable checks on credential management + Enable this to turn on some debug checking for credential management. The additional code keeps +track of the number of pointers from task_structs to any given cred struct, and checks to see +that this number never exceeds the usage count of the cred struct. + +Furthermore, if SELinux is enabled, this also checks that the security pointer in the cred +struct is never seen to be invalid. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_DEBUG_CREDENTIALS, run the following command: + grep CONFIG_DEBUG_CREDENTIALS /boot/config-* + + For each kernel installed, a line with value "y" should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + This adds sanity checks and validations to credential data structures. + CCE-86657-4 + + + + + + + + + Disable kernel debugfs + debugfs is a virtual file system that kernel developers use to put debugging files +into. Enable this option to be able to read and write to these files. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_DEBUG_FS, run the following command: + grep CONFIG_DEBUG_FS /boot/config-* + + Configs with value 'n' are not explicitly set in the file, so either commented lines or no + lines should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + To reduce the attack surface, this file system should be disabled if not in use. + CCE-89033-5 + + + + + + + + + Enable checks on linked list manipulation + Enable this to turn on extended checks in the linked-list walking routines. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_DEBUG_LIST, run the following command: + grep CONFIG_DEBUG_LIST /boot/config-* + + For each kernel installed, a line with value "y" should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + This add sanity checks to manipulation of linked lists structures in the kernel and may +prevent exploits such as CVE-2017-1661, where a race condition and simultaneos operations +caused a list to corrupt. + CCE-86987-5 + + + + + + + + + Enable checks on notifier call chains + Enable this to turn on sanity checking for notifier call chains. This is most useful for kernel +developers to make sure that modules properly unregister themselves from notifier chains. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_DEBUG_NOTIFIERS, run the following command: + grep CONFIG_DEBUG_NOTIFIERS /boot/config-* + + For each kernel installed, a line with value "y" should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + This provides validation of notifier chains, it checks whether the notifiers are from the +kernel or a module that is still loaded prior to being invoked. + CCE-86815-8 + + + + + + + + + Enable checks on scatter-gather (SG) table operations + Scatter-gather tables are mechanism used for high performance I/O on DMA devices. +Enable this to turn on checks on scatter-gather tables. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_DEBUG_SG, run the following command: + grep CONFIG_DEBUG_SG /boot/config-* + + For each kernel installed, a line with value "y" should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + This can help find problems with drivers that do not properly initialize their SG tables. + CCE-87149-1 + + + + + + + + + Warn on W+X mappings found at boot + Generate a warning if any W+X mappings are found at boot. +This configuration is available from kernel 5.8. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_DEBUG_WX, run the following command: + grep CONFIG_DEBUG_WX /boot/config-* + + For each kernel installed, a line with value "y" should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + This is useful for discovering cases where the kernel is leaving W+X mappings after applying NX, +as such mappings are a security risk. +Note that even if the check fails, your kernel is possibly still fine, as W+X mappings are not +a security hole in themselves, what they do is that they make the exploitation of other unfixed +kernel bugs easier. + CCE-88032-8 + + + + + + + + + Configure low address space to protect from user allocation + This is the portion of low virtual memory which should be protected from userspace allocation. +This configuration is available from kernel 3.14, but may be available if backported +by distros. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_DEFAULT_MMAP_MIN_ADDR, run the following command: + grep CONFIG_DEFAULT_MMAP_MIN_ADDR /boot/config-* + + For each kernel installed, a line with value "65536" should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + Keeping a user from writing to low pages can help reduce the impact of kernel NULL pointer bugs. + CCE-88161-5 + + + + + + + + + Disable /dev/kmem virtual device support + Disable support for the /dev/kmem device. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_DEVKMEM, run the following command: + grep CONFIG_DEVKMEM /boot/config-* + + Configs with value 'n' are not explicitly set in the file, so either commented lines or no + lines should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + The /dev/kmem device is rarely used, but can be used for certain kind of kernel debugging +operations. + CCE-86948-7 + + + + + + + + + Harden common str/mem functions against buffer overflows + Detect overflows of buffers in common string and memory functions where the compiler can +determine and validate the buffer sizes. +This configuration is available from kernel 4.13, but may be available if backported by distros. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_FORTIFY_SOURCE, run the following command: + grep CONFIG_FORTIFY_SOURCE /boot/config-* + + For each kernel installed, a line with value "y" should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + This features helps reduce likelihood of memory corruption of kernel structures. + CCE-86546-9 + + + + + + + + + Harden memory copies between kernel and userspace + This option checks for obviously wrong memory regions when copying memory to/from the kernel +(via copy_to_user() and copy_from_user() functions) by rejecting memory ranges that are larger +than the specified heap object, span multiple separately allocated pages, are not on the +process stack, or are part of the kernel text. +This configuration is available from kernel 4.8, and may be available if backported by distros. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_HARDENED_USERCOPY, run the following command: + grep CONFIG_HARDENED_USERCOPY /boot/config-* + + For each kernel installed, a line with value "y" should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + This config prevents entire classes of heap overflow exploits and similar kernel memory exposures. + + CCE-89299-2 + + + + + + + + + Do not allow usercopy whitelist violations to fallback to object size + This is a temporary option that allows missing usercopy whitelists to be discovered via a WARN() +to the kernel log, instead of rejecting the copy, falling back to non-whitelisted hardened +usercopy that checks the slab allocation size instead of the whitelist size. +This configuration is available from kernel 4.16. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_HARDENED_USERCOPY_FALLBACK, run the following command: + grep CONFIG_HARDENED_USERCOPY_FALLBACK /boot/config-* + + Configs with value 'n' are not explicitly set in the file, so either commented lines or no + lines should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + This config prevents entire classes of heap overflow exploits and similar kernel memory exposures. + CCE-86092-4 + + + + + + + + + Disable hibernation + Enable the suspend to disk (STD) functionality, which is usually called "hibernation" in user +interfaces. STD checkpoints the system and powers it off; and restores that checkpoint on +reboot. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_HIBERNATION, run the following command: + grep CONFIG_HIBERNATION /boot/config-* + + Configs with value 'n' are not explicitly set in the file, so either commented lines or no + lines should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + Suspending to disk allows one to replace the running kernel. + CCE-87609-4 + + + + + + + + + Disable IA32 emulation + Disables support for legacy 32-bit programs under a 64-bit kernel. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_IA32_EMULATION, run the following command: + grep CONFIG_IA32_EMULATION /boot/config-* + + Configs with value 'n' are not explicitly set in the file, so either commented lines or no + lines should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + Only disable support for 32-bit programs if you are sure you don't need any 32-bit program. + Disabling 32-bit backwards compatibility helps reduce the attack surface. + CCE-88747-1 + + + + + + + + + Disable the IPv6 protocol + Disable support for IP version 6 (IPv6). + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_IPV6, run the following command: + grep CONFIG_IPV6 /boot/config-* + + Configs with value 'n' are not explicitly set in the file, so either commented lines or no + lines should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + Any unnecessary network stacks, including IPv6, should be disabled to reduce +the vulnerability to exploitation. + CCE-87226-7 + + + + + + + + + Disable kexec system call + kexec is a system call that implements the ability to shutdown your current kernel, +and to start another kernel. It is like a reboot but it is independent of the system firmware. +And like a reboot you can start any kernel with it, not just Linux. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_KEXEC, run the following command: + grep CONFIG_KEXEC /boot/config-* + + Configs with value 'n' are not explicitly set in the file, so either commented lines or no + lines should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + Prohibits the execution of a new kernel image after reboot. + CCE-87489-1 + + + + + + + + + Disable legacy (BSD) PTY support + Disable the Linux traditional BSD-like terminal names /dev/ptyxx for masters and /dev/ttyxx for +slaves of pseudo terminals, and use only the modern ptys (devpts) interface. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_LEGACY_PTYS, run the following command: + grep CONFIG_LEGACY_PTYS /boot/config-* + + Configs with value 'n' are not explicitly set in the file, so either commented lines or no + lines should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + The legacy scheme has a number of security problems. + CCE-87926-2 + + + + + + + + + Disable vsyscall emulation + The kernel traps and emulates calls into the fixed vsyscall address mapping. +This configuration is available from kernel 5.3, but may be available if backported by distros. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_LEGACY_VSYSCALL_EMULATE, run the following command: + grep CONFIG_LEGACY_VSYSCALL_EMULATE /boot/config-* + + Configs with value 'n' are not explicitly set in the file, so either commented lines or no + lines should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + The mapping is non-executable, but it still contains known contents, which could be +used in certain rare security vulnerability exploits. + CCE-87650-8 + + + + + + + + + Disable vsyscall mapping + This config disables the vsyscall mapping at all. Attempts to use the vsyscalls will be reported to +dmesg, so that either old or malicious userspace programs can be identified. +This configuration is available from kernel 4.4. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_LEGACY_VSYSCALL_NONE, run the following command: + grep CONFIG_LEGACY_VSYSCALL_NONE /boot/config-* + + For each kernel installed, a line with value "y" should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + This will eliminate any risk of ASLR bypass due to the vsyscall fixed address mapping. + CCE-87574-0 + + + + + + + + + Disable vsyscall emulate execution only + The kernel traps and emulates calls into the fixed vsyscall address mapping and does not allow +reads. +This configuration is available from kernel 5.3. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_LEGACY_VSYSCALL_XONLY, run the following command: + grep CONFIG_LEGACY_VSYSCALL_XONLY /boot/config-* + + Configs with value 'n' are not explicitly set in the file, so either commented lines or no + lines should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + Disabling this mitigates certain uses of the vsyscall area as an ASLR-bypassing buffer. + CCE-87805-8 + + + + + + + + + Disable the LDT (local descriptor table) + Linux can allow user programs to install a per-process x86 Local Descriptor Table (LDT) using +the modify_ldt(2) system call. This is required to run 16-bit or segmented code such as DOSEMU +or some Wine programs. It is also used by some very old threading libraries. +This configuration is available from kernel 4.3, but may be available if backported +by distros. + +Disable LDT if 16-bit program emulation is not necessary. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_MODIFY_LDT_SYSCALL, run the following command: + grep CONFIG_MODIFY_LDT_SYSCALL /boot/config-* + + Configs with value 'n' are not explicitly set in the file, so either commented lines or no + lines should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + Disabling support for unnecessary code reduces attack surface. + CCE-88828-9 + + + + + + + + + Enable module signature verification + Check modules for valid signatures upon load. +Note that this option adds the OpenSSL development packages as a kernel build dependency so +that the signing tool can use its crypto library. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_MODULE_SIG, run the following command: + grep CONFIG_MODULE_SIG /boot/config-* + + For each kernel installed, a line with value "y" should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + Loaded modules must be signed. + CCE-89379-2 + + + + + + + + + Enable automatic signing of all modules + Sign all modules during make modules_install. Without this option, modules must be signed +manually, using the scripts/sign-file tool. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_MODULE_SIG_ALL, run the following command: + grep CONFIG_MODULE_SIG_ALL /boot/config-* + + For each kernel installed, a line with value "y" should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + This ensures the modules are signed during install process. + CCE-89616-7 + + + + + + + + + Require modules to be validly signed + Reject unsigned modules or signed modules with an unknown key. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_MODULE_SIG_FORCE, run the following command: + grep CONFIG_MODULE_SIG_FORCE /boot/config-* + + For each kernel installed, a line with value "y" should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + Prevent loading modules that are unsigned or signed with an unknown key. + CCE-89460-0 + + + + + + + + + Specify the hash to use when signing modules + This configures the kernel to build and sign modules using + as the hash function. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_MODULE_SIG_HASH, run the following command: + grep CONFIG_MODULE_SIG_HASH /boot/config-* + + For each kernel installed, a line with value "" should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + Use of strong hash function is important to secure the module against counterfeit signatures. + CCE-89844-5 + + + + + + + + + + Specify module signing key to use + Setting this option to something other than its default of certs/signing_key.pem will +disable the autogeneration of signing keys and allow the kernel modules to be signed with a key +of your choosing. + +The string provided should identify a file containing both a private key and +its corresponding X.509 certificate in PEM form, or — on systems where the OpenSSL ENGINE_pkcs11 +is functional — a PKCS#11 URI as defined by RFC7512. In the latter case, the PKCS#11 URI should +reference both a certificate and a private key. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_MODULE_SIG_KEY, run the following command: + grep CONFIG_MODULE_SIG_KEY /boot/config-* + + For each kernel installed, a line with value "" should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + A key and certificate is required to sign the built modules. + CCE-89999-7 + + + + + + + + + + Sign kernel modules with SHA-512 + This configures the kernel to build and sign modules using SHA512 as the hash function. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_MODULE_SIG_SHA512, run the following command: + grep CONFIG_MODULE_SIG_SHA512 /boot/config-* + + For each kernel installed, a line with value "y" should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + Use of strong hash function is important to secure the module against counterfeit signatures. + CCE-89691-0 + + + + + + + + + Enable poison of pages after freeing + Fill the pages with poison patterns after free_pages() and verify the patterns before +alloc_pages. This does have a potential performance impact if enabled with the "page_poison=1" +kernel boot option. +This configuration is available from kernel 4.6. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_PAGE_POISONING, run the following command: + grep CONFIG_PAGE_POISONING /boot/config-* + + For each kernel installed, a line with value "y" should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + The filling of the memory helps reduce the risk of information leaks from freed data. + CCE-88427-0 + + + + + + + + + Enable poison without sanity check + Skip the sanity checking on alloc, only fill the pages with poison on free. This reduces some +of the overhead of the poisoning feature. +This configuration is available from kernel 4.6. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_PAGE_POISONING_NO_SANITY, run the following command: + grep CONFIG_PAGE_POISONING_NO_SANITY /boot/config-* + + For each kernel installed, a line with value "y" should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + This configuration helps alleviates the performance impact of poisonining. + CCE-88575-6 + + + + + + + + + Use zero for poisoning instead of debugging value + Instead of using the existing poison value, fill the pages with zeros. This makes it harder to +detect when errors are occurring due to sanitization but the zeroing at free means that it is +no longer necessary to write zeros when GFP_ZERO is used on allocation. +This configuration is available from kernel 4.19. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_PAGE_POISONING_ZERO, run the following command: + grep CONFIG_PAGE_POISONING_ZERO /boot/config-* + + For each kernel installed, a line with value "y" should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + This configuration helps alleviates the performance impact of poisonining. + CCE-88809-9 + + + + + + + + + Remove the kernel mapping in user mode + This feature reduces the number of hardware side channels by ensuring that the majority of +kernel addresses are not mapped into userspace. +This configuration is available from kernel 4.15, but may be available if backported +by distros. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_PAGE_TABLE_ISOLATION, run the following command: + grep CONFIG_PAGE_TABLE_ISOLATION /boot/config-* + + For each kernel installed, a line with value "y" should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + This is a countermeasure to the Meltdown attack. + CCE-88592-1 + + + + + + + + + Kernel panic oops + Enable the kernel to panic when it oopses. +This has the same effect as setting oops=panic on the kernel command line. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_PANIC_ON_OOPS, run the following command: + grep CONFIG_PANIC_ON_OOPS /boot/config-* + + For each kernel installed, a line with value "y" should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + This feature ensures that the kernel does not do anything erroneous after an oops which +could result in data corruption or other issues. + CCE-86177-3 + + + + + + + + + Kernel panic timeout + Set the timeout value (in seconds) until a reboot occurs when the kernel panics. +A timeout of 0 configures the system to wait forever. With a timeout value greater than 0, +the system will wait the specified amount of seconds before rebooting. While a timeout value +less than 0 makes the system reboot immediately. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_PANIC_TIMEOUT, run the following command: + grep CONFIG_PANIC_TIMEOUT /boot/config-* + + For each kernel installed, a line with value "" should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + This is required to enable protection against Spectre v2. + CCE-86350-6 + + + + + + + + + + Disable support for /proc/kkcore + Provides a virtual ELF core file of the live kernel. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_PROC_KCORE, run the following command: + grep CONFIG_PROC_KCORE /boot/config-* + + Configs with value 'n' are not explicitly set in the file, so either commented lines or no + lines should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + This feature exposes the memory to the userspace and can assist an attacker in discovering +attack vectors. + CCE-87106-1 + + + + + + + + + Randomize the address of the kernel image (KASLR) + In support of Kernel Address Space Layout Randomization (KASLR), this randomizes the physical +address at which the kernel image is decompressed and the virtual address where the kernel +image is mapped. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_RANDOMIZE_BASE, run the following command: + grep CONFIG_RANDOMIZE_BASE /boot/config-* + + For each kernel installed, a line with value "y" should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + An unpredictable kernel address makes it more difficult to succeed with exploits that rely on +knowledge of the location of kernel code internals. + CCE-88319-9 + + + + + + + + + Randomize the kernel memory sections + Randomizes the base virtual address of kernel memory sections (physical memory mapping, +vmalloc & vmemmap). +This configuration is available from kernel 4.8, but may be available if backported +by distros. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_RANDOMIZE_MEMORY, run the following command: + grep CONFIG_RANDOMIZE_MEMORY /boot/config-* + + For each kernel installed, a line with value "y" should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + This security feature makes exploits relying on predictable memory locations less reliable. + CCE-88441-1 + + + + + + + + + Perform full reference count validation + Enabling this switches the refcounting infrastructure from a fast unchecked atomic_t +implementation to a fully state checked implementation, which can have a slight +impact in performance. +This configuration is available from kernel 4.13, but may be available if backported +by distros. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_REFCOUNT_FULL, run the following command: + grep CONFIG_REFCOUNT_FULL /boot/config-* + + For each kernel installed, a line with value "y" should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + Refcounting provides protections against various use-after-free conditions that can be +used in security flaw exploits. + CCE-86423-1 + + + + + + + + + Avoid speculative indirect branches in kernel + Compile kernel with the retpoline compiler options to guard against kernel-to-user data leaks +by avoiding speculative indirect branches. +Requires a compiler with -mindirect-branch=thunk-extern support for full protection. +The kernel may run slower. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_RETPOLINE, run the following command: + grep CONFIG_RETPOLINE /boot/config-* + + For each kernel installed, a line with value "y" should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + This is required to enable protection against Spectre v2. + CCE-87495-8 + + + + + + + + + Detect stack corruption on calls to schedule() + This option checks for a stack overrun on calls to schedule(). If the stack end location is +found to be overwritten always panic as the content of the corrupted region can no longer +be trusted. +This configuration is available from kernel 3.18. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_SCHED_STACK_END_CHECK, run the following command: + grep CONFIG_SCHED_STACK_END_CHECK /boot/config-* + + For each kernel installed, a line with value "y" should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + This ensures no erroneous behaviour occurs which could result in data corruption or a +sporadic crash at a later stage once the region is examined. + CCE-89041-8 + + + + + + + + + Enable seccomp to safely compute untrusted bytecode + This kernel feature is useful for number crunching applications that may need to compute +untrusted bytecode during their execution. By using pipes or other transports made available +to the process as file descriptors supporting the read/write syscalls, it's possible to isolate +those applications in their own address space using seccomp. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_SECCOMP, run the following command: + grep CONFIG_SECCOMP /boot/config-* + + For each kernel installed, a line with value "y" should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + seccomp enables the ability to filter system calls made by an application, effectively +isolating the system's resources from it. + CCE-86451-2 + + + + + + + + + Enable use of Berkeley Packet Filter with seccomp + Enable tasks to build secure computing environments defined in terms of Berkeley Packet Filter +programs which implement task-defined system call filtering polices. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_SECCOMP_FILTER, run the following command: + grep CONFIG_SECCOMP_FILTER /boot/config-* + + For each kernel installed, a line with value "y" should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + Use of BPF filters allows for expressive filtering of system calls using a filter program +language with a long history of being exposed to userland. + CCE-86491-8 + + + + + + + + + Enable different security models + This allows you to choose different security modules to be configured into your kernel. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_SECURITY, run the following command: + grep CONFIG_SECURITY /boot/config-* + + For each kernel installed, a line with value "y" should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + This is enables kernel security primitives required by the LSM framework. + CCE-86573-3 + + + + + + + + + Restrict unprivileged access to the kernel syslog + Enforce restrictions on unprivileged users reading the kernel syslog via dmesg(8). + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_SECURITY_DMESG_RESTRICT, run the following command: + grep CONFIG_SECURITY_DMESG_RESTRICT /boot/config-* + + Configs with value 'n' are not explicitly set in the file, so either commented lines or no + lines should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + Prevents unprivileged users from retrieving kernel addresses with dmesg. + CCE-87340-6 + + + + + + + + + Disable mutable hooks + Ensure kernel structures associated with LSMs are always mapped as read-only after system boot. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_SECURITY_WRITABLE_HOOKS, run the following command: + grep CONFIG_SECURITY_WRITABLE_HOOKS /boot/config-* + + For each kernel installed, a line with value "y" should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + If CONFIG_SECURITY_WRITABLE_HOOKS is enabled, then hooks can be loaded at runtime and +being able to manipulate hooks is a way to bypass all LSMs. + CCE-86885-1 + + + + + + + + + Enable Yama support + This enables support for LSM module Yama, which extends DAC support with additional system-wide +security settings beyond regular Linux discretionary access controls. The module will limit the +use of the system call ptrace(). + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_SECURITY_YAMA, run the following command: + grep CONFIG_SECURITY_YAMA /boot/config-* + + For each kernel installed, a line with value "y" should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + Unrestricted usage of ptrace allows compromised binaries to run ptrace +on another processes of the user. + CCE-86717-6 + + + + + + + + + Harden slab freelist metadata + This feature protects integrity of the allocator's metadata. +This configuration is available from kernel 4.14. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_SLAB_FREELIST_HARDENED, run the following command: + grep CONFIG_SLAB_FREELIST_HARDENED /boot/config-* + + For each kernel installed, a line with value "y" should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + Many kernel heap attacks try to target slab cache metadata and other infrastructure. +This options makes minor performance sacrifices to harden the kernel slab allocator against +common freelist exploit methods. + CCE-87963-5 + + + + + + + + + Randomize slab freelist + Randomizes the freelist order used on creating new pages. +This configuration is available from kernel 5.9, but may be available if backported by distros. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_SLAB_FRELIST_RANDOM, run the following command: + grep CONFIG_SLAB_FRELIST_RANDOM /boot/config-* + + For each kernel installed, a line with value "y" should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + This security feature reduces the predictability of the kernel slab allocator against heap overflows. + CCE-87726-6 + + + + + + + + + Disallow merge of slab caches + For reduced kernel memory fragmentation, slab caches can be merged when they share the same +size and other characteristics. This carries a risk of kernel heap overflows being able to +overwrite objects from merged caches (and more easily control cache layout), which makes such +heap attacks easier to exploit by attackers. +This configuration is available from kernel 4.13. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_SLAB_MERGE_DEFAULT, run the following command: + grep CONFIG_SLAB_MERGE_DEFAULT /boot/config-* + + Configs with value 'n' are not explicitly set in the file, so either commented lines or no + lines should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + Disabling the merge of slabs of similar sizes prevents the kernel from +merging a seemingly useless but vulnerable slab with a useful and valuable slab. +This increase the risk that a heap overflow could overwrite objects from merged caches, +with unmerged caches the heap overflow would only affect the objects in the same cache. +Overall, this reduces the kernel attack surface area by isolating slabs from each other. + CCE-88121-9 + + + + + + + + + Enable SLUB debugging support + SLUB has extensive debug support features and this allows the allocator validation checking to +be enabled. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_SLUB_DEBUG, run the following command: + grep CONFIG_SLUB_DEBUG /boot/config-* + + For each kernel installed, a line with value "y" should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + This activates the checking of the memory allocator structures and resets to zero the zones +allocated when they are released. + CCE-88276-1 + + + + + + + + + Stack Protector buffer overlow detection + This feature puts, at the beginning of functions, a canary value on the stack just before the +return address, and validates the value just before actually returning. +This configuration is available from kernel 4.18. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_STACKPROTECTOR, run the following command: + grep CONFIG_STACKPROTECTOR /boot/config-* + + For each kernel installed, a line with value "y" should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + This halts the program when a stack overflow is detected, potentially reducing the impact of +exploits. + CCE-89055-8 + + + + + + + + + Strong Stack Protector + This features adds canary logic protection to more kinds of vulnerable functions than +CONFIG_STACKPROTECTOR, but not to all functions so that performance is not severily impacted. +This configuration is available from kernel 4.18. +This config requires gcc version 4.9 or above, or a distribution gcc with the feature +backported ("-fstack-protector-strong"). + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_STACKPROTECTOR_STRONG, run the following command: + grep CONFIG_STACKPROTECTOR_STRONG /boot/config-* + + For each kernel installed, a line with value "y" should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + This provides a mechanism that protects more vulnerable functions than CONFIG_STACKPROTECTOR, +balancing between security and performance. + CCE-89036-8 + + + + + + + + + Make the kernel text and rodata read-only + When set, kernel text and rodata memory will be made read-only, and non-text memory will be made non-executable. +This configuration is available from kernel 4.11. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_STRICT_KERNEL_RWX, run the following command: + grep CONFIG_STRICT_KERNEL_RWX /boot/config-* + + For each kernel installed, a line with value "y" should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + This provides protection against certain security exploits (e.g. executing the heap or modifying text) + CCE-86993-3 + + + + + + + + + Make the module text and rodata read-only + When set, module text and rodata memory will be made read-only, and non-text memory will be made non-executable. +This configuration is available from kernel 4.11. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_STRICT_MODULE_RWX, run the following command: + grep CONFIG_STRICT_MODULE_RWX /boot/config-* + + For each kernel installed, a line with value "y" should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + This provides protection against certain security exploits (e.g. executing the heap or modifying text) + CCE-89228-1 + + + + + + + + + Enable TCP/IP syncookie support + Normal TCP/IP networking is open to an attack known as SYN flooding. +It is denial-of-service attack that prevents legitimate remote users from being able to connect +to your computer during an ongoing attack. + +When enabled the TCP/IP stack will use a cryptographic challenge protocol known as SYN cookies +to enable legitimate users to continue to connect, even when your machine is under attack. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_SYN_COOKIES, run the following command: + grep CONFIG_SYN_COOKIES /boot/config-* + + For each kernel installed, a line with value "y" should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + SYN cookies provide protection against SYN flooding attacks. + CCE-87331-5 + + + + + + + + + Unmap kernel when running in userspace (aka KAISER) + Speculation attacks against some high-performance processors can be used to bypass MMU +permission checks and leak kernel data to userspace. This can be defended against by unmapping +the kernel when running in userspace, mapping it back in on exception entry via a trampoline +page in the vector table. +This configuration is available from kernel 4.16, but may be available if backported +by distros. +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_UNMAP_KERNEL_AT_EL0, run the following command: + grep CONFIG_UNMAP_KERNEL_AT_EL0 /boot/config-* + + For each kernel installed, a line with value "y" should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + This is a countermeasure to the Meltdown attack. + + CCE-89180-4 + + + + + + + + + User a virtually-mapped stack + Enable this to use virtually-mapped kernel stacks with guard pages. +This configuration is available from kernel 4.9. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_VMAP_STACK, run the following command: + grep CONFIG_VMAP_STACK /boot/config-* + + For each kernel installed, a line with value "y" should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + This causes kernel stack overflows to be caught immediately rather than causing difficult-to-diagnose corruption. + CCE-86252-4 + + + + + + + + + Disable x86 vsyscall emulation + Disabling it is roughly equivalent to booting with vsyscall=none, except that it will also +disable the helpful warning if a program tries to use a vsyscall. With this option set to N, +offending programs will just segfault, citing addresses of the form 0xffffffffff600?00. +This configuration is available from kernel 3.19. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_X86_VSYSCALL_EMULATION, run the following command: + grep CONFIG_X86_VSYSCALL_EMULATION /boot/config-* + + Configs with value 'n' are not explicitly set in the file, so either commented lines or no + lines should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + The vsyscall table is no longer required and is a potential source of ROP gadgets. + CCE-87884-3 + + + + + + + + + Kernel GCC plugin configuration + Contains rules that check the configuration of GCC plugins used by the compiler + + + Generate some entropy during boot and runtime + Instrument some kernel code to extract some entropy from both original and artificially created +program state. This will help especially embedded systems where there is little 'natural' source +of entropy normally. + +This configuration is available from kernel 4.9, but may be available if backported +by distros. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_GCC_PLUGIN_LATENT_ENTROPY, run the following command: + grep CONFIG_GCC_PLUGIN_LATENT_ENTROPY /boot/config-* + + For each kernel installed, a line with value "y" should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + Note that entropy extracted this way is not cryptographically secure! + There is a performance cost during the boot process (about 0.5%) and fork and irq processing. + This helps generate entropy during startup and is particularly relevant for devices with +inappropriate entropy sources. + CCE-87035-2 + + + + + + + + + Randomize layout of sensitive kernel structures + Randomize at compile-time the layouts of structures that are entirely function pointers +(and have not been manually annotated with __no_randomize_layout), or structures that have +been explicitly marked with __randomize_layout. +This configuration is available from kernel 4.13, but may be available if backported +by distros. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_GCC_PLUGIN_RANDSTRUCT, run the following command: + grep CONFIG_GCC_PLUGIN_RANDSTRUCT /boot/config-* + + For each kernel installed, a line with value "y" should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + Randomizing the layout of kernel data structures make it more difficult for an attacker to +know the location of sensitive data. + CCE-87109-5 + + + + + + + + + Poison kernel stack before returning from syscalls + This option makes the kernel erase the kernel stack before returning from system calls. +This has the effect of leaving the stack initialized to the poison value, which both reduces +the lifetime of any sensitive stack contents and reduces potential for uninitialized stack +variable exploits or information exposures (it does not cover functions reaching the same +stack depth as prior functions during the same syscall). + +This configuration is available from kernel 4.20, but may be available if backported +by distros. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_GCC_PLUGIN_STACKLEAK, run the following command: + grep CONFIG_GCC_PLUGIN_STACKLEAK /boot/config-* + + For each kernel installed, a line with value "y" should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + The performance impact on a single CPU system kernel is of 1% slowdown. + This blocks most uninitialized stack variable attacks, with the performance impact being +driven by the depth of the stack usage, rather than the function calling complexity. + CCE-87128-5 + + + + + + + + + Force initialization of variables containing userspace addresses + While the kernel is built with warnings enabled for any missed stack variable initializations, +this warning is silenced for anything passed by reference to another function, under the +occasionally misguided assumption that the function will do the initialization. As this +regularly leads to exploitable flaws, this plugin is available to identify and zero-initialize +such variables, depending on the chosen level of coverage. +This configuration is available from kernel 4.11, but may be available if backported +by distros. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_GCC_PLUGIN_STRUCTLEAK, run the following command: + grep CONFIG_GCC_PLUGIN_STRUCTLEAK /boot/config-* + + For each kernel installed, a line with value "y" should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + Initializing structures from userspace can prevent some classes of information exposure. + CCE-87047-7 + + + + + + + + + zero-init everything passed by reference + Zero-initialize any stack variables that may be passed by reference and had not already been explicitly initialized. +This configuration is available from kernel 4.14, but may be available if backported +by distros. + +The configuration that was used to build kernel is available at /boot/config-*. + To check the configuration value for CONFIG_GCC_PLUGIN_STRUCTLEAK_BYREF_ALL, run the following command: + grep CONFIG_GCC_PLUGIN_STRUCTLEAK_BYREF_ALL /boot/config-* + + For each kernel installed, a line with value "y" should be returned. + + There is no remediation for this besides re-compiling the kernel with the appropriate value for the config. + This eliminates all classes of uninitialized stack variable exploits and information exposures. + CCE-87090-7 + + + + + + + + + + + Configure Syslog + The syslog service has been the default Unix logging mechanism for +many years. It has a number of downsides, including inconsistent log format, +lack of authentication for received messages, and lack of authentication, +encryption, or reliable transport for messages sent over a network. However, +due to its long history, syslog is a de facto standard which is supported by +almost all Unix applications. + + +In Red Hat Enterprise Linux 9, rsyslog has replaced ksyslogd as the +syslog daemon of choice, and it includes some additional security features +such as reliable, connection-oriented (i.e. TCP) transmission of logs, the +option to log to database formats, and the encryption of log data en route to +a central logging server. +This section discusses how to configure rsyslog for +best effect, and how to use tools provided with the system to maintain and +monitor logs. + + + Ensure rsyslog-gnutls is installed + TLS protocol support for rsyslog is installed. +The rsyslog-gnutls package can be installed with the following command: + +$ sudo dnf install rsyslog-gnutls + BP28(R43) + CCI-000366 + FTP_ITC_EXT.1.1 + SRG-OS-000480-GPOS-00227 + SRG-OS-000120-GPOS-00061 + The rsyslog-gnutls package provides Transport Layer Security (TLS) support +for the rsyslog daemon, which enables secure remote logging. + CCE-83987-8 + +package --add=rsyslog-gnutls + + include install_rsyslog-gnutls + +class install_rsyslog-gnutls { + package { 'rsyslog-gnutls': + ensure => 'installed', + } +} + + - name: Ensure rsyslog-gnutls is installed + package: + name: rsyslog-gnutls + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83987-8 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_rsyslog-gnutls_installed + + +[[packages]] +name = "rsyslog-gnutls" +version = "*" + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if ! rpm -q --quiet "rsyslog-gnutls" ; then + dnf install -y "rsyslog-gnutls" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure rsyslog is Installed + Rsyslog is installed by default. The rsyslog package can be installed with the following command: $ sudo dnf install rsyslog + BP28(R5) + NT28(R46) + 1 + 14 + 15 + 16 + 3 + 5 + 6 + APO11.04 + BAI03.05 + DSS05.04 + DSS05.07 + MEA02.01 + CCI-001311 + CCI-001312 + CCI-000366 + 164.312(a)(2)(ii) + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + CM-6(a) + PR.PT-1 + FTP_ITC_EXT.1.1 + SRG-OS-000479-GPOS-00224 + SRG-OS-000051-GPOS-00024 + SRG-OS-000480-GPOS-00227 + The rsyslog package provides the rsyslog daemon, which provides +system logging services. + CCE-84063-7 + +package --add=rsyslog + + include install_rsyslog + +class install_rsyslog { + package { 'rsyslog': + ensure => 'installed', + } +} + + - name: Ensure rsyslog is installed + package: + name: rsyslog + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84063-7 + - NIST-800-53-CM-6(a) + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_rsyslog_installed + + +[[packages]] +name = "rsyslog" +version = "*" + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if ! rpm -q --quiet "rsyslog" ; then + dnf install -y "rsyslog" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Enable rsyslog Service + The rsyslog service provides syslog-style logging by default on Red Hat Enterprise Linux 9. + +The rsyslog service can be enabled with the following command: +$ sudo systemctl enable rsyslog.service + BP28(R5) + NT28(R46) + 1 + 12 + 13 + 14 + 15 + 16 + 2 + 3 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO13.01 + BAI03.05 + BAI04.04 + DSS01.03 + DSS03.05 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + CCI-001311 + CCI-001312 + CCI-001557 + CCI-001851 + CCI-000366 + 164.312(a)(2)(ii) + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.2 + A.12.1.3 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.17.2.1 + CM-6(a) + AU-4(1) + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.DS-4 + PR.PT-1 + SRG-OS-000480-GPOS-00227 + The rsyslog service must be running in order to provide +logging services, which are essential to system administration. + CCE-83989-4 + include enable_rsyslog + +class enable_rsyslog { + service {'rsyslog': + enable => true, + ensure => 'running', + } +} + + - name: Enable service rsyslog + block: + + - name: Gather the package facts + package_facts: + manager: auto + + - name: Enable service rsyslog + service: + name: rsyslog + enabled: 'yes' + state: started + masked: 'no' + when: + - '"rsyslog" in ansible_facts.packages' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83989-4 + - NIST-800-53-AU-4(1) + - NIST-800-53-CM-6(a) + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_rsyslog_enabled + + +[customizations.services] +enabled = ["rsyslog"] + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" unmask 'rsyslog.service' +"$SYSTEMCTL_EXEC" start 'rsyslog.service' +"$SYSTEMCTL_EXEC" enable 'rsyslog.service' + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure Logwatch on the Central Log Server + Is this system the central log server? If so, edit the file /etc/logwatch/conf/logwatch.conf as shown below. + + + Ensure Proper Configuration of Log Files + The file /etc/rsyslog.conf controls where log message are written. +These are controlled by lines called rules, which consist of a +selector and an action. +These rules are often customized depending on the role of the system, the +requirements of the environment, and whatever may enable +the administrator to most effectively make use of log data. +The default rules in Red Hat Enterprise Linux 9 are: +*.info;mail.none;authpriv.none;cron.none /var/log/messages +authpriv.* /var/log/secure +mail.* -/var/log/maillog +cron.* /var/log/cron +*.emerg * +uucp,news.crit /var/log/spooler +local7.* /var/log/boot.log +See the man page rsyslog.conf(5) for more information. +Note that the rsyslog daemon can be configured to use a timestamp format that +some log processing programs may not understand. If this occurs, +edit the file /etc/rsyslog.conf and add or edit the following line: +$ ActionFileDefaultTemplate RSYSLOG_TraditionalFileFormat + + group who owns log files + Specify group owner of all logfiles specified in +/etc/rsyslog.conf. + root + adm + root + + + User who owns log files + Specify user owner of all logfiles specified in +/etc/rsyslog.conf. + root + adm + root + syslog + + + Ensure cron Is Logging To Rsyslog + Cron logging must be implemented to spot intrusions or trace +cron job status. If cron is not logging to rsyslog, it +can be implemented by adding the following to the RULES section of +/etc/rsyslog.conf: +cron.* /var/log/cron + 1 + 14 + 15 + 16 + 3 + 5 + 6 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + BAI03.05 + DSS05.04 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + CCI-000366 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 6.1 + 0988 + 1405 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.15.2.1 + A.15.2.2 + CM-6(a) + ID.SC-4 + PR.PT-1 + FAU_GEN.1.1.c + SRG-OS-000480-GPOS-00227 + Cron logging can be used to trace the successful or unsuccessful execution +of cron jobs. It can also be used to spot intrusions into the use of the cron +facility by unauthorized and malicious users. + CCE-83994-4 + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if ! grep -s "^\s*cron\.\*\s*/var/log/cron$" /etc/rsyslog.conf /etc/rsyslog.d/*.conf; then + mkdir -p /etc/rsyslog.d + echo "cron.* /var/log/cron" >> /etc/rsyslog.d/cron.conf +fi + +systemctl restart rsyslog.service + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure Rsyslog Authenticates Off-Loaded Audit Records + Rsyslogd is a system utility providing support for message logging. Support +for both internet and UNIX domain sockets enables this utility to support both local +and remote logging. Couple this utility with gnutls (which is a secure communications +library implementing the SSL, TLS and DTLS protocols), and you have a method to securely +encrypt and off-load auditing. + +When using rsyslogd to off-load logs the remote system must be authenticated. + CCI-001851 + AU-4(1) + SRG-OS-000342-GPOS-00133 + SRG-OS-000479-GPOS-00224 + The audit records generated by Rsyslog contain valuable information regarding system +configuration, user authentication, and other such information. Audit records should be +protected from unauthorized access. + CCE-86871-1 + - name: Ensure Rsyslog Authenticates Off-Loaded Audit Records + block: + + - name: Deduplicate values from /etc/rsyslog.conf + lineinfile: + path: /etc/rsyslog.conf + create: false + regexp: ^\s*{{ "$ActionSendStreamDriverAuthMode"| regex_escape }}\s + state: absent + + - name: Check if /etc/rsyslog.d exists + stat: + path: /etc/rsyslog.d + register: _etc_rsyslog_d_exists + + - name: Check if the parameter $ActionSendStreamDriverAuthMode is present in /etc/rsyslog.d + find: + paths: /etc/rsyslog.d + recurse: 'yes' + follow: 'no' + contains: ^\s*{{ "$ActionSendStreamDriverAuthMode"| regex_escape }}\s + register: _etc_rsyslog_d_has_parameter + when: _etc_rsyslog_d_exists.stat.isdir is defined and _etc_rsyslog_d_exists.stat.isdir + + - name: Remove parameter from files in /etc/rsyslog.d + lineinfile: + path: '{{ item.path }}' + create: false + regexp: ^\s*{{ "$ActionSendStreamDriverAuthMode"| regex_escape }}\s + state: absent + with_items: '{{ _etc_rsyslog_d_has_parameter.files }}' + when: _etc_rsyslog_d_has_parameter.matched + + - name: Insert correct line to /etc/rsyslog.conf + lineinfile: + path: /etc/rsyslog.conf + create: true + regexp: ^\s*{{ "$ActionSendStreamDriverAuthMode"| regex_escape }}\s + line: $ActionSendStreamDriverAuthMode x509/name + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86871-1 + - NIST-800-53-AU-4(1) + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - rsyslog_encrypt_offload_actionsendstreamdriverauthmode + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +sed -i '/^.*\$ActionSendStreamDriverAuthMode.*/d' /etc/rsyslog.conf /etc/rsyslog.d/*.conf 2> /dev/null + +if [ -e "/etc/rsyslog.d/stream_driver_auth.conf" ] ; then + + LC_ALL=C sed -i "/^\s*\$ActionSendStreamDriverAuthMode /Id" "/etc/rsyslog.d/stream_driver_auth.conf" +else + touch "/etc/rsyslog.d/stream_driver_auth.conf" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/rsyslog.d/stream_driver_auth.conf" + +cp "/etc/rsyslog.d/stream_driver_auth.conf" "/etc/rsyslog.d/stream_driver_auth.conf.bak" +# Insert at the end of the file +printf '%s\n' "\$ActionSendStreamDriverAuthMode x509/name" >> "/etc/rsyslog.d/stream_driver_auth.conf" +# Clean up after ourselves. +rm "/etc/rsyslog.d/stream_driver_auth.conf.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure Rsyslog Encrypts Off-Loaded Audit Records + Rsyslogd is a system utility providing support for message logging. Support +for both internet and UNIX domain sockets enables this utility to support both local +and remote logging. Couple this utility with gnutls (which is a secure communications +library implementing the SSL, TLS and DTLS protocols), and you have a method to securely +encrypt and off-load auditing. + +When using rsyslogd to off-load logs off a encrpytion system must be used. + CCI-001851 + AU-4(1) + SRG-OS-000342-GPOS-00133 + SRG-OS-000479-GPOS-00224 + The audit records generated by Rsyslog contain valuable information regarding system +configuration, user authentication, and other such information. Audit records should be +protected from unauthorized access. + CCE-90191-8 + - name: Ensure Rsyslog Encrypts Off-Loaded Audit Records + block: + + - name: Deduplicate values from /etc/rsyslog.conf + lineinfile: + path: /etc/rsyslog.conf + create: false + regexp: '^\s*{{ "$ActionSendStreamDriverMode"| regex_escape }} ' + state: absent + + - name: Check if /etc/rsyslog.d exists + stat: + path: /etc/rsyslog.d + register: _etc_rsyslog_d_exists + + - name: Check if the parameter $ActionSendStreamDriverMode is present in /etc/rsyslog.d + find: + paths: /etc/rsyslog.d + recurse: 'yes' + follow: 'no' + contains: '^\s*{{ "$ActionSendStreamDriverMode"| regex_escape }} ' + register: _etc_rsyslog_d_has_parameter + when: _etc_rsyslog_d_exists.stat.isdir is defined and _etc_rsyslog_d_exists.stat.isdir + + - name: Remove parameter from files in /etc/rsyslog.d + lineinfile: + path: '{{ item.path }}' + create: false + regexp: '^\s*{{ "$ActionSendStreamDriverMode"| regex_escape }} ' + state: absent + with_items: '{{ _etc_rsyslog_d_has_parameter.files }}' + when: _etc_rsyslog_d_has_parameter.matched + + - name: Insert correct line to /etc/rsyslog.conf + lineinfile: + path: /etc/rsyslog.conf + create: true + regexp: '^\s*{{ "$ActionSendStreamDriverMode"| regex_escape }} ' + line: $ActionSendStreamDriverMode 1 + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-90191-8 + - NIST-800-53-AU-4(1) + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - rsyslog_encrypt_offload_actionsendstreamdrivermode + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if [ -e "/etc/rsyslog.d/encrypt.conf" ] ; then + + LC_ALL=C sed -i "/^\s*\$ActionSendStreamDriverMode /Id" "/etc/rsyslog.d/encrypt.conf" +else + touch "/etc/rsyslog.d/encrypt.conf" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/rsyslog.d/encrypt.conf" + +cp "/etc/rsyslog.d/encrypt.conf" "/etc/rsyslog.d/encrypt.conf.bak" +# Insert at the end of the file +printf '%s\n' "\$ActionSendStreamDriverMode 1" >> "/etc/rsyslog.d/encrypt.conf" +# Clean up after ourselves. +rm "/etc/rsyslog.d/encrypt.conf.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure Rsyslog Encrypts Off-Loaded Audit Records + Rsyslogd is a system utility providing support for message logging. Support +for both internet and UNIX domain sockets enables this utility to support both local +and remote logging. Couple this utility with gnutls (which is a secure communications +library implementing the SSL, TLS and DTLS protocols), and you have a method to securely +encrypt and off-load auditing. + +When using rsyslogd to off-load logs off an encryption system must be used. + CCI-001851 + AU-4(1) + SRG-OS-000342-GPOS-00133 + SRG-OS-000479-GPOS-00224 + The audit records generated by Rsyslog contain valuable information regarding system +configuration, user authentication, and other such information. Audit records should be +protected from unauthorized access. + CCE-86782-0 + - name: Ensure Rsyslog Encrypts Off-Loaded Audit Records + block: + + - name: Deduplicate values from /etc/rsyslog.conf + lineinfile: + path: /etc/rsyslog.conf + create: false + regexp: '^\s*{{ "$DefaultNetstreamDriver"| regex_escape }} ' + state: absent + + - name: Check if /etc/rsyslog.d exists + stat: + path: /etc/rsyslog.d + register: _etc_rsyslog_d_exists + + - name: Check if the parameter $DefaultNetstreamDriver is present in /etc/rsyslog.d + find: + paths: /etc/rsyslog.d + recurse: 'yes' + follow: 'no' + contains: '^\s*{{ "$DefaultNetstreamDriver"| regex_escape }} ' + register: _etc_rsyslog_d_has_parameter + when: _etc_rsyslog_d_exists.stat.isdir is defined and _etc_rsyslog_d_exists.stat.isdir + + - name: Remove parameter from files in /etc/rsyslog.d + lineinfile: + path: '{{ item.path }}' + create: false + regexp: '^\s*{{ "$DefaultNetstreamDriver"| regex_escape }} ' + state: absent + with_items: '{{ _etc_rsyslog_d_has_parameter.files }}' + when: _etc_rsyslog_d_has_parameter.matched + + - name: Insert correct line to /etc/rsyslog.conf + lineinfile: + path: /etc/rsyslog.conf + create: true + regexp: '^\s*{{ "$DefaultNetstreamDriver"| regex_escape }} ' + line: $DefaultNetstreamDriver gtls + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86782-0 + - NIST-800-53-AU-4(1) + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - rsyslog_encrypt_offload_defaultnetstreamdriver + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if [ -e "/etc/rsyslog.d/encrypt.conf" ] ; then + + LC_ALL=C sed -i "/^\s*\$DefaultNetstreamDriver /Id" "/etc/rsyslog.d/encrypt.conf" +else + touch "/etc/rsyslog.d/encrypt.conf" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/rsyslog.d/encrypt.conf" + +cp "/etc/rsyslog.d/encrypt.conf" "/etc/rsyslog.d/encrypt.conf.bak" +# Insert at the end of the file +printf '%s\n' "\$DefaultNetstreamDriver gtls" >> "/etc/rsyslog.d/encrypt.conf" +# Clean up after ourselves. +rm "/etc/rsyslog.d/encrypt.conf.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure Log Files Are Owned By Appropriate Group + The group-owner of all log files written by +rsyslog should be . +These log files are determined by the second part of each Rule line in +/etc/rsyslog.conf and typically all appear in /var/log. +For each log file LOGFILE referenced in /etc/rsyslog.conf, +run the following command to inspect the file's group owner: +$ ls -l LOGFILE +If the owner is not , run the following command to +correct this: +$ sudo chgrp LOGFILE + BP28(R46) + BP28(R5) + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + CCI-001314 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + 0988 + 1405 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + Req-10.5.1 + Req-10.5.2 + The log files generated by rsyslog contain valuable information regarding system +configuration, user authentication, and other such information. Log files should be +protected from unauthorized access. + CCE-83834-2 + + + + + + + + + Ensure Log Files Are Owned By Appropriate User + The owner of all log files written by +rsyslog should be . +These log files are determined by the second part of each Rule line in +/etc/rsyslog.conf and typically all appear in /var/log. +For each log file LOGFILE referenced in /etc/rsyslog.conf, +run the following command to inspect the file's owner: +$ ls -l LOGFILE +If the owner is not , run the following command to +correct this: +$ sudo chown LOGFILE + BP28(R46) + BP28(R5) + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + CCI-001314 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + 0988 + 1405 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + Req-10.5.1 + Req-10.5.2 + The log files generated by rsyslog contain valuable information regarding system +configuration, user authentication, and other such information. Log files should be +protected from unauthorized access. + CCE-83946-4 + + + + + + + + + Ensure System Log Files Have Correct Permissions + The file permissions for all log files written by rsyslog should +be set to 600, or more restrictive. These log files are determined by the +second part of each Rule line in /etc/rsyslog.conf and typically +all appear in /var/log. For each log file LOGFILE +referenced in /etc/rsyslog.conf, run the following command to +inspect the file's permissions: +$ ls -l LOGFILE +If the permissions are not 600 or more restrictive, run the following +command to correct this: +$ sudo chmod 0600 LOGFILE" + BP28(R36) + CCI-001314 + 0988 + 1405 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-6(a) + AC-6(1) + Req-10.5.1 + Req-10.5.2 + Log files can contain valuable information regarding system +configuration. If the system log files are not protected unauthorized +users could change the logged data, eliminating their forensic value. + CCE-83689-0 + - name: Set rsyslog logfile configuration facts + set_fact: + rsyslog_etc_config: /etc/rsyslog.conf + desired_perm_mode: '600' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83689-0 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.1 + - PCI-DSS-Req-10.5.2 + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - rsyslog_files_permissions + +- name: Get IncludeConfig directive + shell: | + set -o pipefail + grep -e '$IncludeConfig' {{ rsyslog_etc_config }} | cut -d ' ' -f 2 || true + register: rsyslog_old_inc + changed_when: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83689-0 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.1 + - PCI-DSS-Req-10.5.2 + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - rsyslog_files_permissions + +- name: Get include files directives + shell: | + set -o pipefail + grep -oP '^\s*include\s*\(\s*file.*' {{ rsyslog_etc_config }} |cut -d"\"" -f 2 || true + register: rsyslog_new_inc + changed_when: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83689-0 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.1 + - PCI-DSS-Req-10.5.2 + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - rsyslog_files_permissions + +- name: Expand glob expressions + shell: | + set -o pipefail + eval printf '%s\\n' {{ item }} + register: include_config_output + loop: '{{ rsyslog_old_inc.stdout_lines + rsyslog_new_inc.stdout_lines }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83689-0 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.1 + - PCI-DSS-Req-10.5.2 + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - rsyslog_files_permissions + +- name: List all config files + shell: find {{ item }} -not -path "*/.*" -type f + loop: '{{ include_config_output.results|map(attribute=''stdout_lines'')|list|flatten + }}' + register: rsyslog_config_files + failed_when: false + changed_when: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83689-0 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.1 + - PCI-DSS-Req-10.5.2 + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - rsyslog_files_permissions + +- name: Extract log files + shell: | + set -o pipefail + grep -oP '^[^(\s|#|\$)]+[\s]+.*[\s]+-?(/+[^:;\s]+);*\.*$' {{ item }} |awk '{print $NF}'|sed -e 's/^-//' || true + loop: '{{ rsyslog_config_files.results|map(attribute=''stdout_lines'')|list|flatten|unique + + [ rsyslog_etc_config ] }}' + register: log_files + changed_when: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83689-0 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.1 + - PCI-DSS-Req-10.5.2 + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - rsyslog_files_permissions + +- name: Setup log files permissions + ignore_errors: true + file: + path: '{{ item }}' + mode: '{{ desired_perm_mode }}' + loop: '{{ log_files.results|map(attribute=''stdout_lines'')|list|flatten|unique + }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83689-0 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.5.1 + - PCI-DSS-Req-10.5.2 + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - rsyslog_files_permissions + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# List of log file paths to be inspected for correct permissions +# * Primarily inspect log file paths listed in /etc/rsyslog.conf +RSYSLOG_ETC_CONFIG="/etc/rsyslog.conf" +# * And also the log file paths listed after rsyslog's $IncludeConfig directive +# (store the result into array for the case there's shell glob used as value of IncludeConfig) +readarray -t OLD_INC < <(grep -e "\$IncludeConfig[[:space:]]\+[^[:space:];]\+" /etc/rsyslog.conf | cut -d ' ' -f 2) +readarray -t RSYSLOG_INCLUDE_CONFIG < <(for INCPATH in "${OLD_INC[@]}"; do eval printf '%s\\n' "${INCPATH}"; done) +readarray -t NEW_INC < <(awk '/)/{f=0} /include\(/{f=1} f{nf=gensub("^(include\\(|\\s*)file=\"(\\S+)\".*","\\2",1); if($0!=nf){print nf}}' /etc/rsyslog.conf) +readarray -t RSYSLOG_INCLUDE < <(for INCPATH in "${NEW_INC[@]}"; do eval printf '%s\\n' "${INCPATH}"; done) + +# Declare an array to hold the final list of different log file paths +declare -a LOG_FILE_PATHS + +# Array to hold all rsyslog config entries +RSYSLOG_CONFIGS=() +RSYSLOG_CONFIGS=("${RSYSLOG_ETC_CONFIG}" "${RSYSLOG_INCLUDE_CONFIG[@]}" "${RSYSLOG_INCLUDE[@]}") + +# Get full list of files to be checked +# RSYSLOG_CONFIGS may contain globs such as +# /etc/rsyslog.d/*.conf /etc/rsyslog.d/*.frule +# So, loop over the entries in RSYSLOG_CONFIGS and use find to get the list of included files. +RSYSLOG_CONFIG_FILES=() +for ENTRY in "${RSYSLOG_CONFIGS[@]}" +do + # If directory, rsyslog will search for config files in recursively. + # However, files in hidden sub-directories or hidden files will be ignored. + if [ -d "${ENTRY}" ] + then + readarray -t FINDOUT < <(find "${ENTRY}" -not -path '*/.*' -type f) + RSYSLOG_CONFIG_FILES+=("${FINDOUT[@]}") + elif [ -f "${ENTRY}" ] + then + RSYSLOG_CONFIG_FILES+=("${ENTRY}") + else + echo "Invalid include object: ${ENTRY}" + fi +done + +# Browse each file selected above as containing paths of log files +# ('/etc/rsyslog.conf' and '/etc/rsyslog.d/*.conf' in the default configuration) +for LOG_FILE in "${RSYSLOG_CONFIG_FILES[@]}" +do + # From each of these files extract just particular log file path(s), thus: + # * Ignore lines starting with space (' '), comment ('#"), or variable syntax ('$') characters, + # * Ignore empty lines, + # * Strip quotes and closing brackets from paths. + # * Ignore paths that match /dev|/etc.*\.conf, as those are paths, but likely not log files + # * From the remaining valid rows select only fields constituting a log file path + # Text file column is understood to represent a log file path if and only if all of the following are met: + # * it contains at least one slash '/' character, + # * it is preceded by space + # * it doesn't contain space (' '), colon (':'), and semicolon (';') characters + # Search log file for path(s) only in case it exists! + if [[ -f "${LOG_FILE}" ]] + then + NORMALIZED_CONFIG_FILE_LINES=$(sed -e "/^[#|$]/d" "${LOG_FILE}") + LINES_WITH_PATHS=$(grep '[^/]*\s\+\S*/\S\+$' <<< "${NORMALIZED_CONFIG_FILE_LINES}") + FILTERED_PATHS=$(awk '{if(NF>=2&&($2~/^\//||$2~/^-\//)){sub(/^-\//,"/",$2);print $2}}' <<< "${LINES_WITH_PATHS}") + CLEANED_PATHS=$(sed -e "s/[\"')]//g; /\\/etc.*\.conf/d; /\\/dev\\//d" <<< "${FILTERED_PATHS}") + MATCHED_ITEMS=$(sed -e "/^$/d" <<< "${CLEANED_PATHS}") + # Since above sed command might return more than one item (delimited by newline), split the particular + # matches entries into new array specific for this log file + readarray -t ARRAY_FOR_LOG_FILE <<< "$MATCHED_ITEMS" + # Concatenate the two arrays - previous content of $LOG_FILE_PATHS array with + # items from newly created array for this log file + LOG_FILE_PATHS+=("${ARRAY_FOR_LOG_FILE[@]}") + # Delete the temporary array + unset ARRAY_FOR_LOG_FILE + fi +done + +DESIRED_PERM_MOD=600 + +# Correct the form o +for LOG_FILE_PATH in "${LOG_FILE_PATHS[@]}" +do + # Sanity check - if particular $LOG_FILE_PATH is empty string, skip it from further processing + if [ -z "$LOG_FILE_PATH" ] + then + continue + fi + + # Also for each log file check if its permissions differ from 600. If so, correct them + if [ -f "$LOG_FILE_PATH" ] && [ "$(/usr/bin/stat -c %a "$LOG_FILE_PATH")" -ne $DESIRED_PERM_MOD ] + then + /bin/chmod $DESIRED_PERM_MOD "$LOG_FILE_PATH" + fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure remote access methods are monitored in Rsyslog + Logging of remote access methods must be implemented to help identify cyber +attacks and ensure ongoing compliance with remote access policies are being +audited and upheld. An examples of a remote access method is the use of the +Remote Desktop Protocol (RDP) from an external, non-organization controlled +network. The /etc/rsyslog.conf or +/etc/rsyslog.d/*.conf file should contain a match for the following +selectors: auth.*, authpriv.*, and daemon.*. If +not, use the following as an example configuration: +auth.*;authpriv.*;daemon.* /var/log/secure + CCI-000067 + AC-17(1) + SRG-OS-000032-GPOS-00013 + Logging remote access methods can be used to trace the decrease the risks +associated with remote user access management. It can also be used to spot +cyber attacks and ensure ongoing compliance with organizational policies +surrounding the use of remote access methods. + CCE-87960-1 + - name: 'Ensure remote access methods are monitored in Rsyslog: Set facts' + set_fact: + conf_files: + - /etc/rsyslog.conf + remote_methods: + - selector: auth.* + regexp: ^.*auth\.\*.*$ + - selector: authpriv.* + regexp: ^.*authpriv\.\*.*$ + - selector: daemon.* + regexp: ^.*daemon\.\*.*$ + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-87960-1 + - NIST-800-53-AC-17(1) + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - rsyslog_remote_access_monitoring + +- name: 'Ensure remote access methods are monitored in Rsyslog: Ensure rsyslog.conf + exists' + file: + path: '{{ conf_files.0 }}' + state: touch + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-87960-1 + - NIST-800-53-AC-17(1) + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - rsyslog_remote_access_monitoring + +- name: 'Ensure remote access methods are monitored in Rsyslog: Gather conf.d files' + find: + patterns: + - '*.conf' + paths: + - /etc/rsyslog.d + register: rsyslogd + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-87960-1 + - NIST-800-53-AC-17(1) + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - rsyslog_remote_access_monitoring + +- name: 'Ensure remote access methods are monitored in Rsyslog: Set conf file(s)' + set_fact: + conf_files: '{{ conf_files + [item.path] }}' + loop: '{{ rsyslogd.files }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - rsyslogd.matched > 0 + tags: + - CCE-87960-1 + - NIST-800-53-AC-17(1) + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - rsyslog_remote_access_monitoring + +- name: 'Ensure remote access methods are monitored in Rsyslog: Check for existing + values' + lineinfile: + path: '{{ item.1 }}' + regexp: '{{ item.0.regexp }}' + state: absent + check_mode: true + changed_when: false + register: remote_method_values + loop: '{{ remote_methods|product(conf_files)|list }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-87960-1 + - NIST-800-53-AC-17(1) + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - rsyslog_remote_access_monitoring + +- name: 'Ensure remote access methods are monitored in Rsyslog: Configure' + lineinfile: + path: /etc/rsyslog.conf + line: '{{ item.item.0.selector }} /var/log/secure' + insertafter: ^.*\/var\/log\/secure.*$ + create: true + loop: '{{ remote_method_values.results }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - item.found == 0 + tags: + - CCE-87960-1 + - NIST-800-53-AC-17(1) + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - rsyslog_remote_access_monitoring + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +declare -A REMOTE_METHODS=( ['auth.*']='^.*auth\.\*.*$' ['authpriv.*']='^.*authpriv\.\*.*$' ['daemon.*']='^.*daemon\.\*.*$' ) + +if [[ ! -f /etc/rsyslog.conf ]]; then + # Something is not right, create the file + touch /etc/rsyslog.conf +fi + +APPEND_LINE=$(sed -rn '/^\S+\s+\/var\/log\/secure$/p' /etc/rsyslog.conf) + +# Loop through the remote methods associative array +for K in "${!REMOTE_METHODS[@]}" +do + # Check to see if selector/value exists + if ! grep -rq "${REMOTE_METHODS[$K]}" /etc/rsyslog.*; then + # Make sure we have a line to insert after, otherwise append to end + if [[ ! -z ${APPEND_LINE} ]]; then + # Add selector to file + sed -r -i "0,/^(\S+\s+\/var\/log\/secure$)/s//\1\n${K} \/var\/log\/secure/" /etc/rsyslog.conf + else + echo "${K} /var/log/secure" >> /etc/rsyslog.conf + fi + fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + systemd-journald + systemd-journald is a system service that collects and stores +logging data. It creates and maintains structured, indexed +journals based on logging information that is received from a +variety of sources. + +For more information on systemd-journald and additional systemd-journald configuration options, see +https://systemd.io/. + + Enable systemd-journald Service + The systemd-journald service is an essential component of +systemd. + +The systemd-journald service can be enabled with the following command: +$ sudo systemctl enable systemd-journald.service + CCI-001665 + SC-24 + SRG-OS-000269-GPOS-00103 + In the event of a system failure, Red Hat Enterprise Linux 9 must preserve any information necessary to determine cause of failure and any information necessary to return to operations with least disruption to system processes. + + CCE-85941-3 + include enable_systemd-journald + +class enable_systemd-journald { + service {'systemd-journald': + enable => true, + ensure => 'running', + } +} + + - name: Enable service systemd-journald + block: + + - name: Gather the package facts + package_facts: + manager: auto + + - name: Enable service systemd-journald + service: + name: systemd-journald + enabled: 'yes' + state: started + masked: 'no' + when: + - '"systemd" in ansible_facts.packages' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-85941-3 + - NIST-800-53-SC-24 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_systemd-journald_enabled + + +[customizations.services] +enabled = ["systemd-journald"] + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" unmask 'systemd-journald.service' +"$SYSTEMCTL_EXEC" start 'systemd-journald.service' +"$SYSTEMCTL_EXEC" enable 'systemd-journald.service' + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure journald is configured to compress large log files + The journald system can compress large log files to avoid fill the system disk. + Log files that are not properly compressed run the risk of growing so large that they fill up the log partition. Valuable logging information could be lost if the log partition becomes full. + CCE-85931-4 + - name: Setting shell-quoted shell-style assignment of 'Compress' to 'yes' in '/etc/systemd/journald.conf' + block: + + - name: Check for duplicate values + lineinfile: + path: /etc/systemd/journald.conf + create: false + regexp: ^\s*Compress= + state: absent + check_mode: true + changed_when: false + register: dupes + + - name: Deduplicate values from /etc/systemd/journald.conf + lineinfile: + path: /etc/systemd/journald.conf + create: false + regexp: ^\s*Compress= + state: absent + when: dupes.found is defined and dupes.found > 1 + + - name: Insert correct line to /etc/systemd/journald.conf + lineinfile: + path: /etc/systemd/journald.conf + create: true + regexp: ^\s*Compress= + line: Compress="yes" + state: present + insertbefore: ^# Compress + validate: /usr/bin/bash -n %s + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-85931-4 + - journald_compress + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if [ -e "/etc/systemd/journald.conf" ] ; then + + LC_ALL=C sed -i "/^\s*Compress=/d" "/etc/systemd/journald.conf" +else + touch "/etc/systemd/journald.conf" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/systemd/journald.conf" + +cp "/etc/systemd/journald.conf" "/etc/systemd/journald.conf.bak" +# Insert before the line matching the regex '^#\s*Compress'. +line_number="$(LC_ALL=C grep -n "^#\s*Compress" "/etc/systemd/journald.conf.bak" | LC_ALL=C sed 's/:.*//g')" +if [ -z "$line_number" ]; then + # There was no match of '^#\s*Compress', insert at + # the end of the file. + printf '%s\n' "Compress='yes'" >> "/etc/systemd/journald.conf" +else + head -n "$(( line_number - 1 ))" "/etc/systemd/journald.conf.bak" > "/etc/systemd/journald.conf" + printf '%s\n' "Compress='yes'" >> "/etc/systemd/journald.conf" + tail -n "+$(( line_number ))" "/etc/systemd/journald.conf.bak" >> "/etc/systemd/journald.conf" +fi +# Clean up after ourselves. +rm "/etc/systemd/journald.conf.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure journald is configured to send logs to rsyslog + Data from journald may be stored in volatile memory or persisted locally. +Utilities exist to accept remote export of journald logs. + Storing log data on a remote host protects log integrity from local attacks. If an attacker gains root access on the local system, they could tamper with or remove log data that is stored on the local system. + CCE-85996-7 + - name: Setting shell-quoted shell-style assignment of 'ForwardToSyslog' to 'yes' + in '/etc/systemd/journald.conf' + block: + + - name: Check for duplicate values + lineinfile: + path: /etc/systemd/journald.conf + create: false + regexp: ^\s*ForwardToSyslog= + state: absent + check_mode: true + changed_when: false + register: dupes + + - name: Deduplicate values from /etc/systemd/journald.conf + lineinfile: + path: /etc/systemd/journald.conf + create: false + regexp: ^\s*ForwardToSyslog= + state: absent + when: dupes.found is defined and dupes.found > 1 + + - name: Insert correct line to /etc/systemd/journald.conf + lineinfile: + path: /etc/systemd/journald.conf + create: true + regexp: ^\s*ForwardToSyslog= + line: ForwardToSyslog="yes" + state: present + insertbefore: ^# ForwardToSyslog + validate: /usr/bin/bash -n %s + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-85996-7 + - journald_forward_to_syslog + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if [ -e "/etc/systemd/journald.conf" ] ; then + + LC_ALL=C sed -i "/^\s*ForwardToSyslog=/d" "/etc/systemd/journald.conf" +else + touch "/etc/systemd/journald.conf" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/systemd/journald.conf" + +cp "/etc/systemd/journald.conf" "/etc/systemd/journald.conf.bak" +# Insert before the line matching the regex '^#\s*ForwardToSyslog'. +line_number="$(LC_ALL=C grep -n "^#\s*ForwardToSyslog" "/etc/systemd/journald.conf.bak" | LC_ALL=C sed 's/:.*//g')" +if [ -z "$line_number" ]; then + # There was no match of '^#\s*ForwardToSyslog', insert at + # the end of the file. + printf '%s\n' "ForwardToSyslog='yes'" >> "/etc/systemd/journald.conf" +else + head -n "$(( line_number - 1 ))" "/etc/systemd/journald.conf.bak" > "/etc/systemd/journald.conf" + printf '%s\n' "ForwardToSyslog='yes'" >> "/etc/systemd/journald.conf" + tail -n "+$(( line_number ))" "/etc/systemd/journald.conf.bak" >> "/etc/systemd/journald.conf" +fi +# Clean up after ourselves. +rm "/etc/systemd/journald.conf.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure journald is configured to write log files to persistent disk + The journald system may store log files in volatile memory or locally on disk. +If the logs are only stored in volatile memory they will we lost upon reboot. + Log files contain valuable data and need to be persistent to aid in possible investigations. + CCE-86046-0 + - name: Setting shell-quoted shell-style assignment of 'Storage' to 'persistent' in + '/etc/systemd/journald.conf' + block: + + - name: Check for duplicate values + lineinfile: + path: /etc/systemd/journald.conf + create: false + regexp: ^\s*Storage= + state: absent + check_mode: true + changed_when: false + register: dupes + + - name: Deduplicate values from /etc/systemd/journald.conf + lineinfile: + path: /etc/systemd/journald.conf + create: false + regexp: ^\s*Storage= + state: absent + when: dupes.found is defined and dupes.found > 1 + + - name: Insert correct line to /etc/systemd/journald.conf + lineinfile: + path: /etc/systemd/journald.conf + create: true + regexp: ^\s*Storage= + line: Storage="persistent" + state: present + insertbefore: ^# Storage + validate: /usr/bin/bash -n %s + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86046-0 + - journald_storage + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if [ -e "/etc/systemd/journald.conf" ] ; then + + LC_ALL=C sed -i "/^\s*Storage=/d" "/etc/systemd/journald.conf" +else + touch "/etc/systemd/journald.conf" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/systemd/journald.conf" + +cp "/etc/systemd/journald.conf" "/etc/systemd/journald.conf.bak" +# Insert before the line matching the regex '^#\s*Storage'. +line_number="$(LC_ALL=C grep -n "^#\s*Storage" "/etc/systemd/journald.conf.bak" | LC_ALL=C sed 's/:.*//g')" +if [ -z "$line_number" ]; then + # There was no match of '^#\s*Storage', insert at + # the end of the file. + printf '%s\n' "Storage='persistent'" >> "/etc/systemd/journald.conf" +else + head -n "$(( line_number - 1 ))" "/etc/systemd/journald.conf.bak" > "/etc/systemd/journald.conf" + printf '%s\n' "Storage='persistent'" >> "/etc/systemd/journald.conf" + tail -n "+$(( line_number ))" "/etc/systemd/journald.conf.bak" >> "/etc/systemd/journald.conf" +fi +# Clean up after ourselves. +rm "/etc/systemd/journald.conf.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Ensure All Logs are Rotated by logrotate + +Edit the file /etc/logrotate.d/syslog. Find the first + +line, which should look like this (wrapped for clarity): +/var/log/messages /var/log/secure /var/log/maillog /var/log/spooler \ + /var/log/boot.log /var/log/cron { +Edit this line so that it contains a one-space-separated +listing of each log file referenced in /etc/rsyslog.conf. + +All logs in use on a system must be rotated regularly, or the +log files will consume disk space over time, eventually interfering +with system operation. The file /etc/logrotate.d/syslog is the +configuration file used by the logrotate program to maintain all +log files written by syslog. By default, it rotates logs weekly and +stores four archival copies of each log. These settings can be +modified by editing /etc/logrotate.conf, but the defaults are +sufficient for purposes of this guide. + +Note that logrotate is run nightly by the cron job +/etc/cron.daily/logrotate. If particularly active logs need to be +rotated more often than once a day, some other mechanism must be +used. + + Ensure Logrotate Runs Periodically + The logrotate utility allows for the automatic rotation of +log files. The frequency of rotation is specified in /etc/logrotate.conf, +which triggers a cron task. To configure logrotate to run daily, add or correct +the following line in /etc/logrotate.conf: +# rotate log files frequency +daily + BP28(R43) + NT12(R18) + 1 + 14 + 15 + 16 + 3 + 5 + 6 + APO11.04 + BAI03.05 + DSS05.04 + DSS05.07 + MEA02.01 + CCI-000366 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + CM-6(a) + PR.PT-1 + Req-10.7 + Log files that are not properly rotated run the risk of growing so large +that they fill up the /var/log partition. Valuable logging information could be lost +if the /var/log partition becomes full. + CCE-83993-6 + - name: Configure daily log rotation in /etc/logrotate.conf + lineinfile: + create: true + dest: /etc/logrotate.conf + regexp: ^daily$ + line: daily + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83993-6 + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.7 + - configure_strategy + - ensure_logrotate_activated + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Make sure daily log rotation setting is not overriden in /etc/logrotate.conf + lineinfile: + create: false + dest: /etc/logrotate.conf + regexp: ^[\s]*(weekly|monthly|yearly)$ + state: absent + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83993-6 + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.7 + - configure_strategy + - ensure_logrotate_activated + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Configure cron.daily if not already + block: + + - name: Add shebang + lineinfile: + path: /etc/cron.daily/logrotate + line: '#!/bin/sh' + insertbefore: BOF + create: true + + - name: Add logrotate call + lineinfile: + path: /etc/cron.daily/logrotate + line: /usr/sbin/logrotate /etc/logrotate.conf + regexp: ^[\s]*/usr/sbin/logrotate[\s\S]*/etc/logrotate.conf$ + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83993-6 + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.7 + - configure_strategy + - ensure_logrotate_activated + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,{{ %23%20see%20%22man%20logrotate%22%20for%20details%0A%23%20rotate%20log%20files%20daily%0Adaily%0A%0A%23%20keep%204%20weeks%20worth%20of%20backlogs%0Arotate%2030%0A%0A%23%20create%20new%20%28empty%29%20log%20files%20after%20rotating%20old%20ones%0Acreate%0A%0A%23%20use%20date%20as%20a%20suffix%20of%20the%20rotated%20file%0Adateext%0A%0A%23%20uncomment%20this%20if%20you%20want%20your%20log%20files%20compressed%0A%23compress%0A%0A%23%20RPM%20packages%20drop%20log%20rotation%20information%20into%20this%20directory%0Ainclude%20/etc/logrotate.d%0A%0A%23%20system-specific%20logs%20may%20be%20also%20be%20configured%20here. }} + mode: 0644 + path: /etc/logrotate.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +LOGROTATE_CONF_FILE="/etc/logrotate.conf" +CRON_DAILY_LOGROTATE_FILE="/etc/cron.daily/logrotate" + +# daily rotation is configured +grep -q "^daily$" $LOGROTATE_CONF_FILE|| echo "daily" >> $LOGROTATE_CONF_FILE + +# remove any line configuring weekly, monthly or yearly rotation +sed -i '/^\s*\(weekly\|monthly\|yearly\).*$/d' $LOGROTATE_CONF_FILE + +# configure cron.daily if not already +if ! grep -q "^[[:space:]]*/usr/sbin/logrotate[[:alnum:][:blank:][:punct:]]*$LOGROTATE_CONF_FILE$" $CRON_DAILY_LOGROTATE_FILE; then + echo "#!/bin/sh" > $CRON_DAILY_LOGROTATE_FILE + echo "/usr/sbin/logrotate $LOGROTATE_CONF_FILE" >> $CRON_DAILY_LOGROTATE_FILE +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Configure rsyslogd to Accept Remote Messages If Acting as a Log Server + By default, rsyslog does not listen over the network +for log messages. If needed, modules can be enabled to allow +the rsyslog daemon to receive messages from other systems and for the system +thus to act as a log server. +If the system is not a log server, then lines concerning these modules +should remain commented out. + + + Ensure syslog-ng is Installed + syslog-ng can be installed in replacement of rsyslog. +The syslog-ng-core package can be installed with the following command: + +$ sudo dnf install syslog-ng-core + BP28(R46) + BP28(R5) + 1 + 14 + 15 + 16 + 3 + 5 + 6 + APO11.04 + BAI03.05 + DSS05.04 + DSS05.07 + MEA02.01 + CCI-001311 + CCI-001312 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + CM-6(a) + PR.PT-1 + The syslog-ng-core package provides the syslog-ng daemon, which provides +system logging services. + +package --add=syslog-ng + + include install_syslog-ng + +class install_syslog-ng { + package { 'syslog-ng': + ensure => 'installed', + } +} + + - name: Ensure syslog-ng is installed + package: + name: syslog-ng + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-CM-6(a) + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_syslogng_installed + + +[[packages]] +name = "syslog-ng" +version = "*" + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if ! rpm -q --quiet "syslog-ng" ; then + dnf install -y "syslog-ng" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Enable syslog-ng Service + The syslog-ng service (in replacement of rsyslog) provides syslog-style logging by default on Debian. + +The syslog-ng service can be enabled with the following command: +$ sudo systemctl enable syslog-ng.service + BP28(R46) + BP28(R5) + 1 + 12 + 13 + 14 + 15 + 16 + 2 + 3 + 5 + 6 + 7 + 8 + 9 + APO10.01 + APO10.03 + APO10.04 + APO10.05 + APO11.04 + APO13.01 + BAI03.05 + BAI04.04 + DSS01.03 + DSS03.05 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + MEA01.01 + MEA01.02 + MEA01.03 + MEA01.04 + MEA01.05 + MEA02.01 + CCI-001311 + CCI-001312 + CCI-001557 + CCI-001851 + 4.3.2.6.7 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 6.1 + SR 6.2 + SR 7.1 + SR 7.2 + A.12.1.3 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.14.2.7 + A.15.2.1 + A.15.2.2 + A.17.2.1 + CM-6(a) + AU-4(1) + DE.CM-1 + DE.CM-3 + DE.CM-7 + ID.SC-4 + PR.DS-4 + PR.PT-1 + The syslog-ng service must be running in order to provide +logging services, which are essential to system administration. + include enable_syslog-ng + +class enable_syslog-ng { + service {'syslog-ng': + enable => true, + ensure => 'running', + } +} + + - name: Enable service syslog-ng + block: + + - name: Gather the package facts + package_facts: + manager: auto + + - name: Enable service syslog-ng + service: + name: syslog-ng + enabled: 'yes' + state: started + masked: 'no' + when: + - '"syslog-ng" in ansible_facts.packages' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AU-4(1) + - NIST-800-53-CM-6(a) + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_syslogng_enabled + + +[customizations.services] +enabled = ["syslog-ng"] + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" unmask 'syslog-ng.service' +"$SYSTEMCTL_EXEC" start 'syslog-ng.service' +"$SYSTEMCTL_EXEC" enable 'syslog-ng.service' + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Enable rsyslog to Accept Messages via TCP, if Acting As Log Server + The rsyslog daemon should not accept remote messages +unless the system acts as a log server. +If the system needs to act as a central log server, add the following lines to +/etc/rsyslog.conf to enable reception of messages over TCP: +$ModLoad imtcp +$InputTCPServerRun 514 + 1 + 14 + 15 + 16 + 3 + 5 + 6 + APO11.04 + BAI03.05 + DSS05.04 + DSS05.07 + MEA02.01 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + CIP-004-6 R2.2.2 + CIP-004-6 R3.3 + CIP-007-3 R.1.3 + CIP-007-3 R5 + CIP-007-3 R5.1.1 + CIP-007-3 R6.5 + CM-6(a) + AU-6(3) + AU-6(4) + PR.PT-1 + If the system needs to act as a log server, this ensures that it can receive +messages over a reliable TCP connection. + + + Enable rsyslog to Accept Messages via UDP, if Acting As Log Server + The rsyslog daemon should not accept remote messages +unless the system acts as a log server. +If the system needs to act as a central log server, add the following lines to +/etc/rsyslog.conf to enable reception of messages over UDP: +$ModLoad imudp +$UDPServerRun 514 + 1 + 14 + 15 + 16 + 3 + 5 + 6 + APO11.04 + BAI03.05 + DSS05.04 + DSS05.07 + MEA02.01 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + CIP-004-6 R2.2.2 + CIP-004-6 R3.3 + CIP-007-3 R.1.3 + CIP-007-3 R5 + CIP-007-3 R5.1.1 + CIP-007-3 R6.5 + CM-6(a) + AU-6(3) + AU-6(4) + PR.PT-1 + Many devices, such as switches, routers, and other Unix-like systems, may only support +the traditional syslog transmission over UDP. If the system must act as a log server, +this enables it to receive their messages as well. + + + Ensure rsyslog Does Not Accept Remote Messages Unless Acting As Log Server + The rsyslog daemon should not accept remote messages +unless the system acts as a log server. +To ensure that it is not listening on the network, ensure the following lines are +not found in /etc/rsyslog.conf: +$ModLoad imtcp +$InputTCPServerRun port +$ModLoad imudp +$UDPServerRun port +$ModLoad imrelp +$InputRELPServerRun port + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 4 + 5 + 6 + 8 + 9 + APO01.06 + APO11.04 + APO13.01 + BAI03.05 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.05 + DSS03.01 + DSS05.02 + DSS05.04 + DSS05.07 + DSS06.02 + MEA02.01 + CCI-000318 + CCI-000366 + CCI-000368 + CCI-001812 + CCI-001813 + CCI-001814 + 4.2.3.4 + 4.3.3.3.9 + 4.3.3.4 + 4.3.3.5.8 + 4.3.4.3.2 + 4.3.4.3.3 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + 4.4.3.3 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + 0988 + 1405 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.12.1.1 + A.12.1.2 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.5.1 + A.12.6.2 + A.12.7.1 + A.13.1.1 + A.13.1.2 + A.13.1.3 + A.13.2.1 + A.13.2.2 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-7(a) + CM-7(b) + CM-6(a) + DE.AE-1 + ID.AM-3 + PR.AC-5 + PR.DS-5 + PR.IP-1 + PR.PT-1 + PR.PT-4 + SRG-OS-000480-GPOS-00227 + Any process which receives messages from the network incurs some risk +of receiving malicious messages. This risk can be eliminated for +rsyslog by configuring it not to listen on the network. + CCE-83995-1 + + + + + + + + + + Rsyslog Logs Sent To Remote Host + If system logs are to be useful in detecting malicious +activities, it is necessary to send logs to a remote server. An +intruder who has compromised the root account on a system may +delete the log entries which indicate that the system was attacked +before they are seen by an administrator. + +However, it is recommended that logs be stored on the local +host in addition to being sent to the loghost, especially if +rsyslog has been configured to use the UDP protocol to send +messages over a network. UDP does not guarantee reliable delivery, +and moderately busy sites will lose log messages occasionally, +especially in periods of high traffic which may be the result of an +attack. In addition, remote rsyslog messages are not +authenticated in any way by default, so it is easy for an attacker to +introduce spurious messages to the central log server. Also, some +problems cause loss of network connectivity, which will prevent the +sending of messages to the central server. For all of these reasons, it is +better to store log messages both centrally and on each host, so +that they can be correlated if necessary. + + Remote Log Server + Specify an URI or IP address of a remote host where the log messages will be sent and stored. + logcollector + + + Ensure Logs Sent To Remote Host + To configure rsyslog to send logs to a remote log server, +open /etc/rsyslog.conf and read and understand the last section of the file, +which describes the multiple directives necessary to activate remote +logging. +Along with these other directives, the system can be configured +to forward its logs to a particular log server by +adding or correcting one of the following lines, +substituting appropriately. +The choice of protocol depends on the environment of the system; +although TCP and RELP provide more reliable message delivery, +they may not be supported in all environments. + +To use UDP for log message delivery: +*.* @ + +To use TCP for log message delivery: +*.* @@ + +To use RELP for log message delivery: +*.* :omrelp: + +There must be a resolvable DNS CNAME or Alias record set to "" for logs to be sent correctly to the centralized logging utility. + It is important to configure queues in case the client is sending log +messages to a remote server. If queues are not configured, +the system will stop functioning when the connection +to the remote server is not available. Please consult Rsyslog +documentation for more information about configuration of queues. The +example configuration which should go into /etc/rsyslog.conf +can look like the following lines: + +$ActionQueueType LinkedList +$ActionQueueFileName queuefilename +$ActionQueueMaxDiskSpace 1g +$ActionQueueSaveOnShutdown on +$ActionResumeRetryCount -1 + + BP28(R7) + NT28(R43) + NT12(R5) + 1 + 13 + 14 + 15 + 16 + 2 + 3 + 5 + 6 + APO11.04 + APO13.01 + BAI03.05 + BAI04.04 + DSS05.04 + DSS05.07 + MEA02.01 + CCI-000366 + CCI-001348 + CCI-000136 + CCI-001851 + 164.308(a)(1)(ii)(D) + 164.308(a)(5)(ii)(B) + 164.308(a)(5)(ii)(C) + 164.308(a)(6)(ii) + 164.308(a)(8) + 164.310(d)(2)(iii) + 164.312(b) + 164.314(a)(2)(i)(C) + 164.314(a)(2)(iii) + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + SR 7.1 + SR 7.2 + 0988 + 1405 + A.12.1.3 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.17.2.1 + CIP-003-8 R5.2 + CIP-004-6 R3.3 + CM-6(a) + AU-4(1) + AU-9(2) + PR.DS-4 + PR.PT-1 + FAU_GEN.1.1.c + SRG-OS-000479-GPOS-00224 + SRG-OS-000480-GPOS-00227 + SRG-OS-000342-GPOS-00133 + SRG-OS-000032-VMM-000130 + A log server (loghost) receives syslog messages from one or more +systems. This data can be used as an additional log source in the event a +system is compromised and its local logs are suspect. Forwarding log messages +to a remote loghost also provides system administrators with a centralized +place to view the status of multiple hosts within the enterprise. + CCE-83990-2 + - name: XCCDF Value rsyslog_remote_loghost_address # promote to variable + set_fact: + rsyslog_remote_loghost_address: !!str + tags: + - always + +- name: Set rsyslog remote loghost + lineinfile: + dest: /etc/rsyslog.conf + regexp: ^\*\.\* + line: '*.* @@{{ rsyslog_remote_loghost_address }}' + create: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83990-2 + - NIST-800-53-AU-4(1) + - NIST-800-53-AU-9(2) + - NIST-800-53-CM-6(a) + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - rsyslog_remote_loghost + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +rsyslog_remote_loghost_address='' + + +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/rsyslog.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^\*\.\*") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s %s" "$stripped_key" "@@$rsyslog_remote_loghost_address" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^\*\.\*\\>" "/etc/rsyslog.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^\*\.\*\\>.*/$escaped_formatted_output/gi" "/etc/rsyslog.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-83990-2" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/rsyslog.conf" >> "/etc/rsyslog.conf" + printf '%s\n' "$formatted_output" >> "/etc/rsyslog.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure TLS for rsyslog remote logging + Configure rsyslog to use Transport Layer +Security (TLS) support for logging to remote server +for the Forwarding Output Module in /etc/rsyslog.conf +using action. You can use the following command: +echo 'action(type="omfwd" protocol="tcp" Target="<remote system>" port="6514" + StreamDriver="gtls" StreamDriverMode="1" StreamDriverAuthMode="x509/name" streamdriver.CheckExtendedKeyPurpose="on")' >> /etc/rsyslog.conf + +Replace the <remote system> in the above command with an IP address or a host name of the remote logging server. + BP28(R43) + 0988 + 1405 + AU-9(3) + CM-6(a) + FCS_TLSC_EXT.1 + FTP_ITC_EXT.1.1 + FIA_X509_EXT.1.1 + FMT_SMF_EXT.1.1 + SRG-OS-000480-GPOS-00227 + SRG-OS-000120-GPOS-00061 + For protection of data being logged, the connection to the +remote logging server needs to be authenticated and encrypted. + CCE-83991-0 + + + + + + + + + Configure CA certificate for rsyslog remote logging + Configure CA certificate for rsyslog logging +to remote server using Transport Layer Security (TLS) +using correct path for the DefaultNetstreamDriverCAFile +global option in /etc/rsyslog.conf, for example with the following command: +echo 'global(DefaultNetstreamDriverCAFile="/etc/pki/tls/cert.pem")' >> /etc/rsyslog.conf +Replace the /etc/pki/tls/cert.pem in the above command with the path to the file with CA certificate generated for the purpose of remote logging. + BP28(R43) + 0988 + 1405 + FCS_TLSC_EXT.1 + SRG-OS-000480-GPOS-00227 + The CA certificate needs to be set or rsyslog.service +fails to start with +error: ca certificate is not set, cannot continue + CCE-83992-8 + + + + + + + + + + + Network Configuration and Firewalls + Most systems must be connected to a network of some +sort, and this brings with it the substantial risk of network +attack. This section discusses the security impact of decisions +about networking which must be made when configuring a system. + +This section also discusses firewalls, network access +controls, and other network security frameworks, which allow +system-level rules to be written that can limit an attackers' ability +to connect to your system. These rules can specify that network +traffic should be allowed or denied from certain IP addresses, +hosts, and networks. The rules can also specify which of the +system's network services are available to particular hosts or +networks. + + Configure Multiple DNS Servers in /etc/resolv.conf + +Determine whether the system is using local or DNS name resolution with the +following command: +$ sudo grep hosts /etc/nsswitch.conf +hosts: files dns +If the DNS entry is missing from the host's line in the "/etc/nsswitch.conf" +file, the "/etc/resolv.conf" file must be empty. +Verify the "/etc/resolv.conf" file is empty with the following command: +$ sudo ls -al /etc/resolv.conf +-rw-r--r-- 1 root root 0 Aug 19 08:31 resolv.conf +If the DNS entry is found on the host's line of the "/etc/nsswitch.conf" file, +then verify the following: + +Multiple Domain Name System (DNS) Servers should be configured +in /etc/resolv.conf. This provides redundant name resolution services +in the event that a domain server crashes. To configure the system to contain +as least 2 DNS servers, add a corresponding nameserver +ip_address entry in /etc/resolv.conf for each DNS +server where ip_address is the IP address of a valid DNS server. +For example: +search example.com +nameserver 192.168.0.1 +nameserver 192.168.0.2 + 12 + 15 + 8 + APO13.01 + DSS05.02 + CCI-000366 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + A.13.1.1 + A.13.2.1 + A.14.1.3 + SC-20(a) + CM-6(a) + PR.PT-4 + SRG-OS-000480-GPOS-00227 + To provide availability for name resolution services, multiple redundant +name servers are mandated. A failure in name resolution could lead to the +failure of security functions requiring name resolution, which may include +time synchronization, centralized authentication, and remote system logging. + CCE-86858-8 + + + + + + + + + Prevent non-Privileged Users from Modifying Network Interfaces using nmcli + By default, non-privileged users are given permissions to modify networking +interfaces and configurations using the nmcli command. Non-privileged +users should not be making configuration changes to network configurations. To +ensure that non-privileged users do not have permissions to make changes to the +network configuration using nmcli, create the following configuration in +/etc/polkit-1/localauthority/20-org.d/10-nm-harden-access.pkla: + +[Disable General User Access to NetworkManager] +Identity=default +Action=org.freedesktop.NetworkManager.* +ResultAny=no +ResultInactive=no +ResultActive=auth_admin + + 3.1.16 + 0418 + 1055 + 1402 + AC-18(4) + CM-6(a) + Allowing non-privileged users to make changes to network settings can allow +untrusted access, prevent system availability, and/or can lead to a compromise or +attack. + + CCE-90061-3 + +printf "[Disable General User Access to NetworkManager]\nIdentity=default\nAction=org.freedesktop.NetworkManager.*\nResultAny=no\nResultInactive=no\nResultActive=auth_admin\n" > /etc/polkit-1/localauthority/20-org.d/10-nm-harden-access.pkla + + + + + + + + + + Ensure System is Not Acting as a Network Sniffer + The system should not be acting as a network sniffer, which can +capture all traffic on the network to which it is connected. Run the following +to determine if any interface is running in promiscuous mode: +$ ip link | grep PROMISC +Promiscuous mode of an interface can be disabled with the following command: +$ sudo ip link set dev device_name multicast off promisc off + 1 + 11 + 14 + 3 + 9 + APO11.06 + APO12.06 + BAI03.10 + BAI09.01 + BAI09.02 + BAI09.03 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.05 + DSS04.05 + DSS05.02 + DSS05.05 + DSS06.06 + CCI-000366 + 4.2.3.4 + 4.3.3.3.7 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + 4.4.3.4 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + SR 7.8 + A.11.1.2 + A.11.2.4 + A.11.2.5 + A.11.2.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.16.1.6 + A.8.1.1 + A.8.1.2 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + CM-7(2) + MA-3 + DE.DP-5 + ID.AM-1 + PR.IP-1 + PR.MA-1 + PR.PT-3 + SRG-OS-000480-GPOS-00227 + Network interfaces in promiscuous mode allow for the capture of all network traffic +visible to the system. If unauthorized individuals can access these applications, it +may allow them to collect information such as logon IDs, passwords, and key exchanges +between systems. + +If the system is being used to perform a network troubleshooting function, the use of these +tools must be documented with the Information Systems Security Manager (ISSM) and restricted +to only authorized personnel. + + CCE-83996-9 + + + + + + + + + firewalld + The dynamic firewall daemon firewalld provides a +dynamically managed firewall with support for network “zones” to assign +a level of trust to a network and its associated connections and interfaces. +It has support for IPv4 and IPv6 firewall settings. It supports Ethernet +bridges and has a separation of runtime and permanent configuration options. +It also has an interface for services or applications to add firewall rules +directly. + +A graphical configuration tool, firewall-config, is used to configure +firewalld, which in turn uses iptables tool to communicate +with Netfilter in the kernel which implements packet filtering. + +The firewall service provided by firewalld is dynamic rather than +static because changes to the configuration can be made at anytime and are +immediately implemented. There is no need to save or apply the changes. No +unintended disruption of existing network connections occurs as no part of +the firewall has to be reloaded. + + + Configure Firewalld to Use the Nftables Backend + Firewalld can be configured with many backends, such as nftables. + CCI-002385 + SC-5 + SRG-OS-000420-GPOS-00186 + Nftables is modern kernel module for controling network connections coming into a system. +Utilizing the limit statement in "nftables" can help to mitigate DoS attacks. + CCE-86507-1 + - name: Setting shell-quoted shell-style assignment of 'FirewallBackend' to 'nftables' + in '/etc/firewalld/firewalld.conf' + block: + + - name: Check for duplicate values + lineinfile: + path: /etc/firewalld/firewalld.conf + create: false + regexp: ^\s*FirewallBackend= + state: absent + check_mode: true + changed_when: false + register: dupes + + - name: Deduplicate values from /etc/firewalld/firewalld.conf + lineinfile: + path: /etc/firewalld/firewalld.conf + create: false + regexp: ^\s*FirewallBackend= + state: absent + when: dupes.found is defined and dupes.found > 1 + + - name: Insert correct line to /etc/firewalld/firewalld.conf + lineinfile: + path: /etc/firewalld/firewalld.conf + create: true + regexp: ^\s*FirewallBackend= + line: FirewallBackend="nftables" + state: present + insertbefore: ^# FirewallBackend + validate: /usr/bin/bash -n %s + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86507-1 + - NIST-800-53-SC-5 + - firewalld-backend + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if [ -e "/etc/firewalld/firewalld.conf" ] ; then + + LC_ALL=C sed -i "/^\s*FirewallBackend=/d" "/etc/firewalld/firewalld.conf" +else + touch "/etc/firewalld/firewalld.conf" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/firewalld/firewalld.conf" + +cp "/etc/firewalld/firewalld.conf" "/etc/firewalld/firewalld.conf.bak" +# Insert before the line matching the regex '^#\s*FirewallBackend'. +line_number="$(LC_ALL=C grep -n "^#\s*FirewallBackend" "/etc/firewalld/firewalld.conf.bak" | LC_ALL=C sed 's/:.*//g')" +if [ -z "$line_number" ]; then + # There was no match of '^#\s*FirewallBackend', insert at + # the end of the file. + printf '%s\n' "FirewallBackend='nftables'" >> "/etc/firewalld/firewalld.conf" +else + head -n "$(( line_number - 1 ))" "/etc/firewalld/firewalld.conf.bak" > "/etc/firewalld/firewalld.conf" + printf '%s\n' "FirewallBackend='nftables'" >> "/etc/firewalld/firewalld.conf" + tail -n "+$(( line_number ))" "/etc/firewalld/firewalld.conf.bak" >> "/etc/firewalld/firewalld.conf" +fi +# Clean up after ourselves. +rm "/etc/firewalld/firewalld.conf.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Inspect and Activate Default firewalld Rules + Firewalls can be used to separate networks into different zones +based on the level of trust the user has decided to place on the devices and +traffic within that network. NetworkManager informs firewalld to which +zone an interface belongs. An interface's assigned zone can be changed by +NetworkManager or via the firewall-config tool. + +The zone settings in /etc/firewalld/ are a range of preset settings +which can be quickly applied to a network interface. These are the zones +provided by firewalld sorted according to the default trust level of the +zones from untrusted to trusted: +dropAny incoming network packets are dropped, there is no +reply. Only outgoing network connections are possible.blockAny incoming network connections are rejected with an +icmp-host-prohibited message for IPv4 and icmp6-adm-prohibited +for IPv6. Only network connections initiated from within the system are +possible.publicFor use in public areas. You do not trust the other +computers on the network to not harm your computer. Only selected incoming +connections are accepted.externalFor use on external networks with masquerading enabled +especially for routers. You do not trust the other computers on the network to +not harm your computer. Only selected incoming connections are accepted.dmzFor computers in your demilitarized zone that are +publicly-accessible with limited access to your internal network. Only selected +incoming connections are accepted.workFor use in work areas. You mostly trust the other computers +on networks to not harm your computer. Only selected incoming connections are +accepted.homeFor use in home areas. You mostly trust the other computers +on networks to not harm your computer. Only selected incoming connections are +accepted.internalFor use on internal networks. You mostly trust the +other computers on the networks to not harm your computer. Only selected +incoming connections are accepted.trustedAll network connections are accepted. + +It is possible to designate one of these zones to be the default zone. When +interface connections are added to NetworkManager, they are assigned +to the default zone. On installation, the default zone in firewalld is set to +be the public zone. + +To find out all the settings of a zone, for example the public zone, +enter the following command as root: +# firewall-cmd --zone=public --list-all +Example output of this command might look like the following: + +# firewall-cmd --zone=public --list-all +public + interfaces: + services: mdns dhcpv6-client ssh + ports: + forward-ports: + icmp-blocks: source-quench + +To view the network zones currently active, enter the following command as root: +# firewall-cmd --get-service +The following listing displays the result of this command +on common Red Hat Enterprise Linux 9 system: + +# firewall-cmd --get-service +amanda-client bacula bacula-client dhcp dhcpv6 dhcpv6-client dns ftp +high-availability http https imaps ipp ipp-client ipsec kerberos kpasswd +ldap ldaps libvirt libvirt-tls mdns mountd ms-wbt mysql nfs ntp openvpn +pmcd pmproxy pmwebapi pmwebapis pop3s postgresql proxy-dhcp radius rpc-bind +samba samba-client smtp ssh telnet tftp tftp-client transmission-client +vnc-server wbem-https + +Finally to view the network zones that will be active after the next firewalld +service reload, enter the following command as root: +# firewall-cmd --get-service --permanent + + Install firewalld Package + The firewalld package can be installed with the following command: + +$ sudo dnf install firewalld + CCI-002314 + CM-6(a) + FMT_SMF_EXT.1 + SRG-OS-000096-GPOS-00050 + SRG-OS-000297-GPOS-00115 + SRG-OS-000298-GPOS-00116 + SRG-OS-000480-GPOS-00227 + SRG-OS-000480-GPOS-00232 + "Firewalld" provides an easy and effective way to block/limit remote access to the system via ports, services, and protocols. + +Remote access services, such as those providing remote access to network devices and information systems, which lack automated control capabilities, increase risk and make remote user access management difficult at best. + +Remote access is access to DoD nonpublic information systems by an authorized user (or an information system) communicating through an external, non-organization-controlled network. Remote access methods include, for example, dial-up, broadband, and wireless. + +Red Hat Enterprise Linux 9 functionality (e.g., SSH) must be capable of taking enforcement action if the audit reveals unauthorized activity. +Automated control of remote access sessions allows organizations to ensure ongoing compliance with remote access policies by enforcing connection rules of remote access applications on a variety of information system components (e.g., servers, workstations, notebook computers, smartphones, and tablets)." + CCE-84021-5 + +package --add=firewalld + + include install_firewalld + +class install_firewalld { + package { 'firewalld': + ensure => 'installed', + } +} + + - name: Ensure firewalld is installed + package: + name: firewalld + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84021-5 + - NIST-800-53-CM-6(a) + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_firewalld_installed + + +[[packages]] +name = "firewalld" +version = "*" + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if ! rpm -q --quiet "firewalld" ; then + dnf install -y "firewalld" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Verify firewalld Enabled + +The firewalld service can be enabled with the following command: +$ sudo systemctl enable firewalld.service + 11 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + 3.1.3 + 3.4.7 + CCI-000366 + CCI-000382 + CCI-002314 + 4.3.4.3.2 + 4.3.4.3.3 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + CIP-003-8 R4 + CIP-003-8 R5 + CIP-004-6 R3 + AC-4 + CM-7(b) + CA-3(5) + SC-7(21) + CM-6(a) + PR.IP-1 + FMT_SMF_EXT.1 + SRG-OS-000096-GPOS-00050 + SRG-OS-000297-GPOS-00115 + SRG-OS-000480-GPOS-00227 + SRG-OS-000480-GPOS-00231 + SRG-OS-000480-GPOS-00232 + Access control methods provide the ability to enhance system security posture +by restricting services and known good IP addresses and address ranges. This +prevents connections from unknown hosts and protocols. + CCE-90833-5 + include enable_firewalld + +class enable_firewalld { + service {'firewalld': + enable => true, + ensure => 'running', + } +} + + - name: Enable service firewalld + block: + + - name: Gather the package facts + package_facts: + manager: auto + + - name: Enable service firewalld + service: + name: firewalld + enabled: 'yes' + state: started + masked: 'no' + when: + - '"firewalld" in ansible_facts.packages' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-90833-5 + - NIST-800-171-3.1.3 + - NIST-800-171-3.4.7 + - NIST-800-53-AC-4 + - NIST-800-53-CA-3(5) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-7(21) + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_firewalld_enabled + + +[customizations.services] +enabled = ["firewalld"] + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" unmask 'firewalld.service' +"$SYSTEMCTL_EXEC" start 'firewalld.service' +"$SYSTEMCTL_EXEC" enable 'firewalld.service' + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Strengthen the Default Ruleset + The default rules can be strengthened. The system +scripts that activate the firewall rules expect them to be defined +in configuration files under the /etc/firewalld/services +and /etc/firewalld/zones directories. + +The following recommendations describe how to strengthen the +default ruleset configuration file. An alternative to editing this +configuration file is to create a shell script that makes calls to +the firewall-cmd program to load in rules under the /etc/firewalld/services +and /etc/firewalld/zones directories. + +Instructions apply to both unless otherwise noted. Language and address +conventions for regular firewalld rules are used throughout this section. + The program firewall-config +allows additional services to penetrate the default firewall rules +and automatically adjusts the firewalld ruleset(s). + + Configure the Firewalld Ports + Configure the firewalld ports to allow approved services to have access to the system. +To configure firewalld to open ports, run the following command: +firewall-cmd --permanent --add-port=port_number/tcp +To configure firewalld to allow access for pre-defined services, run the following +command: +firewall-cmd --permanent --add-service=service_name + 11 + 12 + 14 + 15 + 3 + 8 + 9 + APO13.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.04 + DSS05.02 + DSS05.03 + DSS05.05 + DSS06.06 + CCI-000382 + CCI-002314 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + 1416 + A.11.2.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.6.2.1 + A.6.2.2 + A.9.1.2 + AC-4 + CM-7(b) + CA-3(5) + SC-7(21) + CM-6(a) + PR.AC-3 + PR.IP-1 + PR.PT-3 + PR.PT-4 + SRG-OS-000096-GPOS-00050 + SRG-OS-000297-GPOS-00115 + SRG-OS-000096-VMM-000490 + SRG-OS-000480-VMM-002000 + In order to prevent unauthorized connection of devices, unauthorized transfer of information, +or unauthorized tunneling (i.e., embedding of data types within data types), organizations must +disable or restrict unused or unnecessary physical and logical ports/protocols on information +systems. + +Operating systems are capable of providing a wide variety of functions and services. +Some of the functions and services provided by default may not be necessary to support +essential organizational operations. +Additionally, it is sometimes convenient to provide multiple services from a single component +(e.g., VPN and IPS); however, doing so increases risk over limiting the services provided by +one component. + +To support the requirements and principles of least functionality, the operating system must +support the organizational requirements, providing only essential capabilities and limiting the +use of ports, protocols, and/or services to only those required, authorized, and approved to +conduct official business. + CCE-86041-1 + + + + + + Firewalld Must Employ a Deny-all, Allow-by-exception Policy for Allowing Connections to Other Systems + Red Hat Enterprise Linux 9 incorporates the "firewalld" daemon, which allows for many different configurations. One of these configurations is zones. +Zones can be utilized to a deny-all, allow-by-exception approach. +The default "drop" zone will drop all incoming network packets unless it is explicitly allowed by the configuration file or is related to an outgoing network connection. + CCI-002314 + AC-17 (1) + SRG-OS-000297-GPOS-00115 + Failure to restrict network connectivity only to authorized systems permits inbound connections from malicious systems. +It also permits outbound connections that may facilitate exfiltration of data. + CCE-86049-4 + + + + + + Set Default firewalld Zone for Incoming Packets + To set the default zone to drop for +the built-in default zone which processes incoming IPv4 and IPv6 packets, +modify the following line in +/etc/firewalld/firewalld.conf to be: +DefaultZone=drop + To prevent denying any access to the system, automatic remediation +of this control is not available. Remediation must be automated as +a component of machine provisioning, or followed manually as outlined +above. + 11 + 14 + 3 + 9 + 5.10.1 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + 3.1.3 + 3.4.7 + 3.13.6 + CCI-000366 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + 1416 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CA-3(5) + CM-7(b) + SC-7(23) + CM-6(a) + PR.IP-1 + PR.PT-3 + FMT_MOF_EXT.1 + SRG-OS-000480-GPOS-00227 + SRG-OS-000480-VMM-002000 + In firewalld the default zone is applied only after all +the applicable rules in the table are examined for a match. Setting the +default zone to drop implements proper design for a firewall, i.e. +any packets which are not explicitly permitted should not be +accepted. + CCE-84023-1 + + + + + + + + + + + IPSec Support + Support for Internet Protocol Security (IPsec) +is provided with Libreswan. + + Install libreswan Package + The Libreswan package provides an implementation of IPsec +and IKE, which permits the creation of secure tunnels over +untrusted networks. The libreswan package can be installed with the following command: + +$ sudo dnf install libreswan + 12 + 15 + 3 + 5 + 8 + APO13.01 + DSS01.04 + DSS05.02 + DSS05.03 + DSS05.04 + CCI-001130 + CCI-001131 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + SR 1.13 + SR 2.6 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + A.11.2.4 + A.11.2.6 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.15.1.1 + A.15.2.1 + A.6.2.1 + A.6.2.2 + CM-6(a) + PR.AC-3 + PR.MA-2 + PR.PT-4 + Req-4.1 + SRG-OS-000480-GPOS-00227 + SRG-OS-000120-GPOS-00061 + Providing the ability for remote users or systems +to initiate a secure VPN connection protects information when it is +transmitted over a wide area network. + CCE-84068-6 + +package --add=libreswan + + include install_libreswan + +class install_libreswan { + package { 'libreswan': + ensure => 'installed', + } +} + + - name: Ensure libreswan is installed + package: + name: libreswan + state: present + tags: + - CCE-84068-6 + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-4.1 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_libreswan_installed + + +[[packages]] +name = "libreswan" +version = "*" + + +if ! rpm -q --quiet "libreswan" ; then + dnf install -y "libreswan" +fi + + + + + + + + + + Verify Any Configured IPSec Tunnel Connections + Libreswan provides an implementation of IPsec +and IKE, which permits the creation of secure tunnels over +untrusted networks. As such, IPsec can be used to circumvent certain +network requirements such as filtering. Verify that if any IPsec connection +(conn) configured in /etc/ipsec.conf and /etc/ipsec.d +exists is an approved organizational connection. + Automatic remediation of this control is not available due to the unique +requirements of each system. + 1 + 12 + 13 + 14 + 15 + 16 + 18 + 4 + 6 + 8 + 9 + APO01.06 + APO13.01 + DSS01.05 + DSS03.01 + DSS05.02 + DSS05.04 + DSS05.07 + DSS06.02 + CCI-000336 + 164.308(a)(4)(i) + 164.308(b)(1) + 164.308(b)(3) + 164.310(b) + 164.312(e)(1) + 164.312(e)(2)(ii) + 4.2.3.4 + 4.3.3.4 + 4.4.3.3 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.12.1.1 + A.12.1.2 + A.13.1.1 + A.13.1.2 + A.13.1.3 + A.13.2.1 + A.13.2.2 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + AC-17(a) + MA-4(6) + CM-6(a) + AC-4 + SC-8 + DE.AE-1 + ID.AM-3 + PR.AC-5 + PR.DS-5 + PR.PT-4 + SRG-OS-000480-GPOS-00227 + IP tunneling mechanisms can be used to bypass network filtering. + CCE-90319-5 + + + + + + + iptables and ip6tables + A host-based firewall called netfilter is included as +part of the Linux kernel distributed with the system. It is +activated by default. This firewall is controlled by the program +iptables, and the entire capability is frequently referred to by +this name. An analogous program called ip6tables handles filtering +for IPv6. + +Unlike TCP Wrappers, which depends on the network server +program to support and respect the rules written, netfilter +filtering occurs at the kernel level, before a program can even +process the data from the network packet. As such, any program on +the system is affected by the rules written. + +This section provides basic information about strengthening +the iptables and ip6tables configurations included with the system. +For more complete information that may allow the construction of a +sophisticated ruleset tailored to your environment, please consult +the references at the end of this section. + + Inspect and Activate Default Rules + View the currently-enforced iptables rules by running +the command: +$ sudo iptables -nL --line-numbers +The command is analogous for ip6tables. + +If the firewall does not appear to be active (i.e., no rules +appear), activate it and ensure that it starts at boot by issuing +the following commands (and analogously for ip6tables): +$ sudo service iptables restart +The default iptables rules are: +Chain INPUT (policy ACCEPT) +num target prot opt source destination +1 ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 state RELATED,ESTABLISHED +2 ACCEPT icmp -- 0.0.0.0/0 0.0.0.0/0 +3 ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 +4 ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 state NEW tcp dpt:22 +5 REJECT all -- 0.0.0.0/0 0.0.0.0/0 reject-with icmp-host-prohibited + +Chain FORWARD (policy ACCEPT) +num target prot opt source destination +1 REJECT all -- 0.0.0.0/0 0.0.0.0/0 reject-with icmp-host-prohibited + +Chain OUTPUT (policy ACCEPT) +num target prot opt source destination +The ip6tables default rules are essentially the same. + + Verify ip6tables Enabled if Using IPv6 + +The ip6tables service can be enabled with the following command: +$ sudo systemctl enable ip6tables.service + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 4 + 6 + 8 + 9 + APO01.06 + APO13.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.05 + DSS03.01 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + DSS06.02 + DSS06.06 + 4.2.3.4 + 4.3.3.4 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + 4.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.12.1.1 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.13.1.1 + A.13.1.2 + A.13.1.3 + A.13.2.1 + A.13.2.2 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R4 + CIP-003-8 R5 + CIP-004-6 R3 + AC-4 + CM-7(b) + CA-3(5) + SC-7(21) + CM-6(a) + DE.AE-1 + ID.AM-3 + PR.AC-5 + PR.DS-5 + PR.IP-1 + PR.PT-3 + PR.PT-4 + The ip6tables service provides the system's host-based firewalling +capability for IPv6 and ICMPv6. + + CCE-85960-3 + include enable_ip6tables + +class enable_ip6tables { + service {'ip6tables': + enable => true, + ensure => 'running', + } +} + + - name: Enable service ip6tables + block: + + - name: Gather the package facts + package_facts: + manager: auto + + - name: Enable service ip6tables + service: + name: ip6tables + enabled: 'yes' + state: started + masked: 'no' + when: + - '"iptables-ipv6" in ansible_facts.packages' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-85960-3 + - NIST-800-53-AC-4 + - NIST-800-53-CA-3(5) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-7(21) + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_ip6tables_enabled + + +[customizations.services] +enabled = ["ip6tables"] + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" unmask 'ip6tables.service' +"$SYSTEMCTL_EXEC" start 'ip6tables.service' +"$SYSTEMCTL_EXEC" enable 'ip6tables.service' + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Verify iptables Enabled + +The iptables service can be enabled with the following command: +$ sudo systemctl enable iptables.service + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 4 + 6 + 8 + 9 + APO01.06 + APO13.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.05 + DSS03.01 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + DSS06.02 + DSS06.06 + 4.2.3.4 + 4.3.3.4 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + 4.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.12.1.1 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.13.1.1 + A.13.1.2 + A.13.1.3 + A.13.2.1 + A.13.2.2 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R4 + CIP-003-8 R5 + CIP-004-6 R3 + AC-4 + CM-7(b) + CA-3(5) + SC-7(21) + CM-6(a) + DE.AE-1 + ID.AM-3 + PR.AC-5 + PR.DS-5 + PR.IP-1 + PR.PT-3 + PR.PT-4 + The iptables service provides the system's host-based firewalling +capability for IPv4 and ICMP. + + CCE-85962-9 + include enable_iptables + +class enable_iptables { + service {'iptables': + enable => true, + ensure => 'running', + } +} + + - name: Enable service iptables + block: + + - name: Gather the package facts + package_facts: + manager: auto + + - name: Enable service iptables + service: + name: iptables + enabled: 'yes' + state: started + masked: 'no' + when: + - '"iptables" in ansible_facts.packages' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-85962-9 + - NIST-800-53-AC-4 + - NIST-800-53-CA-3(5) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-7(21) + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_iptables_enabled + + +[customizations.services] +enabled = ["iptables"] + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" unmask 'iptables.service' +"$SYSTEMCTL_EXEC" start 'iptables.service' +"$SYSTEMCTL_EXEC" enable 'iptables.service' + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Set Default ip6tables Policy for Incoming Packets + To set the default policy to DROP (instead of ACCEPT) for +the built-in INPUT chain which processes incoming packets, +add or correct the following line in +/etc/sysconfig/ip6tables: +:INPUT DROP [0:0] +If changes were required, reload the ip6tables rules: +$ sudo service ip6tables reload + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CIP-003-8 R4 + CIP-003-8 R5 + CIP-004-6 R3 + AC-4 + CM-7(b) + CA-3(5) + SC-7(21) + CM-6(a) + PR.IP-1 + PR.PT-3 + In ip6tables, the default policy is applied only after all +the applicable rules in the table are examined for a match. Setting the +default policy to DROP implements proper design for a firewall, i.e. +any packets which are not explicitly permitted should not be +accepted. + CCE-85966-0 + sed -i 's/^:INPUT ACCEPT.*/:INPUT DROP [0:0]/g' /etc/sysconfig/ip6tables + + + + + + + + Strengthen the Default Ruleset + The default rules can be strengthened. The system +scripts that activate the firewall rules expect them to be defined +in the configuration files iptables and ip6tables in the directory +/etc/sysconfig. Many of the lines in these files are similar +to the command line arguments that would be provided to the programs +/sbin/iptables or /sbin/ip6tables - but some are quite +different. + +The following recommendations describe how to strengthen the +default ruleset configuration file. An alternative to editing this +configuration file is to create a shell script that makes calls to +the iptables program to load in rules, and then invokes service +iptables save to write those loaded rules to +/etc/sysconfig/iptables. + +The following alterations can be made directly to +/etc/sysconfig/iptables and /etc/sysconfig/ip6tables. +Instructions apply to both unless otherwise noted. Language and address +conventions for regular iptables are used throughout this section; +configuration for ip6tables will be either analogous or explicitly +covered. + The program system-config-securitylevel +allows additional services to penetrate the default firewall rules +and automatically adjusts /etc/sysconfig/iptables. This program +is only useful if the default ruleset meets your security +requirements. Otherwise, this program should not be used to make +changes to the firewall configuration because it re-writes the +saved configuration file. + + Set Default iptables Policy for Incoming Packets + To set the default policy to DROP (instead of ACCEPT) for +the built-in INPUT chain which processes incoming packets, +add or correct the following line in +/etc/sysconfig/iptables: +:INPUT DROP [0:0] + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CA-3(5) + CM-7(b) + SC-7(23) + CM-6(a) + PR.IP-1 + PR.PT-3 + In iptables the default policy is applied only after all +the applicable rules in the table are examined for a match. Setting the +default policy to DROP implements proper design for a firewall, i.e. +any packets which are not explicitly permitted should not be +accepted. + CCE-85969-4 + sed -i 's/^:INPUT ACCEPT.*/:INPUT DROP [0:0]/g' /etc/sysconfig/iptables + + + + + + + Set Default iptables Policy for Forwarded Packets + To set the default policy to DROP (instead of ACCEPT) for +the built-in FORWARD chain which processes packets that will be forwarded from +one interface to another, +add or correct the following line in +/etc/sysconfig/iptables: +:FORWARD DROP [0:0] + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CA-3(5) + CM-7(b) + SC-7(23) + CM-6(a) + PR.IP-1 + PR.PT-3 + In iptables, the default policy is applied only after all +the applicable rules in the table are examined for a match. Setting the +default policy to DROP implements proper design for a firewall, i.e. +any packets which are not explicitly permitted should not be +accepted. + sed -i 's/^:FORWARD ACCEPT.*/:FORWARD DROP [0:0]/g' /etc/sysconfig/iptables + + + + + + + Restrict ICMP Message Types + In /etc/sysconfig/iptables, the accepted ICMP messages +types can be restricted. To accept only ICMP echo reply, destination +unreachable, and time exceeded messages, remove the line: +-A INPUT -p icmp --icmp-type any -j ACCEPT +and insert the lines: +-A INPUT -p icmp --icmp-type echo-reply -j ACCEPT +-A INPUT -p icmp --icmp-type destination-unreachable -j ACCEPT +-A INPUT -p icmp --icmp-type time-exceeded -j ACCEPT +To allow the system to respond to pings, also insert the following line: +-A INPUT -p icmp --icmp-type echo-request -j ACCEPT +Ping responses can also be limited to certain networks or hosts by using the -s +option in the previous rule. Because IPv6 depends so heavily on ICMPv6, it is +preferable to deny the ICMPv6 packets you know you don't need (e.g. ping +requests) in /etc/sysconfig/ip6tables, while letting everything else +through: +-A INPUT -p icmpv6 --icmpv6-type echo-request -j DROP +If you are going to statically configure the system's address, it should +ignore Router Advertisements which could add another IPv6 address to the +interface or alter important network settings: +-A INPUT -p icmpv6 --icmpv6-type router-advertisement -j DROP +Restricting ICMPv6 message types in /etc/sysconfig/ip6tables is not +recommended because the operation of IPv6 depends heavily on ICMPv6. Thus, great +care must be taken if any other ICMPv6 types are blocked. + + + Log and Drop Packets with Suspicious Source Addresses + Packets with non-routable source addresses should be rejected, as they may indicate spoofing. Because the +modified policy will reject non-matching packets, you only need to add these rules if you are interested in also +logging these spoofing or suspicious attempts before they are dropped. If you do choose to log various suspicious +traffic, add identical rules with a target of DROP after each LOG. +To log and then drop these IPv4 packets, insert the following rules in /etc/sysconfig/iptables (excepting +any that are intentionally used): +-A INPUT -s 10.0.0.0/8 -j LOG --log-prefix "IP DROP SPOOF A: " +-A INPUT -s 172.16.0.0/12 -j LOG --log-prefix "IP DROP SPOOF B: " +-A INPUT -s 192.168.0.0/16 -j LOG --log-prefix "IP DROP SPOOF C: " +-A INPUT -s 224.0.0.0/4 -j LOG --log-prefix "IP DROP MULTICAST D: " +-A INPUT -s 240.0.0.0/5 -j LOG --log-prefix "IP DROP SPOOF E: " +-A INPUT -d 127.0.0.0/8 -j LOG --log-prefix "IP DROP LOOPBACK: " +Similarly, you might wish to log packets containing some IPv6 reserved addresses if they are not expected +on your network: +-A INPUT -i eth0 -s ::1 -j LOG --log-prefix "IPv6 DROP LOOPBACK: " +-A INPUT -s 2002:E000::/20 -j LOG --log-prefix "IPv6 6to4 TRAFFIC: " +-A INPUT -s 2002:7F00::/24 -j LOG --log-prefix "IPv6 6to4 TRAFFIC: " +-A INPUT -s 2002:0000::/24 -j LOG --log-prefix "IPv6 6to4 TRAFFIC: " +-A INPUT -s 2002:FF00::/24 -j LOG --log-prefix "IPv6 6to4 TRAFFIC: " +-A INPUT -s 2002:0A00::/24 -j LOG --log-prefix "IPv6 6to4 TRAFFIC: " +-A INPUT -s 2002:AC10::/28 -j LOG --log-prefix "IPv6 6to4 TRAFFIC: " +-A INPUT -s 2002:C0A8::/32 -j LOG --log-prefix "IPv6 6to4 TRAFFIC: " +If you are not expecting to see site-local multicast or auto-tunneled traffic, you can log those: +-A INPUT -s FF05::/16 -j LOG --log-prefix "IPv6 SITE-LOCAL MULTICAST: " +-A INPUT -s ::0.0.0.0/96 -j LOG --log-prefix "IPv4 COMPATIBLE IPv6 ADDR: " +If you wish to block multicasts to all link-local nodes (e.g. if you are not using router auto-configuration and +do not plan to have any services that multicast to the entire local network), you can block the link-local +all-nodes multicast address (before accepting incoming ICMPv6): +-A INPUT -d FF02::1 -j LOG --log-prefix "Link-local All-Nodes Multicast: " +However, if you're going to allow IPv4 compatible IPv6 addresses (of the form ::0.0.0.0/96), you should +then consider logging the non-routable IPv4-compatible addresses: +-A INPUT -s ::0.0.0.0/104 -j LOG --log-prefix "IP NON-ROUTABLE ADDR: " +-A INPUT -s ::127.0.0.0/104 -j LOG --log-prefix "IP DROP LOOPBACK: " +-A INPUT -s ::224.0.0.0.0/100 -j LOG --log-prefix "IP DROP MULTICAST D: " +-A INPUT -s ::255.0.0.0/104 -j LOG --log-prefix "IP BROADCAST: " +If you are not expecting to see any IPv4 (or IPv4-compatible) traffic on your network, consider logging it before it gets dropped: +-A INPUT -s ::FFFF:0.0.0.0/96 -j LOG --log-prefix "IPv4 MAPPED IPv6 ADDR: " +-A INPUT -s 2002::/16 -j LOG --log-prefix "IPv6 6to4 ADDR: " +The following rule will log all traffic originating from a site-local address, which is deprecated address space: +-A INPUT -s FEC0::/10 -j LOG --log-prefix "SITE-LOCAL ADDRESS TRAFFIC: " + + + + + IPv6 + The system includes support for Internet Protocol +version 6. A major and often-mentioned improvement over IPv4 is its +enormous increase in the number of available addresses. Another +important feature is its support for automatic configuration of +many network settings. + + Disable Support for IPv6 Unless Needed + Despite configuration that suggests support for IPv6 has +been disabled, link-local IPv6 address auto-configuration occurs +even when only an IPv4 address is assigned. The only way to +effectively prevent execution of the IPv6 networking stack is to +instruct the system not to activate the IPv6 kernel module. + + Ensure IPv6 is disabled through kernel boot parameter + To disable IPv6 protocol support in the Linux kernel, +add the argument ipv6.disable=1 to the default +GRUB2 command line for the Linux operating system. +To ensure that ipv6.disable=1 is added as a kernel command line +argument to newly installed kernels, add ipv6.disable=1 to the +default Grub2 command line for Linux operating systems. Modify the line within +/etc/default/grub as shown below: +GRUB_CMDLINE_LINUX="... ipv6.disable=1 ..." +Run the following command to update command line for already installed kernels:# grubby --update-kernel=ALL --args="ipv6.disable=1" + Any unnecessary network stacks, including IPv6, should be disabled to reduce +the vulnerability to exploitation. + + - name: Gather the package facts + package_facts: + manager: auto + tags: + - grub2_ipv6_disable_argument + - low_disruption + - low_severity + - medium_complexity + - reboot_required + - restrict_strategy + +- name: Update grub defaults and the bootloader menu + command: /sbin/grubby --update-kernel=ALL --args="ipv6.disable=1" + when: '"grub2-common" in ansible_facts.packages' + tags: + - grub2_ipv6_disable_argument + - low_disruption + - low_severity + - medium_complexity + - reboot_required + - restrict_strategy + + [customizations.kernel] +append = "ipv6.disable=1" + + # Remediation is applicable only in certain platforms +if rpm --quiet -q grub2-common; then + +grubby --update-kernel=ALL --args=ipv6.disable=1 + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable IPv6 Networking Support Automatic Loading + To prevent the IPv6 kernel module (ipv6) from binding to the +IPv6 networking stack, add the following line to +/etc/modprobe.d/disabled.conf (or another file in +/etc/modprobe.d): +options ipv6 disable=1 +This permits the IPv6 module to be loaded (and thus satisfy other modules that +depend on it), while disabling support for the IPv6 protocol. + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + PR.PT-3 + Any unnecessary network stacks - including IPv6 - should be disabled, to reduce +the vulnerability to exploitation. + + CCE-84024-9 + - name: Disable IPv6 Networking kernel module + lineinfile: + create: true + dest: /etc/modprobe.d/ipv6.conf + regexp: ^options\s+ipv6\s+disable=\d + line: options ipv6 disable=1 + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84024-9 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - kernel_module_ipv6_option_disabled + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + +- name: Ensure disable_ipv6 (all and default) is set to 1 + sysctl: + name: '{{ item }}' + value: '1' + state: present + reload: true + with_items: + - net.ipv6.conf.all.disable_ipv6 + - net.ipv6.conf.default.disable_ipv6 + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84024-9 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - kernel_module_ipv6_option_disabled + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Prevent the IPv6 kernel module (ipv6) from loading the IPv6 networking stack +echo "options ipv6 disable=1" > /etc/modprobe.d/ipv6.conf + +# Since according to: https://access.redhat.com/solutions/72733 +# "ipv6 disable=1" options doesn't always disable the IPv6 networking stack from +# loading, instruct also sysctl configuration to disable IPv6 according to: +# https://access.redhat.com/solutions/8709#rhel6disable + +declare -a IPV6_SETTINGS=("net.ipv6.conf.all.disable_ipv6" "net.ipv6.conf.default.disable_ipv6") + +for setting in "${IPV6_SETTINGS[@]}" +do + # Set runtime =1 for setting + /sbin/sysctl -q -n -w "$setting=1" + + # If setting is present in /etc/sysctl.conf, change value to "1" + # else, add "$setting = 1" to /etc/sysctl.conf + if grep -q ^"$setting" /etc/sysctl.conf ; then + sed -i "s/^$setting.*/$setting = 1/g" /etc/sysctl.conf + else + echo "" >> /etc/sysctl.conf + echo "# Set $setting = 1 per security requirements" >> /etc/sysctl.conf + echo "$setting = 1" >> /etc/sysctl.conf + fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable IPv6 Addressing on All IPv6 Interfaces + To disable support for (ipv6) addressing on all interface add the following line to +/etc/sysctl.d/ipv6.conf (or another file in /etc/sysctl.d): +net.ipv6.conf.all.disable_ipv6 = 1 +This disables IPv6 on all network interfaces as other services and system +functionality require the IPv6 stack loaded to work. + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + 3.1.20 + CCI-001551 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + PR.PT-3 + Any unnecessary network stacks - including IPv6 - should be disabled, to reduce +the vulnerability to exploitation. + + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.ipv6.conf.all.disable_ipv6.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv6_conf_all_disable_ipv6 + +- name: Comment out any occurrences of net.ipv6.conf.all.disable_ipv6 from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.ipv6.conf.all.disable_ipv6 + replace: '#net.ipv6.conf.all.disable_ipv6' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv6_conf_all_disable_ipv6 + +- name: Ensure sysctl net.ipv6.conf.all.disable_ipv6 is set to 1 + sysctl: + name: net.ipv6.conf.all.disable_ipv6 + value: '1' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv6_conf_all_disable_ipv6 + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.ipv6.conf.all.disable_ipv6 from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.ipv6.conf.all.disable_ipv6.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.ipv6.conf.all.disable_ipv6" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done + +# +# Set runtime for net.ipv6.conf.all.disable_ipv6 +# +/sbin/sysctl -q -n -w net.ipv6.conf.all.disable_ipv6="1" + +# +# If net.ipv6.conf.all.disable_ipv6 present in /etc/sysctl.conf, change value to "1" +# else, add "net.ipv6.conf.all.disable_ipv6 = 1" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv6.conf.all.disable_ipv6") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "1" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.ipv6.conf.all.disable_ipv6\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.ipv6.conf.all.disable_ipv6\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable IPv6 Addressing on IPv6 Interfaces by Default + To disable support for (ipv6) addressing on interfaces by default add the following line to +/etc/sysctl.d/ipv6.conf (or another file in /etc/sysctl.d): +net.ipv6.conf.default.disable_ipv6 = 1 +This disables IPv6 on network interfaces by default as other services and system +functionality require the IPv6 stack loaded to work. + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + 3.1.20 + CCI-001551 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + PR.PT-3 + Any unnecessary network stacks - including IPv6 - should be disabled, to reduce +the vulnerability to exploitation. + + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.ipv6.conf.default.disable_ipv6.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv6_conf_default_disable_ipv6 + +- name: Comment out any occurrences of net.ipv6.conf.default.disable_ipv6 from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.ipv6.conf.default.disable_ipv6 + replace: '#net.ipv6.conf.default.disable_ipv6' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv6_conf_default_disable_ipv6 + +- name: Ensure sysctl net.ipv6.conf.default.disable_ipv6 is set to 1 + sysctl: + name: net.ipv6.conf.default.disable_ipv6 + value: '1' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv6_conf_default_disable_ipv6 + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.ipv6.conf.default.disable_ipv6 from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.ipv6.conf.default.disable_ipv6.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.ipv6.conf.default.disable_ipv6" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done + +# +# Set runtime for net.ipv6.conf.default.disable_ipv6 +# +/sbin/sysctl -q -n -w net.ipv6.conf.default.disable_ipv6="1" + +# +# If net.ipv6.conf.default.disable_ipv6 present in /etc/sysctl.conf, change value to "1" +# else, add "net.ipv6.conf.default.disable_ipv6 = 1" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv6.conf.default.disable_ipv6") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "1" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.ipv6.conf.default.disable_ipv6\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.ipv6.conf.default.disable_ipv6\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Configure IPv6 Settings if Necessary + A major feature of IPv6 is the extent to which systems +implementing it can automatically configure their networking +devices using information from the network. From a security +perspective, manually configuring important configuration +information is preferable to accepting it from the network +in an unauthenticated fashion. + + IPV6_AUTOCONF + Toggle global IPv6 auto-configuration (only, if global +forwarding is disabled) + no + no + yes + + + net.ipv6.conf.all.accept_ra_defrtr + Accept default router in router advertisements? + 0 + 0 + 1 + + + net.ipv6.conf.all.accept_ra_pinfo + Accept prefix information in router advertisements? + 0 + 0 + 1 + + + net.ipv6.conf.all.accept_ra_rtr_pref + Accept router preference in router advertisements? + 0 + 0 + 1 + + + net.ipv6.conf.all.accept_ra + Accept all router advertisements? + 0 + 0 + 1 + + + net.ipv6.conf.all.accept_redirects + Toggle ICMP Redirect Acceptance + 0 + 0 + 1 + + + net.ipv6.conf.all.accept_source_route + Trackers could be using source-routed packets to +generate traffic that seems to be intra-net, but actually was +created outside and has been redirected. + 0 + 0 + 1 + + + net.ipv6.conf.all.autoconf + Enable auto configuration on IPv6 interfaces + 0 + 0 + 1 + + + net.ipv6.conf.all.forwarding + Toggle IPv6 Forwarding + 0 + 0 + 1 + + + net.ipv6.conf.all.max_addresses + Maximum number of autoconfigured IPv6 addresses + 1 + + + net.ipv6.conf.all.router_solicitations + Accept all router solicitations? + 0 + 0 + 1 + + + net.ipv6.conf.default.accept_ra_defrtr + Accept default router in router advertisements? + 0 + 0 + 1 + + + net.ipv6.conf.default.accept_ra_pinfo + Accept prefix information in router advertisements? + 0 + 0 + 1 + + + net.ipv6.conf.default.accept_ra_rtr_pref + Accept router preference in router advertisements? + 0 + 0 + 1 + + + net.ipv6.conf.default.accept_ra + Accept default router advertisements by default? + 0 + 0 + 1 + + + net.ipv6.conf.default.accept_redirects + Toggle ICMP Redirect Acceptance By Default + 0 + 0 + 1 + + + net.ipv6.conf.default.accept_source_route + Trackers could be using source-routed packets to +generate traffic that seems to be intra-net, but actually was +created outside and has been redirected. + 0 + 0 + 1 + + + net.ipv6.conf.default.autoconf + Enable auto configuration on IPv6 interfaces + 0 + 0 + 1 + + + net.ipv6.conf.default.forwarding + Toggle IPv6 default Forwarding + 0 + 0 + 1 + + + net.ipv6.conf.default.max_addresses + Maximum number of autoconfigured IPv6 addresses + 1 + + + net.ipv6.conf.default.router_solicitations + Accept all router solicitations by default? + 0 + 0 + 1 + + + Configure Accepting Router Advertisements on All IPv6 Interfaces + To set the runtime status of the net.ipv6.conf.all.accept_ra kernel parameter, run the following command: $ sudo sysctl -w net.ipv6.conf.all.accept_ra=0 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: net.ipv6.conf.all.accept_ra = 0 + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + 3.1.20 + CCI-000366 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + PR.PT-3 + SRG-OS-000480-GPOS-00227 + An illicit router advertisement message could result in a man-in-the-middle attack. + + CCE-84120-5 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.ipv6.conf.all.accept_ra.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84120-5 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv6_conf_all_accept_ra + +- name: Comment out any occurrences of net.ipv6.conf.all.accept_ra from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.ipv6.conf.all.accept_ra + replace: '#net.ipv6.conf.all.accept_ra' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84120-5 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv6_conf_all_accept_ra +- name: XCCDF Value sysctl_net_ipv6_conf_all_accept_ra_value # promote to variable + set_fact: + sysctl_net_ipv6_conf_all_accept_ra_value: !!str + tags: + - always + +- name: Ensure sysctl net.ipv6.conf.all.accept_ra is set + sysctl: + name: net.ipv6.conf.all.accept_ra + value: '{{ sysctl_net_ipv6_conf_all_accept_ra_value }}' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84120-5 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv6_conf_all_accept_ra + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,net.ipv6.conf.all.accept_ra%3D0%0A + mode: 0644 + path: /etc/sysctl.d/75-sysctl_net_ipv6_conf_all_accept_ra.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.ipv6.conf.all.accept_ra from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.ipv6.conf.all.accept_ra.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.ipv6.conf.all.accept_ra" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done +sysctl_net_ipv6_conf_all_accept_ra_value='' + + +# +# Set runtime for net.ipv6.conf.all.accept_ra +# +/sbin/sysctl -q -n -w net.ipv6.conf.all.accept_ra="$sysctl_net_ipv6_conf_all_accept_ra_value" + +# +# If net.ipv6.conf.all.accept_ra present in /etc/sysctl.conf, change value to appropriate value +# else, add "net.ipv6.conf.all.accept_ra = value" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv6.conf.all.accept_ra") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv6_conf_all_accept_ra_value" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.ipv6.conf.all.accept_ra\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.ipv6.conf.all.accept_ra\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-84120-5" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Configure Accepting Default Router in Router Advertisements on All IPv6 Interfaces + To set the runtime status of the net.ipv6.conf.all.accept_ra_defrtr kernel parameter, run the following command: $ sudo sysctl -w net.ipv6.conf.all.accept_ra_defrtr=0 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: net.ipv6.conf.all.accept_ra_defrtr = 0 + BP28(R22) + An illicit router advertisement message could result in a man-in-the-middle attack. + + CCE-84115-5 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.ipv6.conf.all.accept_ra_defrtr.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84115-5 + - disable_strategy + - low_complexity + - medium_disruption + - reboot_required + - sysctl_net_ipv6_conf_all_accept_ra_defrtr + - unknown_severity + +- name: Comment out any occurrences of net.ipv6.conf.all.accept_ra_defrtr from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.ipv6.conf.all.accept_ra_defrtr + replace: '#net.ipv6.conf.all.accept_ra_defrtr' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84115-5 + - disable_strategy + - low_complexity + - medium_disruption + - reboot_required + - sysctl_net_ipv6_conf_all_accept_ra_defrtr + - unknown_severity +- name: XCCDF Value sysctl_net_ipv6_conf_all_accept_ra_defrtr_value # promote to variable + set_fact: + sysctl_net_ipv6_conf_all_accept_ra_defrtr_value: !!str + tags: + - always + +- name: Ensure sysctl net.ipv6.conf.all.accept_ra_defrtr is set + sysctl: + name: net.ipv6.conf.all.accept_ra_defrtr + value: '{{ sysctl_net_ipv6_conf_all_accept_ra_defrtr_value }}' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84115-5 + - disable_strategy + - low_complexity + - medium_disruption + - reboot_required + - sysctl_net_ipv6_conf_all_accept_ra_defrtr + - unknown_severity + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.ipv6.conf.all.accept_ra_defrtr from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.ipv6.conf.all.accept_ra_defrtr.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.ipv6.conf.all.accept_ra_defrtr" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done +sysctl_net_ipv6_conf_all_accept_ra_defrtr_value='' + + +# +# Set runtime for net.ipv6.conf.all.accept_ra_defrtr +# +/sbin/sysctl -q -n -w net.ipv6.conf.all.accept_ra_defrtr="$sysctl_net_ipv6_conf_all_accept_ra_defrtr_value" + +# +# If net.ipv6.conf.all.accept_ra_defrtr present in /etc/sysctl.conf, change value to appropriate value +# else, add "net.ipv6.conf.all.accept_ra_defrtr = value" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv6.conf.all.accept_ra_defrtr") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv6_conf_all_accept_ra_defrtr_value" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.ipv6.conf.all.accept_ra_defrtr\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.ipv6.conf.all.accept_ra_defrtr\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-84115-5" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Configure Accepting Prefix Information in Router Advertisements on All IPv6 Interfaces + To set the runtime status of the net.ipv6.conf.all.accept_ra_pinfo kernel parameter, run the following command: $ sudo sysctl -w net.ipv6.conf.all.accept_ra_pinfo=0 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: net.ipv6.conf.all.accept_ra_pinfo = 0 + BP28(R22) + An illicit router advertisement message could result in a man-in-the-middle attack. + + CCE-84122-1 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.ipv6.conf.all.accept_ra_pinfo.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84122-1 + - disable_strategy + - low_complexity + - medium_disruption + - reboot_required + - sysctl_net_ipv6_conf_all_accept_ra_pinfo + - unknown_severity + +- name: Comment out any occurrences of net.ipv6.conf.all.accept_ra_pinfo from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.ipv6.conf.all.accept_ra_pinfo + replace: '#net.ipv6.conf.all.accept_ra_pinfo' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84122-1 + - disable_strategy + - low_complexity + - medium_disruption + - reboot_required + - sysctl_net_ipv6_conf_all_accept_ra_pinfo + - unknown_severity +- name: XCCDF Value sysctl_net_ipv6_conf_all_accept_ra_pinfo_value # promote to variable + set_fact: + sysctl_net_ipv6_conf_all_accept_ra_pinfo_value: !!str + tags: + - always + +- name: Ensure sysctl net.ipv6.conf.all.accept_ra_pinfo is set + sysctl: + name: net.ipv6.conf.all.accept_ra_pinfo + value: '{{ sysctl_net_ipv6_conf_all_accept_ra_pinfo_value }}' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84122-1 + - disable_strategy + - low_complexity + - medium_disruption + - reboot_required + - sysctl_net_ipv6_conf_all_accept_ra_pinfo + - unknown_severity + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.ipv6.conf.all.accept_ra_pinfo from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.ipv6.conf.all.accept_ra_pinfo.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.ipv6.conf.all.accept_ra_pinfo" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done +sysctl_net_ipv6_conf_all_accept_ra_pinfo_value='' + + +# +# Set runtime for net.ipv6.conf.all.accept_ra_pinfo +# +/sbin/sysctl -q -n -w net.ipv6.conf.all.accept_ra_pinfo="$sysctl_net_ipv6_conf_all_accept_ra_pinfo_value" + +# +# If net.ipv6.conf.all.accept_ra_pinfo present in /etc/sysctl.conf, change value to appropriate value +# else, add "net.ipv6.conf.all.accept_ra_pinfo = value" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv6.conf.all.accept_ra_pinfo") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv6_conf_all_accept_ra_pinfo_value" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.ipv6.conf.all.accept_ra_pinfo\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.ipv6.conf.all.accept_ra_pinfo\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-84122-1" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Configure Accepting Router Preference in Router Advertisements on All IPv6 Interfaces + To set the runtime status of the net.ipv6.conf.all.accept_ra_rtr_pref kernel parameter, run the following command: $ sudo sysctl -w net.ipv6.conf.all.accept_ra_rtr_pref=0 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: net.ipv6.conf.all.accept_ra_rtr_pref = 0 + BP28(R22) + An illicit router advertisement message could result in a man-in-the-middle attack. + + CCE-84111-4 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.ipv6.conf.all.accept_ra_rtr_pref.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84111-4 + - disable_strategy + - low_complexity + - medium_disruption + - reboot_required + - sysctl_net_ipv6_conf_all_accept_ra_rtr_pref + - unknown_severity + +- name: Comment out any occurrences of net.ipv6.conf.all.accept_ra_rtr_pref from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.ipv6.conf.all.accept_ra_rtr_pref + replace: '#net.ipv6.conf.all.accept_ra_rtr_pref' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84111-4 + - disable_strategy + - low_complexity + - medium_disruption + - reboot_required + - sysctl_net_ipv6_conf_all_accept_ra_rtr_pref + - unknown_severity +- name: XCCDF Value sysctl_net_ipv6_conf_all_accept_ra_rtr_pref_value # promote to variable + set_fact: + sysctl_net_ipv6_conf_all_accept_ra_rtr_pref_value: !!str + tags: + - always + +- name: Ensure sysctl net.ipv6.conf.all.accept_ra_rtr_pref is set + sysctl: + name: net.ipv6.conf.all.accept_ra_rtr_pref + value: '{{ sysctl_net_ipv6_conf_all_accept_ra_rtr_pref_value }}' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84111-4 + - disable_strategy + - low_complexity + - medium_disruption + - reboot_required + - sysctl_net_ipv6_conf_all_accept_ra_rtr_pref + - unknown_severity + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.ipv6.conf.all.accept_ra_rtr_pref from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.ipv6.conf.all.accept_ra_rtr_pref.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.ipv6.conf.all.accept_ra_rtr_pref" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done +sysctl_net_ipv6_conf_all_accept_ra_rtr_pref_value='' + + +# +# Set runtime for net.ipv6.conf.all.accept_ra_rtr_pref +# +/sbin/sysctl -q -n -w net.ipv6.conf.all.accept_ra_rtr_pref="$sysctl_net_ipv6_conf_all_accept_ra_rtr_pref_value" + +# +# If net.ipv6.conf.all.accept_ra_rtr_pref present in /etc/sysctl.conf, change value to appropriate value +# else, add "net.ipv6.conf.all.accept_ra_rtr_pref = value" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv6.conf.all.accept_ra_rtr_pref") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv6_conf_all_accept_ra_rtr_pref_value" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.ipv6.conf.all.accept_ra_rtr_pref\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.ipv6.conf.all.accept_ra_rtr_pref\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-84111-4" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable Accepting ICMP Redirects for All IPv6 Interfaces + To set the runtime status of the net.ipv6.conf.all.accept_redirects kernel parameter, run the following command: $ sudo sysctl -w net.ipv6.conf.all.accept_redirects=0 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: net.ipv6.conf.all.accept_redirects = 0 + BP28(R22) + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + 3.1.20 + CCI-000366 + CCI-001551 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + CM-6(b) + CM-6.1(iv) + PR.IP-1 + PR.PT-3 + SRG-OS-000480-GPOS-00227 + An illicit ICMP redirect message could result in a man-in-the-middle attack. + + CCE-84125-4 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.ipv6.conf.all.accept_redirects.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84125-4 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-6(b) + - NIST-800-53-CM-6.1(iv) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv6_conf_all_accept_redirects + +- name: Comment out any occurrences of net.ipv6.conf.all.accept_redirects from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.ipv6.conf.all.accept_redirects + replace: '#net.ipv6.conf.all.accept_redirects' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84125-4 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-6(b) + - NIST-800-53-CM-6.1(iv) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv6_conf_all_accept_redirects +- name: XCCDF Value sysctl_net_ipv6_conf_all_accept_redirects_value # promote to variable + set_fact: + sysctl_net_ipv6_conf_all_accept_redirects_value: !!str + tags: + - always + +- name: Ensure sysctl net.ipv6.conf.all.accept_redirects is set + sysctl: + name: net.ipv6.conf.all.accept_redirects + value: '{{ sysctl_net_ipv6_conf_all_accept_redirects_value }}' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84125-4 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-6(b) + - NIST-800-53-CM-6.1(iv) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv6_conf_all_accept_redirects + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,net.ipv6.conf.all.accept_redirects%3D0%0A + mode: 0644 + path: /etc/sysctl.d/75-sysctl_net_ipv6_conf_all_accept_redirects.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.ipv6.conf.all.accept_redirects from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.ipv6.conf.all.accept_redirects.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.ipv6.conf.all.accept_redirects" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done +sysctl_net_ipv6_conf_all_accept_redirects_value='' + + +# +# Set runtime for net.ipv6.conf.all.accept_redirects +# +/sbin/sysctl -q -n -w net.ipv6.conf.all.accept_redirects="$sysctl_net_ipv6_conf_all_accept_redirects_value" + +# +# If net.ipv6.conf.all.accept_redirects present in /etc/sysctl.conf, change value to appropriate value +# else, add "net.ipv6.conf.all.accept_redirects = value" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv6.conf.all.accept_redirects") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv6_conf_all_accept_redirects_value" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.ipv6.conf.all.accept_redirects\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.ipv6.conf.all.accept_redirects\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-84125-4" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable Kernel Parameter for Accepting Source-Routed Packets on all IPv6 Interfaces + To set the runtime status of the net.ipv6.conf.all.accept_source_route kernel parameter, run the following command: $ sudo sysctl -w net.ipv6.conf.all.accept_source_route=0 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: net.ipv6.conf.all.accept_source_route = 0 + BP28(R22) + 1 + 12 + 13 + 14 + 15 + 16 + 18 + 4 + 6 + 8 + 9 + APO01.06 + APO13.01 + DSS01.05 + DSS03.01 + DSS05.02 + DSS05.04 + DSS05.07 + DSS06.02 + 3.1.20 + CCI-000366 + 4.2.3.4 + 4.3.3.4 + 4.4.3.3 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.12.1.1 + A.12.1.2 + A.13.1.1 + A.13.1.2 + A.13.1.3 + A.13.2.1 + A.13.2.2 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-7(a) + CM-7(b) + CM-6(a) + DE.AE-1 + ID.AM-3 + PR.AC-5 + PR.DS-5 + PR.PT-4 + SRG-OS-000480-GPOS-00227 + Source-routed packets allow the source of the packet to suggest routers +forward the packet along a different path than configured on the router, which can +be used to bypass network security measures. This requirement applies only to the +forwarding of source-routerd traffic, such as when IPv6 forwarding is enabled and +the system is functioning as a router. + +Accepting source-routed packets in the IPv6 protocol has few legitimate +uses. It should be disabled unless it is absolutely required. + + CCE-84131-2 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.ipv6.conf.all.accept_source_route.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84131-2 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv6_conf_all_accept_source_route + +- name: Comment out any occurrences of net.ipv6.conf.all.accept_source_route from + /etc/sysctl.d/*.conf files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.ipv6.conf.all.accept_source_route + replace: '#net.ipv6.conf.all.accept_source_route' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84131-2 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv6_conf_all_accept_source_route +- name: XCCDF Value sysctl_net_ipv6_conf_all_accept_source_route_value # promote to variable + set_fact: + sysctl_net_ipv6_conf_all_accept_source_route_value: !!str + tags: + - always + +- name: Ensure sysctl net.ipv6.conf.all.accept_source_route is set + sysctl: + name: net.ipv6.conf.all.accept_source_route + value: '{{ sysctl_net_ipv6_conf_all_accept_source_route_value }}' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84131-2 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv6_conf_all_accept_source_route + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,net.ipv6.conf.all.accept_source_route%3D0%0A + mode: 0644 + path: /etc/sysctl.d/75-sysctl_net_ipv6_conf_all_accept_source_route.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.ipv6.conf.all.accept_source_route from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.ipv6.conf.all.accept_source_route.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.ipv6.conf.all.accept_source_route" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done +sysctl_net_ipv6_conf_all_accept_source_route_value='' + + +# +# Set runtime for net.ipv6.conf.all.accept_source_route +# +/sbin/sysctl -q -n -w net.ipv6.conf.all.accept_source_route="$sysctl_net_ipv6_conf_all_accept_source_route_value" + +# +# If net.ipv6.conf.all.accept_source_route present in /etc/sysctl.conf, change value to appropriate value +# else, add "net.ipv6.conf.all.accept_source_route = value" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv6.conf.all.accept_source_route") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv6_conf_all_accept_source_route_value" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.ipv6.conf.all.accept_source_route\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.ipv6.conf.all.accept_source_route\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-84131-2" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Configure Auto Configuration on All IPv6 Interfaces + To set the runtime status of the net.ipv6.conf.all.autoconf kernel parameter, run the following command: $ sudo sysctl -w net.ipv6.conf.all.autoconf=0 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: net.ipv6.conf.all.autoconf = 0 + BP28(R22) + An illicit router advertisement message could result in a man-in-the-middle attack. + + CCE-84126-2 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.ipv6.conf.all.autoconf.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84126-2 + - disable_strategy + - low_complexity + - medium_disruption + - reboot_required + - sysctl_net_ipv6_conf_all_autoconf + - unknown_severity + +- name: Comment out any occurrences of net.ipv6.conf.all.autoconf from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.ipv6.conf.all.autoconf + replace: '#net.ipv6.conf.all.autoconf' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84126-2 + - disable_strategy + - low_complexity + - medium_disruption + - reboot_required + - sysctl_net_ipv6_conf_all_autoconf + - unknown_severity +- name: XCCDF Value sysctl_net_ipv6_conf_all_autoconf_value # promote to variable + set_fact: + sysctl_net_ipv6_conf_all_autoconf_value: !!str + tags: + - always + +- name: Ensure sysctl net.ipv6.conf.all.autoconf is set + sysctl: + name: net.ipv6.conf.all.autoconf + value: '{{ sysctl_net_ipv6_conf_all_autoconf_value }}' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84126-2 + - disable_strategy + - low_complexity + - medium_disruption + - reboot_required + - sysctl_net_ipv6_conf_all_autoconf + - unknown_severity + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.ipv6.conf.all.autoconf from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.ipv6.conf.all.autoconf.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.ipv6.conf.all.autoconf" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done +sysctl_net_ipv6_conf_all_autoconf_value='' + + +# +# Set runtime for net.ipv6.conf.all.autoconf +# +/sbin/sysctl -q -n -w net.ipv6.conf.all.autoconf="$sysctl_net_ipv6_conf_all_autoconf_value" + +# +# If net.ipv6.conf.all.autoconf present in /etc/sysctl.conf, change value to appropriate value +# else, add "net.ipv6.conf.all.autoconf = value" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv6.conf.all.autoconf") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv6_conf_all_autoconf_value" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.ipv6.conf.all.autoconf\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.ipv6.conf.all.autoconf\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-84126-2" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable Kernel Parameter for IPv6 Forwarding + To set the runtime status of the net.ipv6.conf.all.forwarding kernel parameter, run the following command: $ sudo sysctl -w net.ipv6.conf.all.forwarding=0 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: net.ipv6.conf.all.forwarding = 0 + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 2 + 3 + 7 + 8 + 9 + APO13.01 + BAI04.04 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.03 + DSS03.05 + DSS05.02 + DSS05.05 + DSS05.07 + DSS06.06 + CCI-000366 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 6.2 + SR 7.1 + SR 7.2 + SR 7.6 + A.12.1.2 + A.12.1.3 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.17.2.1 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + CM-6(b) + CM-6.1(iv) + DE.CM-1 + PR.DS-4 + PR.IP-1 + PR.PT-3 + SRG-OS-000480-GPOS-00227 + IP forwarding permits the kernel to forward packets from one network +interface to another. The ability to forward packets between two networks is +only appropriate for systems acting as routers. + + CCE-84114-8 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.ipv6.conf.all.forwarding.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84114-8 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-6(b) + - NIST-800-53-CM-6.1(iv) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv6_conf_all_forwarding + +- name: Comment out any occurrences of net.ipv6.conf.all.forwarding from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.ipv6.conf.all.forwarding + replace: '#net.ipv6.conf.all.forwarding' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84114-8 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-6(b) + - NIST-800-53-CM-6.1(iv) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv6_conf_all_forwarding +- name: XCCDF Value sysctl_net_ipv6_conf_all_forwarding_value # promote to variable + set_fact: + sysctl_net_ipv6_conf_all_forwarding_value: !!str + tags: + - always + +- name: Ensure sysctl net.ipv6.conf.all.forwarding is set + sysctl: + name: net.ipv6.conf.all.forwarding + value: '{{ sysctl_net_ipv6_conf_all_forwarding_value }}' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84114-8 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-6(b) + - NIST-800-53-CM-6.1(iv) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv6_conf_all_forwarding + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.ipv6.conf.all.forwarding from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.ipv6.conf.all.forwarding.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.ipv6.conf.all.forwarding" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done +sysctl_net_ipv6_conf_all_forwarding_value='' + + +# +# Set runtime for net.ipv6.conf.all.forwarding +# +/sbin/sysctl -q -n -w net.ipv6.conf.all.forwarding="$sysctl_net_ipv6_conf_all_forwarding_value" + +# +# If net.ipv6.conf.all.forwarding present in /etc/sysctl.conf, change value to appropriate value +# else, add "net.ipv6.conf.all.forwarding = value" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv6.conf.all.forwarding") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv6_conf_all_forwarding_value" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.ipv6.conf.all.forwarding\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.ipv6.conf.all.forwarding\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-84114-8" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Configure Maximum Number of Autoconfigured Addresses on All IPv6 Interfaces + To set the runtime status of the net.ipv6.conf.all.max_addresses kernel parameter, run the following command: $ sudo sysctl -w net.ipv6.conf.all.max_addresses=1 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: net.ipv6.conf.all.max_addresses = 1 + BP28(R22) + The number of global unicast IPv6 addresses for each interface should be limited exactly to the number of statically configured addresses. + + CCE-84112-2 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.ipv6.conf.all.max_addresses.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84112-2 + - disable_strategy + - low_complexity + - medium_disruption + - reboot_required + - sysctl_net_ipv6_conf_all_max_addresses + - unknown_severity + +- name: Comment out any occurrences of net.ipv6.conf.all.max_addresses from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.ipv6.conf.all.max_addresses + replace: '#net.ipv6.conf.all.max_addresses' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84112-2 + - disable_strategy + - low_complexity + - medium_disruption + - reboot_required + - sysctl_net_ipv6_conf_all_max_addresses + - unknown_severity +- name: XCCDF Value sysctl_net_ipv6_conf_all_max_addresses_value # promote to variable + set_fact: + sysctl_net_ipv6_conf_all_max_addresses_value: !!str + tags: + - always + +- name: Ensure sysctl net.ipv6.conf.all.max_addresses is set + sysctl: + name: net.ipv6.conf.all.max_addresses + value: '{{ sysctl_net_ipv6_conf_all_max_addresses_value }}' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84112-2 + - disable_strategy + - low_complexity + - medium_disruption + - reboot_required + - sysctl_net_ipv6_conf_all_max_addresses + - unknown_severity + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.ipv6.conf.all.max_addresses from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.ipv6.conf.all.max_addresses.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.ipv6.conf.all.max_addresses" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done +sysctl_net_ipv6_conf_all_max_addresses_value='' + + +# +# Set runtime for net.ipv6.conf.all.max_addresses +# +/sbin/sysctl -q -n -w net.ipv6.conf.all.max_addresses="$sysctl_net_ipv6_conf_all_max_addresses_value" + +# +# If net.ipv6.conf.all.max_addresses present in /etc/sysctl.conf, change value to appropriate value +# else, add "net.ipv6.conf.all.max_addresses = value" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv6.conf.all.max_addresses") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv6_conf_all_max_addresses_value" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.ipv6.conf.all.max_addresses\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.ipv6.conf.all.max_addresses\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-84112-2" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Configure Denying Router Solicitations on All IPv6 Interfaces + To set the runtime status of the net.ipv6.conf.all.router_solicitations kernel parameter, run the following command: $ sudo sysctl -w net.ipv6.conf.all.router_solicitations=0 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: net.ipv6.conf.all.router_solicitations = 0 + BP28(R22) + To prevent discovery of the system by other systems, router solicitation requests should be denied. + + CCE-84128-8 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.ipv6.conf.all.router_solicitations.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84128-8 + - disable_strategy + - low_complexity + - medium_disruption + - reboot_required + - sysctl_net_ipv6_conf_all_router_solicitations + - unknown_severity + +- name: Comment out any occurrences of net.ipv6.conf.all.router_solicitations from + /etc/sysctl.d/*.conf files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.ipv6.conf.all.router_solicitations + replace: '#net.ipv6.conf.all.router_solicitations' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84128-8 + - disable_strategy + - low_complexity + - medium_disruption + - reboot_required + - sysctl_net_ipv6_conf_all_router_solicitations + - unknown_severity +- name: XCCDF Value sysctl_net_ipv6_conf_all_router_solicitations_value # promote to variable + set_fact: + sysctl_net_ipv6_conf_all_router_solicitations_value: !!str + tags: + - always + +- name: Ensure sysctl net.ipv6.conf.all.router_solicitations is set + sysctl: + name: net.ipv6.conf.all.router_solicitations + value: '{{ sysctl_net_ipv6_conf_all_router_solicitations_value }}' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84128-8 + - disable_strategy + - low_complexity + - medium_disruption + - reboot_required + - sysctl_net_ipv6_conf_all_router_solicitations + - unknown_severity + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.ipv6.conf.all.router_solicitations from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.ipv6.conf.all.router_solicitations.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.ipv6.conf.all.router_solicitations" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done +sysctl_net_ipv6_conf_all_router_solicitations_value='' + + +# +# Set runtime for net.ipv6.conf.all.router_solicitations +# +/sbin/sysctl -q -n -w net.ipv6.conf.all.router_solicitations="$sysctl_net_ipv6_conf_all_router_solicitations_value" + +# +# If net.ipv6.conf.all.router_solicitations present in /etc/sysctl.conf, change value to appropriate value +# else, add "net.ipv6.conf.all.router_solicitations = value" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv6.conf.all.router_solicitations") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv6_conf_all_router_solicitations_value" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.ipv6.conf.all.router_solicitations\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.ipv6.conf.all.router_solicitations\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-84128-8" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable Accepting Router Advertisements on all IPv6 Interfaces by Default + To set the runtime status of the net.ipv6.conf.default.accept_ra kernel parameter, run the following command: $ sudo sysctl -w net.ipv6.conf.default.accept_ra=0 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: net.ipv6.conf.default.accept_ra = 0 + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + 3.1.20 + CCI-000366 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + PR.PT-3 + SRG-OS-000480-GPOS-00227 + An illicit router advertisement message could result in a man-in-the-middle attack. + + CCE-84124-7 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.ipv6.conf.default.accept_ra.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84124-7 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv6_conf_default_accept_ra + +- name: Comment out any occurrences of net.ipv6.conf.default.accept_ra from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.ipv6.conf.default.accept_ra + replace: '#net.ipv6.conf.default.accept_ra' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84124-7 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv6_conf_default_accept_ra +- name: XCCDF Value sysctl_net_ipv6_conf_default_accept_ra_value # promote to variable + set_fact: + sysctl_net_ipv6_conf_default_accept_ra_value: !!str + tags: + - always + +- name: Ensure sysctl net.ipv6.conf.default.accept_ra is set + sysctl: + name: net.ipv6.conf.default.accept_ra + value: '{{ sysctl_net_ipv6_conf_default_accept_ra_value }}' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84124-7 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv6_conf_default_accept_ra + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,net.ipv6.conf.default.accept_ra%3D0%0A + mode: 0644 + path: /etc/sysctl.d/75-sysctl_net_ipv6_conf_default_accept_ra.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.ipv6.conf.default.accept_ra from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.ipv6.conf.default.accept_ra.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.ipv6.conf.default.accept_ra" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done +sysctl_net_ipv6_conf_default_accept_ra_value='' + + +# +# Set runtime for net.ipv6.conf.default.accept_ra +# +/sbin/sysctl -q -n -w net.ipv6.conf.default.accept_ra="$sysctl_net_ipv6_conf_default_accept_ra_value" + +# +# If net.ipv6.conf.default.accept_ra present in /etc/sysctl.conf, change value to appropriate value +# else, add "net.ipv6.conf.default.accept_ra = value" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv6.conf.default.accept_ra") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv6_conf_default_accept_ra_value" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.ipv6.conf.default.accept_ra\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.ipv6.conf.default.accept_ra\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-84124-7" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Configure Accepting Default Router in Router Advertisements on All IPv6 Interfaces By Default + To set the runtime status of the net.ipv6.conf.default.accept_ra_defrtr kernel parameter, run the following command: $ sudo sysctl -w net.ipv6.conf.default.accept_ra_defrtr=0 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: net.ipv6.conf.default.accept_ra_defrtr = 0 + BP28(R22) + An illicit router advertisement message could result in a man-in-the-middle attack. + + CCE-84116-3 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.ipv6.conf.default.accept_ra_defrtr.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84116-3 + - disable_strategy + - low_complexity + - medium_disruption + - reboot_required + - sysctl_net_ipv6_conf_default_accept_ra_defrtr + - unknown_severity + +- name: Comment out any occurrences of net.ipv6.conf.default.accept_ra_defrtr from + /etc/sysctl.d/*.conf files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.ipv6.conf.default.accept_ra_defrtr + replace: '#net.ipv6.conf.default.accept_ra_defrtr' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84116-3 + - disable_strategy + - low_complexity + - medium_disruption + - reboot_required + - sysctl_net_ipv6_conf_default_accept_ra_defrtr + - unknown_severity +- name: XCCDF Value sysctl_net_ipv6_conf_default_accept_ra_defrtr_value # promote to variable + set_fact: + sysctl_net_ipv6_conf_default_accept_ra_defrtr_value: !!str + tags: + - always + +- name: Ensure sysctl net.ipv6.conf.default.accept_ra_defrtr is set + sysctl: + name: net.ipv6.conf.default.accept_ra_defrtr + value: '{{ sysctl_net_ipv6_conf_default_accept_ra_defrtr_value }}' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84116-3 + - disable_strategy + - low_complexity + - medium_disruption + - reboot_required + - sysctl_net_ipv6_conf_default_accept_ra_defrtr + - unknown_severity + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.ipv6.conf.default.accept_ra_defrtr from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.ipv6.conf.default.accept_ra_defrtr.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.ipv6.conf.default.accept_ra_defrtr" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done +sysctl_net_ipv6_conf_default_accept_ra_defrtr_value='' + + +# +# Set runtime for net.ipv6.conf.default.accept_ra_defrtr +# +/sbin/sysctl -q -n -w net.ipv6.conf.default.accept_ra_defrtr="$sysctl_net_ipv6_conf_default_accept_ra_defrtr_value" + +# +# If net.ipv6.conf.default.accept_ra_defrtr present in /etc/sysctl.conf, change value to appropriate value +# else, add "net.ipv6.conf.default.accept_ra_defrtr = value" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv6.conf.default.accept_ra_defrtr") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv6_conf_default_accept_ra_defrtr_value" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.ipv6.conf.default.accept_ra_defrtr\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.ipv6.conf.default.accept_ra_defrtr\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-84116-3" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Configure Accepting Prefix Information in Router Advertisements on All IPv6 Interfaces By Default + To set the runtime status of the net.ipv6.conf.default.accept_ra_pinfo kernel parameter, run the following command: $ sudo sysctl -w net.ipv6.conf.default.accept_ra_pinfo=0 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: net.ipv6.conf.default.accept_ra_pinfo = 0 + BP28(R22) + An illicit router advertisement message could result in a man-in-the-middle attack. + + CCE-84118-9 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.ipv6.conf.default.accept_ra_pinfo.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84118-9 + - disable_strategy + - low_complexity + - medium_disruption + - reboot_required + - sysctl_net_ipv6_conf_default_accept_ra_pinfo + - unknown_severity + +- name: Comment out any occurrences of net.ipv6.conf.default.accept_ra_pinfo from + /etc/sysctl.d/*.conf files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.ipv6.conf.default.accept_ra_pinfo + replace: '#net.ipv6.conf.default.accept_ra_pinfo' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84118-9 + - disable_strategy + - low_complexity + - medium_disruption + - reboot_required + - sysctl_net_ipv6_conf_default_accept_ra_pinfo + - unknown_severity +- name: XCCDF Value sysctl_net_ipv6_conf_default_accept_ra_pinfo_value # promote to variable + set_fact: + sysctl_net_ipv6_conf_default_accept_ra_pinfo_value: !!str + tags: + - always + +- name: Ensure sysctl net.ipv6.conf.default.accept_ra_pinfo is set + sysctl: + name: net.ipv6.conf.default.accept_ra_pinfo + value: '{{ sysctl_net_ipv6_conf_default_accept_ra_pinfo_value }}' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84118-9 + - disable_strategy + - low_complexity + - medium_disruption + - reboot_required + - sysctl_net_ipv6_conf_default_accept_ra_pinfo + - unknown_severity + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.ipv6.conf.default.accept_ra_pinfo from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.ipv6.conf.default.accept_ra_pinfo.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.ipv6.conf.default.accept_ra_pinfo" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done +sysctl_net_ipv6_conf_default_accept_ra_pinfo_value='' + + +# +# Set runtime for net.ipv6.conf.default.accept_ra_pinfo +# +/sbin/sysctl -q -n -w net.ipv6.conf.default.accept_ra_pinfo="$sysctl_net_ipv6_conf_default_accept_ra_pinfo_value" + +# +# If net.ipv6.conf.default.accept_ra_pinfo present in /etc/sysctl.conf, change value to appropriate value +# else, add "net.ipv6.conf.default.accept_ra_pinfo = value" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv6.conf.default.accept_ra_pinfo") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv6_conf_default_accept_ra_pinfo_value" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.ipv6.conf.default.accept_ra_pinfo\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.ipv6.conf.default.accept_ra_pinfo\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-84118-9" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Configure Accepting Router Preference in Router Advertisements on All IPv6 Interfaces By Default + To set the runtime status of the net.ipv6.conf.default.accept_ra_rtr_pref kernel parameter, run the following command: $ sudo sysctl -w net.ipv6.conf.default.accept_ra_rtr_pref=0 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: net.ipv6.conf.default.accept_ra_rtr_pref = 0 + BP28(R22) + An illicit router advertisement message could result in a man-in-the-middle attack. + + CCE-84121-3 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.ipv6.conf.default.accept_ra_rtr_pref.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84121-3 + - disable_strategy + - low_complexity + - medium_disruption + - reboot_required + - sysctl_net_ipv6_conf_default_accept_ra_rtr_pref + - unknown_severity + +- name: Comment out any occurrences of net.ipv6.conf.default.accept_ra_rtr_pref from + /etc/sysctl.d/*.conf files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.ipv6.conf.default.accept_ra_rtr_pref + replace: '#net.ipv6.conf.default.accept_ra_rtr_pref' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84121-3 + - disable_strategy + - low_complexity + - medium_disruption + - reboot_required + - sysctl_net_ipv6_conf_default_accept_ra_rtr_pref + - unknown_severity +- name: XCCDF Value sysctl_net_ipv6_conf_default_accept_ra_rtr_pref_value # promote to variable + set_fact: + sysctl_net_ipv6_conf_default_accept_ra_rtr_pref_value: !!str + tags: + - always + +- name: Ensure sysctl net.ipv6.conf.default.accept_ra_rtr_pref is set + sysctl: + name: net.ipv6.conf.default.accept_ra_rtr_pref + value: '{{ sysctl_net_ipv6_conf_default_accept_ra_rtr_pref_value }}' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84121-3 + - disable_strategy + - low_complexity + - medium_disruption + - reboot_required + - sysctl_net_ipv6_conf_default_accept_ra_rtr_pref + - unknown_severity + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.ipv6.conf.default.accept_ra_rtr_pref from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.ipv6.conf.default.accept_ra_rtr_pref.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.ipv6.conf.default.accept_ra_rtr_pref" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done +sysctl_net_ipv6_conf_default_accept_ra_rtr_pref_value='' + + +# +# Set runtime for net.ipv6.conf.default.accept_ra_rtr_pref +# +/sbin/sysctl -q -n -w net.ipv6.conf.default.accept_ra_rtr_pref="$sysctl_net_ipv6_conf_default_accept_ra_rtr_pref_value" + +# +# If net.ipv6.conf.default.accept_ra_rtr_pref present in /etc/sysctl.conf, change value to appropriate value +# else, add "net.ipv6.conf.default.accept_ra_rtr_pref = value" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv6.conf.default.accept_ra_rtr_pref") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv6_conf_default_accept_ra_rtr_pref_value" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.ipv6.conf.default.accept_ra_rtr_pref\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.ipv6.conf.default.accept_ra_rtr_pref\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-84121-3" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable Kernel Parameter for Accepting ICMP Redirects by Default on IPv6 Interfaces + To set the runtime status of the net.ipv6.conf.default.accept_redirects kernel parameter, run the following command: $ sudo sysctl -w net.ipv6.conf.default.accept_redirects=0 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: net.ipv6.conf.default.accept_redirects = 0 + BP28(R22) + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + 3.1.20 + CCI-000366 + CCI-001551 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + PR.PT-3 + SRG-OS-000480-GPOS-00227 + An illicit ICMP redirect message could result in a man-in-the-middle attack. + + CCE-84113-0 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.ipv6.conf.default.accept_redirects.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84113-0 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv6_conf_default_accept_redirects + +- name: Comment out any occurrences of net.ipv6.conf.default.accept_redirects from + /etc/sysctl.d/*.conf files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.ipv6.conf.default.accept_redirects + replace: '#net.ipv6.conf.default.accept_redirects' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84113-0 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv6_conf_default_accept_redirects +- name: XCCDF Value sysctl_net_ipv6_conf_default_accept_redirects_value # promote to variable + set_fact: + sysctl_net_ipv6_conf_default_accept_redirects_value: !!str + tags: + - always + +- name: Ensure sysctl net.ipv6.conf.default.accept_redirects is set + sysctl: + name: net.ipv6.conf.default.accept_redirects + value: '{{ sysctl_net_ipv6_conf_default_accept_redirects_value }}' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84113-0 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv6_conf_default_accept_redirects + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,net.ipv6.conf.default.accept_redirects%20%3D%200%0A + mode: 0644 + path: /etc/sysctl.d/75-sysctl_net_ipv6_conf_default_accept_redirects.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.ipv6.conf.default.accept_redirects from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.ipv6.conf.default.accept_redirects.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.ipv6.conf.default.accept_redirects" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done +sysctl_net_ipv6_conf_default_accept_redirects_value='' + + +# +# Set runtime for net.ipv6.conf.default.accept_redirects +# +/sbin/sysctl -q -n -w net.ipv6.conf.default.accept_redirects="$sysctl_net_ipv6_conf_default_accept_redirects_value" + +# +# If net.ipv6.conf.default.accept_redirects present in /etc/sysctl.conf, change value to appropriate value +# else, add "net.ipv6.conf.default.accept_redirects = value" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv6.conf.default.accept_redirects") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv6_conf_default_accept_redirects_value" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.ipv6.conf.default.accept_redirects\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.ipv6.conf.default.accept_redirects\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-84113-0" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable Kernel Parameter for Accepting Source-Routed Packets on IPv6 Interfaces by Default + To set the runtime status of the net.ipv6.conf.default.accept_source_route kernel parameter, run the following command: $ sudo sysctl -w net.ipv6.conf.default.accept_source_route=0 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: net.ipv6.conf.default.accept_source_route = 0 + BP28(R22) + 1 + 12 + 13 + 14 + 15 + 16 + 18 + 4 + 6 + 8 + 9 + APO01.06 + APO13.01 + DSS01.05 + DSS03.01 + DSS05.02 + DSS05.04 + DSS05.07 + DSS06.02 + 3.1.20 + CCI-000366 + 4.2.3.4 + 4.3.3.4 + 4.4.3.3 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.12.1.1 + A.12.1.2 + A.13.1.1 + A.13.1.2 + A.13.1.3 + A.13.2.1 + A.13.2.2 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-7(a) + CM-7(b) + CM-6(a) + CM-6(b) + CM-6.1(iv) + DE.AE-1 + ID.AM-3 + PR.AC-5 + PR.DS-5 + PR.PT-4 + SRG-OS-000480-GPOS-00227 + Source-routed packets allow the source of the packet to suggest routers +forward the packet along a different path than configured on the router, which can +be used to bypass network security measures. This requirement applies only to the +forwarding of source-routerd traffic, such as when IPv6 forwarding is enabled and +the system is functioning as a router. + +Accepting source-routed packets in the IPv6 protocol has few legitimate +uses. It should be disabled unless it is absolutely required. + + CCE-84130-4 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.ipv6.conf.default.accept_source_route.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84130-4 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-6(b) + - NIST-800-53-CM-6.1(iv) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv6_conf_default_accept_source_route + +- name: Comment out any occurrences of net.ipv6.conf.default.accept_source_route from + /etc/sysctl.d/*.conf files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.ipv6.conf.default.accept_source_route + replace: '#net.ipv6.conf.default.accept_source_route' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84130-4 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-6(b) + - NIST-800-53-CM-6.1(iv) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv6_conf_default_accept_source_route +- name: XCCDF Value sysctl_net_ipv6_conf_default_accept_source_route_value # promote to variable + set_fact: + sysctl_net_ipv6_conf_default_accept_source_route_value: !!str + tags: + - always + +- name: Ensure sysctl net.ipv6.conf.default.accept_source_route is set + sysctl: + name: net.ipv6.conf.default.accept_source_route + value: '{{ sysctl_net_ipv6_conf_default_accept_source_route_value }}' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84130-4 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-6(b) + - NIST-800-53-CM-6.1(iv) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv6_conf_default_accept_source_route + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,net.ipv6.conf.default.accept_source_route%3D0%0A + mode: 0644 + path: /etc/sysctl.d/75-sysctl_net_ipv6_conf_default_accept_source_route.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.ipv6.conf.default.accept_source_route from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.ipv6.conf.default.accept_source_route.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.ipv6.conf.default.accept_source_route" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done +sysctl_net_ipv6_conf_default_accept_source_route_value='' + + +# +# Set runtime for net.ipv6.conf.default.accept_source_route +# +/sbin/sysctl -q -n -w net.ipv6.conf.default.accept_source_route="$sysctl_net_ipv6_conf_default_accept_source_route_value" + +# +# If net.ipv6.conf.default.accept_source_route present in /etc/sysctl.conf, change value to appropriate value +# else, add "net.ipv6.conf.default.accept_source_route = value" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv6.conf.default.accept_source_route") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv6_conf_default_accept_source_route_value" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.ipv6.conf.default.accept_source_route\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.ipv6.conf.default.accept_source_route\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-84130-4" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Configure Auto Configuration on All IPv6 Interfaces By Default + To set the runtime status of the net.ipv6.conf.default.autoconf kernel parameter, run the following command: $ sudo sysctl -w net.ipv6.conf.default.autoconf=0 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: net.ipv6.conf.default.autoconf = 0 + BP28(R22) + An illicit router advertisement message could result in a man-in-the-middle attack. + + CCE-84133-8 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.ipv6.conf.default.autoconf.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84133-8 + - disable_strategy + - low_complexity + - medium_disruption + - reboot_required + - sysctl_net_ipv6_conf_default_autoconf + - unknown_severity + +- name: Comment out any occurrences of net.ipv6.conf.default.autoconf from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.ipv6.conf.default.autoconf + replace: '#net.ipv6.conf.default.autoconf' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84133-8 + - disable_strategy + - low_complexity + - medium_disruption + - reboot_required + - sysctl_net_ipv6_conf_default_autoconf + - unknown_severity +- name: XCCDF Value sysctl_net_ipv6_conf_default_autoconf_value # promote to variable + set_fact: + sysctl_net_ipv6_conf_default_autoconf_value: !!str + tags: + - always + +- name: Ensure sysctl net.ipv6.conf.default.autoconf is set + sysctl: + name: net.ipv6.conf.default.autoconf + value: '{{ sysctl_net_ipv6_conf_default_autoconf_value }}' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84133-8 + - disable_strategy + - low_complexity + - medium_disruption + - reboot_required + - sysctl_net_ipv6_conf_default_autoconf + - unknown_severity + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.ipv6.conf.default.autoconf from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.ipv6.conf.default.autoconf.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.ipv6.conf.default.autoconf" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done +sysctl_net_ipv6_conf_default_autoconf_value='' + + +# +# Set runtime for net.ipv6.conf.default.autoconf +# +/sbin/sysctl -q -n -w net.ipv6.conf.default.autoconf="$sysctl_net_ipv6_conf_default_autoconf_value" + +# +# If net.ipv6.conf.default.autoconf present in /etc/sysctl.conf, change value to appropriate value +# else, add "net.ipv6.conf.default.autoconf = value" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv6.conf.default.autoconf") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv6_conf_default_autoconf_value" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.ipv6.conf.default.autoconf\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.ipv6.conf.default.autoconf\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-84133-8" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Configure Maximum Number of Autoconfigured Addresses on All IPv6 Interfaces By Default + To set the runtime status of the net.ipv6.conf.default.max_addresses kernel parameter, run the following command: $ sudo sysctl -w net.ipv6.conf.default.max_addresses=1 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: net.ipv6.conf.default.max_addresses = 1 + BP28(R22) + The number of global unicast IPv6 addresses for each interface should be limited exactly to the number of statically configured addresses. + + CCE-84117-1 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.ipv6.conf.default.max_addresses.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84117-1 + - disable_strategy + - low_complexity + - medium_disruption + - reboot_required + - sysctl_net_ipv6_conf_default_max_addresses + - unknown_severity + +- name: Comment out any occurrences of net.ipv6.conf.default.max_addresses from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.ipv6.conf.default.max_addresses + replace: '#net.ipv6.conf.default.max_addresses' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84117-1 + - disable_strategy + - low_complexity + - medium_disruption + - reboot_required + - sysctl_net_ipv6_conf_default_max_addresses + - unknown_severity +- name: XCCDF Value sysctl_net_ipv6_conf_default_max_addresses_value # promote to variable + set_fact: + sysctl_net_ipv6_conf_default_max_addresses_value: !!str + tags: + - always + +- name: Ensure sysctl net.ipv6.conf.default.max_addresses is set + sysctl: + name: net.ipv6.conf.default.max_addresses + value: '{{ sysctl_net_ipv6_conf_default_max_addresses_value }}' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84117-1 + - disable_strategy + - low_complexity + - medium_disruption + - reboot_required + - sysctl_net_ipv6_conf_default_max_addresses + - unknown_severity + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.ipv6.conf.default.max_addresses from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.ipv6.conf.default.max_addresses.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.ipv6.conf.default.max_addresses" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done +sysctl_net_ipv6_conf_default_max_addresses_value='' + + +# +# Set runtime for net.ipv6.conf.default.max_addresses +# +/sbin/sysctl -q -n -w net.ipv6.conf.default.max_addresses="$sysctl_net_ipv6_conf_default_max_addresses_value" + +# +# If net.ipv6.conf.default.max_addresses present in /etc/sysctl.conf, change value to appropriate value +# else, add "net.ipv6.conf.default.max_addresses = value" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv6.conf.default.max_addresses") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv6_conf_default_max_addresses_value" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.ipv6.conf.default.max_addresses\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.ipv6.conf.default.max_addresses\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-84117-1" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Configure Denying Router Solicitations on All IPv6 Interfaces By Default + To set the runtime status of the net.ipv6.conf.default.router_solicitations kernel parameter, run the following command: $ sudo sysctl -w net.ipv6.conf.default.router_solicitations=0 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: net.ipv6.conf.default.router_solicitations = 0 + BP28(R22) + To prevent discovery of the system by other systems, router solicitation requests should be denied. + + CCE-84026-4 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.ipv6.conf.default.router_solicitations.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84026-4 + - disable_strategy + - low_complexity + - medium_disruption + - reboot_required + - sysctl_net_ipv6_conf_default_router_solicitations + - unknown_severity + +- name: Comment out any occurrences of net.ipv6.conf.default.router_solicitations + from /etc/sysctl.d/*.conf files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.ipv6.conf.default.router_solicitations + replace: '#net.ipv6.conf.default.router_solicitations' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84026-4 + - disable_strategy + - low_complexity + - medium_disruption + - reboot_required + - sysctl_net_ipv6_conf_default_router_solicitations + - unknown_severity +- name: XCCDF Value sysctl_net_ipv6_conf_default_router_solicitations_value # promote to variable + set_fact: + sysctl_net_ipv6_conf_default_router_solicitations_value: !!str + tags: + - always + +- name: Ensure sysctl net.ipv6.conf.default.router_solicitations is set + sysctl: + name: net.ipv6.conf.default.router_solicitations + value: '{{ sysctl_net_ipv6_conf_default_router_solicitations_value }}' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84026-4 + - disable_strategy + - low_complexity + - medium_disruption + - reboot_required + - sysctl_net_ipv6_conf_default_router_solicitations + - unknown_severity + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.ipv6.conf.default.router_solicitations from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.ipv6.conf.default.router_solicitations.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.ipv6.conf.default.router_solicitations" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done +sysctl_net_ipv6_conf_default_router_solicitations_value='' + + +# +# Set runtime for net.ipv6.conf.default.router_solicitations +# +/sbin/sysctl -q -n -w net.ipv6.conf.default.router_solicitations="$sysctl_net_ipv6_conf_default_router_solicitations_value" + +# +# If net.ipv6.conf.default.router_solicitations present in /etc/sysctl.conf, change value to appropriate value +# else, add "net.ipv6.conf.default.router_solicitations = value" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv6.conf.default.router_solicitations") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv6_conf_default_router_solicitations_value" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.ipv6.conf.default.router_solicitations\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.ipv6.conf.default.router_solicitations\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-84026-4" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Limit Network-Transmitted Configuration if Using Static IPv6 Addresses + To limit the configuration information requested from other +systems and accepted from the network on a system that uses +statically-configured IPv6 addresses, add the following lines to +/etc/sysctl.conf: +net.ipv6.conf.default.router_solicitations = 0 +net.ipv6.conf.default.accept_ra_rtr_pref = 0 +net.ipv6.conf.default.accept_ra_pinfo = 0 +net.ipv6.conf.default.accept_ra_defrtr = 0 +net.ipv6.conf.default.autoconf = 0 +net.ipv6.conf.default.dad_transmits = 0 +net.ipv6.conf.default.max_addresses = 1 +The router_solicitations setting determines how many router +solicitations are sent when bringing up the interface. If addresses are +statically assigned, there is no need to send any solicitations. + +The accept_ra_pinfo setting controls whether the system will accept +prefix info from the router. + +The accept_ra_defrtr setting controls whether the system will accept +Hop Limit settings from a router advertisement. Setting it to 0 prevents a +router from changing your default IPv6 Hop Limit for outgoing packets. + +The autoconf setting controls whether router advertisements can cause +the system to assign a global unicast address to an interface. + +The dad_transmits setting determines how many neighbor solicitations +to send out per address (global and link-local) when bringing up an interface +to ensure the desired address is unique on the network. + +The max_addresses setting determines how many global unicast IPv6 +addresses can be assigned to each interface. The default is 16, but it should +be set to exactly the number of statically configured global addresses +required. + + + + + Kernel Parameters Which Affect Networking + The sysctl utility is used to set +parameters which affect the operation of the Linux kernel. Kernel parameters +which affect networking and have security implications are described here. + + Network Related Kernel Runtime Parameters for Hosts and Routers + Certain kernel parameters should be set for systems which are +acting as either hosts or routers to improve the system's ability defend +against certain types of IPv4 protocol attacks. + + + net.ipv4.conf.all.accept_redirects + Disable ICMP Redirect Acceptance + 0 + 0 + 1 + + + net.ipv4.conf.all.accept_source_route + Trackers could be using source-routed packets to +generate traffic that seems to be intra-net, but actually was +created outside and has been redirected. + 0 + 0 + 1 + + + net.ipv4.conf.default.arp_filter + Controls whether the ARP filter is enabled or not. + +1 - Allows you to have multiple network interfaces on the same subnet, and have the ARPs for each +interface be answered based on whether or not the kernel would route a packet from the ARP’d IP out that interface. +In other words it allows control of which cards (usually 1) will respond to an ARP request. + +0 - (default) The kernel can respond to arp requests with addresses from other interfaces. +This may seem wrong but it usually makes sense, because it increases the chance of successful communication. +IP addresses are owned by the complete host on Linux, not by particular interfaces. + 0 + 0 + 1 + + + net.ipv4.conf.default.arp_ignore + Control the response modes for ARP queries that resolve local target IP addresses: + +0 - (default): reply for any local target IP address, configured on any interface +1 - reply only if the target IP address is local address configured on the incoming interface +2 - reply only if the target IP address is local address configured on the incoming interface and both with the sender’s IP address are part from same subnet on this interface +3 - do not reply for local addresses configured with scope host, only resolutions for global and link addresses are replied +4-7 - reserved +8 - do not reply for all local addresses + 0 + 0 + 1 + 2 + 3 + 8 + + + net.ipv4.conf.all.forwarding + Toggle IPv4 Forwarding + 0 + 0 + 1 + + + net.ipv4.conf.all.log_martians + Disable so you don't Log Spoofed Packets, Source +Routed Packets, Redirect Packets + 1 + 0 + 1 + + + net.ipv4.conf.all.rp_filter + Enable to enforce sanity checking, also called ingress +filtering or egress filtering. The point is to drop a packet if the +source and destination IP addresses in the IP header do not make +sense when considered in light of the physical interface on which +it arrived. + 1 + 1 + 2 + + + net.ipv4.conf.all.secure_redirects + Enable to prevent hijacking of routing path by only +allowing redirects from gateways known in routing +table. Disable to refuse acceptance of secure ICMP redirected packets on all interfaces. + 0 + 0 + 1 + + + net.ipv4.conf.all.shared_media + Controls whether the system can send (router) or accept (host) RFC1620 shared media redirects. +shared_media for the interface will be enabled if at least one of conf/{all,interface}/shared_media +is set to TRUE, it will be disabled otherwise. + 0 + 0 + 1 + + + net.ipv4.conf.default.accept_redirects + Disable ICMP Redirect Acceptance? + 0 + 0 + 1 + + + net.ipv4.conf.default.accept_source_route + Disable IP source routing? + 0 + 0 + 1 + + + net.ipv4.conf.default.log_martians + Disable so you don't Log Spoofed Packets, Source +Routed Packets, Redirect Packets + 1 + 0 + 1 + + + net.ipv4.conf.default.rp_filter + Enables source route verification + 1 + 0 + 1 + + + net.ipv4.conf.default.secure_redirects + Enable to prevent hijacking of routing path by only +allowing redirects from gateways known in routing +table. Disable to refuse acceptance of secure ICMP redirected packages by default. + 0 + 0 + 1 + + + net.ipv4.conf.default.shared_media + Controls whether the system can send(router) or accept(host) RFC1620 shared media redirects. +shared_media for the interface will be enabled if at least one of conf/{all,interface}/shared_media +is set to TRUE, it will be disabled otherwise. + 0 + 0 + 1 + + + net.ipv4.icmp_echo_ignore_broadcasts + Ignore all ICMP ECHO and TIMESTAMP requests sent to it +via broadcast/multicast + 1 + 0 + 1 + + + net.ipv4.icmp_ignore_bogus_error_responses + Enable to prevent unnecessary logging + 1 + 0 + 1 + + + net.ipv4.tcp_invalid_ratelimit + Configure the maximal rate for sending duplicate acknowledgments in +response to incoming invalid TCP packets. + 500 + 1000 + 500 + 250 + 100 + + + net.ipv4.tcp_rfc1337 + Enable to enable TCP behavior conformant with RFC 1337 + 1 + 0 + 1 + + + net.ipv4.tcp_syncookies + Enable to turn on TCP SYN Cookie +Protection + 1 + 0 + 1 + + + Disable Accepting Packets Routed Between Local Interfaces + To set the runtime status of the net.ipv4.conf.all.accept_local kernel parameter, run the following command: $ sudo sysctl -w net.ipv4.conf.all.accept_local=0 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: net.ipv4.conf.all.accept_local = 0 + Configure net.ipv4.conf.all.accept_local=0 to consider as invalid the packets +received from outside whose source is the 127.0.0.0/8 address block. +In combination with suitable routing, this can be used to direct packets between two +local interfaces over the wire and have them accepted properly. + CCE-89789-2 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.ipv4.conf.all.accept_local.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-89789-2 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_all_accept_local + +- name: Comment out any occurrences of net.ipv4.conf.all.accept_local from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.ipv4.conf.all.accept_local + replace: '#net.ipv4.conf.all.accept_local' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-89789-2 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_all_accept_local + +- name: Ensure sysctl net.ipv4.conf.all.accept_local is set to 0 + sysctl: + name: net.ipv4.conf.all.accept_local + value: '0' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-89789-2 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_all_accept_local + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.ipv4.conf.all.accept_local from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.ipv4.conf.all.accept_local.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.ipv4.conf.all.accept_local" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done + +# +# Set runtime for net.ipv4.conf.all.accept_local +# +/sbin/sysctl -q -n -w net.ipv4.conf.all.accept_local="0" + +# +# If net.ipv4.conf.all.accept_local present in /etc/sysctl.conf, change value to "0" +# else, add "net.ipv4.conf.all.accept_local = 0" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv4.conf.all.accept_local") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "0" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.ipv4.conf.all.accept_local\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.ipv4.conf.all.accept_local\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-89789-2" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable Accepting ICMP Redirects for All IPv4 Interfaces + To set the runtime status of the net.ipv4.conf.all.accept_redirects kernel parameter, run the following command: $ sudo sysctl -w net.ipv4.conf.all.accept_redirects=0 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: net.ipv4.conf.all.accept_redirects = 0 + BP28(R22) + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 2 + 3 + 7 + 8 + 9 + 5.10.1.1 + APO13.01 + BAI04.04 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.03 + DSS03.05 + DSS05.02 + DSS05.05 + DSS05.07 + DSS06.06 + 3.1.20 + CCI-000366 + CCI-001503 + CCI-001551 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 6.2 + SR 7.1 + SR 7.2 + SR 7.6 + A.12.1.2 + A.12.1.3 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.17.2.1 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + SC-7(a) + DE.CM-1 + PR.DS-4 + PR.IP-1 + PR.PT-3 + SRG-OS-000480-GPOS-00227 + ICMP redirect messages are used by routers to inform hosts that a more +direct route exists for a particular destination. These messages modify the +host's route table and are unauthenticated. An illicit ICMP redirect +message could result in a man-in-the-middle attack. + +This feature of the IPv4 protocol has few legitimate uses. It should be +disabled unless absolutely required." + CCE-84011-6 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.ipv4.conf.all.accept_redirects.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84011-6 + - CJIS-5.10.1.1 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-7(a) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_all_accept_redirects + +- name: Comment out any occurrences of net.ipv4.conf.all.accept_redirects from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.ipv4.conf.all.accept_redirects + replace: '#net.ipv4.conf.all.accept_redirects' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84011-6 + - CJIS-5.10.1.1 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-7(a) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_all_accept_redirects +- name: XCCDF Value sysctl_net_ipv4_conf_all_accept_redirects_value # promote to variable + set_fact: + sysctl_net_ipv4_conf_all_accept_redirects_value: !!str + tags: + - always + +- name: Ensure sysctl net.ipv4.conf.all.accept_redirects is set + sysctl: + name: net.ipv4.conf.all.accept_redirects + value: '{{ sysctl_net_ipv4_conf_all_accept_redirects_value }}' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84011-6 + - CJIS-5.10.1.1 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-7(a) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_all_accept_redirects + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,net.ipv4.conf.all.accept_redirects%3D0%0A + mode: 0644 + path: /etc/sysctl.d/75-sysctl_net_ipv4_conf_all_accept_redirects.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.ipv4.conf.all.accept_redirects from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.ipv4.conf.all.accept_redirects.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.ipv4.conf.all.accept_redirects" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done +sysctl_net_ipv4_conf_all_accept_redirects_value='' + + +# +# Set runtime for net.ipv4.conf.all.accept_redirects +# +/sbin/sysctl -q -n -w net.ipv4.conf.all.accept_redirects="$sysctl_net_ipv4_conf_all_accept_redirects_value" + +# +# If net.ipv4.conf.all.accept_redirects present in /etc/sysctl.conf, change value to appropriate value +# else, add "net.ipv4.conf.all.accept_redirects = value" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv4.conf.all.accept_redirects") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv4_conf_all_accept_redirects_value" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.ipv4.conf.all.accept_redirects\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.ipv4.conf.all.accept_redirects\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-84011-6" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable Kernel Parameter for Accepting Source-Routed Packets on all IPv4 Interfaces + To set the runtime status of the net.ipv4.conf.all.accept_source_route kernel parameter, run the following command: $ sudo sysctl -w net.ipv4.conf.all.accept_source_route=0 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: net.ipv4.conf.all.accept_source_route = 0 + BP28(R22) + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 18 + 2 + 3 + 4 + 6 + 7 + 8 + 9 + APO01.06 + APO13.01 + BAI04.04 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.03 + DSS01.05 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + DSS06.02 + DSS06.06 + 3.1.20 + CCI-000366 + 4.2.3.4 + 4.3.3.4 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + 4.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.2 + SR 7.1 + SR 7.2 + SR 7.6 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.12.1.1 + A.12.1.2 + A.12.1.3 + A.12.5.1 + A.12.6.2 + A.13.1.1 + A.13.1.2 + A.13.1.3 + A.13.2.1 + A.13.2.2 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.17.2.1 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-007-3 R4 + CIP-007-3 R4.1 + CIP-007-3 R4.2 + CIP-007-3 R5.1 + CM-7(a) + CM-7(b) + SC-5 + CM-6(a) + SC-7(a) + DE.AE-1 + DE.CM-1 + ID.AM-3 + PR.AC-5 + PR.DS-4 + PR.DS-5 + PR.IP-1 + PR.PT-3 + PR.PT-4 + SRG-OS-000480-GPOS-00227 + Source-routed packets allow the source of the packet to suggest routers +forward the packet along a different path than configured on the router, +which can be used to bypass network security measures. This requirement +applies only to the forwarding of source-routerd traffic, such as when IPv4 +forwarding is enabled and the system is functioning as a router. + +Accepting source-routed packets in the IPv4 protocol has few legitimate +uses. It should be disabled unless it is absolutely required. + CCE-84001-7 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.ipv4.conf.all.accept_source_route.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84001-7 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-5 + - NIST-800-53-SC-7(a) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_all_accept_source_route + +- name: Comment out any occurrences of net.ipv4.conf.all.accept_source_route from + /etc/sysctl.d/*.conf files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.ipv4.conf.all.accept_source_route + replace: '#net.ipv4.conf.all.accept_source_route' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84001-7 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-5 + - NIST-800-53-SC-7(a) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_all_accept_source_route +- name: XCCDF Value sysctl_net_ipv4_conf_all_accept_source_route_value # promote to variable + set_fact: + sysctl_net_ipv4_conf_all_accept_source_route_value: !!str + tags: + - always + +- name: Ensure sysctl net.ipv4.conf.all.accept_source_route is set + sysctl: + name: net.ipv4.conf.all.accept_source_route + value: '{{ sysctl_net_ipv4_conf_all_accept_source_route_value }}' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84001-7 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-5 + - NIST-800-53-SC-7(a) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_all_accept_source_route + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,net.ipv4.conf.all.accept_source_route%3D0%0A + mode: 0644 + path: /etc/sysctl.d/75-sysctl_net_ipv4_conf_all_accept_source_route.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.ipv4.conf.all.accept_source_route from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.ipv4.conf.all.accept_source_route.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.ipv4.conf.all.accept_source_route" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done +sysctl_net_ipv4_conf_all_accept_source_route_value='' + + +# +# Set runtime for net.ipv4.conf.all.accept_source_route +# +/sbin/sysctl -q -n -w net.ipv4.conf.all.accept_source_route="$sysctl_net_ipv4_conf_all_accept_source_route_value" + +# +# If net.ipv4.conf.all.accept_source_route present in /etc/sysctl.conf, change value to appropriate value +# else, add "net.ipv4.conf.all.accept_source_route = value" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv4.conf.all.accept_source_route") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv4_conf_all_accept_source_route_value" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.ipv4.conf.all.accept_source_route\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.ipv4.conf.all.accept_source_route\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-84001-7" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Configure ARP filtering for All IPv4 Interfaces + To set the runtime status of the net.ipv4.conf.all.arp_filter kernel parameter, run the following command: $ sudo sysctl -w net.ipv4.conf.all.arp_filter= +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: net.ipv4.conf.all.arp_filter = + This behaviour may cause problems to system on a high availability or load balancing configuration. + Prevents the Linux Kernel from handling the ARP table globally. +By default, the kernel may respond to an ARP request from a certain interface with information +from another interface. + CCE-89555-7 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.ipv4.conf.all.arp_filter.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-89555-7 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_all_arp_filter + +- name: Comment out any occurrences of net.ipv4.conf.all.arp_filter from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.ipv4.conf.all.arp_filter + replace: '#net.ipv4.conf.all.arp_filter' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-89555-7 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_all_arp_filter +- name: XCCDF Value sysctl_net_ipv4_conf_all_arp_filter_value # promote to variable + set_fact: + sysctl_net_ipv4_conf_all_arp_filter_value: !!str + tags: + - always + +- name: Ensure sysctl net.ipv4.conf.all.arp_filter is set + sysctl: + name: net.ipv4.conf.all.arp_filter + value: '{{ sysctl_net_ipv4_conf_all_arp_filter_value }}' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-89555-7 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_all_arp_filter + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.ipv4.conf.all.arp_filter from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.ipv4.conf.all.arp_filter.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.ipv4.conf.all.arp_filter" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done +sysctl_net_ipv4_conf_all_arp_filter_value='' + + +# +# Set runtime for net.ipv4.conf.all.arp_filter +# +/sbin/sysctl -q -n -w net.ipv4.conf.all.arp_filter="$sysctl_net_ipv4_conf_all_arp_filter_value" + +# +# If net.ipv4.conf.all.arp_filter present in /etc/sysctl.conf, change value to appropriate value +# else, add "net.ipv4.conf.all.arp_filter = value" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv4.conf.all.arp_filter") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv4_conf_all_arp_filter_value" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.ipv4.conf.all.arp_filter\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.ipv4.conf.all.arp_filter\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-89555-7" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Configure Response Mode of ARP Requests for All IPv4 Interfaces + To set the runtime status of the net.ipv4.conf.all.arp_ignore kernel parameter, run the following command: $ sudo sysctl -w net.ipv4.conf.all.arp_ignore= +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: net.ipv4.conf.all.arp_ignore = + The ARP response mode may impact behaviour of workloads and firewalls on the system. + Avoids ARP Flux on system that have more than one interface on the same subnet. + CCE-89889-0 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.ipv4.conf.all.arp_ignore.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-89889-0 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_all_arp_ignore + +- name: Comment out any occurrences of net.ipv4.conf.all.arp_ignore from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.ipv4.conf.all.arp_ignore + replace: '#net.ipv4.conf.all.arp_ignore' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-89889-0 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_all_arp_ignore +- name: XCCDF Value sysctl_net_ipv4_conf_all_arp_ignore_value # promote to variable + set_fact: + sysctl_net_ipv4_conf_all_arp_ignore_value: !!str + tags: + - always + +- name: Ensure sysctl net.ipv4.conf.all.arp_ignore is set + sysctl: + name: net.ipv4.conf.all.arp_ignore + value: '{{ sysctl_net_ipv4_conf_all_arp_ignore_value }}' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-89889-0 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_all_arp_ignore + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.ipv4.conf.all.arp_ignore from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.ipv4.conf.all.arp_ignore.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.ipv4.conf.all.arp_ignore" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done +sysctl_net_ipv4_conf_all_arp_ignore_value='' + + +# +# Set runtime for net.ipv4.conf.all.arp_ignore +# +/sbin/sysctl -q -n -w net.ipv4.conf.all.arp_ignore="$sysctl_net_ipv4_conf_all_arp_ignore_value" + +# +# If net.ipv4.conf.all.arp_ignore present in /etc/sysctl.conf, change value to appropriate value +# else, add "net.ipv4.conf.all.arp_ignore = value" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv4.conf.all.arp_ignore") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv4_conf_all_arp_ignore_value" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.ipv4.conf.all.arp_ignore\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.ipv4.conf.all.arp_ignore\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-89889-0" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Drop Gratuitious ARP frames on All IPv4 Interfaces + To set the runtime status of the net.ipv4.conf.all.drop_gratuitous_arp kernel parameter, run the following command: $ sudo sysctl -w net.ipv4.conf.all.drop_gratuitous_arp=1 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: net.ipv4.conf.all.drop_gratuitous_arp = 1 + This can cause problems if ARP proxies are used in the network. + Drop Gratuitous ARP frames to prevent ARP poisoning. + CCE-89001-2 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.ipv4.conf.all.drop_gratuitous_arp.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-89001-2 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_all_drop_gratuitous_arp + +- name: Comment out any occurrences of net.ipv4.conf.all.drop_gratuitous_arp from + /etc/sysctl.d/*.conf files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.ipv4.conf.all.drop_gratuitous_arp + replace: '#net.ipv4.conf.all.drop_gratuitous_arp' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-89001-2 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_all_drop_gratuitous_arp + +- name: Ensure sysctl net.ipv4.conf.all.drop_gratuitous_arp is set to 1 + sysctl: + name: net.ipv4.conf.all.drop_gratuitous_arp + value: '1' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-89001-2 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_all_drop_gratuitous_arp + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.ipv4.conf.all.drop_gratuitous_arp from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.ipv4.conf.all.drop_gratuitous_arp.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.ipv4.conf.all.drop_gratuitous_arp" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done + +# +# Set runtime for net.ipv4.conf.all.drop_gratuitous_arp +# +/sbin/sysctl -q -n -w net.ipv4.conf.all.drop_gratuitous_arp="1" + +# +# If net.ipv4.conf.all.drop_gratuitous_arp present in /etc/sysctl.conf, change value to "1" +# else, add "net.ipv4.conf.all.drop_gratuitous_arp = 1" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv4.conf.all.drop_gratuitous_arp") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "1" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.ipv4.conf.all.drop_gratuitous_arp\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.ipv4.conf.all.drop_gratuitous_arp\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-89001-2" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Enable Kernel Parameter to Log Martian Packets on all IPv4 Interfaces + To set the runtime status of the net.ipv4.conf.all.log_martians kernel parameter, run the following command: $ sudo sysctl -w net.ipv4.conf.all.log_martians=1 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: net.ipv4.conf.all.log_martians = 1 + BP28(R22) + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 2 + 3 + 7 + 8 + 9 + APO13.01 + BAI04.04 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.03 + DSS01.04 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.05 + DSS05.07 + DSS06.06 + 3.1.20 + CCI-000126 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.2 + SR 7.1 + SR 7.2 + SR 7.6 + A.11.2.6 + A.12.1.2 + A.12.1.3 + A.12.5.1 + A.12.6.2 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.17.2.1 + A.6.2.1 + A.6.2.2 + A.9.1.2 + CM-7(a) + CM-7(b) + SC-5(3)(a) + DE.CM-1 + PR.AC-3 + PR.DS-4 + PR.IP-1 + PR.PT-3 + PR.PT-4 + SRG-OS-000480-GPOS-00227 + The presence of "martian" packets (which have impossible addresses) +as well as spoofed packets, source-routed packets, and redirects could be a +sign of nefarious network activity. Logging these packets enables this activity +to be detected. + CCE-84000-9 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.ipv4.conf.all.log_martians.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84000-9 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-5(3)(a) + - disable_strategy + - low_complexity + - medium_disruption + - reboot_required + - sysctl_net_ipv4_conf_all_log_martians + - unknown_severity + +- name: Comment out any occurrences of net.ipv4.conf.all.log_martians from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.ipv4.conf.all.log_martians + replace: '#net.ipv4.conf.all.log_martians' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84000-9 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-5(3)(a) + - disable_strategy + - low_complexity + - medium_disruption + - reboot_required + - sysctl_net_ipv4_conf_all_log_martians + - unknown_severity +- name: XCCDF Value sysctl_net_ipv4_conf_all_log_martians_value # promote to variable + set_fact: + sysctl_net_ipv4_conf_all_log_martians_value: !!str + tags: + - always + +- name: Ensure sysctl net.ipv4.conf.all.log_martians is set + sysctl: + name: net.ipv4.conf.all.log_martians + value: '{{ sysctl_net_ipv4_conf_all_log_martians_value }}' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84000-9 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-5(3)(a) + - disable_strategy + - low_complexity + - medium_disruption + - reboot_required + - sysctl_net_ipv4_conf_all_log_martians + - unknown_severity + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,net.ipv4.conf.all.log_martians%3D1%0A + mode: 0644 + path: /etc/sysctl.d/75-sysctl_net_ipv4_conf_all_log_martians.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.ipv4.conf.all.log_martians from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.ipv4.conf.all.log_martians.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.ipv4.conf.all.log_martians" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done +sysctl_net_ipv4_conf_all_log_martians_value='' + + +# +# Set runtime for net.ipv4.conf.all.log_martians +# +/sbin/sysctl -q -n -w net.ipv4.conf.all.log_martians="$sysctl_net_ipv4_conf_all_log_martians_value" + +# +# If net.ipv4.conf.all.log_martians present in /etc/sysctl.conf, change value to appropriate value +# else, add "net.ipv4.conf.all.log_martians = value" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv4.conf.all.log_martians") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv4_conf_all_log_martians_value" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.ipv4.conf.all.log_martians\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.ipv4.conf.all.log_martians\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-84000-9" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Prevent Routing External Traffic to Local Loopback on All IPv4 Interfaces + To set the runtime status of the net.ipv4.conf.all.route_localnet kernel parameter, run the following command: $ sudo sysctl -w net.ipv4.conf.all.route_localnet=0 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: net.ipv4.conf.all.route_localnet = 0 + Refuse the routing of packets whose source or destination address is the local loopback. +This prohibits the use of network 127/8 for local routing purposes. +Enabling route_localnet can expose applications listening on localhost to external traffic. + CCE-89023-6 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.ipv4.conf.all.route_localnet.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-89023-6 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_all_route_localnet + +- name: Comment out any occurrences of net.ipv4.conf.all.route_localnet from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.ipv4.conf.all.route_localnet + replace: '#net.ipv4.conf.all.route_localnet' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-89023-6 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_all_route_localnet + +- name: Ensure sysctl net.ipv4.conf.all.route_localnet is set to 0 + sysctl: + name: net.ipv4.conf.all.route_localnet + value: '0' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-89023-6 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_all_route_localnet + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.ipv4.conf.all.route_localnet from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.ipv4.conf.all.route_localnet.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.ipv4.conf.all.route_localnet" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done + +# +# Set runtime for net.ipv4.conf.all.route_localnet +# +/sbin/sysctl -q -n -w net.ipv4.conf.all.route_localnet="0" + +# +# If net.ipv4.conf.all.route_localnet present in /etc/sysctl.conf, change value to "0" +# else, add "net.ipv4.conf.all.route_localnet = 0" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv4.conf.all.route_localnet") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "0" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.ipv4.conf.all.route_localnet\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.ipv4.conf.all.route_localnet\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-89023-6" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Enable Kernel Parameter to Use Reverse Path Filtering on all IPv4 Interfaces + To set the runtime status of the net.ipv4.conf.all.rp_filter kernel parameter, run the following command: $ sudo sysctl -w net.ipv4.conf.all.rp_filter=1 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: net.ipv4.conf.all.rp_filter = 1 + BP28(R22) + 1 + 12 + 13 + 14 + 15 + 16 + 18 + 2 + 4 + 6 + 7 + 8 + 9 + APO01.06 + APO13.01 + BAI04.04 + DSS01.03 + DSS01.05 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.04 + DSS05.07 + DSS06.02 + 3.1.20 + CCI-000366 + CCI-001551 + 4.2.3.4 + 4.3.3.4 + 4.4.3.3 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.2 + SR 7.1 + SR 7.2 + SR 7.6 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.12.1.1 + A.12.1.2 + A.12.1.3 + A.13.1.1 + A.13.1.2 + A.13.1.3 + A.13.2.1 + A.13.2.2 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.17.2.1 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-7(a) + CM-7(b) + CM-6(a) + SC-7(a) + DE.AE-1 + DE.CM-1 + ID.AM-3 + PR.AC-5 + PR.DS-4 + PR.DS-5 + PR.PT-4 + SRG-OS-000480-GPOS-00227 + Enabling reverse path filtering drops packets with source addresses +that should not have been able to be received on the interface they were +received on. It should not be used on systems which are routers for +complicated networks, but is helpful for end hosts and routers serving small +networks. + CCE-84008-2 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.ipv4.conf.all.rp_filter.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84008-2 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-7(a) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_all_rp_filter + +- name: Comment out any occurrences of net.ipv4.conf.all.rp_filter from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.ipv4.conf.all.rp_filter + replace: '#net.ipv4.conf.all.rp_filter' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84008-2 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-7(a) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_all_rp_filter +- name: XCCDF Value sysctl_net_ipv4_conf_all_rp_filter_value # promote to variable + set_fact: + sysctl_net_ipv4_conf_all_rp_filter_value: !!str + tags: + - always + +- name: Ensure sysctl net.ipv4.conf.all.rp_filter is set + sysctl: + name: net.ipv4.conf.all.rp_filter + value: '{{ sysctl_net_ipv4_conf_all_rp_filter_value }}' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84008-2 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-7(a) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_all_rp_filter + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,net.ipv4.conf.all.rp_filter%3D1%0A + mode: 0644 + path: /etc/sysctl.d/75-sysctl_net_ipv4_conf_all_rp_filter.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.ipv4.conf.all.rp_filter from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.ipv4.conf.all.rp_filter.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.ipv4.conf.all.rp_filter" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done +sysctl_net_ipv4_conf_all_rp_filter_value='' + + +# +# Set runtime for net.ipv4.conf.all.rp_filter +# +/sbin/sysctl -q -n -w net.ipv4.conf.all.rp_filter="$sysctl_net_ipv4_conf_all_rp_filter_value" + +# +# If net.ipv4.conf.all.rp_filter present in /etc/sysctl.conf, change value to appropriate value +# else, add "net.ipv4.conf.all.rp_filter = value" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv4.conf.all.rp_filter") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv4_conf_all_rp_filter_value" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.ipv4.conf.all.rp_filter\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.ipv4.conf.all.rp_filter\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-84008-2" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable Kernel Parameter for Accepting Secure ICMP Redirects on all IPv4 Interfaces + To set the runtime status of the net.ipv4.conf.all.secure_redirects kernel parameter, run the following command: $ sudo sysctl -w net.ipv4.conf.all.secure_redirects=0 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: net.ipv4.conf.all.secure_redirects = 0 + BP28(R22) + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 18 + 2 + 3 + 4 + 6 + 7 + 8 + 9 + APO01.06 + APO13.01 + BAI04.04 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.03 + DSS01.05 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + DSS06.02 + DSS06.06 + 3.1.20 + CCI-001503 + CCI-001551 + 4.2.3.4 + 4.3.3.4 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + 4.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.2 + SR 7.1 + SR 7.2 + SR 7.6 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.12.1.1 + A.12.1.2 + A.12.1.3 + A.12.5.1 + A.12.6.2 + A.13.1.1 + A.13.1.2 + A.13.1.3 + A.13.2.1 + A.13.2.2 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.17.2.1 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-7(a) + CM-7(b) + CM-6(a) + SC-7(a) + DE.AE-1 + DE.CM-1 + ID.AM-3 + PR.AC-5 + PR.DS-4 + PR.DS-5 + PR.IP-1 + PR.PT-3 + PR.PT-4 + SRG-OS-000480-GPOS-00227 + Accepting "secure" ICMP redirects (from those gateways listed as +default gateways) has few legitimate uses. It should be disabled unless it is +absolutely required. + CCE-84016-5 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.ipv4.conf.all.secure_redirects.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84016-5 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-7(a) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_all_secure_redirects + +- name: Comment out any occurrences of net.ipv4.conf.all.secure_redirects from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.ipv4.conf.all.secure_redirects + replace: '#net.ipv4.conf.all.secure_redirects' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84016-5 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-7(a) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_all_secure_redirects +- name: XCCDF Value sysctl_net_ipv4_conf_all_secure_redirects_value # promote to variable + set_fact: + sysctl_net_ipv4_conf_all_secure_redirects_value: !!str + tags: + - always + +- name: Ensure sysctl net.ipv4.conf.all.secure_redirects is set + sysctl: + name: net.ipv4.conf.all.secure_redirects + value: '{{ sysctl_net_ipv4_conf_all_secure_redirects_value }}' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84016-5 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-7(a) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_all_secure_redirects + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,net.ipv4.conf.all.secure_redirects%3D0%0A + mode: 0644 + path: /etc/sysctl.d/75-sysctl_net_ipv4_conf_all_secure_redirects.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.ipv4.conf.all.secure_redirects from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.ipv4.conf.all.secure_redirects.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.ipv4.conf.all.secure_redirects" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done +sysctl_net_ipv4_conf_all_secure_redirects_value='' + + +# +# Set runtime for net.ipv4.conf.all.secure_redirects +# +/sbin/sysctl -q -n -w net.ipv4.conf.all.secure_redirects="$sysctl_net_ipv4_conf_all_secure_redirects_value" + +# +# If net.ipv4.conf.all.secure_redirects present in /etc/sysctl.conf, change value to appropriate value +# else, add "net.ipv4.conf.all.secure_redirects = value" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv4.conf.all.secure_redirects") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv4_conf_all_secure_redirects_value" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.ipv4.conf.all.secure_redirects\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.ipv4.conf.all.secure_redirects\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-84016-5" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Configure Sending and Accepting Shared Media Redirects for All IPv4 Interfaces + To set the runtime status of the net.ipv4.conf.all.shared_media kernel parameter, run the following command: $ sudo sysctl -w net.ipv4.conf.all.shared_media= +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: net.ipv4.conf.all.shared_media = + This setting should be aligned with net.ipv4.conf.all.secure_redirects because it overrides it. +If shared_media is enabled for an interface secure_redirects will be enabled too. + CCE-89333-9 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.ipv4.conf.all.shared_media.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-89333-9 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_all_shared_media + +- name: Comment out any occurrences of net.ipv4.conf.all.shared_media from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.ipv4.conf.all.shared_media + replace: '#net.ipv4.conf.all.shared_media' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-89333-9 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_all_shared_media +- name: XCCDF Value sysctl_net_ipv4_conf_all_shared_media_value # promote to variable + set_fact: + sysctl_net_ipv4_conf_all_shared_media_value: !!str + tags: + - always + +- name: Ensure sysctl net.ipv4.conf.all.shared_media is set + sysctl: + name: net.ipv4.conf.all.shared_media + value: '{{ sysctl_net_ipv4_conf_all_shared_media_value }}' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-89333-9 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_all_shared_media + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.ipv4.conf.all.shared_media from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.ipv4.conf.all.shared_media.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.ipv4.conf.all.shared_media" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done +sysctl_net_ipv4_conf_all_shared_media_value='' + + +# +# Set runtime for net.ipv4.conf.all.shared_media +# +/sbin/sysctl -q -n -w net.ipv4.conf.all.shared_media="$sysctl_net_ipv4_conf_all_shared_media_value" + +# +# If net.ipv4.conf.all.shared_media present in /etc/sysctl.conf, change value to appropriate value +# else, add "net.ipv4.conf.all.shared_media = value" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv4.conf.all.shared_media") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv4_conf_all_shared_media_value" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.ipv4.conf.all.shared_media\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.ipv4.conf.all.shared_media\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-89333-9" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable Kernel Parameter for Accepting ICMP Redirects by Default on IPv4 Interfaces + To set the runtime status of the net.ipv4.conf.default.accept_redirects kernel parameter, run the following command: $ sudo sysctl -w net.ipv4.conf.default.accept_redirects=0 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: net.ipv4.conf.default.accept_redirects = 0 + BP28(R22) + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 18 + 2 + 3 + 4 + 6 + 7 + 8 + 9 + 5.10.1.1 + APO01.06 + APO13.01 + BAI04.04 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.03 + DSS01.05 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + DSS06.02 + DSS06.06 + 3.1.20 + CCI-000366 + CCI-001551 + 4.2.3.4 + 4.3.3.4 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + 4.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.2 + SR 7.1 + SR 7.2 + SR 7.6 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.12.1.1 + A.12.1.2 + A.12.1.3 + A.12.5.1 + A.12.6.2 + A.13.1.1 + A.13.1.2 + A.13.1.3 + A.13.2.1 + A.13.2.2 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.17.2.1 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-7(a) + CM-7(b) + CM-6(a) + SC-7(a) + DE.AE-1 + DE.CM-1 + ID.AM-3 + PR.AC-5 + PR.DS-4 + PR.DS-5 + PR.IP-1 + PR.PT-3 + PR.PT-4 + SRG-OS-000480-GPOS-00227 + ICMP redirect messages are used by routers to inform hosts that a more +direct route exists for a particular destination. These messages modify the +host's route table and are unauthenticated. An illicit ICMP redirect +message could result in a man-in-the-middle attack. +This feature of the IPv4 protocol has few legitimate uses. It should +be disabled unless absolutely required. + CCE-84003-3 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.ipv4.conf.default.accept_redirects.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84003-3 + - CJIS-5.10.1.1 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-7(a) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_default_accept_redirects + +- name: Comment out any occurrences of net.ipv4.conf.default.accept_redirects from + /etc/sysctl.d/*.conf files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.ipv4.conf.default.accept_redirects + replace: '#net.ipv4.conf.default.accept_redirects' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84003-3 + - CJIS-5.10.1.1 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-7(a) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_default_accept_redirects +- name: XCCDF Value sysctl_net_ipv4_conf_default_accept_redirects_value # promote to variable + set_fact: + sysctl_net_ipv4_conf_default_accept_redirects_value: !!str + tags: + - always + +- name: Ensure sysctl net.ipv4.conf.default.accept_redirects is set + sysctl: + name: net.ipv4.conf.default.accept_redirects + value: '{{ sysctl_net_ipv4_conf_default_accept_redirects_value }}' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84003-3 + - CJIS-5.10.1.1 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-7(a) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_default_accept_redirects + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,net.ipv4.conf.default.accept_redirects%3D0%0A + mode: 0644 + path: /etc/sysctl.d/75-sysctl_net_ipv4_conf_default_accept_redirects.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.ipv4.conf.default.accept_redirects from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.ipv4.conf.default.accept_redirects.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.ipv4.conf.default.accept_redirects" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done +sysctl_net_ipv4_conf_default_accept_redirects_value='' + + +# +# Set runtime for net.ipv4.conf.default.accept_redirects +# +/sbin/sysctl -q -n -w net.ipv4.conf.default.accept_redirects="$sysctl_net_ipv4_conf_default_accept_redirects_value" + +# +# If net.ipv4.conf.default.accept_redirects present in /etc/sysctl.conf, change value to appropriate value +# else, add "net.ipv4.conf.default.accept_redirects = value" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv4.conf.default.accept_redirects") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv4_conf_default_accept_redirects_value" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.ipv4.conf.default.accept_redirects\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.ipv4.conf.default.accept_redirects\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-84003-3" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable Kernel Parameter for Accepting Source-Routed Packets on IPv4 Interfaces by Default + To set the runtime status of the net.ipv4.conf.default.accept_source_route kernel parameter, run the following command: $ sudo sysctl -w net.ipv4.conf.default.accept_source_route=0 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: net.ipv4.conf.default.accept_source_route = 0 + BP28(R22) + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 18 + 2 + 3 + 4 + 6 + 7 + 8 + 9 + 5.10.1.1 + APO01.06 + APO13.01 + BAI04.04 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.03 + DSS01.05 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + DSS06.02 + DSS06.06 + 3.1.20 + CCI-000366 + CCI-001551 + 4.2.3.4 + 4.3.3.4 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + 4.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.2 + SR 7.1 + SR 7.2 + SR 7.6 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.12.1.1 + A.12.1.2 + A.12.1.3 + A.12.5.1 + A.12.6.2 + A.13.1.1 + A.13.1.2 + A.13.1.3 + A.13.2.1 + A.13.2.2 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.17.2.1 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-007-3 R4 + CIP-007-3 R4.1 + CIP-007-3 R4.2 + CIP-007-3 R5.1 + CM-7(a) + CM-7(b) + SC-5 + SC-7(a) + DE.AE-1 + DE.CM-1 + ID.AM-3 + PR.AC-5 + PR.DS-4 + PR.DS-5 + PR.IP-1 + PR.PT-3 + PR.PT-4 + SRG-OS-000480-GPOS-00227 + Source-routed packets allow the source of the packet to suggest routers +forward the packet along a different path than configured on the router, +which can be used to bypass network security measures. + +Accepting source-routed packets in the IPv4 protocol has few legitimate +uses. It should be disabled unless it is absolutely required, such as when +IPv4 forwarding is enabled and the system is legitimately functioning as a +router. + CCE-84007-4 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.ipv4.conf.default.accept_source_route.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84007-4 + - CJIS-5.10.1.1 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-5 + - NIST-800-53-SC-7(a) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_default_accept_source_route + +- name: Comment out any occurrences of net.ipv4.conf.default.accept_source_route from + /etc/sysctl.d/*.conf files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.ipv4.conf.default.accept_source_route + replace: '#net.ipv4.conf.default.accept_source_route' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84007-4 + - CJIS-5.10.1.1 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-5 + - NIST-800-53-SC-7(a) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_default_accept_source_route +- name: XCCDF Value sysctl_net_ipv4_conf_default_accept_source_route_value # promote to variable + set_fact: + sysctl_net_ipv4_conf_default_accept_source_route_value: !!str + tags: + - always + +- name: Ensure sysctl net.ipv4.conf.default.accept_source_route is set + sysctl: + name: net.ipv4.conf.default.accept_source_route + value: '{{ sysctl_net_ipv4_conf_default_accept_source_route_value }}' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84007-4 + - CJIS-5.10.1.1 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-5 + - NIST-800-53-SC-7(a) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_default_accept_source_route + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,net.ipv4.conf.default.accept_source_route%3D0%0A + mode: 0644 + path: /etc/sysctl.d/75-sysctl_net_ipv4_conf_default_accept_source_route.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.ipv4.conf.default.accept_source_route from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.ipv4.conf.default.accept_source_route.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.ipv4.conf.default.accept_source_route" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done +sysctl_net_ipv4_conf_default_accept_source_route_value='' + + +# +# Set runtime for net.ipv4.conf.default.accept_source_route +# +/sbin/sysctl -q -n -w net.ipv4.conf.default.accept_source_route="$sysctl_net_ipv4_conf_default_accept_source_route_value" + +# +# If net.ipv4.conf.default.accept_source_route present in /etc/sysctl.conf, change value to appropriate value +# else, add "net.ipv4.conf.default.accept_source_route = value" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv4.conf.default.accept_source_route") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv4_conf_default_accept_source_route_value" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.ipv4.conf.default.accept_source_route\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.ipv4.conf.default.accept_source_route\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-84007-4" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable Kernel Paremeter to Log Martian Packets on all IPv4 Interfaces by Default + To set the runtime status of the net.ipv4.conf.default.log_martians kernel parameter, run the following command: $ sudo sysctl -w net.ipv4.conf.default.log_martians=1 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: net.ipv4.conf.default.log_martians = 1 + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 2 + 3 + 7 + 8 + 9 + APO13.01 + BAI04.04 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.03 + DSS01.04 + DSS03.05 + DSS05.02 + DSS05.03 + DSS05.05 + DSS05.07 + DSS06.06 + 3.1.20 + CCI-000126 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.2 + SR 7.1 + SR 7.2 + SR 7.6 + A.11.2.6 + A.12.1.2 + A.12.1.3 + A.12.5.1 + A.12.6.2 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.17.2.1 + A.6.2.1 + A.6.2.2 + A.9.1.2 + CM-7(a) + CM-7(b) + SC-5(3)(a) + DE.CM-1 + PR.AC-3 + PR.DS-4 + PR.IP-1 + PR.PT-3 + PR.PT-4 + SRG-OS-000480-GPOS-00227 + The presence of "martian" packets (which have impossible addresses) +as well as spoofed packets, source-routed packets, and redirects could be a +sign of nefarious network activity. Logging these packets enables this activity +to be detected. + CCE-84014-0 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.ipv4.conf.default.log_martians.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84014-0 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-5(3)(a) + - disable_strategy + - low_complexity + - medium_disruption + - reboot_required + - sysctl_net_ipv4_conf_default_log_martians + - unknown_severity + +- name: Comment out any occurrences of net.ipv4.conf.default.log_martians from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.ipv4.conf.default.log_martians + replace: '#net.ipv4.conf.default.log_martians' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84014-0 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-5(3)(a) + - disable_strategy + - low_complexity + - medium_disruption + - reboot_required + - sysctl_net_ipv4_conf_default_log_martians + - unknown_severity +- name: XCCDF Value sysctl_net_ipv4_conf_default_log_martians_value # promote to variable + set_fact: + sysctl_net_ipv4_conf_default_log_martians_value: !!str + tags: + - always + +- name: Ensure sysctl net.ipv4.conf.default.log_martians is set + sysctl: + name: net.ipv4.conf.default.log_martians + value: '{{ sysctl_net_ipv4_conf_default_log_martians_value }}' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84014-0 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-5(3)(a) + - disable_strategy + - low_complexity + - medium_disruption + - reboot_required + - sysctl_net_ipv4_conf_default_log_martians + - unknown_severity + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,net.ipv4.conf.default.log_martians%3D1%0A + mode: 0644 + path: /etc/sysctl.d/75-sysctl_net_ipv4_conf_default_log_martians.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.ipv4.conf.default.log_martians from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.ipv4.conf.default.log_martians.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.ipv4.conf.default.log_martians" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done +sysctl_net_ipv4_conf_default_log_martians_value='' + + +# +# Set runtime for net.ipv4.conf.default.log_martians +# +/sbin/sysctl -q -n -w net.ipv4.conf.default.log_martians="$sysctl_net_ipv4_conf_default_log_martians_value" + +# +# If net.ipv4.conf.default.log_martians present in /etc/sysctl.conf, change value to appropriate value +# else, add "net.ipv4.conf.default.log_martians = value" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv4.conf.default.log_martians") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv4_conf_default_log_martians_value" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.ipv4.conf.default.log_martians\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.ipv4.conf.default.log_martians\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-84014-0" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable Kernel Parameter to Use Reverse Path Filtering on all IPv4 Interfaces by Default + To set the runtime status of the net.ipv4.conf.default.rp_filter kernel parameter, run the following command: $ sudo sysctl -w net.ipv4.conf.default.rp_filter=1 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: net.ipv4.conf.default.rp_filter = 1 + BP28(R22) + 1 + 12 + 13 + 14 + 15 + 16 + 18 + 2 + 4 + 6 + 7 + 8 + 9 + APO01.06 + APO13.01 + BAI04.04 + DSS01.03 + DSS01.05 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.04 + DSS05.07 + DSS06.02 + 3.1.20 + CCI-000366 + 4.2.3.4 + 4.3.3.4 + 4.4.3.3 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.2 + SR 7.1 + SR 7.2 + SR 7.6 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.12.1.1 + A.12.1.2 + A.12.1.3 + A.13.1.1 + A.13.1.2 + A.13.1.3 + A.13.2.1 + A.13.2.2 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.17.2.1 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-7(a) + CM-7(b) + CM-6(a) + SC-7(a) + DE.AE-1 + DE.CM-1 + ID.AM-3 + PR.AC-5 + PR.DS-4 + PR.DS-5 + PR.PT-4 + SRG-OS-000480-GPOS-00227 + Enabling reverse path filtering drops packets with source addresses +that should not have been able to be received on the interface they were +received on. It should not be used on systems which are routers for +complicated networks, but is helpful for end hosts and routers serving small +networks. + CCE-84009-0 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.ipv4.conf.default.rp_filter.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84009-0 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-7(a) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_default_rp_filter + +- name: Comment out any occurrences of net.ipv4.conf.default.rp_filter from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.ipv4.conf.default.rp_filter + replace: '#net.ipv4.conf.default.rp_filter' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84009-0 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-7(a) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_default_rp_filter +- name: XCCDF Value sysctl_net_ipv4_conf_default_rp_filter_value # promote to variable + set_fact: + sysctl_net_ipv4_conf_default_rp_filter_value: !!str + tags: + - always + +- name: Ensure sysctl net.ipv4.conf.default.rp_filter is set + sysctl: + name: net.ipv4.conf.default.rp_filter + value: '{{ sysctl_net_ipv4_conf_default_rp_filter_value }}' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84009-0 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-7(a) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_default_rp_filter + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,net.ipv4.conf.default.rp_filter%3D1%0A + mode: 0644 + path: /etc/sysctl.d/75-sysctl_net_ipv4_conf_default_rp_filter.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.ipv4.conf.default.rp_filter from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.ipv4.conf.default.rp_filter.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.ipv4.conf.default.rp_filter" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done +sysctl_net_ipv4_conf_default_rp_filter_value='' + + +# +# Set runtime for net.ipv4.conf.default.rp_filter +# +/sbin/sysctl -q -n -w net.ipv4.conf.default.rp_filter="$sysctl_net_ipv4_conf_default_rp_filter_value" + +# +# If net.ipv4.conf.default.rp_filter present in /etc/sysctl.conf, change value to appropriate value +# else, add "net.ipv4.conf.default.rp_filter = value" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv4.conf.default.rp_filter") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv4_conf_default_rp_filter_value" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.ipv4.conf.default.rp_filter\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.ipv4.conf.default.rp_filter\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-84009-0" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Configure Kernel Parameter for Accepting Secure Redirects By Default + To set the runtime status of the net.ipv4.conf.default.secure_redirects kernel parameter, run the following command: $ sudo sysctl -w net.ipv4.conf.default.secure_redirects=0 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: net.ipv4.conf.default.secure_redirects = 0 + BP28(R22) + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 18 + 2 + 3 + 4 + 6 + 7 + 8 + 9 + APO01.06 + APO13.01 + BAI04.04 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.03 + DSS01.05 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + DSS06.02 + DSS06.06 + 3.1.20 + CCI-001551 + 4.2.3.4 + 4.3.3.4 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + 4.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.2 + SR 7.1 + SR 7.2 + SR 7.6 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.12.1.1 + A.12.1.2 + A.12.1.3 + A.12.5.1 + A.12.6.2 + A.13.1.1 + A.13.1.2 + A.13.1.3 + A.13.2.1 + A.13.2.2 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.17.2.1 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-007-3 R4 + CIP-007-3 R4.1 + CIP-007-3 R4.2 + CIP-007-3 R5.1 + CM-7(a) + CM-7(b) + SC-5 + SC-7(a) + DE.AE-1 + DE.CM-1 + ID.AM-3 + PR.AC-5 + PR.DS-4 + PR.DS-5 + PR.IP-1 + PR.PT-3 + PR.PT-4 + SRG-OS-000480-GPOS-00227 + Accepting "secure" ICMP redirects (from those gateways listed as +default gateways) has few legitimate uses. It should be disabled unless it is +absolutely required. + CCE-84019-9 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.ipv4.conf.default.secure_redirects.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84019-9 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-5 + - NIST-800-53-SC-7(a) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_default_secure_redirects + +- name: Comment out any occurrences of net.ipv4.conf.default.secure_redirects from + /etc/sysctl.d/*.conf files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.ipv4.conf.default.secure_redirects + replace: '#net.ipv4.conf.default.secure_redirects' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84019-9 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-5 + - NIST-800-53-SC-7(a) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_default_secure_redirects +- name: XCCDF Value sysctl_net_ipv4_conf_default_secure_redirects_value # promote to variable + set_fact: + sysctl_net_ipv4_conf_default_secure_redirects_value: !!str + tags: + - always + +- name: Ensure sysctl net.ipv4.conf.default.secure_redirects is set + sysctl: + name: net.ipv4.conf.default.secure_redirects + value: '{{ sysctl_net_ipv4_conf_default_secure_redirects_value }}' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84019-9 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-5 + - NIST-800-53-SC-7(a) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_default_secure_redirects + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,net.ipv4.conf.default.secure_redirects%3D0%0A + mode: 0644 + path: /etc/sysctl.d/75-sysctl_net_ipv4_conf_default_secure_redirects.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.ipv4.conf.default.secure_redirects from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.ipv4.conf.default.secure_redirects.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.ipv4.conf.default.secure_redirects" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done +sysctl_net_ipv4_conf_default_secure_redirects_value='' + + +# +# Set runtime for net.ipv4.conf.default.secure_redirects +# +/sbin/sysctl -q -n -w net.ipv4.conf.default.secure_redirects="$sysctl_net_ipv4_conf_default_secure_redirects_value" + +# +# If net.ipv4.conf.default.secure_redirects present in /etc/sysctl.conf, change value to appropriate value +# else, add "net.ipv4.conf.default.secure_redirects = value" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv4.conf.default.secure_redirects") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv4_conf_default_secure_redirects_value" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.ipv4.conf.default.secure_redirects\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.ipv4.conf.default.secure_redirects\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-84019-9" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Configure Sending and Accepting Shared Media Redirects by Default + To set the runtime status of the net.ipv4.conf.default.shared_media kernel parameter, run the following command: $ sudo sysctl -w net.ipv4.conf.default.shared_media= +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: net.ipv4.conf.default.shared_media = + This setting should be aligned with net.ipv4.conf.default.secure_redirects because it overrides it. +If shared_media is enabled for an interface secure_redirects will be enabled too. + CCE-89444-4 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.ipv4.conf.default.shared_media.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-89444-4 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_default_shared_media + +- name: Comment out any occurrences of net.ipv4.conf.default.shared_media from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.ipv4.conf.default.shared_media + replace: '#net.ipv4.conf.default.shared_media' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-89444-4 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_default_shared_media +- name: XCCDF Value sysctl_net_ipv4_conf_default_shared_media_value # promote to variable + set_fact: + sysctl_net_ipv4_conf_default_shared_media_value: !!str + tags: + - always + +- name: Ensure sysctl net.ipv4.conf.default.shared_media is set + sysctl: + name: net.ipv4.conf.default.shared_media + value: '{{ sysctl_net_ipv4_conf_default_shared_media_value }}' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-89444-4 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_default_shared_media + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.ipv4.conf.default.shared_media from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.ipv4.conf.default.shared_media.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.ipv4.conf.default.shared_media" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done +sysctl_net_ipv4_conf_default_shared_media_value='' + + +# +# Set runtime for net.ipv4.conf.default.shared_media +# +/sbin/sysctl -q -n -w net.ipv4.conf.default.shared_media="$sysctl_net_ipv4_conf_default_shared_media_value" + +# +# If net.ipv4.conf.default.shared_media present in /etc/sysctl.conf, change value to appropriate value +# else, add "net.ipv4.conf.default.shared_media = value" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv4.conf.default.shared_media") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv4_conf_default_shared_media_value" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.ipv4.conf.default.shared_media\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.ipv4.conf.default.shared_media\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-89444-4" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable Kernel Parameter to Ignore ICMP Broadcast Echo Requests on IPv4 Interfaces + To set the runtime status of the net.ipv4.icmp_echo_ignore_broadcasts kernel parameter, run the following command: $ sudo sysctl -w net.ipv4.icmp_echo_ignore_broadcasts=1 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: net.ipv4.icmp_echo_ignore_broadcasts = 1 + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 18 + 2 + 3 + 4 + 6 + 7 + 8 + 9 + 5.10.1.1 + APO01.06 + APO13.01 + BAI04.04 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.03 + DSS01.05 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + DSS06.02 + DSS06.06 + 3.1.20 + CCI-000366 + 4.2.3.4 + 4.3.3.4 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + 4.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.2 + SR 7.1 + SR 7.2 + SR 7.6 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.12.1.1 + A.12.1.2 + A.12.1.3 + A.12.5.1 + A.12.6.2 + A.13.1.1 + A.13.1.2 + A.13.1.3 + A.13.2.1 + A.13.2.2 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.17.2.1 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-007-3 R4 + CIP-007-3 R4.1 + CIP-007-3 R4.2 + CIP-007-3 R5.1 + CM-7(a) + CM-7(b) + SC-5 + DE.AE-1 + DE.CM-1 + ID.AM-3 + PR.AC-5 + PR.DS-4 + PR.DS-5 + PR.IP-1 + PR.PT-3 + PR.PT-4 + SRG-OS-000480-GPOS-00227 + Responding to broadcast (ICMP) echoes facilitates network mapping +and provides a vector for amplification attacks. + +Ignoring ICMP echo requests (pings) sent to broadcast or multicast +addresses makes the system slightly more difficult to enumerate on the network. + CCE-84004-1 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.ipv4.icmp_echo_ignore_broadcasts.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84004-1 + - CJIS-5.10.1.1 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-5 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_icmp_echo_ignore_broadcasts + +- name: Comment out any occurrences of net.ipv4.icmp_echo_ignore_broadcasts from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.ipv4.icmp_echo_ignore_broadcasts + replace: '#net.ipv4.icmp_echo_ignore_broadcasts' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84004-1 + - CJIS-5.10.1.1 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-5 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_icmp_echo_ignore_broadcasts +- name: XCCDF Value sysctl_net_ipv4_icmp_echo_ignore_broadcasts_value # promote to variable + set_fact: + sysctl_net_ipv4_icmp_echo_ignore_broadcasts_value: !!str + tags: + - always + +- name: Ensure sysctl net.ipv4.icmp_echo_ignore_broadcasts is set + sysctl: + name: net.ipv4.icmp_echo_ignore_broadcasts + value: '{{ sysctl_net_ipv4_icmp_echo_ignore_broadcasts_value }}' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84004-1 + - CJIS-5.10.1.1 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-5 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_icmp_echo_ignore_broadcasts + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,net.ipv4.icmp_echo_ignore_broadcasts%3D1%0A + mode: 0644 + path: /etc/sysctl.d/75-sysctl_net_ipv4_icmp_echo_ignore_broadcasts.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.ipv4.icmp_echo_ignore_broadcasts from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.ipv4.icmp_echo_ignore_broadcasts.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.ipv4.icmp_echo_ignore_broadcasts" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done +sysctl_net_ipv4_icmp_echo_ignore_broadcasts_value='' + + +# +# Set runtime for net.ipv4.icmp_echo_ignore_broadcasts +# +/sbin/sysctl -q -n -w net.ipv4.icmp_echo_ignore_broadcasts="$sysctl_net_ipv4_icmp_echo_ignore_broadcasts_value" + +# +# If net.ipv4.icmp_echo_ignore_broadcasts present in /etc/sysctl.conf, change value to appropriate value +# else, add "net.ipv4.icmp_echo_ignore_broadcasts = value" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv4.icmp_echo_ignore_broadcasts") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv4_icmp_echo_ignore_broadcasts_value" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.ipv4.icmp_echo_ignore_broadcasts\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.ipv4.icmp_echo_ignore_broadcasts\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-84004-1" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable Kernel Parameter to Ignore Bogus ICMP Error Responses on IPv4 Interfaces + To set the runtime status of the net.ipv4.icmp_ignore_bogus_error_responses kernel parameter, run the following command: $ sudo sysctl -w net.ipv4.icmp_ignore_bogus_error_responses=1 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: net.ipv4.icmp_ignore_bogus_error_responses = 1 + BP28(R22) + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 2 + 3 + 7 + 8 + 9 + APO13.01 + BAI04.04 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.03 + DSS03.05 + DSS05.02 + DSS05.05 + DSS05.07 + DSS06.06 + 3.1.20 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 6.2 + SR 7.1 + SR 7.2 + SR 7.6 + A.12.1.2 + A.12.1.3 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.17.2.1 + A.9.1.2 + CIP-007-3 R4 + CIP-007-3 R4.1 + CIP-007-3 R4.2 + CIP-007-3 R5.1 + CM-7(a) + CM-7(b) + SC-5 + DE.CM-1 + PR.DS-4 + PR.IP-1 + PR.PT-3 + SRG-OS-000480-GPOS-00227 + Ignoring bogus ICMP error responses reduces +log size, although some activity would not be logged. + CCE-84015-7 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.ipv4.icmp_ignore_bogus_error_responses.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84015-7 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-5 + - disable_strategy + - low_complexity + - medium_disruption + - reboot_required + - sysctl_net_ipv4_icmp_ignore_bogus_error_responses + - unknown_severity + +- name: Comment out any occurrences of net.ipv4.icmp_ignore_bogus_error_responses + from /etc/sysctl.d/*.conf files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.ipv4.icmp_ignore_bogus_error_responses + replace: '#net.ipv4.icmp_ignore_bogus_error_responses' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84015-7 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-5 + - disable_strategy + - low_complexity + - medium_disruption + - reboot_required + - sysctl_net_ipv4_icmp_ignore_bogus_error_responses + - unknown_severity +- name: XCCDF Value sysctl_net_ipv4_icmp_ignore_bogus_error_responses_value # promote to variable + set_fact: + sysctl_net_ipv4_icmp_ignore_bogus_error_responses_value: !!str + tags: + - always + +- name: Ensure sysctl net.ipv4.icmp_ignore_bogus_error_responses is set + sysctl: + name: net.ipv4.icmp_ignore_bogus_error_responses + value: '{{ sysctl_net_ipv4_icmp_ignore_bogus_error_responses_value }}' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84015-7 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-5 + - disable_strategy + - low_complexity + - medium_disruption + - reboot_required + - sysctl_net_ipv4_icmp_ignore_bogus_error_responses + - unknown_severity + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,net.ipv4.icmp_ignore_bogus_error_responses%3D1%0A + mode: 0644 + path: /etc/sysctl.d/75-sysctl_net_ipv4_icmp_ignore_bogus_error_responses.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.ipv4.icmp_ignore_bogus_error_responses from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.ipv4.icmp_ignore_bogus_error_responses.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.ipv4.icmp_ignore_bogus_error_responses" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done +sysctl_net_ipv4_icmp_ignore_bogus_error_responses_value='' + + +# +# Set runtime for net.ipv4.icmp_ignore_bogus_error_responses +# +/sbin/sysctl -q -n -w net.ipv4.icmp_ignore_bogus_error_responses="$sysctl_net_ipv4_icmp_ignore_bogus_error_responses_value" + +# +# If net.ipv4.icmp_ignore_bogus_error_responses present in /etc/sysctl.conf, change value to appropriate value +# else, add "net.ipv4.icmp_ignore_bogus_error_responses = value" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv4.icmp_ignore_bogus_error_responses") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv4_icmp_ignore_bogus_error_responses_value" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.ipv4.icmp_ignore_bogus_error_responses\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.ipv4.icmp_ignore_bogus_error_responses\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-84015-7" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Set Kernel Parameter to Increase Local Port Range + To set the runtime status of the net.ipv4.ip_local_port_range kernel parameter, run the following command: $ sudo sysctl -w net.ipv4.ip_local_port_range=32768 65535 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: net.ipv4.ip_local_port_range = 32768 65535 + BP28(R22) + This setting defines the local port range that is used by TCP and UDP to +choose the local port. The first number is the first, the second the last +local port number. + CCE-90834-3 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.ipv4.ip_local_port_range.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-90834-3 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_ip_local_port_range + +- name: Comment out any occurrences of net.ipv4.ip_local_port_range from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.ipv4.ip_local_port_range + replace: '#net.ipv4.ip_local_port_range' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-90834-3 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_ip_local_port_range + +- name: Ensure sysctl net.ipv4.ip_local_port_range is set to 32768 65535 + sysctl: + name: net.ipv4.ip_local_port_range + value: 32768 65535 + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-90834-3 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_ip_local_port_range + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.ipv4.ip_local_port_range from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.ipv4.ip_local_port_range.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.ipv4.ip_local_port_range" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done + +# +# Set runtime for net.ipv4.ip_local_port_range +# +/sbin/sysctl -q -n -w net.ipv4.ip_local_port_range="32768 65535" + +# +# If net.ipv4.ip_local_port_range present in /etc/sysctl.conf, change value to "32768 65535" +# else, add "net.ipv4.ip_local_port_range = 32768 65535" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv4.ip_local_port_range") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "32768 65535" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.ipv4.ip_local_port_range\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.ipv4.ip_local_port_range\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-90834-3" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure Kernel to Rate Limit Sending of Duplicate TCP Acknowledgments + Make sure that the system is configured to limit the maximal rate for sending +duplicate acknowledgments in response to incoming TCP packets that are for +an existing connection but that are invalid due to any of these reasons: + +(a) out-of-window sequence number, (b) out-of-window acknowledgment number, +or (c) PAWS (Protection Against Wrapped Sequence numbers) check failure +This measure protects against or limits effects of DoS attacks against the system. +Set the system to implement rate-limiting measures by adding the following line to +/etc/sysctl.conf or a configuration file in the /etc/sysctl.d/ directory +(or modify the line to have the required value): +net.ipv4.tcp_invalid_ratelimit = +Issue the following command to make the changes take effect: +# sysctl --system + CCI-002385 + CIP-007-3 R4 + CIP-007-3 R4.1 + CIP-007-3 R4.2 + CIP-007-3 R5.1 + SC-5 + SRG-OS-000420-GPOS-00186 + SRG-OS-000420-VMM-001690 + Denial of Service (DoS) is a condition when a resource is not available for legitimate users. When +this occurs, the organization either cannot accomplish its mission or must +operate at degraded capacity. + +This can help mitigate simple “ack loop” DoS attacks, wherein a buggy or +malicious middlebox or man-in-the-middle can rewrite TCP header fields in +manner that causes each endpoint to think that the other is sending invalid +TCP segments, thus causing each side to send an unterminating stream of +duplicate acknowledgments for invalid segments. + CCE-86394-4 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.ipv4.tcp_invalid_ratelimit.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86394-4 + - NIST-800-53-SC-5 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_tcp_invalid_ratelimit + +- name: Comment out any occurrences of net.ipv4.tcp_invalid_ratelimit from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.ipv4.tcp_invalid_ratelimit + replace: '#net.ipv4.tcp_invalid_ratelimit' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86394-4 + - NIST-800-53-SC-5 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_tcp_invalid_ratelimit +- name: XCCDF Value sysctl_net_ipv4_tcp_invalid_ratelimit_value # promote to variable + set_fact: + sysctl_net_ipv4_tcp_invalid_ratelimit_value: !!str + tags: + - always + +- name: Ensure sysctl net.ipv4.tcp_invalid_ratelimit is set + sysctl: + name: net.ipv4.tcp_invalid_ratelimit + value: '{{ sysctl_net_ipv4_tcp_invalid_ratelimit_value }}' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86394-4 + - NIST-800-53-SC-5 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_tcp_invalid_ratelimit + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.ipv4.tcp_invalid_ratelimit from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.ipv4.tcp_invalid_ratelimit.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.ipv4.tcp_invalid_ratelimit" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done +sysctl_net_ipv4_tcp_invalid_ratelimit_value='' + + +# +# Set runtime for net.ipv4.tcp_invalid_ratelimit +# +/sbin/sysctl -q -n -w net.ipv4.tcp_invalid_ratelimit="$sysctl_net_ipv4_tcp_invalid_ratelimit_value" + +# +# If net.ipv4.tcp_invalid_ratelimit present in /etc/sysctl.conf, change value to appropriate value +# else, add "net.ipv4.tcp_invalid_ratelimit = value" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv4.tcp_invalid_ratelimit") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv4_tcp_invalid_ratelimit_value" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.ipv4.tcp_invalid_ratelimit\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.ipv4.tcp_invalid_ratelimit\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-86394-4" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable Kernel Parameter to Use TCP RFC 1337 on IPv4 Interfaces + To set the runtime status of the net.ipv4.tcp_rfc1337 kernel parameter, run the following command: $ sudo sysctl -w net.ipv4.tcp_rfc1337=1 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: net.ipv4.tcp_rfc1337 = 1 + BP28(R22) + Enable TCP behavior conformant with RFC 1337. When disabled, if a RST is +received in TIME_WAIT state, we close the socket immediately without waiting +for the end of the TIME_WAIT period. + CCE-84012-4 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.ipv4.tcp_rfc1337.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84012-4 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_tcp_rfc1337 + +- name: Comment out any occurrences of net.ipv4.tcp_rfc1337 from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.ipv4.tcp_rfc1337 + replace: '#net.ipv4.tcp_rfc1337' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84012-4 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_tcp_rfc1337 +- name: XCCDF Value sysctl_net_ipv4_tcp_rfc1337_value # promote to variable + set_fact: + sysctl_net_ipv4_tcp_rfc1337_value: !!str + tags: + - always + +- name: Ensure sysctl net.ipv4.tcp_rfc1337 is set + sysctl: + name: net.ipv4.tcp_rfc1337 + value: '{{ sysctl_net_ipv4_tcp_rfc1337_value }}' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84012-4 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_tcp_rfc1337 + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.ipv4.tcp_rfc1337 from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.ipv4.tcp_rfc1337.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.ipv4.tcp_rfc1337" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done +sysctl_net_ipv4_tcp_rfc1337_value='' + + +# +# Set runtime for net.ipv4.tcp_rfc1337 +# +/sbin/sysctl -q -n -w net.ipv4.tcp_rfc1337="$sysctl_net_ipv4_tcp_rfc1337_value" + +# +# If net.ipv4.tcp_rfc1337 present in /etc/sysctl.conf, change value to appropriate value +# else, add "net.ipv4.tcp_rfc1337 = value" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv4.tcp_rfc1337") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv4_tcp_rfc1337_value" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.ipv4.tcp_rfc1337\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.ipv4.tcp_rfc1337\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-84012-4" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable Kernel Parameter to Use TCP Syncookies on Network Interfaces + To set the runtime status of the net.ipv4.tcp_syncookies kernel parameter, run the following command: $ sudo sysctl -w net.ipv4.tcp_syncookies=1 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: net.ipv4.tcp_syncookies = 1 + BP28(R22) + 1 + 12 + 13 + 14 + 15 + 16 + 18 + 2 + 4 + 6 + 7 + 8 + 9 + 5.10.1.1 + APO01.06 + APO13.01 + BAI04.04 + DSS01.03 + DSS01.05 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.04 + DSS05.07 + DSS06.02 + 3.1.20 + CCI-000366 + CCI-001095 + 4.2.3.4 + 4.3.3.4 + 4.4.3.3 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.2 + SR 7.1 + SR 7.2 + SR 7.6 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.12.1.1 + A.12.1.2 + A.12.1.3 + A.13.1.1 + A.13.1.2 + A.13.1.3 + A.13.2.1 + A.13.2.2 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.17.2.1 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-7(a) + CM-7(b) + SC-5(1) + SC-5(2) + SC-5(3)(a) + CM-6(a) + DE.AE-1 + DE.CM-1 + ID.AM-3 + PR.AC-5 + PR.DS-4 + PR.DS-5 + PR.PT-4 + SRG-OS-000480-GPOS-00227 + SRG-OS-000420-GPOS-00186 + SRG-OS-000142-GPOS-00071 + A TCP SYN flood attack can cause a denial of service by filling a +system's TCP connection table with connections in the SYN_RCVD state. +Syncookies can be used to track a connection when a subsequent ACK is received, +verifying the initiator is attempting a valid connection and is not a flood +source. This feature is activated when a flood condition is detected, and +enables the system to continue servicing valid connection requests. + CCE-84006-6 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.ipv4.tcp_syncookies.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84006-6 + - CJIS-5.10.1.1 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-5(1) + - NIST-800-53-SC-5(2) + - NIST-800-53-SC-5(3)(a) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_tcp_syncookies + +- name: Comment out any occurrences of net.ipv4.tcp_syncookies from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.ipv4.tcp_syncookies + replace: '#net.ipv4.tcp_syncookies' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84006-6 + - CJIS-5.10.1.1 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-5(1) + - NIST-800-53-SC-5(2) + - NIST-800-53-SC-5(3)(a) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_tcp_syncookies +- name: XCCDF Value sysctl_net_ipv4_tcp_syncookies_value # promote to variable + set_fact: + sysctl_net_ipv4_tcp_syncookies_value: !!str + tags: + - always + +- name: Ensure sysctl net.ipv4.tcp_syncookies is set + sysctl: + name: net.ipv4.tcp_syncookies + value: '{{ sysctl_net_ipv4_tcp_syncookies_value }}' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84006-6 + - CJIS-5.10.1.1 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-5(1) + - NIST-800-53-SC-5(2) + - NIST-800-53-SC-5(3)(a) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_tcp_syncookies + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,net.ipv4.tcp_syncookies%3D1%0A + mode: 0644 + path: /etc/sysctl.d/75-sysctl_net_ipv4_tcp_syncookies.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.ipv4.tcp_syncookies from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.ipv4.tcp_syncookies.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.ipv4.tcp_syncookies" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done +sysctl_net_ipv4_tcp_syncookies_value='' + + +# +# Set runtime for net.ipv4.tcp_syncookies +# +/sbin/sysctl -q -n -w net.ipv4.tcp_syncookies="$sysctl_net_ipv4_tcp_syncookies_value" + +# +# If net.ipv4.tcp_syncookies present in /etc/sysctl.conf, change value to appropriate value +# else, add "net.ipv4.tcp_syncookies = value" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv4.tcp_syncookies") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv4_tcp_syncookies_value" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.ipv4.tcp_syncookies\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.ipv4.tcp_syncookies\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-84006-6" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + + Network Parameters for Hosts Only + If the system is not going to be used as a router, then setting certain +kernel parameters ensure that the host will not perform routing +of network traffic. + + + Disable Kernel Parameter for Sending ICMP Redirects on all IPv4 Interfaces + To set the runtime status of the net.ipv4.conf.all.send_redirects kernel parameter, run the following command: $ sudo sysctl -w net.ipv4.conf.all.send_redirects=0 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: net.ipv4.conf.all.send_redirects = 0 + BP28(R22) + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 18 + 2 + 3 + 4 + 6 + 7 + 8 + 9 + 5.10.1.1 + APO01.06 + APO13.01 + BAI04.04 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.03 + DSS01.05 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + DSS06.02 + DSS06.06 + 3.1.20 + CCI-000366 + 4.2.3.4 + 4.3.3.4 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + 4.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.2 + SR 7.1 + SR 7.2 + SR 7.6 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.12.1.1 + A.12.1.2 + A.12.1.3 + A.12.5.1 + A.12.6.2 + A.13.1.1 + A.13.1.2 + A.13.1.3 + A.13.2.1 + A.13.2.2 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.17.2.1 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-007-3 R4 + CIP-007-3 R4.1 + CIP-007-3 R4.2 + CIP-007-3 R5.1 + CM-7(a) + CM-7(b) + SC-5 + CM-6(a) + SC-7(a) + DE.AE-1 + DE.CM-1 + ID.AM-3 + PR.AC-5 + PR.DS-4 + PR.DS-5 + PR.IP-1 + PR.PT-3 + PR.PT-4 + SRG-OS-000480-GPOS-00227 + ICMP redirect messages are used by routers to inform hosts that a more +direct route exists for a particular destination. These messages contain information +from the system's route table possibly revealing portions of the network topology. + +The ability to send ICMP redirects is only appropriate for systems acting as routers. + CCE-83997-7 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.ipv4.conf.all.send_redirects.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83997-7 + - CJIS-5.10.1.1 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-5 + - NIST-800-53-SC-7(a) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_all_send_redirects + +- name: Comment out any occurrences of net.ipv4.conf.all.send_redirects from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.ipv4.conf.all.send_redirects + replace: '#net.ipv4.conf.all.send_redirects' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83997-7 + - CJIS-5.10.1.1 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-5 + - NIST-800-53-SC-7(a) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_all_send_redirects + +- name: Ensure sysctl net.ipv4.conf.all.send_redirects is set to 0 + sysctl: + name: net.ipv4.conf.all.send_redirects + value: '0' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83997-7 + - CJIS-5.10.1.1 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-5 + - NIST-800-53-SC-7(a) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_all_send_redirects + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,net.ipv4.conf.all.send_redirects%3D0%0A + mode: 0644 + path: /etc/sysctl.d/75-sysctl_net_ipv4_conf_all_send_redirects.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.ipv4.conf.all.send_redirects from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.ipv4.conf.all.send_redirects.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.ipv4.conf.all.send_redirects" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done + +# +# Set runtime for net.ipv4.conf.all.send_redirects +# +/sbin/sysctl -q -n -w net.ipv4.conf.all.send_redirects="0" + +# +# If net.ipv4.conf.all.send_redirects present in /etc/sysctl.conf, change value to "0" +# else, add "net.ipv4.conf.all.send_redirects = 0" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv4.conf.all.send_redirects") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "0" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.ipv4.conf.all.send_redirects\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.ipv4.conf.all.send_redirects\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-83997-7" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable Kernel Parameter for Sending ICMP Redirects on all IPv4 Interfaces by Default + To set the runtime status of the net.ipv4.conf.default.send_redirects kernel parameter, run the following command: $ sudo sysctl -w net.ipv4.conf.default.send_redirects=0 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: net.ipv4.conf.default.send_redirects = 0 + BP28(R22) + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 18 + 2 + 3 + 4 + 6 + 7 + 8 + 9 + 5.10.1.1 + APO01.06 + APO13.01 + BAI04.04 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.03 + DSS01.05 + DSS03.01 + DSS03.05 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + DSS06.02 + DSS06.06 + 3.1.20 + CCI-000366 + 4.2.3.4 + 4.3.3.4 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + 4.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.2 + SR 7.1 + SR 7.2 + SR 7.6 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.12.1.1 + A.12.1.2 + A.12.1.3 + A.12.5.1 + A.12.6.2 + A.13.1.1 + A.13.1.2 + A.13.1.3 + A.13.2.1 + A.13.2.2 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.17.2.1 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-007-3 R4 + CIP-007-3 R4.1 + CIP-007-3 R4.2 + CIP-007-3 R5.1 + CM-7(a) + CM-7(b) + SC-5 + CM-6(a) + SC-7(a) + DE.AE-1 + DE.CM-1 + ID.AM-3 + PR.AC-5 + PR.DS-4 + PR.DS-5 + PR.IP-1 + PR.PT-3 + PR.PT-4 + SRG-OS-000480-GPOS-00227 + ICMP redirect messages are used by routers to inform hosts that a more +direct route exists for a particular destination. These messages contain information +from the system's route table possibly revealing portions of the network topology. + +The ability to send ICMP redirects is only appropriate for systems acting as routers. + CCE-83999-3 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.ipv4.conf.default.send_redirects.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83999-3 + - CJIS-5.10.1.1 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-5 + - NIST-800-53-SC-7(a) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_default_send_redirects + +- name: Comment out any occurrences of net.ipv4.conf.default.send_redirects from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.ipv4.conf.default.send_redirects + replace: '#net.ipv4.conf.default.send_redirects' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83999-3 + - CJIS-5.10.1.1 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-5 + - NIST-800-53-SC-7(a) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_default_send_redirects + +- name: Ensure sysctl net.ipv4.conf.default.send_redirects is set to 0 + sysctl: + name: net.ipv4.conf.default.send_redirects + value: '0' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83999-3 + - CJIS-5.10.1.1 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-5 + - NIST-800-53-SC-7(a) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_conf_default_send_redirects + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,net.ipv4.conf.default.send_redirects%3D0%0A + mode: 0644 + path: /etc/sysctl.d/75-sysctl_net_ipv4_conf_default_send_redirects.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.ipv4.conf.default.send_redirects from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.ipv4.conf.default.send_redirects.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.ipv4.conf.default.send_redirects" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done + +# +# Set runtime for net.ipv4.conf.default.send_redirects +# +/sbin/sysctl -q -n -w net.ipv4.conf.default.send_redirects="0" + +# +# If net.ipv4.conf.default.send_redirects present in /etc/sysctl.conf, change value to "0" +# else, add "net.ipv4.conf.default.send_redirects = 0" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv4.conf.default.send_redirects") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "0" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.ipv4.conf.default.send_redirects\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.ipv4.conf.default.send_redirects\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-83999-3" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable Kernel Parameter for IP Forwarding on IPv4 Interfaces + To set the runtime status of the net.ipv4.ip_forward kernel parameter, run the following command: $ sudo sysctl -w net.ipv4.ip_forward=0 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: net.ipv4.ip_forward = 0 + Certain technologies such as virtual machines, containers, etc. rely on IPv4 forwarding to enable and use networking. +Disabling IPv4 forwarding would cause those technologies to stop working. Therefore, this rule should not be used in +profiles or benchmarks that target usage of IPv4 forwarding. + BP28(R22) + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 2 + 3 + 7 + 8 + 9 + APO13.01 + BAI04.04 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.03 + DSS03.05 + DSS05.02 + DSS05.05 + DSS05.07 + DSS06.06 + 3.1.20 + CCI-000366 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 6.2 + SR 7.1 + SR 7.2 + SR 7.6 + A.12.1.2 + A.12.1.3 + A.12.5.1 + A.12.6.2 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.17.2.1 + A.9.1.2 + CIP-007-3 R4 + CIP-007-3 R4.1 + CIP-007-3 R4.2 + CIP-007-3 R5.1 + CM-7(a) + CM-7(b) + SC-5 + CM-6(a) + SC-7(a) + DE.CM-1 + PR.DS-4 + PR.IP-1 + PR.PT-3 + PR.PT-4 + SRG-OS-000480-GPOS-00227 + Routing protocol daemons are typically used on routers to exchange +network topology information with other routers. If this capability is used when +not required, system network information may be unnecessarily transmitted across +the network. + CCE-83998-5 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.ipv4.ip_forward.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83998-5 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-5 + - NIST-800-53-SC-7(a) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_ip_forward + +- name: Comment out any occurrences of net.ipv4.ip_forward from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.ipv4.ip_forward + replace: '#net.ipv4.ip_forward' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83998-5 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-5 + - NIST-800-53-SC-7(a) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_ip_forward + +- name: Ensure sysctl net.ipv4.ip_forward is set to 0 + sysctl: + name: net.ipv4.ip_forward + value: '0' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83998-5 + - NIST-800-171-3.1.20 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-SC-5 + - NIST-800-53-SC-7(a) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_ipv4_ip_forward + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.ipv4.ip_forward from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.ipv4.ip_forward.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.ipv4.ip_forward" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done + +# +# Set runtime for net.ipv4.ip_forward +# +/sbin/sysctl -q -n -w net.ipv4.ip_forward="0" + +# +# If net.ipv4.ip_forward present in /etc/sysctl.conf, change value to "0" +# else, add "net.ipv4.ip_forward = 0" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv4.ip_forward") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "0" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.ipv4.ip_forward\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.ipv4.ip_forward\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-83998-5" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + + SuSEfirewall2 + The SuSEfirewall2 provides a managed firewall. + + + Uncomplicated Firewall (ufw) + The Linux kernel in Ubuntu provides a packet filtering system called +netfilter, and the traditional interface for manipulating netfilter are +the iptables suite of commands. iptables provide a complete firewall +solution that is both highly configurable and highly flexible. + +Becoming proficient in iptables takes time, and getting started with +netfilter firewalling using only iptables can be a daunting task. As a +result, many frontends for iptables have been created over the years, +each trying to achieve a different result and targeting a different +audience. + +The Uncomplicated Firewall (ufw) is a frontend for iptables and is +particularly well-suited for host-based firewalls. ufw provides a +framework for managing netfilter, as well as a command-line interface +for manipulating the firewall. ufw aims to provide an easy to use +interface for people unfamiliar with firewall concepts, while at the +same time simplifies complicated iptables commands to help an +administrator who knows what he or she is doing. ufw is an upstream +for other distributions and graphical frontends. + + + Verify ufw Enabled + +The ufw service can be enabled with the following command: +$ sudo systemctl enable ufw.service + CCI-002314 + SRG-OS-000297-GPOS-00115 + The ufw service must be enabled and running in order for ufw to protect the system + + include enable_ufw + +class enable_ufw { + service {'ufw': + enable => true, + ensure => 'running', + } +} + + - name: Enable service ufw + block: + + - name: Gather the package facts + package_facts: + manager: auto + + - name: Enable service ufw + service: + name: ufw + enabled: 'yes' + state: started + masked: 'no' + when: + - '"ufw" in ansible_facts.packages' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_ufw_enabled + + +[customizations.services] +enabled = ["ufw"] + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" unmask 'ufw.service' +"$SYSTEMCTL_EXEC" start 'ufw.service' +"$SYSTEMCTL_EXEC" enable 'ufw.service' + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Uncommon Network Protocols + The system includes support for several network protocols which are not commonly used. +Although security vulnerabilities in kernel networking code are not frequently discovered, +the consequences can be dramatic. Ensuring uncommon network protocols are disabled +reduces the system's risk to attacks targeted at its implementation of those protocols. + Although these protocols are not commonly used, avoid disruption +in your network environment by ensuring they are not needed +prior to disabling them. + + + Disable ATM Support + The Asynchronous Transfer Mode (ATM) is a protocol operating on +network, data link, and physical layers, based on virtual circuits +and virtual paths. + +To configure the system to prevent the atm +kernel module from being loaded, add the following line to the file /etc/modprobe.d/atm.conf: +install atm /bin/true + CCI-000381 + CCI-000366 + AC-18 + FMT_SMF_EXT.1 + SRG-OS-000095-GPOS-00049 + SRG-OS-000480-GPOS-00227 + Disabling ATM protects the system against exploitation of any +flaws in its implementation. + CCE-84137-9 + - name: Ensure kernel module 'atm' is disabled + lineinfile: + create: true + dest: /etc/modprobe.d/atm.conf + regexp: atm + line: install atm /bin/true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84137-9 + - NIST-800-53-AC-18 + - disable_strategy + - kernel_module_atm_disabled + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + +- name: Ensure kernel module 'atm' is blacklisted + lineinfile: + create: true + dest: /etc/modprobe.d/atm.conf + regexp: ^blacklist atm$ + line: blacklist atm + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84137-9 + - NIST-800-53-AC-18 + - disable_strategy + - kernel_module_atm_disabled + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,install%20atm%20/bin/true%0Ablacklist%20atm%0A + mode: 0644 + path: /etc/modprobe.d/atm.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if LC_ALL=C grep -q -m 1 "^install atm" /etc/modprobe.d/atm.conf ; then + + sed -i 's#^install atm.*#install atm /bin/true#g' /etc/modprobe.d/atm.conf +else + echo -e "\n# Disable per security requirements" >> /etc/modprobe.d/atm.conf + echo "install atm /bin/true" >> /etc/modprobe.d/atm.conf +fi + +if ! LC_ALL=C grep -q -m 1 "^blacklist atm$" /etc/modprobe.d/atm.conf ; then + echo "blacklist atm" >> /etc/modprobe.d/atm.conf +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable CAN Support + The Controller Area Network (CAN) is a serial communications +protocol which was initially developed for automotive and +is now also used in marine, industrial, and medical applications. + +To configure the system to prevent the can +kernel module from being loaded, add the following line to the file /etc/modprobe.d/can.conf: +install can /bin/true + CCI-000381 + CCI-000366 + AC-18 + FMT_SMF_EXT.1 + SRG-OS-000095-GPOS-00049 + SRG-OS-000480-GPOS-00227 + Disabling CAN protects the system against exploitation of any +flaws in its implementation. + CCE-84134-6 + - name: Ensure kernel module 'can' is disabled + lineinfile: + create: true + dest: /etc/modprobe.d/can.conf + regexp: can + line: install can /bin/true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84134-6 + - NIST-800-53-AC-18 + - disable_strategy + - kernel_module_can_disabled + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + +- name: Ensure kernel module 'can' is blacklisted + lineinfile: + create: true + dest: /etc/modprobe.d/can.conf + regexp: ^blacklist can$ + line: blacklist can + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84134-6 + - NIST-800-53-AC-18 + - disable_strategy + - kernel_module_can_disabled + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,install%20can%20/bin/true%0Ablacklist%20can%0A + mode: 0644 + path: /etc/modprobe.d/can.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if LC_ALL=C grep -q -m 1 "^install can" /etc/modprobe.d/can.conf ; then + + sed -i 's#^install can.*#install can /bin/true#g' /etc/modprobe.d/can.conf +else + echo -e "\n# Disable per security requirements" >> /etc/modprobe.d/can.conf + echo "install can /bin/true" >> /etc/modprobe.d/can.conf +fi + +if ! LC_ALL=C grep -q -m 1 "^blacklist can$" /etc/modprobe.d/can.conf ; then + echo "blacklist can" >> /etc/modprobe.d/can.conf +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable DCCP Support + The Datagram Congestion Control Protocol (DCCP) is a +relatively new transport layer protocol, designed to support +streaming media and telephony. + +To configure the system to prevent the dccp +kernel module from being loaded, add the following line to the file /etc/modprobe.d/dccp.conf: +install dccp /bin/true + 11 + 14 + 3 + 9 + 5.10.1 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + 3.4.6 + CCI-001958 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + PR.PT-3 + SRG-OS-000096-GPOS-00050 + SRG-OS-000378-GPOS-00163 + Disabling DCCP protects +the system against exploitation of any flaws in its implementation. + CCE-84136-1 + - name: Ensure kernel module 'dccp' is disabled + lineinfile: + create: true + dest: /etc/modprobe.d/dccp.conf + regexp: dccp + line: install dccp /bin/true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84136-1 + - CJIS-5.10.1 + - NIST-800-171-3.4.6 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - kernel_module_dccp_disabled + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + +- name: Ensure kernel module 'dccp' is blacklisted + lineinfile: + create: true + dest: /etc/modprobe.d/dccp.conf + regexp: ^blacklist dccp$ + line: blacklist dccp + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84136-1 + - CJIS-5.10.1 + - NIST-800-171-3.4.6 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - kernel_module_dccp_disabled + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,install%20dccp%20/bin/true%0Ablacklist%20dccp%0A + mode: 0644 + path: /etc/modprobe.d/dccp.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if LC_ALL=C grep -q -m 1 "^install dccp" /etc/modprobe.d/dccp.conf ; then + + sed -i 's#^install dccp.*#install dccp /bin/true#g' /etc/modprobe.d/dccp.conf +else + echo -e "\n# Disable per security requirements" >> /etc/modprobe.d/dccp.conf + echo "install dccp /bin/true" >> /etc/modprobe.d/dccp.conf +fi + +if ! LC_ALL=C grep -q -m 1 "^blacklist dccp$" /etc/modprobe.d/dccp.conf ; then + echo "blacklist dccp" >> /etc/modprobe.d/dccp.conf +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable IEEE 1394 (FireWire) Support + The IEEE 1394 (FireWire) is a serial bus standard for +high-speed real-time communication. + +To configure the system to prevent the firewire-core +kernel module from being loaded, add the following line to the file /etc/modprobe.d/firewire-core.conf: +install firewire-core /bin/true + CCI-000381 + AC-18 + FMT_SMF_EXT.1 + SRG-OS-000095-GPOS-00049 + Disabling FireWire protects the system against exploitation of any +flaws in its implementation. + CCE-84060-3 + - name: Ensure kernel module 'firewire-core' is disabled + lineinfile: + create: true + dest: /etc/modprobe.d/firewire-core.conf + regexp: firewire-core + line: install firewire-core /bin/true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84060-3 + - NIST-800-53-AC-18 + - disable_strategy + - kernel_module_firewire-core_disabled + - low_complexity + - low_severity + - medium_disruption + - reboot_required + +- name: Ensure kernel module 'firewire-core' is blacklisted + lineinfile: + create: true + dest: /etc/modprobe.d/firewire-core.conf + regexp: ^blacklist firewire-core$ + line: blacklist firewire-core + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84060-3 + - NIST-800-53-AC-18 + - disable_strategy + - kernel_module_firewire-core_disabled + - low_complexity + - low_severity + - medium_disruption + - reboot_required + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,install%20firewire-core%20/bin/true%0Ablacklist%20firewire-core%0A + mode: 0644 + path: /etc/modprobe.d/firewire-core.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if LC_ALL=C grep -q -m 1 "^install firewire-core" /etc/modprobe.d/firewire-core.conf ; then + + sed -i 's#^install firewire-core.*#install firewire-core /bin/true#g' /etc/modprobe.d/firewire-core.conf +else + echo -e "\n# Disable per security requirements" >> /etc/modprobe.d/firewire-core.conf + echo "install firewire-core /bin/true" >> /etc/modprobe.d/firewire-core.conf +fi + +if ! LC_ALL=C grep -q -m 1 "^blacklist firewire-core$" /etc/modprobe.d/firewire-core.conf ; then + echo "blacklist firewire-core" >> /etc/modprobe.d/firewire-core.conf +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable RDS Support + The Reliable Datagram Sockets (RDS) protocol is a transport +layer protocol designed to provide reliable high-bandwidth, +low-latency communications between nodes in a cluster. + +To configure the system to prevent the rds +kernel module from being loaded, add the following line to the file /etc/modprobe.d/rds.conf: +install rds /bin/true + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + PR.PT-3 + Disabling RDS protects +the system against exploitation of any flaws in its implementation. + CCE-84064-5 + - name: Ensure kernel module 'rds' is disabled + lineinfile: + create: true + dest: /etc/modprobe.d/rds.conf + regexp: rds + line: install rds /bin/true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84064-5 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - kernel_module_rds_disabled + - low_complexity + - low_severity + - medium_disruption + - reboot_required + +- name: Ensure kernel module 'rds' is blacklisted + lineinfile: + create: true + dest: /etc/modprobe.d/rds.conf + regexp: ^blacklist rds$ + line: blacklist rds + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84064-5 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - kernel_module_rds_disabled + - low_complexity + - low_severity + - medium_disruption + - reboot_required + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,install%20rds%20/bin/true%0Ablacklist%20rds%0A + mode: 0644 + path: /etc/modprobe.d/rds.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if LC_ALL=C grep -q -m 1 "^install rds" /etc/modprobe.d/rds.conf ; then + + sed -i 's#^install rds.*#install rds /bin/true#g' /etc/modprobe.d/rds.conf +else + echo -e "\n# Disable per security requirements" >> /etc/modprobe.d/rds.conf + echo "install rds /bin/true" >> /etc/modprobe.d/rds.conf +fi + +if ! LC_ALL=C grep -q -m 1 "^blacklist rds$" /etc/modprobe.d/rds.conf ; then + echo "blacklist rds" >> /etc/modprobe.d/rds.conf +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable SCTP Support + The Stream Control Transmission Protocol (SCTP) is a +transport layer protocol, designed to support the idea of +message-oriented communication, with several streams of messages +within one connection. + +To configure the system to prevent the sctp +kernel module from being loaded, add the following line to the file /etc/modprobe.d/sctp.conf: +install sctp /bin/true + 11 + 14 + 3 + 9 + 5.10.1 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + 3.4.6 + CCI-000381 + CCI-000366 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + PR.PT-3 + SRG-OS-000095-GPOS-00049 + SRG-OS-000480-GPOS-00227 + Disabling SCTP protects +the system against exploitation of any flaws in its implementation. + CCE-84139-5 + - name: Ensure kernel module 'sctp' is disabled + lineinfile: + create: true + dest: /etc/modprobe.d/sctp.conf + regexp: sctp + line: install sctp /bin/true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84139-5 + - CJIS-5.10.1 + - NIST-800-171-3.4.6 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - kernel_module_sctp_disabled + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + +- name: Ensure kernel module 'sctp' is blacklisted + lineinfile: + create: true + dest: /etc/modprobe.d/sctp.conf + regexp: ^blacklist sctp$ + line: blacklist sctp + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84139-5 + - CJIS-5.10.1 + - NIST-800-171-3.4.6 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - kernel_module_sctp_disabled + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,install%20sctp%20/bin/true%0Ablacklist%20sctp%0A + mode: 0644 + path: /etc/modprobe.d/sctp.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if LC_ALL=C grep -q -m 1 "^install sctp" /etc/modprobe.d/sctp.conf ; then + + sed -i 's#^install sctp.*#install sctp /bin/true#g' /etc/modprobe.d/sctp.conf +else + echo -e "\n# Disable per security requirements" >> /etc/modprobe.d/sctp.conf + echo "install sctp /bin/true" >> /etc/modprobe.d/sctp.conf +fi + +if ! LC_ALL=C grep -q -m 1 "^blacklist sctp$" /etc/modprobe.d/sctp.conf ; then + echo "blacklist sctp" >> /etc/modprobe.d/sctp.conf +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable TIPC Support + The Transparent Inter-Process Communication (TIPC) protocol +is designed to provide communications between nodes in a +cluster. + +To configure the system to prevent the tipc +kernel module from being loaded, add the following line to the file /etc/modprobe.d/tipc.conf: +install tipc /bin/true + This configuration baseline was created to deploy the base operating system for general purpose +workloads. When the operating system is configured for certain purposes, such as +a node in High Performance Computing cluster, it is expected that +the tipc kernel module will be loaded. + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + CCI-000381 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + PR.PT-3 + FMT_SMF_EXT.1 + SRG-OS-000095-GPOS-00049 + Disabling TIPC protects +the system against exploitation of any flaws in its implementation. + CCE-84065-2 + - name: Ensure kernel module 'tipc' is disabled + lineinfile: + create: true + dest: /etc/modprobe.d/tipc.conf + regexp: tipc + line: install tipc /bin/true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84065-2 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - kernel_module_tipc_disabled + - low_complexity + - low_severity + - medium_disruption + - reboot_required + +- name: Ensure kernel module 'tipc' is blacklisted + lineinfile: + create: true + dest: /etc/modprobe.d/tipc.conf + regexp: ^blacklist tipc$ + line: blacklist tipc + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84065-2 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - kernel_module_tipc_disabled + - low_complexity + - low_severity + - medium_disruption + - reboot_required + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,install%20tipc%20/bin/true%0Ablacklist%20tipc%0A + mode: 0644 + path: /etc/modprobe.d/tipc.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if LC_ALL=C grep -q -m 1 "^install tipc" /etc/modprobe.d/tipc.conf ; then + + sed -i 's#^install tipc.*#install tipc /bin/true#g' /etc/modprobe.d/tipc.conf +else + echo -e "\n# Disable per security requirements" >> /etc/modprobe.d/tipc.conf + echo "install tipc /bin/true" >> /etc/modprobe.d/tipc.conf +fi + +if ! LC_ALL=C grep -q -m 1 "^blacklist tipc$" /etc/modprobe.d/tipc.conf ; then + echo "blacklist tipc" >> /etc/modprobe.d/tipc.conf +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Wireless Networking + Wireless networking, such as 802.11 +(WiFi) and Bluetooth, can present a security risk to sensitive or +classified systems and networks. Wireless networking hardware is +much more likely to be included in laptop or portable systems than +in desktops or servers. + +Removal of hardware provides the greatest assurance that the wireless +capability remains disabled. Acquisition policies often include provisions to +prevent the purchase of equipment that will be used in sensitive spaces and +includes wireless capabilities. If it is impractical to remove the wireless +hardware, and policy permits the device to enter sensitive spaces as long +as wireless is disabled, efforts should instead focus on disabling wireless capability +via software. + + Disable Wireless Through Software Configuration + If it is impossible to remove the wireless hardware +from the device in question, disable as much of it as possible +through software. The following methods can disable software +support for wireless networking, but note that these methods do not +prevent malicious software or careless users from re-activating the +devices. + + Disable Bluetooth Kernel Module + The kernel's module loading system can be configured to prevent +loading of the Bluetooth module. Add the following to +the appropriate /etc/modprobe.d configuration file +to prevent the loading of the Bluetooth module: +install bluetooth /bin/true + 11 + 12 + 14 + 15 + 3 + 8 + 9 + 5.13.1.3 + APO13.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.04 + DSS05.02 + DSS05.03 + DSS05.05 + DSS06.06 + 3.1.16 + CCI-000085 + CCI-001443 + CCI-001444 + CCI-001551 + CCI-002418 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.6.2.1 + A.6.2.2 + A.9.1.2 + AC-18(a) + AC-18(3) + CM-7(a) + CM-7(b) + CM-6(a) + MP-7 + PR.AC-3 + PR.IP-1 + PR.PT-3 + PR.PT-4 + SRG-OS-000095-GPOS-00049 + SRG-OS-000300-GPOS-00118 + If Bluetooth functionality must be disabled, preventing the kernel +from loading the kernel module provides an additional safeguard against its +activation. + + CCE-84067-8 + - name: Ensure kernel module 'bluetooth' is disabled + lineinfile: + create: true + dest: /etc/modprobe.d/bluetooth.conf + regexp: bluetooth + line: install bluetooth /bin/true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84067-8 + - CJIS-5.13.1.3 + - NIST-800-171-3.1.16 + - NIST-800-53-AC-18(3) + - NIST-800-53-AC-18(a) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - disable_strategy + - kernel_module_bluetooth_disabled + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + +- name: Ensure kernel module 'bluetooth' is blacklisted + lineinfile: + create: true + dest: /etc/modprobe.d/bluetooth.conf + regexp: ^blacklist bluetooth$ + line: blacklist bluetooth + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84067-8 + - CJIS-5.13.1.3 + - NIST-800-171-3.1.16 + - NIST-800-53-AC-18(3) + - NIST-800-53-AC-18(a) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - disable_strategy + - kernel_module_bluetooth_disabled + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,install%20bluetooth%20/bin/true%0Ablacklist%20bluetooth%0A + mode: 0644 + path: /etc/modprobe.d/bluetooth.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if LC_ALL=C grep -q -m 1 "^install bluetooth" /etc/modprobe.d/bluetooth.conf ; then + + sed -i 's#^install bluetooth.*#install bluetooth /bin/true#g' /etc/modprobe.d/bluetooth.conf +else + echo -e "\n# Disable per security requirements" >> /etc/modprobe.d/bluetooth.conf + echo "install bluetooth /bin/true" >> /etc/modprobe.d/bluetooth.conf +fi + +if ! LC_ALL=C grep -q -m 1 "^blacklist bluetooth$" /etc/modprobe.d/bluetooth.conf ; then + echo "blacklist bluetooth" >> /etc/modprobe.d/bluetooth.conf +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable Kernel cfg80211 Module + +To configure the system to prevent the cfg80211 +kernel module from being loaded, add the following line to the file /etc/modprobe.d/cfg80211.conf: +install cfg80211 /bin/true + AC-18(a) + AC-18(3) + CM-7(a) + CM-7(b) + CM-6(a) + MP-7 + AC-18(4) + If Wireless functionality must be disabled, preventing the kernel +from loading the kernel module provides an additional safeguard against its +activation. + + - name: Ensure kernel module 'cfg80211' is disabled + lineinfile: + create: true + dest: /etc/modprobe.d/cfg80211.conf + regexp: cfg80211 + line: install cfg80211 /bin/true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AC-18(3) + - NIST-800-53-AC-18(4) + - NIST-800-53-AC-18(a) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - disable_strategy + - kernel_module_cfg80211_disabled + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + +- name: Ensure kernel module 'cfg80211' is blacklisted + lineinfile: + create: true + dest: /etc/modprobe.d/cfg80211.conf + regexp: ^blacklist cfg80211$ + line: blacklist cfg80211 + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AC-18(3) + - NIST-800-53-AC-18(4) + - NIST-800-53-AC-18(a) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - disable_strategy + - kernel_module_cfg80211_disabled + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,install%20cfg80211%20/bin/true%0Ablacklist%20cfg80211%0A + mode: 0644 + path: /etc/modprobe.d/cfg80211.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if LC_ALL=C grep -q -m 1 "^install cfg80211" /etc/modprobe.d/cfg80211.conf ; then + + sed -i 's#^install cfg80211.*#install cfg80211 /bin/true#g' /etc/modprobe.d/cfg80211.conf +else + echo -e "\n# Disable per security requirements" >> /etc/modprobe.d/cfg80211.conf + echo "install cfg80211 /bin/true" >> /etc/modprobe.d/cfg80211.conf +fi + +if ! LC_ALL=C grep -q -m 1 "^blacklist cfg80211$" /etc/modprobe.d/cfg80211.conf ; then + echo "blacklist cfg80211" >> /etc/modprobe.d/cfg80211.conf +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable Kernel iwlmvm Module + +To configure the system to prevent the iwlmvm +kernel module from being loaded, add the following line to the file /etc/modprobe.d/iwlmvm.conf: +install iwlmvm /bin/true + AC-18(a) + AC-18(3) + CM-7(a) + CM-7(b) + CM-6(a) + MP-7 + AC-18(4) + If Wireless functionality must be disabled, preventing the kernel +from loading the kernel module provides an additional safeguard against its +activation. + + - name: Ensure kernel module 'iwlmvm' is disabled + lineinfile: + create: true + dest: /etc/modprobe.d/iwlmvm.conf + regexp: iwlmvm + line: install iwlmvm /bin/true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AC-18(3) + - NIST-800-53-AC-18(4) + - NIST-800-53-AC-18(a) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - disable_strategy + - kernel_module_iwlmvm_disabled + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + +- name: Ensure kernel module 'iwlmvm' is blacklisted + lineinfile: + create: true + dest: /etc/modprobe.d/iwlmvm.conf + regexp: ^blacklist iwlmvm$ + line: blacklist iwlmvm + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AC-18(3) + - NIST-800-53-AC-18(4) + - NIST-800-53-AC-18(a) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - disable_strategy + - kernel_module_iwlmvm_disabled + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,install%20iwlmvm%20/bin/true%0Ablacklist%20iwlmvm%0A + mode: 0644 + path: /etc/modprobe.d/iwlmvm.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if LC_ALL=C grep -q -m 1 "^install iwlmvm" /etc/modprobe.d/iwlmvm.conf ; then + + sed -i 's#^install iwlmvm.*#install iwlmvm /bin/true#g' /etc/modprobe.d/iwlmvm.conf +else + echo -e "\n# Disable per security requirements" >> /etc/modprobe.d/iwlmvm.conf + echo "install iwlmvm /bin/true" >> /etc/modprobe.d/iwlmvm.conf +fi + +if ! LC_ALL=C grep -q -m 1 "^blacklist iwlmvm$" /etc/modprobe.d/iwlmvm.conf ; then + echo "blacklist iwlmvm" >> /etc/modprobe.d/iwlmvm.conf +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable Kernel iwlwifi Module + +To configure the system to prevent the iwlwifi +kernel module from being loaded, add the following line to the file /etc/modprobe.d/iwlwifi.conf: +install iwlwifi /bin/true + AC-18(a) + AC-18(3) + CM-7(a) + CM-7(b) + CM-6(a) + MP-7 + AC-18(4) + If Wireless functionality must be disabled, preventing the kernel +from loading the kernel module provides an additional safeguard against its +activation. + + - name: Ensure kernel module 'iwlwifi' is disabled + lineinfile: + create: true + dest: /etc/modprobe.d/iwlwifi.conf + regexp: iwlwifi + line: install iwlwifi /bin/true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AC-18(3) + - NIST-800-53-AC-18(4) + - NIST-800-53-AC-18(a) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - disable_strategy + - kernel_module_iwlwifi_disabled + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + +- name: Ensure kernel module 'iwlwifi' is blacklisted + lineinfile: + create: true + dest: /etc/modprobe.d/iwlwifi.conf + regexp: ^blacklist iwlwifi$ + line: blacklist iwlwifi + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AC-18(3) + - NIST-800-53-AC-18(4) + - NIST-800-53-AC-18(a) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - disable_strategy + - kernel_module_iwlwifi_disabled + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,install%20iwlwifi%20/bin/true%0Ablacklist%20iwlwifi%0A + mode: 0644 + path: /etc/modprobe.d/iwlwifi.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if LC_ALL=C grep -q -m 1 "^install iwlwifi" /etc/modprobe.d/iwlwifi.conf ; then + + sed -i 's#^install iwlwifi.*#install iwlwifi /bin/true#g' /etc/modprobe.d/iwlwifi.conf +else + echo -e "\n# Disable per security requirements" >> /etc/modprobe.d/iwlwifi.conf + echo "install iwlwifi /bin/true" >> /etc/modprobe.d/iwlwifi.conf +fi + +if ! LC_ALL=C grep -q -m 1 "^blacklist iwlwifi$" /etc/modprobe.d/iwlwifi.conf ; then + echo "blacklist iwlwifi" >> /etc/modprobe.d/iwlwifi.conf +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable Kernel mac80211 Module + +To configure the system to prevent the mac80211 +kernel module from being loaded, add the following line to the file /etc/modprobe.d/mac80211.conf: +install mac80211 /bin/true + AC-18(a) + AC-18(3) + CM-7(a) + CM-7(b) + CM-6(a) + MP-7 + AC-18(4) + If Wireless functionality must be disabled, preventing the kernel +from loading the kernel module provides an additional safeguard against its +activation. + + - name: Ensure kernel module 'mac80211' is disabled + lineinfile: + create: true + dest: /etc/modprobe.d/mac80211.conf + regexp: mac80211 + line: install mac80211 /bin/true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AC-18(3) + - NIST-800-53-AC-18(4) + - NIST-800-53-AC-18(a) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - disable_strategy + - kernel_module_mac80211_disabled + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + +- name: Ensure kernel module 'mac80211' is blacklisted + lineinfile: + create: true + dest: /etc/modprobe.d/mac80211.conf + regexp: ^blacklist mac80211$ + line: blacklist mac80211 + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AC-18(3) + - NIST-800-53-AC-18(4) + - NIST-800-53-AC-18(a) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - disable_strategy + - kernel_module_mac80211_disabled + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,install%20mac80211%20/bin/true%0Ablacklist%20mac80211%0A + mode: 0644 + path: /etc/modprobe.d/mac80211.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if LC_ALL=C grep -q -m 1 "^install mac80211" /etc/modprobe.d/mac80211.conf ; then + + sed -i 's#^install mac80211.*#install mac80211 /bin/true#g' /etc/modprobe.d/mac80211.conf +else + echo -e "\n# Disable per security requirements" >> /etc/modprobe.d/mac80211.conf + echo "install mac80211 /bin/true" >> /etc/modprobe.d/mac80211.conf +fi + +if ! LC_ALL=C grep -q -m 1 "^blacklist mac80211$" /etc/modprobe.d/mac80211.conf ; then + echo "blacklist mac80211" >> /etc/modprobe.d/mac80211.conf +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Deactivate Wireless Network Interfaces + Deactivating wireless network interfaces should prevent normal usage of the wireless +capability. + + +Configure the system to disable all wireless network interfaces with the following command: +$ sudo nmcli radio all off + 11 + 12 + 14 + 15 + 3 + 8 + 9 + APO13.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.04 + DSS05.02 + DSS05.03 + DSS05.05 + DSS06.06 + 3.1.16 + CCI-000085 + CCI-002418 + CCI-002421 + CCI-001443 + CCI-001444 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + 1315 + 1319 + A.11.2.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.6.2.1 + A.6.2.2 + A.9.1.2 + AC-18(a) + AC-18(3) + CM-7(a) + CM-7(b) + CM-6(a) + MP-7 + PR.AC-3 + PR.IP-1 + PR.PT-3 + PR.PT-4 + SRG-OS-000299-GPOS-00117 + SRG-OS-000300-GPOS-00118 + SRG-OS-000424-GPOS-00188 + SRG-OS-000481-GPOS-000481 + The use of wireless networking can introduce many different attack vectors into +the organization's network. Common attack vectors such as malicious association +and ad hoc networks will allow an attacker to spoof a wireless access point +(AP), allowing validated systems to connect to the malicious AP and enabling the +attacker to monitor and record network traffic. These malicious APs can also +serve to create a man-in-the-middle attack or be used to create a denial of +service to valid network resources. + + CCE-84066-0 + - name: Deactivate Wireless Network Interfaces + command: nmcli radio wifi off + tags: + - CCE-84066-0 + - NIST-800-171-3.1.16 + - NIST-800-53-AC-18(3) + - NIST-800-53-AC-18(a) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - unknown_strategy + - wireless_disable_interfaces + + +nmcli radio all off + + + + + + + + + + + + Disable Unused Interfaces + Network interfaces expand the attack surface of the +system. Unused interfaces are not monitored or controlled, and +should be disabled. + +If the system does not require network communications but still +needs to use the loopback interface, remove all files of the form +ifcfg-interface except for ifcfg-lo from +/etc/sysconfig/network-scripts: +$ sudo rm /etc/sysconfig/network-scripts/ifcfg-interface +If the system is a standalone machine with no need for network access or even +communication over the loopback device, then disable this service. + +The network service can be disabled with the following command: +$ sudo systemctl mask --now network.service + + + Transport Layer Security Support + Support for Transport Layer Security (TLS), and its predecessor, the Secure +Sockets Layer (SSL), is included in Red Hat Enterprise Linux in the OpenSSL software (RPM package +openssl). TLS provides encrypted and authenticated network +communications, and many network services include support for it. TLS or SSL +can be leveraged to avoid any plaintext transmission of sensitive data. + +For information on how to use OpenSSL, see +http://www.openssl.org/docs/. Information on FIPS validation +of OpenSSL is available at http://www.openssl.org/docs/fips.html +and http://csrc.nist.gov/groups/STM/cmvp/documents/140-1/140val-all.htm. + + + + File Permissions and Masks + Traditional Unix security relies heavily on file and +directory permissions to prevent unauthorized users from reading or +modifying files to which they should not have access. + +Several of the commands in this section search filesystems +for files or directories with certain characteristics, and are +intended to be run on every local partition on a given system. +When the variable PART appears in one of the commands below, +it means that the command is intended to be run repeatedly, with the +name of each local partition substituted for PART in turn. + +The following command prints a list of all xfs partitions on the local +system, which is the default filesystem for Red Hat Enterprise Linux 9 +installations: +$ mount -t xfs | awk '{print $3}' +For any systems that use a different +local filesystem type, modify this command as appropriate. + + Verify Permissions on Important Files and +Directories + Permissions for many files on a system must be set +restrictively to ensure sensitive information is properly protected. +This section discusses important +permission restrictions which can be verified +to ensure that no harmful discrepancies have +arisen. + + Ensure All World-Writable Directories Are Owned by root user + All directories in local partitions which are world-writable should be owned +by root. If any world-writable directories are not owned by root, this +should be investigated. Following this, the files should be deleted or +assigned to root user. + BP28(R40) + CCI-000366 + SRG-OS-000480-GPOS-00227 + SRG-OS-000138-GPOS-00069 + Allowing a user account to own a world-writable directory is +undesirable because it allows the owner of that directory to remove +or replace any files that may be placed in the directory by other +users. + CCE-83903-5 + +find / -not -fstype afs -not -fstype ceph -not -fstype cifs -not -fstype smb3 -not -fstype smbfs -not -fstype sshfs -not -fstype ncpfs -not -fstype ncp -not -fstype nfs -not -fstype nfs4 -not -fstype gfs -not -fstype gfs2 -not -fstype glusterfs -not -fstype gpfs -not -fstype pvfs2 -not -fstype ocfs2 -not -fstype lustre -not -fstype davfs -not -fstype fuse.sshfs -type d -perm -0002 -uid +0 -exec chown root {} \; + + + + + + + + + + Verify that All World-Writable Directories Have Sticky Bits Set + When the so-called 'sticky bit' is set on a directory, +only the owner of a given file may remove that file from the +directory. Without the sticky bit, any user with write access to a +directory may remove any file in the directory. Setting the sticky +bit prevents users from removing each other's files. In cases where +there is no reason for a directory to be world-writable, a better +solution is to remove that permission rather than to set the sticky +bit. However, if a directory is used by a particular application, +consult that application's documentation instead of blindly +changing modes. + +To set the sticky bit on a world-writable directory DIR, run the +following command: +$ sudo chmod +t DIR + BP28(R40) + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + CCI-001090 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + SRG-OS-000138-GPOS-00069 + Failing to set the sticky bit on public directories allows unauthorized +users to delete files in the directory structure. + +The only authorized public directories are those temporary directories +supplied with the system, or those designed to be temporary file +repositories. The setting is normally reserved for directories used by the +system, by users for temporary file storage (such as /tmp), and +for directories requiring global read/write access. + CCE-83895-3 + - name: Get all world-writable directories with no sticky bits set + shell: | + set -o pipefail + df --local -P | awk '{if (NR!=1) print $6}' | xargs -I '{}' find '{}' -xdev -type d \( -perm -0002 -a ! -perm -1000 \) 2>/dev/null + register: dir_output + tags: + - CCE-83895-3 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - dir_perms_world_writable_sticky_bits + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: ensure sticky bit is set + file: + path: '{{ item }}' + mode: a+t + with_items: + - '{{ dir_output.stdout_lines }}' + tags: + - CCE-83895-3 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - dir_perms_world_writable_sticky_bits + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + df --local -P | awk '{if (NR!=1) print $6}' \ +| xargs -I '{}' find '{}' -xdev -type d \ +\( -perm -0002 -a ! -perm -1000 \) 2>/dev/null \ +| xargs chmod a+t + + + + + + + + + + Verify Permissions on /etc/audit/auditd.conf + +To properly set the permissions of /etc/audit/auditd.conf, run the command: +$ sudo chmod 0640 /etc/audit/auditd.conf + CCI-000171 + AU-12(b) + SRG-OS-000063-GPOS-00032 + Without the capability to restrict the roles and individuals that can select which events +are audited, unauthorized personnel may be able to prevent the auditing of critical +events. Misconfigured audits may degrade the system's performance by overwhelming +the audit log. Misconfigured audits may also make it more difficult to establish, +correlate, and investigate the events relating to an incident or identify +those responsible for one. + CCE-89284-4 + - name: Test for existence /etc/audit/auditd.conf + stat: + path: /etc/audit/auditd.conf + register: file_exists + tags: + - CCE-89284-4 + - NIST-800-53-AU-12(b) + - configure_strategy + - file_permissions_etc_audit_auditd + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure permission u-xs,g-xws,o-xwrt on /etc/audit/auditd.conf + file: + path: /etc/audit/auditd.conf + mode: u-xs,g-xws,o-xwrt + when: file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-89284-4 + - NIST-800-53-AU-12(b) + - configure_strategy + - file_permissions_etc_audit_auditd + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + + + + +chmod u-xs,g-xws,o-xwrt /etc/audit/auditd.conf + + + + + + + + + + Verify Permissions on /etc/audit/rules.d/*.rules + +To properly set the permissions of /etc/audit/rules.d/*.rules, run the command: +$ sudo chmod 0640 /etc/audit/rules.d/*.rules + CCI-000171 + AU-12(b) + SRG-OS-000063-GPOS-00032 + Without the capability to restrict the roles and individuals that can select which events +are audited, unauthorized personnel may be able to prevent the auditing of critical +events. Misconfigured audits may degrade the system's performance by overwhelming +the audit log. Misconfigured audits may also make it more difficult to establish, +correlate, and investigate the events relating to an incident or identify +those responsible for one. + CCE-89977-3 + - name: Find /etc/audit/rules.d/ file(s) + command: find -H /etc/audit/rules.d/ -maxdepth 1 -perm /u+xs,g+xws,o+xwrt -type + f -regex "^.*rules$" + register: files_found + changed_when: false + failed_when: false + check_mode: false + tags: + - CCE-89977-3 + - NIST-800-53-AU-12(b) + - configure_strategy + - file_permissions_etc_audit_rulesd + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Set permissions for /etc/audit/rules.d/ file(s) + file: + path: '{{ item }}' + mode: u-xs,g-xws,o-xwrt + state: file + with_items: + - '{{ files_found.stdout_lines }}' + tags: + - CCE-89977-3 + - NIST-800-53-AU-12(b) + - configure_strategy + - file_permissions_etc_audit_rulesd + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + + + + +find -H /etc/audit/rules.d/ -maxdepth 1 -perm /u+xs,g+xws,o+xwrt -type f -regex '^.*rules$' -exec chmod u-xs,g-xws,o-xwrt {} \; + + + + + + + + + + Verify that local System.map file (if exists) is readable only by root + Files containing sensitive informations should be protected by restrictive + permissions. Most of the time, there is no need that these files need to be read by any non-root user + +To properly set the permissions of /boot/System.map-*, run the command: +$ sudo chmod 0600 /boot/System.map-* + BP28(R13) + The System.map file contains information about kernel symbols and + can give some hints to generate local exploitation. + + + + + + + + + Ensure All SGID Executables Are Authorized + The SGID (set group id) bit should be set only on files that were +installed via authorized means. A straightforward means of identifying +unauthorized SGID files is determine if any were not installed as part of an +RPM package, which is cryptographically verified. Investigate the origin +of any unpackaged SGID files. +This configuration check considers authorized SGID files which were installed via RPM. +It is assumed that when an individual has sudo access to install an RPM +and all packages are signed with an organizationally-recognized GPG key, +the software should be considered an approved package on the system. +Any SGID file not deployed through an RPM will be flagged for further review. + BP28(R37) + BP28(R38) + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + Executable files with the SGID permission run with the privileges of +the owner of the file. SGID files of uncertain provenance could allow for +unprivileged users to elevate privileges. The presence of these files should be +strictly controlled on the system. + CCE-83901-9 + + + + + + + + + Ensure All SUID Executables Are Authorized + The SUID (set user id) bit should be set only on files that were +installed via authorized means. A straightforward means of identifying +unauthorized SUID files is determine if any were not installed as part of an +RPM package, which is cryptographically verified. Investigate the origin +of any unpackaged SUID files. +This configuration check considers authorized SUID files which were installed via RPM. +It is assumed that when an individual has sudo access to install an RPM +and all packages are signed with an organizationally-recognized GPG key, +the software should be considered an approved package on the system. +Any SUID file not deployed through an RPM will be flagged for further review. + BP28(R37) + BP28(R38) + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + Executable files with the SUID permission run with the privileges of +the owner of the file. SUID files of uncertain provenance could allow for +unprivileged users to elevate privileges. The presence of these files should be +strictly controlled on the system. + CCE-83897-9 + + + + + + + + + Ensure No World-Writable Files Exist + It is generally a good idea to remove global (other) write +access to a file when it is discovered. However, check with +documentation for specific applications before making changes. +Also, monitor for recurring world-writable files, as these may be +symptoms of a misconfigured application or user account. Finally, +this applies to real files and not virtual files that are a part of +pseudo file systems such as sysfs or procfs. + BP28(R40) + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + Data in world-writable files can be modified by any +user on the system. In almost all circumstances, files can be +configured using a combination of user and group permissions to +support whatever legitimate access is needed without the risk +caused by world-writable files. + CCE-83902-7 + +find / -xdev -type f -perm -002 -exec chmod o-w {} \; + + + + + + + + + + Ensure All Files Are Owned by a Group + If any files are not owned by a group, then the +cause of their lack of group-ownership should be investigated. +Following this, the files should be deleted or assigned to an +appropriate group. The following command will discover and print +any files on local partitions which do not belong to a valid group: +$ df --local -P | awk '{if (NR!=1) print $6}' | sudo xargs -I '{}' find '{}' -xdev -nogroup +To search all filesystems on a system including network mounted +filesystems the following command can be run manually for each partition: +$ sudo find PARTITION -xdev -nogroup + This rule only considers local groups. +If you have your groups defined outside /etc/group, the rule won't consider those. + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.02 + DSS06.03 + DSS06.06 + DSS06.10 + CCI-000366 + CCI-002165 + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.18.1.4 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.1 + A.9.4.2 + A.9.4.3 + A.9.4.4 + A.9.4.5 + CM-6(a) + AC-6(1) + PR.AC-1 + PR.AC-4 + PR.AC-6 + PR.AC-7 + PR.DS-5 + PR.PT-3 + SRG-OS-000480-GPOS-00227 + Unowned files do not directly imply a security problem, but they are generally +a sign that something is amiss. They may +be caused by an intruder, by incorrect software installation or +draft software removal, or by failure to remove all files belonging +to a deleted account. The files should be repaired so they +will not cause problems when accounts are created in the future, +and the cause should be discovered and addressed. + CCE-83906-8 + + + + + + + + + Ensure All Files Are Owned by a User + If any files are not owned by a user, then the +cause of their lack of ownership should be investigated. +Following this, the files should be deleted or assigned to an +appropriate user. The following command will discover and print +any files on local partitions which do not belong to a valid user: +$ df --local -P | awk {'if (NR!=1) print $6'} | sudo xargs -I '{}' find '{}' -xdev -nouser +To search all filesystems on a system including network mounted +filesystems the following command can be run manually for each partition: +$ sudo find PARTITION -xdev -nouser + For this rule to evaluate centralized user accounts, getent must be working properly +so that running the command getent passwd returns a list of all users in your organization. +If using the System Security Services Daemon (SSSD), enumerate = true must be configured +in your organization's domain to return a complete list of users + Enabling this rule will result in slower scan times depending on the size of your organization +and number of centralized users. + 11 + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + 9 + APO01.06 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + DSS06.02 + DSS06.03 + DSS06.06 + CCI-000366 + CCI-002165 + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 5.2 + SR 7.6 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.1 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.AC-6 + PR.DS-5 + PR.IP-1 + PR.PT-3 + SRG-OS-000480-GPOS-00227 + Unowned files do not directly imply a security problem, but they are generally +a sign that something is amiss. They may +be caused by an intruder, by incorrect software installation or +draft software removal, or by failure to remove all files belonging +to a deleted account. The files should be repaired so they +will not cause problems when accounts are created in the future, +and the cause should be discovered and addressed. + + CCE-83896-1 + + + + + + + + + Enable Kernel Parameter to Enforce DAC on FIFOs + To set the runtime status of the fs.protected_fifos kernel parameter, run the following command: $ sudo sysctl -w fs.protected_fifos=2 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: fs.protected_fifos = 2 + CM-6(a) + AC-6(1) + This parameter is available since Linux Kernel 4.19 and allows to prohibit opening +FIFOs that are not owned by the user in world and group writeable sticky directories. +It avoids unintentional writes to an attacker-controlled FIFO where a program expects +to create the regular file. + + CCE-85884-5 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*fs.protected_fifos.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-85884-5 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_fs_protected_fifos + +- name: Comment out any occurrences of fs.protected_fifos from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*fs.protected_fifos + replace: '#fs.protected_fifos' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-85884-5 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_fs_protected_fifos + +- name: Ensure sysctl fs.protected_fifos is set to 2 + sysctl: + name: fs.protected_fifos + value: '2' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-85884-5 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_fs_protected_fifos + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of fs.protected_fifos from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*fs.protected_fifos.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "fs.protected_fifos" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done + +# +# Set runtime for fs.protected_fifos +# +/sbin/sysctl -q -n -w fs.protected_fifos="2" + +# +# If fs.protected_fifos present in /etc/sysctl.conf, change value to "2" +# else, add "fs.protected_fifos = 2" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^fs.protected_fifos") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "2" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^fs.protected_fifos\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^fs.protected_fifos\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-85884-5" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Enable Kernel Parameter to Enforce DAC on Hardlinks + To set the runtime status of the fs.protected_hardlinks kernel parameter, run the following command: $ sudo sysctl -w fs.protected_hardlinks=1 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: fs.protected_hardlinks = 1 + BP28(R23) + CCI-002165 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-6(a) + AC-6(1) + SRG-OS-000312-GPOS-00122 + SRG-OS-000312-GPOS-00123 + SRG-OS-000324-GPOS-00125 + By enabling this kernel parameter, users can no longer create soft or hard links to +files which they do not own. Disallowing such hardlinks mitigate vulnerabilities +based on insecure file system accessed by privileged programs, avoiding an +exploitation vector exploiting unsafe use of open() or creat(). + + CCE-84110-6 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*fs.protected_hardlinks.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84110-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_fs_protected_hardlinks + +- name: Comment out any occurrences of fs.protected_hardlinks from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*fs.protected_hardlinks + replace: '#fs.protected_hardlinks' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84110-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_fs_protected_hardlinks + +- name: Ensure sysctl fs.protected_hardlinks is set to 1 + sysctl: + name: fs.protected_hardlinks + value: '1' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84110-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_fs_protected_hardlinks + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,fs.protected_hardlinks%3D1%0A + mode: 0644 + path: /etc/sysctl.d/75-sysctl_fs_protected_hardlinks.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of fs.protected_hardlinks from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*fs.protected_hardlinks.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "fs.protected_hardlinks" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done + +# +# Set runtime for fs.protected_hardlinks +# +/sbin/sysctl -q -n -w fs.protected_hardlinks="1" + +# +# If fs.protected_hardlinks present in /etc/sysctl.conf, change value to "1" +# else, add "fs.protected_hardlinks = 1" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^fs.protected_hardlinks") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "1" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^fs.protected_hardlinks\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^fs.protected_hardlinks\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-84110-6" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Enable Kernel Parameter to Enforce DAC on Regular files + To set the runtime status of the fs.protected_regular kernel parameter, run the following command: $ sudo sysctl -w fs.protected_regular=2 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: fs.protected_regular = 2 + CM-6(a) + AC-6(1) + This parameter is available since Linux Kernel 4.19 and allows to prohibit opening +"regular" files that are not owned by the user in world and group writeable sticky +directories. It avoids writes to an attacker-controlled regular file, for example, +when a program expects to create the regular file. + + CCE-85885-2 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*fs.protected_regular.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-85885-2 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_fs_protected_regular + +- name: Comment out any occurrences of fs.protected_regular from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*fs.protected_regular + replace: '#fs.protected_regular' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-85885-2 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_fs_protected_regular + +- name: Ensure sysctl fs.protected_regular is set to 2 + sysctl: + name: fs.protected_regular + value: '2' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-85885-2 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_fs_protected_regular + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of fs.protected_regular from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*fs.protected_regular.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "fs.protected_regular" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done + +# +# Set runtime for fs.protected_regular +# +/sbin/sysctl -q -n -w fs.protected_regular="2" + +# +# If fs.protected_regular present in /etc/sysctl.conf, change value to "2" +# else, add "fs.protected_regular = 2" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^fs.protected_regular") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "2" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^fs.protected_regular\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^fs.protected_regular\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-85885-2" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Enable Kernel Parameter to Enforce DAC on Symlinks + To set the runtime status of the fs.protected_symlinks kernel parameter, run the following command: $ sudo sysctl -w fs.protected_symlinks=1 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: fs.protected_symlinks = 1 + BP28(R23) + CCI-002165 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-6(a) + AC-6(1) + SRG-OS-000312-GPOS-00122 + SRG-OS-000312-GPOS-00123 + SRG-OS-000324-GPOS-00125 + By enabling this kernel parameter, symbolic links are permitted to be followed +only when outside a sticky world-writable directory, or when the UID of the +link and follower match, or when the directory owner matches the symlink's owner. +Disallowing such symlinks helps mitigate vulnerabilities based on insecure file system +accessed by privileged programs, avoiding an exploitation vector exploiting unsafe use of +open() or creat(). + + CCE-83900-1 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*fs.protected_symlinks.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83900-1 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_fs_protected_symlinks + +- name: Comment out any occurrences of fs.protected_symlinks from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*fs.protected_symlinks + replace: '#fs.protected_symlinks' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83900-1 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_fs_protected_symlinks + +- name: Ensure sysctl fs.protected_symlinks is set to 1 + sysctl: + name: fs.protected_symlinks + value: '1' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83900-1 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_fs_protected_symlinks + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,fs.protected_symlinks%3D1%0A + mode: 0644 + path: /etc/sysctl.d/75-sysctl_fs_protected_symlinks.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of fs.protected_symlinks from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*fs.protected_symlinks.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "fs.protected_symlinks" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done + +# +# Set runtime for fs.protected_symlinks +# +/sbin/sysctl -q -n -w fs.protected_symlinks="1" + +# +# If fs.protected_symlinks present in /etc/sysctl.conf, change value to "1" +# else, add "fs.protected_symlinks = 1" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^fs.protected_symlinks") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "1" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^fs.protected_symlinks\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^fs.protected_symlinks\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-83900-1" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Verify Permissions on Files with Local Account Information and Credentials + The default restrictive permissions for files which act as +important security databases such as passwd, shadow, +group, and gshadow files must be maintained. Many utilities +need read access to the passwd file in order to function properly, but +read access to the shadow file allows malicious attacks against system +passwords, and should never be enabled. + + Verify Group Who Owns Backup group File + To properly set the group owner of /etc/group-, run the command: $ sudo chgrp root /etc/group- + CCI-002223 + AC-6 (1) + SRG-OS-000480-GPOS-00227 + The /etc/group- file is a backup file of /etc/group, and as such, +it contains information regarding groups that are configured on the system. +Protection of this file is important for system security. + CCE-83928-2 + - name: Test for existence /etc/group- + stat: + path: /etc/group- + register: file_exists + tags: + - CCE-83928-2 + - NIST-800-53-AC-6 (1) + - configure_strategy + - file_groupowner_backup_etc_group + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure group owner 0 on /etc/group- + file: + path: /etc/group- + group: '0' + when: file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-83928-2 + - NIST-800-53-AC-6 (1) + - configure_strategy + - file_groupowner_backup_etc_group + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + + + +chgrp 0 /etc/group- + + + + + + + + + + Verify Group Who Owns Backup gshadow File + To properly set the group owner of /etc/gshadow-, run the command: $ sudo chgrp root /etc/gshadow- + CCI-002223 + AC-6 (1) + SRG-OS-000480-GPOS-00227 + The /etc/gshadow- file is a backup of /etc/gshadow, and as such, +it contains group password hashes. Protection of this file is critical for system security. + CCE-83951-4 + - name: Test for existence /etc/gshadow- + stat: + path: /etc/gshadow- + register: file_exists + tags: + - CCE-83951-4 + - NIST-800-53-AC-6 (1) + - configure_strategy + - file_groupowner_backup_etc_gshadow + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure group owner 0 on /etc/gshadow- + file: + path: /etc/gshadow- + group: '0' + when: file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-83951-4 + - NIST-800-53-AC-6 (1) + - configure_strategy + - file_groupowner_backup_etc_gshadow + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + + + +chgrp 0 /etc/gshadow- + + + + + + + + + + Verify Group Who Owns Backup passwd File + To properly set the group owner of /etc/passwd-, run the command: $ sudo chgrp root /etc/passwd- + CCI-002223 + AC-6 (1) + SRG-OS-000480-GPOS-00227 + The /etc/passwd- file is a backup file of /etc/passwd, and as such, +it contains information about the users that are configured on the system. +Protection of this file is critical for system security. + CCE-83933-2 + - name: Test for existence /etc/passwd- + stat: + path: /etc/passwd- + register: file_exists + tags: + - CCE-83933-2 + - NIST-800-53-AC-6 (1) + - configure_strategy + - file_groupowner_backup_etc_passwd + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure group owner 0 on /etc/passwd- + file: + path: /etc/passwd- + group: '0' + when: file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-83933-2 + - NIST-800-53-AC-6 (1) + - configure_strategy + - file_groupowner_backup_etc_passwd + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + + + +chgrp 0 /etc/passwd- + + + + + + + + + + Verify User Who Owns Backup shadow File + To properly set the group owner of /etc/shadow-, run the command: $ sudo chgrp root /etc/shadow- + SRG-OS-000480-GPOS-00227 + The /etc/shadow- file is a backup file of /etc/shadow, and as such, +it contains the list of local system accounts and password hashes. +Protection of this file is critical for system security. + CCE-83938-1 + - name: Test for existence /etc/shadow- + stat: + path: /etc/shadow- + register: file_exists + tags: + - CCE-83938-1 + - configure_strategy + - file_groupowner_backup_etc_shadow + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure group owner 0 on /etc/shadow- + file: + path: /etc/shadow- + group: '0' + when: file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-83938-1 + - configure_strategy + - file_groupowner_backup_etc_shadow + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + + + +chgrp 0 /etc/shadow- + + + + + + + + + + Verify Group Who Owns group File + To properly set the group owner of /etc/group, run the command: $ sudo chgrp root /etc/group + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + 5.5.2.2 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + Req-8.7.c + SRG-OS-000480-GPOS-00227 + The /etc/group file contains information regarding groups that are configured +on the system. Protection of this file is important for system security. + CCE-83945-6 + - name: Test for existence /etc/group + stat: + path: /etc/group + register: file_exists + tags: + - CCE-83945-6 + - CJIS-5.5.2.2 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.7.c + - configure_strategy + - file_groupowner_etc_group + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure group owner 0 on /etc/group + file: + path: /etc/group + group: '0' + when: file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-83945-6 + - CJIS-5.5.2.2 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.7.c + - configure_strategy + - file_groupowner_etc_group + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + + + +chgrp 0 /etc/group + + + + + + + + + + Verify Group Who Owns gshadow File + To properly set the group owner of /etc/gshadow, run the command: $ sudo chgrp root /etc/gshadow + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + SRG-OS-000480-GPOS-00227 + The /etc/gshadow file contains group password hashes. Protection of this file +is critical for system security. + CCE-83948-0 + - name: Test for existence /etc/gshadow + stat: + path: /etc/gshadow + register: file_exists + tags: + - CCE-83948-0 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_groupowner_etc_gshadow + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure group owner 0 on /etc/gshadow + file: + path: /etc/gshadow + group: '0' + when: file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-83948-0 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_groupowner_etc_gshadow + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + + + +chgrp 0 /etc/gshadow + + + + + + + + + + Verify Group Who Owns passwd File + To properly set the group owner of /etc/passwd, run the command: $ sudo chgrp root /etc/passwd + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + 5.5.2.2 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + Req-8.7.c + SRG-OS-000480-GPOS-00227 + The /etc/passwd file contains information about the users that are configured on +the system. Protection of this file is critical for system security. + CCE-83950-6 + - name: Test for existence /etc/passwd + stat: + path: /etc/passwd + register: file_exists + tags: + - CCE-83950-6 + - CJIS-5.5.2.2 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.7.c + - configure_strategy + - file_groupowner_etc_passwd + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure group owner 0 on /etc/passwd + file: + path: /etc/passwd + group: '0' + when: file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-83950-6 + - CJIS-5.5.2.2 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.7.c + - configure_strategy + - file_groupowner_etc_passwd + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + + + +chgrp 0 /etc/passwd + + + + + + + + + + Verify Group Who Owns shadow File + To properly set the group owner of /etc/shadow, run the command: $ sudo chgrp root /etc/shadow + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + 5.5.2.2 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + Req-8.7.c + SRG-OS-000480-GPOS-00227 + The /etc/shadow file stores password hashes. Protection of this file is +critical for system security. + CCE-83930-8 + - name: Test for existence /etc/shadow + stat: + path: /etc/shadow + register: file_exists + tags: + - CCE-83930-8 + - CJIS-5.5.2.2 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.7.c + - configure_strategy + - file_groupowner_etc_shadow + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure group owner 0 on /etc/shadow + file: + path: /etc/shadow + group: '0' + when: file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-83930-8 + - CJIS-5.5.2.2 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.7.c + - configure_strategy + - file_groupowner_etc_shadow + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + + + +chgrp 0 /etc/shadow + + + + + + + + + + Verify User Who Owns Backup group File + To properly set the owner of /etc/group-, run the command: $ sudo chown root /etc/group- + CCI-002223 + AC-6 (1) + SRG-OS-000480-GPOS-00227 + The /etc/group- file is a backup file of /etc/group, and as such, +it contains information regarding groups that are configured on the system. +Protection of this file is important for system security. + CCE-83944-9 + - name: Test for existence /etc/group- + stat: + path: /etc/group- + register: file_exists + tags: + - CCE-83944-9 + - NIST-800-53-AC-6 (1) + - configure_strategy + - file_owner_backup_etc_group + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure owner 0 on /etc/group- + file: + path: /etc/group- + owner: '0' + when: file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-83944-9 + - NIST-800-53-AC-6 (1) + - configure_strategy + - file_owner_backup_etc_group + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + + + +chown 0 /etc/group- + + + + + + + + + + Verify User Who Owns Backup gshadow File + To properly set the owner of /etc/gshadow-, run the command: $ sudo chown root /etc/gshadow- + CCI-002223 + AC-6 (1) + SRG-OS-000480-GPOS-00227 + The /etc/gshadow- file is a backup of /etc/gshadow, and as such, +it contains group password hashes. Protection of this file is critical for system security. + CCE-83929-0 + - name: Test for existence /etc/gshadow- + stat: + path: /etc/gshadow- + register: file_exists + tags: + - CCE-83929-0 + - NIST-800-53-AC-6 (1) + - configure_strategy + - file_owner_backup_etc_gshadow + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure owner 0 on /etc/gshadow- + file: + path: /etc/gshadow- + owner: '0' + when: file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-83929-0 + - NIST-800-53-AC-6 (1) + - configure_strategy + - file_owner_backup_etc_gshadow + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + + + +chown 0 /etc/gshadow- + + + + + + + + + + Verify User Who Owns Backup passwd File + To properly set the owner of /etc/passwd-, run the command: $ sudo chown root /etc/passwd- + CCI-002223 + AC-6 (1) + SRG-OS-000480-GPOS-00227 + The /etc/passwd- file is a backup file of /etc/passwd, and as such, +it contains information about the users that are configured on the system. +Protection of this file is critical for system security. + CCE-83947-2 + - name: Test for existence /etc/passwd- + stat: + path: /etc/passwd- + register: file_exists + tags: + - CCE-83947-2 + - NIST-800-53-AC-6 (1) + - configure_strategy + - file_owner_backup_etc_passwd + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure owner 0 on /etc/passwd- + file: + path: /etc/passwd- + owner: '0' + when: file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-83947-2 + - NIST-800-53-AC-6 (1) + - configure_strategy + - file_owner_backup_etc_passwd + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + + + +chown 0 /etc/passwd- + + + + + + + + + + Verify Group Who Owns Backup shadow File + To properly set the owner of /etc/shadow-, run the command: $ sudo chown root /etc/shadow- + CCI-002223 + AC-6 (1) + SRG-OS-000480-GPOS-00227 + The /etc/shadow- file is a backup file of /etc/shadow, and as such, +it contains the list of local system accounts and password hashes. +Protection of this file is critical for system security. + CCE-83949-8 + - name: Test for existence /etc/shadow- + stat: + path: /etc/shadow- + register: file_exists + tags: + - CCE-83949-8 + - NIST-800-53-AC-6 (1) + - configure_strategy + - file_owner_backup_etc_shadow + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure owner 0 on /etc/shadow- + file: + path: /etc/shadow- + owner: '0' + when: file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-83949-8 + - NIST-800-53-AC-6 (1) + - configure_strategy + - file_owner_backup_etc_shadow + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + + + +chown 0 /etc/shadow- + + + + + + + + + + Verify User Who Owns group File + To properly set the owner of /etc/group, run the command: $ sudo chown root /etc/group + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + 5.5.2.2 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + CCI-002223 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + Req-8.7.c + SRG-OS-000480-GPOS-00227 + The /etc/group file contains information regarding groups that are configured +on the system. Protection of this file is important for system security. + CCE-83925-8 + - name: Test for existence /etc/group + stat: + path: /etc/group + register: file_exists + tags: + - CCE-83925-8 + - CJIS-5.5.2.2 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.7.c + - configure_strategy + - file_owner_etc_group + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure owner 0 on /etc/group + file: + path: /etc/group + owner: '0' + when: file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-83925-8 + - CJIS-5.5.2.2 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.7.c + - configure_strategy + - file_owner_etc_group + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + + + +chown 0 /etc/group + + + + + + + + + + Verify User Who Owns gshadow File + To properly set the owner of /etc/gshadow, run the command: $ sudo chown root /etc/gshadow + BP28(R36) + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + CCI-002223 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + SRG-OS-000480-GPOS-00227 + The /etc/gshadow file contains group password hashes. Protection of this file +is critical for system security. + CCE-83924-1 + - name: Test for existence /etc/gshadow + stat: + path: /etc/gshadow + register: file_exists + tags: + - CCE-83924-1 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_owner_etc_gshadow + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure owner 0 on /etc/gshadow + file: + path: /etc/gshadow + owner: '0' + when: file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-83924-1 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_owner_etc_gshadow + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + + + +chown 0 /etc/gshadow + + + + + + + + + + Verify User Who Owns passwd File + To properly set the owner of /etc/passwd, run the command: $ sudo chown root /etc/passwd + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + 5.5.2.2 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + CCI-002223 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + Req-8.7.c + SRG-OS-000480-GPOS-00227 + The /etc/passwd file contains information about the users that are configured on +the system. Protection of this file is critical for system security. + CCE-83943-1 + - name: Test for existence /etc/passwd + stat: + path: /etc/passwd + register: file_exists + tags: + - CCE-83943-1 + - CJIS-5.5.2.2 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.7.c + - configure_strategy + - file_owner_etc_passwd + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure owner 0 on /etc/passwd + file: + path: /etc/passwd + owner: '0' + when: file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-83943-1 + - CJIS-5.5.2.2 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.7.c + - configure_strategy + - file_owner_etc_passwd + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + + + +chown 0 /etc/passwd + + + + + + + + + + Verify User Who Owns shadow File + To properly set the owner of /etc/shadow, run the command: $ sudo chown root /etc/shadow + BP28(R36) + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + 5.5.2.2 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + CCI-002223 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + Req-8.7.c + SRG-OS-000480-GPOS-00227 + The /etc/shadow file contains the list of local +system accounts and stores password hashes. Protection of this file is +critical for system security. Failure to give ownership of this file +to root provides the designated owner with access to sensitive information +which could weaken the system security posture. + CCE-83926-6 + - name: Test for existence /etc/shadow + stat: + path: /etc/shadow + register: file_exists + tags: + - CCE-83926-6 + - CJIS-5.5.2.2 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.7.c + - configure_strategy + - file_owner_etc_shadow + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure owner 0 on /etc/shadow + file: + path: /etc/shadow + owner: '0' + when: file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-83926-6 + - CJIS-5.5.2.2 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.7.c + - configure_strategy + - file_owner_etc_shadow + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + + + +chown 0 /etc/shadow + + + + + + + + + + Verify Permissions on Backup group File + +To properly set the permissions of /etc/group-, run the command: +$ sudo chmod 0644 /etc/group- + CCI-002223 + AC-6 (1) + SRG-OS-000480-GPOS-00227 + The /etc/group- file is a backup file of /etc/group, and as such, +it contains information regarding groups that are configured on the system. +Protection of this file is important for system security. + CCE-83939-9 + - name: Test for existence /etc/group- + stat: + path: /etc/group- + register: file_exists + tags: + - CCE-83939-9 + - NIST-800-53-AC-6 (1) + - configure_strategy + - file_permissions_backup_etc_group + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure permission u-xs,g-xws,o-xwt on /etc/group- + file: + path: /etc/group- + mode: u-xs,g-xws,o-xwt + when: file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-83939-9 + - NIST-800-53-AC-6 (1) + - configure_strategy + - file_permissions_backup_etc_group + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + + + + +chmod u-xs,g-xws,o-xwt /etc/group- + + + + + + + + + + Verify Permissions on Backup gshadow File + +To properly set the permissions of /etc/gshadow-, run the command: +$ sudo chmod 0000 /etc/gshadow- + CCI-002223 + AC-6 (1) + SRG-OS-000480-GPOS-00227 + The /etc/gshadow- file is a backup of /etc/gshadow, and as such, +it contains group password hashes. Protection of this file is critical for system security. + CCE-83942-3 + - name: Test for existence /etc/gshadow- + stat: + path: /etc/gshadow- + register: file_exists + tags: + - CCE-83942-3 + - NIST-800-53-AC-6 (1) + - configure_strategy + - file_permissions_backup_etc_gshadow + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure permission u-xwrs,g-xwrs,o-xwrt on /etc/gshadow- + file: + path: /etc/gshadow- + mode: u-xwrs,g-xwrs,o-xwrt + when: file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-83942-3 + - NIST-800-53-AC-6 (1) + - configure_strategy + - file_permissions_backup_etc_gshadow + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + + + + +chmod u-xwrs,g-xwrs,o-xwrt /etc/gshadow- + + + + + + + + + + Verify Permissions on Backup passwd File + +To properly set the permissions of /etc/passwd-, run the command: +$ sudo chmod 0644 /etc/passwd- + CCI-002223 + AC-6 (1) + SRG-OS-000480-GPOS-00227 + The /etc/passwd- file is a backup file of /etc/passwd, and as such, +it contains information about the users that are configured on the system. +Protection of this file is critical for system security. + CCE-83940-7 + - name: Test for existence /etc/passwd- + stat: + path: /etc/passwd- + register: file_exists + tags: + - CCE-83940-7 + - NIST-800-53-AC-6 (1) + - configure_strategy + - file_permissions_backup_etc_passwd + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure permission u-xs,g-xws,o-xwt on /etc/passwd- + file: + path: /etc/passwd- + mode: u-xs,g-xws,o-xwt + when: file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-83940-7 + - NIST-800-53-AC-6 (1) + - configure_strategy + - file_permissions_backup_etc_passwd + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + + + + +chmod u-xs,g-xws,o-xwt /etc/passwd- + + + + + + + + + + Verify Permissions on Backup shadow File + +To properly set the permissions of /etc/shadow-, run the command: +$ sudo chmod 0000 /etc/shadow- + CCI-002223 + AC-6 (1) + SRG-OS-000480-GPOS-00227 + The /etc/shadow- file is a backup file of /etc/shadow, and as such, +it contains the list of local system accounts and password hashes. +Protection of this file is critical for system security. + CCE-83935-7 + - name: Test for existence /etc/shadow- + stat: + path: /etc/shadow- + register: file_exists + tags: + - CCE-83935-7 + - NIST-800-53-AC-6 (1) + - configure_strategy + - file_permissions_backup_etc_shadow + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure permission u-xwrs,g-xwrs,o-xwrt on /etc/shadow- + file: + path: /etc/shadow- + mode: u-xwrs,g-xwrs,o-xwrt + when: file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-83935-7 + - NIST-800-53-AC-6 (1) + - configure_strategy + - file_permissions_backup_etc_shadow + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + + + + +chmod u-xwrs,g-xwrs,o-xwrt /etc/shadow- + + + + + + + + + + Verify Permissions on group File + +To properly set the permissions of /etc/passwd, run the command: +$ sudo chmod 0644 /etc/passwd + BP28(R36) + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + 5.5.2.2 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + CCI-002223 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + Req-8.7.c + SRG-OS-000480-GPOS-00227 + The /etc/group file contains information regarding groups that are configured +on the system. Protection of this file is important for system security. + CCE-83934-0 + - name: Test for existence /etc/group + stat: + path: /etc/group + register: file_exists + tags: + - CCE-83934-0 + - CJIS-5.5.2.2 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.7.c + - configure_strategy + - file_permissions_etc_group + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure permission u-xs,g-xws,o-xwt on /etc/group + file: + path: /etc/group + mode: u-xs,g-xws,o-xwt + when: file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-83934-0 + - CJIS-5.5.2.2 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.7.c + - configure_strategy + - file_permissions_etc_group + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + + + + +chmod u-xs,g-xws,o-xwt /etc/group + + + + + + + + + + Verify Permissions on gshadow File + +To properly set the permissions of /etc/gshadow, run the command: +$ sudo chmod 0000 /etc/gshadow + BP28(R36) + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + CCI-002223 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + SRG-OS-000480-GPOS-00227 + The /etc/gshadow file contains group password hashes. Protection of this file +is critical for system security. + CCE-83921-7 + - name: Test for existence /etc/gshadow + stat: + path: /etc/gshadow + register: file_exists + tags: + - CCE-83921-7 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_permissions_etc_gshadow + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure permission u-xwrs,g-xwrs,o-xwrt on /etc/gshadow + file: + path: /etc/gshadow + mode: u-xwrs,g-xwrs,o-xwrt + when: file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-83921-7 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_permissions_etc_gshadow + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + + + + +chmod u-xwrs,g-xwrs,o-xwrt /etc/gshadow + + + + + + + + + + Verify Permissions on passwd File + +To properly set the permissions of /etc/passwd, run the command: +$ sudo chmod 0644 /etc/passwd + BP28(R36) + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + 5.5.2.2 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + CCI-002223 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + Req-8.7.c + SRG-OS-000480-GPOS-00227 + If the /etc/passwd file is writable by a group-owner or the +world the risk of its compromise is increased. The file contains the list of +accounts on the system and associated information, and protection of this file +is critical for system security. + CCE-83931-6 + - name: Test for existence /etc/passwd + stat: + path: /etc/passwd + register: file_exists + tags: + - CCE-83931-6 + - CJIS-5.5.2.2 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.7.c + - configure_strategy + - file_permissions_etc_passwd + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure permission u-xs,g-xws,o-xwt on /etc/passwd + file: + path: /etc/passwd + mode: u-xs,g-xws,o-xwt + when: file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-83931-6 + - CJIS-5.5.2.2 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.7.c + - configure_strategy + - file_permissions_etc_passwd + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + + + + +chmod u-xs,g-xws,o-xwt /etc/passwd + + + + + + + + + + Verify Permissions on shadow File + +To properly set the permissions of /etc/shadow, run the command: +$ sudo chmod 0000 /etc/shadow + BP28(R36) + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + 5.5.2.2 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + CCI-002223 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + Req-8.7.c + SRG-OS-000480-GPOS-00227 + The /etc/shadow file contains the list of local +system accounts and stores password hashes. Protection of this file is +critical for system security. Failure to give ownership of this file +to root provides the designated owner with access to sensitive information +which could weaken the system security posture. + CCE-83941-5 + - name: Test for existence /etc/shadow + stat: + path: /etc/shadow + register: file_exists + tags: + - CCE-83941-5 + - CJIS-5.5.2.2 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.7.c + - configure_strategy + - file_permissions_etc_shadow + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure permission u-xwrs,g-xwrs,o-xwrt on /etc/shadow + file: + path: /etc/shadow + mode: u-xwrs,g-xwrs,o-xwrt + when: file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-83941-5 + - CJIS-5.5.2.2 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-8.7.c + - configure_strategy + - file_permissions_etc_shadow + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + + + + +chmod u-xwrs,g-xwrs,o-xwrt /etc/shadow + + + + + + + + + + + Verify Permissions on Files within /var/log Directory + The /var/log directory contains files with logs of error +messages in the system and should only be accessed by authorized +personnel. + + Verify Group Who Owns /var/log Directory + To properly set the group owner of /var/log, run the command: $ sudo chgrp root /var/log + CCI-001314 + SRG-OS-000206-GPOS-00084 + The /var/log directory contains files with logs of error +messages in the system and should only be accessed by authorized +personnel. + CCE-83912-6 + - name: Ensure group owner on /var/log/ + file: + path: /var/log/ + state: directory + group: '0' + tags: + - CCE-83912-6 + - configure_strategy + - file_groupowner_var_log + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + + + +find -H /var/log/ -maxdepth 1 -type d -exec chgrp 0 {} \; + + + + + + + + + + Verify Group Who Owns /var/log/messages File + To properly set the group owner of /var/log/messages, run the command: $ sudo chgrp root /var/log/messages + CCI-001314 + SRG-OS-000206-GPOS-00084 + The /var/log/messages file contains logs of error messages in +the system and should only be accessed by authorized personnel. + CCE-83916-7 + - name: Test for existence /var/log/messages + stat: + path: /var/log/messages + register: file_exists + tags: + - CCE-83916-7 + - configure_strategy + - file_groupowner_var_log_messages + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure group owner 0 on /var/log/messages + file: + path: /var/log/messages + group: '0' + when: file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-83916-7 + - configure_strategy + - file_groupowner_var_log_messages + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + + + +chgrp 0 /var/log/messages + + + + + + + + + + Verify Group Who Owns /var/log/syslog File + To properly set the group owner of /var/log/syslog, run the command: $ sudo chgrp adm /var/log/syslog + CCI-001314 + SRG-OS-000206-GPOS-00084 + The /var/log/syslog file contains logs of error messages in +the system and should only be accessed by authorized personnel. + - name: Test for existence /var/log/syslog + stat: + path: /var/log/syslog + register: file_exists + tags: + - configure_strategy + - file_groupowner_var_log_syslog + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure group owner 4 on /var/log/syslog + file: + path: /var/log/syslog + group: '4' + when: file_exists.stat is defined and file_exists.stat.exists + tags: + - configure_strategy + - file_groupowner_var_log_syslog + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + + + +chgrp 4 /var/log/syslog + + + + + + + + + + Verify User Who Owns /var/log Directory + To properly set the owner of /var/log, run the command: $ sudo chown root /var/log + CCI-001314 + SRG-OS-000206-GPOS-00084 + The /var/log directory contains files with logs of error +messages in the system and should only be accessed by authorized +personnel. + CCE-83914-2 + - name: Ensure owner on directory /var/log/ + file: + path: /var/log/ + state: directory + owner: '0' + tags: + - CCE-83914-2 + - configure_strategy + - file_owner_var_log + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + + + +find -H /var/log/ -maxdepth 1 -type d -exec chown 0 {} \; + + + + + + + + + + Verify User Who Owns /var/log/messages File + To properly set the owner of /var/log/messages, run the command: $ sudo chown root /var/log/messages + CCI-001314 + SRG-OS-000206-GPOS-00084 + The /var/log/messages file contains logs of error messages in +the system and should only be accessed by authorized personnel. + CCE-83915-9 + - name: Test for existence /var/log/messages + stat: + path: /var/log/messages + register: file_exists + tags: + - CCE-83915-9 + - configure_strategy + - file_owner_var_log_messages + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure owner 0 on /var/log/messages + file: + path: /var/log/messages + owner: '0' + when: file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-83915-9 + - configure_strategy + - file_owner_var_log_messages + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + + + +chown 0 /var/log/messages + + + + + + + + + + Verify User Who Owns /var/log/syslog File + To properly set the owner of /var/log/syslog, run the command: $ sudo chown syslog /var/log/syslog + CCI-001314 + SRG-OS-000206-GPOS-00084 + The /var/log/syslog file contains logs of error messages in +the system and should only be accessed by authorized personnel. + - name: Test for existence /var/log/syslog + stat: + path: /var/log/syslog + register: file_exists + tags: + - configure_strategy + - file_owner_var_log_syslog + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure owner 104 on /var/log/syslog + file: + path: /var/log/syslog + owner: '104' + when: file_exists.stat is defined and file_exists.stat.exists + tags: + - configure_strategy + - file_owner_var_log_syslog + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + + + +chown 104 /var/log/syslog + + + + + + + + + + Verify Permissions on /var/log Directory + +To properly set the permissions of /var/log, run the command: +$ sudo chmod 0755 /var/log + CCI-001314 + SRG-OS-000206-GPOS-00084 + The /var/log directory contains files with logs of error +messages in the system and should only be accessed by authorized +personnel. + CCE-83917-5 + - name: Set permissions for /var/log/ + file: + path: /var/log/ + state: directory + mode: u-s,g-ws,o-wt + tags: + - CCE-83917-5 + - configure_strategy + - file_permissions_var_log + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + + + + +find -H /var/log/ -maxdepth 1 -perm /u+s,g+ws,o+wt -type d -exec chmod u-s,g-ws,o-wt {} \; + + + + + + + + + + Verify Permissions on /var/log/messages File + +To properly set the permissions of /var/log/messages, run the command: +$ sudo chmod 0640 /var/log/messages + CCI-001314 + SRG-OS-000206-GPOS-00084 + The /var/log/messages file contains logs of error messages in +the system and should only be accessed by authorized personnel. + CCE-83913-4 + - name: Test for existence /var/log/messages + stat: + path: /var/log/messages + register: file_exists + tags: + - CCE-83913-4 + - configure_strategy + - file_permissions_var_log_messages + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure permission u-xs,g-xws,o-xwrt on /var/log/messages + file: + path: /var/log/messages + mode: u-xs,g-xws,o-xwrt + when: file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-83913-4 + - configure_strategy + - file_permissions_var_log_messages + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + + + + +chmod u-xs,g-xws,o-xwrt /var/log/messages + + + + + + + + + + Verify Permissions on /var/log/syslog File + +To properly set the permissions of /var/log/syslog, run the command: +$ sudo chmod 0640 /var/log/syslog + CCI-001314 + SRG-OS-000206-GPOS-00084 + The /var/log/syslog file contains logs of error messages in +the system and should only be accessed by authorized personnel. + - name: Test for existence /var/log/syslog + stat: + path: /var/log/syslog + register: file_exists + tags: + - configure_strategy + - file_permissions_var_log_syslog + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure permission u-xs,g-xws,o-xwrt on /var/log/syslog + file: + path: /var/log/syslog + mode: u-xs,g-xws,o-xwrt + when: file_exists.stat is defined and file_exists.stat.exists + tags: + - configure_strategy + - file_permissions_var_log_syslog + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + + + + +chmod u-xs,g-xws,o-xwrt /var/log/syslog + + + + + + + + + + + Verify File Permissions Within Some Important Directories + Some directories contain files whose confidentiality or integrity +is notably important and may also be susceptible to misconfiguration over time, particularly if +unpackaged software is installed. As such, +an argument exists to verify that files' permissions within these directories remain +configured correctly and restrictively. + + Verify that Shared Library Directories Have Root Group Ownership + System-wide shared library files, which are linked to executables +during process load time or run time, are stored in the following directories +by default: +/lib +/lib64 +/usr/lib +/usr/lib64 + +Kernel modules, which can be added to the kernel during runtime, are also +stored in /lib/modules. All files in these directories should be +group-owned by the root user. If the directories, is found to be owned +by a user other than root correct its +ownership with the following command: +$ sudo chgrp root DIR + CCI-001499 + CM-5(6) + CM-5(6).1 + SRG-OS-000259-GPOS-00100 + Files from shared library directories are loaded into the address +space of processes (including privileged ones) or of the kernel itself at +runtime. Proper ownership of library directories is necessary to protect +the integrity of the system. + CCE-89858-5 + - name: Ensure group owner on /lib/ recursively + file: + path: /lib/ + state: directory + recurse: true + group: '0' + tags: + - CCE-89858-5 + - NIST-800-53-CM-5(6) + - NIST-800-53-CM-5(6).1 + - configure_strategy + - dir_group_ownership_library_dirs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure group owner on /lib64/ recursively + file: + path: /lib64/ + state: directory + recurse: true + group: '0' + tags: + - CCE-89858-5 + - NIST-800-53-CM-5(6) + - NIST-800-53-CM-5(6).1 + - configure_strategy + - dir_group_ownership_library_dirs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure group owner on /usr/lib/ recursively + file: + path: /usr/lib/ + state: directory + recurse: true + group: '0' + tags: + - CCE-89858-5 + - NIST-800-53-CM-5(6) + - NIST-800-53-CM-5(6).1 + - configure_strategy + - dir_group_ownership_library_dirs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure group owner on /usr/lib64/ recursively + file: + path: /usr/lib64/ + state: directory + recurse: true + group: '0' + tags: + - CCE-89858-5 + - NIST-800-53-CM-5(6) + - NIST-800-53-CM-5(6).1 + - configure_strategy + - dir_group_ownership_library_dirs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + + + +find -H /lib/ -type d -exec chgrp 0 {} \; + +find -H /lib64/ -type d -exec chgrp 0 {} \; + +find -H /usr/lib/ -type d -exec chgrp 0 {} \; + +find -H /usr/lib64/ -type d -exec chgrp 0 {} \; + + + + + + + + + + Verify that System Executable Have Root Ownership + /bin +/sbin +/usr/bin +/usr/sbin +/usr/local/bin +/usr/local/sbin +All these directories should be owned by the root user. +If any directory DIR in these directories is found +to be owned by a user other than root, correct its ownership with the +following command: +$ sudo chown root DIR + CCI-001495 + SRG-OS-000258-GPOS-00099 + System binaries are executed by privileged users as well as system services, +and restrictive permissions are necessary to ensure that their +execution of these programs cannot be co-opted. + - name: Ensure owner on directory /bin/ recursively + file: + path: /bin/ + state: directory + recurse: true + owner: '0' + tags: + - configure_strategy + - dir_ownership_binary_dirs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure owner on directory /sbin/ recursively + file: + path: /sbin/ + state: directory + recurse: true + owner: '0' + tags: + - configure_strategy + - dir_ownership_binary_dirs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure owner on directory /usr/bin/ recursively + file: + path: /usr/bin/ + state: directory + recurse: true + owner: '0' + tags: + - configure_strategy + - dir_ownership_binary_dirs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure owner on directory /usr/sbin/ recursively + file: + path: /usr/sbin/ + state: directory + recurse: true + owner: '0' + tags: + - configure_strategy + - dir_ownership_binary_dirs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure owner on directory /usr/local/bin/ recursively + file: + path: /usr/local/bin/ + state: directory + recurse: true + owner: '0' + tags: + - configure_strategy + - dir_ownership_binary_dirs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure owner on directory /usr/local/sbin/ recursively + file: + path: /usr/local/sbin/ + state: directory + recurse: true + owner: '0' + tags: + - configure_strategy + - dir_ownership_binary_dirs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + + + +find -H /bin/ -type d -exec chown 0 {} \; + +find -H /sbin/ -type d -exec chown 0 {} \; + +find -H /usr/bin/ -type d -exec chown 0 {} \; + +find -H /usr/sbin/ -type d -exec chown 0 {} \; + +find -H /usr/local/bin/ -type d -exec chown 0 {} \; + +find -H /usr/local/sbin/ -type d -exec chown 0 {} \; + + + + + + + + + + Verify that Shared Library Directories Have Root Ownership + System-wide shared library files, which are linked to executables +during process load time or run time, are stored in the following directories +by default: +/lib +/lib64 +/usr/lib +/usr/lib64 + +Kernel modules, which can be added to the kernel during runtime, are also +stored in /lib/modules. All files in these directories should be +owned by the root user. If the directories, is found to be owned +by a user other than root correct its +ownership with the following command: +$ sudo chown root DIR + CCI-001499 + CM-5(6) + CM-5(6).1 + SRG-OS-000259-GPOS-00100 + Files from shared library directories are loaded into the address +space of processes (including privileged ones) or of the kernel itself at +runtime. Proper ownership of library directories is necessary to protect +the integrity of the system. + CCE-89022-8 + - name: Ensure owner on directory /lib/ recursively + file: + path: /lib/ + state: directory + recurse: true + owner: '0' + tags: + - CCE-89022-8 + - NIST-800-53-CM-5(6) + - NIST-800-53-CM-5(6).1 + - configure_strategy + - dir_ownership_library_dirs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure owner on directory /lib64/ recursively + file: + path: /lib64/ + state: directory + recurse: true + owner: '0' + tags: + - CCE-89022-8 + - NIST-800-53-CM-5(6) + - NIST-800-53-CM-5(6).1 + - configure_strategy + - dir_ownership_library_dirs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure owner on directory /usr/lib/ recursively + file: + path: /usr/lib/ + state: directory + recurse: true + owner: '0' + tags: + - CCE-89022-8 + - NIST-800-53-CM-5(6) + - NIST-800-53-CM-5(6).1 + - configure_strategy + - dir_ownership_library_dirs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure owner on directory /usr/lib64/ recursively + file: + path: /usr/lib64/ + state: directory + recurse: true + owner: '0' + tags: + - CCE-89022-8 + - NIST-800-53-CM-5(6) + - NIST-800-53-CM-5(6).1 + - configure_strategy + - dir_ownership_library_dirs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + + + +find -H /lib/ -type d -exec chown 0 {} \; + +find -H /lib64/ -type d -exec chown 0 {} \; + +find -H /usr/lib/ -type d -exec chown 0 {} \; + +find -H /usr/lib64/ -type d -exec chown 0 {} \; + + + + + + + + + + Verify that System Executable Directories Have Restrictive Permissions + System executables are stored in the following directories by default: +/bin +/sbin +/usr/bin +/usr/sbin +/usr/local/bin +/usr/local/sbin +These directories should not be group-writable or world-writable. +If any directory DIR in these directories is found to be +group-writable or world-writable, correct its permission with the +following command: +$ sudo chmod go-w DIR + CCI-001495 + SRG-OS-000258-GPOS-00099 + System binaries are executed by privileged users, as well as system services, +and restrictive permissions are necessary to ensure execution of these programs +cannot be co-opted. + - name: Set permissions for /bin/ recursively + file: + path: /bin/ + state: directory + recurse: true + mode: u-s,g-ws,o-wt + tags: + - configure_strategy + - dir_permissions_binary_dirs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Set permissions for /sbin/ recursively + file: + path: /sbin/ + state: directory + recurse: true + mode: u-s,g-ws,o-wt + tags: + - configure_strategy + - dir_permissions_binary_dirs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Set permissions for /usr/bin/ recursively + file: + path: /usr/bin/ + state: directory + recurse: true + mode: u-s,g-ws,o-wt + tags: + - configure_strategy + - dir_permissions_binary_dirs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Set permissions for /usr/sbin/ recursively + file: + path: /usr/sbin/ + state: directory + recurse: true + mode: u-s,g-ws,o-wt + tags: + - configure_strategy + - dir_permissions_binary_dirs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Set permissions for /usr/local/bin/ recursively + file: + path: /usr/local/bin/ + state: directory + recurse: true + mode: u-s,g-ws,o-wt + tags: + - configure_strategy + - dir_permissions_binary_dirs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Set permissions for /usr/local/sbin/ recursively + file: + path: /usr/local/sbin/ + state: directory + recurse: true + mode: u-s,g-ws,o-wt + tags: + - configure_strategy + - dir_permissions_binary_dirs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + + + + +find -H /bin/ -perm /u+s,g+ws,o+wt -type d -exec chmod u-s,g-ws,o-wt {} \; + +find -H /sbin/ -perm /u+s,g+ws,o+wt -type d -exec chmod u-s,g-ws,o-wt {} \; + +find -H /usr/bin/ -perm /u+s,g+ws,o+wt -type d -exec chmod u-s,g-ws,o-wt {} \; + +find -H /usr/sbin/ -perm /u+s,g+ws,o+wt -type d -exec chmod u-s,g-ws,o-wt {} \; + +find -H /usr/local/bin/ -perm /u+s,g+ws,o+wt -type d -exec chmod u-s,g-ws,o-wt {} \; + +find -H /usr/local/sbin/ -perm /u+s,g+ws,o+wt -type d -exec chmod u-s,g-ws,o-wt {} \; + + + + + + + + + + Verify that Shared Library Directories Have Restrictive Permissions + System-wide shared library directories, which contain are linked to executables +during process load time or run time, are stored in the following directories +by default: +/lib +/lib64 +/usr/lib +/usr/lib64 + +Kernel modules, which can be added to the kernel during runtime, are +stored in /lib/modules. All sub-directories in these directories +should not be group-writable or world-writable. If any file in these +directories is found to be group-writable or world-writable, correct +its permission with the following command: +$ sudo chmod go-w DIR + CCI-001499 + CIP-003-8 R6 + CM-5 + CM-5(6) + CM-5(6).1 + SRG-OS-000259-GPOS-00100 + If the operating system were to allow any user to make changes to software libraries, +then those changes might be implemented without undergoing the appropriate testing +and approvals that are part of a robust change management process. + +This requirement applies to operating systems with software libraries that are accessible +and configurable, as in the case of interpreted languages. Software libraries also include +privileged programs which execute with escalated privileges. Only qualified and authorized +individuals must be allowed to obtain access to information system components for purposes +of initiating changes, including upgrades and modifications. + CCE-88693-7 + - name: Set permissions for /lib/ recursively + file: + path: /lib/ + state: directory + recurse: true + mode: g-w,o-w + tags: + - CCE-88693-7 + - NIST-800-53-CM-5 + - NIST-800-53-CM-5(6) + - NIST-800-53-CM-5(6).1 + - configure_strategy + - dir_permissions_library_dirs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Set permissions for /lib64/ recursively + file: + path: /lib64/ + state: directory + recurse: true + mode: g-w,o-w + tags: + - CCE-88693-7 + - NIST-800-53-CM-5 + - NIST-800-53-CM-5(6) + - NIST-800-53-CM-5(6).1 + - configure_strategy + - dir_permissions_library_dirs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Set permissions for /usr/lib/ recursively + file: + path: /usr/lib/ + state: directory + recurse: true + mode: g-w,o-w + tags: + - CCE-88693-7 + - NIST-800-53-CM-5 + - NIST-800-53-CM-5(6) + - NIST-800-53-CM-5(6).1 + - configure_strategy + - dir_permissions_library_dirs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Set permissions for /usr/lib64/ recursively + file: + path: /usr/lib64/ + state: directory + recurse: true + mode: g-w,o-w + tags: + - CCE-88693-7 + - NIST-800-53-CM-5 + - NIST-800-53-CM-5(6) + - NIST-800-53-CM-5(6).1 + - configure_strategy + - dir_permissions_library_dirs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + + + + +find -H /lib/ -perm /g+w,o+w -type d -exec chmod g-w,o-w {} \; + +find -H /lib64/ -perm /g+w,o+w -type d -exec chmod g-w,o-w {} \; + +find -H /usr/lib/ -perm /g+w,o+w -type d -exec chmod g-w,o-w {} \; + +find -H /usr/lib64/ -perm /g+w,o+w -type d -exec chmod g-w,o-w {} \; + + + + + + + + + + Verify that system commands files are group owned by root or a system account + System commands files are stored in the following directories by default: +/bin +/sbin +/usr/bin +/usr/sbin +/usr/local/bin +/usr/local/sbin + +All files in these directories should be owned by the root group, +or a system account. +If the directory, or any file in these directories, is found to be owned +by a group other than root or a a system account correct its ownership +with the following command: +$ sudo chgrp root FILE + CCI-001499 + CM-5(6) + CM-5(6).1 + SRG-OS-000259-GPOS-00100 + If the operating system allows any user to make changes to software +libraries, then those changes might be implemented without undergoing the +appropriate testing and approvals that are part of a robust change management +process. +This requirement applies to operating systems with software libraries +that are accessible and configurable, as in the case of interpreted languages. +Software libraries also include privileged programs which execute with +escalated privileges. Only qualified and authorized individuals must be +allowed to obtain access to information system components for purposes +of initiating changes, including upgrades and modifications. + CCE-89442-8 + - name: Retrieve the system command files and set their group ownership to root + command: find -L {{ item }} ! -group root -type f -exec chgrp root '{}' \; + with_items: + - /bin + - /sbin + - /usr/bin + - /usr/sbin + - /usr/local/bin + - /usr/local/sbin + changed_when: false + failed_when: false + check_mode: false + tags: + - CCE-89442-8 + - NIST-800-53-CM-5(6) + - NIST-800-53-CM-5(6).1 + - file_groupownership_system_commands_dirs + - medium_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + +for SYSCMDFILES in /bin /sbin /usr/bin /usr/sbin /usr/local/bin /usr/local/sbin +do + find -L $SYSCMDFILES \! -group root -type f -exec chgrp root '{}' \; +done + + + + + + + + + + Verify that System Executables Have Root Ownership + System executables are stored in the following directories by default: +/bin +/sbin +/usr/bin +/usr/libexec +/usr/local/bin +/usr/local/sbin +/usr/sbin +All files in these directories should be owned by the root user. +If any file FILE in these directories is found +to be owned by a user other than root, correct its ownership with the +following command: +$ sudo chown root FILE + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + CCI-001499 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-5(6) + CM-5(6).1 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + SRG-OS-000259-GPOS-00100 + System binaries are executed by privileged users as well as system services, +and restrictive permissions are necessary to ensure that their +execution of these programs cannot be co-opted. + CCE-83908-4 + - name: Read list of system executables without root ownership + command: find /bin/ /usr/bin/ /usr/local/bin/ /sbin/ /usr/sbin/ /usr/local/sbin/ + /usr/libexec \! -user root + register: no_root_system_executables + changed_when: false + failed_when: false + check_mode: false + tags: + - CCE-83908-4 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-5(6) + - NIST-800-53-CM-5(6).1 + - NIST-800-53-CM-6(a) + - file_ownership_binary_dirs + - medium_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Set ownership to root of system executables + file: + path: '{{ item }}' + owner: root + with_items: '{{ no_root_system_executables.stdout_lines }}' + when: no_root_system_executables.stdout_lines | length > 0 + tags: + - CCE-83908-4 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-5(6) + - NIST-800-53-CM-5(6).1 + - NIST-800-53-CM-6(a) + - file_ownership_binary_dirs + - medium_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + find /bin/ \ +/usr/bin/ \ +/usr/local/bin/ \ +/sbin/ \ +/usr/sbin/ \ +/usr/local/sbin/ \ +/usr/libexec \ +\! -user root -execdir chown root {} \; + + + + + + + + + + Verify that Shared Library Files Have Root Ownership + System-wide shared library files, which are linked to executables +during process load time or run time, are stored in the following directories +by default: +/lib +/lib64 +/usr/lib +/usr/lib64 + +Kernel modules, which can be added to the kernel during runtime, are also +stored in /lib/modules. All files in these directories should be +owned by the root user. If the directory, or any file in these +directories, is found to be owned by a user other than root correct its +ownership with the following command: +$ sudo chown root FILE + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + CCI-001499 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-5(6) + CM-5(6).1 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + SRG-OS-000259-GPOS-00100 + Files from shared library directories are loaded into the address +space of processes (including privileged ones) or of the kernel itself at +runtime. Proper ownership is necessary to protect the integrity of the system. + CCE-83907-6 + - name: Find /lib/ file(s) matching ^.*$ recursively + command: find -H /lib/ -type f ! -uid 0 -regex "^.*$" + register: files_found + changed_when: false + failed_when: false + check_mode: false + tags: + - CCE-83907-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-5(6) + - NIST-800-53-CM-5(6).1 + - NIST-800-53-CM-6(a) + - configure_strategy + - file_ownership_library_dirs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure owner on /lib/ file(s) matching ^.*$ + file: + path: '{{ item }}' + owner: '0' + state: file + with_items: + - '{{ files_found.stdout_lines }}' + tags: + - CCE-83907-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-5(6) + - NIST-800-53-CM-5(6).1 + - NIST-800-53-CM-6(a) + - configure_strategy + - file_ownership_library_dirs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Find /lib64/ file(s) matching ^.*$ recursively + command: find -H /lib64/ -type f ! -uid 0 -regex "^.*$" + register: files_found + changed_when: false + failed_when: false + check_mode: false + tags: + - CCE-83907-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-5(6) + - NIST-800-53-CM-5(6).1 + - NIST-800-53-CM-6(a) + - configure_strategy + - file_ownership_library_dirs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure owner on /lib64/ file(s) matching ^.*$ + file: + path: '{{ item }}' + owner: '0' + state: file + with_items: + - '{{ files_found.stdout_lines }}' + tags: + - CCE-83907-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-5(6) + - NIST-800-53-CM-5(6).1 + - NIST-800-53-CM-6(a) + - configure_strategy + - file_ownership_library_dirs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Find /usr/lib/ file(s) matching ^.*$ recursively + command: find -H /usr/lib/ -type f ! -uid 0 -regex "^.*$" + register: files_found + changed_when: false + failed_when: false + check_mode: false + tags: + - CCE-83907-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-5(6) + - NIST-800-53-CM-5(6).1 + - NIST-800-53-CM-6(a) + - configure_strategy + - file_ownership_library_dirs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure owner on /usr/lib/ file(s) matching ^.*$ + file: + path: '{{ item }}' + owner: '0' + state: file + with_items: + - '{{ files_found.stdout_lines }}' + tags: + - CCE-83907-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-5(6) + - NIST-800-53-CM-5(6).1 + - NIST-800-53-CM-6(a) + - configure_strategy + - file_ownership_library_dirs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Find /usr/lib64/ file(s) matching ^.*$ recursively + command: find -H /usr/lib64/ -type f ! -uid 0 -regex "^.*$" + register: files_found + changed_when: false + failed_when: false + check_mode: false + tags: + - CCE-83907-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-5(6) + - NIST-800-53-CM-5(6).1 + - NIST-800-53-CM-6(a) + - configure_strategy + - file_ownership_library_dirs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure owner on /usr/lib64/ file(s) matching ^.*$ + file: + path: '{{ item }}' + owner: '0' + state: file + with_items: + - '{{ files_found.stdout_lines }}' + tags: + - CCE-83907-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-5(6) + - NIST-800-53-CM-5(6).1 + - NIST-800-53-CM-6(a) + - configure_strategy + - file_ownership_library_dirs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + + + +find /lib/ -type f ! -uid 0 -regex '^.*$' -exec chown 0 {} \; + +find /lib64/ -type f ! -uid 0 -regex '^.*$' -exec chown 0 {} \; + +find /usr/lib/ -type f ! -uid 0 -regex '^.*$' -exec chown 0 {} \; + +find /usr/lib64/ -type f ! -uid 0 -regex '^.*$' -exec chown 0 {} \; + + + + + + + + + + Verify that System Executables Have Restrictive Permissions + System executables are stored in the following directories by default: +/bin +/sbin +/usr/bin +/usr/libexec +/usr/local/bin +/usr/local/sbin +/usr/sbin +All files in these directories should not be group-writable or world-writable. +If any file FILE in these directories is found +to be group-writable or world-writable, correct its permission with the +following command: +$ sudo chmod go-w FILE + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + CCI-001499 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-5(6) + CM-5(6).1 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + SRG-OS-000259-GPOS-00100 + System binaries are executed by privileged users, as well as system services, +and restrictive permissions are necessary to ensure execution of these programs +cannot be co-opted. + CCE-83911-8 + - name: Read list of world and group writable system executables + command: find /bin /usr/bin /usr/local/bin /sbin /usr/sbin /usr/local/sbin /usr/libexec + -perm /022 -type f + register: world_writable_library_files + changed_when: false + failed_when: false + check_mode: false + tags: + - CCE-83911-8 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-5(6) + - NIST-800-53-CM-5(6).1 + - NIST-800-53-CM-6(a) + - file_permissions_binary_dirs + - medium_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Remove world/group writability of system executables + file: + path: '{{ item }}' + mode: go-w + with_items: '{{ world_writable_library_files.stdout_lines }}' + when: world_writable_library_files.stdout_lines | length > 0 + tags: + - CCE-83911-8 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-5(6) + - NIST-800-53-CM-5(6).1 + - NIST-800-53-CM-6(a) + - file_permissions_binary_dirs + - medium_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + DIRS="/bin /usr/bin /usr/local/bin /sbin /usr/sbin /usr/local/sbin /usr/libexec" +for dirPath in $DIRS; do + find "$dirPath" -perm /022 -exec chmod go-w '{}' \; +done + + + + + + + + + + Verify that Shared Library Files Have Restrictive Permissions + System-wide shared library files, which are linked to executables +during process load time or run time, are stored in the following directories +by default: +/lib +/lib64 +/usr/lib +/usr/lib64 + +Kernel modules, which can be added to the kernel during runtime, are +stored in /lib/modules. All files in these directories +should not be group-writable or world-writable. If any file in these +directories is found to be group-writable or world-writable, correct +its permission with the following command: +$ sudo chmod go-w FILE + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + CCI-001499 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-6(a) + CM-5(6) + CM-5(6).1 + AC-6(1) + PR.AC-4 + PR.DS-5 + SRG-OS-000259-GPOS-00100 + Files from shared library directories are loaded into the address +space of processes (including privileged ones) or of the kernel itself at +runtime. Restrictive permissions are necessary to protect the integrity of the system. + CCE-83909-2 + - name: Find /lib/ file(s) recursively + command: find -H /lib/ -perm /g+w,o+w -type f -regex "^.*$" + register: files_found + changed_when: false + failed_when: false + check_mode: false + tags: + - CCE-83909-2 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-5(6) + - NIST-800-53-CM-5(6).1 + - NIST-800-53-CM-6(a) + - configure_strategy + - file_permissions_library_dirs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Set permissions for /lib/ file(s) + file: + path: '{{ item }}' + mode: g-w,o-w + state: file + with_items: + - '{{ files_found.stdout_lines }}' + tags: + - CCE-83909-2 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-5(6) + - NIST-800-53-CM-5(6).1 + - NIST-800-53-CM-6(a) + - configure_strategy + - file_permissions_library_dirs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Find /lib64/ file(s) recursively + command: find -H /lib64/ -perm /g+w,o+w -type f -regex "^.*$" + register: files_found + changed_when: false + failed_when: false + check_mode: false + tags: + - CCE-83909-2 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-5(6) + - NIST-800-53-CM-5(6).1 + - NIST-800-53-CM-6(a) + - configure_strategy + - file_permissions_library_dirs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Set permissions for /lib64/ file(s) + file: + path: '{{ item }}' + mode: g-w,o-w + state: file + with_items: + - '{{ files_found.stdout_lines }}' + tags: + - CCE-83909-2 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-5(6) + - NIST-800-53-CM-5(6).1 + - NIST-800-53-CM-6(a) + - configure_strategy + - file_permissions_library_dirs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Find /usr/lib/ file(s) recursively + command: find -H /usr/lib/ -perm /g+w,o+w -type f -regex "^.*$" + register: files_found + changed_when: false + failed_when: false + check_mode: false + tags: + - CCE-83909-2 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-5(6) + - NIST-800-53-CM-5(6).1 + - NIST-800-53-CM-6(a) + - configure_strategy + - file_permissions_library_dirs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Set permissions for /usr/lib/ file(s) + file: + path: '{{ item }}' + mode: g-w,o-w + state: file + with_items: + - '{{ files_found.stdout_lines }}' + tags: + - CCE-83909-2 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-5(6) + - NIST-800-53-CM-5(6).1 + - NIST-800-53-CM-6(a) + - configure_strategy + - file_permissions_library_dirs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Find /usr/lib64/ file(s) recursively + command: find -H /usr/lib64/ -perm /g+w,o+w -type f -regex "^.*$" + register: files_found + changed_when: false + failed_when: false + check_mode: false + tags: + - CCE-83909-2 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-5(6) + - NIST-800-53-CM-5(6).1 + - NIST-800-53-CM-6(a) + - configure_strategy + - file_permissions_library_dirs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Set permissions for /usr/lib64/ file(s) + file: + path: '{{ item }}' + mode: g-w,o-w + state: file + with_items: + - '{{ files_found.stdout_lines }}' + tags: + - CCE-83909-2 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-5(6) + - NIST-800-53-CM-5(6).1 + - NIST-800-53-CM-6(a) + - configure_strategy + - file_permissions_library_dirs + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + + + + +find -H /lib/ -perm /g+w,o+w -type f -regex '^.*$' -exec chmod g-w,o-w {} \; + +find -H /lib64/ -perm /g+w,o+w -type f -regex '^.*$' -exec chmod g-w,o-w {} \; + +find -H /usr/lib/ -perm /g+w,o+w -type f -regex '^.*$' -exec chmod g-w,o-w {} \; + +find -H /usr/lib64/ -perm /g+w,o+w -type f -regex '^.*$' -exec chmod g-w,o-w {} \; + + + + + + + + + + Verify the system-wide library files in directories +"/lib", "/lib64", "/usr/lib/" and "/usr/lib64" are group-owned by root. + System-wide library files are stored in the following directories +by default: +/lib +/lib64 +/usr/lib +/usr/lib64 + +All system-wide shared library files should be protected from unauthorised +access. If any of these files is not group-owned by root, correct its group-owner with +the following command: +$ sudo chgrp root FILE + CCI-001499 + CM-5(6) + CM-5(6).1 + SRG-OS-000259-GPOS-00100 + If the operating system were to allow any user to make changes to software libraries, +then those changes might be implemented without undergoing the appropriate testing and +approvals that are part of a robust change management process. + +This requirement applies to operating systems with software libraries that are +accessible and configurable, as in the case of interpreted languages. Software libraries +also include privileged programs which execute with escalated privileges. Only qualified +and authorized individuals must be allowed to obtain access to information system components +for purposes of initiating changes, including upgrades and modifications. + CCE-87108-7 + - name: Ensure group owner on /lib/ recursively + file: + path: /lib/ + state: directory + recurse: true + group: '0' + tags: + - CCE-87108-7 + - NIST-800-53-CM-5(6) + - NIST-800-53-CM-5(6).1 + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - root_permissions_syslibrary_files + +- name: Ensure group owner on /lib64/ recursively + file: + path: /lib64/ + state: directory + recurse: true + group: '0' + tags: + - CCE-87108-7 + - NIST-800-53-CM-5(6) + - NIST-800-53-CM-5(6).1 + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - root_permissions_syslibrary_files + +- name: Ensure group owner on /usr/lib/ recursively + file: + path: /usr/lib/ + state: directory + recurse: true + group: '0' + tags: + - CCE-87108-7 + - NIST-800-53-CM-5(6) + - NIST-800-53-CM-5(6).1 + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - root_permissions_syslibrary_files + +- name: Ensure group owner on /usr/lib64/ recursively + file: + path: /usr/lib64/ + state: directory + recurse: true + group: '0' + tags: + - CCE-87108-7 + - NIST-800-53-CM-5(6) + - NIST-800-53-CM-5(6).1 + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - root_permissions_syslibrary_files + + + + +find -H /lib/ -type d -exec chgrp 0 {} \; + +find -H /lib64/ -type d -exec chgrp 0 {} \; + +find -H /usr/lib/ -type d -exec chgrp 0 {} \; + +find -H /usr/lib64/ -type d -exec chgrp 0 {} \; + + + + + + + + + + + + Restrict Dynamic Mounting and Unmounting of +Filesystems + Linux includes a number of facilities for the automated addition +and removal of filesystems on a running system. These facilities may be +necessary in many environments, but this capability also carries some risk -- whether direct +risk from allowing users to introduce arbitrary filesystems, +or risk that software flaws in the automated mount facility itself could +allow an attacker to compromise the system. + +This command can be used to list the types of filesystems that are +available to the currently executing kernel: +$ find /lib/modules/`uname -r`/kernel/fs -type f -name '*.ko' +If these filesystems are not required then they can be explicitly disabled +in a configuratio file in /etc/modprobe.d. + + Disable the Automounter + The autofs daemon mounts and unmounts filesystems, such as user +home directories shared via NFS, on demand. In addition, autofs can be used to handle +removable media, and the default configuration provides the cdrom device as /misc/cd. +However, this method of providing access to removable media is not common, so autofs +can almost always be disabled if NFS is not in use. Even if NFS is required, it may be +possible to configure filesystem mounts statically by editing /etc/fstab +rather than relying on the automounter. + + +The autofs service can be disabled with the following command: +$ sudo systemctl mask --now autofs.service + 1 + 12 + 15 + 16 + 5 + APO13.01 + DSS01.04 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.03 + DSS06.10 + 3.4.6 + CCI-000366 + CCI-000778 + CCI-001958 + 164.308(a)(3)(i) + 164.308(a)(3)(ii)(A) + 164.310(d)(1) + 164.310(d)(2) + 164.312(a)(1) + 164.312(a)(2)(iv) + 164.312(b) + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.2 + 4.3.3.7.4 + SR 1.1 + SR 1.10 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.6 + A.11.2.6 + A.13.1.1 + A.13.2.1 + A.18.1.4 + A.6.2.1 + A.6.2.2 + A.7.1.1 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.2 + A.9.4.3 + CM-7(a) + CM-7(b) + CM-6(a) + MP-7 + PR.AC-1 + PR.AC-3 + PR.AC-6 + PR.AC-7 + SRG-OS-000114-GPOS-00059 + SRG-OS-000378-GPOS-00163 + SRG-OS-000480-GPOS-00227 + Disabling the automounter permits the administrator to +statically control filesystem mounting through /etc/fstab. + +Additionally, automatically mounting filesystems permits easy introduction of +unknown devices, thereby facilitating malicious activity. + + CCE-83850-8 + include disable_autofs + +class disable_autofs { + service {'autofs': + enable => false, + ensure => 'stopped', + } +} + + - name: Disable service autofs + block: + + - name: Disable service autofs + systemd: + name: autofs.service + enabled: 'no' + state: stopped + masked: 'yes' + ignore_errors: 'yes' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83850-8 + - NIST-800-171-3.4.6 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_autofs_disabled + +- name: Unit Socket Exists - autofs.socket + command: systemctl list-unit-files autofs.socket + args: + warn: false + register: socket_file_exists + changed_when: false + ignore_errors: true + check_mode: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83850-8 + - NIST-800-171-3.4.6 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_autofs_disabled + +- name: Disable socket autofs + systemd: + name: autofs.socket + enabled: 'no' + state: stopped + masked: 'yes' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"autofs.socket" in socket_file_exists.stdout_lines[1]' + tags: + - CCE-83850-8 + - NIST-800-171-3.4.6 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_autofs_disabled + + +[customizations.services] +disabled = ["autofs"] + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + systemd: + units: + - enabled: false + name: autofs.service + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" stop 'autofs.service' +"$SYSTEMCTL_EXEC" disable 'autofs.service' +"$SYSTEMCTL_EXEC" mask 'autofs.service' +# Disable socket activation if we have a unit file for it +if "$SYSTEMCTL_EXEC" list-unit-files | grep -q '^autofs.socket'; then + "$SYSTEMCTL_EXEC" stop 'autofs.socket' + "$SYSTEMCTL_EXEC" mask 'autofs.socket' +fi +# The service may not be running because it has been started and failed, +# so let's reset the state so OVAL checks pass. +# Service should be 'inactive', not 'failed' after reboot though. +"$SYSTEMCTL_EXEC" reset-failed 'autofs.service' || true + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable Mounting of cramfs + +To configure the system to prevent the cramfs +kernel module from being loaded, add the following line to the file /etc/modprobe.d/cramfs.conf: +install cramfs /bin/true + +This effectively prevents usage of this uncommon filesystem. + +The cramfs filesystem type is a compressed read-only +Linux filesystem embedded in small footprint systems. A +cramfs image can be used without having to first +decompress the image. + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + 3.4.6 + CCI-000381 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + PR.PT-3 + SRG-OS-000095-GPOS-00049 + Removing support for unneeded filesystem types reduces the local attack surface +of the server. + + CCE-83853-2 + - name: Ensure kernel module 'cramfs' is disabled + lineinfile: + create: true + dest: /etc/modprobe.d/cramfs.conf + regexp: cramfs + line: install cramfs /bin/true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83853-2 + - NIST-800-171-3.4.6 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - kernel_module_cramfs_disabled + - low_complexity + - low_severity + - medium_disruption + - reboot_required + +- name: Ensure kernel module 'cramfs' is blacklisted + lineinfile: + create: true + dest: /etc/modprobe.d/cramfs.conf + regexp: ^blacklist cramfs$ + line: blacklist cramfs + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83853-2 + - NIST-800-171-3.4.6 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - kernel_module_cramfs_disabled + - low_complexity + - low_severity + - medium_disruption + - reboot_required + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,install%20cramfs%20/bin/true%0Ablacklist%20cramfs%0A + mode: 0644 + path: /etc/modprobe.d/cramfs.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if LC_ALL=C grep -q -m 1 "^install cramfs" /etc/modprobe.d/cramfs.conf ; then + + sed -i 's#^install cramfs.*#install cramfs /bin/true#g' /etc/modprobe.d/cramfs.conf +else + echo -e "\n# Disable per security requirements" >> /etc/modprobe.d/cramfs.conf + echo "install cramfs /bin/true" >> /etc/modprobe.d/cramfs.conf +fi + +if ! LC_ALL=C grep -q -m 1 "^blacklist cramfs$" /etc/modprobe.d/cramfs.conf ; then + echo "blacklist cramfs" >> /etc/modprobe.d/cramfs.conf +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable Mounting of freevxfs + +To configure the system to prevent the freevxfs +kernel module from being loaded, add the following line to the file /etc/modprobe.d/freevxfs.conf: +install freevxfs /bin/true + +This effectively prevents usage of this uncommon filesystem. + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + 3.4.6 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + PR.PT-3 + Linux kernel modules which implement filesystems that are not needed by the +local system should be disabled. + + - name: Ensure kernel module 'freevxfs' is disabled + lineinfile: + create: true + dest: /etc/modprobe.d/freevxfs.conf + regexp: freevxfs + line: install freevxfs /bin/true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-171-3.4.6 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - kernel_module_freevxfs_disabled + - low_complexity + - low_severity + - medium_disruption + - reboot_required + +- name: Ensure kernel module 'freevxfs' is blacklisted + lineinfile: + create: true + dest: /etc/modprobe.d/freevxfs.conf + regexp: ^blacklist freevxfs$ + line: blacklist freevxfs + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-171-3.4.6 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - kernel_module_freevxfs_disabled + - low_complexity + - low_severity + - medium_disruption + - reboot_required + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,install%20freevxfs%20/bin/true%0Ablacklist%20freevxfs%0A + mode: 0644 + path: /etc/modprobe.d/freevxfs.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if LC_ALL=C grep -q -m 1 "^install freevxfs" /etc/modprobe.d/freevxfs.conf ; then + + sed -i 's#^install freevxfs.*#install freevxfs /bin/true#g' /etc/modprobe.d/freevxfs.conf +else + echo -e "\n# Disable per security requirements" >> /etc/modprobe.d/freevxfs.conf + echo "install freevxfs /bin/true" >> /etc/modprobe.d/freevxfs.conf +fi + +if ! LC_ALL=C grep -q -m 1 "^blacklist freevxfs$" /etc/modprobe.d/freevxfs.conf ; then + echo "blacklist freevxfs" >> /etc/modprobe.d/freevxfs.conf +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + Disable Mounting of hfs + +To configure the system to prevent the hfs +kernel module from being loaded, add the following line to the file /etc/modprobe.d/hfs.conf: +install hfs /bin/true + +This effectively prevents usage of this uncommon filesystem. + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + 3.4.6 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + PR.PT-3 + Linux kernel modules which implement filesystems that are not needed by the +local system should be disabled. + + - name: Ensure kernel module 'hfs' is disabled + lineinfile: + create: true + dest: /etc/modprobe.d/hfs.conf + regexp: hfs + line: install hfs /bin/true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-171-3.4.6 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - kernel_module_hfs_disabled + - low_complexity + - low_severity + - medium_disruption + - reboot_required + +- name: Ensure kernel module 'hfs' is blacklisted + lineinfile: + create: true + dest: /etc/modprobe.d/hfs.conf + regexp: ^blacklist hfs$ + line: blacklist hfs + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-171-3.4.6 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - kernel_module_hfs_disabled + - low_complexity + - low_severity + - medium_disruption + - reboot_required + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,install%20hfs%20/bin/true%0Ablacklist%20hfs%0A + mode: 0644 + path: /etc/modprobe.d/hfs.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if LC_ALL=C grep -q -m 1 "^install hfs" /etc/modprobe.d/hfs.conf ; then + + sed -i 's#^install hfs.*#install hfs /bin/true#g' /etc/modprobe.d/hfs.conf +else + echo -e "\n# Disable per security requirements" >> /etc/modprobe.d/hfs.conf + echo "install hfs /bin/true" >> /etc/modprobe.d/hfs.conf +fi + +if ! LC_ALL=C grep -q -m 1 "^blacklist hfs$" /etc/modprobe.d/hfs.conf ; then + echo "blacklist hfs" >> /etc/modprobe.d/hfs.conf +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + Disable Mounting of hfsplus + +To configure the system to prevent the hfsplus +kernel module from being loaded, add the following line to the file /etc/modprobe.d/hfsplus.conf: +install hfsplus /bin/true + +This effectively prevents usage of this uncommon filesystem. + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + 3.4.6 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + PR.PT-3 + Linux kernel modules which implement filesystems that are not needed by the +local system should be disabled. + + - name: Ensure kernel module 'hfsplus' is disabled + lineinfile: + create: true + dest: /etc/modprobe.d/hfsplus.conf + regexp: hfsplus + line: install hfsplus /bin/true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-171-3.4.6 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - kernel_module_hfsplus_disabled + - low_complexity + - low_severity + - medium_disruption + - reboot_required + +- name: Ensure kernel module 'hfsplus' is blacklisted + lineinfile: + create: true + dest: /etc/modprobe.d/hfsplus.conf + regexp: ^blacklist hfsplus$ + line: blacklist hfsplus + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-171-3.4.6 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - kernel_module_hfsplus_disabled + - low_complexity + - low_severity + - medium_disruption + - reboot_required + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,install%20hfsplus%20/bin/true%0Ablacklist%20hfsplus%0A + mode: 0644 + path: /etc/modprobe.d/hfsplus.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if LC_ALL=C grep -q -m 1 "^install hfsplus" /etc/modprobe.d/hfsplus.conf ; then + + sed -i 's#^install hfsplus.*#install hfsplus /bin/true#g' /etc/modprobe.d/hfsplus.conf +else + echo -e "\n# Disable per security requirements" >> /etc/modprobe.d/hfsplus.conf + echo "install hfsplus /bin/true" >> /etc/modprobe.d/hfsplus.conf +fi + +if ! LC_ALL=C grep -q -m 1 "^blacklist hfsplus$" /etc/modprobe.d/hfsplus.conf ; then + echo "blacklist hfsplus" >> /etc/modprobe.d/hfsplus.conf +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + Disable Mounting of jffs2 + +To configure the system to prevent the jffs2 +kernel module from being loaded, add the following line to the file /etc/modprobe.d/jffs2.conf: +install jffs2 /bin/true + +This effectively prevents usage of this uncommon filesystem. + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + 3.4.6 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + PR.PT-3 + Linux kernel modules which implement filesystems that are not needed by the +local system should be disabled. + + - name: Ensure kernel module 'jffs2' is disabled + lineinfile: + create: true + dest: /etc/modprobe.d/jffs2.conf + regexp: jffs2 + line: install jffs2 /bin/true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-171-3.4.6 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - kernel_module_jffs2_disabled + - low_complexity + - low_severity + - medium_disruption + - reboot_required + +- name: Ensure kernel module 'jffs2' is blacklisted + lineinfile: + create: true + dest: /etc/modprobe.d/jffs2.conf + regexp: ^blacklist jffs2$ + line: blacklist jffs2 + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-171-3.4.6 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - kernel_module_jffs2_disabled + - low_complexity + - low_severity + - medium_disruption + - reboot_required + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,install%20jffs2%20/bin/true%0Ablacklist%20jffs2%0A + mode: 0644 + path: /etc/modprobe.d/jffs2.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if LC_ALL=C grep -q -m 1 "^install jffs2" /etc/modprobe.d/jffs2.conf ; then + + sed -i 's#^install jffs2.*#install jffs2 /bin/true#g' /etc/modprobe.d/jffs2.conf +else + echo -e "\n# Disable per security requirements" >> /etc/modprobe.d/jffs2.conf + echo "install jffs2 /bin/true" >> /etc/modprobe.d/jffs2.conf +fi + +if ! LC_ALL=C grep -q -m 1 "^blacklist jffs2$" /etc/modprobe.d/jffs2.conf ; then + echo "blacklist jffs2" >> /etc/modprobe.d/jffs2.conf +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + Disable Mounting of squashfs + +To configure the system to prevent the squashfs +kernel module from being loaded, add the following line to the file /etc/modprobe.d/squashfs.conf: +install squashfs /bin/true + +This effectively prevents usage of this uncommon filesystem. + +The squashfs filesystem type is a compressed read-only Linux +filesystem embedded in small footprint systems (similar to +cramfs). A squashfs image can be used without having +to first decompress the image. + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + 3.4.6 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + PR.PT-3 + Removing support for unneeded filesystem types reduces the local attack +surface of the system. + + CCE-83855-7 + - name: Ensure kernel module 'squashfs' is disabled + lineinfile: + create: true + dest: /etc/modprobe.d/squashfs.conf + regexp: squashfs + line: install squashfs /bin/true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83855-7 + - NIST-800-171-3.4.6 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - kernel_module_squashfs_disabled + - low_complexity + - low_severity + - medium_disruption + - reboot_required + +- name: Ensure kernel module 'squashfs' is blacklisted + lineinfile: + create: true + dest: /etc/modprobe.d/squashfs.conf + regexp: ^blacklist squashfs$ + line: blacklist squashfs + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83855-7 + - NIST-800-171-3.4.6 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - kernel_module_squashfs_disabled + - low_complexity + - low_severity + - medium_disruption + - reboot_required + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,install%20squashfs%20/bin/true%0Ablacklist%20squashfs%0A + mode: 0644 + path: /etc/modprobe.d/squashfs.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if LC_ALL=C grep -q -m 1 "^install squashfs" /etc/modprobe.d/squashfs.conf ; then + + sed -i 's#^install squashfs.*#install squashfs /bin/true#g' /etc/modprobe.d/squashfs.conf +else + echo -e "\n# Disable per security requirements" >> /etc/modprobe.d/squashfs.conf + echo "install squashfs /bin/true" >> /etc/modprobe.d/squashfs.conf +fi + +if ! LC_ALL=C grep -q -m 1 "^blacklist squashfs$" /etc/modprobe.d/squashfs.conf ; then + echo "blacklist squashfs" >> /etc/modprobe.d/squashfs.conf +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + Disable Mounting of udf + +To configure the system to prevent the udf +kernel module from being loaded, add the following line to the file /etc/modprobe.d/udf.conf: +install udf /bin/true + +This effectively prevents usage of this uncommon filesystem. + +The udf filesystem type is the universal disk format +used to implement the ISO/IEC 13346 and ECMA-167 specifications. +This is an open vendor filesystem type for data storage on a broad +range of media. This filesystem type is neccessary to support +writing DVDs and newer optical disc formats. + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + 3.4.6 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + PR.PT-3 + Removing support for unneeded filesystem types reduces the local +attack surface of the system. + + CCE-83852-4 + - name: Ensure kernel module 'udf' is disabled + lineinfile: + create: true + dest: /etc/modprobe.d/udf.conf + regexp: udf + line: install udf /bin/true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83852-4 + - NIST-800-171-3.4.6 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - kernel_module_udf_disabled + - low_complexity + - low_severity + - medium_disruption + - reboot_required + +- name: Ensure kernel module 'udf' is blacklisted + lineinfile: + create: true + dest: /etc/modprobe.d/udf.conf + regexp: ^blacklist udf$ + line: blacklist udf + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83852-4 + - NIST-800-171-3.4.6 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - kernel_module_udf_disabled + - low_complexity + - low_severity + - medium_disruption + - reboot_required + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,install%20udf%20/bin/true%0Ablacklist%20udf%0A + mode: 0644 + path: /etc/modprobe.d/udf.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if LC_ALL=C grep -q -m 1 "^install udf" /etc/modprobe.d/udf.conf ; then + + sed -i 's#^install udf.*#install udf /bin/true#g' /etc/modprobe.d/udf.conf +else + echo -e "\n# Disable per security requirements" >> /etc/modprobe.d/udf.conf + echo "install udf /bin/true" >> /etc/modprobe.d/udf.conf +fi + +if ! LC_ALL=C grep -q -m 1 "^blacklist udf$" /etc/modprobe.d/udf.conf ; then + echo "blacklist udf" >> /etc/modprobe.d/udf.conf +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + Disable Modprobe Loading of USB Storage Driver + To prevent USB storage devices from being used, configure the kernel module loading system +to prevent automatic loading of the USB storage driver. + +To configure the system to prevent the usb-storage +kernel module from being loaded, add the following line to the file /etc/modprobe.d/usb-storage.conf: +install usb-storage /bin/true + +This will prevent the modprobe program from loading the usb-storage +module, but will not prevent an administrator (or another program) from using the +insmod program to load the module manually. + 1 + 12 + 15 + 16 + 5 + APO13.01 + DSS01.04 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.03 + DSS06.10 + 3.1.21 + CCI-000366 + CCI-000778 + CCI-001958 + 164.308(a)(3)(i) + 164.308(a)(3)(ii)(A) + 164.310(d)(1) + 164.310(d)(2) + 164.312(a)(1) + 164.312(a)(2)(iv) + 164.312(b) + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.2 + 4.3.3.7.4 + SR 1.1 + SR 1.10 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.6 + A.11.2.6 + A.13.1.1 + A.13.2.1 + A.18.1.4 + A.6.2.1 + A.6.2.2 + A.7.1.1 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.2 + A.9.4.3 + CM-7(a) + CM-7(b) + CM-6(a) + MP-7 + PR.AC-1 + PR.AC-3 + PR.AC-6 + PR.AC-7 + SRG-OS-000114-GPOS-00059 + SRG-OS-000378-GPOS-00163 + SRG-OS-000480-GPOS-00227 + USB storage devices such as thumb drives can be used to introduce +malicious software. + + CCE-83851-6 + - name: Ensure kernel module 'usb-storage' is disabled + lineinfile: + create: true + dest: /etc/modprobe.d/usb-storage.conf + regexp: usb-storage + line: install usb-storage /bin/true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83851-6 + - NIST-800-171-3.1.21 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - disable_strategy + - kernel_module_usb-storage_disabled + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + +- name: Ensure kernel module 'usb-storage' is blacklisted + lineinfile: + create: true + dest: /etc/modprobe.d/usb-storage.conf + regexp: ^blacklist usb-storage$ + line: blacklist usb-storage + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83851-6 + - NIST-800-171-3.1.21 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - disable_strategy + - kernel_module_usb-storage_disabled + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,install%20usb-storage%20/bin/true%0Ablacklist%20usb-storage%0A + mode: 0644 + path: /etc/modprobe.d/usb-storage.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if LC_ALL=C grep -q -m 1 "^install usb-storage" /etc/modprobe.d/usb-storage.conf ; then + + sed -i 's#^install usb-storage.*#install usb-storage /bin/true#g' /etc/modprobe.d/usb-storage.conf +else + echo -e "\n# Disable per security requirements" >> /etc/modprobe.d/usb-storage.conf + echo "install usb-storage /bin/true" >> /etc/modprobe.d/usb-storage.conf +fi + +if ! LC_ALL=C grep -q -m 1 "^blacklist usb-storage$" /etc/modprobe.d/usb-storage.conf ; then + echo "blacklist usb-storage" >> /etc/modprobe.d/usb-storage.conf +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable Mounting of vFAT filesystems + +To configure the system to prevent the vfat +kernel module from being loaded, add the following line to the file /etc/modprobe.d/vfat.conf: +install vfat /bin/true + +This effectively prevents usage of this uncommon filesystem. + +The vFAT filesystem format is primarily used on older +windows systems and portable USB drives or flash modules. It comes +in three types FAT12, FAT16, and FAT32 +all of which are supported by the vfat kernel module. + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + 3.4.6 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + PR.PT-3 + Removing support for unneeded filesystems reduces the local attack +surface of the system. + + - name: Ensure kernel module 'vfat' is disabled + lineinfile: + create: true + dest: /etc/modprobe.d/vfat.conf + regexp: vfat + line: install vfat /bin/true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-171-3.4.6 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - kernel_module_vfat_disabled + - low_complexity + - low_severity + - medium_disruption + - reboot_required + +- name: Ensure kernel module 'vfat' is blacklisted + lineinfile: + create: true + dest: /etc/modprobe.d/vfat.conf + regexp: ^blacklist vfat$ + line: blacklist vfat + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-171-3.4.6 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - kernel_module_vfat_disabled + - low_complexity + - low_severity + - medium_disruption + - reboot_required + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,install%20vfat%20/bin/true%0Ablacklist%20vfat%0A + mode: 0644 + path: /etc/modprobe.d/vfat.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if LC_ALL=C grep -q -m 1 "^install vfat" /etc/modprobe.d/vfat.conf ; then + + sed -i 's#^install vfat.*#install vfat /bin/true#g' /etc/modprobe.d/vfat.conf +else + echo -e "\n# Disable per security requirements" >> /etc/modprobe.d/vfat.conf + echo "install vfat /bin/true" >> /etc/modprobe.d/vfat.conf +fi + +if ! LC_ALL=C grep -q -m 1 "^blacklist vfat$" /etc/modprobe.d/vfat.conf ; then + echo "blacklist vfat" >> /etc/modprobe.d/vfat.conf +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + Restrict Partition Mount Options + System partitions can be mounted with certain options +that limit what files on those partitions can do. These options +are set in the /etc/fstab configuration file, and can be +used to make certain types of malicious behavior more difficult. + + Value for hidepid option + The hidepid mount option is applicable to /proc and is used to control who can access +the information in /proc/[pid] directories. The option can have one of the following +values: +0: Everybody may access all /proc/[pid] directories. +1: Users may not access files and subdirectories inside any /proc/[pid] directories + but their own. The /proc/[pid] directories themselves remain visible. +2: Same as for mode 1, but in addition the /proc/[pid] directories belonging to other + users become invisible. + 0 + noaccess + invisible + invisible + + + Removable Partition + This value is used by the checks mount_option_nodev_removable_partitions, mount_option_nodev_removable_partitions, +and mount_option_nodev_removable_partitions to ensure that the correct mount options are set on partitions mounted from +removable media such as CD-ROMs, USB keys, and floppy drives. This value should be modified to reflect any removable +partitions that are required on the local system. + /dev/cdrom + + + Add nosuid Option to /boot/efi + The nosuid mount option can be used to prevent +execution of setuid programs in /boot/efi. The SUID and SGID permissions +should not be required on the boot partition. +Add the nosuid option to the fourth column of +/etc/fstab for the line which controls mounting of +/boot/efi. + CCI-000366 + CM-6(b) + CM-6.1(iv) + SRG-OS-000480-GPOS-00227 + The presence of SUID and SGID executables should be tightly controlled. Users +should not be able to execute SUID or SGID binaries from boot partitions. + + CCE-86040-3 + - name: 'Add nosuid Option to /boot/efi: Check information associated to mountpoint' + command: findmnt '/boot/efi' + register: device_name + failed_when: device_name.rc > 1 + changed_when: false + when: ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", + "container"] and "/boot/efi" in ansible_mounts | map(attribute="mount") | list + ) + tags: + - CCE-86040-3 + - NIST-800-53-CM-6(b) + - NIST-800-53-CM-6.1(iv) + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_boot_efi_nosuid + - no_reboot_needed + +- name: 'Add nosuid Option to /boot/efi: Create mount_info dictionary variable' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - '{{ device_name.stdout_lines[0].split() | list | lower }}' + - '{{ device_name.stdout_lines[1].split() | list }}' + when: + - ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + and "/boot/efi" in ansible_mounts | map(attribute="mount") | list ) + - device_name.stdout is defined and device_name.stdout_lines is defined + - (device_name.stdout | length > 0) + tags: + - CCE-86040-3 + - NIST-800-53-CM-6(b) + - NIST-800-53-CM-6.1(iv) + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_boot_efi_nosuid + - no_reboot_needed + +- name: 'Add nosuid Option to /boot/efi: If /boot/efi not mounted, craft mount_info + manually' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - - target + - source + - fstype + - options + - - /boot/efi + - '' + - '' + - defaults + when: + - ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + and "/boot/efi" in ansible_mounts | map(attribute="mount") | list ) + - ("" | length == 0) + - (device_name.stdout | length == 0) + tags: + - CCE-86040-3 + - NIST-800-53-CM-6(b) + - NIST-800-53-CM-6.1(iv) + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_boot_efi_nosuid + - no_reboot_needed + +- name: 'Add nosuid Option to /boot/efi: Make sure nosuid option is part of the to + /boot/efi options' + set_fact: + mount_info: '{{ mount_info | combine( {''options'':''''~mount_info.options~'',nosuid'' + }) }}' + when: + - ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + and "/boot/efi" in ansible_mounts | map(attribute="mount") | list ) + - mount_info is defined and "nosuid" not in mount_info.options + tags: + - CCE-86040-3 + - NIST-800-53-CM-6(b) + - NIST-800-53-CM-6.1(iv) + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_boot_efi_nosuid + - no_reboot_needed + +- name: 'Add nosuid Option to /boot/efi: Ensure /boot/efi is mounted with nosuid option' + mount: + path: /boot/efi + src: '{{ mount_info.source }}' + opts: '{{ mount_info.options }}' + state: mounted + fstype: '{{ mount_info.fstype }}' + when: + - ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + and "/boot/efi" in ansible_mounts | map(attribute="mount") | list ) + - (device_name.stdout is defined and (device_name.stdout | length > 0)) or ("" | + length == 0) + tags: + - CCE-86040-3 + - NIST-800-53-CM-6(b) + - NIST-800-53-CM-6.1(iv) + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_boot_efi_nosuid + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if ( [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && [ -f /sys/firmware/efi ] ); then + +function perform_remediation { + + + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" /boot/efi)" + + # If the mount point is not in /etc/fstab, get previous mount options from /etc/mtab + if [ "$(grep -c "$mount_point_match_regexp" /etc/fstab)" -eq 0 ]; then + # runtime opts without some automatic kernel/userspace-added defaults + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/mtab | head -1 | awk '{print $4}' \ + | sed -E "s/(rw|defaults|seclabel|nosuid)(,|$)//g;s/,$//") + [ "$previous_mount_opts" ] && previous_mount_opts+="," + echo " /boot/efi defaults,${previous_mount_opts}nosuid 0 0" >> /etc/fstab + # If the mount_opt option is not already in the mount point's /etc/fstab entry, add it + elif [ "$(grep "$mount_point_match_regexp" /etc/fstab | grep -c "nosuid")" -eq 0 ]; then + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/fstab | awk '{print $4}') + sed -i "s|\(${mount_point_match_regexp}.*${previous_mount_opts}\)|\1,nosuid|" /etc/fstab + fi + + + if mkdir -p "/boot/efi"; then + if mountpoint -q "/boot/efi"; then + mount -o remount --target "/boot/efi" + else + mount --target "/boot/efi" + fi + fi +} + +perform_remediation + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Add noauto Option to /boot + The noauto mount option is used to prevent automatic mounting of th +/boot partition. +Add the noauto option to the fourth column of +/etc/fstab for the line which controls mounting of +/boot. + Although contents of the /boot partition should not be needed +during normal system operation, they might need to be accessible during +system maintenance and upgrades. Make sure that applying this rule will +not break upgrade or maintenance processes affecting the system. + BP28(R12) + The /boot partition contains the kernel and the bootloader. Access +to the partition after the boot process finishes should not be needed. Files +contained within this partition can be analysed and gained information can +be used for exploit creation. + + +part /boot --mountoptions="noauto" + + - name: 'Add noauto Option to /boot: Check information associated to mountpoint' + command: findmnt --fstab '/boot' + register: device_name + failed_when: device_name.rc > 1 + changed_when: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_boot_noauto + - no_reboot_needed + +- name: 'Add noauto Option to /boot: Create mount_info dictionary variable' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - '{{ device_name.stdout_lines[0].split() | list | lower }}' + - '{{ device_name.stdout_lines[1].split() | list }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - device_name.stdout is defined and device_name.stdout_lines is defined + - (device_name.stdout | length > 0) + tags: + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_boot_noauto + - no_reboot_needed + +- name: 'Add noauto Option to /boot: If /boot not mounted, craft mount_info manually' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - - target + - source + - fstype + - options + - - /boot + - '' + - '' + - defaults + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ("--fstab" | length == 0) + - (device_name.stdout | length == 0) + tags: + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_boot_noauto + - no_reboot_needed + +- name: 'Add noauto Option to /boot: Make sure noauto option is part of the to /boot + options' + set_fact: + mount_info: '{{ mount_info | combine( {''options'':''''~mount_info.options~'',noauto'' + }) }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - mount_info is defined and "noauto" not in mount_info.options + tags: + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_boot_noauto + - no_reboot_needed + +- name: 'Add noauto Option to /boot: Ensure /boot is mounted with noauto option' + mount: + path: /boot + src: '{{ mount_info.source }}' + opts: '{{ mount_info.options }}' + state: mounted + fstype: '{{ mount_info.fstype }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - (device_name.stdout is defined and (device_name.stdout | length > 0)) or ("--fstab" + | length == 0) + tags: + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_boot_noauto + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +function perform_remediation { + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" "/boot")" + + grep "$mount_point_match_regexp" -q /etc/fstab \ + || { echo "The mount point '/boot' is not even in /etc/fstab, so we can't set up mount options" >&2; + echo "Not remediating, because there is no record of /boot in /etc/fstab" >&2; return 1; } + + + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" /boot)" + + # If the mount point is not in /etc/fstab, get previous mount options from /etc/mtab + if [ "$(grep -c "$mount_point_match_regexp" /etc/fstab)" -eq 0 ]; then + # runtime opts without some automatic kernel/userspace-added defaults + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/mtab | head -1 | awk '{print $4}' \ + | sed -E "s/(rw|defaults|seclabel|noauto)(,|$)//g;s/,$//") + [ "$previous_mount_opts" ] && previous_mount_opts+="," + echo " /boot defaults,${previous_mount_opts}noauto 0 0" >> /etc/fstab + # If the mount_opt option is not already in the mount point's /etc/fstab entry, add it + elif [ "$(grep "$mount_point_match_regexp" /etc/fstab | grep -c "noauto")" -eq 0 ]; then + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/fstab | awk '{print $4}') + sed -i "s|\(${mount_point_match_regexp}.*${previous_mount_opts}\)|\1,noauto|" /etc/fstab + fi + + + if mkdir -p "/boot"; then + if mountpoint -q "/boot"; then + mount -o remount --target "/boot" + else + mount --target "/boot" + fi + fi +} + +perform_remediation + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Add nodev Option to /boot + The nodev mount option can be used to prevent device files from +being created in /boot. +Legitimate character and block devices should exist only in +the /dev directory on the root partition or within chroot +jails built for system services. +Add the nodev option to the fourth column of +/etc/fstab for the line which controls mounting of +/boot. + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + AC-6 + AC-6(1) + MP-7 + PR.IP-1 + PR.PT-2 + PR.PT-3 + SRG-OS-000368-GPOS-00154 + The only legitimate location for device files is the /dev directory +located on the root partition. The only exception to this is chroot jails. + + CCE-83884-7 + +part /boot --mountoptions="nodev" + + - name: 'Add nodev Option to /boot: Check information associated to mountpoint' + command: findmnt --fstab '/boot' + register: device_name + failed_when: device_name.rc > 1 + changed_when: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83884-7 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_boot_nodev + - no_reboot_needed + +- name: 'Add nodev Option to /boot: Create mount_info dictionary variable' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - '{{ device_name.stdout_lines[0].split() | list | lower }}' + - '{{ device_name.stdout_lines[1].split() | list }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - device_name.stdout is defined and device_name.stdout_lines is defined + - (device_name.stdout | length > 0) + tags: + - CCE-83884-7 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_boot_nodev + - no_reboot_needed + +- name: 'Add nodev Option to /boot: If /boot not mounted, craft mount_info manually' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - - target + - source + - fstype + - options + - - /boot + - '' + - '' + - defaults + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ("--fstab" | length == 0) + - (device_name.stdout | length == 0) + tags: + - CCE-83884-7 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_boot_nodev + - no_reboot_needed + +- name: 'Add nodev Option to /boot: Make sure nodev option is part of the to /boot + options' + set_fact: + mount_info: '{{ mount_info | combine( {''options'':''''~mount_info.options~'',nodev'' + }) }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - mount_info is defined and "nodev" not in mount_info.options + tags: + - CCE-83884-7 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_boot_nodev + - no_reboot_needed + +- name: 'Add nodev Option to /boot: Ensure /boot is mounted with nodev option' + mount: + path: /boot + src: '{{ mount_info.source }}' + opts: '{{ mount_info.options }}' + state: mounted + fstype: '{{ mount_info.fstype }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - (device_name.stdout is defined and (device_name.stdout | length > 0)) or ("--fstab" + | length == 0) + tags: + - CCE-83884-7 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_boot_nodev + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +function perform_remediation { + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" "/boot")" + + grep "$mount_point_match_regexp" -q /etc/fstab \ + || { echo "The mount point '/boot' is not even in /etc/fstab, so we can't set up mount options" >&2; + echo "Not remediating, because there is no record of /boot in /etc/fstab" >&2; return 1; } + + + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" /boot)" + + # If the mount point is not in /etc/fstab, get previous mount options from /etc/mtab + if [ "$(grep -c "$mount_point_match_regexp" /etc/fstab)" -eq 0 ]; then + # runtime opts without some automatic kernel/userspace-added defaults + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/mtab | head -1 | awk '{print $4}' \ + | sed -E "s/(rw|defaults|seclabel|nodev)(,|$)//g;s/,$//") + [ "$previous_mount_opts" ] && previous_mount_opts+="," + echo " /boot defaults,${previous_mount_opts}nodev 0 0" >> /etc/fstab + # If the mount_opt option is not already in the mount point's /etc/fstab entry, add it + elif [ "$(grep "$mount_point_match_regexp" /etc/fstab | grep -c "nodev")" -eq 0 ]; then + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/fstab | awk '{print $4}') + sed -i "s|\(${mount_point_match_regexp}.*${previous_mount_opts}\)|\1,nodev|" /etc/fstab + fi + + + if mkdir -p "/boot"; then + if mountpoint -q "/boot"; then + mount -o remount --target "/boot" + else + mount --target "/boot" + fi + fi +} + +perform_remediation + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Add noexec Option to /boot + The noexec mount option can be used to prevent binaries from being +executed out of /boot. +Add the noexec option to the fourth column of +/etc/fstab for the line which controls mounting of +/boot. + BP28(R12) + The /boot partition contains the kernel and the bootloader. No +binaries should be executed from this partition after the booting process +finishes. + + CCE-83892-0 + +part /boot --mountoptions="noexec" + + - name: 'Add noexec Option to /boot: Check information associated to mountpoint' + command: findmnt --fstab '/boot' + register: device_name + failed_when: device_name.rc > 1 + changed_when: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83892-0 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_boot_noexec + - no_reboot_needed + +- name: 'Add noexec Option to /boot: Create mount_info dictionary variable' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - '{{ device_name.stdout_lines[0].split() | list | lower }}' + - '{{ device_name.stdout_lines[1].split() | list }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - device_name.stdout is defined and device_name.stdout_lines is defined + - (device_name.stdout | length > 0) + tags: + - CCE-83892-0 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_boot_noexec + - no_reboot_needed + +- name: 'Add noexec Option to /boot: If /boot not mounted, craft mount_info manually' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - - target + - source + - fstype + - options + - - /boot + - '' + - '' + - defaults + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ("--fstab" | length == 0) + - (device_name.stdout | length == 0) + tags: + - CCE-83892-0 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_boot_noexec + - no_reboot_needed + +- name: 'Add noexec Option to /boot: Make sure noexec option is part of the to /boot + options' + set_fact: + mount_info: '{{ mount_info | combine( {''options'':''''~mount_info.options~'',noexec'' + }) }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - mount_info is defined and "noexec" not in mount_info.options + tags: + - CCE-83892-0 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_boot_noexec + - no_reboot_needed + +- name: 'Add noexec Option to /boot: Ensure /boot is mounted with noexec option' + mount: + path: /boot + src: '{{ mount_info.source }}' + opts: '{{ mount_info.options }}' + state: mounted + fstype: '{{ mount_info.fstype }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - (device_name.stdout is defined and (device_name.stdout | length > 0)) or ("--fstab" + | length == 0) + tags: + - CCE-83892-0 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_boot_noexec + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +function perform_remediation { + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" "/boot")" + + grep "$mount_point_match_regexp" -q /etc/fstab \ + || { echo "The mount point '/boot' is not even in /etc/fstab, so we can't set up mount options" >&2; + echo "Not remediating, because there is no record of /boot in /etc/fstab" >&2; return 1; } + + + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" /boot)" + + # If the mount point is not in /etc/fstab, get previous mount options from /etc/mtab + if [ "$(grep -c "$mount_point_match_regexp" /etc/fstab)" -eq 0 ]; then + # runtime opts without some automatic kernel/userspace-added defaults + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/mtab | head -1 | awk '{print $4}' \ + | sed -E "s/(rw|defaults|seclabel|noexec)(,|$)//g;s/,$//") + [ "$previous_mount_opts" ] && previous_mount_opts+="," + echo " /boot defaults,${previous_mount_opts}noexec 0 0" >> /etc/fstab + # If the mount_opt option is not already in the mount point's /etc/fstab entry, add it + elif [ "$(grep "$mount_point_match_regexp" /etc/fstab | grep -c "noexec")" -eq 0 ]; then + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/fstab | awk '{print $4}') + sed -i "s|\(${mount_point_match_regexp}.*${previous_mount_opts}\)|\1,noexec|" /etc/fstab + fi + + + if mkdir -p "/boot"; then + if mountpoint -q "/boot"; then + mount -o remount --target "/boot" + else + mount --target "/boot" + fi + fi +} + +perform_remediation + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Add nosuid Option to /boot + The nosuid mount option can be used to prevent +execution of setuid programs in /boot. The SUID and SGID permissions +should not be required on the boot partition. +Add the nosuid option to the fourth column of +/etc/fstab for the line which controls mounting of +/boot. + BP28(R12) + CCI-000366 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + AC-6 + AC-6(1) + MP-7 + PR.IP-1 + PR.PT-2 + PR.PT-3 + SRG-OS-000368-GPOS-00154 + SRG-OS-000480-GPOS-00227 + The presence of SUID and SGID executables should be tightly controlled. Users +should not be able to execute SUID or SGID binaries from boot partitions. + + CCE-83877-1 + +part /boot --mountoptions="nosuid" + + - name: 'Add nosuid Option to /boot: Check information associated to mountpoint' + command: findmnt --fstab '/boot' + register: device_name + failed_when: device_name.rc > 1 + changed_when: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83877-1 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_boot_nosuid + - no_reboot_needed + +- name: 'Add nosuid Option to /boot: Create mount_info dictionary variable' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - '{{ device_name.stdout_lines[0].split() | list | lower }}' + - '{{ device_name.stdout_lines[1].split() | list }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - device_name.stdout is defined and device_name.stdout_lines is defined + - (device_name.stdout | length > 0) + tags: + - CCE-83877-1 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_boot_nosuid + - no_reboot_needed + +- name: 'Add nosuid Option to /boot: If /boot not mounted, craft mount_info manually' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - - target + - source + - fstype + - options + - - /boot + - '' + - '' + - defaults + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ("--fstab" | length == 0) + - (device_name.stdout | length == 0) + tags: + - CCE-83877-1 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_boot_nosuid + - no_reboot_needed + +- name: 'Add nosuid Option to /boot: Make sure nosuid option is part of the to /boot + options' + set_fact: + mount_info: '{{ mount_info | combine( {''options'':''''~mount_info.options~'',nosuid'' + }) }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - mount_info is defined and "nosuid" not in mount_info.options + tags: + - CCE-83877-1 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_boot_nosuid + - no_reboot_needed + +- name: 'Add nosuid Option to /boot: Ensure /boot is mounted with nosuid option' + mount: + path: /boot + src: '{{ mount_info.source }}' + opts: '{{ mount_info.options }}' + state: mounted + fstype: '{{ mount_info.fstype }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - (device_name.stdout is defined and (device_name.stdout | length > 0)) or ("--fstab" + | length == 0) + tags: + - CCE-83877-1 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_boot_nosuid + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +function perform_remediation { + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" "/boot")" + + grep "$mount_point_match_regexp" -q /etc/fstab \ + || { echo "The mount point '/boot' is not even in /etc/fstab, so we can't set up mount options" >&2; + echo "Not remediating, because there is no record of /boot in /etc/fstab" >&2; return 1; } + + + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" /boot)" + + # If the mount point is not in /etc/fstab, get previous mount options from /etc/mtab + if [ "$(grep -c "$mount_point_match_regexp" /etc/fstab)" -eq 0 ]; then + # runtime opts without some automatic kernel/userspace-added defaults + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/mtab | head -1 | awk '{print $4}' \ + | sed -E "s/(rw|defaults|seclabel|nosuid)(,|$)//g;s/,$//") + [ "$previous_mount_opts" ] && previous_mount_opts+="," + echo " /boot defaults,${previous_mount_opts}nosuid 0 0" >> /etc/fstab + # If the mount_opt option is not already in the mount point's /etc/fstab entry, add it + elif [ "$(grep "$mount_point_match_regexp" /etc/fstab | grep -c "nosuid")" -eq 0 ]; then + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/fstab | awk '{print $4}') + sed -i "s|\(${mount_point_match_regexp}.*${previous_mount_opts}\)|\1,nosuid|" /etc/fstab + fi + + + if mkdir -p "/boot"; then + if mountpoint -q "/boot"; then + mount -o remount --target "/boot" + else + mount --target "/boot" + fi + fi +} + +perform_remediation + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Add nodev Option to /dev/shm + The nodev mount option can be used to prevent creation of device +files in /dev/shm. Legitimate character and block devices should +not exist within temporary directories like /dev/shm. +Add the nodev option to the fourth column of +/etc/fstab for the line which controls mounting of +/dev/shm. + 11 + 13 + 14 + 3 + 8 + 9 + APO13.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS05.06 + DSS06.06 + CCI-001764 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.11.2.9 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.8.2.1 + A.8.2.2 + A.8.2.3 + A.8.3.1 + A.8.3.3 + A.9.1.2 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + AC-6 + AC-6(1) + MP-7 + PR.IP-1 + PR.PT-2 + PR.PT-3 + SRG-OS-000368-GPOS-00154 + The only legitimate location for device files is the /dev directory +located on the root partition. The only exception to this is chroot jails. + + CCE-83881-3 + - name: 'Add nodev Option to /dev/shm: Check information associated to mountpoint' + command: findmnt '/dev/shm' + register: device_name + failed_when: device_name.rc > 1 + changed_when: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83881-3 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_dev_shm_nodev + - no_reboot_needed + +- name: 'Add nodev Option to /dev/shm: Create mount_info dictionary variable' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - '{{ device_name.stdout_lines[0].split() | list | lower }}' + - '{{ device_name.stdout_lines[1].split() | list }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - device_name.stdout is defined and device_name.stdout_lines is defined + - (device_name.stdout | length > 0) + tags: + - CCE-83881-3 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_dev_shm_nodev + - no_reboot_needed + +- name: 'Add nodev Option to /dev/shm: If /dev/shm not mounted, craft mount_info manually' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - - target + - source + - fstype + - options + - - /dev/shm + - tmpfs + - tmpfs + - defaults + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ("" | length == 0) + - (device_name.stdout | length == 0) + tags: + - CCE-83881-3 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_dev_shm_nodev + - no_reboot_needed + +- name: 'Add nodev Option to /dev/shm: Make sure nodev option is part of the to /dev/shm + options' + set_fact: + mount_info: '{{ mount_info | combine( {''options'':''''~mount_info.options~'',nodev'' + }) }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - mount_info is defined and "nodev" not in mount_info.options + tags: + - CCE-83881-3 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_dev_shm_nodev + - no_reboot_needed + +- name: 'Add nodev Option to /dev/shm: Ensure /dev/shm is mounted with nodev option' + mount: + path: /dev/shm + src: '{{ mount_info.source }}' + opts: '{{ mount_info.options }}' + state: mounted + fstype: '{{ mount_info.fstype }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - (device_name.stdout is defined and (device_name.stdout | length > 0)) or ("" | + length == 0) + tags: + - CCE-83881-3 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_dev_shm_nodev + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +function perform_remediation { + + + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" /dev/shm)" + + # If the mount point is not in /etc/fstab, get previous mount options from /etc/mtab + if [ "$(grep -c "$mount_point_match_regexp" /etc/fstab)" -eq 0 ]; then + # runtime opts without some automatic kernel/userspace-added defaults + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/mtab | head -1 | awk '{print $4}' \ + | sed -E "s/(rw|defaults|seclabel|nodev)(,|$)//g;s/,$//") + [ "$previous_mount_opts" ] && previous_mount_opts+="," + echo "tmpfs /dev/shm tmpfs defaults,${previous_mount_opts}nodev 0 0" >> /etc/fstab + # If the mount_opt option is not already in the mount point's /etc/fstab entry, add it + elif [ "$(grep "$mount_point_match_regexp" /etc/fstab | grep -c "nodev")" -eq 0 ]; then + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/fstab | awk '{print $4}') + sed -i "s|\(${mount_point_match_regexp}.*${previous_mount_opts}\)|\1,nodev|" /etc/fstab + fi + + + if mkdir -p "/dev/shm"; then + if mountpoint -q "/dev/shm"; then + mount -o remount --target "/dev/shm" + else + mount --target "/dev/shm" + fi + fi +} + +perform_remediation + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Add noexec Option to /dev/shm + The noexec mount option can be used to prevent binaries +from being executed out of /dev/shm. +It can be dangerous to allow the execution of binaries +from world-writable temporary storage directories such as /dev/shm. +Add the noexec option to the fourth column of +/etc/fstab for the line which controls mounting of +/dev/shm. + 11 + 13 + 14 + 3 + 8 + 9 + APO13.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS05.06 + DSS06.06 + CCI-001764 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.11.2.9 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.8.2.1 + A.8.2.2 + A.8.2.3 + A.8.3.1 + A.8.3.3 + A.9.1.2 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + AC-6 + AC-6(1) + MP-7 + PR.IP-1 + PR.PT-2 + PR.PT-3 + SRG-OS-000368-GPOS-00154 + Allowing users to execute binaries from world-writable directories +such as /dev/shm can expose the system to potential compromise. + + CCE-83857-3 + - name: 'Add noexec Option to /dev/shm: Check information associated to mountpoint' + command: findmnt '/dev/shm' + register: device_name + failed_when: device_name.rc > 1 + changed_when: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83857-3 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_dev_shm_noexec + - no_reboot_needed + +- name: 'Add noexec Option to /dev/shm: Create mount_info dictionary variable' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - '{{ device_name.stdout_lines[0].split() | list | lower }}' + - '{{ device_name.stdout_lines[1].split() | list }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - device_name.stdout is defined and device_name.stdout_lines is defined + - (device_name.stdout | length > 0) + tags: + - CCE-83857-3 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_dev_shm_noexec + - no_reboot_needed + +- name: 'Add noexec Option to /dev/shm: If /dev/shm not mounted, craft mount_info + manually' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - - target + - source + - fstype + - options + - - /dev/shm + - tmpfs + - tmpfs + - defaults + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ("" | length == 0) + - (device_name.stdout | length == 0) + tags: + - CCE-83857-3 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_dev_shm_noexec + - no_reboot_needed + +- name: 'Add noexec Option to /dev/shm: Make sure noexec option is part of the to + /dev/shm options' + set_fact: + mount_info: '{{ mount_info | combine( {''options'':''''~mount_info.options~'',noexec'' + }) }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - mount_info is defined and "noexec" not in mount_info.options + tags: + - CCE-83857-3 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_dev_shm_noexec + - no_reboot_needed + +- name: 'Add noexec Option to /dev/shm: Ensure /dev/shm is mounted with noexec option' + mount: + path: /dev/shm + src: '{{ mount_info.source }}' + opts: '{{ mount_info.options }}' + state: mounted + fstype: '{{ mount_info.fstype }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - (device_name.stdout is defined and (device_name.stdout | length > 0)) or ("" | + length == 0) + tags: + - CCE-83857-3 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_dev_shm_noexec + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +function perform_remediation { + + + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" /dev/shm)" + + # If the mount point is not in /etc/fstab, get previous mount options from /etc/mtab + if [ "$(grep -c "$mount_point_match_regexp" /etc/fstab)" -eq 0 ]; then + # runtime opts without some automatic kernel/userspace-added defaults + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/mtab | head -1 | awk '{print $4}' \ + | sed -E "s/(rw|defaults|seclabel|noexec)(,|$)//g;s/,$//") + [ "$previous_mount_opts" ] && previous_mount_opts+="," + echo "tmpfs /dev/shm tmpfs defaults,${previous_mount_opts}noexec 0 0" >> /etc/fstab + # If the mount_opt option is not already in the mount point's /etc/fstab entry, add it + elif [ "$(grep "$mount_point_match_regexp" /etc/fstab | grep -c "noexec")" -eq 0 ]; then + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/fstab | awk '{print $4}') + sed -i "s|\(${mount_point_match_regexp}.*${previous_mount_opts}\)|\1,noexec|" /etc/fstab + fi + + + if mkdir -p "/dev/shm"; then + if mountpoint -q "/dev/shm"; then + mount -o remount --target "/dev/shm" + else + mount --target "/dev/shm" + fi + fi +} + +perform_remediation + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Add nosuid Option to /dev/shm + The nosuid mount option can be used to prevent execution +of setuid programs in /dev/shm. The SUID and SGID permissions should not +be required in these world-writable directories. +Add the nosuid option to the fourth column of +/etc/fstab for the line which controls mounting of +/dev/shm. + 11 + 13 + 14 + 3 + 8 + 9 + APO13.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS05.06 + DSS06.06 + CCI-001764 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.11.2.9 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.8.2.1 + A.8.2.2 + A.8.2.3 + A.8.3.1 + A.8.3.3 + A.9.1.2 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + AC-6 + AC-6(1) + MP-7 + PR.IP-1 + PR.PT-2 + PR.PT-3 + SRG-OS-000368-GPOS-00154 + The presence of SUID and SGID executables should be tightly controlled. Users +should not be able to execute SUID or SGID binaries from temporary storage partitions. + + CCE-83891-2 + - name: 'Add nosuid Option to /dev/shm: Check information associated to mountpoint' + command: findmnt '/dev/shm' + register: device_name + failed_when: device_name.rc > 1 + changed_when: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83891-2 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_dev_shm_nosuid + - no_reboot_needed + +- name: 'Add nosuid Option to /dev/shm: Create mount_info dictionary variable' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - '{{ device_name.stdout_lines[0].split() | list | lower }}' + - '{{ device_name.stdout_lines[1].split() | list }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - device_name.stdout is defined and device_name.stdout_lines is defined + - (device_name.stdout | length > 0) + tags: + - CCE-83891-2 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_dev_shm_nosuid + - no_reboot_needed + +- name: 'Add nosuid Option to /dev/shm: If /dev/shm not mounted, craft mount_info + manually' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - - target + - source + - fstype + - options + - - /dev/shm + - tmpfs + - tmpfs + - defaults + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ("" | length == 0) + - (device_name.stdout | length == 0) + tags: + - CCE-83891-2 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_dev_shm_nosuid + - no_reboot_needed + +- name: 'Add nosuid Option to /dev/shm: Make sure nosuid option is part of the to + /dev/shm options' + set_fact: + mount_info: '{{ mount_info | combine( {''options'':''''~mount_info.options~'',nosuid'' + }) }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - mount_info is defined and "nosuid" not in mount_info.options + tags: + - CCE-83891-2 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_dev_shm_nosuid + - no_reboot_needed + +- name: 'Add nosuid Option to /dev/shm: Ensure /dev/shm is mounted with nosuid option' + mount: + path: /dev/shm + src: '{{ mount_info.source }}' + opts: '{{ mount_info.options }}' + state: mounted + fstype: '{{ mount_info.fstype }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - (device_name.stdout is defined and (device_name.stdout | length > 0)) or ("" | + length == 0) + tags: + - CCE-83891-2 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_dev_shm_nosuid + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +function perform_remediation { + + + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" /dev/shm)" + + # If the mount point is not in /etc/fstab, get previous mount options from /etc/mtab + if [ "$(grep -c "$mount_point_match_regexp" /etc/fstab)" -eq 0 ]; then + # runtime opts without some automatic kernel/userspace-added defaults + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/mtab | head -1 | awk '{print $4}' \ + | sed -E "s/(rw|defaults|seclabel|nosuid)(,|$)//g;s/,$//") + [ "$previous_mount_opts" ] && previous_mount_opts+="," + echo "tmpfs /dev/shm tmpfs defaults,${previous_mount_opts}nosuid 0 0" >> /etc/fstab + # If the mount_opt option is not already in the mount point's /etc/fstab entry, add it + elif [ "$(grep "$mount_point_match_regexp" /etc/fstab | grep -c "nosuid")" -eq 0 ]; then + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/fstab | awk '{print $4}') + sed -i "s|\(${mount_point_match_regexp}.*${previous_mount_opts}\)|\1,nosuid|" /etc/fstab + fi + + + if mkdir -p "/dev/shm"; then + if mountpoint -q "/dev/shm"; then + mount -o remount --target "/dev/shm" + else + mount --target "/dev/shm" + fi + fi +} + +perform_remediation + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Add nodev Option to /home + The nodev mount option can be used to prevent device files from +being created in /home. +Legitimate character and block devices should exist only in +the /dev directory on the root partition or within chroot +jails built for system services. +Add the nodev option to the fourth column of +/etc/fstab for the line which controls mounting of +/home. + BP28(R12) + SRG-OS-000368-GPOS-00154 + The only legitimate location for device files is the /dev directory +located on the root partition. The only exception to this is chroot jails. + + CCE-83871-4 + +part /home --mountoptions="nodev" + + - name: 'Add nodev Option to /home: Check information associated to mountpoint' + command: findmnt --fstab '/home' + register: device_name + failed_when: device_name.rc > 1 + changed_when: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83871-4 + - configure_strategy + - high_disruption + - low_complexity + - mount_option_home_nodev + - no_reboot_needed + - unknown_severity + +- name: 'Add nodev Option to /home: Create mount_info dictionary variable' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - '{{ device_name.stdout_lines[0].split() | list | lower }}' + - '{{ device_name.stdout_lines[1].split() | list }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - device_name.stdout is defined and device_name.stdout_lines is defined + - (device_name.stdout | length > 0) + tags: + - CCE-83871-4 + - configure_strategy + - high_disruption + - low_complexity + - mount_option_home_nodev + - no_reboot_needed + - unknown_severity + +- name: 'Add nodev Option to /home: If /home not mounted, craft mount_info manually' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - - target + - source + - fstype + - options + - - /home + - '' + - '' + - defaults + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ("--fstab" | length == 0) + - (device_name.stdout | length == 0) + tags: + - CCE-83871-4 + - configure_strategy + - high_disruption + - low_complexity + - mount_option_home_nodev + - no_reboot_needed + - unknown_severity + +- name: 'Add nodev Option to /home: Make sure nodev option is part of the to /home + options' + set_fact: + mount_info: '{{ mount_info | combine( {''options'':''''~mount_info.options~'',nodev'' + }) }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - mount_info is defined and "nodev" not in mount_info.options + tags: + - CCE-83871-4 + - configure_strategy + - high_disruption + - low_complexity + - mount_option_home_nodev + - no_reboot_needed + - unknown_severity + +- name: 'Add nodev Option to /home: Ensure /home is mounted with nodev option' + mount: + path: /home + src: '{{ mount_info.source }}' + opts: '{{ mount_info.options }}' + state: mounted + fstype: '{{ mount_info.fstype }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - (device_name.stdout is defined and (device_name.stdout | length > 0)) or ("--fstab" + | length == 0) + tags: + - CCE-83871-4 + - configure_strategy + - high_disruption + - low_complexity + - mount_option_home_nodev + - no_reboot_needed + - unknown_severity + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +function perform_remediation { + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" "/home")" + + grep "$mount_point_match_regexp" -q /etc/fstab \ + || { echo "The mount point '/home' is not even in /etc/fstab, so we can't set up mount options" >&2; + echo "Not remediating, because there is no record of /home in /etc/fstab" >&2; return 1; } + + + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" /home)" + + # If the mount point is not in /etc/fstab, get previous mount options from /etc/mtab + if [ "$(grep -c "$mount_point_match_regexp" /etc/fstab)" -eq 0 ]; then + # runtime opts without some automatic kernel/userspace-added defaults + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/mtab | head -1 | awk '{print $4}' \ + | sed -E "s/(rw|defaults|seclabel|nodev)(,|$)//g;s/,$//") + [ "$previous_mount_opts" ] && previous_mount_opts+="," + echo " /home defaults,${previous_mount_opts}nodev 0 0" >> /etc/fstab + # If the mount_opt option is not already in the mount point's /etc/fstab entry, add it + elif [ "$(grep "$mount_point_match_regexp" /etc/fstab | grep -c "nodev")" -eq 0 ]; then + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/fstab | awk '{print $4}') + sed -i "s|\(${mount_point_match_regexp}.*${previous_mount_opts}\)|\1,nodev|" /etc/fstab + fi + + + if mkdir -p "/home"; then + if mountpoint -q "/home"; then + mount -o remount --target "/home" + else + mount --target "/home" + fi + fi +} + +perform_remediation + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Add noexec Option to /home + The noexec mount option can be used to prevent binaries from being +executed out of /home. +Add the noexec option to the fourth column of +/etc/fstab for the line which controls mounting of +/home. + BP28(R12) + CCI-000366 + CM-6(b) + SRG-OS-000480-GPOS-00227 + The /home directory contains data of individual users. Binaries in +this directory should not be considered as trusted and users should not be +able to execute them. + + CCE-83875-5 + +part /home --mountoptions="noexec" + + - name: 'Add noexec Option to /home: Check information associated to mountpoint' + command: findmnt --fstab '/home' + register: device_name + failed_when: device_name.rc > 1 + changed_when: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83875-5 + - NIST-800-53-CM-6(b) + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_home_noexec + - no_reboot_needed + +- name: 'Add noexec Option to /home: Create mount_info dictionary variable' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - '{{ device_name.stdout_lines[0].split() | list | lower }}' + - '{{ device_name.stdout_lines[1].split() | list }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - device_name.stdout is defined and device_name.stdout_lines is defined + - (device_name.stdout | length > 0) + tags: + - CCE-83875-5 + - NIST-800-53-CM-6(b) + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_home_noexec + - no_reboot_needed + +- name: 'Add noexec Option to /home: If /home not mounted, craft mount_info manually' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - - target + - source + - fstype + - options + - - /home + - '' + - '' + - defaults + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ("--fstab" | length == 0) + - (device_name.stdout | length == 0) + tags: + - CCE-83875-5 + - NIST-800-53-CM-6(b) + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_home_noexec + - no_reboot_needed + +- name: 'Add noexec Option to /home: Make sure noexec option is part of the to /home + options' + set_fact: + mount_info: '{{ mount_info | combine( {''options'':''''~mount_info.options~'',noexec'' + }) }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - mount_info is defined and "noexec" not in mount_info.options + tags: + - CCE-83875-5 + - NIST-800-53-CM-6(b) + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_home_noexec + - no_reboot_needed + +- name: 'Add noexec Option to /home: Ensure /home is mounted with noexec option' + mount: + path: /home + src: '{{ mount_info.source }}' + opts: '{{ mount_info.options }}' + state: mounted + fstype: '{{ mount_info.fstype }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - (device_name.stdout is defined and (device_name.stdout | length > 0)) or ("--fstab" + | length == 0) + tags: + - CCE-83875-5 + - NIST-800-53-CM-6(b) + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_home_noexec + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +function perform_remediation { + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" "/home")" + + grep "$mount_point_match_regexp" -q /etc/fstab \ + || { echo "The mount point '/home' is not even in /etc/fstab, so we can't set up mount options" >&2; + echo "Not remediating, because there is no record of /home in /etc/fstab" >&2; return 1; } + + + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" /home)" + + # If the mount point is not in /etc/fstab, get previous mount options from /etc/mtab + if [ "$(grep -c "$mount_point_match_regexp" /etc/fstab)" -eq 0 ]; then + # runtime opts without some automatic kernel/userspace-added defaults + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/mtab | head -1 | awk '{print $4}' \ + | sed -E "s/(rw|defaults|seclabel|noexec)(,|$)//g;s/,$//") + [ "$previous_mount_opts" ] && previous_mount_opts+="," + echo " /home defaults,${previous_mount_opts}noexec 0 0" >> /etc/fstab + # If the mount_opt option is not already in the mount point's /etc/fstab entry, add it + elif [ "$(grep "$mount_point_match_regexp" /etc/fstab | grep -c "noexec")" -eq 0 ]; then + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/fstab | awk '{print $4}') + sed -i "s|\(${mount_point_match_regexp}.*${previous_mount_opts}\)|\1,noexec|" /etc/fstab + fi + + + if mkdir -p "/home"; then + if mountpoint -q "/home"; then + mount -o remount --target "/home" + else + mount --target "/home" + fi + fi +} + +perform_remediation + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Add nosuid Option to /home + The nosuid mount option can be used to prevent +execution of setuid programs in /home. The SUID and SGID permissions +should not be required in these user data directories. +Add the nosuid option to the fourth column of +/etc/fstab for the line which controls mounting of +/home. + BP28(R12) + 11 + 13 + 14 + 3 + 8 + 9 + APO13.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS05.06 + DSS06.06 + CCI-000366 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.11.2.9 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.8.2.1 + A.8.2.2 + A.8.2.3 + A.8.3.1 + A.8.3.3 + A.9.1.2 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + AC-6 + AC-6(1) + MP-7 + PR.IP-1 + PR.PT-2 + PR.PT-3 + SRG-OS-000368-GPOS-00154 + SRG-OS-000480-GPOS-00227 + The presence of SUID and SGID executables should be tightly controlled. Users +should not be able to execute SUID or SGID binaries from user home directory partitions. + + CCE-83894-6 + +part /home --mountoptions="nosuid" + + - name: 'Add nosuid Option to /home: Check information associated to mountpoint' + command: findmnt --fstab '/home' + register: device_name + failed_when: device_name.rc > 1 + changed_when: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83894-6 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_home_nosuid + - no_reboot_needed + +- name: 'Add nosuid Option to /home: Create mount_info dictionary variable' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - '{{ device_name.stdout_lines[0].split() | list | lower }}' + - '{{ device_name.stdout_lines[1].split() | list }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - device_name.stdout is defined and device_name.stdout_lines is defined + - (device_name.stdout | length > 0) + tags: + - CCE-83894-6 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_home_nosuid + - no_reboot_needed + +- name: 'Add nosuid Option to /home: If /home not mounted, craft mount_info manually' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - - target + - source + - fstype + - options + - - /home + - '' + - '' + - defaults + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ("--fstab" | length == 0) + - (device_name.stdout | length == 0) + tags: + - CCE-83894-6 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_home_nosuid + - no_reboot_needed + +- name: 'Add nosuid Option to /home: Make sure nosuid option is part of the to /home + options' + set_fact: + mount_info: '{{ mount_info | combine( {''options'':''''~mount_info.options~'',nosuid'' + }) }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - mount_info is defined and "nosuid" not in mount_info.options + tags: + - CCE-83894-6 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_home_nosuid + - no_reboot_needed + +- name: 'Add nosuid Option to /home: Ensure /home is mounted with nosuid option' + mount: + path: /home + src: '{{ mount_info.source }}' + opts: '{{ mount_info.options }}' + state: mounted + fstype: '{{ mount_info.fstype }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - (device_name.stdout is defined and (device_name.stdout | length > 0)) or ("--fstab" + | length == 0) + tags: + - CCE-83894-6 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_home_nosuid + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +function perform_remediation { + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" "/home")" + + grep "$mount_point_match_regexp" -q /etc/fstab \ + || { echo "The mount point '/home' is not even in /etc/fstab, so we can't set up mount options" >&2; + echo "Not remediating, because there is no record of /home in /etc/fstab" >&2; return 1; } + + + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" /home)" + + # If the mount point is not in /etc/fstab, get previous mount options from /etc/mtab + if [ "$(grep -c "$mount_point_match_regexp" /etc/fstab)" -eq 0 ]; then + # runtime opts without some automatic kernel/userspace-added defaults + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/mtab | head -1 | awk '{print $4}' \ + | sed -E "s/(rw|defaults|seclabel|nosuid)(,|$)//g;s/,$//") + [ "$previous_mount_opts" ] && previous_mount_opts+="," + echo " /home defaults,${previous_mount_opts}nosuid 0 0" >> /etc/fstab + # If the mount_opt option is not already in the mount point's /etc/fstab entry, add it + elif [ "$(grep "$mount_point_match_regexp" /etc/fstab | grep -c "nosuid")" -eq 0 ]; then + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/fstab | awk '{print $4}') + sed -i "s|\(${mount_point_match_regexp}.*${previous_mount_opts}\)|\1,nosuid|" /etc/fstab + fi + + + if mkdir -p "/home"; then + if mountpoint -q "/home"; then + mount -o remount --target "/home" + else + mount --target "/home" + fi + fi +} + +perform_remediation + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Add nodev Option to Non-Root Local Partitions + The nodev mount option prevents files from being interpreted as +character or block devices. Legitimate character and block devices should +exist only in the /dev directory on the root partition or within +chroot jails built for system services. +Add the nodev option to the fourth column of +/etc/fstab for the line which controls mounting of + + any non-root local partitions. + BP28(R12) + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + CCI-000366 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + AC-6 + AC-6(1) + MP-7 + PR.IP-1 + PR.PT-3 + SRG-OS-000368-GPOS-00154 + SRG-OS-000480-GPOS-00227 + The nodev mount option prevents files from being +interpreted as character or block devices. The only legitimate location +for device files is the /dev directory located on the root partition. +The only exception to this is chroot jails, for which it is not advised +to set nodev on these filesystems. + + CCE-83873-0 + - name: Ensure non-root local partitions are mounted with nodev option + mount: + path: '{{ item.mount }}' + src: '{{ item.device }}' + opts: '{{ item.options }},nodev' + state: mounted + fstype: '{{ item.fstype }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - item.mount is match('/\w') + - item.options is not search('nodev') + with_items: + - '{{ ansible_facts.mounts }}' + tags: + - CCE-83873-0 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_nodev_nonroot_local_partitions + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +MOUNT_OPTION="nodev" +# Create array of local non-root partitions +readarray -t partitions_records < <(findmnt --mtab --raw --evaluate | grep "^/\w" | grep "\s/dev/\w") + +for partition_record in "${partitions_records[@]}"; do + # Get all important information for fstab + mount_point="$(echo ${partition_record} | cut -d " " -f1)" + device="$(echo ${partition_record} | cut -d " " -f2)" + device_type="$(echo ${partition_record} | cut -d " " -f3)" + # device and device_type will be used only in case when the device doesn't have fstab record + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" $mount_point)" + +# If the mount point is not in /etc/fstab, get previous mount options from /etc/mtab +if [ "$(grep -c "$mount_point_match_regexp" /etc/fstab)" -eq 0 ]; then + # runtime opts without some automatic kernel/userspace-added defaults + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/mtab | head -1 | awk '{print $4}' \ + | sed -E "s/(rw|defaults|seclabel|$MOUNT_OPTION)(,|$)//g;s/,$//") + [ "$previous_mount_opts" ] && previous_mount_opts+="," + echo "$device $mount_point $device_type defaults,${previous_mount_opts}$MOUNT_OPTION 0 0" >> /etc/fstab +# If the mount_opt option is not already in the mount point's /etc/fstab entry, add it +elif [ "$(grep "$mount_point_match_regexp" /etc/fstab | grep -c "$MOUNT_OPTION")" -eq 0 ]; then + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/fstab | awk '{print $4}') + sed -i "s|\(${mount_point_match_regexp}.*${previous_mount_opts}\)|\1,$MOUNT_OPTION|" /etc/fstab +fi + if mkdir -p "$mount_point"; then + if mountpoint -q "$mount_point"; then + mount -o remount --target "$mount_point" + else + mount --target "$mount_point" + fi +fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Add nodev Option to Removable Media Partitions + The nodev mount option prevents files from being +interpreted as character or block devices. +Legitimate character and block devices should exist only in +the /dev directory on the root partition or within chroot +jails built for system services. +Add the nodev option to the fourth column of +/etc/fstab for the line which controls mounting of + + any removable media partitions. + 11 + 12 + 13 + 14 + 16 + 3 + 8 + 9 + APO13.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.04 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.06 + DSS05.07 + DSS06.03 + DSS06.06 + CCI-000366 + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.11.2.6 + A.11.2.9 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.13.1.1 + A.13.2.1 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.6.2.1 + A.6.2.2 + A.7.1.1 + A.8.2.1 + A.8.2.2 + A.8.2.3 + A.8.3.1 + A.8.3.3 + A.9.1.2 + A.9.2.1 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + AC-6 + AC-6(1) + MP-7 + PR.AC-3 + PR.AC-6 + PR.IP-1 + PR.PT-2 + PR.PT-3 + SRG-OS-000480-GPOS-00227 + The only legitimate location for device files is the /dev directory +located on the root partition. An exception to this is chroot jails, and it is +not advised to set nodev on partitions which contain their root +filesystems. + + CCE-83856-5 + - name: XCCDF Value var_removable_partition # promote to variable + set_fact: + var_removable_partition: !!str + tags: + - always + +- name: Ensure permission nodev are set on var_removable_partition + lineinfile: + path: /etc/fstab + regexp: ^\s*({{ var_removable_partition }})\s+([^\s]*)\s+([^\s]*)\s+([^\s]*)(.*)$ + backrefs: true + line: \1 \2 \3 \4,nodev \5 + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83856-5 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_nodev_removable_partitions + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_removable_partition='' + + +device_regex="^\s*$var_removable_partition\s\+" +mount_option="nodev" + +if grep -q $device_regex /etc/fstab ; then + previous_opts=$(grep $device_regex /etc/fstab | awk '{print $4}') + sed -i "s|\($device_regex.*$previous_opts\)|\1,$mount_option|" /etc/fstab +else + echo "Not remediating, because there is no record of $var_removable_partition in /etc/fstab" >&2 + return 1 +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Add noexec Option to Removable Media Partitions + The noexec mount option prevents the direct execution of binaries +on the mounted filesystem. Preventing the direct execution of binaries from +removable media (such as a USB key) provides a defense against malicious +software that may be present on such untrusted media. +Add the noexec option to the fourth column of +/etc/fstab for the line which controls mounting of + + any removable media partitions. + 11 + 12 + 13 + 14 + 16 + 3 + 8 + 9 + APO13.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.04 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.06 + DSS05.07 + DSS06.03 + DSS06.06 + CCI-000087 + CCI-000366 + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.11.2.6 + A.11.2.9 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.13.1.1 + A.13.2.1 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.6.2.1 + A.6.2.2 + A.7.1.1 + A.8.2.1 + A.8.2.2 + A.8.2.3 + A.8.3.1 + A.8.3.3 + A.9.1.2 + A.9.2.1 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + AC-6 + AC-6(1) + MP-7 + PR.AC-3 + PR.AC-6 + PR.IP-1 + PR.PT-2 + PR.PT-3 + SRG-OS-000480-GPOS-00227 + Allowing users to execute binaries from removable media such as USB keys exposes +the system to potential compromise. + + CCE-83883-9 + - name: XCCDF Value var_removable_partition # promote to variable + set_fact: + var_removable_partition: !!str + tags: + - always + +- name: Ensure permission noexec are set on var_removable_partition + lineinfile: + path: /etc/fstab + regexp: ^\s*({{ var_removable_partition }})\s+([^\s]*)\s+([^\s]*)\s+([^\s]*)(.*)$ + backrefs: true + line: \1 \2 \3 \4,noexec \5 + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83883-9 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_noexec_removable_partitions + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_removable_partition='' + + +device_regex="^\s*$var_removable_partition\s\+" +mount_option="noexec" + +if grep -q $device_regex /etc/fstab ; then + previous_opts=$(grep $device_regex /etc/fstab | awk '{print $4}') + sed -i "s|\($device_regex.*$previous_opts\)|\1,$mount_option|" /etc/fstab +else + echo "Not remediating, because there is no record of $var_removable_partition in /etc/fstab" >&2 + return 1 +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Add nosuid Option to Removable Media Partitions + The nosuid mount option prevents set-user-identifier (SUID) +and set-group-identifier (SGID) permissions from taking effect. These permissions +allow users to execute binaries with the same permissions as the owner and group +of the file respectively. Users should not be allowed to introduce SUID and SGID +files into the system via partitions mounted from removeable media. +Add the nosuid option to the fourth column of +/etc/fstab for the line which controls mounting of + + any removable media partitions. + 11 + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + 8 + 9 + APO01.06 + APO13.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.04 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.06 + DSS05.07 + DSS06.02 + DSS06.03 + DSS06.06 + CCI-000366 + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 5.2 + SR 7.6 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.11.2.6 + A.11.2.9 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.6.1.2 + A.6.2.1 + A.6.2.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.1 + A.8.2.2 + A.8.2.3 + A.8.3.1 + A.8.3.3 + A.9.1.1 + A.9.1.2 + A.9.2.1 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + AC-6 + AC-6(1) + MP-7 + PR.AC-3 + PR.AC-4 + PR.AC-6 + PR.DS-5 + PR.IP-1 + PR.PT-2 + PR.PT-3 + SRG-OS-000480-GPOS-00227 + The presence of SUID and SGID executables should be tightly controlled. Allowing +users to introduce SUID or SGID binaries from partitions mounted off of +removable media would allow them to introduce their own highly-privileged programs. + + CCE-83874-8 + - name: XCCDF Value var_removable_partition # promote to variable + set_fact: + var_removable_partition: !!str + tags: + - always + +- name: Ensure permission nosuid are set on var_removable_partition + lineinfile: + path: /etc/fstab + regexp: ^\s*({{ var_removable_partition }})\s+([^\s]*)\s+([^\s]*)\s+([^\s]*)(.*)$ + backrefs: true + line: \1 \2 \3 \4,nosuid \5 + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83874-8 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_nosuid_removable_partitions + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_removable_partition='' + + +device_regex="^\s*$var_removable_partition\s\+" +mount_option="nosuid" + +if grep -q $device_regex /etc/fstab ; then + previous_opts=$(grep $device_regex /etc/fstab | awk '{print $4}') + sed -i "s|\($device_regex.*$previous_opts\)|\1,$mount_option|" /etc/fstab +else + echo "Not remediating, because there is no record of $var_removable_partition in /etc/fstab" >&2 + return 1 +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Add nosuid Option to /opt + The nosuid mount option can be used to prevent +execution of setuid programs in /opt. The SUID and SGID permissions +should not be required in this directory. +Add the nosuid option to the fourth column of +/etc/fstab for the line which controls mounting of +/opt. + BP28(R12) + The presence of SUID and SGID executables should be tightly controlled. The +/opt directory contains additional software packages. Users should +not be able to execute SUID or SGID binaries from this directory. + + CCE-83880-5 + +part /opt --mountoptions="nosuid" + + - name: 'Add nosuid Option to /opt: Check information associated to mountpoint' + command: findmnt --fstab '/opt' + register: device_name + failed_when: device_name.rc > 1 + changed_when: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83880-5 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_opt_nosuid + - no_reboot_needed + +- name: 'Add nosuid Option to /opt: Create mount_info dictionary variable' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - '{{ device_name.stdout_lines[0].split() | list | lower }}' + - '{{ device_name.stdout_lines[1].split() | list }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - device_name.stdout is defined and device_name.stdout_lines is defined + - (device_name.stdout | length > 0) + tags: + - CCE-83880-5 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_opt_nosuid + - no_reboot_needed + +- name: 'Add nosuid Option to /opt: If /opt not mounted, craft mount_info manually' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - - target + - source + - fstype + - options + - - /opt + - '' + - '' + - defaults + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ("--fstab" | length == 0) + - (device_name.stdout | length == 0) + tags: + - CCE-83880-5 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_opt_nosuid + - no_reboot_needed + +- name: 'Add nosuid Option to /opt: Make sure nosuid option is part of the to /opt + options' + set_fact: + mount_info: '{{ mount_info | combine( {''options'':''''~mount_info.options~'',nosuid'' + }) }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - mount_info is defined and "nosuid" not in mount_info.options + tags: + - CCE-83880-5 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_opt_nosuid + - no_reboot_needed + +- name: 'Add nosuid Option to /opt: Ensure /opt is mounted with nosuid option' + mount: + path: /opt + src: '{{ mount_info.source }}' + opts: '{{ mount_info.options }}' + state: mounted + fstype: '{{ mount_info.fstype }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - (device_name.stdout is defined and (device_name.stdout | length > 0)) or ("--fstab" + | length == 0) + tags: + - CCE-83880-5 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_opt_nosuid + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +function perform_remediation { + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" "/opt")" + + grep "$mount_point_match_regexp" -q /etc/fstab \ + || { echo "The mount point '/opt' is not even in /etc/fstab, so we can't set up mount options" >&2; + echo "Not remediating, because there is no record of /opt in /etc/fstab" >&2; return 1; } + + + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" /opt)" + + # If the mount point is not in /etc/fstab, get previous mount options from /etc/mtab + if [ "$(grep -c "$mount_point_match_regexp" /etc/fstab)" -eq 0 ]; then + # runtime opts without some automatic kernel/userspace-added defaults + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/mtab | head -1 | awk '{print $4}' \ + | sed -E "s/(rw|defaults|seclabel|nosuid)(,|$)//g;s/,$//") + [ "$previous_mount_opts" ] && previous_mount_opts+="," + echo " /opt defaults,${previous_mount_opts}nosuid 0 0" >> /etc/fstab + # If the mount_opt option is not already in the mount point's /etc/fstab entry, add it + elif [ "$(grep "$mount_point_match_regexp" /etc/fstab | grep -c "nosuid")" -eq 0 ]; then + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/fstab | awk '{print $4}') + sed -i "s|\(${mount_point_match_regexp}.*${previous_mount_opts}\)|\1,nosuid|" /etc/fstab + fi + + + if mkdir -p "/opt"; then + if mountpoint -q "/opt"; then + mount -o remount --target "/opt" + else + mount --target "/opt" + fi + fi +} + +perform_remediation + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Add hidepid Option to /proc + The hidepid mount option is applicable to /proc and is used to +control who can access the information in /proc/[pid] directories. +The option can have one of the following values: + +0: Everybody may access all /proc/[pid] directories. +1: Users may not access files and subdirectories inside any /proc/[pid] directories + but their own. The /proc/[pid] directories themselves remain visible. +2: Same as for mode 1, but in addition the /proc/[pid] directories belonging to other + users become invisible. + +For example, if you choose the value 2: +Add the hidepid=2 option to the fourth column of +/etc/fstab for the line which controls mounting of +/proc. + Hiding the pid of processes may lead to problems with PolicyKit and D-Bus, +it may also convey a false sense of security. + +Proceed to +https://access.redhat.com/solutions/6704531 for more details. + BP28(R12) + Users should not be able to see and access directories within /proc, which are not +related to their own processes in a system. Otherwise, sensitive information from +other users could be seem. + + CCE-85883-7 + - name: XCCDF Value var_mount_option_proc_hidepid # promote to variable + set_fact: + var_mount_option_proc_hidepid: !!str + tags: + - always + +- name: 'Add hidepid Option to /proc: Check information associated to mountpoint' + command: findmnt '/proc' + register: device_name + failed_when: device_name.rc > 1 + changed_when: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-85883-7 + - configure_strategy + - high_disruption + - low_complexity + - low_severity + - mount_option_proc_hidepid + - no_reboot_needed + +- name: 'Add hidepid Option to /proc: Create mount_info dictionary variable' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - '{{ device_name.stdout_lines[0].split() | list | lower }}' + - '{{ device_name.stdout_lines[1].split() | list }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - device_name.stdout is defined and device_name.stdout_lines is defined + - (device_name.stdout | length > 0) + tags: + - CCE-85883-7 + - configure_strategy + - high_disruption + - low_complexity + - low_severity + - mount_option_proc_hidepid + - no_reboot_needed + +- name: 'Add hidepid Option to /proc: If /proc not mounted, craft mount_info manually' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - - target + - source + - fstype + - options + - - /proc + - proc + - proc + - defaults + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ("" | length == 0) + - (device_name.stdout | length == 0) + tags: + - CCE-85883-7 + - configure_strategy + - high_disruption + - low_complexity + - low_severity + - mount_option_proc_hidepid + - no_reboot_needed + +- name: 'Add hidepid Option to /proc: Make sure hidepid option is part of the to /proc + options' + set_fact: + mount_info: '{{ mount_info | combine( {''options'':''''~mount_info.options~'',hidepid=''~var_mount_option_proc_hidepid~'''' + }) }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - mount_info is defined and "hidepid" not in mount_info.options + tags: + - CCE-85883-7 + - configure_strategy + - high_disruption + - low_complexity + - low_severity + - mount_option_proc_hidepid + - no_reboot_needed + +- name: 'Add hidepid Option to /proc: Ensure /proc is mounted with hidepid option' + mount: + path: /proc + src: '{{ mount_info.source }}' + opts: '{{ mount_info.options }}' + state: mounted + fstype: '{{ mount_info.fstype }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - (device_name.stdout is defined and (device_name.stdout | length > 0)) or ("" | + length == 0) + tags: + - CCE-85883-7 + - configure_strategy + - high_disruption + - low_complexity + - low_severity + - mount_option_proc_hidepid + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +function perform_remediation { + + + + var_mount_option_proc_hidepid='' + + mountoption="hidepid=$var_mount_option_proc_hidepid" + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" /proc)" + + # If the mount point is not in /etc/fstab, get previous mount options from /etc/mtab + if [ "$(grep -c "$mount_point_match_regexp" /etc/fstab)" -eq 0 ]; then + # runtime opts without some automatic kernel/userspace-added defaults + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/mtab | head -1 | awk '{print $4}' \ + | sed -E "s/(rw|defaults|seclabel|$mountoption)(,|$)//g;s/,$//") + [ "$previous_mount_opts" ] && previous_mount_opts+="," + echo "proc /proc proc defaults,${previous_mount_opts}$mountoption 0 0" >> /etc/fstab + # If the mount_opt option is not already in the mount point's /etc/fstab entry, add it + elif [ "$(grep "$mount_point_match_regexp" /etc/fstab | grep -c "$mountoption")" -eq 0 ]; then + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/fstab | awk '{print $4}') + sed -i "s|\(${mount_point_match_regexp}.*${previous_mount_opts}\)|\1,$mountoption|" /etc/fstab + fi + + + if mkdir -p "/proc"; then + if mountpoint -q "/proc"; then + mount -o remount --target "/proc" + else + mount --target "/proc" + fi + fi +} + +perform_remediation + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Add nosuid Option to /srv + The nosuid mount option can be used to prevent +execution of setuid programs in /srv. The SUID and SGID permissions +should not be required in this directory. +Add the nosuid option to the fourth column of +/etc/fstab for the line which controls mounting of +/srv. + BP28(R12) + The presence of SUID and SGID executables should be tightly controlled. The +/srv directory contains files served by various network services such as FTP. Users should +not be able to execute SUID or SGID binaries from this directory. + + CCE-83862-3 + +part /srv --mountoptions="nosuid" + + - name: 'Add nosuid Option to /srv: Check information associated to mountpoint' + command: findmnt --fstab '/srv' + register: device_name + failed_when: device_name.rc > 1 + changed_when: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83862-3 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_srv_nosuid + - no_reboot_needed + +- name: 'Add nosuid Option to /srv: Create mount_info dictionary variable' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - '{{ device_name.stdout_lines[0].split() | list | lower }}' + - '{{ device_name.stdout_lines[1].split() | list }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - device_name.stdout is defined and device_name.stdout_lines is defined + - (device_name.stdout | length > 0) + tags: + - CCE-83862-3 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_srv_nosuid + - no_reboot_needed + +- name: 'Add nosuid Option to /srv: If /srv not mounted, craft mount_info manually' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - - target + - source + - fstype + - options + - - /srv + - '' + - '' + - defaults + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ("--fstab" | length == 0) + - (device_name.stdout | length == 0) + tags: + - CCE-83862-3 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_srv_nosuid + - no_reboot_needed + +- name: 'Add nosuid Option to /srv: Make sure nosuid option is part of the to /srv + options' + set_fact: + mount_info: '{{ mount_info | combine( {''options'':''''~mount_info.options~'',nosuid'' + }) }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - mount_info is defined and "nosuid" not in mount_info.options + tags: + - CCE-83862-3 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_srv_nosuid + - no_reboot_needed + +- name: 'Add nosuid Option to /srv: Ensure /srv is mounted with nosuid option' + mount: + path: /srv + src: '{{ mount_info.source }}' + opts: '{{ mount_info.options }}' + state: mounted + fstype: '{{ mount_info.fstype }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - (device_name.stdout is defined and (device_name.stdout | length > 0)) or ("--fstab" + | length == 0) + tags: + - CCE-83862-3 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_srv_nosuid + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +function perform_remediation { + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" "/srv")" + + grep "$mount_point_match_regexp" -q /etc/fstab \ + || { echo "The mount point '/srv' is not even in /etc/fstab, so we can't set up mount options" >&2; + echo "Not remediating, because there is no record of /srv in /etc/fstab" >&2; return 1; } + + + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" /srv)" + + # If the mount point is not in /etc/fstab, get previous mount options from /etc/mtab + if [ "$(grep -c "$mount_point_match_regexp" /etc/fstab)" -eq 0 ]; then + # runtime opts without some automatic kernel/userspace-added defaults + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/mtab | head -1 | awk '{print $4}' \ + | sed -E "s/(rw|defaults|seclabel|nosuid)(,|$)//g;s/,$//") + [ "$previous_mount_opts" ] && previous_mount_opts+="," + echo " /srv defaults,${previous_mount_opts}nosuid 0 0" >> /etc/fstab + # If the mount_opt option is not already in the mount point's /etc/fstab entry, add it + elif [ "$(grep "$mount_point_match_regexp" /etc/fstab | grep -c "nosuid")" -eq 0 ]; then + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/fstab | awk '{print $4}') + sed -i "s|\(${mount_point_match_regexp}.*${previous_mount_opts}\)|\1,nosuid|" /etc/fstab + fi + + + if mkdir -p "/srv"; then + if mountpoint -q "/srv"; then + mount -o remount --target "/srv" + else + mount --target "/srv" + fi + fi +} + +perform_remediation + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Add nodev Option to /tmp + The nodev mount option can be used to prevent device files from +being created in /tmp. Legitimate character and block devices +should not exist within temporary directories like /tmp. +Add the nodev option to the fourth column of +/etc/fstab for the line which controls mounting of +/tmp. + BP28(R12) + 11 + 13 + 14 + 3 + 8 + 9 + APO13.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS05.06 + DSS06.06 + CCI-001764 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.11.2.9 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.8.2.1 + A.8.2.2 + A.8.2.3 + A.8.3.1 + A.8.3.3 + A.9.1.2 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + AC-6 + AC-6(1) + MP-7 + PR.IP-1 + PR.PT-2 + PR.PT-3 + SRG-OS-000368-GPOS-00154 + The only legitimate location for device files is the /dev directory +located on the root partition. The only exception to this is chroot jails. + + CCE-83869-8 + +part /tmp --mountoptions="nodev" + + - name: 'Add nodev Option to /tmp: Check information associated to mountpoint' + command: findmnt --fstab '/tmp' + register: device_name + failed_when: device_name.rc > 1 + changed_when: false + when: ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", + "container"] and "/tmp" in ansible_mounts | map(attribute="mount") | list ) + tags: + - CCE-83869-8 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_tmp_nodev + - no_reboot_needed + +- name: 'Add nodev Option to /tmp: Create mount_info dictionary variable' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - '{{ device_name.stdout_lines[0].split() | list | lower }}' + - '{{ device_name.stdout_lines[1].split() | list }}' + when: + - ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + and "/tmp" in ansible_mounts | map(attribute="mount") | list ) + - device_name.stdout is defined and device_name.stdout_lines is defined + - (device_name.stdout | length > 0) + tags: + - CCE-83869-8 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_tmp_nodev + - no_reboot_needed + +- name: 'Add nodev Option to /tmp: If /tmp not mounted, craft mount_info manually' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - - target + - source + - fstype + - options + - - /tmp + - '' + - '' + - defaults + when: + - ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + and "/tmp" in ansible_mounts | map(attribute="mount") | list ) + - ("--fstab" | length == 0) + - (device_name.stdout | length == 0) + tags: + - CCE-83869-8 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_tmp_nodev + - no_reboot_needed + +- name: 'Add nodev Option to /tmp: Make sure nodev option is part of the to /tmp options' + set_fact: + mount_info: '{{ mount_info | combine( {''options'':''''~mount_info.options~'',nodev'' + }) }}' + when: + - ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + and "/tmp" in ansible_mounts | map(attribute="mount") | list ) + - mount_info is defined and "nodev" not in mount_info.options + tags: + - CCE-83869-8 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_tmp_nodev + - no_reboot_needed + +- name: 'Add nodev Option to /tmp: Ensure /tmp is mounted with nodev option' + mount: + path: /tmp + src: '{{ mount_info.source }}' + opts: '{{ mount_info.options }}' + state: mounted + fstype: '{{ mount_info.fstype }}' + when: + - ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + and "/tmp" in ansible_mounts | map(attribute="mount") | list ) + - (device_name.stdout is defined and (device_name.stdout | length > 0)) or ("--fstab" + | length == 0) + tags: + - CCE-83869-8 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_tmp_nodev + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if ( [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && findmnt --kernel "/tmp" > /dev/null ); then + +function perform_remediation { + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" "/tmp")" + + grep "$mount_point_match_regexp" -q /etc/fstab \ + || { echo "The mount point '/tmp' is not even in /etc/fstab, so we can't set up mount options" >&2; + echo "Not remediating, because there is no record of /tmp in /etc/fstab" >&2; return 1; } + + + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" /tmp)" + + # If the mount point is not in /etc/fstab, get previous mount options from /etc/mtab + if [ "$(grep -c "$mount_point_match_regexp" /etc/fstab)" -eq 0 ]; then + # runtime opts without some automatic kernel/userspace-added defaults + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/mtab | head -1 | awk '{print $4}' \ + | sed -E "s/(rw|defaults|seclabel|nodev)(,|$)//g;s/,$//") + [ "$previous_mount_opts" ] && previous_mount_opts+="," + echo " /tmp defaults,${previous_mount_opts}nodev 0 0" >> /etc/fstab + # If the mount_opt option is not already in the mount point's /etc/fstab entry, add it + elif [ "$(grep "$mount_point_match_regexp" /etc/fstab | grep -c "nodev")" -eq 0 ]; then + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/fstab | awk '{print $4}') + sed -i "s|\(${mount_point_match_regexp}.*${previous_mount_opts}\)|\1,nodev|" /etc/fstab + fi + + + if mkdir -p "/tmp"; then + if mountpoint -q "/tmp"; then + mount -o remount --target "/tmp" + else + mount --target "/tmp" + fi + fi +} + +perform_remediation + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Add noexec Option to /tmp + The noexec mount option can be used to prevent binaries +from being executed out of /tmp. +Add the noexec option to the fourth column of +/etc/fstab for the line which controls mounting of +/tmp. + BP28(R12) + 11 + 13 + 14 + 3 + 8 + 9 + APO13.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS05.06 + DSS06.06 + CCI-001764 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.11.2.9 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.8.2.1 + A.8.2.2 + A.8.2.3 + A.8.3.1 + A.8.3.3 + A.9.1.2 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + AC-6 + AC-6(1) + MP-7 + PR.IP-1 + PR.PT-2 + PR.PT-3 + SRG-OS-000368-GPOS-00154 + Allowing users to execute binaries from world-writable directories +such as /tmp should never be necessary in normal operation and +can expose the system to potential compromise. + + CCE-83885-4 + +part /tmp --mountoptions="noexec" + + - name: 'Add noexec Option to /tmp: Check information associated to mountpoint' + command: findmnt --fstab '/tmp' + register: device_name + failed_when: device_name.rc > 1 + changed_when: false + when: ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", + "container"] and "/tmp" in ansible_mounts | map(attribute="mount") | list ) + tags: + - CCE-83885-4 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_tmp_noexec + - no_reboot_needed + +- name: 'Add noexec Option to /tmp: Create mount_info dictionary variable' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - '{{ device_name.stdout_lines[0].split() | list | lower }}' + - '{{ device_name.stdout_lines[1].split() | list }}' + when: + - ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + and "/tmp" in ansible_mounts | map(attribute="mount") | list ) + - device_name.stdout is defined and device_name.stdout_lines is defined + - (device_name.stdout | length > 0) + tags: + - CCE-83885-4 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_tmp_noexec + - no_reboot_needed + +- name: 'Add noexec Option to /tmp: If /tmp not mounted, craft mount_info manually' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - - target + - source + - fstype + - options + - - /tmp + - '' + - '' + - defaults + when: + - ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + and "/tmp" in ansible_mounts | map(attribute="mount") | list ) + - ("--fstab" | length == 0) + - (device_name.stdout | length == 0) + tags: + - CCE-83885-4 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_tmp_noexec + - no_reboot_needed + +- name: 'Add noexec Option to /tmp: Make sure noexec option is part of the to /tmp + options' + set_fact: + mount_info: '{{ mount_info | combine( {''options'':''''~mount_info.options~'',noexec'' + }) }}' + when: + - ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + and "/tmp" in ansible_mounts | map(attribute="mount") | list ) + - mount_info is defined and "noexec" not in mount_info.options + tags: + - CCE-83885-4 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_tmp_noexec + - no_reboot_needed + +- name: 'Add noexec Option to /tmp: Ensure /tmp is mounted with noexec option' + mount: + path: /tmp + src: '{{ mount_info.source }}' + opts: '{{ mount_info.options }}' + state: mounted + fstype: '{{ mount_info.fstype }}' + when: + - ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + and "/tmp" in ansible_mounts | map(attribute="mount") | list ) + - (device_name.stdout is defined and (device_name.stdout | length > 0)) or ("--fstab" + | length == 0) + tags: + - CCE-83885-4 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_tmp_noexec + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if ( [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && findmnt --kernel "/tmp" > /dev/null ); then + +function perform_remediation { + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" "/tmp")" + + grep "$mount_point_match_regexp" -q /etc/fstab \ + || { echo "The mount point '/tmp' is not even in /etc/fstab, so we can't set up mount options" >&2; + echo "Not remediating, because there is no record of /tmp in /etc/fstab" >&2; return 1; } + + + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" /tmp)" + + # If the mount point is not in /etc/fstab, get previous mount options from /etc/mtab + if [ "$(grep -c "$mount_point_match_regexp" /etc/fstab)" -eq 0 ]; then + # runtime opts without some automatic kernel/userspace-added defaults + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/mtab | head -1 | awk '{print $4}' \ + | sed -E "s/(rw|defaults|seclabel|noexec)(,|$)//g;s/,$//") + [ "$previous_mount_opts" ] && previous_mount_opts+="," + echo " /tmp defaults,${previous_mount_opts}noexec 0 0" >> /etc/fstab + # If the mount_opt option is not already in the mount point's /etc/fstab entry, add it + elif [ "$(grep "$mount_point_match_regexp" /etc/fstab | grep -c "noexec")" -eq 0 ]; then + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/fstab | awk '{print $4}') + sed -i "s|\(${mount_point_match_regexp}.*${previous_mount_opts}\)|\1,noexec|" /etc/fstab + fi + + + if mkdir -p "/tmp"; then + if mountpoint -q "/tmp"; then + mount -o remount --target "/tmp" + else + mount --target "/tmp" + fi + fi +} + +perform_remediation + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Add nosuid Option to /tmp + The nosuid mount option can be used to prevent +execution of setuid programs in /tmp. The SUID and SGID permissions +should not be required in these world-writable directories. +Add the nosuid option to the fourth column of +/etc/fstab for the line which controls mounting of +/tmp. + BP28(R12) + 11 + 13 + 14 + 3 + 8 + 9 + APO13.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS05.06 + DSS06.06 + CCI-001764 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.11.2.9 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.8.2.1 + A.8.2.2 + A.8.2.3 + A.8.3.1 + A.8.3.3 + A.9.1.2 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + AC-6 + AC-6(1) + MP-7 + PR.IP-1 + PR.PT-2 + PR.PT-3 + SRG-OS-000368-GPOS-00154 + The presence of SUID and SGID executables should be tightly controlled. Users +should not be able to execute SUID or SGID binaries from temporary storage partitions. + + CCE-83872-2 + +part /tmp --mountoptions="nosuid" + + - name: 'Add nosuid Option to /tmp: Check information associated to mountpoint' + command: findmnt --fstab '/tmp' + register: device_name + failed_when: device_name.rc > 1 + changed_when: false + when: ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", + "container"] and "/tmp" in ansible_mounts | map(attribute="mount") | list ) + tags: + - CCE-83872-2 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_tmp_nosuid + - no_reboot_needed + +- name: 'Add nosuid Option to /tmp: Create mount_info dictionary variable' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - '{{ device_name.stdout_lines[0].split() | list | lower }}' + - '{{ device_name.stdout_lines[1].split() | list }}' + when: + - ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + and "/tmp" in ansible_mounts | map(attribute="mount") | list ) + - device_name.stdout is defined and device_name.stdout_lines is defined + - (device_name.stdout | length > 0) + tags: + - CCE-83872-2 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_tmp_nosuid + - no_reboot_needed + +- name: 'Add nosuid Option to /tmp: If /tmp not mounted, craft mount_info manually' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - - target + - source + - fstype + - options + - - /tmp + - '' + - '' + - defaults + when: + - ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + and "/tmp" in ansible_mounts | map(attribute="mount") | list ) + - ("--fstab" | length == 0) + - (device_name.stdout | length == 0) + tags: + - CCE-83872-2 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_tmp_nosuid + - no_reboot_needed + +- name: 'Add nosuid Option to /tmp: Make sure nosuid option is part of the to /tmp + options' + set_fact: + mount_info: '{{ mount_info | combine( {''options'':''''~mount_info.options~'',nosuid'' + }) }}' + when: + - ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + and "/tmp" in ansible_mounts | map(attribute="mount") | list ) + - mount_info is defined and "nosuid" not in mount_info.options + tags: + - CCE-83872-2 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_tmp_nosuid + - no_reboot_needed + +- name: 'Add nosuid Option to /tmp: Ensure /tmp is mounted with nosuid option' + mount: + path: /tmp + src: '{{ mount_info.source }}' + opts: '{{ mount_info.options }}' + state: mounted + fstype: '{{ mount_info.fstype }}' + when: + - ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + and "/tmp" in ansible_mounts | map(attribute="mount") | list ) + - (device_name.stdout is defined and (device_name.stdout | length > 0)) or ("--fstab" + | length == 0) + tags: + - CCE-83872-2 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_tmp_nosuid + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if ( [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && findmnt --kernel "/tmp" > /dev/null ); then + +function perform_remediation { + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" "/tmp")" + + grep "$mount_point_match_regexp" -q /etc/fstab \ + || { echo "The mount point '/tmp' is not even in /etc/fstab, so we can't set up mount options" >&2; + echo "Not remediating, because there is no record of /tmp in /etc/fstab" >&2; return 1; } + + + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" /tmp)" + + # If the mount point is not in /etc/fstab, get previous mount options from /etc/mtab + if [ "$(grep -c "$mount_point_match_regexp" /etc/fstab)" -eq 0 ]; then + # runtime opts without some automatic kernel/userspace-added defaults + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/mtab | head -1 | awk '{print $4}' \ + | sed -E "s/(rw|defaults|seclabel|nosuid)(,|$)//g;s/,$//") + [ "$previous_mount_opts" ] && previous_mount_opts+="," + echo " /tmp defaults,${previous_mount_opts}nosuid 0 0" >> /etc/fstab + # If the mount_opt option is not already in the mount point's /etc/fstab entry, add it + elif [ "$(grep "$mount_point_match_regexp" /etc/fstab | grep -c "nosuid")" -eq 0 ]; then + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/fstab | awk '{print $4}') + sed -i "s|\(${mount_point_match_regexp}.*${previous_mount_opts}\)|\1,nosuid|" /etc/fstab + fi + + + if mkdir -p "/tmp"; then + if mountpoint -q "/tmp"; then + mount -o remount --target "/tmp" + else + mount --target "/tmp" + fi + fi +} + +perform_remediation + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Add nodev Option to /var/log/audit + The nodev mount option can be used to prevent device files from +being created in /var/log/audit. +Legitimate character and block devices should exist only in +the /dev directory on the root partition or within chroot +jails built for system services. +Add the nodev option to the fourth column of +/etc/fstab for the line which controls mounting of +/var/log/audit. + CCI-001764 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + AC-6 + AC-6(1) + MP-7 + PR.IP-1 + PR.PT-2 + PR.PT-3 + SRG-OS-000368-GPOS-00154 + The only legitimate location for device files is the /dev directory +located on the root partition. The only exception to this is chroot jails. + + CCE-83882-1 + +part /var/log/audit --mountoptions="nodev" + + - name: 'Add nodev Option to /var/log/audit: Check information associated to mountpoint' + command: findmnt --fstab '/var/log/audit' + register: device_name + failed_when: device_name.rc > 1 + changed_when: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83882-1 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_log_audit_nodev + - no_reboot_needed + +- name: 'Add nodev Option to /var/log/audit: Create mount_info dictionary variable' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - '{{ device_name.stdout_lines[0].split() | list | lower }}' + - '{{ device_name.stdout_lines[1].split() | list }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - device_name.stdout is defined and device_name.stdout_lines is defined + - (device_name.stdout | length > 0) + tags: + - CCE-83882-1 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_log_audit_nodev + - no_reboot_needed + +- name: 'Add nodev Option to /var/log/audit: If /var/log/audit not mounted, craft + mount_info manually' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - - target + - source + - fstype + - options + - - /var/log/audit + - '' + - '' + - defaults + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ("--fstab" | length == 0) + - (device_name.stdout | length == 0) + tags: + - CCE-83882-1 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_log_audit_nodev + - no_reboot_needed + +- name: 'Add nodev Option to /var/log/audit: Make sure nodev option is part of the + to /var/log/audit options' + set_fact: + mount_info: '{{ mount_info | combine( {''options'':''''~mount_info.options~'',nodev'' + }) }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - mount_info is defined and "nodev" not in mount_info.options + tags: + - CCE-83882-1 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_log_audit_nodev + - no_reboot_needed + +- name: 'Add nodev Option to /var/log/audit: Ensure /var/log/audit is mounted with + nodev option' + mount: + path: /var/log/audit + src: '{{ mount_info.source }}' + opts: '{{ mount_info.options }}' + state: mounted + fstype: '{{ mount_info.fstype }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - (device_name.stdout is defined and (device_name.stdout | length > 0)) or ("--fstab" + | length == 0) + tags: + - CCE-83882-1 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_log_audit_nodev + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +function perform_remediation { + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" "/var/log/audit")" + + grep "$mount_point_match_regexp" -q /etc/fstab \ + || { echo "The mount point '/var/log/audit' is not even in /etc/fstab, so we can't set up mount options" >&2; + echo "Not remediating, because there is no record of /var/log/audit in /etc/fstab" >&2; return 1; } + + + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" /var/log/audit)" + + # If the mount point is not in /etc/fstab, get previous mount options from /etc/mtab + if [ "$(grep -c "$mount_point_match_regexp" /etc/fstab)" -eq 0 ]; then + # runtime opts without some automatic kernel/userspace-added defaults + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/mtab | head -1 | awk '{print $4}' \ + | sed -E "s/(rw|defaults|seclabel|nodev)(,|$)//g;s/,$//") + [ "$previous_mount_opts" ] && previous_mount_opts+="," + echo " /var/log/audit defaults,${previous_mount_opts}nodev 0 0" >> /etc/fstab + # If the mount_opt option is not already in the mount point's /etc/fstab entry, add it + elif [ "$(grep "$mount_point_match_regexp" /etc/fstab | grep -c "nodev")" -eq 0 ]; then + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/fstab | awk '{print $4}') + sed -i "s|\(${mount_point_match_regexp}.*${previous_mount_opts}\)|\1,nodev|" /etc/fstab + fi + + + if mkdir -p "/var/log/audit"; then + if mountpoint -q "/var/log/audit"; then + mount -o remount --target "/var/log/audit" + else + mount --target "/var/log/audit" + fi + fi +} + +perform_remediation + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Add noexec Option to /var/log/audit + The noexec mount option can be used to prevent binaries +from being executed out of /var/log/audit. +Add the noexec option to the fourth column of +/etc/fstab for the line which controls mounting of +/var/log/audit. + CCI-001764 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + AC-6 + AC-6(1) + MP-7 + PR.IP-1 + PR.PT-2 + PR.PT-3 + SRG-OS-000368-GPOS-00154 + Allowing users to execute binaries from directories containing audit log files +such as /var/log/audit should never be necessary in normal operation and +can expose the system to potential compromise. + + CCE-83878-9 + +part /var/log/audit --mountoptions="noexec" + + - name: 'Add noexec Option to /var/log/audit: Check information associated to mountpoint' + command: findmnt --fstab '/var/log/audit' + register: device_name + failed_when: device_name.rc > 1 + changed_when: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83878-9 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_log_audit_noexec + - no_reboot_needed + +- name: 'Add noexec Option to /var/log/audit: Create mount_info dictionary variable' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - '{{ device_name.stdout_lines[0].split() | list | lower }}' + - '{{ device_name.stdout_lines[1].split() | list }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - device_name.stdout is defined and device_name.stdout_lines is defined + - (device_name.stdout | length > 0) + tags: + - CCE-83878-9 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_log_audit_noexec + - no_reboot_needed + +- name: 'Add noexec Option to /var/log/audit: If /var/log/audit not mounted, craft + mount_info manually' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - - target + - source + - fstype + - options + - - /var/log/audit + - '' + - '' + - defaults + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ("--fstab" | length == 0) + - (device_name.stdout | length == 0) + tags: + - CCE-83878-9 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_log_audit_noexec + - no_reboot_needed + +- name: 'Add noexec Option to /var/log/audit: Make sure noexec option is part of the + to /var/log/audit options' + set_fact: + mount_info: '{{ mount_info | combine( {''options'':''''~mount_info.options~'',noexec'' + }) }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - mount_info is defined and "noexec" not in mount_info.options + tags: + - CCE-83878-9 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_log_audit_noexec + - no_reboot_needed + +- name: 'Add noexec Option to /var/log/audit: Ensure /var/log/audit is mounted with + noexec option' + mount: + path: /var/log/audit + src: '{{ mount_info.source }}' + opts: '{{ mount_info.options }}' + state: mounted + fstype: '{{ mount_info.fstype }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - (device_name.stdout is defined and (device_name.stdout | length > 0)) or ("--fstab" + | length == 0) + tags: + - CCE-83878-9 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_log_audit_noexec + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +function perform_remediation { + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" "/var/log/audit")" + + grep "$mount_point_match_regexp" -q /etc/fstab \ + || { echo "The mount point '/var/log/audit' is not even in /etc/fstab, so we can't set up mount options" >&2; + echo "Not remediating, because there is no record of /var/log/audit in /etc/fstab" >&2; return 1; } + + + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" /var/log/audit)" + + # If the mount point is not in /etc/fstab, get previous mount options from /etc/mtab + if [ "$(grep -c "$mount_point_match_regexp" /etc/fstab)" -eq 0 ]; then + # runtime opts without some automatic kernel/userspace-added defaults + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/mtab | head -1 | awk '{print $4}' \ + | sed -E "s/(rw|defaults|seclabel|noexec)(,|$)//g;s/,$//") + [ "$previous_mount_opts" ] && previous_mount_opts+="," + echo " /var/log/audit defaults,${previous_mount_opts}noexec 0 0" >> /etc/fstab + # If the mount_opt option is not already in the mount point's /etc/fstab entry, add it + elif [ "$(grep "$mount_point_match_regexp" /etc/fstab | grep -c "noexec")" -eq 0 ]; then + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/fstab | awk '{print $4}') + sed -i "s|\(${mount_point_match_regexp}.*${previous_mount_opts}\)|\1,noexec|" /etc/fstab + fi + + + if mkdir -p "/var/log/audit"; then + if mountpoint -q "/var/log/audit"; then + mount -o remount --target "/var/log/audit" + else + mount --target "/var/log/audit" + fi + fi +} + +perform_remediation + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Add nosuid Option to /var/log/audit + The nosuid mount option can be used to prevent +execution of setuid programs in /var/log/audit. The SUID and SGID permissions +should not be required in directories containing audit log files. +Add the nosuid option to the fourth column of +/etc/fstab for the line which controls mounting of +/var/log/audit. + CCI-001764 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + AC-6 + AC-6(1) + MP-7 + PR.IP-1 + PR.PT-2 + PR.PT-3 + SRG-OS-000368-GPOS-00154 + The presence of SUID and SGID executables should be tightly controlled. Users +should not be able to execute SUID or SGID binaries from partitions +designated for audit log files. + + CCE-83893-8 + +part /var/log/audit --mountoptions="nosuid" + + - name: 'Add nosuid Option to /var/log/audit: Check information associated to mountpoint' + command: findmnt --fstab '/var/log/audit' + register: device_name + failed_when: device_name.rc > 1 + changed_when: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83893-8 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_log_audit_nosuid + - no_reboot_needed + +- name: 'Add nosuid Option to /var/log/audit: Create mount_info dictionary variable' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - '{{ device_name.stdout_lines[0].split() | list | lower }}' + - '{{ device_name.stdout_lines[1].split() | list }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - device_name.stdout is defined and device_name.stdout_lines is defined + - (device_name.stdout | length > 0) + tags: + - CCE-83893-8 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_log_audit_nosuid + - no_reboot_needed + +- name: 'Add nosuid Option to /var/log/audit: If /var/log/audit not mounted, craft + mount_info manually' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - - target + - source + - fstype + - options + - - /var/log/audit + - '' + - '' + - defaults + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ("--fstab" | length == 0) + - (device_name.stdout | length == 0) + tags: + - CCE-83893-8 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_log_audit_nosuid + - no_reboot_needed + +- name: 'Add nosuid Option to /var/log/audit: Make sure nosuid option is part of the + to /var/log/audit options' + set_fact: + mount_info: '{{ mount_info | combine( {''options'':''''~mount_info.options~'',nosuid'' + }) }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - mount_info is defined and "nosuid" not in mount_info.options + tags: + - CCE-83893-8 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_log_audit_nosuid + - no_reboot_needed + +- name: 'Add nosuid Option to /var/log/audit: Ensure /var/log/audit is mounted with + nosuid option' + mount: + path: /var/log/audit + src: '{{ mount_info.source }}' + opts: '{{ mount_info.options }}' + state: mounted + fstype: '{{ mount_info.fstype }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - (device_name.stdout is defined and (device_name.stdout | length > 0)) or ("--fstab" + | length == 0) + tags: + - CCE-83893-8 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_log_audit_nosuid + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +function perform_remediation { + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" "/var/log/audit")" + + grep "$mount_point_match_regexp" -q /etc/fstab \ + || { echo "The mount point '/var/log/audit' is not even in /etc/fstab, so we can't set up mount options" >&2; + echo "Not remediating, because there is no record of /var/log/audit in /etc/fstab" >&2; return 1; } + + + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" /var/log/audit)" + + # If the mount point is not in /etc/fstab, get previous mount options from /etc/mtab + if [ "$(grep -c "$mount_point_match_regexp" /etc/fstab)" -eq 0 ]; then + # runtime opts without some automatic kernel/userspace-added defaults + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/mtab | head -1 | awk '{print $4}' \ + | sed -E "s/(rw|defaults|seclabel|nosuid)(,|$)//g;s/,$//") + [ "$previous_mount_opts" ] && previous_mount_opts+="," + echo " /var/log/audit defaults,${previous_mount_opts}nosuid 0 0" >> /etc/fstab + # If the mount_opt option is not already in the mount point's /etc/fstab entry, add it + elif [ "$(grep "$mount_point_match_regexp" /etc/fstab | grep -c "nosuid")" -eq 0 ]; then + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/fstab | awk '{print $4}') + sed -i "s|\(${mount_point_match_regexp}.*${previous_mount_opts}\)|\1,nosuid|" /etc/fstab + fi + + + if mkdir -p "/var/log/audit"; then + if mountpoint -q "/var/log/audit"; then + mount -o remount --target "/var/log/audit" + else + mount --target "/var/log/audit" + fi + fi +} + +perform_remediation + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Add nodev Option to /var/log + The nodev mount option can be used to prevent device files from +being created in /var/log. +Legitimate character and block devices should exist only in +the /dev directory on the root partition or within chroot +jails built for system services. +Add the nodev option to the fourth column of +/etc/fstab for the line which controls mounting of +/var/log. + CCI-001764 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + AC-6 + AC-6(1) + MP-7 + PR.IP-1 + PR.PT-2 + PR.PT-3 + SRG-OS-000368-GPOS-00154 + The only legitimate location for device files is the /dev directory +located on the root partition. The only exception to this is chroot jails. + + CCE-83886-2 + +part /var/log --mountoptions="nodev" + + - name: 'Add nodev Option to /var/log: Check information associated to mountpoint' + command: findmnt --fstab '/var/log' + register: device_name + failed_when: device_name.rc > 1 + changed_when: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83886-2 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_log_nodev + - no_reboot_needed + +- name: 'Add nodev Option to /var/log: Create mount_info dictionary variable' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - '{{ device_name.stdout_lines[0].split() | list | lower }}' + - '{{ device_name.stdout_lines[1].split() | list }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - device_name.stdout is defined and device_name.stdout_lines is defined + - (device_name.stdout | length > 0) + tags: + - CCE-83886-2 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_log_nodev + - no_reboot_needed + +- name: 'Add nodev Option to /var/log: If /var/log not mounted, craft mount_info manually' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - - target + - source + - fstype + - options + - - /var/log + - '' + - '' + - defaults + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ("--fstab" | length == 0) + - (device_name.stdout | length == 0) + tags: + - CCE-83886-2 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_log_nodev + - no_reboot_needed + +- name: 'Add nodev Option to /var/log: Make sure nodev option is part of the to /var/log + options' + set_fact: + mount_info: '{{ mount_info | combine( {''options'':''''~mount_info.options~'',nodev'' + }) }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - mount_info is defined and "nodev" not in mount_info.options + tags: + - CCE-83886-2 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_log_nodev + - no_reboot_needed + +- name: 'Add nodev Option to /var/log: Ensure /var/log is mounted with nodev option' + mount: + path: /var/log + src: '{{ mount_info.source }}' + opts: '{{ mount_info.options }}' + state: mounted + fstype: '{{ mount_info.fstype }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - (device_name.stdout is defined and (device_name.stdout | length > 0)) or ("--fstab" + | length == 0) + tags: + - CCE-83886-2 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_log_nodev + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +function perform_remediation { + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" "/var/log")" + + grep "$mount_point_match_regexp" -q /etc/fstab \ + || { echo "The mount point '/var/log' is not even in /etc/fstab, so we can't set up mount options" >&2; + echo "Not remediating, because there is no record of /var/log in /etc/fstab" >&2; return 1; } + + + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" /var/log)" + + # If the mount point is not in /etc/fstab, get previous mount options from /etc/mtab + if [ "$(grep -c "$mount_point_match_regexp" /etc/fstab)" -eq 0 ]; then + # runtime opts without some automatic kernel/userspace-added defaults + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/mtab | head -1 | awk '{print $4}' \ + | sed -E "s/(rw|defaults|seclabel|nodev)(,|$)//g;s/,$//") + [ "$previous_mount_opts" ] && previous_mount_opts+="," + echo " /var/log defaults,${previous_mount_opts}nodev 0 0" >> /etc/fstab + # If the mount_opt option is not already in the mount point's /etc/fstab entry, add it + elif [ "$(grep "$mount_point_match_regexp" /etc/fstab | grep -c "nodev")" -eq 0 ]; then + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/fstab | awk '{print $4}') + sed -i "s|\(${mount_point_match_regexp}.*${previous_mount_opts}\)|\1,nodev|" /etc/fstab + fi + + + if mkdir -p "/var/log"; then + if mountpoint -q "/var/log"; then + mount -o remount --target "/var/log" + else + mount --target "/var/log" + fi + fi +} + +perform_remediation + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Add noexec Option to /var/log + The noexec mount option can be used to prevent binaries +from being executed out of /var/log. +Add the noexec option to the fourth column of +/etc/fstab for the line which controls mounting of +/var/log. + BP28(R12) + CCI-001764 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + AC-6 + AC-6(1) + MP-7 + PR.IP-1 + PR.PT-2 + PR.PT-3 + SRG-OS-000368-GPOS-00154 + Allowing users to execute binaries from directories containing log files +such as /var/log should never be necessary in normal operation and +can expose the system to potential compromise. + + CCE-83887-0 + +part /var/log --mountoptions="noexec" + + - name: 'Add noexec Option to /var/log: Check information associated to mountpoint' + command: findmnt --fstab '/var/log' + register: device_name + failed_when: device_name.rc > 1 + changed_when: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83887-0 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_log_noexec + - no_reboot_needed + +- name: 'Add noexec Option to /var/log: Create mount_info dictionary variable' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - '{{ device_name.stdout_lines[0].split() | list | lower }}' + - '{{ device_name.stdout_lines[1].split() | list }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - device_name.stdout is defined and device_name.stdout_lines is defined + - (device_name.stdout | length > 0) + tags: + - CCE-83887-0 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_log_noexec + - no_reboot_needed + +- name: 'Add noexec Option to /var/log: If /var/log not mounted, craft mount_info + manually' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - - target + - source + - fstype + - options + - - /var/log + - '' + - '' + - defaults + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ("--fstab" | length == 0) + - (device_name.stdout | length == 0) + tags: + - CCE-83887-0 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_log_noexec + - no_reboot_needed + +- name: 'Add noexec Option to /var/log: Make sure noexec option is part of the to + /var/log options' + set_fact: + mount_info: '{{ mount_info | combine( {''options'':''''~mount_info.options~'',noexec'' + }) }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - mount_info is defined and "noexec" not in mount_info.options + tags: + - CCE-83887-0 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_log_noexec + - no_reboot_needed + +- name: 'Add noexec Option to /var/log: Ensure /var/log is mounted with noexec option' + mount: + path: /var/log + src: '{{ mount_info.source }}' + opts: '{{ mount_info.options }}' + state: mounted + fstype: '{{ mount_info.fstype }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - (device_name.stdout is defined and (device_name.stdout | length > 0)) or ("--fstab" + | length == 0) + tags: + - CCE-83887-0 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_log_noexec + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +function perform_remediation { + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" "/var/log")" + + grep "$mount_point_match_regexp" -q /etc/fstab \ + || { echo "The mount point '/var/log' is not even in /etc/fstab, so we can't set up mount options" >&2; + echo "Not remediating, because there is no record of /var/log in /etc/fstab" >&2; return 1; } + + + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" /var/log)" + + # If the mount point is not in /etc/fstab, get previous mount options from /etc/mtab + if [ "$(grep -c "$mount_point_match_regexp" /etc/fstab)" -eq 0 ]; then + # runtime opts without some automatic kernel/userspace-added defaults + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/mtab | head -1 | awk '{print $4}' \ + | sed -E "s/(rw|defaults|seclabel|noexec)(,|$)//g;s/,$//") + [ "$previous_mount_opts" ] && previous_mount_opts+="," + echo " /var/log defaults,${previous_mount_opts}noexec 0 0" >> /etc/fstab + # If the mount_opt option is not already in the mount point's /etc/fstab entry, add it + elif [ "$(grep "$mount_point_match_regexp" /etc/fstab | grep -c "noexec")" -eq 0 ]; then + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/fstab | awk '{print $4}') + sed -i "s|\(${mount_point_match_regexp}.*${previous_mount_opts}\)|\1,noexec|" /etc/fstab + fi + + + if mkdir -p "/var/log"; then + if mountpoint -q "/var/log"; then + mount -o remount --target "/var/log" + else + mount --target "/var/log" + fi + fi +} + +perform_remediation + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Add nosuid Option to /var/log + The nosuid mount option can be used to prevent +execution of setuid programs in /var/log. The SUID and SGID permissions +should not be required in directories containing log files. +Add the nosuid option to the fourth column of +/etc/fstab for the line which controls mounting of +/var/log. + BP28(R12) + CCI-001764 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + AC-6 + AC-6(1) + MP-7 + PR.IP-1 + PR.PT-2 + PR.PT-3 + SRG-OS-000368-GPOS-00154 + The presence of SUID and SGID executables should be tightly controlled. Users +should not be able to execute SUID or SGID binaries from partitions +designated for log files. + + CCE-83870-6 + +part /var/log --mountoptions="nosuid" + + - name: 'Add nosuid Option to /var/log: Check information associated to mountpoint' + command: findmnt --fstab '/var/log' + register: device_name + failed_when: device_name.rc > 1 + changed_when: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83870-6 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_log_nosuid + - no_reboot_needed + +- name: 'Add nosuid Option to /var/log: Create mount_info dictionary variable' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - '{{ device_name.stdout_lines[0].split() | list | lower }}' + - '{{ device_name.stdout_lines[1].split() | list }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - device_name.stdout is defined and device_name.stdout_lines is defined + - (device_name.stdout | length > 0) + tags: + - CCE-83870-6 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_log_nosuid + - no_reboot_needed + +- name: 'Add nosuid Option to /var/log: If /var/log not mounted, craft mount_info + manually' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - - target + - source + - fstype + - options + - - /var/log + - '' + - '' + - defaults + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ("--fstab" | length == 0) + - (device_name.stdout | length == 0) + tags: + - CCE-83870-6 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_log_nosuid + - no_reboot_needed + +- name: 'Add nosuid Option to /var/log: Make sure nosuid option is part of the to + /var/log options' + set_fact: + mount_info: '{{ mount_info | combine( {''options'':''''~mount_info.options~'',nosuid'' + }) }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - mount_info is defined and "nosuid" not in mount_info.options + tags: + - CCE-83870-6 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_log_nosuid + - no_reboot_needed + +- name: 'Add nosuid Option to /var/log: Ensure /var/log is mounted with nosuid option' + mount: + path: /var/log + src: '{{ mount_info.source }}' + opts: '{{ mount_info.options }}' + state: mounted + fstype: '{{ mount_info.fstype }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - (device_name.stdout is defined and (device_name.stdout | length > 0)) or ("--fstab" + | length == 0) + tags: + - CCE-83870-6 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_log_nosuid + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +function perform_remediation { + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" "/var/log")" + + grep "$mount_point_match_regexp" -q /etc/fstab \ + || { echo "The mount point '/var/log' is not even in /etc/fstab, so we can't set up mount options" >&2; + echo "Not remediating, because there is no record of /var/log in /etc/fstab" >&2; return 1; } + + + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" /var/log)" + + # If the mount point is not in /etc/fstab, get previous mount options from /etc/mtab + if [ "$(grep -c "$mount_point_match_regexp" /etc/fstab)" -eq 0 ]; then + # runtime opts without some automatic kernel/userspace-added defaults + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/mtab | head -1 | awk '{print $4}' \ + | sed -E "s/(rw|defaults|seclabel|nosuid)(,|$)//g;s/,$//") + [ "$previous_mount_opts" ] && previous_mount_opts+="," + echo " /var/log defaults,${previous_mount_opts}nosuid 0 0" >> /etc/fstab + # If the mount_opt option is not already in the mount point's /etc/fstab entry, add it + elif [ "$(grep "$mount_point_match_regexp" /etc/fstab | grep -c "nosuid")" -eq 0 ]; then + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/fstab | awk '{print $4}') + sed -i "s|\(${mount_point_match_regexp}.*${previous_mount_opts}\)|\1,nosuid|" /etc/fstab + fi + + + if mkdir -p "/var/log"; then + if mountpoint -q "/var/log"; then + mount -o remount --target "/var/log" + else + mount --target "/var/log" + fi + fi +} + +perform_remediation + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Add nodev Option to /var + The nodev mount option can be used to prevent device files from +being created in /var. +Legitimate character and block devices should exist only in +the /dev directory on the root partition or within chroot +jails built for system services. +Add the nodev option to the fourth column of +/etc/fstab for the line which controls mounting of +/var. + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + AC-6 + AC-6(1) + MP-7 + PR.IP-1 + PR.PT-2 + PR.PT-3 + SRG-OS-000368-GPOS-00154 + The only legitimate location for device files is the /dev directory +located on the root partition. The only exception to this is chroot jails. + + CCE-83868-0 + +part /var --mountoptions="nodev" + + - name: 'Add nodev Option to /var: Check information associated to mountpoint' + command: findmnt --fstab '/var' + register: device_name + failed_when: device_name.rc > 1 + changed_when: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83868-0 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_nodev + - no_reboot_needed + +- name: 'Add nodev Option to /var: Create mount_info dictionary variable' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - '{{ device_name.stdout_lines[0].split() | list | lower }}' + - '{{ device_name.stdout_lines[1].split() | list }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - device_name.stdout is defined and device_name.stdout_lines is defined + - (device_name.stdout | length > 0) + tags: + - CCE-83868-0 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_nodev + - no_reboot_needed + +- name: 'Add nodev Option to /var: If /var not mounted, craft mount_info manually' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - - target + - source + - fstype + - options + - - /var + - '' + - '' + - defaults + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ("--fstab" | length == 0) + - (device_name.stdout | length == 0) + tags: + - CCE-83868-0 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_nodev + - no_reboot_needed + +- name: 'Add nodev Option to /var: Make sure nodev option is part of the to /var options' + set_fact: + mount_info: '{{ mount_info | combine( {''options'':''''~mount_info.options~'',nodev'' + }) }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - mount_info is defined and "nodev" not in mount_info.options + tags: + - CCE-83868-0 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_nodev + - no_reboot_needed + +- name: 'Add nodev Option to /var: Ensure /var is mounted with nodev option' + mount: + path: /var + src: '{{ mount_info.source }}' + opts: '{{ mount_info.options }}' + state: mounted + fstype: '{{ mount_info.fstype }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - (device_name.stdout is defined and (device_name.stdout | length > 0)) or ("--fstab" + | length == 0) + tags: + - CCE-83868-0 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_nodev + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +function perform_remediation { + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" "/var")" + + grep "$mount_point_match_regexp" -q /etc/fstab \ + || { echo "The mount point '/var' is not even in /etc/fstab, so we can't set up mount options" >&2; + echo "Not remediating, because there is no record of /var in /etc/fstab" >&2; return 1; } + + + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" /var)" + + # If the mount point is not in /etc/fstab, get previous mount options from /etc/mtab + if [ "$(grep -c "$mount_point_match_regexp" /etc/fstab)" -eq 0 ]; then + # runtime opts without some automatic kernel/userspace-added defaults + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/mtab | head -1 | awk '{print $4}' \ + | sed -E "s/(rw|defaults|seclabel|nodev)(,|$)//g;s/,$//") + [ "$previous_mount_opts" ] && previous_mount_opts+="," + echo " /var defaults,${previous_mount_opts}nodev 0 0" >> /etc/fstab + # If the mount_opt option is not already in the mount point's /etc/fstab entry, add it + elif [ "$(grep "$mount_point_match_regexp" /etc/fstab | grep -c "nodev")" -eq 0 ]; then + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/fstab | awk '{print $4}') + sed -i "s|\(${mount_point_match_regexp}.*${previous_mount_opts}\)|\1,nodev|" /etc/fstab + fi + + + if mkdir -p "/var"; then + if mountpoint -q "/var"; then + mount -o remount --target "/var" + else + mount --target "/var" + fi + fi +} + +perform_remediation + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Add noexec Option to /var + The noexec mount option can be used to prevent binaries from being +executed out of /var. +Add the noexec option to the fourth column of +/etc/fstab for the line which controls mounting of +/var. + BP28(R12) + The /var directory contains variable system data such as logs, +mails and caches. No binaries should be executed from this directory. + + CCE-83865-6 + +part /var --mountoptions="noexec" + + - name: 'Add noexec Option to /var: Check information associated to mountpoint' + command: findmnt --fstab '/var' + register: device_name + failed_when: device_name.rc > 1 + changed_when: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83865-6 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_noexec + - no_reboot_needed + +- name: 'Add noexec Option to /var: Create mount_info dictionary variable' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - '{{ device_name.stdout_lines[0].split() | list | lower }}' + - '{{ device_name.stdout_lines[1].split() | list }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - device_name.stdout is defined and device_name.stdout_lines is defined + - (device_name.stdout | length > 0) + tags: + - CCE-83865-6 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_noexec + - no_reboot_needed + +- name: 'Add noexec Option to /var: If /var not mounted, craft mount_info manually' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - - target + - source + - fstype + - options + - - /var + - '' + - '' + - defaults + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ("--fstab" | length == 0) + - (device_name.stdout | length == 0) + tags: + - CCE-83865-6 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_noexec + - no_reboot_needed + +- name: 'Add noexec Option to /var: Make sure noexec option is part of the to /var + options' + set_fact: + mount_info: '{{ mount_info | combine( {''options'':''''~mount_info.options~'',noexec'' + }) }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - mount_info is defined and "noexec" not in mount_info.options + tags: + - CCE-83865-6 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_noexec + - no_reboot_needed + +- name: 'Add noexec Option to /var: Ensure /var is mounted with noexec option' + mount: + path: /var + src: '{{ mount_info.source }}' + opts: '{{ mount_info.options }}' + state: mounted + fstype: '{{ mount_info.fstype }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - (device_name.stdout is defined and (device_name.stdout | length > 0)) or ("--fstab" + | length == 0) + tags: + - CCE-83865-6 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_noexec + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +function perform_remediation { + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" "/var")" + + grep "$mount_point_match_regexp" -q /etc/fstab \ + || { echo "The mount point '/var' is not even in /etc/fstab, so we can't set up mount options" >&2; + echo "Not remediating, because there is no record of /var in /etc/fstab" >&2; return 1; } + + + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" /var)" + + # If the mount point is not in /etc/fstab, get previous mount options from /etc/mtab + if [ "$(grep -c "$mount_point_match_regexp" /etc/fstab)" -eq 0 ]; then + # runtime opts without some automatic kernel/userspace-added defaults + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/mtab | head -1 | awk '{print $4}' \ + | sed -E "s/(rw|defaults|seclabel|noexec)(,|$)//g;s/,$//") + [ "$previous_mount_opts" ] && previous_mount_opts+="," + echo " /var defaults,${previous_mount_opts}noexec 0 0" >> /etc/fstab + # If the mount_opt option is not already in the mount point's /etc/fstab entry, add it + elif [ "$(grep "$mount_point_match_regexp" /etc/fstab | grep -c "noexec")" -eq 0 ]; then + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/fstab | awk '{print $4}') + sed -i "s|\(${mount_point_match_regexp}.*${previous_mount_opts}\)|\1,noexec|" /etc/fstab + fi + + + if mkdir -p "/var"; then + if mountpoint -q "/var"; then + mount -o remount --target "/var" + else + mount --target "/var" + fi + fi +} + +perform_remediation + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Add nosuid Option to /var + The nosuid mount option can be used to prevent +execution of setuid programs in /var. The SUID and SGID permissions +should not be required for this directory. +Add the nosuid option to the fourth column of +/etc/fstab for the line which controls mounting of +/var. + BP28(R12) + The presence of SUID and SGID executables should be tightly controlled. + + CCE-83867-2 + +part /var --mountoptions="nosuid" + + - name: 'Add nosuid Option to /var: Check information associated to mountpoint' + command: findmnt --fstab '/var' + register: device_name + failed_when: device_name.rc > 1 + changed_when: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83867-2 + - configure_strategy + - high_disruption + - low_complexity + - mount_option_var_nosuid + - no_reboot_needed + - unknown_severity + +- name: 'Add nosuid Option to /var: Create mount_info dictionary variable' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - '{{ device_name.stdout_lines[0].split() | list | lower }}' + - '{{ device_name.stdout_lines[1].split() | list }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - device_name.stdout is defined and device_name.stdout_lines is defined + - (device_name.stdout | length > 0) + tags: + - CCE-83867-2 + - configure_strategy + - high_disruption + - low_complexity + - mount_option_var_nosuid + - no_reboot_needed + - unknown_severity + +- name: 'Add nosuid Option to /var: If /var not mounted, craft mount_info manually' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - - target + - source + - fstype + - options + - - /var + - '' + - '' + - defaults + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ("--fstab" | length == 0) + - (device_name.stdout | length == 0) + tags: + - CCE-83867-2 + - configure_strategy + - high_disruption + - low_complexity + - mount_option_var_nosuid + - no_reboot_needed + - unknown_severity + +- name: 'Add nosuid Option to /var: Make sure nosuid option is part of the to /var + options' + set_fact: + mount_info: '{{ mount_info | combine( {''options'':''''~mount_info.options~'',nosuid'' + }) }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - mount_info is defined and "nosuid" not in mount_info.options + tags: + - CCE-83867-2 + - configure_strategy + - high_disruption + - low_complexity + - mount_option_var_nosuid + - no_reboot_needed + - unknown_severity + +- name: 'Add nosuid Option to /var: Ensure /var is mounted with nosuid option' + mount: + path: /var + src: '{{ mount_info.source }}' + opts: '{{ mount_info.options }}' + state: mounted + fstype: '{{ mount_info.fstype }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - (device_name.stdout is defined and (device_name.stdout | length > 0)) or ("--fstab" + | length == 0) + tags: + - CCE-83867-2 + - configure_strategy + - high_disruption + - low_complexity + - mount_option_var_nosuid + - no_reboot_needed + - unknown_severity + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +function perform_remediation { + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" "/var")" + + grep "$mount_point_match_regexp" -q /etc/fstab \ + || { echo "The mount point '/var' is not even in /etc/fstab, so we can't set up mount options" >&2; + echo "Not remediating, because there is no record of /var in /etc/fstab" >&2; return 1; } + + + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" /var)" + + # If the mount point is not in /etc/fstab, get previous mount options from /etc/mtab + if [ "$(grep -c "$mount_point_match_regexp" /etc/fstab)" -eq 0 ]; then + # runtime opts without some automatic kernel/userspace-added defaults + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/mtab | head -1 | awk '{print $4}' \ + | sed -E "s/(rw|defaults|seclabel|nosuid)(,|$)//g;s/,$//") + [ "$previous_mount_opts" ] && previous_mount_opts+="," + echo " /var defaults,${previous_mount_opts}nosuid 0 0" >> /etc/fstab + # If the mount_opt option is not already in the mount point's /etc/fstab entry, add it + elif [ "$(grep "$mount_point_match_regexp" /etc/fstab | grep -c "nosuid")" -eq 0 ]; then + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/fstab | awk '{print $4}') + sed -i "s|\(${mount_point_match_regexp}.*${previous_mount_opts}\)|\1,nosuid|" /etc/fstab + fi + + + if mkdir -p "/var"; then + if mountpoint -q "/var"; then + mount -o remount --target "/var" + else + mount --target "/var" + fi + fi +} + +perform_remediation + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Bind Mount /var/tmp To /tmp + The /var/tmp directory is a world-writable directory. Bind-mount +it to /tmp in order to consolidate temporary storage into one +location protected by the same techniques as /tmp. To do so, edit +/etc/fstab and add the following line: +/tmp /var/tmp none rw,nodev,noexec,nosuid,bind 0 0 +See the mount(8) man page for further explanation of bind mounting. + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + AC-6 + AC-6(1) + MP-7 + PR.IP-1 + PR.PT-3 + Having multiple locations for temporary storage is not required. Unless absolutely +necessary to meet requirements, the storage location /var/tmp should be bind mounted to +/tmp and thus share the same protections. + + +part /var/tmp --mountoptions="bind" + + - name: 'Bind Mount /var/tmp To /tmp: Check information associated to mountpoint' + command: findmnt --fstab '/var/tmp' + register: device_name + failed_when: device_name.rc > 1 + changed_when: false + when: ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", + "container"] and "/var/tmp" in ansible_mounts | map(attribute="mount") | list + ) + tags: + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - mount_option_var_tmp_bind + - no_reboot_needed + - unknown_severity + +- name: 'Bind Mount /var/tmp To /tmp: Create mount_info dictionary variable' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - '{{ device_name.stdout_lines[0].split() | list | lower }}' + - '{{ device_name.stdout_lines[1].split() | list }}' + when: + - ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + and "/var/tmp" in ansible_mounts | map(attribute="mount") | list ) + - device_name.stdout is defined and device_name.stdout_lines is defined + - (device_name.stdout | length > 0) + tags: + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - mount_option_var_tmp_bind + - no_reboot_needed + - unknown_severity + +- name: 'Bind Mount /var/tmp To /tmp: If /var/tmp not mounted, craft mount_info manually' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - - target + - source + - fstype + - options + - - /var/tmp + - '' + - '' + - defaults + when: + - ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + and "/var/tmp" in ansible_mounts | map(attribute="mount") | list ) + - ("--fstab" | length == 0) + - (device_name.stdout | length == 0) + tags: + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - mount_option_var_tmp_bind + - no_reboot_needed + - unknown_severity + +- name: 'Bind Mount /var/tmp To /tmp: Make sure bind option is part of the to /var/tmp + options' + set_fact: + mount_info: '{{ mount_info | combine( {''options'':''''~mount_info.options~'',bind'' + }) }}' + when: + - ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + and "/var/tmp" in ansible_mounts | map(attribute="mount") | list ) + - mount_info is defined and "bind" not in mount_info.options + tags: + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - mount_option_var_tmp_bind + - no_reboot_needed + - unknown_severity + +- name: 'Bind Mount /var/tmp To /tmp: Ensure /var/tmp is mounted with bind option' + mount: + path: /var/tmp + src: '{{ mount_info.source }}' + opts: '{{ mount_info.options }}' + state: mounted + fstype: '{{ mount_info.fstype }}' + when: + - ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + and "/var/tmp" in ansible_mounts | map(attribute="mount") | list ) + - (device_name.stdout is defined and (device_name.stdout | length > 0)) or ("--fstab" + | length == 0) + tags: + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-MP-7 + - configure_strategy + - high_disruption + - low_complexity + - mount_option_var_tmp_bind + - no_reboot_needed + - unknown_severity + + # Remediation is applicable only in certain platforms +if ( [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && findmnt --kernel "/var/tmp" > /dev/null ); then + +# Delete particular /etc/fstab's row if /var/tmp is already configured to +# represent a mount point (for some device or filesystem other than /tmp) +if grep -q -P '.*\/var\/tmp.*' /etc/fstab +then + sed -i '/.*\/var\/tmp.*/d' /etc/fstab +fi +umount /var/tmp + +# Bind-mount /var/tmp to /tmp via /etc/fstab (preserving the /etc/fstab form) +printf "%-24s%-24s%-8s%-32s%-3s\n" "/tmp" "/var/tmp" "none" "rw,nodev,noexec,nosuid,bind" "0 0" >> /etc/fstab + +mkdir -p /var/tmp +mount -B /tmp /var/tmp + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + Add nodev Option to /var/tmp + The nodev mount option can be used to prevent device files from +being created in /var/tmp. Legitimate character and block devices +should not exist within temporary directories like /var/tmp. +Add the nodev option to the fourth column of +/etc/fstab for the line which controls mounting of +/var/tmp. + BP28(R12) + CCI-001764 + SRG-OS-000368-GPOS-00154 + The only legitimate location for device files is the /dev directory +located on the root partition. The only exception to this is chroot jails. + + CCE-83864-9 + +part /var/tmp --mountoptions="nodev" + + - name: 'Add nodev Option to /var/tmp: Check information associated to mountpoint' + command: findmnt --fstab '/var/tmp' + register: device_name + failed_when: device_name.rc > 1 + changed_when: false + when: ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", + "container"] and "/var/tmp" in ansible_mounts | map(attribute="mount") | list + ) + tags: + - CCE-83864-9 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_tmp_nodev + - no_reboot_needed + +- name: 'Add nodev Option to /var/tmp: Create mount_info dictionary variable' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - '{{ device_name.stdout_lines[0].split() | list | lower }}' + - '{{ device_name.stdout_lines[1].split() | list }}' + when: + - ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + and "/var/tmp" in ansible_mounts | map(attribute="mount") | list ) + - device_name.stdout is defined and device_name.stdout_lines is defined + - (device_name.stdout | length > 0) + tags: + - CCE-83864-9 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_tmp_nodev + - no_reboot_needed + +- name: 'Add nodev Option to /var/tmp: If /var/tmp not mounted, craft mount_info manually' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - - target + - source + - fstype + - options + - - /var/tmp + - '' + - '' + - defaults + when: + - ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + and "/var/tmp" in ansible_mounts | map(attribute="mount") | list ) + - ("--fstab" | length == 0) + - (device_name.stdout | length == 0) + tags: + - CCE-83864-9 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_tmp_nodev + - no_reboot_needed + +- name: 'Add nodev Option to /var/tmp: Make sure nodev option is part of the to /var/tmp + options' + set_fact: + mount_info: '{{ mount_info | combine( {''options'':''''~mount_info.options~'',nodev'' + }) }}' + when: + - ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + and "/var/tmp" in ansible_mounts | map(attribute="mount") | list ) + - mount_info is defined and "nodev" not in mount_info.options + tags: + - CCE-83864-9 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_tmp_nodev + - no_reboot_needed + +- name: 'Add nodev Option to /var/tmp: Ensure /var/tmp is mounted with nodev option' + mount: + path: /var/tmp + src: '{{ mount_info.source }}' + opts: '{{ mount_info.options }}' + state: mounted + fstype: '{{ mount_info.fstype }}' + when: + - ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + and "/var/tmp" in ansible_mounts | map(attribute="mount") | list ) + - (device_name.stdout is defined and (device_name.stdout | length > 0)) or ("--fstab" + | length == 0) + tags: + - CCE-83864-9 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_tmp_nodev + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if ( [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && findmnt --kernel "/var/tmp" > /dev/null ); then + +function perform_remediation { + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" "/var/tmp")" + + grep "$mount_point_match_regexp" -q /etc/fstab \ + || { echo "The mount point '/var/tmp' is not even in /etc/fstab, so we can't set up mount options" >&2; + echo "Not remediating, because there is no record of /var/tmp in /etc/fstab" >&2; return 1; } + + + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" /var/tmp)" + + # If the mount point is not in /etc/fstab, get previous mount options from /etc/mtab + if [ "$(grep -c "$mount_point_match_regexp" /etc/fstab)" -eq 0 ]; then + # runtime opts without some automatic kernel/userspace-added defaults + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/mtab | head -1 | awk '{print $4}' \ + | sed -E "s/(rw|defaults|seclabel|nodev)(,|$)//g;s/,$//") + [ "$previous_mount_opts" ] && previous_mount_opts+="," + echo " /var/tmp defaults,${previous_mount_opts}nodev 0 0" >> /etc/fstab + # If the mount_opt option is not already in the mount point's /etc/fstab entry, add it + elif [ "$(grep "$mount_point_match_regexp" /etc/fstab | grep -c "nodev")" -eq 0 ]; then + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/fstab | awk '{print $4}') + sed -i "s|\(${mount_point_match_regexp}.*${previous_mount_opts}\)|\1,nodev|" /etc/fstab + fi + + + if mkdir -p "/var/tmp"; then + if mountpoint -q "/var/tmp"; then + mount -o remount --target "/var/tmp" + else + mount --target "/var/tmp" + fi + fi +} + +perform_remediation + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Add noexec Option to /var/tmp + The noexec mount option can be used to prevent binaries +from being executed out of /var/tmp. +Add the noexec option to the fourth column of +/etc/fstab for the line which controls mounting of +/var/tmp. + BP28(R12) + CCI-001764 + SRG-OS-000368-GPOS-00154 + Allowing users to execute binaries from world-writable directories +such as /var/tmp should never be necessary in normal operation and +can expose the system to potential compromise. + + CCE-83866-4 + +part /var/tmp --mountoptions="noexec" + + - name: 'Add noexec Option to /var/tmp: Check information associated to mountpoint' + command: findmnt --fstab '/var/tmp' + register: device_name + failed_when: device_name.rc > 1 + changed_when: false + when: ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", + "container"] and "/var/tmp" in ansible_mounts | map(attribute="mount") | list + ) + tags: + - CCE-83866-4 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_tmp_noexec + - no_reboot_needed + +- name: 'Add noexec Option to /var/tmp: Create mount_info dictionary variable' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - '{{ device_name.stdout_lines[0].split() | list | lower }}' + - '{{ device_name.stdout_lines[1].split() | list }}' + when: + - ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + and "/var/tmp" in ansible_mounts | map(attribute="mount") | list ) + - device_name.stdout is defined and device_name.stdout_lines is defined + - (device_name.stdout | length > 0) + tags: + - CCE-83866-4 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_tmp_noexec + - no_reboot_needed + +- name: 'Add noexec Option to /var/tmp: If /var/tmp not mounted, craft mount_info + manually' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - - target + - source + - fstype + - options + - - /var/tmp + - '' + - '' + - defaults + when: + - ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + and "/var/tmp" in ansible_mounts | map(attribute="mount") | list ) + - ("--fstab" | length == 0) + - (device_name.stdout | length == 0) + tags: + - CCE-83866-4 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_tmp_noexec + - no_reboot_needed + +- name: 'Add noexec Option to /var/tmp: Make sure noexec option is part of the to + /var/tmp options' + set_fact: + mount_info: '{{ mount_info | combine( {''options'':''''~mount_info.options~'',noexec'' + }) }}' + when: + - ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + and "/var/tmp" in ansible_mounts | map(attribute="mount") | list ) + - mount_info is defined and "noexec" not in mount_info.options + tags: + - CCE-83866-4 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_tmp_noexec + - no_reboot_needed + +- name: 'Add noexec Option to /var/tmp: Ensure /var/tmp is mounted with noexec option' + mount: + path: /var/tmp + src: '{{ mount_info.source }}' + opts: '{{ mount_info.options }}' + state: mounted + fstype: '{{ mount_info.fstype }}' + when: + - ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + and "/var/tmp" in ansible_mounts | map(attribute="mount") | list ) + - (device_name.stdout is defined and (device_name.stdout | length > 0)) or ("--fstab" + | length == 0) + tags: + - CCE-83866-4 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_tmp_noexec + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if ( [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && findmnt --kernel "/var/tmp" > /dev/null ); then + +function perform_remediation { + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" "/var/tmp")" + + grep "$mount_point_match_regexp" -q /etc/fstab \ + || { echo "The mount point '/var/tmp' is not even in /etc/fstab, so we can't set up mount options" >&2; + echo "Not remediating, because there is no record of /var/tmp in /etc/fstab" >&2; return 1; } + + + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" /var/tmp)" + + # If the mount point is not in /etc/fstab, get previous mount options from /etc/mtab + if [ "$(grep -c "$mount_point_match_regexp" /etc/fstab)" -eq 0 ]; then + # runtime opts without some automatic kernel/userspace-added defaults + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/mtab | head -1 | awk '{print $4}' \ + | sed -E "s/(rw|defaults|seclabel|noexec)(,|$)//g;s/,$//") + [ "$previous_mount_opts" ] && previous_mount_opts+="," + echo " /var/tmp defaults,${previous_mount_opts}noexec 0 0" >> /etc/fstab + # If the mount_opt option is not already in the mount point's /etc/fstab entry, add it + elif [ "$(grep "$mount_point_match_regexp" /etc/fstab | grep -c "noexec")" -eq 0 ]; then + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/fstab | awk '{print $4}') + sed -i "s|\(${mount_point_match_regexp}.*${previous_mount_opts}\)|\1,noexec|" /etc/fstab + fi + + + if mkdir -p "/var/tmp"; then + if mountpoint -q "/var/tmp"; then + mount -o remount --target "/var/tmp" + else + mount --target "/var/tmp" + fi + fi +} + +perform_remediation + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Add nosuid Option to /var/tmp + The nosuid mount option can be used to prevent +execution of setuid programs in /var/tmp. The SUID and SGID permissions +should not be required in these world-writable directories. +Add the nosuid option to the fourth column of +/etc/fstab for the line which controls mounting of +/var/tmp. + BP28(R12) + CCI-001764 + SRG-OS-000368-GPOS-00154 + The presence of SUID and SGID executables should be tightly controlled. Users +should not be able to execute SUID or SGID binaries from temporary storage partitions. + + CCE-83863-1 + +part /var/tmp --mountoptions="nosuid" + + - name: 'Add nosuid Option to /var/tmp: Check information associated to mountpoint' + command: findmnt --fstab '/var/tmp' + register: device_name + failed_when: device_name.rc > 1 + changed_when: false + when: ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", + "container"] and "/var/tmp" in ansible_mounts | map(attribute="mount") | list + ) + tags: + - CCE-83863-1 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_tmp_nosuid + - no_reboot_needed + +- name: 'Add nosuid Option to /var/tmp: Create mount_info dictionary variable' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - '{{ device_name.stdout_lines[0].split() | list | lower }}' + - '{{ device_name.stdout_lines[1].split() | list }}' + when: + - ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + and "/var/tmp" in ansible_mounts | map(attribute="mount") | list ) + - device_name.stdout is defined and device_name.stdout_lines is defined + - (device_name.stdout | length > 0) + tags: + - CCE-83863-1 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_tmp_nosuid + - no_reboot_needed + +- name: 'Add nosuid Option to /var/tmp: If /var/tmp not mounted, craft mount_info + manually' + set_fact: + mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}' + with_together: + - - target + - source + - fstype + - options + - - /var/tmp + - '' + - '' + - defaults + when: + - ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + and "/var/tmp" in ansible_mounts | map(attribute="mount") | list ) + - ("--fstab" | length == 0) + - (device_name.stdout | length == 0) + tags: + - CCE-83863-1 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_tmp_nosuid + - no_reboot_needed + +- name: 'Add nosuid Option to /var/tmp: Make sure nosuid option is part of the to + /var/tmp options' + set_fact: + mount_info: '{{ mount_info | combine( {''options'':''''~mount_info.options~'',nosuid'' + }) }}' + when: + - ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + and "/var/tmp" in ansible_mounts | map(attribute="mount") | list ) + - mount_info is defined and "nosuid" not in mount_info.options + tags: + - CCE-83863-1 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_tmp_nosuid + - no_reboot_needed + +- name: 'Add nosuid Option to /var/tmp: Ensure /var/tmp is mounted with nosuid option' + mount: + path: /var/tmp + src: '{{ mount_info.source }}' + opts: '{{ mount_info.options }}' + state: mounted + fstype: '{{ mount_info.fstype }}' + when: + - ( ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + and "/var/tmp" in ansible_mounts | map(attribute="mount") | list ) + - (device_name.stdout is defined and (device_name.stdout | length > 0)) or ("--fstab" + | length == 0) + tags: + - CCE-83863-1 + - configure_strategy + - high_disruption + - low_complexity + - medium_severity + - mount_option_var_tmp_nosuid + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if ( [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && findmnt --kernel "/var/tmp" > /dev/null ); then + +function perform_remediation { + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" "/var/tmp")" + + grep "$mount_point_match_regexp" -q /etc/fstab \ + || { echo "The mount point '/var/tmp' is not even in /etc/fstab, so we can't set up mount options" >&2; + echo "Not remediating, because there is no record of /var/tmp in /etc/fstab" >&2; return 1; } + + + + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" /var/tmp)" + + # If the mount point is not in /etc/fstab, get previous mount options from /etc/mtab + if [ "$(grep -c "$mount_point_match_regexp" /etc/fstab)" -eq 0 ]; then + # runtime opts without some automatic kernel/userspace-added defaults + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/mtab | head -1 | awk '{print $4}' \ + | sed -E "s/(rw|defaults|seclabel|nosuid)(,|$)//g;s/,$//") + [ "$previous_mount_opts" ] && previous_mount_opts+="," + echo " /var/tmp defaults,${previous_mount_opts}nosuid 0 0" >> /etc/fstab + # If the mount_opt option is not already in the mount point's /etc/fstab entry, add it + elif [ "$(grep "$mount_point_match_regexp" /etc/fstab | grep -c "nosuid")" -eq 0 ]; then + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/fstab | awk '{print $4}') + sed -i "s|\(${mount_point_match_regexp}.*${previous_mount_opts}\)|\1,nosuid|" /etc/fstab + fi + + + if mkdir -p "/var/tmp"; then + if mountpoint -q "/var/tmp"; then + mount -o remount --target "/var/tmp" + else + mount --target "/var/tmp" + fi + fi +} + +perform_remediation + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Verify Permissions on Important Files and +Directories Are Configured in /etc/permissions.local + Permissions for many files on a system must be set +restrictively to ensure sensitive information is properly protected. +This section discusses the /etc/permissions.local file, where +expected permissions can be configured to be checked and fixed through +usage of the chkstat command. + + + Restrict Programs from Dangerous Execution Patterns + The recommendations in this section are designed to +ensure that the system's features to protect against potentially +dangerous program execution are activated. +These protections are applied at the system initialization or +kernel level, and defend against certain types of badly-configured +or compromised programs. + + kernel.unprivileged_bpf_disabled + Prevent unprivileged processes from using the bpf() syscall. + 2 + 1 + 2 + + + Disable the uvcvideo module + If the device contains a camera it should be covered or disabled when not in use. + CCI-000381 + CM-7 (a) + CM-7 (5) (b) + SRG-OS-000095-GPOS-00049 + SRG-OS-000370-GPOS-00155 + Failing to disconnect from collaborative computing devices (i.e., cameras) can result in subsequent compromises of organizational information. +Providing easy methods to physically disconnect from such devices after a collaborative computing session helps to ensure participants actually carry out the disconnect activity without having to go through complex and tedious procedures. + + CCE-86961-0 + - name: Ensure kernel module 'uvcvideo' is disabled + lineinfile: + create: true + dest: /etc/modprobe.d/uvcvideo.conf + regexp: uvcvideo + line: install uvcvideo /bin/true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86961-0 + - NIST-800-53-CM-7 (5) (b) + - NIST-800-53-CM-7 (a) + - disable_strategy + - kernel_module_uvcvideo_disabled + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + +- name: Ensure kernel module 'uvcvideo' is blacklisted + lineinfile: + create: true + dest: /etc/modprobe.d/uvcvideo.conf + regexp: ^blacklist uvcvideo$ + line: blacklist uvcvideo + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86961-0 + - NIST-800-53-CM-7 (5) (b) + - NIST-800-53-CM-7 (a) + - disable_strategy + - kernel_module_uvcvideo_disabled + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,install%20uvcvideo%20/bin/true%0Ablacklist%20uvcvideo%0A + mode: 0644 + path: /etc/modprobe.d/uvcvideo.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if LC_ALL=C grep -q -m 1 "^install uvcvideo" /etc/modprobe.d/uvcvideo.conf ; then + + sed -i 's#^install uvcvideo.*#install uvcvideo /bin/true#g' /etc/modprobe.d/uvcvideo.conf +else + echo -e "\n# Disable per security requirements" >> /etc/modprobe.d/uvcvideo.conf + echo "install uvcvideo /bin/true" >> /etc/modprobe.d/uvcvideo.conf +fi + +if ! LC_ALL=C grep -q -m 1 "^blacklist uvcvideo$" /etc/modprobe.d/uvcvideo.conf ; then + echo "blacklist uvcvideo" >> /etc/modprobe.d/uvcvideo.conf +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable storing core dumps + To set the runtime status of the kernel.core_pattern kernel parameter, run the following command: $ sudo sysctl -w kernel.core_pattern=|/bin/false +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: kernel.core_pattern = |/bin/false + CCI-000366 + SC-7(10) + FMT_SMF_EXT.1 + SRG-OS-000480-GPOS-00227 + A core dump includes a memory image taken at the time the operating system +terminates an application. The memory image could contain sensitive data and is generally useful +only for developers trying to debug problems. + + + CCE-83961-3 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*kernel.core_pattern.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83961-3 + - NIST-800-53-SC-7(10) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_kernel_core_pattern + +- name: Comment out any occurrences of kernel.core_pattern from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*kernel.core_pattern + replace: '#kernel.core_pattern' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83961-3 + - NIST-800-53-SC-7(10) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_kernel_core_pattern + +- name: Ensure sysctl kernel.core_pattern is set to |/bin/false + sysctl: + name: kernel.core_pattern + value: '|/bin/false' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83961-3 + - NIST-800-53-SC-7(10) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_kernel_core_pattern + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,kernel.core_pattern%20%3D%20%7C/bin/false%0A + mode: 0644 + path: /etc/sysctl.d/75-sysctl_kernel_core_pattern.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of kernel.core_pattern from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*kernel.core_pattern.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "kernel.core_pattern" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done + +# +# Set runtime for kernel.core_pattern +# +/sbin/sysctl -q -n -w kernel.core_pattern="|/bin/false" + +# +# If kernel.core_pattern present in /etc/sysctl.conf, change value to "|/bin/false" +# else, add "kernel.core_pattern = |/bin/false" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^kernel.core_pattern") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "|/bin/false" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^kernel.core_pattern\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^kernel.core_pattern\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-83961-3" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable storing core dumps + The kernel.core_pattern option specifies the core dumpfile pattern +name. It can be set to an empty string. In this case, the kernel +behaves differently based on another related option. If +kernel.core_uses_pid is set to 1, then a file named as +.PID (where PID is process ID of the crashed process) is +created in the working directory. If kernel.core_uses_pid is set to +0, no coredump is saved. +To set the runtime status of the kernel.core_pattern kernel parameter, +run the following command: +$ sudo sysctl -w kernel.core_pattern= + +To make sure that the setting is persistent, +add the following line to a file in the directory /etc/sysctl.d: +kernel.core_pattern = + FMT_SMF_EXT.1 + A core dump includes a memory image taken at the time the operating system +terminates an application. The memory image could contain sensitive data and is generally useful +only for developers trying to debug problems. + + + + CCE-86005-6 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*kernel.core_pattern.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86005-6 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_kernel_core_pattern_empty_string + +- name: Comment out any occurrences of kernel.core_pattern from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*kernel.core_pattern + replace: '#kernel.core_pattern' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86005-6 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_kernel_core_pattern_empty_string + +- name: Comment out any occurrences of kernel.core_pattern with value from /etc/sysctl.conf + files + replace: + path: /etc/sysctl.conf + regexp: ^[\s]*kernel.core_pattern([ \t]*=[ \t]*\S+) + replace: '#kernel.core_pattern\1' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86005-6 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_kernel_core_pattern_empty_string + +- name: Ensure sysctl kernel.core_pattern is set to empty + sysctl: + name: kernel.core_pattern + value: ' ' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86005-6 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_kernel_core_pattern_empty_string + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of kernel.core_pattern from /etc/sysctl.d/*.conf files +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*kernel.core_pattern.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "kernel.core_pattern" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done + +# +# Set runtime for kernel.core_pattern +# +/sbin/sysctl -q -n -w kernel.core_pattern="" + +# +# If kernel.core_pattern present in /etc/sysctl.conf, change value to empty +# else, add "kernel.core_pattern =" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^kernel.core_pattern") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s=" "$stripped_key" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^kernel.core_pattern\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^kernel.core_pattern\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure file name of core dumps + To set the runtime status of the kernel.core_uses_pid kernel parameter, run the following command: $ sudo sysctl -w kernel.core_uses_pid=0 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: kernel.core_uses_pid = 0 + FMT_SMF_EXT.1 + The default coredump filename is core. By setting +core_uses_pid to 1, the coredump filename becomes +core.PID. If core_pattern does not include +%p (default does not) and core_uses_pid is set, then +.PID will be appended to the filename. +When combined with kernel.core_pattern = "" configuration, it +is ensured that no core dumps are generated and also no confusing error +messages are printed by a shell. + + CCE-86003-1 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*kernel.core_uses_pid.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86003-1 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_kernel_core_uses_pid + +- name: Comment out any occurrences of kernel.core_uses_pid from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*kernel.core_uses_pid + replace: '#kernel.core_uses_pid' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86003-1 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_kernel_core_uses_pid + +- name: Ensure sysctl kernel.core_uses_pid is set to 0 + sysctl: + name: kernel.core_uses_pid + value: '0' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86003-1 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_kernel_core_uses_pid + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of kernel.core_uses_pid from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*kernel.core_uses_pid.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "kernel.core_uses_pid" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done + +# +# Set runtime for kernel.core_uses_pid +# +/sbin/sysctl -q -n -w kernel.core_uses_pid="0" + +# +# If kernel.core_uses_pid present in /etc/sysctl.conf, change value to "0" +# else, add "kernel.core_uses_pid = 0" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^kernel.core_uses_pid") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "0" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^kernel.core_uses_pid\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^kernel.core_uses_pid\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-86003-1" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Restrict Access to Kernel Message Buffer + To set the runtime status of the kernel.dmesg_restrict kernel parameter, run the following command: $ sudo sysctl -w kernel.dmesg_restrict=1 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: kernel.dmesg_restrict = 1 + BP28(R23) + 3.1.5 + CCI-001090 + CCI-001314 + 164.308(a)(1)(ii)(D) + 164.308(a)(3) + 164.308(a)(4) + 164.310(b) + 164.310(c) + 164.312(a) + 164.312(e) + SI-11(a) + SI-11(b) + SRG-OS-000132-GPOS-00067 + SRG-OS-000138-GPOS-00069 + Unprivileged access to the kernel syslog can expose sensitive kernel +address information. + + CCE-83952-2 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*kernel.dmesg_restrict.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83952-2 + - NIST-800-171-3.1.5 + - NIST-800-53-SI-11(a) + - NIST-800-53-SI-11(b) + - disable_strategy + - low_complexity + - low_severity + - medium_disruption + - reboot_required + - sysctl_kernel_dmesg_restrict + +- name: Comment out any occurrences of kernel.dmesg_restrict from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*kernel.dmesg_restrict + replace: '#kernel.dmesg_restrict' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83952-2 + - NIST-800-171-3.1.5 + - NIST-800-53-SI-11(a) + - NIST-800-53-SI-11(b) + - disable_strategy + - low_complexity + - low_severity + - medium_disruption + - reboot_required + - sysctl_kernel_dmesg_restrict + +- name: Ensure sysctl kernel.dmesg_restrict is set to 1 + sysctl: + name: kernel.dmesg_restrict + value: '1' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83952-2 + - NIST-800-171-3.1.5 + - NIST-800-53-SI-11(a) + - NIST-800-53-SI-11(b) + - disable_strategy + - low_complexity + - low_severity + - medium_disruption + - reboot_required + - sysctl_kernel_dmesg_restrict + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,kernel.dmesg_restrict%3D1%0A + mode: 0644 + path: /etc/sysctl.d/75-sysctl_kernel_dmesg_restrict.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of kernel.dmesg_restrict from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*kernel.dmesg_restrict.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "kernel.dmesg_restrict" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done + +# +# Set runtime for kernel.dmesg_restrict +# +/sbin/sysctl -q -n -w kernel.dmesg_restrict="1" + +# +# If kernel.dmesg_restrict present in /etc/sysctl.conf, change value to "1" +# else, add "kernel.dmesg_restrict = 1" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^kernel.dmesg_restrict") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "1" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^kernel.dmesg_restrict\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^kernel.dmesg_restrict\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-83952-2" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable Kernel Image Loading + To set the runtime status of the kernel.kexec_load_disabled kernel parameter, run the following command: $ sudo sysctl -w kernel.kexec_load_disabled=1 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: kernel.kexec_load_disabled = 1 + CCI-001749 + CM-6 + SRG-OS-000480-GPOS-00227 + SRG-OS-000366-GPOS-00153 + Disabling kexec_load allows greater control of the kernel memory. +It makes it impossible to load another kernel image after it has been disabled. + + + CCE-83954-8 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*kernel.kexec_load_disabled.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83954-8 + - NIST-800-53-CM-6 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_kernel_kexec_load_disabled + +- name: Comment out any occurrences of kernel.kexec_load_disabled from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*kernel.kexec_load_disabled + replace: '#kernel.kexec_load_disabled' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83954-8 + - NIST-800-53-CM-6 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_kernel_kexec_load_disabled + +- name: Ensure sysctl kernel.kexec_load_disabled is set to 1 + sysctl: + name: kernel.kexec_load_disabled + value: '1' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83954-8 + - NIST-800-53-CM-6 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_kernel_kexec_load_disabled + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,kernel.kexec_load_disabled%3D1%0A + mode: 0644 + path: /etc/sysctl.d/75-sysctl_kernel_kexec_load_disabled.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of kernel.kexec_load_disabled from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*kernel.kexec_load_disabled.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "kernel.kexec_load_disabled" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done + +# +# Set runtime for kernel.kexec_load_disabled +# +/sbin/sysctl -q -n -w kernel.kexec_load_disabled="1" + +# +# If kernel.kexec_load_disabled present in /etc/sysctl.conf, change value to "1" +# else, add "kernel.kexec_load_disabled = 1" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^kernel.kexec_load_disabled") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "1" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^kernel.kexec_load_disabled\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^kernel.kexec_load_disabled\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-83954-8" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable loading and unloading of kernel modules + To set the runtime status of the kernel.modules_disabled kernel parameter, run the following command: $ sudo sysctl -w kernel.modules_disabled=1 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: kernel.modules_disabled = 1 + This rule doesn't come with Bash remediation. Remediating this rule during the installation process disrupts the install and boot process. + BP28(R24) + Malicious kernel modules can have a significant impact on system security and +availability. Disabling loading of kernel modules prevents this threat. Note +that once this option has been set, it cannot be reverted without doing a +system reboot. Make sure that all needed kernel modules are loaded before +setting this option. + + CCE-83967-0 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*kernel.modules_disabled.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83967-0 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_kernel_modules_disabled + +- name: Comment out any occurrences of kernel.modules_disabled from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*kernel.modules_disabled + replace: '#kernel.modules_disabled' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83967-0 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_kernel_modules_disabled + +- name: Ensure sysctl kernel.modules_disabled is set to 1 + sysctl: + name: kernel.modules_disabled + value: '1' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83967-0 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_kernel_modules_disabled + + + + + + + + + + Kernel panic on oops + To set the runtime status of the kernel.panic_on_oops kernel parameter, run the following command: $ sudo sysctl -w kernel.panic_on_oops=1 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: kernel.panic_on_oops = 1 + The system may start to panic when it normally wouldn't. A non-catastrophic error that +would have allowed the system to continue operating will now result in a panic. + An attacker trying to exploit the kernel may trigger kernel OOPSes, +panicking the system will impede them from continuing. + + CCE-88666-3 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*kernel.panic_on_oops.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-88666-3 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_kernel_panic_on_oops + +- name: Comment out any occurrences of kernel.panic_on_oops from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*kernel.panic_on_oops + replace: '#kernel.panic_on_oops' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-88666-3 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_kernel_panic_on_oops + +- name: Ensure sysctl kernel.panic_on_oops is set to 1 + sysctl: + name: kernel.panic_on_oops + value: '1' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-88666-3 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_kernel_panic_on_oops + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of kernel.panic_on_oops from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*kernel.panic_on_oops.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "kernel.panic_on_oops" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done + +# +# Set runtime for kernel.panic_on_oops +# +/sbin/sysctl -q -n -w kernel.panic_on_oops="1" + +# +# If kernel.panic_on_oops present in /etc/sysctl.conf, change value to "1" +# else, add "kernel.panic_on_oops = 1" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^kernel.panic_on_oops") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "1" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^kernel.panic_on_oops\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^kernel.panic_on_oops\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-88666-3" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Limit CPU consumption of the Perf system + To set the runtime status of the kernel.perf_cpu_time_max_percent kernel parameter, run the following command: $ sudo sysctl -w kernel.perf_cpu_time_max_percent=1 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: kernel.perf_cpu_time_max_percent = 1 + BP28(R23) + The kernel.perf_cpu_time_max_percent configures a treshold of +maximum percentile of CPU that can be used by Perf system. Restricting usage +of Perf system decreases risk of potential availability problems. + + CCE-83969-6 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*kernel.perf_cpu_time_max_percent.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83969-6 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_kernel_perf_cpu_time_max_percent + +- name: Comment out any occurrences of kernel.perf_cpu_time_max_percent from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*kernel.perf_cpu_time_max_percent + replace: '#kernel.perf_cpu_time_max_percent' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83969-6 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_kernel_perf_cpu_time_max_percent + +- name: Ensure sysctl kernel.perf_cpu_time_max_percent is set to 1 + sysctl: + name: kernel.perf_cpu_time_max_percent + value: '1' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83969-6 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_kernel_perf_cpu_time_max_percent + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of kernel.perf_cpu_time_max_percent from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*kernel.perf_cpu_time_max_percent.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "kernel.perf_cpu_time_max_percent" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done + +# +# Set runtime for kernel.perf_cpu_time_max_percent +# +/sbin/sysctl -q -n -w kernel.perf_cpu_time_max_percent="1" + +# +# If kernel.perf_cpu_time_max_percent present in /etc/sysctl.conf, change value to "1" +# else, add "kernel.perf_cpu_time_max_percent = 1" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^kernel.perf_cpu_time_max_percent") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "1" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^kernel.perf_cpu_time_max_percent\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^kernel.perf_cpu_time_max_percent\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-83969-6" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Limit sampling frequency of the Perf system + To set the runtime status of the kernel.perf_event_max_sample_rate kernel parameter, run the following command: $ sudo sysctl -w kernel.perf_event_max_sample_rate=1 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: kernel.perf_event_max_sample_rate = 1 + BP28(R23) + The kernel.perf_event_max_sample_rate parameter configures maximum +frequency of collecting of samples for the Perf system. It is expressed in +samples per second. Restricting usage of Perf system decreases risk +of potential availability problems. + + CCE-83962-1 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*kernel.perf_event_max_sample_rate.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83962-1 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_kernel_perf_event_max_sample_rate + +- name: Comment out any occurrences of kernel.perf_event_max_sample_rate from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*kernel.perf_event_max_sample_rate + replace: '#kernel.perf_event_max_sample_rate' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83962-1 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_kernel_perf_event_max_sample_rate + +- name: Ensure sysctl kernel.perf_event_max_sample_rate is set to 1 + sysctl: + name: kernel.perf_event_max_sample_rate + value: '1' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83962-1 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_kernel_perf_event_max_sample_rate + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of kernel.perf_event_max_sample_rate from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*kernel.perf_event_max_sample_rate.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "kernel.perf_event_max_sample_rate" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done + +# +# Set runtime for kernel.perf_event_max_sample_rate +# +/sbin/sysctl -q -n -w kernel.perf_event_max_sample_rate="1" + +# +# If kernel.perf_event_max_sample_rate present in /etc/sysctl.conf, change value to "1" +# else, add "kernel.perf_event_max_sample_rate = 1" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^kernel.perf_event_max_sample_rate") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "1" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^kernel.perf_event_max_sample_rate\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^kernel.perf_event_max_sample_rate\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-83962-1" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disallow kernel profiling by unprivileged users + To set the runtime status of the kernel.perf_event_paranoid kernel parameter, run the following command: $ sudo sysctl -w kernel.perf_event_paranoid=2 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: kernel.perf_event_paranoid = 2 + BP28(R23) + CCI-001090 + AC-6 + FMT_SMF_EXT.1 + SRG-OS-000132-GPOS-00067 + SRG-OS-000138-GPOS-00069 + Kernel profiling can reveal sensitive information about kernel behaviour. + + CCE-83959-7 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*kernel.perf_event_paranoid.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83959-7 + - NIST-800-53-AC-6 + - disable_strategy + - low_complexity + - low_severity + - medium_disruption + - reboot_required + - sysctl_kernel_perf_event_paranoid + +- name: Comment out any occurrences of kernel.perf_event_paranoid from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*kernel.perf_event_paranoid + replace: '#kernel.perf_event_paranoid' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83959-7 + - NIST-800-53-AC-6 + - disable_strategy + - low_complexity + - low_severity + - medium_disruption + - reboot_required + - sysctl_kernel_perf_event_paranoid + +- name: Ensure sysctl kernel.perf_event_paranoid is set to 2 + sysctl: + name: kernel.perf_event_paranoid + value: '2' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83959-7 + - NIST-800-53-AC-6 + - disable_strategy + - low_complexity + - low_severity + - medium_disruption + - reboot_required + - sysctl_kernel_perf_event_paranoid + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,kernel.perf_event_paranoid%3D2%0A + mode: 0644 + path: /etc/sysctl.d/75-sysctl_kernel_perf_event_paranoid.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of kernel.perf_event_paranoid from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*kernel.perf_event_paranoid.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "kernel.perf_event_paranoid" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done + +# +# Set runtime for kernel.perf_event_paranoid +# +/sbin/sysctl -q -n -w kernel.perf_event_paranoid="2" + +# +# If kernel.perf_event_paranoid present in /etc/sysctl.conf, change value to "2" +# else, add "kernel.perf_event_paranoid = 2" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^kernel.perf_event_paranoid") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "2" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^kernel.perf_event_paranoid\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^kernel.perf_event_paranoid\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-83959-7" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure maximum number of process identifiers + To set the runtime status of the kernel.pid_max kernel parameter, run the following command: $ sudo sysctl -w kernel.pid_max=65536 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: kernel.pid_max = 65536 + BP28(R23) + The kernel.pid_max parameter configures upper limit on process +identifiers (PID). If this number is not high enough, it might happen that +forking of new processes is not possible, because all available PIDs are +exhausted. Increasing this number enhances availability. + + CCE-83960-5 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*kernel.pid_max.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83960-5 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_kernel_pid_max + +- name: Comment out any occurrences of kernel.pid_max from /etc/sysctl.d/*.conf files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*kernel.pid_max + replace: '#kernel.pid_max' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83960-5 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_kernel_pid_max + +- name: Ensure sysctl kernel.pid_max is set to 65536 + sysctl: + name: kernel.pid_max + value: '65536' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83960-5 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_kernel_pid_max + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of kernel.pid_max from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*kernel.pid_max.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "kernel.pid_max" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done + +# +# Set runtime for kernel.pid_max +# +/sbin/sysctl -q -n -w kernel.pid_max="65536" + +# +# If kernel.pid_max present in /etc/sysctl.conf, change value to "65536" +# else, add "kernel.pid_max = 65536" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^kernel.pid_max") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "65536" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^kernel.pid_max\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^kernel.pid_max\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-83960-5" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disallow magic SysRq key + To set the runtime status of the kernel.sysrq kernel parameter, run the following command: $ sudo sysctl -w kernel.sysrq=0 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: kernel.sysrq = 0 + BP28(R23) + The Magic SysRq key allows sending certain commands directly to the running +kernel. It can dump various system and process information, potentially +revealing sensitive information. It can also reboot or shutdown the machine, +disturbing its availability. + + CCE-83968-8 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*kernel.sysrq.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83968-8 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_kernel_sysrq + +- name: Comment out any occurrences of kernel.sysrq from /etc/sysctl.d/*.conf files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*kernel.sysrq + replace: '#kernel.sysrq' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83968-8 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_kernel_sysrq + +- name: Ensure sysctl kernel.sysrq is set to 0 + sysctl: + name: kernel.sysrq + value: '0' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83968-8 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_kernel_sysrq + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of kernel.sysrq from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*kernel.sysrq.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "kernel.sysrq" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done + +# +# Set runtime for kernel.sysrq +# +/sbin/sysctl -q -n -w kernel.sysrq="0" + +# +# If kernel.sysrq present in /etc/sysctl.conf, change value to "0" +# else, add "kernel.sysrq = 0" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^kernel.sysrq") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "0" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^kernel.sysrq\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^kernel.sysrq\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-83968-8" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable Access to Network bpf() Syscall From Unprivileged Processes + To set the runtime status of the kernel.unprivileged_bpf_disabled kernel parameter, run the following command: $ sudo sysctl -w kernel.unprivileged_bpf_disabled=1 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: kernel.unprivileged_bpf_disabled = 1 + CCI-000366 + AC-6 + SC-7(10) + FMT_SMF_EXT.1 + SRG-OS-000132-GPOS-00067 + SRG-OS-000480-GPOS-00227 + Loading and accessing the packet filters programs and maps using the bpf() +syscall has the potential of revealing sensitive information about the kernel state. + + CCE-83957-1 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*kernel.unprivileged_bpf_disabled.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83957-1 + - NIST-800-53-AC-6 + - NIST-800-53-SC-7(10) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_kernel_unprivileged_bpf_disabled + +- name: Comment out any occurrences of kernel.unprivileged_bpf_disabled from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*kernel.unprivileged_bpf_disabled + replace: '#kernel.unprivileged_bpf_disabled' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83957-1 + - NIST-800-53-AC-6 + - NIST-800-53-SC-7(10) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_kernel_unprivileged_bpf_disabled + +- name: Ensure sysctl kernel.unprivileged_bpf_disabled is set to 1 + sysctl: + name: kernel.unprivileged_bpf_disabled + value: '1' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83957-1 + - NIST-800-53-AC-6 + - NIST-800-53-SC-7(10) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_kernel_unprivileged_bpf_disabled + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,kernel.unprivileged_bpf_disabled%3D1%0A + mode: 0644 + path: /etc/sysctl.d/75-sysctl_kernel_unprivileged_bpf_disabled.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of kernel.unprivileged_bpf_disabled from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*kernel.unprivileged_bpf_disabled.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "kernel.unprivileged_bpf_disabled" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done + +# +# Set runtime for kernel.unprivileged_bpf_disabled +# +/sbin/sysctl -q -n -w kernel.unprivileged_bpf_disabled="1" + +# +# If kernel.unprivileged_bpf_disabled present in /etc/sysctl.conf, change value to "1" +# else, add "kernel.unprivileged_bpf_disabled = 1" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^kernel.unprivileged_bpf_disabled") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "1" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^kernel.unprivileged_bpf_disabled\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^kernel.unprivileged_bpf_disabled\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-83957-1" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable Access to Network bpf() Syscall From Unprivileged Processes + To prevent unprivileged processes from using the bpf() syscall +the kernel.unprivileged_bpf_disabled kernel parameter must +be set to 1 or 2. + +Writing 1 to this entry will disable unprivileged calls to bpf(); once +disabled, calling bpf() without CAP_SYS_ADMIN or CAP_BPF will return -EPERM. +Once set to 1, this can't be cleared from the running kernel anymore. + +To set the runtime status of the kernel.unprivileged_bpf_disabled kernel parameter, +run the following command: +$ sudo sysctl -w kernel.unprivileged_bpf_disabled=1 + +To make sure that the setting is persistent, +add the following line to a file in the directory /etc/sysctl.d: +kernel.unprivileged_bpf_disabled = 1 + +Writing 2 to this entry will also disable unprivileged calls to bpf(), +however, an admin can still change this setting later on, if needed, by +writing 0 or 1 to this entry. + +To set the runtime status of the kernel.unprivileged_bpf_disabled kernel parameter, +run the following command: +$ sudo sysctl -w kernel.unprivileged_bpf_disabled=2 + +To make sure that the setting is persistent, +add the following line to a file in the directory /etc/sysctl.d: +kernel.unprivileged_bpf_disabled = 2 + CCI-000366 + AC-6 + SC-7(10) + FMT_SMF_EXT.1 + SRG-OS-000132-GPOS-00067 + SRG-OS-000480-GPOS-00227 + Loading and accessing the packet filters programs and maps using the bpf() +syscall has the potential of revealing sensitive information about the kernel state. + + CCE-87712-6 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*kernel.unprivileged_bpf_disabled.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-87712-6 + - NIST-800-53-AC-6 + - NIST-800-53-SC-7(10) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_kernel_unprivileged_bpf_disabled_accept_default + +- name: Comment out any occurrences of kernel.unprivileged_bpf_disabled from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*kernel.unprivileged_bpf_disabled + replace: '#kernel.unprivileged_bpf_disabled' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-87712-6 + - NIST-800-53-AC-6 + - NIST-800-53-SC-7(10) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_kernel_unprivileged_bpf_disabled_accept_default +- name: XCCDF Value sysctl_kernel_unprivileged_bpf_disabled_value # promote to variable + set_fact: + sysctl_kernel_unprivileged_bpf_disabled_value: !!str + tags: + - always + +- name: Ensure sysctl kernel.unprivileged_bpf_disabled is set + sysctl: + name: kernel.unprivileged_bpf_disabled + value: '{{ sysctl_kernel_unprivileged_bpf_disabled_value }}' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-87712-6 + - NIST-800-53-AC-6 + - NIST-800-53-SC-7(10) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_kernel_unprivileged_bpf_disabled_accept_default + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of kernel.unprivileged_bpf_disabled from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*kernel.unprivileged_bpf_disabled.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "kernel.unprivileged_bpf_disabled" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done +sysctl_kernel_unprivileged_bpf_disabled_value='' + + +# +# Set runtime for kernel.unprivileged_bpf_disabled +# +/sbin/sysctl -q -n -w kernel.unprivileged_bpf_disabled="$sysctl_kernel_unprivileged_bpf_disabled_value" + +# +# If kernel.unprivileged_bpf_disabled present in /etc/sysctl.conf, change value to appropriate value +# else, add "kernel.unprivileged_bpf_disabled = value" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^kernel.unprivileged_bpf_disabled") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_kernel_unprivileged_bpf_disabled_value" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^kernel.unprivileged_bpf_disabled\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^kernel.unprivileged_bpf_disabled\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-87712-6" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Restrict usage of ptrace to descendant processes + To set the runtime status of the kernel.yama.ptrace_scope kernel parameter, run the following command: $ sudo sysctl -w kernel.yama.ptrace_scope=1 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: kernel.yama.ptrace_scope = 1 + BP28(R25) + CCI-000366 + SC-7(10) + SRG-OS-000132-GPOS-00067 + SRG-OS-000480-GPOS-00227 + Unrestricted usage of ptrace allows compromised binaries to run ptrace +on another processes of the user. Like this, the attacker can steal +sensitive information from the target processes (e.g. SSH sessions, web browser, ...) +without any additional assistance from the user (i.e. without resorting to phishing). + + + CCE-83965-4 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*kernel.yama.ptrace_scope.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83965-4 + - NIST-800-53-SC-7(10) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_kernel_yama_ptrace_scope + +- name: Comment out any occurrences of kernel.yama.ptrace_scope from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*kernel.yama.ptrace_scope + replace: '#kernel.yama.ptrace_scope' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83965-4 + - NIST-800-53-SC-7(10) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_kernel_yama_ptrace_scope + +- name: Ensure sysctl kernel.yama.ptrace_scope is set to 1 + sysctl: + name: kernel.yama.ptrace_scope + value: '1' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83965-4 + - NIST-800-53-SC-7(10) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_kernel_yama_ptrace_scope + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,kernel.yama.ptrace_scope%3D1%0A + mode: 0644 + path: /etc/sysctl.d/75-sysctl_kernel_yama_ptrace_scope.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of kernel.yama.ptrace_scope from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*kernel.yama.ptrace_scope.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "kernel.yama.ptrace_scope" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done + +# +# Set runtime for kernel.yama.ptrace_scope +# +/sbin/sysctl -q -n -w kernel.yama.ptrace_scope="1" + +# +# If kernel.yama.ptrace_scope present in /etc/sysctl.conf, change value to "1" +# else, add "kernel.yama.ptrace_scope = 1" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^kernel.yama.ptrace_scope") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "1" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^kernel.yama.ptrace_scope\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^kernel.yama.ptrace_scope\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-83965-4" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Harden the operation of the BPF just-in-time compiler + To set the runtime status of the net.core.bpf_jit_harden kernel parameter, run the following command: $ sudo sysctl -w net.core.bpf_jit_harden=2 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: net.core.bpf_jit_harden = 2 + CCI-000366 + CM-6 + SC-7(10) + FMT_SMF_EXT.1 + SRG-OS-000480-GPOS-00227 + When hardened, the extended Berkeley Packet Filter just-in-time compiler +will randomize any kernel addresses in the BPF programs and maps, +and will not expose the JIT addresses in /proc/kallsyms. + + CCE-83966-2 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*net.core.bpf_jit_harden.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83966-2 + - NIST-800-53-CM-6 + - NIST-800-53-SC-7(10) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_core_bpf_jit_harden + +- name: Comment out any occurrences of net.core.bpf_jit_harden from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*net.core.bpf_jit_harden + replace: '#net.core.bpf_jit_harden' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83966-2 + - NIST-800-53-CM-6 + - NIST-800-53-SC-7(10) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_core_bpf_jit_harden + +- name: Ensure sysctl net.core.bpf_jit_harden is set to 2 + sysctl: + name: net.core.bpf_jit_harden + value: '2' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83966-2 + - NIST-800-53-CM-6 + - NIST-800-53-SC-7(10) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_net_core_bpf_jit_harden + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,net.core.bpf_jit_harden%3D2%0A + mode: 0644 + path: /etc/sysctl.d/75-sysctl_net_core_bpf_jit_harden.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of net.core.bpf_jit_harden from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*net.core.bpf_jit_harden.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "net.core.bpf_jit_harden" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done + +# +# Set runtime for net.core.bpf_jit_harden +# +/sbin/sysctl -q -n -w net.core.bpf_jit_harden="2" + +# +# If net.core.bpf_jit_harden present in /etc/sysctl.conf, change value to "2" +# else, add "net.core.bpf_jit_harden = 2" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.core.bpf_jit_harden") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "2" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^net.core.bpf_jit_harden\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^net.core.bpf_jit_harden\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-83966-2" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable the use of user namespaces + To set the runtime status of the user.max_user_namespaces kernel parameter, +run the following command: +$ sudo sysctl -w user.max_user_namespaces=0 + +To make sure that the setting is persistent, +add the following line to a file in the directory /etc/sysctl.d: +user.max_user_namespaces = 0 +When containers are deployed on the machine, the value should be set +to large non-zero value. + This configuration baseline was created to deploy the base operating system for general purpose +workloads. When the operating system is configured for certain purposes, such as to host Linux Containers, +it is expected that user.max_user_namespaces will be enabled. + CCI-000366 + SC-39 + CM-6(a) + FMT_SMF_EXT.1 + SRG-OS-000480-GPOS-00227 + It is detrimental for operating systems to provide, or install by default, functionality exceeding requirements or system objectives. +These unnecessary capabilities or services are often overlooked and therefore may remain unsecured. +They increase the risk to the platform by providing additional attack vectors. +User namespaces are used primarily for Linux containers. The value 0 +disallows the use of user namespaces. + + CCE-83956-3 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*user.max_user_namespaces.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83956-3 + - NIST-800-53-CM-6(a) + - NIST-800-53-SC-39 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_user_max_user_namespaces + +- name: Comment out any occurrences of user.max_user_namespaces from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*user.max_user_namespaces + replace: '#user.max_user_namespaces' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83956-3 + - NIST-800-53-CM-6(a) + - NIST-800-53-SC-39 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_user_max_user_namespaces + +- name: Ensure sysctl user.max_user_namespaces is set to 0 + sysctl: + name: user.max_user_namespaces + value: '0' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83956-3 + - NIST-800-53-CM-6(a) + - NIST-800-53-SC-39 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_user_max_user_namespaces + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,user.max_user_namespaces%20%3D%200%0A + mode: 0644 + path: /etc/sysctl.d/75-sysctl_user_max_user_namespaces.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of user.max_user_namespaces from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*user.max_user_namespaces.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "user.max_user_namespaces" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done + +# +# Set runtime for user.max_user_namespaces +# +/sbin/sysctl -q -n -w user.max_user_namespaces="0" + +# +# If user.max_user_namespaces present in /etc/sysctl.conf, change value to "0" +# else, add "user.max_user_namespaces = 0" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^user.max_user_namespaces") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "0" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^user.max_user_namespaces\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^user.max_user_namespaces\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-83956-3" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Prevent applications from mapping low portion of virtual memory + To set the runtime status of the vm.mmap_min_addr kernel parameter, run the following command: $ sudo sysctl -w vm.mmap_min_addr=65536 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: vm.mmap_min_addr = 65536 + BP28(R23) + The vm.mmap_min_addr parameter specifies the minimum virtual +address that a process is allowed to mmap. Allowing a process to mmap low +portion of virtual memory can have security implications such as such as +heightened risk of kernel null pointer dereference defects. + + CCE-83958-9 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*vm.mmap_min_addr.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83958-9 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_vm_mmap_min_addr + +- name: Comment out any occurrences of vm.mmap_min_addr from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*vm.mmap_min_addr + replace: '#vm.mmap_min_addr' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83958-9 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_vm_mmap_min_addr + +- name: Ensure sysctl vm.mmap_min_addr is set to 65536 + sysctl: + name: vm.mmap_min_addr + value: '65536' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83958-9 + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_vm_mmap_min_addr + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of vm.mmap_min_addr from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*vm.mmap_min_addr.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "vm.mmap_min_addr" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done + +# +# Set runtime for vm.mmap_min_addr +# +/sbin/sysctl -q -n -w vm.mmap_min_addr="65536" + +# +# If vm.mmap_min_addr present in /etc/sysctl.conf, change value to "65536" +# else, add "vm.mmap_min_addr = 65536" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^vm.mmap_min_addr") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "65536" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^vm.mmap_min_addr\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^vm.mmap_min_addr\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-83958-9" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable Core Dumps + A core dump file is the memory image of an executable +program when it was terminated by the operating system due to +errant behavior. In most cases, only software developers +legitimately need to access these files. The core dump files may +also contain sensitive information, or unnecessarily occupy large +amounts of disk space. + +Once a hard limit is set in /etc/security/limits.conf, or +to a file within the /etc/security/limits.d/ directory, a +user cannot increase that limit within his or her own session. If access +to core dumps is required, consider restricting them to only +certain users or groups. See the limits.conf man page for more +information. + +The core dumps of setuid programs are further protected. The +sysctl variable fs.suid_dumpable controls whether +the kernel allows core dumps from these programs at all. The default +value of 0 is recommended. + + Disable acquiring, saving, and processing core dumps + The systemd-coredump.socket unit is a socket activation of +the systemd-coredump@.service which processes core dumps. +By masking the unit, core dump processing is disabled. + CCI-000366 + SC-7(10) + FMT_SMF_EXT.1 + SRG-OS-000480-GPOS-00227 + A core dump includes a memory image taken at the time the operating system +terminates an application. The memory image could contain sensitive data +and is generally useful only for developers trying to debug problems. + + CCE-83974-6 + include disable_systemd-coredump + +class disable_systemd-coredump { + service {'systemd-coredump': + enable => false, + ensure => 'stopped', + } +} + + - name: Disable service systemd-coredump + block: + + - name: Disable service systemd-coredump + systemd: + name: systemd-coredump.service + enabled: 'no' + state: stopped + masked: 'yes' + ignore_errors: 'yes' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83974-6 + - NIST-800-53-SC-7(10) + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_systemd-coredump_disabled + +- name: Unit Socket Exists - systemd-coredump.socket + command: systemctl list-unit-files systemd-coredump.socket + args: + warn: false + register: socket_file_exists + changed_when: false + ignore_errors: true + check_mode: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83974-6 + - NIST-800-53-SC-7(10) + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_systemd-coredump_disabled + +- name: Disable socket systemd-coredump + systemd: + name: systemd-coredump.socket + enabled: 'no' + state: stopped + masked: 'yes' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"systemd-coredump.socket" in socket_file_exists.stdout_lines[1]' + tags: + - CCE-83974-6 + - NIST-800-53-SC-7(10) + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_systemd-coredump_disabled + + +[customizations.services] +disabled = ["systemd-coredump"] + + apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + systemd: + units: + - name: systemd-coredump.service + enabled: false + mask: true + - name: systemd-coredump.socket + enabled: false + mask: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" stop 'systemd-coredump.service' +"$SYSTEMCTL_EXEC" disable 'systemd-coredump.service' +"$SYSTEMCTL_EXEC" mask 'systemd-coredump.service' +# Disable socket activation if we have a unit file for it +if "$SYSTEMCTL_EXEC" list-unit-files | grep -q '^systemd-coredump.socket'; then + "$SYSTEMCTL_EXEC" stop 'systemd-coredump.socket' + "$SYSTEMCTL_EXEC" mask 'systemd-coredump.socket' +fi +# The service may not be running because it has been started and failed, +# so let's reset the state so OVAL checks pass. +# Service should be 'inactive', not 'failed' after reboot though. +"$SYSTEMCTL_EXEC" reset-failed 'systemd-coredump.service' || true + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable core dump backtraces + The ProcessSizeMax option in [Coredump] section +of /etc/systemd/coredump.conf +specifies the maximum size in bytes of a core which will be processed. +Core dumps exceeding this size may be stored, but the backtrace will not +be generated. + If the /etc/systemd/coredump.conf file +does not already contain the [Coredump] section, +the value will not be configured correctly. + CCI-000366 + CM-6 + FMT_SMF_EXT.1 + SRG-OS-000480-GPOS-00227 + A core dump includes a memory image taken at the time the operating system +terminates an application. The memory image could contain sensitive data +and is generally useful only for developers or system operators trying to +debug problems. + +Enabling core dumps on production systems is not recommended, +however there may be overriding operational requirements to enable advanced +debuging. Permitting temporary enablement of core dumps during such situations +should be reviewed through local needs and policy. + CCE-83984-5 + - name: Disable core dump backtraces + block: + + - name: Check for duplicate values + lineinfile: + path: /etc/systemd/coredump.conf + create: false + regexp: ^\s*ProcessSizeMax\s*=\s* + state: absent + check_mode: true + changed_when: false + register: dupes + + - name: Deduplicate values from /etc/systemd/coredump.conf + lineinfile: + path: /etc/systemd/coredump.conf + create: false + regexp: ^\s*ProcessSizeMax\s*=\s* + state: absent + when: dupes.found is defined and dupes.found > 1 + + - name: Insert correct line to /etc/systemd/coredump.conf + lineinfile: + path: /etc/systemd/coredump.conf + create: false + regexp: ^\s*ProcessSizeMax\s*=\s* + line: ProcessSizeMax=0 + state: present + tags: + - CCE-83984-5 + - NIST-800-53-CM-6 + - coredump_disable_backtraces + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,%23%20%20This%20file%20is%20part%20of%20systemd.%0A%23%0A%23%20%20systemd%20is%20free%20software%3B%20you%20can%20redistribute%20it%20and/or%20modify%20it%0A%23%20%20under%20the%20terms%20of%20the%20GNU%20Lesser%20General%20Public%20License%20as%20published%20by%0A%23%20%20the%20Free%20Software%20Foundation%3B%20either%20version%202.1%20of%20the%20License%2C%20or%0A%23%20%20%28at%20your%20option%29%20any%20later%20version.%0A%23%0A%23%20Entries%20in%20this%20file%20show%20the%20compile%20time%20defaults.%0A%23%20You%20can%20change%20settings%20by%20editing%20this%20file.%0A%23%20Defaults%20can%20be%20restored%20by%20simply%20deleting%20this%20file.%0A%23%0A%23%20See%20coredump.conf%285%29%20for%20details.%0A%0A%5BCoredump%5D%0A%23Storage%3Dexternal%0A%23Compress%3Dyes%0A%23ProcessSizeMax%3D2G%0A%23ExternalSizeMax%3D2G%0A%23JournalSizeMax%3D767M%0A%23MaxUse%3D%0A%23KeepFree%3D%0AStorage%3Dnone%0AProcessSizeMax%3D0%0A + mode: 0644 + path: /etc/systemd/coredump.conf + overwrite: true + + if [ -e "/etc/systemd/coredump.conf" ] ; then + + LC_ALL=C sed -i "/^\s*ProcessSizeMax\s*=\s*/Id" "/etc/systemd/coredump.conf" +else + touch "/etc/systemd/coredump.conf" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/systemd/coredump.conf" + +cp "/etc/systemd/coredump.conf" "/etc/systemd/coredump.conf.bak" +# Insert at the end of the file +printf '%s\n' "ProcessSizeMax=0" >> "/etc/systemd/coredump.conf" +# Clean up after ourselves. +rm "/etc/systemd/coredump.conf.bak" + + + + + + + + + + Disable storing core dump + The Storage option in [Coredump] section +of /etc/systemd/coredump.conf +can be set to none to disable storing core dumps permanently. + If the /etc/systemd/coredump.conf file +does not already contain the [Coredump] section, +the value will not be configured correctly. + CCI-000366 + CM-6 + FMT_SMF_EXT.1 + SRG-OS-000480-GPOS-00227 + A core dump includes a memory image taken at the time the operating system +terminates an application. The memory image could contain sensitive data +and is generally useful only for developers or system operators trying to +debug problems. Enabling core dumps on production systems is not recommended, +however there may be overriding operational requirements to enable advanced +debuging. Permitting temporary enablement of core dumps during such situations +should be reviewed through local needs and policy. + CCE-83979-5 + - name: Disable storing core dump + block: + + - name: Check for duplicate values + lineinfile: + path: /etc/systemd/coredump.conf + create: false + regexp: ^\s*Storage\s*=\s* + state: absent + check_mode: true + changed_when: false + register: dupes + + - name: Deduplicate values from /etc/systemd/coredump.conf + lineinfile: + path: /etc/systemd/coredump.conf + create: false + regexp: ^\s*Storage\s*=\s* + state: absent + when: dupes.found is defined and dupes.found > 1 + + - name: Insert correct line to /etc/systemd/coredump.conf + lineinfile: + path: /etc/systemd/coredump.conf + create: false + regexp: ^\s*Storage\s*=\s* + line: Storage=none + state: present + tags: + - CCE-83979-5 + - NIST-800-53-CM-6 + - coredump_disable_storage + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,%23%20%20This%20file%20is%20part%20of%20systemd.%0A%23%0A%23%20%20systemd%20is%20free%20software%3B%20you%20can%20redistribute%20it%20and/or%20modify%20it%0A%23%20%20under%20the%20terms%20of%20the%20GNU%20Lesser%20General%20Public%20License%20as%20published%20by%0A%23%20%20the%20Free%20Software%20Foundation%3B%20either%20version%202.1%20of%20the%20License%2C%20or%0A%23%20%20%28at%20your%20option%29%20any%20later%20version.%0A%23%0A%23%20Entries%20in%20this%20file%20show%20the%20compile%20time%20defaults.%0A%23%20You%20can%20change%20settings%20by%20editing%20this%20file.%0A%23%20Defaults%20can%20be%20restored%20by%20simply%20deleting%20this%20file.%0A%23%0A%23%20See%20coredump.conf%285%29%20for%20details.%0A%0A%5BCoredump%5D%0A%23Storage%3Dexternal%0A%23Compress%3Dyes%0A%23ProcessSizeMax%3D2G%0A%23ExternalSizeMax%3D2G%0A%23JournalSizeMax%3D767M%0A%23MaxUse%3D%0A%23KeepFree%3D%0AStorage%3Dnone%0AProcessSizeMax%3D0%0A + mode: 0644 + path: /etc/systemd/coredump.conf + overwrite: true + + if [ -e "/etc/systemd/coredump.conf" ] ; then + + LC_ALL=C sed -i "/^\s*Storage\s*=\s*/Id" "/etc/systemd/coredump.conf" +else + touch "/etc/systemd/coredump.conf" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/systemd/coredump.conf" + +cp "/etc/systemd/coredump.conf" "/etc/systemd/coredump.conf.bak" +# Insert at the end of the file +printf '%s\n' "Storage=none" >> "/etc/systemd/coredump.conf" +# Clean up after ourselves. +rm "/etc/systemd/coredump.conf.bak" + + + + + + + + + + Disable Core Dumps for All Users + To disable core dumps for all users, add the following line to +/etc/security/limits.conf, or to a file within the +/etc/security/limits.d/ directory: +* hard core 0 + 1 + 12 + 13 + 15 + 16 + 2 + 7 + 8 + APO13.01 + BAI04.04 + DSS01.03 + DSS03.05 + DSS05.07 + CCI-000366 + SR 6.2 + SR 7.1 + SR 7.2 + A.12.1.3 + A.17.2.1 + CM-6 + SC-7(10) + DE.CM-1 + PR.DS-4 + SRG-OS-000480-GPOS-00227 + A core dump includes a memory image taken at the time the operating system +terminates an application. The memory image could contain sensitive data and is generally useful +only for developers trying to debug problems. + + CCE-83980-3 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83980-3 + - NIST-800-53-CM-6 + - NIST-800-53-SC-7(10) + - disable_users_coredumps + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: disable core dumps with limits + lineinfile: + dest: /etc/security/limits.conf + regexp: ^[^#].*core + line: '* hard core 0' + create: true + when: '"pam" in ansible_facts.packages' + tags: + - CCE-83980-3 + - NIST-800-53-CM-6 + - NIST-800-53-SC-7(10) + - disable_users_coredumps + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,%2A%20%20%20%20%20hard%20%20%20core%20%20%20%200 + mode: 0644 + path: /etc/security/limits.d/75-disable_users_coredumps.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if rpm --quiet -q pam; then + +SECURITY_LIMITS_FILE="/etc/security/limits.conf" + +if grep -qE '^\s*\*\s+hard\s+core' $SECURITY_LIMITS_FILE; then + sed -ri 's/(hard\s+core\s+)[[:digit:]]+/\1 0/' $SECURITY_LIMITS_FILE +else + echo "* hard core 0" >> $SECURITY_LIMITS_FILE +fi + +if ls /etc/security/limits.d/*.conf > /dev/null; then + sed -ri '/^\s*\*\s+hard\s+core/d' /etc/security/limits.d/*.conf +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable Core Dumps for SUID programs + To set the runtime status of the fs.suid_dumpable kernel parameter, run the following command: $ sudo sysctl -w fs.suid_dumpable=0 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: fs.suid_dumpable = 0 + BP28(R23) + 164.308(a)(1)(ii)(D) + 164.308(a)(3) + 164.308(a)(4) + 164.310(b) + 164.310(c) + 164.312(a) + 164.312(e) + SI-11(a) + SI-11(b) + The core dump of a setuid program is more likely to contain +sensitive data, as the program itself runs with greater privileges than the +user who initiated execution of the program. Disabling the ability for any +setuid program to write a core file decreases the risk of unauthorized access +of such data. + + CCE-83981-1 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*fs.suid_dumpable.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83981-1 + - NIST-800-53-SI-11(a) + - NIST-800-53-SI-11(b) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_fs_suid_dumpable + +- name: Comment out any occurrences of fs.suid_dumpable from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*fs.suid_dumpable + replace: '#fs.suid_dumpable' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83981-1 + - NIST-800-53-SI-11(a) + - NIST-800-53-SI-11(b) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_fs_suid_dumpable + +- name: Ensure sysctl fs.suid_dumpable is set to 0 + sysctl: + name: fs.suid_dumpable + value: '0' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83981-1 + - NIST-800-53-SI-11(a) + - NIST-800-53-SI-11(b) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_fs_suid_dumpable + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of fs.suid_dumpable from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*fs.suid_dumpable.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "fs.suid_dumpable" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done + +# +# Set runtime for fs.suid_dumpable +# +/sbin/sysctl -q -n -w fs.suid_dumpable="0" + +# +# If fs.suid_dumpable present in /etc/sysctl.conf, change value to "0" +# else, add "fs.suid_dumpable = 0" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^fs.suid_dumpable") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "0" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^fs.suid_dumpable\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^fs.suid_dumpable\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-83981-1" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Daemon Umask + The umask is a per-process setting which limits +the default permissions for creation of new files and directories. +The system includes initialization scripts which set the default umask +for system daemons. + + daemon umask + Enter umask for daemons + 022 + 027 + 022 + + + + Enable ExecShield + ExecShield describes kernel features that provide +protection against exploitation of memory corruption errors such as buffer +overflows. These features include random placement of the stack and other +memory regions, prevention of execution in memory that should only hold data, +and special handling of text buffers. These protections are enabled by default +on 32-bit systems and controlled through sysctl variables +kernel.exec-shield and kernel.randomize_va_space. On the latest +64-bit systems, kernel.exec-shield cannot be enabled or disabled with +sysctl. + + kernel.kptr_restrict + Configure exposition of kernel pointer addresses + 1 + 1 + 2 + + + Enable ExecShield via sysctl + By default on Red Hat Enterprise Linux 9 64-bit systems, ExecShield is +enabled and can only be disabled if the hardware does not support +ExecShield or is disabled in /etc/default/grub. + BP28(R9) + 12 + 15 + 8 + APO13.01 + DSS05.02 + 3.1.7 + CCI-002530 + 164.308(a)(1)(ii)(D) + 164.308(a)(3) + 164.308(a)(4) + 164.310(b) + 164.310(c) + 164.312(a) + 164.312(e) + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + A.13.1.1 + A.13.2.1 + A.14.1.3 + SC-39 + CM-6(a) + PR.PT-4 + SRG-OS-000433-GPOS-00192 + ExecShield uses the segmentation feature on all x86 systems to prevent +execution in memory higher than a certain address. It writes an address as +a limit in the code segment descriptor, to control where code can be +executed, on a per-process basis. When the kernel places a process's memory +regions such as the stack and heap higher than this address, the hardware +prevents execution in that address range. This is enabled by default on the +latest Red Hat and Fedora systems if supported by the hardware. + + CCE-83970-4 + - name: Update grub defaults and the bootloader menu + command: /sbin/grubby --update-kernel=ALL --remove-args="noexec" + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83970-4 + - NIST-800-171-3.1.7 + - NIST-800-53-CM-6(a) + - NIST-800-53-SC-39 + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + - sysctl_kernel_exec_shield + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +grubby --update-kernel=ALL --remove-args=noexec + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Restrict Exposed Kernel Pointer Addresses Access + To set the runtime status of the kernel.kptr_restrict kernel parameter, run the following command: $ sudo sysctl -w kernel.kptr_restrict= +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: kernel.kptr_restrict = + BP28(R23) + CCI-002824 + CCI-000366 + CIP-002-5 R1.1 + CIP-002-5 R1.2 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 4.1 + CIP-004-6 4.2 + CIP-004-6 R2.2.3 + CIP-004-6 R2.2.4 + CIP-004-6 R2.3 + CIP-004-6 R4 + CIP-005-6 R1 + CIP-005-6 R1.1 + CIP-005-6 R1.2 + CIP-007-3 R3 + CIP-007-3 R3.1 + CIP-007-3 R5.1 + CIP-007-3 R5.1.2 + CIP-007-3 R5.1.3 + CIP-007-3 R5.2.1 + CIP-007-3 R5.2.3 + CIP-007-3 R8.4 + CIP-009-6 R.1.1 + CIP-009-6 R4 + SC-30 + SC-30(2) + SC-30(5) + CM-6(a) + SRG-OS-000132-GPOS-00067 + SRG-OS-000433-GPOS-00192 + SRG-OS-000480-GPOS-00227 + Exposing kernel pointers (through procfs or seq_printf()) exposes kernel +writeable structures which may contain functions pointers. If a write vulnerability +occurs in the kernel, allowing write access to any of this structure, the kernel can +be compromised. This option disallow any program without the CAP_SYSLOG capability +to get the addresses of kernel pointers by replacing them with 0. + + CCE-83972-0 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*kernel.kptr_restrict.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83972-0 + - NIST-800-53-CM-6(a) + - NIST-800-53-SC-30 + - NIST-800-53-SC-30(2) + - NIST-800-53-SC-30(5) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_kernel_kptr_restrict + +- name: Comment out any occurrences of kernel.kptr_restrict from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*kernel.kptr_restrict + replace: '#kernel.kptr_restrict' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83972-0 + - NIST-800-53-CM-6(a) + - NIST-800-53-SC-30 + - NIST-800-53-SC-30(2) + - NIST-800-53-SC-30(5) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_kernel_kptr_restrict +- name: XCCDF Value sysctl_kernel_kptr_restrict_value # promote to variable + set_fact: + sysctl_kernel_kptr_restrict_value: !!str + tags: + - always + +- name: Ensure sysctl kernel.kptr_restrict is set + sysctl: + name: kernel.kptr_restrict + value: '{{ sysctl_kernel_kptr_restrict_value }}' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83972-0 + - NIST-800-53-CM-6(a) + - NIST-800-53-SC-30 + - NIST-800-53-SC-30(2) + - NIST-800-53-SC-30(5) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_kernel_kptr_restrict + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,kernel.kptr_restrict%3D1%0A + mode: 0644 + path: /etc/sysctl.d/75-sysctl_kernel_kptr_restrict.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of kernel.kptr_restrict from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*kernel.kptr_restrict.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "kernel.kptr_restrict" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done +sysctl_kernel_kptr_restrict_value='' + + +# +# Set runtime for kernel.kptr_restrict +# +/sbin/sysctl -q -n -w kernel.kptr_restrict="$sysctl_kernel_kptr_restrict_value" + +# +# If kernel.kptr_restrict present in /etc/sysctl.conf, change value to appropriate value +# else, add "kernel.kptr_restrict = value" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^kernel.kptr_restrict") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_kernel_kptr_restrict_value" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^kernel.kptr_restrict\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^kernel.kptr_restrict\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-83972-0" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Enable Randomized Layout of Virtual Address Space + To set the runtime status of the kernel.randomize_va_space kernel parameter, run the following command: $ sudo sysctl -w kernel.randomize_va_space=2 +To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d: kernel.randomize_va_space = 2 + BP28(R23) + 3.1.7 + CCI-000366 + CCI-002824 + 164.308(a)(1)(ii)(D) + 164.308(a)(3) + 164.308(a)(4) + 164.310(b) + 164.310(c) + 164.312(a) + 164.312(e) + CIP-002-5 R1.1 + CIP-002-5 R1.2 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 4.1 + CIP-004-6 4.2 + CIP-004-6 R2.2.3 + CIP-004-6 R2.2.4 + CIP-004-6 R2.3 + CIP-004-6 R4 + CIP-005-6 R1 + CIP-005-6 R1.1 + CIP-005-6 R1.2 + CIP-007-3 R3 + CIP-007-3 R3.1 + CIP-007-3 R5.1 + CIP-007-3 R5.1.2 + CIP-007-3 R5.1.3 + CIP-007-3 R5.2.1 + CIP-007-3 R5.2.3 + CIP-007-3 R8.4 + CIP-009-6 R.1.1 + CIP-009-6 R4 + SC-30 + SC-30(2) + CM-6(a) + SRG-OS-000433-GPOS-00193 + SRG-OS-000480-GPOS-00227 + Address space layout randomization (ASLR) makes it more difficult for an +attacker to predict the location of attack code they have introduced into a +process's address space during an attempt at exploitation. Additionally, +ASLR makes it more difficult for an attacker to know the location of +existing code in order to re-purpose it using return oriented programming +(ROP) techniques. + + CCE-83971-2 + - name: List /etc/sysctl.d/*.conf files + find: + paths: + - /etc/sysctl.d/ + - /run/sysctl.d/ + contains: ^[\s]*kernel.randomize_va_space.*$ + patterns: '*.conf' + file_type: any + register: find_sysctl_d + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83971-2 + - NIST-800-171-3.1.7 + - NIST-800-53-CM-6(a) + - NIST-800-53-SC-30 + - NIST-800-53-SC-30(2) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_kernel_randomize_va_space + +- name: Comment out any occurrences of kernel.randomize_va_space from /etc/sysctl.d/*.conf + files + replace: + path: '{{ item.path }}' + regexp: ^[\s]*kernel.randomize_va_space + replace: '#kernel.randomize_va_space' + loop: '{{ find_sysctl_d.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83971-2 + - NIST-800-171-3.1.7 + - NIST-800-53-CM-6(a) + - NIST-800-53-SC-30 + - NIST-800-53-SC-30(2) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_kernel_randomize_va_space + +- name: Ensure sysctl kernel.randomize_va_space is set to 2 + sysctl: + name: kernel.randomize_va_space + value: '2' + state: present + reload: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-83971-2 + - NIST-800-171-3.1.7 + - NIST-800-53-CM-6(a) + - NIST-800-53-SC-30 + - NIST-800-53-SC-30(2) + - disable_strategy + - low_complexity + - medium_disruption + - medium_severity + - reboot_required + - sysctl_kernel_randomize_va_space + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,kernel.randomize_va_space%3D2%0A + mode: 0644 + path: /etc/sysctl.d/75-sysctl_kernel_randomize_va_space.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Comment out any occurrences of kernel.randomize_va_space from /etc/sysctl.d/*.conf files + +for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf; do + + matching_list=$(grep -P '^(?!#).*[\s]*kernel.randomize_va_space.*$' $f | uniq ) + if ! test -z "$matching_list"; then + while IFS= read -r entry; do + escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") + # comment out "kernel.randomize_va_space" matches to preserve user data + sed -i "s/^${escaped_entry}$/# &/g" $f + done <<< "$matching_list" + fi +done + +# +# Set runtime for kernel.randomize_va_space +# +/sbin/sysctl -q -n -w kernel.randomize_va_space="2" + +# +# If kernel.randomize_va_space present in /etc/sysctl.conf, change value to "2" +# else, add "kernel.randomize_va_space = 2" to /etc/sysctl.conf +# +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/sysctl.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^kernel.randomize_va_space") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s = %s" "$stripped_key" "2" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^kernel.randomize_va_space\\>" "/etc/sysctl.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^kernel.randomize_va_space\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-83971-2" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/sysctl.conf" >> "/etc/sysctl.conf" + printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable Execute Disable (XD) or No Execute (NX) Support on +x86 Systems + Recent processors in the x86 family support the +ability to prevent code execution on a per memory page basis. +Generically and on AMD processors, this ability is called No +Execute (NX), while on Intel processors it is called Execute +Disable (XD). This ability can help prevent exploitation of buffer +overflow vulnerabilities and should be activated whenever possible. +Extra steps must be taken to ensure that this protection is +enabled, particularly on 32-bit x86 systems. Other processors, such +as Itanium and POWER, have included such support since inception +and the standard kernel for those platforms supports the +feature. This is enabled by default on the latest Red Hat and +Fedora systems if supported by the hardware. + + Enable NX or XD Support in the BIOS + Reboot the system and enter the BIOS or Setup configuration menu. +Navigate the BIOS configuration menu and make sure that the option is enabled. The setting may be located +under a Security section. Look for Execute Disable (XD) on Intel-based systems and No Execute (NX) +on AMD-based systems. + BP28(R9) + 11 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + 3.1.7 + CCI-002824 + 4.3.4.3.2 + 4.3.4.3.3 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + SC-39 + CM-6(a) + PR.IP-1 + SRG-OS-000433-GPOS-00192 + Computers with the ability to prevent this type of code execution frequently put an option in the BIOS that will +allow users to turn the feature on or off at will. + + CCE-88577-2 + + + + + + + + + + Memory Poisoning + Memory Poisoning consists of writing a special value to uninitialized or freed memory. +Poisoning can be used as a mechanism to prevent leak of information and detection of +corrupted memory. + + + slub_debug - debug options + Defines the debug options to use in slub_debug kernel command line argument. + P + F + Z + P + FZ + FZP + + + Enable page allocator poisoning + To enable poisoning of free pages, +add the argument page_poison=1 to the default +GRUB 2 command line for the Linux operating system. +To ensure that page_poison=1 is added as a kernel command line +argument to newly installed kernels, add page_poison=1 to the +default Grub2 command line for Linux operating systems. Modify the line within +/etc/default/grub as shown below: +GRUB_CMDLINE_LINUX="... page_poison=1 ..." +Run the following command to update command line for already installed kernels:# grubby --update-kernel=ALL --args="page_poison=1" + CCI-001084 + CM-6(a) + SRG-OS-000480-GPOS-00227 + SRG-OS-000134-GPOS-00068 + Poisoning writes an arbitrary value to freed pages, so any modification or +reference to that page after being freed or before being initialized will be +detected and prevented. +This prevents many types of use-after-free vulnerabilities at little performance cost. +Also prevents leak of data and detection of corrupted memory. + + CCE-83985-2 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83985-2 + - NIST-800-53-CM-6(a) + - grub2_page_poison_argument + - low_disruption + - medium_complexity + - medium_severity + - reboot_required + - restrict_strategy + +- name: Update grub defaults and the bootloader menu + command: /sbin/grubby --update-kernel=ALL --args="page_poison=1" + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"grub2-common" in ansible_facts.packages' + tags: + - CCE-83985-2 + - NIST-800-53-CM-6(a) + - grub2_page_poison_argument + - low_disruption + - medium_complexity + - medium_severity + - reboot_required + - restrict_strategy + + [customizations.kernel] +append = "page_poison=1" + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && { rpm --quiet -q grub2-common; }; then + +grubby --update-kernel=ALL --args=page_poison=1 + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Enable SLUB/SLAB allocator poisoning + To enable poisoning of SLUB/SLAB objects, +add the argument slub_debug= to the default +GRUB 2 command line for the Linux operating system. +To ensure that slub_debug= is added as a kernel command line +argument to newly installed kernels, add slub_debug= to the +default Grub2 command line for Linux operating systems. Modify the line within +/etc/default/grub as shown below: +GRUB_CMDLINE_LINUX="... slub_debug= ..." +Run the following command to update command line for already installed kernels:# grubby --update-kernel=ALL --args="slub_debug=" + CCI-001084 + CM-6(a) + SRG-OS-000433-GPOS-00192 + SRG-OS-000134-GPOS-00068 + Poisoning writes an arbitrary value to freed objects, so any modification or +reference to that object after being freed or before being initialized will be +detected and prevented. +This prevents many types of use-after-free vulnerabilities at little performance cost. +Also prevents leak of data and detection of corrupted memory. + + CCE-83986-0 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-83986-0 + - NIST-800-53-CM-6(a) + - grub2_slub_debug_argument + - low_disruption + - medium_complexity + - medium_severity + - reboot_required + - restrict_strategy +- name: XCCDF Value var_slub_debug_options # promote to variable + set_fact: + var_slub_debug_options: !!str + tags: + - always + +- name: Update grub defaults and the bootloader menu + command: /sbin/grubby --update-kernel=ALL --args="slub_debug={{ var_slub_debug_options + }}" + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"grub2-common" in ansible_facts.packages' + tags: + - CCE-83986-0 + - NIST-800-53-CM-6(a) + - grub2_slub_debug_argument + - low_disruption + - medium_complexity + - medium_severity + - reboot_required + - restrict_strategy + + [customizations.kernel] +append = "slub_debug=" + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && { rpm --quiet -q grub2-common; }; then + +var_slub_debug_options='' + + + +grubby --update-kernel=ALL --args=slub_debug=$var_slub_debug_options + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + + + + SELinux + SELinux is a feature of the Linux kernel which can be +used to guard against misconfigured or compromised programs. +SELinux enforces the idea that programs should be limited in what +files they can access and what actions they can take. + +The default SELinux policy, as configured on Red Hat Enterprise Linux 9, has been +sufficiently developed and debugged that it should be usable on +almost any system with minimal configuration and a small +amount of system administrator training. This policy prevents +system services - including most of the common network-visible +services such as mail servers, FTP servers, and DNS servers - from +accessing files which those services have no valid reason to +access. This action alone prevents a huge amount of possible damage +from network attacks against services, from trojaned software, and +so forth. + +This guide recommends that SELinux be enabled using the +default (targeted) policy on every Red Hat Enterprise Linux 9 system, unless that +system has unusual requirements which make a stronger policy +appropriate. + + + SELinux policy + Type of policy in use. Possible values are: +targeted - Only targeted network daemons are protected. +strict - Full SELinux protection. +mls - Multiple levels of security + targeted + mls + targeted + + + SELinux state + enforcing - SELinux security policy is enforced. +permissive - SELinux prints warnings instead of enforcing. +disabled - SELinux is fully disabled. + enforcing + disabled + enforcing + permissive + + + Install libselinux Package + The libselinux package can be installed with the following command: + +$ sudo dnf install libselinux + Security-enhanced Linux is a feature of the Linux kernel and a number of utilities +with enhanced security functionality designed to add rhisam access controls to Linux. + +The libselinux package contains the core library of the Security-enhanced Linux system. + CCE-84069-4 + +package --add=libselinux + + include install_libselinux + +class install_libselinux { + package { 'libselinux': + ensure => 'installed', + } +} + + - name: Ensure libselinux is installed + package: + name: libselinux + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84069-4 + - enable_strategy + - high_severity + - low_complexity + - low_disruption + - no_reboot_needed + - package_libselinux_installed + + +[[packages]] +name = "libselinux" +version = "*" + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if ! rpm -q --quiet "libselinux" ; then + dnf install -y "libselinux" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Install policycoreutils-python-utils package + The policycoreutils-python-utils package can be installed with the following command: + +$ sudo dnf install policycoreutils-python-utils + SRG-OS-000480-GPOS-00227 + This package is required to operate and manage an SELinux environment and its policies. +It provides utilities such as semanage, audit2allow, audit2why, chcat and sandbox. + CCE-84070-2 + +package --add=policycoreutils-python-utils + + include install_policycoreutils-python-utils + +class install_policycoreutils-python-utils { + package { 'policycoreutils-python-utils': + ensure => 'installed', + } +} + + - name: Ensure policycoreutils-python-utils is installed + package: + name: policycoreutils-python-utils + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84070-2 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_policycoreutils-python-utils_installed + + +[[packages]] +name = "policycoreutils-python-utils" +version = "*" + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if ! rpm -q --quiet "policycoreutils-python-utils" ; then + dnf install -y "policycoreutils-python-utils" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Install policycoreutils Package + The policycoreutils package can be installed with the following command: + +$ sudo dnf install policycoreutils + CCI-001084 + SRG-OS-000480-GPOS-00227 + SRG-OS-000134-GPOS-00068 + Security-enhanced Linux is a feature of the Linux kernel and a number of utilities +with enhanced security functionality designed to add rhisam access controls to Linux. +The Security-enhanced Linux kernel contains new architectural components originally +developed to improve security of the Flask operating system. These architectural components +provide general support for the enforcement of many kinds of rhisam access control +policies, including those based on the concepts of Type Enforcement, Role-based Access +Control, and Multi-level Security. + +policycoreutils contains the policy core utilities that are required for +basic operation of an SELinux-enabled system. These utilities include load_policy +to load SELinux policies, setfiles to label filesystems, newrole to +switch roles, and so on. + CCE-84071-0 + +package --add=policycoreutils + + include install_policycoreutils + +class install_policycoreutils { + package { 'policycoreutils': + ensure => 'installed', + } +} + + - name: Ensure policycoreutils is installed + package: + name: policycoreutils + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84071-0 + - enable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - package_policycoreutils_installed + + +[[packages]] +name = "policycoreutils" +version = "*" + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if ! rpm -q --quiet "policycoreutils" ; then + dnf install -y "policycoreutils" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Uninstall mcstrans Package + The mcstransd daemon provides category label information +to client processes requesting information. The label translations are defined +in /etc/selinux/targeted/setrans.conf. +The mcstrans package can be removed with the following command: + +$ sudo dnf erase mcstrans + Since this service is not used very often, disable it to reduce the +amount of potentially vulnerable code running on the system. + CCE-84072-8 + +package --remove=mcstrans + + include remove_mcstrans + +class remove_mcstrans { + package { 'mcstrans': + ensure => 'purged', + } +} + + - name: Ensure mcstrans is removed + package: + name: mcstrans + state: absent + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84072-8 + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - package_mcstrans_removed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# CAUTION: This remediation script will remove mcstrans +# from the system, and may remove any packages +# that depend on mcstrans. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "mcstrans" ; then + + dnf remove -y "mcstrans" + +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + Uninstall setroubleshoot-plugins Package + The SETroubleshoot plugins are used to analyze SELinux AVC data. The service provides information around configuration errors, +unauthorized intrusions, and other potential errors. +The setroubleshoot-plugins package can be removed with the following command: + +$ sudo dnf erase setroubleshoot-plugins + BP28(R68) + The SETroubleshoot service is an unnecessary daemon to +have running on a server. + CCE-84251-8 + +package --remove=setroubleshoot-plugins + + include remove_setroubleshoot-plugins + +class remove_setroubleshoot-plugins { + package { 'setroubleshoot-plugins': + ensure => 'purged', + } +} + + - name: Ensure setroubleshoot-plugins is removed + package: + name: setroubleshoot-plugins + state: absent + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84251-8 + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - package_setroubleshoot-plugins_removed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# CAUTION: This remediation script will remove setroubleshoot-plugins +# from the system, and may remove any packages +# that depend on setroubleshoot-plugins. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "setroubleshoot-plugins" ; then + + dnf remove -y "setroubleshoot-plugins" + +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Uninstall setroubleshoot-server Package + The SETroubleshoot service notifies desktop users of SELinux +denials. The service provides information around configuration errors, +unauthorized intrusions, and other potential errors. +The setroubleshoot-server package can be removed with the following command: + +$ sudo dnf erase setroubleshoot-server + BP28(R68) + The SETroubleshoot service is an unnecessary daemon to have +running on a server. + CCE-84252-6 + +package --remove=setroubleshoot-server + + include remove_setroubleshoot-server + +class remove_setroubleshoot-server { + package { 'setroubleshoot-server': + ensure => 'purged', + } +} + + - name: Ensure setroubleshoot-server is removed + package: + name: setroubleshoot-server + state: absent + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84252-6 + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - package_setroubleshoot-server_removed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# CAUTION: This remediation script will remove setroubleshoot-server +# from the system, and may remove any packages +# that depend on setroubleshoot-server. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "setroubleshoot-server" ; then + + dnf remove -y "setroubleshoot-server" + +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Uninstall setroubleshoot Package + The SETroubleshoot service notifies desktop users of SELinux +denials. The service provides information around configuration errors, +unauthorized intrusions, and other potential errors. +The setroubleshoot package can be removed with the following command: + +$ sudo dnf erase setroubleshoot + BP28(R68) + The SETroubleshoot service is an unnecessary daemon to +have running on a server, especially if +X Windows is removed or disabled. + CCE-84073-6 + +package --remove=setroubleshoot + + include remove_setroubleshoot + +class remove_setroubleshoot { + package { 'setroubleshoot': + ensure => 'purged', + } +} + + - name: Ensure setroubleshoot is removed + package: + name: setroubleshoot + state: absent + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84073-6 + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - package_setroubleshoot_removed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# CAUTION: This remediation script will remove setroubleshoot +# from the system, and may remove any packages +# that depend on setroubleshoot. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "setroubleshoot" ; then + + dnf remove -y "setroubleshoot" + +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + Ensure SELinux Not Disabled in the kernel arguments + SELinux can be disabled at boot time by disabling it via a kernel argument. +Remove any instances of selinux=0 from the kernel arguments in that +file to prevent SELinux from being disabled at boot. + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 4 + 5 + 6 + 8 + 9 + APO01.06 + APO11.04 + APO13.01 + BAI03.05 + DSS01.05 + DSS03.01 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + DSS06.02 + DSS06.03 + DSS06.06 + MEA02.01 + 3.1.2 + 3.7.2 + CCI-000022 + CCI-000032 + 164.308(a)(1)(ii)(D) + 164.308(a)(3) + 164.308(a)(4) + 164.310(b) + 164.310(c) + 164.312(a) + 164.312(e) + 4.2.3.4 + 4.3.3.2.2 + 4.3.3.3.9 + 4.3.3.4 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + 4.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.12.1.1 + A.12.1.2 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.1.2 + A.13.1.3 + A.13.2.1 + A.13.2.2 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.1 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.2.3 + CIP-004-6 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.2 + CIP-007-3 R5.2 + CIP-007-3 R5.3.1 + CIP-007-3 R5.3.2 + CIP-007-3 R5.3.3 + AC-3 + AC-3(3)(a) + DE.AE-1 + ID.AM-3 + PR.AC-4 + PR.AC-5 + PR.AC-6 + PR.DS-5 + PR.PT-1 + PR.PT-3 + PR.PT-4 + SRG-OS-000445-VMM-001780 + Disabling a major host protection feature, such as SELinux, at boot time prevents +it from confining system services at boot time. Further, it increases +the chances that it will remain off during system operation. + + + + + + + + + Ensure SELinux Not Disabled in /etc/default/grub + SELinux can be disabled at boot time by an argument in +/etc/default/grub. +Remove any instances of selinux=0 from the kernel arguments in that +file to prevent SELinux from being disabled at boot. + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 4 + 5 + 6 + 8 + 9 + APO01.06 + APO11.04 + APO13.01 + BAI03.05 + DSS01.05 + DSS03.01 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + DSS06.02 + DSS06.03 + DSS06.06 + MEA02.01 + 3.1.2 + 3.7.2 + CCI-000022 + CCI-000032 + 164.308(a)(1)(ii)(D) + 164.308(a)(3) + 164.308(a)(4) + 164.310(b) + 164.310(c) + 164.312(a) + 164.312(e) + 4.2.3.4 + 4.3.3.2.2 + 4.3.3.3.9 + 4.3.3.4 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + 4.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.12.1.1 + A.12.1.2 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.1.2 + A.13.1.3 + A.13.2.1 + A.13.2.2 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.1 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.2.3 + CIP-004-6 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.2 + CIP-007-3 R5.2 + CIP-007-3 R5.3.1 + CIP-007-3 R5.3.2 + CIP-007-3 R5.3.3 + AC-3 + AC-3(3)(a) + DE.AE-1 + ID.AM-3 + PR.AC-4 + PR.AC-5 + PR.AC-6 + PR.DS-5 + PR.PT-1 + PR.PT-3 + PR.PT-4 + SRG-OS-000445-VMM-001780 + Disabling a major host protection feature, such as SELinux, at boot time prevents +it from confining system services at boot time. Further, it increases +the chances that it will remain off during system operation. + CCE-84078-5 + - name: Find /etc/grub.d/ files + find: + paths: + - /etc/grub.d/ + follow: true + register: grub + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84078-5 + - NIST-800-171-3.1.2 + - NIST-800-171-3.7.2 + - NIST-800-53-AC-3 + - NIST-800-53-AC-3(3)(a) + - grub2_enable_selinux + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Ensure SELinux Not Disabled in grub files + replace: + dest: '{{ item.path }}' + regexp: (selinux|enforcing)=0 + with_items: + - '{{ grub.files }}' + - path: /etc/grub2.cfg + - path: /etc/default/grub + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84078-5 + - NIST-800-171-3.1.2 + - NIST-800-171-3.7.2 + - NIST-800-53-AC-3 + - NIST-800-53-AC-3(3)(a) + - grub2_enable_selinux + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +sed -i --follow-symlinks "s/selinux=0//gI" /etc/default/grub /etc/grub2.cfg /etc/grub.d/* +sed -i --follow-symlinks "s/enforcing=0//gI" /etc/default/grub /etc/grub2.cfg /etc/grub.d/* + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure No Device Files are Unlabeled by SELinux + Device files, which are used for communication with important system +resources, should be labeled with proper SELinux types. If any device files +carry the SELinux type device_t or unlabeled_t, report the +bug so that policy can be corrected. Supply information about what the +device is and what programs use it. + +To check for incorrectly labeled device files, run following commands: +$ sudo find /dev -context *:device_t:* \( -type c -o -type b \) -printf "%p %Z\n" +$ sudo find /dev -context *:unlabeled_t:* \( -type c -o -type b \) -printf "%p %Z\n" +It should produce no output in a well-configured system. + Automatic remediation of this control is not available. The remediation +can be achieved by amending SELinux policy. + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 18 + 2 + 3 + 5 + 6 + 7 + 8 + 9 + APO01.06 + APO11.04 + BAI01.06 + BAI03.05 + BAI06.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.03 + DSS03.05 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + DSS06.02 + DSS06.06 + MEA02.01 + 3.1.2 + 3.1.5 + 3.7.2 + CCI-000022 + CCI-000032 + CCI-000318 + CCI-000366 + CCI-000368 + CCI-001812 + CCI-001813 + CCI-001814 + 4.3.3.3.9 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 2.8 + SR 2.9 + SR 5.2 + SR 6.2 + SR 7.6 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.12.1.2 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.5.1 + A.12.6.2 + A.12.7.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.14.2.7 + A.15.2.1 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-7(a) + CM-7(b) + CM-6(a) + AC-3(3)(a) + AC-6 + DE.CM-1 + DE.CM-7 + PR.AC-4 + PR.DS-5 + PR.IP-1 + PR.IP-3 + PR.PT-1 + PR.PT-3 + SRG-OS-000480-GPOS-00227 + If a device file carries the SELinux type device_t or +unlabeled_t, then SELinux cannot properly restrict access to the +device file. + CCE-85920-7 + + + + + + + + + Ensure No Daemons are Unconfined by SELinux + Daemons for which the SELinux policy does not contain rules will inherit the +context of the parent process. Because daemons are launched during +startup and descend from the init process, they inherit the unconfined_service_t context. + + +To check for unconfined daemons, run the following command: +$ sudo ps -eZ | grep "unconfined_service_t" +It should produce no output in a well-configured system. + Automatic remediation of this control is not available. Remediation +can be achieved by amending SELinux policy or stopping the unconfined +daemons as outlined above. + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + 6 + 9 + APO01.06 + APO11.04 + BAI03.05 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + DSS06.02 + DSS06.06 + MEA02.01 + 3.1.2 + 3.1.5 + 3.7.2 + 164.308(a)(1)(ii)(D) + 164.308(a)(3) + 164.308(a)(4) + 164.310(b) + 164.310(c) + 164.312(a) + 164.312(e) + 4.3.3.3.9 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 2.8 + SR 2.9 + SR 5.2 + SR 7.6 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.12.1.2 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.5.1 + A.12.6.2 + A.12.7.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + AC-3(3)(a) + AC-6 + PR.AC-4 + PR.DS-5 + PR.IP-1 + PR.PT-1 + PR.PT-3 + Daemons which run with the unconfined_service_t context may cause AVC denials, +or allow privileges that the daemon does not require. + CCE-84075-1 + + + + + + + + + Configure SELinux Policy + The SELinux targeted policy is appropriate for +general-purpose desktops and servers, as well as systems in many other roles. +To configure the system to use this policy, add or correct the following line +in /etc/selinux/config: +SELINUXTYPE= +Other policies, such as mls, provide additional security labeling +and greater confinement but are not compatible with many general-purpose +use cases. + BP28(R66) + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 4 + 5 + 6 + 8 + 9 + APO01.06 + APO11.04 + APO13.01 + BAI03.05 + DSS01.05 + DSS03.01 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + DSS06.02 + DSS06.03 + DSS06.06 + MEA02.01 + 3.1.2 + 3.7.2 + CCI-002165 + CCI-002696 + 164.308(a)(1)(ii)(D) + 164.308(a)(3) + 164.308(a)(4) + 164.310(b) + 164.310(c) + 164.312(a) + 164.312(e) + 4.2.3.4 + 4.3.3.2.2 + 4.3.3.3.9 + 4.3.3.4 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + 4.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.12.1.1 + A.12.1.2 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.1.2 + A.13.1.3 + A.13.2.1 + A.13.2.2 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.1 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.2 + CIP-003-8 R5.3 + CIP-004-6 R2.2.3 + CIP-004-6 R2.3 + CIP-004-6 R3.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.2 + CIP-007-3 R5.2 + CIP-007-3 R5.3.1 + CIP-007-3 R5.3.2 + CIP-007-3 R5.3.3 + CIP-007-3 R6.5 + AC-3 + AC-3(3)(a) + AU-9 + SC-7(21) + DE.AE-1 + ID.AM-3 + PR.AC-4 + PR.AC-5 + PR.AC-6 + PR.DS-5 + PR.PT-1 + PR.PT-3 + PR.PT-4 + SRG-OS-000445-GPOS-00199 + SRG-OS-000445-VMM-001780 + Setting the SELinux policy to targeted or a more specialized policy +ensures the system will confine processes that are likely to be +targeted for exploitation, such as network or system services. + +Note: During the development or debugging of SELinux modules, it is common to +temporarily place non-production systems in permissive mode. In such +temporary cases, SELinux policies should be developed, and once work +is completed, the system should be reconfigured to +. + CCE-84074-4 + - name: XCCDF Value var_selinux_policy_name # promote to variable + set_fact: + var_selinux_policy_name: !!str + tags: + - always + +- name: Configure SELinux Policy + block: + + - name: Check for duplicate values + lineinfile: + path: /etc/selinux/config + create: false + regexp: ^SELINUXTYPE= + state: absent + check_mode: true + changed_when: false + register: dupes + + - name: Deduplicate values from /etc/selinux/config + lineinfile: + path: /etc/selinux/config + create: false + regexp: ^SELINUXTYPE= + state: absent + when: dupes.found is defined and dupes.found > 1 + + - name: Insert correct line to /etc/selinux/config + lineinfile: + path: /etc/selinux/config + create: true + regexp: ^SELINUXTYPE= + line: SELINUXTYPE={{ var_selinux_policy_name }} + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84074-4 + - NIST-800-171-3.1.2 + - NIST-800-171-3.7.2 + - NIST-800-53-AC-3 + - NIST-800-53-AC-3(3)(a) + - NIST-800-53-AU-9 + - NIST-800-53-SC-7(21) + - low_complexity + - low_disruption + - medium_severity + - reboot_required + - restrict_strategy + - selinux_policytype + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_selinux_policy_name='' + + +if [ -e "/etc/selinux/config" ] ; then + + LC_ALL=C sed -i "/^SELINUXTYPE=/Id" "/etc/selinux/config" +else + touch "/etc/selinux/config" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/selinux/config" + +cp "/etc/selinux/config" "/etc/selinux/config.bak" +# Insert at the end of the file +printf '%s\n' "SELINUXTYPE=$var_selinux_policy_name" >> "/etc/selinux/config" +# Clean up after ourselves. +rm "/etc/selinux/config.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Ensure SELinux State is Enforcing + The SELinux state should be set to at +system boot time. In the file /etc/selinux/config, add or correct the +following line to configure the system to boot into enforcing mode: +SELINUX= + BP28(R4) + BP28(R66) + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 4 + 5 + 6 + 8 + 9 + APO01.06 + APO11.04 + APO13.01 + BAI03.05 + DSS01.05 + DSS03.01 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + DSS06.02 + DSS06.03 + DSS06.06 + MEA02.01 + 3.1.2 + 3.7.2 + CCI-001084 + CCI-002165 + CCI-002696 + 164.308(a)(1)(ii)(D) + 164.308(a)(3) + 164.308(a)(4) + 164.310(b) + 164.310(c) + 164.312(a) + 164.312(e) + 4.2.3.4 + 4.3.3.2.2 + 4.3.3.3.9 + 4.3.3.4 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + 4.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 2.8 + SR 2.9 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.12.1.1 + A.12.1.2 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + A.13.1.1 + A.13.1.2 + A.13.1.3 + A.13.2.1 + A.13.2.2 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.1 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.2 + CIP-003-8 R5.3 + CIP-004-6 R2.2.3 + CIP-004-6 R2.3 + CIP-004-6 R3.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.2 + CIP-007-3 R5.2 + CIP-007-3 R5.3.1 + CIP-007-3 R5.3.2 + CIP-007-3 R5.3.3 + CIP-007-3 R6.5 + AC-3 + AC-3(3)(a) + AU-9 + SC-7(21) + DE.AE-1 + ID.AM-3 + PR.AC-4 + PR.AC-5 + PR.AC-6 + PR.DS-5 + PR.PT-1 + PR.PT-3 + PR.PT-4 + SRG-OS-000445-GPOS-00199 + SRG-OS-000134-GPOS-00068 + SRG-OS-000445-VMM-001780 + Setting the SELinux state to enforcing ensures SELinux is able to confine +potentially compromised processes to the security policy, which is designed to +prevent them from causing damage to the system or further elevating their +privileges. + CCE-84079-3 + - name: XCCDF Value var_selinux_state # promote to variable + set_fact: + var_selinux_state: !!str + tags: + - always + +- name: Ensure SELinux State is Enforcing + block: + + - name: Check for duplicate values + lineinfile: + path: /etc/selinux/config + create: false + regexp: ^SELINUX= + state: absent + check_mode: true + changed_when: false + register: dupes + + - name: Deduplicate values from /etc/selinux/config + lineinfile: + path: /etc/selinux/config + create: false + regexp: ^SELINUX= + state: absent + when: dupes.found is defined and dupes.found > 1 + + - name: Insert correct line to /etc/selinux/config + lineinfile: + path: /etc/selinux/config + create: true + regexp: ^SELINUX= + line: SELINUX={{ var_selinux_state }} + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84079-3 + - NIST-800-171-3.1.2 + - NIST-800-171-3.7.2 + - NIST-800-53-AC-3 + - NIST-800-53-AC-3(3)(a) + - NIST-800-53-AU-9 + - NIST-800-53-SC-7(21) + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - selinux_state + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_selinux_state='' + + +if [ -e "/etc/selinux/config" ] ; then + + LC_ALL=C sed -i "/^SELINUX=/Id" "/etc/selinux/config" +else + touch "/etc/selinux/config" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/selinux/config" + +cp "/etc/selinux/config" "/etc/selinux/config.bak" +# Insert at the end of the file +printf '%s\n' "SELINUX=$var_selinux_state" >> "/etc/selinux/config" +# Clean up after ourselves. +rm "/etc/selinux/config.bak" + +fixfiles onboot +fixfiles -f relabel + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Map System Users To The Appropriate SELinux Role + Configure the operating system to prevent non-privileged users from executing +privileged functions to include disabling, circumventing, or altering +implemented security safeguards/countermeasures. All administrators must be +mapped to the sysadm_u or staff_u users with the +appropriate domains (sysadm_t and staff_t). +$ sudo semanage login -m -s sysadm_u USER or +$ sudo semanage login -m -s staff_u USER + +All authorized non-administrative +users must be mapped to the user_u role or the appropriate domain +(user_t). +$ sudo semanage login -m -s user_u USER + CCI-002165 + CCI-002235 + SRG-OS-000324-GPOS-00125 + Preventing non-privileged users from executing privileged functions mitigates +the risk that unauthorized individuals or processes may gain unnecessary access +to information or privileges. + +Privileged functions include, for example, +establishing accounts, performing system integrity checks, or administering +cryptographic key management activities. Non-privileged users are individuals +who do not possess appropriate authorizations. Circumventing intrusion detection +and prevention mechanisms or malicious code protection mechanisms are examples +of privileged functions that require protection from non-privileged users. + + + + + + SELinux - Booleans + Enable or Disable runtime customization of SELinux system policies +without having to reload or recompile the SELinux policy. + + abrt_anon_write SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + abrt_handle_event SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + abrt_upload_watch_anon_write SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + antivirus_can_scan_system SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + antivirus_use_jit SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + auditadm_exec_content SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + authlogin_nsswitch_use_ldap SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + authlogin_radius SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + authlogin_yubikey SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + awstats_purge_apache_log_files SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + boinc_execmem SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + cdrecord_read_content SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + cluster_can_network_connect SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + cluster_manage_all_files SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + cluster_use_execmem SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + cobbler_anon_write SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + cobbler_can_network_connect SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + cobbler_use_cifs SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + cobbler_use_nfs SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + collectd_tcp_network_connect SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + condor_tcp_network_connect SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + conman_can_network SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + container_connect_any SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + cron_can_relabel SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + cron_system_cronjob_use_shares SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + cron_userdomain_transition SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + cups_execmem SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + cvs_read_shadow SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + daemons_dump_core SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + daemons_enable_cluster_mode SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + daemons_use_tcp_wrapper SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + daemons_use_tty SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + dbadm_exec_content SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + dbadm_manage_user_files SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + dbadm_read_user_files SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + deny_execmem SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + deny_ptrace SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + dhcpc_exec_iptables SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + dhcpd_use_ldap SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + domain_fd_use SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + domain_kernel_load_modules SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + entropyd_use_audio SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + exim_can_connect_db SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + exim_manage_user_files SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + exim_read_user_files SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + fcron_crond SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + fenced_can_network_connect SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + fenced_can_ssh SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + fips_mode SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + ftpd_anon_write SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + ftpd_connect_all_unreserved SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + ftpd_connect_db SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + ftpd_full_access SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + ftpd_use_cifs SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + ftpd_use_fusefs SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + ftpd_use_nfs SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + ftpd_use_passive_mode SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + git_cgi_enable_homedirs SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + git_cgi_use_cifs SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + git_cgi_use_nfs SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + git_session_bind_all_unreserved_ports SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + git_session_users SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + git_system_enable_homedirs SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + git_system_use_cifs SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + git_system_use_nfs SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + gitosis_can_sendmail SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + glance_api_can_network SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + glance_use_execmem SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + glance_use_fusefs SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + global_ssp SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + gluster_anon_write SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + gluster_export_all_ro SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + gluster_export_all_rw SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + gpg_web_anon_write SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + gssd_read_tmp SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + guest_exec_content SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + haproxy_connect_any SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + httpd_anon_write SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + httpd_builtin_scripting SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + httpd_can_check_spam SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + httpd_can_connect_ftp SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + httpd_can_connect_ldap SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + httpd_can_connect_mythtv SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + httpd_can_connect_zabbix SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + httpd_can_network_connect SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + httpd_can_network_connect_cobbler SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + httpd_can_network_connect_db SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + httpd_can_network_memcache SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + httpd_can_network_relay SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + httpd_can_sendmail SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + httpd_dbus_avahi SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + httpd_dbus_sssd SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + httpd_dontaudit_search_dirs SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + httpd_enable_cgi SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + httpd_enable_ftp_server SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + httpd_enable_homedirs SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + httpd_execmem SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + httpd_graceful_shutdown SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + httpd_manage_ipa SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + httpd_mod_auth_ntlm_winbind SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + httpd_mod_auth_pam SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + httpd_read_user_content SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + httpd_run_ipa SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + httpd_run_preupgrade SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + httpd_run_stickshift SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + httpd_serve_cobbler_files SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + httpd_setrlimit SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + httpd_ssi_exec SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + httpd_sys_script_anon_write SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + httpd_tmp_exec SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + httpd_tty_comm SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + httpd_unified SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + httpd_use_cifs SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + httpd_use_fusefs SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + httpd_use_gpg SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + httpd_use_nfs SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + httpd_use_openstack SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + httpd_use_sasl SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + httpd_verify_dns SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + icecast_use_any_tcp_ports SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + irc_use_any_tcp_ports SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + irssi_use_full_network SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + kdumpgui_run_bootloader SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + kerberos_enabled SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + ksmtuned_use_cifs SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + ksmtuned_use_nfs SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + logadm_exec_content SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + logging_syslogd_can_sendmail SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + logging_syslogd_run_nagios_plugins SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + logging_syslogd_use_tty SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + login_console_enabled SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + logrotate_use_nfs SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + logwatch_can_network_connect_mail SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + lsmd_plugin_connect_any SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + mailman_use_fusefs SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + mcelog_client SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + mcelog_exec_scripts SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + mcelog_foreground SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + mcelog_server SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + minidlna_read_generic_user_content SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + mmap_low_allowed SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + mock_enable_homedirs SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + mount_anyfile SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + mozilla_plugin_bind_unreserved_ports SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + mozilla_plugin_can_network_connect SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + mozilla_plugin_use_bluejeans SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + mozilla_plugin_use_gps SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + mozilla_plugin_use_spice SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + mozilla_read_content SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + mpd_enable_homedirs SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + mpd_use_cifs SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + mpd_use_nfs SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + mplayer_execstack SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + mysql_connect_any SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + nagios_run_pnp4nagios SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + nagios_run_sudo SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + named_tcp_bind_http_port SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + named_write_master_zones SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + neutron_can_network SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + nfs_export_all_ro SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + nfs_export_all_rw SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + nfsd_anon_write SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + nis_enabled SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + nscd_use_shm SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + openshift_use_nfs SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + openvpn_can_network_connect SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + openvpn_enable_homedirs SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + openvpn_run_unconfined SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + pcp_bind_all_unreserved_ports SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + pcp_read_generic_logs SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + piranha_lvs_can_network_connect SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + polipo_connect_all_unreserved SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + polipo_session_bind_all_unreserved_ports SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + polipo_session_users SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + polipo_use_cifs SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + polipo_use_nfs SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + polyinstantiation_enabled SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + postfix_local_write_mail_spool SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + postgresql_can_rsync SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + postgresql_selinux_transmit_client_label SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + postgresql_selinux_unconfined_dbadm SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + postgresql_selinux_users_ddl SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + pppd_can_insmod SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + pppd_for_user SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + privoxy_connect_any SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + prosody_bind_http_port SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + puppetagent_manage_all_files SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + puppetmaster_use_db SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + racoon_read_shadow SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + rsync_anon_write SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + rsync_client SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + rsync_export_all_ro SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + rsync_full_access SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + samba_create_home_dirs SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + samba_domain_controller SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + samba_enable_home_dirs SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + samba_export_all_ro SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + samba_export_all_rw SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + samba_load_libgfapi SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + samba_portmapper SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + samba_run_unconfined SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + samba_share_fusefs SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + samba_share_nfs SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + sanlock_use_fusefs SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + sanlock_use_nfs SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + sanlock_use_samba SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + saslauthd_read_shadow SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + secadm_exec_content SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + secure_mode SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + secure_mode_insmod SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + secure_mode_policyload SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + selinuxuser_direct_dri_enabled SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + selinuxuser_execheap SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + selinuxuser_execmod SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + selinuxuser_execstack SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + selinuxuser_mysql_connect_enabled SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + selinuxuser_ping SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + selinuxuser_postgresql_connect_enabled SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + selinuxuser_rw_noexattrfile SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + selinuxuser_share_music SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + selinuxuser_tcp_server SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + selinuxuser_udp_server SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + selinuxuser_use_ssh_chroot SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + sge_domain_can_network_connect SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + sge_use_nfs SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + smartmon_3ware SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + smbd_anon_write SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + spamassassin_can_network SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + spamd_enable_home_dirs SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + squid_connect_any SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + squid_use_tproxy SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + ssh_chroot_rw_homedirs SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + ssh_keysign SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + ssh_sysadm_login SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + staff_exec_content SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + staff_use_svirt SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + swift_can_network SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + sysadm_exec_content SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + telepathy_connect_all_ports SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + telepathy_tcp_connect_generic_network_ports SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + tftp_anon_write SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + tftp_home_dir SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + tmpreaper_use_nfs SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + tmpreaper_use_samba SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + tor_bind_all_unreserved_ports SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + tor_can_network_relay SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + unconfined_chrome_sandbox_transition SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + unconfined_login SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + unconfined_mozilla_plugin_transition SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + unprivuser_use_svirt SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + use_ecryptfs_home_dirs SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + use_fusefs_home_dirs SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + use_lpd_server SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + use_nfs_home_dirs SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + use_samba_home_dirs SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + user_exec_content SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + varnishd_connect_any SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + virt_read_qemu_ga_data SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + virt_rw_qemu_ga_data SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + virt_sandbox_use_all_caps SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + virt_sandbox_use_audit SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + virt_sandbox_use_mknod SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + virt_sandbox_use_netlink SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + virt_sandbox_use_sys_admin SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + virt_transition_userdomain SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + virt_use_comm SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + virt_use_execmem SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + virt_use_fusefs SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + virt_use_nfs SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + virt_use_rawip SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + virt_use_samba SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + virt_use_sanlock SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + virt_use_usb SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + virt_use_xserver SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + webadm_manage_user_files SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + webadm_read_user_files SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + wine_mmap_zero_ignore SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + xdm_bind_vnc_tcp_port SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + xdm_exec_bootloader SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + xdm_sysadm_login SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + xdm_write_home SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + xen_use_nfs SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + xend_run_blktap SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + xend_run_qemu SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + xguest_connect_network SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + xguest_exec_content SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + xguest_mount_media SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + xguest_use_bluetooth SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + true + false + true + + + xserver_clients_write_xshm SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + xserver_execmem SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + xserver_object_manager SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + zabbix_can_network SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + zarafa_setrlimit SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + zebra_write_config SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + zoneminder_anon_write SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + zoneminder_run_sudo SELinux Boolean + default - Default SELinux boolean setting. +on - SELinux boolean is enabled. +off - SELinux boolean is disabled. + false + false + true + + + Enable the antivirus_can_scan_system SELinux Boolean + By default, the SELinux boolean antivirus_can_scan_system is disabled. +This setting should be enabled as it allows antivirus programs to read non-security +files on a system. + +To enable the antivirus_can_scan_system SELinux boolean, run the following command: +$ sudo setsebool -P antivirus_can_scan_system on + 3.7.2 + + - name: XCCDF Value var_antivirus_can_scan_system # promote to variable + set_fact: + var_antivirus_can_scan_system: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-171-3.7.2 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_antivirus_can_scan_system + +- name: Set SELinux boolean antivirus_can_scan_system accordingly + seboolean: + name: antivirus_can_scan_system + state: '{{ var_antivirus_can_scan_system }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-171-3.7.2 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_antivirus_can_scan_system + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_antivirus_can_scan_system='' + + +setsebool -P antivirus_can_scan_system $var_antivirus_can_scan_system + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the antivirus_use_jit SELinux Boolean + By default, the SELinux boolean antivirus_use_jit is disabled. +If this setting is enabled, it should be disabled. + +To disable the antivirus_use_jit SELinux boolean, run the following command: +$ sudo setsebool -P antivirus_use_jit off + 3.7.2 + + - name: XCCDF Value var_antivirus_use_jit # promote to variable + set_fact: + var_antivirus_use_jit: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-171-3.7.2 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_antivirus_use_jit + +- name: Set SELinux boolean antivirus_use_jit accordingly + seboolean: + name: antivirus_use_jit + state: '{{ var_antivirus_use_jit }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-171-3.7.2 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_antivirus_use_jit + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_antivirus_use_jit='' + + +setsebool -P antivirus_use_jit $var_antivirus_use_jit + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable the auditadm_exec_content SELinux Boolean + By default, the SELinux boolean auditadm_exec_content is enabled. +If this setting is disabled, it should be enabled. + +To enable the auditadm_exec_content SELinux boolean, run the following command: +$ sudo setsebool -P auditadm_exec_content on + 80424-5 + 0582 + 0584 + 05885 + 0586 + 0846 + 0957 + + CCE-84090-0 + - name: XCCDF Value var_auditadm_exec_content # promote to variable + set_fact: + var_auditadm_exec_content: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84090-0 + - NIST-800-171-80424-5 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_auditadm_exec_content + +- name: Set SELinux boolean auditadm_exec_content accordingly + seboolean: + name: auditadm_exec_content + state: '{{ var_auditadm_exec_content }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84090-0 + - NIST-800-171-80424-5 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_auditadm_exec_content + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_auditadm_exec_content='' + + +setsebool -P auditadm_exec_content $var_auditadm_exec_content + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the authlogin_nsswitch_use_ldap SELinux Boolean + By default, the SELinux boolean authlogin_nsswitch_use_ldap is disabled. +If this setting is enabled, it should be disabled. + +To disable the authlogin_nsswitch_use_ldap SELinux boolean, run the following command: +$ sudo setsebool -P authlogin_nsswitch_use_ldap off + 3.7.2 + 0421 + 0422 + 0431 + 0974 + 1173 + 1401 + 1504 + 1505 + 1546 + 1557 + 1558 + 1559 + 1560 + 1561 + + - name: XCCDF Value var_authlogin_nsswitch_use_ldap # promote to variable + set_fact: + var_authlogin_nsswitch_use_ldap: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-171-3.7.2 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_authlogin_nsswitch_use_ldap + +- name: Set SELinux boolean authlogin_nsswitch_use_ldap accordingly + seboolean: + name: authlogin_nsswitch_use_ldap + state: '{{ var_authlogin_nsswitch_use_ldap }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-171-3.7.2 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_authlogin_nsswitch_use_ldap + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_authlogin_nsswitch_use_ldap='' + + +setsebool -P authlogin_nsswitch_use_ldap $var_authlogin_nsswitch_use_ldap + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the authlogin_radius SELinux Boolean + By default, the SELinux boolean authlogin_radius is disabled. +If this setting is enabled, it should be disabled. + +To disable the authlogin_radius SELinux boolean, run the following command: +$ sudo setsebool -P authlogin_radius off + 3.7.2 + 0421 + 0422 + 0431 + 0974 + 1173 + 1401 + 1504 + 1505 + 1546 + 1557 + 1558 + 1559 + 1560 + 1561 + + - name: XCCDF Value var_authlogin_radius # promote to variable + set_fact: + var_authlogin_radius: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-171-3.7.2 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_authlogin_radius + +- name: Set SELinux boolean authlogin_radius accordingly + seboolean: + name: authlogin_radius + state: '{{ var_authlogin_radius }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-171-3.7.2 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_authlogin_radius + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_authlogin_radius='' + + +setsebool -P authlogin_radius $var_authlogin_radius + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the authlogin_yubikey SELinux Boolean + By default, the SELinux boolean authlogin_yubikey is disabled. +If this setting is enabled, it should be disabled. + +To disable the authlogin_yubikey SELinux boolean, run the following command: +$ sudo setsebool -P authlogin_yubikey off + 3.7.2 + + - name: XCCDF Value var_authlogin_yubikey # promote to variable + set_fact: + var_authlogin_yubikey: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-171-3.7.2 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_authlogin_yubikey + +- name: Set SELinux boolean authlogin_yubikey accordingly + seboolean: + name: authlogin_yubikey + state: '{{ var_authlogin_yubikey }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-171-3.7.2 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_authlogin_yubikey + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_authlogin_yubikey='' + + +setsebool -P authlogin_yubikey $var_authlogin_yubikey + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the awstats_purge_apache_log_files SELinux Boolean + By default, the SELinux boolean awstats_purge_apache_log_files is disabled. +If this setting is enabled, it should be disabled. + +To disable the awstats_purge_apache_log_files SELinux boolean, run the following command: +$ sudo setsebool -P awstats_purge_apache_log_files off + 3.7.2 + + - name: XCCDF Value var_awstats_purge_apache_log_files # promote to variable + set_fact: + var_awstats_purge_apache_log_files: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-171-3.7.2 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_awstats_purge_apache_log_files + +- name: Set SELinux boolean awstats_purge_apache_log_files accordingly + seboolean: + name: awstats_purge_apache_log_files + state: '{{ var_awstats_purge_apache_log_files }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-171-3.7.2 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_awstats_purge_apache_log_files + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_awstats_purge_apache_log_files='' + + +setsebool -P awstats_purge_apache_log_files $var_awstats_purge_apache_log_files + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the boinc_execmem SELinux Boolean + By default, the SELinux boolean boinc_execmem is enabled. +This setting should be disabled. + +To disable the boinc_execmem SELinux boolean, run the following command: +$ sudo setsebool -P boinc_execmem off + BP28(R67) + 3.7.2 + + - name: XCCDF Value var_boinc_execmem # promote to variable + set_fact: + var_boinc_execmem: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-171-3.7.2 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_boinc_execmem + +- name: Set SELinux boolean boinc_execmem accordingly + seboolean: + name: boinc_execmem + state: '{{ var_boinc_execmem }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-171-3.7.2 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_boinc_execmem + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_boinc_execmem='' + + +setsebool -P boinc_execmem $var_boinc_execmem + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the cdrecord_read_content SELinux Boolean + By default, the SELinux boolean cdrecord_read_content is disabled. +If this setting is enabled, it should be disabled. + +To disable the cdrecord_read_content SELinux boolean, run the following command: +$ sudo setsebool -P cdrecord_read_content off + + - name: XCCDF Value var_cdrecord_read_content # promote to variable + set_fact: + var_cdrecord_read_content: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_cdrecord_read_content + +- name: Set SELinux boolean cdrecord_read_content accordingly + seboolean: + name: cdrecord_read_content + state: '{{ var_cdrecord_read_content }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_cdrecord_read_content + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_cdrecord_read_content='' + + +setsebool -P cdrecord_read_content $var_cdrecord_read_content + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the cluster_can_network_connect SELinux Boolean + By default, the SELinux boolean cluster_can_network_connect is disabled. +If this setting is enabled, it should be disabled. + +To disable the cluster_can_network_connect SELinux boolean, run the following command: +$ sudo setsebool -P cluster_can_network_connect off + + - name: XCCDF Value var_cluster_can_network_connect # promote to variable + set_fact: + var_cluster_can_network_connect: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_cluster_can_network_connect + +- name: Set SELinux boolean cluster_can_network_connect accordingly + seboolean: + name: cluster_can_network_connect + state: '{{ var_cluster_can_network_connect }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_cluster_can_network_connect + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_cluster_can_network_connect='' + + +setsebool -P cluster_can_network_connect $var_cluster_can_network_connect + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the cluster_manage_all_files SELinux Boolean + By default, the SELinux boolean cluster_manage_all_files is disabled. +If this setting is enabled, it should be disabled. + +To disable the cluster_manage_all_files SELinux boolean, run the following command: +$ sudo setsebool -P cluster_manage_all_files off + + - name: XCCDF Value var_cluster_manage_all_files # promote to variable + set_fact: + var_cluster_manage_all_files: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_cluster_manage_all_files + +- name: Set SELinux boolean cluster_manage_all_files accordingly + seboolean: + name: cluster_manage_all_files + state: '{{ var_cluster_manage_all_files }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_cluster_manage_all_files + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_cluster_manage_all_files='' + + +setsebool -P cluster_manage_all_files $var_cluster_manage_all_files + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the cluster_use_execmem SELinux Boolean + By default, the SELinux boolean cluster_use_execmem is disabled. +If this setting is enabled, it should be disabled. + +To disable the cluster_use_execmem SELinux boolean, run the following command: +$ sudo setsebool -P cluster_use_execmem off + BP28(R67) + + - name: XCCDF Value var_cluster_use_execmem # promote to variable + set_fact: + var_cluster_use_execmem: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_cluster_use_execmem + +- name: Set SELinux boolean cluster_use_execmem accordingly + seboolean: + name: cluster_use_execmem + state: '{{ var_cluster_use_execmem }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_cluster_use_execmem + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_cluster_use_execmem='' + + +setsebool -P cluster_use_execmem $var_cluster_use_execmem + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the cobbler_anon_write SELinux Boolean + By default, the SELinux boolean cobbler_anon_write is disabled. +If this setting is enabled, it should be disabled. + +To disable the cobbler_anon_write SELinux boolean, run the following command: +$ sudo setsebool -P cobbler_anon_write off + + - name: XCCDF Value var_cobbler_anon_write # promote to variable + set_fact: + var_cobbler_anon_write: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_cobbler_anon_write + +- name: Set SELinux boolean cobbler_anon_write accordingly + seboolean: + name: cobbler_anon_write + state: '{{ var_cobbler_anon_write }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_cobbler_anon_write + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_cobbler_anon_write='' + + +setsebool -P cobbler_anon_write $var_cobbler_anon_write + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the cobbler_can_network_connect SELinux Boolean + By default, the SELinux boolean cobbler_can_network_connect is disabled. +If this setting is enabled, it should be disabled. + +To disable the cobbler_can_network_connect SELinux boolean, run the following command: +$ sudo setsebool -P cobbler_can_network_connect off + + - name: XCCDF Value var_cobbler_can_network_connect # promote to variable + set_fact: + var_cobbler_can_network_connect: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_cobbler_can_network_connect + +- name: Set SELinux boolean cobbler_can_network_connect accordingly + seboolean: + name: cobbler_can_network_connect + state: '{{ var_cobbler_can_network_connect }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_cobbler_can_network_connect + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_cobbler_can_network_connect='' + + +setsebool -P cobbler_can_network_connect $var_cobbler_can_network_connect + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the cobbler_use_cifs SELinux Boolean + By default, the SELinux boolean cobbler_use_cifs is disabled. +If this setting is enabled, it should be disabled. + +To disable the cobbler_use_cifs SELinux boolean, run the following command: +$ sudo setsebool -P cobbler_use_cifs off + + - name: XCCDF Value var_cobbler_use_cifs # promote to variable + set_fact: + var_cobbler_use_cifs: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_cobbler_use_cifs + +- name: Set SELinux boolean cobbler_use_cifs accordingly + seboolean: + name: cobbler_use_cifs + state: '{{ var_cobbler_use_cifs }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_cobbler_use_cifs + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_cobbler_use_cifs='' + + +setsebool -P cobbler_use_cifs $var_cobbler_use_cifs + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the cobbler_use_nfs SELinux Boolean + By default, the SELinux boolean cobbler_use_nfs is disabled. +If this setting is enabled, it should be disabled. + +To disable the cobbler_use_nfs SELinux boolean, run the following command: +$ sudo setsebool -P cobbler_use_nfs off + + - name: XCCDF Value var_cobbler_use_nfs # promote to variable + set_fact: + var_cobbler_use_nfs: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_cobbler_use_nfs + +- name: Set SELinux boolean cobbler_use_nfs accordingly + seboolean: + name: cobbler_use_nfs + state: '{{ var_cobbler_use_nfs }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_cobbler_use_nfs + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_cobbler_use_nfs='' + + +setsebool -P cobbler_use_nfs $var_cobbler_use_nfs + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the collectd_tcp_network_connect SELinux Boolean + By default, the SELinux boolean collectd_tcp_network_connect is disabled. +If this setting is enabled, it should be disabled. + +To disable the collectd_tcp_network_connect SELinux boolean, run the following command: +$ sudo setsebool -P collectd_tcp_network_connect off + + - name: XCCDF Value var_collectd_tcp_network_connect # promote to variable + set_fact: + var_collectd_tcp_network_connect: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_collectd_tcp_network_connect + +- name: Set SELinux boolean collectd_tcp_network_connect accordingly + seboolean: + name: collectd_tcp_network_connect + state: '{{ var_collectd_tcp_network_connect }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_collectd_tcp_network_connect + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_collectd_tcp_network_connect='' + + +setsebool -P collectd_tcp_network_connect $var_collectd_tcp_network_connect + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the condor_tcp_network_connect SELinux Boolean + By default, the SELinux boolean condor_tcp_network_connect is disabled. +If this setting is enabled, it should be disabled. + +To disable the condor_tcp_network_connect SELinux boolean, run the following command: +$ sudo setsebool -P condor_tcp_network_connect off + + - name: XCCDF Value var_condor_tcp_network_connect # promote to variable + set_fact: + var_condor_tcp_network_connect: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_condor_tcp_network_connect + +- name: Set SELinux boolean condor_tcp_network_connect accordingly + seboolean: + name: condor_tcp_network_connect + state: '{{ var_condor_tcp_network_connect }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_condor_tcp_network_connect + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_condor_tcp_network_connect='' + + +setsebool -P condor_tcp_network_connect $var_condor_tcp_network_connect + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the conman_can_network SELinux Boolean + By default, the SELinux boolean conman_can_network is disabled. +If this setting is enabled, it should be disabled. + +To disable the conman_can_network SELinux boolean, run the following command: +$ sudo setsebool -P conman_can_network off + + - name: XCCDF Value var_conman_can_network # promote to variable + set_fact: + var_conman_can_network: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_conman_can_network + +- name: Set SELinux boolean conman_can_network accordingly + seboolean: + name: conman_can_network + state: '{{ var_conman_can_network }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_conman_can_network + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_conman_can_network='' + + +setsebool -P conman_can_network $var_conman_can_network + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the container_connect_any SELinux Boolean + By default, the SELinux boolean container_connect_any is disabled. +If this setting is enabled, it should be disabled. + +To disable the container_connect_any SELinux boolean, run the following command: +$ sudo setsebool -P container_connect_any off + + - name: XCCDF Value var_container_connect_any # promote to variable + set_fact: + var_container_connect_any: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_container_connect_any + +- name: Set SELinux boolean container_connect_any accordingly + seboolean: + name: container_connect_any + state: '{{ var_container_connect_any }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_container_connect_any + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_container_connect_any='' + + +setsebool -P container_connect_any $var_container_connect_any + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the cron_can_relabel SELinux Boolean + By default, the SELinux boolean cron_can_relabel is disabled. +If this setting is enabled, it should be disabled. + +To disable the cron_can_relabel SELinux boolean, run the following command: +$ sudo setsebool -P cron_can_relabel off + + - name: XCCDF Value var_cron_can_relabel # promote to variable + set_fact: + var_cron_can_relabel: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_cron_can_relabel + +- name: Set SELinux boolean cron_can_relabel accordingly + seboolean: + name: cron_can_relabel + state: '{{ var_cron_can_relabel }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_cron_can_relabel + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_cron_can_relabel='' + + +setsebool -P cron_can_relabel $var_cron_can_relabel + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the cron_system_cronjob_use_shares SELinux Boolean + By default, the SELinux boolean cron_system_cronjob_use_shares is disabled. +If this setting is enabled, it should be disabled. + +To disable the cron_system_cronjob_use_shares SELinux boolean, run the following command: +$ sudo setsebool -P cron_system_cronjob_use_shares off + + - name: XCCDF Value var_cron_system_cronjob_use_shares # promote to variable + set_fact: + var_cron_system_cronjob_use_shares: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_cron_system_cronjob_use_shares + +- name: Set SELinux boolean cron_system_cronjob_use_shares accordingly + seboolean: + name: cron_system_cronjob_use_shares + state: '{{ var_cron_system_cronjob_use_shares }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_cron_system_cronjob_use_shares + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_cron_system_cronjob_use_shares='' + + +setsebool -P cron_system_cronjob_use_shares $var_cron_system_cronjob_use_shares + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable the cron_userdomain_transition SELinux Boolean + By default, the SELinux boolean cron_userdomain_transition is enabled. +This setting should be enabled as end user cron jobs run in their default +associated user domain(s) instead of the general cronjob domain. + +To enable the cron_userdomain_transition SELinux boolean, run the following command: +$ sudo setsebool -P cron_userdomain_transition on + + - name: XCCDF Value var_cron_userdomain_transition # promote to variable + set_fact: + var_cron_userdomain_transition: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_cron_userdomain_transition + +- name: Set SELinux boolean cron_userdomain_transition accordingly + seboolean: + name: cron_userdomain_transition + state: '{{ var_cron_userdomain_transition }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_cron_userdomain_transition + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_cron_userdomain_transition='' + + +setsebool -P cron_userdomain_transition $var_cron_userdomain_transition + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the cups_execmem SELinux Boolean + By default, the SELinux boolean cups_execmem is disabled. +If this setting is enabled, it should be disabled. + +To disable the cups_execmem SELinux boolean, run the following command: +$ sudo setsebool -P cups_execmem off + BP28(R67) + + - name: XCCDF Value var_cups_execmem # promote to variable + set_fact: + var_cups_execmem: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_cups_execmem + +- name: Set SELinux boolean cups_execmem accordingly + seboolean: + name: cups_execmem + state: '{{ var_cups_execmem }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_cups_execmem + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_cups_execmem='' + + +setsebool -P cups_execmem $var_cups_execmem + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the cvs_read_shadow SELinux Boolean + By default, the SELinux boolean cvs_read_shadow is disabled. +If this setting is enabled, it should be disabled. + +To disable the cvs_read_shadow SELinux boolean, run the following command: +$ sudo setsebool -P cvs_read_shadow off + + - name: XCCDF Value var_cvs_read_shadow # promote to variable + set_fact: + var_cvs_read_shadow: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_cvs_read_shadow + +- name: Set SELinux boolean cvs_read_shadow accordingly + seboolean: + name: cvs_read_shadow + state: '{{ var_cvs_read_shadow }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_cvs_read_shadow + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_cvs_read_shadow='' + + +setsebool -P cvs_read_shadow $var_cvs_read_shadow + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the daemons_dump_core SELinux Boolean + By default, the SELinux boolean daemons_dump_core is disabled. +If this setting is enabled, it should be disabled. + +To disable the daemons_dump_core SELinux boolean, run the following command: +$ sudo setsebool -P daemons_dump_core off + + - name: XCCDF Value var_daemons_dump_core # promote to variable + set_fact: + var_daemons_dump_core: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_daemons_dump_core + +- name: Set SELinux boolean daemons_dump_core accordingly + seboolean: + name: daemons_dump_core + state: '{{ var_daemons_dump_core }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_daemons_dump_core + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_daemons_dump_core='' + + +setsebool -P daemons_dump_core $var_daemons_dump_core + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the daemons_enable_cluster_mode SELinux Boolean + By default, the SELinux boolean daemons_enable_cluster_mode is disabled. +If this setting is enabled, it should be disabled. + +To disable the daemons_enable_cluster_mode SELinux boolean, run the following command: +$ sudo setsebool -P daemons_enable_cluster_mode off + + - name: XCCDF Value var_daemons_enable_cluster_mode # promote to variable + set_fact: + var_daemons_enable_cluster_mode: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_daemons_enable_cluster_mode + +- name: Set SELinux boolean daemons_enable_cluster_mode accordingly + seboolean: + name: daemons_enable_cluster_mode + state: '{{ var_daemons_enable_cluster_mode }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_daemons_enable_cluster_mode + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_daemons_enable_cluster_mode='' + + +setsebool -P daemons_enable_cluster_mode $var_daemons_enable_cluster_mode + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the daemons_use_tcp_wrapper SELinux Boolean + By default, the SELinux boolean daemons_use_tcp_wrapper is disabled. +If this setting is enabled, it should be disabled. + +To disable the daemons_use_tcp_wrapper SELinux boolean, run the following command: +$ sudo setsebool -P daemons_use_tcp_wrapper off + + - name: XCCDF Value var_daemons_use_tcp_wrapper # promote to variable + set_fact: + var_daemons_use_tcp_wrapper: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_daemons_use_tcp_wrapper + +- name: Set SELinux boolean daemons_use_tcp_wrapper accordingly + seboolean: + name: daemons_use_tcp_wrapper + state: '{{ var_daemons_use_tcp_wrapper }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_daemons_use_tcp_wrapper + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_daemons_use_tcp_wrapper='' + + +setsebool -P daemons_use_tcp_wrapper $var_daemons_use_tcp_wrapper + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the daemons_use_tty SELinux Boolean + By default, the SELinux boolean daemons_use_tty is disabled. +If this setting is enabled, it should be disabled. + +To disable the daemons_use_tty SELinux boolean, run the following command: +$ sudo setsebool -P daemons_use_tty off + + - name: XCCDF Value var_daemons_use_tty # promote to variable + set_fact: + var_daemons_use_tty: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_daemons_use_tty + +- name: Set SELinux boolean daemons_use_tty accordingly + seboolean: + name: daemons_use_tty + state: '{{ var_daemons_use_tty }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_daemons_use_tty + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_daemons_use_tty='' + + +setsebool -P daemons_use_tty $var_daemons_use_tty + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable the dbadm_exec_content SELinux Boolean + By default, the SELinux boolean dbadm_exec_content is enabled. +If this setting is disabled, it should be enabled. + +To enable the dbadm_exec_content SELinux boolean, run the following command: +$ sudo setsebool -P dbadm_exec_content on + + - name: XCCDF Value var_dbadm_exec_content # promote to variable + set_fact: + var_dbadm_exec_content: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_dbadm_exec_content + +- name: Set SELinux boolean dbadm_exec_content accordingly + seboolean: + name: dbadm_exec_content + state: '{{ var_dbadm_exec_content }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_dbadm_exec_content + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_dbadm_exec_content='' + + +setsebool -P dbadm_exec_content $var_dbadm_exec_content + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the dbadm_manage_user_files SELinux Boolean + By default, the SELinux boolean dbadm_manage_user_files is disabled. +If this setting is enabled, it should be disabled. + +To disable the dbadm_manage_user_files SELinux boolean, run the following command: +$ sudo setsebool -P dbadm_manage_user_files off + + - name: XCCDF Value var_dbadm_manage_user_files # promote to variable + set_fact: + var_dbadm_manage_user_files: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_dbadm_manage_user_files + +- name: Set SELinux boolean dbadm_manage_user_files accordingly + seboolean: + name: dbadm_manage_user_files + state: '{{ var_dbadm_manage_user_files }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_dbadm_manage_user_files + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_dbadm_manage_user_files='' + + +setsebool -P dbadm_manage_user_files $var_dbadm_manage_user_files + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the dbadm_read_user_files SELinux Boolean + By default, the SELinux boolean dbadm_read_user_files is disabled. +If this setting is enabled, it should be disabled. + +To disable the dbadm_read_user_files SELinux boolean, run the following command: +$ sudo setsebool -P dbadm_read_user_files off + + - name: XCCDF Value var_dbadm_read_user_files # promote to variable + set_fact: + var_dbadm_read_user_files: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_dbadm_read_user_files + +- name: Set SELinux boolean dbadm_read_user_files accordingly + seboolean: + name: dbadm_read_user_files + state: '{{ var_dbadm_read_user_files }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_dbadm_read_user_files + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_dbadm_read_user_files='' + + +setsebool -P dbadm_read_user_files $var_dbadm_read_user_files + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Configure the deny_execmem SELinux Boolean + By default, the SELinux boolean deny_execmem is disabled. +This setting should be configured to . + +To set the deny_execmem SELinux boolean, run the following command: +$ sudo setsebool -P deny_execmem + This rule doesn't come with a remediation, as enabling this SELinux boolean can cause +applications to malfunction, for example Graphical login managers and Firefox. + Proper function and stability should be assessed before applying enabling the SELinux +boolean in production systems. + BP28(R67) + Allowing user domain applications to map a memory region as both writable and +executable makes them more susceptible to data execution attacks. + CCE-84082-7 + + + + + + + + + + Disable the deny_ptrace SELinux Boolean + By default, the SELinux boolean deny_ptrace is disabled. +If this setting is enabled, it should be disabled. + +To disable the deny_ptrace SELinux boolean, run the following command: +$ sudo setsebool -P deny_ptrace off + + - name: XCCDF Value var_deny_ptrace # promote to variable + set_fact: + var_deny_ptrace: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_deny_ptrace + +- name: Set SELinux boolean deny_ptrace accordingly + seboolean: + name: deny_ptrace + state: '{{ var_deny_ptrace }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_deny_ptrace + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_deny_ptrace='' + + +setsebool -P deny_ptrace $var_deny_ptrace + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the dhcpc_exec_iptables SELinux Boolean + By default, the SELinux boolean dhcpc_exec_iptables is disabled. +If this setting is enabled, it should be disabled. + +To disable the dhcpc_exec_iptables SELinux boolean, run the following command: +$ sudo setsebool -P dhcpc_exec_iptables off + + - name: XCCDF Value var_dhcpc_exec_iptables # promote to variable + set_fact: + var_dhcpc_exec_iptables: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_dhcpc_exec_iptables + +- name: Set SELinux boolean dhcpc_exec_iptables accordingly + seboolean: + name: dhcpc_exec_iptables + state: '{{ var_dhcpc_exec_iptables }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_dhcpc_exec_iptables + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_dhcpc_exec_iptables='' + + +setsebool -P dhcpc_exec_iptables $var_dhcpc_exec_iptables + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the dhcpd_use_ldap SELinux Boolean + By default, the SELinux boolean dhcpd_use_ldap is disabled. +If this setting is enabled, it should be disabled. + +To disable the dhcpd_use_ldap SELinux boolean, run the following command: +$ sudo setsebool -P dhcpd_use_ldap off + + - name: XCCDF Value var_dhcpd_use_ldap # promote to variable + set_fact: + var_dhcpd_use_ldap: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_dhcpd_use_ldap + +- name: Set SELinux boolean dhcpd_use_ldap accordingly + seboolean: + name: dhcpd_use_ldap + state: '{{ var_dhcpd_use_ldap }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_dhcpd_use_ldap + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_dhcpd_use_ldap='' + + +setsebool -P dhcpd_use_ldap $var_dhcpd_use_ldap + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable the domain_fd_use SELinux Boolean + By default, the SELinux boolean domain_fd_use is enabled. +If this setting is disabled, it should be enabled. + +To enable the domain_fd_use SELinux boolean, run the following command: +$ sudo setsebool -P domain_fd_use on + + - name: XCCDF Value var_domain_fd_use # promote to variable + set_fact: + var_domain_fd_use: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_domain_fd_use + +- name: Set SELinux boolean domain_fd_use accordingly + seboolean: + name: domain_fd_use + state: '{{ var_domain_fd_use }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_domain_fd_use + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_domain_fd_use='' + + +setsebool -P domain_fd_use $var_domain_fd_use + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the domain_kernel_load_modules SELinux Boolean + By default, the SELinux boolean domain_kernel_load_modules is disabled. +If this setting is enabled, it should be disabled. + +To disable the domain_kernel_load_modules SELinux boolean, run the following command: +$ sudo setsebool -P domain_kernel_load_modules off + + - name: XCCDF Value var_domain_kernel_load_modules # promote to variable + set_fact: + var_domain_kernel_load_modules: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_domain_kernel_load_modules + +- name: Set SELinux boolean domain_kernel_load_modules accordingly + seboolean: + name: domain_kernel_load_modules + state: '{{ var_domain_kernel_load_modules }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_domain_kernel_load_modules + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_domain_kernel_load_modules='' + + +setsebool -P domain_kernel_load_modules $var_domain_kernel_load_modules + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the entropyd_use_audio SELinux Boolean + By default, the SELinux boolean entropyd_use_audio is enabled. +This setting should be disabled as it uses audit input to generate entropy. + +To disable the entropyd_use_audio SELinux boolean, run the following command: +$ sudo setsebool -P entropyd_use_audio off + + - name: XCCDF Value var_entropyd_use_audio # promote to variable + set_fact: + var_entropyd_use_audio: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_entropyd_use_audio + +- name: Set SELinux boolean entropyd_use_audio accordingly + seboolean: + name: entropyd_use_audio + state: '{{ var_entropyd_use_audio }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_entropyd_use_audio + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_entropyd_use_audio='' + + +setsebool -P entropyd_use_audio $var_entropyd_use_audio + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the exim_can_connect_db SELinux Boolean + By default, the SELinux boolean exim_can_connect_db is disabled. +If this setting is enabled, it should be disabled. + +To disable the exim_can_connect_db SELinux boolean, run the following command: +$ sudo setsebool -P exim_can_connect_db off + + - name: XCCDF Value var_exim_can_connect_db # promote to variable + set_fact: + var_exim_can_connect_db: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_exim_can_connect_db + +- name: Set SELinux boolean exim_can_connect_db accordingly + seboolean: + name: exim_can_connect_db + state: '{{ var_exim_can_connect_db }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_exim_can_connect_db + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_exim_can_connect_db='' + + +setsebool -P exim_can_connect_db $var_exim_can_connect_db + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the exim_manage_user_files SELinux Boolean + By default, the SELinux boolean exim_manage_user_files is disabled. +If this setting is enabled, it should be disabled. + +To disable the exim_manage_user_files SELinux boolean, run the following command: +$ sudo setsebool -P exim_manage_user_files off + + - name: XCCDF Value var_exim_manage_user_files # promote to variable + set_fact: + var_exim_manage_user_files: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_exim_manage_user_files + +- name: Set SELinux boolean exim_manage_user_files accordingly + seboolean: + name: exim_manage_user_files + state: '{{ var_exim_manage_user_files }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_exim_manage_user_files + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_exim_manage_user_files='' + + +setsebool -P exim_manage_user_files $var_exim_manage_user_files + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the exim_read_user_files SELinux Boolean + By default, the SELinux boolean exim_read_user_files is disabled. +If this setting is enabled, it should be disabled. + +To disable the exim_read_user_files SELinux boolean, run the following command: +$ sudo setsebool -P exim_read_user_files off + + - name: XCCDF Value var_exim_read_user_files # promote to variable + set_fact: + var_exim_read_user_files: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_exim_read_user_files + +- name: Set SELinux boolean exim_read_user_files accordingly + seboolean: + name: exim_read_user_files + state: '{{ var_exim_read_user_files }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_exim_read_user_files + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_exim_read_user_files='' + + +setsebool -P exim_read_user_files $var_exim_read_user_files + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the fcron_crond SELinux Boolean + By default, the SELinux boolean fcron_crond is disabled. +If this setting is enabled, it should be disabled. + +To disable the fcron_crond SELinux boolean, run the following command: +$ sudo setsebool -P fcron_crond off + + - name: XCCDF Value var_fcron_crond # promote to variable + set_fact: + var_fcron_crond: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_fcron_crond + +- name: Set SELinux boolean fcron_crond accordingly + seboolean: + name: fcron_crond + state: '{{ var_fcron_crond }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_fcron_crond + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_fcron_crond='' + + +setsebool -P fcron_crond $var_fcron_crond + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the fenced_can_network_connect SELinux Boolean + By default, the SELinux boolean fenced_can_network_connect is disabled. +If this setting is enabled, it should be disabled. + +To disable the fenced_can_network_connect SELinux boolean, run the following command: +$ sudo setsebool -P fenced_can_network_connect off + + - name: XCCDF Value var_fenced_can_network_connect # promote to variable + set_fact: + var_fenced_can_network_connect: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_fenced_can_network_connect + +- name: Set SELinux boolean fenced_can_network_connect accordingly + seboolean: + name: fenced_can_network_connect + state: '{{ var_fenced_can_network_connect }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_fenced_can_network_connect + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_fenced_can_network_connect='' + + +setsebool -P fenced_can_network_connect $var_fenced_can_network_connect + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the fenced_can_ssh SELinux Boolean + By default, the SELinux boolean fenced_can_ssh is disabled. +If this setting is enabled, it should be disabled. + +To disable the fenced_can_ssh SELinux boolean, run the following command: +$ sudo setsebool -P fenced_can_ssh off + + - name: XCCDF Value var_fenced_can_ssh # promote to variable + set_fact: + var_fenced_can_ssh: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_fenced_can_ssh + +- name: Set SELinux boolean fenced_can_ssh accordingly + seboolean: + name: fenced_can_ssh + state: '{{ var_fenced_can_ssh }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_fenced_can_ssh + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_fenced_can_ssh='' + + +setsebool -P fenced_can_ssh $var_fenced_can_ssh + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable the fips_mode SELinux Boolean + By default, the SELinux boolean fips_mode is enabled. +This allows all SELinux domains to execute in fips_mode. +If this setting is disabled, it should be enabled. + +To enable the fips_mode SELinux boolean, run the following command: +$ sudo setsebool -P fips_mode on + 13 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + 3.13.11 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + SC-12(2) + SC-12(3) + IA-7 + SC-13 + CM-6(a) + SC-12 + PR.DS-5 + + - name: XCCDF Value var_fips_mode # promote to variable + set_fact: + var_fips_mode: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-171-3.13.11 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-7 + - NIST-800-53-SC-12 + - NIST-800-53-SC-12(2) + - NIST-800-53-SC-12(3) + - NIST-800-53-SC-13 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_fips_mode + +- name: Set SELinux boolean fips_mode accordingly + seboolean: + name: fips_mode + state: '{{ var_fips_mode }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-171-3.13.11 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-7 + - NIST-800-53-SC-12 + - NIST-800-53-SC-12(2) + - NIST-800-53-SC-12(3) + - NIST-800-53-SC-13 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_fips_mode + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_fips_mode='' + + +setsebool -P fips_mode $var_fips_mode + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the ftpd_anon_write SELinux Boolean + By default, the SELinux boolean ftpd_anon_write is disabled. +If this setting is enabled, it should be disabled. + +To disable the ftpd_anon_write SELinux boolean, run the following command: +$ sudo setsebool -P ftpd_anon_write off + + - name: XCCDF Value var_ftpd_anon_write # promote to variable + set_fact: + var_ftpd_anon_write: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_ftpd_anon_write + +- name: Set SELinux boolean ftpd_anon_write accordingly + seboolean: + name: ftpd_anon_write + state: '{{ var_ftpd_anon_write }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_ftpd_anon_write + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_ftpd_anon_write='' + + +setsebool -P ftpd_anon_write $var_ftpd_anon_write + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the ftpd_connect_all_unreserved SELinux Boolean + By default, the SELinux boolean ftpd_connect_all_unreserved is disabled. +If this setting is enabled, it should be disabled. + +To disable the ftpd_connect_all_unreserved SELinux boolean, run the following command: +$ sudo setsebool -P ftpd_connect_all_unreserved off + + - name: XCCDF Value var_ftpd_connect_all_unreserved # promote to variable + set_fact: + var_ftpd_connect_all_unreserved: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_ftpd_connect_all_unreserved + +- name: Set SELinux boolean ftpd_connect_all_unreserved accordingly + seboolean: + name: ftpd_connect_all_unreserved + state: '{{ var_ftpd_connect_all_unreserved }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_ftpd_connect_all_unreserved + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_ftpd_connect_all_unreserved='' + + +setsebool -P ftpd_connect_all_unreserved $var_ftpd_connect_all_unreserved + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the ftpd_connect_db SELinux Boolean + By default, the SELinux boolean ftpd_connect_db is disabled. +If this setting is enabled, it should be disabled. + +To disable the ftpd_connect_db SELinux boolean, run the following command: +$ sudo setsebool -P ftpd_connect_db off + + - name: XCCDF Value var_ftpd_connect_db # promote to variable + set_fact: + var_ftpd_connect_db: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_ftpd_connect_db + +- name: Set SELinux boolean ftpd_connect_db accordingly + seboolean: + name: ftpd_connect_db + state: '{{ var_ftpd_connect_db }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_ftpd_connect_db + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_ftpd_connect_db='' + + +setsebool -P ftpd_connect_db $var_ftpd_connect_db + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the ftpd_full_access SELinux Boolean + By default, the SELinux boolean ftpd_full_access is disabled. +If this setting is enabled, it should be disabled. + +To disable the ftpd_full_access SELinux boolean, run the following command: +$ sudo setsebool -P ftpd_full_access off + + - name: XCCDF Value var_ftpd_full_access # promote to variable + set_fact: + var_ftpd_full_access: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_ftpd_full_access + +- name: Set SELinux boolean ftpd_full_access accordingly + seboolean: + name: ftpd_full_access + state: '{{ var_ftpd_full_access }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_ftpd_full_access + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_ftpd_full_access='' + + +setsebool -P ftpd_full_access $var_ftpd_full_access + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the ftpd_use_cifs SELinux Boolean + By default, the SELinux boolean ftpd_use_cifs is disabled. +If this setting is enabled, it should be disabled. + +To disable the ftpd_use_cifs SELinux boolean, run the following command: +$ sudo setsebool -P ftpd_use_cifs off + + - name: XCCDF Value var_ftpd_use_cifs # promote to variable + set_fact: + var_ftpd_use_cifs: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_ftpd_use_cifs + +- name: Set SELinux boolean ftpd_use_cifs accordingly + seboolean: + name: ftpd_use_cifs + state: '{{ var_ftpd_use_cifs }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_ftpd_use_cifs + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_ftpd_use_cifs='' + + +setsebool -P ftpd_use_cifs $var_ftpd_use_cifs + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the ftpd_use_fusefs SELinux Boolean + By default, the SELinux boolean ftpd_use_fusefs is disabled. +If this setting is enabled, it should be disabled. + +To disable the ftpd_use_fusefs SELinux boolean, run the following command: +$ sudo setsebool -P ftpd_use_fusefs off + + - name: XCCDF Value var_ftpd_use_fusefs # promote to variable + set_fact: + var_ftpd_use_fusefs: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_ftpd_use_fusefs + +- name: Set SELinux boolean ftpd_use_fusefs accordingly + seboolean: + name: ftpd_use_fusefs + state: '{{ var_ftpd_use_fusefs }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_ftpd_use_fusefs + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_ftpd_use_fusefs='' + + +setsebool -P ftpd_use_fusefs $var_ftpd_use_fusefs + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the ftpd_use_nfs SELinux Boolean + By default, the SELinux boolean ftpd_use_nfs is disabled. +If this setting is enabled, it should be disabled. + +To disable the ftpd_use_nfs SELinux boolean, run the following command: +$ sudo setsebool -P ftpd_use_nfs off + + - name: XCCDF Value var_ftpd_use_nfs # promote to variable + set_fact: + var_ftpd_use_nfs: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_ftpd_use_nfs + +- name: Set SELinux boolean ftpd_use_nfs accordingly + seboolean: + name: ftpd_use_nfs + state: '{{ var_ftpd_use_nfs }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_ftpd_use_nfs + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_ftpd_use_nfs='' + + +setsebool -P ftpd_use_nfs $var_ftpd_use_nfs + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the ftpd_use_passive_mode SELinux Boolean + By default, the SELinux boolean ftpd_use_passive_mode is disabled. +If this setting is enabled, it should be disabled. + +To disable the ftpd_use_passive_mode SELinux boolean, run the following command: +$ sudo setsebool -P ftpd_use_passive_mode off + + - name: XCCDF Value var_ftpd_use_passive_mode # promote to variable + set_fact: + var_ftpd_use_passive_mode: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_ftpd_use_passive_mode + +- name: Set SELinux boolean ftpd_use_passive_mode accordingly + seboolean: + name: ftpd_use_passive_mode + state: '{{ var_ftpd_use_passive_mode }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_ftpd_use_passive_mode + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_ftpd_use_passive_mode='' + + +setsebool -P ftpd_use_passive_mode $var_ftpd_use_passive_mode + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the git_cgi_enable_homedirs SELinux Boolean + By default, the SELinux boolean git_cgi_enable_homedirs is disabled. +If this setting is enabled, it should be disabled. + +To disable the git_cgi_enable_homedirs SELinux boolean, run the following command: +$ sudo setsebool -P git_cgi_enable_homedirs off + + - name: XCCDF Value var_git_cgi_enable_homedirs # promote to variable + set_fact: + var_git_cgi_enable_homedirs: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_git_cgi_enable_homedirs + +- name: Set SELinux boolean git_cgi_enable_homedirs accordingly + seboolean: + name: git_cgi_enable_homedirs + state: '{{ var_git_cgi_enable_homedirs }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_git_cgi_enable_homedirs + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_git_cgi_enable_homedirs='' + + +setsebool -P git_cgi_enable_homedirs $var_git_cgi_enable_homedirs + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the git_cgi_use_cifs SELinux Boolean + By default, the SELinux boolean git_cgi_use_cifs is disabled. +If this setting is enabled, it should be disabled. + +To disable the git_cgi_use_cifs SELinux boolean, run the following command: +$ sudo setsebool -P git_cgi_use_cifs off + + - name: XCCDF Value var_git_cgi_use_cifs # promote to variable + set_fact: + var_git_cgi_use_cifs: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_git_cgi_use_cifs + +- name: Set SELinux boolean git_cgi_use_cifs accordingly + seboolean: + name: git_cgi_use_cifs + state: '{{ var_git_cgi_use_cifs }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_git_cgi_use_cifs + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_git_cgi_use_cifs='' + + +setsebool -P git_cgi_use_cifs $var_git_cgi_use_cifs + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the git_cgi_use_nfs SELinux Boolean + By default, the SELinux boolean git_cgi_use_nfs is disabled. +If this setting is enabled, it should be disabled. + +To disable the git_cgi_use_nfs SELinux boolean, run the following command: +$ sudo setsebool -P git_cgi_use_nfs off + + - name: XCCDF Value var_git_cgi_use_nfs # promote to variable + set_fact: + var_git_cgi_use_nfs: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_git_cgi_use_nfs + +- name: Set SELinux boolean git_cgi_use_nfs accordingly + seboolean: + name: git_cgi_use_nfs + state: '{{ var_git_cgi_use_nfs }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_git_cgi_use_nfs + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_git_cgi_use_nfs='' + + +setsebool -P git_cgi_use_nfs $var_git_cgi_use_nfs + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the git_session_bind_all_unreserved_ports SELinux Boolean + By default, the SELinux boolean git_session_bind_all_unreserved_ports is disabled. +If this setting is enabled, it should be disabled. + +To disable the git_session_bind_all_unreserved_ports SELinux boolean, run the following command: +$ sudo setsebool -P git_session_bind_all_unreserved_ports off + + - name: XCCDF Value var_git_session_bind_all_unreserved_ports # promote to variable + set_fact: + var_git_session_bind_all_unreserved_ports: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_git_session_bind_all_unreserved_ports + +- name: Set SELinux boolean git_session_bind_all_unreserved_ports accordingly + seboolean: + name: git_session_bind_all_unreserved_ports + state: '{{ var_git_session_bind_all_unreserved_ports }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_git_session_bind_all_unreserved_ports + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_git_session_bind_all_unreserved_ports='' + + +setsebool -P git_session_bind_all_unreserved_ports $var_git_session_bind_all_unreserved_ports + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the git_session_users SELinux Boolean + By default, the SELinux boolean git_session_users is disabled. +If this setting is enabled, it should be disabled. + +To disable the git_session_users SELinux boolean, run the following command: +$ sudo setsebool -P git_session_users off + + - name: XCCDF Value var_git_session_users # promote to variable + set_fact: + var_git_session_users: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_git_session_users + +- name: Set SELinux boolean git_session_users accordingly + seboolean: + name: git_session_users + state: '{{ var_git_session_users }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_git_session_users + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_git_session_users='' + + +setsebool -P git_session_users $var_git_session_users + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the git_system_enable_homedirs SELinux Boolean + By default, the SELinux boolean git_system_enable_homedirs is disabled. +If this setting is enabled, it should be disabled. + +To disable the git_system_enable_homedirs SELinux boolean, run the following command: +$ sudo setsebool -P git_system_enable_homedirs off + + - name: XCCDF Value var_git_system_enable_homedirs # promote to variable + set_fact: + var_git_system_enable_homedirs: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_git_system_enable_homedirs + +- name: Set SELinux boolean git_system_enable_homedirs accordingly + seboolean: + name: git_system_enable_homedirs + state: '{{ var_git_system_enable_homedirs }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_git_system_enable_homedirs + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_git_system_enable_homedirs='' + + +setsebool -P git_system_enable_homedirs $var_git_system_enable_homedirs + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the git_system_use_cifs SELinux Boolean + By default, the SELinux boolean git_system_use_cifs is disabled. +If this setting is enabled, it should be disabled. + +To disable the git_system_use_cifs SELinux boolean, run the following command: +$ sudo setsebool -P git_system_use_cifs off + + - name: XCCDF Value var_git_system_use_cifs # promote to variable + set_fact: + var_git_system_use_cifs: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_git_system_use_cifs + +- name: Set SELinux boolean git_system_use_cifs accordingly + seboolean: + name: git_system_use_cifs + state: '{{ var_git_system_use_cifs }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_git_system_use_cifs + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_git_system_use_cifs='' + + +setsebool -P git_system_use_cifs $var_git_system_use_cifs + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the git_system_use_nfs SELinux Boolean + By default, the SELinux boolean git_system_use_nfs is disabled. +If this setting is enabled, it should be disabled. + +To disable the git_system_use_nfs SELinux boolean, run the following command: +$ sudo setsebool -P git_system_use_nfs off + + - name: XCCDF Value var_git_system_use_nfs # promote to variable + set_fact: + var_git_system_use_nfs: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_git_system_use_nfs + +- name: Set SELinux boolean git_system_use_nfs accordingly + seboolean: + name: git_system_use_nfs + state: '{{ var_git_system_use_nfs }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_git_system_use_nfs + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_git_system_use_nfs='' + + +setsebool -P git_system_use_nfs $var_git_system_use_nfs + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the gitosis_can_sendmail SELinux Boolean + By default, the SELinux boolean gitosis_can_sendmail is disabled. +If this setting is enabled, it should be disabled. + +To disable the gitosis_can_sendmail SELinux boolean, run the following command: +$ sudo setsebool -P gitosis_can_sendmail off + + - name: XCCDF Value var_gitosis_can_sendmail # promote to variable + set_fact: + var_gitosis_can_sendmail: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_gitosis_can_sendmail + +- name: Set SELinux boolean gitosis_can_sendmail accordingly + seboolean: + name: gitosis_can_sendmail + state: '{{ var_gitosis_can_sendmail }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_gitosis_can_sendmail + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_gitosis_can_sendmail='' + + +setsebool -P gitosis_can_sendmail $var_gitosis_can_sendmail + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the glance_api_can_network SELinux Boolean + By default, the SELinux boolean glance_api_can_network is disabled. +If this setting is enabled, it should be disabled. + +To disable the glance_api_can_network SELinux boolean, run the following command: +$ sudo setsebool -P glance_api_can_network off + + - name: XCCDF Value var_glance_api_can_network # promote to variable + set_fact: + var_glance_api_can_network: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_glance_api_can_network + +- name: Set SELinux boolean glance_api_can_network accordingly + seboolean: + name: glance_api_can_network + state: '{{ var_glance_api_can_network }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_glance_api_can_network + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_glance_api_can_network='' + + +setsebool -P glance_api_can_network $var_glance_api_can_network + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the glance_use_execmem SELinux Boolean + By default, the SELinux boolean glance_use_execmem is disabled. +If this setting is enabled, it should be disabled. + +To disable the glance_use_execmem SELinux boolean, run the following command: +$ sudo setsebool -P glance_use_execmem off + BP28(R67) + + - name: XCCDF Value var_glance_use_execmem # promote to variable + set_fact: + var_glance_use_execmem: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_glance_use_execmem + +- name: Set SELinux boolean glance_use_execmem accordingly + seboolean: + name: glance_use_execmem + state: '{{ var_glance_use_execmem }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_glance_use_execmem + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_glance_use_execmem='' + + +setsebool -P glance_use_execmem $var_glance_use_execmem + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the glance_use_fusefs SELinux Boolean + By default, the SELinux boolean glance_use_fusefs is disabled. +If this setting is enabled, it should be disabled. + +To disable the glance_use_fusefs SELinux boolean, run the following command: +$ sudo setsebool -P glance_use_fusefs off + + - name: XCCDF Value var_glance_use_fusefs # promote to variable + set_fact: + var_glance_use_fusefs: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_glance_use_fusefs + +- name: Set SELinux boolean glance_use_fusefs accordingly + seboolean: + name: glance_use_fusefs + state: '{{ var_glance_use_fusefs }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_glance_use_fusefs + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_glance_use_fusefs='' + + +setsebool -P glance_use_fusefs $var_glance_use_fusefs + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the global_ssp SELinux Boolean + By default, the SELinux boolean global_ssp is disabled. +If this setting is enabled, it should be disabled. + +To disable the global_ssp SELinux boolean, run the following command: +$ sudo setsebool -P global_ssp off + + - name: XCCDF Value var_global_ssp # promote to variable + set_fact: + var_global_ssp: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_global_ssp + +- name: Set SELinux boolean global_ssp accordingly + seboolean: + name: global_ssp + state: '{{ var_global_ssp }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_global_ssp + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_global_ssp='' + + +setsebool -P global_ssp $var_global_ssp + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the gluster_anon_write SELinux Boolean + By default, the SELinux boolean gluster_anon_write is disabled. +If this setting is enabled, it should be disabled. + +To disable the gluster_anon_write SELinux boolean, run the following command: +$ sudo setsebool -P gluster_anon_write off + + - name: XCCDF Value var_gluster_anon_write # promote to variable + set_fact: + var_gluster_anon_write: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_gluster_anon_write + +- name: Set SELinux boolean gluster_anon_write accordingly + seboolean: + name: gluster_anon_write + state: '{{ var_gluster_anon_write }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_gluster_anon_write + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_gluster_anon_write='' + + +setsebool -P gluster_anon_write $var_gluster_anon_write + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the gluster_export_all_ro SELinux Boolean + By default, the SELinux boolean gluster_export_all_ro is disabled. +If this setting is enabled, it should be disabled. + +To disable the gluster_export_all_ro SELinux boolean, run the following command: +$ sudo setsebool -P gluster_export_all_ro off + + - name: XCCDF Value var_gluster_export_all_ro # promote to variable + set_fact: + var_gluster_export_all_ro: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_gluster_export_all_ro + +- name: Set SELinux boolean gluster_export_all_ro accordingly + seboolean: + name: gluster_export_all_ro + state: '{{ var_gluster_export_all_ro }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_gluster_export_all_ro + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_gluster_export_all_ro='' + + +setsebool -P gluster_export_all_ro $var_gluster_export_all_ro + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Configure the gluster_export_all_rw SELinux Boolean + By default, the SELinux boolean gluster_export_all_rw is enabled. +If GlusterFS is in use, this setting should be enabled. Otherwise, +disable it. + +To disable the gluster_export_all_rw SELinux boolean, run the following command: +$ sudo setsebool -P gluster_export_all_rw off + + - name: XCCDF Value var_gluster_export_all_rw # promote to variable + set_fact: + var_gluster_export_all_rw: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_gluster_export_all_rw + +- name: Set SELinux boolean gluster_export_all_rw accordingly + seboolean: + name: gluster_export_all_rw + state: '{{ var_gluster_export_all_rw }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_gluster_export_all_rw + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_gluster_export_all_rw='' + + +setsebool -P gluster_export_all_rw $var_gluster_export_all_rw + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the gpg_web_anon_write SELinux Boolean + By default, the SELinux boolean gpg_web_anon_write is disabled. +If this setting is enabled, it should be disabled. + +To disable the gpg_web_anon_write SELinux boolean, run the following command: +$ sudo setsebool -P gpg_web_anon_write off + + - name: XCCDF Value var_gpg_web_anon_write # promote to variable + set_fact: + var_gpg_web_anon_write: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_gpg_web_anon_write + +- name: Set SELinux boolean gpg_web_anon_write accordingly + seboolean: + name: gpg_web_anon_write + state: '{{ var_gpg_web_anon_write }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_gpg_web_anon_write + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_gpg_web_anon_write='' + + +setsebool -P gpg_web_anon_write $var_gpg_web_anon_write + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable the gssd_read_tmp SELinux Boolean + By default, the SELinux boolean gssd_read_tmp is enabled. +This setting allows gssd processes to access Kerberos to read +TGTs in the temp directory. If this setting is disabled, it should +be enabled. + +To enable the gssd_read_tmp SELinux boolean, run the following command: +$ sudo setsebool -P gssd_read_tmp on + + - name: XCCDF Value var_gssd_read_tmp # promote to variable + set_fact: + var_gssd_read_tmp: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_gssd_read_tmp + +- name: Set SELinux boolean gssd_read_tmp accordingly + seboolean: + name: gssd_read_tmp + state: '{{ var_gssd_read_tmp }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_gssd_read_tmp + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_gssd_read_tmp='' + + +setsebool -P gssd_read_tmp $var_gssd_read_tmp + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the guest_exec_content SELinux Boolean + By default, the SELinux boolean guest_exec_content is enabled. +This setting should be disabled as no guest accounts should be used. + +To disable the guest_exec_content SELinux boolean, run the following command: +$ sudo setsebool -P guest_exec_content off + + - name: XCCDF Value var_guest_exec_content # promote to variable + set_fact: + var_guest_exec_content: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_guest_exec_content + +- name: Set SELinux boolean guest_exec_content accordingly + seboolean: + name: guest_exec_content + state: '{{ var_guest_exec_content }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_guest_exec_content + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_guest_exec_content='' + + +setsebool -P guest_exec_content $var_guest_exec_content + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the haproxy_connect_any SELinux Boolean + By default, the SELinux boolean haproxy_connect_any is disabled. +If this setting is enabled, it should be disabled. + +To disable the haproxy_connect_any SELinux boolean, run the following command: +$ sudo setsebool -P haproxy_connect_any off + + - name: XCCDF Value var_haproxy_connect_any # promote to variable + set_fact: + var_haproxy_connect_any: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_haproxy_connect_any + +- name: Set SELinux boolean haproxy_connect_any accordingly + seboolean: + name: haproxy_connect_any + state: '{{ var_haproxy_connect_any }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_haproxy_connect_any + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_haproxy_connect_any='' + + +setsebool -P haproxy_connect_any $var_haproxy_connect_any + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the httpd_anon_write SELinux Boolean + By default, the SELinux boolean httpd_anon_write is disabled. +If this setting is enabled, it should be disabled. + +To disable the httpd_anon_write SELinux boolean, run the following command: +$ sudo setsebool -P httpd_anon_write off + + - name: XCCDF Value var_httpd_anon_write # promote to variable + set_fact: + var_httpd_anon_write: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_anon_write + +- name: Set SELinux boolean httpd_anon_write accordingly + seboolean: + name: httpd_anon_write + state: '{{ var_httpd_anon_write }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_anon_write + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_httpd_anon_write='' + + +setsebool -P httpd_anon_write $var_httpd_anon_write + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Configure the httpd_builtin_scripting SELinux Boolean + By default, the SELinux boolean httpd_builtin_scripting is enabled. +This setting should be disabled if httpd is not running php +or some similary scripting language. + +To disable the httpd_builtin_scripting SELinux boolean, run the following command: +$ sudo setsebool -P httpd_builtin_scripting off + + - name: XCCDF Value var_httpd_builtin_scripting # promote to variable + set_fact: + var_httpd_builtin_scripting: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_builtin_scripting + +- name: Set SELinux boolean httpd_builtin_scripting accordingly + seboolean: + name: httpd_builtin_scripting + state: '{{ var_httpd_builtin_scripting }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_builtin_scripting + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_httpd_builtin_scripting='' + + +setsebool -P httpd_builtin_scripting $var_httpd_builtin_scripting + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the httpd_can_check_spam SELinux Boolean + By default, the SELinux boolean httpd_can_check_spam is disabled. +If this setting is enabled, it should be disabled. + +To disable the httpd_can_check_spam SELinux boolean, run the following command: +$ sudo setsebool -P httpd_can_check_spam off + + - name: XCCDF Value var_httpd_can_check_spam # promote to variable + set_fact: + var_httpd_can_check_spam: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_can_check_spam + +- name: Set SELinux boolean httpd_can_check_spam accordingly + seboolean: + name: httpd_can_check_spam + state: '{{ var_httpd_can_check_spam }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_can_check_spam + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_httpd_can_check_spam='' + + +setsebool -P httpd_can_check_spam $var_httpd_can_check_spam + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the httpd_can_connect_ftp SELinux Boolean + By default, the SELinux boolean httpd_can_connect_ftp is disabled. +If this setting is enabled, it should be disabled. + +To disable the httpd_can_connect_ftp SELinux boolean, run the following command: +$ sudo setsebool -P httpd_can_connect_ftp off + + - name: XCCDF Value var_httpd_can_connect_ftp # promote to variable + set_fact: + var_httpd_can_connect_ftp: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_can_connect_ftp + +- name: Set SELinux boolean httpd_can_connect_ftp accordingly + seboolean: + name: httpd_can_connect_ftp + state: '{{ var_httpd_can_connect_ftp }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_can_connect_ftp + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_httpd_can_connect_ftp='' + + +setsebool -P httpd_can_connect_ftp $var_httpd_can_connect_ftp + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the httpd_can_connect_ldap SELinux Boolean + By default, the SELinux boolean httpd_can_connect_ldap is disabled. +If this setting is enabled, it should be disabled. + +To disable the httpd_can_connect_ldap SELinux boolean, run the following command: +$ sudo setsebool -P httpd_can_connect_ldap off + + - name: XCCDF Value var_httpd_can_connect_ldap # promote to variable + set_fact: + var_httpd_can_connect_ldap: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_can_connect_ldap + +- name: Set SELinux boolean httpd_can_connect_ldap accordingly + seboolean: + name: httpd_can_connect_ldap + state: '{{ var_httpd_can_connect_ldap }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_can_connect_ldap + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_httpd_can_connect_ldap='' + + +setsebool -P httpd_can_connect_ldap $var_httpd_can_connect_ldap + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the httpd_can_connect_mythtv SELinux Boolean + By default, the SELinux boolean httpd_can_connect_mythtv is disabled. +If this setting is enabled, it should be disabled. + +To disable the httpd_can_connect_mythtv SELinux boolean, run the following command: +$ sudo setsebool -P httpd_can_connect_mythtv off + + - name: XCCDF Value var_httpd_can_connect_mythtv # promote to variable + set_fact: + var_httpd_can_connect_mythtv: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_can_connect_mythtv + +- name: Set SELinux boolean httpd_can_connect_mythtv accordingly + seboolean: + name: httpd_can_connect_mythtv + state: '{{ var_httpd_can_connect_mythtv }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_can_connect_mythtv + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_httpd_can_connect_mythtv='' + + +setsebool -P httpd_can_connect_mythtv $var_httpd_can_connect_mythtv + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the httpd_can_connect_zabbix SELinux Boolean + By default, the SELinux boolean httpd_can_connect_zabbix is disabled. +If this setting is enabled, it should be disabled. + +To disable the httpd_can_connect_zabbix SELinux boolean, run the following command: +$ sudo setsebool -P httpd_can_connect_zabbix off + + - name: XCCDF Value var_httpd_can_connect_zabbix # promote to variable + set_fact: + var_httpd_can_connect_zabbix: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_can_connect_zabbix + +- name: Set SELinux boolean httpd_can_connect_zabbix accordingly + seboolean: + name: httpd_can_connect_zabbix + state: '{{ var_httpd_can_connect_zabbix }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_can_connect_zabbix + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_httpd_can_connect_zabbix='' + + +setsebool -P httpd_can_connect_zabbix $var_httpd_can_connect_zabbix + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the httpd_can_network_connect SELinux Boolean + By default, the SELinux boolean httpd_can_network_connect is disabled. +If this setting is enabled, it should be disabled. + +To disable the httpd_can_network_connect SELinux boolean, run the following command: +$ sudo setsebool -P httpd_can_network_connect off + + - name: XCCDF Value var_httpd_can_network_connect # promote to variable + set_fact: + var_httpd_can_network_connect: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_can_network_connect + +- name: Set SELinux boolean httpd_can_network_connect accordingly + seboolean: + name: httpd_can_network_connect + state: '{{ var_httpd_can_network_connect }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_can_network_connect + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_httpd_can_network_connect='' + + +setsebool -P httpd_can_network_connect $var_httpd_can_network_connect + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the httpd_can_network_connect_cobbler SELinux Boolean + By default, the SELinux boolean httpd_can_network_connect_cobbler is disabled. +If this setting is enabled, it should be disabled. + +To disable the httpd_can_network_connect_cobbler SELinux boolean, run the following command: +$ sudo setsebool -P httpd_can_network_connect_cobbler off + + - name: XCCDF Value var_httpd_can_network_connect_cobbler # promote to variable + set_fact: + var_httpd_can_network_connect_cobbler: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_can_network_connect_cobbler + +- name: Set SELinux boolean httpd_can_network_connect_cobbler accordingly + seboolean: + name: httpd_can_network_connect_cobbler + state: '{{ var_httpd_can_network_connect_cobbler }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_can_network_connect_cobbler + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_httpd_can_network_connect_cobbler='' + + +setsebool -P httpd_can_network_connect_cobbler $var_httpd_can_network_connect_cobbler + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the httpd_can_network_connect_db SELinux Boolean + By default, the SELinux boolean httpd_can_network_connect_db is disabled. +If this setting is enabled, it should be disabled. + +To disable the httpd_can_network_connect_db SELinux boolean, run the following command: +$ sudo setsebool -P httpd_can_network_connect_db off + + - name: XCCDF Value var_httpd_can_network_connect_db # promote to variable + set_fact: + var_httpd_can_network_connect_db: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_can_network_connect_db + +- name: Set SELinux boolean httpd_can_network_connect_db accordingly + seboolean: + name: httpd_can_network_connect_db + state: '{{ var_httpd_can_network_connect_db }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_can_network_connect_db + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_httpd_can_network_connect_db='' + + +setsebool -P httpd_can_network_connect_db $var_httpd_can_network_connect_db + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the httpd_can_network_memcache SELinux Boolean + By default, the SELinux boolean httpd_can_network_memcache is disabled. +If this setting is enabled, it should be disabled. + +To disable the httpd_can_network_memcache SELinux boolean, run the following command: +$ sudo setsebool -P httpd_can_network_memcache off + + - name: XCCDF Value var_httpd_can_network_memcache # promote to variable + set_fact: + var_httpd_can_network_memcache: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_can_network_memcache + +- name: Set SELinux boolean httpd_can_network_memcache accordingly + seboolean: + name: httpd_can_network_memcache + state: '{{ var_httpd_can_network_memcache }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_can_network_memcache + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_httpd_can_network_memcache='' + + +setsebool -P httpd_can_network_memcache $var_httpd_can_network_memcache + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the httpd_can_network_relay SELinux Boolean + By default, the SELinux boolean httpd_can_network_relay is disabled. +If this setting is enabled, it should be disabled. + +To disable the httpd_can_network_relay SELinux boolean, run the following command: +$ sudo setsebool -P httpd_can_network_relay off + + - name: XCCDF Value var_httpd_can_network_relay # promote to variable + set_fact: + var_httpd_can_network_relay: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_can_network_relay + +- name: Set SELinux boolean httpd_can_network_relay accordingly + seboolean: + name: httpd_can_network_relay + state: '{{ var_httpd_can_network_relay }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_can_network_relay + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_httpd_can_network_relay='' + + +setsebool -P httpd_can_network_relay $var_httpd_can_network_relay + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the httpd_can_sendmail SELinux Boolean + By default, the SELinux boolean httpd_can_sendmail is disabled. +If this setting is enabled, it should be disabled. + +To disable the httpd_can_sendmail SELinux boolean, run the following command: +$ sudo setsebool -P httpd_can_sendmail off + + - name: XCCDF Value var_httpd_can_sendmail # promote to variable + set_fact: + var_httpd_can_sendmail: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_can_sendmail + +- name: Set SELinux boolean httpd_can_sendmail accordingly + seboolean: + name: httpd_can_sendmail + state: '{{ var_httpd_can_sendmail }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_can_sendmail + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_httpd_can_sendmail='' + + +setsebool -P httpd_can_sendmail $var_httpd_can_sendmail + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the httpd_dbus_avahi SELinux Boolean + By default, the SELinux boolean httpd_dbus_avahi is disabled. +If this setting is enabled, it should be disabled. + +To disable the httpd_dbus_avahi SELinux boolean, run the following command: +$ sudo setsebool -P httpd_dbus_avahi off + + - name: XCCDF Value var_httpd_dbus_avahi # promote to variable + set_fact: + var_httpd_dbus_avahi: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_dbus_avahi + +- name: Set SELinux boolean httpd_dbus_avahi accordingly + seboolean: + name: httpd_dbus_avahi + state: '{{ var_httpd_dbus_avahi }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_dbus_avahi + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_httpd_dbus_avahi='' + + +setsebool -P httpd_dbus_avahi $var_httpd_dbus_avahi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the httpd_dbus_sssd SELinux Boolean + By default, the SELinux boolean httpd_dbus_sssd is disabled. +If this setting is enabled, it should be disabled. + +To disable the httpd_dbus_sssd SELinux boolean, run the following command: +$ sudo setsebool -P httpd_dbus_sssd off + + - name: XCCDF Value var_httpd_dbus_sssd # promote to variable + set_fact: + var_httpd_dbus_sssd: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_dbus_sssd + +- name: Set SELinux boolean httpd_dbus_sssd accordingly + seboolean: + name: httpd_dbus_sssd + state: '{{ var_httpd_dbus_sssd }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_dbus_sssd + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_httpd_dbus_sssd='' + + +setsebool -P httpd_dbus_sssd $var_httpd_dbus_sssd + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the httpd_dontaudit_search_dirs SELinux Boolean + By default, the SELinux boolean httpd_dontaudit_search_dirs is disabled. +If this setting is enabled, it should be disabled. + +To disable the httpd_dontaudit_search_dirs SELinux boolean, run the following command: +$ sudo setsebool -P httpd_dontaudit_search_dirs off + + - name: XCCDF Value var_httpd_dontaudit_search_dirs # promote to variable + set_fact: + var_httpd_dontaudit_search_dirs: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_dontaudit_search_dirs + +- name: Set SELinux boolean httpd_dontaudit_search_dirs accordingly + seboolean: + name: httpd_dontaudit_search_dirs + state: '{{ var_httpd_dontaudit_search_dirs }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_dontaudit_search_dirs + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_httpd_dontaudit_search_dirs='' + + +setsebool -P httpd_dontaudit_search_dirs $var_httpd_dontaudit_search_dirs + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Configure the httpd_enable_cgi SELinux Boolean + By default, the SELinux boolean httpd_enable_cgi is enabled. +This setting should be disabled unless httpd is used with CGI +scripting. + +To disable the httpd_enable_cgi SELinux boolean, run the following command: +$ sudo setsebool -P httpd_enable_cgi off + + - name: XCCDF Value var_httpd_enable_cgi # promote to variable + set_fact: + var_httpd_enable_cgi: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_enable_cgi + +- name: Set SELinux boolean httpd_enable_cgi accordingly + seboolean: + name: httpd_enable_cgi + state: '{{ var_httpd_enable_cgi }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_enable_cgi + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_httpd_enable_cgi='' + + +setsebool -P httpd_enable_cgi $var_httpd_enable_cgi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the httpd_enable_ftp_server SELinux Boolean + By default, the SELinux boolean httpd_enable_ftp_server is disabled. +If this setting is enabled, it should be disabled. + +To disable the httpd_enable_ftp_server SELinux boolean, run the following command: +$ sudo setsebool -P httpd_enable_ftp_server off + + - name: XCCDF Value var_httpd_enable_ftp_server # promote to variable + set_fact: + var_httpd_enable_ftp_server: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_enable_ftp_server + +- name: Set SELinux boolean httpd_enable_ftp_server accordingly + seboolean: + name: httpd_enable_ftp_server + state: '{{ var_httpd_enable_ftp_server }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_enable_ftp_server + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_httpd_enable_ftp_server='' + + +setsebool -P httpd_enable_ftp_server $var_httpd_enable_ftp_server + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the httpd_enable_homedirs SELinux Boolean + By default, the SELinux boolean httpd_enable_homedirs is disabled. +If this setting is enabled, it should be disabled. + +To disable the httpd_enable_homedirs SELinux boolean, run the following command: +$ sudo setsebool -P httpd_enable_homedirs off + + - name: XCCDF Value var_httpd_enable_homedirs # promote to variable + set_fact: + var_httpd_enable_homedirs: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_enable_homedirs + +- name: Set SELinux boolean httpd_enable_homedirs accordingly + seboolean: + name: httpd_enable_homedirs + state: '{{ var_httpd_enable_homedirs }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_enable_homedirs + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_httpd_enable_homedirs='' + + +setsebool -P httpd_enable_homedirs $var_httpd_enable_homedirs + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the httpd_execmem SELinux Boolean + By default, the SELinux boolean httpd_execmem is disabled. +If this setting is enabled, it should be disabled. + +To disable the httpd_execmem SELinux boolean, run the following command: +$ sudo setsebool -P httpd_execmem off + BP28(R67) + + - name: XCCDF Value var_httpd_execmem # promote to variable + set_fact: + var_httpd_execmem: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_execmem + +- name: Set SELinux boolean httpd_execmem accordingly + seboolean: + name: httpd_execmem + state: '{{ var_httpd_execmem }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_execmem + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_httpd_execmem='' + + +setsebool -P httpd_execmem $var_httpd_execmem + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable the httpd_graceful_shutdown SELinux Boolean + By default, the SELinux boolean httpd_graceful_shutdown is enabled. +If this setting is disabled, it should be enabled. + +To enable the httpd_graceful_shutdown SELinux boolean, run the following command: +$ sudo setsebool -P httpd_graceful_shutdown on + + - name: XCCDF Value var_httpd_graceful_shutdown # promote to variable + set_fact: + var_httpd_graceful_shutdown: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_graceful_shutdown + +- name: Set SELinux boolean httpd_graceful_shutdown accordingly + seboolean: + name: httpd_graceful_shutdown + state: '{{ var_httpd_graceful_shutdown }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_graceful_shutdown + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_httpd_graceful_shutdown='' + + +setsebool -P httpd_graceful_shutdown $var_httpd_graceful_shutdown + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the httpd_manage_ipa SELinux Boolean + By default, the SELinux boolean httpd_manage_ipa is disabled. +If this setting is enabled, it should be disabled. + +To disable the httpd_manage_ipa SELinux boolean, run the following command: +$ sudo setsebool -P httpd_manage_ipa off + + - name: XCCDF Value var_httpd_manage_ipa # promote to variable + set_fact: + var_httpd_manage_ipa: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_manage_ipa + +- name: Set SELinux boolean httpd_manage_ipa accordingly + seboolean: + name: httpd_manage_ipa + state: '{{ var_httpd_manage_ipa }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_manage_ipa + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_httpd_manage_ipa='' + + +setsebool -P httpd_manage_ipa $var_httpd_manage_ipa + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the httpd_mod_auth_ntlm_winbind SELinux Boolean + By default, the SELinux boolean httpd_mod_auth_ntlm_winbind is disabled. +If this setting is enabled, it should be disabled. + +To disable the httpd_mod_auth_ntlm_winbind SELinux boolean, run the following command: +$ sudo setsebool -P httpd_mod_auth_ntlm_winbind off + + - name: XCCDF Value var_httpd_mod_auth_ntlm_winbind # promote to variable + set_fact: + var_httpd_mod_auth_ntlm_winbind: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_mod_auth_ntlm_winbind + +- name: Set SELinux boolean httpd_mod_auth_ntlm_winbind accordingly + seboolean: + name: httpd_mod_auth_ntlm_winbind + state: '{{ var_httpd_mod_auth_ntlm_winbind }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_mod_auth_ntlm_winbind + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_httpd_mod_auth_ntlm_winbind='' + + +setsebool -P httpd_mod_auth_ntlm_winbind $var_httpd_mod_auth_ntlm_winbind + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the httpd_mod_auth_pam SELinux Boolean + By default, the SELinux boolean httpd_mod_auth_pam is disabled. +If this setting is enabled, it should be disabled. + +To disable the httpd_mod_auth_pam SELinux boolean, run the following command: +$ sudo setsebool -P httpd_mod_auth_pam off + + - name: XCCDF Value var_httpd_mod_auth_pam # promote to variable + set_fact: + var_httpd_mod_auth_pam: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_mod_auth_pam + +- name: Set SELinux boolean httpd_mod_auth_pam accordingly + seboolean: + name: httpd_mod_auth_pam + state: '{{ var_httpd_mod_auth_pam }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_mod_auth_pam + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_httpd_mod_auth_pam='' + + +setsebool -P httpd_mod_auth_pam $var_httpd_mod_auth_pam + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the httpd_read_user_content SELinux Boolean + By default, the SELinux boolean httpd_read_user_content is disabled. +If this setting is enabled, it should be disabled. + +To disable the httpd_read_user_content SELinux boolean, run the following command: +$ sudo setsebool -P httpd_read_user_content off + + - name: XCCDF Value var_httpd_read_user_content # promote to variable + set_fact: + var_httpd_read_user_content: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_read_user_content + +- name: Set SELinux boolean httpd_read_user_content accordingly + seboolean: + name: httpd_read_user_content + state: '{{ var_httpd_read_user_content }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_read_user_content + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_httpd_read_user_content='' + + +setsebool -P httpd_read_user_content $var_httpd_read_user_content + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the httpd_run_ipa SELinux Boolean + By default, the SELinux boolean httpd_run_ipa is disabled. +If this setting is enabled, it should be disabled. + +To disable the httpd_run_ipa SELinux boolean, run the following command: +$ sudo setsebool -P httpd_run_ipa off + + - name: XCCDF Value var_httpd_run_ipa # promote to variable + set_fact: + var_httpd_run_ipa: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_run_ipa + +- name: Set SELinux boolean httpd_run_ipa accordingly + seboolean: + name: httpd_run_ipa + state: '{{ var_httpd_run_ipa }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_run_ipa + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_httpd_run_ipa='' + + +setsebool -P httpd_run_ipa $var_httpd_run_ipa + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the httpd_run_preupgrade SELinux Boolean + By default, the SELinux boolean httpd_run_preupgrade is disabled. +If this setting is enabled, it should be disabled. + +To disable the httpd_run_preupgrade SELinux boolean, run the following command: +$ sudo setsebool -P httpd_run_preupgrade off + + - name: XCCDF Value var_httpd_run_preupgrade # promote to variable + set_fact: + var_httpd_run_preupgrade: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_run_preupgrade + +- name: Set SELinux boolean httpd_run_preupgrade accordingly + seboolean: + name: httpd_run_preupgrade + state: '{{ var_httpd_run_preupgrade }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_run_preupgrade + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_httpd_run_preupgrade='' + + +setsebool -P httpd_run_preupgrade $var_httpd_run_preupgrade + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the httpd_run_stickshift SELinux Boolean + By default, the SELinux boolean httpd_run_stickshift is disabled. +If this setting is enabled, it should be disabled. + +To disable the httpd_run_stickshift SELinux boolean, run the following command: +$ sudo setsebool -P httpd_run_stickshift off + + - name: XCCDF Value var_httpd_run_stickshift # promote to variable + set_fact: + var_httpd_run_stickshift: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_run_stickshift + +- name: Set SELinux boolean httpd_run_stickshift accordingly + seboolean: + name: httpd_run_stickshift + state: '{{ var_httpd_run_stickshift }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_run_stickshift + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_httpd_run_stickshift='' + + +setsebool -P httpd_run_stickshift $var_httpd_run_stickshift + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the httpd_serve_cobbler_files SELinux Boolean + By default, the SELinux boolean httpd_serve_cobbler_files is disabled. +If this setting is enabled, it should be disabled. + +To disable the httpd_serve_cobbler_files SELinux boolean, run the following command: +$ sudo setsebool -P httpd_serve_cobbler_files off + + - name: XCCDF Value var_httpd_serve_cobbler_files # promote to variable + set_fact: + var_httpd_serve_cobbler_files: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_serve_cobbler_files + +- name: Set SELinux boolean httpd_serve_cobbler_files accordingly + seboolean: + name: httpd_serve_cobbler_files + state: '{{ var_httpd_serve_cobbler_files }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_serve_cobbler_files + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_httpd_serve_cobbler_files='' + + +setsebool -P httpd_serve_cobbler_files $var_httpd_serve_cobbler_files + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the httpd_setrlimit SELinux Boolean + By default, the SELinux boolean httpd_setrlimit is disabled. +If this setting is enabled, it should be disabled. + +To disable the httpd_setrlimit SELinux boolean, run the following command: +$ sudo setsebool -P httpd_setrlimit off + + - name: XCCDF Value var_httpd_setrlimit # promote to variable + set_fact: + var_httpd_setrlimit: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_setrlimit + +- name: Set SELinux boolean httpd_setrlimit accordingly + seboolean: + name: httpd_setrlimit + state: '{{ var_httpd_setrlimit }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_setrlimit + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_httpd_setrlimit='' + + +setsebool -P httpd_setrlimit $var_httpd_setrlimit + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the httpd_ssi_exec SELinux Boolean + By default, the SELinux boolean httpd_ssi_exec is disabled. +If this setting is enabled, it should be disabled. + +To disable the httpd_ssi_exec SELinux boolean, run the following command: +$ sudo setsebool -P httpd_ssi_exec off + + - name: XCCDF Value var_httpd_ssi_exec # promote to variable + set_fact: + var_httpd_ssi_exec: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_ssi_exec + +- name: Set SELinux boolean httpd_ssi_exec accordingly + seboolean: + name: httpd_ssi_exec + state: '{{ var_httpd_ssi_exec }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_ssi_exec + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_httpd_ssi_exec='' + + +setsebool -P httpd_ssi_exec $var_httpd_ssi_exec + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the httpd_sys_script_anon_write SELinux Boolean + By default, the SELinux boolean httpd_sys_script_anon_write is disabled. +If this setting is enabled, it should be disabled. + +To disable the httpd_sys_script_anon_write SELinux boolean, run the following command: +$ sudo setsebool -P httpd_sys_script_anon_write off + + - name: XCCDF Value var_httpd_sys_script_anon_write # promote to variable + set_fact: + var_httpd_sys_script_anon_write: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_sys_script_anon_write + +- name: Set SELinux boolean httpd_sys_script_anon_write accordingly + seboolean: + name: httpd_sys_script_anon_write + state: '{{ var_httpd_sys_script_anon_write }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_sys_script_anon_write + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_httpd_sys_script_anon_write='' + + +setsebool -P httpd_sys_script_anon_write $var_httpd_sys_script_anon_write + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the httpd_tmp_exec SELinux Boolean + By default, the SELinux boolean httpd_tmp_exec is disabled. +If this setting is enabled, it should be disabled. + +To disable the httpd_tmp_exec SELinux boolean, run the following command: +$ sudo setsebool -P httpd_tmp_exec off + + - name: XCCDF Value var_httpd_tmp_exec # promote to variable + set_fact: + var_httpd_tmp_exec: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_tmp_exec + +- name: Set SELinux boolean httpd_tmp_exec accordingly + seboolean: + name: httpd_tmp_exec + state: '{{ var_httpd_tmp_exec }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_tmp_exec + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_httpd_tmp_exec='' + + +setsebool -P httpd_tmp_exec $var_httpd_tmp_exec + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the httpd_tty_comm SELinux Boolean + By default, the SELinux boolean httpd_tty_comm is disabled. +If this setting is enabled, it should be disabled. + +To disable the httpd_tty_comm SELinux boolean, run the following command: +$ sudo setsebool -P httpd_tty_comm off + + - name: XCCDF Value var_httpd_tty_comm # promote to variable + set_fact: + var_httpd_tty_comm: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_tty_comm + +- name: Set SELinux boolean httpd_tty_comm accordingly + seboolean: + name: httpd_tty_comm + state: '{{ var_httpd_tty_comm }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_tty_comm + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_httpd_tty_comm='' + + +setsebool -P httpd_tty_comm $var_httpd_tty_comm + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the httpd_unified SELinux Boolean + By default, the SELinux boolean httpd_unified is disabled. +If this setting is enabled, it should be disabled. + +To disable the httpd_unified SELinux boolean, run the following command: +$ sudo setsebool -P httpd_unified off + + - name: XCCDF Value var_httpd_unified # promote to variable + set_fact: + var_httpd_unified: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_unified + +- name: Set SELinux boolean httpd_unified accordingly + seboolean: + name: httpd_unified + state: '{{ var_httpd_unified }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_unified + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_httpd_unified='' + + +setsebool -P httpd_unified $var_httpd_unified + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the httpd_use_cifs SELinux Boolean + By default, the SELinux boolean httpd_use_cifs is disabled. +If this setting is enabled, it should be disabled. + +To disable the httpd_use_cifs SELinux boolean, run the following command: +$ sudo setsebool -P httpd_use_cifs off + + - name: XCCDF Value var_httpd_use_cifs # promote to variable + set_fact: + var_httpd_use_cifs: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_use_cifs + +- name: Set SELinux boolean httpd_use_cifs accordingly + seboolean: + name: httpd_use_cifs + state: '{{ var_httpd_use_cifs }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_use_cifs + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_httpd_use_cifs='' + + +setsebool -P httpd_use_cifs $var_httpd_use_cifs + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the httpd_use_fusefs SELinux Boolean + By default, the SELinux boolean httpd_use_fusefs is disabled. +If this setting is enabled, it should be disabled. + +To disable the httpd_use_fusefs SELinux boolean, run the following command: +$ sudo setsebool -P httpd_use_fusefs off + + - name: XCCDF Value var_httpd_use_fusefs # promote to variable + set_fact: + var_httpd_use_fusefs: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_use_fusefs + +- name: Set SELinux boolean httpd_use_fusefs accordingly + seboolean: + name: httpd_use_fusefs + state: '{{ var_httpd_use_fusefs }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_use_fusefs + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_httpd_use_fusefs='' + + +setsebool -P httpd_use_fusefs $var_httpd_use_fusefs + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the httpd_use_gpg SELinux Boolean + By default, the SELinux boolean httpd_use_gpg is disabled. +If this setting is enabled, it should be disabled. + +To disable the httpd_use_gpg SELinux boolean, run the following command: +$ sudo setsebool -P httpd_use_gpg off + + - name: XCCDF Value var_httpd_use_gpg # promote to variable + set_fact: + var_httpd_use_gpg: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_use_gpg + +- name: Set SELinux boolean httpd_use_gpg accordingly + seboolean: + name: httpd_use_gpg + state: '{{ var_httpd_use_gpg }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_use_gpg + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_httpd_use_gpg='' + + +setsebool -P httpd_use_gpg $var_httpd_use_gpg + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the httpd_use_nfs SELinux Boolean + By default, the SELinux boolean httpd_use_nfs is disabled. +If this setting is enabled, it should be disabled. + +To disable the httpd_use_nfs SELinux boolean, run the following command: +$ sudo setsebool -P httpd_use_nfs off + + - name: XCCDF Value var_httpd_use_nfs # promote to variable + set_fact: + var_httpd_use_nfs: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_use_nfs + +- name: Set SELinux boolean httpd_use_nfs accordingly + seboolean: + name: httpd_use_nfs + state: '{{ var_httpd_use_nfs }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_use_nfs + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_httpd_use_nfs='' + + +setsebool -P httpd_use_nfs $var_httpd_use_nfs + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the httpd_use_openstack SELinux Boolean + By default, the SELinux boolean httpd_use_openstack is disabled. +If this setting is enabled, it should be disabled. + +To disable the httpd_use_openstack SELinux boolean, run the following command: +$ sudo setsebool -P httpd_use_openstack off + + - name: XCCDF Value var_httpd_use_openstack # promote to variable + set_fact: + var_httpd_use_openstack: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_use_openstack + +- name: Set SELinux boolean httpd_use_openstack accordingly + seboolean: + name: httpd_use_openstack + state: '{{ var_httpd_use_openstack }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_use_openstack + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_httpd_use_openstack='' + + +setsebool -P httpd_use_openstack $var_httpd_use_openstack + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the httpd_use_sasl SELinux Boolean + By default, the SELinux boolean httpd_use_sasl is disabled. +If this setting is enabled, it should be disabled. + +To disable the httpd_use_sasl SELinux boolean, run the following command: +$ sudo setsebool -P httpd_use_sasl off + + - name: XCCDF Value var_httpd_use_sasl # promote to variable + set_fact: + var_httpd_use_sasl: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_use_sasl + +- name: Set SELinux boolean httpd_use_sasl accordingly + seboolean: + name: httpd_use_sasl + state: '{{ var_httpd_use_sasl }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_use_sasl + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_httpd_use_sasl='' + + +setsebool -P httpd_use_sasl $var_httpd_use_sasl + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the httpd_verify_dns SELinux Boolean + By default, the SELinux boolean httpd_verify_dns is disabled. +If this setting is enabled, it should be disabled. + +To disable the httpd_verify_dns SELinux boolean, run the following command: +$ sudo setsebool -P httpd_verify_dns off + + - name: XCCDF Value var_httpd_verify_dns # promote to variable + set_fact: + var_httpd_verify_dns: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_verify_dns + +- name: Set SELinux boolean httpd_verify_dns accordingly + seboolean: + name: httpd_verify_dns + state: '{{ var_httpd_verify_dns }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_httpd_verify_dns + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_httpd_verify_dns='' + + +setsebool -P httpd_verify_dns $var_httpd_verify_dns + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the icecast_use_any_tcp_ports SELinux Boolean + By default, the SELinux boolean icecast_use_any_tcp_ports is disabled. +If this setting is enabled, it should be disabled. + +To disable the icecast_use_any_tcp_ports SELinux boolean, run the following command: +$ sudo setsebool -P icecast_use_any_tcp_ports off + + - name: XCCDF Value var_icecast_use_any_tcp_ports # promote to variable + set_fact: + var_icecast_use_any_tcp_ports: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_icecast_use_any_tcp_ports + +- name: Set SELinux boolean icecast_use_any_tcp_ports accordingly + seboolean: + name: icecast_use_any_tcp_ports + state: '{{ var_icecast_use_any_tcp_ports }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_icecast_use_any_tcp_ports + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_icecast_use_any_tcp_ports='' + + +setsebool -P icecast_use_any_tcp_ports $var_icecast_use_any_tcp_ports + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the irc_use_any_tcp_ports SELinux Boolean + By default, the SELinux boolean irc_use_any_tcp_ports is disabled. +If this setting is enabled, it should be disabled. + +To disable the irc_use_any_tcp_ports SELinux boolean, run the following command: +$ sudo setsebool -P irc_use_any_tcp_ports off + + - name: XCCDF Value var_irc_use_any_tcp_ports # promote to variable + set_fact: + var_irc_use_any_tcp_ports: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_irc_use_any_tcp_ports + +- name: Set SELinux boolean irc_use_any_tcp_ports accordingly + seboolean: + name: irc_use_any_tcp_ports + state: '{{ var_irc_use_any_tcp_ports }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_irc_use_any_tcp_ports + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_irc_use_any_tcp_ports='' + + +setsebool -P irc_use_any_tcp_ports $var_irc_use_any_tcp_ports + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the irssi_use_full_network SELinux Boolean + By default, the SELinux boolean irssi_use_full_network is disabled. +If this setting is enabled, it should be disabled. + +To disable the irssi_use_full_network SELinux boolean, run the following command: +$ sudo setsebool -P irssi_use_full_network off + + - name: XCCDF Value var_irssi_use_full_network # promote to variable + set_fact: + var_irssi_use_full_network: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_irssi_use_full_network + +- name: Set SELinux boolean irssi_use_full_network accordingly + seboolean: + name: irssi_use_full_network + state: '{{ var_irssi_use_full_network }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_irssi_use_full_network + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_irssi_use_full_network='' + + +setsebool -P irssi_use_full_network $var_irssi_use_full_network + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the kdumpgui_run_bootloader SELinux Boolean + By default, the SELinux boolean kdumpgui_run_bootloader is disabled. +If this setting is enabled, it should be disabled. + +To disable the kdumpgui_run_bootloader SELinux boolean, run the following command: +$ sudo setsebool -P kdumpgui_run_bootloader off + + - name: XCCDF Value var_kdumpgui_run_bootloader # promote to variable + set_fact: + var_kdumpgui_run_bootloader: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_kdumpgui_run_bootloader + +- name: Set SELinux boolean kdumpgui_run_bootloader accordingly + seboolean: + name: kdumpgui_run_bootloader + state: '{{ var_kdumpgui_run_bootloader }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_kdumpgui_run_bootloader + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_kdumpgui_run_bootloader='' + + +setsebool -P kdumpgui_run_bootloader $var_kdumpgui_run_bootloader + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable the kerberos_enabled SELinux Boolean + By default, the SELinux boolean kerberos_enabled is enabled. +If this setting is disabled, it should be enabled to allow confined +applications to run with Kerberos. + +To enable the kerberos_enabled SELinux boolean, run the following command: +$ sudo setsebool -P kerberos_enabled on + 0418 + 1055 + 1402 + + - name: XCCDF Value var_kerberos_enabled # promote to variable + set_fact: + var_kerberos_enabled: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_kerberos_enabled + +- name: Set SELinux boolean kerberos_enabled accordingly + seboolean: + name: kerberos_enabled + state: '{{ var_kerberos_enabled }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_kerberos_enabled + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_kerberos_enabled='' + + +setsebool -P kerberos_enabled $var_kerberos_enabled + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the ksmtuned_use_cifs SELinux Boolean + By default, the SELinux boolean ksmtuned_use_cifs is disabled. +If this setting is enabled, it should be disabled. + +To disable the ksmtuned_use_cifs SELinux boolean, run the following command: +$ sudo setsebool -P ksmtuned_use_cifs off + + - name: XCCDF Value var_ksmtuned_use_cifs # promote to variable + set_fact: + var_ksmtuned_use_cifs: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_ksmtuned_use_cifs + +- name: Set SELinux boolean ksmtuned_use_cifs accordingly + seboolean: + name: ksmtuned_use_cifs + state: '{{ var_ksmtuned_use_cifs }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_ksmtuned_use_cifs + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_ksmtuned_use_cifs='' + + +setsebool -P ksmtuned_use_cifs $var_ksmtuned_use_cifs + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the ksmtuned_use_nfs SELinux Boolean + By default, the SELinux boolean ksmtuned_use_nfs is disabled. +If this setting is enabled, it should be disabled. + +To disable the ksmtuned_use_nfs SELinux boolean, run the following command: +$ sudo setsebool -P ksmtuned_use_nfs off + + - name: XCCDF Value var_ksmtuned_use_nfs # promote to variable + set_fact: + var_ksmtuned_use_nfs: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_ksmtuned_use_nfs + +- name: Set SELinux boolean ksmtuned_use_nfs accordingly + seboolean: + name: ksmtuned_use_nfs + state: '{{ var_ksmtuned_use_nfs }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_ksmtuned_use_nfs + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_ksmtuned_use_nfs='' + + +setsebool -P ksmtuned_use_nfs $var_ksmtuned_use_nfs + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable the logadm_exec_content SELinux Boolean + By default, the SELinux boolean logadm_exec_content is enabled. +If this setting is disabled, it should be enabled. + +To enable the logadm_exec_content SELinux boolean, run the following command: +$ sudo setsebool -P logadm_exec_content on + + - name: XCCDF Value var_logadm_exec_content # promote to variable + set_fact: + var_logadm_exec_content: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_logadm_exec_content + +- name: Set SELinux boolean logadm_exec_content accordingly + seboolean: + name: logadm_exec_content + state: '{{ var_logadm_exec_content }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_logadm_exec_content + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_logadm_exec_content='' + + +setsebool -P logadm_exec_content $var_logadm_exec_content + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the logging_syslogd_can_sendmail SELinux Boolean + By default, the SELinux boolean logging_syslogd_can_sendmail is disabled. +If this setting is enabled, it should be disabled. + +To disable the logging_syslogd_can_sendmail SELinux boolean, run the following command: +$ sudo setsebool -P logging_syslogd_can_sendmail off + + - name: XCCDF Value var_logging_syslogd_can_sendmail # promote to variable + set_fact: + var_logging_syslogd_can_sendmail: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_logging_syslogd_can_sendmail + +- name: Set SELinux boolean logging_syslogd_can_sendmail accordingly + seboolean: + name: logging_syslogd_can_sendmail + state: '{{ var_logging_syslogd_can_sendmail }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_logging_syslogd_can_sendmail + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_logging_syslogd_can_sendmail='' + + +setsebool -P logging_syslogd_can_sendmail $var_logging_syslogd_can_sendmail + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the logging_syslogd_run_nagios_plugins SELinux Boolean + By default, the SELinux boolean logging_syslogd_run_nagios_plugins is disabled. +If this setting is enabled, it should be disabled. + +To disable the logging_syslogd_run_nagios_plugins SELinux boolean, run the following command: +$ sudo setsebool -P logging_syslogd_run_nagios_plugins off + + - name: XCCDF Value var_logging_syslogd_run_nagios_plugins # promote to variable + set_fact: + var_logging_syslogd_run_nagios_plugins: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_logging_syslogd_run_nagios_plugins + +- name: Set SELinux boolean logging_syslogd_run_nagios_plugins accordingly + seboolean: + name: logging_syslogd_run_nagios_plugins + state: '{{ var_logging_syslogd_run_nagios_plugins }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_logging_syslogd_run_nagios_plugins + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_logging_syslogd_run_nagios_plugins='' + + +setsebool -P logging_syslogd_run_nagios_plugins $var_logging_syslogd_run_nagios_plugins + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable the logging_syslogd_use_tty SELinux Boolean + By default, the SELinux boolean logging_syslogd_use_tty is enabled. +If this setting is disabled, it should be enabled as it allows syslog +the ability to read/write to terminal. + +To enable the logging_syslogd_use_tty SELinux boolean, run the following command: +$ sudo setsebool -P logging_syslogd_use_tty on + + - name: XCCDF Value var_logging_syslogd_use_tty # promote to variable + set_fact: + var_logging_syslogd_use_tty: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_logging_syslogd_use_tty + +- name: Set SELinux boolean logging_syslogd_use_tty accordingly + seboolean: + name: logging_syslogd_use_tty + state: '{{ var_logging_syslogd_use_tty }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_logging_syslogd_use_tty + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_logging_syslogd_use_tty='' + + +setsebool -P logging_syslogd_use_tty $var_logging_syslogd_use_tty + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable the login_console_enabled SELinux Boolean + By default, the SELinux boolean login_console_enabled is enabled. +If this setting is disabled, it should be enabled as it allows login from +/dev/console to a console session. + +To enable the login_console_enabled SELinux boolean, run the following command: +$ sudo setsebool -P login_console_enabled on + + - name: XCCDF Value var_login_console_enabled # promote to variable + set_fact: + var_login_console_enabled: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_login_console_enabled + +- name: Set SELinux boolean login_console_enabled accordingly + seboolean: + name: login_console_enabled + state: '{{ var_login_console_enabled }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_login_console_enabled + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_login_console_enabled='' + + +setsebool -P login_console_enabled $var_login_console_enabled + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the logrotate_use_nfs SELinux Boolean + By default, the SELinux boolean logrotate_use_nfs is disabled. +If this setting is enabled, it should be disabled. + +To disable the logrotate_use_nfs SELinux boolean, run the following command: +$ sudo setsebool -P logrotate_use_nfs off + + - name: XCCDF Value var_logrotate_use_nfs # promote to variable + set_fact: + var_logrotate_use_nfs: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_logrotate_use_nfs + +- name: Set SELinux boolean logrotate_use_nfs accordingly + seboolean: + name: logrotate_use_nfs + state: '{{ var_logrotate_use_nfs }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_logrotate_use_nfs + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_logrotate_use_nfs='' + + +setsebool -P logrotate_use_nfs $var_logrotate_use_nfs + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the logwatch_can_network_connect_mail SELinux Boolean + By default, the SELinux boolean logwatch_can_network_connect_mail is disabled. +If this setting is enabled, it should be disabled. + +To disable the logwatch_can_network_connect_mail SELinux boolean, run the following command: +$ sudo setsebool -P logwatch_can_network_connect_mail off + + - name: XCCDF Value var_logwatch_can_network_connect_mail # promote to variable + set_fact: + var_logwatch_can_network_connect_mail: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_logwatch_can_network_connect_mail + +- name: Set SELinux boolean logwatch_can_network_connect_mail accordingly + seboolean: + name: logwatch_can_network_connect_mail + state: '{{ var_logwatch_can_network_connect_mail }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_logwatch_can_network_connect_mail + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_logwatch_can_network_connect_mail='' + + +setsebool -P logwatch_can_network_connect_mail $var_logwatch_can_network_connect_mail + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the lsmd_plugin_connect_any SELinux Boolean + By default, the SELinux boolean lsmd_plugin_connect_any is disabled. +If this setting is enabled, it should be disabled. + +To disable the lsmd_plugin_connect_any SELinux boolean, run the following command: +$ sudo setsebool -P lsmd_plugin_connect_any off + + - name: XCCDF Value var_lsmd_plugin_connect_any # promote to variable + set_fact: + var_lsmd_plugin_connect_any: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_lsmd_plugin_connect_any + +- name: Set SELinux boolean lsmd_plugin_connect_any accordingly + seboolean: + name: lsmd_plugin_connect_any + state: '{{ var_lsmd_plugin_connect_any }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_lsmd_plugin_connect_any + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_lsmd_plugin_connect_any='' + + +setsebool -P lsmd_plugin_connect_any $var_lsmd_plugin_connect_any + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the mailman_use_fusefs SELinux Boolean + By default, the SELinux boolean mailman_use_fusefs is disabled. +If this setting is enabled, it should be disabled. + +To disable the mailman_use_fusefs SELinux boolean, run the following command: +$ sudo setsebool -P mailman_use_fusefs off + + - name: XCCDF Value var_mailman_use_fusefs # promote to variable + set_fact: + var_mailman_use_fusefs: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_mailman_use_fusefs + +- name: Set SELinux boolean mailman_use_fusefs accordingly + seboolean: + name: mailman_use_fusefs + state: '{{ var_mailman_use_fusefs }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_mailman_use_fusefs + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_mailman_use_fusefs='' + + +setsebool -P mailman_use_fusefs $var_mailman_use_fusefs + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the mcelog_client SELinux Boolean + By default, the SELinux boolean mcelog_client is disabled. +If this setting is enabled, it should be disabled. + +To disable the mcelog_client SELinux boolean, run the following command: +$ sudo setsebool -P mcelog_client off + + - name: XCCDF Value var_mcelog_client # promote to variable + set_fact: + var_mcelog_client: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_mcelog_client + +- name: Set SELinux boolean mcelog_client accordingly + seboolean: + name: mcelog_client + state: '{{ var_mcelog_client }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_mcelog_client + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_mcelog_client='' + + +setsebool -P mcelog_client $var_mcelog_client + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable the mcelog_exec_scripts SELinux Boolean + By default, the SELinux boolean mcelog_exec_scripts is enabled. +If this setting is disabled, it should be enabled. + +To enable the mcelog_exec_scripts SELinux boolean, run the following command: +$ sudo setsebool -P mcelog_exec_scripts on + + - name: XCCDF Value var_mcelog_exec_scripts # promote to variable + set_fact: + var_mcelog_exec_scripts: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_mcelog_exec_scripts + +- name: Set SELinux boolean mcelog_exec_scripts accordingly + seboolean: + name: mcelog_exec_scripts + state: '{{ var_mcelog_exec_scripts }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_mcelog_exec_scripts + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_mcelog_exec_scripts='' + + +setsebool -P mcelog_exec_scripts $var_mcelog_exec_scripts + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the mcelog_foreground SELinux Boolean + By default, the SELinux boolean mcelog_foreground is disabled. +If this setting is enabled, it should be disabled. + +To disable the mcelog_foreground SELinux boolean, run the following command: +$ sudo setsebool -P mcelog_foreground off + + - name: XCCDF Value var_mcelog_foreground # promote to variable + set_fact: + var_mcelog_foreground: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_mcelog_foreground + +- name: Set SELinux boolean mcelog_foreground accordingly + seboolean: + name: mcelog_foreground + state: '{{ var_mcelog_foreground }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_mcelog_foreground + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_mcelog_foreground='' + + +setsebool -P mcelog_foreground $var_mcelog_foreground + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the mcelog_server SELinux Boolean + By default, the SELinux boolean mcelog_server is disabled. +If this setting is enabled, it should be disabled. + +To disable the mcelog_server SELinux boolean, run the following command: +$ sudo setsebool -P mcelog_server off + + - name: XCCDF Value var_mcelog_server # promote to variable + set_fact: + var_mcelog_server: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_mcelog_server + +- name: Set SELinux boolean mcelog_server accordingly + seboolean: + name: mcelog_server + state: '{{ var_mcelog_server }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_mcelog_server + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_mcelog_server='' + + +setsebool -P mcelog_server $var_mcelog_server + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the minidlna_read_generic_user_content SELinux Boolean + By default, the SELinux boolean minidlna_read_generic_user_content is disabled. +If this setting is enabled, it should be disabled. + +To disable the minidlna_read_generic_user_content SELinux boolean, run the following command: +$ sudo setsebool -P minidlna_read_generic_user_content off + + - name: XCCDF Value var_minidlna_read_generic_user_content # promote to variable + set_fact: + var_minidlna_read_generic_user_content: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_minidlna_read_generic_user_content + +- name: Set SELinux boolean minidlna_read_generic_user_content accordingly + seboolean: + name: minidlna_read_generic_user_content + state: '{{ var_minidlna_read_generic_user_content }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_minidlna_read_generic_user_content + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_minidlna_read_generic_user_content='' + + +setsebool -P minidlna_read_generic_user_content $var_minidlna_read_generic_user_content + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the mmap_low_allowed SELinux Boolean + By default, the SELinux boolean mmap_low_allowed is disabled. +If this setting is enabled, it should be disabled. + +To disable the mmap_low_allowed SELinux boolean, run the following command: +$ sudo setsebool -P mmap_low_allowed off + + - name: XCCDF Value var_mmap_low_allowed # promote to variable + set_fact: + var_mmap_low_allowed: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_mmap_low_allowed + +- name: Set SELinux boolean mmap_low_allowed accordingly + seboolean: + name: mmap_low_allowed + state: '{{ var_mmap_low_allowed }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_mmap_low_allowed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_mmap_low_allowed='' + + +setsebool -P mmap_low_allowed $var_mmap_low_allowed + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the mock_enable_homedirs SELinux Boolean + By default, the SELinux boolean mock_enable_homedirs is disabled. +If this setting is enabled, it should be disabled. + +To disable the mock_enable_homedirs SELinux boolean, run the following command: +$ sudo setsebool -P mock_enable_homedirs off + + - name: XCCDF Value var_mock_enable_homedirs # promote to variable + set_fact: + var_mock_enable_homedirs: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_mock_enable_homedirs + +- name: Set SELinux boolean mock_enable_homedirs accordingly + seboolean: + name: mock_enable_homedirs + state: '{{ var_mock_enable_homedirs }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_mock_enable_homedirs + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_mock_enable_homedirs='' + + +setsebool -P mock_enable_homedirs $var_mock_enable_homedirs + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable the mount_anyfile SELinux Boolean + By default, the SELinux boolean mount_anyfile is enabled. +If this setting is disabled, it should be enabled to allow any file +or directory to be mounted. + +To enable the mount_anyfile SELinux boolean, run the following command: +$ sudo setsebool -P mount_anyfile on + + - name: XCCDF Value var_mount_anyfile # promote to variable + set_fact: + var_mount_anyfile: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_mount_anyfile + +- name: Set SELinux boolean mount_anyfile accordingly + seboolean: + name: mount_anyfile + state: '{{ var_mount_anyfile }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_mount_anyfile + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_mount_anyfile='' + + +setsebool -P mount_anyfile $var_mount_anyfile + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the mozilla_plugin_bind_unreserved_ports SELinux Boolean + By default, the SELinux boolean mozilla_plugin_bind_unreserved_ports is disabled. +If this setting is enabled, it should be disabled. + +To disable the mozilla_plugin_bind_unreserved_ports SELinux boolean, run the following command: +$ sudo setsebool -P mozilla_plugin_bind_unreserved_ports off + + - name: XCCDF Value var_mozilla_plugin_bind_unreserved_ports # promote to variable + set_fact: + var_mozilla_plugin_bind_unreserved_ports: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_mozilla_plugin_bind_unreserved_ports + +- name: Set SELinux boolean mozilla_plugin_bind_unreserved_ports accordingly + seboolean: + name: mozilla_plugin_bind_unreserved_ports + state: '{{ var_mozilla_plugin_bind_unreserved_ports }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_mozilla_plugin_bind_unreserved_ports + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_mozilla_plugin_bind_unreserved_ports='' + + +setsebool -P mozilla_plugin_bind_unreserved_ports $var_mozilla_plugin_bind_unreserved_ports + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the mozilla_plugin_can_network_connect SELinux Boolean + By default, the SELinux boolean mozilla_plugin_can_network_connect is disabled. +If this setting is enabled, it should be disabled. + +To disable the mozilla_plugin_can_network_connect SELinux boolean, run the following command: +$ sudo setsebool -P mozilla_plugin_can_network_connect off + + - name: XCCDF Value var_mozilla_plugin_can_network_connect # promote to variable + set_fact: + var_mozilla_plugin_can_network_connect: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_mozilla_plugin_can_network_connect + +- name: Set SELinux boolean mozilla_plugin_can_network_connect accordingly + seboolean: + name: mozilla_plugin_can_network_connect + state: '{{ var_mozilla_plugin_can_network_connect }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_mozilla_plugin_can_network_connect + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_mozilla_plugin_can_network_connect='' + + +setsebool -P mozilla_plugin_can_network_connect $var_mozilla_plugin_can_network_connect + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the mozilla_plugin_use_bluejeans SELinux Boolean + By default, the SELinux boolean mozilla_plugin_use_bluejeans is disabled. +If this setting is enabled, it should be disabled. + +To disable the mozilla_plugin_use_bluejeans SELinux boolean, run the following command: +$ sudo setsebool -P mozilla_plugin_use_bluejeans off + + - name: XCCDF Value var_mozilla_plugin_use_bluejeans # promote to variable + set_fact: + var_mozilla_plugin_use_bluejeans: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_mozilla_plugin_use_bluejeans + +- name: Set SELinux boolean mozilla_plugin_use_bluejeans accordingly + seboolean: + name: mozilla_plugin_use_bluejeans + state: '{{ var_mozilla_plugin_use_bluejeans }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_mozilla_plugin_use_bluejeans + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_mozilla_plugin_use_bluejeans='' + + +setsebool -P mozilla_plugin_use_bluejeans $var_mozilla_plugin_use_bluejeans + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the mozilla_plugin_use_gps SELinux Boolean + By default, the SELinux boolean mozilla_plugin_use_gps is disabled. +If this setting is enabled, it should be disabled. + +To disable the mozilla_plugin_use_gps SELinux boolean, run the following command: +$ sudo setsebool -P mozilla_plugin_use_gps off + + - name: XCCDF Value var_mozilla_plugin_use_gps # promote to variable + set_fact: + var_mozilla_plugin_use_gps: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_mozilla_plugin_use_gps + +- name: Set SELinux boolean mozilla_plugin_use_gps accordingly + seboolean: + name: mozilla_plugin_use_gps + state: '{{ var_mozilla_plugin_use_gps }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_mozilla_plugin_use_gps + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_mozilla_plugin_use_gps='' + + +setsebool -P mozilla_plugin_use_gps $var_mozilla_plugin_use_gps + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the mozilla_plugin_use_spice SELinux Boolean + By default, the SELinux boolean mozilla_plugin_use_spice is disabled. +If this setting is enabled, it should be disabled. + +To disable the mozilla_plugin_use_spice SELinux boolean, run the following command: +$ sudo setsebool -P mozilla_plugin_use_spice off + + - name: XCCDF Value var_mozilla_plugin_use_spice # promote to variable + set_fact: + var_mozilla_plugin_use_spice: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_mozilla_plugin_use_spice + +- name: Set SELinux boolean mozilla_plugin_use_spice accordingly + seboolean: + name: mozilla_plugin_use_spice + state: '{{ var_mozilla_plugin_use_spice }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_mozilla_plugin_use_spice + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_mozilla_plugin_use_spice='' + + +setsebool -P mozilla_plugin_use_spice $var_mozilla_plugin_use_spice + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the mozilla_read_content SELinux Boolean + By default, the SELinux boolean mozilla_read_content is disabled. +If this setting is enabled, it should be disabled. + +To disable the mozilla_read_content SELinux boolean, run the following command: +$ sudo setsebool -P mozilla_read_content off + + - name: XCCDF Value var_mozilla_read_content # promote to variable + set_fact: + var_mozilla_read_content: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_mozilla_read_content + +- name: Set SELinux boolean mozilla_read_content accordingly + seboolean: + name: mozilla_read_content + state: '{{ var_mozilla_read_content }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_mozilla_read_content + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_mozilla_read_content='' + + +setsebool -P mozilla_read_content $var_mozilla_read_content + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the mpd_enable_homedirs SELinux Boolean + By default, the SELinux boolean mpd_enable_homedirs is disabled. +If this setting is enabled, it should be disabled. + +To disable the mpd_enable_homedirs SELinux boolean, run the following command: +$ sudo setsebool -P mpd_enable_homedirs off + + - name: XCCDF Value var_mpd_enable_homedirs # promote to variable + set_fact: + var_mpd_enable_homedirs: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_mpd_enable_homedirs + +- name: Set SELinux boolean mpd_enable_homedirs accordingly + seboolean: + name: mpd_enable_homedirs + state: '{{ var_mpd_enable_homedirs }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_mpd_enable_homedirs + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_mpd_enable_homedirs='' + + +setsebool -P mpd_enable_homedirs $var_mpd_enable_homedirs + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the mpd_use_cifs SELinux Boolean + By default, the SELinux boolean mpd_use_cifs is disabled. +If this setting is enabled, it should be disabled. + +To disable the mpd_use_cifs SELinux boolean, run the following command: +$ sudo setsebool -P mpd_use_cifs off + + - name: XCCDF Value var_mpd_use_cifs # promote to variable + set_fact: + var_mpd_use_cifs: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_mpd_use_cifs + +- name: Set SELinux boolean mpd_use_cifs accordingly + seboolean: + name: mpd_use_cifs + state: '{{ var_mpd_use_cifs }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_mpd_use_cifs + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_mpd_use_cifs='' + + +setsebool -P mpd_use_cifs $var_mpd_use_cifs + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the mpd_use_nfs SELinux Boolean + By default, the SELinux boolean mpd_use_nfs is disabled. +If this setting is enabled, it should be disabled. + +To disable the mpd_use_nfs SELinux boolean, run the following command: +$ sudo setsebool -P mpd_use_nfs off + + - name: XCCDF Value var_mpd_use_nfs # promote to variable + set_fact: + var_mpd_use_nfs: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_mpd_use_nfs + +- name: Set SELinux boolean mpd_use_nfs accordingly + seboolean: + name: mpd_use_nfs + state: '{{ var_mpd_use_nfs }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_mpd_use_nfs + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_mpd_use_nfs='' + + +setsebool -P mpd_use_nfs $var_mpd_use_nfs + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the mplayer_execstack SELinux Boolean + By default, the SELinux boolean mplayer_execstack is disabled. +If this setting is enabled, it should be disabled. + +To disable the mplayer_execstack SELinux boolean, run the following command: +$ sudo setsebool -P mplayer_execstack off + + - name: XCCDF Value var_mplayer_execstack # promote to variable + set_fact: + var_mplayer_execstack: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_mplayer_execstack + +- name: Set SELinux boolean mplayer_execstack accordingly + seboolean: + name: mplayer_execstack + state: '{{ var_mplayer_execstack }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_mplayer_execstack + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_mplayer_execstack='' + + +setsebool -P mplayer_execstack $var_mplayer_execstack + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the mysql_connect_any SELinux Boolean + By default, the SELinux boolean mysql_connect_any is disabled. +If this setting is enabled, it should be disabled. + +To disable the mysql_connect_any SELinux boolean, run the following command: +$ sudo setsebool -P mysql_connect_any off + + - name: XCCDF Value var_mysql_connect_any # promote to variable + set_fact: + var_mysql_connect_any: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_mysql_connect_any + +- name: Set SELinux boolean mysql_connect_any accordingly + seboolean: + name: mysql_connect_any + state: '{{ var_mysql_connect_any }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_mysql_connect_any + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_mysql_connect_any='' + + +setsebool -P mysql_connect_any $var_mysql_connect_any + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the nagios_run_pnp4nagios SELinux Boolean + By default, the SELinux boolean nagios_run_pnp4nagios is disabled. +If this setting is enabled, it should be disabled. + +To disable the nagios_run_pnp4nagios SELinux boolean, run the following command: +$ sudo setsebool -P nagios_run_pnp4nagios off + + - name: XCCDF Value var_nagios_run_pnp4nagios # promote to variable + set_fact: + var_nagios_run_pnp4nagios: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_nagios_run_pnp4nagios + +- name: Set SELinux boolean nagios_run_pnp4nagios accordingly + seboolean: + name: nagios_run_pnp4nagios + state: '{{ var_nagios_run_pnp4nagios }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_nagios_run_pnp4nagios + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_nagios_run_pnp4nagios='' + + +setsebool -P nagios_run_pnp4nagios $var_nagios_run_pnp4nagios + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the nagios_run_sudo SELinux Boolean + By default, the SELinux boolean nagios_run_sudo is disabled. +If this setting is enabled, it should be disabled. + +To disable the nagios_run_sudo SELinux boolean, run the following command: +$ sudo setsebool -P nagios_run_sudo off + + - name: XCCDF Value var_nagios_run_sudo # promote to variable + set_fact: + var_nagios_run_sudo: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_nagios_run_sudo + +- name: Set SELinux boolean nagios_run_sudo accordingly + seboolean: + name: nagios_run_sudo + state: '{{ var_nagios_run_sudo }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_nagios_run_sudo + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_nagios_run_sudo='' + + +setsebool -P nagios_run_sudo $var_nagios_run_sudo + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the named_tcp_bind_http_port SELinux Boolean + By default, the SELinux boolean named_tcp_bind_http_port is disabled. +If this setting is enabled, it should be disabled. + +To disable the named_tcp_bind_http_port SELinux boolean, run the following command: +$ sudo setsebool -P named_tcp_bind_http_port off + + - name: XCCDF Value var_named_tcp_bind_http_port # promote to variable + set_fact: + var_named_tcp_bind_http_port: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_named_tcp_bind_http_port + +- name: Set SELinux boolean named_tcp_bind_http_port accordingly + seboolean: + name: named_tcp_bind_http_port + state: '{{ var_named_tcp_bind_http_port }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_named_tcp_bind_http_port + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_named_tcp_bind_http_port='' + + +setsebool -P named_tcp_bind_http_port $var_named_tcp_bind_http_port + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the named_write_master_zones SELinux Boolean + By default, the SELinux boolean named_write_master_zones is disabled. +If this setting is enabled, it should be disabled. + +To disable the named_write_master_zones SELinux boolean, run the following command: +$ sudo setsebool -P named_write_master_zones off + + - name: XCCDF Value var_named_write_master_zones # promote to variable + set_fact: + var_named_write_master_zones: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_named_write_master_zones + +- name: Set SELinux boolean named_write_master_zones accordingly + seboolean: + name: named_write_master_zones + state: '{{ var_named_write_master_zones }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_named_write_master_zones + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_named_write_master_zones='' + + +setsebool -P named_write_master_zones $var_named_write_master_zones + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the neutron_can_network SELinux Boolean + By default, the SELinux boolean neutron_can_network is disabled. +If this setting is enabled, it should be disabled. + +To disable the neutron_can_network SELinux boolean, run the following command: +$ sudo setsebool -P neutron_can_network off + + - name: XCCDF Value var_neutron_can_network # promote to variable + set_fact: + var_neutron_can_network: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_neutron_can_network + +- name: Set SELinux boolean neutron_can_network accordingly + seboolean: + name: neutron_can_network + state: '{{ var_neutron_can_network }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_neutron_can_network + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_neutron_can_network='' + + +setsebool -P neutron_can_network $var_neutron_can_network + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable the nfs_export_all_ro SELinux Boolean + By default, the SELinux boolean nfs_export_all_ro is enabled. +If this setting is disabled, it should be enabled as it allows NFS to +export read-only mounts. + +To enable the nfs_export_all_ro SELinux boolean, run the following command: +$ sudo setsebool -P nfs_export_all_ro on + + - name: XCCDF Value var_nfs_export_all_ro # promote to variable + set_fact: + var_nfs_export_all_ro: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_nfs_export_all_ro + +- name: Set SELinux boolean nfs_export_all_ro accordingly + seboolean: + name: nfs_export_all_ro + state: '{{ var_nfs_export_all_ro }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_nfs_export_all_ro + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_nfs_export_all_ro='' + + +setsebool -P nfs_export_all_ro $var_nfs_export_all_ro + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable the nfs_export_all_rw SELinux Boolean + By default, the SELinux boolean nfs_export_all_rw is enabled. +If this setting is disabled, it should be enabled as it allows NFS to +export read/write mounts. + +To enable the nfs_export_all_rw SELinux boolean, run the following command: +$ sudo setsebool -P nfs_export_all_rw on + + - name: XCCDF Value var_nfs_export_all_rw # promote to variable + set_fact: + var_nfs_export_all_rw: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_nfs_export_all_rw + +- name: Set SELinux boolean nfs_export_all_rw accordingly + seboolean: + name: nfs_export_all_rw + state: '{{ var_nfs_export_all_rw }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_nfs_export_all_rw + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_nfs_export_all_rw='' + + +setsebool -P nfs_export_all_rw $var_nfs_export_all_rw + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the nfsd_anon_write SELinux Boolean + By default, the SELinux boolean nfsd_anon_write is disabled. +If this setting is enabled, it should be disabled. + +To disable the nfsd_anon_write SELinux boolean, run the following command: +$ sudo setsebool -P nfsd_anon_write off + + - name: XCCDF Value var_nfsd_anon_write # promote to variable + set_fact: + var_nfsd_anon_write: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_nfsd_anon_write + +- name: Set SELinux boolean nfsd_anon_write accordingly + seboolean: + name: nfsd_anon_write + state: '{{ var_nfsd_anon_write }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_nfsd_anon_write + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_nfsd_anon_write='' + + +setsebool -P nfsd_anon_write $var_nfsd_anon_write + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the nis_enabled SELinux Boolean + By default, the SELinux boolean nis_enabled is disabled. +If this setting is enabled, it should be disabled. + +To disable the nis_enabled SELinux boolean, run the following command: +$ sudo setsebool -P nis_enabled off + + - name: XCCDF Value var_nis_enabled # promote to variable + set_fact: + var_nis_enabled: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_nis_enabled + +- name: Set SELinux boolean nis_enabled accordingly + seboolean: + name: nis_enabled + state: '{{ var_nis_enabled }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_nis_enabled + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_nis_enabled='' + + +setsebool -P nis_enabled $var_nis_enabled + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable the nscd_use_shm SELinux Boolean + By default, the SELinux boolean nscd_use_shm is enabled. +If this setting is disabled, it should be enabled to allow nscd +to use shared memory. + +To enable the nscd_use_shm SELinux boolean, run the following command: +$ sudo setsebool -P nscd_use_shm on + + - name: XCCDF Value var_nscd_use_shm # promote to variable + set_fact: + var_nscd_use_shm: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_nscd_use_shm + +- name: Set SELinux boolean nscd_use_shm accordingly + seboolean: + name: nscd_use_shm + state: '{{ var_nscd_use_shm }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_nscd_use_shm + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_nscd_use_shm='' + + +setsebool -P nscd_use_shm $var_nscd_use_shm + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the openshift_use_nfs SELinux Boolean + By default, the SELinux boolean openshift_use_nfs is disabled. +If this setting is enabled, it should be disabled. + +To disable the openshift_use_nfs SELinux boolean, run the following command: +$ sudo setsebool -P openshift_use_nfs off + + - name: XCCDF Value var_openshift_use_nfs # promote to variable + set_fact: + var_openshift_use_nfs: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_openshift_use_nfs + +- name: Set SELinux boolean openshift_use_nfs accordingly + seboolean: + name: openshift_use_nfs + state: '{{ var_openshift_use_nfs }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_openshift_use_nfs + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_openshift_use_nfs='' + + +setsebool -P openshift_use_nfs $var_openshift_use_nfs + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the openvpn_can_network_connect SELinux Boolean + By default, the SELinux boolean openvpn_can_network_connect is enabled. +This setting should be disabled. + +To disable the openvpn_can_network_connect SELinux boolean, run the following command: +$ sudo setsebool -P openvpn_can_network_connect off + + - name: XCCDF Value var_openvpn_can_network_connect # promote to variable + set_fact: + var_openvpn_can_network_connect: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_openvpn_can_network_connect + +- name: Set SELinux boolean openvpn_can_network_connect accordingly + seboolean: + name: openvpn_can_network_connect + state: '{{ var_openvpn_can_network_connect }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_openvpn_can_network_connect + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_openvpn_can_network_connect='' + + +setsebool -P openvpn_can_network_connect $var_openvpn_can_network_connect + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the openvpn_enable_homedirs SELinux Boolean + By default, the SELinux boolean openvpn_enable_homedirs is enabled. +This setting should be disabled. + +To disable the openvpn_enable_homedirs SELinux boolean, run the following command: +$ sudo setsebool -P openvpn_enable_homedirs off + + - name: XCCDF Value var_openvpn_enable_homedirs # promote to variable + set_fact: + var_openvpn_enable_homedirs: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_openvpn_enable_homedirs + +- name: Set SELinux boolean openvpn_enable_homedirs accordingly + seboolean: + name: openvpn_enable_homedirs + state: '{{ var_openvpn_enable_homedirs }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_openvpn_enable_homedirs + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_openvpn_enable_homedirs='' + + +setsebool -P openvpn_enable_homedirs $var_openvpn_enable_homedirs + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the openvpn_run_unconfined SELinux Boolean + By default, the SELinux boolean openvpn_run_unconfined is disabled. +If this setting is enabled, it should be disabled. + +To disable the openvpn_run_unconfined SELinux boolean, run the following command: +$ sudo setsebool -P openvpn_run_unconfined off + + - name: XCCDF Value var_openvpn_run_unconfined # promote to variable + set_fact: + var_openvpn_run_unconfined: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_openvpn_run_unconfined + +- name: Set SELinux boolean openvpn_run_unconfined accordingly + seboolean: + name: openvpn_run_unconfined + state: '{{ var_openvpn_run_unconfined }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_openvpn_run_unconfined + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_openvpn_run_unconfined='' + + +setsebool -P openvpn_run_unconfined $var_openvpn_run_unconfined + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the pcp_bind_all_unreserved_ports SELinux Boolean + By default, the SELinux boolean pcp_bind_all_unreserved_ports is disabled. +If this setting is enabled, it should be disabled. + +To disable the pcp_bind_all_unreserved_ports SELinux boolean, run the following command: +$ sudo setsebool -P pcp_bind_all_unreserved_ports off + + - name: XCCDF Value var_pcp_bind_all_unreserved_ports # promote to variable + set_fact: + var_pcp_bind_all_unreserved_ports: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_pcp_bind_all_unreserved_ports + +- name: Set SELinux boolean pcp_bind_all_unreserved_ports accordingly + seboolean: + name: pcp_bind_all_unreserved_ports + state: '{{ var_pcp_bind_all_unreserved_ports }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_pcp_bind_all_unreserved_ports + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_pcp_bind_all_unreserved_ports='' + + +setsebool -P pcp_bind_all_unreserved_ports $var_pcp_bind_all_unreserved_ports + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the pcp_read_generic_logs SELinux Boolean + By default, the SELinux boolean pcp_read_generic_logs is disabled. +If this setting is enabled, it should be disabled. + +To disable the pcp_read_generic_logs SELinux boolean, run the following command: +$ sudo setsebool -P pcp_read_generic_logs off + + - name: XCCDF Value var_pcp_read_generic_logs # promote to variable + set_fact: + var_pcp_read_generic_logs: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_pcp_read_generic_logs + +- name: Set SELinux boolean pcp_read_generic_logs accordingly + seboolean: + name: pcp_read_generic_logs + state: '{{ var_pcp_read_generic_logs }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_pcp_read_generic_logs + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_pcp_read_generic_logs='' + + +setsebool -P pcp_read_generic_logs $var_pcp_read_generic_logs + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the piranha_lvs_can_network_connect SELinux Boolean + By default, the SELinux boolean piranha_lvs_can_network_connect is disabled. +If this setting is enabled, it should be disabled. + +To disable the piranha_lvs_can_network_connect SELinux boolean, run the following command: +$ sudo setsebool -P piranha_lvs_can_network_connect off + + - name: XCCDF Value var_piranha_lvs_can_network_connect # promote to variable + set_fact: + var_piranha_lvs_can_network_connect: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_piranha_lvs_can_network_connect + +- name: Set SELinux boolean piranha_lvs_can_network_connect accordingly + seboolean: + name: piranha_lvs_can_network_connect + state: '{{ var_piranha_lvs_can_network_connect }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_piranha_lvs_can_network_connect + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_piranha_lvs_can_network_connect='' + + +setsebool -P piranha_lvs_can_network_connect $var_piranha_lvs_can_network_connect + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the polipo_connect_all_unreserved SELinux Boolean + By default, the SELinux boolean polipo_connect_all_unreserved is disabled. +If this setting is enabled, it should be disabled. + +To disable the polipo_connect_all_unreserved SELinux boolean, run the following command: +$ sudo setsebool -P polipo_connect_all_unreserved off + + - name: XCCDF Value var_polipo_connect_all_unreserved # promote to variable + set_fact: + var_polipo_connect_all_unreserved: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_polipo_connect_all_unreserved + +- name: Set SELinux boolean polipo_connect_all_unreserved accordingly + seboolean: + name: polipo_connect_all_unreserved + state: '{{ var_polipo_connect_all_unreserved }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_polipo_connect_all_unreserved + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_polipo_connect_all_unreserved='' + + +setsebool -P polipo_connect_all_unreserved $var_polipo_connect_all_unreserved + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the polipo_session_bind_all_unreserved_ports SELinux Boolean + By default, the SELinux boolean polipo_session_bind_all_unreserved_ports is disabled. +If this setting is enabled, it should be disabled. + +To disable the polipo_session_bind_all_unreserved_ports SELinux boolean, run the following command: +$ sudo setsebool -P polipo_session_bind_all_unreserved_ports off + + - name: XCCDF Value var_polipo_session_bind_all_unreserved_ports # promote to variable + set_fact: + var_polipo_session_bind_all_unreserved_ports: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_polipo_session_bind_all_unreserved_ports + +- name: Set SELinux boolean polipo_session_bind_all_unreserved_ports accordingly + seboolean: + name: polipo_session_bind_all_unreserved_ports + state: '{{ var_polipo_session_bind_all_unreserved_ports }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_polipo_session_bind_all_unreserved_ports + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_polipo_session_bind_all_unreserved_ports='' + + +setsebool -P polipo_session_bind_all_unreserved_ports $var_polipo_session_bind_all_unreserved_ports + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the polipo_session_users SELinux Boolean + By default, the SELinux boolean polipo_session_users is disabled. +If this setting is enabled, it should be disabled. + +To disable the polipo_session_users SELinux boolean, run the following command: +$ sudo setsebool -P polipo_session_users off + + - name: XCCDF Value var_polipo_session_users # promote to variable + set_fact: + var_polipo_session_users: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_polipo_session_users + +- name: Set SELinux boolean polipo_session_users accordingly + seboolean: + name: polipo_session_users + state: '{{ var_polipo_session_users }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_polipo_session_users + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_polipo_session_users='' + + +setsebool -P polipo_session_users $var_polipo_session_users + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the polipo_use_cifs SELinux Boolean + By default, the SELinux boolean polipo_use_cifs is disabled. +If this setting is enabled, it should be disabled. + +To disable the polipo_use_cifs SELinux boolean, run the following command: +$ sudo setsebool -P polipo_use_cifs off + + - name: XCCDF Value var_polipo_use_cifs # promote to variable + set_fact: + var_polipo_use_cifs: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_polipo_use_cifs + +- name: Set SELinux boolean polipo_use_cifs accordingly + seboolean: + name: polipo_use_cifs + state: '{{ var_polipo_use_cifs }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_polipo_use_cifs + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_polipo_use_cifs='' + + +setsebool -P polipo_use_cifs $var_polipo_use_cifs + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the polipo_use_nfs SELinux Boolean + By default, the SELinux boolean polipo_use_nfs is disabled. +If this setting is enabled, it should be disabled. + +To disable the polipo_use_nfs SELinux boolean, run the following command: +$ sudo setsebool -P polipo_use_nfs off + + - name: XCCDF Value var_polipo_use_nfs # promote to variable + set_fact: + var_polipo_use_nfs: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_polipo_use_nfs + +- name: Set SELinux boolean polipo_use_nfs accordingly + seboolean: + name: polipo_use_nfs + state: '{{ var_polipo_use_nfs }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_polipo_use_nfs + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_polipo_use_nfs='' + + +setsebool -P polipo_use_nfs $var_polipo_use_nfs + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Configure the polyinstantiation_enabled SELinux Boolean + By default, the SELinux boolean polyinstantiation_enabled is disabled. +This setting should be configured to . + +To set the polyinstantiation_enabled SELinux boolean, run the following command: +$ sudo setsebool -P polyinstantiation_enabled + BP28(R39) + + CCE-84083-5 + - name: XCCDF Value var_polyinstantiation_enabled # promote to variable + set_fact: + var_polyinstantiation_enabled: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84083-5 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_polyinstantiation_enabled + +- name: Set SELinux boolean polyinstantiation_enabled accordingly + seboolean: + name: polyinstantiation_enabled + state: '{{ var_polyinstantiation_enabled }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84083-5 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_polyinstantiation_enabled + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_polyinstantiation_enabled='' + + +setsebool -P polyinstantiation_enabled $var_polyinstantiation_enabled + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable the postfix_local_write_mail_spool SELinux Boolean + By default, the SELinux boolean postfix_local_write_mail_spool is enabled. +If this setting is disabled, it should be enabled as it allows Postfix to write +to the mail spool directories. + +To enable the postfix_local_write_mail_spool SELinux boolean, run the following command: +$ sudo setsebool -P postfix_local_write_mail_spool on + + - name: XCCDF Value var_postfix_local_write_mail_spool # promote to variable + set_fact: + var_postfix_local_write_mail_spool: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_postfix_local_write_mail_spool + +- name: Set SELinux boolean postfix_local_write_mail_spool accordingly + seboolean: + name: postfix_local_write_mail_spool + state: '{{ var_postfix_local_write_mail_spool }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_postfix_local_write_mail_spool + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_postfix_local_write_mail_spool='' + + +setsebool -P postfix_local_write_mail_spool $var_postfix_local_write_mail_spool + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the postgresql_can_rsync SELinux Boolean + By default, the SELinux boolean postgresql_can_rsync is disabled. +If this setting is enabled, it should be disabled. + +To disable the postgresql_can_rsync SELinux boolean, run the following command: +$ sudo setsebool -P postgresql_can_rsync off + + - name: XCCDF Value var_postgresql_can_rsync # promote to variable + set_fact: + var_postgresql_can_rsync: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_postgresql_can_rsync + +- name: Set SELinux boolean postgresql_can_rsync accordingly + seboolean: + name: postgresql_can_rsync + state: '{{ var_postgresql_can_rsync }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_postgresql_can_rsync + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_postgresql_can_rsync='' + + +setsebool -P postgresql_can_rsync $var_postgresql_can_rsync + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the postgresql_selinux_transmit_client_label SELinux Boolean + By default, the SELinux boolean postgresql_selinux_transmit_client_label is disabled. +If this setting is enabled, it should be disabled. + +To disable the postgresql_selinux_transmit_client_label SELinux boolean, run the following command: +$ sudo setsebool -P postgresql_selinux_transmit_client_label off + + - name: XCCDF Value var_postgresql_selinux_transmit_client_label # promote to variable + set_fact: + var_postgresql_selinux_transmit_client_label: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_postgresql_selinux_transmit_client_label + +- name: Set SELinux boolean postgresql_selinux_transmit_client_label accordingly + seboolean: + name: postgresql_selinux_transmit_client_label + state: '{{ var_postgresql_selinux_transmit_client_label }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_postgresql_selinux_transmit_client_label + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_postgresql_selinux_transmit_client_label='' + + +setsebool -P postgresql_selinux_transmit_client_label $var_postgresql_selinux_transmit_client_label + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable the postgresql_selinux_unconfined_dbadm SELinux Boolean + By default, the SELinux boolean postgresql_selinux_unconfined_dbadm is enabled. +If this setting is disabled, it should be enabled as it allows Database Administrators to +execute Data Manipulation Language (DML) statements. + +To enable the postgresql_selinux_unconfined_dbadm SELinux boolean, run the following command: +$ sudo setsebool -P postgresql_selinux_unconfined_dbadm on + + - name: XCCDF Value var_postgresql_selinux_unconfined_dbadm # promote to variable + set_fact: + var_postgresql_selinux_unconfined_dbadm: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_postgresql_selinux_unconfined_dbadm + +- name: Set SELinux boolean postgresql_selinux_unconfined_dbadm accordingly + seboolean: + name: postgresql_selinux_unconfined_dbadm + state: '{{ var_postgresql_selinux_unconfined_dbadm }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_postgresql_selinux_unconfined_dbadm + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_postgresql_selinux_unconfined_dbadm='' + + +setsebool -P postgresql_selinux_unconfined_dbadm $var_postgresql_selinux_unconfined_dbadm + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable the postgresql_selinux_users_ddl SELinux Boolean + By default, the SELinux boolean postgresql_selinux_users_ddl is enabled. +If this setting is disabled, it should be enabled as it allows Database Administrators to +execute Data Definition Language (DDL) statements. + +To enable the postgresql_selinux_users_ddl SELinux boolean, run the following command: +$ sudo setsebool -P postgresql_selinux_users_ddl on + + - name: XCCDF Value var_postgresql_selinux_users_ddl # promote to variable + set_fact: + var_postgresql_selinux_users_ddl: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_postgresql_selinux_users_ddl + +- name: Set SELinux boolean postgresql_selinux_users_ddl accordingly + seboolean: + name: postgresql_selinux_users_ddl + state: '{{ var_postgresql_selinux_users_ddl }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_postgresql_selinux_users_ddl + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_postgresql_selinux_users_ddl='' + + +setsebool -P postgresql_selinux_users_ddl $var_postgresql_selinux_users_ddl + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the pppd_can_insmod SELinux Boolean + By default, the SELinux boolean pppd_can_insmod is disabled. +If this setting is enabled, it should be disabled. + +To disable the pppd_can_insmod SELinux boolean, run the following command: +$ sudo setsebool -P pppd_can_insmod off + + - name: XCCDF Value var_pppd_can_insmod # promote to variable + set_fact: + var_pppd_can_insmod: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_pppd_can_insmod + +- name: Set SELinux boolean pppd_can_insmod accordingly + seboolean: + name: pppd_can_insmod + state: '{{ var_pppd_can_insmod }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_pppd_can_insmod + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_pppd_can_insmod='' + + +setsebool -P pppd_can_insmod $var_pppd_can_insmod + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the pppd_for_user SELinux Boolean + By default, the SELinux boolean pppd_for_user is disabled. +If this setting is enabled, it should be disabled. + +To disable the pppd_for_user SELinux boolean, run the following command: +$ sudo setsebool -P pppd_for_user off + + - name: XCCDF Value var_pppd_for_user # promote to variable + set_fact: + var_pppd_for_user: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_pppd_for_user + +- name: Set SELinux boolean pppd_for_user accordingly + seboolean: + name: pppd_for_user + state: '{{ var_pppd_for_user }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_pppd_for_user + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_pppd_for_user='' + + +setsebool -P pppd_for_user $var_pppd_for_user + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the privoxy_connect_any SELinux Boolean + By default, the SELinux boolean privoxy_connect_any is enabled. +This setting should be disabled. + +To disable the privoxy_connect_any SELinux boolean, run the following command: +$ sudo setsebool -P privoxy_connect_any off + + - name: XCCDF Value var_privoxy_connect_any # promote to variable + set_fact: + var_privoxy_connect_any: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_privoxy_connect_any + +- name: Set SELinux boolean privoxy_connect_any accordingly + seboolean: + name: privoxy_connect_any + state: '{{ var_privoxy_connect_any }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_privoxy_connect_any + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_privoxy_connect_any='' + + +setsebool -P privoxy_connect_any $var_privoxy_connect_any + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the prosody_bind_http_port SELinux Boolean + By default, the SELinux boolean prosody_bind_http_port is disabled. +If this setting is enabled, it should be disabled. + +To disable the prosody_bind_http_port SELinux boolean, run the following command: +$ sudo setsebool -P prosody_bind_http_port off + + - name: XCCDF Value var_prosody_bind_http_port # promote to variable + set_fact: + var_prosody_bind_http_port: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_prosody_bind_http_port + +- name: Set SELinux boolean prosody_bind_http_port accordingly + seboolean: + name: prosody_bind_http_port + state: '{{ var_prosody_bind_http_port }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_prosody_bind_http_port + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_prosody_bind_http_port='' + + +setsebool -P prosody_bind_http_port $var_prosody_bind_http_port + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the puppetagent_manage_all_files SELinux Boolean + By default, the SELinux boolean puppetagent_manage_all_files is disabled. +If this setting is enabled, it should be disabled. + +To disable the puppetagent_manage_all_files SELinux boolean, run the following command: +$ sudo setsebool -P puppetagent_manage_all_files off + + - name: XCCDF Value var_puppetagent_manage_all_files # promote to variable + set_fact: + var_puppetagent_manage_all_files: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_puppetagent_manage_all_files + +- name: Set SELinux boolean puppetagent_manage_all_files accordingly + seboolean: + name: puppetagent_manage_all_files + state: '{{ var_puppetagent_manage_all_files }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_puppetagent_manage_all_files + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_puppetagent_manage_all_files='' + + +setsebool -P puppetagent_manage_all_files $var_puppetagent_manage_all_files + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the puppetmaster_use_db SELinux Boolean + By default, the SELinux boolean puppetmaster_use_db is disabled. +If this setting is enabled, it should be disabled. + +To disable the puppetmaster_use_db SELinux boolean, run the following command: +$ sudo setsebool -P puppetmaster_use_db off + + - name: XCCDF Value var_puppetmaster_use_db # promote to variable + set_fact: + var_puppetmaster_use_db: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_puppetmaster_use_db + +- name: Set SELinux boolean puppetmaster_use_db accordingly + seboolean: + name: puppetmaster_use_db + state: '{{ var_puppetmaster_use_db }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_puppetmaster_use_db + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_puppetmaster_use_db='' + + +setsebool -P puppetmaster_use_db $var_puppetmaster_use_db + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the racoon_read_shadow SELinux Boolean + By default, the SELinux boolean racoon_read_shadow is disabled. +If this setting is enabled, it should be disabled. + +To disable the racoon_read_shadow SELinux boolean, run the following command: +$ sudo setsebool -P racoon_read_shadow off + + - name: XCCDF Value var_racoon_read_shadow # promote to variable + set_fact: + var_racoon_read_shadow: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_racoon_read_shadow + +- name: Set SELinux boolean racoon_read_shadow accordingly + seboolean: + name: racoon_read_shadow + state: '{{ var_racoon_read_shadow }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_racoon_read_shadow + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_racoon_read_shadow='' + + +setsebool -P racoon_read_shadow $var_racoon_read_shadow + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the rsync_anon_write SELinux Boolean + By default, the SELinux boolean rsync_anon_write is disabled. +If this setting is enabled, it should be disabled. + +To disable the rsync_anon_write SELinux boolean, run the following command: +$ sudo setsebool -P rsync_anon_write off + + - name: XCCDF Value var_rsync_anon_write # promote to variable + set_fact: + var_rsync_anon_write: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_rsync_anon_write + +- name: Set SELinux boolean rsync_anon_write accordingly + seboolean: + name: rsync_anon_write + state: '{{ var_rsync_anon_write }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_rsync_anon_write + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_rsync_anon_write='' + + +setsebool -P rsync_anon_write $var_rsync_anon_write + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the rsync_client SELinux Boolean + By default, the SELinux boolean rsync_client is disabled. +If this setting is enabled, it should be disabled. + +To disable the rsync_client SELinux boolean, run the following command: +$ sudo setsebool -P rsync_client off + + - name: XCCDF Value var_rsync_client # promote to variable + set_fact: + var_rsync_client: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_rsync_client + +- name: Set SELinux boolean rsync_client accordingly + seboolean: + name: rsync_client + state: '{{ var_rsync_client }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_rsync_client + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_rsync_client='' + + +setsebool -P rsync_client $var_rsync_client + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the rsync_export_all_ro SELinux Boolean + By default, the SELinux boolean rsync_export_all_ro is disabled. +If this setting is enabled, it should be disabled. + +To disable the rsync_export_all_ro SELinux boolean, run the following command: +$ sudo setsebool -P rsync_export_all_ro off + + - name: XCCDF Value var_rsync_export_all_ro # promote to variable + set_fact: + var_rsync_export_all_ro: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_rsync_export_all_ro + +- name: Set SELinux boolean rsync_export_all_ro accordingly + seboolean: + name: rsync_export_all_ro + state: '{{ var_rsync_export_all_ro }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_rsync_export_all_ro + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_rsync_export_all_ro='' + + +setsebool -P rsync_export_all_ro $var_rsync_export_all_ro + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the rsync_full_access SELinux Boolean + By default, the SELinux boolean rsync_full_access is disabled. +If this setting is enabled, it should be disabled. + +To disable the rsync_full_access SELinux boolean, run the following command: +$ sudo setsebool -P rsync_full_access off + + - name: XCCDF Value var_rsync_full_access # promote to variable + set_fact: + var_rsync_full_access: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_rsync_full_access + +- name: Set SELinux boolean rsync_full_access accordingly + seboolean: + name: rsync_full_access + state: '{{ var_rsync_full_access }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_rsync_full_access + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_rsync_full_access='' + + +setsebool -P rsync_full_access $var_rsync_full_access + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the samba_create_home_dirs SELinux Boolean + By default, the SELinux boolean samba_create_home_dirs is disabled. +If this setting is enabled, it should be disabled. + +To disable the samba_create_home_dirs SELinux boolean, run the following command: +$ sudo setsebool -P samba_create_home_dirs off + + - name: XCCDF Value var_samba_create_home_dirs # promote to variable + set_fact: + var_samba_create_home_dirs: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_samba_create_home_dirs + +- name: Set SELinux boolean samba_create_home_dirs accordingly + seboolean: + name: samba_create_home_dirs + state: '{{ var_samba_create_home_dirs }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_samba_create_home_dirs + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_samba_create_home_dirs='' + + +setsebool -P samba_create_home_dirs $var_samba_create_home_dirs + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the samba_domain_controller SELinux Boolean + By default, the SELinux boolean samba_domain_controller is disabled. +If this setting is enabled, it should be disabled. + +To disable the samba_domain_controller SELinux boolean, run the following command: +$ sudo setsebool -P samba_domain_controller off + + - name: XCCDF Value var_samba_domain_controller # promote to variable + set_fact: + var_samba_domain_controller: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_samba_domain_controller + +- name: Set SELinux boolean samba_domain_controller accordingly + seboolean: + name: samba_domain_controller + state: '{{ var_samba_domain_controller }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_samba_domain_controller + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_samba_domain_controller='' + + +setsebool -P samba_domain_controller $var_samba_domain_controller + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the samba_enable_home_dirs SELinux Boolean + By default, the SELinux boolean samba_enable_home_dirs is disabled. +If this setting is enabled, it should be disabled. + +To disable the samba_enable_home_dirs SELinux boolean, run the following command: +$ sudo setsebool -P samba_enable_home_dirs off + + - name: XCCDF Value var_samba_enable_home_dirs # promote to variable + set_fact: + var_samba_enable_home_dirs: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_samba_enable_home_dirs + +- name: Set SELinux boolean samba_enable_home_dirs accordingly + seboolean: + name: samba_enable_home_dirs + state: '{{ var_samba_enable_home_dirs }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_samba_enable_home_dirs + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_samba_enable_home_dirs='' + + +setsebool -P samba_enable_home_dirs $var_samba_enable_home_dirs + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the samba_export_all_ro SELinux Boolean + By default, the SELinux boolean samba_export_all_ro is disabled. +If this setting is enabled, it should be disabled. + +To disable the samba_export_all_ro SELinux boolean, run the following command: +$ sudo setsebool -P samba_export_all_ro off + + - name: XCCDF Value var_samba_export_all_ro # promote to variable + set_fact: + var_samba_export_all_ro: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_samba_export_all_ro + +- name: Set SELinux boolean samba_export_all_ro accordingly + seboolean: + name: samba_export_all_ro + state: '{{ var_samba_export_all_ro }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_samba_export_all_ro + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_samba_export_all_ro='' + + +setsebool -P samba_export_all_ro $var_samba_export_all_ro + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the samba_export_all_rw SELinux Boolean + By default, the SELinux boolean samba_export_all_rw is disabled. +If this setting is enabled, it should be disabled. + +To disable the samba_export_all_rw SELinux boolean, run the following command: +$ sudo setsebool -P samba_export_all_rw off + + - name: XCCDF Value var_samba_export_all_rw # promote to variable + set_fact: + var_samba_export_all_rw: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_samba_export_all_rw + +- name: Set SELinux boolean samba_export_all_rw accordingly + seboolean: + name: samba_export_all_rw + state: '{{ var_samba_export_all_rw }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_samba_export_all_rw + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_samba_export_all_rw='' + + +setsebool -P samba_export_all_rw $var_samba_export_all_rw + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the samba_load_libgfapi SELinux Boolean + By default, the SELinux boolean samba_load_libgfapi is disabled. +If this setting is enabled, it should be disabled. + +To disable the samba_load_libgfapi SELinux boolean, run the following command: +$ sudo setsebool -P samba_load_libgfapi off + + - name: XCCDF Value var_samba_load_libgfapi # promote to variable + set_fact: + var_samba_load_libgfapi: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_samba_load_libgfapi + +- name: Set SELinux boolean samba_load_libgfapi accordingly + seboolean: + name: samba_load_libgfapi + state: '{{ var_samba_load_libgfapi }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_samba_load_libgfapi + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_samba_load_libgfapi='' + + +setsebool -P samba_load_libgfapi $var_samba_load_libgfapi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the samba_portmapper SELinux Boolean + By default, the SELinux boolean samba_portmapper is disabled. +If this setting is enabled, it should be disabled. + +To disable the samba_portmapper SELinux boolean, run the following command: +$ sudo setsebool -P samba_portmapper off + + - name: XCCDF Value var_samba_portmapper # promote to variable + set_fact: + var_samba_portmapper: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_samba_portmapper + +- name: Set SELinux boolean samba_portmapper accordingly + seboolean: + name: samba_portmapper + state: '{{ var_samba_portmapper }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_samba_portmapper + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_samba_portmapper='' + + +setsebool -P samba_portmapper $var_samba_portmapper + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the samba_run_unconfined SELinux Boolean + By default, the SELinux boolean samba_run_unconfined is disabled. +If this setting is enabled, it should be disabled. + +To disable the samba_run_unconfined SELinux boolean, run the following command: +$ sudo setsebool -P samba_run_unconfined off + + - name: XCCDF Value var_samba_run_unconfined # promote to variable + set_fact: + var_samba_run_unconfined: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_samba_run_unconfined + +- name: Set SELinux boolean samba_run_unconfined accordingly + seboolean: + name: samba_run_unconfined + state: '{{ var_samba_run_unconfined }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_samba_run_unconfined + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_samba_run_unconfined='' + + +setsebool -P samba_run_unconfined $var_samba_run_unconfined + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the samba_share_fusefs SELinux Boolean + By default, the SELinux boolean samba_share_fusefs is disabled. +If this setting is enabled, it should be disabled. + +To disable the samba_share_fusefs SELinux boolean, run the following command: +$ sudo setsebool -P samba_share_fusefs off + + - name: XCCDF Value var_samba_share_fusefs # promote to variable + set_fact: + var_samba_share_fusefs: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_samba_share_fusefs + +- name: Set SELinux boolean samba_share_fusefs accordingly + seboolean: + name: samba_share_fusefs + state: '{{ var_samba_share_fusefs }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_samba_share_fusefs + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_samba_share_fusefs='' + + +setsebool -P samba_share_fusefs $var_samba_share_fusefs + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the samba_share_nfs SELinux Boolean + By default, the SELinux boolean samba_share_nfs is disabled. +If this setting is enabled, it should be disabled. + +To disable the samba_share_nfs SELinux boolean, run the following command: +$ sudo setsebool -P samba_share_nfs off + + - name: XCCDF Value var_samba_share_nfs # promote to variable + set_fact: + var_samba_share_nfs: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_samba_share_nfs + +- name: Set SELinux boolean samba_share_nfs accordingly + seboolean: + name: samba_share_nfs + state: '{{ var_samba_share_nfs }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_samba_share_nfs + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_samba_share_nfs='' + + +setsebool -P samba_share_nfs $var_samba_share_nfs + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the sanlock_use_fusefs SELinux Boolean + By default, the SELinux boolean sanlock_use_fusefs is disabled. +If this setting is enabled, it should be disabled. + +To disable the sanlock_use_fusefs SELinux boolean, run the following command: +$ sudo setsebool -P sanlock_use_fusefs off + + - name: XCCDF Value var_sanlock_use_fusefs # promote to variable + set_fact: + var_sanlock_use_fusefs: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_sanlock_use_fusefs + +- name: Set SELinux boolean sanlock_use_fusefs accordingly + seboolean: + name: sanlock_use_fusefs + state: '{{ var_sanlock_use_fusefs }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_sanlock_use_fusefs + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_sanlock_use_fusefs='' + + +setsebool -P sanlock_use_fusefs $var_sanlock_use_fusefs + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the sanlock_use_nfs SELinux Boolean + By default, the SELinux boolean sanlock_use_nfs is disabled. +If this setting is enabled, it should be disabled. + +To disable the sanlock_use_nfs SELinux boolean, run the following command: +$ sudo setsebool -P sanlock_use_nfs off + + - name: XCCDF Value var_sanlock_use_nfs # promote to variable + set_fact: + var_sanlock_use_nfs: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_sanlock_use_nfs + +- name: Set SELinux boolean sanlock_use_nfs accordingly + seboolean: + name: sanlock_use_nfs + state: '{{ var_sanlock_use_nfs }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_sanlock_use_nfs + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_sanlock_use_nfs='' + + +setsebool -P sanlock_use_nfs $var_sanlock_use_nfs + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the sanlock_use_samba SELinux Boolean + By default, the SELinux boolean sanlock_use_samba is disabled. +If this setting is enabled, it should be disabled. + +To disable the sanlock_use_samba SELinux boolean, run the following command: +$ sudo setsebool -P sanlock_use_samba off + + - name: XCCDF Value var_sanlock_use_samba # promote to variable + set_fact: + var_sanlock_use_samba: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_sanlock_use_samba + +- name: Set SELinux boolean sanlock_use_samba accordingly + seboolean: + name: sanlock_use_samba + state: '{{ var_sanlock_use_samba }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_sanlock_use_samba + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_sanlock_use_samba='' + + +setsebool -P sanlock_use_samba $var_sanlock_use_samba + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the saslauthd_read_shadow SELinux Boolean + By default, the SELinux boolean saslauthd_read_shadow is disabled. +If this setting is enabled, it should be disabled. + +To disable the saslauthd_read_shadow SELinux boolean, run the following command: +$ sudo setsebool -P saslauthd_read_shadow off + + - name: XCCDF Value var_saslauthd_read_shadow # promote to variable + set_fact: + var_saslauthd_read_shadow: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_saslauthd_read_shadow + +- name: Set SELinux boolean saslauthd_read_shadow accordingly + seboolean: + name: saslauthd_read_shadow + state: '{{ var_saslauthd_read_shadow }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_saslauthd_read_shadow + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_saslauthd_read_shadow='' + + +setsebool -P saslauthd_read_shadow $var_saslauthd_read_shadow + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable the secadm_exec_content SELinux Boolean + By default, the SELinux boolean secadm_exec_content is enabled. +If this setting is disabled, it should be enabled. + +To enable the secadm_exec_content SELinux boolean, run the following command: +$ sudo setsebool -P secadm_exec_content on + + - name: XCCDF Value var_secadm_exec_content # promote to variable + set_fact: + var_secadm_exec_content: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_secadm_exec_content + +- name: Set SELinux boolean secadm_exec_content accordingly + seboolean: + name: secadm_exec_content + state: '{{ var_secadm_exec_content }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_secadm_exec_content + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_secadm_exec_content='' + + +setsebool -P secadm_exec_content $var_secadm_exec_content + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the secure_mode SELinux Boolean + By default, the SELinux boolean secure_mode is disabled. +If this setting is enabled, it should be disabled. + +To disable the secure_mode SELinux boolean, run the following command: +$ sudo setsebool -P secure_mode off + + - name: XCCDF Value var_secure_mode # promote to variable + set_fact: + var_secure_mode: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_secure_mode + +- name: Set SELinux boolean secure_mode accordingly + seboolean: + name: secure_mode + state: '{{ var_secure_mode }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_secure_mode + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_secure_mode='' + + +setsebool -P secure_mode $var_secure_mode + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Configure the secure_mode_insmod SELinux Boolean + By default, the SELinux boolean secure_mode_insmod is disabled. +This setting should be configured to . + +To set the secure_mode_insmod SELinux boolean, run the following command: +$ sudo setsebool -P secure_mode_insmod + BP28(R67) + + CCE-84087-6 + - name: XCCDF Value var_secure_mode_insmod # promote to variable + set_fact: + var_secure_mode_insmod: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84087-6 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_secure_mode_insmod + +- name: Set SELinux boolean secure_mode_insmod accordingly + seboolean: + name: secure_mode_insmod + state: '{{ var_secure_mode_insmod }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84087-6 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_secure_mode_insmod + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_secure_mode_insmod='' + + +setsebool -P secure_mode_insmod $var_secure_mode_insmod + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the secure_mode_policyload SELinux Boolean + By default, the SELinux boolean secure_mode_policyload is disabled. +If this setting is enabled, it should be disabled. + +To disable the secure_mode_policyload SELinux boolean, run the following command: +$ sudo setsebool -P secure_mode_policyload off + + - name: XCCDF Value var_secure_mode_policyload # promote to variable + set_fact: + var_secure_mode_policyload: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_secure_mode_policyload + +- name: Set SELinux boolean secure_mode_policyload accordingly + seboolean: + name: secure_mode_policyload + state: '{{ var_secure_mode_policyload }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_secure_mode_policyload + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_secure_mode_policyload='' + + +setsebool -P secure_mode_policyload $var_secure_mode_policyload + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Configure the selinuxuser_direct_dri_enabled SELinux Boolean + By default, the SELinux boolean selinuxuser_direct_dri_enabled is enabled. +If XWindows is not installed or used on the system, this setting should be disabled. +Otherwise, enable it. + +To disable the selinuxuser_direct_dri_enabled SELinux boolean, run the following command: +$ sudo setsebool -P selinuxuser_direct_dri_enabled off + + - name: XCCDF Value var_selinuxuser_direct_dri_enabled # promote to variable + set_fact: + var_selinuxuser_direct_dri_enabled: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_selinuxuser_direct_dri_enabled + +- name: Set SELinux boolean selinuxuser_direct_dri_enabled accordingly + seboolean: + name: selinuxuser_direct_dri_enabled + state: '{{ var_selinuxuser_direct_dri_enabled }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_selinuxuser_direct_dri_enabled + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_selinuxuser_direct_dri_enabled='' + + +setsebool -P selinuxuser_direct_dri_enabled $var_selinuxuser_direct_dri_enabled + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the selinuxuser_execheap SELinux Boolean + By default, the SELinux boolean selinuxuser_execheap is disabled. +When enabled this boolean is enabled it allows selinuxusers to execute code from the heap. +If this setting is enabled, it should be disabled. + +To disable the selinuxuser_execheap SELinux boolean, run the following command: +$ sudo setsebool -P selinuxuser_execheap off + BP28(R67) + 164.308(a)(1)(ii)(D) + 164.308(a)(3) + 164.308(a)(4) + 164.310(b) + 164.310(c) + 164.312(a) + 164.312(e) + Disabling code execution from the heap blocks buffer overflow attacks. + CCE-84084-3 + - name: XCCDF Value var_selinuxuser_execheap # promote to variable + set_fact: + var_selinuxuser_execheap: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84084-3 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_selinuxuser_execheap + +- name: Set SELinux boolean selinuxuser_execheap accordingly + seboolean: + name: selinuxuser_execheap + state: '{{ var_selinuxuser_execheap }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84084-3 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_selinuxuser_execheap + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_selinuxuser_execheap='' + + +setsebool -P selinuxuser_execheap $var_selinuxuser_execheap + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable the selinuxuser_execmod SELinux Boolean + By default, the SELinux boolean selinuxuser_execmod is enabled. +If this setting is disabled, it should be enabled. + +To enable the selinuxuser_execmod SELinux boolean, run the following command: +$ sudo setsebool -P selinuxuser_execmod on + 164.308(a)(1)(ii)(D) + 164.308(a)(3) + 164.308(a)(4) + 164.310(b) + 164.310(c) + 164.312(a) + 164.312(e) + + CCE-84086-8 + - name: XCCDF Value var_selinuxuser_execmod # promote to variable + set_fact: + var_selinuxuser_execmod: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84086-8 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_selinuxuser_execmod + +- name: Set SELinux boolean selinuxuser_execmod accordingly + seboolean: + name: selinuxuser_execmod + state: '{{ var_selinuxuser_execmod }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84086-8 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_selinuxuser_execmod + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_selinuxuser_execmod='' + + +setsebool -P selinuxuser_execmod $var_selinuxuser_execmod + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + disable the selinuxuser_execstack SELinux Boolean + By default, the SELinux boolean selinuxuser_execstack is enabled. +This setting should be disabled as unconfined executables should not be able +to make their stack executable. + +To disable the selinuxuser_execstack SELinux boolean, run the following command: +$ sudo setsebool -P selinuxuser_execstack off + BP28(R67) + 164.308(a)(1)(ii)(D) + 164.308(a)(3) + 164.308(a)(4) + 164.310(b) + 164.310(c) + 164.312(a) + 164.312(e) + Disabling code execution from the stack blocks buffer overflow attacks. + CCE-84089-2 + - name: XCCDF Value var_selinuxuser_execstack # promote to variable + set_fact: + var_selinuxuser_execstack: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84089-2 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_selinuxuser_execstack + +- name: Set SELinux boolean selinuxuser_execstack accordingly + seboolean: + name: selinuxuser_execstack + state: '{{ var_selinuxuser_execstack }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84089-2 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_selinuxuser_execstack + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_selinuxuser_execstack='' + + +setsebool -P selinuxuser_execstack $var_selinuxuser_execstack + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the selinuxuser_mysql_connect_enabled SELinux Boolean + By default, the SELinux boolean selinuxuser_mysql_connect_enabled is disabled. +If this setting is enabled, it should be disabled. + +To disable the selinuxuser_mysql_connect_enabled SELinux boolean, run the following command: +$ sudo setsebool -P selinuxuser_mysql_connect_enabled off + + - name: XCCDF Value var_selinuxuser_mysql_connect_enabled # promote to variable + set_fact: + var_selinuxuser_mysql_connect_enabled: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_selinuxuser_mysql_connect_enabled + +- name: Set SELinux boolean selinuxuser_mysql_connect_enabled accordingly + seboolean: + name: selinuxuser_mysql_connect_enabled + state: '{{ var_selinuxuser_mysql_connect_enabled }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_selinuxuser_mysql_connect_enabled + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_selinuxuser_mysql_connect_enabled='' + + +setsebool -P selinuxuser_mysql_connect_enabled $var_selinuxuser_mysql_connect_enabled + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable the selinuxuser_ping SELinux Boolean + By default, the SELinux boolean selinuxuser_ping is enabled. +If this setting is disabled, it should be enabled as it allows confined users +to use ping and traceroute which is helpful for network troubleshooting. + +To enable the selinuxuser_ping SELinux boolean, run the following command: +$ sudo setsebool -P selinuxuser_ping on + + - name: XCCDF Value var_selinuxuser_ping # promote to variable + set_fact: + var_selinuxuser_ping: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_selinuxuser_ping + +- name: Set SELinux boolean selinuxuser_ping accordingly + seboolean: + name: selinuxuser_ping + state: '{{ var_selinuxuser_ping }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_selinuxuser_ping + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_selinuxuser_ping='' + + +setsebool -P selinuxuser_ping $var_selinuxuser_ping + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the selinuxuser_postgresql_connect_enabled SELinux Boolean + By default, the SELinux boolean selinuxuser_postgresql_connect_enabled is disabled. +If this setting is enabled, it should be disabled. + +To disable the selinuxuser_postgresql_connect_enabled SELinux boolean, run the following command: +$ sudo setsebool -P selinuxuser_postgresql_connect_enabled off + + - name: XCCDF Value var_selinuxuser_postgresql_connect_enabled # promote to variable + set_fact: + var_selinuxuser_postgresql_connect_enabled: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_selinuxuser_postgresql_connect_enabled + +- name: Set SELinux boolean selinuxuser_postgresql_connect_enabled accordingly + seboolean: + name: selinuxuser_postgresql_connect_enabled + state: '{{ var_selinuxuser_postgresql_connect_enabled }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_selinuxuser_postgresql_connect_enabled + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_selinuxuser_postgresql_connect_enabled='' + + +setsebool -P selinuxuser_postgresql_connect_enabled $var_selinuxuser_postgresql_connect_enabled + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the selinuxuser_rw_noexattrfile SELinux Boolean + By default, the SELinux boolean selinuxuser_rw_noexattrfile is enabled. +This setting should be disabled as users should not be able to read/write files +on filesystems that do not have extended attributes e.g. FAT, CDROM, FLOPPY, etc. + +To disable the selinuxuser_rw_noexattrfile SELinux boolean, run the following command: +$ sudo setsebool -P selinuxuser_rw_noexattrfile off + + - name: XCCDF Value var_selinuxuser_rw_noexattrfile # promote to variable + set_fact: + var_selinuxuser_rw_noexattrfile: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_selinuxuser_rw_noexattrfile + +- name: Set SELinux boolean selinuxuser_rw_noexattrfile accordingly + seboolean: + name: selinuxuser_rw_noexattrfile + state: '{{ var_selinuxuser_rw_noexattrfile }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_selinuxuser_rw_noexattrfile + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_selinuxuser_rw_noexattrfile='' + + +setsebool -P selinuxuser_rw_noexattrfile $var_selinuxuser_rw_noexattrfile + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the selinuxuser_share_music SELinux Boolean + By default, the SELinux boolean selinuxuser_share_music is disabled. +If this setting is enabled, it should be disabled. + +To disable the selinuxuser_share_music SELinux boolean, run the following command: +$ sudo setsebool -P selinuxuser_share_music off + + - name: XCCDF Value var_selinuxuser_share_music # promote to variable + set_fact: + var_selinuxuser_share_music: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_selinuxuser_share_music + +- name: Set SELinux boolean selinuxuser_share_music accordingly + seboolean: + name: selinuxuser_share_music + state: '{{ var_selinuxuser_share_music }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_selinuxuser_share_music + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_selinuxuser_share_music='' + + +setsebool -P selinuxuser_share_music $var_selinuxuser_share_music + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the selinuxuser_tcp_server SELinux Boolean + By default, the SELinux boolean selinuxuser_tcp_server is disabled. +If this setting is enabled, it should be disabled. + +To disable the selinuxuser_tcp_server SELinux boolean, run the following command: +$ sudo setsebool -P selinuxuser_tcp_server off + + - name: XCCDF Value var_selinuxuser_tcp_server # promote to variable + set_fact: + var_selinuxuser_tcp_server: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_selinuxuser_tcp_server + +- name: Set SELinux boolean selinuxuser_tcp_server accordingly + seboolean: + name: selinuxuser_tcp_server + state: '{{ var_selinuxuser_tcp_server }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_selinuxuser_tcp_server + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_selinuxuser_tcp_server='' + + +setsebool -P selinuxuser_tcp_server $var_selinuxuser_tcp_server + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the selinuxuser_udp_server SELinux Boolean + By default, the SELinux boolean selinuxuser_udp_server is disabled. +If this setting is enabled, it should be disabled. + +To disable the selinuxuser_udp_server SELinux boolean, run the following command: +$ sudo setsebool -P selinuxuser_udp_server off + + - name: XCCDF Value var_selinuxuser_udp_server # promote to variable + set_fact: + var_selinuxuser_udp_server: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_selinuxuser_udp_server + +- name: Set SELinux boolean selinuxuser_udp_server accordingly + seboolean: + name: selinuxuser_udp_server + state: '{{ var_selinuxuser_udp_server }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_selinuxuser_udp_server + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_selinuxuser_udp_server='' + + +setsebool -P selinuxuser_udp_server $var_selinuxuser_udp_server + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the selinuxuser_use_ssh_chroot SELinux Boolean + By default, the SELinux boolean selinuxuser_use_ssh_chroot is disabled. +If this setting is enabled, it should be disabled. + +To disable the selinuxuser_use_ssh_chroot SELinux boolean, run the following command: +$ sudo setsebool -P selinuxuser_use_ssh_chroot off + + - name: XCCDF Value var_selinuxuser_use_ssh_chroot # promote to variable + set_fact: + var_selinuxuser_use_ssh_chroot: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_selinuxuser_use_ssh_chroot + +- name: Set SELinux boolean selinuxuser_use_ssh_chroot accordingly + seboolean: + name: selinuxuser_use_ssh_chroot + state: '{{ var_selinuxuser_use_ssh_chroot }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_selinuxuser_use_ssh_chroot + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_selinuxuser_use_ssh_chroot='' + + +setsebool -P selinuxuser_use_ssh_chroot $var_selinuxuser_use_ssh_chroot + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the sge_domain_can_network_connect SELinux Boolean + By default, the SELinux boolean sge_domain_can_network_connect is disabled. +If this setting is enabled, it should be disabled. + +To disable the sge_domain_can_network_connect SELinux boolean, run the following command: +$ sudo setsebool -P sge_domain_can_network_connect off + + - name: XCCDF Value var_sge_domain_can_network_connect # promote to variable + set_fact: + var_sge_domain_can_network_connect: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_sge_domain_can_network_connect + +- name: Set SELinux boolean sge_domain_can_network_connect accordingly + seboolean: + name: sge_domain_can_network_connect + state: '{{ var_sge_domain_can_network_connect }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_sge_domain_can_network_connect + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_sge_domain_can_network_connect='' + + +setsebool -P sge_domain_can_network_connect $var_sge_domain_can_network_connect + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the sge_use_nfs SELinux Boolean + By default, the SELinux boolean sge_use_nfs is disabled. +If this setting is enabled, it should be disabled. + +To disable the sge_use_nfs SELinux boolean, run the following command: +$ sudo setsebool -P sge_use_nfs off + + - name: XCCDF Value var_sge_use_nfs # promote to variable + set_fact: + var_sge_use_nfs: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_sge_use_nfs + +- name: Set SELinux boolean sge_use_nfs accordingly + seboolean: + name: sge_use_nfs + state: '{{ var_sge_use_nfs }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_sge_use_nfs + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_sge_use_nfs='' + + +setsebool -P sge_use_nfs $var_sge_use_nfs + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the smartmon_3ware SELinux Boolean + By default, the SELinux boolean smartmon_3ware is disabled. +If this setting is enabled, it should be disabled. + +To disable the smartmon_3ware SELinux boolean, run the following command: +$ sudo setsebool -P smartmon_3ware off + + - name: XCCDF Value var_smartmon_3ware # promote to variable + set_fact: + var_smartmon_3ware: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_smartmon_3ware + +- name: Set SELinux boolean smartmon_3ware accordingly + seboolean: + name: smartmon_3ware + state: '{{ var_smartmon_3ware }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_smartmon_3ware + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_smartmon_3ware='' + + +setsebool -P smartmon_3ware $var_smartmon_3ware + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the smbd_anon_write SELinux Boolean + By default, the SELinux boolean smbd_anon_write is disabled. +If this setting is enabled, it should be disabled. + +To disable the smbd_anon_write SELinux boolean, run the following command: +$ sudo setsebool -P smbd_anon_write off + + - name: XCCDF Value var_smbd_anon_write # promote to variable + set_fact: + var_smbd_anon_write: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_smbd_anon_write + +- name: Set SELinux boolean smbd_anon_write accordingly + seboolean: + name: smbd_anon_write + state: '{{ var_smbd_anon_write }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_smbd_anon_write + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_smbd_anon_write='' + + +setsebool -P smbd_anon_write $var_smbd_anon_write + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the spamassassin_can_network SELinux Boolean + By default, the SELinux boolean spamassassin_can_network is disabled. +If this setting is enabled, it should be disabled. + +To disable the spamassassin_can_network SELinux boolean, run the following command: +$ sudo setsebool -P spamassassin_can_network off + + - name: XCCDF Value var_spamassassin_can_network # promote to variable + set_fact: + var_spamassassin_can_network: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_spamassassin_can_network + +- name: Set SELinux boolean spamassassin_can_network accordingly + seboolean: + name: spamassassin_can_network + state: '{{ var_spamassassin_can_network }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_spamassassin_can_network + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_spamassassin_can_network='' + + +setsebool -P spamassassin_can_network $var_spamassassin_can_network + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable the spamd_enable_home_dirs SELinux Boolean + By default, the SELinux boolean spamd_enable_home_dirs is enabled. +If this setting is disabled, it should be enabled. + +To enable the spamd_enable_home_dirs SELinux boolean, run the following command: +$ sudo setsebool -P spamd_enable_home_dirs on + + - name: XCCDF Value var_spamd_enable_home_dirs # promote to variable + set_fact: + var_spamd_enable_home_dirs: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_spamd_enable_home_dirs + +- name: Set SELinux boolean spamd_enable_home_dirs accordingly + seboolean: + name: spamd_enable_home_dirs + state: '{{ var_spamd_enable_home_dirs }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_spamd_enable_home_dirs + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_spamd_enable_home_dirs='' + + +setsebool -P spamd_enable_home_dirs $var_spamd_enable_home_dirs + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the squid_connect_any SELinux Boolean + By default, the SELinux boolean squid_connect_any is enabled. +This setting should be disabled as squid should only connect on specified +ports. + +To disable the squid_connect_any SELinux boolean, run the following command: +$ sudo setsebool -P squid_connect_any off + + - name: XCCDF Value var_squid_connect_any # promote to variable + set_fact: + var_squid_connect_any: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_squid_connect_any + +- name: Set SELinux boolean squid_connect_any accordingly + seboolean: + name: squid_connect_any + state: '{{ var_squid_connect_any }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_squid_connect_any + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_squid_connect_any='' + + +setsebool -P squid_connect_any $var_squid_connect_any + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the squid_use_tproxy SELinux Boolean + By default, the SELinux boolean squid_use_tproxy is disabled. +If this setting is enabled, it should be disabled. + +To disable the squid_use_tproxy SELinux boolean, run the following command: +$ sudo setsebool -P squid_use_tproxy off + + - name: XCCDF Value var_squid_use_tproxy # promote to variable + set_fact: + var_squid_use_tproxy: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_squid_use_tproxy + +- name: Set SELinux boolean squid_use_tproxy accordingly + seboolean: + name: squid_use_tproxy + state: '{{ var_squid_use_tproxy }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_squid_use_tproxy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_squid_use_tproxy='' + + +setsebool -P squid_use_tproxy $var_squid_use_tproxy + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the ssh_chroot_rw_homedirs SELinux Boolean + By default, the SELinux boolean ssh_chroot_rw_homedirs is disabled. +If this setting is enabled, it should be disabled. + +To disable the ssh_chroot_rw_homedirs SELinux boolean, run the following command: +$ sudo setsebool -P ssh_chroot_rw_homedirs off + + - name: XCCDF Value var_ssh_chroot_rw_homedirs # promote to variable + set_fact: + var_ssh_chroot_rw_homedirs: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_ssh_chroot_rw_homedirs + +- name: Set SELinux boolean ssh_chroot_rw_homedirs accordingly + seboolean: + name: ssh_chroot_rw_homedirs + state: '{{ var_ssh_chroot_rw_homedirs }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_ssh_chroot_rw_homedirs + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_ssh_chroot_rw_homedirs='' + + +setsebool -P ssh_chroot_rw_homedirs $var_ssh_chroot_rw_homedirs + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the ssh_keysign SELinux Boolean + By default, the SELinux boolean ssh_keysign is disabled. +If this setting is enabled, it should be disabled. + +To disable the ssh_keysign SELinux boolean, run the following command: +$ sudo setsebool -P ssh_keysign off + + - name: XCCDF Value var_ssh_keysign # promote to variable + set_fact: + var_ssh_keysign: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_ssh_keysign + +- name: Set SELinux boolean ssh_keysign accordingly + seboolean: + name: ssh_keysign + state: '{{ var_ssh_keysign }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_ssh_keysign + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_ssh_keysign='' + + +setsebool -P ssh_keysign $var_ssh_keysign + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the ssh_sysadm_login SELinux Boolean + By default, the SELinux boolean ssh_sysadm_login is disabled. +If this setting is enabled, it should be disabled. + +To disable the ssh_sysadm_login SELinux boolean, run the following command: +$ sudo setsebool -P ssh_sysadm_login off + BP28(R67) + CCI-002165 + CCI-002235 + SRG-OS-000324-GPOS-00125 + Preventing non-privileged users from executing privileged functions mitigates +the risk that unauthorized individuals or processes may gain unnecessary access +to information or privileges. + +Privileged functions include, for example, establishing accounts, performing +system integrity checks, or administering cryptographic key management +activities. Non-privileged users are individuals who do not possess appropriate +authorizations. Circumventing intrusion detection and prevention mechanisms or +malicious code protection mechanisms are examples of privileged functions that +require protection from non-privileged users. + CCE-84081-9 + - name: XCCDF Value var_ssh_sysadm_login # promote to variable + set_fact: + var_ssh_sysadm_login: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84081-9 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_ssh_sysadm_login + +- name: Set SELinux boolean ssh_sysadm_login accordingly + seboolean: + name: ssh_sysadm_login + state: '{{ var_ssh_sysadm_login }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84081-9 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_ssh_sysadm_login + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_ssh_sysadm_login='' + + +setsebool -P ssh_sysadm_login $var_ssh_sysadm_login + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable the staff_exec_content SELinux Boolean + By default, the SELinux boolean staff_exec_content is enabled. +If this setting is disabled, it should be enabled. + +To enable the staff_exec_content SELinux boolean, run the following command: +$ sudo setsebool -P staff_exec_content on + + - name: XCCDF Value var_staff_exec_content # promote to variable + set_fact: + var_staff_exec_content: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_staff_exec_content + +- name: Set SELinux boolean staff_exec_content accordingly + seboolean: + name: staff_exec_content + state: '{{ var_staff_exec_content }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_staff_exec_content + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_staff_exec_content='' + + +setsebool -P staff_exec_content $var_staff_exec_content + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the staff_use_svirt SELinux Boolean + By default, the SELinux boolean staff_use_svirt is disabled. +If this setting is enabled, it should be disabled. + +To disable the staff_use_svirt SELinux boolean, run the following command: +$ sudo setsebool -P staff_use_svirt off + + - name: XCCDF Value var_staff_use_svirt # promote to variable + set_fact: + var_staff_use_svirt: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_staff_use_svirt + +- name: Set SELinux boolean staff_use_svirt accordingly + seboolean: + name: staff_use_svirt + state: '{{ var_staff_use_svirt }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_staff_use_svirt + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_staff_use_svirt='' + + +setsebool -P staff_use_svirt $var_staff_use_svirt + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the swift_can_network SELinux Boolean + By default, the SELinux boolean swift_can_network is disabled. +If this setting is enabled, it should be disabled. + +To disable the swift_can_network SELinux boolean, run the following command: +$ sudo setsebool -P swift_can_network off + + - name: XCCDF Value var_swift_can_network # promote to variable + set_fact: + var_swift_can_network: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_swift_can_network + +- name: Set SELinux boolean swift_can_network accordingly + seboolean: + name: swift_can_network + state: '{{ var_swift_can_network }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_swift_can_network + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_swift_can_network='' + + +setsebool -P swift_can_network $var_swift_can_network + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable the sysadm_exec_content SELinux Boolean + By default, the SELinux boolean sysadm_exec_content is enabled. +If this setting is disabled, it should be enabled. + +To enable the sysadm_exec_content SELinux boolean, run the following command: +$ sudo setsebool -P sysadm_exec_content on + + - name: XCCDF Value var_sysadm_exec_content # promote to variable + set_fact: + var_sysadm_exec_content: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_sysadm_exec_content + +- name: Set SELinux boolean sysadm_exec_content accordingly + seboolean: + name: sysadm_exec_content + state: '{{ var_sysadm_exec_content }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_sysadm_exec_content + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_sysadm_exec_content='' + + +setsebool -P sysadm_exec_content $var_sysadm_exec_content + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the telepathy_connect_all_ports SELinux Boolean + By default, the SELinux boolean telepathy_connect_all_ports is disabled. +If this setting is enabled, it should be disabled. + +To disable the telepathy_connect_all_ports SELinux boolean, run the following command: +$ sudo setsebool -P telepathy_connect_all_ports off + + - name: XCCDF Value var_telepathy_connect_all_ports # promote to variable + set_fact: + var_telepathy_connect_all_ports: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_telepathy_connect_all_ports + +- name: Set SELinux boolean telepathy_connect_all_ports accordingly + seboolean: + name: telepathy_connect_all_ports + state: '{{ var_telepathy_connect_all_ports }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_telepathy_connect_all_ports + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_telepathy_connect_all_ports='' + + +setsebool -P telepathy_connect_all_ports $var_telepathy_connect_all_ports + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the telepathy_tcp_connect_generic_network_ports SELinux Boolean + By default, the SELinux boolean telepathy_tcp_connect_generic_network_ports is enabled. +This setting should be disabled as telepathy should not connect to any generic network +ports. + +To disable the telepathy_tcp_connect_generic_network_ports SELinux boolean, run the following command: +$ sudo setsebool -P telepathy_tcp_connect_generic_network_ports off + + - name: XCCDF Value var_telepathy_tcp_connect_generic_network_ports # promote to variable + set_fact: + var_telepathy_tcp_connect_generic_network_ports: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_telepathy_tcp_connect_generic_network_ports + +- name: Set SELinux boolean telepathy_tcp_connect_generic_network_ports accordingly + seboolean: + name: telepathy_tcp_connect_generic_network_ports + state: '{{ var_telepathy_tcp_connect_generic_network_ports }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_telepathy_tcp_connect_generic_network_ports + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_telepathy_tcp_connect_generic_network_ports='' + + +setsebool -P telepathy_tcp_connect_generic_network_ports $var_telepathy_tcp_connect_generic_network_ports + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the tftp_anon_write SELinux Boolean + By default, the SELinux boolean tftp_anon_write is disabled. +If this setting is enabled, it should be disabled. + +To disable the tftp_anon_write SELinux boolean, run the following command: +$ sudo setsebool -P tftp_anon_write off + + - name: XCCDF Value var_tftp_anon_write # promote to variable + set_fact: + var_tftp_anon_write: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_tftp_anon_write + +- name: Set SELinux boolean tftp_anon_write accordingly + seboolean: + name: tftp_anon_write + state: '{{ var_tftp_anon_write }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_tftp_anon_write + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_tftp_anon_write='' + + +setsebool -P tftp_anon_write $var_tftp_anon_write + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the tftp_home_dir SELinux Boolean + By default, the SELinux boolean tftp_home_dir is disabled. +If this setting is enabled, it should be disabled. + +To disable the tftp_home_dir SELinux boolean, run the following command: +$ sudo setsebool -P tftp_home_dir off + + - name: XCCDF Value var_tftp_home_dir # promote to variable + set_fact: + var_tftp_home_dir: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_tftp_home_dir + +- name: Set SELinux boolean tftp_home_dir accordingly + seboolean: + name: tftp_home_dir + state: '{{ var_tftp_home_dir }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_tftp_home_dir + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_tftp_home_dir='' + + +setsebool -P tftp_home_dir $var_tftp_home_dir + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the tmpreaper_use_nfs SELinux Boolean + By default, the SELinux boolean tmpreaper_use_nfs is disabled. +If this setting is enabled, it should be disabled. + +To disable the tmpreaper_use_nfs SELinux boolean, run the following command: +$ sudo setsebool -P tmpreaper_use_nfs off + + - name: XCCDF Value var_tmpreaper_use_nfs # promote to variable + set_fact: + var_tmpreaper_use_nfs: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_tmpreaper_use_nfs + +- name: Set SELinux boolean tmpreaper_use_nfs accordingly + seboolean: + name: tmpreaper_use_nfs + state: '{{ var_tmpreaper_use_nfs }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_tmpreaper_use_nfs + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_tmpreaper_use_nfs='' + + +setsebool -P tmpreaper_use_nfs $var_tmpreaper_use_nfs + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the tmpreaper_use_samba SELinux Boolean + By default, the SELinux boolean tmpreaper_use_samba is disabled. +If this setting is enabled, it should be disabled. + +To disable the tmpreaper_use_samba SELinux boolean, run the following command: +$ sudo setsebool -P tmpreaper_use_samba off + + - name: XCCDF Value var_tmpreaper_use_samba # promote to variable + set_fact: + var_tmpreaper_use_samba: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_tmpreaper_use_samba + +- name: Set SELinux boolean tmpreaper_use_samba accordingly + seboolean: + name: tmpreaper_use_samba + state: '{{ var_tmpreaper_use_samba }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_tmpreaper_use_samba + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_tmpreaper_use_samba='' + + +setsebool -P tmpreaper_use_samba $var_tmpreaper_use_samba + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the tor_bind_all_unreserved_ports SELinux Boolean + By default, the SELinux boolean tor_bind_all_unreserved_ports is disabled. +If this setting is enabled, it should be disabled. + +To disable the tor_bind_all_unreserved_ports SELinux boolean, run the following command: +$ sudo setsebool -P tor_bind_all_unreserved_ports off + + - name: XCCDF Value var_tor_bind_all_unreserved_ports # promote to variable + set_fact: + var_tor_bind_all_unreserved_ports: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_tor_bind_all_unreserved_ports + +- name: Set SELinux boolean tor_bind_all_unreserved_ports accordingly + seboolean: + name: tor_bind_all_unreserved_ports + state: '{{ var_tor_bind_all_unreserved_ports }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_tor_bind_all_unreserved_ports + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_tor_bind_all_unreserved_ports='' + + +setsebool -P tor_bind_all_unreserved_ports $var_tor_bind_all_unreserved_ports + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the tor_can_network_relay SELinux Boolean + By default, the SELinux boolean tor_can_network_relay is disabled. +If this setting is enabled, it should be disabled. + +To disable the tor_can_network_relay SELinux boolean, run the following command: +$ sudo setsebool -P tor_can_network_relay off + + - name: XCCDF Value var_tor_can_network_relay # promote to variable + set_fact: + var_tor_can_network_relay: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_tor_can_network_relay + +- name: Set SELinux boolean tor_can_network_relay accordingly + seboolean: + name: tor_can_network_relay + state: '{{ var_tor_can_network_relay }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_tor_can_network_relay + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_tor_can_network_relay='' + + +setsebool -P tor_can_network_relay $var_tor_can_network_relay + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable the unconfined_chrome_sandbox_transition SELinux Boolean + By default, the SELinux boolean unconfined_chrome_sandbox_transition is enabled. +If this setting is disabled, it should be enabled. + +To enable the unconfined_chrome_sandbox_transition SELinux boolean, run the following command: +$ sudo setsebool -P unconfined_chrome_sandbox_transition on + + - name: XCCDF Value var_unconfined_chrome_sandbox_transition # promote to variable + set_fact: + var_unconfined_chrome_sandbox_transition: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_unconfined_chrome_sandbox_transition + +- name: Set SELinux boolean unconfined_chrome_sandbox_transition accordingly + seboolean: + name: unconfined_chrome_sandbox_transition + state: '{{ var_unconfined_chrome_sandbox_transition }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_unconfined_chrome_sandbox_transition + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_unconfined_chrome_sandbox_transition='' + + +setsebool -P unconfined_chrome_sandbox_transition $var_unconfined_chrome_sandbox_transition + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable the unconfined_login SELinux Boolean + By default, the SELinux boolean unconfined_login is enabled. +If this setting is disabled, it should be enabled. + +To enable the unconfined_login SELinux boolean, run the following command: +$ sudo setsebool -P unconfined_login on + + - name: XCCDF Value var_unconfined_login # promote to variable + set_fact: + var_unconfined_login: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_unconfined_login + +- name: Set SELinux boolean unconfined_login accordingly + seboolean: + name: unconfined_login + state: '{{ var_unconfined_login }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_unconfined_login + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_unconfined_login='' + + +setsebool -P unconfined_login $var_unconfined_login + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable the unconfined_mozilla_plugin_transition SELinux Boolean + By default, the SELinux boolean unconfined_mozilla_plugin_transition is enabled. +If this setting is disabled, it should be enabled. + +To enable the unconfined_mozilla_plugin_transition SELinux boolean, run the following command: +$ sudo setsebool -P unconfined_mozilla_plugin_transition on + + - name: XCCDF Value var_unconfined_mozilla_plugin_transition # promote to variable + set_fact: + var_unconfined_mozilla_plugin_transition: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_unconfined_mozilla_plugin_transition + +- name: Set SELinux boolean unconfined_mozilla_plugin_transition accordingly + seboolean: + name: unconfined_mozilla_plugin_transition + state: '{{ var_unconfined_mozilla_plugin_transition }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_unconfined_mozilla_plugin_transition + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_unconfined_mozilla_plugin_transition='' + + +setsebool -P unconfined_mozilla_plugin_transition $var_unconfined_mozilla_plugin_transition + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the unprivuser_use_svirt SELinux Boolean + By default, the SELinux boolean unprivuser_use_svirt is disabled. +If this setting is enabled, it should be disabled. + +To disable the unprivuser_use_svirt SELinux boolean, run the following command: +$ sudo setsebool -P unprivuser_use_svirt off + + - name: XCCDF Value var_unprivuser_use_svirt # promote to variable + set_fact: + var_unprivuser_use_svirt: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_unprivuser_use_svirt + +- name: Set SELinux boolean unprivuser_use_svirt accordingly + seboolean: + name: unprivuser_use_svirt + state: '{{ var_unprivuser_use_svirt }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_unprivuser_use_svirt + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_unprivuser_use_svirt='' + + +setsebool -P unprivuser_use_svirt $var_unprivuser_use_svirt + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the use_ecryptfs_home_dirs SELinux Boolean + By default, the SELinux boolean use_ecryptfs_home_dirs is disabled. +If this setting is enabled, it should be disabled. + +To disable the use_ecryptfs_home_dirs SELinux boolean, run the following command: +$ sudo setsebool -P use_ecryptfs_home_dirs off + + - name: XCCDF Value var_use_ecryptfs_home_dirs # promote to variable + set_fact: + var_use_ecryptfs_home_dirs: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_use_ecryptfs_home_dirs + +- name: Set SELinux boolean use_ecryptfs_home_dirs accordingly + seboolean: + name: use_ecryptfs_home_dirs + state: '{{ var_use_ecryptfs_home_dirs }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_use_ecryptfs_home_dirs + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_use_ecryptfs_home_dirs='' + + +setsebool -P use_ecryptfs_home_dirs $var_use_ecryptfs_home_dirs + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the use_fusefs_home_dirs SELinux Boolean + By default, the SELinux boolean use_fusefs_home_dirs is disabled. +If this setting is enabled, it should be disabled. + +To disable the use_fusefs_home_dirs SELinux boolean, run the following command: +$ sudo setsebool -P use_fusefs_home_dirs off + + - name: XCCDF Value var_use_fusefs_home_dirs # promote to variable + set_fact: + var_use_fusefs_home_dirs: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_use_fusefs_home_dirs + +- name: Set SELinux boolean use_fusefs_home_dirs accordingly + seboolean: + name: use_fusefs_home_dirs + state: '{{ var_use_fusefs_home_dirs }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_use_fusefs_home_dirs + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_use_fusefs_home_dirs='' + + +setsebool -P use_fusefs_home_dirs $var_use_fusefs_home_dirs + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the use_lpd_server SELinux Boolean + By default, the SELinux boolean use_lpd_server is disabled. +If this setting is enabled, it should be disabled. + +To disable the use_lpd_server SELinux boolean, run the following command: +$ sudo setsebool -P use_lpd_server off + + - name: XCCDF Value var_use_lpd_server # promote to variable + set_fact: + var_use_lpd_server: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_use_lpd_server + +- name: Set SELinux boolean use_lpd_server accordingly + seboolean: + name: use_lpd_server + state: '{{ var_use_lpd_server }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_use_lpd_server + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_use_lpd_server='' + + +setsebool -P use_lpd_server $var_use_lpd_server + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the use_nfs_home_dirs SELinux Boolean + By default, the SELinux boolean use_nfs_home_dirs is disabled. +If this setting is enabled, it should be disabled. + +To disable the use_nfs_home_dirs SELinux boolean, run the following command: +$ sudo setsebool -P use_nfs_home_dirs off + + - name: XCCDF Value var_use_nfs_home_dirs # promote to variable + set_fact: + var_use_nfs_home_dirs: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_use_nfs_home_dirs + +- name: Set SELinux boolean use_nfs_home_dirs accordingly + seboolean: + name: use_nfs_home_dirs + state: '{{ var_use_nfs_home_dirs }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_use_nfs_home_dirs + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_use_nfs_home_dirs='' + + +setsebool -P use_nfs_home_dirs $var_use_nfs_home_dirs + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the use_samba_home_dirs SELinux Boolean + By default, the SELinux boolean use_samba_home_dirs is disabled. +If this setting is enabled, it should be disabled. + +To disable the use_samba_home_dirs SELinux boolean, run the following command: +$ sudo setsebool -P use_samba_home_dirs off + + - name: XCCDF Value var_use_samba_home_dirs # promote to variable + set_fact: + var_use_samba_home_dirs: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_use_samba_home_dirs + +- name: Set SELinux boolean use_samba_home_dirs accordingly + seboolean: + name: use_samba_home_dirs + state: '{{ var_use_samba_home_dirs }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_use_samba_home_dirs + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_use_samba_home_dirs='' + + +setsebool -P use_samba_home_dirs $var_use_samba_home_dirs + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable the user_exec_content SELinux Boolean + By default, the SELinux boolean user_exec_content is enabled. +If this setting is disabled, it should be enabled. + +To enable the user_exec_content SELinux boolean, run the following command: +$ sudo setsebool -P user_exec_content on + + - name: XCCDF Value var_user_exec_content # promote to variable + set_fact: + var_user_exec_content: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_user_exec_content + +- name: Set SELinux boolean user_exec_content accordingly + seboolean: + name: user_exec_content + state: '{{ var_user_exec_content }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_user_exec_content + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_user_exec_content='' + + +setsebool -P user_exec_content $var_user_exec_content + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the varnishd_connect_any SELinux Boolean + By default, the SELinux boolean varnishd_connect_any is disabled. +If this setting is enabled, it should be disabled. + +To disable the varnishd_connect_any SELinux boolean, run the following command: +$ sudo setsebool -P varnishd_connect_any off + + - name: XCCDF Value var_varnishd_connect_any # promote to variable + set_fact: + var_varnishd_connect_any: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_varnishd_connect_any + +- name: Set SELinux boolean varnishd_connect_any accordingly + seboolean: + name: varnishd_connect_any + state: '{{ var_varnishd_connect_any }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_varnishd_connect_any + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_varnishd_connect_any='' + + +setsebool -P varnishd_connect_any $var_varnishd_connect_any + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the virt_read_qemu_ga_data SELinux Boolean + By default, the SELinux boolean virt_read_qemu_ga_data is disabled. +If this setting is enabled, it should be disabled. + +To disable the virt_read_qemu_ga_data SELinux boolean, run the following command: +$ sudo setsebool -P virt_read_qemu_ga_data off + + - name: XCCDF Value var_virt_read_qemu_ga_data # promote to variable + set_fact: + var_virt_read_qemu_ga_data: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_virt_read_qemu_ga_data + +- name: Set SELinux boolean virt_read_qemu_ga_data accordingly + seboolean: + name: virt_read_qemu_ga_data + state: '{{ var_virt_read_qemu_ga_data }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_virt_read_qemu_ga_data + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_virt_read_qemu_ga_data='' + + +setsebool -P virt_read_qemu_ga_data $var_virt_read_qemu_ga_data + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the virt_rw_qemu_ga_data SELinux Boolean + By default, the SELinux boolean virt_rw_qemu_ga_data is disabled. +If this setting is enabled, it should be disabled. + +To disable the virt_rw_qemu_ga_data SELinux boolean, run the following command: +$ sudo setsebool -P virt_rw_qemu_ga_data off + + - name: XCCDF Value var_virt_rw_qemu_ga_data # promote to variable + set_fact: + var_virt_rw_qemu_ga_data: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_virt_rw_qemu_ga_data + +- name: Set SELinux boolean virt_rw_qemu_ga_data accordingly + seboolean: + name: virt_rw_qemu_ga_data + state: '{{ var_virt_rw_qemu_ga_data }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_virt_rw_qemu_ga_data + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_virt_rw_qemu_ga_data='' + + +setsebool -P virt_rw_qemu_ga_data $var_virt_rw_qemu_ga_data + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the virt_sandbox_use_all_caps SELinux Boolean + By default, the SELinux boolean virt_sandbox_use_all_caps is enabled. +This setting is disabled as containers should not run with privileges. + +To disable the virt_sandbox_use_all_caps SELinux boolean, run the following command: +$ sudo setsebool -P virt_sandbox_use_all_caps off + + - name: XCCDF Value var_virt_sandbox_use_all_caps # promote to variable + set_fact: + var_virt_sandbox_use_all_caps: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_virt_sandbox_use_all_caps + +- name: Set SELinux boolean virt_sandbox_use_all_caps accordingly + seboolean: + name: virt_sandbox_use_all_caps + state: '{{ var_virt_sandbox_use_all_caps }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_virt_sandbox_use_all_caps + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_virt_sandbox_use_all_caps='' + + +setsebool -P virt_sandbox_use_all_caps $var_virt_sandbox_use_all_caps + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable the virt_sandbox_use_audit SELinux Boolean + By default, the SELinux boolean virt_sandbox_use_audit is enabled. +If this setting is disabled, it should be enabled to allow sandboxed containers +to send audit messages. + +To enable the virt_sandbox_use_audit SELinux boolean, run the following command: +$ sudo setsebool -P virt_sandbox_use_audit on + + - name: XCCDF Value var_virt_sandbox_use_audit # promote to variable + set_fact: + var_virt_sandbox_use_audit: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_virt_sandbox_use_audit + +- name: Set SELinux boolean virt_sandbox_use_audit accordingly + seboolean: + name: virt_sandbox_use_audit + state: '{{ var_virt_sandbox_use_audit }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_virt_sandbox_use_audit + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_virt_sandbox_use_audit='' + + +setsebool -P virt_sandbox_use_audit $var_virt_sandbox_use_audit + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the virt_sandbox_use_mknod SELinux Boolean + By default, the SELinux boolean virt_sandbox_use_mknod is disabled. +If this setting is enabled, it should be disabled. + +To disable the virt_sandbox_use_mknod SELinux boolean, run the following command: +$ sudo setsebool -P virt_sandbox_use_mknod off + + - name: XCCDF Value var_virt_sandbox_use_mknod # promote to variable + set_fact: + var_virt_sandbox_use_mknod: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_virt_sandbox_use_mknod + +- name: Set SELinux boolean virt_sandbox_use_mknod accordingly + seboolean: + name: virt_sandbox_use_mknod + state: '{{ var_virt_sandbox_use_mknod }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_virt_sandbox_use_mknod + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_virt_sandbox_use_mknod='' + + +setsebool -P virt_sandbox_use_mknod $var_virt_sandbox_use_mknod + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the virt_sandbox_use_netlink SELinux Boolean + By default, the SELinux boolean virt_sandbox_use_netlink is disabled. +If this setting is enabled, it should be disabled. + +To disable the virt_sandbox_use_netlink SELinux boolean, run the following command: +$ sudo setsebool -P virt_sandbox_use_netlink off + + - name: XCCDF Value var_virt_sandbox_use_netlink # promote to variable + set_fact: + var_virt_sandbox_use_netlink: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_virt_sandbox_use_netlink + +- name: Set SELinux boolean virt_sandbox_use_netlink accordingly + seboolean: + name: virt_sandbox_use_netlink + state: '{{ var_virt_sandbox_use_netlink }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_virt_sandbox_use_netlink + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_virt_sandbox_use_netlink='' + + +setsebool -P virt_sandbox_use_netlink $var_virt_sandbox_use_netlink + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the virt_sandbox_use_sys_admin SELinux Boolean + By default, the SELinux boolean virt_sandbox_use_sys_admin is disabled. +If this setting is enabled, it should be disabled. + +To disable the virt_sandbox_use_sys_admin SELinux boolean, run the following command: +$ sudo setsebool -P virt_sandbox_use_sys_admin off + + - name: XCCDF Value var_virt_sandbox_use_sys_admin # promote to variable + set_fact: + var_virt_sandbox_use_sys_admin: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_virt_sandbox_use_sys_admin + +- name: Set SELinux boolean virt_sandbox_use_sys_admin accordingly + seboolean: + name: virt_sandbox_use_sys_admin + state: '{{ var_virt_sandbox_use_sys_admin }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_virt_sandbox_use_sys_admin + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_virt_sandbox_use_sys_admin='' + + +setsebool -P virt_sandbox_use_sys_admin $var_virt_sandbox_use_sys_admin + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the virt_transition_userdomain SELinux Boolean + By default, the SELinux boolean virt_transition_userdomain is disabled. +If this setting is enabled, it should be disabled. + +To disable the virt_transition_userdomain SELinux boolean, run the following command: +$ sudo setsebool -P virt_transition_userdomain off + + - name: XCCDF Value var_virt_transition_userdomain # promote to variable + set_fact: + var_virt_transition_userdomain: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_virt_transition_userdomain + +- name: Set SELinux boolean virt_transition_userdomain accordingly + seboolean: + name: virt_transition_userdomain + state: '{{ var_virt_transition_userdomain }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_virt_transition_userdomain + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_virt_transition_userdomain='' + + +setsebool -P virt_transition_userdomain $var_virt_transition_userdomain + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the virt_use_comm SELinux Boolean + By default, the SELinux boolean virt_use_comm is disabled. +If this setting is enabled, it should be disabled. + +To disable the virt_use_comm SELinux boolean, run the following command: +$ sudo setsebool -P virt_use_comm off + + - name: XCCDF Value var_virt_use_comm # promote to variable + set_fact: + var_virt_use_comm: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_virt_use_comm + +- name: Set SELinux boolean virt_use_comm accordingly + seboolean: + name: virt_use_comm + state: '{{ var_virt_use_comm }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_virt_use_comm + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_virt_use_comm='' + + +setsebool -P virt_use_comm $var_virt_use_comm + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the virt_use_execmem SELinux Boolean + By default, the SELinux boolean virt_use_execmem is disabled. +If this setting is enabled, it should be disabled. + +To disable the virt_use_execmem SELinux boolean, run the following command: +$ sudo setsebool -P virt_use_execmem off + BP28(R67) + + - name: XCCDF Value var_virt_use_execmem # promote to variable + set_fact: + var_virt_use_execmem: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_virt_use_execmem + +- name: Set SELinux boolean virt_use_execmem accordingly + seboolean: + name: virt_use_execmem + state: '{{ var_virt_use_execmem }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_virt_use_execmem + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_virt_use_execmem='' + + +setsebool -P virt_use_execmem $var_virt_use_execmem + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the virt_use_fusefs SELinux Boolean + By default, the SELinux boolean virt_use_fusefs is disabled. +If this setting is enabled, it should be disabled. + +To disable the virt_use_fusefs SELinux boolean, run the following command: +$ sudo setsebool -P virt_use_fusefs off + + - name: XCCDF Value var_virt_use_fusefs # promote to variable + set_fact: + var_virt_use_fusefs: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_virt_use_fusefs + +- name: Set SELinux boolean virt_use_fusefs accordingly + seboolean: + name: virt_use_fusefs + state: '{{ var_virt_use_fusefs }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_virt_use_fusefs + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_virt_use_fusefs='' + + +setsebool -P virt_use_fusefs $var_virt_use_fusefs + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the virt_use_nfs SELinux Boolean + By default, the SELinux boolean virt_use_nfs is disabled. +If this setting is enabled, it should be disabled. + +To disable the virt_use_nfs SELinux boolean, run the following command: +$ sudo setsebool -P virt_use_nfs off + + - name: XCCDF Value var_virt_use_nfs # promote to variable + set_fact: + var_virt_use_nfs: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_virt_use_nfs + +- name: Set SELinux boolean virt_use_nfs accordingly + seboolean: + name: virt_use_nfs + state: '{{ var_virt_use_nfs }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_virt_use_nfs + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_virt_use_nfs='' + + +setsebool -P virt_use_nfs $var_virt_use_nfs + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the virt_use_rawip SELinux Boolean + By default, the SELinux boolean virt_use_rawip is disabled. +If this setting is enabled, it should be disabled. + +To disable the virt_use_rawip SELinux boolean, run the following command: +$ sudo setsebool -P virt_use_rawip off + + - name: XCCDF Value var_virt_use_rawip # promote to variable + set_fact: + var_virt_use_rawip: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_virt_use_rawip + +- name: Set SELinux boolean virt_use_rawip accordingly + seboolean: + name: virt_use_rawip + state: '{{ var_virt_use_rawip }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_virt_use_rawip + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_virt_use_rawip='' + + +setsebool -P virt_use_rawip $var_virt_use_rawip + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the virt_use_samba SELinux Boolean + By default, the SELinux boolean virt_use_samba is disabled. +If this setting is enabled, it should be disabled. + +To disable the virt_use_samba SELinux boolean, run the following command: +$ sudo setsebool -P virt_use_samba off + + - name: XCCDF Value var_virt_use_samba # promote to variable + set_fact: + var_virt_use_samba: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_virt_use_samba + +- name: Set SELinux boolean virt_use_samba accordingly + seboolean: + name: virt_use_samba + state: '{{ var_virt_use_samba }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_virt_use_samba + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_virt_use_samba='' + + +setsebool -P virt_use_samba $var_virt_use_samba + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the virt_use_sanlock SELinux Boolean + By default, the SELinux boolean virt_use_sanlock is disabled. +If this setting is enabled, it should be disabled. + +To disable the virt_use_sanlock SELinux boolean, run the following command: +$ sudo setsebool -P virt_use_sanlock off + + - name: XCCDF Value var_virt_use_sanlock # promote to variable + set_fact: + var_virt_use_sanlock: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_virt_use_sanlock + +- name: Set SELinux boolean virt_use_sanlock accordingly + seboolean: + name: virt_use_sanlock + state: '{{ var_virt_use_sanlock }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_virt_use_sanlock + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_virt_use_sanlock='' + + +setsebool -P virt_use_sanlock $var_virt_use_sanlock + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the virt_use_usb SELinux Boolean + By default, the SELinux boolean virt_use_usb is enabled. +This setting should be disabled. + +To disable the virt_use_usb SELinux boolean, run the following command: +$ sudo setsebool -P virt_use_usb off + + - name: XCCDF Value var_virt_use_usb # promote to variable + set_fact: + var_virt_use_usb: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_virt_use_usb + +- name: Set SELinux boolean virt_use_usb accordingly + seboolean: + name: virt_use_usb + state: '{{ var_virt_use_usb }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_virt_use_usb + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_virt_use_usb='' + + +setsebool -P virt_use_usb $var_virt_use_usb + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the virt_use_xserver SELinux Boolean + By default, the SELinux boolean virt_use_xserver is disabled. +If this setting is enabled, it should be disabled. + +To disable the virt_use_xserver SELinux boolean, run the following command: +$ sudo setsebool -P virt_use_xserver off + + - name: XCCDF Value var_virt_use_xserver # promote to variable + set_fact: + var_virt_use_xserver: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_virt_use_xserver + +- name: Set SELinux boolean virt_use_xserver accordingly + seboolean: + name: virt_use_xserver + state: '{{ var_virt_use_xserver }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_virt_use_xserver + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_virt_use_xserver='' + + +setsebool -P virt_use_xserver $var_virt_use_xserver + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the webadm_manage_user_files SELinux Boolean + By default, the SELinux boolean webadm_manage_user_files is disabled. +If this setting is enabled, it should be disabled. + +To disable the webadm_manage_user_files SELinux boolean, run the following command: +$ sudo setsebool -P webadm_manage_user_files off + + - name: XCCDF Value var_webadm_manage_user_files # promote to variable + set_fact: + var_webadm_manage_user_files: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_webadm_manage_user_files + +- name: Set SELinux boolean webadm_manage_user_files accordingly + seboolean: + name: webadm_manage_user_files + state: '{{ var_webadm_manage_user_files }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_webadm_manage_user_files + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_webadm_manage_user_files='' + + +setsebool -P webadm_manage_user_files $var_webadm_manage_user_files + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the webadm_read_user_files SELinux Boolean + By default, the SELinux boolean webadm_read_user_files is disabled. +If this setting is enabled, it should be disabled. + +To disable the webadm_read_user_files SELinux boolean, run the following command: +$ sudo setsebool -P webadm_read_user_files off + + - name: XCCDF Value var_webadm_read_user_files # promote to variable + set_fact: + var_webadm_read_user_files: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_webadm_read_user_files + +- name: Set SELinux boolean webadm_read_user_files accordingly + seboolean: + name: webadm_read_user_files + state: '{{ var_webadm_read_user_files }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_webadm_read_user_files + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_webadm_read_user_files='' + + +setsebool -P webadm_read_user_files $var_webadm_read_user_files + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the wine_mmap_zero_ignore SELinux Boolean + By default, the SELinux boolean wine_mmap_zero_ignore is disabled. +If this setting is enabled, it should be disabled. + +To disable the wine_mmap_zero_ignore SELinux boolean, run the following command: +$ sudo setsebool -P wine_mmap_zero_ignore off + + - name: XCCDF Value var_wine_mmap_zero_ignore # promote to variable + set_fact: + var_wine_mmap_zero_ignore: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_wine_mmap_zero_ignore + +- name: Set SELinux boolean wine_mmap_zero_ignore accordingly + seboolean: + name: wine_mmap_zero_ignore + state: '{{ var_wine_mmap_zero_ignore }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_wine_mmap_zero_ignore + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_wine_mmap_zero_ignore='' + + +setsebool -P wine_mmap_zero_ignore $var_wine_mmap_zero_ignore + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the xdm_bind_vnc_tcp_port SELinux Boolean + By default, the SELinux boolean xdm_bind_vnc_tcp_port is disabled. +If this setting is enabled, it should be disabled. + +To disable the xdm_bind_vnc_tcp_port SELinux boolean, run the following command: +$ sudo setsebool -P xdm_bind_vnc_tcp_port off + + - name: XCCDF Value var_xdm_bind_vnc_tcp_port # promote to variable + set_fact: + var_xdm_bind_vnc_tcp_port: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_xdm_bind_vnc_tcp_port + +- name: Set SELinux boolean xdm_bind_vnc_tcp_port accordingly + seboolean: + name: xdm_bind_vnc_tcp_port + state: '{{ var_xdm_bind_vnc_tcp_port }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_xdm_bind_vnc_tcp_port + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_xdm_bind_vnc_tcp_port='' + + +setsebool -P xdm_bind_vnc_tcp_port $var_xdm_bind_vnc_tcp_port + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the xdm_exec_bootloader SELinux Boolean + By default, the SELinux boolean xdm_exec_bootloader is disabled. +If this setting is enabled, it should be disabled. + +To disable the xdm_exec_bootloader SELinux boolean, run the following command: +$ sudo setsebool -P xdm_exec_bootloader off + + - name: XCCDF Value var_xdm_exec_bootloader # promote to variable + set_fact: + var_xdm_exec_bootloader: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_xdm_exec_bootloader + +- name: Set SELinux boolean xdm_exec_bootloader accordingly + seboolean: + name: xdm_exec_bootloader + state: '{{ var_xdm_exec_bootloader }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_xdm_exec_bootloader + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_xdm_exec_bootloader='' + + +setsebool -P xdm_exec_bootloader $var_xdm_exec_bootloader + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the xdm_sysadm_login SELinux Boolean + By default, the SELinux boolean xdm_sysadm_login is disabled. +If this setting is enabled, it should be disabled. + +To disable the xdm_sysadm_login SELinux boolean, run the following command: +$ sudo setsebool -P xdm_sysadm_login off + + - name: XCCDF Value var_xdm_sysadm_login # promote to variable + set_fact: + var_xdm_sysadm_login: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_xdm_sysadm_login + +- name: Set SELinux boolean xdm_sysadm_login accordingly + seboolean: + name: xdm_sysadm_login + state: '{{ var_xdm_sysadm_login }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_xdm_sysadm_login + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_xdm_sysadm_login='' + + +setsebool -P xdm_sysadm_login $var_xdm_sysadm_login + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the xdm_write_home SELinux Boolean + By default, the SELinux boolean xdm_write_home is disabled. +If this setting is enabled, it should be disabled. + +To disable the xdm_write_home SELinux boolean, run the following command: +$ sudo setsebool -P xdm_write_home off + + - name: XCCDF Value var_xdm_write_home # promote to variable + set_fact: + var_xdm_write_home: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_xdm_write_home + +- name: Set SELinux boolean xdm_write_home accordingly + seboolean: + name: xdm_write_home + state: '{{ var_xdm_write_home }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_xdm_write_home + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_xdm_write_home='' + + +setsebool -P xdm_write_home $var_xdm_write_home + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the xen_use_nfs SELinux Boolean + By default, the SELinux boolean xen_use_nfs is disabled. +If this setting is enabled, it should be disabled. + +To disable the xen_use_nfs SELinux boolean, run the following command: +$ sudo setsebool -P xen_use_nfs off + + - name: XCCDF Value var_xen_use_nfs # promote to variable + set_fact: + var_xen_use_nfs: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_xen_use_nfs + +- name: Set SELinux boolean xen_use_nfs accordingly + seboolean: + name: xen_use_nfs + state: '{{ var_xen_use_nfs }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_xen_use_nfs + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_xen_use_nfs='' + + +setsebool -P xen_use_nfs $var_xen_use_nfs + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable the xend_run_blktap SELinux Boolean + By default, the SELinux boolean xend_run_blktap is enabled. +If this setting is disabled, it should be enabled. + +To enable the xend_run_blktap SELinux boolean, run the following command: +$ sudo setsebool -P xend_run_blktap on + + - name: XCCDF Value var_xend_run_blktap # promote to variable + set_fact: + var_xend_run_blktap: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_xend_run_blktap + +- name: Set SELinux boolean xend_run_blktap accordingly + seboolean: + name: xend_run_blktap + state: '{{ var_xend_run_blktap }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_xend_run_blktap + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_xend_run_blktap='' + + +setsebool -P xend_run_blktap $var_xend_run_blktap + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable the xend_run_qemu SELinux Boolean + By default, the SELinux boolean xend_run_qemu is enabled. +If this setting is disabled, it should be enabled. + +To enable the xend_run_qemu SELinux boolean, run the following command: +$ sudo setsebool -P xend_run_qemu on + + - name: XCCDF Value var_xend_run_qemu # promote to variable + set_fact: + var_xend_run_qemu: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_xend_run_qemu + +- name: Set SELinux boolean xend_run_qemu accordingly + seboolean: + name: xend_run_qemu + state: '{{ var_xend_run_qemu }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_xend_run_qemu + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_xend_run_qemu='' + + +setsebool -P xend_run_qemu $var_xend_run_qemu + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the xguest_connect_network SELinux Boolean + By default, the SELinux boolean xguest_connect_network is enabled. +This setting should be disabled as guest users should not be able to configure +NetworkManager. + +To disable the xguest_connect_network SELinux boolean, run the following command: +$ sudo setsebool -P xguest_connect_network off + + - name: XCCDF Value var_xguest_connect_network # promote to variable + set_fact: + var_xguest_connect_network: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_xguest_connect_network + +- name: Set SELinux boolean xguest_connect_network accordingly + seboolean: + name: xguest_connect_network + state: '{{ var_xguest_connect_network }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_xguest_connect_network + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_xguest_connect_network='' + + +setsebool -P xguest_connect_network $var_xguest_connect_network + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the xguest_exec_content SELinux Boolean + By default, the SELinux boolean xguest_exec_content is enabled. +This setting should be disabled as guest users should not be able to run +executables. + +To disable the xguest_exec_content SELinux boolean, run the following command: +$ sudo setsebool -P xguest_exec_content off + + - name: XCCDF Value var_xguest_exec_content # promote to variable + set_fact: + var_xguest_exec_content: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_xguest_exec_content + +- name: Set SELinux boolean xguest_exec_content accordingly + seboolean: + name: xguest_exec_content + state: '{{ var_xguest_exec_content }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_xguest_exec_content + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_xguest_exec_content='' + + +setsebool -P xguest_exec_content $var_xguest_exec_content + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the xguest_mount_media SELinux Boolean + By default, the SELinux boolean xguest_mount_media is enabled. +This setting should be disabled as guest users should not be able to mount +any media. + +To disable the xguest_mount_media SELinux boolean, run the following command: +$ sudo setsebool -P xguest_mount_media off + + - name: XCCDF Value var_xguest_mount_media # promote to variable + set_fact: + var_xguest_mount_media: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_xguest_mount_media + +- name: Set SELinux boolean xguest_mount_media accordingly + seboolean: + name: xguest_mount_media + state: '{{ var_xguest_mount_media }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_xguest_mount_media + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_xguest_mount_media='' + + +setsebool -P xguest_mount_media $var_xguest_mount_media + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the xguest_use_bluetooth SELinux Boolean + By default, the SELinux boolean xguest_use_bluetooth is enabled. +This setting should be disabled as guests users should not be able to access +or use bluetooth. + +To disable the xguest_use_bluetooth SELinux boolean, run the following command: +$ sudo setsebool -P xguest_use_bluetooth off + + - name: XCCDF Value var_xguest_use_bluetooth # promote to variable + set_fact: + var_xguest_use_bluetooth: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_xguest_use_bluetooth + +- name: Set SELinux boolean xguest_use_bluetooth accordingly + seboolean: + name: xguest_use_bluetooth + state: '{{ var_xguest_use_bluetooth }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_xguest_use_bluetooth + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_xguest_use_bluetooth='' + + +setsebool -P xguest_use_bluetooth $var_xguest_use_bluetooth + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the xserver_clients_write_xshm SELinux Boolean + By default, the SELinux boolean xserver_clients_write_xshm is disabled. +If this setting is enabled, it should be disabled. + +To disable the xserver_clients_write_xshm SELinux boolean, run the following command: +$ sudo setsebool -P xserver_clients_write_xshm off + + - name: XCCDF Value var_xserver_clients_write_xshm # promote to variable + set_fact: + var_xserver_clients_write_xshm: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_xserver_clients_write_xshm + +- name: Set SELinux boolean xserver_clients_write_xshm accordingly + seboolean: + name: xserver_clients_write_xshm + state: '{{ var_xserver_clients_write_xshm }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_xserver_clients_write_xshm + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_xserver_clients_write_xshm='' + + +setsebool -P xserver_clients_write_xshm $var_xserver_clients_write_xshm + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the xserver_execmem SELinux Boolean + By default, the SELinux boolean xserver_execmem is disabled. +If this setting is enabled, it should be disabled. + +To disable the xserver_execmem SELinux boolean, run the following command: +$ sudo setsebool -P xserver_execmem off + BP28(R67) + + - name: XCCDF Value var_xserver_execmem # promote to variable + set_fact: + var_xserver_execmem: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_xserver_execmem + +- name: Set SELinux boolean xserver_execmem accordingly + seboolean: + name: xserver_execmem + state: '{{ var_xserver_execmem }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_xserver_execmem + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_xserver_execmem='' + + +setsebool -P xserver_execmem $var_xserver_execmem + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the xserver_object_manager SELinux Boolean + By default, the SELinux boolean xserver_object_manager is disabled. +If this setting is enabled, it should be disabled. + +To disable the xserver_object_manager SELinux boolean, run the following command: +$ sudo setsebool -P xserver_object_manager off + + - name: XCCDF Value var_xserver_object_manager # promote to variable + set_fact: + var_xserver_object_manager: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_xserver_object_manager + +- name: Set SELinux boolean xserver_object_manager accordingly + seboolean: + name: xserver_object_manager + state: '{{ var_xserver_object_manager }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_xserver_object_manager + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_xserver_object_manager='' + + +setsebool -P xserver_object_manager $var_xserver_object_manager + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the zabbix_can_network SELinux Boolean + By default, the SELinux boolean zabbix_can_network is disabled. +If this setting is enabled, it should be disabled. + +To disable the zabbix_can_network SELinux boolean, run the following command: +$ sudo setsebool -P zabbix_can_network off + + - name: XCCDF Value var_zabbix_can_network # promote to variable + set_fact: + var_zabbix_can_network: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_zabbix_can_network + +- name: Set SELinux boolean zabbix_can_network accordingly + seboolean: + name: zabbix_can_network + state: '{{ var_zabbix_can_network }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_zabbix_can_network + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_zabbix_can_network='' + + +setsebool -P zabbix_can_network $var_zabbix_can_network + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the zarafa_setrlimit SELinux Boolean + By default, the SELinux boolean zarafa_setrlimit is disabled. +If this setting is enabled, it should be disabled. + +To disable the zarafa_setrlimit SELinux boolean, run the following command: +$ sudo setsebool -P zarafa_setrlimit off + + - name: XCCDF Value var_zarafa_setrlimit # promote to variable + set_fact: + var_zarafa_setrlimit: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_zarafa_setrlimit + +- name: Set SELinux boolean zarafa_setrlimit accordingly + seboolean: + name: zarafa_setrlimit + state: '{{ var_zarafa_setrlimit }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_zarafa_setrlimit + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_zarafa_setrlimit='' + + +setsebool -P zarafa_setrlimit $var_zarafa_setrlimit + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the zebra_write_config SELinux Boolean + By default, the SELinux boolean zebra_write_config is disabled. +If this setting is enabled, it should be disabled. + +To disable the zebra_write_config SELinux boolean, run the following command: +$ sudo setsebool -P zebra_write_config off + + - name: XCCDF Value var_zebra_write_config # promote to variable + set_fact: + var_zebra_write_config: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_zebra_write_config + +- name: Set SELinux boolean zebra_write_config accordingly + seboolean: + name: zebra_write_config + state: '{{ var_zebra_write_config }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_zebra_write_config + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_zebra_write_config='' + + +setsebool -P zebra_write_config $var_zebra_write_config + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the zoneminder_anon_write SELinux Boolean + By default, the SELinux boolean zoneminder_anon_write is disabled. +If this setting is enabled, it should be disabled. + +To disable the zoneminder_anon_write SELinux boolean, run the following command: +$ sudo setsebool -P zoneminder_anon_write off + + - name: XCCDF Value var_zoneminder_anon_write # promote to variable + set_fact: + var_zoneminder_anon_write: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_zoneminder_anon_write + +- name: Set SELinux boolean zoneminder_anon_write accordingly + seboolean: + name: zoneminder_anon_write + state: '{{ var_zoneminder_anon_write }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_zoneminder_anon_write + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_zoneminder_anon_write='' + + +setsebool -P zoneminder_anon_write $var_zoneminder_anon_write + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable the zoneminder_run_sudo SELinux Boolean + By default, the SELinux boolean zoneminder_run_sudo is disabled. +If this setting is enabled, it should be disabled. + +To disable the zoneminder_run_sudo SELinux boolean, run the following command: +$ sudo setsebool -P zoneminder_run_sudo off + + - name: XCCDF Value var_zoneminder_run_sudo # promote to variable + set_fact: + var_zoneminder_run_sudo: !!str + tags: + - always + +- name: Ensure python3-libsemanage installed + package: + name: python3-libsemanage + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_zoneminder_run_sudo + +- name: Set SELinux boolean zoneminder_run_sudo accordingly + seboolean: + name: zoneminder_run_sudo + state: '{{ var_zoneminder_run_sudo }}' + persistent: true + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sebool_zoneminder_run_sudo + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_zoneminder_run_sudo='' + + +setsebool -P zoneminder_run_sudo $var_zoneminder_run_sudo + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + + + + Services + The best protection against vulnerable software is running less software. This section describes how to review +the software which Red Hat Enterprise Linux 9 installs on a system and disable software which is not needed. It +then enumerates the software packages installed on a default Red Hat Enterprise Linux 9 system and provides guidance about which +ones can be safely disabled. + +Red Hat Enterprise Linux 9 provides a convenient minimal install option that essentially installs the bare necessities for a functional +system. When building Red Hat Enterprise Linux 9 systems, it is highly recommended to select the minimal packages and then build up +the system from there. + + Apport Service + The Apport service provides debugging and crash reporting +features on Ubuntu distributions. + + + APT service configuration + The apt service manage the package management and update of the whole system. Its configuration need to be properly defined to ensure efficient security updates, packages and repository authentication and proper lifecycle management. + + + Avahi Server + The Avahi daemon implements the DNS Service Discovery +and Multicast DNS protocols, which provide service and host +discovery on a network. It allows a system to automatically +identify resources on the network, such as printers or web servers. +This capability is also known as mDNSresponder and is a major part +of Zeroconf networking. + + Configure Avahi if Necessary + If your system requires the Avahi daemon, its configuration can be restricted +to improve security. The Avahi daemon configuration file is +/etc/avahi/avahi-daemon.conf. The following security recommendations +should be applied to this file: +See the avahi-daemon.conf(5) man page, or documentation at + + http://www.avahi.org, for more detailed information +about the configuration options. + + Disable Avahi Publishing + To prevent Avahi from publishing its records, edit /etc/avahi/avahi-daemon.conf +and ensure the following line appears in the [publish] section: +disable-publishing=yes + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + PR.PT-3 + This helps ensure that no record will be published by Avahi. + + + + Disable Avahi Server if Possible + Because the Avahi daemon service keeps an open network +port, it is subject to network attacks. +Disabling it can reduce the system's vulnerability to such attacks. + + Disable Avahi Server Software + +The avahi-daemon service can be disabled with the following command: +$ sudo systemctl mask --now avahi-daemon.service + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + CCI-000366 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + PR.PT-3 + Because the Avahi daemon service keeps an open network +port, it is subject to network attacks. Its functionality +is convenient but is only appropriate if the local network +can be trusted. + + CCE-90824-4 + include disable_avahi-daemon + +class disable_avahi-daemon { + service {'avahi-daemon': + enable => false, + ensure => 'stopped', + } +} + + - name: Disable service avahi-daemon + block: + + - name: Disable service avahi-daemon + systemd: + name: avahi-daemon.service + enabled: 'no' + state: stopped + masked: 'yes' + ignore_errors: 'yes' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-90824-4 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_avahi-daemon_disabled + +- name: Unit Socket Exists - avahi-daemon.socket + command: systemctl list-unit-files avahi-daemon.socket + args: + warn: false + register: socket_file_exists + changed_when: false + ignore_errors: true + check_mode: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-90824-4 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_avahi-daemon_disabled + +- name: Disable socket avahi-daemon + systemd: + name: avahi-daemon.socket + enabled: 'no' + state: stopped + masked: 'yes' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"avahi-daemon.socket" in socket_file_exists.stdout_lines[1]' + tags: + - CCE-90824-4 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_avahi-daemon_disabled + + +[customizations.services] +disabled = ["avahi-daemon"] + + apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + systemd: + units: + - name: avahi-daemon.service + enabled: false + mask: true + - name: avahi-daemon.socket + enabled: false + mask: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" stop 'avahi-daemon.service' +"$SYSTEMCTL_EXEC" disable 'avahi-daemon.service' +"$SYSTEMCTL_EXEC" mask 'avahi-daemon.service' +# Disable socket activation if we have a unit file for it +if "$SYSTEMCTL_EXEC" list-unit-files | grep -q '^avahi-daemon.socket'; then + "$SYSTEMCTL_EXEC" stop 'avahi-daemon.socket' + "$SYSTEMCTL_EXEC" mask 'avahi-daemon.socket' +fi +# The service may not be running because it has been started and failed, +# so let's reset the state so OVAL checks pass. +# Service should be 'inactive', not 'failed' after reboot though. +"$SYSTEMCTL_EXEC" reset-failed 'avahi-daemon.service' || true + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + + Base Services + This section addresses the base services that are installed on a +Red Hat Enterprise Linux 9 default installation which are not covered in other +sections. Some of these services listen on the network and +should be treated with particular discretion. Other services are local +system utilities that may or may not be extraneous. In general, system services +should be disabled if not required. + + Disable Cockpit Management Server + The Cockpit Management Server (cockpit) provides a web based +login and management framework. + +The cockpit service can be disabled with the following command: +$ sudo systemctl mask --now cockpit.service + Cockpit provides a form of remote login. + + include disable_cockpit + +class disable_cockpit { + service {'cockpit': + enable => false, + ensure => 'stopped', + } +} + + - name: Disable service cockpit + block: + + - name: Disable service cockpit + systemd: + name: cockpit.service + enabled: 'no' + state: stopped + masked: 'yes' + ignore_errors: 'yes' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_cockpit_disabled + +- name: Unit Socket Exists - cockpit.socket + command: systemctl list-unit-files cockpit.socket + args: + warn: false + register: socket_file_exists + changed_when: false + ignore_errors: true + check_mode: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_cockpit_disabled + +- name: Disable socket cockpit + systemd: + name: cockpit.socket + enabled: 'no' + state: stopped + masked: 'yes' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"cockpit.socket" in socket_file_exists.stdout_lines[1]' + tags: + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_cockpit_disabled + + +[customizations.services] +disabled = ["cockpit"] + + apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + systemd: + units: + - name: cockpit.service + enabled: false + mask: true + - name: cockpit.socket + enabled: false + mask: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" stop 'cockpit.service' +"$SYSTEMCTL_EXEC" disable 'cockpit.service' +"$SYSTEMCTL_EXEC" mask 'cockpit.service' +# Disable socket activation if we have a unit file for it +if "$SYSTEMCTL_EXEC" list-unit-files | grep -q '^cockpit.socket'; then + "$SYSTEMCTL_EXEC" stop 'cockpit.socket' + "$SYSTEMCTL_EXEC" mask 'cockpit.socket' +fi +# The service may not be running because it has been started and failed, +# so let's reset the state so OVAL checks pass. +# Service should be 'inactive', not 'failed' after reboot though. +"$SYSTEMCTL_EXEC" reset-failed 'cockpit.service' || true + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable KDump Kernel Crash Analyzer (kdump) + The kdump service provides a kernel crash dump analyzer. It uses the kexec +system call to boot a secondary kernel ("capture" kernel) following a system +crash, which can load information from the crashed kernel for analysis. + +The kdump service can be disabled with the following command: +$ sudo systemctl mask --now kdump.service + 11 + 12 + 14 + 15 + 3 + 8 + 9 + APO13.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.04 + DSS05.02 + DSS05.03 + DSS05.05 + DSS06.06 + CCI-000366 + CCI-001665 + 164.308(a)(1)(ii)(D) + 164.308(a)(3) + 164.308(a)(4) + 164.310(b) + 164.310(c) + 164.312(a) + 164.312(e) + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.6.2.1 + A.6.2.2 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.AC-3 + PR.IP-1 + PR.PT-3 + PR.PT-4 + FMT_SMF_EXT.1.1 + SRG-OS-000269-GPOS-00103 + SRG-OS-000480-GPOS-00227 + Kernel core dumps may contain the full contents of system memory at the +time of the crash. Kernel core dumps consume a considerable amount of disk +space and may result in denial of service by exhausting the available space +on the target file system partition. Unless the system is used for kernel +development or testing, there is little need to run the kdump service. + + CCE-84232-8 + +kdump --disable + + include disable_kdump + +class disable_kdump { + service {'kdump': + enable => false, + ensure => 'stopped', + } +} + + - name: Disable service kdump + block: + + - name: Disable service kdump + systemd: + name: kdump.service + enabled: 'no' + state: stopped + masked: 'yes' + ignore_errors: 'yes' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84232-8 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_kdump_disabled + +- name: Unit Socket Exists - kdump.socket + command: systemctl list-unit-files kdump.socket + args: + warn: false + register: socket_file_exists + changed_when: false + ignore_errors: true + check_mode: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84232-8 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_kdump_disabled + +- name: Disable socket kdump + systemd: + name: kdump.socket + enabled: 'no' + state: stopped + masked: 'yes' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"kdump.socket" in socket_file_exists.stdout_lines[1]' + tags: + - CCE-84232-8 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_kdump_disabled + + +[customizations.services] +disabled = ["kdump"] + + apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + systemd: + units: + - name: kdump.service + enabled: false + mask: true + - name: kdump.socket + enabled: false + mask: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" stop 'kdump.service' +"$SYSTEMCTL_EXEC" disable 'kdump.service' +"$SYSTEMCTL_EXEC" mask 'kdump.service' +# Disable socket activation if we have a unit file for it +if "$SYSTEMCTL_EXEC" list-unit-files | grep -q '^kdump.socket'; then + "$SYSTEMCTL_EXEC" stop 'kdump.socket' + "$SYSTEMCTL_EXEC" mask 'kdump.socket' +fi +# The service may not be running because it has been started and failed, +# so let's reset the state so OVAL checks pass. +# Service should be 'inactive', not 'failed' after reboot though. +"$SYSTEMCTL_EXEC" reset-failed 'kdump.service' || true + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable ntpdate Service (ntpdate) + The ntpdate service sets the local hardware clock by polling NTP servers +when the system boots. It synchronizes to the NTP servers listed in +/etc/ntp/step-tickers or /etc/ntp.conf +and then sets the local hardware clock to the newly synchronized +system time. + +The ntpdate service can be disabled with the following command: +$ sudo systemctl mask --now ntpdate.service + 11 + 12 + 14 + 15 + 3 + 8 + 9 + APO13.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.04 + DSS05.02 + DSS05.03 + DSS05.05 + DSS06.06 + CCI-000382 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.6.2.1 + A.6.2.2 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.AC-3 + PR.IP-1 + PR.PT-3 + PR.PT-4 + The ntpdate service may only be suitable for systems which +are rebooted frequently enough that clock drift does not cause problems between +reboots. In any event, the functionality of the ntpdate service is now +available in the ntpd program and should be considered deprecated. + + CCE-84236-9 + include disable_ntpdate + +class disable_ntpdate { + service {'ntpdate': + enable => false, + ensure => 'stopped', + } +} + + - name: Disable service ntpdate + block: + + - name: Disable service ntpdate + systemd: + name: ntpdate.service + enabled: 'no' + state: stopped + masked: 'yes' + ignore_errors: 'yes' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84236-9 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - service_ntpdate_disabled + +- name: Unit Socket Exists - ntpdate.socket + command: systemctl list-unit-files ntpdate.socket + args: + warn: false + register: socket_file_exists + changed_when: false + ignore_errors: true + check_mode: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84236-9 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - service_ntpdate_disabled + +- name: Disable socket ntpdate + systemd: + name: ntpdate.socket + enabled: 'no' + state: stopped + masked: 'yes' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"ntpdate.socket" in socket_file_exists.stdout_lines[1]' + tags: + - CCE-84236-9 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - service_ntpdate_disabled + + +[customizations.services] +disabled = ["ntpdate"] + + apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + systemd: + units: + - name: ntpdate.service + enabled: false + mask: true + - name: ntpdate.socket + enabled: false + mask: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" stop 'ntpdate.service' +"$SYSTEMCTL_EXEC" disable 'ntpdate.service' +"$SYSTEMCTL_EXEC" mask 'ntpdate.service' +# Disable socket activation if we have a unit file for it +if "$SYSTEMCTL_EXEC" list-unit-files | grep -q '^ntpdate.socket'; then + "$SYSTEMCTL_EXEC" stop 'ntpdate.socket' + "$SYSTEMCTL_EXEC" mask 'ntpdate.socket' +fi +# The service may not be running because it has been started and failed, +# so let's reset the state so OVAL checks pass. +# Service should be 'inactive', not 'failed' after reboot though. +"$SYSTEMCTL_EXEC" reset-failed 'ntpdate.service' || true + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable Odd Job Daemon (oddjobd) + The oddjobd service exists to provide an interface and +access control mechanism through which +specified privileged tasks can run tasks for unprivileged client +applications. Communication with oddjobd through the system message bus. + +The oddjobd service can be disabled with the following command: +$ sudo systemctl mask --now oddjobd.service + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + CCI-000381 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + PR.PT-3 + The oddjobd service may provide necessary functionality in +some environments, and can be disabled if it is not needed. Execution of +tasks by privileged programs, on behalf of unprivileged ones, has traditionally +been a source of privilege escalation security issues. + + CCE-84229-4 + include disable_oddjobd + +class disable_oddjobd { + service {'oddjobd': + enable => false, + ensure => 'stopped', + } +} + + - name: Disable service oddjobd + block: + + - name: Disable service oddjobd + systemd: + name: oddjobd.service + enabled: 'no' + state: stopped + masked: 'yes' + ignore_errors: 'yes' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84229-4 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_oddjobd_disabled + +- name: Unit Socket Exists - oddjobd.socket + command: systemctl list-unit-files oddjobd.socket + args: + warn: false + register: socket_file_exists + changed_when: false + ignore_errors: true + check_mode: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84229-4 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_oddjobd_disabled + +- name: Disable socket oddjobd + systemd: + name: oddjobd.socket + enabled: 'no' + state: stopped + masked: 'yes' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"oddjobd.socket" in socket_file_exists.stdout_lines[1]' + tags: + - CCE-84229-4 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_oddjobd_disabled + + +[customizations.services] +disabled = ["oddjobd"] + + apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + systemd: + units: + - name: oddjobd.service + enabled: false + mask: true + - name: oddjobd.socket + enabled: false + mask: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" stop 'oddjobd.service' +"$SYSTEMCTL_EXEC" disable 'oddjobd.service' +"$SYSTEMCTL_EXEC" mask 'oddjobd.service' +# Disable socket activation if we have a unit file for it +if "$SYSTEMCTL_EXEC" list-unit-files | grep -q '^oddjobd.socket'; then + "$SYSTEMCTL_EXEC" stop 'oddjobd.socket' + "$SYSTEMCTL_EXEC" mask 'oddjobd.socket' +fi +# The service may not be running because it has been started and failed, +# so let's reset the state so OVAL checks pass. +# Service should be 'inactive', not 'failed' after reboot though. +"$SYSTEMCTL_EXEC" reset-failed 'oddjobd.service' || true + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable Apache Qpid (qpidd) + The qpidd service provides high speed, secure, +guaranteed delivery services. It is an implementation of the Advanced Message +Queuing Protocol. By default the qpidd service will bind to port 5672 and +listen for connection attempts. + +The qpidd service can be disabled with the following command: +$ sudo systemctl mask --now qpidd.service + 11 + 12 + 14 + 15 + 3 + 8 + 9 + APO13.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.04 + DSS05.02 + DSS05.03 + DSS05.05 + DSS06.06 + CCI-000382 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.6.2.1 + A.6.2.2 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.AC-3 + PR.IP-1 + PR.PT-3 + PR.PT-4 + The qpidd service is automatically installed when the base package +selection is selected during installation. The qpidd service listens for +network connections, which increases the attack surface of the system. If +the system is not intended to receive AMQP traffic, then the qpidd +service is not needed and should be disabled or removed. + + CCE-84231-0 + include disable_qpidd + +class disable_qpidd { + service {'qpidd': + enable => false, + ensure => 'stopped', + } +} + + - name: Disable service qpidd + block: + + - name: Disable service qpidd + systemd: + name: qpidd.service + enabled: 'no' + state: stopped + masked: 'yes' + ignore_errors: 'yes' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84231-0 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - service_qpidd_disabled + +- name: Unit Socket Exists - qpidd.socket + command: systemctl list-unit-files qpidd.socket + args: + warn: false + register: socket_file_exists + changed_when: false + ignore_errors: true + check_mode: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84231-0 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - service_qpidd_disabled + +- name: Disable socket qpidd + systemd: + name: qpidd.socket + enabled: 'no' + state: stopped + masked: 'yes' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"qpidd.socket" in socket_file_exists.stdout_lines[1]' + tags: + - CCE-84231-0 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - service_qpidd_disabled + + +[customizations.services] +disabled = ["qpidd"] + + apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + systemd: + units: + - name: qpidd.service + enabled: false + mask: true + - name: qpidd.socket + enabled: false + mask: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" stop 'qpidd.service' +"$SYSTEMCTL_EXEC" disable 'qpidd.service' +"$SYSTEMCTL_EXEC" mask 'qpidd.service' +# Disable socket activation if we have a unit file for it +if "$SYSTEMCTL_EXEC" list-unit-files | grep -q '^qpidd.socket'; then + "$SYSTEMCTL_EXEC" stop 'qpidd.socket' + "$SYSTEMCTL_EXEC" mask 'qpidd.socket' +fi +# The service may not be running because it has been started and failed, +# so let's reset the state so OVAL checks pass. +# Service should be 'inactive', not 'failed' after reboot though. +"$SYSTEMCTL_EXEC" reset-failed 'qpidd.service' || true + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable Network Router Discovery Daemon (rdisc) + The rdisc service implements the client side of the ICMP +Internet Router Discovery Protocol (IRDP), which allows discovery of routers on +the local subnet. If a router is discovered then the local routing table is +updated with a corresponding default route. By default this daemon is disabled. + +The rdisc service can be disabled with the following command: +$ sudo systemctl mask --now rdisc.service + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 4 + 6 + 8 + 9 + APO01.06 + APO13.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.04 + DSS01.05 + DSS03.01 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + DSS06.02 + DSS06.06 + CCI-000382 + 4.2.3.4 + 4.3.3.4 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + 4.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.11.2.6 + A.12.1.1 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.13.1.1 + A.13.1.2 + A.13.1.3 + A.13.2.1 + A.13.2.2 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.6.1.2 + A.6.2.1 + A.6.2.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + AC-4 + CM-7(a) + CM-7(b) + CM-6(a) + DE.AE-1 + ID.AM-3 + PR.AC-3 + PR.AC-5 + PR.DS-5 + PR.IP-1 + PR.PT-3 + PR.PT-4 + General-purpose systems typically have their network and routing +information configured statically by a system administrator. Workstations or +some special-purpose systems often use DHCP (instead of IRDP) to retrieve +dynamic network configuration information. + + CCE-84237-7 + include disable_rdisc + +class disable_rdisc { + service {'rdisc': + enable => false, + ensure => 'stopped', + } +} + + - name: Disable service rdisc + block: + + - name: Disable service rdisc + systemd: + name: rdisc.service + enabled: 'no' + state: stopped + masked: 'yes' + ignore_errors: 'yes' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84237-7 + - NIST-800-53-AC-4 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_rdisc_disabled + +- name: Unit Socket Exists - rdisc.socket + command: systemctl list-unit-files rdisc.socket + args: + warn: false + register: socket_file_exists + changed_when: false + ignore_errors: true + check_mode: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84237-7 + - NIST-800-53-AC-4 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_rdisc_disabled + +- name: Disable socket rdisc + systemd: + name: rdisc.socket + enabled: 'no' + state: stopped + masked: 'yes' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"rdisc.socket" in socket_file_exists.stdout_lines[1]' + tags: + - CCE-84237-7 + - NIST-800-53-AC-4 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_rdisc_disabled + + +[customizations.services] +disabled = ["rdisc"] + + apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + systemd: + units: + - name: rdisc.service + enabled: false + mask: true + - name: rdisc.socket + enabled: false + mask: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" stop 'rdisc.service' +"$SYSTEMCTL_EXEC" disable 'rdisc.service' +"$SYSTEMCTL_EXEC" mask 'rdisc.service' +# Disable socket activation if we have a unit file for it +if "$SYSTEMCTL_EXEC" list-unit-files | grep -q '^rdisc.socket'; then + "$SYSTEMCTL_EXEC" stop 'rdisc.socket' + "$SYSTEMCTL_EXEC" mask 'rdisc.socket' +fi +# The service may not be running because it has been started and failed, +# so let's reset the state so OVAL checks pass. +# Service should be 'inactive', not 'failed' after reboot though. +"$SYSTEMCTL_EXEC" reset-failed 'rdisc.service' || true + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable Red Hat Network Service (rhnsd) + The Red Hat Network service automatically queries Red Hat Network +servers to determine whether there are any actions that should be executed, +such as package updates. This only occurs if the system was registered to an +RHN server or satellite and managed as such. + +The rhnsd service can be disabled with the following command: +$ sudo systemctl mask --now rhnsd.service + 11 + 12 + 14 + 15 + 3 + 8 + 9 + APO13.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.04 + DSS05.02 + DSS05.03 + DSS05.05 + DSS06.06 + CCI-000382 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.6.2.1 + A.6.2.2 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.AC-3 + PR.IP-1 + PR.PT-3 + PR.PT-4 + Although systems management and patching is extremely important to +system security, management by a system outside the enterprise enclave is not +desirable for some environments. However, if the system is being managed by RHN or + RHN Satellite Server the rhnsd daemon can remain on. + + CCE-84235-1 + include disable_rhnsd + +class disable_rhnsd { + service {'rhnsd': + enable => false, + ensure => 'stopped', + } +} + + - name: Disable service rhnsd + block: + + - name: Disable service rhnsd + systemd: + name: rhnsd.service + enabled: 'no' + state: stopped + masked: 'yes' + ignore_errors: 'yes' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84235-1 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - service_rhnsd_disabled + +- name: Unit Socket Exists - rhnsd.socket + command: systemctl list-unit-files rhnsd.socket + args: + warn: false + register: socket_file_exists + changed_when: false + ignore_errors: true + check_mode: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84235-1 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - service_rhnsd_disabled + +- name: Disable socket rhnsd + systemd: + name: rhnsd.socket + enabled: 'no' + state: stopped + masked: 'yes' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"rhnsd.socket" in socket_file_exists.stdout_lines[1]' + tags: + - CCE-84235-1 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - service_rhnsd_disabled + + +[customizations.services] +disabled = ["rhnsd"] + + apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + systemd: + units: + - name: rhnsd.service + enabled: false + mask: true + - name: rhnsd.socket + enabled: false + mask: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" stop 'rhnsd.service' +"$SYSTEMCTL_EXEC" disable 'rhnsd.service' +"$SYSTEMCTL_EXEC" mask 'rhnsd.service' +# Disable socket activation if we have a unit file for it +if "$SYSTEMCTL_EXEC" list-unit-files | grep -q '^rhnsd.socket'; then + "$SYSTEMCTL_EXEC" stop 'rhnsd.socket' + "$SYSTEMCTL_EXEC" mask 'rhnsd.socket' +fi +# The service may not be running because it has been started and failed, +# so let's reset the state so OVAL checks pass. +# Service should be 'inactive', not 'failed' after reboot though. +"$SYSTEMCTL_EXEC" reset-failed 'rhnsd.service' || true + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Cron and At Daemons + The cron and at services are used to allow commands to +be executed at a later time. The cron service is required by almost +all systems to perform necessary maintenance tasks, while at may or +may not be required on a given system. Both daemons should be +configured defensively. + + + Install the cron service + The Cron service should be installed. + BP28(R50) + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-6(a) + PR.IP-1 + PR.PT-3 + The cron service allow periodic job execution, needed for almost all administrative tasks and services (software update, log rotating, etc.). Access to cron service should be restricted to administrative accounts only. + +package --add=cron + + include install_cron + +class install_cron { + package { 'cron': + ensure => 'installed', + } +} + + - name: Ensure cron is installed + package: + name: cron + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-CM-6(a) + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_cron_installed + + +[[packages]] +name = "cron" +version = "*" + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if ! rpm -q --quiet "cron" ; then + dnf install -y "cron" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + Enable cron Service + The crond service is used to execute commands at +preconfigured times. It is required by almost all systems to perform necessary +maintenance tasks, such as notifying root of system activity. + +The cron service can be enabled with the following command: +$ sudo systemctl enable cron.service + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + 164.308(a)(4)(i) + 164.308(b)(1) + 164.308(b)(3) + 164.310(b) + 164.312(e)(1) + 164.312(e)(2)(ii) + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-6(a) + PR.IP-1 + PR.PT-3 + Due to its usage for maintenance and security-supporting tasks, +enabling the cron daemon is essential. + include enable_cron + +class enable_cron { + service {'cron': + enable => true, + ensure => 'running', + } +} + + - name: Enable service cron + block: + + - name: Gather the package facts + package_facts: + manager: auto + + - name: Enable service cron + service: + name: cron + enabled: 'yes' + state: started + masked: 'no' + when: + - '"cron" in ansible_facts.packages' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-CM-6(a) + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_cron_enabled + + +[customizations.services] +enabled = ["cron"] + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" unmask 'cron.service' +"$SYSTEMCTL_EXEC" start 'cron.service' +"$SYSTEMCTL_EXEC" enable 'cron.service' + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Enable cron Service + The crond service is used to execute commands at +preconfigured times. It is required by almost all systems to perform necessary +maintenance tasks, such as notifying root of system activity. + +The crond service can be enabled with the following command: +$ sudo systemctl enable crond.service + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + 164.308(a)(4)(i) + 164.308(b)(1) + 164.308(b)(3) + 164.310(b) + 164.312(e)(1) + 164.312(e)(2)(ii) + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-6(a) + PR.IP-1 + PR.PT-3 + Due to its usage for maintenance and security-supporting tasks, +enabling the cron daemon is essential. + CCE-84163-5 + include enable_crond + +class enable_crond { + service {'crond': + enable => true, + ensure => 'running', + } +} + + - name: Enable service crond + block: + + - name: Gather the package facts + package_facts: + manager: auto + + - name: Enable service crond + service: + name: crond + enabled: 'yes' + state: started + masked: 'no' + when: + - '"cronie" in ansible_facts.packages' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84163-5 + - NIST-800-53-CM-6(a) + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_crond_enabled + + +[customizations.services] +enabled = ["crond"] + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" unmask 'crond.service' +"$SYSTEMCTL_EXEC" start 'crond.service' +"$SYSTEMCTL_EXEC" enable 'crond.service' + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable At Service (atd) + The at and batch commands can be used to +schedule tasks that are meant to be executed only once. This allows delayed +execution in a manner similar to cron, except that it is not +recurring. The daemon atd keeps track of tasks scheduled via +at and batch, and executes them at the specified time. + +The atd service can be disabled with the following command: +$ sudo systemctl mask --now atd.service + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + CCI-000381 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + PR.PT-3 + The atd service could be used by an unsophisticated insider to carry +out activities outside of a normal login session, which could complicate +accountability. Furthermore, the need to schedule tasks with at or +batch is not common. + + CCE-84164-3 + include disable_atd + +class disable_atd { + service {'atd': + enable => false, + ensure => 'stopped', + } +} + + - name: Disable service atd + block: + + - name: Disable service atd + systemd: + name: atd.service + enabled: 'no' + state: stopped + masked: 'yes' + ignore_errors: 'yes' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84164-3 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_atd_disabled + +- name: Unit Socket Exists - atd.socket + command: systemctl list-unit-files atd.socket + args: + warn: false + register: socket_file_exists + changed_when: false + ignore_errors: true + check_mode: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84164-3 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_atd_disabled + +- name: Disable socket atd + systemd: + name: atd.socket + enabled: 'no' + state: stopped + masked: 'yes' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"atd.socket" in socket_file_exists.stdout_lines[1]' + tags: + - CCE-84164-3 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_atd_disabled + + +[customizations.services] +disabled = ["atd"] + + apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + systemd: + units: + - name: atd.service + enabled: false + mask: true + - name: atd.socket + enabled: false + mask: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" stop 'atd.service' +"$SYSTEMCTL_EXEC" disable 'atd.service' +"$SYSTEMCTL_EXEC" mask 'atd.service' +# Disable socket activation if we have a unit file for it +if "$SYSTEMCTL_EXEC" list-unit-files | grep -q '^atd.socket'; then + "$SYSTEMCTL_EXEC" stop 'atd.socket' + "$SYSTEMCTL_EXEC" mask 'atd.socket' +fi +# The service may not be running because it has been started and failed, +# so let's reset the state so OVAL checks pass. +# Service should be 'inactive', not 'failed' after reboot though. +"$SYSTEMCTL_EXEC" reset-failed 'atd.service' || true + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Verify Group Who Owns cron.d + +To properly set the group owner of /etc/cron.d, run the command: +$ sudo chgrp root /etc/cron.d + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + SRG-OS-000480-GPOS-00227 + Service configuration files enable or disable features of their respective services that if configured incorrectly +can lead to insecure and vulnerable configurations. Therefore, service configuration files should be owned by the +correct group to prevent unauthorized changes. + CCE-84177-5 + - name: Ensure group owner on /etc/cron.d/ + file: + path: /etc/cron.d/ + state: directory + group: '0' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84177-5 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_groupowner_cron_d + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +find -H /etc/cron.d/ -maxdepth 1 -type d -exec chgrp 0 {} \; + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Verify Group Who Owns cron.daily + +To properly set the group owner of /etc/cron.daily, run the command: +$ sudo chgrp root /etc/cron.daily + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + SRG-OS-000480-GPOS-00227 + Service configuration files enable or disable features of their respective services that if configured incorrectly +can lead to insecure and vulnerable configurations. Therefore, service configuration files should be owned by the +correct group to prevent unauthorized changes. + CCE-84170-0 + - name: Ensure group owner on /etc/cron.daily/ + file: + path: /etc/cron.daily/ + state: directory + group: '0' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84170-0 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_groupowner_cron_daily + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +find -H /etc/cron.daily/ -maxdepth 1 -type d -exec chgrp 0 {} \; + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Verify Group Who Owns cron.hourly + +To properly set the group owner of /etc/cron.hourly, run the command: +$ sudo chgrp root /etc/cron.hourly + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + SRG-OS-000480-GPOS-00227 + Service configuration files enable or disable features of their respective services that if configured incorrectly +can lead to insecure and vulnerable configurations. Therefore, service configuration files should be owned by the +correct group to prevent unauthorized changes. + CCE-84186-6 + - name: Ensure group owner on /etc/cron.hourly/ + file: + path: /etc/cron.hourly/ + state: directory + group: '0' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84186-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_groupowner_cron_hourly + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +find -H /etc/cron.hourly/ -maxdepth 1 -type d -exec chgrp 0 {} \; + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Verify Group Who Owns cron.monthly + +To properly set the group owner of /etc/cron.monthly, run the command: +$ sudo chgrp root /etc/cron.monthly + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + SRG-OS-000480-GPOS-00227 + Service configuration files enable or disable features of their respective services that if configured incorrectly +can lead to insecure and vulnerable configurations. Therefore, service configuration files should be owned by the +correct group to prevent unauthorized changes. + CCE-84189-0 + - name: Ensure group owner on /etc/cron.monthly/ + file: + path: /etc/cron.monthly/ + state: directory + group: '0' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84189-0 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_groupowner_cron_monthly + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +find -H /etc/cron.monthly/ -maxdepth 1 -type d -exec chgrp 0 {} \; + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Verify Group Who Owns cron.weekly + +To properly set the group owner of /etc/cron.weekly, run the command: +$ sudo chgrp root /etc/cron.weekly + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + SRG-OS-000480-GPOS-00227 + Service configuration files enable or disable features of their respective services that if configured incorrectly +can lead to insecure and vulnerable configurations. Therefore, service configuration files should be owned by the +correct group to prevent unauthorized changes. + CCE-84174-2 + - name: Ensure group owner on /etc/cron.weekly/ + file: + path: /etc/cron.weekly/ + state: directory + group: '0' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84174-2 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_groupowner_cron_weekly + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +find -H /etc/cron.weekly/ -maxdepth 1 -type d -exec chgrp 0 {} \; + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Verify Group Who Owns Crontab + +To properly set the group owner of /etc/crontab, run the command: +$ sudo chgrp root /etc/crontab + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + SRG-OS-000480-GPOS-00227 + Service configuration files enable or disable features of their respective services that if configured incorrectly +can lead to insecure and vulnerable configurations. Therefore, service configuration files should be owned by the +correct group to prevent unauthorized changes. + CCE-84171-8 + - name: Test for existence /etc/crontab + stat: + path: /etc/crontab + register: file_exists + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84171-8 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_groupowner_crontab + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure group owner 0 on /etc/crontab + file: + path: /etc/crontab + group: '0' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-84171-8 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_groupowner_crontab + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +chgrp 0 /etc/crontab + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Verify Owner on cron.d + +To properly set the owner of /etc/cron.d, run the command: +$ sudo chown root /etc/cron.d + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + SRG-OS-000480-GPOS-00227 + Service configuration files enable or disable features of their respective services that if configured incorrectly +can lead to insecure and vulnerable configurations. Therefore, service configuration files should be owned by the +correct user to prevent unauthorized changes. + CCE-84169-2 + - name: Ensure owner on directory /etc/cron.d/ + file: + path: /etc/cron.d/ + state: directory + owner: '0' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84169-2 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_owner_cron_d + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +find -H /etc/cron.d/ -maxdepth 1 -type d -exec chown 0 {} \; + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Verify Owner on cron.daily + +To properly set the owner of /etc/cron.daily, run the command: +$ sudo chown root /etc/cron.daily + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + SRG-OS-000480-GPOS-00227 + Service configuration files enable or disable features of their respective services that if configured incorrectly +can lead to insecure and vulnerable configurations. Therefore, service configuration files should be owned by the +correct user to prevent unauthorized changes. + CCE-84188-2 + - name: Ensure owner on directory /etc/cron.daily/ + file: + path: /etc/cron.daily/ + state: directory + owner: '0' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84188-2 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_owner_cron_daily + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +find -H /etc/cron.daily/ -maxdepth 1 -type d -exec chown 0 {} \; + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Verify Owner on cron.hourly + +To properly set the owner of /etc/cron.hourly, run the command: +$ sudo chown root /etc/cron.hourly + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + SRG-OS-000480-GPOS-00227 + Service configuration files enable or disable features of their respective services that if configured incorrectly +can lead to insecure and vulnerable configurations. Therefore, service configuration files should be owned by the +correct user to prevent unauthorized changes. + CCE-84168-4 + - name: Ensure owner on directory /etc/cron.hourly/ + file: + path: /etc/cron.hourly/ + state: directory + owner: '0' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84168-4 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_owner_cron_hourly + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +find -H /etc/cron.hourly/ -maxdepth 1 -type d -exec chown 0 {} \; + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Verify Owner on cron.monthly + +To properly set the owner of /etc/cron.monthly, run the command: +$ sudo chown root /etc/cron.monthly + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + SRG-OS-000480-GPOS-00227 + Service configuration files enable or disable features of their respective services that if configured incorrectly +can lead to insecure and vulnerable configurations. Therefore, service configuration files should be owned by the +correct user to prevent unauthorized changes. + CCE-84179-1 + - name: Ensure owner on directory /etc/cron.monthly/ + file: + path: /etc/cron.monthly/ + state: directory + owner: '0' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84179-1 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_owner_cron_monthly + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +find -H /etc/cron.monthly/ -maxdepth 1 -type d -exec chown 0 {} \; + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Verify Owner on cron.weekly + +To properly set the owner of /etc/cron.weekly, run the command: +$ sudo chown root /etc/cron.weekly + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + SRG-OS-000480-GPOS-00227 + Service configuration files enable or disable features of their respective services that if configured incorrectly +can lead to insecure and vulnerable configurations. Therefore, service configuration files should be owned by the +correct user to prevent unauthorized changes. + CCE-84190-8 + - name: Ensure owner on directory /etc/cron.weekly/ + file: + path: /etc/cron.weekly/ + state: directory + owner: '0' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84190-8 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_owner_cron_weekly + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +find -H /etc/cron.weekly/ -maxdepth 1 -type d -exec chown 0 {} \; + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Verify Owner on crontab + +To properly set the owner of /etc/crontab, run the command: +$ sudo chown root /etc/crontab + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + SRG-OS-000480-GPOS-00227 + Service configuration files enable or disable features of their respective services that if configured incorrectly +can lead to insecure and vulnerable configurations. Therefore, service configuration files should be owned by the +correct user to prevent unauthorized changes. + CCE-84167-6 + - name: Test for existence /etc/crontab + stat: + path: /etc/crontab + register: file_exists + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84167-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_owner_crontab + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure owner 0 on /etc/crontab + file: + path: /etc/crontab + owner: '0' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-84167-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_owner_crontab + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +chown 0 /etc/crontab + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Verify Permissions on cron.d + +To properly set the permissions of /etc/cron.d, run the command: +$ sudo chmod 0700 /etc/cron.d + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + SRG-OS-000480-GPOS-00227 + Service configuration files enable or disable features of their respective services that if configured incorrectly +can lead to insecure and vulnerable configurations. Therefore, service configuration files should have the +correct access rights to prevent unauthorized changes. + CCE-84183-3 + - name: Set permissions for /etc/cron.d/ + file: + path: /etc/cron.d/ + state: directory + mode: u-s,g-xwrs,o-xwrt + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84183-3 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_permissions_cron_d + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +find -H /etc/cron.d/ -maxdepth 1 -perm /u+s,g+xwrs,o+xwrt -type d -exec chmod u-s,g-xwrs,o-xwrt {} \; + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Verify Permissions on cron.daily + +To properly set the permissions of /etc/cron.daily, run the command: +$ sudo chmod 0700 /etc/cron.daily + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + SRG-OS-000480-GPOS-00227 + Service configuration files enable or disable features of their respective services that if configured incorrectly +can lead to insecure and vulnerable configurations. Therefore, service configuration files should have the +correct access rights to prevent unauthorized changes. + CCE-84175-9 + - name: Set permissions for /etc/cron.daily/ + file: + path: /etc/cron.daily/ + state: directory + mode: u-s,g-xwrs,o-xwrt + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84175-9 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_permissions_cron_daily + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +find -H /etc/cron.daily/ -maxdepth 1 -perm /u+s,g+xwrs,o+xwrt -type d -exec chmod u-s,g-xwrs,o-xwrt {} \; + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Verify Permissions on cron.hourly + +To properly set the permissions of /etc/cron.hourly, run the command: +$ sudo chmod 0700 /etc/cron.hourly + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + SRG-OS-000480-GPOS-00227 + Service configuration files enable or disable features of their respective services that if configured incorrectly +can lead to insecure and vulnerable configurations. Therefore, service configuration files should have the +correct access rights to prevent unauthorized changes. + CCE-84173-4 + - name: Set permissions for /etc/cron.hourly/ + file: + path: /etc/cron.hourly/ + state: directory + mode: u-s,g-xwrs,o-xwrt + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84173-4 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_permissions_cron_hourly + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +find -H /etc/cron.hourly/ -maxdepth 1 -perm /u+s,g+xwrs,o+xwrt -type d -exec chmod u-s,g-xwrs,o-xwrt {} \; + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Verify Permissions on cron.monthly + +To properly set the permissions of /etc/cron.monthly, run the command: +$ sudo chmod 0700 /etc/cron.monthly + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + SRG-OS-000480-GPOS-00227 + Service configuration files enable or disable features of their respective services that if configured incorrectly +can lead to insecure and vulnerable configurations. Therefore, service configuration files should have the +correct access rights to prevent unauthorized changes. + CCE-84181-7 + - name: Set permissions for /etc/cron.monthly/ + file: + path: /etc/cron.monthly/ + state: directory + mode: u-s,g-xwrs,o-xwrt + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84181-7 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_permissions_cron_monthly + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +find -H /etc/cron.monthly/ -maxdepth 1 -perm /u+s,g+xwrs,o+xwrt -type d -exec chmod u-s,g-xwrs,o-xwrt {} \; + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Verify Permissions on cron.weekly + +To properly set the permissions of /etc/cron.weekly, run the command: +$ sudo chmod 0700 /etc/cron.weekly + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + SRG-OS-000480-GPOS-00227 + Service configuration files enable or disable features of their respective services that if configured incorrectly +can lead to insecure and vulnerable configurations. Therefore, service configuration files should have the +correct access rights to prevent unauthorized changes. + CCE-84187-4 + - name: Set permissions for /etc/cron.weekly/ + file: + path: /etc/cron.weekly/ + state: directory + mode: u-s,g-xwrs,o-xwrt + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84187-4 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_permissions_cron_weekly + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +find -H /etc/cron.weekly/ -maxdepth 1 -perm /u+s,g+xwrs,o+xwrt -type d -exec chmod u-s,g-xwrs,o-xwrt {} \; + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Verify Permissions on crontab + +To properly set the permissions of /etc/crontab, run the command: +$ sudo chmod 0600 /etc/crontab + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + SRG-OS-000480-GPOS-00227 + Service configuration files enable or disable features of their respective services that if configured incorrectly +can lead to insecure and vulnerable configurations. Therefore, service configuration files should have the +correct access rights to prevent unauthorized changes. + CCE-84176-7 + - name: Test for existence /etc/crontab + stat: + path: /etc/crontab + register: file_exists + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84176-7 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_permissions_crontab + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure permission u-xs,g-xwrs,o-xwrt on /etc/crontab + file: + path: /etc/crontab + mode: u-xs,g-xwrs,o-xwrt + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-84176-7 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_permissions_crontab + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +chmod u-xs,g-xwrs,o-xwrt /etc/crontab + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Restrict at and cron to Authorized Users if Necessary + The /etc/cron.allow and /etc/at.allow files contain lists of +users who are allowed to use cron and at to delay execution of +processes. If these files exist and if the corresponding files +/etc/cron.deny and /etc/at.deny do not exist, then only users +listed in the relevant allow files can run the crontab and at commands +to submit jobs to be run at scheduled intervals. On many systems, only the +system administrator needs the ability to schedule jobs. Note that even if a +given user is not listed in cron.allow, cron jobs can still be run as +that user. The cron.allow file controls only administrative access +to the crontab command for scheduling and modifying cron jobs. + + +To restrict at and cron to only authorized users: +Remove the cron.deny file:$ sudo rm /etc/cron.denyEdit /etc/cron.allow, adding one line for each user allowed to use +the crontab command to create cron jobs.Remove the at.deny file:$ sudo rm /etc/at.denyEdit /etc/at.allow, adding one line for each user allowed to use +the at command to create at jobs. + + Ensure that /etc/at.deny does not exist + The file /etc/at.deny should not exist. +Use /etc/at.allow instead. + Access to at should be restricted. +It is easier to manage an allow list than a deny list. + CCE-86946-1 + - name: Remove /etc/at.deny + file: + path: /etc/at.deny + state: absent + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86946-1 + - disable_strategy + - file_at_deny_not_exist + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +#!/bin/bash + + + + if [[ -f /etc/at.deny ]]; then + rm /etc/at.deny + fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure that /etc/cron.deny does not exist + The file /etc/cron.deny should not exist. +Use /etc/cron.allow instead. + Access to cron should be restricted. +It is easier to manage an allow list than a deny list. + CCE-86850-5 + - name: Remove /etc/cron.deny + file: + path: /etc/cron.deny + state: absent + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86850-5 + - disable_strategy + - file_cron_deny_not_exist + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +#!/bin/bash + + + + if [[ -f /etc/cron.deny ]]; then + rm /etc/cron.deny + fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Verify Group Who Owns /etc/at.allow file + If /etc/at.allow exists, it must be group-owned by root. + +To properly set the group owner of /etc/at.allow, run the command: +$ sudo chgrp root /etc/at.allow + If the owner of the at.allow file is not set to root, the possibility exists for an +unauthorized user to view or edit sensitive information. + CCE-87103-8 + - name: Test for existence /etc/at.allow + stat: + path: /etc/at.allow + register: file_exists + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-87103-8 + - configure_strategy + - file_groupowner_at_allow + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure group owner 0 on /etc/at.allow + file: + path: /etc/at.allow + group: '0' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-87103-8 + - configure_strategy + - file_groupowner_at_allow + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +chgrp 0 /etc/at.allow + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Verify Group Who Owns /etc/cron.allow file + If /etc/cron.allow exists, it must be group-owned by root. + +To properly set the group owner of /etc/cron.allow, run the command: +$ sudo chgrp root /etc/cron.allow + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + CCI-000366 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + SRG-OS-000480-GPOS-00227 + If the owner of the cron.allow file is not set to root, the possibility exists for an +unauthorized user to view or edit sensitive information. + CCE-86830-7 + - name: Test for existence /etc/cron.allow + stat: + path: /etc/cron.allow + register: file_exists + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86830-7 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_groupowner_cron_allow + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure group owner 0 on /etc/cron.allow + file: + path: /etc/cron.allow + group: '0' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-86830-7 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_groupowner_cron_allow + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +chgrp 0 /etc/cron.allow + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Verify User Who Owns /etc/cron.allow file + If /etc/cron.allow exists, it must be owned by root. + +To properly set the owner of /etc/cron.allow, run the command: +$ sudo chown root /etc/cron.allow + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + CCI-000366 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + SRG-OS-000480-GPOS-00227 + If the owner of the cron.allow file is not set to root, the possibility exists for an +unauthorized user to view or edit sensitive information. + CCE-86844-8 + - name: Test for existence /etc/cron.allow + stat: + path: /etc/cron.allow + register: file_exists + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86844-8 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_owner_cron_allow + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure owner 0 on /etc/cron.allow + file: + path: /etc/cron.allow + owner: '0' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-86844-8 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_owner_cron_allow + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +chown 0 /etc/cron.allow + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Verify Permissions on /etc/at.allow file + If /etc/at.allow exists, it must have permissions 0600 +or more restrictive. + + +To properly set the permissions of /etc/at.allow, run the command: +$ sudo chmod 0600 /etc/at.allow + If the permissions of the at.allow file are not set to 0600 or more restrictive, +the possibility exists for an unauthorized user to view or edit sensitive information. + CCE-86904-0 + - name: Test for existence /etc/at.allow + stat: + path: /etc/at.allow + register: file_exists + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86904-0 + - configure_strategy + - file_permissions_at_allow + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure permission u-xs,g-xwrs,o-xwrt on /etc/at.allow + file: + path: /etc/at.allow + mode: u-xs,g-xwrs,o-xwrt + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-86904-0 + - configure_strategy + - file_permissions_at_allow + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +chmod u-xs,g-xwrs,o-xwrt /etc/at.allow + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Verify Permissions on /etc/cron.allow file + If /etc/cron.allow exists, it must have permissions 0600 +or more restrictive. + + +To properly set the permissions of /etc/cron.allow, run the command: +$ sudo chmod 0600 /etc/cron.allow + SRG-OS-000480-GPOS-00227 + If the permissions of the cron.allow file are not set to 0600 or more restrictive, +the possibility exists for an unauthorized user to view or edit sensitive information. + CCE-86877-8 + - name: Test for existence /etc/cron.allow + stat: + path: /etc/cron.allow + register: file_exists + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86877-8 + - configure_strategy + - file_permissions_cron_allow + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure permission u-xs,g-xwrs,o-xwrt on /etc/cron.allow + file: + path: /etc/cron.allow + mode: u-xs,g-xwrs,o-xwrt + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-86877-8 + - configure_strategy + - file_permissions_cron_allow + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +chmod u-xs,g-xwrs,o-xwrt /etc/cron.allow + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + + Deprecated services + Some deprecated software services impact the overall system security due to their behavior (leak of +confidentiality in network exchange, usage as uncontrolled communication channel, risk associated with the service due to its old age, etc. + + Uninstall the inet-based telnet server + The inet-based telnet daemon should be uninstalled. + NT007(R03) + 11 + 12 + 14 + 15 + 3 + 8 + 9 + APO13.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.04 + DSS05.02 + DSS05.03 + DSS05.05 + DSS06.06 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.6.2.1 + A.6.2.2 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.AC-3 + PR.IP-1 + PR.PT-3 + PR.PT-4 + telnet allows clear text communications, and does not protect any +data transmission between client and server. Any confidential data can be +listened and no integrity checking is made. + +package --remove=inetutils-telnetd + + include remove_inetutils-telnetd + +class remove_inetutils-telnetd { + package { 'inetutils-telnetd': + ensure => 'purged', + } +} + + - name: Ensure inetutils-telnetd is removed + package: + name: inetutils-telnetd + state: absent + tags: + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - high_severity + - low_complexity + - low_disruption + - no_reboot_needed + - package_inetutils-telnetd_removed + + +# CAUTION: This remediation script will remove inetutils-telnetd +# from the system, and may remove any packages +# that depend on inetutils-telnetd. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "inetutils-telnetd" ; then + + dnf remove -y "inetutils-telnetd" + +fi + + + + + + + Uninstall the nis package + The support for Yellowpages should not be installed unless it is required. + NIS is the historical SUN service for central account management, more and more replaced by LDAP. +NIS does not support efficiently security constraints, ACL, etc. and should not be used. + +package --remove=nis + + include remove_nis + +class remove_nis { + package { 'nis': + ensure => 'purged', + } +} + + - name: Ensure nis is removed + package: + name: nis + state: absent + tags: + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - package_nis_removed + + +# CAUTION: This remediation script will remove nis +# from the system, and may remove any packages +# that depend on nis. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "nis" ; then + + dnf remove -y "nis" + +fi + + + + + + + Uninstall the ntpdate package + ntpdate is a historical ntp synchronization client for unixes. It sould be uninstalled. + ntpdate is an old not security-compliant ntp client. It should be replaced by modern ntp clients such as ntpd, able to use cryptographic mechanisms integrated in NTP. + +package --remove=ntpdate + + include remove_ntpdate + +class remove_ntpdate { + package { 'ntpdate': + ensure => 'purged', + } +} + + - name: Ensure ntpdate is removed + package: + name: ntpdate + state: absent + tags: + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - package_ntpdate_removed + + +# CAUTION: This remediation script will remove ntpdate +# from the system, and may remove any packages +# that depend on ntpdate. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "ntpdate" ; then + + dnf remove -y "ntpdate" + +fi + + + + + + + Uninstall the ssl compliant telnet server + The telnet daemon, even with ssl support, should be uninstalled. + NT007(R02) + 11 + 12 + 14 + 15 + 3 + 8 + 9 + APO13.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.04 + DSS05.02 + DSS05.03 + DSS05.05 + DSS06.06 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.6.2.1 + A.6.2.2 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.AC-3 + PR.IP-1 + PR.PT-3 + PR.PT-4 + telnet, even with ssl support, should not be installed. +When remote shell is required, up-to-date ssh daemon can be used. + +package --remove=telnetd-ssl + + include remove_telnetd-ssl + +class remove_telnetd-ssl { + package { 'telnetd-ssl': + ensure => 'purged', + } +} + + - name: Ensure telnetd-ssl is removed + package: + name: telnetd-ssl + state: absent + tags: + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - high_severity + - low_complexity + - low_disruption + - no_reboot_needed + - package_telnetd-ssl_removed + + +# CAUTION: This remediation script will remove telnetd-ssl +# from the system, and may remove any packages +# that depend on telnetd-ssl. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "telnetd-ssl" ; then + + dnf remove -y "telnetd-ssl" + +fi + + + + + + + Uninstall the telnet server + The telnet daemon should be uninstalled. + BP28(R1) + NT007(R03) + 11 + 12 + 14 + 15 + 3 + 8 + 9 + APO13.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.04 + DSS05.02 + DSS05.03 + DSS05.05 + DSS06.06 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.6.2.1 + A.6.2.2 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.AC-3 + PR.IP-1 + PR.PT-3 + PR.PT-4 + telnet allows clear text communications, and does not protect +any data transmission between client and server. Any confidential data +can be listened and no integrity checking is made.' + +package --remove=telnetd + + include remove_telnetd + +class remove_telnetd { + package { 'telnetd': + ensure => 'purged', + } +} + + - name: Ensure telnetd is removed + package: + name: telnetd + state: absent + tags: + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - high_severity + - low_complexity + - low_disruption + - no_reboot_needed + - package_telnetd_removed + + +# CAUTION: This remediation script will remove telnetd +# from the system, and may remove any packages +# that depend on telnetd. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "telnetd" ; then + + dnf remove -y "telnetd" + +fi + + + + + + + + DHCP + The Dynamic Host Configuration Protocol (DHCP) allows +systems to request and obtain an IP address and other configuration +parameters from a server. + +This guide recommends configuring networking on clients by manually editing +the appropriate files under /etc/sysconfig. Use of DHCP can make client +systems vulnerable to compromise by rogue DHCP servers, and should be avoided +unless necessary. If using DHCP is necessary, however, there are best practices +that should be followed to minimize security risk. + + Configure DHCP Client if Necessary + If DHCP must be used, then certain configuration changes can +minimize the amount of information it receives and applies from the network, +and thus the amount of incorrect information a rogue DHCP server could +successfully distribute. For more information on configuring dhclient, see the +dhclient(8) and dhclient.conf(5) man pages. + + Minimize the DHCP-Configured Options + Create the file /etc/dhcp/dhclient.conf, and add an +appropriate setting for each of the ten configuration settings which can be +obtained via DHCP. For each setting, do one of the following: + +If the setting should not be configured remotely by the DHCP server, +select an appropriate static value, and add the line: +supersede setting value; +If the setting should be configured remotely by the DHCP server, add the lines: +request setting; +require setting; +For example, suppose the DHCP server should provide only the IP address itself +and the subnet mask. Then the entire file should look like: +supersede domain-name "example.com"; +supersede domain-name-servers 192.168.1.2; +supersede nis-domain ""; +supersede nis-servers ""; +supersede ntp-servers "ntp.example.com "; +supersede routers 192.168.1.1; +supersede time-offset -18000; +request subnet-mask; +require subnet-mask; + In this example, the options nis-servers and +nis-domain are set to empty strings, on the assumption that the deprecated NIS +protocol is not in use. It is necessary to supersede settings for unused +services so that they cannot be set by a hostile DHCP server. If an option is +set to an empty string, dhclient will typically not attempt to configure the +service. + By default, the DHCP client program, dhclient, requests and applies +ten configuration options (in addition to the IP address) from the DHCP server. +subnet-mask, broadcast-address, time-offset, routers, domain-name, +domain-name-servers, host-name, nis-domain, nis-servers, and ntp-servers. Many +of the options requested and applied by dhclient may be the same for every +system on a network. It is recommended that almost all configuration options be +assigned statically, and only options which must vary on a host-by-host basis +be assigned via DHCP. This limits the damage which can be done by a rogue DHCP +server. If appropriate for your site, it is also possible to supersede the +host-name directive in /etc/dhcp/dhclient.conf, establishing a static +hostname for the system. However, dhclient does not use the host name option +provided by the DHCP server (instead using the value provided by a reverse DNS +lookup). + + + + Configure DHCP Server + If the system must act as a DHCP server, the configuration +information it serves should be minimized. Also, support for other protocols +and DNS-updating schemes should be explicitly disabled unless needed. The +configuration file for dhcpd is called /etc/dhcp/dhcpd.conf. The file +begins with a number of global configuration options. The remainder of the file +is divided into sections, one for each block of addresses offered by dhcpd, +each of which contains configuration options specific to that address +block. + + Minimize Served Information + Edit /etc/dhcp/dhcpd.conf. Examine each address range section within +the file, and ensure that the following options are not defined unless there is +an operational need to provide this information via DHCP: +option domain-name +option domain-name-servers +option nis-domain +option nis-servers +option ntp-servers +option routers +option time-offset + By default, the Red Hat Enterprise Linux client installation uses DHCP +to request much of the above information from the DHCP server. In particular, +domain-name, domain-name-servers, and routers are configured via DHCP. These +settings are typically necessary for proper network functionality, but are also +usually static across systems at a given site. + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + PR.PT-3 + Because the configuration information provided by the DHCP server +could be maliciously provided to clients by a rogue DHCP server, the amount of +information provided via DHCP should be minimized. Remove these definitions +from the DHCP server configuration to ensure that legitimate clients do not +unnecessarily rely on DHCP for this information. + + + + Disable DHCP Client + DHCP is the default network configuration method provided by the system +installer, and common on many networks. Nevertheless, manual management +of IP addresses for systems implies a greater degree of management and +accountability for network activity. + + + Disable DHCP Server + The DHCP server dhcpd is not installed or activated by +default. If the software was installed and activated, but the +system does not need to act as a DHCP server, it should be disabled +and removed. + + Uninstall DHCP Server Package + If the system does not need to act as a DHCP server, +the dhcp package can be uninstalled. + +The dhcp-server package can be removed with the following command: + +$ sudo dnf erase dhcp-server + BP28(R1) + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + CCI-000366 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + PR.PT-3 + Removing the DHCP server ensures that it cannot be easily or +accidentally reactivated and disrupt network operation. + CCE-84240-1 + +package --remove=dhcp-server + + include remove_dhcp-server + +class remove_dhcp-server { + package { 'dhcp-server': + ensure => 'purged', + } +} + + - name: Ensure dhcp-server is removed + package: + name: dhcp-server + state: absent + tags: + - CCE-84240-1 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_dhcp_removed + + +# CAUTION: This remediation script will remove dhcp-server +# from the system, and may remove any packages +# that depend on dhcp-server. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "dhcp-server" ; then + + dnf remove -y "dhcp-server" + +fi + + + + + + + + + + Disable DHCP Service + The dhcpd service should be disabled on +any system that does not need to act as a DHCP server. + +The dhcpd service can be disabled with the following command: +$ sudo systemctl mask --now dhcpd.service + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + CCI-000366 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + PR.PT-3 + Unmanaged or unintentionally activated DHCP servers may provide faulty information +to clients, interfering with the operation of a legitimate site +DHCP server if there is one. + + CCE-84241-9 + include disable_dhcpd + +class disable_dhcpd { + service {'dhcpd': + enable => false, + ensure => 'stopped', + } +} + + - name: Disable service dhcpd + block: + + - name: Disable service dhcpd + systemd: + name: dhcpd.service + enabled: 'no' + state: stopped + masked: 'yes' + ignore_errors: 'yes' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84241-9 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_dhcpd_disabled + +- name: Unit Socket Exists - dhcpd.socket + command: systemctl list-unit-files dhcpd.socket + args: + warn: false + register: socket_file_exists + changed_when: false + ignore_errors: true + check_mode: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84241-9 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_dhcpd_disabled + +- name: Disable socket dhcpd + systemd: + name: dhcpd.socket + enabled: 'no' + state: stopped + masked: 'yes' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"dhcpd.socket" in socket_file_exists.stdout_lines[1]' + tags: + - CCE-84241-9 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_dhcpd_disabled + + +[customizations.services] +disabled = ["dhcpd"] + + apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + systemd: + units: + - name: dhcpd.service + enabled: false + mask: true + - name: dhcpd.socket + enabled: false + mask: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" stop 'dhcpd.service' +"$SYSTEMCTL_EXEC" disable 'dhcpd.service' +"$SYSTEMCTL_EXEC" mask 'dhcpd.service' +# Disable socket activation if we have a unit file for it +if "$SYSTEMCTL_EXEC" list-unit-files | grep -q '^dhcpd.socket'; then + "$SYSTEMCTL_EXEC" stop 'dhcpd.socket' + "$SYSTEMCTL_EXEC" mask 'dhcpd.socket' +fi +# The service may not be running because it has been started and failed, +# so let's reset the state so OVAL checks pass. +# Service should be 'inactive', not 'failed' after reboot though. +"$SYSTEMCTL_EXEC" reset-failed 'dhcpd.service' || true + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + + DNS Server + Most organizations have an operational need to run at +least one nameserver. However, there are many common attacks +involving DNS server software, and this server software should +be disabled on any system +on which it is not needed. + + Disable DNS Server + DNS software should be disabled on any systems which does not +need to be a nameserver. Note that the BIND DNS server software is +not installed on Red Hat Enterprise Linux 9 by default. The remainder of this section +discusses secure configuration of systems which must be +nameservers. + + Uninstall bind Package + The named service is provided by the bind package. +The bind package can be removed with the following command: + +$ sudo dnf erase bind + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + CCI-000366 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + PR.PT-3 + If there is no need to make DNS server software available, +removing it provides a safeguard against its activation. + +package --remove=bind + + include remove_bind + +class remove_bind { + package { 'bind': + ensure => 'purged', + } +} + + - name: Ensure bind is removed + package: + name: bind + state: absent + tags: + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - package_bind_removed + + +# CAUTION: This remediation script will remove bind +# from the system, and may remove any packages +# that depend on bind. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "bind" ; then + + dnf remove -y "bind" + +fi + + + + + + + + + + Disable named Service + +The named service can be disabled with the following command: +$ sudo systemctl mask --now named.service + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + CCI-000366 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + PR.PT-3 + All network services involve some risk of compromise due to +implementation flaws and should be disabled if possible. + + CCE-84194-0 + include disable_named + +class disable_named { + service {'named': + enable => false, + ensure => 'stopped', + } +} + + - name: Disable service named + block: + + - name: Disable service named + systemd: + name: named.service + enabled: 'no' + state: stopped + masked: 'yes' + ignore_errors: 'yes' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84194-0 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_named_disabled + +- name: Unit Socket Exists - named.socket + command: systemctl list-unit-files named.socket + args: + warn: false + register: socket_file_exists + changed_when: false + ignore_errors: true + check_mode: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84194-0 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_named_disabled + +- name: Disable socket named + systemd: + name: named.socket + enabled: 'no' + state: stopped + masked: 'yes' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"named.socket" in socket_file_exists.stdout_lines[1]' + tags: + - CCE-84194-0 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_named_disabled + + +[customizations.services] +disabled = ["named"] + + apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + systemd: + units: + - name: named.service + enabled: false + mask: true + - name: named.socket + enabled: false + mask: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" stop 'named.service' +"$SYSTEMCTL_EXEC" disable 'named.service' +"$SYSTEMCTL_EXEC" mask 'named.service' +# Disable socket activation if we have a unit file for it +if "$SYSTEMCTL_EXEC" list-unit-files | grep -q '^named.socket'; then + "$SYSTEMCTL_EXEC" stop 'named.socket' + "$SYSTEMCTL_EXEC" mask 'named.socket' +fi +# The service may not be running because it has been started and failed, +# so let's reset the state so OVAL checks pass. +# Service should be 'inactive', not 'failed' after reboot though. +"$SYSTEMCTL_EXEC" reset-failed 'named.service' || true + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Isolate DNS from Other Services + This section discusses mechanisms for preventing the DNS server +from interfering with other services. This is done both to protect the +remainder of the network should a nameserver be compromised, and to make direct +attacks on nameservers more difficult. + + Run DNS Software in a chroot Jail + Install the bind-chroot package: +$ sudo yum install bind-chroot +Place a valid named.conf file inside the chroot jail: +$ sudo cp /etc/named.conf /var/named/chroot/etc/named.conf +$ sudo chown root:root /var/named/chroot/etc/named.conf +$ sudo chmod 644 /var/named/chroot/etc/named.conf +Create and populate an appropriate zone directory within the jail, based on the +options directive. If your named.conf includes: +options { +directory "/path/to/DIRNAME "; +... +} +then copy that directory and its contents from the original zone directory: +$ sudo cp -r /path/to/DIRNAME /var/named/chroot/DIRNAME +Add or correct the following line within /etc/sysconfig/named: +ROOTDIR=/var/named/chroot + If you are running BIND in a chroot jail, then you +should use the jailed named.conf as the primary nameserver +configuration file. That is, when this guide recommends editing +/etc/named.conf, you should instead edit +/var/named/chroot/etc/named.conf. + + + Run DNS Software on Dedicated Servers + Since DNS is +a high-risk service which must frequently be made available to the entire +Internet, it is strongly recommended that no other services be offered by +systems which act as organizational DNS servers. + + + + Protect DNS Data from Tampering or Attack + This section discusses DNS configuration options which make it +more difficult for attackers to gain access to private DNS data or to modify +DNS data. + + Use Views to Partition External and Internal Information + If it is not possible to run external and internal nameservers on +separate physical systems, run BIND9 and simulate this feature using views. +Edit /etc/named.conf. Add or correct the following directives (where +SUBNET is the numerical IP representation of your organization in the form +xxx.xxx.xxx.xxx/xx): +acl internal { + SUBNET ; + localhost; +}; +view "internal-view" { + match-clients { internal; }; + zone "." IN { + type hint; + file "db.cache"; + }; + zone "internal.example.com " IN { + ... + }; +}; + +view "external-view" { + match-clients { any; }; + recursion no; + zone "example.com " IN { + ... + }; +}; + As shown in the example, database files which are +required for recursion, such as the root hints file, must be available to any +clients which are allowed to make recursive queries. Under typical +circumstances, this includes only the internal clients which are allowed to use +this server as a general-purpose nameserver. + + + Run Separate DNS Servers for External and Internal Queries + Is it possible to run external and internal nameservers on +separate systems? If so, follow the configuration guidance in this section. On +the external nameserver, edit /etc/named.conf to add or correct the +following directives: +options { + allow-query { any; }; + recursion no; + ... +}; +zone "example.com " IN { + ... +}; +On the internal nameserver, edit /etc/named.conf. Add or correct the +following directives, where SUBNET is the numerical IP representation of your +organization in the form xxx.xxx.xxx.xxx/xx: +acl internal { + SUBNET ; + localhost; +}; +options { + allow-query { internal; }; + ... +}; +zone "internal.example.com " IN { + ... +}; + + + + + Docker Service + The docker service is necessary to create containers, which are + self-sufficient and self-contained applications using the resource + isolation features of the kernel. + + + Application Whitelisting Daemon + Fapolicyd (File Access Policy Daemon) implements application whitelisting +to decide file access rights. Applications that are known via a reputation +source are allowed access while unknown applications are not. The daemon +makes use of the kernel's fanotify interface to determine file access rights. + + + Install fapolicyd Package + The fapolicyd package can be installed with the following command: + +$ sudo dnf install fapolicyd + CCI-001764 + CCI-001774 + CM-6(a) + SI-4(22) + SRG-OS-000370-GPOS-00155 + SRG-OS-000368-GPOS-00154 + SRG-OS-000480-GPOS-00230 + fapolicyd (File Access Policy Daemon) +implements application whitelisting to decide file access rights. + CCE-84224-5 + +package --add=fapolicyd + + include install_fapolicyd + +class install_fapolicyd { + package { 'fapolicyd': + ensure => 'installed', + } +} + + - name: Ensure fapolicyd is installed + package: + name: fapolicyd + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84224-5 + - NIST-800-53-CM-6(a) + - NIST-800-53-SI-4(22) + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_fapolicyd_installed + + +[[packages]] +name = "fapolicyd" +version = "*" + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if ! rpm -q --quiet "fapolicyd" ; then + dnf install -y "fapolicyd" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Enable the File Access Policy Service + The File Access Policy service should be enabled. + +The fapolicyd service can be enabled with the following command: +$ sudo systemctl enable fapolicyd.service + CCI-001764 + CCI-001774 + CM-6(a) + SI-4(22) + FMT_SMF_EXT.1 + SRG-OS-000370-GPOS-00155 + SRG-OS-000368-GPOS-00154 + SRG-OS-000480-GPOS-00230 + The fapolicyd service (File Access Policy Daemon) +implements application whitelisting to decide file access rights. + CCE-84227-8 + include enable_fapolicyd + +class enable_fapolicyd { + service {'fapolicyd': + enable => true, + ensure => 'running', + } +} + + - name: Enable service fapolicyd + block: + + - name: Gather the package facts + package_facts: + manager: auto + + - name: Enable service fapolicyd + service: + name: fapolicyd + enabled: 'yes' + state: started + masked: 'no' + when: + - '"fapolicyd" in ansible_facts.packages' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84227-8 + - NIST-800-53-CM-6(a) + - NIST-800-53-SI-4(22) + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_fapolicyd_enabled + + +[customizations.services] +enabled = ["fapolicyd"] + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" unmask 'fapolicyd.service' +"$SYSTEMCTL_EXEC" start 'fapolicyd.service' +"$SYSTEMCTL_EXEC" enable 'fapolicyd.service' + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure Fapolicy Module to Employ a Deny-all, Permit-by-exception Policy to Allow the Execution of Authorized Software Programs. + The Fapolicy module must be configured to employ a deny-all, permit-by-exception policy to allow the execution of authorized software programs and to prevent unauthorized software from running. + CCI-001764 + CM-7 (2) + CM-7 (5) (b) + CM-6 b + SRG-OS-000368-GPOS-00154 + SRG-OS-000370-GPOS-00155 + SRG-OS-000480-GPOS-00232 + Utilizing a whitelist provides a configuration management method for allowing the execution of only authorized software. +Using only authorized software decreases risk by limiting the number of potential vulnerabilities. Verification of whitelisted software occurs prior to execution or at system startup. + +Proceed with caution with enforcing the use of this daemon. +Improper configuration may render the system non-functional. +The "fapolicyd" API is not namespace aware and can cause issues when launching or running containers. + CCE-86479-3 + + + + + + + + + fapolicyd Must be Configured to Limit Access to Users Home Folders + fapolicyd needs be configured so that users cannot give access to their home folders to other users. + CCI-000366 + CM-6 b + SRG-OS-000480-GPOS-00230 + Users' home directories/folders may contain information of a sensitive nature. +Non-privileged users should coordinate any sharing of information with a System Administrator (SA) through shared resources. +fapolicyd can confine users to their home directory, not allowing them to make any changes outside of their own home directories. +Confining users to their home directory will minimize the risk of sharing information. + CCE-86018-9 + + + + + + + FTP Server + FTP is a common method for allowing remote access to +files. Like telnet, the FTP protocol is unencrypted, which means +that passwords and other data transmitted during the session can be +captured and that the session is vulnerable to hijacking. +Therefore, running the FTP server software is not recommended. + +However, there are some FTP server configurations which may +be appropriate for some environments, particularly those which +allow only read-only anonymous access as a means of downloading +data available to the public. + + Disable vsftpd if Possible + To minimize attack surface, disable vsftpd if at all +possible. + + Uninstall vsftpd Package + The vsftpd package can be removed with the following command: $ sudo dnf erase vsftpd + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + CCI-000197 + CCI-000366 + CCI-000381 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + IA-5(1)(c) + IA-5(1).1(v) + CM-7 + CM-7.1(ii) + PR.IP-1 + PR.PT-3 + SRG-OS-000074-GPOS-00042 + SRG-OS-000095-GPOS-00049 + SRG-OS-000480-GPOS-00227 + Removing the vsftpd package decreases the risk of its +accidental activation. + CCE-84159-3 + +package --remove=vsftpd + + include remove_vsftpd + +class remove_vsftpd { + package { 'vsftpd': + ensure => 'purged', + } +} + + - name: Ensure vsftpd is removed + package: + name: vsftpd + state: absent + tags: + - CCE-84159-3 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7 + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-CM-7.1(ii) + - NIST-800-53-IA-5(1)(c) + - NIST-800-53-IA-5(1).1(v) + - disable_strategy + - high_severity + - low_complexity + - low_disruption + - no_reboot_needed + - package_vsftpd_removed + + +# CAUTION: This remediation script will remove vsftpd +# from the system, and may remove any packages +# that depend on vsftpd. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "vsftpd" ; then + + dnf remove -y "vsftpd" + +fi + + + + + + + + + + Disable vsftpd Service + +The vsftpd service can be disabled with the following command: +$ sudo systemctl mask --now vsftpd.service + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + CCI-001436 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + PR.PT-3 + Running FTP server software provides a network-based avenue +of attack, and should be disabled if not needed. +Furthermore, the FTP protocol is unencrypted and creates +a risk of compromising sensitive information. + + CCE-84160-1 + include disable_vsftpd + +class disable_vsftpd { + service {'vsftpd': + enable => false, + ensure => 'stopped', + } +} + + - name: Disable service vsftpd + block: + + - name: Disable service vsftpd + systemd: + name: vsftpd.service + enabled: 'no' + state: stopped + masked: 'yes' + ignore_errors: 'yes' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84160-1 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_vsftpd_disabled + +- name: Unit Socket Exists - vsftpd.socket + command: systemctl list-unit-files vsftpd.socket + args: + warn: false + register: socket_file_exists + changed_when: false + ignore_errors: true + check_mode: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84160-1 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_vsftpd_disabled + +- name: Disable socket vsftpd + systemd: + name: vsftpd.socket + enabled: 'no' + state: stopped + masked: 'yes' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"vsftpd.socket" in socket_file_exists.stdout_lines[1]' + tags: + - CCE-84160-1 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_vsftpd_disabled + + +[customizations.services] +disabled = ["vsftpd"] + + apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + systemd: + units: + - name: vsftpd.service + enabled: false + mask: true + - name: vsftpd.socket + enabled: false + mask: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" stop 'vsftpd.service' +"$SYSTEMCTL_EXEC" disable 'vsftpd.service' +"$SYSTEMCTL_EXEC" mask 'vsftpd.service' +# Disable socket activation if we have a unit file for it +if "$SYSTEMCTL_EXEC" list-unit-files | grep -q '^vsftpd.socket'; then + "$SYSTEMCTL_EXEC" stop 'vsftpd.socket' + "$SYSTEMCTL_EXEC" mask 'vsftpd.socket' +fi +# The service may not be running because it has been started and failed, +# so let's reset the state so OVAL checks pass. +# Service should be 'inactive', not 'failed' after reboot though. +"$SYSTEMCTL_EXEC" reset-failed 'vsftpd.service' || true + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Configure vsftpd to Provide FTP Service if Necessary + The primary vsftpd configuration file is +/etc/vsftpd.conf, if that file exists, or +/etc/vsftpd/vsftpd.conf if it does not. + + Configure Firewalls to Protect the FTP Server + +By default, iptables +blocks access to the ports used by the web server. + +To configure iptables to allow port 21 traffic, one must edit +/etc/sysconfig/iptables and +/etc/sysconfig/ip6tables (if IPv6 is in use). +Add the following line, ensuring that it appears before the final LOG and DROP lines for the INPUT chain: +-A INPUT -m state --state NEW -p tcp --dport 21 -j ACCEPT +Edit the file /etc/sysconfig/iptables-config. Ensure that the space-separated list of modules contains +the FTP connection tracking module: +IPTABLES_MODULES="ip_conntrack_ftp" + These settings configure the firewall to allow connections to an FTP server. + + +The first line allows initial connections to the FTP server port. +FTP is an older protocol which is not very compatible with firewalls. During the initial FTP dialogue, the client +and server negotiate an arbitrary port to be used for data transfer. The ip_conntrack_ftp module is used by +iptables to listen to that dialogue and allow connections to the data ports which FTP negotiates. This allows an +FTP server to operate on a system which is running a firewall. + + + Restrict the Set of Users Allowed to Access FTP + This section describes how to disable non-anonymous (password-based) FTP logins, or, if it is not possible to +do this entirely due to legacy applications, how to restrict insecure FTP login to only those users who have an +identified need for this access. + + Limit Users Allowed FTP Access if Necessary + If there is a mission-critical reason for users to access their accounts via the insecure FTP protocol, limit the set of users who are allowed this access. Edit the vsftpd configuration file. Add or correct the following configuration options: +userlist_enable=YES +userlist_file=/etc/vsftp.ftpusers +userlist_deny=NO +Edit the file /etc/vsftp.ftpusers. For each user USERNAME who should be allowed to access the system via FTP, add a line containing that user's name: +USERNAME +If anonymous access is also required, add the anonymous usernames to /etc/vsftp.ftpusers as well. +anonymous +ftp + Historically, the file /etc/ftpusers contained a list of users who were not allowed to access the system via FTP. It was used to prevent system users such as the root user from logging in via the insecure FTP protocol. However, when the configuration option userlist deny=NO is set, vsftpd interprets ftpusers as the set of users who are allowed to login via FTP. Since it should be possible for most users to access their accounts via secure protocols, it is recommended that this setting be used, so that non-anonymous FTP access can be limited to legacy users who have been explicitly identified. + + + + + Use vsftpd to Provide FTP Service if Necessary + If your use-case requires FTP service, install and +set-up vsftpd to provide it. + + + + Web Server + The web server is responsible for providing access to +content via the HTTP protocol. Web servers represent a significant +security risk because: + +The HTTP port is commonly probed by malicious sourcesWeb server software is very complex, and includes a long +history of vulnerabilitiesThe HTTP protocol is unencrypted and vulnerable to passive +monitoring + +The system's default web server software is Apache 2 and is +provided in the RPM package httpd. + + Disable Apache if Possible + If Apache was installed and activated, but the system +does not need to act as a web server, then it should be disabled +and removed from the system. + + Uninstall httpd Package + +The httpd package can be removed with the following command: + +$ sudo dnf erase httpd + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + PR.PT-3 + If there is no need to make the web server software available, +removing it provides a safeguard against its activation. + CCE-85974-4 + +package --remove=httpd + + include remove_httpd + +class remove_httpd { + package { 'httpd': + ensure => 'purged', + } +} + + - name: Ensure httpd is removed + package: + name: httpd + state: absent + tags: + - CCE-85974-4 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - no_reboot_needed + - package_httpd_removed + - unknown_severity + + +# CAUTION: This remediation script will remove httpd +# from the system, and may remove any packages +# that depend on httpd. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "httpd" ; then + + dnf remove -y "httpd" + +fi + + + + + + + + + + Disable httpd Service + +The httpd service can be disabled with the following command: +$ sudo systemctl mask --now httpd.service + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + PR.PT-3 + Running web server software provides a network-based avenue +of attack, and should be disabled if not needed. + + CCE-84213-8 + include disable_httpd + +class disable_httpd { + service {'httpd': + enable => false, + ensure => 'stopped', + } +} + + - name: Disable service httpd + block: + + - name: Disable service httpd + systemd: + name: httpd.service + enabled: 'no' + state: stopped + masked: 'yes' + ignore_errors: 'yes' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84213-8 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - no_reboot_needed + - service_httpd_disabled + - unknown_severity + +- name: Unit Socket Exists - httpd.socket + command: systemctl list-unit-files httpd.socket + args: + warn: false + register: socket_file_exists + changed_when: false + ignore_errors: true + check_mode: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84213-8 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - no_reboot_needed + - service_httpd_disabled + - unknown_severity + +- name: Disable socket httpd + systemd: + name: httpd.socket + enabled: 'no' + state: stopped + masked: 'yes' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"httpd.socket" in socket_file_exists.stdout_lines[1]' + tags: + - CCE-84213-8 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - no_reboot_needed + - service_httpd_disabled + - unknown_severity + + +[customizations.services] +disabled = ["httpd"] + + apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + systemd: + units: + - name: httpd.service + enabled: false + mask: true + - name: httpd.socket + enabled: false + mask: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" stop 'httpd.service' +"$SYSTEMCTL_EXEC" disable 'httpd.service' +"$SYSTEMCTL_EXEC" mask 'httpd.service' +# Disable socket activation if we have a unit file for it +if "$SYSTEMCTL_EXEC" list-unit-files | grep -q '^httpd.socket'; then + "$SYSTEMCTL_EXEC" stop 'httpd.socket' + "$SYSTEMCTL_EXEC" mask 'httpd.socket' +fi +# The service may not be running because it has been started and failed, +# so let's reset the state so OVAL checks pass. +# Service should be 'inactive', not 'failed' after reboot though. +"$SYSTEMCTL_EXEC" reset-failed 'httpd.service' || true + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Install Apache if Necessary + If httpd was not installed and activated, but the system +needs to act as a web server, then it should be installed on the system. Follow these +guidelines to install it defensively. The httpd package can be installed with +the following command: +$ sudo yum install httpd +This method of installation is recommended over installing the "Web Server" +package group during the system installation process. The Web Server package +group includes many packages which are likely extraneous, while the +command-line method installs only the required httpd package itself. + + Confirm Minimal Built-in Modules Installed + The default httpd installation minimizes the number of +modules that are compiled directly into the binary (core prefork http_core +mod_so). This minimizes risk by limiting the capabilities allowed by the +web server. + +Query the set of compiled-in modules using the following command: +$ httpd -l +If the number of compiled-in modules is significantly larger than the +aforementioned set, this guide recommends re-installing httpd with a +reduced configuration. Minimizing the number of modules that are compiled into +the httpd binary, reduces risk by limiting the capabilities allowed by +the webserver. + + + + Secure Apache Configuration + The httpd configuration file is +/etc/httpd/conf/httpd.conf. Apply the recommendations in the remainder +of this section to this file. + + HTTPD Log Level + The setting for LogLevel in /etc/httpd/conf/httpd.conf + alert + crit + warn + emerg + error + warn + + + Maximum KeepAlive Requests for HTTPD + The setting for MaxKeepAliveRequests in httpd.conf + 100 + 1000 + 10000 + 100000 + 500 + 100 + + + Configure Operating System to Protect Web Server + The following configuration steps should be taken on the system which hosts the +web server, in order to provide as safe an environment as possible for the web server. + + Run httpd in a chroot Jail if Practical + Running httpd inside a chroot jail is designed to isolate the +web server process to a small section of the filesystem, limiting the damage if +it is compromised. Versions of Apache greater than 2.2.10 (such as the one +included with Red Hat Enterprise Linux 9) provide the ChrootDir directive. To run Apache +inside a chroot jail in /chroot/apache, add the following line to +/etc/httpd/conf/httpd.conf: ChrootDir /chroot/apache This +necessitates placing all files required by httpd inside +/chroot/apache , including httpd's binaries, modules, +configuration files, and served web pages. The details of this configuration +are beyond the scope of this guide. This may also require additional SELinux +configuration. + + + Restrict File and Directory Access + Minimize access to critical httpd files and directories. + + Set Permissions on All Configuration Files Inside /etc/httpd/conf.d/ + To properly set the permissions of /etc/http/conf.d/*, run the command: $ sudo chmod 0640 /etc/http/conf.d/* + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + AC-6(1) + PR.IP-1 + PR.PT-3 + Access to the web server's configuration files may allow an unauthorized user or attacker +to access information about the web server or to alter the server's configuration files. + - name: Find /etc/httpd/conf.d/ file(s) + command: find -H /etc/httpd/conf.d/ -maxdepth 1 -perm /u+xs,g+xws,o+xwrt -type f + -regex "^.*$" + register: files_found + changed_when: false + failed_when: false + check_mode: false + tags: + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - configure_strategy + - file_permissions_httpd_server_conf_d_files + - low_complexity + - low_disruption + - no_reboot_needed + - unknown_severity + +- name: Set permissions for /etc/httpd/conf.d/ file(s) + file: + path: '{{ item }}' + mode: u-xs,g-xws,o-xwrt + state: file + with_items: + - '{{ files_found.stdout_lines }}' + tags: + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - configure_strategy + - file_permissions_httpd_server_conf_d_files + - low_complexity + - low_disruption + - no_reboot_needed + - unknown_severity + + + + + +find -H /etc/httpd/conf.d/ -maxdepth 1 -perm /u+xs,g+xws,o+xwrt -type f -regex '^.*$' -exec chmod u-xs,g-xws,o-xwrt {} \; + + + + + + + + + + Set Permissions on All Configuration Files Inside /etc/httpd/conf/ + To properly set the permissions of /etc/http/conf/*, run the command: $ sudo chmod 0640 /etc/http/conf/* + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + AC-6(1) + PR.IP-1 + PR.PT-3 + Access to the web server's configuration files may allow an unauthorized user or attacker +to access information about the web server or to alter the server's configuration files. + - name: Find /etc/httpd/conf/ file(s) + command: find -H /etc/httpd/conf/ -maxdepth 1 -perm /u+xs,g+xws,o+xwrt -type f -regex + "^.*$" + register: files_found + changed_when: false + failed_when: false + check_mode: false + tags: + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - configure_strategy + - file_permissions_httpd_server_conf_files + - low_complexity + - low_disruption + - no_reboot_needed + - unknown_severity + +- name: Set permissions for /etc/httpd/conf/ file(s) + file: + path: '{{ item }}' + mode: u-xs,g-xws,o-xwrt + state: file + with_items: + - '{{ files_found.stdout_lines }}' + tags: + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - configure_strategy + - file_permissions_httpd_server_conf_files + - low_complexity + - low_disruption + - no_reboot_needed + - unknown_severity + + + + + +find -H /etc/httpd/conf/ -maxdepth 1 -perm /u+xs,g+xws,o+xwrt -type f -regex '^.*$' -exec chmod u-xs,g-xws,o-xwrt {} \; + + + + + + + + + + Set Permissions on All Configuration Files Inside /etc/httpd/conf.modules.d/ + To properly set the permissions of /etc/http/conf.modules.d/*, run the command: $ sudo chmod 0640 /etc/http/conf.modules.d/* + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + AC-6(1) + PR.IP-1 + PR.PT-3 + Access to the web server's configuration files may allow an unauthorized user or attacker +to access information about the web server or to alter the server's configuration files. + + + + + + + + + + + Configure PERL Securely + PERL (Practical Extraction and Report Language) is an interpreted language +optimized for scanning arbitrary text files, extracting information from those +text files, and printing reports based on that information. The language is +often used in shell scripting and is intended to be practical, easy to use, and +efficient means of generating interactive web pages for the user. + + + Configure PHP Securely + PHP is a widely-used and often misconfigured server-side scripting language. It should +be used with caution, but configured appropriately when needed. + +Review /etc/php.ini and make the following changes if possible: +# Do not expose PHP error messages to external users +display_errors = Off + +# Enable safe mode +safe_mode = On + +# Only allow access to executables in isolated directory +safe_mode_exec_dir = php-required-executables-path + +# Limit external access to PHP environment +safe_mode_allowed_env_vars = PHP_ + +# Restrict PHP information leakage +expose_php = Off + +# Log all errors +log_errors = On + +# Do not register globals for input data +register_globals = Off + +# Minimize allowable PHP post size +post_max_size = 1K + +# Ensure PHP redirects appropriately +cgi.force_redirect = 0 + +# Disallow uploading unless necessary +file_uploads = Off + +# Disallow treatment of file requests as fopen calls +allow_url_fopen = Off + +# Enable SQL safe mode +sql.safe_mode = On + + + + Directory Restrictions + The Directory tags in the web server configuration file allow finer grained access +control for a specified directory. All web directories should be configured on a +case-by-case basis, allowing access only where needed. + + + Minimize Web Server Loadable Modules + A default installation of httpd includes a plethora of dynamically shared objects (DSO) +that are loaded at run-time. Unlike the aforementioned compiled-in modules, a DSO can be +disabled in the configuration file by removing the corresponding LoadModule directive. + +Note: A DSO only provides additional functionality if associated directives are included +in the httpd configuration file. It should also be noted that removing a DSO will produce +errors on httpd startup if the configuration file contains directives that apply to that +module. Refer to http://httpd.apache.org/docs/ for details on which directives +are associated with each DSO. + +Following each DSO removal, the configuration can be tested with the following command +to check if everything still works: +$ sudo service httpd configtest +The purpose of each of the modules loaded by default will now be addressed one at a time. +If none of a module's directives are being used, remove it. + + httpd Core Modules + These modules comprise a basic subset of modules that are likely needed for base httpd +functionality; ensure they are not commented out in /etc/httpd/conf/httpd.conf: +LoadModule auth_basic_module modules/mod_auth_basic.so +LoadModule authn_default_module modules/mod_authn_default.so +LoadModule authz_host_module modules/mod_authz_host.so +LoadModule authz_user_module modules/mod_authz_user.so +LoadModule authz_groupfile_module modules/mod_authz_groupfile.so +LoadModule authz_default_module modules/mod_authz_default.so +LoadModule log_config_module modules/mod_log_config.so +LoadModule logio_module modules/mod_logio.so +LoadModule setenvif_module modules/mod_setenvif.so +LoadModule mime_module modules/mod_mome.so +LoadModule autoindex_module modules/mod_autoindex.so +LoadModule negotiation_module modules/mod_negotiation.so +LoadModule dir_module modules/mod_dir.so +LoadModule alias_module modules/mod_alias.so +Minimizing the number of loadable modules available to the web server reduces risk +by limiting the capabilities allowed by the web server. + + Minimize Modules for HTTP Basic Authentication + The following modules are necessary if this web server will provide content that will +be restricted by a password. + +Authentication can be performed using local plain text password files (authn_file), +local DBM password files (authn_dbm) or an LDAP directory. The only module required by +the web server depends on your choice of authentication. Comment out the modules you don't +need from the following: +LoadModule authn_file_module modules/mod_authn_file.so +LoadModule authn_dbm_module modules/mod_authn_dbm.so +authn_alias allows for authentication based on aliases. authn_anon +allows anonymous authentication similar to that of anonymous ftp sites. authz_owner +allows authorization based on file ownership. authz_dbm allows for authorization +based on group membership if the web server is using DBM authentication. + +If the above functionality is unnecessary, comment out the related module: +#LoadModule authn_alias_module modules/mod_authn_alias.so +#LoadModule authn_anon_module modules/mod_authn_anon.so +#LoadModule authz_owner_module modules/mod_authz_owner.so +#LoadModule authz_dbm_module modules/mod_authz_dbm.so + + + Minimize Configuration Files Included + The Include directive directs httpd to load supplementary configuration files +from a provided path. The default configuration loads all files that end in .conf +from the /etc/httpd/conf.d directory. + +To restrict excess configuration, the following line should be commented out and +replaced with Include directives that only reference required configuration files: +#Include conf.d/*.conf +If the above change was made, ensure that the SSL encryption remains loaded by +explicitly including the corresponding configuration file: +Include conf.d/ssl.conf +If PHP is necessary, a similar alteration must be made: +Include conf.d/php.conf + +Explicitly listing the configuration files to be loaded during web server start-up avoids +the possibility of unwanted or malicious configuration files to be automatically included as +part of the server's running configuration. + + + Minimize Various Optional Components + The following modules perform very specific tasks, sometimes providing access to +just a few additional directives. If such functionality is not required (or if you +are not using these directives), comment out the associated module: +External filtering (response passed through external program prior to client delivery) +#LoadModule ext_filter_module modules/mod_ext_filter.soUser-specified Cache Control and Expiration +#LoadModule expires_module modules/mod_expires.soCompression Output Filter (provides content compression prior to client delivery) +#LoadModule deflate_module modules/mod_deflate.soHTTP Response/Request Header Customization +#LoadModule headers_module modules/mod_headers.soUser activity monitoring via cookies +#LoadModule usertrack_module modules/mod_usertrack.soDynamically configured mass virtual hosting +#LoadModule vhost_alias_module modules/mod_vhost_alias.so +Minimizing the number of loadable modules available to the web server reduces risk +by limiting the capabilities allowed by the web server. + + + + + Use Appropriate Modules to Improve httpd's Security + Among the modules available for httpd are several whose use may improve the +security of the web server installation. This section recommends and discusses +the deployment of security-relevant modules. + + Deploy mod_security + The security module provides an application level firewall for httpd. +Following its installation with the base ruleset, specific configuration advice can be found at + + http://www.modsecurity.org/ to design a policy that best matches the security needs of +the web applications. Usage of mod_security is highly recommended for some environments, +but it should be noted this module does not ship with Red Hat Enterprise Linux itself, +and instead is provided via Extra Packages for Enterprise Linux (EPEL). +For more information on EPEL please refer to + http://fedoraproject.org/wiki/EPEL. + + + Deploy mod_ssl + Because HTTP is a plain text protocol, all traffic is susceptible to passive +monitoring. If there is a need for confidentiality, SSL should be configured +and enabled to encrypt content. + +Note: mod_nss is a FIPS 140-2 certified alternative to mod_ssl. +The modules share a considerable amount of code and should be nearly identical +in functionality. If FIPS 140-2 validation is required, then mod_nss should +be used. If it provides some feature or its greater compatibility is required, +then mod_ssl should be used. + + + + Restrict Web Server Information Leakage + The ServerTokens and ServerSignature directives determine how +much information the web server discloses about the configuration of the +system. + + + Configure HTTPD-Served Web Content Securely + Running httpd inside a chroot jail is designed to isolate the +web server process to a small section of the filesystem, limiting the damage if +it is compromised. Versions of Apache greater than 2.2.10 (such as the one +included with Red Hat Enterprise Linux 7) provide the ChrootDir directive. To run Apache +inside a chroot jail in /chroot/apache, add the following line to +/etc/httpd/conf/httpd.conf: ChrootDir /chroot/apache This +necessitates placing all files required by httpd inside +/chroot/apache , including httpd's binaries, modules, +configuration files, and served web pages. The details of this configuration +are beyond the scope of this guide. This may also require additional SELinux +configuration. + + Web Login Banner Verbiage + Enter an appropriate login banner for your organization. Please note that new lines must +be expressed by the '\n' character and special characters like parentheses and quotation marks must be escaped with '\\'. + ^(You[\s\n]+are[\s\n]+accessing[\s\n]+a[\s\n]+U\.S\.[\s\n]+Government[\s\n]+\(USG\)[\s\n]+Information[\s\n]+System[\s\n]+\(IS\)[\s\n]+that[\s\n]+is[\s\n]+provided[\s\n]+for[\s\n]+USG\-authorized[\s\n]+use[\s\n]+only\.[\s\n]+By[\s\n]+using[\s\n]+this[\s\n]+IS[\s\n]+\(which[\s\n]+includes[\s\n]+any[\s\n]+device[\s\n]+attached[\s\n]+to[\s\n]+this[\s\n]+IS\)\,[\s\n]+you[\s\n]+consent[\s\n]+to[\s\n]+the[\s\n]+following[\s\n]+conditions\:(?:[\n]+|(?:\\n)+)\-The[\s\n]+USG[\s\n]+routinely[\s\n]+intercepts[\s\n]+and[\s\n]+monitors[\s\n]+communications[\s\n]+on[\s\n]+this[\s\n]+IS[\s\n]+for[\s\n]+purposes[\s\n]+including\,[\s\n]+but[\s\n]+not[\s\n]+limited[\s\n]+to\,[\s\n]+penetration[\s\n]+testing\,[\s\n]+COMSEC[\s\n]+monitoring\,[\s\n]+network[\s\n]+operations[\s\n]+and[\s\n]+defense\,[\s\n]+personnel[\s\n]+misconduct[\s\n]+\(PM\)\,[\s\n]+law[\s\n]+enforcement[\s\n]+\(LE\)\,[\s\n]+and[\s\n]+counterintelligence[\s\n]+\(CI\)[\s\n]+investigations\.(?:[\n]+|(?:\\n)+)\-At[\s\n]+any[\s\n]+time\,[\s\n]+the[\s\n]+USG[\s\n]+may[\s\n]+inspect[\s\n]+and[\s\n]+seize[\s\n]+data[\s\n]+stored[\s\n]+on[\s\n]+this[\s\n]+IS\.(?:[\n]+|(?:\\n)+)\-Communications[\s\n]+using\,[\s\n]+or[\s\n]+data[\s\n]+stored[\s\n]+on\,[\s\n]+this[\s\n]+IS[\s\n]+are[\s\n]+not[\s\n]+private\,[\s\n]+are[\s\n]+subject[\s\n]+to[\s\n]+routine[\s\n]+monitoring\,[\s\n]+interception\,[\s\n]+and[\s\n]+search\,[\s\n]+and[\s\n]+may[\s\n]+be[\s\n]+disclosed[\s\n]+or[\s\n]+used[\s\n]+for[\s\n]+any[\s\n]+USG\-authorized[\s\n]+purpose\.(?:[\n]+|(?:\\n)+)\-This[\s\n]+IS[\s\n]+includes[\s\n]+security[\s\n]+measures[\s\n]+\(e\.g\.\,[\s\n]+authentication[\s\n]+and[\s\n]+access[\s\n]+controls\)[\s\n]+to[\s\n]+protect[\s\n]+USG[\s\n]+interests\-\-not[\s\n]+for[\s\n]+your[\s\n]+personal[\s\n]+benefit[\s\n]+or[\s\n]+privacy\.(?:[\n]+|(?:\\n)+)\-Notwithstanding[\s\n]+the[\s\n]+above\,[\s\n]+using[\s\n]+this[\s\n]+IS[\s\n]+does[\s\n]+not[\s\n]+constitute[\s\n]+consent[\s\n]+to[\s\n]+PM\,[\s\n]+LE[\s\n]+or[\s\n]+CI[\s\n]+investigative[\s\n]+searching[\s\n]+or[\s\n]+monitoring[\s\n]+of[\s\n]+the[\s\n]+content[\s\n]+of[\s\n]+privileged[\s\n]+communications\,[\s\n]+or[\s\n]+work[\s\n]+product\,[\s\n]+related[\s\n]+to[\s\n]+personal[\s\n]+representation[\s\n]+or[\s\n]+services[\s\n]+by[\s\n]+attorneys\,[\s\n]+psychotherapists\,[\s\n]+or[\s\n]+clergy\,[\s\n]+and[\s\n]+their[\s\n]+assistants\.[\s\n]+Such[\s\n]+communications[\s\n]+and[\s\n]+work[\s\n]+product[\s\n]+are[\s\n]+private[\s\n]+and[\s\n]+confidential\.[\s\n]+See[\s\n]+User[\s\n]+Agreement[\s\n]+for[\s\n]+details\.|I've[\s\n]+read[\s\n]+\&[\s\n]+consent[\s\n]+to[\s\n]+terms[\s\n]+in[\s\n]+IS[\s\n]+user[\s\n]+agreem't\.)$ + ^You[\s\n]+are[\s\n]+accessing[\s\n]+a[\s\n]+U\.S\.[\s\n]+Government[\s\n]+\(USG\)[\s\n]+Information[\s\n]+System[\s\n]+\(IS\)[\s\n]+that[\s\n]+is[\s\n]+provided[\s\n]+for[\s\n]+USG\-authorized[\s\n]+use[\s\n]+only\.[\s\n]+By[\s\n]+using[\s\n]+this[\s\n]+IS[\s\n]+\(which[\s\n]+includes[\s\n]+any[\s\n]+device[\s\n]+attached[\s\n]+to[\s\n]+this[\s\n]+IS\)\,[\s\n]+you[\s\n]+consent[\s\n]+to[\s\n]+the[\s\n]+following[\s\n]+conditions\:(?:[\n]+|(?:\\n)+)\-The[\s\n]+USG[\s\n]+routinely[\s\n]+intercepts[\s\n]+and[\s\n]+monitors[\s\n]+communications[\s\n]+on[\s\n]+this[\s\n]+IS[\s\n]+for[\s\n]+purposes[\s\n]+including\,[\s\n]+but[\s\n]+not[\s\n]+limited[\s\n]+to\,[\s\n]+penetration[\s\n]+testing\,[\s\n]+COMSEC[\s\n]+monitoring\,[\s\n]+network[\s\n]+operations[\s\n]+and[\s\n]+defense\,[\s\n]+personnel[\s\n]+misconduct[\s\n]+\(PM\)\,[\s\n]+law[\s\n]+enforcement[\s\n]+\(LE\)\,[\s\n]+and[\s\n]+counterintelligence[\s\n]+\(CI\)[\s\n]+investigations\.(?:[\n]+|(?:\\n)+)\-At[\s\n]+any[\s\n]+time\,[\s\n]+the[\s\n]+USG[\s\n]+may[\s\n]+inspect[\s\n]+and[\s\n]+seize[\s\n]+data[\s\n]+stored[\s\n]+on[\s\n]+this[\s\n]+IS\.(?:[\n]+|(?:\\n)+)\-Communications[\s\n]+using\,[\s\n]+or[\s\n]+data[\s\n]+stored[\s\n]+on\,[\s\n]+this[\s\n]+IS[\s\n]+are[\s\n]+not[\s\n]+private\,[\s\n]+are[\s\n]+subject[\s\n]+to[\s\n]+routine[\s\n]+monitoring\,[\s\n]+interception\,[\s\n]+and[\s\n]+search\,[\s\n]+and[\s\n]+may[\s\n]+be[\s\n]+disclosed[\s\n]+or[\s\n]+used[\s\n]+for[\s\n]+any[\s\n]+USG\-authorized[\s\n]+purpose\.(?:[\n]+|(?:\\n)+)\-This[\s\n]+IS[\s\n]+includes[\s\n]+security[\s\n]+measures[\s\n]+\(e\.g\.\,[\s\n]+authentication[\s\n]+and[\s\n]+access[\s\n]+controls\)[\s\n]+to[\s\n]+protect[\s\n]+USG[\s\n]+interests\-\-not[\s\n]+for[\s\n]+your[\s\n]+personal[\s\n]+benefit[\s\n]+or[\s\n]+privacy\.(?:[\n]+|(?:\\n)+)\-Notwithstanding[\s\n]+the[\s\n]+above\,[\s\n]+using[\s\n]+this[\s\n]+IS[\s\n]+does[\s\n]+not[\s\n]+constitute[\s\n]+consent[\s\n]+to[\s\n]+PM\,[\s\n]+LE[\s\n]+or[\s\n]+CI[\s\n]+investigative[\s\n]+searching[\s\n]+or[\s\n]+monitoring[\s\n]+of[\s\n]+the[\s\n]+content[\s\n]+of[\s\n]+privileged[\s\n]+communications\,[\s\n]+or[\s\n]+work[\s\n]+product\,[\s\n]+related[\s\n]+to[\s\n]+personal[\s\n]+representation[\s\n]+or[\s\n]+services[\s\n]+by[\s\n]+attorneys\,[\s\n]+psychotherapists\,[\s\n]+or[\s\n]+clergy\,[\s\n]+and[\s\n]+their[\s\n]+assistants\.[\s\n]+Such[\s\n]+communications[\s\n]+and[\s\n]+work[\s\n]+product[\s\n]+are[\s\n]+private[\s\n]+and[\s\n]+confidential\.[\s\n]+See[\s\n]+User[\s\n]+Agreement[\s\n]+for[\s\n]+details\.$ + ^I've[\s\n]+read[\s\n]+\&[\s\n]+consent[\s\n]+to[\s\n]+terms[\s\n]+in[\s\n]+IS[\s\n]+user[\s\n]+agreem't\.$ + ^Use[\s\n]+of[\s\n]+this[\s\n]+or[\s\n]+any[\s\n]+other[\s\n]+DoD[\s\n]+interest[\s\n]+computer[\s\n]+system[\s\n]+constitutes[\s\n]+consent[\s\n]+to[\s\n]+monitoring[\s\n]+at[\s\n]+all[\s\n]+times\.[\s\n]+This[\s\n]+is[\s\n]+a[\s\n]+DoD[\s\n]+interest[\s\n]+computer[\s\n]+system\.[\s\n]+All[\s\n]+DoD[\s\n]+interest[\s\n]+computer[\s\n]+systems[\s\n]+and[\s\n]+related[\s\n]+equipment[\s\n]+are[\s\n]+intended[\s\n]+for[\s\n]+the[\s\n]+communication\,[\s\n]+transmission\,[\s\n]+processing\,[\s\n]+and[\s\n]+storage[\s\n]+of[\s\n]+official[\s\n]+U\.S\.[\s\n]+Government[\s\n]+or[\s\n]+other[\s\n]+authorized[\s\n]+information[\s\n]+only\.[\s\n]+All[\s\n]+DoD[\s\n]+interest[\s\n]+computer[\s\n]+systems[\s\n]+are[\s\n]+subject[\s\n]+to[\s\n]+monitoring[\s\n]+at[\s\n]+all[\s\n]+times[\s\n]+to[\s\n]+ensure[\s\n]+proper[\s\n]+functioning[\s\n]+of[\s\n]+equipment[\s\n]+and[\s\n]+systems[\s\n]+including[\s\n]+security[\s\n]+devices[\s\n]+and[\s\n]+systems\,[\s\n]+to[\s\n]+prevent[\s\n]+unauthorized[\s\n]+use[\s\n]+and[\s\n]+violations[\s\n]+of[\s\n]+statutes[\s\n]+and[\s\n]+security[\s\n]+regulations\,[\s\n]+to[\s\n]+deter[\s\n]+criminal[\s\n]+activity\,[\s\n]+and[\s\n]+for[\s\n]+other[\s\n]+similar[\s\n]+purposes\.[\s\n]+Any[\s\n]+user[\s\n]+of[\s\n]+a[\s\n]+DoD[\s\n]+interest[\s\n]+computer[\s\n]+system[\s\n]+should[\s\n]+be[\s\n]+aware[\s\n]+that[\s\n]+any[\s\n]+information[\s\n]+placed[\s\n]+in[\s\n]+the[\s\n]+system[\s\n]+is[\s\n]+subject[\s\n]+to[\s\n]+monitoring[\s\n]+and[\s\n]+is[\s\n]+not[\s\n]+subject[\s\n]+to[\s\n]+any[\s\n]+expectation[\s\n]+of[\s\n]+privacy\.[\s\n]+If[\s\n]+monitoring[\s\n]+of[\s\n]+this[\s\n]+or[\s\n]+any[\s\n]+other[\s\n]+DoD[\s\n]+interest[\s\n]+computer[\s\n]+system[\s\n]+reveals[\s\n]+possible[\s\n]+evidence[\s\n]+of[\s\n]+violation[\s\n]+of[\s\n]+criminal[\s\n]+statutes\,[\s\n]+this[\s\n]+evidence[\s\n]+and[\s\n]+any[\s\n]+other[\s\n]+related[\s\n]+information\,[\s\n]+including[\s\n]+identification[\s\n]+information[\s\n]+about[\s\n]+the[\s\n]+user\,[\s\n]+may[\s\n]+be[\s\n]+provided[\s\n]+to[\s\n]+law[\s\n]+enforcement[\s\n]+officials\.[\s\n]+If[\s\n]+monitoring[\s\n]+of[\s\n]+this[\s\n]+or[\s\n]+any[\s\n]+other[\s\n]+DoD[\s\n]+interest[\s\n]+computer[\s\n]+systems[\s\n]+reveals[\s\n]+violations[\s\n]+of[\s\n]+security[\s\n]+regulations[\s\n]+or[\s\n]+unauthorized[\s\n]+use\,[\s\n]+employees[\s\n]+who[\s\n]+violate[\s\n]+security[\s\n]+regulations[\s\n]+or[\s\n]+make[\s\n]+unauthorized[\s\n]+use[\s\n]+of[\s\n]+DoD[\s\n]+interest[\s\n]+computer[\s\n]+systems[\s\n]+are[\s\n]+subject[\s\n]+to[\s\n]+appropriate[\s\n]+disciplinary[\s\n]+action\.[\s\n]+Use[\s\n]+of[\s\n]+this[\s\n]+or[\s\n]+any[\s\n]+other[\s\n]+DoD[\s\n]+interest[\s\n]+computer[\s\n]+system[\s\n]+constitutes[\s\n]+consent[\s\n]+to[\s\n]+monitoring[\s\n]+at[\s\n]+all[\s\n]+times\.$ + ^\-\-[\s\n]+WARNING[\s\n]+\-\-[\s\n]+This[\s\n]+system[\s\n]+is[\s\n]+for[\s\n]+the[\s\n]+use[\s\n]+of[\s\n]+authorized[\s\n]+users[\s\n]+only\.[\s\n]+Individuals[\s\n]+using[\s\n]+this[\s\n]+computer[\s\n]+system[\s\n]+without[\s\n]+authority[\s\n]+or[\s\n]+in[\s\n]+excess[\s\n]+of[\s\n]+their[\s\n]+authority[\s\n]+are[\s\n]+subject[\s\n]+to[\s\n]+having[\s\n]+all[\s\n]+their[\s\n]+activities[\s\n]+on[\s\n]+this[\s\n]+system[\s\n]+monitored[\s\n]+and[\s\n]+recorded[\s\n]+by[\s\n]+system[\s\n]+personnel\.[\s\n]+Anyone[\s\n]+using[\s\n]+this[\s\n]+system[\s\n]+expressly[\s\n]+consents[\s\n]+to[\s\n]+such[\s\n]+monitoring[\s\n]+and[\s\n]+is[\s\n]+advised[\s\n]+that[\s\n]+if[\s\n]+such[\s\n]+monitoring[\s\n]+reveals[\s\n]+possible[\s\n]+evidence[\s\n]+of[\s\n]+criminal[\s\n]+activity[\s\n]+system[\s\n]+personal[\s\n]+may[\s\n]+provide[\s\n]+the[\s\n]+evidence[\s\n]+of[\s\n]+such[\s\n]+monitoring[\s\n]+to[\s\n]+law[\s\n]+enforcement[\s\n]+officials\.$ + + + + Use Denial-of-Service Protection Modules + Denial-of-service attacks are difficult to detect and prevent while maintaining +acceptable access to authorized users. However, some traffic-shaping +modules can be used to address the problem. Well-known DoS protection modules include: +mod_cband mod_bwshare mod_limitipconn mod_evasive +Denial-of-service prevention should be implemented for a web server if such a threat exists. +However, specific configuration details are very dependent on the environment and often best left +at the discretion of the administrator. + + + + + IMAP and POP3 Server + Dovecot provides IMAP and POP3 services. It is not +installed by default. The project page at + http://www.dovecot.org +contains more detailed information about Dovecot +configuration. + + Configure Dovecot if Necessary + If the system will operate as an IMAP or +POP3 server, the dovecot software should be configured securely by following +the recommendations below. + + Allow IMAP Clients to Access the Server + +The default iptables configuration does not allow inbound access to any services. +This modification will allow remote hosts to initiate connections to the IMAP daemon, +while keeping all other ports on the server in their default protected state. +To configure iptables to allow port 143 traffic, one must edit +/etc/sysconfig/iptables and +/etc/sysconfig/ip6tables (if IPv6 is in use). +Add the following line, ensuring that it appears before the final LOG and DROP lines for the INPUT chain: +-A INPUT -m state --state NEW -p tcp --dport 143 -j ACCEPT + + + Enable SSL Support + SSL should be used to encrypt network traffic between the +Dovecot server and its clients. Users must authenticate to the Dovecot +server in order to read their mail, and passwords should never be +transmitted in clear text. In addition, protecting mail as it is +downloaded is a privacy measure, and clients may use SSL certificates +to authenticate the server, preventing another system from impersonating +the server. + + + Support Only the Necessary Protocols + Dovecot supports the IMAP and POP3 protocols, as well as +SSL-protected versions of those protocols. Configure the Dovecot server +to support only the protocols needed by your site. Edit /etc/dovecot/dovecot.conf. +Add or correct the following lines, replacing PROTOCOL with +only the subset of protocols (imap, imaps, +pop3, pop3s) required: +protocols = PROTOCOL +If possible, require SSL protection for all transactions. The SSL +protocol variants listen on alternate ports (995 instead of 110 for +pop3s, and 993 instead of 143 for imaps), and require SSL-aware clients. +An alternate approach is to listen on the standard port and require the +client to use the STARTTLS command before authenticating. + + + + Disable Dovecot + If the system does not need to operate as an IMAP or +POP3 server, the dovecot software should be disabled and removed. + + Uninstall dovecot Package + +The dovecot package can be removed with the following command: + +$ sudo dnf erase dovecot + If there is no need to make the Dovecot software available, +removing it provides a safeguard against its activation. + CCE-85977-7 + +package --remove=dovecot + + include remove_dovecot + +class remove_dovecot { + package { 'dovecot': + ensure => 'purged', + } +} + + - name: Ensure dovecot is removed + package: + name: dovecot + state: absent + tags: + - CCE-85977-7 + - disable_strategy + - low_complexity + - low_disruption + - no_reboot_needed + - package_dovecot_removed + - unknown_severity + + +# CAUTION: This remediation script will remove dovecot +# from the system, and may remove any packages +# that depend on dovecot. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "dovecot" ; then + + dnf remove -y "dovecot" + +fi + + + + + + + + + + Disable Dovecot Service + +The dovecot service can be disabled with the following command: +$ sudo systemctl mask --now dovecot.service + Running an IMAP or POP3 server provides a network-based +avenue of attack, and should be disabled if not needed. + + CCE-84242-7 + include disable_dovecot + +class disable_dovecot { + service {'dovecot': + enable => false, + ensure => 'stopped', + } +} + + - name: Disable service dovecot + block: + + - name: Disable service dovecot + systemd: + name: dovecot.service + enabled: 'no' + state: stopped + masked: 'yes' + ignore_errors: 'yes' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84242-7 + - disable_strategy + - low_complexity + - low_disruption + - no_reboot_needed + - service_dovecot_disabled + - unknown_severity + +- name: Unit Socket Exists - dovecot.socket + command: systemctl list-unit-files dovecot.socket + args: + warn: false + register: socket_file_exists + changed_when: false + ignore_errors: true + check_mode: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84242-7 + - disable_strategy + - low_complexity + - low_disruption + - no_reboot_needed + - service_dovecot_disabled + - unknown_severity + +- name: Disable socket dovecot + systemd: + name: dovecot.socket + enabled: 'no' + state: stopped + masked: 'yes' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"dovecot.socket" in socket_file_exists.stdout_lines[1]' + tags: + - CCE-84242-7 + - disable_strategy + - low_complexity + - low_disruption + - no_reboot_needed + - service_dovecot_disabled + - unknown_severity + + +[customizations.services] +disabled = ["dovecot"] + + apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + systemd: + units: + - name: dovecot.service + enabled: false + mask: true + - name: dovecot.socket + enabled: false + mask: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" stop 'dovecot.service' +"$SYSTEMCTL_EXEC" disable 'dovecot.service' +"$SYSTEMCTL_EXEC" mask 'dovecot.service' +# Disable socket activation if we have a unit file for it +if "$SYSTEMCTL_EXEC" list-unit-files | grep -q '^dovecot.socket'; then + "$SYSTEMCTL_EXEC" stop 'dovecot.socket' + "$SYSTEMCTL_EXEC" mask 'dovecot.socket' +fi +# The service may not be running because it has been started and failed, +# so let's reset the state so OVAL checks pass. +# Service should be 'inactive', not 'failed' after reboot though. +"$SYSTEMCTL_EXEC" reset-failed 'dovecot.service' || true + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + + Kerberos + The Kerberos protocol is used for authentication across +non-secure network. Authentication can happen between +various types of principals -- users, service, or hosts. +Their identity and encryption keys can be stored in keytab +files. + + + Remove the Kerberos Server Package + The krb5-server package should be removed if not in use. +Is this system the Kerberos server? If not, remove the package. +The krb5-server package can be removed with the following command: + +$ sudo dnf erase krb5-server +The krb5-server RPM is not installed by default on a Red Hat Enterprise Linux 9 +system. It is needed only by the Kerberos servers, not by the +clients which use Kerberos for authentication. If the system is not +intended for use as a Kerberos Server it should be removed. + CCI-000803 + IA-7 + IA-7.1 + SRG-OS-000120-GPOS-00061 + Unnecessary packages should not be installed to decrease the attack +surface of the system. While this software is clearly essential on an KDC +server, it is not necessary on typical desktop or workstation systems. + +package --remove=krb5-server + + include remove_krb5-server + +class remove_krb5-server { + package { 'krb5-server': + ensure => 'purged', + } +} + + - name: Ensure krb5-server is removed + package: + name: krb5-server + state: absent + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-IA-7 + - NIST-800-53-IA-7.1 + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_krb5-server_removed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# CAUTION: This remediation script will remove krb5-server +# from the system, and may remove any packages +# that depend on krb5-server. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "krb5-server" ; then + + dnf remove -y "krb5-server" + +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable Kerberos by removing host keytab + Kerberos is not an approved key distribution method for +Common Criteria. To prevent using Kerberos by system daemons, +remove the Kerberos keytab files, especially +/etc/krb5.keytab. + CCI-000803 + 0418 + 1055 + 1402 + FTP_ITC_EXT.1 + SRG-OS-000120-GPOS-00061 + The key derivation function (KDF) in Kerberos is not FIPS compatible. + CCE-84221-1 + - name: Find keytab files + find: + paths: /etc/ + patterns: '*.keytab' + register: keytab_files + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84221-1 + - disable_strategy + - kerberos_disable_no_keytab + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Remove keytab files + file: + path: '{{ item.path }}' + state: absent + with_items: '{{ keytab_files.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84221-1 + - disable_strategy + - kerberos_disable_no_keytab + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +rm -f /etc/*.keytab + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + LDAP + LDAP is a popular directory service, that is, a +standardized way of looking up information from a central database. +Red Hat Enterprise Linux 9 includes software that enables a system to act as both +an LDAP client and server. + + Configure OpenLDAP Clients + This section provides information on which security settings are +important to configure in OpenLDAP clients by manually editing the appropriate +configuration files. Red Hat Enterprise Linux 9 provides an automated configuration tool called +authconfig and a graphical wrapper for authconfig called +system-config-authentication. However, these tools do not provide as +much control over configuration as manual editing of configuration files. The +authconfig tools do not allow you to specify locations of SSL certificate +files, which is useful when trying to use SSL cleanly across several protocols. +Installation and configuration of OpenLDAP on Red Hat Enterprise Linux 9 is available at + Before configuring any system to be an +LDAP client, ensure that a working LDAP server is present on the +network. + + Ensure LDAP client is not installed + The Lightweight Directory Access Protocol (LDAP) is a service that provides +a method for looking up information from a central database. +The openldap-clients package can be removed with the following command: + +$ sudo dnf erase openldap-clients + If the system does not need to act as an LDAP client, it is recommended that the software is removed to reduce the potential attack surface. + CCE-90831-9 + +package --remove=openldap-clients + + include remove_openldap-clients + +class remove_openldap-clients { + package { 'openldap-clients': + ensure => 'purged', + } +} + + - name: Ensure openldap-clients is removed + package: + name: openldap-clients + state: absent + tags: + - CCE-90831-9 + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - package_openldap-clients_removed + + +# CAUTION: This remediation script will remove openldap-clients +# from the system, and may remove any packages +# that depend on openldap-clients. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "openldap-clients" ; then + + dnf remove -y "openldap-clients" + +fi + + + + + + + + + + + Configure OpenLDAP Server + This section details some security-relevant settings +for an OpenLDAP server. + + Uninstall openldap-servers Package + +The openldap-servers package is not installed by default on a Red Hat Enterprise Linux 9 + +system. It is needed only by the OpenLDAP server, not by the +clients which use LDAP for authentication. If the system is not +intended for use as an LDAP Server it should be removed. + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + CCI-000366 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + PR.PT-3 + Unnecessary packages should not be installed to decrease the attack +surface of the system. While this software is clearly essential on an LDAP +server, it is not necessary on typical desktop or workstation systems. + +package --remove=openldap-servers + + include remove_openldap-servers + +class remove_openldap-servers { + package { 'openldap-servers': + ensure => 'purged', + } +} + + - name: Ensure openldap-servers is removed + package: + name: openldap-servers + state: absent + tags: + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - package_openldap-servers_removed + + +# CAUTION: This remediation script will remove openldap-servers +# from the system, and may remove any packages +# that depend on openldap-servers. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "openldap-servers" ; then + + dnf remove -y "openldap-servers" + +fi + + + + + + + + + + Disable LDAP Server (slapd) + The Lightweight Directory Access Protocol (LDAP) is a service that provides a method for looking up information from a central database. + If the system will not need to act as an LDAP server, it is recommended that the software be +disabled to reduce the potential attack surface. + + CCE-87263-0 + include disable_slapd + +class disable_slapd { + service {'slapd': + enable => false, + ensure => 'stopped', + } +} + + - name: Disable service slapd + block: + + - name: Disable service slapd + systemd: + name: slapd.service + enabled: 'no' + state: stopped + masked: 'yes' + ignore_errors: 'yes' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-87263-0 + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_slapd_disabled + +- name: Unit Socket Exists - slapd.socket + command: systemctl list-unit-files slapd.socket + args: + warn: false + register: socket_file_exists + changed_when: false + ignore_errors: true + check_mode: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-87263-0 + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_slapd_disabled + +- name: Disable socket slapd + systemd: + name: slapd.socket + enabled: 'no' + state: stopped + masked: 'yes' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"slapd.socket" in socket_file_exists.stdout_lines[1]' + tags: + - CCE-87263-0 + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_slapd_disabled + + +[customizations.services] +disabled = ["slapd"] + + apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + systemd: + units: + - name: slapd.service + enabled: false + mask: true + - name: slapd.socket + enabled: false + mask: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" stop 'slapd.service' +"$SYSTEMCTL_EXEC" disable 'slapd.service' +"$SYSTEMCTL_EXEC" mask 'slapd.service' +# Disable socket activation if we have a unit file for it +if "$SYSTEMCTL_EXEC" list-unit-files | grep -q '^slapd.socket'; then + "$SYSTEMCTL_EXEC" stop 'slapd.socket' + "$SYSTEMCTL_EXEC" mask 'slapd.socket' +fi +# The service may not be running because it has been started and failed, +# so let's reset the state so OVAL checks pass. +# Service should be 'inactive', not 'failed' after reboot though. +"$SYSTEMCTL_EXEC" reset-failed 'slapd.service' || true + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Install and Protect LDAP Certificate Files + Create the PKI directory for LDAP certificates if it does not already exist: +$ sudo mkdir /etc/pki/tls/ldap +$ sudo chown root:root /etc/pki/tls/ldap +$ sudo chmod 755 /etc/pki/tls/ldap +Using removable media or some other secure transmission format, install the certificate files +onto the LDAP server: +/etc/pki/tls/ldap/serverkey.pem: the private key ldapserverkey.pem/etc/pki/tls/ldap/servercert.pem: the certificate file ldapservercert.pem +Verify the ownership and permissions of these files: +$ sudo chown root:ldap /etc/pki/tls/ldap/serverkey.pem +$ sudo chown root:ldap /etc/pki/tls/ldap/servercert.pem +$ sudo chmod 640 /etc/pki/tls/ldap/serverkey.pem +$ sudo chmod 640 /etc/pki/tls/ldap/servercert.pem +Verify that the CA's public certificate file has been installed as +/etc/pki/tls/CA/cacert.pem, and has the correct permissions: +$ sudo mkdir /etc/pki/tls/CA +$ sudo chown root:root /etc/pki/tls/CA/cacert.pem +$ sudo chmod 644 /etc/pki/tls/CA/cacert.pem + +As a result of these steps, the LDAP server will have access to its own private +certificate and the key with which that certificate is encrypted, and to the +public certificate file belonging to the CA. Note that it would be possible for +the key to be protected further, so that processes running as ldap could not +read it. If this were done, the LDAP server process would need to be restarted +manually whenever the server rebooted. + + + + + Mail Server Software + Mail servers are used to send and receive email over the network. +Mail is a very common service, and Mail Transfer Agents (MTAs) are obvious +targets of network attack. +Ensure that systems are not running MTAs unnecessarily, +and configure needed MTAs as defensively as possible. + +Very few systems at any site should be configured to directly receive email over the +network. Users should instead use mail client programs to retrieve email +from a central server that supports protocols such as IMAP or POP3. +However, it is normal for most systems to be independently capable of sending email, +for instance so that cron jobs can report output to an administrator. +Most MTAs, including Postfix, support a submission-only mode in which mail can be sent from +the local system to a central site MTA (or directly delivered to a local account), +but the system still cannot receive mail directly over a network. + +The alternatives program in Red Hat Enterprise Linux 9 permits selection of other mail server software +(such as Sendmail), but Postfix is the default and is preferred. +Postfix was coded with security in mind and can also be more effectively contained by +SELinux as its modular design has resulted in separate processes performing specific actions. +More information is available on its website, + http://www.postfix.org. + + + The Postfix package is installed + A mail server is required for sending emails. +The postfix package can be installed with the following command: + +$ sudo dnf install postfix + SRG-OS-000046-GPOS-00022 + Emails can be used to notify designated personnel about important +system events such as failures or warnings. + CCE-85984-3 + +package --add=postfix + + include install_postfix + +class install_postfix { + package { 'postfix': + ensure => 'installed', + } +} + + - name: Ensure postfix is installed + package: + name: postfix + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-85984-3 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_postfix_installed + + +[[packages]] +name = "postfix" +version = "*" + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if ! rpm -q --quiet "postfix" ; then + dnf install -y "postfix" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Uninstall Sendmail Package + Sendmail is not the default mail transfer agent and is +not installed by default. +The sendmail package can be removed with the following command: + +$ sudo dnf erase sendmail + BP28(R1) + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + CCI-000381 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + PR.PT-3 + SRG-OS-000480-GPOS-00227 + SRG-OS-000095-GPOS-00049 + The sendmail software was not developed with security in mind and +its design prevents it from being effectively contained by SELinux. Postfix +should be used instead. + CCE-90830-1 + +package --remove=sendmail + + include remove_sendmail + +class remove_sendmail { + package { 'sendmail': + ensure => 'purged', + } +} + + - name: Ensure sendmail is removed + package: + name: sendmail + state: absent + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-90830-1 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_sendmail_removed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# CAUTION: This remediation script will remove sendmail +# from the system, and may remove any packages +# that depend on sendmail. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "sendmail" ; then + + dnf remove -y "sendmail" + +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Enable Postfix Service + The Postfix mail transfer agent is used for local mail delivery +within the system. The default configuration only listens for connections to +the default SMTP port (port 25) on the loopback interface (127.0.0.1). It is +recommended to leave this service enabled for local mail delivery. + +The postfix service can be enabled with the following command: +$ sudo systemctl enable postfix.service + Local mail delivery is essential to some system maintenance and +notification tasks. + include enable_postfix + +class enable_postfix { + service {'postfix': + enable => true, + ensure => 'running', + } +} + + - name: Enable service postfix + block: + + - name: Gather the package facts + package_facts: + manager: auto + + - name: Enable service postfix + service: + name: postfix + enabled: 'yes' + state: started + masked: 'no' + when: + - '"postfix" in ansible_facts.packages' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - enable_strategy + - low_complexity + - low_disruption + - no_reboot_needed + - service_postfix_enabled + - unknown_severity + + +[customizations.services] +enabled = ["postfix"] + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" unmask 'postfix.service' +"$SYSTEMCTL_EXEC" start 'postfix.service' +"$SYSTEMCTL_EXEC" enable 'postfix.service' + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure SMTP For Mail Clients + This section discusses settings for Postfix in a submission-only +e-mail configuration. + + Postfix Network Interfaces + The setting for inet_interfaces in /etc/postfix/main.cf + loopback-only + loopback-only + localhost + + + Postfix relayhost + Specify the host all outbound email should be routed into. + smtp.$mydomain + + + Postfix Root Mail Alias + Specify an email address (string) for a root mail alias. + system.administrator@mail.mil + system.administrator@mail.mil + + + Configure System to Forward All Mail For The Root Account + Make sure that mails delivered to root user are forwarded to a monitored +email address. Make sure that the address + is a valid email address +reachable from the system in question. Use the following command to +configure the alias: +$ sudo echo "root: " >> /etc/aliases +$ sudo newaliases + BP28(R49) + CCI-000139 + CCI-000366 + CM-6(a) + SRG-OS-000046-GPOS-00022 + A number of system services utilize email messages sent to the root user to +notify system administrators of active or impending issues. These messages must +be forwarded to at least one monitored email address. + CCE-90826-9 + - name: XCCDF Value var_postfix_root_mail_alias # promote to variable + set_fact: + var_postfix_root_mail_alias: !!str + tags: + - always + +- name: Make sure that "/etc/aliases" has a defined value for root + lineinfile: + path: /etc/aliases + line: 'root: {{ var_postfix_root_mail_alias }}' + regexp: ^(?:[rR][oO][oO][tT]|"[rR][oO][oO][tT]")\s*:\s*(.+)$ + create: true + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-90826-9 + - NIST-800-53-CM-6(a) + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - postfix_client_configure_mail_alias + +- name: Check if newaliases command is available + ansible.builtin.stat: + path: /usr/bin/newaliases + register: result_newaliases_present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-90826-9 + - NIST-800-53-CM-6(a) + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - postfix_client_configure_mail_alias + +- name: Update postfix aliases + ansible.builtin.command: + cmd: newaliases + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - result_newaliases_present.stat.exists + tags: + - CCE-90826-9 + - NIST-800-53-CM-6(a) + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - postfix_client_configure_mail_alias + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_postfix_root_mail_alias='' + + +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/aliases"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^root") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s: %s" "$stripped_key" "$var_postfix_root_mail_alias" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^root\\>" "/etc/aliases"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^root\\>.*/$escaped_formatted_output/gi" "/etc/aliases" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-90826-9" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/aliases" >> "/etc/aliases" + printf '%s\n' "$formatted_output" >> "/etc/aliases" +fi + +if [ -f /usr/bin/newaliases ]; then + newaliases +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Configure System to Forward All Mail From Postmaster to The Root Account + Verify the administrators are notified in the event of an audit processing failure. +Check that the "/etc/aliases" file has a defined value for "root". +$ sudo grep "postmaster:\s*root$" /etc/aliases + +postmaster: root + CCI-000139 + AU-5(a) + AU-5.1(ii) + SRG-OS-000046-GPOS-00022 + It is critical for the appropriate personnel to be aware if a system is at risk of failing to +process audit logs as required. Without this notification, the security personnel may be +unaware of an impending failure of the audit capability, and system operation may be adversely +affected. + +Audit processing failures include software/hardware errors, failures in the audit capturing +mechanisms, and audit storage capacity being reached or exceeded. + CCE-89064-0 + - name: Configure System to Forward All Mail From Postmaster to The Root Account + block: + + - name: Check for duplicate values + lineinfile: + path: /etc/aliases + create: false + regexp: ^\s*postmaster\s*:\s* + state: absent + check_mode: true + changed_when: false + register: dupes + + - name: Deduplicate values from /etc/aliases + lineinfile: + path: /etc/aliases + create: false + regexp: ^\s*postmaster\s*:\s* + state: absent + when: dupes.found is defined and dupes.found > 1 + + - name: Insert correct line to /etc/aliases + lineinfile: + path: /etc/aliases + create: true + regexp: ^\s*postmaster\s*:\s* + line: 'postmaster: root' + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-89064-0 + - NIST-800-53-AU-5(a) + - NIST-800-53-AU-5.1(ii) + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - postfix_client_configure_mail_alias_postmaster + +- name: Check if newaliases command is available + ansible.builtin.stat: + path: /usr/bin/newaliases + register: result_newaliases_present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-89064-0 + - NIST-800-53-AU-5(a) + - NIST-800-53-AU-5.1(ii) + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - postfix_client_configure_mail_alias_postmaster + +- name: Update postfix aliases + ansible.builtin.command: + cmd: newaliases + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - result_newaliases_present.stat.exists + tags: + - CCE-89064-0 + - NIST-800-53-AU-5(a) + - NIST-800-53-AU-5.1(ii) + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - postfix_client_configure_mail_alias_postmaster + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if [ -e "/etc/aliases" ] ; then + + LC_ALL=C sed -i "/^\s*postmaster\s*:\s*/Id" "/etc/aliases" +else + touch "/etc/aliases" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/aliases" + +cp "/etc/aliases" "/etc/aliases.bak" +# Insert at the end of the file +printf '%s\n' "postmaster: root" >> "/etc/aliases" +# Clean up after ourselves. +rm "/etc/aliases.bak" + +if [ -f /usr/bin/newaliases ]; then + newaliases +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure System to Forward All Mail through a specific host + Set up a relay host that will act as a gateway for all outbound email. +Edit the file /etc/postfix/main.cf to ensure that only the following +relayhost line appears: +relayhost = + A central outbound email location ensures messages sent from any network host +can be audited for potential unexpected content. Tooling on the central server +may help prevent spam or viruses from being delivered. + + + + + + + Disable Postfix Network Listening + Edit the file /etc/postfix/main.cf to ensure that only the following +inet_interfaces line appears: +inet_interfaces = + BP28(R48) + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + CCI-000382 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + PR.PT-3 + This ensures postfix accepts mail messages +(such as cron job reports) from the local system only, +and not from the network, which protects it from network attack. + + CCE-90825-1 + - name: XCCDF Value var_postfix_inet_interfaces # promote to variable + set_fact: + var_postfix_inet_interfaces: !!str + tags: + - always + +- name: Gather list of packages + package_facts: + manager: auto + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-90825-1 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - postfix_network_listening_disabled + - restrict_strategy + +- name: Make changes to Postfix configuration file + lineinfile: + path: /etc/postfix/main.cf + create: false + regexp: ^inet_interfaces\s*=\s.* + line: inet_interfaces = {{ var_postfix_inet_interfaces }} + state: present + insertafter: ^inet_interfaces\s*=\s.* + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"postfix" in ansible_facts.packages' + - '"postfix" in ansible_facts.packages' + tags: + - CCE-90825-1 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - postfix_network_listening_disabled + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && { rpm --quiet -q postfix; }; then + +var_postfix_inet_interfaces='' + + +if [ -e "/etc/postfix/main.cf" ] ; then + + LC_ALL=C sed -i "/^\s*inet_interfaces\s\+=\s\+/Id" "/etc/postfix/main.cf" +else + touch "/etc/postfix/main.cf" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/postfix/main.cf" + +cp "/etc/postfix/main.cf" "/etc/postfix/main.cf.bak" +# Insert at the end of the file +printf '%s\n' "inet_interfaces=$var_postfix_inet_interfaces" >> "/etc/postfix/main.cf" +# Clean up after ourselves. +rm "/etc/postfix/main.cf.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + + Configure Operating System to Protect Mail Server + The guidance in this section is appropriate for any host which is +operating as a site MTA, whether the mail server runs using Sendmail, Postfix, +or some other software. + + + Configure SSL Certificates for Use with SMTP AUTH + If SMTP AUTH is to be used, the use of SSL to protect credentials in transit is strongly recommended. +There are also configurations for which it may be desirable to encrypt all mail in transit from one MTA to another, +though such configurations are beyond the scope of this guide. In either event, the steps for creating and installing +an SSL certificate are independent of the MTA in use, and are described here. + + Ensure Security of Postfix SSL Certificate + Create the PKI directory for mail certificates, if it does not already exist: +$ sudo mkdir /etc/pki/tls/mail +$ sudo chown root:root /etc/pki/tls/mail +$ sudo chmod 755 /etc/pki/tls/mail +Using removable media or some other secure transmission format, install the files generated in the previous +step onto the mail server: +/etc/pki/tls/mail/serverkey.pem: the private key mailserverkey.pem +/etc/pki/tls/mail/servercert.pem: the certificate file mailservercert.pem +Verify the ownership and permissions of these files: +$ sudo chown root:root /etc/pki/tls/mail/serverkey.pem +$ sudo chown root:root /etc/pki/tls/mail/servercert.pem +$ sudo chmod 600 /etc/pki/tls/mail/serverkey.pem +$ sudo chmod 644 /etc/pki/tls/mail/servercert.pem +Verify that the CA's public certificate file has been installed as /etc/pki/tls/CA/cacert.pem, and has the +correct permissions: +$ sudo chown root:root /etc/pki/tls/CA/cacert.pem +$ sudo chmod 644 /etc/pki/tls/CA/cacert.pem + + + + Configure Postfix if Necessary + Postfix stores its configuration files in the directory +/etc/postfix by default. The primary configuration file is +/etc/postfix/main.cf. + + Configure Postfix Resource Usage to Limit Denial of Service Attacks + Edit /etc/postfix/main.cf. Edit the following lines to +configure the amount of system resources Postfix can consume: +default_process_limit = 100 +smtpd_client_connection_count_limit = 10 +smtpd_client_connection_rate_limit = 30 +queue_minfree = 20971520 +header_size_limit = 51200 +message_size_limit = 10485760 +smtpd_recipient_limit = 100 +The values here are examples. + Note: The values given here are examples, and may +need to be modified for any particular site. By default, the Postfix anvil +process gathers mail receipt statistics. To get information about about what +connection rates are typical at your site, look in /var/log/maillog +for lines with the daemon name postfix/anvil. + + + Control Mail Relaying + Postfix's mail relay controls are implemented with the help of the +smtpd recipient restrictions option, which controls the restrictions placed on +the SMTP dialogue once the sender and recipient envelope addresses are known. +The guidance in the following sections should be applied to all systems. If +there are systems which must be allowed to relay mail, but which cannot be +trusted to relay unconditionally, configure SMTP AUTH with SSL support. + + Prevent Unrestricted Mail Relaying + Modify the /etc/postfix/main.cf file to restrict client connections +to the local network with the following command: +$ sudo postconf -e 'smtpd_client_restrictions = permit_mynetworks,reject' + CCI-000366 + SRG-OS-000480-GPOS-00227 + If unrestricted mail relaying is permitted, unauthorized senders could use this +host as a mail relay for the purpose of sending spam or other unauthorized +activity. + CCE-87232-5 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-87232-5 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - postfix_prevent_unrestricted_relay + - restrict_strategy + +- name: Prevent Unrestricted Mail Relaying + block: + + - name: Check for duplicate values + lineinfile: + path: /etc/postfix/main.cf + create: false + regexp: ^[ \t]*smtpd_client_restrictions\s*=\s* + state: absent + check_mode: true + changed_when: false + register: dupes + + - name: Deduplicate values from /etc/postfix/main.cf + lineinfile: + path: /etc/postfix/main.cf + create: false + regexp: ^[ \t]*smtpd_client_restrictions\s*=\s* + state: absent + when: dupes.found is defined and dupes.found > 1 + + - name: Insert correct line to /etc/postfix/main.cf + lineinfile: + path: /etc/postfix/main.cf + create: true + regexp: ^[ \t]*smtpd_client_restrictions\s*=\s* + line: smtpd_client_restrictions = permit_mynetworks,reject + state: present + when: + - '"postfix" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-87232-5 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - postfix_prevent_unrestricted_relay + - restrict_strategy + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q postfix; then + +if ! grep -q ^smtpd_client_restrictions /etc/postfix/main.cf; then + echo "smtpd_client_restrictions = permit_mynetworks,reject" >> /etc/postfix/main.cf +else + sed -i "s/^smtpd_client_restrictions.*/smtpd_client_restrictions = permit_mynetworks,reject/g" /etc/postfix/main.cf +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Enact SMTP Recipient Restrictions + To configure Postfix to restrict addresses to which it +will send mail, see: + + http://www.postfix.org/SMTPD_ACCESS_README.html#danger + +The full contents of smtpd_recipient_restrictions will +vary by site, since this is a common place to put spam restrictions and other +site-specific options. The permit_mynetworks option allows all mail to +be relayed from the systems in mynetworks. Then, the +reject_unauth_destination option denies all mail whose destination +address is not local, preventing any other systems from relaying. These two +options should always appear in this order, and should usually follow one +another immediately unless SMTP AUTH is used. + + + Enact SMTP Relay Restrictions + To configure Postfix to restrict addresses to which it +will send mail, see: + + http://www.postfix.org/SMTPD_ACCESS_README.html#danger + +The full contents of smtpd_recipient_restrictions will +vary by site, since this is a common place to put spam restrictions and other +site-specific options. The permit_mynetworks option allows all mail to +be relayed from the systems in mynetworks. Then, the +reject_unauth_destination option denies all mail whose destination +address is not local, preventing any other systems from relaying. These two +options should always appear in this order, and should usually follow one +another immediately unless SMTP AUTH is used. + + + Use TLS for SMTP AUTH + Postfix provides options to use TLS for certificate-based +authentication and encrypted sessions. An encrypted session protects the +information that is transmitted with SMTP mail or with SASL authentication. +To configure Postfix to protect all SMTP AUTH transactions +using TLS, see + http://www.postfix.org/TLS_README.html. + + + Configure Trusted Networks and Hosts + Edit /etc/postfix/main.cf, and configure the contents of +the mynetworks variable in one of the following ways: +If any system in the subnet containing the MTA may be trusted to relay +messages, add or correct the following line: +mynetworks_style = subnet +This is also the default setting, and is in effect if all +my_networks_style directives are commented.If only the MTA host itself is trusted to relay messages, add or correct +the following line: +mynetworks_style = hostIf the set of systems which can relay is more complicated, manually +specify an entry for each netblock or IP address which is trusted to relay by +setting the mynetworks variable directly: +mynetworks = 10.0.0.0/16, 192.168.1.0/24, 127.0.0.1 + + + Require SMTP AUTH Before Relaying from Untrusted Clients + SMTP authentication allows remote clients to relay mail safely by +requiring them to authenticate before submitting mail. Postfix's SMTP AUTH uses +an authentication library called SASL, which is not part of Postfix itself. To +enable the use of SASL authentication, see + + http://www.postfix.org/SASL_README.html + + + + + + + NFS and RPC + The Network File System is a popular distributed filesystem for +the Unix environment, and is very widely deployed. This section discusses the +circumstances under which it is possible to disable NFS and its dependencies, +and then details steps which should be taken to secure +NFS's configuration. This section is relevant to systems operating as NFS +clients, as well as to those operating as NFS servers. + + Uninstall nfs-utils Package + The nfs-utils package can be removed with the following command: + +$ sudo dnf erase nfs-utils + SRG-OS-000095-GPOS-00049 + nfs-utils provides a daemon for the kernel NFS server and related tools. This +package also contains the showmount program. showmount queries the mount +daemon on a remote host for information about the Network File System (NFS) server on the +remote host. For example, showmount can display the clients which are mounted on +that host. + CCE-84243-5 + +package --remove=nfs-utils + + include remove_nfs-utils + +class remove_nfs-utils { + package { 'nfs-utils': + ensure => 'purged', + } +} + + - name: Ensure nfs-utils is removed + package: + name: nfs-utils + state: absent + tags: + - CCE-84243-5 + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - package_nfs-utils_removed + + +# CAUTION: This remediation script will remove nfs-utils +# from the system, and may remove any packages +# that depend on nfs-utils. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "nfs-utils" ; then + + dnf remove -y "nfs-utils" + +fi + + + + + + + + + + Disable All NFS Services if Possible + If there is not a reason for the system to operate as either an +NFS client or an NFS server, follow all instructions in this section to disable +subsystems required by NFS. + The steps in this section will prevent a system +from operating as either an NFS client or an NFS server. Only perform these +steps on systems which do not need NFS at all. + + + Disable netfs if Possible + To determine if any network filesystems handled by netfs are +currently mounted on the system execute the following command: +$ mount -t nfs,nfs4,smbfs,cifs,ncpfs +If the command did not return any output then disable netfs. + + Disable Network File Systems (netfs) + The netfs script manages the boot-time mounting of several types +of networked filesystems, of which NFS and Samba are the most common. If these +filesystem types are not in use, the script can be disabled, protecting the +system somewhat against accidental or malicious changes to /etc/fstab +and against flaws in the netfs script itself. + +The netfs service can be disabled with the following command: +$ sudo systemctl mask --now netfs.service + + include disable_netfs + +class disable_netfs { + service {'netfs': + enable => false, + ensure => 'stopped', + } +} + + - name: Disable service netfs + block: + + - name: Disable service netfs + systemd: + name: netfs.service + enabled: 'no' + state: stopped + masked: 'yes' + ignore_errors: 'yes' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - disable_strategy + - low_complexity + - low_disruption + - no_reboot_needed + - service_netfs_disabled + - unknown_severity + +- name: Unit Socket Exists - netfs.socket + command: systemctl list-unit-files netfs.socket + args: + warn: false + register: socket_file_exists + changed_when: false + ignore_errors: true + check_mode: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - disable_strategy + - low_complexity + - low_disruption + - no_reboot_needed + - service_netfs_disabled + - unknown_severity + +- name: Disable socket netfs + systemd: + name: netfs.socket + enabled: 'no' + state: stopped + masked: 'yes' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"netfs.socket" in socket_file_exists.stdout_lines[1]' + tags: + - disable_strategy + - low_complexity + - low_disruption + - no_reboot_needed + - service_netfs_disabled + - unknown_severity + + +[customizations.services] +disabled = ["netfs"] + + apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + systemd: + units: + - name: netfs.service + enabled: false + mask: true + - name: netfs.socket + enabled: false + mask: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" stop 'netfs.service' +"$SYSTEMCTL_EXEC" disable 'netfs.service' +"$SYSTEMCTL_EXEC" mask 'netfs.service' +# Disable socket activation if we have a unit file for it +if "$SYSTEMCTL_EXEC" list-unit-files | grep -q '^netfs.socket'; then + "$SYSTEMCTL_EXEC" stop 'netfs.socket' + "$SYSTEMCTL_EXEC" mask 'netfs.socket' +fi +# The service may not be running because it has been started and failed, +# so let's reset the state so OVAL checks pass. +# Service should be 'inactive', not 'failed' after reboot though. +"$SYSTEMCTL_EXEC" reset-failed 'netfs.service' || true + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + Disable Services Used Only by NFS + If NFS is not needed, disable the NFS client daemons nfslock, rpcgssd, and rpcidmapd. + +All of these daemons run with elevated privileges, and many listen for network +connections. If they are not needed, they should be disabled to improve system +security posture. + + + Disable rpcbind Service + The rpcbind utility maps RPC services to the ports on which they listen. +RPC processes notify rpcbind when they start, registering the ports they +are listening on and the RPC program numbers they expect to serve. The +rpcbind service redirects the client to the proper port number so it can +communicate with the requested service. If the system does not require RPC +(such as for NFS servers) then this service should be disabled. + +The rpcbind service can be disabled with the following command: +$ sudo systemctl mask --now rpcbind.service + If the system does not require rpc based services, it is recommended that +rpcbind be disabled to reduce the attack surface. + CCE-84245-0 + include disable_rpcbind + +class disable_rpcbind { + service {'rpcbind': + enable => false, + ensure => 'stopped', + } +} + + - name: Disable service rpcbind + block: + + - name: Disable service rpcbind + systemd: + name: rpcbind.service + enabled: 'no' + state: stopped + masked: 'yes' + ignore_errors: 'yes' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84245-0 + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - service_rpcbind_disabled + +- name: Unit Socket Exists - rpcbind.socket + command: systemctl list-unit-files rpcbind.socket + args: + warn: false + register: socket_file_exists + changed_when: false + ignore_errors: true + check_mode: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84245-0 + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - service_rpcbind_disabled + +- name: Disable socket rpcbind + systemd: + name: rpcbind.socket + enabled: 'no' + state: stopped + masked: 'yes' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"rpcbind.socket" in socket_file_exists.stdout_lines[1]' + tags: + - CCE-84245-0 + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - service_rpcbind_disabled + + +[customizations.services] +disabled = ["rpcbind"] + + apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + systemd: + units: + - name: rpcbind.service + enabled: false + mask: true + - name: rpcbind.socket + enabled: false + mask: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" stop 'rpcbind.service' +"$SYSTEMCTL_EXEC" disable 'rpcbind.service' +"$SYSTEMCTL_EXEC" mask 'rpcbind.service' +# Disable socket activation if we have a unit file for it +if "$SYSTEMCTL_EXEC" list-unit-files | grep -q '^rpcbind.socket'; then + "$SYSTEMCTL_EXEC" stop 'rpcbind.socket' + "$SYSTEMCTL_EXEC" mask 'rpcbind.socket' +fi +# The service may not be running because it has been started and failed, +# so let's reset the state so OVAL checks pass. +# Service should be 'inactive', not 'failed' after reboot though. +"$SYSTEMCTL_EXEC" reset-failed 'rpcbind.service' || true + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + Configure All Systems which Use NFS + The steps in this section are appropriate for all systems which +run NFS, whether they operate as clients or as servers. + + Make Each System a Client or a Server, not Both + If NFS must be used, it should be deployed in the simplest +configuration possible to avoid maintainability problems which may lead to +unnecessary security exposure. Due to the reliability and security problems +caused by NFS (specially NFSv3 and NFSv2), it is not a good idea for systems +which act as NFS servers to also mount filesystems via NFS. At the least, +crossed mounts (the situation in which each of two servers mounts a filesystem +from the other) should never be used. + + + Configure NFS Services to Use Fixed Ports (NFSv3 and NFSv2) + Firewalling should be done at each host and at the border +firewalls to protect the NFS daemons from remote access, since NFS servers +should never be accessible from outside the organization. However, by default +for NFSv3 and NFSv2, the RPC Bind service assigns each NFS service to a port +dynamically at service startup time. Dynamic ports cannot be protected by port + +filtering firewalls such as iptables. + + +Therefore, restrict each service to always use a given port, so that +firewalling can be done effectively. Note that, because of the way RPC is +implemented, it is not possible to disable the RPC Bind service even if ports +are assigned statically to all RPC services. + +In NFSv4, the mounting and locking protocols have been incorporated into the +protocol, and the server listens on the the well-known TCP port 2049. As such, +NFSv4 does not need to interact with the rpcbind, lockd, and rpc.statd +daemons, which can and should be disabled in a pure NFSv4 environment. The +rpc.mountd daemon is still required on the NFS server to setup +exports, but is not involved in any over-the-wire operations. + + + + Configure NFS Clients + The steps in this section are appropriate for systems which operate as NFS clients. + + Disable NFS Server Daemons + There is no need to run the NFS server daemons nfs and +rpcsvcgssd except on a small number of properly secured systems +designated as NFS servers. Ensure that these daemons are turned off on +clients. + + Disable Network File System (nfs) + The Network File System (NFS) service allows remote hosts to mount +and interact with shared filesystems on the local system. If the local system +is not designated as a NFS server then this service should be disabled. + +The nfs-server service can be disabled with the following command: +$ sudo systemctl mask --now nfs-server.service + 11 + 12 + 14 + 15 + 16 + 18 + 3 + 5 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + DSS06.03 + DSS06.06 + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + A.6.1.2 + A.7.1.1 + A.9.1.2 + A.9.2.1 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-7(a) + CM-7(b) + CM-6(a) + PR.AC-4 + PR.AC-6 + PR.PT-3 + Unnecessary services should be disabled to decrease the attack surface of the system. + + CCE-90850-9 + include disable_nfs-server + +class disable_nfs-server { + service {'nfs-server': + enable => false, + ensure => 'stopped', + } +} + + - name: Disable service nfs-server + block: + + - name: Disable service nfs-server + systemd: + name: nfs-server.service + enabled: 'no' + state: stopped + masked: 'yes' + ignore_errors: 'yes' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-90850-9 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - no_reboot_needed + - service_nfs_disabled + - unknown_severity + +- name: Unit Socket Exists - nfs-server.socket + command: systemctl list-unit-files nfs-server.socket + args: + warn: false + register: socket_file_exists + changed_when: false + ignore_errors: true + check_mode: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-90850-9 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - no_reboot_needed + - service_nfs_disabled + - unknown_severity + +- name: Disable socket nfs-server + systemd: + name: nfs-server.socket + enabled: 'no' + state: stopped + masked: 'yes' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"nfs-server.socket" in socket_file_exists.stdout_lines[1]' + tags: + - CCE-90850-9 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - no_reboot_needed + - service_nfs_disabled + - unknown_severity + + +[customizations.services] +disabled = ["nfs-server"] + + apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + systemd: + units: + - name: nfs-server.service + enabled: false + mask: true + - name: nfs-server.socket + enabled: false + mask: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" stop 'nfs-server.service' +"$SYSTEMCTL_EXEC" disable 'nfs-server.service' +"$SYSTEMCTL_EXEC" mask 'nfs-server.service' +# Disable socket activation if we have a unit file for it +if "$SYSTEMCTL_EXEC" list-unit-files | grep -q '^nfs-server.socket'; then + "$SYSTEMCTL_EXEC" stop 'nfs-server.socket' + "$SYSTEMCTL_EXEC" mask 'nfs-server.socket' +fi +# The service may not be running because it has been started and failed, +# so let's reset the state so OVAL checks pass. +# Service should be 'inactive', not 'failed' after reboot though. +"$SYSTEMCTL_EXEC" reset-failed 'nfs-server.service' || true + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Mount Remote Filesystems with Restrictive Options + Edit the file /etc/fstab. For each filesystem whose type +(column 3) is nfs or nfs4, add the text +,nodev,nosuid to the list of mount options in column 4. If +appropriate, also add ,noexec. + +See the section titled "Restrict Partition Mount Options" for a description of +the effects of these options. In general, execution of files mounted via NFS +should be considered risky because of the possibility that an adversary could +intercept the request and substitute a malicious file. Allowing setuid files to +be executed from remote servers is particularly risky, both for this reason and +because it requires the clients to extend root-level trust to the NFS +server. + + + Mount Remote Filesystems with Kerberos Security + Add the sec=krb5:krb5i:krb5p option to the fourth column of /etc/fstab for the line which controls mounting of +any NFS mounts. + 1 + 12 + 14 + 15 + 16 + 18 + 3 + 5 + DSS05.04 + DSS05.10 + DSS06.10 + CCI-000366 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.3 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + A.18.1.4 + A.6.1.2 + A.9.1.2 + A.9.2.1 + A.9.2.3 + A.9.2.4 + A.9.3.1 + A.9.4.1 + A.9.4.2 + A.9.4.3 + A.9.4.4 + A.9.4.5 + CM-7(a) + CM-7(b) + CM-6(a) + IA-2 + IA-2(8) + IA-2(9) + AC-17(a) + PR.AC-4 + PR.AC-7 + SRG-OS-000480-GPOS-00227 + When an NFS server is configured to use AUTH_SYS a selected userid and groupid are used to handle +requests from the remote user. The userid and groupid could mistakenly or maliciously be set +incorrectly. The AUTH_GSS method of authentication uses certificates on the server and client +systems to more securely authenticate the remote mount request. + CCE-87416-4 + - name: Get nfs and nfs4 mount points, that don't have sec=krb5:krb5i:krb5p + command: findmnt --fstab --types nfs,nfs4 -O nosec=krb5:krb5i:krb5p -n + register: points_register + check_mode: false + changed_when: false + failed_when: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-87416-4 + - NIST-800-53-AC-17(a) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-IA-2 + - NIST-800-53-IA-2(8) + - NIST-800-53-IA-2(9) + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - mount_option_krb_sec_remote_filesystems + - no_reboot_needed + +- name: Add sec=krb5:krb5i:krb5p to nfs and nfs4 mount points + mount: + path: '{{ item.split()[0] }}' + src: '{{ item.split()[1] }}' + fstype: '{{ item.split()[2] }}' + state: mounted + opts: '{{ item.split()[3] }},sec=krb5:krb5i:krb5p' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - (points_register.stdout | length > 0) + with_items: '{{ points_register.stdout_lines }}' + tags: + - CCE-87416-4 + - NIST-800-53-AC-17(a) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-IA-2 + - NIST-800-53-IA-2(8) + - NIST-800-53-IA-2(9) + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - mount_option_krb_sec_remote_filesystems + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +vfstype_points=() +readarray -t vfstype_points < <(grep -E "[[:space:]]nfs[4]?[[:space:]]" /etc/fstab | awk '{print $2}') + +for vfstype_point in "${vfstype_points[@]}" +do + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" ${vfstype_point//\\/\\\\})" + + # If the mount point is not in /etc/fstab, get previous mount options from /etc/mtab + if [ "$(grep -c "$mount_point_match_regexp" /etc/fstab)" -eq 0 ]; then + # runtime opts without some automatic kernel/userspace-added defaults + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/mtab | head -1 | awk '{print $4}' \ + | sed -E "s/(rw|defaults|seclabel|sec=krb5:krb5i:krb5p)(,|$)//g;s/,$//") + [ "$previous_mount_opts" ] && previous_mount_opts+="," + echo " ${vfstype_point//\\/\\\\} nfs4 defaults,${previous_mount_opts}sec=krb5:krb5i:krb5p 0 0" >> /etc/fstab + # If the mount_opt option is not already in the mount point's /etc/fstab entry, add it + elif [ "$(grep "$mount_point_match_regexp" /etc/fstab | grep -c "sec=krb5:krb5i:krb5p")" -eq 0 ]; then + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/fstab | awk '{print $4}') + sed -i "s|\(${mount_point_match_regexp}.*${previous_mount_opts}\)|\1,sec=krb5:krb5i:krb5p|" /etc/fstab + fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Mount Remote Filesystems with nodev + Add the nodev option to the fourth column of /etc/fstab for the line which controls mounting of +any NFS mounts. + 11 + 13 + 14 + 3 + 8 + 9 + APO13.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS05.06 + DSS06.06 + CCI-000366 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.11.2.9 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.8.2.1 + A.8.2.2 + A.8.2.3 + A.8.3.1 + A.8.3.3 + A.9.1.2 + CM-6(a) + MP-2 + PR.IP-1 + PR.PT-2 + PR.PT-3 + SRG-OS-000480-GPOS-00227 + Legitimate device files should only exist in the /dev directory. NFS mounts +should not present device files to users. + CCE-90838-4 + - name: Get nfs and nfs4 mount points, that don't have nodev + command: findmnt --fstab --types nfs,nfs4 -O nonodev -n + register: points_register + check_mode: false + changed_when: false + failed_when: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-90838-4 + - NIST-800-53-CM-6(a) + - NIST-800-53-MP-2 + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - mount_option_nodev_remote_filesystems + - no_reboot_needed + +- name: Add nodev to nfs and nfs4 mount points + mount: + path: '{{ item.split()[0] }}' + src: '{{ item.split()[1] }}' + fstype: '{{ item.split()[2] }}' + state: mounted + opts: '{{ item.split()[3] }},nodev' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - (points_register.stdout | length > 0) + with_items: '{{ points_register.stdout_lines }}' + tags: + - CCE-90838-4 + - NIST-800-53-CM-6(a) + - NIST-800-53-MP-2 + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - mount_option_nodev_remote_filesystems + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +vfstype_points=() +readarray -t vfstype_points < <(grep -E "[[:space:]]nfs[4]?[[:space:]]" /etc/fstab | awk '{print $2}') + +for vfstype_point in "${vfstype_points[@]}" +do + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" ${vfstype_point//\\/\\\\})" + + # If the mount point is not in /etc/fstab, get previous mount options from /etc/mtab + if [ "$(grep -c "$mount_point_match_regexp" /etc/fstab)" -eq 0 ]; then + # runtime opts without some automatic kernel/userspace-added defaults + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/mtab | head -1 | awk '{print $4}' \ + | sed -E "s/(rw|defaults|seclabel|nodev)(,|$)//g;s/,$//") + [ "$previous_mount_opts" ] && previous_mount_opts+="," + echo " ${vfstype_point//\\/\\\\} nfs4 defaults,${previous_mount_opts}nodev 0 0" >> /etc/fstab + # If the mount_opt option is not already in the mount point's /etc/fstab entry, add it + elif [ "$(grep "$mount_point_match_regexp" /etc/fstab | grep -c "nodev")" -eq 0 ]; then + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/fstab | awk '{print $4}') + sed -i "s|\(${mount_point_match_regexp}.*${previous_mount_opts}\)|\1,nodev|" /etc/fstab + fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Mount Remote Filesystems with noexec + Add the noexec option to the fourth column of /etc/fstab for the line which controls mounting of +any NFS mounts. + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + CCI-000366 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + AC-6 + AC-6(8) + AC-6(10) + CM-6(a) + PR.AC-4 + PR.DS-5 + SRG-OS-000480-GPOS-00227 + The noexec mount option causes the system not to execute binary files. This option must be used +for mounting any file system not containing approved binary files as they may be incompatible. Executing +files from untrusted file systems increases the opportunity for unprivileged users to attain unauthorized +administrative access. + CCE-84246-8 + - name: Get nfs and nfs4 mount points, that don't have noexec + command: findmnt --fstab --types nfs,nfs4 -O nonoexec -n + register: points_register + check_mode: false + changed_when: false + failed_when: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84246-8 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(10) + - NIST-800-53-AC-6(8) + - NIST-800-53-CM-6(a) + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - mount_option_noexec_remote_filesystems + - no_reboot_needed + +- name: Add noexec to nfs and nfs4 mount points + mount: + path: '{{ item.split()[0] }}' + src: '{{ item.split()[1] }}' + fstype: '{{ item.split()[2] }}' + state: mounted + opts: '{{ item.split()[3] }},noexec' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - (points_register.stdout | length > 0) + with_items: '{{ points_register.stdout_lines }}' + tags: + - CCE-84246-8 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(10) + - NIST-800-53-AC-6(8) + - NIST-800-53-CM-6(a) + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - mount_option_noexec_remote_filesystems + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +vfstype_points=() +readarray -t vfstype_points < <(grep -E "[[:space:]]nfs[4]?[[:space:]]" /etc/fstab | awk '{print $2}') + +for vfstype_point in "${vfstype_points[@]}" +do + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" ${vfstype_point//\\/\\\\})" + + # If the mount point is not in /etc/fstab, get previous mount options from /etc/mtab + if [ "$(grep -c "$mount_point_match_regexp" /etc/fstab)" -eq 0 ]; then + # runtime opts without some automatic kernel/userspace-added defaults + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/mtab | head -1 | awk '{print $4}' \ + | sed -E "s/(rw|defaults|seclabel|noexec)(,|$)//g;s/,$//") + [ "$previous_mount_opts" ] && previous_mount_opts+="," + echo " ${vfstype_point//\\/\\\\} nfs4 defaults,${previous_mount_opts}noexec 0 0" >> /etc/fstab + # If the mount_opt option is not already in the mount point's /etc/fstab entry, add it + elif [ "$(grep "$mount_point_match_regexp" /etc/fstab | grep -c "noexec")" -eq 0 ]; then + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/fstab | awk '{print $4}') + sed -i "s|\(${mount_point_match_regexp}.*${previous_mount_opts}\)|\1,noexec|" /etc/fstab + fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Mount Remote Filesystems with nosuid + Add the nosuid option to the fourth column of /etc/fstab for the line which controls mounting of +any NFS mounts. + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + CCI-000366 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + AC-6 + AC-6(1) + CM6(a) + PR.AC-4 + PR.DS-5 + SRG-OS-000480-GPOS-00227 + NFS mounts should not present suid binaries to users. Only vendor-supplied suid executables +should be installed to their default location on the local filesystem. + CCE-84247-6 + - name: Get nfs and nfs4 mount points, that don't have nosuid + command: findmnt --fstab --types nfs,nfs4 -O nonosuid -n + register: points_register + check_mode: false + changed_when: false + failed_when: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84247-6 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM6(a) + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - mount_option_nosuid_remote_filesystems + - no_reboot_needed + +- name: Add nosuid to nfs and nfs4 mount points + mount: + path: '{{ item.split()[0] }}' + src: '{{ item.split()[1] }}' + fstype: '{{ item.split()[2] }}' + state: mounted + opts: '{{ item.split()[3] }},nosuid' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - (points_register.stdout | length > 0) + with_items: '{{ points_register.stdout_lines }}' + tags: + - CCE-84247-6 + - NIST-800-53-AC-6 + - NIST-800-53-AC-6(1) + - NIST-800-53-CM6(a) + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - mount_option_nosuid_remote_filesystems + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +vfstype_points=() +readarray -t vfstype_points < <(grep -E "[[:space:]]nfs[4]?[[:space:]]" /etc/fstab | awk '{print $2}') + +for vfstype_point in "${vfstype_points[@]}" +do + mount_point_match_regexp="$(printf "[[:space:]]%s[[:space:]]" ${vfstype_point//\\/\\\\})" + + # If the mount point is not in /etc/fstab, get previous mount options from /etc/mtab + if [ "$(grep -c "$mount_point_match_regexp" /etc/fstab)" -eq 0 ]; then + # runtime opts without some automatic kernel/userspace-added defaults + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/mtab | head -1 | awk '{print $4}' \ + | sed -E "s/(rw|defaults|seclabel|nosuid)(,|$)//g;s/,$//") + [ "$previous_mount_opts" ] && previous_mount_opts+="," + echo " ${vfstype_point//\\/\\\\} nfs4 defaults,${previous_mount_opts}nosuid 0 0" >> /etc/fstab + # If the mount_opt option is not already in the mount point's /etc/fstab entry, add it + elif [ "$(grep "$mount_point_match_regexp" /etc/fstab | grep -c "nosuid")" -eq 0 ]; then + previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/fstab | awk '{print $4}') + sed -i "s|\(${mount_point_match_regexp}.*${previous_mount_opts}\)|\1,nosuid|" /etc/fstab + fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + + Configure NFS Servers + The steps in this section are appropriate for systems which operate as NFS servers. + + Ensure All-Squashing Disabled On All Exports + The all_squash maps all uids and gids to an anonymous user. +This should be disabled by removing any instances of the +all_squash option from the file /etc/exports. + The all_squash option maps all client requests to a single anonymous +uid/gid on the NFS server, negating the ability to track file access +by user ID. + + + + + + Use Kerberos Security on All Exports + Using Kerberos on all exported mounts prevents a malicious client or user from +impersonating a system user. To cryptography authenticate users to the NFS server, +add sec=krb5:krb5i:krb5p to each export in /etc/exports. + 1 + 12 + 14 + 15 + 16 + 18 + 3 + 5 + DSS05.04 + DSS05.10 + DSS06.10 + CCI-000366 + 164.308(a)(4)(i) + 164.308(b)(1) + 164.308(b)(3) + 164.310(b) + 164.312(e)(1) + 164.312(e)(2)(ii) + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.3 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + A.18.1.4 + A.6.1.2 + A.9.1.2 + A.9.2.1 + A.9.2.3 + A.9.2.4 + A.9.3.1 + A.9.4.1 + A.9.4.2 + A.9.4.3 + A.9.4.4 + A.9.4.5 + CM-7(a) + CM-7(b) + CM-6(a) + IA-2 + IA-2(8) + IA-2(9) + AC-17(a) + PR.AC-4 + PR.AC-7 + SRG-OS-000480-GPOS-00227 + When an NFS server is configured to use AUTH_SYS a selected userid and groupid are used to handle +requests from the remote user. The userid and groupid could mistakenly or maliciously be set +incorrectly. The AUTH_GSS method of authentication uses certificates on the server and client +systems to more securely authenticate the remote mount request. + CCE-89947-6 + - name: Drop any security clause for every export + replace: + path: /etc/exports + regexp: ^(/.*\w+.*\(.*),sec=[^,]*(.*\)\w*$) + replace: \1\2 + ignore_errors: true + tags: + - CCE-89947-6 + - NIST-800-53-AC-17(a) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-IA-2 + - NIST-800-53-IA-2(8) + - NIST-800-53-IA-2(9) + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - use_kerberos_security_all_exports + +- name: Add kerberos security when no security is defined for an export + replace: + path: /etc/exports + regexp: ^(/.*\w+.*\(.*)(\)\w*$) + replace: \1,sec=krb5:krb5i:krb5p\2 + tags: + - CCE-89947-6 + - NIST-800-53-AC-17(a) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-IA-2 + - NIST-800-53-IA-2(8) + - NIST-800-53-IA-2(9) + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - use_kerberos_security_all_exports + + +nfs_exports=() +readarray -t nfs_exports < <(grep -E "^/.*[[:space:]]+ .*\(.*\)[[:space:]]*$" /etc/exports | awk '{print $2}') + +for nfs_export in "${nfs_exports[@]}" +do + correct_export="" + if [ "$(grep -c "sec=" <<<"$nfs_export")" -eq 0 ]; then + correct_export="$(echo $nfs_export|sed -e 's/).*$/,sec=krb5\:krb5i\:krb5p)/')" + else + correct_export="$(echo $nfs_export|sed -e 's/sec=[^\,\)]*/sec=krb5\:krb5i\:krb5p/')" + fi + sed -i "s|$nfs_export|$correct_export|g" /etc/exports +done + + + + + + + + + + Configure the Exports File Restrictively + Linux's NFS implementation uses the file /etc/exports to control what filesystems +and directories may be accessed via NFS. (See the exports(5) manpage for more information about the +format of this file.) + +The syntax of the exports file is not necessarily checked fully on reload, and syntax errors +can leave your NFS configuration more open than intended. Therefore, exercise caution when modifying +the file. + +The syntax of each line in /etc/exports is: +/DIR host1(opt1,opt2) host2(opt3) +where /DIR is a directory or filesystem to export, hostN is an IP address, netblock, +hostname, domain, or netgroup to which to export, and optN is an option. + + + Export Filesystems Read-Only if Possible + If a filesystem is being exported so that users can view the files in a convenient +fashion, but there is no need for users to edit those files, exporting the filesystem read-only +removes an attack vector against the server. The default filesystem export mode is ro, +so do not specify rw without a good reason. + + + Use Access Lists to Enforce Authorization Restrictions + When configuring NFS exports, ensure that each export line in /etc/exports contains +a list of hosts which are allowed to access that export. If no hosts are specified on an export line, +then that export is available to any remote host which requests it. All lines of the exports file should +specify the hosts (or subnets, if needed) which are allowed to access the exported directory, so that +unknown or remote hosts will be denied. + +Authorized hosts can be specified in several different formats: +Name or alias that is recognized by the resolverFully qualified domain nameIP addressIP subnets in the format address/netmask or address/CIDR + + + + + Network Time Protocol + The Network Time Protocol is used to manage the system +clock over a network. Computer clocks are not very accurate, so +time will drift unpredictably on unmanaged systems. Central time +protocols can be used both to ensure that time is consistent among +a network of systems, and that their time is consistent with the +outside world. + +If every system on a network reliably reports the same time, then it is much +easier to correlate log messages in case of an attack. In addition, a number of +cryptographic protocols (such as Kerberos) use timestamps to prevent certain +types of attacks. If your network does not have synchronized time, these +protocols may be unreliable or even unusable. + +Depending on the specifics of the network, global time accuracy may be just as +important as local synchronization, or not very important at all. If your +network is connected to the Internet, using a public timeserver (or one +provided by your enterprise) provides globally accurate timestamps which may be +essential in investigating or responding to an attack which originated outside +of your network. + +A typical network setup involves a small number of internal systems operating +as NTP servers, and the remainder obtaining time information from those +internal servers. + +There is a choice between the daemons ntpd and chronyd, which +are available from the repositories in the ntp and chrony +packages respectively. + +The default chronyd daemon can work well when external time references +are only intermittently accesible, can perform well even when the network is +congested for longer periods of time, can usually synchronize the clock faster +and with better time accuracy, and quickly adapts to sudden changes in the rate +of the clock, for example, due to changes in the temperature of the crystal +oscillator. Chronyd should be considered for all systems which are +frequently suspended or otherwise intermittently disconnected and reconnected +to a network. Mobile and virtual systems for example. + +The ntpd NTP daemon fully supports NTP protocol version 4 (RFC 5905), +including broadcast, multicast, manycast clients and servers, and the orphan +mode. It also supports extra authentication schemes based on public-key +cryptography (RFC 5906). The NTP daemon (ntpd) should be considered +for systems which are normally kept permanently on. Systems which are required +to use broadcast or multicast IP, or to perform authentication of packets with +the Autokey protocol, should consider using ntpd. + +Refer to + + + https://docs.fedoraproject.org/en-US/fedora/rawhide/system-administrators-guide/servers/Configuring_NTP_Using_the_chrony_Suite/ + +for more detailed comparison of features of chronyd +and ntpd daemon features respectively, and for further guidance how to +choose between the two NTP daemons. + +The upstream manual pages at + http://chrony.tuxfamily.org/manual.html for +chronyd and + http://www.ntp.org for ntpd provide additional +information on the capabilities and configuration of each of the NTP daemons. + + + Vendor Approved Time Servers + The list of vendor-approved time servers + 0.pool.ntp.org,1.pool.ntp.org,2.pool.ntp.org,3.pool.ntp.org + 0.fedora.pool.ntp.org,1.fedora.pool.ntp.org,2.fedora.pool.ntp.org,3.fedora.pool.ntp.org + 0.rhel.pool.ntp.org,1.rhel.pool.ntp.org,2.rhel.pool.ntp.org,3.rhel.pool.ntp.org + 0.pool.ntp.org,1.pool.ntp.org,2.pool.ntp.org,3.pool.ntp.org + 0.suse.pool.ntp.org,1.suse.pool.ntp.org,2.suse.pool.ntp.org,3.suse.pool.ntp.org + 0.ntp.cloud.aliyuncs.com,1.ntp.aliyun.com,2.ntp1.aliyun.com,3.ntp1.cloud.aliyuncs.com + + + Maximum NTP or Chrony Poll + The maximum NTP or Chrony poll interval number in seconds specified as a power of two. + 17 + 16 + 10 + 10 + + + The Chrony package is installed + System time should be synchronized between all systems in an environment. This is +typically done by establishing an authoritative time server or set of servers and having all +systems synchronize their clocks to them. +The chrony package can be installed with the following command: + +$ sudo dnf install chrony + BP28(R43) + 0988 + 1405 + FMT_SMF_EXT.1 + SRG-OS-000355-GPOS-00143 + Time synchronization is important to support time sensitive security mechanisms like +Kerberos and also ensures log files have consistent time records across the enterprise, +which aids in forensic investigations. + + CCE-84215-3 + +package --add=chrony + + include install_chrony + +class install_chrony { + package { 'chrony': + ensure => 'installed', + } +} + + - name: Ensure chrony is installed + package: + name: chrony + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84215-3 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_chrony_installed + + +[[packages]] +name = "chrony" +version = "*" + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if ! rpm -q --quiet "chrony" ; then + dnf install -y "chrony" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Install the ntp service + The ntpd service should be installed. + NT012(R03) + 1 + 14 + 15 + 16 + 3 + 5 + 6 + APO11.04 + BAI03.05 + DSS05.04 + DSS05.07 + MEA02.01 + CCI-000160 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + CM-6(a) + PR.PT-1 + Req-10.4 + Time synchronization (using NTP) is required by almost all network and administrative tasks (syslog, cryptographic based services (authentication, etc.), etc.). Ntpd is regulary maintained and updated, supporting security features such as RFC 5906. + +package --add=ntp + + include install_ntp + +class install_ntp { + package { 'ntp': + ensure => 'installed', + } +} + + - name: Ensure ntp is installed + package: + name: ntp + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.4 + - enable_strategy + - high_severity + - low_complexity + - low_disruption + - no_reboot_needed + - package_ntp_installed + + +[[packages]] +name = "ntp" +version = "*" + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if ! rpm -q --quiet "ntp" ; then + dnf install -y "ntp" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + The Chronyd service is enabled + chrony is a daemon which implements the Network Time Protocol (NTP) is designed to +synchronize system clocks across a variety of systems and use a source that is highly +accurate. More information on chrony can be found at + + http://chrony.tuxfamily.org/. +Chrony can be configured to be a client and/or a server. +To enable Chronyd service, you can run: +# systemctl enable chronyd.service +This recommendation only applies if chrony is in use on the system. + 0988 + 1405 + SRG-OS-000355-GPOS-00143 + If chrony is in use on the system proper configuration is vital to ensuring time +synchronization is working properly. + + CCE-84217-9 + include enable_chronyd + +class enable_chronyd { + service {'chronyd': + enable => true, + ensure => 'running', + } +} + + - name: Enable service chronyd + block: + + - name: Gather the package facts + package_facts: + manager: auto + + - name: Enable service chronyd + service: + name: chronyd + enabled: 'yes' + state: started + masked: 'no' + when: + - '"chrony" in ansible_facts.packages' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84217-9 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_chronyd_enabled + + +[customizations.services] +enabled = ["chronyd"] + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" unmask 'chronyd.service' +"$SYSTEMCTL_EXEC" start 'chronyd.service' +"$SYSTEMCTL_EXEC" enable 'chronyd.service' + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Enable the NTP Daemon + +The ntpd service can be enabled with the following command: +$ sudo systemctl enable ntpd.service + NT012(R03) + 1 + 14 + 15 + 16 + 3 + 5 + 6 + APO11.04 + BAI03.05 + DSS05.04 + DSS05.07 + MEA02.01 + CCI-000160 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + CM-6(a) + AU-8(1)(a) + PR.PT-1 + Req-10.4 + Enabling the ntpd service ensures that the ntpd +service will be running and that the system will synchronize its time to +any servers specified. This is important whether the system is configured to be +a client (and synchronize only its own clock) or it is also acting as an NTP +server to other systems. Synchronizing time is essential for authentication +services such as Kerberos, but it is also important for maintaining accurate +logs and auditing possible security breaches. + +The NTP daemon offers all of the functionality of ntpdate, which is now +deprecated. + include enable_ntp + +class enable_ntp { + service {'ntp': + enable => true, + ensure => 'running', + } +} + + - name: Enable service ntp + block: + + - name: Gather the package facts + package_facts: + manager: auto + + - name: Enable service ntp + service: + name: ntp + enabled: 'yes' + state: started + masked: 'no' + when: + - '"ntp" in ansible_facts.packages' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-AU-8(1)(a) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.4 + - enable_strategy + - high_severity + - low_complexity + - low_disruption + - no_reboot_needed + - service_ntp_enabled + + +[customizations.services] +enabled = ["ntp"] + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" unmask 'ntp.service' +"$SYSTEMCTL_EXEC" start 'ntp.service' +"$SYSTEMCTL_EXEC" enable 'ntp.service' + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Enable the NTP Daemon + +The ntpd service can be enabled with the following command: +$ sudo systemctl enable ntpd.service + 1 + 14 + 15 + 16 + 3 + 5 + 6 + APO11.04 + BAI03.05 + DSS05.04 + DSS05.07 + MEA02.01 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + CM-6(a) + AU-8(1)(a) + PR.PT-1 + Req-10.4 + Enabling the ntpd service ensures that the ntpd +service will be running and that the system will synchronize its time to +any servers specified. This is important whether the system is configured to be +a client (and synchronize only its own clock) or it is also acting as an NTP +server to other systems. Synchronizing time is essential for authentication +services such as Kerberos, but it is also important for maintaining accurate +logs and auditing possible security breaches. + +The NTP daemon offers all of the functionality of ntpdate, which is now +deprecated. + + CCE-87863-7 + include enable_ntpd + +class enable_ntpd { + service {'ntpd': + enable => true, + ensure => 'running', + } +} + + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-87863-7 + - NIST-800-53-AU-8(1)(a) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.4 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_ntpd_enabled + +- name: Enable service ntpd + block: + + - name: Gather the package facts + package_facts: + manager: auto + + - name: Enable service ntpd + service: + name: ntpd + enabled: 'yes' + state: started + masked: 'no' + when: + - '"ntp" in ansible_facts.packages' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"ntp" in ansible_facts.packages' + tags: + - CCE-87863-7 + - NIST-800-53-AU-8(1)(a) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.4 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_ntpd_enabled + + +[customizations.services] +enabled = ["ntpd"] + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && { rpm --quiet -q ntp; }; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" unmask 'ntpd.service' +"$SYSTEMCTL_EXEC" start 'ntpd.service' +"$SYSTEMCTL_EXEC" enable 'ntpd.service' + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable chrony daemon from acting as server + The port option in /etc/chrony.conf can be set to +0 to make chrony daemon to never open any listening port +for server operation and to operate strictly in a client-only mode. + CCI-000381 + AU-8(1) + AU-12(1) + FMT_SMF_EXT.1 + SRG-OS-000096-GPOS-00050 + SRG-OS-000095-GPOS-00049 + Minimizing the exposure of the server functionality of the chrony +daemon diminishes the attack surface. + + CCE-87543-5 + - name: Disable chrony daemon from acting as server + block: + + - name: Check for duplicate values + lineinfile: + path: /etc/chrony.conf + create: false + regexp: ^\s*port\s+ + state: absent + check_mode: true + changed_when: false + register: dupes + + - name: Deduplicate values from /etc/chrony.conf + lineinfile: + path: /etc/chrony.conf + create: false + regexp: ^\s*port\s+ + state: absent + when: dupes.found is defined and dupes.found > 1 + + - name: Insert correct line to /etc/chrony.conf + lineinfile: + path: /etc/chrony.conf + create: true + regexp: ^\s*port\s+ + line: port 0 + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-87543-5 + - NIST-800-53-AU-12(1) + - NIST-800-53-AU-8(1) + - chronyd_client_only + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,{{ %23%20Allow%20for%20extra%20configuration%20files.%20This%20is%20useful%0A%23%20for%20admins%20specifying%20their%20own%20NTP%20servers%0Ainclude%20/etc/chrony.d/%2A.conf%0A%0A%23%20Set%20chronyd%20as%20client-only.%0Aport%200%0A%0A%23%20Disable%20chronyc%20from%20the%20network%0Acmdport%200%0A%0A%23%20Record%20the%20rate%20at%20which%20the%20system%20clock%20gains/losses%20time.%0Adriftfile%20/var/lib/chrony/drift%0A%0A%23%20Allow%20the%20system%20clock%20to%20be%20stepped%20in%20the%20first%20three%20updates%0A%23%20if%20its%20offset%20is%20larger%20than%201%20second.%0Amakestep%201.0%203%0A%0A%23%20Enable%20kernel%20synchronization%20of%20the%20real-time%20clock%20%28RTC%29.%0Artcsync%0A%0A%23%20Enable%20hardware%20timestamping%20on%20all%20interfaces%20that%20support%20it.%0A%23hwtimestamp%20%2A%0A%0A%23%20Increase%20the%20minimum%20number%20of%20selectable%20sources%20required%20to%20adjust%0A%23%20the%20system%20clock.%0A%23minsources%202%0A%0A%23%20Allow%20NTP%20client%20access%20from%20local%20network.%0A%23allow%20192.168.0.0/16%0A%0A%23%20Serve%20time%20even%20if%20not%20synchronized%20to%20a%20time%20source.%0A%23local%20stratum%2010%0A%0A%23%20Require%20authentication%20%28nts%20or%20key%20option%29%20for%20all%20NTP%20sources.%0A%23authselectmode%20require%0A%0A%23%20Specify%20file%20containing%20keys%20for%20NTP%20authentication.%0Akeyfile%20/etc/chrony.keys%0A%0A%23%20Insert/delete%20leap%20seconds%20by%20slewing%20instead%20of%20stepping.%0A%23leapsecmode%20slew%0A%0A%23%20Get%20TAI-UTC%20offset%20and%20leap%20seconds%20from%20the%20system%20tz%20database.%0Aleapsectz%20right/UTC%0A%0A%23%20Specify%20directory%20for%20log%20files.%0Alogdir%20/var/log/chrony%0A%0A%23%20Select%20which%20information%20is%20logged.%0A%23log%20measurements%20statistics%20tracking }} + mode: 420 + overwrite: true + path: /etc/chrony.conf + - contents: + source: data:, + mode: 420 + overwrite: true + path: /etc/chrony.d/.mco-keep + - contents: + source: data:,{{ %23%0A%23%20This%20file%20controls%20the%20configuration%20of%20the%20ntp%20server%0A%23%20%7B%7B.var_multiple_time_servers%7D%7D%20we%20have%20to%20put%20variable%20array%20name%20here%20for%20mutilines%20remediation%20%0A%7B%7B%24var_time_service_set_maxpoll%3A%3D.var_time_service_set_maxpoll%7D%7D%0A%7B%7Brange%20%24element%3A%3D.var_multiple_time_servers%7CtoArrayByComma%7D%7Dserver%20%7B%7B%24element%7D%7D%20minpoll%204%20maxpoll%20%7B%7B%24var_time_service_set_maxpoll%7D%7D%0A%7B%7Bend%7D%7D }} + mode: 420 + overwrite: true + path: /etc/chrony.d/ntp-server.conf + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/chrony.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^port") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s %s" "$stripped_key" "0" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^port\\>" "/etc/chrony.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^port\\>.*/$escaped_formatted_output/gi" "/etc/chrony.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-87543-5" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/chrony.conf" >> "/etc/chrony.conf" + printf '%s\n' "$formatted_output" >> "/etc/chrony.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable network management of chrony daemon + The cmdport option in /etc/chrony.conf can be set to +0 to stop chrony daemon from listening on the UDP port 323 +for management connections made by chronyc. + CCI-000381 + CM-7(1) + FMT_SMF_EXT.1 + SRG-OS-000096-GPOS-00050 + SRG-OS-000095-GPOS-00049 + Not exposing the management interface of the chrony daemon on +the network diminishes the attack space. + + CCE-88876-8 + - name: Disable network management of chrony daemon + block: + + - name: Check for duplicate values + lineinfile: + path: /etc/chrony.conf + create: false + regexp: ^\s*cmdport\s+ + state: absent + check_mode: true + changed_when: false + register: dupes + + - name: Deduplicate values from /etc/chrony.conf + lineinfile: + path: /etc/chrony.conf + create: false + regexp: ^\s*cmdport\s+ + state: absent + when: dupes.found is defined and dupes.found > 1 + + - name: Insert correct line to /etc/chrony.conf + lineinfile: + path: /etc/chrony.conf + create: true + regexp: ^\s*cmdport\s+ + line: cmdport 0 + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-88876-8 + - NIST-800-53-CM-7(1) + - chronyd_no_chronyc_network + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,{{ %23%20Allow%20for%20extra%20configuration%20files.%20This%20is%20useful%0A%23%20for%20admins%20specifying%20their%20own%20NTP%20servers%0Ainclude%20/etc/chrony.d/%2A.conf%0A%0A%23%20Set%20chronyd%20as%20client-only.%0Aport%200%0A%0A%23%20Disable%20chronyc%20from%20the%20network%0Acmdport%200%0A%0A%23%20Record%20the%20rate%20at%20which%20the%20system%20clock%20gains/losses%20time.%0Adriftfile%20/var/lib/chrony/drift%0A%0A%23%20Allow%20the%20system%20clock%20to%20be%20stepped%20in%20the%20first%20three%20updates%0A%23%20if%20its%20offset%20is%20larger%20than%201%20second.%0Amakestep%201.0%203%0A%0A%23%20Enable%20kernel%20synchronization%20of%20the%20real-time%20clock%20%28RTC%29.%0Artcsync%0A%0A%23%20Enable%20hardware%20timestamping%20on%20all%20interfaces%20that%20support%20it.%0A%23hwtimestamp%20%2A%0A%0A%23%20Increase%20the%20minimum%20number%20of%20selectable%20sources%20required%20to%20adjust%0A%23%20the%20system%20clock.%0A%23minsources%202%0A%0A%23%20Allow%20NTP%20client%20access%20from%20local%20network.%0A%23allow%20192.168.0.0/16%0A%0A%23%20Serve%20time%20even%20if%20not%20synchronized%20to%20a%20time%20source.%0A%23local%20stratum%2010%0A%0A%23%20Require%20authentication%20%28nts%20or%20key%20option%29%20for%20all%20NTP%20sources.%0A%23authselectmode%20require%0A%0A%23%20Specify%20file%20containing%20keys%20for%20NTP%20authentication.%0Akeyfile%20/etc/chrony.keys%0A%0A%23%20Insert/delete%20leap%20seconds%20by%20slewing%20instead%20of%20stepping.%0A%23leapsecmode%20slew%0A%0A%23%20Get%20TAI-UTC%20offset%20and%20leap%20seconds%20from%20the%20system%20tz%20database.%0Aleapsectz%20right/UTC%0A%0A%23%20Specify%20directory%20for%20log%20files.%0Alogdir%20/var/log/chrony%0A%0A%23%20Select%20which%20information%20is%20logged.%0A%23log%20measurements%20statistics%20tracking }} + mode: 420 + overwrite: true + path: /etc/chrony.conf + - contents: + source: data:, + mode: 420 + overwrite: true + path: /etc/chrony.d/.mco-keep + - contents: + source: data:,{{ %23%0A%23%20This%20file%20controls%20the%20configuration%20of%20the%20ntp%20server%0A%23%20%7B%7B.var_multiple_time_servers%7D%7D%20we%20have%20to%20put%20variable%20array%20name%20here%20for%20mutilines%20remediation%20%0A%7B%7B%24var_time_service_set_maxpoll%3A%3D.var_time_service_set_maxpoll%7D%7D%0A%7B%7Brange%20%24element%3A%3D.var_multiple_time_servers%7CtoArrayByComma%7D%7Dserver%20%7B%7B%24element%7D%7D%20minpoll%204%20maxpoll%20%7B%7B%24var_time_service_set_maxpoll%7D%7D%0A%7B%7Bend%7D%7D }} + mode: 420 + overwrite: true + path: /etc/chrony.d/ntp-server.conf + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/chrony.conf"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^cmdport") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s %s" "$stripped_key" "0" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^cmdport\\>" "/etc/chrony.conf"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^cmdport\\>.*/$escaped_formatted_output/gi" "/etc/chrony.conf" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-88876-8" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/chrony.conf" >> "/etc/chrony.conf" + printf '%s\n' "$formatted_output" >> "/etc/chrony.conf" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure Time Service Maxpoll Interval + The maxpoll should be configured to + in /etc/ntp.conf or +/etc/chrony.conf to continuously poll time servers. To configure +maxpoll in /etc/ntp.conf or /etc/chrony.conf +add the following after each `server`, `pool` or `peer` entry: +maxpoll +to server directives. If using chrony any pool directives +should be configured too. +If no server or pool directives are configured, the rule evaluates +to pass. + 1 + 14 + 15 + 16 + 3 + 5 + 6 + APO11.04 + BAI03.05 + DSS05.04 + DSS05.07 + MEA02.01 + CCI-001891 + CCI-002046 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + CM-6(a) + AU-8(1)(b) + AU-12(1) + PR.PT-1 + SRG-OS-000355-GPOS-00143 + SRG-OS-000356-GPOS-00144 + SRG-OS-000359-GPOS-00146 + Inaccurate time stamps make it more difficult to correlate events and can lead to an inaccurate analysis. Determining the correct time a particular event occurred on a system is critical when conducting forensic analysis and investigating system events. Sources outside the configured acceptable allowance (drift) may be inaccurate. +Synchronizing internal information system clocks provides uniformity of time stamps for information systems with multiple system clocks and systems connected over a network. +Organizations should consider endpoints that may not have regular access to the authoritative time server (e.g., mobile, teleworking, and tactical endpoints). + + CCE-88648-1 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-88648-1 + - NIST-800-53-AU-12(1) + - NIST-800-53-AU-8(1)(b) + - NIST-800-53-CM-6(a) + - chronyd_or_ntpd_set_maxpoll + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy +- name: XCCDF Value var_time_service_set_maxpoll # promote to variable + set_fact: + var_time_service_set_maxpoll: !!str + tags: + - always + +- name: Check that /etc/ntp.conf exist + stat: + path: /etc/ntp.conf + register: ntp_conf_exist_result + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ( "chrony" in ansible_facts.packages or "ntp" in ansible_facts.packages ) + tags: + - CCE-88648-1 + - NIST-800-53-AU-12(1) + - NIST-800-53-AU-8(1)(b) + - NIST-800-53-CM-6(a) + - chronyd_or_ntpd_set_maxpoll + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Update the maxpoll values in /etc/ntp.conf + replace: + path: /etc/ntp.conf + regexp: ^(server.*maxpoll)[ ]+[0-9]+(.*)$ + replace: \1 {{ var_time_service_set_maxpoll }}\2 + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ( "chrony" in ansible_facts.packages or "ntp" in ansible_facts.packages ) + - ntp_conf_exist_result.stat.exists + tags: + - CCE-88648-1 + - NIST-800-53-AU-12(1) + - NIST-800-53-AU-8(1)(b) + - NIST-800-53-CM-6(a) + - chronyd_or_ntpd_set_maxpoll + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Set the maxpoll values in /etc/ntp.conf + replace: + path: /etc/ntp.conf + regexp: (^server\s+((?!maxpoll).)*)$ + replace: \1 maxpoll {{ var_time_service_set_maxpoll }}\n + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ( "chrony" in ansible_facts.packages or "ntp" in ansible_facts.packages ) + - ntp_conf_exist_result.stat.exists + tags: + - CCE-88648-1 + - NIST-800-53-AU-12(1) + - NIST-800-53-AU-8(1)(b) + - NIST-800-53-CM-6(a) + - chronyd_or_ntpd_set_maxpoll + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Check that /etc/chrony.conf exist + stat: + path: /etc/chrony.conf + register: chrony_conf_exist_result + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ( "chrony" in ansible_facts.packages or "ntp" in ansible_facts.packages ) + tags: + - CCE-88648-1 + - NIST-800-53-AU-12(1) + - NIST-800-53-AU-8(1)(b) + - NIST-800-53-CM-6(a) + - chronyd_or_ntpd_set_maxpoll + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Get get conf files from /etc/chrony.conf + shell: | + set -o pipefail + CHRONY_NAME=/etc/chrony.conf + CHRONY_PATH=${CHRONY_NAME%%.*} + find ${CHRONY_PATH}.* -type f -name '*.conf' + register: update_chrony_files + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ( "chrony" in ansible_facts.packages or "ntp" in ansible_facts.packages ) + - chrony_conf_exist_result.stat.exists + changed_when: false + tags: + - CCE-88648-1 + - NIST-800-53-AU-12(1) + - NIST-800-53-AU-8(1)(b) + - NIST-800-53-CM-6(a) + - chronyd_or_ntpd_set_maxpoll + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Update the maxpoll values in /etc/chrony.conf + replace: + path: '{{ item }}' + regexp: ^((?:server|pool|peer).*maxpoll)[ ]+[0-9]+(.*)$ + replace: \1 {{ var_time_service_set_maxpoll }}\2 + loop: '{{ update_chrony_files.stdout_lines|list|flatten|unique }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ( "chrony" in ansible_facts.packages or "ntp" in ansible_facts.packages ) + - chrony_conf_exist_result.stat.exists + tags: + - CCE-88648-1 + - NIST-800-53-AU-12(1) + - NIST-800-53-AU-8(1)(b) + - NIST-800-53-CM-6(a) + - chronyd_or_ntpd_set_maxpoll + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + +- name: Set the maxpoll values in /etc/chrony.conf + replace: + path: '{{ item }}' + regexp: (^(?:server|pool|peer)\s+((?!maxpoll).)*)$ + replace: \1 maxpoll {{ var_time_service_set_maxpoll }}\n + loop: '{{ update_chrony_files.stdout_lines|list|flatten|unique }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - ( "chrony" in ansible_facts.packages or "ntp" in ansible_facts.packages ) + - chrony_conf_exist_result.stat.exists + tags: + - CCE-88648-1 + - NIST-800-53-AU-12(1) + - NIST-800-53-AU-8(1)(b) + - NIST-800-53-CM-6(a) + - chronyd_or_ntpd_set_maxpoll + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,{{ %23%20Allow%20for%20extra%20configuration%20files.%20This%20is%20useful%0A%23%20for%20admins%20specifying%20their%20own%20NTP%20servers%0Ainclude%20/etc/chrony.d/%2A.conf%0A%0A%23%20Set%20chronyd%20as%20client-only.%0Aport%200%0A%0A%23%20Disable%20chronyc%20from%20the%20network%0Acmdport%200%0A%0A%23%20Record%20the%20rate%20at%20which%20the%20system%20clock%20gains/losses%20time.%0Adriftfile%20/var/lib/chrony/drift%0A%0A%23%20Allow%20the%20system%20clock%20to%20be%20stepped%20in%20the%20first%20three%20updates%0A%23%20if%20its%20offset%20is%20larger%20than%201%20second.%0Amakestep%201.0%203%0A%0A%23%20Enable%20kernel%20synchronization%20of%20the%20real-time%20clock%20%28RTC%29.%0Artcsync%0A%0A%23%20Enable%20hardware%20timestamping%20on%20all%20interfaces%20that%20support%20it.%0A%23hwtimestamp%20%2A%0A%0A%23%20Increase%20the%20minimum%20number%20of%20selectable%20sources%20required%20to%20adjust%0A%23%20the%20system%20clock.%0A%23minsources%202%0A%0A%23%20Allow%20NTP%20client%20access%20from%20local%20network.%0A%23allow%20192.168.0.0/16%0A%0A%23%20Serve%20time%20even%20if%20not%20synchronized%20to%20a%20time%20source.%0A%23local%20stratum%2010%0A%0A%23%20Require%20authentication%20%28nts%20or%20key%20option%29%20for%20all%20NTP%20sources.%0A%23authselectmode%20require%0A%0A%23%20Specify%20file%20containing%20keys%20for%20NTP%20authentication.%0Akeyfile%20/etc/chrony.keys%0A%0A%23%20Insert/delete%20leap%20seconds%20by%20slewing%20instead%20of%20stepping.%0A%23leapsecmode%20slew%0A%0A%23%20Get%20TAI-UTC%20offset%20and%20leap%20seconds%20from%20the%20system%20tz%20database.%0Aleapsectz%20right/UTC%0A%0A%23%20Specify%20directory%20for%20log%20files.%0Alogdir%20/var/log/chrony%0A%0A%23%20Select%20which%20information%20is%20logged.%0A%23log%20measurements%20statistics%20tracking }} + mode: 420 + overwrite: true + path: /etc/chrony.conf + - contents: + source: data:, + mode: 420 + overwrite: true + path: /etc/chrony.d/.mco-keep + - contents: + source: data:,{{ %23%0A%23%20This%20file%20controls%20the%20configuration%20of%20the%20ntp%20server%0A%23%20%7B%7B.var_multiple_time_servers%7D%7D%20we%20have%20to%20put%20variable%20array%20name%20here%20for%20mutilines%20remediation%20%0A%7B%7B%24var_time_service_set_maxpoll%3A%3D.var_time_service_set_maxpoll%7D%7D%0A%7B%7Brange%20%24element%3A%3D.var_multiple_time_servers%7CtoArrayByComma%7D%7Dserver%20%7B%7B%24element%7D%7D%20minpoll%204%20maxpoll%20%7B%7B%24var_time_service_set_maxpoll%7D%7D%0A%7B%7Bend%7D%7D }} + mode: 420 + overwrite: true + path: /etc/chrony.d/ntp-server.conf + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && { ( rpm --quiet -q chrony || rpm --quiet -q ntp ); }; then + +var_time_service_set_maxpoll='' + + + + +pof="/usr/sbin/pidof" + + +CONFIG_FILES="/etc/ntp.conf" +$pof ntpd || { + CHRONY_NAME=/etc/chrony.conf + CHRONY_PATH=${CHRONY_NAME%%.*} + CONFIG_FILES=$(find ${CHRONY_PATH}.* -type f -name '*.conf') +} + +# get list of ntp files + +for config_file in $CONFIG_FILES; do + # Set maxpoll values to var_time_service_set_maxpoll + sed -i "s/^\(\(server\|pool\|peer\).*maxpoll\) [0-9][0-9]*\(.*\)$/\1 $var_time_service_set_maxpoll \3/" "$config_file" +done + + + + +for config_file in $CONFIG_FILES; do + # Add maxpoll to server, pool or peer entries without maxpoll + grep "^\(server\|pool\|peer\)" "$config_file" | grep -v maxpoll | while read -r line ; do + sed -i "s/$line/& maxpoll $var_time_service_set_maxpoll/" "$config_file" + done +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Ensure that chronyd is running under chrony user account + chrony is a daemon which implements the Network Time Protocol (NTP). It is designed to +synchronize system clocks across a variety of systems and use a source that is highly +accurate. More information on chrony can be found at + + http://chrony.tuxfamily.org/. +Chrony can be configured to be a client and/or a server. +To ensure that chronyd is running under chrony user account, +remove any -u ... option from OPTIONS other than -u chrony, +as chrony is run under its own user by default. +This recommendation only applies if chrony is in use on the system. + If chrony is in use on the system proper configuration is vital to ensuring time synchronization +is working properly. + + CCE-84108-0 + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && { rpm --quiet -q chrony; }; then + +if grep -q 'OPTIONS=.*' /etc/sysconfig/chronyd; then + # trying to solve cases where the parameter after OPTIONS + #may or may not be enclosed in quotes + sed -i -E -e 's/\s*-u\s*\w+\s*/ /' -e 's/^([\s]*OPTIONS=["]?[^"]*)("?)/\1\2/' /etc/sysconfig/chronyd +fi + +if grep -q 'OPTIONS=.*' /etc/sysconfig/chronyd; then + # trying to solve cases where the parameter after OPTIONS + #may or may not be enclosed in quotes + sed -i -E -e 's/\s*-u\s*\w+\s*/ /' -e 's/^([\s]*OPTIONS=["]?[^"]*)("?)/\1 -u chrony\2/' /etc/sysconfig/chronyd +else + echo 'OPTIONS="-u chrony"' >> /etc/sysconfig/chronyd +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Ensure Chrony is only configured with the server directive + Check that Chrony only has time sources configured with the server directive. + This rule doesn't come with a remediation, the time source needs to be added by the adminstrator. + CCI-001891 + SRG-OS-000355-GPOS-00143 + SRG-OS-000356-GPOS-00144 + SRG-OS-000359-GPOS-00146 + Depending on the infrastruture being used the pool directive may not be supported. + + CCE-87077-4 + + + + + + + + + A remote time server for Chrony is configured + Chrony is a daemon which implements the Network Time Protocol (NTP). It is designed to +synchronize system clocks across a variety of systems and use a source that is highly +accurate. More information on chrony can be found at + + http://chrony.tuxfamily.org/. +Chrony can be configured to be a client and/or a server. +Add or edit server or pool lines to /etc/chrony.conf as appropriate: +server <remote-server> +Multiple servers may be configured. + BP28(R43) + CCI-000160 + CCI-001891 + 0988 + 1405 + CM-6(a) + AU-8(1)(a) + Req-10.4.3 + If chrony is in use on the system proper configuration is vital to ensuring time +synchronization is working properly. + + CCE-84218-7 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-84218-7 + - NIST-800-53-AU-8(1)(a) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.4.3 + - chronyd_specify_remote_server + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed +- name: XCCDF Value var_multiple_time_servers # promote to variable + set_fact: + var_multiple_time_servers: !!str + tags: + - always + +- name: Detect if chrony is already configured with pools or servers + find: + path: /etc + patterns: chrony.conf + contains: ^[\s]*(?:server|pool)[\s]+[\w]+ + register: chrony_servers + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"chrony" in ansible_facts.packages' + tags: + - CCE-84218-7 + - NIST-800-53-AU-8(1)(a) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.4.3 + - chronyd_specify_remote_server + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Configure remote time servers + lineinfile: + path: /etc/chrony.conf + line: server {{ item }} + state: present + create: true + loop: '{{ var_multiple_time_servers.split(",") }}' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"chrony" in ansible_facts.packages' + - chrony_servers.matched == 0 + tags: + - CCE-84218-7 + - NIST-800-53-AU-8(1)(a) + - NIST-800-53-CM-6(a) + - PCI-DSS-Req-10.4.3 + - chronyd_specify_remote_server + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && { rpm --quiet -q chrony; }; then + +var_multiple_time_servers='' + + +config_file="/etc/chrony.conf" + +if ! grep -q '^[\s]*(?:server|pool)[\s]+[\w]+' "$config_file" ; then + if ! grep -q '#[[:space:]]*server' "$config_file" ; then + for server in $(echo "$var_multiple_time_servers" | tr ',' '\n') ; do + printf '\nserver %s' "$server" >> "$config_file" + done + else + sed -i 's/#[ \t]*server/server/g' "$config_file" + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Specify Additional Remote NTP Servers + Additional NTP servers can be specified for time synchronization +in the file /etc/ntp.conf. To do so, add additional lines of the +following form, substituting the IP address or hostname of a remote NTP server for +ntpserver: +server ntpserver + 1 + 14 + 15 + 16 + 3 + 5 + 6 + APO11.04 + BAI03.05 + DSS05.04 + DSS05.07 + MEA02.01 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + CM-6(a) + AU-8(1)(a) + AU-8(2) + PR.PT-1 + Req-10.4.3 + Specifying additional NTP servers increases the availability of +accurate time data, in the event that one of the specified servers becomes +unavailable. This is typical for a system acting as an NTP server for +other systems. + + + + + + Specify a Remote NTP Server + To specify a remote NTP server for time synchronization, edit +the file /etc/ntp.conf. Add or correct the following lines, +substituting the IP or hostname of a remote NTP server for ntpserver: +server ntpserver +This instructs the NTP software to contact that remote server to obtain time +data. + 1 + 14 + 15 + 16 + 3 + 5 + 6 + APO11.04 + BAI03.05 + DSS05.04 + DSS05.07 + MEA02.01 + 4.3.3.3.9 + 4.3.3.5.8 + 4.3.4.4.7 + 4.4.2.1 + 4.4.2.2 + 4.4.2.4 + SR 2.10 + SR 2.11 + SR 2.12 + SR 2.8 + SR 2.9 + A.12.4.1 + A.12.4.2 + A.12.4.3 + A.12.4.4 + A.12.7.1 + CM-6(a) + AU-8(1)(a) + PR.PT-1 + Req-10.4.1 + Req-10.4.3 + Synchronizing with an NTP server makes it possible +to collate system logs from multiple sources or correlate computer events with +real time events. + + + + + + + + + + + Obsolete Services + This section discusses a number of network-visible +services which have historically caused problems for system +security, and for which disabling or severely limiting the service +has been the best available guidance for some time. As a result of +this, many of these services are not installed as part of Red Hat Enterprise Linux 9 +by default. + +Organizations which are running these services should +switch to more secure equivalents as soon as possible. +If it remains absolutely necessary to run one of +these services for legacy reasons, care should be taken to restrict +the service as much as possible, for instance by configuring host + +firewall software such as iptables to restrict access to the + +vulnerable service to only those remote hosts which have a known +need to use it. + + Ensure rsyncd service is diabled + +The rsyncd service can be disabled with the following command: +$ sudo systemctl mask --now rsyncd.service + The rsyncd service presents a security risk as it uses unencrypted protocols for +communication. + + CCE-84140-3 + include disable_rsyncd + +class disable_rsyncd { + service {'rsyncd': + enable => false, + ensure => 'stopped', + } +} + + - name: Disable service rsyncd + block: + + - name: Disable service rsyncd + systemd: + name: rsyncd.service + enabled: 'no' + state: stopped + masked: 'yes' + ignore_errors: 'yes' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84140-3 + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_rsyncd_disabled + +- name: Unit Socket Exists - rsyncd.socket + command: systemctl list-unit-files rsyncd.socket + args: + warn: false + register: socket_file_exists + changed_when: false + ignore_errors: true + check_mode: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84140-3 + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_rsyncd_disabled + +- name: Disable socket rsyncd + systemd: + name: rsyncd.socket + enabled: 'no' + state: stopped + masked: 'yes' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"rsyncd.socket" in socket_file_exists.stdout_lines[1]' + tags: + - CCE-84140-3 + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_rsyncd_disabled + + +[customizations.services] +disabled = ["rsyncd"] + + apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + systemd: + units: + - name: rsyncd.service + enabled: false + mask: true + - name: rsyncd.socket + enabled: false + mask: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" stop 'rsyncd.service' +"$SYSTEMCTL_EXEC" disable 'rsyncd.service' +"$SYSTEMCTL_EXEC" mask 'rsyncd.service' +# Disable socket activation if we have a unit file for it +if "$SYSTEMCTL_EXEC" list-unit-files | grep -q '^rsyncd.socket'; then + "$SYSTEMCTL_EXEC" stop 'rsyncd.socket' + "$SYSTEMCTL_EXEC" mask 'rsyncd.socket' +fi +# The service may not be running because it has been started and failed, +# so let's reset the state so OVAL checks pass. +# Service should be 'inactive', not 'failed' after reboot though. +"$SYSTEMCTL_EXEC" reset-failed 'rsyncd.service' || true + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Xinetd + The xinetd service acts as a dedicated listener for some +network services (mostly, obsolete ones) and can be used to provide access +controls and perform some logging. It has been largely obsoleted by other +features, and it is not installed by default. The older Inetd service +is not even available as part of Red Hat Enterprise Linux 9. + + + Uninstall xinetd Package + The xinetd package can be removed with the following command: + +$ sudo dnf erase xinetd + BP28(R1) + 11 + 12 + 14 + 15 + 3 + 8 + 9 + APO13.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.04 + DSS05.02 + DSS05.03 + DSS05.05 + DSS06.06 + CCI-000305 + 164.308(a)(4)(i) + 164.308(b)(1) + 164.308(b)(3) + 164.310(b) + 164.312(e)(1) + 164.312(e)(2)(ii) + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.6.2.1 + A.6.2.2 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.AC-3 + PR.IP-1 + PR.PT-3 + PR.PT-4 + Removing the xinetd package decreases the risk of the +xinetd service's accidental (or intentional) activation. + CCE-84155-1 + +package --remove=xinetd + + include remove_xinetd + +class remove_xinetd { + package { 'xinetd': + ensure => 'purged', + } +} + + - name: Ensure xinetd is removed + package: + name: xinetd + state: absent + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84155-1 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - package_xinetd_removed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# CAUTION: This remediation script will remove xinetd +# from the system, and may remove any packages +# that depend on xinetd. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "xinetd" ; then + + dnf remove -y "xinetd" + +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable xinetd Service + +The xinetd service can be disabled with the following command: +$ sudo systemctl mask --now xinetd.service + 11 + 12 + 14 + 15 + 3 + 8 + 9 + APO13.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.04 + DSS05.02 + DSS05.03 + DSS05.05 + DSS06.06 + 3.4.7 + CCI-000305 + 164.308(a)(4)(i) + 164.308(b)(1) + 164.308(b)(3) + 164.310(b) + 164.312(e)(1) + 164.312(e)(2)(ii) + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.6.2.1 + A.6.2.2 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.AC-3 + PR.IP-1 + PR.PT-3 + PR.PT-4 + The xinetd service provides a dedicated listener service for some programs, +which is no longer necessary for commonly-used network services. Disabling +it ensures that these uncommon services are not running, and also prevents +attacks against xinetd itself. + + CCE-84156-9 + include disable_xinetd + +class disable_xinetd { + service {'xinetd': + enable => false, + ensure => 'stopped', + } +} + + - name: Disable service xinetd + block: + + - name: Disable service xinetd + systemd: + name: xinetd.service + enabled: 'no' + state: stopped + masked: 'yes' + ignore_errors: 'yes' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84156-9 + - NIST-800-171-3.4.7 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_xinetd_disabled + +- name: Unit Socket Exists - xinetd.socket + command: systemctl list-unit-files xinetd.socket + args: + warn: false + register: socket_file_exists + changed_when: false + ignore_errors: true + check_mode: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84156-9 + - NIST-800-171-3.4.7 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_xinetd_disabled + +- name: Disable socket xinetd + systemd: + name: xinetd.socket + enabled: 'no' + state: stopped + masked: 'yes' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"xinetd.socket" in socket_file_exists.stdout_lines[1]' + tags: + - CCE-84156-9 + - NIST-800-171-3.4.7 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_xinetd_disabled + + +[customizations.services] +disabled = ["xinetd"] + + apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + systemd: + units: + - name: xinetd.service + enabled: false + mask: true + - name: xinetd.socket + enabled: false + mask: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" stop 'xinetd.service' +"$SYSTEMCTL_EXEC" disable 'xinetd.service' +"$SYSTEMCTL_EXEC" mask 'xinetd.service' +# Disable socket activation if we have a unit file for it +if "$SYSTEMCTL_EXEC" list-unit-files | grep -q '^xinetd.socket'; then + "$SYSTEMCTL_EXEC" stop 'xinetd.socket' + "$SYSTEMCTL_EXEC" mask 'xinetd.socket' +fi +# The service may not be running because it has been started and failed, +# so let's reset the state so OVAL checks pass. +# Service should be 'inactive', not 'failed' after reboot though. +"$SYSTEMCTL_EXEC" reset-failed 'xinetd.service' || true + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + NIS + The Network Information Service (NIS), also known as 'Yellow +Pages' (YP), and its successor NIS+ have been made obsolete by +Kerberos, LDAP, and other modern centralized authentication +services. NIS should not be used because it suffers from security +problems inherent in its design, such as inadequate protection of +important authentication information. + + Remove NIS Client + The Network Information Service (NIS), formerly known as Yellow Pages, +is a client-server directory service protocol used to distribute system configuration +files. The NIS client (ypbind) was used to bind a system to an NIS server +and receive the distributed configuration files. + BP28(R1) + 164.308(a)(4)(i) + 164.308(b)(1) + 164.308(b)(3) + 164.310(b) + 164.312(e)(1) + 164.312(e)(2)(ii) + The NIS service is inherently an insecure system that has been vulnerable +to DOS attacks, buffer overflows and has poor authentication for querying +NIS maps. NIS generally has been replaced by such protocols as Lightweight +Directory Access Protocol (LDAP). It is recommended that the service be +removed. + CCE-84151-0 + +package --remove=ypbind + + include remove_ypbind + +class remove_ypbind { + package { 'ypbind': + ensure => 'purged', + } +} + + - name: Ensure ypbind is removed + package: + name: ypbind + state: absent + tags: + - CCE-84151-0 + - disable_strategy + - low_complexity + - low_disruption + - no_reboot_needed + - package_ypbind_removed + - unknown_severity + + +# CAUTION: This remediation script will remove ypbind +# from the system, and may remove any packages +# that depend on ypbind. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "ypbind" ; then + + dnf remove -y "ypbind" + +fi + + + + + + + + + + Uninstall ypserv Package + The ypserv package can be removed with the following command: + +$ sudo dnf erase ypserv + BP28(R1) + 11 + 12 + 14 + 15 + 3 + 8 + 9 + APO13.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.04 + DSS05.02 + DSS05.03 + DSS05.05 + DSS06.06 + CCI-000381 + 164.308(a)(4)(i) + 164.308(b)(1) + 164.308(b)(3) + 164.310(b) + 164.312(e)(1) + 164.312(e)(2)(ii) + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.6.2.1 + A.6.2.2 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + IA-5(1)(c) + PR.AC-3 + PR.IP-1 + PR.PT-3 + PR.PT-4 + SRG-OS-000095-GPOS-00049 + The NIS service provides an unencrypted authentication service which does +not provide for the confidentiality and integrity of user passwords or the +remote session. + +Removing the ypserv package decreases the risk of the accidental +(or intentional) activation of NIS or NIS+ services. + CCE-84152-8 + +package --remove=ypserv + + include remove_ypserv + +class remove_ypserv { + package { 'ypserv': + ensure => 'purged', + } +} + + - name: Ensure ypserv is removed + package: + name: ypserv + state: absent + tags: + - CCE-84152-8 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-IA-5(1)(c) + - disable_strategy + - high_severity + - low_complexity + - low_disruption + - no_reboot_needed + - package_ypserv_removed + + +# CAUTION: This remediation script will remove ypserv +# from the system, and may remove any packages +# that depend on ypserv. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "ypserv" ; then + + dnf remove -y "ypserv" + +fi + + + + + + + + + + Disable ypserv Service + The ypserv service, which allows the system to act as a client in +a NIS or NIS+ domain, should be disabled. + +The ypserv service can be disabled with the following command: +$ sudo systemctl mask --now ypserv.service + Disabling the ypserv service ensures the system is not acting +as a client in a NIS or NIS+ domain. This service should be disabled +unless in use. + + CCE-86122-9 + include disable_ypserv + +class disable_ypserv { + service {'ypserv': + enable => false, + ensure => 'stopped', + } +} + + - name: Disable service ypserv + block: + + - name: Disable service ypserv + systemd: + name: ypserv.service + enabled: 'no' + state: stopped + masked: 'yes' + ignore_errors: 'yes' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86122-9 + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_ypserv_disabled + +- name: Unit Socket Exists - ypserv.socket + command: systemctl list-unit-files ypserv.socket + args: + warn: false + register: socket_file_exists + changed_when: false + ignore_errors: true + check_mode: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86122-9 + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_ypserv_disabled + +- name: Disable socket ypserv + systemd: + name: ypserv.socket + enabled: 'no' + state: stopped + masked: 'yes' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"ypserv.socket" in socket_file_exists.stdout_lines[1]' + tags: + - CCE-86122-9 + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_ypserv_disabled + + +[customizations.services] +disabled = ["ypserv"] + + apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + systemd: + units: + - name: ypserv.service + enabled: false + mask: true + - name: ypserv.socket + enabled: false + mask: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" stop 'ypserv.service' +"$SYSTEMCTL_EXEC" disable 'ypserv.service' +"$SYSTEMCTL_EXEC" mask 'ypserv.service' +# Disable socket activation if we have a unit file for it +if "$SYSTEMCTL_EXEC" list-unit-files | grep -q '^ypserv.socket'; then + "$SYSTEMCTL_EXEC" stop 'ypserv.socket' + "$SYSTEMCTL_EXEC" mask 'ypserv.socket' +fi +# The service may not be running because it has been started and failed, +# so let's reset the state so OVAL checks pass. +# Service should be 'inactive', not 'failed' after reboot though. +"$SYSTEMCTL_EXEC" reset-failed 'ypserv.service' || true + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Rlogin, Rsh, and Rexec + The Berkeley r-commands are legacy services which +allow cleartext remote access and have an insecure trust +model. + + Uninstall rsh-server Package + The rsh-server package can be removed with the following command: + +$ sudo dnf erase rsh-server + BP28(R1) + 11 + 12 + 14 + 15 + 3 + 8 + 9 + APO13.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.04 + DSS05.02 + DSS05.03 + DSS05.05 + DSS06.06 + CCI-000381 + 164.308(a)(4)(i) + 164.308(b)(1) + 164.308(b)(3) + 164.310(b) + 164.312(e)(1) + 164.312(e)(2)(ii) + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.6.2.1 + A.6.2.2 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + IA-5(1)(c) + PR.AC-3 + PR.IP-1 + PR.PT-3 + PR.PT-4 + SRG-OS-000095-GPOS-00049 + The rsh-server service provides unencrypted remote access service which does not +provide for the confidentiality and integrity of user passwords or the remote session and has very weak +authentication. If a privileged user were to login using this service, the privileged user password +could be compromised. The rsh-server package provides several obsolete and insecure +network services. Removing it decreases the risk of those services' accidental (or intentional) +activation. + CCE-84143-7 + +package --remove=rsh-server + + include remove_rsh-server + +class remove_rsh-server { + package { 'rsh-server': + ensure => 'purged', + } +} + + - name: Ensure rsh-server is removed + package: + name: rsh-server + state: absent + tags: + - CCE-84143-7 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-IA-5(1)(c) + - disable_strategy + - high_severity + - low_complexity + - low_disruption + - no_reboot_needed + - package_rsh-server_removed + + +# CAUTION: This remediation script will remove rsh-server +# from the system, and may remove any packages +# that depend on rsh-server. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "rsh-server" ; then + + dnf remove -y "rsh-server" + +fi + + + + + + + + + + Uninstall rsh Package + +The rsh package contains the client commands + +for the rsh services + BP28(R1) + 3.1.13 + 164.308(a)(4)(i) + 164.308(b)(1) + 164.308(b)(3) + 164.310(b) + 164.312(e)(1) + 164.312(e)(2)(ii) + A.8.2.3 + A.13.1.1 + A.13.2.1 + A.13.2.3 + A.14.1.2 + A.14.1.3 + These legacy clients contain numerous security exposures and have +been replaced with the more secure SSH package. Even if the server is removed, +it is best to ensure the clients are also removed to prevent users from +inadvertently attempting to use these commands and therefore exposing + +their credentials. Note that removing the rsh package removes + +the clients for rsh,rcp, and rlogin. + CCE-84142-9 + +package --remove=rsh + + include remove_rsh + +class remove_rsh { + package { 'rsh': + ensure => 'purged', + } +} + + - name: Ensure rsh is removed + package: + name: rsh + state: absent + tags: + - CCE-84142-9 + - NIST-800-171-3.1.13 + - disable_strategy + - low_complexity + - low_disruption + - no_reboot_needed + - package_rsh_removed + - unknown_severity + + +# CAUTION: This remediation script will remove rsh +# from the system, and may remove any packages +# that depend on rsh. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "rsh" ; then + + dnf remove -y "rsh" + +fi + + + + + + + + + + Disable rlogin Service + The rlogin service, which is available with +the rsh-server package and runs as a service through xinetd or separately +as a systemd socket, should be disabled. +If using xinetd, set disable to yes in /etc/xinetd.d/rlogin. + +The rlogin socket can be disabled with the following command: +$ sudo systemctl mask --now rlogin.socket + 1 + 11 + 12 + 14 + 15 + 16 + 3 + 5 + 8 + 9 + APO13.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.04 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.03 + DSS06.06 + DSS06.10 + 3.1.13 + 3.4.7 + CCI-001436 + 164.308(a)(4)(i) + 164.308(b)(1) + 164.308(b)(3) + 164.310(b) + 164.312(e)(1) + 164.312(e)(2)(ii) + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.18.1.4 + A.6.2.1 + A.6.2.2 + A.7.1.1 + A.9.1.2 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.2 + A.9.4.3 + CM-7(a) + CM-7(b) + CM-6(a) + IA-5(1)(c) + PR.AC-1 + PR.AC-3 + PR.AC-6 + PR.AC-7 + PR.IP-1 + PR.PT-3 + PR.PT-4 + The rlogin service uses unencrypted network communications, which +means that data from the login session, including passwords and +all other information transmitted during the session, can be +stolen by eavesdroppers on the network. + + CCE-88395-9 + include disable_rlogin + +class disable_rlogin { + service {'rlogin': + enable => false, + ensure => 'stopped', + } +} + + - name: Disable service rlogin + block: + + - name: Disable service rlogin + systemd: + name: rlogin.service + enabled: 'no' + state: stopped + masked: 'yes' + ignore_errors: 'yes' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-88395-9 + - NIST-800-171-3.1.13 + - NIST-800-171-3.4.7 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-IA-5(1)(c) + - disable_strategy + - high_severity + - low_complexity + - low_disruption + - no_reboot_needed + - service_rlogin_disabled + +- name: Unit Socket Exists - rlogin.socket + command: systemctl list-unit-files rlogin.socket + args: + warn: false + register: socket_file_exists + changed_when: false + ignore_errors: true + check_mode: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-88395-9 + - NIST-800-171-3.1.13 + - NIST-800-171-3.4.7 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-IA-5(1)(c) + - disable_strategy + - high_severity + - low_complexity + - low_disruption + - no_reboot_needed + - service_rlogin_disabled + +- name: Disable socket rlogin + systemd: + name: rlogin.socket + enabled: 'no' + state: stopped + masked: 'yes' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"rlogin.socket" in socket_file_exists.stdout_lines[1]' + tags: + - CCE-88395-9 + - NIST-800-171-3.1.13 + - NIST-800-171-3.4.7 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-IA-5(1)(c) + - disable_strategy + - high_severity + - low_complexity + - low_disruption + - no_reboot_needed + - service_rlogin_disabled + + +[customizations.services] +disabled = ["rlogin"] + + apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + systemd: + units: + - name: rlogin.service + enabled: false + mask: true + - name: rlogin.socket + enabled: false + mask: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" stop 'rlogin.service' +"$SYSTEMCTL_EXEC" disable 'rlogin.service' +"$SYSTEMCTL_EXEC" mask 'rlogin.service' +# Disable socket activation if we have a unit file for it +if "$SYSTEMCTL_EXEC" list-unit-files | grep -q '^rlogin.socket'; then + "$SYSTEMCTL_EXEC" stop 'rlogin.socket' + "$SYSTEMCTL_EXEC" mask 'rlogin.socket' +fi +# The service may not be running because it has been started and failed, +# so let's reset the state so OVAL checks pass. +# Service should be 'inactive', not 'failed' after reboot though. +"$SYSTEMCTL_EXEC" reset-failed 'rlogin.service' || true + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Remove Host-Based Authentication Files + The shosts.equiv file list remote hosts +and users that are trusted by the local system. +To remove these files, run the following command to delete them from any +location: +$ sudo rm /[path]/[to]/[file]/shosts.equiv + CCI-000366 + SRG-OS-000480-GPOS-00227 + The shosts.equiv files are used to configure host-based authentication for the +system via SSH. Host-based authentication is not sufficient for preventing +unauthorized access to the system, as it does not require interactive +identification and authentication of a connection request, or for the use of +two-factor authentication. + CCE-90208-0 + +# Identify local mounts +MOUNT_LIST=$(df --local | awk '{ print $6 }') + +# Find file on each listed mount point +for cur_mount in ${MOUNT_LIST} +do + find ${cur_mount} -xdev -type f -name "shosts.equiv" -exec rm -f {} \; +done + + + + + + + + + + Remove Rsh Trust Files + The files /etc/hosts.equiv and ~/.rhosts (in +each user's home directory) list remote hosts and users that are trusted by the +local system when using the rshd daemon. +To remove these files, run the following command to delete them from any +location: +$ sudo rm /etc/hosts.equiv +$ rm ~/.rhosts + 11 + 12 + 14 + 15 + 3 + 8 + 9 + APO13.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.04 + DSS05.02 + DSS05.03 + DSS05.05 + DSS06.06 + CCI-001436 + 164.308(a)(4)(i) + 164.308(b)(1) + 164.308(b)(3) + 164.310(b) + 164.312(e)(1) + 164.312(e)(2)(ii) + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.6.2.1 + A.6.2.2 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.AC-3 + PR.IP-1 + PR.PT-3 + PR.PT-4 + This action is only meaningful if .rhosts support is permitted +through PAM. Trust files are convenient, but when used in conjunction with +the R-services, they can allow unauthenticated access to a system. + CCE-84145-2 + - block: + + - name: Detect .rhosts files in users home directories + find: + paths: + - /root + - /home + recurse: true + patterns: .rhosts + hidden: true + file_type: file + check_mode: false + register: rhosts_locations + + - name: Remove .rhosts files + file: + path: '{{ item }}' + state: absent + with_items: '{{ rhosts_locations.files | map(attribute=''path'') | list }}' + when: rhosts_locations is success + + - name: Remove /etc/hosts.equiv file + file: + path: /etc/hosts.equiv + state: absent + tags: + - CCE-84145-2 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - high_severity + - low_complexity + - low_disruption + - no_reboot_needed + - no_rsh_trust_files + - restrict_strategy + + +find /root -xdev -type f -name ".rhosts" -exec rm -f {} \; +find /home -maxdepth 2 -xdev -type f -name ".rhosts" -exec rm -f {} \; +rm -f /etc/hosts.equiv + + + + + + + + + + Remove User Host-Based Authentication Files + The ~/.shosts (in each user's home directory) files +list remote hosts and users that are trusted by the +local system. To remove these files, run the following command +to delete them from any location: +$ sudo find / -name '.shosts' -type f -delete + CCI-000366 + SRG-OS-000480-GPOS-00227 + The .shosts files are used to configure host-based authentication for +individual users or the system via SSH. Host-based authentication is not +sufficient for preventing unauthorized access to the system, as it does not +require interactive identification and authentication of a connection request, +or for the use of two-factor authentication. + CCE-86532-9 + +# Identify local mounts +MOUNT_LIST=$(df --local | awk '{ print $6 }') + +# Find file on each listed mount point +for cur_mount in ${MOUNT_LIST} +do + find ${cur_mount} -xdev -type f -name ".shosts" -exec rm -f {} \; +done + + + + + + + + + + + Chat/Messaging Services + The talk software makes it possible for users to send and receive messages +across systems through a terminal session. + + Uninstall talk-server Package + The talk-server package can be removed with the following command: $ sudo dnf erase talk-server + BP28(R1) + 164.308(a)(4)(i) + 164.308(b)(1) + 164.308(b)(3) + 164.310(b) + 164.312(e)(1) + 164.312(e)(2)(ii) + The talk software presents a security risk as it uses unencrypted protocols +for communications. Removing the talk-server package decreases the +risk of the accidental (or intentional) activation of talk services. + CCE-84158-5 + +package --remove=talk-server + + include remove_talk-server + +class remove_talk-server { + package { 'talk-server': + ensure => 'purged', + } +} + + - name: Ensure talk-server is removed + package: + name: talk-server + state: absent + tags: + - CCE-84158-5 + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_talk-server_removed + + +# CAUTION: This remediation script will remove talk-server +# from the system, and may remove any packages +# that depend on talk-server. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "talk-server" ; then + + dnf remove -y "talk-server" + +fi + + + + + + + + + + Uninstall talk Package + The talk package contains the client program for the +Internet talk protocol, which allows the user to chat with other users on +different systems. Talk is a communication program which copies lines from one +terminal to the terminal of another user. +The talk package can be removed with the following command: + +$ sudo dnf erase talk + BP28(R1) + 164.308(a)(4)(i) + 164.308(b)(1) + 164.308(b)(3) + 164.310(b) + 164.312(e)(1) + 164.312(e)(2)(ii) + The talk software presents a security risk as it uses unencrypted protocols +for communications. Removing the talk package decreases the +risk of the accidental (or intentional) activation of talk client program. + CCE-84157-7 + +package --remove=talk + + include remove_talk + +class remove_talk { + package { 'talk': + ensure => 'purged', + } +} + + - name: Ensure talk is removed + package: + name: talk + state: absent + tags: + - CCE-84157-7 + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_talk_removed + + +# CAUTION: This remediation script will remove talk +# from the system, and may remove any packages +# that depend on talk. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "talk" ; then + + dnf remove -y "talk" + +fi + + + + + + + + + + + Telnet + The telnet protocol does not provide confidentiality or integrity +for information transmitted on the network. This includes authentication +information such as passwords. Organizations which use telnet should be +actively working to migrate to a more secure protocol. + + Uninstall telnet-server Package + The telnet-server package can be removed with the following command: + +$ sudo dnf erase telnet-server + BP28(R1) + 11 + 12 + 14 + 15 + 3 + 8 + 9 + APO13.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.04 + DSS05.02 + DSS05.03 + DSS05.05 + DSS06.06 + CCI-000381 + 164.308(a)(4)(i) + 164.308(b)(1) + 164.308(b)(3) + 164.310(b) + 164.312(e)(1) + 164.312(e)(2)(ii) + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.6.2.1 + A.6.2.2 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.AC-3 + PR.IP-1 + PR.PT-3 + PR.PT-4 + SRG-OS-000095-GPOS-00049 + It is detrimental for operating systems to provide, or install by default, +functionality exceeding requirements or mission objectives. These +unnecessary capabilities are often overlooked and therefore may remain +unsecure. They increase the risk to the platform by providing additional +attack vectors. + +The telnet service provides an unencrypted remote access service which does +not provide for the confidentiality and integrity of user passwords or the +remote session. If a privileged user were to login using this service, the +privileged user password could be compromised. + +Removing the telnet-server package decreases the risk of the +telnet service's accidental (or intentional) activation. + CCE-84149-4 + +package --remove=telnet-server + + include remove_telnet-server + +class remove_telnet-server { + package { 'telnet-server': + ensure => 'purged', + } +} + + - name: Ensure telnet-server is removed + package: + name: telnet-server + state: absent + tags: + - CCE-84149-4 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - high_severity + - low_complexity + - low_disruption + - no_reboot_needed + - package_telnet-server_removed + + +# CAUTION: This remediation script will remove telnet-server +# from the system, and may remove any packages +# that depend on telnet-server. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "telnet-server" ; then + + dnf remove -y "telnet-server" + +fi + + + + + + + + + + Remove telnet Clients + The telnet client allows users to start connections to other systems via +the telnet protocol. + BP28(R1) + 3.1.13 + 164.308(a)(4)(i) + 164.308(b)(1) + 164.308(b)(3) + 164.310(b) + 164.312(e)(1) + 164.312(e)(2)(ii) + A.8.2.3 + A.13.1.1 + A.13.2.1 + A.13.2.3 + A.14.1.2 + A.14.1.3 + The telnet protocol is insecure and unencrypted. The use +of an unencrypted transmission medium could allow an unauthorized user +to steal credentials. The ssh package provides an +encrypted session and stronger security and is included in Red Hat Enterprise Linux 9. + CCE-84146-0 + +package --remove=telnet + + include remove_telnet + +class remove_telnet { + package { 'telnet': + ensure => 'purged', + } +} + + - name: Ensure telnet is removed + package: + name: telnet + state: absent + tags: + - CCE-84146-0 + - NIST-800-171-3.1.13 + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - package_telnet_removed + + +# CAUTION: This remediation script will remove telnet +# from the system, and may remove any packages +# that depend on telnet. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "telnet" ; then + + dnf remove -y "telnet" + +fi + + + + + + + + + + Disable telnet Service + The telnet service configuration file /etc/xinetd.d/telnet +is not created automatically. If it was created manually, check the +/etc/xinetd.d/telnet file and ensure that disable = no +is changed to read disable = yes as follows below: + +# description: The telnet server serves telnet sessions; it uses \\ +# unencrypted username/password pairs for authentication. +service telnet +{ + flags = REUSE + socket_type = stream + + wait = no + user = root + server = /usr/sbin/in.telnetd + log_on_failure += USERID + disable = yes +} + +If the /etc/xinetd.d/telnet file does not exist, make sure that +the activation of the telnet service on system boot is disabled +via the following command: + +The rexec socket can be disabled with the following command: +$ sudo systemctl mask --now rexec.socket + 1 + 11 + 12 + 14 + 15 + 16 + 3 + 5 + 8 + 9 + APO13.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.04 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.03 + DSS06.06 + DSS06.10 + 3.1.13 + 3.4.7 + 164.308(a)(4)(i) + 164.308(b)(1) + 164.308(b)(3) + 164.310(b) + 164.312(e)(1) + 164.312(e)(2)(ii) + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.18.1.4 + A.6.2.1 + A.6.2.2 + A.7.1.1 + A.9.1.2 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.2 + A.9.4.3 + CM-7(a) + CM-7(b) + CM-6(a) + IA-5(1)(c) + PR.AC-1 + PR.AC-3 + PR.AC-6 + PR.AC-7 + PR.IP-1 + PR.PT-3 + PR.PT-4 + The telnet protocol uses unencrypted network communication, which +means that data from the login session, including passwords and +all other information transmitted during the session, can be +stolen by eavesdroppers on the network. The telnet protocol is also +subject to man-in-the-middle attacks. + + CCE-84150-2 + include disable_telnet + +class disable_telnet { + service {'telnet': + enable => false, + ensure => 'stopped', + } +} + + - name: Disable service telnet + block: + + - name: Disable service telnet + systemd: + name: telnet.service + enabled: 'no' + state: stopped + masked: 'yes' + ignore_errors: 'yes' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84150-2 + - NIST-800-171-3.1.13 + - NIST-800-171-3.4.7 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-IA-5(1)(c) + - disable_strategy + - high_severity + - low_complexity + - low_disruption + - no_reboot_needed + - service_telnet_disabled + +- name: Unit Socket Exists - telnet.socket + command: systemctl list-unit-files telnet.socket + args: + warn: false + register: socket_file_exists + changed_when: false + ignore_errors: true + check_mode: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84150-2 + - NIST-800-171-3.1.13 + - NIST-800-171-3.4.7 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-IA-5(1)(c) + - disable_strategy + - high_severity + - low_complexity + - low_disruption + - no_reboot_needed + - service_telnet_disabled + +- name: Disable socket telnet + systemd: + name: telnet.socket + enabled: 'no' + state: stopped + masked: 'yes' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"telnet.socket" in socket_file_exists.stdout_lines[1]' + tags: + - CCE-84150-2 + - NIST-800-171-3.1.13 + - NIST-800-171-3.4.7 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-IA-5(1)(c) + - disable_strategy + - high_severity + - low_complexity + - low_disruption + - no_reboot_needed + - service_telnet_disabled + + +[customizations.services] +disabled = ["telnet"] + + apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + systemd: + units: + - name: telnet.service + enabled: false + mask: true + - name: telnet.socket + enabled: false + mask: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" stop 'telnet.service' +"$SYSTEMCTL_EXEC" disable 'telnet.service' +"$SYSTEMCTL_EXEC" mask 'telnet.service' +# Disable socket activation if we have a unit file for it +if "$SYSTEMCTL_EXEC" list-unit-files | grep -q '^telnet.socket'; then + "$SYSTEMCTL_EXEC" stop 'telnet.socket' + "$SYSTEMCTL_EXEC" mask 'telnet.socket' +fi +# The service may not be running because it has been started and failed, +# so let's reset the state so OVAL checks pass. +# Service should be 'inactive', not 'failed' after reboot though. +"$SYSTEMCTL_EXEC" reset-failed 'telnet.service' || true + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + TFTP Server + TFTP is a lightweight version of the FTP protocol which has +traditionally been used to configure networking equipment. However, +TFTP provides little security, and modern versions of networking +operating systems frequently support configuration via SSH or other +more secure protocols. A TFTP server should be run only if no more +secure method of supporting existing equipment can be +found. + + TFTP server secure directory + Specify the directory which is used by TFTP server as a root directory when running in secure mode. + /var/lib/tftpboot + + + Uninstall tftp-server Package + The tftp-server package can be removed with the following command: $ sudo dnf erase tftp-server + BP28(R1) + 11 + 12 + 14 + 15 + 3 + 8 + 9 + APO13.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.04 + DSS05.02 + DSS05.03 + DSS05.05 + DSS06.06 + CCI-000318 + CCI-000366 + CCI-000368 + CCI-001812 + CCI-001813 + CCI-001814 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + A.11.2.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.6.2.1 + A.6.2.2 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.AC-3 + PR.IP-1 + PR.PT-3 + PR.PT-4 + SRG-OS-000480-GPOS-00227 + Removing the tftp-server package decreases the risk of the accidental +(or intentional) activation of tftp services. + +If TFTP is required for operational support (such as transmission of router +configurations), its use must be documented with the Information Systems +Securty Manager (ISSM), restricted to only authorized personnel, and have +access control rules established. + CCE-84154-4 + +package --remove=tftp-server + + include remove_tftp-server + +class remove_tftp-server { + package { 'tftp-server': + ensure => 'purged', + } +} + + - name: Ensure tftp-server is removed + package: + name: tftp-server + state: absent + tags: + - CCE-84154-4 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - high_severity + - low_complexity + - low_disruption + - no_reboot_needed + - package_tftp-server_removed + + +# CAUTION: This remediation script will remove tftp-server +# from the system, and may remove any packages +# that depend on tftp-server. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "tftp-server" ; then + + dnf remove -y "tftp-server" + +fi + + + + + + + + + + Remove tftp Daemon + Trivial File Transfer Protocol (TFTP) is a simple file transfer protocol, +typically used to automatically transfer configuration or boot files between systems. +TFTP does not support authentication and can be easily hacked. The package +tftp is a client program that allows for connections to a tftp server. + BP28(R1) + It is recommended that TFTP be removed, unless there is a specific need +for TFTP (such as a boot server). In that case, use extreme caution when configuring +the services. + CCE-84153-6 + +package --remove=tftp + + include remove_tftp + +class remove_tftp { + package { 'tftp': + ensure => 'purged', + } +} + + - name: Ensure tftp is removed + package: + name: tftp + state: absent + tags: + - CCE-84153-6 + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - package_tftp_removed + + +# CAUTION: This remediation script will remove tftp +# from the system, and may remove any packages +# that depend on tftp. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "tftp" ; then + + dnf remove -y "tftp" + +fi + + + + + + + + + + Ensure tftp Daemon Uses Secure Mode + If running the tftp service is necessary, it should be configured +to change its root directory at startup. To do so, ensure +/etc/xinetd.d/tftp includes -s as a command line argument, as shown in +the following example: +server_args = -s + 11 + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + 8 + 9 + APO01.06 + APO13.01 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS01.04 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + DSS06.02 + DSS06.06 + CCI-000366 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.11.2.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.6.1.2 + A.6.2.1 + A.6.2.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-6(b) + AC-6 + CM-7(a) + PR.AC-3 + PR.AC-4 + PR.DS-5 + PR.IP-1 + PR.PT-3 + PR.PT-4 + SRG-OS-000480-GPOS-00227 + Using the -s option causes the TFTP service to only serve files from the +given directory. Serving files from an intentionally-specified directory +reduces the risk of sharing files which should remain private. + + CCE-90736-0 + + + + + + + + + + + + Print Support + The Common Unix Printing System (CUPS) service provides both local +and network printing support. A system running the CUPS service can accept +print jobs from other systems, process them, and send them to the appropriate +printer. It also provides an interface for remote administration through a web +browser. The CUPS service is installed and activated by default. The project +homepage and more detailed documentation are available at + + http://www.cups.org. + + + Disable the CUPS Service + +The cups service can be disabled with the following command: +$ sudo systemctl mask --now cups.service + 11 + 14 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.05 + DSS06.06 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.9.1.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + PR.PT-3 + Turn off unneeded services to reduce attack surface. + + CCE-90795-6 + include disable_cups + +class disable_cups { + service {'cups': + enable => false, + ensure => 'stopped', + } +} + + - name: Disable service cups + block: + + - name: Disable service cups + systemd: + name: cups.service + enabled: 'no' + state: stopped + masked: 'yes' + ignore_errors: 'yes' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-90795-6 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - no_reboot_needed + - service_cups_disabled + - unknown_severity + +- name: Unit Socket Exists - cups.socket + command: systemctl list-unit-files cups.socket + args: + warn: false + register: socket_file_exists + changed_when: false + ignore_errors: true + check_mode: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-90795-6 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - no_reboot_needed + - service_cups_disabled + - unknown_severity + +- name: Disable socket cups + systemd: + name: cups.socket + enabled: 'no' + state: stopped + masked: 'yes' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"cups.socket" in socket_file_exists.stdout_lines[1]' + tags: + - CCE-90795-6 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - no_reboot_needed + - service_cups_disabled + - unknown_severity + + +[customizations.services] +disabled = ["cups"] + + apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + systemd: + units: + - name: cups.service + enabled: false + mask: true + - name: cups.socket + enabled: false + mask: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" stop 'cups.service' +"$SYSTEMCTL_EXEC" disable 'cups.service' +"$SYSTEMCTL_EXEC" mask 'cups.service' +# Disable socket activation if we have a unit file for it +if "$SYSTEMCTL_EXEC" list-unit-files | grep -q '^cups.socket'; then + "$SYSTEMCTL_EXEC" stop 'cups.socket' + "$SYSTEMCTL_EXEC" mask 'cups.socket' +fi +# The service may not be running because it has been started and failed, +# so let's reset the state so OVAL checks pass. +# Service should be 'inactive', not 'failed' after reboot though. +"$SYSTEMCTL_EXEC" reset-failed 'cups.service' || true + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Configure the CUPS Service if Necessary + CUPS provides the ability to easily share local printers with +other systems over the network. It does this by allowing systems to share +lists of available printers. Additionally, each system that runs the CUPS +service can potentially act as a print server. Whenever possible, the printer +sharing and print server capabilities of CUPS should be limited or disabled. +The following recommendations should demonstrate how to do just that. + + + + Proxy Server + A proxy server is a very desirable target for a +potential adversary because much (or all) sensitive data for a +given infrastructure may flow through it. Therefore, if one is +required, the system acting as a proxy server should be dedicated +to that purpose alone and be stored in a physically secure +location. The system's default proxy server software is Squid, and +provided in an RPM package of the same name. + + Disable Squid if Possible + If Squid was installed and activated, but the system +does not need to act as a proxy server, then it should be disabled +and removed. + + Uninstall squid Package + The squid package can be removed with the following command: $ sudo dnf erase squid + If there is no need to make the proxy server software available, +removing it provides a safeguard against its activation. + CCE-84238-5 + +package --remove=squid + + include remove_squid + +class remove_squid { + package { 'squid': + ensure => 'purged', + } +} + + - name: Ensure squid is removed + package: + name: squid + state: absent + tags: + - CCE-84238-5 + - disable_strategy + - low_complexity + - low_disruption + - no_reboot_needed + - package_squid_removed + - unknown_severity + + +# CAUTION: This remediation script will remove squid +# from the system, and may remove any packages +# that depend on squid. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "squid" ; then + + dnf remove -y "squid" + +fi + + + + + + + + + + Disable Squid + +The squid service can be disabled with the following command: +$ sudo systemctl mask --now squid.service + Running proxy server software provides a network-based avenue +of attack, and should be removed if not needed. + + CCE-84239-3 + include disable_squid + +class disable_squid { + service {'squid': + enable => false, + ensure => 'stopped', + } +} + + - name: Disable service squid + block: + + - name: Disable service squid + systemd: + name: squid.service + enabled: 'no' + state: stopped + masked: 'yes' + ignore_errors: 'yes' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84239-3 + - disable_strategy + - low_complexity + - low_disruption + - no_reboot_needed + - service_squid_disabled + - unknown_severity + +- name: Unit Socket Exists - squid.socket + command: systemctl list-unit-files squid.socket + args: + warn: false + register: socket_file_exists + changed_when: false + ignore_errors: true + check_mode: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84239-3 + - disable_strategy + - low_complexity + - low_disruption + - no_reboot_needed + - service_squid_disabled + - unknown_severity + +- name: Disable socket squid + systemd: + name: squid.socket + enabled: 'no' + state: stopped + masked: 'yes' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"squid.socket" in socket_file_exists.stdout_lines[1]' + tags: + - CCE-84239-3 + - disable_strategy + - low_complexity + - low_disruption + - no_reboot_needed + - service_squid_disabled + - unknown_severity + + +[customizations.services] +disabled = ["squid"] + + apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + systemd: + units: + - name: squid.service + enabled: false + mask: true + - name: squid.socket + enabled: false + mask: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" stop 'squid.service' +"$SYSTEMCTL_EXEC" disable 'squid.service' +"$SYSTEMCTL_EXEC" mask 'squid.service' +# Disable socket activation if we have a unit file for it +if "$SYSTEMCTL_EXEC" list-unit-files | grep -q '^squid.socket'; then + "$SYSTEMCTL_EXEC" stop 'squid.socket' + "$SYSTEMCTL_EXEC" mask 'squid.socket' +fi +# The service may not be running because it has been started and failed, +# so let's reset the state so OVAL checks pass. +# Service should be 'inactive', not 'failed' after reboot though. +"$SYSTEMCTL_EXEC" reset-failed 'squid.service' || true + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + + Remote Authentication Dial-In User Service (RADIUS) + Remote Authentication Dial-In User Service (RADIUS) is a networking +protocol, operating on port 1812 that provides centralized +Authentication, Authorization, and Accounting (AAA or Triple A) +management for users who connect and use a network service. + + Remove the FreeRadius Server Package + The freeradius package should be removed if not in use. +Is this system a RADIUS server? If not, remove the package. +The freeradius package can be removed with the following command: + +$ sudo dnf erase freeradius +The freeradius RPM is not installed by default on a Red Hat Enterprise Linux 9 +system. It is needed only by the RADIUS servers, not by the +clients which use RADIUS for authentication. If the system is not +intended for use as a RADIUS Server it should be removed. + Unnecessary packages should not be installed to decrease the attack +surface of the system. While this software is clearly essential on a +RADIUS server, it is not necessary on typical desktop or workstation systems. + +package --remove=freeradius + + include remove_freeradius + +class remove_freeradius { + package { 'freeradius': + ensure => 'purged', + } +} + + - name: Ensure freeradius is removed + package: + name: freeradius + state: absent + tags: + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - package_freeradius_removed + + +# CAUTION: This remediation script will remove freeradius +# from the system, and may remove any packages +# that depend on freeradius. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "freeradius" ; then + + dnf remove -y "freeradius" + +fi + + + + + + + + + + + Hardware RNG Entropy Gatherer Daemon + The rngd feeds random data from hardware device to kernel random device. + + + Enable the Hardware RNG Entropy Gatherer Service + The Hardware RNG Entropy Gatherer service should be enabled. + +The rngd service can be enabled with the following command: +$ sudo systemctl enable rngd.service + CCI-000366 + FCS_RBG_EXT.1 + SRG-OS-000480-GPOS-00227 + The rngd service +feeds random data from hardware device to kernel random device. + CCE-84223-7 + include enable_rngd + +class enable_rngd { + service {'rngd': + enable => true, + ensure => 'running', + } +} + + - name: Enable service rngd + block: + + - name: Gather the package facts + package_facts: + manager: auto + + - name: Enable service rngd + service: + name: rngd + enabled: 'yes' + state: started + masked: 'no' + when: + - '"rng-tools" in ansible_facts.packages' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84223-7 + - enable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - service_rngd_enabled + + +[customizations.services] +enabled = ["rngd"] + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" unmask 'rngd.service' +"$SYSTEMCTL_EXEC" start 'rngd.service' +"$SYSTEMCTL_EXEC" enable 'rngd.service' + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Network Routing + A router is a very desirable target for a +potential adversary because they fulfill a variety of +infrastructure networking roles such as access to network segments, +gateways to other networks, filtering, etc. Therefore, if one is +required, the system acting as a router should be dedicated +to that purpose alone and be stored in a physically secure +location. The system's default routing software is Quagga, and +provided in an RPM package of the same name. + + Disable Quagga if Possible + If Quagga was installed and activated, but the system +does not need to act as a router, then it should be disabled +and removed. + + Uninstall quagga Package + The quagga package can be removed with the following command: $ sudo dnf erase quagga + 12 + 15 + 8 + APO13.01 + DSS05.02 + CCI-000366 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + A.13.1.1 + A.13.2.1 + A.14.1.3 + CM-7(a) + CM-7(b) + CM-6(a) + PR.PT-4 + SRG-OS-000480-GPOS-00227 + Routing software is typically used on routers to exchange network topology information +with other routers. If routing software is used when not required, system network +information may be unnecessarily transmitted across the network. + +If there is no need to make the router software available, +removing it provides a safeguard against its activation. + CCE-84191-6 + +package --remove=quagga + + include remove_quagga + +class remove_quagga { + package { 'quagga': + ensure => 'purged', + } +} + + - name: Ensure quagga is removed + package: + name: quagga + state: absent + tags: + - CCE-84191-6 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - package_quagga_removed + + +# CAUTION: This remediation script will remove quagga +# from the system, and may remove any packages +# that depend on quagga. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "quagga" ; then + + dnf remove -y "quagga" + +fi + + + + + + + + + + + + Samba(SMB) Microsoft Windows File Sharing Server + When properly configured, the Samba service allows +Linux systems to provide file and print sharing to Microsoft +Windows systems. There are two software packages that provide +Samba support. The first, samba-client, provides a series of +command line tools that enable a client system to access Samba +shares. The second, simply labeled samba, provides the Samba +service. It is this second package that allows a Linux system to +act as an Active Directory server, a domain controller, or as a +domain member. Only the samba-client package is installed by +default. + + Configure Samba if Necessary + All settings for the Samba daemon can be found in +/etc/samba/smb.conf. Settings are divided between a +[global] configuration section and a series of user +created share definition sections meant to describe file or print +shares on the system. By default, Samba will operate in user mode +and allow client systems to access local home directories and +printers. It is recommended that these settings be changed or that +additional limitations be set in place. + + Install the Samba Common Package + The samba-common package should be installed. +The samba-common package can be installed with the following command: + +$ sudo dnf install samba-common + If the samba-common package is not installed, samba cannot be configured. + +package --add=samba-common + + include install_samba-common + +class install_samba-common { + package { 'samba-common': + ensure => 'installed', + } +} + + - name: Ensure samba-common is installed + package: + name: samba-common + state: present + tags: + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_samba-common_installed + + +[[packages]] +name = "samba-common" +version = "*" + + +if ! rpm -q --quiet "samba-common" ; then + dnf install -y "samba-common" +fi + + + + + + + + + + Require Client SMB Packet Signing, if using mount.cifs + Require packet signing of clients who mount Samba +shares using the mount.cifs program (e.g., those who specify shares +in /etc/fstab). To do so, ensure signing options (either +sec=krb5i or sec=ntlmv2i) are used. + +See the mount.cifs(8) man page for more information. A Samba +client should only communicate with servers who can support SMB +packet signing. + Packet signing can prevent man-in-the-middle +attacks which modify SMB packets in transit. + + + + + + + + + + Restrict Printer Sharing + By default, Samba utilizes the CUPS printing service to enable +printer sharing with Microsoft Windows workstations. If there are no printers +on the local system, or if printer sharing with Microsoft Windows is not +required, disable the printer sharing capability by commenting out the +following lines, found in /etc/samba/smb.conf: +[global] + load printers = yes + cups options = raw +[printers] + comment = All Printers + path = /usr/spool/samba + browseable = no + guest ok = no + writable = no + printable = yes +There may be other options present, but these are the only options enabled and +uncommented by default. Removing the [printers] share should be enough +for most users. If the Samba printer sharing capability is needed, consider +disabling the Samba network browsing capability or restricting access to a +particular set of users or network addresses. Set the valid users +parameter to a small subset of users or restrict it to a particular group of +users with the shorthand @. Separate each user or group of users with +a space. For example, under the [printers] share: +[printers] + valid users = user @printerusers + + + Restrict SMB File Sharing to Configured Networks + Only users with local user accounts will be able to log in to +Samba shares by default. Shares can be limited to particular users or network +addresses. Use the hosts allow and hosts deny directives +accordingly, and consider setting the valid users directive to a limited subset +of users or to a group of users. Separate each address, user, or user group +with a space as follows for a particular share or global: +[share] + hosts allow = 192.168.1. 127.0.0.1 + valid users = userone usertwo @usergroup +It is also possible to limit read and write access to particular users with the +read list and write list options, though the permissions set by the system +itself will override these settings. Set the read only attribute for each share +to ensure that global settings will not accidentally override the individual +share settings. Then, as with the valid users directive, separate each user or +group of users with a space: +[share] + read only = yes + write list = userone usertwo @usergroup + + + + Disable Samba if Possible + Even after the Samba server package has been installed, it +will remain disabled. Do not enable this service unless it is +absolutely necessary to provide Microsoft Windows file and print +sharing functionality. + + Uninstall Samba Package + The samba package can be removed with the following command: $ sudo dnf erase samba + If there is no need to make the Samba software available, +removing it provides a safeguard against its activation. + CCE-85979-3 + +package --remove=samba + + include remove_samba + +class remove_samba { + package { 'samba': + ensure => 'purged', + } +} + + - name: Ensure samba is removed + package: + name: samba + state: absent + tags: + - CCE-85979-3 + - disable_strategy + - low_complexity + - low_disruption + - no_reboot_needed + - package_samba_removed + - unknown_severity + + +# CAUTION: This remediation script will remove samba +# from the system, and may remove any packages +# that depend on samba. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "samba" ; then + + dnf remove -y "samba" + +fi + + + + + + + + + + Disable Samba + +The smb service can be disabled with the following command: +$ sudo systemctl mask --now smb.service + CCI-001436 + Running a Samba server provides a network-based avenue of attack, and +should be disabled if not needed. + + CCE-84201-3 + include disable_smb + +class disable_smb { + service {'smb': + enable => false, + ensure => 'stopped', + } +} + + - name: Disable service smb + block: + + - name: Disable service smb + systemd: + name: smb.service + enabled: 'no' + state: stopped + masked: 'yes' + ignore_errors: 'yes' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84201-3 + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - service_smb_disabled + +- name: Unit Socket Exists - smb.socket + command: systemctl list-unit-files smb.socket + args: + warn: false + register: socket_file_exists + changed_when: false + ignore_errors: true + check_mode: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84201-3 + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - service_smb_disabled + +- name: Disable socket smb + systemd: + name: smb.socket + enabled: 'no' + state: stopped + masked: 'yes' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"smb.socket" in socket_file_exists.stdout_lines[1]' + tags: + - CCE-84201-3 + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - service_smb_disabled + + +[customizations.services] +disabled = ["smb"] + + apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + systemd: + units: + - name: smb.service + enabled: false + mask: true + - name: smb.socket + enabled: false + mask: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" stop 'smb.service' +"$SYSTEMCTL_EXEC" disable 'smb.service' +"$SYSTEMCTL_EXEC" mask 'smb.service' +# Disable socket activation if we have a unit file for it +if "$SYSTEMCTL_EXEC" list-unit-files | grep -q '^smb.socket'; then + "$SYSTEMCTL_EXEC" stop 'smb.socket' + "$SYSTEMCTL_EXEC" mask 'smb.socket' +fi +# The service may not be running because it has been started and failed, +# so let's reset the state so OVAL checks pass. +# Service should be 'inactive', not 'failed' after reboot though. +"$SYSTEMCTL_EXEC" reset-failed 'smb.service' || true + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + + SNMP Server + The Simple Network Management Protocol allows +administrators to monitor the state of network devices, including +computers. Older versions of SNMP were well-known for weak +security, such as plaintext transmission of the community string +(used for authentication) and usage of easily-guessable +choices for the community string. + + Disable SNMP Server if Possible + The system includes an SNMP daemon that allows for its remote +monitoring, though it not installed by default. If it was installed and +activated but is not needed, the software should be disabled and removed. + + Uninstall net-snmp Package + +The net-snmp package provides the snmpd service. +The net-snmp package can be removed with the following command: + +$ sudo dnf erase net-snmp + If there is no need to run SNMP server software, +removing the package provides a safeguard against its +activation. + CCE-85981-9 + +package --remove=net-snmp + + include remove_net-snmp + +class remove_net-snmp { + package { 'net-snmp': + ensure => 'purged', + } +} + + - name: Ensure net-snmp is removed + package: + name: net-snmp + state: absent + tags: + - CCE-85981-9 + - disable_strategy + - low_complexity + - low_disruption + - no_reboot_needed + - package_net-snmp_removed + - unknown_severity + + +# CAUTION: This remediation script will remove net-snmp +# from the system, and may remove any packages +# that depend on net-snmp. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "net-snmp" ; then + + dnf remove -y "net-snmp" + +fi + + + + + + + + + + Disable snmpd Service + +The snmpd service can be disabled with the following command: +$ sudo systemctl mask --now snmpd.service + 1311 + SRG-OS-000480-VMM-002000 + Running SNMP software provides a network-based avenue of attack, and +should be disabled if not needed. + + CCE-90832-7 + include disable_snmpd + +class disable_snmpd { + service {'snmpd': + enable => false, + ensure => 'stopped', + } +} + + - name: Disable service snmpd + block: + + - name: Disable service snmpd + systemd: + name: snmpd.service + enabled: 'no' + state: stopped + masked: 'yes' + ignore_errors: 'yes' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-90832-7 + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - service_snmpd_disabled + +- name: Unit Socket Exists - snmpd.socket + command: systemctl list-unit-files snmpd.socket + args: + warn: false + register: socket_file_exists + changed_when: false + ignore_errors: true + check_mode: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-90832-7 + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - service_snmpd_disabled + +- name: Disable socket snmpd + systemd: + name: snmpd.socket + enabled: 'no' + state: stopped + masked: 'yes' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"snmpd.socket" in socket_file_exists.stdout_lines[1]' + tags: + - CCE-90832-7 + - disable_strategy + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - service_snmpd_disabled + + +[customizations.services] +disabled = ["snmpd"] + + apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + systemd: + units: + - name: snmpd.service + enabled: false + mask: true + - name: snmpd.socket + enabled: false + mask: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" stop 'snmpd.service' +"$SYSTEMCTL_EXEC" disable 'snmpd.service' +"$SYSTEMCTL_EXEC" mask 'snmpd.service' +# Disable socket activation if we have a unit file for it +if "$SYSTEMCTL_EXEC" list-unit-files | grep -q '^snmpd.socket'; then + "$SYSTEMCTL_EXEC" stop 'snmpd.socket' + "$SYSTEMCTL_EXEC" mask 'snmpd.socket' +fi +# The service may not be running because it has been started and failed, +# so let's reset the state so OVAL checks pass. +# Service should be 'inactive', not 'failed' after reboot though. +"$SYSTEMCTL_EXEC" reset-failed 'snmpd.service' || true + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Configure SNMP Server if Necessary + If it is necessary to run the snmpd agent on the system, some best +practices should be followed to minimize the security risk from the +installation. The multiple security models implemented by SNMP cannot be fully +covered here so only the following general configuration advice can be offered: +use only SNMP version 3 security models and enable the use of authentication and encryptionwrite access to the MIB (Management Information Base) should be allowed only if necessaryall access to the MIB should be restricted following a principle of least privilegenetwork access should be limited to the maximum extent possible including restricting to expected network +addresses both in the configuration files and in the system firewall rulesensure SNMP agents send traps only to, and accept SNMP queries only from, authorized management +stationsensure that permissions on the snmpd.conf configuration file (by default, in /etc/snmp) are 640 or more restrictiveensure that any MIB files' permissions are also 640 or more restrictive + + SNMP read-only community string + Specify the SNMP community string used for read-only access. + changemero + + + SNMP read-write community string + Specify the SNMP community string used for read-write access. + changemerw + + + Ensure SNMP Read Write is disabled + Edit /etc/snmp/snmpd.conf, remove any rwuser entries. +Once the read write users have been removed, restart the SNMP service: +$ sudo service snmpd restart + Certain SNMP settings can permit users to execute system behaviors from user +writes to the community strings. +This may permit a compromised account to execute commands on a remote system. + + + + + + + Configure SNMP Service to Use Only SNMPv3 or Newer + Edit /etc/snmp/snmpd.conf, removing any references to rocommunity, rwcommunity, or com2sec. +Upon doing that, restart the SNMP service: +$ sudo service snmpd restart + 1311 + Earlier versions of SNMP are considered insecure, as they potentially allow +unauthorized access to detailed system management information. + + CCE-87293-7 + + + + + + + + + + + SSH Server + The SSH protocol is recommended for remote login and +remote file transfer. SSH provides confidentiality and integrity +for data exchanged between two systems, as well as server +authentication, through the use of public key cryptography. The +implementation included with the system is called OpenSSH, and more +detailed documentation is available from its website, + + https://www.openssh.com. +Its server program is called sshd and provided by the RPM package +openssh-server. + + + SSH enabled firewalld zone + Specify firewalld zone to enable SSH service. This value is used only for remediation purposes. + block + public + dmz + drop + external + home + internal + public + trusted + work + + + SSH Approved ciphers by FIPS + Specify the FIPS approved ciphers that are used for data integrity protection by the SSH server. + aes256-ctr,aes192-ctr,aes128-ctr + aes128-ctr,aes192-ctr,aes256-ctr,aes128-cbc,3des-cbc,aes192-cbc,aes256-cbc,rijndael-cbc@lysator.liu.se + chacha20-poly1305@openssh.com,aes128-ctr,aes192-ctr,aes256-ctr,aes128-gcm@openssh.com,aes256-gcm@openssh.com,aes128-cbc,aes192-cbc,aes256-cbc,blowfish-cbc,cast128-cbc,3des-cbc + chacha20-poly1305@openssh.com,aes128-ctr,aes192-ctr,aes256-ctr,aes128-gcm@openssh.com,aes256-gcm@openssh.com,aes128-cbc,aes192-cbc,aes256-cbc,blowfish-cbc,cast128-cbc,3des-cbc + chacha20-poly1305@openssh.com,aes128-ctr,aes192-ctr,aes256-ctr,aes128-gcm@openssh.com,aes256-gcm@openssh.com,aes128-cbc,aes192-cbc,aes256-cbc,blowfish-cbc,cast128-cbc,3des-cbc + chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr + + + SSH Approved MACs by FIPS + Specify the FIPS approved MACs (message authentication code) algorithms + that are used for data integrity protection by the SSH server. + hmac-sha2-512,hmac-sha2-256 + hmac-sha2-512,hmac-sha2-256,hmac-sha1,hmac-sha1-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com + umac-64-etm@openssh.com,umac-128-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com,hmac-sha1-etm@openssh.com,umac-64@openssh.com,umac-128@openssh.com,hmac-sha2-256,hmac-sha2-512,hmac-sha1,hmac-sha1-etm@openssh.com + umac-64-etm@openssh.com,umac-128-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com,hmac-sha1-etm@openssh.com,umac-64@openssh.com,umac-128@openssh.com,hmac-sha2-256,hmac-sha2-512,hmac-sha1,hmac-sha1-etm@openssh.com + umac-64-etm@openssh.com,umac-128-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com,hmac-sha1-etm@openssh.com,umac-64@openssh.com,umac-128@openssh.com,hmac-sha2-256,hmac-sha2-512,hmac-sha1,hmac-sha1-etm@openssh.com + hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-sha2-512,hmac-sha2-256 + + + SSH session Idle time + Specify duration of allowed idle time. + 600 + 7200 + 840 + 900 + 1800 + 300 + 3600 + 300 + + + SSH Server Listening Port + Specify port the SSH server is listening. + 22 + + + SSH Max authentication attempts + Specify the maximum number of authentication attempts per connection. + 10 + 3 + 4 + 5 + 4 + + + SSH is required to be installed + Specify if the Policy requires SSH to be installed. Used by SSH Rules +to determine if SSH should be uninstalled or configured. +A value of 0 means that the policy doesn't care if OpenSSH server is installed or not. If it is installed, scanner will check for it's configuration, if it's not installed, the check will pass. +A value of 1 indicates that OpenSSH server package is not required by the policy; +A value of 2 indicates that OpenSSH server package is required by the policy. + 0 + 1 + 2 + + + SSH Max Sessions Count + Specify the maximum number of open sessions permitted. + 10 + 4 + 3 + 2 + 1 + 0 + 10 + + + SSH Max Keep Alive Count + Specify the maximum number of idle message counts before session is terminated. + 10 + 3 + 5 + 0 + 1 + 0 + + + Install OpenSSH client software + The openssh-clients package can be installed with the following command: + +$ sudo dnf install openssh-clients + FIA_UAU.5 + FTP_ITC_EXT.1 + FCS_SSH_EXT.1 + FCS_SSHC_EXT.1 + SRG-OS-000480-GPOS-00227 + This package includes utilities to make encrypted connections and transfer +files securely to SSH servers. + CCE-90836-8 + +package --add=openssh-clients + + include install_openssh-clients + +class install_openssh-clients { + package { 'openssh-clients': + ensure => 'installed', + } +} + + - name: Ensure openssh-clients is installed + package: + name: openssh-clients + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-90836-8 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_openssh-clients_installed + + +[[packages]] +name = "openssh-clients" +version = "*" + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if ! rpm -q --quiet "openssh-clients" ; then + dnf install -y "openssh-clients" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Install the OpenSSH Server Package + The openssh-server package should be installed. +The openssh-server package can be installed with the following command: + +$ sudo dnf install openssh-server + 13 + 14 + APO01.06 + DSS05.02 + DSS05.04 + DSS05.07 + DSS06.02 + DSS06.06 + CCI-002418 + CCI-002420 + CCI-002421 + CCI-002422 + SR 3.1 + SR 3.8 + SR 4.1 + SR 4.2 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-6(a) + PR.DS-2 + PR.DS-5 + FIA_UAU.5 + FTP_ITC_EXT.1 + FCS_SSH_EXT.1 + FCS_SSHS_EXT.1 + SRG-OS-000423-GPOS-00187 + SRG-OS-000424-GPOS-00188 + SRG-OS-000425-GPOS-00189 + SRG-OS-000426-GPOS-00190 + Without protection of the transmitted information, confidentiality, and +integrity may be compromised because unprotected communications can be +intercepted and either read or altered. + CCE-90823-6 + +package --add=openssh-server + + include install_openssh-server + +class install_openssh-server { + package { 'openssh-server': + ensure => 'installed', + } +} + + - name: Ensure openssh-server is installed + package: + name: openssh-server + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-90823-6 + - NIST-800-53-CM-6(a) + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_openssh-server_installed + + +[[packages]] +name = "openssh-server" +version = "*" + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if ! rpm -q --quiet "openssh-server" ; then + dnf install -y "openssh-server" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Remove the OpenSSH Server Package + The openssh-server package should be removed. +The openssh-server package can be removed with the following command: + +$ sudo dnf erase openssh-server + Without protection of the transmitted information, confidentiality, and +integrity may be compromised because unprotected communications can be +intercepted and either read or altered. + +package --remove=openssh-server + + include remove_openssh-server + +class remove_openssh-server { + package { 'openssh-server': + ensure => 'purged', + } +} + + - name: Ensure openssh-server is removed + package: + name: openssh-server + state: absent + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_openssh-server_removed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# CAUTION: This remediation script will remove openssh-server +# from the system, and may remove any packages +# that depend on openssh-server. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "openssh-server" ; then + + dnf remove -y "openssh-server" + +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Enable the OpenSSH Service + The SSH server service, sshd, is commonly needed. + +The sshd service can be enabled with the following command: +$ sudo systemctl enable sshd.service + 13 + 14 + APO01.06 + DSS05.02 + DSS05.04 + DSS05.07 + DSS06.02 + DSS06.06 + 3.1.13 + 3.5.4 + 3.13.8 + CCI-002418 + CCI-002420 + CCI-002421 + CCI-002422 + SR 3.1 + SR 3.8 + SR 4.1 + SR 4.2 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CM-6(a) + SC-8 + SC-8(1) + SC-8(2) + SC-8(3) + SC-8(4) + PR.DS-2 + PR.DS-5 + SRG-OS-000423-GPOS-00187 + SRG-OS-000424-GPOS-00188 + SRG-OS-000425-GPOS-00189 + SRG-OS-000426-GPOS-00190 + Without protection of the transmitted information, confidentiality, and +integrity may be compromised because unprotected communications can be +intercepted and either read or altered. + +This checklist item applies to both internal and external networks and all types +of information system components from which information can be transmitted (e.g., servers, +mobile devices, notebook computers, printers, copiers, scanners, etc). Communication paths +outside the physical protection of a controlled boundary are exposed to the possibility +of interception and modification. + CCE-90822-8 + include enable_sshd + +class enable_sshd { + service {'sshd': + enable => true, + ensure => 'running', + } +} + + - name: Enable service sshd + block: + + - name: Gather the package facts + package_facts: + manager: auto + + - name: Enable service sshd + service: + name: sshd + enabled: 'yes' + state: started + masked: 'no' + when: + - '"openssh-server" in ansible_facts.packages' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-90822-8 + - NIST-800-171-3.1.13 + - NIST-800-171-3.13.8 + - NIST-800-171-3.5.4 + - NIST-800-53-CM-6(a) + - NIST-800-53-SC-8 + - NIST-800-53-SC-8(1) + - NIST-800-53-SC-8(2) + - NIST-800-53-SC-8(3) + - NIST-800-53-SC-8(4) + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_sshd_enabled + + +[customizations.services] +enabled = ["sshd"] + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" unmask 'sshd.service' +"$SYSTEMCTL_EXEC" start 'sshd.service' +"$SYSTEMCTL_EXEC" enable 'sshd.service' + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Disable SSH Server If Possible (Unusual) + The SSH server service, sshd, is commonly needed. +However, if it can be disabled, do so. + + +The sshd service can be disabled with the following command: +$ sudo systemctl mask --now sshd.service + +This is unusual, as SSH is a common method for encrypted and authenticated +remote access. + CM-3(6) + IA-2(4) + + include disable_sshd + +class disable_sshd { + service {'sshd': + enable => false, + ensure => 'stopped', + } +} + + - name: Disable service sshd + block: + + - name: Disable service sshd + systemd: + name: sshd.service + enabled: 'no' + state: stopped + masked: 'yes' + ignore_errors: 'yes' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-CM-3(6) + - NIST-800-53-IA-2(4) + - disable_strategy + - low_complexity + - low_disruption + - no_reboot_needed + - service_sshd_disabled + - unknown_severity + +- name: Unit Socket Exists - sshd.socket + command: systemctl list-unit-files sshd.socket + args: + warn: false + register: socket_file_exists + changed_when: false + ignore_errors: true + check_mode: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - NIST-800-53-CM-3(6) + - NIST-800-53-IA-2(4) + - disable_strategy + - low_complexity + - low_disruption + - no_reboot_needed + - service_sshd_disabled + - unknown_severity + +- name: Disable socket sshd + systemd: + name: sshd.socket + enabled: 'no' + state: stopped + masked: 'yes' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"sshd.socket" in socket_file_exists.stdout_lines[1]' + tags: + - NIST-800-53-CM-3(6) + - NIST-800-53-IA-2(4) + - disable_strategy + - low_complexity + - low_disruption + - no_reboot_needed + - service_sshd_disabled + - unknown_severity + + +[customizations.services] +disabled = ["sshd"] + + apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + systemd: + units: + - name: sshd.service + enabled: false + mask: true + - name: sshd.socket + enabled: false + mask: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" stop 'sshd.service' +"$SYSTEMCTL_EXEC" disable 'sshd.service' +"$SYSTEMCTL_EXEC" mask 'sshd.service' +# Disable socket activation if we have a unit file for it +if "$SYSTEMCTL_EXEC" list-unit-files | grep -q '^sshd.socket'; then + "$SYSTEMCTL_EXEC" stop 'sshd.socket' + "$SYSTEMCTL_EXEC" mask 'sshd.socket' +fi +# The service may not be running because it has been started and failed, +# so let's reset the state so OVAL checks pass. +# Service should be 'inactive', not 'failed' after reboot though. +"$SYSTEMCTL_EXEC" reset-failed 'sshd.service' || true + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + Verify Group Who Owns SSH Server config file + +To properly set the group owner of /etc/ssh/sshd_config, run the command: +$ sudo chgrp root /etc/ssh/sshd_config + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + AC-17(a) + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + SRG-OS-000480-GPOS-00227 + Service configuration files enable or disable features of their respective +services that if configured incorrectly can lead to insecure and vulnerable +configurations. Therefore, service configuration files should be owned by the +correct group to prevent unauthorized changes. + CCE-90817-8 + - name: Test for existence /etc/ssh/sshd_config + stat: + path: /etc/ssh/sshd_config + register: file_exists + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-90817-8 + - NIST-800-53-AC-17(a) + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_groupowner_sshd_config + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure group owner 0 on /etc/ssh/sshd_config + file: + path: /etc/ssh/sshd_config + group: '0' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-90817-8 + - NIST-800-53-AC-17(a) + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_groupowner_sshd_config + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +chgrp 0 /etc/ssh/sshd_config + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Verify Owner on SSH Server config file + +To properly set the owner of /etc/ssh/sshd_config, run the command: +$ sudo chown root /etc/ssh/sshd_config + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + AC-17(a) + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + SRG-OS-000480-GPOS-00227 + Service configuration files enable or disable features of their respective +services that if configured incorrectly can lead to insecure and vulnerable +configurations. Therefore, service configuration files should be owned by the +correct group to prevent unauthorized changes. + CCE-90821-0 + - name: Test for existence /etc/ssh/sshd_config + stat: + path: /etc/ssh/sshd_config + register: file_exists + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-90821-0 + - NIST-800-53-AC-17(a) + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_owner_sshd_config + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure owner 0 on /etc/ssh/sshd_config + file: + path: /etc/ssh/sshd_config + owner: '0' + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-90821-0 + - NIST-800-53-AC-17(a) + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_owner_sshd_config + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +chown 0 /etc/ssh/sshd_config + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Verify Permissions on SSH Server config file + +To properly set the permissions of /etc/ssh/sshd_config, run the command: +$ sudo chmod 0600 /etc/ssh/sshd_config + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + AC-17(a) + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + SRG-OS-000480-GPOS-00227 + Service configuration files enable or disable features of their respective +services that if configured incorrectly can lead to insecure and vulnerable +configurations. Therefore, service configuration files should be owned by the +correct group to prevent unauthorized changes. + CCE-90818-6 + - name: Test for existence /etc/ssh/sshd_config + stat: + path: /etc/ssh/sshd_config + register: file_exists + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-90818-6 + - NIST-800-53-AC-17(a) + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_permissions_sshd_config + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Ensure permission u-xs,g-xwrs,o-xwrt on /etc/ssh/sshd_config + file: + path: /etc/ssh/sshd_config + mode: u-xs,g-xwrs,o-xwrt + when: + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - file_exists.stat is defined and file_exists.stat.exists + tags: + - CCE-90818-6 + - NIST-800-53-AC-17(a) + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_permissions_sshd_config + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +chmod u-xs,g-xwrs,o-xwrt /etc/ssh/sshd_config + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Verify Permissions on SSH Server Private *_key Key Files + SSH server private keys - files that match the /etc/ssh/*_key glob, have to have restricted permissions. +If those files are owned by the root user and the root group, they have to have the 0600 permission or stricter. +If they are owned by the root user, but by a dedicated group ssh_keys, they can have the 0640 permission or stricter. + BP28(R36) + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + 3.1.13 + 3.13.10 + CCI-000366 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + AC-17(a) + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + SRG-OS-000480-GPOS-00227 + If an unauthorized user obtains the private SSH host key file, the host could be +impersonated. + CCE-90820-2 + include ssh_private_key_perms + +class ssh_private_key_perms { + exec { 'sshd_priv_key': + command => "chmod 0640 /etc/ssh/*_key", + path => '/bin:/usr/bin' + } +} + + - name: Find root:root-owned keys + command: find -H /etc/ssh/ -maxdepth 1 -user root -regex ".*_key$" -type f -group + root -perm /u+xs,g+xwrs,o+xwrt + register: root_owned_keys + changed_when: false + failed_when: false + check_mode: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-90820-2 + - NIST-800-171-3.1.13 + - NIST-800-171-3.13.10 + - NIST-800-53-AC-17(a) + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_permissions_sshd_private_key + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Set permissions for root:root-owned keys + file: + path: '{{ item }}' + mode: u-xs,g-xwrs,o-xwrt + state: file + with_items: + - '{{ root_owned_keys.stdout_lines }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-90820-2 + - NIST-800-171-3.1.13 + - NIST-800-171-3.13.10 + - NIST-800-53-AC-17(a) + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_permissions_sshd_private_key + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Find root:ssh_keys-owned keys + command: find -H /etc/ssh/ -maxdepth 1 -user root -regex ".*_key$" -type f -group + ssh_keys -perm /u+xs,g+xws,o+xwrt + register: dedicated_group_owned_keys + changed_when: false + failed_when: false + check_mode: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-90820-2 + - NIST-800-171-3.1.13 + - NIST-800-171-3.13.10 + - NIST-800-53-AC-17(a) + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_permissions_sshd_private_key + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Set permissions for root:ssh_keys-owned keys + file: + path: '{{ item }}' + mode: u-xs,g-xws,o-xwrt + state: file + with_items: + - '{{ dedicated_group_owned_keys.stdout_lines }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-90820-2 + - NIST-800-171-3.1.13 + - NIST-800-171-3.13.10 + - NIST-800-53-AC-17(a) + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_permissions_sshd_private_key + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +for keyfile in /etc/ssh/*_key; do + test -f "$keyfile" || continue + if test root:root = "$(stat -c "%U:%G" "$keyfile")"; then + chmod u-xs,g-xwrs,o-xwrt "$keyfile" + elif test root:ssh_keys = "$(stat -c "%U:%G" "$keyfile")"; then + chmod u-xs,g-xws,o-xwrt "$keyfile" + else + echo "Key-like file '$keyfile' is owned by an unexpected user:group combination" + fi +done + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Verify Permissions on SSH Server Public *.pub Key Files + To properly set the permissions of /etc/ssh/*.pub, run the command: $ sudo chmod 0644 /etc/ssh/*.pub + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + 3.1.13 + 3.13.10 + CCI-000366 + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + AC-17(a) + CM-6(a) + AC-6(1) + PR.AC-4 + PR.DS-5 + SRG-OS-000480-GPOS-00227 + If a public host key file is modified by an unauthorized user, the SSH service +may be compromised. + CCE-90819-4 + include ssh_public_key_perms + +class ssh_public_key_perms { + exec { 'sshd_pub_key': + command => "chmod 0644 /etc/ssh/*.pub", + path => '/bin:/usr/bin' + } +} + + - name: Find /etc/ssh/ file(s) + command: find -H /etc/ssh/ -maxdepth 1 -perm /u+xs,g+xws,o+xwt -type f -regex "^.*\.pub$" + register: files_found + changed_when: false + failed_when: false + check_mode: false + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-90819-4 + - NIST-800-171-3.1.13 + - NIST-800-171-3.13.10 + - NIST-800-53-AC-17(a) + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_permissions_sshd_pub_key + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + +- name: Set permissions for /etc/ssh/ file(s) + file: + path: '{{ item }}' + mode: u-xs,g-xws,o-xwt + state: file + with_items: + - '{{ files_found.stdout_lines }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-90819-4 + - NIST-800-171-3.1.13 + - NIST-800-171-3.13.10 + - NIST-800-53-AC-17(a) + - NIST-800-53-AC-6(1) + - NIST-800-53-CM-6(a) + - configure_strategy + - file_permissions_sshd_pub_key + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +find -H /etc/ssh/ -maxdepth 1 -perm /u+xs,g+xws,o+xwt -type f -regex '^.*\.pub$' -exec chmod u-xs,g-xws,o-xwt {} \; + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Remove SSH Server iptables Firewall exception (Unusual) + By default, inbound connections to SSH's port are allowed. If the SSH +server is not being used, this exception should be removed from the +firewall configuration. + +Edit the files /etc/sysconfig/iptables and +/etc/sysconfig/ip6tables (if IPv6 is in use). In each file, locate +and delete the line: +-A INPUT -m state --state NEW -m tcp -p tcp --dport 22 -j ACCEPT +This is unusual, as SSH is a common method for encrypted and authenticated +remote access. + If inbound SSH connections are not expected, disallowing access to the SSH +port will avoid possible exploitation of the port by an attacker. + + + OpenSSH Service Must Use Passcode for Their Private Keys + Verify the SSH private key files have a passcode. +For each private key stored on the system, use the following command: + +$ sudo ssh-keygen -y -f /path/to/file + +If the contents of the key are displayed, without asking a passphrase this is a finding. + CCI-000186 + IA-5(2) + IA-5(2).1 + SRG-OS-000067-GPOS-00035 + If an unauthorized user obtains access to a private key without a passcode, that user would +have unauthorized access to any system where the associated public key has been installed. + CCE-86194-8 + + + + + + Configure OpenSSH Client if Necessary + The following configuration changes apply to the SSH client. They can +improve security parameters relwevant to the client user, e.g. increasing +entropy while generating initialization vectors. Note that these changes +influence only the default SSH client configuration. Changes in this group +can be overridden by the client user by modifying files within the +~/.ssh directory or by supplying parameters on the command line. + + Configure session renegotiation for SSH client + The RekeyLimit parameter specifies how often +the session key is renegotiated, both in terms of +amount of data that may be transmitted and the time +elapsed. To decrease the default limits, put line +RekeyLimit to file /etc/ssh/ssh_config.d/02-rekey-limit.conf. +Make sure that there is no other RekeyLimit configuration preceding +the include directive in the main config file +/etc/ssh/ssh_config. Check also other files in +/etc/ssh/ssh_config.d directory. Files are processed according to +lexicographical order of file names. Make sure that there is no file +processed before 02-rekey-limit.conf containing definition of +RekeyLimit. + CCI-000068 + FCS_SSH_EXT.1.8 + SRG-OS-000423-GPOS-00187 + SRG-OS-000033-GPOS-00014 + SRG-OS-000424-GPOS-00188 + By decreasing the limit based on the amount of data and enabling +time-based limit, effects of potential attacks against +encryption keys are limited. + CCE-87522-9 + - name: XCCDF Value var_ssh_client_rekey_limit_size # promote to variable + set_fact: + var_ssh_client_rekey_limit_size: !!str + tags: + - always +- name: XCCDF Value var_ssh_client_rekey_limit_time # promote to variable + set_fact: + var_ssh_client_rekey_limit_time: !!str + tags: + - always + +- name: Ensure RekeyLimit is not configured in /etc/ssh/ssh_config + lineinfile: + path: /etc/ssh/ssh_config + create: false + regexp: ^\s*RekeyLimit.*$ + state: absent + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-87522-9 + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - ssh_client_rekey_limit + +- name: Collect all include config files for ssh client which configure RekeyLimit + find: + paths: /etc/ssh/ssh_config.d/ + contains: ^[\s]*RekeyLimit.*$ + patterns: '*.config' + register: ssh_config_include_files + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-87522-9 + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - ssh_client_rekey_limit + +- name: Remove all occurences of RekeyLimit configuration from include config files + of ssh client + lineinfile: + path: '{{ item }}' + regexp: ^[\s]*RekeyLimit.*$ + state: absent + loop: '{{ ssh_config_include_files.files }}' + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-87522-9 + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - ssh_client_rekey_limit + +- name: Ensure that rekey limit is set to {{ var_ssh_client_rekey_limit_size }} {{ + var_ssh_client_rekey_limit_time }} in /etc/ssh/ssh_config.d/02-rekey-limit.conf + lineinfile: + path: /etc/ssh/ssh_config.d/02-rekey-limit.conf + create: true + regexp: ^\s*RekeyLimit.*$ + line: RekeyLimit {{ var_ssh_client_rekey_limit_size }} {{ var_ssh_client_rekey_limit_time + }} + state: present + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-87522-9 + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - ssh_client_rekey_limit + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_ssh_client_rekey_limit_size='' +var_ssh_client_rekey_limit_time='' + + +main_config="/etc/ssh/ssh_config" +include_directory="/etc/ssh/ssh_config.d" + +if grep -q '^[\s]*RekeyLimit.*$' "$main_config"; then + sed -i '/^[\s]*RekeyLimit.*/d' "$main_config" +fi + +for file in "$include_directory"/*.conf; do + if grep -q '^[\s]*RekeyLimit.*$' "$file"; then + sed -i '/^[\s]*RekeyLimit.*/d' "$file" + fi +done + +if [ -e "/etc/ssh/ssh_config.d/02-rekey-limit.conf" ] ; then + + LC_ALL=C sed -i "/^\s*RekeyLimit\s\+/d" "/etc/ssh/ssh_config.d/02-rekey-limit.conf" +else + touch "/etc/ssh/ssh_config.d/02-rekey-limit.conf" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/ssh/ssh_config.d/02-rekey-limit.conf" + +cp "/etc/ssh/ssh_config.d/02-rekey-limit.conf" "/etc/ssh/ssh_config.d/02-rekey-limit.conf.bak" +# Insert at the end of the file +printf '%s\n' "RekeyLimit $var_ssh_client_rekey_limit_size $var_ssh_client_rekey_limit_time" >> "/etc/ssh/ssh_config.d/02-rekey-limit.conf" +# Clean up after ourselves. +rm "/etc/ssh/ssh_config.d/02-rekey-limit.conf.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + + + Configure OpenSSH Server if Necessary + If the system needs to act as an SSH server, then +certain changes should be made to the OpenSSH daemon configuration +file /etc/ssh/sshd_config. The following recommendations can be +applied to this file. See the sshd_config(5) man page for more +detailed information. + + SSH RekeyLimit - size + Specify the size component of the rekey limit. + default + 512M + 512M + 1G + + + SSH RekeyLimit - size + Specify the size component of the rekey limit. + none + 1h + 1h + + + SSH Compression Setting + Specify the compression setting for SSH connections. + no + delayed + no + + + SSH Privilege Separation Setting + Specify whether and how sshd separates privileges when handling incoming network connections. + no + yes + sandbox + sandbox + + + SSH LoginGraceTime setting + Configure parameters for how long the servers stays connected before the user has successfully logged in + 60 + 60 + + + SSH MaxStartups setting + Configure parameters for maximum concurrent unauthenticated connections to the SSH daemon. + 10:30:100 + 10:30:60 + + + Set SSH Client Alive Count Max to zero + The SSH server sends at most ClientAliveCountMax messages +during a SSH session and waits for a response from the SSH client. +The option ClientAliveInterval configures timeout after +each ClientAliveCountMax message. If the SSH server does not +receive a response from the client, then the connection is considered idle +and terminated. + +To ensure the SSH idle timeout occurs precisely when the +ClientAliveInterval is set, set the ClientAliveCountMax to +value of 0 in + + +/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf: + 1 + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + 7 + 8 + 5.5.6 + APO13.01 + BAI03.01 + BAI03.02 + BAI03.03 + DSS01.03 + DSS03.05 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.03 + DSS06.10 + 3.1.11 + CCI-000879 + CCI-001133 + CCI-002361 + 164.308(a)(4)(i) + 164.308(b)(1) + 164.308(b)(3) + 164.310(b) + 164.312(e)(1) + 164.312(e)(2)(ii) + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 6.2 + A.12.4.1 + A.12.4.3 + A.14.1.1 + A.14.2.1 + A.14.2.5 + A.18.1.4 + A.6.1.2 + A.6.1.5 + A.7.1.1 + A.9.1.2 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.1 + A.9.4.2 + A.9.4.3 + A.9.4.4 + A.9.4.5 + CIP-004-6 R2.2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.2 + CIP-007-3 R5.3.1 + CIP-007-3 R5.3.2 + CIP-007-3 R5.3.3 + AC-2(5) + AC-12 + AC-17(a) + SC-10 + CM-6(a) + DE.CM-1 + DE.CM-3 + PR.AC-1 + PR.AC-4 + PR.AC-6 + PR.AC-7 + PR.IP-2 + Req-8.1.8 + SRG-OS-000126-GPOS-00066 + SRG-OS-000163-GPOS-00072 + SRG-OS-000279-GPOS-00109 + SRG-OS-000480-VMM-002000 + This ensures a user login will be terminated as soon as the ClientAliveInterval +is reached. + + CCE-90271-8 + - name: Set SSH Client Alive Count Max to zero + block: + + - name: Deduplicate values from /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*{{ "ClientAliveCountMax"| regex_escape }}\s+ + state: absent + + - name: Check if /etc/ssh/sshd_config.d exists + stat: + path: /etc/ssh/sshd_config.d + register: _etc_ssh_sshd_config_d_exists + + - name: Check if the parameter ClientAliveCountMax is present in /etc/ssh/sshd_config.d + find: + paths: /etc/ssh/sshd_config.d + recurse: 'yes' + follow: 'no' + contains: (?i)^\s*{{ "ClientAliveCountMax"| regex_escape }}\s+ + register: _etc_ssh_sshd_config_d_has_parameter + when: _etc_ssh_sshd_config_d_exists.stat.isdir is defined and _etc_ssh_sshd_config_d_exists.stat.isdir + + - name: Remove parameter from files in /etc/ssh/sshd_config.d + lineinfile: + path: '{{ item.path }}' + create: false + regexp: (?i)^\s*{{ "ClientAliveCountMax"| regex_escape }}\s+ + state: absent + with_items: '{{ _etc_ssh_sshd_config_d_has_parameter.files }}' + when: _etc_ssh_sshd_config_d_has_parameter.matched + + - name: Insert correct line to /etc/ssh/sshd_config.d/00-complianceascode-hardening.conf + lineinfile: + path: /etc/ssh/sshd_config.d/00-complianceascode-hardening.conf + create: true + regexp: (?i)^\s*{{ "ClientAliveCountMax"| regex_escape }}\s+ + line: ClientAliveCountMax 0 + state: present + insertbefore: ^[#\s]*Match + validate: /usr/sbin/sshd -t -f %s + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-90271-8 + - CJIS-5.5.6 + - NIST-800-171-3.1.11 + - NIST-800-53-AC-12 + - NIST-800-53-AC-17(a) + - NIST-800-53-AC-2(5) + - NIST-800-53-CM-6(a) + - NIST-800-53-SC-10 + - PCI-DSS-Req-8.1.8 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sshd_set_keepalive_0 + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +mkdir -p /etc/ssh/sshd_config.d +touch /etc/ssh/sshd_config.d/00-complianceascode-hardening.conf + +LC_ALL=C sed -i "/^\s*ClientAliveCountMax\s\+/Id" "/etc/ssh/sshd_config" +LC_ALL=C sed -i "/^\s*ClientAliveCountMax\s\+/Id" "/etc/ssh/sshd_config.d"/*.conf +if [ -e "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" ] ; then + + LC_ALL=C sed -i "/^\s*ClientAliveCountMax\s\+/Id" "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" +else + touch "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" + +cp "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" +# Insert before the line matching the regex '^Match'. +line_number="$(LC_ALL=C grep -n "^Match" "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" | LC_ALL=C sed 's/:.*//g')" +if [ -z "$line_number" ]; then + # There was no match of '^Match', insert at + # the end of the file. + printf '%s\n' "ClientAliveCountMax 0" >> "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" +else + head -n "$(( line_number - 1 ))" "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" > "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" + printf '%s\n' "ClientAliveCountMax 0" >> "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" + tail -n "+$(( line_number ))" "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" >> "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" +fi +# Clean up after ourselves. +rm "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Set SSH Client Alive Count Max + The SSH server sends at most ClientAliveCountMax messages +during a SSH session and waits for a response from the SSH client. +The option ClientAliveInterval configures timeout after +each ClientAliveCountMax message. If the SSH server does not +receive a response from the client, then the connection is considered idle +and terminated. +For SSH earlier than v8.2, a ClientAliveCountMax value of 0 +causes an idle timeout precisely when the ClientAliveInterval is set. +Starting with v8.2, a value of 0 disables the timeout functionality +completely. If the option is set to a number greater than 0, then +the idle session will be disconnected after +ClientAliveInterval * ClientAliveCountMax seconds. + BP28(R29) + 1 + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + 7 + 8 + 5.5.6 + APO13.01 + BAI03.01 + BAI03.02 + BAI03.03 + DSS01.03 + DSS03.05 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.03 + DSS06.10 + 3.1.11 + CCI-000879 + CCI-001133 + CCI-002361 + 164.308(a)(4)(i) + 164.308(b)(1) + 164.308(b)(3) + 164.310(b) + 164.312(e)(1) + 164.312(e)(2)(ii) + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 6.2 + A.12.4.1 + A.12.4.3 + A.14.1.1 + A.14.2.1 + A.14.2.5 + A.18.1.4 + A.6.1.2 + A.6.1.5 + A.7.1.1 + A.9.1.2 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.1 + A.9.4.2 + A.9.4.3 + A.9.4.4 + A.9.4.5 + CIP-004-6 R2.2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.2 + CIP-007-3 R5.3.1 + CIP-007-3 R5.3.2 + CIP-007-3 R5.3.3 + AC-2(5) + AC-12 + AC-17(a) + SC-10 + CM-6(a) + DE.CM-1 + DE.CM-3 + PR.AC-1 + PR.AC-4 + PR.AC-6 + PR.AC-7 + PR.IP-2 + Req-8.1.8 + SRG-OS-000163-GPOS-00072 + SRG-OS-000279-GPOS-00109 + SRG-OS-000480-VMM-002000 + This ensures a user login will be terminated as soon as the ClientAliveInterval +is reached. + + CCE-90805-3 + - name: XCCDF Value var_sshd_set_keepalive # promote to variable + set_fact: + var_sshd_set_keepalive: !!str + tags: + - always + +- name: Set SSH Client Alive Count Max + block: + + - name: Check for duplicate values + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*ClientAliveCountMax\s+ + state: absent + check_mode: true + changed_when: false + register: dupes + + - name: Deduplicate values from /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*ClientAliveCountMax\s+ + state: absent + when: dupes.found is defined and dupes.found > 1 + + - name: Insert correct line to /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: true + regexp: (?i)^\s*ClientAliveCountMax\s+ + line: ClientAliveCountMax {{ var_sshd_set_keepalive }} + state: present + insertbefore: ^[#\s]*Match + validate: /usr/sbin/sshd -t -f %s + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-90805-3 + - CJIS-5.5.6 + - NIST-800-171-3.1.11 + - NIST-800-53-AC-12 + - NIST-800-53-AC-17(a) + - NIST-800-53-AC-2(5) + - NIST-800-53-CM-6(a) + - NIST-800-53-SC-10 + - PCI-DSS-Req-8.1.8 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sshd_set_keepalive + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_sshd_set_keepalive='' + + +if [ -e "/etc/ssh/sshd_config" ] ; then + + LC_ALL=C sed -i "/^\s*ClientAliveCountMax\s\+/Id" "/etc/ssh/sshd_config" +else + touch "/etc/ssh/sshd_config" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/ssh/sshd_config" + +cp "/etc/ssh/sshd_config" "/etc/ssh/sshd_config.bak" +# Insert before the line matching the regex '^Match'. +line_number="$(LC_ALL=C grep -n "^Match" "/etc/ssh/sshd_config.bak" | LC_ALL=C sed 's/:.*//g')" +if [ -z "$line_number" ]; then + # There was no match of '^Match', insert at + # the end of the file. + printf '%s\n' "ClientAliveCountMax $var_sshd_set_keepalive" >> "/etc/ssh/sshd_config" +else + head -n "$(( line_number - 1 ))" "/etc/ssh/sshd_config.bak" > "/etc/ssh/sshd_config" + printf '%s\n' "ClientAliveCountMax $var_sshd_set_keepalive" >> "/etc/ssh/sshd_config" + tail -n "+$(( line_number ))" "/etc/ssh/sshd_config.bak" >> "/etc/ssh/sshd_config" +fi +# Clean up after ourselves. +rm "/etc/ssh/sshd_config.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + + Set SSH Idle Timeout Interval + SSH allows administrators to set an idle timeout interval. After this interval +has passed, the idle user will be automatically logged out. + +To set an idle timeout interval, edit the following line in /etc/ssh/sshd_config as +follows: +ClientAliveInterval + +The timeout interval is given in seconds. For example, have a timeout +of 10 minutes, set interval to 600. + +If a shorter timeout has already been set for the login shell, that value will +preempt any SSH setting made in /etc/ssh/sshd_config. Keep in mind that +some processes may stop SSH from correctly detecting that the user is idle. + SSH disconnecting idle clients will not have desired effect without also +configuring ClientAliveCountMax in the SSH service configuration. + Following conditions may prevent the SSH session to time out: +Remote processes on the remote machine generates output. As the output has to be transferred over the network to the client, the timeout is reset every time such transfer happens.Any scp or sftp activity by the same user to the host resets the timeout. + BP28(R29) + 1 + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + 7 + 8 + 5.5.6 + APO13.01 + BAI03.01 + BAI03.02 + BAI03.03 + DSS01.03 + DSS03.05 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.03 + DSS06.10 + 3.1.11 + CCI-000879 + CCI-001133 + CCI-002361 + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 6.2 + A.12.4.1 + A.12.4.3 + A.14.1.1 + A.14.2.1 + A.14.2.5 + A.18.1.4 + A.6.1.2 + A.6.1.5 + A.7.1.1 + A.9.1.2 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.1 + A.9.4.2 + A.9.4.3 + A.9.4.4 + A.9.4.5 + CIP-004-6 R2.2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.2 + CIP-007-3 R5.3.1 + CIP-007-3 R5.3.2 + CIP-007-3 R5.3.3 + CM-6(a) + AC-17(a) + AC-2(5) + AC-12 + AC-17(a) + SC-10 + CM-6(a) + DE.CM-1 + DE.CM-3 + PR.AC-1 + PR.AC-4 + PR.AC-6 + PR.AC-7 + PR.IP-2 + Req-8.1.8 + SRG-OS-000126-GPOS-00066 + SRG-OS-000163-GPOS-00072 + SRG-OS-000279-GPOS-00109 + SRG-OS-000395-GPOS-00175 + SRG-OS-000480-VMM-002000 + Terminating an idle ssh session within a short time period reduces the window of +opportunity for unauthorized personnel to take control of a management session +enabled on the console or console port that has been let unattended. + + CCE-90811-1 + - name: XCCDF Value sshd_idle_timeout_value # promote to variable + set_fact: + sshd_idle_timeout_value: !!str + tags: + - always + +- name: Set SSH Idle Timeout Interval + block: + + - name: Check for duplicate values + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*ClientAliveInterval\s+ + state: absent + check_mode: true + changed_when: false + register: dupes + + - name: Deduplicate values from /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*ClientAliveInterval\s+ + state: absent + when: dupes.found is defined and dupes.found > 1 + + - name: Insert correct line to /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: true + regexp: (?i)^\s*ClientAliveInterval\s+ + line: ClientAliveInterval {{ sshd_idle_timeout_value }} + state: present + insertbefore: ^[#\s]*Match + validate: /usr/sbin/sshd -t -f %s + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-90811-1 + - CJIS-5.5.6 + - NIST-800-171-3.1.11 + - NIST-800-53-AC-12 + - NIST-800-53-AC-17(a) + - NIST-800-53-AC-17(a) + - NIST-800-53-AC-2(5) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-6(a) + - NIST-800-53-SC-10 + - PCI-DSS-Req-8.1.8 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sshd_set_idle_timeout + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +sshd_idle_timeout_value='' + + +if [ -e "/etc/ssh/sshd_config" ] ; then + + LC_ALL=C sed -i "/^\s*ClientAliveInterval\s\+/Id" "/etc/ssh/sshd_config" +else + touch "/etc/ssh/sshd_config" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/ssh/sshd_config" + +cp "/etc/ssh/sshd_config" "/etc/ssh/sshd_config.bak" +# Insert before the line matching the regex '^Match'. +line_number="$(LC_ALL=C grep -n "^Match" "/etc/ssh/sshd_config.bak" | LC_ALL=C sed 's/:.*//g')" +if [ -z "$line_number" ]; then + # There was no match of '^Match', insert at + # the end of the file. + printf '%s\n' "ClientAliveInterval $sshd_idle_timeout_value" >> "/etc/ssh/sshd_config" +else + head -n "$(( line_number - 1 ))" "/etc/ssh/sshd_config.bak" > "/etc/ssh/sshd_config" + printf '%s\n' "ClientAliveInterval $sshd_idle_timeout_value" >> "/etc/ssh/sshd_config" + tail -n "+$(( line_number ))" "/etc/ssh/sshd_config.bak" >> "/etc/ssh/sshd_config" +fi +# Clean up after ourselves. +rm "/etc/ssh/sshd_config.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + + Disable Host-Based Authentication + SSH's cryptographic host-based authentication is +more secure than .rhosts authentication. However, it is +not recommended that hosts unilaterally trust one another, even +within an organization. + +The default SSH configuration disables host-based authentication. The appropriate +configuration is used if no value is set for HostbasedAuthentication. + +To explicitly disable host-based authentication, add or correct the +following line in + + +/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf: + +HostbasedAuthentication no + 11 + 12 + 14 + 15 + 16 + 18 + 3 + 5 + 9 + 5.5.6 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + DSS06.03 + DSS06.06 + 3.1.12 + CCI-000366 + 164.308(a)(4)(i) + 164.308(b)(1) + 164.308(b)(3) + 164.310(b) + 164.312(e)(1) + 164.312(e)(2)(ii) + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + 0421 + 0422 + 0431 + 0974 + 1173 + 1401 + 1504 + 1505 + 1546 + 1557 + 1558 + 1559 + 1560 + 1561 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.6.1.2 + A.7.1.1 + A.9.1.2 + A.9.2.1 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.2.3 + CIP-004-6 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.2 + CIP-007-3 R5.2 + CIP-007-3 R5.3.1 + CIP-007-3 R5.3.2 + CIP-007-3 R5.3.3 + AC-3 + AC-17(a) + CM-7(a) + CM-7(b) + CM-6(a) + PR.AC-4 + PR.AC-6 + PR.IP-1 + PR.PT-3 + FIA_UAU.1 + SRG-OS-000480-GPOS-00229 + SRG-OS-000480-VMM-002000 + SSH trust relationships mean a compromise on one host +can allow an attacker to move trivially to other hosts. + CCE-90816-0 + - name: Disable Host-Based Authentication + block: + + - name: Deduplicate values from /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*{{ "HostbasedAuthentication"| regex_escape }}\s+ + state: absent + + - name: Check if /etc/ssh/sshd_config.d exists + stat: + path: /etc/ssh/sshd_config.d + register: _etc_ssh_sshd_config_d_exists + + - name: Check if the parameter HostbasedAuthentication is present in /etc/ssh/sshd_config.d + find: + paths: /etc/ssh/sshd_config.d + recurse: 'yes' + follow: 'no' + contains: (?i)^\s*{{ "HostbasedAuthentication"| regex_escape }}\s+ + register: _etc_ssh_sshd_config_d_has_parameter + when: _etc_ssh_sshd_config_d_exists.stat.isdir is defined and _etc_ssh_sshd_config_d_exists.stat.isdir + + - name: Remove parameter from files in /etc/ssh/sshd_config.d + lineinfile: + path: '{{ item.path }}' + create: false + regexp: (?i)^\s*{{ "HostbasedAuthentication"| regex_escape }}\s+ + state: absent + with_items: '{{ _etc_ssh_sshd_config_d_has_parameter.files }}' + when: _etc_ssh_sshd_config_d_has_parameter.matched + + - name: Insert correct line to /etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf + lineinfile: + path: /etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf + create: true + regexp: (?i)^\s*{{ "HostbasedAuthentication"| regex_escape }}\s+ + line: HostbasedAuthentication no + state: present + insertbefore: ^[#\s]*Match + validate: /usr/sbin/sshd -t -f %s + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-90816-0 + - CJIS-5.5.6 + - NIST-800-171-3.1.12 + - NIST-800-53-AC-17(a) + - NIST-800-53-AC-3 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_host_auth + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,%23%09%24OpenBSD%3A%20sshd_config%2Cv%201.103%202018%2F04%2F09%2020%3A41%3A22%20tj%20Exp%20%24%0A%0A%23%20This%20is%20the%20sshd%20server%20system-wide%20configuration%20file.%20%20See%0A%23%20sshd_config%285%29%20for%20more%20information.%0A%0A%23%20This%20sshd%20was%20compiled%20with%20PATH%3D%2Fusr%2Flocal%2Fbin%3A%2Fusr%2Fbin%3A%2Fusr%2Flocal%2Fsbin%3A%2Fusr%2Fsbin%0A%0A%23%20The%20strategy%20used%20for%20options%20in%20the%20default%20sshd_config%20shipped%20with%0A%23%20OpenSSH%20is%20to%20specify%20options%20with%20their%20default%20value%20where%0A%23%20possible%2C%20but%20leave%20them%20commented.%20%20Uncommented%20options%20override%20the%0A%23%20default%20value.%0A%0A%23%20If%20you%20want%20to%20change%20the%20port%20on%20a%20SELinux%20system%2C%20you%20have%20to%20tell%0A%23%20SELinux%20about%20this%20change.%0A%23%20semanage%20port%20-a%20-t%20ssh_port_t%20-p%20tcp%20%23PORTNUMBER%0A%23%0A%23Port%2022%0A%23AddressFamily%20any%0A%23ListenAddress%200.0.0.0%0A%23ListenAddress%20%3A%3A%0A%0AHostKey%20%2Fetc%2Fssh%2Fssh_host_rsa_key%0AHostKey%20%2Fetc%2Fssh%2Fssh_host_ecdsa_key%0AHostKey%20%2Fetc%2Fssh%2Fssh_host_ed25519_key%0A%0A%23%20Ciphers%20and%20keying%0ARekeyLimit%20512M%201h%0A%0A%23%20System-wide%20Crypto%20policy%3A%0A%23%20This%20system%20is%20following%20system-wide%20crypto%20policy.%20The%20changes%20to%0A%23%20Ciphers%2C%20MACs%2C%20KexAlgoritms%20and%20GSSAPIKexAlgorithsm%20will%20not%20have%20any%0A%23%20effect%20here.%20They%20will%20be%20overridden%20by%20command-line%20options%20passed%20on%0A%23%20the%20server%20start%20up.%0A%23%20To%20opt%20out%2C%20uncomment%20a%20line%20with%20redefinition%20of%20%20CRYPTO_POLICY%3D%0A%23%20variable%20in%20%20%2Fetc%2Fsysconfig%2Fsshd%20%20to%20overwrite%20the%20policy.%0A%23%20For%20more%20information%2C%20see%20manual%20page%20for%20update-crypto-policies%288%29.%0A%0A%23%20Logging%0A%23SyslogFacility%20AUTH%0ASyslogFacility%20AUTHPRIV%0A%23LogLevel%20INFO%0A%0A%23%20Authentication%3A%0A%0A%23LoginGraceTime%202m%0APermitRootLogin%20no%0AStrictModes%20yes%0A%23MaxAuthTries%206%0A%23MaxSessions%2010%0A%0APubkeyAuthentication%20yes%0A%0A%23%20The%20default%20is%20to%20check%20both%20.ssh%2Fauthorized_keys%20and%20.ssh%2Fauthorized_keys2%0A%23%20but%20this%20is%20overridden%20so%20installations%20will%20only%20check%20.ssh%2Fauthorized_keys%0AAuthorizedKeysFile%09.ssh%2Fauthorized_keys%0A%0A%23AuthorizedPrincipalsFile%20none%0A%0A%23AuthorizedKeysCommand%20none%0A%23AuthorizedKeysCommandUser%20nobody%0A%0A%23%20For%20this%20to%20work%20you%20will%20also%20need%20host%20keys%20in%20%2Fetc%2Fssh%2Fssh_known_hosts%0AHostbasedAuthentication%20no%0A%23%20Change%20to%20yes%20if%20you%20don%27t%20trust%20~%2F.ssh%2Fknown_hosts%20for%0A%23%20HostbasedAuthentication%0AIgnoreUserKnownHosts%20yes%0A%23%20Don%27t%20read%20the%20user%27s%20~%2F.rhosts%20and%20~%2F.shosts%20files%0AIgnoreRhosts%20yes%0A%0A%23%20To%20disable%20tunneled%20clear%20text%20passwords%2C%20change%20to%20no%20here%21%0A%23PasswordAuthentication%20yes%0APermitEmptyPasswords%20no%0APasswordAuthentication%20no%0A%0A%23%20Change%20to%20no%20to%20disable%20s%2Fkey%20passwords%0A%23ChallengeResponseAuthentication%20yes%0AChallengeResponseAuthentication%20no%0A%0A%23%20Kerberos%20options%0AKerberosAuthentication%20no%0A%23KerberosOrLocalPasswd%20yes%0A%23KerberosTicketCleanup%20yes%0A%23KerberosGetAFSToken%20no%0A%23KerberosUseKuserok%20yes%0A%0A%23%20GSSAPI%20options%0AGSSAPIAuthentication%20no%0AGSSAPICleanupCredentials%20no%0A%23GSSAPIStrictAcceptorCheck%20yes%0A%23GSSAPIKeyExchange%20no%0A%23GSSAPIEnablek5users%20no%0A%0A%23%20Set%20this%20to%20%27yes%27%20to%20enable%20PAM%20authentication%2C%20account%20processing%2C%0A%23%20and%20session%20processing.%20If%20this%20is%20enabled%2C%20PAM%20authentication%20will%0A%23%20be%20allowed%20through%20the%20ChallengeResponseAuthentication%20and%0A%23%20PasswordAuthentication.%20%20Depending%20on%20your%20PAM%20configuration%2C%0A%23%20PAM%20authentication%20via%20ChallengeResponseAuthentication%20may%20bypass%0A%23%20the%20setting%20of%20%22PermitRootLogin%20without-password%22.%0A%23%20If%20you%20just%20want%20the%20PAM%20account%20and%20session%20checks%20to%20run%20without%0A%23%20PAM%20authentication%2C%20then%20enable%20this%20but%20set%20PasswordAuthentication%0A%23%20and%20ChallengeResponseAuthentication%20to%20%27no%27.%0A%23%20WARNING%3A%20%27UsePAM%20no%27%20is%20not%20supported%20in%20Fedora%20and%20may%20cause%20several%0A%23%20problems.%0AUsePAM%20yes%0A%0A%23AllowAgentForwarding%20yes%0A%23AllowTcpForwarding%20yes%0A%23GatewayPorts%20no%0AX11Forwarding%20yes%0A%23X11DisplayOffset%2010%0A%23X11UseLocalhost%20yes%0A%23PermitTTY%20yes%0A%0A%23%20It%20is%20recommended%20to%20use%20pam_motd%20in%20%2Fetc%2Fpam.d%2Fsshd%20instead%20of%20PrintMotd%2C%0A%23%20as%20it%20is%20more%20configurable%20and%20versatile%20than%20the%20built-in%20version.%0APrintMotd%20no%0A%0APrintLastLog%20yes%0A%23TCPKeepAlive%20yes%0APermitUserEnvironment%20no%0ACompression%20no%0AClientAliveInterval%20600%0AClientAliveCountMax%200%0A%23UseDNS%20no%0A%23PidFile%20%2Fvar%2Frun%2Fsshd.pid%0A%23MaxStartups%2010%3A30%3A100%0A%23PermitTunnel%20no%0A%23ChrootDirectory%20none%0A%23VersionAddendum%20none%0A%0A%23%20no%20default%20banner%20path%0ABanner%20%2Fetc%2Fissue%0A%0A%23%20Accept%20locale-related%20environment%20variables%0AAcceptEnv%20LANG%20LC_CTYPE%20LC_NUMERIC%20LC_TIME%20LC_COLLATE%20LC_MONETARY%20LC_MESSAGES%0AAcceptEnv%20LC_PAPER%20LC_NAME%20LC_ADDRESS%20LC_TELEPHONE%20LC_MEASUREMENT%0AAcceptEnv%20LC_IDENTIFICATION%20LC_ALL%20LANGUAGE%0AAcceptEnv%20XMODIFIERS%0A%0A%23%20override%20default%20of%20no%20subsystems%0ASubsystem%09sftp%09%2Fusr%2Flibexec%2Fopenssh%2Fsftp-server%0A%0A%23%20Example%20of%20overriding%20settings%20on%20a%20per-user%20basis%0A%23Match%20User%20anoncvs%0A%23%09X11Forwarding%20no%0A%23%09AllowTcpForwarding%20no%0A%23%09PermitTTY%20no%0A%23%09ForceCommand%20cvs%20server%0A%0AUsePrivilegeSeparation%20sandbox + mode: 0600 + path: /etc/ssh/sshd_config + overwrite: true + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +mkdir -p /etc/ssh/sshd_config.d +touch /etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf + +LC_ALL=C sed -i "/^\s*HostbasedAuthentication\s\+/Id" "/etc/ssh/sshd_config" +LC_ALL=C sed -i "/^\s*HostbasedAuthentication\s\+/Id" "/etc/ssh/sshd_config.d"/*.conf +if [ -e "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" ] ; then + + LC_ALL=C sed -i "/^\s*HostbasedAuthentication\s\+/Id" "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" +else + touch "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" + +cp "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf.bak" +# Insert before the line matching the regex '^Match'. +line_number="$(LC_ALL=C grep -n "^Match" "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf.bak" | LC_ALL=C sed 's/:.*//g')" +if [ -z "$line_number" ]; then + # There was no match of '^Match', insert at + # the end of the file. + printf '%s\n' "HostbasedAuthentication no" >> "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" +else + head -n "$(( line_number - 1 ))" "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf.bak" > "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" + printf '%s\n' "HostbasedAuthentication no" >> "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" + tail -n "+$(( line_number ))" "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf.bak" >> "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" +fi +# Clean up after ourselves. +rm "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable SSH Server firewalld Firewall Exception + By default, inbound connections to SSH's port are allowed. If +the SSH server is being used but denied by the firewall, this exception should +be added to the firewall configuration. + + + + +To configure firewalld to allow ssh access, run the following command(s): +firewall-cmd --permanent --add-service=ssh + +Then run the following command to load the newly created rule(s): +firewall-cmd --reload + 3.1.12 + 1416 + AC-17(a) + CM-6(b) + CM-7(a) + CM-7(b) + SRG-OS-000096-GPOS-00050 + If inbound SSH connections are expected, adding a firewall rule exception +will allow remote access through the SSH port. + CCE-89175-4 + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if ! rpm -q --quiet "firewalld" ; then + dnf install -y "firewalld" +fi + +firewalld_sshd_zone='' + + + + + + +# This assumes that firewalld_sshd_zone is one of the pre-defined zones +if [ ! -f "/etc/firewalld/zones/${firewalld_sshd_zone}.xml" ]; then + cp "/usr/lib/firewalld/zones/${firewalld_sshd_zone}.xml" "/etc/firewalld/zones/${firewalld_sshd_zone}.xml" +fi +if ! grep -q 'service name="ssh"' "/etc/firewalld/zones/${firewalld_sshd_zone}.xml"; then + sed -i '/<\/description>/a \ + <service name="ssh"/>' "/etc/firewalld/zones/${firewalld_sshd_zone}.xml" +fi + +# Check if any eth interface is bounded to the zone with SSH service enabled +nic_bound=false +readarray -t eth_interface_list < <(ip link show up | cut -d ' ' -f2 | cut -d ':' -s -f1 | grep -E '^(en|eth)') +for interface in "${eth_interface_list[@]}"; do + if grep -qi "ZONE=$firewalld_sshd_zone" "/etc/NetworkManager/system-connections/${interface}.nmconnection"; then + nic_bound=true + break; + fi +done + +if [ $nic_bound = false ];then + # Add first NIC to SSH enabled zone + interface="${eth_interface_list[0]}" + + if ! firewall-cmd --state -q; then + + # Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. + # Otherwise, regular sed command will do. + sed_command=('sed' '-i') + if test -L "/etc/NetworkManager/system-connections/${interface}.nmconnection"; then + sed_command+=('--follow-symlinks') + fi + + # Strip any search characters in the key arg so that the key can be replaced without + # adding any search characters to the config file. + stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^zone=") + + # shellcheck disable=SC2059 + printf -v formatted_output "%s=%s" "$stripped_key" "$firewalld_sshd_zone" + + # If the key exists, change it. Otherwise, add it to the config_file. + # We search for the key string followed by a word boundary (matched by \>), + # so if we search for 'setting', 'setting2' won't match. + if LC_ALL=C grep -q -m 1 -i -e "^zone=\\>" "/etc/NetworkManager/system-connections/${interface}.nmconnection"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^zone=\\>.*/$escaped_formatted_output/gi" "/etc/NetworkManager/system-connections/${interface}.nmconnection" + else + # \n is precaution for case where file ends without trailing newline + cce="CCE-89175-4" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/NetworkManager/system-connections/${interface}.nmconnection" >> "/etc/NetworkManager/system-connections/${interface}.nmconnection" + printf '%s\n' "$formatted_output" >> "/etc/NetworkManager/system-connections/${interface}.nmconnection" + fi + + else + # If firewalld service is running, we need to do this step with firewall-cmd + # Otherwise firewalld will communicate with NetworkManage and will revert assigned zone + # of NetworkManager managed interfaces upon reload + firewall-cmd --permanent --zone="$firewalld_sshd_zone" --add-interface="${eth_interface_list[0]}" + firewall-cmd --reload + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Allow Only SSH Protocol 2 + Only SSH protocol version 2 connections should be +permitted. The default setting in +/etc/ssh/sshd_config is correct, and can be +verified by ensuring that the following +line appears: +Protocol 2 + As of openssh-server version 7.4 and above, the only protocol +supported is version 2, and line Protocol 2 in +/etc/ssh/sshd_config is not necessary. + NT007(R1) + 1 + 12 + 15 + 16 + 5 + 8 + 5.5.6 + APO13.01 + DSS01.04 + DSS05.02 + DSS05.03 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.03 + DSS06.10 + 3.1.13 + 3.5.4 + CCI-000197 + CCI-000366 + 164.308(a)(4)(i) + 164.308(b)(1) + 164.308(b)(3) + 164.310(b) + 164.312(e)(1) + 164.312(e)(2)(ii) + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.2 + 4.3.3.7.4 + SR 1.1 + SR 1.10 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.6 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + 0487 + 1449 + 1506 + A.11.2.6 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.18.1.4 + A.6.2.1 + A.6.2.2 + A.7.1.1 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.2 + A.9.4.3 + CIP-003-8 R4.2 + CIP-007-3 R5.1 + CIP-007-3 R7.1 + CM-6(a) + AC-17(a) + AC-17(2) + IA-5(1)(c) + SC-13 + MA-4(6) + PR.AC-1 + PR.AC-3 + PR.AC-6 + PR.AC-7 + PR.PT-4 + SRG-OS-000074-GPOS-00042 + SRG-OS-000480-GPOS-00227 + SRG-OS-000033-VMM-000140 + SSH protocol version 1 is an insecure implementation of the SSH protocol and +has many well-known vulnerability exploits. Exploits of the SSH daemon could provide +immediate root access to the system. + CCE-90812-9 + - name: Allow Only SSH Protocol 2 + block: + + - name: Check for duplicate values + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*Protocol\s+ + state: absent + check_mode: true + changed_when: false + register: dupes + + - name: Deduplicate values from /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*Protocol\s+ + state: absent + when: dupes.found is defined and dupes.found > 1 + + - name: Insert correct line to /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: true + regexp: (?i)^\s*Protocol\s+ + line: Protocol 2 + state: present + insertbefore: ^[#\s]*Match + validate: /usr/sbin/sshd -t -f %s + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-90812-9 + - CJIS-5.5.6 + - NIST-800-171-3.1.13 + - NIST-800-171-3.5.4 + - NIST-800-53-AC-17(2) + - NIST-800-53-AC-17(a) + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(1)(c) + - NIST-800-53-MA-4(6) + - NIST-800-53-SC-13 + - high_severity + - low_complexity + - low_disruption + - no_reboot_needed + - restrict_strategy + - sshd_allow_only_protocol2 + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/ssh/sshd_config"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^Protocol") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s %s" "$stripped_key" "2" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^Protocol\\>" "/etc/ssh/sshd_config"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^Protocol\\>.*/$escaped_formatted_output/gi" "/etc/ssh/sshd_config" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-90812-9" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/ssh/sshd_config" >> "/etc/ssh/sshd_config" + printf '%s\n' "$formatted_output" >> "/etc/ssh/sshd_config" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable Compression Or Set Compression to delayed + Compression is useful for slow network connections over long +distances but can cause performance issues on local LANs. If use of compression +is required, it should be enabled only after a user has authenticated; otherwise, +it should be disabled. To disable compression or delay compression until after +a user has successfully authenticated, add or correct the following line in the +/etc/ssh/sshd_config file: +Compression + 11 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + 3.1.12 + CCI-000366 + 164.308(a)(4)(i) + 164.308(b)(1) + 164.308(b)(3) + 164.310(b) + 164.312(e)(1) + 164.312(e)(2)(ii) + 4.3.4.3.2 + 4.3.4.3.3 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + AC-17(a) + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + SRG-OS-000480-GPOS-00227 + SRG-OS-000480-VMM-002000 + If compression is allowed in an SSH connection prior to authentication, +vulnerabilities in the compression software could result in compromise of the +system from an unauthenticated connection, potentially with root privileges. + CCE-90801-2 + - name: XCCDF Value var_sshd_disable_compression # promote to variable + set_fact: + var_sshd_disable_compression: !!str + tags: + - always + +- name: Disable Compression Or Set Compression to delayed + block: + + - name: Check for duplicate values + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*Compression\s+ + state: absent + check_mode: true + changed_when: false + register: dupes + + - name: Deduplicate values from /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*Compression\s+ + state: absent + when: dupes.found is defined and dupes.found > 1 + + - name: Insert correct line to /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: true + regexp: (?i)^\s*Compression\s+ + line: Compression {{ var_sshd_disable_compression }} + state: present + insertbefore: ^[#\s]*Match + validate: /usr/sbin/sshd -t -f %s + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-90801-2 + - NIST-800-171-3.1.12 + - NIST-800-53-AC-17(a) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sshd_disable_compression + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_sshd_disable_compression='' + + +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/ssh/sshd_config"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^Compression") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s %s" "$stripped_key" "$var_sshd_disable_compression" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^Compression\\>" "/etc/ssh/sshd_config"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^Compression\\>.*/$escaped_formatted_output/gi" "/etc/ssh/sshd_config" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-90801-2" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/ssh/sshd_config" >> "/etc/ssh/sshd_config" + printf '%s\n' "$formatted_output" >> "/etc/ssh/sshd_config" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + + Disable SSH Access via Empty Passwords + Disallow SSH login with empty passwords. +The default SSH configuration disables logins with empty passwords. The appropriate +configuration is used if no value is set for PermitEmptyPasswords. + +To explicitly disallow SSH login from accounts with empty passwords, +add or correct the following line in + + +/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf: + + +PermitEmptyPasswords no +Any accounts with empty passwords should be disabled immediately, and PAM configuration +should prevent users from being able to assign themselves empty passwords. + NT007(R17) + 11 + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + 9 + 5.5.6 + APO01.06 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + DSS06.02 + DSS06.03 + DSS06.06 + 3.1.1 + 3.1.5 + CCI-000366 + CCI-000766 + 164.308(a)(4)(i) + 164.308(b)(1) + 164.308(b)(3) + 164.310(b) + 164.312(e)(1) + 164.312(e)(2)(ii) + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 5.2 + SR 7.6 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.1 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + AC-17(a) + CM-7(a) + CM-7(b) + CM-6(a) + PR.AC-4 + PR.AC-6 + PR.DS-5 + PR.IP-1 + PR.PT-3 + FIA_UAU.1 + SRG-OS-000106-GPOS-00053 + SRG-OS-000480-GPOS-00229 + SRG-OS-000480-GPOS-00227 + SRG-OS-000480-VMM-002000 + Configuring this setting for the SSH daemon provides additional assurance +that remote login via SSH will require a password, even in the event of +misconfiguration elsewhere. + CCE-90799-8 + - name: Disable SSH Access via Empty Passwords + block: + + - name: Deduplicate values from /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*{{ "PermitEmptyPasswords"| regex_escape }}\s+ + state: absent + + - name: Check if /etc/ssh/sshd_config.d exists + stat: + path: /etc/ssh/sshd_config.d + register: _etc_ssh_sshd_config_d_exists + + - name: Check if the parameter PermitEmptyPasswords is present in /etc/ssh/sshd_config.d + find: + paths: /etc/ssh/sshd_config.d + recurse: 'yes' + follow: 'no' + contains: (?i)^\s*{{ "PermitEmptyPasswords"| regex_escape }}\s+ + register: _etc_ssh_sshd_config_d_has_parameter + when: _etc_ssh_sshd_config_d_exists.stat.isdir is defined and _etc_ssh_sshd_config_d_exists.stat.isdir + + - name: Remove parameter from files in /etc/ssh/sshd_config.d + lineinfile: + path: '{{ item.path }}' + create: false + regexp: (?i)^\s*{{ "PermitEmptyPasswords"| regex_escape }}\s+ + state: absent + with_items: '{{ _etc_ssh_sshd_config_d_has_parameter.files }}' + when: _etc_ssh_sshd_config_d_has_parameter.matched + + - name: Insert correct line to /etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf + lineinfile: + path: /etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf + create: true + regexp: (?i)^\s*{{ "PermitEmptyPasswords"| regex_escape }}\s+ + line: PermitEmptyPasswords no + state: present + insertbefore: ^[#\s]*Match + validate: /usr/sbin/sshd -t -f %s + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-90799-8 + - CJIS-5.5.6 + - NIST-800-171-3.1.1 + - NIST-800-171-3.1.5 + - NIST-800-53-AC-17(a) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - high_severity + - low_complexity + - low_disruption + - no_reboot_needed + - restrict_strategy + - sshd_disable_empty_passwords + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +mkdir -p /etc/ssh/sshd_config.d +touch /etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf + +LC_ALL=C sed -i "/^\s*PermitEmptyPasswords\s\+/Id" "/etc/ssh/sshd_config" +LC_ALL=C sed -i "/^\s*PermitEmptyPasswords\s\+/Id" "/etc/ssh/sshd_config.d"/*.conf +if [ -e "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" ] ; then + + LC_ALL=C sed -i "/^\s*PermitEmptyPasswords\s\+/Id" "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" +else + touch "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" + +cp "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf.bak" +# Insert before the line matching the regex '^Match'. +line_number="$(LC_ALL=C grep -n "^Match" "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf.bak" | LC_ALL=C sed 's/:.*//g')" +if [ -z "$line_number" ]; then + # There was no match of '^Match', insert at + # the end of the file. + printf '%s\n' "PermitEmptyPasswords no" >> "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" +else + head -n "$(( line_number - 1 ))" "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf.bak" > "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" + printf '%s\n' "PermitEmptyPasswords no" >> "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" + tail -n "+$(( line_number ))" "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf.bak" >> "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" +fi +# Clean up after ourselves. +rm "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable GSSAPI Authentication + Unless needed, SSH should not permit extraneous or unnecessary +authentication mechanisms like GSSAPI. + +The default SSH configuration disallows authentications based on GSSAPI. The appropriate +configuration is used if no value is set for GSSAPIAuthentication. + +To explicitly disable GSSAPI authentication, add or correct the following line in + + +/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf: + +GSSAPIAuthentication no + 11 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + 3.1.12 + CCI-000318 + CCI-000368 + CCI-001812 + CCI-001813 + CCI-001814 + CCI-000366 + 164.308(a)(4)(i) + 164.308(b)(1) + 164.308(b)(3) + 164.310(b) + 164.312(e)(1) + 164.312(e)(2)(ii) + 4.3.4.3.2 + 4.3.4.3.3 + SR 7.6 + 0418 + 1055 + 1402 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + CM-7(a) + CM-7(b) + CM-6(a) + AC-17(a) + PR.IP-1 + FTP_ITC_EXT.1 + FCS_SSH_EXT.1.2 + SRG-OS-000364-GPOS-00151 + SRG-OS-000480-GPOS-00227 + SRG-OS-000480-VMM-002000 + GSSAPI authentication is used to provide additional authentication mechanisms to +applications. Allowing GSSAPI authentication through SSH exposes the system's +GSSAPI to remote hosts, increasing the attack surface of the system. + CCE-90808-7 + - name: Disable GSSAPI Authentication + block: + + - name: Deduplicate values from /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*{{ "GSSAPIAuthentication"| regex_escape }}\s+ + state: absent + + - name: Check if /etc/ssh/sshd_config.d exists + stat: + path: /etc/ssh/sshd_config.d + register: _etc_ssh_sshd_config_d_exists + + - name: Check if the parameter GSSAPIAuthentication is present in /etc/ssh/sshd_config.d + find: + paths: /etc/ssh/sshd_config.d + recurse: 'yes' + follow: 'no' + contains: (?i)^\s*{{ "GSSAPIAuthentication"| regex_escape }}\s+ + register: _etc_ssh_sshd_config_d_has_parameter + when: _etc_ssh_sshd_config_d_exists.stat.isdir is defined and _etc_ssh_sshd_config_d_exists.stat.isdir + + - name: Remove parameter from files in /etc/ssh/sshd_config.d + lineinfile: + path: '{{ item.path }}' + create: false + regexp: (?i)^\s*{{ "GSSAPIAuthentication"| regex_escape }}\s+ + state: absent + with_items: '{{ _etc_ssh_sshd_config_d_has_parameter.files }}' + when: _etc_ssh_sshd_config_d_has_parameter.matched + + - name: Insert correct line to /etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf + lineinfile: + path: /etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf + create: true + regexp: (?i)^\s*{{ "GSSAPIAuthentication"| regex_escape }}\s+ + line: GSSAPIAuthentication no + state: present + insertbefore: ^[#\s]*Match + validate: /usr/sbin/sshd -t -f %s + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-90808-7 + - NIST-800-171-3.1.12 + - NIST-800-53-AC-17(a) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sshd_disable_gssapi_auth + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +mkdir -p /etc/ssh/sshd_config.d +touch /etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf + +LC_ALL=C sed -i "/^\s*GSSAPIAuthentication\s\+/Id" "/etc/ssh/sshd_config" +LC_ALL=C sed -i "/^\s*GSSAPIAuthentication\s\+/Id" "/etc/ssh/sshd_config.d"/*.conf +if [ -e "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" ] ; then + + LC_ALL=C sed -i "/^\s*GSSAPIAuthentication\s\+/Id" "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" +else + touch "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" + +cp "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf.bak" +# Insert before the line matching the regex '^Match'. +line_number="$(LC_ALL=C grep -n "^Match" "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf.bak" | LC_ALL=C sed 's/:.*//g')" +if [ -z "$line_number" ]; then + # There was no match of '^Match', insert at + # the end of the file. + printf '%s\n' "GSSAPIAuthentication no" >> "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" +else + head -n "$(( line_number - 1 ))" "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf.bak" > "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" + printf '%s\n' "GSSAPIAuthentication no" >> "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" + tail -n "+$(( line_number ))" "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf.bak" >> "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" +fi +# Clean up after ourselves. +rm "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable Kerberos Authentication + Unless needed, SSH should not permit extraneous or unnecessary +authentication mechanisms like Kerberos. + +The default SSH configuration disallows authentication validation through Kerberos. +The appropriate configuration is used if no value is set for KerberosAuthentication. + +To explicitly disable Kerberos authentication, add or correct the following line in + + +/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf: + +KerberosAuthentication no + 11 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + 3.1.12 + CCI-000318 + CCI-000368 + CCI-001812 + CCI-001813 + CCI-001814 + CCI-000366 + 164.308(a)(4)(i) + 164.308(b)(1) + 164.308(b)(3) + 164.310(b) + 164.312(e)(1) + 164.312(e)(2)(ii) + 4.3.4.3.2 + 4.3.4.3.3 + SR 7.6 + 0421 + 0422 + 0431 + 0974 + 1173 + 1401 + 1504 + 1505 + 1546 + 1557 + 1558 + 1559 + 1560 + 1561 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + AC-17(a) + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + FTP_ITC_EXT.1 + FCS_SSH_EXT.1.2 + SRG-OS-000364-GPOS-00151 + SRG-OS-000480-GPOS-00227 + SRG-OS-000480-VMM-002000 + Kerberos authentication for SSH is often implemented using GSSAPI. If Kerberos +is enabled through SSH, the SSH daemon provides a means of access to the +system's Kerberos implementation. +Configuring these settings for the SSH daemon provides additional assurance that remote logon via SSH will not use unused methods of authentication, even in the event of misconfiguration elsewhere. + CCE-90802-0 + - name: Disable Kerberos Authentication + block: + + - name: Deduplicate values from /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*{{ "KerberosAuthentication"| regex_escape }}\s+ + state: absent + + - name: Check if /etc/ssh/sshd_config.d exists + stat: + path: /etc/ssh/sshd_config.d + register: _etc_ssh_sshd_config_d_exists + + - name: Check if the parameter KerberosAuthentication is present in /etc/ssh/sshd_config.d + find: + paths: /etc/ssh/sshd_config.d + recurse: 'yes' + follow: 'no' + contains: (?i)^\s*{{ "KerberosAuthentication"| regex_escape }}\s+ + register: _etc_ssh_sshd_config_d_has_parameter + when: _etc_ssh_sshd_config_d_exists.stat.isdir is defined and _etc_ssh_sshd_config_d_exists.stat.isdir + + - name: Remove parameter from files in /etc/ssh/sshd_config.d + lineinfile: + path: '{{ item.path }}' + create: false + regexp: (?i)^\s*{{ "KerberosAuthentication"| regex_escape }}\s+ + state: absent + with_items: '{{ _etc_ssh_sshd_config_d_has_parameter.files }}' + when: _etc_ssh_sshd_config_d_has_parameter.matched + + - name: Insert correct line to /etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf + lineinfile: + path: /etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf + create: true + regexp: (?i)^\s*{{ "KerberosAuthentication"| regex_escape }}\s+ + line: KerberosAuthentication no + state: present + insertbefore: ^[#\s]*Match + validate: /usr/sbin/sshd -t -f %s + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-90802-0 + - NIST-800-171-3.1.12 + - NIST-800-53-AC-17(a) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sshd_disable_kerb_auth + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +mkdir -p /etc/ssh/sshd_config.d +touch /etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf + +LC_ALL=C sed -i "/^\s*KerberosAuthentication\s\+/Id" "/etc/ssh/sshd_config" +LC_ALL=C sed -i "/^\s*KerberosAuthentication\s\+/Id" "/etc/ssh/sshd_config.d"/*.conf +if [ -e "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" ] ; then + + LC_ALL=C sed -i "/^\s*KerberosAuthentication\s\+/Id" "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" +else + touch "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" + +cp "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf.bak" +# Insert before the line matching the regex '^Match'. +line_number="$(LC_ALL=C grep -n "^Match" "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf.bak" | LC_ALL=C sed 's/:.*//g')" +if [ -z "$line_number" ]; then + # There was no match of '^Match', insert at + # the end of the file. + printf '%s\n' "KerberosAuthentication no" >> "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" +else + head -n "$(( line_number - 1 ))" "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf.bak" > "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" + printf '%s\n' "KerberosAuthentication no" >> "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" + tail -n "+$(( line_number ))" "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf.bak" >> "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" +fi +# Clean up after ourselves. +rm "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable PubkeyAuthentication Authentication + Unless needed, SSH should not permit extraneous or unnecessary +authentication mechanisms. To disable PubkeyAuthentication authentication, add or +correct the following line in + + +/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf: + +PubkeyAuthentication no + PubkeyAuthentication authentication is used to provide additional authentication mechanisms to +applications. Allowing PubkeyAuthentication authentication through SSH allows users to +generate their own authentication tokens, increasing the attack surface of the system. + - name: Disable PubkeyAuthentication Authentication + block: + + - name: Deduplicate values from /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*{{ "PubkeyAuthentication"| regex_escape }}\s+ + state: absent + + - name: Check if /etc/ssh/sshd_config.d exists + stat: + path: /etc/ssh/sshd_config.d + register: _etc_ssh_sshd_config_d_exists + + - name: Check if the parameter PubkeyAuthentication is present in /etc/ssh/sshd_config.d + find: + paths: /etc/ssh/sshd_config.d + recurse: 'yes' + follow: 'no' + contains: (?i)^\s*{{ "PubkeyAuthentication"| regex_escape }}\s+ + register: _etc_ssh_sshd_config_d_has_parameter + when: _etc_ssh_sshd_config_d_exists.stat.isdir is defined and _etc_ssh_sshd_config_d_exists.stat.isdir + + - name: Remove parameter from files in /etc/ssh/sshd_config.d + lineinfile: + path: '{{ item.path }}' + create: false + regexp: (?i)^\s*{{ "PubkeyAuthentication"| regex_escape }}\s+ + state: absent + with_items: '{{ _etc_ssh_sshd_config_d_has_parameter.files }}' + when: _etc_ssh_sshd_config_d_has_parameter.matched + + - name: Insert correct line to /etc/ssh/sshd_config.d/00-complianceascode-hardening.conf + lineinfile: + path: /etc/ssh/sshd_config.d/00-complianceascode-hardening.conf + create: true + regexp: (?i)^\s*{{ "PubkeyAuthentication"| regex_escape }}\s+ + line: PubkeyAuthentication no + state: present + insertbefore: ^[#\s]*Match + validate: /usr/sbin/sshd -t -f %s + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sshd_disable_pubkey_auth + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +mkdir -p /etc/ssh/sshd_config.d +touch /etc/ssh/sshd_config.d/00-complianceascode-hardening.conf + +LC_ALL=C sed -i "/^\s*PubkeyAuthentication\s\+/Id" "/etc/ssh/sshd_config" +LC_ALL=C sed -i "/^\s*PubkeyAuthentication\s\+/Id" "/etc/ssh/sshd_config.d"/*.conf +if [ -e "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" ] ; then + + LC_ALL=C sed -i "/^\s*PubkeyAuthentication\s\+/Id" "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" +else + touch "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" + +cp "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" +# Insert before the line matching the regex '^Match'. +line_number="$(LC_ALL=C grep -n "^Match" "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" | LC_ALL=C sed 's/:.*//g')" +if [ -z "$line_number" ]; then + # There was no match of '^Match', insert at + # the end of the file. + printf '%s\n' "PubkeyAuthentication no" >> "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" +else + head -n "$(( line_number - 1 ))" "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" > "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" + printf '%s\n' "PubkeyAuthentication no" >> "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" + tail -n "+$(( line_number ))" "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" >> "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" +fi +# Clean up after ourselves. +rm "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable SSH Support for .rhosts Files + SSH can emulate the behavior of the obsolete rsh +command in allowing users to enable insecure access to their +accounts via .rhosts files. + +The default SSH configuration disables support for .rhosts. The appropriate +configuration is used if no value is set for IgnoreRhosts. + +To explicitly disable support for .rhosts files, add or correct the following line in + + +/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf: + +IgnoreRhosts yes + 11 + 12 + 14 + 15 + 16 + 18 + 3 + 5 + 9 + 5.5.6 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + DSS06.03 + DSS06.06 + 3.1.12 + CCI-000366 + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + 4.3.4.3.2 + 4.3.4.3.3 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + A.6.1.2 + A.7.1.1 + A.9.1.2 + A.9.2.1 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + AC-17(a) + CM-7(a) + CM-7(b) + CM-6(a) + PR.AC-4 + PR.AC-6 + PR.IP-1 + PR.PT-3 + FIA_UAU.1 + SRG-OS-000480-GPOS-00227 + SRG-OS-000107-VMM-000530 + SSH trust relationships mean a compromise on one host +can allow an attacker to move trivially to other hosts. + CCE-90797-2 + - name: Disable SSH Support for .rhosts Files + block: + + - name: Deduplicate values from /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*{{ "IgnoreRhosts"| regex_escape }}\s+ + state: absent + + - name: Check if /etc/ssh/sshd_config.d exists + stat: + path: /etc/ssh/sshd_config.d + register: _etc_ssh_sshd_config_d_exists + + - name: Check if the parameter IgnoreRhosts is present in /etc/ssh/sshd_config.d + find: + paths: /etc/ssh/sshd_config.d + recurse: 'yes' + follow: 'no' + contains: (?i)^\s*{{ "IgnoreRhosts"| regex_escape }}\s+ + register: _etc_ssh_sshd_config_d_has_parameter + when: _etc_ssh_sshd_config_d_exists.stat.isdir is defined and _etc_ssh_sshd_config_d_exists.stat.isdir + + - name: Remove parameter from files in /etc/ssh/sshd_config.d + lineinfile: + path: '{{ item.path }}' + create: false + regexp: (?i)^\s*{{ "IgnoreRhosts"| regex_escape }}\s+ + state: absent + with_items: '{{ _etc_ssh_sshd_config_d_has_parameter.files }}' + when: _etc_ssh_sshd_config_d_has_parameter.matched + + - name: Insert correct line to /etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf + lineinfile: + path: /etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf + create: true + regexp: (?i)^\s*{{ "IgnoreRhosts"| regex_escape }}\s+ + line: IgnoreRhosts yes + state: present + insertbefore: ^[#\s]*Match + validate: /usr/sbin/sshd -t -f %s + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-90797-2 + - CJIS-5.5.6 + - NIST-800-171-3.1.12 + - NIST-800-53-AC-17(a) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sshd_disable_rhosts + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +mkdir -p /etc/ssh/sshd_config.d +touch /etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf + +LC_ALL=C sed -i "/^\s*IgnoreRhosts\s\+/Id" "/etc/ssh/sshd_config" +LC_ALL=C sed -i "/^\s*IgnoreRhosts\s\+/Id" "/etc/ssh/sshd_config.d"/*.conf +if [ -e "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" ] ; then + + LC_ALL=C sed -i "/^\s*IgnoreRhosts\s\+/Id" "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" +else + touch "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" + +cp "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf.bak" +# Insert before the line matching the regex '^Match'. +line_number="$(LC_ALL=C grep -n "^Match" "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf.bak" | LC_ALL=C sed 's/:.*//g')" +if [ -z "$line_number" ]; then + # There was no match of '^Match', insert at + # the end of the file. + printf '%s\n' "IgnoreRhosts yes" >> "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" +else + head -n "$(( line_number - 1 ))" "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf.bak" > "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" + printf '%s\n' "IgnoreRhosts yes" >> "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" + tail -n "+$(( line_number ))" "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf.bak" >> "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" +fi +# Clean up after ourselves. +rm "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable SSH Support for Rhosts RSA Authentication + SSH can allow authentication through the obsolete rsh +command through the use of the authenticating user's SSH keys. This should be disabled. + +To ensure this behavior is disabled, add or correct the +following line in /etc/ssh/sshd_config: +RhostsRSAAuthentication no + As of openssh-server version 7.4 and above, +the RhostsRSAAuthentication option has been deprecated, and the line +RhostsRSAAuthentication no in /etc/ssh/sshd_config is not +necessary. + 11 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + 3.1.12 + CCI-000366 + 164.308(a)(4)(i) + 164.308(b)(1) + 164.308(b)(3) + 164.310(b) + 164.312(e)(1) + 164.312(e)(2)(ii) + 4.3.4.3.2 + 4.3.4.3.3 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + AC-17(a) + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + FIA_UAU.1 + SRG-OS-000480-GPOS-00227 + Configuring this setting for the SSH daemon provides additional +assurance that remote login via SSH will require a password, even +in the event of misconfiguration elsewhere. + CCE-87836-3 + - name: Disable SSH Support for Rhosts RSA Authentication + block: + + - name: Check for duplicate values + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*RhostsRSAAuthentication\s+ + state: absent + check_mode: true + changed_when: false + register: dupes + + - name: Deduplicate values from /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*RhostsRSAAuthentication\s+ + state: absent + when: dupes.found is defined and dupes.found > 1 + + - name: Insert correct line to /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: true + regexp: (?i)^\s*RhostsRSAAuthentication\s+ + line: RhostsRSAAuthentication no + state: present + insertbefore: ^[#\s]*Match + validate: /usr/sbin/sshd -t -f %s + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-87836-3 + - NIST-800-171-3.1.12 + - NIST-800-53-AC-17(a) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sshd_disable_rhosts_rsa + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed. +# Otherwise, regular sed command will do. +sed_command=('sed' '-i') +if test -L "/etc/ssh/sshd_config"; then + sed_command+=('--follow-symlinks') +fi + +# Strip any search characters in the key arg so that the key can be replaced without +# adding any search characters to the config file. +stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^RhostsRSAAuthentication") + +# shellcheck disable=SC2059 +printf -v formatted_output "%s %s" "$stripped_key" "no" + +# If the key exists, change it. Otherwise, add it to the config_file. +# We search for the key string followed by a word boundary (matched by \>), +# so if we search for 'setting', 'setting2' won't match. +if LC_ALL=C grep -q -m 1 -i -e "^RhostsRSAAuthentication\\>" "/etc/ssh/sshd_config"; then + escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") + "${sed_command[@]}" "s/^RhostsRSAAuthentication\\>.*/$escaped_formatted_output/gi" "/etc/ssh/sshd_config" +else + # \n is precaution for case where file ends without trailing newline + cce="CCE-87836-3" + printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "/etc/ssh/sshd_config" >> "/etc/ssh/sshd_config" + printf '%s\n' "$formatted_output" >> "/etc/ssh/sshd_config" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable SSH Root Login + The root user should never be allowed to login to a +system directly over a network. +To disable root login via SSH, add or correct the following line in + + +/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf: + +PermitRootLogin no + BP28(R19) + NT007(R21) + 1 + 11 + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + 5.5.6 + APO01.06 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.02 + DSS06.03 + DSS06.06 + DSS06.10 + 3.1.1 + 3.1.5 + CCI-000366 + CCI-000770 + 164.308(a)(4)(i) + 164.308(b)(1) + 164.308(b)(3) + 164.310(b) + 164.312(e)(1) + 164.312(e)(2)(ii) + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.18.1.4 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.1 + A.9.4.2 + A.9.4.3 + A.9.4.4 + A.9.4.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.2.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CIP-007-3 R5.2 + CIP-007-3 R5.3.1 + CIP-007-3 R5.3.2 + CIP-007-3 R5.3.3 + AC-6(2) + AC-17(a) + IA-2 + IA-2(5) + CM-7(a) + CM-7(b) + CM-6(a) + PR.AC-1 + PR.AC-4 + PR.AC-6 + PR.AC-7 + PR.DS-5 + PR.PT-3 + FAU_GEN.1 + SRG-OS-000109-GPOS-00056 + SRG-OS-000480-GPOS-00227 + SRG-OS-000480-VMM-002000 + Even though the communications channel may be encrypted, an additional layer of +security is gained by extending the policy of not logging directly on as root. +In addition, logging in with a user-specific account provides individual +accountability of actions performed on the system and also helps to minimize +direct attack attempts on root's password. + CCE-90800-4 + - name: Disable SSH Root Login + block: + + - name: Deduplicate values from /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*{{ "PermitRootLogin"| regex_escape }}\s+ + state: absent + + - name: Check if /etc/ssh/sshd_config.d exists + stat: + path: /etc/ssh/sshd_config.d + register: _etc_ssh_sshd_config_d_exists + + - name: Check if the parameter PermitRootLogin is present in /etc/ssh/sshd_config.d + find: + paths: /etc/ssh/sshd_config.d + recurse: 'yes' + follow: 'no' + contains: (?i)^\s*{{ "PermitRootLogin"| regex_escape }}\s+ + register: _etc_ssh_sshd_config_d_has_parameter + when: _etc_ssh_sshd_config_d_exists.stat.isdir is defined and _etc_ssh_sshd_config_d_exists.stat.isdir + + - name: Remove parameter from files in /etc/ssh/sshd_config.d + lineinfile: + path: '{{ item.path }}' + create: false + regexp: (?i)^\s*{{ "PermitRootLogin"| regex_escape }}\s+ + state: absent + with_items: '{{ _etc_ssh_sshd_config_d_has_parameter.files }}' + when: _etc_ssh_sshd_config_d_has_parameter.matched + + - name: Insert correct line to /etc/ssh/sshd_config.d/00-complianceascode-hardening.conf + lineinfile: + path: /etc/ssh/sshd_config.d/00-complianceascode-hardening.conf + create: true + regexp: (?i)^\s*{{ "PermitRootLogin"| regex_escape }}\s+ + line: PermitRootLogin no + state: present + insertbefore: ^[#\s]*Match + validate: /usr/sbin/sshd -t -f %s + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-90800-4 + - CJIS-5.5.6 + - NIST-800-171-3.1.1 + - NIST-800-171-3.1.5 + - NIST-800-53-AC-17(a) + - NIST-800-53-AC-6(2) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - NIST-800-53-IA-2 + - NIST-800-53-IA-2(5) + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sshd_disable_root_login + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +mkdir -p /etc/ssh/sshd_config.d +touch /etc/ssh/sshd_config.d/00-complianceascode-hardening.conf + +LC_ALL=C sed -i "/^\s*PermitRootLogin\s\+/Id" "/etc/ssh/sshd_config" +LC_ALL=C sed -i "/^\s*PermitRootLogin\s\+/Id" "/etc/ssh/sshd_config.d"/*.conf +if [ -e "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" ] ; then + + LC_ALL=C sed -i "/^\s*PermitRootLogin\s\+/Id" "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" +else + touch "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" + +cp "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" +# Insert before the line matching the regex '^Match'. +line_number="$(LC_ALL=C grep -n "^Match" "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" | LC_ALL=C sed 's/:.*//g')" +if [ -z "$line_number" ]; then + # There was no match of '^Match', insert at + # the end of the file. + printf '%s\n' "PermitRootLogin no" >> "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" +else + head -n "$(( line_number - 1 ))" "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" > "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" + printf '%s\n' "PermitRootLogin no" >> "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" + tail -n "+$(( line_number ))" "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" >> "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" +fi +# Clean up after ourselves. +rm "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable SSH root Login with a Password (Insecure) + To disable password-based root logins over SSH, add or correct the following line in + + +/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf: + +PermitRootLogin prohibit-password + While this disables password-based root logins, direct root logins +through other means such as through SSH keys or GSSAPI will still be +permitted. Permitting any sort of root login remotely opens up the +root account to attack. +To fully disable direct root logins over SSH (which is considered a +best practice) and prevent remote attacks against the root account, +see CCE-27100-7, CCE-27445-6, CCE-80901-2, and similar. + Even though the communications channel may be encrypted, an additional +layer of security is gained by preventing use of a password. +This also helps to minimize direct attack attempts on root's password. + - name: Disable SSH root Login with a Password (Insecure) + block: + + - name: Deduplicate values from /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*{{ "PermitRootLogin"| regex_escape }}\s+ + state: absent + + - name: Check if /etc/ssh/sshd_config.d exists + stat: + path: /etc/ssh/sshd_config.d + register: _etc_ssh_sshd_config_d_exists + + - name: Check if the parameter PermitRootLogin is present in /etc/ssh/sshd_config.d + find: + paths: /etc/ssh/sshd_config.d + recurse: 'yes' + follow: 'no' + contains: (?i)^\s*{{ "PermitRootLogin"| regex_escape }}\s+ + register: _etc_ssh_sshd_config_d_has_parameter + when: _etc_ssh_sshd_config_d_exists.stat.isdir is defined and _etc_ssh_sshd_config_d_exists.stat.isdir + + - name: Remove parameter from files in /etc/ssh/sshd_config.d + lineinfile: + path: '{{ item.path }}' + create: false + regexp: (?i)^\s*{{ "PermitRootLogin"| regex_escape }}\s+ + state: absent + with_items: '{{ _etc_ssh_sshd_config_d_has_parameter.files }}' + when: _etc_ssh_sshd_config_d_has_parameter.matched + + - name: Insert correct line to /etc/ssh/sshd_config.d/00-complianceascode-hardening.conf + lineinfile: + path: /etc/ssh/sshd_config.d/00-complianceascode-hardening.conf + create: true + regexp: (?i)^\s*{{ "PermitRootLogin"| regex_escape }}\s+ + line: PermitRootLogin prohibit-password + state: present + insertbefore: ^[#\s]*Match + validate: /usr/sbin/sshd -t -f %s + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sshd_disable_root_password_login + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +mkdir -p /etc/ssh/sshd_config.d +touch /etc/ssh/sshd_config.d/00-complianceascode-hardening.conf + +LC_ALL=C sed -i "/^\s*PermitRootLogin\s\+/Id" "/etc/ssh/sshd_config" +LC_ALL=C sed -i "/^\s*PermitRootLogin\s\+/Id" "/etc/ssh/sshd_config.d"/*.conf +if [ -e "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" ] ; then + + LC_ALL=C sed -i "/^\s*PermitRootLogin\s\+/Id" "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" +else + touch "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" + +cp "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" +# Insert before the line matching the regex '^Match'. +line_number="$(LC_ALL=C grep -n "^Match" "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" | LC_ALL=C sed 's/:.*//g')" +if [ -z "$line_number" ]; then + # There was no match of '^Match', insert at + # the end of the file. + printf '%s\n' "PermitRootLogin prohibit-password" >> "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" +else + head -n "$(( line_number - 1 ))" "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" > "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" + printf '%s\n' "PermitRootLogin prohibit-password" >> "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" + tail -n "+$(( line_number ))" "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" >> "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" +fi +# Clean up after ourselves. +rm "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable SSH TCP Forwarding + The AllowTcpForwarding parameter specifies whether TCP forwarding is permitted. +To disable TCP forwarding, add or correct the following line in + + +/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf: + +AllowTcpForwarding no + Leaving port forwarding enabled can expose the organization to security risks and back-doors. + CCE-90806-1 + - name: Disable SSH TCP Forwarding + block: + + - name: Deduplicate values from /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*{{ "AllowTcpForwarding"| regex_escape }}\s+ + state: absent + + - name: Check if /etc/ssh/sshd_config.d exists + stat: + path: /etc/ssh/sshd_config.d + register: _etc_ssh_sshd_config_d_exists + + - name: Check if the parameter AllowTcpForwarding is present in /etc/ssh/sshd_config.d + find: + paths: /etc/ssh/sshd_config.d + recurse: 'yes' + follow: 'no' + contains: (?i)^\s*{{ "AllowTcpForwarding"| regex_escape }}\s+ + register: _etc_ssh_sshd_config_d_has_parameter + when: _etc_ssh_sshd_config_d_exists.stat.isdir is defined and _etc_ssh_sshd_config_d_exists.stat.isdir + + - name: Remove parameter from files in /etc/ssh/sshd_config.d + lineinfile: + path: '{{ item.path }}' + create: false + regexp: (?i)^\s*{{ "AllowTcpForwarding"| regex_escape }}\s+ + state: absent + with_items: '{{ _etc_ssh_sshd_config_d_has_parameter.files }}' + when: _etc_ssh_sshd_config_d_has_parameter.matched + + - name: Insert correct line to /etc/ssh/sshd_config.d/00-complianceascode-hardening.conf + lineinfile: + path: /etc/ssh/sshd_config.d/00-complianceascode-hardening.conf + create: true + regexp: (?i)^\s*{{ "AllowTcpForwarding"| regex_escape }}\s+ + line: AllowTcpForwarding no + state: present + insertbefore: ^[#\s]*Match + validate: /usr/sbin/sshd -t -f %s + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-90806-1 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sshd_disable_tcp_forwarding + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +mkdir -p /etc/ssh/sshd_config.d +touch /etc/ssh/sshd_config.d/00-complianceascode-hardening.conf + +LC_ALL=C sed -i "/^\s*AllowTcpForwarding\s\+/Id" "/etc/ssh/sshd_config" +LC_ALL=C sed -i "/^\s*AllowTcpForwarding\s\+/Id" "/etc/ssh/sshd_config.d"/*.conf +if [ -e "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" ] ; then + + LC_ALL=C sed -i "/^\s*AllowTcpForwarding\s\+/Id" "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" +else + touch "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" + +cp "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" +# Insert before the line matching the regex '^Match'. +line_number="$(LC_ALL=C grep -n "^Match" "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" | LC_ALL=C sed 's/:.*//g')" +if [ -z "$line_number" ]; then + # There was no match of '^Match', insert at + # the end of the file. + printf '%s\n' "AllowTcpForwarding no" >> "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" +else + head -n "$(( line_number - 1 ))" "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" > "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" + printf '%s\n' "AllowTcpForwarding no" >> "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" + tail -n "+$(( line_number ))" "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" >> "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" +fi +# Clean up after ourselves. +rm "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable SSH Support for User Known Hosts + SSH can allow system users to connect to systems if a cache of the remote +systems public keys is available. This should be disabled. + +To ensure this behavior is disabled, add or correct the following line in + + +/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf: + +IgnoreUserKnownHosts yes + 11 + 3 + 9 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + 3.1.12 + CCI-000366 + 164.308(a)(4)(i) + 164.308(b)(1) + 164.308(b)(3) + 164.310(b) + 164.312(e)(1) + 164.312(e)(2)(ii) + 4.3.4.3.2 + 4.3.4.3.3 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + AC-17(a) + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + FIA_UAU.1 + SRG-OS-000480-GPOS-00227 + Configuring this setting for the SSH daemon provides additional +assurance that remote login via SSH will require a password, even +in the event of misconfiguration elsewhere. + CCE-90796-4 + - name: Disable SSH Support for User Known Hosts + block: + + - name: Deduplicate values from /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*{{ "IgnoreUserKnownHosts"| regex_escape }}\s+ + state: absent + + - name: Check if /etc/ssh/sshd_config.d exists + stat: + path: /etc/ssh/sshd_config.d + register: _etc_ssh_sshd_config_d_exists + + - name: Check if the parameter IgnoreUserKnownHosts is present in /etc/ssh/sshd_config.d + find: + paths: /etc/ssh/sshd_config.d + recurse: 'yes' + follow: 'no' + contains: (?i)^\s*{{ "IgnoreUserKnownHosts"| regex_escape }}\s+ + register: _etc_ssh_sshd_config_d_has_parameter + when: _etc_ssh_sshd_config_d_exists.stat.isdir is defined and _etc_ssh_sshd_config_d_exists.stat.isdir + + - name: Remove parameter from files in /etc/ssh/sshd_config.d + lineinfile: + path: '{{ item.path }}' + create: false + regexp: (?i)^\s*{{ "IgnoreUserKnownHosts"| regex_escape }}\s+ + state: absent + with_items: '{{ _etc_ssh_sshd_config_d_has_parameter.files }}' + when: _etc_ssh_sshd_config_d_has_parameter.matched + + - name: Insert correct line to /etc/ssh/sshd_config.d/00-complianceascode-hardening.conf + lineinfile: + path: /etc/ssh/sshd_config.d/00-complianceascode-hardening.conf + create: true + regexp: (?i)^\s*{{ "IgnoreUserKnownHosts"| regex_escape }}\s+ + line: IgnoreUserKnownHosts yes + state: present + insertbefore: ^[#\s]*Match + validate: /usr/sbin/sshd -t -f %s + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-90796-4 + - NIST-800-171-3.1.12 + - NIST-800-53-AC-17(a) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sshd_disable_user_known_hosts + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +mkdir -p /etc/ssh/sshd_config.d +touch /etc/ssh/sshd_config.d/00-complianceascode-hardening.conf + +LC_ALL=C sed -i "/^\s*IgnoreUserKnownHosts\s\+/Id" "/etc/ssh/sshd_config" +LC_ALL=C sed -i "/^\s*IgnoreUserKnownHosts\s\+/Id" "/etc/ssh/sshd_config.d"/*.conf +if [ -e "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" ] ; then + + LC_ALL=C sed -i "/^\s*IgnoreUserKnownHosts\s\+/Id" "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" +else + touch "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" + +cp "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" +# Insert before the line matching the regex '^Match'. +line_number="$(LC_ALL=C grep -n "^Match" "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" | LC_ALL=C sed 's/:.*//g')" +if [ -z "$line_number" ]; then + # There was no match of '^Match', insert at + # the end of the file. + printf '%s\n' "IgnoreUserKnownHosts yes" >> "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" +else + head -n "$(( line_number - 1 ))" "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" > "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" + printf '%s\n' "IgnoreUserKnownHosts yes" >> "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" + tail -n "+$(( line_number ))" "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" >> "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" +fi +# Clean up after ourselves. +rm "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Disable X11 Forwarding + The X11Forwarding parameter provides the ability to tunnel X11 traffic +through the connection to enable remote graphic connections. +SSH has the capability to encrypt remote X11 connections when SSH's +X11Forwarding option is enabled. + +The default SSH configuration disables X11Forwarding. The appropriate +configuration is used if no value is set for X11Forwarding. + +To explicitly disable X11 Forwarding, add or correct the following line in + + +/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf: + +X11Forwarding no + CCI-000366 + CM-6(b) + SRG-OS-000480-GPOS-00227 + Disable X11 forwarding unless there is an operational requirement to use X11 +applications directly. There is a small risk that the remote X11 servers of +users who are logged in via SSH with X11 forwarding could be compromised by +other users on the X11 server. Note that even if X11 forwarding is disabled, +users can always install their own forwarders. + CCE-90798-0 + - name: Disable X11 Forwarding + block: + + - name: Deduplicate values from /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*{{ "X11Forwarding"| regex_escape }}\s+ + state: absent + + - name: Check if /etc/ssh/sshd_config.d exists + stat: + path: /etc/ssh/sshd_config.d + register: _etc_ssh_sshd_config_d_exists + + - name: Check if the parameter X11Forwarding is present in /etc/ssh/sshd_config.d + find: + paths: /etc/ssh/sshd_config.d + recurse: 'yes' + follow: 'no' + contains: (?i)^\s*{{ "X11Forwarding"| regex_escape }}\s+ + register: _etc_ssh_sshd_config_d_has_parameter + when: _etc_ssh_sshd_config_d_exists.stat.isdir is defined and _etc_ssh_sshd_config_d_exists.stat.isdir + + - name: Remove parameter from files in /etc/ssh/sshd_config.d + lineinfile: + path: '{{ item.path }}' + create: false + regexp: (?i)^\s*{{ "X11Forwarding"| regex_escape }}\s+ + state: absent + with_items: '{{ _etc_ssh_sshd_config_d_has_parameter.files }}' + when: _etc_ssh_sshd_config_d_has_parameter.matched + + - name: Insert correct line to /etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf + lineinfile: + path: /etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf + create: true + regexp: (?i)^\s*{{ "X11Forwarding"| regex_escape }}\s+ + line: X11Forwarding no + state: present + insertbefore: ^[#\s]*Match + validate: /usr/sbin/sshd -t -f %s + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-90798-0 + - NIST-800-53-CM-6(b) + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sshd_disable_x11_forwarding + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +mkdir -p /etc/ssh/sshd_config.d +touch /etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf + +LC_ALL=C sed -i "/^\s*X11Forwarding\s\+/Id" "/etc/ssh/sshd_config" +LC_ALL=C sed -i "/^\s*X11Forwarding\s\+/Id" "/etc/ssh/sshd_config.d"/*.conf +if [ -e "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" ] ; then + + LC_ALL=C sed -i "/^\s*X11Forwarding\s\+/Id" "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" +else + touch "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" + +cp "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf.bak" +# Insert before the line matching the regex '^Match'. +line_number="$(LC_ALL=C grep -n "^Match" "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf.bak" | LC_ALL=C sed 's/:.*//g')" +if [ -z "$line_number" ]; then + # There was no match of '^Match', insert at + # the end of the file. + printf '%s\n' "X11Forwarding no" >> "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" +else + head -n "$(( line_number - 1 ))" "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf.bak" > "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" + printf '%s\n' "X11Forwarding no" >> "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" + tail -n "+$(( line_number ))" "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf.bak" >> "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" +fi +# Clean up after ourselves. +rm "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Do Not Allow SSH Environment Options + Ensure that users are not able to override environment variables of the SSH daemon. + +The default SSH configuration disables environment processing. The appropriate +configuration is used if no value is set for PermitUserEnvironment. + +To explicitly disable Environment options, add or correct the following + + +/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf: + +PermitUserEnvironment no + 11 + 3 + 9 + 5.5.6 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + 3.1.12 + CCI-000366 + 164.308(a)(4)(i) + 164.308(b)(1) + 164.308(b)(3) + 164.310(b) + 164.312(e)(1) + 164.312(e)(2)(ii) + 4.3.4.3.2 + 4.3.4.3.3 + SR 7.6 + A.12.1.2 + A.12.5.1 + A.12.6.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + AC-17(a) + CM-7(a) + CM-7(b) + CM-6(a) + PR.IP-1 + SRG-OS-000480-GPOS-00229 + SRG-OS-000480-VMM-002000 + SSH environment options potentially allow users to bypass +access restriction in some configurations. + CCE-90803-8 + - name: Do Not Allow SSH Environment Options + block: + + - name: Deduplicate values from /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*{{ "PermitUserEnvironment"| regex_escape }}\s+ + state: absent + + - name: Check if /etc/ssh/sshd_config.d exists + stat: + path: /etc/ssh/sshd_config.d + register: _etc_ssh_sshd_config_d_exists + + - name: Check if the parameter PermitUserEnvironment is present in /etc/ssh/sshd_config.d + find: + paths: /etc/ssh/sshd_config.d + recurse: 'yes' + follow: 'no' + contains: (?i)^\s*{{ "PermitUserEnvironment"| regex_escape }}\s+ + register: _etc_ssh_sshd_config_d_has_parameter + when: _etc_ssh_sshd_config_d_exists.stat.isdir is defined and _etc_ssh_sshd_config_d_exists.stat.isdir + + - name: Remove parameter from files in /etc/ssh/sshd_config.d + lineinfile: + path: '{{ item.path }}' + create: false + regexp: (?i)^\s*{{ "PermitUserEnvironment"| regex_escape }}\s+ + state: absent + with_items: '{{ _etc_ssh_sshd_config_d_has_parameter.files }}' + when: _etc_ssh_sshd_config_d_has_parameter.matched + + - name: Insert correct line to /etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf + lineinfile: + path: /etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf + create: true + regexp: (?i)^\s*{{ "PermitUserEnvironment"| regex_escape }}\s+ + line: PermitUserEnvironment no + state: present + insertbefore: ^[#\s]*Match + validate: /usr/sbin/sshd -t -f %s + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-90803-8 + - CJIS-5.5.6 + - NIST-800-171-3.1.12 + - NIST-800-53-AC-17(a) + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sshd_do_not_permit_user_env + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +mkdir -p /etc/ssh/sshd_config.d +touch /etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf + +LC_ALL=C sed -i "/^\s*PermitUserEnvironment\s\+/Id" "/etc/ssh/sshd_config" +LC_ALL=C sed -i "/^\s*PermitUserEnvironment\s\+/Id" "/etc/ssh/sshd_config.d"/*.conf +if [ -e "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" ] ; then + + LC_ALL=C sed -i "/^\s*PermitUserEnvironment\s\+/Id" "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" +else + touch "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" + +cp "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf.bak" +# Insert before the line matching the regex '^Match'. +line_number="$(LC_ALL=C grep -n "^Match" "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf.bak" | LC_ALL=C sed 's/:.*//g')" +if [ -z "$line_number" ]; then + # There was no match of '^Match', insert at + # the end of the file. + printf '%s\n' "PermitUserEnvironment no" >> "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" +else + head -n "$(( line_number - 1 ))" "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf.bak" > "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" + printf '%s\n' "PermitUserEnvironment no" >> "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" + tail -n "+$(( line_number ))" "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf.bak" >> "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" +fi +# Clean up after ourselves. +rm "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable GSSAPI Authentication + Sites setup to use Kerberos or other GSSAPI Authenticaion require setting +sshd to accept this authentication. +To enable GSSAPI authentication, add or correct the following line in + + +/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf: + +GSSAPIAuthentication yes + Kerberos authentication for SSH is often implemented using GSSAPI. If +Kerberos is enabled through SSH, the SSH daemon provides a means of access +to the system's Kerberos implementation. Vulnerabilities in the system's +Kerberos implementations may be subject to exploitation. + +For enterprises, Kerberos is often enabled and used with GSSAPI for +centralized user account management which may necessitate enabling of +GSSAPI functionality in SSH. + - name: Enable GSSAPI Authentication + block: + + - name: Deduplicate values from /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*{{ "GSSAPIAuthentication"| regex_escape }}\s+ + state: absent + + - name: Check if /etc/ssh/sshd_config.d exists + stat: + path: /etc/ssh/sshd_config.d + register: _etc_ssh_sshd_config_d_exists + + - name: Check if the parameter GSSAPIAuthentication is present in /etc/ssh/sshd_config.d + find: + paths: /etc/ssh/sshd_config.d + recurse: 'yes' + follow: 'no' + contains: (?i)^\s*{{ "GSSAPIAuthentication"| regex_escape }}\s+ + register: _etc_ssh_sshd_config_d_has_parameter + when: _etc_ssh_sshd_config_d_exists.stat.isdir is defined and _etc_ssh_sshd_config_d_exists.stat.isdir + + - name: Remove parameter from files in /etc/ssh/sshd_config.d + lineinfile: + path: '{{ item.path }}' + create: false + regexp: (?i)^\s*{{ "GSSAPIAuthentication"| regex_escape }}\s+ + state: absent + with_items: '{{ _etc_ssh_sshd_config_d_has_parameter.files }}' + when: _etc_ssh_sshd_config_d_has_parameter.matched + + - name: Insert correct line to /etc/ssh/sshd_config.d/00-complianceascode-hardening.conf + lineinfile: + path: /etc/ssh/sshd_config.d/00-complianceascode-hardening.conf + create: true + regexp: (?i)^\s*{{ "GSSAPIAuthentication"| regex_escape }}\s+ + line: GSSAPIAuthentication yes + state: present + insertbefore: ^[#\s]*Match + validate: /usr/sbin/sshd -t -f %s + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sshd_enable_gssapi_auth + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +mkdir -p /etc/ssh/sshd_config.d +touch /etc/ssh/sshd_config.d/00-complianceascode-hardening.conf + +LC_ALL=C sed -i "/^\s*GSSAPIAuthentication\s\+/Id" "/etc/ssh/sshd_config" +LC_ALL=C sed -i "/^\s*GSSAPIAuthentication\s\+/Id" "/etc/ssh/sshd_config.d"/*.conf +if [ -e "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" ] ; then + + LC_ALL=C sed -i "/^\s*GSSAPIAuthentication\s\+/Id" "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" +else + touch "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" + +cp "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" +# Insert before the line matching the regex '^Match'. +line_number="$(LC_ALL=C grep -n "^Match" "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" | LC_ALL=C sed 's/:.*//g')" +if [ -z "$line_number" ]; then + # There was no match of '^Match', insert at + # the end of the file. + printf '%s\n' "GSSAPIAuthentication yes" >> "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" +else + head -n "$(( line_number - 1 ))" "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" > "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" + printf '%s\n' "GSSAPIAuthentication yes" >> "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" + tail -n "+$(( line_number ))" "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" >> "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" +fi +# Clean up after ourselves. +rm "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable PAM + UsePAM Enables the Pluggable Authentication Module interface. If set to “yes” this will +enable PAM authentication using ChallengeResponseAuthentication and +PasswordAuthentication in addition to PAM account and session module processing for all +authentication types. + +To enable PAM authentication, add or correct the following line in + + +/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf: + +UsePAM yes + CCI-000877 + SRG-OS-000125-GPOS-00065 + When UsePAM is set to yes, PAM runs through account and session types properly. This is +important if you want to restrict access to services based off of IP, time or other factors of +the account. Additionally, you can make sure users inherit certain environment variables +on login or disallow access to the server. + CCE-86722-6 + - name: Enable PAM + block: + + - name: Deduplicate values from /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*{{ "UsePAM"| regex_escape }}\s+ + state: absent + + - name: Check if /etc/ssh/sshd_config.d exists + stat: + path: /etc/ssh/sshd_config.d + register: _etc_ssh_sshd_config_d_exists + + - name: Check if the parameter UsePAM is present in /etc/ssh/sshd_config.d + find: + paths: /etc/ssh/sshd_config.d + recurse: 'yes' + follow: 'no' + contains: (?i)^\s*{{ "UsePAM"| regex_escape }}\s+ + register: _etc_ssh_sshd_config_d_has_parameter + when: _etc_ssh_sshd_config_d_exists.stat.isdir is defined and _etc_ssh_sshd_config_d_exists.stat.isdir + + - name: Remove parameter from files in /etc/ssh/sshd_config.d + lineinfile: + path: '{{ item.path }}' + create: false + regexp: (?i)^\s*{{ "UsePAM"| regex_escape }}\s+ + state: absent + with_items: '{{ _etc_ssh_sshd_config_d_has_parameter.files }}' + when: _etc_ssh_sshd_config_d_has_parameter.matched + + - name: Insert correct line to /etc/ssh/sshd_config.d/00-complianceascode-hardening.conf + lineinfile: + path: /etc/ssh/sshd_config.d/00-complianceascode-hardening.conf + create: true + regexp: (?i)^\s*{{ "UsePAM"| regex_escape }}\s+ + line: UsePAM yes + state: present + insertbefore: ^[#\s]*Match + validate: /usr/sbin/sshd -t -f %s + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86722-6 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sshd_enable_pam + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +mkdir -p /etc/ssh/sshd_config.d +touch /etc/ssh/sshd_config.d/00-complianceascode-hardening.conf + +LC_ALL=C sed -i "/^\s*UsePAM\s\+/Id" "/etc/ssh/sshd_config" +LC_ALL=C sed -i "/^\s*UsePAM\s\+/Id" "/etc/ssh/sshd_config.d"/*.conf +if [ -e "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" ] ; then + + LC_ALL=C sed -i "/^\s*UsePAM\s\+/Id" "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" +else + touch "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" + +cp "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" +# Insert before the line matching the regex '^Match'. +line_number="$(LC_ALL=C grep -n "^Match" "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" | LC_ALL=C sed 's/:.*//g')" +if [ -z "$line_number" ]; then + # There was no match of '^Match', insert at + # the end of the file. + printf '%s\n' "UsePAM yes" >> "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" +else + head -n "$(( line_number - 1 ))" "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" > "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" + printf '%s\n' "UsePAM yes" >> "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" + tail -n "+$(( line_number ))" "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" >> "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" +fi +# Clean up after ourselves. +rm "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable Public Key Authentication + Enable SSH login with public keys. + +The default SSH configuration enables authentication based on public keys. The appropriate +configuration is used if no value is set for PubkeyAuthentication. + +To explicitly enable Public Key Authentication, add or correct the following + + +/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf: + +PubkeyAuthentication yes + CCI-000765 + CCI-000766 + CCI-000767 + CCI-000768 + SRG-OS-000105-GPOS-00052 + SRG-OS-000106-GPOS-00053 + SRG-OS-000107-GPOS-00054 + SRG-OS-000108-GPOS-00055 + Without the use of multifactor authentication, the ease of access to +privileged functions is greatly increased. Multifactor authentication +requires using two or more factors to achieve authentication. +A privileged account is defined as an information system account with +authorizations of a privileged user. +The DoD CAC with DoD-approved PKI is an example of multifactor +authentication. + CCE-86138-5 + - name: Enable Public Key Authentication + block: + + - name: Deduplicate values from /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*{{ "PubkeyAuthentication"| regex_escape }}\s+ + state: absent + + - name: Check if /etc/ssh/sshd_config.d exists + stat: + path: /etc/ssh/sshd_config.d + register: _etc_ssh_sshd_config_d_exists + + - name: Check if the parameter PubkeyAuthentication is present in /etc/ssh/sshd_config.d + find: + paths: /etc/ssh/sshd_config.d + recurse: 'yes' + follow: 'no' + contains: (?i)^\s*{{ "PubkeyAuthentication"| regex_escape }}\s+ + register: _etc_ssh_sshd_config_d_has_parameter + when: _etc_ssh_sshd_config_d_exists.stat.isdir is defined and _etc_ssh_sshd_config_d_exists.stat.isdir + + - name: Remove parameter from files in /etc/ssh/sshd_config.d + lineinfile: + path: '{{ item.path }}' + create: false + regexp: (?i)^\s*{{ "PubkeyAuthentication"| regex_escape }}\s+ + state: absent + with_items: '{{ _etc_ssh_sshd_config_d_has_parameter.files }}' + when: _etc_ssh_sshd_config_d_has_parameter.matched + + - name: Insert correct line to /etc/ssh/sshd_config.d/00-complianceascode-hardening.conf + lineinfile: + path: /etc/ssh/sshd_config.d/00-complianceascode-hardening.conf + create: true + regexp: (?i)^\s*{{ "PubkeyAuthentication"| regex_escape }}\s+ + line: PubkeyAuthentication yes + state: present + insertbefore: ^[#\s]*Match + validate: /usr/sbin/sshd -t -f %s + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86138-5 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sshd_enable_pubkey_auth + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +mkdir -p /etc/ssh/sshd_config.d +touch /etc/ssh/sshd_config.d/00-complianceascode-hardening.conf + +LC_ALL=C sed -i "/^\s*PubkeyAuthentication\s\+/Id" "/etc/ssh/sshd_config" +LC_ALL=C sed -i "/^\s*PubkeyAuthentication\s\+/Id" "/etc/ssh/sshd_config.d"/*.conf +if [ -e "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" ] ; then + + LC_ALL=C sed -i "/^\s*PubkeyAuthentication\s\+/Id" "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" +else + touch "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" + +cp "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" +# Insert before the line matching the regex '^Match'. +line_number="$(LC_ALL=C grep -n "^Match" "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" | LC_ALL=C sed 's/:.*//g')" +if [ -z "$line_number" ]; then + # There was no match of '^Match', insert at + # the end of the file. + printf '%s\n' "PubkeyAuthentication yes" >> "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" +else + head -n "$(( line_number - 1 ))" "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" > "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" + printf '%s\n' "PubkeyAuthentication yes" >> "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" + tail -n "+$(( line_number ))" "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" >> "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" +fi +# Clean up after ourselves. +rm "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable Use of Strict Mode Checking + SSHs StrictModes option checks file and ownership permissions in +the user's home directory .ssh folder before accepting login. If world- +writable permissions are found, logon is rejected. + +The default SSH configuration has StrictModes enabled. The appropriate +configuration is used if no value is set for StrictModes. + +To explicitly enable StrictModes in SSH, add or correct the following line in + + +/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf: + +StrictModes yes + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + 3.1.12 + CCI-000366 + 164.308(a)(4)(i) + 164.308(b)(1) + 164.308(b)(3) + 164.310(b) + 164.312(e)(1) + 164.312(e)(2)(ii) + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + AC-6 + AC-17(a) + CM-6(a) + PR.AC-4 + PR.DS-5 + SRG-OS-000480-GPOS-00227 + SRG-OS-000480-VMM-002000 + If other users have access to modify user-specific SSH configuration files, they +may be able to log into the system as another user. + CCE-90809-5 + - name: Enable Use of Strict Mode Checking + block: + + - name: Deduplicate values from /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*{{ "StrictModes"| regex_escape }}\s+ + state: absent + + - name: Check if /etc/ssh/sshd_config.d exists + stat: + path: /etc/ssh/sshd_config.d + register: _etc_ssh_sshd_config_d_exists + + - name: Check if the parameter StrictModes is present in /etc/ssh/sshd_config.d + find: + paths: /etc/ssh/sshd_config.d + recurse: 'yes' + follow: 'no' + contains: (?i)^\s*{{ "StrictModes"| regex_escape }}\s+ + register: _etc_ssh_sshd_config_d_has_parameter + when: _etc_ssh_sshd_config_d_exists.stat.isdir is defined and _etc_ssh_sshd_config_d_exists.stat.isdir + + - name: Remove parameter from files in /etc/ssh/sshd_config.d + lineinfile: + path: '{{ item.path }}' + create: false + regexp: (?i)^\s*{{ "StrictModes"| regex_escape }}\s+ + state: absent + with_items: '{{ _etc_ssh_sshd_config_d_has_parameter.files }}' + when: _etc_ssh_sshd_config_d_has_parameter.matched + + - name: Insert correct line to /etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf + lineinfile: + path: /etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf + create: true + regexp: (?i)^\s*{{ "StrictModes"| regex_escape }}\s+ + line: StrictModes yes + state: present + insertbefore: ^[#\s]*Match + validate: /usr/sbin/sshd -t -f %s + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-90809-5 + - NIST-800-171-3.1.12 + - NIST-800-53-AC-17(a) + - NIST-800-53-AC-6 + - NIST-800-53-CM-6(a) + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sshd_enable_strictmodes + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +mkdir -p /etc/ssh/sshd_config.d +touch /etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf + +LC_ALL=C sed -i "/^\s*StrictModes\s\+/Id" "/etc/ssh/sshd_config" +LC_ALL=C sed -i "/^\s*StrictModes\s\+/Id" "/etc/ssh/sshd_config.d"/*.conf +if [ -e "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" ] ; then + + LC_ALL=C sed -i "/^\s*StrictModes\s\+/Id" "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" +else + touch "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" + +cp "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf.bak" +# Insert before the line matching the regex '^Match'. +line_number="$(LC_ALL=C grep -n "^Match" "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf.bak" | LC_ALL=C sed 's/:.*//g')" +if [ -z "$line_number" ]; then + # There was no match of '^Match', insert at + # the end of the file. + printf '%s\n' "StrictModes yes" >> "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" +else + head -n "$(( line_number - 1 ))" "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf.bak" > "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" + printf '%s\n' "StrictModes yes" >> "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" + tail -n "+$(( line_number ))" "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf.bak" >> "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" +fi +# Clean up after ourselves. +rm "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable SSH Warning Banner + To enable the warning banner and ensure it is consistent +across the system, add or correct the following line in + + +/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf: + +Banner /etc/issue +Another section contains information on how to create an +appropriate system-wide warning banner. + 1 + 12 + 15 + 16 + 5.5.6 + DSS05.04 + DSS05.10 + DSS06.10 + 3.1.9 + CCI-000048 + CCI-000050 + CCI-001384 + CCI-001385 + CCI-001386 + CCI-001387 + CCI-001388 + 164.308(a)(4)(i) + 164.308(b)(1) + 164.308(b)(3) + 164.310(b) + 164.312(e)(1) + 164.312(e)(2)(ii) + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + A.18.1.4 + A.9.2.1 + A.9.2.4 + A.9.3.1 + A.9.4.2 + A.9.4.3 + AC-8(a) + AC-8(c) + AC-17(a) + CM-6(a) + PR.AC-7 + FTA_TAB.1 + SRG-OS-000023-GPOS-00006 + SRG-OS-000228-GPOS-00088 + SRG-OS-000023-VMM-000060 + SRG-OS-000024-VMM-000070 + The warning message reinforces policy awareness during the logon process and +facilitates possible legal action against attackers. Alternatively, systems +whose ownership should not be obvious should ensure usage of a banner that does +not provide easy attribution. + CCE-90807-9 + - name: Enable SSH Warning Banner + block: + + - name: Deduplicate values from /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*{{ "Banner"| regex_escape }}\s+ + state: absent + + - name: Check if /etc/ssh/sshd_config.d exists + stat: + path: /etc/ssh/sshd_config.d + register: _etc_ssh_sshd_config_d_exists + + - name: Check if the parameter Banner is present in /etc/ssh/sshd_config.d + find: + paths: /etc/ssh/sshd_config.d + recurse: 'yes' + follow: 'no' + contains: (?i)^\s*{{ "Banner"| regex_escape }}\s+ + register: _etc_ssh_sshd_config_d_has_parameter + when: _etc_ssh_sshd_config_d_exists.stat.isdir is defined and _etc_ssh_sshd_config_d_exists.stat.isdir + + - name: Remove parameter from files in /etc/ssh/sshd_config.d + lineinfile: + path: '{{ item.path }}' + create: false + regexp: (?i)^\s*{{ "Banner"| regex_escape }}\s+ + state: absent + with_items: '{{ _etc_ssh_sshd_config_d_has_parameter.files }}' + when: _etc_ssh_sshd_config_d_has_parameter.matched + + - name: Insert correct line to /etc/ssh/sshd_config.d/00-complianceascode-hardening.conf + lineinfile: + path: /etc/ssh/sshd_config.d/00-complianceascode-hardening.conf + create: true + regexp: (?i)^\s*{{ "Banner"| regex_escape }}\s+ + line: Banner /etc/issue + state: present + insertbefore: ^[#\s]*Match + validate: /usr/sbin/sshd -t -f %s + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-90807-9 + - CJIS-5.5.6 + - NIST-800-171-3.1.9 + - NIST-800-53-AC-17(a) + - NIST-800-53-AC-8(a) + - NIST-800-53-AC-8(c) + - NIST-800-53-CM-6(a) + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sshd_enable_warning_banner + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +mkdir -p /etc/ssh/sshd_config.d +touch /etc/ssh/sshd_config.d/00-complianceascode-hardening.conf + +LC_ALL=C sed -i "/^\s*Banner\s\+/Id" "/etc/ssh/sshd_config" +LC_ALL=C sed -i "/^\s*Banner\s\+/Id" "/etc/ssh/sshd_config.d"/*.conf +if [ -e "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" ] ; then + + LC_ALL=C sed -i "/^\s*Banner\s\+/Id" "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" +else + touch "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" + +cp "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" +# Insert before the line matching the regex '^Match'. +line_number="$(LC_ALL=C grep -n "^Match" "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" | LC_ALL=C sed 's/:.*//g')" +if [ -z "$line_number" ]; then + # There was no match of '^Match', insert at + # the end of the file. + printf '%s\n' "Banner /etc/issue" >> "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" +else + head -n "$(( line_number - 1 ))" "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" > "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" + printf '%s\n' "Banner /etc/issue" >> "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" + tail -n "+$(( line_number ))" "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" >> "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" +fi +# Clean up after ourselves. +rm "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable SSH Warning Banner + To enable the warning banner and ensure it is consistent +across the system, add or correct the following line in + +/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf: + +Banner /etc/issue.net +Another section contains information on how to create an +appropriate system-wide warning banner. + 5.5.6 + DSS05.04 + DSS05.10 + DSS06.10 + 3.1.9 + CCI-000048 + CCI-000050 + CCI-001384 + CCI-001385 + CCI-001386 + CCI-001387 + CCI-001388 + 164.308(a)(4)(i) + 164.308(b)(1) + 164.308(b)(3) + 164.310(b) + 164.312(e)(1) + 164.312(e)(2)(ii) + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + A.18.1.4 + A.9.2.1 + A.9.2.4 + A.9.3.1 + A.9.4.2 + A.9.4.3 + AC-8(a) + AC-8(c) + AC-17(a) + CM-6(a) + PR.AC-7 + FTA_TAB.1 + SRG-OS-000023-GPOS-00006 + SRG-OS-000228-GPOS-00088 + SRG-OS-000023-VMM-000060 + SRG-OS-000024-VMM-000070 + The warning message reinforces policy awareness during the logon process and +facilitates possible legal action against attackers. Alternatively, systems +whose ownership should not be obvious should ensure usage of a banner that does +not provide easy attribution. + - name: Enable SSH Warning Banner + block: + + - name: Deduplicate values from /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*{{ "Banner"| regex_escape }}\s+ + state: absent + + - name: Check if /etc/ssh/sshd_config.d exists + stat: + path: /etc/ssh/sshd_config.d + register: _etc_ssh_sshd_config_d_exists + + - name: Check if the parameter Banner is present in /etc/ssh/sshd_config.d + find: + paths: /etc/ssh/sshd_config.d + recurse: 'yes' + follow: 'no' + contains: (?i)^\s*{{ "Banner"| regex_escape }}\s+ + register: _etc_ssh_sshd_config_d_has_parameter + when: _etc_ssh_sshd_config_d_exists.stat.isdir is defined and _etc_ssh_sshd_config_d_exists.stat.isdir + + - name: Remove parameter from files in /etc/ssh/sshd_config.d + lineinfile: + path: '{{ item.path }}' + create: false + regexp: (?i)^\s*{{ "Banner"| regex_escape }}\s+ + state: absent + with_items: '{{ _etc_ssh_sshd_config_d_has_parameter.files }}' + when: _etc_ssh_sshd_config_d_has_parameter.matched + + - name: Insert correct line to /etc/ssh/sshd_config.d/00-complianceascode-hardening.conf + lineinfile: + path: /etc/ssh/sshd_config.d/00-complianceascode-hardening.conf + create: true + regexp: (?i)^\s*{{ "Banner"| regex_escape }}\s+ + line: Banner /etc/issue.net + state: present + insertbefore: ^[#\s]*Match + validate: /usr/sbin/sshd -t -f %s + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CJIS-5.5.6 + - NIST-800-171-3.1.9 + - NIST-800-53-AC-17(a) + - NIST-800-53-AC-8(a) + - NIST-800-53-AC-8(c) + - NIST-800-53-CM-6(a) + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sshd_enable_warning_banner_net + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +mkdir -p /etc/ssh/sshd_config.d +touch /etc/ssh/sshd_config.d/00-complianceascode-hardening.conf + +LC_ALL=C sed -i "/^\s*Banner\s\+/Id" "/etc/ssh/sshd_config" +LC_ALL=C sed -i "/^\s*Banner\s\+/Id" "/etc/ssh/sshd_config.d"/*.conf +if [ -e "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" ] ; then + + LC_ALL=C sed -i "/^\s*Banner\s\+/Id" "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" +else + touch "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" + +cp "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" +# Insert before the line matching the regex '^Match'. +line_number="$(LC_ALL=C grep -n "^Match" "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" | LC_ALL=C sed 's/:.*//g')" +if [ -z "$line_number" ]; then + # There was no match of '^Match', insert at + # the end of the file. + printf '%s\n' "Banner /etc/issue.net" >> "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" +else + head -n "$(( line_number - 1 ))" "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" > "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" + printf '%s\n' "Banner /etc/issue.net" >> "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" + tail -n "+$(( line_number ))" "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" >> "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" +fi +# Clean up after ourselves. +rm "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable Encrypted X11 Forwarding + By default, remote X11 connections are not encrypted when initiated +by users. SSH has the capability to encrypt remote X11 connections when SSH's +X11Forwarding option is enabled. + +To enable X11 Forwarding, add or correct the following line in + + +/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf: + +X11Forwarding yes + 1 + 11 + 12 + 13 + 15 + 16 + 18 + 20 + 3 + 4 + 6 + 9 + BAI03.08 + BAI07.04 + BAI10.01 + BAI10.02 + BAI10.03 + BAI10.05 + DSS03.01 + 3.1.13 + CCI-000366 + 4.3.4.3.2 + 4.3.4.3.3 + 4.4.3.3 + SR 7.6 + A.12.1.1 + A.12.1.2 + A.12.1.4 + A.12.5.1 + A.12.6.2 + A.13.1.1 + A.13.1.2 + A.14.2.2 + A.14.2.3 + A.14.2.4 + CIP-007-3 R7.1 + CM-6(a) + AC-17(a) + AC-17(2) + DE.AE-1 + PR.DS-7 + PR.IP-1 + SRG-OS-000480-GPOS-00227 + Non-encrypted X displays allow an attacker to capture keystrokes and to execute commands +remotely. + CCE-89696-9 + - name: Enable Encrypted X11 Forwarding + block: + + - name: Deduplicate values from /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*{{ "X11Forwarding"| regex_escape }}\s+ + state: absent + + - name: Check if /etc/ssh/sshd_config.d exists + stat: + path: /etc/ssh/sshd_config.d + register: _etc_ssh_sshd_config_d_exists + + - name: Check if the parameter X11Forwarding is present in /etc/ssh/sshd_config.d + find: + paths: /etc/ssh/sshd_config.d + recurse: 'yes' + follow: 'no' + contains: (?i)^\s*{{ "X11Forwarding"| regex_escape }}\s+ + register: _etc_ssh_sshd_config_d_has_parameter + when: _etc_ssh_sshd_config_d_exists.stat.isdir is defined and _etc_ssh_sshd_config_d_exists.stat.isdir + + - name: Remove parameter from files in /etc/ssh/sshd_config.d + lineinfile: + path: '{{ item.path }}' + create: false + regexp: (?i)^\s*{{ "X11Forwarding"| regex_escape }}\s+ + state: absent + with_items: '{{ _etc_ssh_sshd_config_d_has_parameter.files }}' + when: _etc_ssh_sshd_config_d_has_parameter.matched + + - name: Insert correct line to /etc/ssh/sshd_config.d/00-complianceascode-hardening.conf + lineinfile: + path: /etc/ssh/sshd_config.d/00-complianceascode-hardening.conf + create: true + regexp: (?i)^\s*{{ "X11Forwarding"| regex_escape }}\s+ + line: X11Forwarding yes + state: present + insertbefore: ^[#\s]*Match + validate: /usr/sbin/sshd -t -f %s + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-89696-9 + - NIST-800-171-3.1.13 + - NIST-800-53-AC-17(2) + - NIST-800-53-AC-17(a) + - NIST-800-53-CM-6(a) + - high_severity + - low_complexity + - low_disruption + - no_reboot_needed + - restrict_strategy + - sshd_enable_x11_forwarding + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +mkdir -p /etc/ssh/sshd_config.d +touch /etc/ssh/sshd_config.d/00-complianceascode-hardening.conf + +LC_ALL=C sed -i "/^\s*X11Forwarding\s\+/Id" "/etc/ssh/sshd_config" +LC_ALL=C sed -i "/^\s*X11Forwarding\s\+/Id" "/etc/ssh/sshd_config.d"/*.conf +if [ -e "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" ] ; then + + LC_ALL=C sed -i "/^\s*X11Forwarding\s\+/Id" "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" +else + touch "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" + +cp "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" +# Insert before the line matching the regex '^Match'. +line_number="$(LC_ALL=C grep -n "^Match" "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" | LC_ALL=C sed 's/:.*//g')" +if [ -z "$line_number" ]; then + # There was no match of '^Match', insert at + # the end of the file. + printf '%s\n' "X11Forwarding yes" >> "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" +else + head -n "$(( line_number - 1 ))" "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" > "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" + printf '%s\n' "X11Forwarding yes" >> "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" + tail -n "+$(( line_number ))" "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" >> "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" +fi +# Clean up after ourselves. +rm "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Limit Users' SSH Access + By default, the SSH configuration allows any user with an account +to access the system. In order to specify the users that are allowed to login +via SSH and deny all other users, add or correct the following line in the +/etc/ssh/sshd_config file: +AllowUsers USER1 USER2 +Where USER1 and USER2 are valid user names. + 11 + 12 + 14 + 15 + 16 + 18 + 3 + 5 + DSS05.02 + DSS05.04 + DSS05.05 + DSS05.07 + DSS06.03 + DSS06.06 + 3.1.12 + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.5.3 + 4.3.3.5.4 + 4.3.3.5.5 + 4.3.3.5.6 + 4.3.3.5.7 + 4.3.3.5.8 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.1 + 4.3.3.7.2 + 4.3.3.7.3 + 4.3.3.7.4 + SR 1.1 + SR 1.10 + SR 1.11 + SR 1.12 + SR 1.13 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.6 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + SR 2.2 + SR 2.3 + SR 2.4 + SR 2.5 + SR 2.6 + SR 2.7 + A.6.1.2 + A.7.1.1 + A.9.1.2 + A.9.2.1 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.2.3 + CIP-004-6 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.2 + CIP-007-3 R5.2 + CIP-007-3 R5.3.1 + CIP-007-3 R5.3.2 + CIP-007-3 R5.3.3 + AC-3 + CM-6(a) + PR.AC-4 + PR.AC-6 + PR.PT-3 + Specifying which accounts are allowed SSH access into the system reduces the +possibility of unauthorized access to the system. + + + + + + Enable SSH Print Last Log + Ensure that SSH will display the date and time of the last successful account logon. + +The default SSH configuration enables print of the date and time of the last login. +The appropriate configuration is used if no value is set for PrintLastLog. + +To explicitly enable LastLog in SSH, add or correct the following line in + + +/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf: + +PrintLastLog yes + 1 + 12 + 15 + 16 + DSS05.04 + DSS05.10 + DSS06.10 + CCI-000366 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + A.18.1.4 + A.9.2.1 + A.9.2.4 + A.9.3.1 + A.9.4.2 + A.9.4.3 + AC-9 + AC-17(a) + CM-6(a) + PR.AC-7 + SRG-OS-000480-GPOS-00227 + Providing users feedback on when account accesses last occurred facilitates user +recognition and reporting of unauthorized account use. + CCE-90804-6 + - name: Enable SSH Print Last Log + block: + + - name: Deduplicate values from /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*{{ "PrintLastLog"| regex_escape }}\s+ + state: absent + + - name: Check if /etc/ssh/sshd_config.d exists + stat: + path: /etc/ssh/sshd_config.d + register: _etc_ssh_sshd_config_d_exists + + - name: Check if the parameter PrintLastLog is present in /etc/ssh/sshd_config.d + find: + paths: /etc/ssh/sshd_config.d + recurse: 'yes' + follow: 'no' + contains: (?i)^\s*{{ "PrintLastLog"| regex_escape }}\s+ + register: _etc_ssh_sshd_config_d_has_parameter + when: _etc_ssh_sshd_config_d_exists.stat.isdir is defined and _etc_ssh_sshd_config_d_exists.stat.isdir + + - name: Remove parameter from files in /etc/ssh/sshd_config.d + lineinfile: + path: '{{ item.path }}' + create: false + regexp: (?i)^\s*{{ "PrintLastLog"| regex_escape }}\s+ + state: absent + with_items: '{{ _etc_ssh_sshd_config_d_has_parameter.files }}' + when: _etc_ssh_sshd_config_d_has_parameter.matched + + - name: Insert correct line to /etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf + lineinfile: + path: /etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf + create: true + regexp: (?i)^\s*{{ "PrintLastLog"| regex_escape }}\s+ + line: PrintLastLog yes + state: present + insertbefore: ^[#\s]*Match + validate: /usr/sbin/sshd -t -f %s + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-90804-6 + - NIST-800-53-AC-17(a) + - NIST-800-53-AC-9 + - NIST-800-53-CM-6(a) + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sshd_print_last_log + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +mkdir -p /etc/ssh/sshd_config.d +touch /etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf + +LC_ALL=C sed -i "/^\s*PrintLastLog\s\+/Id" "/etc/ssh/sshd_config" +LC_ALL=C sed -i "/^\s*PrintLastLog\s\+/Id" "/etc/ssh/sshd_config.d"/*.conf +if [ -e "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" ] ; then + + LC_ALL=C sed -i "/^\s*PrintLastLog\s\+/Id" "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" +else + touch "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" + +cp "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf.bak" +# Insert before the line matching the regex '^Match'. +line_number="$(LC_ALL=C grep -n "^Match" "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf.bak" | LC_ALL=C sed 's/:.*//g')" +if [ -z "$line_number" ]; then + # There was no match of '^Match', insert at + # the end of the file. + printf '%s\n' "PrintLastLog yes" >> "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" +else + head -n "$(( line_number - 1 ))" "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf.bak" > "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" + printf '%s\n' "PrintLastLog yes" >> "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" + tail -n "+$(( line_number ))" "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf.bak" >> "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" +fi +# Clean up after ourselves. +rm "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Force frequent session key renegotiation + The RekeyLimit parameter specifies how often +the session key of the is renegotiated, both in terms of +amount of data that may be transmitted and the time +elapsed. +To decrease the default limits, add or correct the following line in + + +/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf: + +RekeyLimit + CCI-000068 + FCS_SSH_EXT.1.8 + SRG-OS-000480-GPOS-00227 + SRG-OS-000033-GPOS-00014 + By decreasing the limit based on the amount of data and enabling +time-based limit, effects of potential attacks against +encryption keys are limited. + CCE-90815-2 + - name: XCCDF Value var_rekey_limit_size # promote to variable + set_fact: + var_rekey_limit_size: !!str + tags: + - always +- name: XCCDF Value var_rekey_limit_time # promote to variable + set_fact: + var_rekey_limit_time: !!str + tags: + - always + +- name: Force frequent session key renegotiation + block: + + - name: Deduplicate values from /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*{{ "RekeyLimit"| regex_escape }}\s+ + state: absent + + - name: Check if /etc/ssh/sshd_config.d exists + stat: + path: /etc/ssh/sshd_config.d + register: _etc_ssh_sshd_config_d_exists + + - name: Check if the parameter RekeyLimit is present in /etc/ssh/sshd_config.d + find: + paths: /etc/ssh/sshd_config.d + recurse: 'yes' + follow: 'no' + contains: (?i)^\s*{{ "RekeyLimit"| regex_escape }}\s+ + register: _etc_ssh_sshd_config_d_has_parameter + when: _etc_ssh_sshd_config_d_exists.stat.isdir is defined and _etc_ssh_sshd_config_d_exists.stat.isdir + + - name: Remove parameter from files in /etc/ssh/sshd_config.d + lineinfile: + path: '{{ item.path }}' + create: false + regexp: (?i)^\s*{{ "RekeyLimit"| regex_escape }}\s+ + state: absent + with_items: '{{ _etc_ssh_sshd_config_d_has_parameter.files }}' + when: _etc_ssh_sshd_config_d_has_parameter.matched + + - name: Insert correct line to /etc/ssh/sshd_config.d/00-complianceascode-hardening.conf + lineinfile: + path: /etc/ssh/sshd_config.d/00-complianceascode-hardening.conf + create: true + regexp: (?i)^\s*{{ "RekeyLimit"| regex_escape }}\s+ + line: RekeyLimit {{ var_rekey_limit_size }} {{ var_rekey_limit_time }} + state: present + insertbefore: ^[#\s]*Match + validate: /usr/sbin/sshd -t -f %s + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-90815-2 + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sshd_rekey_limit + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_rekey_limit_size='' +var_rekey_limit_time='' + + + +mkdir -p /etc/ssh/sshd_config.d +touch /etc/ssh/sshd_config.d/00-complianceascode-hardening.conf + +LC_ALL=C sed -i "/^\s*RekeyLimit\s\+/Id" "/etc/ssh/sshd_config" +LC_ALL=C sed -i "/^\s*RekeyLimit\s\+/Id" "/etc/ssh/sshd_config.d"/*.conf +if [ -e "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" ] ; then + + LC_ALL=C sed -i "/^\s*RekeyLimit\s\+/Id" "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" +else + touch "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" + +cp "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" +# Insert before the line matching the regex '^Match'. +line_number="$(LC_ALL=C grep -n "^Match" "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" | LC_ALL=C sed 's/:.*//g')" +if [ -z "$line_number" ]; then + # There was no match of '^Match', insert at + # the end of the file. + printf '%s\n' "RekeyLimit $var_rekey_limit_size $var_rekey_limit_time" >> "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" +else + head -n "$(( line_number - 1 ))" "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" > "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" + printf '%s\n' "RekeyLimit $var_rekey_limit_size $var_rekey_limit_time" >> "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" + tail -n "+$(( line_number ))" "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" >> "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" +fi +# Clean up after ourselves. +rm "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + + + Ensure SSH LoginGraceTime is configured + The LoginGraceTime parameter to the SSH server specifies the time allowed for successful authentication to +the SSH server. The longer the Grace period is the more open unauthenticated connections +can exist. Like other session controls in this session the Grace Period should be limited to +appropriate limits to ensure the service is available for needed access. + Setting the LoginGraceTime parameter to a low number will minimize the risk of successful +brute force attacks to the SSH server. It will also limit the number of concurrent +unauthenticated connections. + CCE-86552-7 + - name: XCCDF Value var_sshd_set_login_grace_time # promote to variable + set_fact: + var_sshd_set_login_grace_time: !!str + tags: + - always + +- name: Ensure SSH LoginGraceTime is configured + block: + + - name: Check for duplicate values + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*LoginGraceTime\s+ + state: absent + check_mode: true + changed_when: false + register: dupes + + - name: Deduplicate values from /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*LoginGraceTime\s+ + state: absent + when: dupes.found is defined and dupes.found > 1 + + - name: Insert correct line to /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: true + regexp: (?i)^\s*LoginGraceTime\s+ + line: LoginGraceTime {{ var_sshd_set_login_grace_time }} + state: present + insertbefore: ^[#\s]*Match + validate: /usr/sbin/sshd -t -f %s + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86552-7 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sshd_set_login_grace_time + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_sshd_set_login_grace_time='' + + +if [ -e "/etc/ssh/sshd_config" ] ; then + + LC_ALL=C sed -i "/^\s*LoginGraceTime\s\+/Id" "/etc/ssh/sshd_config" +else + touch "/etc/ssh/sshd_config" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/ssh/sshd_config" + +cp "/etc/ssh/sshd_config" "/etc/ssh/sshd_config.bak" +# Insert before the line matching the regex '^Match'. +line_number="$(LC_ALL=C grep -n "^Match" "/etc/ssh/sshd_config.bak" | LC_ALL=C sed 's/:.*//g')" +if [ -z "$line_number" ]; then + # There was no match of '^Match', insert at + # the end of the file. + printf '%s\n' "LoginGraceTime $var_sshd_set_login_grace_time" >> "/etc/ssh/sshd_config" +else + head -n "$(( line_number - 1 ))" "/etc/ssh/sshd_config.bak" > "/etc/ssh/sshd_config" + printf '%s\n' "LoginGraceTime $var_sshd_set_login_grace_time" >> "/etc/ssh/sshd_config" + tail -n "+$(( line_number ))" "/etc/ssh/sshd_config.bak" >> "/etc/ssh/sshd_config" +fi +# Clean up after ourselves. +rm "/etc/ssh/sshd_config.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + + Set LogLevel to INFO + The INFO parameter specifices that record login and logout activity will be logged. + +The default SSH configuration sets the log level to INFO. The appropriate +configuration is used if no value is set for LogLevel. + +To explicitly specify the log level in SSH, add or correct the following line in + + +/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf: + +LogLevel INFO + AC-17(a) + CM-6(a) + SSH provides several logging levels with varying amounts of verbosity. DEBUG is specifically +not recommended other than strictly for debugging SSH communications since it provides +so much data that it is difficult to identify important security information. INFO level is the +basic level that only records login activity of SSH users. In many situations, such as Incident +Response, it is important to determine when a particular user was active on a system. The +logout record can eliminate those users who disconnected, which helps narrow the field. + CCE-90813-7 + - name: Set LogLevel to INFO + block: + + - name: Deduplicate values from /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*{{ "LogLevel"| regex_escape }}\s+ + state: absent + + - name: Check if /etc/ssh/sshd_config.d exists + stat: + path: /etc/ssh/sshd_config.d + register: _etc_ssh_sshd_config_d_exists + + - name: Check if the parameter LogLevel is present in /etc/ssh/sshd_config.d + find: + paths: /etc/ssh/sshd_config.d + recurse: 'yes' + follow: 'no' + contains: (?i)^\s*{{ "LogLevel"| regex_escape }}\s+ + register: _etc_ssh_sshd_config_d_has_parameter + when: _etc_ssh_sshd_config_d_exists.stat.isdir is defined and _etc_ssh_sshd_config_d_exists.stat.isdir + + - name: Remove parameter from files in /etc/ssh/sshd_config.d + lineinfile: + path: '{{ item.path }}' + create: false + regexp: (?i)^\s*{{ "LogLevel"| regex_escape }}\s+ + state: absent + with_items: '{{ _etc_ssh_sshd_config_d_has_parameter.files }}' + when: _etc_ssh_sshd_config_d_has_parameter.matched + + - name: Insert correct line to /etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf + lineinfile: + path: /etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf + create: true + regexp: (?i)^\s*{{ "LogLevel"| regex_escape }}\s+ + line: LogLevel INFO + state: present + insertbefore: ^[#\s]*Match + validate: /usr/sbin/sshd -t -f %s + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-90813-7 + - NIST-800-53-AC-17(a) + - NIST-800-53-CM-6(a) + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - restrict_strategy + - sshd_set_loglevel_info + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +mkdir -p /etc/ssh/sshd_config.d +touch /etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf + +LC_ALL=C sed -i "/^\s*LogLevel\s\+/Id" "/etc/ssh/sshd_config" +LC_ALL=C sed -i "/^\s*LogLevel\s\+/Id" "/etc/ssh/sshd_config.d"/*.conf +if [ -e "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" ] ; then + + LC_ALL=C sed -i "/^\s*LogLevel\s\+/Id" "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" +else + touch "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" + +cp "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf.bak" +# Insert before the line matching the regex '^Match'. +line_number="$(LC_ALL=C grep -n "^Match" "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf.bak" | LC_ALL=C sed 's/:.*//g')" +if [ -z "$line_number" ]; then + # There was no match of '^Match', insert at + # the end of the file. + printf '%s\n' "LogLevel INFO" >> "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" +else + head -n "$(( line_number - 1 ))" "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf.bak" > "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" + printf '%s\n' "LogLevel INFO" >> "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" + tail -n "+$(( line_number ))" "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf.bak" >> "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" +fi +# Clean up after ourselves. +rm "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Set SSH Daemon LogLevel to VERBOSE + The VERBOSE parameter configures the SSH daemon to record login and logout activity. +To specify the log level in +SSH, add or correct the following line in + + +/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf: + +LogLevel VERBOSE + CCI-000067 + CIP-007-3 R7.1 + AC-17(a) + AC-17(1) + CM-6(a) + SRG-OS-000032-GPOS-00013 + SSH provides several logging levels with varying amounts of verbosity. DEBUG is specifically +not recommended other than strictly for debugging SSH communications since it provides +so much data that it is difficult to identify important security information. INFO or +VERBOSE level is the basic level that only records login activity of SSH users. In many +situations, such as Incident Response, it is important to determine when a particular user was active +on a system. The logout record can eliminate those users who disconnected, which helps narrow the +field. + CCE-86923-0 + - name: Set SSH Daemon LogLevel to VERBOSE + block: + + - name: Deduplicate values from /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*{{ "LogLevel"| regex_escape }}\s+ + state: absent + + - name: Check if /etc/ssh/sshd_config.d exists + stat: + path: /etc/ssh/sshd_config.d + register: _etc_ssh_sshd_config_d_exists + + - name: Check if the parameter LogLevel is present in /etc/ssh/sshd_config.d + find: + paths: /etc/ssh/sshd_config.d + recurse: 'yes' + follow: 'no' + contains: (?i)^\s*{{ "LogLevel"| regex_escape }}\s+ + register: _etc_ssh_sshd_config_d_has_parameter + when: _etc_ssh_sshd_config_d_exists.stat.isdir is defined and _etc_ssh_sshd_config_d_exists.stat.isdir + + - name: Remove parameter from files in /etc/ssh/sshd_config.d + lineinfile: + path: '{{ item.path }}' + create: false + regexp: (?i)^\s*{{ "LogLevel"| regex_escape }}\s+ + state: absent + with_items: '{{ _etc_ssh_sshd_config_d_has_parameter.files }}' + when: _etc_ssh_sshd_config_d_has_parameter.matched + + - name: Insert correct line to /etc/ssh/sshd_config.d/00-complianceascode-hardening.conf + lineinfile: + path: /etc/ssh/sshd_config.d/00-complianceascode-hardening.conf + create: true + regexp: (?i)^\s*{{ "LogLevel"| regex_escape }}\s+ + line: LogLevel VERBOSE + state: present + insertbefore: ^[#\s]*Match + validate: /usr/sbin/sshd -t -f %s + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-86923-0 + - NIST-800-53-AC-17(1) + - NIST-800-53-AC-17(a) + - NIST-800-53-CM-6(a) + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sshd_set_loglevel_verbose + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +mkdir -p /etc/ssh/sshd_config.d +touch /etc/ssh/sshd_config.d/00-complianceascode-hardening.conf + +LC_ALL=C sed -i "/^\s*LogLevel\s\+/Id" "/etc/ssh/sshd_config" +LC_ALL=C sed -i "/^\s*LogLevel\s\+/Id" "/etc/ssh/sshd_config.d"/*.conf +if [ -e "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" ] ; then + + LC_ALL=C sed -i "/^\s*LogLevel\s\+/Id" "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" +else + touch "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" + +cp "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" +# Insert before the line matching the regex '^Match'. +line_number="$(LC_ALL=C grep -n "^Match" "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" | LC_ALL=C sed 's/:.*//g')" +if [ -z "$line_number" ]; then + # There was no match of '^Match', insert at + # the end of the file. + printf '%s\n' "LogLevel VERBOSE" >> "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" +else + head -n "$(( line_number - 1 ))" "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" > "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" + printf '%s\n' "LogLevel VERBOSE" >> "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" + tail -n "+$(( line_number ))" "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" >> "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" +fi +# Clean up after ourselves. +rm "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Set SSH authentication attempt limit + The MaxAuthTries parameter specifies the maximum number of authentication attempts +permitted per connection. Once the number of failures reaches half this value, additional failures are logged. +to set MaxAUthTries edit /etc/ssh/sshd_config as follows: +MaxAuthTries + 0421 + 0422 + 0431 + 0974 + 1173 + 1401 + 1504 + 1505 + 1546 + 1557 + 1558 + 1559 + 1560 + 1561 + Setting the MaxAuthTries parameter to a low number will minimize the risk of successful +brute force attacks to the SSH server. + CCE-90810-3 + - name: XCCDF Value sshd_max_auth_tries_value # promote to variable + set_fact: + sshd_max_auth_tries_value: !!str + tags: + - always + +- name: Set SSH authentication attempt limit + block: + + - name: Check for duplicate values + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*MaxAuthTries\s+ + state: absent + check_mode: true + changed_when: false + register: dupes + + - name: Deduplicate values from /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*MaxAuthTries\s+ + state: absent + when: dupes.found is defined and dupes.found > 1 + + - name: Insert correct line to /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: true + regexp: (?i)^\s*MaxAuthTries\s+ + line: MaxAuthTries {{ sshd_max_auth_tries_value }} + state: present + insertbefore: ^[#\s]*Match + validate: /usr/sbin/sshd -t -f %s + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-90810-3 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sshd_set_max_auth_tries + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +sshd_max_auth_tries_value='' + + +if [ -e "/etc/ssh/sshd_config" ] ; then + + LC_ALL=C sed -i "/^\s*MaxAuthTries\s\+/Id" "/etc/ssh/sshd_config" +else + touch "/etc/ssh/sshd_config" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/ssh/sshd_config" + +cp "/etc/ssh/sshd_config" "/etc/ssh/sshd_config.bak" +# Insert before the line matching the regex '^Match'. +line_number="$(LC_ALL=C grep -n "^Match" "/etc/ssh/sshd_config.bak" | LC_ALL=C sed 's/:.*//g')" +if [ -z "$line_number" ]; then + # There was no match of '^Match', insert at + # the end of the file. + printf '%s\n' "MaxAuthTries $sshd_max_auth_tries_value" >> "/etc/ssh/sshd_config" +else + head -n "$(( line_number - 1 ))" "/etc/ssh/sshd_config.bak" > "/etc/ssh/sshd_config" + printf '%s\n' "MaxAuthTries $sshd_max_auth_tries_value" >> "/etc/ssh/sshd_config" + tail -n "+$(( line_number ))" "/etc/ssh/sshd_config.bak" >> "/etc/ssh/sshd_config" +fi +# Clean up after ourselves. +rm "/etc/ssh/sshd_config.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + + Set SSH MaxSessions limit + The MaxSessions parameter specifies the maximum number of open sessions permitted +from a given connection. To set MaxSessions edit +/etc/ssh/sshd_config as follows: MaxSessions + To protect a system from denial of service due to a large number of concurrent +sessions, use the rate limiting function of MaxSessions to protect availability +of sshd logins and prevent overwhelming the daemon. + CCE-84103-1 + - name: XCCDF Value var_sshd_max_sessions # promote to variable + set_fact: + var_sshd_max_sessions: !!str + tags: + - always + +- name: Set SSH MaxSessions limit + block: + + - name: Check for duplicate values + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*MaxSessions\s+ + state: absent + check_mode: true + changed_when: false + register: dupes + + - name: Deduplicate values from /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*MaxSessions\s+ + state: absent + when: dupes.found is defined and dupes.found > 1 + + - name: Insert correct line to /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: true + regexp: (?i)^\s*MaxSessions\s+ + line: MaxSessions {{ var_sshd_max_sessions }} + state: present + insertbefore: ^[#\s]*Match + validate: /usr/sbin/sshd -t -f %s + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84103-1 + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - sshd_set_max_sessions + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_sshd_max_sessions='' + + +if [ -e "/etc/ssh/sshd_config" ] ; then + + LC_ALL=C sed -i "/^\s*MaxSessions\s\+/Id" "/etc/ssh/sshd_config" +else + touch "/etc/ssh/sshd_config" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/ssh/sshd_config" + +cp "/etc/ssh/sshd_config" "/etc/ssh/sshd_config.bak" +# Insert before the line matching the regex '^Match'. +line_number="$(LC_ALL=C grep -n "^Match" "/etc/ssh/sshd_config.bak" | LC_ALL=C sed 's/:.*//g')" +if [ -z "$line_number" ]; then + # There was no match of '^Match', insert at + # the end of the file. + printf '%s\n' "MaxSessions $var_sshd_max_sessions" >> "/etc/ssh/sshd_config" +else + head -n "$(( line_number - 1 ))" "/etc/ssh/sshd_config.bak" > "/etc/ssh/sshd_config" + printf '%s\n' "MaxSessions $var_sshd_max_sessions" >> "/etc/ssh/sshd_config" + tail -n "+$(( line_number ))" "/etc/ssh/sshd_config.bak" >> "/etc/ssh/sshd_config" +fi +# Clean up after ourselves. +rm "/etc/ssh/sshd_config.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + + Ensure SSH MaxStartups is configured + The MaxStartups parameter specifies the maximum number of concurrent +unauthenticated connections to the SSH daemon. Additional connections will be +dropped until authentication succeeds or the LoginGraceTime expires for a +connection. To confgure MaxStartups, you should add or correct the following +line in the +/etc/ssh/sshd_config file: +MaxStartups +CIS recommends a MaxStartups value of '10:30:60', or more restrictive where +dictated by site policy. + To protect a system from denial of service due to a large number of pending +authentication connection attempts, use the rate limiting function of MaxStartups +to protect availability of sshd logins and prevent overwhelming the daemon. + CCE-87872-8 + - name: XCCDF Value var_sshd_set_maxstartups # promote to variable + set_fact: + var_sshd_set_maxstartups: !!str + tags: + - always + +- name: Ensure SSH MaxStartups is configured + block: + + - name: Check for duplicate values + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*MaxStartups\s+ + state: absent + check_mode: true + changed_when: false + register: dupes + + - name: Deduplicate values from /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*MaxStartups\s+ + state: absent + when: dupes.found is defined and dupes.found > 1 + + - name: Insert correct line to /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: true + regexp: (?i)^\s*MaxStartups\s+ + line: MaxStartups {{ var_sshd_set_maxstartups }} + state: present + insertbefore: ^[#\s]*Match + validate: /usr/sbin/sshd -t -f %s + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-87872-8 + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sshd_set_maxstartups + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_sshd_set_maxstartups='' + + +if [ -e "/etc/ssh/sshd_config" ] ; then + + LC_ALL=C sed -i "/^\s*MaxStartups\s\+/Id" "/etc/ssh/sshd_config" +else + touch "/etc/ssh/sshd_config" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/ssh/sshd_config" + +cp "/etc/ssh/sshd_config" "/etc/ssh/sshd_config.bak" +# Insert before the line matching the regex '^Match'. +line_number="$(LC_ALL=C grep -n "^Match" "/etc/ssh/sshd_config.bak" | LC_ALL=C sed 's/:.*//g')" +if [ -z "$line_number" ]; then + # There was no match of '^Match', insert at + # the end of the file. + printf '%s\n' "MaxStartups $var_sshd_set_maxstartups" >> "/etc/ssh/sshd_config" +else + head -n "$(( line_number - 1 ))" "/etc/ssh/sshd_config.bak" > "/etc/ssh/sshd_config" + printf '%s\n' "MaxStartups $var_sshd_set_maxstartups" >> "/etc/ssh/sshd_config" + tail -n "+$(( line_number ))" "/etc/ssh/sshd_config.bak" >> "/etc/ssh/sshd_config" +fi +# Clean up after ourselves. +rm "/etc/ssh/sshd_config.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Distribute the SSH Server configuration to multiple files in a config directory. + Make sure to have the Include /etc/ssh/sshd_config.d/*.conf line in the /etc/ssh/sshd_config file. +Ideally, don't have any active configuration directives in that file, and distribute the service configuration +to several files in the /etc/ssh/sshd_config.d directory. + This form of distributed configuration is considered as a good practice, and as other sshd rules assume that directives in files in the /etc/ssh/sshd_config.d config directory are effective, there has to be a rule that ensures this. +Aside from that, having multiple configuration files makes the SSH Server configuration changes easier to partition according to the reason that they were introduced, and therefore it should help to perform merges of hardening updates. + CCE-87681-3 + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if test -f /etc/ssh/sshd_config.d/sshd_config_original.conf; then + printf '%s\n' "Remediation probably already happened, '/etc/ssh/sshd_config.d/sshd_config_original.conf' already exists, not doing anything." >&2 +false 1 +elif grep -Eq '^\s*Include\s+/etc/ssh/sshd_config\.d/\*\.conf' /etc/ssh/sshd_config && ! grep -Eq '^\s*Match\s' /etc/ssh/sshd_config; then + printf '%s\n' "Remediation probably already happened, '/etc/ssh/sshd_config' already contains the include directive." >&2 +false 1 +else + mkdir -p /etc/ssh/sshd_config.d + mv /etc/ssh/sshd_config /etc/ssh/sshd_config.d/sshd_config_original.conf +cat > /etc/ssh/sshd_config << EOF +# To modify the system-wide sshd configuration, create a *.conf file under +# /etc/ssh/sshd_config.d/ which will be automatically included below + +Include /etc/ssh/sshd_config.d/*.conf +EOF +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable Use of Privilege Separation + When enabled, SSH will create an unprivileged child process that +has the privilege of the authenticated user. To enable privilege separation in +SSH, add or correct the following line in the /etc/ssh/sshd_config file: +UsePrivilegeSeparation + 12 + 13 + 14 + 15 + 16 + 18 + 3 + 5 + APO01.06 + DSS05.04 + DSS05.07 + DSS06.02 + 3.1.12 + CCI-000366 + 164.308(a)(4)(i) + 164.308(b)(1) + 164.308(b)(3) + 164.310(b) + 164.312(e)(1) + 164.312(e)(2)(ii) + 4.3.3.7.3 + SR 2.1 + SR 5.2 + A.10.1.1 + A.11.1.4 + A.11.1.5 + A.11.2.1 + A.13.1.1 + A.13.1.3 + A.13.2.1 + A.13.2.3 + A.13.2.4 + A.14.1.2 + A.14.1.3 + A.6.1.2 + A.7.1.1 + A.7.1.2 + A.7.3.1 + A.8.2.2 + A.8.2.3 + A.9.1.1 + A.9.1.2 + A.9.2.3 + A.9.4.1 + A.9.4.4 + A.9.4.5 + CIP-003-8 R5.1.1 + CIP-003-8 R5.3 + CIP-004-6 R2.3 + CIP-007-3 R2.1 + CIP-007-3 R2.2 + CIP-007-3 R2.3 + CIP-007-3 R5.1 + CIP-007-3 R5.1.1 + CIP-007-3 R5.1.2 + CM-6(a) + AC-17(a) + AC-6 + PR.AC-4 + PR.DS-5 + SRG-OS-000480-GPOS-00227 + SSH daemon privilege separation causes the SSH process to drop root privileges +when not needed which would decrease the impact of software vulnerabilities in +the unprivileged section. + CCE-88822-2 + - name: XCCDF Value var_sshd_priv_separation # promote to variable + set_fact: + var_sshd_priv_separation: !!str + tags: + - always + +- name: Enable Use of Privilege Separation + block: + + - name: Check for duplicate values + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*UsePrivilegeSeparation\s+ + state: absent + check_mode: true + changed_when: false + register: dupes + + - name: Deduplicate values from /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*UsePrivilegeSeparation\s+ + state: absent + when: dupes.found is defined and dupes.found > 1 + + - name: Insert correct line to /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: true + regexp: (?i)^\s*UsePrivilegeSeparation\s+ + line: UsePrivilegeSeparation {{ var_sshd_priv_separation }} + state: present + insertbefore: ^[#\s]*Match + validate: /usr/sbin/sshd -t -f %s + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-88822-2 + - NIST-800-171-3.1.12 + - NIST-800-53-AC-17(a) + - NIST-800-53-AC-6 + - NIST-800-53-CM-6(a) + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sshd_use_priv_separation + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +var_sshd_priv_separation='' + + +if [ -e "/etc/ssh/sshd_config" ] ; then + + LC_ALL=C sed -i "/^\s*UsePrivilegeSeparation\s\+/Id" "/etc/ssh/sshd_config" +else + touch "/etc/ssh/sshd_config" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/ssh/sshd_config" + +cp "/etc/ssh/sshd_config" "/etc/ssh/sshd_config.bak" +# Insert before the line matching the regex '^Match'. +line_number="$(LC_ALL=C grep -n "^Match" "/etc/ssh/sshd_config.bak" | LC_ALL=C sed 's/:.*//g')" +if [ -z "$line_number" ]; then + # There was no match of '^Match', insert at + # the end of the file. + printf '%s\n' "UsePrivilegeSeparation $var_sshd_priv_separation" >> "/etc/ssh/sshd_config" +else + head -n "$(( line_number - 1 ))" "/etc/ssh/sshd_config.bak" > "/etc/ssh/sshd_config" + printf '%s\n' "UsePrivilegeSeparation $var_sshd_priv_separation" >> "/etc/ssh/sshd_config" + tail -n "+$(( line_number ))" "/etc/ssh/sshd_config.bak" >> "/etc/ssh/sshd_config" +fi +# Clean up after ourselves. +rm "/etc/ssh/sshd_config.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + + SSH server uses strong entropy to seed + To set up SSH server to use entropy from a high-quality source, edit the /etc/sysconfig/sshd file. +The SSH_USE_STRONG_RNG configuration value determines how many bytes of entropy to use, so +make sure that the file contains line +SSH_USE_STRONG_RNG=32 + This setting can cause problems on computers without the hardware random generator, because insufficient entropy causes the connection to be blocked until enough entropy is available. + CCI-000366 + FCS_RBG_EXT.1.2 + SRG-OS-000480-GPOS-00232 + SRG-OS-000480-GPOS-00227 + SSH implementation in Red Hat Enterprise Linux 9 uses the openssl library, which doesn't use +high-entropy sources by default. Randomness is needed to generate data-encryption keys, and as +plaintext padding and initialization vectors in encryption algorithms, and high-quality +entropy elliminates the possibility that the output of the random number generator used by SSH +would be known to potential attackers. + CCE-88165-6 + - name: Setting unquoted shell-style assignment of 'SSH_USE_STRONG_RNG' to '32' in + '/etc/sysconfig/sshd' + block: + + - name: Check for duplicate values + lineinfile: + path: /etc/sysconfig/sshd + create: false + regexp: ^\s*SSH_USE_STRONG_RNG= + state: absent + check_mode: true + changed_when: false + register: dupes + + - name: Deduplicate values from /etc/sysconfig/sshd + lineinfile: + path: /etc/sysconfig/sshd + create: false + regexp: ^\s*SSH_USE_STRONG_RNG= + state: absent + when: dupes.found is defined and dupes.found > 1 + + - name: Insert correct line to /etc/sysconfig/sshd + lineinfile: + path: /etc/sysconfig/sshd + create: true + regexp: ^\s*SSH_USE_STRONG_RNG= + line: SSH_USE_STRONG_RNG=32 + state: present + insertbefore: ^# SSH_USE_STRONG_RNG + validate: /usr/bin/bash -n %s + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-88165-6 + - low_complexity + - low_disruption + - low_severity + - no_reboot_needed + - restrict_strategy + - sshd_use_strong_rng + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +if [ -e "/etc/sysconfig/sshd" ] ; then + + LC_ALL=C sed -i "/^\s*SSH_USE_STRONG_RNG=/d" "/etc/sysconfig/sshd" +else + touch "/etc/sysconfig/sshd" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/sysconfig/sshd" + +cp "/etc/sysconfig/sshd" "/etc/sysconfig/sshd.bak" +# Insert before the line matching the regex '^#\s*SSH_USE_STRONG_RNG'. +line_number="$(LC_ALL=C grep -n "^#\s*SSH_USE_STRONG_RNG" "/etc/sysconfig/sshd.bak" | LC_ALL=C sed 's/:.*//g')" +if [ -z "$line_number" ]; then + # There was no match of '^#\s*SSH_USE_STRONG_RNG', insert at + # the end of the file. + printf '%s\n' "SSH_USE_STRONG_RNG=32" >> "/etc/sysconfig/sshd" +else + head -n "$(( line_number - 1 ))" "/etc/sysconfig/sshd.bak" > "/etc/sysconfig/sshd" + printf '%s\n' "SSH_USE_STRONG_RNG=32" >> "/etc/sysconfig/sshd" + tail -n "+$(( line_number ))" "/etc/sysconfig/sshd.bak" >> "/etc/sysconfig/sshd" +fi +# Clean up after ourselves. +rm "/etc/sysconfig/sshd.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Prevent remote hosts from connecting to the proxy display + The SSH daemon should prevent remote hosts from connecting to the proxy +display. + +The default SSH configuration for X11UseLocalhost is yes, +which prevents remote hosts from connecting to the proxy display. + +To explicitly prevent remote connections to the proxy display, add or correct +the following line in + + +/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf: + +X11UseLocalhost yes + CCI-000366 + CM-6(b) + SRG-OS-000480-GPOS-00227 + When X11 forwarding is enabled, there may be additional exposure to the +server and client displays if the sshd proxy display is configured to listen +on the wildcard address. By default, sshd binds the forwarding server to the +loopback address and sets the hostname part of the DISPLAY +environment variable to localhost. This prevents remote hosts from +connecting to the proxy display. + CCE-89105-1 + - name: Prevent remote hosts from connecting to the proxy display + block: + + - name: Deduplicate values from /etc/ssh/sshd_config + lineinfile: + path: /etc/ssh/sshd_config + create: false + regexp: (?i)^\s*{{ "X11UseLocalhost"| regex_escape }}\s+ + state: absent + + - name: Check if /etc/ssh/sshd_config.d exists + stat: + path: /etc/ssh/sshd_config.d + register: _etc_ssh_sshd_config_d_exists + + - name: Check if the parameter X11UseLocalhost is present in /etc/ssh/sshd_config.d + find: + paths: /etc/ssh/sshd_config.d + recurse: 'yes' + follow: 'no' + contains: (?i)^\s*{{ "X11UseLocalhost"| regex_escape }}\s+ + register: _etc_ssh_sshd_config_d_has_parameter + when: _etc_ssh_sshd_config_d_exists.stat.isdir is defined and _etc_ssh_sshd_config_d_exists.stat.isdir + + - name: Remove parameter from files in /etc/ssh/sshd_config.d + lineinfile: + path: '{{ item.path }}' + create: false + regexp: (?i)^\s*{{ "X11UseLocalhost"| regex_escape }}\s+ + state: absent + with_items: '{{ _etc_ssh_sshd_config_d_has_parameter.files }}' + when: _etc_ssh_sshd_config_d_has_parameter.matched + + - name: Insert correct line to /etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf + lineinfile: + path: /etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf + create: true + regexp: (?i)^\s*{{ "X11UseLocalhost"| regex_escape }}\s+ + line: X11UseLocalhost yes + state: present + insertbefore: ^[#\s]*Match + validate: /usr/sbin/sshd -t -f %s + when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-89105-1 + - NIST-800-53-CM-6(b) + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - restrict_strategy + - sshd_x11_use_localhost + + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +mkdir -p /etc/ssh/sshd_config.d +touch /etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf + +LC_ALL=C sed -i "/^\s*X11UseLocalhost\s\+/Id" "/etc/ssh/sshd_config" +LC_ALL=C sed -i "/^\s*X11UseLocalhost\s\+/Id" "/etc/ssh/sshd_config.d"/*.conf +if [ -e "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" ] ; then + + LC_ALL=C sed -i "/^\s*X11UseLocalhost\s\+/Id" "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" +else + touch "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" + +cp "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf.bak" +# Insert before the line matching the regex '^Match'. +line_number="$(LC_ALL=C grep -n "^Match" "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf.bak" | LC_ALL=C sed 's/:.*//g')" +if [ -z "$line_number" ]; then + # There was no match of '^Match', insert at + # the end of the file. + printf '%s\n' "X11UseLocalhost yes" >> "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" +else + head -n "$(( line_number - 1 ))" "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf.bak" > "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" + printf '%s\n' "X11UseLocalhost yes" >> "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" + tail -n "+$(( line_number ))" "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf.bak" >> "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" +fi +# Clean up after ourselves. +rm "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Strengthen Firewall Configuration if Possible + If the SSH server is expected to only receive connections from +the local network, then strengthen the default firewall rule for the SSH service +to only accept connections from the appropriate network segment(s). + +Determine an appropriate network block, netwk, network mask, mask, and +network protocol, ip_protocol, representing the systems on your network which will +be allowed to access this SSH server. + +Run the following command: +firewall-cmd --permanent --add-rich-rule='rule family="ip_protocol" source address="netwk/mask" service name="ssh" accept' + + + + + System Security Services Daemon + The System Security Services Daemon (SSSD) is a system daemon that provides access +to different identity and authentication providers such as Red Hat's IdM, Microsoft's AD, +openLDAP, MIT Kerberos, etc. It uses a common framework that can provide caching and offline +support to systems utilizing SSSD. SSSD using caching to reduce load on authentication +servers permit offline authentication as well as store extended user data. + +For more information, see + https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/9/html-single/installing_identity_management/index#assembly_installing-an-idm-client_installing-identity-management + + + SSSD certificate_verification option + Value of the certificate_verification option in +the SSSD config. + sha1 + sha256 + sha384 + sha512 + sha1 + + + SSSD memcache_timeout option + Value of the memcache_timeout option in the [nss] section +of SSSD config /etc/sssd/sssd.conf. + 180 + 300 + 600 + 900 + 1800 + 86400 + 300 + + + SSSD ssh_known_hosts_timeout option + Value of the ssh_known_hosts_timeout option in the [ssh] section +of SSSD configuration file /etc/sssd/sssd.conf. + 180 + 300 + 600 + 900 + 1800 + 86400 + 180 + + + Certificate status checking in SSSD + Multifactor solutions that require devices separate from information systems gaining access include, +for example, hardware tokens providing time-based or challenge-response authenticators and smart cards. +Configuring certificate_verification to ocsp_dgst= ensures that certificates for +multifactor solutions are checked via Online Certificate Status Protocol (OCSP). + CCI-001948 + CCI-001954 + IA-2(11) + SRG-OS-000375-GPOS-00160 + SRG-OS-000377-GPOS-00162 + Ensuring that multifactor solutions certificates are checked via Online Certificate Status Protocol (OCSP) +ensures the security of the system. + CCE-87088-1 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-87088-1 + - NIST-800-53-IA-2(11) + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - sssd_certificate_verification +- name: XCCDF Value var_sssd_certificate_verification_digest_function # promote to variable + set_fact: + var_sssd_certificate_verification_digest_function: !!str + tags: + - always + +- name: Ensure that "certificate_verification" is not set in /etc/sssd/sssd.conf + ini_file: + path: /etc/sssd/sssd.conf + section: sssd + option: certificate_verification + state: absent + when: '"sssd-common" in ansible_facts.packages' + tags: + - CCE-87088-1 + - NIST-800-53-IA-2(11) + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - sssd_certificate_verification + +- name: Ensure that "certificate_verification" is not set in /etc/sssd/conf.d/*.conf + ini_file: + path: /etc/sssd/conf.d/*.conf + section: sssd + option: certificate_verification + state: absent + when: '"sssd-common" in ansible_facts.packages' + tags: + - CCE-87088-1 + - NIST-800-53-IA-2(11) + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - sssd_certificate_verification + +- name: Ensure that "certificate_verification" is set + ini_file: + path: /etc/sssd/conf.d/certificate_verification.conf + section: sssd + option: certificate_verification + value: ocsp_dgst = {{ var_sssd_certificate_verification_digest_function }} + state: present + when: '"sssd-common" in ansible_facts.packages' + tags: + - CCE-87088-1 + - NIST-800-53-IA-2(11) + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - sssd_certificate_verification + + # Remediation is applicable only in certain platforms +if rpm --quiet -q sssd-common; then + +var_sssd_certificate_verification_digest_function='' + + +MAIN_CONF="/etc/sssd/conf.d/certificate_verification.conf" + +found=false + +# set value in all files if they contain section or key +for f in $(echo -n "$MAIN_CONF /etc/sssd/sssd.conf /etc/sssd/conf.d/*.conf"); do + if [ ! -e "$f" ]; then + continue + fi + + # find key in section and change value + if grep -qzosP "[[:space:]]*\[sssd\]([^\n\[]*\n+)+?[[:space:]]*certificate_verification" "$f"; then + sed -i "s/certificate_verification[^(\n)]*/certificate_verification = ocsp_dgst = $var_sssd_certificate_verification_digest_function/" "$f" + found=true + + # find section and add key = value to it + elif grep -qs "[[:space:]]*\[sssd\]" "$f"; then + sed -i "/[[:space:]]*\[sssd\]/a certificate_verification = ocsp_dgst = $var_sssd_certificate_verification_digest_function" "$f" + found=true + fi +done + +# if section not in any file, append section with key = value to FIRST file in files parameter +if ! $found ; then + file=$(echo "$MAIN_CONF /etc/sssd/sssd.conf /etc/sssd/conf.d/*.conf" | cut -f1 -d ' ') + mkdir -p "$(dirname "$file")" + echo -e "[sssd]\ncertificate_verification = ocsp_dgst = $var_sssd_certificate_verification_digest_function" >> "$file" +fi + +if [ -e "$MAIN_CONF" ]; then + chown root:root "$MAIN_CONF" + chmod 600 "$MAIN_CONF" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + Enable Certmap in SSSD + SSSD should be configured to verify the certificate of the user or group. To set this up + ensure that section like certmap/testing.test/rule_name is setup in +/etc/sssd/sssd.conf. For example + +[certmap/testing.test/rule_name] +matchrule =<SAN>.*EDIPI@mil +maprule = (userCertificate;binary={cert!bin}) +domains = testing.test + + Automatic remediation of this control is not available, since all of the settings in +in the certmap need to be customized. + CCI-000187 + IA-5 (2) (c) + SRG-OS-000068-GPOS-00036 + Without mapping the certificate used to authenticate to the user account, the ability to +determine the identity of the individual user or group will not be available for forensic +analysis. + CCE-89737-1 + + + + + + + + + Enable Smartcards in SSSD + SSSD should be configured to authenticate access to the system using smart cards. +To enable smart cards in SSSD, set pam_cert_auth to True under the +[pam] section in /etc/sssd/sssd.conf. For example: +[pam] +pam_cert_auth = True + + +Add or update "pam_sss.so" line in auth section of "/etc/pam.d/system-auth" file to include +"try_cert_auth" or "require_cert_auth" option, like in the following example: + +/etc/pam.d/system-auth:auth [success=done authinfo_unavail=ignore ignore=ignore default=die] pam_sss.so try_cert_auth + +Also add or update "pam_sss.so" line in auth section of "/etc/pam.d/smartcard-auth" file to +include the "allow_missing_name" option, like in the following example: +/etc/pam.d/smartcard-auth:auth sufficient pam_sss.so allow_missing_name + CCI-001954 + CCI-000765 + CCI-000766 + CCI-000767 + CCI-000768 + 0421 + 0422 + 0431 + 0974 + 1173 + 1401 + 1504 + 1505 + 1546 + 1557 + 1558 + 1559 + 1560 + 1561 + SRG-OS-000375-GPOS-00160 + SRG-OS-000105-GPOS-00052 + SRG-OS-000106-GPOS-00053 + SRG-OS-000107-GPOS-00054 + SRG-OS-000108-GPOS-00055 + SRG-OS-000107-VMM-000530 + Using an authentication device, such as a CAC or token that is separate from +the information system, ensures that even if the information system is +compromised, that compromise will not affect credentials stored on the +authentication device. + +Multi-Factor Authentication (MFA) solutions that require devices separate from +information systems gaining access include, for example, hardware tokens +providing time-based or challenge-response authenticators and smart cards such +as the U.S. Government Personal Identity Verification card and the DoD Common +Access Card. + + CCE-89155-6 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-89155-6 + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - sssd_enable_smartcards + +- name: Test for domain group + command: grep '^\s*\[domain\/[^]]*]' /etc/sssd/sssd.conf + register: test_grep_domain + ignore_errors: true + changed_when: false + check_mode: false + when: + - '"sssd-common" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-89155-6 + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - sssd_enable_smartcards + +- name: Add default domain group (if no domain there) + ini_file: + path: /etc/sssd/sssd.conf + section: '{{ item.section }}' + option: '{{ item.option }}' + value: '{{ item.value }}' + create: true + mode: 384 + with_items: + - section: sssd + option: domains + value: default + - section: domain/default + option: id_provider + value: files + when: + - '"sssd-common" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - test_grep_domain.stdout is defined + - test_grep_domain.stdout | length < 1 + tags: + - CCE-89155-6 + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - sssd_enable_smartcards + +- name: Enable Smartcards in SSSD + ini_file: + dest: /etc/sssd/sssd.conf + section: pam + option: pam_cert_auth + value: 'True' + create: true + mode: 384 + when: + - '"sssd-common" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-89155-6 + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - sssd_enable_smartcards + +- name: Enable Smartcards in SSSD - Check if system relies on authselect + ansible.builtin.stat: + path: /usr/bin/authselect + register: result_authselect_present + when: + - '"sssd-common" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-89155-6 + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - sssd_enable_smartcards + +- name: Enable Smartcards in SSSD - Remediate using authselect + block: + + - name: Enable Smartcards in SSSD - Check integrity of authselect current profile + ansible.builtin.command: + cmd: authselect check + register: result_authselect_check_cmd + changed_when: false + ignore_errors: true + + - name: Enable Smartcards in SSSD - Informative message based on the authselect + integrity check result + ansible.builtin.assert: + that: + - result_authselect_check_cmd is success + fail_msg: + - authselect integrity check failed. Remediation aborted! + - This remediation could not be applied because an authselect profile was not + selected or the selected profile is not intact. + - It is not recommended to manually edit the PAM files when authselect tool + is available. + - In cases where the default authselect profile does not cover a specific demand, + a custom authselect profile is recommended. + success_msg: + - authselect integrity check passed + + - name: Enable Smartcards in SSSD - Get authselect current features + ansible.builtin.shell: + cmd: authselect current | tail -n+3 | awk '{ print $2 }' + register: result_authselect_features + changed_when: false + when: + - result_authselect_check_cmd is success + + - name: Enable Smartcards in SSSD - Ensure "with-smartcard" feature is enabled using + authselect tool + ansible.builtin.command: + cmd: authselect enable-feature with-smartcard + register: result_authselect_enable_feature_cmd + when: + - result_authselect_check_cmd is success + - result_authselect_features.stdout is not search("with-smartcard") + + - name: Enable Smartcards in SSSD - Ensure authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: + - result_authselect_enable_feature_cmd is not skipped + - result_authselect_enable_feature_cmd is success + when: + - '"sssd-common" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - result_authselect_present.stat.exists + tags: + - CCE-89155-6 + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - sssd_enable_smartcards + +- name: Enable Smartcards in SSSD - Remediate by directly editing PAM files + block: + + - name: Enable Smartcards in SSSD - Check if expected PAM module line is present + in /etc/pam.d/smartcard-auth + ansible.builtin.lineinfile: + path: /etc/pam.d/smartcard-auth + regexp: ^\s*auth\s+sufficient\s+pam_sss.so\s*.* + state: absent + check_mode: true + changed_when: false + register: result_pam_line_present + + - name: Enable Smartcards in SSSD - Include or update the PAM module line in /etc/pam.d/smartcard-auth + block: + + - name: Enable Smartcards in SSSD - Check if required PAM module line is present + in /etc/pam.d/smartcard-auth with different control + ansible.builtin.lineinfile: + path: /etc/pam.d/smartcard-auth + regexp: ^\s*auth\s+.*\s+pam_sss.so\s* + state: absent + check_mode: true + changed_when: false + register: result_pam_line_other_control_present + + - name: Enable Smartcards in SSSD - Ensure the correct control for the required + PAM module line in /etc/pam.d/smartcard-auth + ansible.builtin.replace: + dest: /etc/pam.d/smartcard-auth + regexp: ^(\s*auth\s+).*(\bpam_sss.so.*) + replace: \1sufficient \2 + register: result_pam_module_edit + when: + - result_pam_line_other_control_present.found == 1 + + - name: Enable Smartcards in SSSD - Ensure the required PAM module line is included + in /etc/pam.d/smartcard-auth + ansible.builtin.lineinfile: + dest: /etc/pam.d/smartcard-auth + line: auth sufficient pam_sss.so + register: result_pam_module_add + when: + - result_pam_line_other_control_present.found == 0 or result_pam_line_other_control_present.found + > 1 + + - name: Enable Smartcards in SSSD - Ensure authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: | + result_authselect_present is defined and result_authselect_present.stat.exists and ((result_pam_module_add is defined and result_pam_module_add.changed) or (result_pam_module_edit is defined and result_pam_module_edit.changed)) + when: + - result_pam_line_present.found is defined + - result_pam_line_present.found == 0 + + - name: Enable Smartcards in SSSD - Check if the required PAM module option is present + in /etc/pam.d/smartcard-auth + ansible.builtin.lineinfile: + path: /etc/pam.d/smartcard-auth + regexp: ^\s*auth\s+sufficient\s+pam_sss.so\s*.*\sallow_missing_name\b + state: absent + check_mode: true + changed_when: false + register: result_pam_module_allow_missing_name_option_present + + - name: Enable Smartcards in SSSD - Ensure the "allow_missing_name" PAM option for + "pam_sss.so" is included in /etc/pam.d/smartcard-auth + ansible.builtin.lineinfile: + path: /etc/pam.d/smartcard-auth + backrefs: true + regexp: ^(\s*auth\s+sufficient\s+pam_sss.so.*) + line: \1 allow_missing_name + state: present + register: result_pam_allow_missing_name_add + when: + - result_pam_module_allow_missing_name_option_present.found == 0 + + - name: Enable Smartcards in SSSD - Check if expected PAM module line is present + in /etc/pam.d/system-auth + ansible.builtin.lineinfile: + path: /etc/pam.d/system-auth + regexp: ^\s*auth\s+\[success=done authinfo_unavail=ignore ignore=ignore default=die\]\s+pam_sss.so\s*.* + state: absent + check_mode: true + changed_when: false + register: result_pam_line_present + + - name: Enable Smartcards in SSSD - Include or update the PAM module line in /etc/pam.d/system-auth + block: + + - name: Enable Smartcards in SSSD - Check if required PAM module line is present + in /etc/pam.d/system-auth with different control + ansible.builtin.lineinfile: + path: /etc/pam.d/system-auth + regexp: ^\s*auth\s+.*\s+pam_sss.so\s* + state: absent + check_mode: true + changed_when: false + register: result_pam_line_other_control_present + + - name: Enable Smartcards in SSSD - Ensure the correct control for the required + PAM module line in /etc/pam.d/system-auth + ansible.builtin.replace: + dest: /etc/pam.d/system-auth + regexp: ^(\s*auth\s+).*(\bpam_sss.so.*) + replace: \1\[success=done authinfo_unavail=ignore ignore=ignore default=die\] + \2 + register: result_pam_module_edit + when: + - result_pam_line_other_control_present.found == 1 + + - name: Enable Smartcards in SSSD - Ensure the required PAM module line is included + in /etc/pam.d/system-auth + ansible.builtin.lineinfile: + dest: /etc/pam.d/system-auth + line: auth \[success=done authinfo_unavail=ignore ignore=ignore default=die\] pam_sss.so + register: result_pam_module_add + when: + - result_pam_line_other_control_present.found == 0 or result_pam_line_other_control_present.found + > 1 + + - name: Enable Smartcards in SSSD - Ensure authselect changes are applied + ansible.builtin.command: + cmd: authselect apply-changes -b + when: | + result_authselect_present is defined and result_authselect_present.stat.exists and ((result_pam_module_add is defined and result_pam_module_add.changed) or (result_pam_module_edit is defined and result_pam_module_edit.changed)) + when: + - result_pam_line_present.found is defined + - result_pam_line_present.found == 0 + + - name: Enable Smartcards in SSSD - Check if the required PAM module option is present + in /etc/pam.d/system-auth + ansible.builtin.lineinfile: + path: /etc/pam.d/system-auth + regexp: ^\s*auth\s+\[success=done authinfo_unavail=ignore ignore=ignore default=die\]\s+pam_sss.so\s*.*\stry_cert_auth\b + state: absent + check_mode: true + changed_when: false + register: result_pam_module_try_cert_auth_option_present + + - name: Enable Smartcards in SSSD - Ensure the "try_cert_auth" PAM option for "pam_sss.so" + is included in /etc/pam.d/system-auth + ansible.builtin.lineinfile: + path: /etc/pam.d/system-auth + backrefs: true + regexp: ^(\s*auth\s+\[success=done authinfo_unavail=ignore ignore=ignore default=die\]\s+pam_sss.so.*) + line: \1 try_cert_auth + state: present + register: result_pam_try_cert_auth_add + when: + - result_pam_module_try_cert_auth_option_present.found == 0 + when: + - '"sssd-common" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - not result_authselect_present.stat.exists + tags: + - CCE-89155-6 + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - sssd_enable_smartcards + + # Remediation is applicable only in certain platforms +if rpm --quiet -q sssd-common && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +found=false + +# set value in all files if they contain section or key +for f in $(echo -n "/etc/sssd/sssd.conf"); do + if [ ! -e "$f" ]; then + continue + fi + + # find key in section and change value + if grep -qzosP "[[:space:]]*\[pam\]([^\n\[]*\n+)+?[[:space:]]*pam_cert_auth" "$f"; then + sed -i "s/pam_cert_auth[^(\n)]*/pam_cert_auth = True/" "$f" + found=true + + # find section and add key = value to it + elif grep -qs "[[:space:]]*\[pam\]" "$f"; then + sed -i "/[[:space:]]*\[pam\]/a pam_cert_auth = True" "$f" + found=true + fi +done + +# if section not in any file, append section with key = value to FIRST file in files parameter +if ! $found ; then + file=$(echo "/etc/sssd/sssd.conf" | cut -f1 -d ' ') + mkdir -p "$(dirname "$file")" + echo -e "[pam]\npam_cert_auth = True" >> "$file" +fi + + +if [ -f /usr/bin/authselect ]; then + if authselect check; then + if ! authselect check; then + echo " + authselect integrity check failed. Remediation aborted! + This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact. + It is not recommended to manually edit the PAM files when authselect tool is available. + In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended." + exit 1 + fi + authselect enable-feature with-smartcard + + authselect apply-changes -b + fi +else + if ! grep -qP '^\s*auth\s+'"sufficient"'\s+pam_sss.so\s*.*' "/etc/pam.d/smartcard-auth"; then + # Line matching group + control + module was not found. Check group + module. + if [ "$(grep -cP '^\s*auth\s+.*\s+pam_sss.so\s*' "/etc/pam.d/smartcard-auth")" -eq 1 ]; then + # The control is updated only if one single line matches. + sed -i -E --follow-symlinks 's/^(\s*auth\s+).*(\bpam_sss.so.*)/\1'"sufficient"' \2/' "/etc/pam.d/smartcard-auth" + else + echo 'auth '"sufficient"' pam_sss.so' >> "/etc/pam.d/smartcard-auth" + fi + fi + # Check the option + if ! grep -qP '^\s*auth\s+'"sufficient"'\s+pam_sss.so\s*.*\sallow_missing_name\b' "/etc/pam.d/smartcard-auth"; then + sed -i -E --follow-symlinks '/\s*auth\s+'"sufficient"'\s+pam_sss.so.*/ s/$/ allow_missing_name/' "/etc/pam.d/smartcard-auth" + fi + if ! grep -qP '^\s*auth\s+'"\[success=done authinfo_unavail=ignore ignore=ignore default=die\]"'\s+pam_sss.so\s*.*' "/etc/pam.d/system-auth"; then + # Line matching group + control + module was not found. Check group + module. + if [ "$(grep -cP '^\s*auth\s+.*\s+pam_sss.so\s*' "/etc/pam.d/system-auth")" -eq 1 ]; then + # The control is updated only if one single line matches. + sed -i -E --follow-symlinks 's/^(\s*auth\s+).*(\bpam_sss.so.*)/\1'"\[success=done authinfo_unavail=ignore ignore=ignore default=die\]"' \2/' "/etc/pam.d/system-auth" + else + echo 'auth '"\[success=done authinfo_unavail=ignore ignore=ignore default=die\]"' pam_sss.so' >> "/etc/pam.d/system-auth" + fi + fi + # Check the option + if ! grep -qP '^\s*auth\s+'"\[success=done authinfo_unavail=ignore ignore=ignore default=die\]"'\s+pam_sss.so\s*.*\stry_cert_auth\b' "/etc/pam.d/system-auth"; then + sed -i -E --follow-symlinks '/\s*auth\s+'"\[success=done authinfo_unavail=ignore ignore=ignore default=die\]"'\s+pam_sss.so.*/ s/$/ try_cert_auth/' "/etc/pam.d/system-auth" + fi +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + SSSD Has a Correct Trust Anchor + SSSD must have acceptable trust anchor present. + Automatic remediation of this control is not available. + CCI-000185 + IA-5 (2) (a) + SRG-OS-000066-GPOS-00034 + SRG-OS-000384-GPOS-00167 + Without path validation, an informed trust decision by the relying party cannot be made when +presented with any certificate not already explicitly trusted. + +A trust anchor is an authoritative entity represented via a public key and associated data. It +is used in the context of public key infrastructures, X.509 digital certificates, and DNSSEC. + +When there is a chain of trust, usually the top entity to be trusted becomes the trust anchor; +it can be, for example, a Certification Authority (CA). A certification path starts with the +subject certificate and proceeds through a number of intermediate certificates up to a trusted +root certificate, typically issued by a trusted CA. + +This requirement verifies that a certification path to an accepted trust anchor is used for +certificate validation and that the path includes status information. Path validation is +necessary for a relying party to make an informed trust decision when presented with any +certificate not already explicitly trusted. Status information for certification paths includes +certificate revocation lists or online certificate status protocol responses. +Validation of the certificate status information is out of scope for this requirement. + CCE-86321-7 + + + + + + Configure SSSD to Expire Offline Credentials + SSSD should be configured to expire offline credentials after 1 day. + +To configure SSSD to expire offline credentials, set +offline_credentials_expiration to 1 under the [pam] +section in /etc/sssd/sssd.conf. For example: +[pam] +offline_credentials_expiration = 1 + + 1 + 12 + 15 + 16 + 5 + DSS05.04 + DSS05.05 + DSS05.07 + DSS05.10 + DSS06.03 + DSS06.10 + CCI-002007 + 4.3.3.2.2 + 4.3.3.5.1 + 4.3.3.5.2 + 4.3.3.6.1 + 4.3.3.6.2 + 4.3.3.6.3 + 4.3.3.6.4 + 4.3.3.6.5 + 4.3.3.6.6 + 4.3.3.6.7 + 4.3.3.6.8 + 4.3.3.6.9 + 4.3.3.7.2 + 4.3.3.7.4 + SR 1.1 + SR 1.10 + SR 1.2 + SR 1.3 + SR 1.4 + SR 1.5 + SR 1.7 + SR 1.8 + SR 1.9 + SR 2.1 + A.18.1.4 + A.7.1.1 + A.9.2.1 + A.9.2.2 + A.9.2.3 + A.9.2.4 + A.9.2.6 + A.9.3.1 + A.9.4.2 + A.9.4.3 + CM-6(a) + IA-5(13) + PR.AC-1 + PR.AC-6 + PR.AC-7 + SRG-OS-000383-GPOS-00166 + SRG-OS-000383-VMM-001570 + If cached authentication information is out-of-date, the validity of the +authentication information may be questionable. + + CCE-87996-5 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-87996-5 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(13) + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - sssd_offline_cred_expiration + +- name: Test for domain group + command: grep '\s*\[domain\/[^]]*]' /etc/sssd/sssd.conf + register: test_grep_domain + ignore_errors: true + changed_when: false + check_mode: false + when: + - '"sssd-common" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-87996-5 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(13) + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - sssd_offline_cred_expiration + +- name: Add default domain group (if no domain there) + ini_file: + path: /etc/sssd/sssd.conf + section: '{{ item.section }}' + option: '{{ item.option }}' + value: '{{ item.value }}' + create: true + mode: 384 + with_items: + - section: sssd + option: domains + value: default + - section: domain/default + option: id_provider + value: files + when: + - '"sssd-common" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - test_grep_domain.stdout is defined + - test_grep_domain.stdout | length < 1 + tags: + - CCE-87996-5 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(13) + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - sssd_offline_cred_expiration + +- name: Configure SSD to Expire Offline Credentials + ini_file: + dest: /etc/sssd/sssd.conf + section: pam + option: offline_credentials_expiration + value: 1 + create: true + mode: 384 + when: + - '"sssd-common" in ansible_facts.packages' + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-87996-5 + - NIST-800-53-CM-6(a) + - NIST-800-53-IA-5(13) + - configure_strategy + - low_complexity + - medium_disruption + - medium_severity + - no_reboot_needed + - sssd_offline_cred_expiration + + # Remediation is applicable only in certain platforms +if rpm --quiet -q sssd-common && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +found=false + +# set value in all files if they contain section or key +for f in $(echo -n "/etc/sssd/sssd.conf"); do + if [ ! -e "$f" ]; then + continue + fi + + # find key in section and change value + if grep -qzosP "[[:space:]]*\[pam\]([^\n\[]*\n+)+?[[:space:]]*offline_credentials_expiration" "$f"; then + sed -i "s/offline_credentials_expiration[^(\n)]*/offline_credentials_expiration = 1/" "$f" + found=true + + # find section and add key = value to it + elif grep -qs "[[:space:]]*\[pam\]" "$f"; then + sed -i "/[[:space:]]*\[pam\]/a offline_credentials_expiration = 1" "$f" + found=true + fi +done + +# if section not in any file, append section with key = value to FIRST file in files parameter +if ! $found ; then + file=$(echo "/etc/sssd/sssd.conf" | cut -f1 -d ' ') + mkdir -p "$(dirname "$file")" + echo -e "[pam]\noffline_credentials_expiration = 1" >> "$file" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + System Security Services Daemon (SSSD) - LDAP + The System Security Services Daemon (SSSD) is a system daemon that provides access +to different identity and authentication providers such as Red Hat's IdM, Microsoft's AD, +openLDAP, MIT Kerberos, etc. It uses a common framework that can provide caching and offline +support to systems utilizing SSSD. SSSD using caching to reduce load on authentication +servers permit offline authentication as well as store extended user data. + +SSSD can support many backends including LDAP. The sssd-ldap backend +allows SSSD to fetch identity information from an LDAP server. + + + SSSD LDAP Backend Client CA Certificate Location + Path of a directory that contains Certificate Authority certificates. + /etc/openldap/cacerts + + + + + USBGuard daemon + The USBGuard daemon enforces the USB device authorization policy for all USB devices. + + + Install usbguard Package + +The usbguard package can be installed with the following command: + +$ sudo dnf install usbguard + CCI-001958 + 1418 + CM-8(3) + IA-3 + SRG-OS-000378-GPOS-00163 + usbguard is a software framework that helps to protect +against rogue USB devices by implementing basic whitelisting/blacklisting +capabilities based on USB device attributes. + CCE-84203-9 + +package --add=usbguard + + include install_usbguard + +class install_usbguard { + package { 'usbguard': + ensure => 'installed', + } +} + + - name: Ensure usbguard is installed + package: + name: usbguard + state: present + when: ansible_architecture != "s390x" + tags: + - CCE-84203-9 + - NIST-800-53-CM-8(3) + - NIST-800-53-IA-3 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_usbguard_installed + + +[[packages]] +name = "usbguard" +version = "*" + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +spec: + config: + ignition: + version: 3.1.0 + extensions: + - usbguard + + # Remediation is applicable only in certain platforms +if ! grep -q s390x /proc/sys/kernel/osrelease; then + +if ! rpm -q --quiet "usbguard" ; then + dnf install -y "usbguard" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Enable the USBGuard Service + The USBGuard service should be enabled. + +The usbguard service can be enabled with the following command: +$ sudo systemctl enable usbguard.service + CCI-000416 + CCI-001958 + 1418 + CM-8(3)(a) + IA-3 + FMT_SMF_EXT.1 + SRG-OS-000378-GPOS-00163 + The usbguard service must be running in order to +enforce the USB device authorization policy for all USB devices. + + CCE-84205-4 + include enable_usbguard + +class enable_usbguard { + service {'usbguard': + enable => true, + ensure => 'running', + } +} + + - name: Enable service usbguard + block: + + - name: Gather the package facts + package_facts: + manager: auto + + - name: Enable service usbguard + service: + name: usbguard + enabled: 'yes' + state: started + masked: 'no' + when: + - '"usbguard" in ansible_facts.packages' + when: + - ansible_architecture != "s390x" + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + tags: + - CCE-84205-4 + - NIST-800-53-CM-8(3)(a) + - NIST-800-53-IA-3 + - enable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - service_usbguard_enabled + + +[customizations.services] +enabled = ["usbguard"] + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +metadata: + annotations: + complianceascode.io/depends-on: xccdf_org.ssgproject.content_rule_package_usbguard_installed +spec: + config: + ignition: + version: 3.1.0 + systemd: + units: + - name: usbguard.service + enabled: true + + # Remediation is applicable only in certain platforms +if ! grep -q s390x /proc/sys/kernel/osrelease && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +SYSTEMCTL_EXEC='/usr/bin/systemctl' +"$SYSTEMCTL_EXEC" unmask 'usbguard.service' +"$SYSTEMCTL_EXEC" start 'usbguard.service' +"$SYSTEMCTL_EXEC" enable 'usbguard.service' + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Log USBGuard daemon audit events using Linux Audit + To configure USBGuard daemon to log via Linux Audit +(as opposed directly to a file), +AuditBackend option in /etc/usbguard/usbguard-daemon.conf +needs to be set to LinuxAudit. + CCI-000169 + CCI-000172 + AU-2 + CM-8(3) + IA-3 + FMT_SMF_EXT.1 + SRG-OS-000062-GPOS-00031 + SRG-OS-000471-GPOS-00215 + Using the Linux Audit logging allows for centralized trace +of events. + + CCE-84206-2 + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +metadata: + annotations: + complianceascode.io/depends-on: xccdf_org.ssgproject.content_rule_package_usbguard_installed + complianceascode.io/ocp-version: '>=4.7.0' +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,{{ %0A%23%0A%23%20Rule%20set%20file%20path.%0A%23%0A%23%20The%20USBGuard%20daemon%20will%20use%20this%20file%20to%20load%20the%20policy%0A%23%20rule%20set%20from%20it%20and%20to%20write%20new%20rules%20received%20via%20the%0A%23%20IPC%20interface.%0A%23%0A%23%20RuleFile%3D/path/to/rules.conf%0A%23%0ARuleFile%3D/etc/usbguard/rules.conf%0A%0A%23%0A%23%20Rule%20set%20folder%20path.%0A%23%0A%23%20The%20USBGuard%20daemon%20will%20use%20this%20folder%20to%20load%20the%20policy%0A%23%20rule%20set%20from%20it%20and%20to%20write%20new%20rules%20received%20via%20the%0A%23%20IPC%20interface.%20Usually%2C%20we%20set%20the%20option%20to%0A%23%20/etc/usbguard/rules.d/.%20The%20USBGuard%20daemon%20is%20supposed%20to%0A%23%20behave%20like%20any%20other%20standard%20Linux%20daemon%20therefore%20it%0A%23%20loads%20rule%20files%20in%20alpha-numeric%20order.%20File%20names%20inside%0A%23%20RuleFolder%20directory%20should%20start%20with%20a%20two-digit%20number%0A%23%20prefix%20indicating%20the%20position%2C%20in%20which%20the%20rules%20are%0A%23%20scanned%20by%20the%20daemon.%0A%23%0A%23%20RuleFolder%3D/path/to/rulesfolder/%0A%23%0ARuleFolder%3D/etc/usbguard/rules.d/%0A%0A%23%0A%23%20Implicit%20policy%20target.%0A%23%0A%23%20How%20to%20treat%20devices%20that%20don%27t%20match%20any%20rule%20in%20the%0A%23%20policy.%20One%20of%3A%0A%23%0A%23%20%2A%20allow%20%20-%20authorize%20the%20device%0A%23%20%2A%20block%20%20-%20block%20the%20device%0A%23%20%2A%20reject%20-%20remove%20the%20device%0A%23%0AImplicitPolicyTarget%3Dblock%0A%0A%23%0A%23%20Present%20device%20policy.%0A%23%0A%23%20How%20to%20treat%20devices%20that%20are%20already%20connected%20when%20the%0A%23%20daemon%20starts.%20One%20of%3A%0A%23%0A%23%20%2A%20allow%20%20%20%20%20%20%20%20-%20authorize%20every%20present%20device%0A%23%20%2A%20block%20%20%20%20%20%20%20%20-%20deauthorize%20every%20present%20device%0A%23%20%2A%20reject%20%20%20%20%20%20%20-%20remove%20every%20present%20device%0A%23%20%2A%20keep%20%20%20%20%20%20%20%20%20-%20just%20sync%20the%20internal%20state%20and%20leave%20it%0A%23%20%2A%20apply-policy%20-%20evaluate%20the%20ruleset%20for%20every%20present%0A%23%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20device%0A%23%0APresentDevicePolicy%3Dapply-policy%0A%0A%23%0A%23%20Present%20controller%20policy.%0A%23%0A%23%20How%20to%20treat%20USB%20controllers%20that%20are%20already%20connected%0A%23%20when%20the%20daemon%20starts.%20One%20of%3A%0A%23%0A%23%20%2A%20allow%20%20%20%20%20%20%20%20-%20authorize%20every%20present%20device%0A%23%20%2A%20block%20%20%20%20%20%20%20%20-%20deauthorize%20every%20present%20device%0A%23%20%2A%20reject%20%20%20%20%20%20%20-%20remove%20every%20present%20device%0A%23%20%2A%20keep%20%20%20%20%20%20%20%20%20-%20just%20sync%20the%20internal%20state%20and%20leave%20it%0A%23%20%2A%20apply-policy%20-%20evaluate%20the%20ruleset%20for%20every%20present%0A%23%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20device%0A%23%0APresentControllerPolicy%3Dkeep%0A%0A%23%0A%23%20Inserted%20device%20policy.%0A%23%0A%23%20How%20to%20treat%20USB%20devices%20that%20are%20already%20connected%0A%23%20%2Aafter%2A%20the%20daemon%20starts.%20One%20of%3A%0A%23%0A%23%20%2A%20block%20%20%20%20%20%20%20%20-%20deauthorize%20every%20present%20device%0A%23%20%2A%20reject%20%20%20%20%20%20%20-%20remove%20every%20present%20device%0A%23%20%2A%20apply-policy%20-%20evaluate%20the%20ruleset%20for%20every%20present%0A%23%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20device%0A%23%0AInsertedDevicePolicy%3Dapply-policy%0A%0A%23%0A%23%20Control%20which%20devices%20are%20authorized%20by%20default.%0A%23%0A%23%20The%20USBGuard%20daemon%20modifies%20some%20the%20default%20authorization%20state%20attributes%0A%23%20of%20controller%20devices.%20This%20setting%2C%20enables%20you%20to%20define%20what%20value%20the%0A%23%20default%20authorization%20is%20set%20to.%0A%23%0A%23%20%2A%20keep%20%20%20%20%20%20%20%20%20-%20do%20not%20change%20the%20authorization%20state%0A%23%20%2A%20none%20%20%20%20%20%20%20%20%20-%20every%20new%20device%20starts%20out%20deauthorized%0A%23%20%2A%20all%20%20%20%20%20%20%20%20%20%20-%20every%20new%20device%20starts%20out%20authorized%0A%23%20%2A%20internal%20%20%20%20%20-%20internal%20devices%20start%20out%20authorized%2C%20external%20devices%20start%0A%23%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20out%20deauthorized%20%28this%20requires%20the%20ACPI%20tables%20to%20properly%0A%23%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20label%20internal%20devices%2C%20and%20kernel%20support%29%0A%23%0A%23AuthorizedDefault%3Dnone%0A%0A%23%0A%23%20Restore%20controller%20device%20state.%0A%23%0A%23%20The%20USBGuard%20daemon%20modifies%20some%20attributes%20of%20controller%0A%23%20devices%20like%20the%20default%20authorization%20state%20of%20new%20child%20device%0A%23%20instances.%20Using%20this%20setting%2C%20you%20can%20control%20whether%20the%0A%23%20daemon%20will%20try%20to%20restore%20the%20attribute%20values%20to%20the%20state%0A%23%20before%20modification%20on%20shutdown.%0A%23%0A%23%20SECURITY%20CONSIDERATIONS%3A%20If%20set%20to%20true%2C%20the%20USB%20authorization%0A%23%20policy%20could%20be%20bypassed%20by%20performing%20some%20sort%20of%20attack%20on%20the%0A%23%20daemon%20%28via%20a%20local%20exploit%20or%20via%20a%20USB%20device%29%20to%20make%20it%20shutdown%0A%23%20and%20restore%20to%20the%20operating-system%20default%20state%20%28known%20to%20be%20permissive%29.%0A%23%0ARestoreControllerDeviceState%3Dfalse%0A%0A%23%0A%23%20Device%20manager%20backend%0A%23%0A%23%20Which%20device%20manager%20backend%20implementation%20to%20use.%20One%20of%3A%0A%23%0A%23%20%2A%20uevent%20%20%20-%20Netlink%20based%20implementation%20which%20uses%20sysfs%20to%20scan%20for%20present%0A%23%20%20%20%20%20%20%20%20%20%20%20%20%20%20devices%20and%20an%20uevent%20netlink%20socket%20for%20receiving%20USB%20device%0A%23%20%20%20%20%20%20%20%20%20%20%20%20%20%20related%20events.%0A%23%20%2A%20umockdev%20-%20umockdev%20based%20device%20manager%20capable%20of%20simulating%20devices%20based%0A%23%20%20%20%20%20%20%20%20%20%20%20%20%20%20on%20umockdev-record%20files.%20Useful%20for%20testing.%0A%23%0ADeviceManagerBackend%3Duevent%0A%0A%23%21%21%21%20WARNING%3A%20It%27s%20good%20practice%20to%20set%20at%20least%20one%20of%20the%20%21%21%21%0A%23%21%21%21%20%20%20%20%20%20%20%20%20%20two%20options%20bellow.%20If%20none%20of%20them%20are%20set%2C%20%20%21%21%21%0A%23%21%21%21%20%20%20%20%20%20%20%20%20%20the%20daemon%20will%20accept%20IPC%20connections%20from%20%20%20%21%21%21%0A%23%21%21%21%20%20%20%20%20%20%20%20%20%20anyone%2C%20thus%20allowing%20anyone%20to%20modify%20the%20%20%20%20%21%21%21%0A%23%21%21%21%20%20%20%20%20%20%20%20%20%20rule%20set%20and%20%28de%29authorize%20USB%20devices.%20%20%20%20%20%20%20%21%21%21%0A%0A%23%0A%23%20Users%20allowed%20to%20use%20the%20IPC%20interface.%0A%23%0A%23%20A%20space%20delimited%20list%20of%20usernames%20that%20the%20daemon%20will%0A%23%20accept%20IPC%20connections%20from.%0A%23%0A%23%20IPCAllowedUsers%3Dusername1%20username2%20...%0A%23%0AIPCAllowedUsers%3Droot%0A%0A%23%0A%23%20Groups%20allowed%20to%20use%20the%20IPC%20interface.%0A%23%0A%23%20A%20space%20delimited%20list%20of%20groupnames%20that%20the%20daemon%20will%0A%23%20accept%20IPC%20connections%20from.%0A%23%0A%23%20IPCAllowedGroups%3Dgroupname1%20groupname2%20...%0A%23%0AIPCAllowedGroups%3Dwheel%0A%0A%23%0A%23%20IPC%20access%20control%20definition%20files%20path.%0A%23%0A%23%20The%20files%20at%20this%20location%20will%20be%20interpreted%20by%20the%20daemon%0A%23%20as%20access%20control%20definition%20files.%20The%20%28base%29name%20of%20a%20file%0A%23%20should%20be%20in%20the%20form%3A%0A%23%0A%23%20%20%20%5Buser%5D%5B%3A%3Cgroup%3E%5D%0A%23%0A%23%20and%20should%20contain%20lines%20in%20the%20form%3A%0A%23%0A%23%20%20%20%3Csection%3E%3D%5Bprivilege%5D%20...%0A%23%0A%23%20This%20way%20each%20file%20defines%20who%20is%20able%20to%20connect%20to%20the%20IPC%0A%23%20bus%20and%20what%20privileges%20he%20has.%0A%23%0AIPCAccessControlFiles%3D/etc/usbguard/IPCAccessControl.d/%0A%0A%23%0A%23%20Generate%20device%20specific%20rules%20including%20the%20%22via-port%22%0A%23%20attribute.%0A%23%0A%23%20This%20option%20modifies%20the%20behavior%20of%20the%20allowDevice%0A%23%20action.%20When%20instructed%20to%20generate%20a%20permanent%20rule%2C%0A%23%20the%20action%20can%20generate%20a%20port%20specific%20rule.%20Because%0A%23%20some%20systems%20have%20unstable%20port%20numbering%2C%20the%20generated%0A%23%20rule%20might%20not%20match%20the%20device%20after%20rebooting%20the%20system.%0A%23%0A%23%20If%20set%20to%20false%2C%20the%20generated%20rule%20will%20still%20contain%0A%23%20the%20%22parent-hash%22%20attribute%20which%20also%20defines%20an%20association%0A%23%20to%20the%20parent%20device.%20See%20usbguard-rules.conf%285%29%20for%20more%0A%23%20details.%0A%23%0ADeviceRulesWithPort%3Dfalse%0A%0A%23%0A%23%20USBGuard%20Audit%20events%20log%20backend%0A%23%0A%23%20One%20of%3A%0A%23%0A%23%20%2A%20FileAudit%20-%20Log%20audit%20events%20into%20a%20file%20specified%20by%0A%23%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20AuditFilePath%20setting%20%28see%20below%29%0A%23%20%2A%20LinuxAudit%20-%20Log%20audit%20events%20using%20the%20Linux%20Audit%0A%23%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20subsystem%20%28using%20audit_log_user_message%29%0A%23%0AAuditBackend%3DLinuxAudit%0A%0A%23%0A%23%20USBGuard%20audit%20events%20log%20file%20path.%0A%23%0A%23AuditFilePath%3D/var/log/usbguard/usbguard-audit.log%0A%0A%23%0A%23%20Hides%20personally%20identifiable%20information%20such%20as%20device%20serial%20numbers%20and%0A%23%20hashes%20of%20descriptors%20%28which%20include%20the%20serial%20number%29%20from%20audit%20entries.%0A%23%0A%23HidePII%3Dfalse }} + mode: 0600 + path: /etc/usbguard/usbguard-daemon.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if ! grep -q s390x /proc/sys/kernel/osrelease && { rpm --quiet -q usbguard; }; then + +if [ -e "/etc/usbguard/usbguard-daemon.conf" ] ; then + + LC_ALL=C sed -i "/^\s*AuditBackend=/d" "/etc/usbguard/usbguard-daemon.conf" +else + touch "/etc/usbguard/usbguard-daemon.conf" +fi +# make sure file has newline at the end +sed -i -e '$a\' "/etc/usbguard/usbguard-daemon.conf" + +cp "/etc/usbguard/usbguard-daemon.conf" "/etc/usbguard/usbguard-daemon.conf.bak" +# Insert at the end of the file +printf '%s\n' "AuditBackend=LinuxAudit" >> "/etc/usbguard/usbguard-daemon.conf" +# Clean up after ourselves. +rm "/etc/usbguard/usbguard-daemon.conf.bak" + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Authorize Human Interface Devices in USBGuard daemon + To allow authorization of Human Interface Devices (keyboard, mouse) +by USBGuard daemon, +add the line +allow with-interface match-all { 03:*:* } +to /etc/usbguard/rules.conf. + This rule should be understood primarily as a convenience administration feature. This rule ensures that if the USBGuard default rules.conf file is present, it will alter it so that USB human interface devices are allowed. However, if the rules.conf file is altered by system administrator, the rule does not check if USB human interface devices are allowed. This assumes that an administrator modified the file with some purpose in mind. + FMT_SMF_EXT.1 + SRG-OS-000114-GPOS-00059 + Without allowing Human Interface Devices, it might not be possible +to interact with the system. + CCE-85990-0 + - name: allow HID devices + lineinfile: + path: /etc/usbguard/rules.conf + create: true + line: allow with-interface match-all { 03:*:* } + state: present + when: ansible_architecture != "s390x" + tags: + - CCE-85990-0 + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - usbguard_allow_hid + + # Remediation is applicable only in certain platforms +if ! grep -q s390x /proc/sys/kernel/osrelease; then + +# path of file with Usbguard rules +rulesfile="/etc/usbguard/rules.conf" + +echo "allow with-interface match-all { 03:*:* }" >> $rulesfile + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Authorize Human Interface Devices and USB hubs in USBGuard daemon + To allow authorization of USB devices combining human interface device and hub capabilities +by USBGuard daemon, +add the line +allow with-interface match-all { 03:*:* 09:00:* } +to /etc/usbguard/rules.conf. + This rule should be understood primarily as a convenience administration feature. This rule ensures that if the USBGuard default rules.conf file is present, it will alter it so that USB human interface devices and hubs are allowed. However, if the rules.conf file is altered by system administrator, the rule does not check if USB human interface devices and hubs are allowed. This assumes that an administrator modified the file with some purpose in mind. + CM-8(3) + IA-3 + FMT_SMF_EXT.1 + SRG-OS-000114-GPOS-00059 + Without allowing Human Interface Devices, it might not be possible +to interact with the system. Without allowing hubs, it might not be possible to use any +USB devices on the system. + CCE-84210-4 + - name: allow HID devices and hubs + lineinfile: + path: /etc/usbguard/rules.conf + create: true + line: allow with-interface match-all { 03:*:* 09:00:* } + state: present + when: ansible_architecture != "s390x" + tags: + - CCE-84210-4 + - NIST-800-53-CM-8(3) + - NIST-800-53-IA-3 + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - usbguard_allow_hid_and_hub + + --- +apiVersion: machineconfiguration.openshift.io/v1 +kind: MachineConfig +metadata: + annotations: + complianceascode.io/depends-on: xccdf_org.ssgproject.content_rule_package_usbguard_installed +spec: + config: + ignition: + version: 3.1.0 + storage: + files: + - contents: + source: data:,{{ %0Aallow%20with-interface%20match-all%20%7B%2003%3A%2A%3A%2A%2009%3A00%3A%2A%20%7D }} + mode: 0600 + path: /etc/usbguard/rules.d/75-hid-and-hub.conf + overwrite: true + + # Remediation is applicable only in certain platforms +if ! grep -q s390x /proc/sys/kernel/osrelease; then + +echo "allow with-interface match-all { 03:*:* 09:00:* }" >> /etc/usbguard/rules.conf + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Authorize USB hubs in USBGuard daemon + To allow authorization of USB hub devices by USBGuard daemon, +add line +allow with-interface match-all { 09:00:* } +to /etc/usbguard/rules.conf. + This rule should be understood primarily as a convenience administration feature. This rule ensures that if the USBGuard default rules.conf file is present, it will alter it so that USB hub devices are allowed. However, if the rules.conf file is altered by system administrator, the rule does not check if USB hub devices are allowed. This assumes that an administrator modified the file with some purpose in mind. + FMT_SMF_EXT.1 + SRG-OS-000114-GPOS-00059 + Without allowing hubs, it might not be possible to use any +USB devices on the system. + - name: allow hubs + lineinfile: + path: /etc/usbguard/rules.conf + create: true + line: allow with-interface match-all { 09:00:* } + state: present + when: ansible_architecture != "s390x" + tags: + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - usbguard_allow_hub + + # Remediation is applicable only in certain platforms +if ! grep -q s390x /proc/sys/kernel/osrelease; then + +echo "allow with-interface match-all { 09:00:* }" >> /etc/usbguard/rules.conf + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + Generate USBGuard Policy + By default USBGuard when enabled prevents access to all USB devices and this lead +to inaccessible system if they use USB mouse/keyboard. To prevent this scenario, +the initial policy configuration must be generated based on current connected USB +devices. + CCI-000416 + CCI-001958 + CM-8(3)(a) + IA-3 + FMT_SMF_EXT.1 + SRG-OS-000378-GPOS-00163 + The usbguard must be configured to allow connected USB devices to work +properly, avoiding the system to become inaccessible. + + CCE-88882-6 + - name: Gather the package facts + package_facts: + manager: auto + tags: + - CCE-88882-6 + - NIST-800-53-CM-8(3)(a) + - NIST-800-53-IA-3 + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - usbguard_generate_policy + +- name: Generate USBGuard Policy + block: + + - name: Gather the package facts + package_facts: + manager: auto + + - name: Check that the /etc/usbguard/rules.conf exists + stat: + path: /etc/usbguard/rules.conf + register: policy_file + + - name: Create USBGuard Policy configuration + command: usbguard generate-policy + register: policy + when: not policy_file.stat.exists or policy_file.stat.size == 0 + + - name: Copy the Generated Policy configuration to a persistent file + copy: + content: '{{ policy.stdout }}' + dest: /etc/usbguard/rules.conf + mode: 384 + when: not policy_file.stat.exists or policy_file.stat.size == 0 + + - name: Add comment into /etc/usbguard/rules.conf when system has no USB devices + lineinfile: + path: /etc/usbguard/rules.conf + line: '# No USB devices found' + state: present + when: not policy_file.stat.exists or policy_file.stat.size == 0 + + - name: Enable service usbguard + service: + name: usbguard + enabled: 'yes' + state: started + masked: 'no' + when: + - ansible_architecture != "s390x" + - ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"] + - '"usbguard" in ansible_facts.packages' + tags: + - CCE-88882-6 + - NIST-800-53-CM-8(3)(a) + - NIST-800-53-IA-3 + - configure_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - usbguard_generate_policy + + # Remediation is applicable only in certain platforms +if ! grep -q s390x /proc/sys/kernel/osrelease && { [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; }; then + +if rpm --quiet -q usbguard +then + USBGUARD_CONF=/etc/usbguard/rules.conf + if [ ! -f "$USBGUARD_CONF" ] || [ ! -s "$USBGUARD_CONF" ]; then + usbguard generate-policy > $USBGUARD_CONF + if [ ! -s "$USBGUARD_CONF" ]; then + # make sure OVAL check doesn't fail on systems where + # generate-policy doesn't find any USB devices (for + # example a system might not have a USB bus) + echo "# No USB devices found" > $USBGUARD_CONF + fi + # make sure it has correct permissions + chmod 600 $USBGUARD_CONF + + SYSTEMCTL_EXEC='/usr/bin/systemctl' + "$SYSTEMCTL_EXEC" unmask 'usbguard.service' + "$SYSTEMCTL_EXEC" restart 'usbguard.service' + "$SYSTEMCTL_EXEC" enable 'usbguard.service' + fi +else + echo "USBGuard is not installed. No remediation was applied!" +fi + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + X Window System + The X Window System implementation included with the +system is called X.org. + + Disable X Windows + Unless there is a mission-critical reason for the +system to run a graphical user interface, ensure X is not set to start +automatically at boot and remove the X Windows software packages. +There is usually no reason to run X Windows +on a dedicated server system, as it increases the system's attack surface and consumes +system resources. Administrators of server systems should instead login via +SSH or on the text console. + + Remove the X Windows Package Group + By removing the xorg-x11-server-common package, the system no longer has X Windows +installed. If X Windows is not installed then the system cannot boot into graphical user mode. +This prevents the system from being accidentally or maliciously booted into a graphical.target +mode. To do so, run the following command:$ sudo dnf groupremove base-x +$ sudo dnf remove xorg-x11-server-common + The installation and use of a Graphical User Interface (GUI) increases your attack vector and decreases your +overall security posture. Removing the package xorg-x11-server-common package will remove the graphical target +which might bring your system to an inconsistent state requiring additional configuration to access the system +again. If a GUI is an operational requirement, a tailored profile that removes this rule should used before +continuing installation. + 12 + 15 + 8 + APO13.01 + DSS01.04 + DSS05.02 + DSS05.03 + CCI-000366 + 4.3.3.6.6 + SR 1.13 + SR 2.6 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + A.11.2.6 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.6.2.1 + A.6.2.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.AC-3 + PR.PT-4 + SRG-OS-000480-GPOS-00227 + Unnecessary service packages must not be installed to decrease the attack surface of the system. X windows has a long history of security +vulnerabilities and should not be installed unless approved and documented. + CCE-84104-9 + +package --remove=xorg-x11-server-common + + include remove_xorg-x11-server-common + +class remove_xorg-x11-server-common { + package { 'xorg-x11-server-common': + ensure => 'purged', + } +} + + - name: Ensure xorg-x11-server-common is removed + package: + name: xorg-x11-server-common + state: absent + tags: + - CCE-84104-9 + - NIST-800-53-CM-6(a) + - NIST-800-53-CM-7(a) + - NIST-800-53-CM-7(b) + - disable_strategy + - low_complexity + - low_disruption + - medium_severity + - no_reboot_needed + - package_xorg-x11-server-common_removed + + +# CAUTION: This remediation script will remove xorg-x11-server-common +# from the system, and may remove any packages +# that depend on xorg-x11-server-common. Execute this +# remediation AFTER testing on a non-production +# system! + +if rpm -q --quiet "xorg-x11-server-common" ; then + + dnf remove -y "xorg-x11-server-common" + +fi + + + + + + + + + + Disable graphical user interface + By removing the following packages, the system no longer has X Windows installed. + +xorg-x11-server-Xorg xorg-x11-server-common xorg-x11-server-utils xorg-x11-server-Xwayland + +If X Windows is not installed then the system cannot boot into graphical user mode. +This prevents the system from being accidentally or maliciously booted into a graphical.target +mode. To do so, run the following command: + +sudo dnf remove xorg-x11-server-Xorg xorg-x11-server-common xorg-x11-server-utils xorg-x11-server-Xwayland + The installation and use of a Graphical User Interface (GUI) increases your attack vector and decreases your +overall security posture. Removing the package xorg-x11-server-common package will remove the graphical target +which might bring your system to an inconsistent state requiring additional configuration to access the system +again. +The rule xwindows_runlevel_target can be used to configure the system to boot into the multi-user.target. +If a GUI is an operational requirement, a tailored profile that removes this rule should be used before +continuing installation. + CCI-000366 + CM-6(b) + SRG-OS-000480-GPOS-00227 + Unnecessary service packages must not be installed to decrease the attack surface of the system. X windows has a long history of security +vulnerabilities and should not be installed unless approved and documented. + CCE-84106-4 + +package --remove=xorg-x11-server-Xorg --remove=xorg-x11-server-common --remove=xorg-x11-server-utils --remove=xorg-x11-server-Xwayland + + + +# remove packages +if rpm -q --quiet "xorg-x11-server-Xorg" ; then + + dnf remove -y "xorg-x11-server-Xorg" + +fi +if rpm -q --quiet "xorg-x11-server-utils" ; then + + dnf remove -y "xorg-x11-server-utils" + +fi +if rpm -q --quiet "xorg-x11-server-common" ; then + + dnf remove -y "xorg-x11-server-common" + +fi + +if rpm -q --quiet "xorg-x11-server-Xwayland" ; then + + dnf remove -y "xorg-x11-server-Xwayland" + +fi + + + + + + + + + + Disable X Windows Startup By Setting Default Target + Systems that do not require a graphical user interface should only boot by +default into multi-user.target mode. This prevents accidental booting of the system +into a graphical.target mode. Setting the system's default target to +multi-user.target will prevent automatic startup of the X server. To do so, run: +$ systemctl set-default multi-user.target +You should see the following output: +Removed symlink /etc/systemd/system/default.target. +Created symlink from /etc/systemd/system/default.target to /usr/lib/systemd/system/multi-user.target. + 12 + 15 + 8 + APO13.01 + DSS01.04 + DSS05.02 + DSS05.03 + CCI-000366 + 4.3.3.6.6 + SR 1.13 + SR 2.6 + SR 3.1 + SR 3.5 + SR 3.8 + SR 4.1 + SR 4.3 + SR 5.1 + SR 5.2 + SR 5.3 + SR 7.1 + SR 7.6 + A.11.2.6 + A.13.1.1 + A.13.2.1 + A.14.1.3 + A.6.2.1 + A.6.2.2 + CM-7(a) + CM-7(b) + CM-6(a) + PR.AC-3 + PR.PT-4 + SRG-OS-000480-GPOS-00227 + Services that are not required for system and application processes +must not be active to decrease the attack surface of the system. X windows has a +long history of security vulnerabilities and should not be used unless approved +and documented. + + CCE-84105-6 + # Remediation is applicable only in certain platforms +if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then + +systemctl set-default multi-user.target + +else + >&2 echo 'Remediation is not applicable, nothing was done' +fi + + + + + + + + + + + + + Introduction + The purpose of this guidance is to provide security configuration +recommendations and baselines for the Red Hat Enterprise Linux 9 operating +system. Recommended settings for the basic operating system are provided, +as well as for many network services that the system can provide to other systems. +The guide is intended for system administrators. Readers are assumed to +possess basic system administration skills for Unix-like systems, as well +as some familiarity with the product's documentation and administration +conventions. Some instructions within this guide are complex. +All directions should be followed completely and with understanding of +their effects in order to avoid serious adverse effects on the system +and its security. + + General Principles + The following general principles motivate much of the advice in this +guide and should also influence any configuration decisions that are +not explicitly covered. + + Encrypt Transmitted Data Whenever Possible + Data transmitted over a network, whether wired or wireless, is susceptible +to passive monitoring. Whenever practical solutions for encrypting +such data exist, they should be applied. Even if data is expected to +be transmitted only over a local network, it should still be encrypted. +Encrypting authentication data, such as passwords, is particularly +important. Networks of Red Hat Enterprise Linux 9 machines can and should be configured +so that no unencrypted authentication data is ever transmitted between +machines. + + + Least Privilege + Grant the least privilege necessary for user accounts and software to perform tasks. +For example, sudo can be implemented to limit authorization to super user +accounts on the system only to designated personnel. Another example is to limit +logins on server systems to only those administrators who need to log into them in +order to perform administration tasks. Using SELinux also follows the principle of +least privilege: SELinux policy can confine software to perform only actions on the +system that are specifically allowed. This can be far more restrictive than the +actions permissible by the traditional Unix permissions model. + + + Minimize Software to Minimize Vulnerability + The simplest way to avoid vulnerabilities in software is to avoid +installing that software. On Red Hat Enterprise Linux 9,the RPM Package Manager (originally Red Hat Package Manager, abbreviated RPM) +allows for careful management of +the set of software packages installed on a system. Installed software +contributes to system vulnerability in several ways. Packages that +include setuid programs may provide local attackers a potential path to +privilege escalation. Packages that include network services may give +this opportunity to network-based attackers. Packages that include +programs which are predictably executed by local users (e.g. after +graphical login) may provide opportunities for trojan horses or other +attack code to be run undetected. The number of software packages +installed on a system can almost always be significantly pruned to include +only the software for which there is an environmental or operational need. + + + Run Different Network Services on Separate Systems + Whenever possible, a server should be dedicated to serving exactly one +network service. This limits the number of other services that can +be compromised in the event that an attacker is able to successfully +exploit a software flaw in one network service. + + + Configure Security Tools to Improve System Robustness + Several tools exist which can be effectively used to improve a system's +resistance to and detection of unknown attacks. These tools can improve +robustness against attack at the cost of relatively little configuration +effort. In particular, this guide recommends and discusses the use of +host-based firewalling, SELinux for protection against +vulnerable services, and a logging and auditing infrastructure for +detection of problems. + + + + How to Use This Guide + Readers should heed the following points when using the guide. + + Formatting Conventions + Commands intended for shell execution, as well as configuration file text, +are featured in a monospace font. Italics are used +to indicate instances where the system administrator must substitute +the appropriate information into a command or configuration file. + + + Read Sections Completely and in Order + Each section may build on information and recommendations discussed in +prior sections. Each section should be read and understood completely; +instructions should never be blindly applied. Relevant discussion may +occur after instructions for an action. + + + Reboot Required + A system reboot is implicitly required after some actions in order to +complete the reconfiguration of the system. In many cases, the changes +will not take effect until a reboot is performed. In order to ensure +that changes are applied properly and to test functionality, always +reboot the system after applying a set of recommendations from this guide. + + + Root Shell Environment Assumed + Most of the actions listed in this document are written with the +assumption that they will be executed by the root user running the +/bin/bash shell. Commands preceded with a hash mark (#) +assume that the administrator will execute the commands as root, i.e. +apply the command via sudo whenever possible, or use +su to gain root privileges if sudo cannot be +used. Commands which can be executed as a non-root user are are preceded +by a dollar sign ($) prompt. + + + Test in Non-Production Environment + This guidance should always be tested in a non-production environment +before deployment. This test environment should simulate the setup in +which the system will be deployed as closely as possible. + + + + + + + + + combine_ovals.py from SCAP Security Guide + ssg: [0, 1, 64], python: 3.10.4 + 5.11 + 2022-09-30T15:10:20 + + + + + Configure Fapolicy Module to Employ a Deny-all, Permit-by-exception Policy to Allow the Execution of Authorized Software Programs. + + Red Hat Enterprise Linux 9 + + Configure Fapolicy Module to Employ a Deny-all, Permit-by-exception Policy + + + + + + + + + + + Set Permissions on All Configuration Files Inside /etc/httpd/conf.d/ + + Red Hat Enterprise Linux 9 + + The /etc/httpd/conf.d/* files should have the appropriate permissions (0640 or stronger). + + + + + + + + + + Set Permissions on All Configuration Files Inside /etc/httpd/conf/ + + Red Hat Enterprise Linux 9 + + The /etc/httpd/conf/* files should have the appropriate permissions (0640 or stronger). + + + + + + + + + + Set Permissions on All Configuration Files Inside /etc/httpd/conf.modules.d/ + + Red Hat Enterprise Linux 9 + + The /etc/httpd/conf.modules.d/* files should have the appropriate permissions (0640 or stronger). + + + + + + + + + + Disable Kerberos by removing host keytab + + Red Hat Enterprise Linux 9 + + Check that there is no Kerberos keytab file present in /etc + + + + + + + + + + Configure System to Forward All Mail For The Root Account + + Red Hat Enterprise Linux 9 + + Check if root has the correct mail alias. + + + + + + + + + + Configure System to Forward All Mail From Postmaster to The Root Account + + Red Hat Enterprise Linux 9 + + Check if postmaster has the correct mail alias. + + + + + + + + + + Disable Postfix Network Listening + + Red Hat Enterprise Linux 9 + + Postfix network listening should be disabled + + + + + + + + + + + Prevent Unrestricted Mail Relaying + + Red Hat Enterprise Linux 9 + + Ensure 'smtpd_client_restrictions' is configured with value 'permit_mynetworks,reject' in /etc/postfix/main.cf + + + + + + + + + + + + + Use Kerberos Security on All Exports + + Red Hat Enterprise Linux 9 + + Using Kerberos Security allows to cryptography authenticate a + valid user to an NFS share. + + + + + + + + + + + Disable chrony daemon from acting as server + + Red Hat Enterprise Linux 9 + + Configure the port setting in /etc/chrony.conf to disable + server operation. + + + + + + + + + + + Disable network management of chrony daemon + + Red Hat Enterprise Linux 9 + + Configure the cmdport setting in /etc/chrony.conf to disable + chronyc management connections over network. + + + + + + + + + + + Configure Time Service Maxpoll Interval + + Red Hat Enterprise Linux 9 + + Configure the maxpoll setting in /etc/ntp.conf or chrony.conf + to continuously poll the time source servers. + + + + + + + + + + + + + + + + + + + + + + + Ensure that chronyd is running under chrony user account + + Red Hat Enterprise Linux 9 + + Ensure that no other user than chrony is configured to run the chrony service + + + + + + + + + + Ensure Chrony is only configured with the server directive + + Red Hat Enterprise Linux 9 + + Ensure Chrony has time sources configured with server directive + + + + + + + + + + + A remote time server for Chrony is configured + + Red Hat Enterprise Linux 9 + + A remote NTP Server for time synchronization should be + specified (and dependencies are met) + + + + + + + + + + Specify Additional Remote NTP Servers + + Red Hat Enterprise Linux 9 + + Multiple ntpd NTP Servers for time synchronization should be specified. + + + + + + + + + Specify a Remote NTP Server + + Red Hat Enterprise Linux 9 + + A remote ntpd NTP Server for time synchronization should be + specified (and dependencies are met) + + + + + + + + + Remove Host-Based Authentication Files + + Red Hat Enterprise Linux 9 + + There should not be any shosts.equiv files on the system. + + + + + + + + + + Remove Rsh Trust Files + + Red Hat Enterprise Linux 9 + + There should not be any .rhosts or hosts.equiv files on the system. + + + + + + + + + + + + Remove User Host-Based Authentication Files + + Red Hat Enterprise Linux 9 + + There should not be any .shosts files on the system. + + + + + + + + + + Ensure tftp Daemon Uses Secure Mode + + Red Hat Enterprise Linux 9 + + The TFTP daemon should use secure mode. + + + + + + + + + + + Require Client SMB Packet Signing, if using mount.cifs + + Red Hat Enterprise Linux 9 + + Require packet signing of clients who mount + Samba shares using the mount.cifs program (e.g., those who + specify shares in /etc/fstab). To do so, ensure that signing + options (either sec=krb5i or sec=ntlmv2i) are + used. + + + + + + + + + + + + + + + + + + + Configure SNMP Service to Use Only SNMPv3 or Newer + + Red Hat Enterprise Linux 9 + + SNMP version 1 and 2c must not be enabled. + + + + + + + + + + + Verify Permissions on SSH Server Private *_key Key Files + + Red Hat Enterprise Linux 9 + + The system sshd key is owned by root:root and has the 0600 permission, or by a root:ssh_keys with the 0640 permission + + + + + + + + + + Configure session renegotiation for SSH client + + Red Hat Enterprise Linux 9 + + Ensure 'RekeyLimit' is configured with the correct value in /etc/ssh/ssh_config and /etc/ssh/ssh_config.d/*.conf + + + + + + + + + + + Enable SSH Server firewalld Firewall Exception + + Red Hat Enterprise Linux 9 + + If inbound SSH access is needed, the firewall should allow access to + the SSH port (22). + + + + + + + + + + + + + + + + Allow Only SSH Protocol 2 + + Red Hat Enterprise Linux 9 + + The OpenSSH daemon should be running protocol 2. + + + + + + + + + + + + + + + + + + + + + Disable Compression Or Set Compression to delayed + + Red Hat Enterprise Linux 9 + + SSH should either have compression disabled or set to delayed. + + + + + + + + + + + + + + + + + + Disable SSH Support for Rhosts RSA Authentication + + Red Hat Enterprise Linux 9 + + SSH can allow authentication through the obsolete rsh command + through the use of the authenticating user's SSH keys. This should be disabled. + + + + + + + + + + + + + + + + + + + + + Force frequent session key renegotiation + + Red Hat Enterprise Linux 9 + + Ensure RekeyLimit is configured with the appropriate value in /etc/ssh/sshd_config or in /etc/ssh/sshd_config.d + + + + + + + + + + + + + + + + + + + + + Set SSH Idle Timeout Interval + + Red Hat Enterprise Linux 9 + + The SSH idle timeout interval should be set to an + appropriate value. + + + + + + + + + + + + + + + + + + + + + Set SSH Client Alive Count Max + + Red Hat Enterprise Linux 9 + + The SSH ClientAliveCountMax should be set to an appropriate + value (and dependencies are met) + + + + + + + + + + + + + + + + + + + + + Ensure SSH LoginGraceTime is configured + + Red Hat Enterprise Linux 9 + + The SSH number seconds for login grace time should be set to an + appropriate value. + + + + + + + + + + + + + + + + + + Set SSH authentication attempt limit + + Red Hat Enterprise Linux 9 + + The SSH MaxAuthTries should be set to an + appropriate value. + + + + + + + + + + + + + + + + + + Set SSH MaxSessions limit + + Red Hat Enterprise Linux 9 + + The SSH number of max sessions should be set to an + appropriate value. + + + + + + + + + + + + + + + + + + Ensure SSH MaxStartups is configured + + Red Hat Enterprise Linux 9 + + Ensure 'MaxStartups' is configured in + '/etc/ssh/sshd_config' + + + + + + + + + + + + + + + + + + Distribute the SSH Server configuration to multiple files in a config directory. + + Red Hat Enterprise Linux 9 + + foo + + + + + + + + + + + + + + + + + + + + + Enable Use of Privilege Separation + + Red Hat Enterprise Linux 9 + + Ensure 'UsePrivilegeSeparation' is configured with value 'sandbox' in '/etc/ssh/sshd_config' + + + + + + + + + + + + + + Certificate status checking in SSSD + + Red Hat Enterprise Linux 9 + + SSSD should be configured with the correct ocsp_dgst + digest function + + + + + + + + + + Enable Smartcards in SSSD + + Red Hat Enterprise Linux 9 + + SSSD should be configured to authenticate access to the system + using smart cards. + + + + + + + + + + + + Configure SSSD to Expire Offline Credentials + + Red Hat Enterprise Linux 9 + + SSSD should be configured to expire offline credentials after 1 day. + + + + + + + + + + Log USBGuard daemon audit events using Linux Audit + + Red Hat Enterprise Linux 9 + + Ensure 'AuditBackend' is configured with value 'LinuxAudit' in /etc/usbguard/usbguard-daemon.conf + + + + + + + + + + + + + Authorize Human Interface Devices in USBGuard daemon + + Red Hat Enterprise Linux 9 + + Check that /etc/usbguard/rules.conf exists and that it contains at least one non white space character. + + + + + + + + + + Authorize Human Interface Devices and USB hubs in USBGuard daemon + + Red Hat Enterprise Linux 9 + + Check that /etc/usbguard/rules.conf contains at least one non whitespace character and exists. + + + + + + + + + + Authorize USB hubs in USBGuard daemon + + Red Hat Enterprise Linux 9 + + Check that /etc/usbguard/rules.conf contains at least one non whitespace character and exists. + + + + + + + + + Generate USBGuard Policy + + Red Hat Enterprise Linux 9 + + Check that /etc/usbguard/rules.conf contains at least one non whitespace character and exists. + + + + + + + + + + Disable graphical user interface + + Red Hat Enterprise Linux 9 + + Ensure that the default runlevel target is set to multi-user.target. + + + + + + + + + + + + + Disable X Windows Startup By Setting Default Target + + Red Hat Enterprise Linux 9 + + Ensure that the default runlevel target is set to multi-user.target. + + + + + + + + + + Enable authselect + + Red Hat Enterprise Linux 9 + + Check that authselect is enabled + + + + + + + + + + + + + + Modify the System Login Banner + + Red Hat Enterprise Linux 9 + + The system login banner text should be set correctly. + + + + + + + + + + Modify the System Message of the Day Banner + + Red Hat Enterprise Linux 9 + + The system login banner text should be set correctly. + + + + + + + + + + Enable GNOME3 Login Warning Banner + + Red Hat Enterprise Linux 9 + + Enable the GNOME3 Login warning banner. + + + + + + + + + + + + + + + Set the GNOME3 Login Warning Banner Text + + Red Hat Enterprise Linux 9 + + Enable the GUI warning banner. + + + + + + + + + + + + + + + Disallow Configuration to Bypass Password Requirements for Privilege Escalation + + Red Hat Enterprise Linux 9 + + Disallow Configuration to Bypass Password Requirements for Privilege Escalation. + + + + + + + + + + Ensure PAM Displays Last Logon/Access Notification + + Red Hat Enterprise Linux 9 + + Configure the system to notify users of last login/access using pam_lastlog. + + + + + + + + + + + An SELinux Context must be configured for the pam_faillock.so records directory + + Red Hat Enterprise Linux 9 + + An SELinux Context must be configued for the Faillock directory. + + + + + + + + + + + Account Lockouts Must Be Logged + + Red Hat Enterprise Linux 9 + + Account Lockouts Must Be Logged + + + + + + + + + + + + + + + + + + + Limit Password Reuse: password-auth + + Red Hat Enterprise Linux 9 + + The passwords to remember should be set correctly. + + + + + + + + + + Limit Password Reuse: system-auth + + Red Hat Enterprise Linux 9 + + The passwords to remember should be set correctly. + + + + + + + + + + Limit Password Reuse + + Red Hat Enterprise Linux 9 + + The passwords to remember should be set correctly. + + + + + + + + + + + Lock Accounts After Failed Password Attempts + + Red Hat Enterprise Linux 9 + + Lockout account after failed login attempts + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Configure the root Account for Failed Password Attempts + + Red Hat Enterprise Linux 9 + + The root account should be configured to deny access after the number of + defined failed attempts has been reached. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Lock Accounts Must Persist + + Red Hat Enterprise Linux 9 + + Persist lockout account after reboot + + + + + + + + + + + + + + + + + + Enforce pam_faillock for Local Accounts Only + + Red Hat Enterprise Linux 9 + + Enforce pam_faillock for Local Accounts Only + + + + + + + + + + + + + + + + + + + + + Set Interval For Counting Failed Password Attempts + + Red Hat Enterprise Linux 9 + + The number of allowed failed logins should be set correctly. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Set Lockout Time for Failed Password Attempts + + Red Hat Enterprise Linux 9 + + The unlock time after number of failed logins should be set correctly. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Ensure PAM password complexity module is enabled in password-auth + + Red Hat Enterprise Linux 9 + + The PAM module pam_pwquality is used in password-auth + + + + + + + + + + Ensure PAM password complexity module is enabled in system-auth + + Red Hat Enterprise Linux 9 + + The PAM module pam_pwquality is used in system-auth + + + + + + + + + + Ensure PAM Enforces Password Requirements - Authentication Retry Prompts Permitted Per-Session + + Red Hat Enterprise Linux 9 + + The password retry should meet minimum requirements + + + + + + + + + + + + + + + + + + Set Password Hashing Algorithm in /etc/libuser.conf + + Red Hat Enterprise Linux 9 + + The password hashing algorithm should be set correctly in /etc/libuser.conf. + + + + + + + + + + Set Password Hashing Algorithm in /etc/login.defs + + Red Hat Enterprise Linux 9 + + The password hashing algorithm should be set correctly in /etc/login.defs. + + + + + + + + + + Set PAM''s Password Hashing Algorithm - password-auth + + Red Hat Enterprise Linux 9 + + The password hashing algorithm should be set correctly in /etc/pam.d/password-auth. + + + + + + + + + + Set PAM''s Password Hashing Algorithm + + Red Hat Enterprise Linux 9 + + The password hashing algorithm should be set correctly in /etc/pam.d/system-auth. + + + + + + + + + + Set Password Hashing Minimum Rounds in /etc/login.defs + + Red Hat Enterprise Linux 9 + + The password hashing minimum rounds should be set correctly in /etc/login.defs. + + + + + + + + + + + + + + + + + + Disable Ctrl-Alt-Del Burst Action + + Red Hat Enterprise Linux 9 + + Configure the CtrlAltDelBurstAction setting in /etc/systemd/system.conf + or /etc/systemd/system.conf.d/* to none to prevent a reboot if Ctrl-Alt-Delete is + pressed more than 7 times in 2 seconds. + + + + + + + + + + Disable Ctrl-Alt-Del Reboot Activation + + Red Hat Enterprise Linux 9 + + By default, the system will reboot when the + Ctrl-Alt-Del key sequence is pressed. + + + + + + + + + + Verify that Interactive Boot is Disabled + + Red Hat Enterprise Linux 9 + + The ability for users to perform interactive startups should + be disabled. + + + + + + + + + + + + + + Require Authentication for Emergency Systemd Target + + Red Hat Enterprise Linux 9 + + The requirement for a password to boot into emergency mode + should be configured correctly. + + + + + + + + + + + + + Require Authentication for Single User Mode + + Red Hat Enterprise Linux 9 + + The requirement for a password to boot into single-user mode + should be configured correctly. + + + + + + + + + + + + + Support session locking with tmux + + Red Hat Enterprise Linux 9 + + Check if tmux is configured to exec at the end of bashrc. + + + + + + + + + + Configure tmux to lock session after inactivity + + Red Hat Enterprise Linux 9 + + Check if tmux is configured to lock sessions after period of inactivity. + + + + + + + + + + + Configure the tmux Lock Command + + Red Hat Enterprise Linux 9 + + Check if the vlock command is configured to be used as a locking mechanism in tmux. + + + + + + + + + + + Prevent user from disabling the screen lock + + Red Hat Enterprise Linux 9 + + Check that tmux is not listed in /etc/shells + + + + + + + + + + Configure opensc Smart Card Drivers + + Red Hat Enterprise Linux 9 + + Configure the organization's smart card driver so that only + the smart card in use by the organization will be recognized by the system. + + + + + + + + + + Force opensc To Use Defined Smart Card Driver + + Red Hat Enterprise Linux 9 + + Force opensc to use the organization's smart card driver so that only + the smart card in use by the organization will be recognized by the system. + + + + + + + + + + Ensure All Accounts on the System Have Unique User IDs + + Red Hat Enterprise Linux 9 + + All accounts on the system should have unique IDs for proper accountability. + + + + + + + + + + Only Authorized Local User Accounts Exist on Operating System + + Red Hat Enterprise Linux 9 + + Besides the default operating system user, there should be no other users + except the users that are authorized to exist locally on the operating system. + + + + + + + + + + Ensure All Groups on the System Have Unique Group ID + + Red Hat Enterprise Linux 9 + + All groups on the system should have unique names for proper accountability. + + + + + + + + + + Set Account Expiration Following Inactivity + + Red Hat Enterprise Linux 9 + + The accounts should be configured to expire automatically following password expiration. + + + + + + + + + + Ensure All Accounts on the System Have Unique Names + + Red Hat Enterprise Linux 9 + + All accounts on the system should have unique names for proper accountability. + + + + + + + + + + Set Password Maximum Age + + Red Hat Enterprise Linux 9 + + The maximum password age policy should meet minimum requirements. + + + + + + + + + + Set Password Minimum Age + + Red Hat Enterprise Linux 9 + + The minimum password age policy should be set appropriately. + + + + + + + + + + Set Password Minimum Length in login.defs + + Red Hat Enterprise Linux 9 + + The password minimum length should be set appropriately. + + + + + + + + + + Set Existing Passwords Maximum Age + + Red Hat Enterprise Linux 9 + + Set Existing Passwords Maximum Age + + + + + + + + + + + Set Existing Passwords Minimum Age + + Red Hat Enterprise Linux 9 + + Passwords for existing accounts much satisfy minimum age requirements + + + + + + + + + + + Set Password Warning Age + + Red Hat Enterprise Linux 9 + + The password expiration warning age should be set appropriately. + + + + + + + + + + Verify All Account Password Hashes are Shadowed + + Red Hat Enterprise Linux 9 + + All password hashes should be shadowed. + + + + + + + + + + Verify All Account Password Hashes are Shadowed with SHA512 + + Red Hat Enterprise Linux 9 + + All password hashes should be shadowed. + + + + + + + + + + Set number of Password Hashing Rounds - password-auth + + Red Hat Enterprise Linux 9 + + The number of rounds for password hashing should be set correctly. + + + + + + + + + + + + + + Set number of Password Hashing Rounds - system-auth + + Red Hat Enterprise Linux 9 + + The number of rounds for password hashing should be set correctly. + + + + + + + + + + + + + + All GIDs referenced in /etc/passwd must be defined in /etc/group + + Red Hat Enterprise Linux 9 + + All GIDs referenced in /etc/passwd must be defined in /etc/group. + + + + + + + + + + Prevent Login to Accounts With Empty Password + + Red Hat Enterprise Linux 9 + + The file /etc/pam.d/system-auth should not contain the nullok option + + + + + + + + + + Ensure There Are No Accounts With Blank or Null Passwords + + Red Hat Enterprise Linux 9 + + The file /etc/shadow shows that there aren't empty passwords + + + + + + + + + + Ensure there are no legacy + NIS entries in /etc/group + + Red Hat Enterprise Linux 9 + + No lines starting with + are in /etc/group + + + + + + + + + + Ensure there are no legacy + NIS entries in /etc/passwd + + Red Hat Enterprise Linux 9 + + No lines starting with + are in /etc/passwd + + + + + + + + + + Ensure there are no legacy + NIS entries in /etc/shadow + + Red Hat Enterprise Linux 9 + + No lines starting with + are in /etc/shadow + + + + + + + + + + Verify No netrc Files Exist + + Red Hat Enterprise Linux 9 + + The .netrc files contain login information used to auto-login into FTP servers and reside in the user's home directory. Any .netrc files should be removed. + + + + + + + + + + Verify Only Root Has UID 0 + + Red Hat Enterprise Linux 9 + + Only the root account should be assigned a user id of 0. + + + + + + + + + + Verify Root Has A Primary GID 0 + + Red Hat Enterprise Linux 9 + + The root account should have primary group of 0 + + + + + + + + + + Direct root Logins Not Allowed + + Red Hat Enterprise Linux 9 + + Preventing direct root logins help ensure accountability for actions + taken on the system using the root account. + + + + + + + + + + + Ensure that System Accounts Do Not Run a Shell Upon Login + + Red Hat Enterprise Linux 9 + + The root account is the only system account that should have + a login shell. + + + + + + + + + + + + + + + + + + + + Restrict Serial Port Root Logins + + Red Hat Enterprise Linux 9 + + Preventing direct root login to serial port interfaces helps + ensure accountability for actions taken on the system using the root + account. + + + + + + + + + + Restrict Virtual Console Root Logins + + Red Hat Enterprise Linux 9 + + Preventing direct root login to virtual console devices + helps ensure accountability for actions taken on the system using the + root account. + + + + + + + + + + Enforce usage of pam_wheel for su authentication + + Red Hat Enterprise Linux 9 + + Only members of the wheel group should be able to authenticate through the su command. + + + + + + + + + + Ensure Home Directories are Created for New Users + + Red Hat Enterprise Linux 9 + + CREATE_HOME should be enabled + + + + + + + + + + Ensure the Logon Failure Delay is Set Correctly in login.defs + + Red Hat Enterprise Linux 9 + + The delay between failed authentication attempts should be + set for all users specified in /etc/login.defs + + + + + + + + + + Limit the Number of Concurrent Login Sessions Allowed Per User + + Red Hat Enterprise Linux 9 + + The maximum number of concurrent login sessions per user should meet + minimum requirements. + + + + + + + + + + + + + + Configure Polyinstantiation of /tmp Directories + + Red Hat Enterprise Linux 9 + + + + + + + + + + + + + Configure Polyinstantiation of /var/tmp Directories + + Red Hat Enterprise Linux 9 + + + + + + + + + + + + + Set Interactive Session Timeout + + Red Hat Enterprise Linux 9 + + Checks interactive shell timeout + + + + + + + + + + + User Initialization Files Must Not Run World-Writable Programs + + Red Hat Enterprise Linux 9 + + User Initialization Files Must Not Execute World-Writable Programs + + + + + + + + + + All Interactive Users Must Have A Home Directory Defined + + Red Hat Enterprise Linux 9 + + All Interactive Users Must Have A Home Directory Defined + + + + + + + + + + All Interactive Users Home Directories Must Exist + + Red Hat Enterprise Linux 9 + + All Interactive Users Home Directories Must Exist + + + + + + + + + + + All Interactive User Home Directories Must Be Group-Owned By The Primary User + + Red Hat Enterprise Linux 9 + + All interactive user's Home Directories must be group-owned by its user + + + + + + + + + + All Interactive User Home Directories Must Have mode 0750 Or Less Permissive + + Red Hat Enterprise Linux 9 + + All Interactive User Home Directories Must Have mode 0750 Or Less Permissive + + + + + + + + + + Ensure that User Home Directories are not Group-Writable or World-Readable + + Red Hat Enterprise Linux 9 + + Ensure that User Home Directories are not Group-Writable or World-Readable + + + + + + + + + + Ensure that Root's Path Does Not Include World or Group-Writable Directories + + Red Hat Enterprise Linux 9 + + Check each directory in root's path and make use it does + not grant write permission to group and other + + + + + + + + + + Ensure that Root's Path Does Not Include Relative Paths or Null Directories + + Red Hat Enterprise Linux 9 + + The environment variable PATH should be set correctly for + the root user. + + + + + + + + + + + + + + + Ensure the Default Bash Umask is Set Correctly + + Red Hat Enterprise Linux 9 + + The default umask for users of the bash shell + + + + + + + + + + + Ensure the Default C Shell Umask is Set Correctly + + Red Hat Enterprise Linux 9 + + The default umask for users of the csh shell + + + + + + + + + + + Ensure the Default Umask is Set Correctly in login.defs + + Red Hat Enterprise Linux 9 + + The default umask for all users specified in /etc/login.defs + + + + + + + + + + + Ensure the Default Umask is Set Correctly in /etc/profile + + Red Hat Enterprise Linux 9 + + The default umask for all users should be set correctly + + + + + + + + + + + Ensure the Default Umask is Set Correctly For Interactive Users + + Red Hat Enterprise Linux 9 + + Ensure the Default Umask is Set Correctly For Interactive Users + + + + + + + + + + Enable Syscall Auditing + + Red Hat Enterprise Linux 9 + + Syscall auditing should not be disabled. + + + + + + + + + + + + + + + + Make the auditd Configuration Immutable + + Red Hat Enterprise Linux 9 + + Force a reboot to change audit rules is enabled + + + + + + + + + + + + + + + + + Record Events that Modify the System's Mandatory Access Controls + + Red Hat Enterprise Linux 9 + + Audit rules that detect changes to the system's rhisam access controls (SELinux) are enabled. + + + + + + + + + + + + + + + + + Record Events that Modify the System's Network Environment + + Red Hat Enterprise Linux 9 + + The network environment should not be modified by anything other than + administrator action. Any change to network parameters should be audited. + + + + + + + + + + + + + + + + + + + + + + + + + + + Record Attempts to Alter Process and Session Initiation Information + + Red Hat Enterprise Linux 9 + + Audit rules should capture information about session initiation. + + + + + + + + + + + + + + + + + + + + + Ensure auditd Collects System Administrator Actions - /etc/sudoers + + Red Hat Enterprise Linux 9 + + Audit actions taken by system administrators on the system - /etc/sudoers. + + + + + + + + + + + + + + + + + Ensure auditd Collects System Administrator Actions - /etc/sudoers.d/ + + Red Hat Enterprise Linux 9 + + Audit actions taken by system administrators on the system - /etc/sudoers.d/. + + + + + + + + + + + + + + + + + Record Events When Privileged Executables Are Run + + Red Hat Enterprise Linux 9 + + Ensure audit rule for all uses of privileged functions is enabled + + + + + + + + + + + + + + + + + + + + + + + Ensure auditd Collects System Administrator Actions + + Red Hat Enterprise Linux 9 + + Audit actions taken by system administrators on the system. + + + + + + + + + + + + + + + + + + + Shutdown System When Auditing Failures Occur + + Red Hat Enterprise Linux 9 + + The system will shutdown when auditing fails. + + + + + + + + + + + + + + + + + Record Events that Modify User/Group Information + + Red Hat Enterprise Linux 9 + + Audit rules should detect modification to system files that hold information about users and groups. + + + + + + + + + + + + + + + + + + + + + + + + + Record Access Events to Audit Log Directory + + Red Hat Enterprise Linux 9 + + Audit rules about the read events to /var/log/audit + + + + + + + + + + + + + + + + System Audit Directories Must Be Group Owned By Root + + Red Hat Enterprise Linux 9 + + Checks that all /var/log/audit directories are group owned by the root user. + + + + + + + + + + + + + + + + + + + + + System Audit Directories Must Be Owned By Root + + Red Hat Enterprise Linux 9 + + Checks that all /var/log/audit directories are owned by the root user. + + + + + + + + + + + + + + + + + System Audit Logs Must Have Mode 0750 or Less Permissive + + Red Hat Enterprise Linux 9 + + Checks for correct permissions for audit logs. + + + + + + + + + + + + + + + + + + + + + + System Audit Logs Must Be Group Owned By Root + + Red Hat Enterprise Linux 9 + + Checks that all audit log files are group owned by the root user. + + + + + + + + + + + + + + + + + + + + + + + System Audit Logs Must Be Owned By Root + + Red Hat Enterprise Linux 9 + + Checks that all /var/log/audit files and directories are owned by the root user and group. + + + + + + + + + + + + + + + + + + System Audit Logs Must Be Owned By Root + + Red Hat Enterprise Linux 9 + + Checks that all audit log files are owned by the root user. + + + + + + + + + + + + + + System Audit Logs Must Have Mode 0640 or Less Permissive + + Red Hat Enterprise Linux 9 + + Checks for correct permissions for all audit log files. + + + + + + + + + + + + + + + + + Record Events that Modify the System's Discretionary Access Controls - umount + + Red Hat Enterprise Linux 9 + + The changing of file permissions and attributes should be audited. + + + + + + + + + + + + + + + + + Ensure auditd Collects File Deletion Events by User + + Red Hat Enterprise Linux 9 + + Audit files deletion events. + + + + + + + + + + + + + + Ensure auditd Collects Unauthorized Access Attempts to Files (unsuccessful) + + Red Hat Enterprise Linux 9 + + Audit rules about the unauthorized access attempts to files (unsuccessful) are enabled. + + + + + + + + + + + + + + + Ensure auditd Collects Information on Kernel Module Loading and Unloading + + Red Hat Enterprise Linux 9 + + The audit rules should be configured to log information about kernel module loading and unloading. + + + + + + + + + + + + Ensure auditd Collects Information on Kernel Module Unloading - delete_module + + Red Hat Enterprise Linux 9 + + The audit rules should be configured to log information about kernel module loading and unloading. + + + + + + + + + + + + + + + + + + + + + + + + + Ensure auditd Collects Information on Kernel Module Loading and Unloading - finit_module + + Red Hat Enterprise Linux 9 + + The audit rules should be configured to log information about kernel module loading and unloading. + + + + + + + + + + + + + + + + + + + + + + + + + Ensure auditd Collects Information on Kernel Module Loading - init_module + + Red Hat Enterprise Linux 9 + + The audit rules should be configured to log information about kernel module loading and unloading. + + + + + + + + + + + + + + + + + + + + + + + + + Record Attempts to Alter Logon and Logout Events + + Red Hat Enterprise Linux 9 + + Audit rules should be configured to log successful and unsuccessful login and logout events. + + + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands + + Red Hat Enterprise Linux 9 + + Audit rules about the information on the use of privileged commands are enabled. + + + + + + + + + + + + + + + + + + + Record attempts to alter time through adjtimex + + Red Hat Enterprise Linux 9 + + Record attempts to alter time through adjtimex. + + + + + + + + + + + + + + + + + + + + + + + + + Record Attempts to Alter Time Through clock_settime + + Red Hat Enterprise Linux 9 + + Record attempts to alter time through clock_settime. + + + + + + + + + + + + + + + + + + + + + + + + + Record attempts to alter time through settimeofday + + Red Hat Enterprise Linux 9 + + Record attempts to alter time through settimeofday. + + + + + + + + + + + + + + + + + + + + + + + + + Record Attempts to Alter Time Through stime + + Red Hat Enterprise Linux 9 + + Record attempts to alter time through stime. Note that on + 64-bit architectures the stime system call is not defined in the audit + system calls lookup table. + + + + + + + + + + + + + + + + + + + + + + + Record Attempts to Alter the localtime File + + Red Hat Enterprise Linux 9 + + Record attempts to alter time through /etc/localtime. + + + + + + + + + + + + + + + + + Configure audispd Plugin To Send Logs To Remote Server + + Red Hat Enterprise Linux 9 + + remote_server setting in /etc/audit/audisp-remote.conf is set to a certain IP address or hostname + + + + + + + + + + Configure audispd's Plugin disk_full_action When Disk Is Full + + Red Hat Enterprise Linux 9 + + remote_server setting in /etc/audit/audisp-remote.conf is set to a certain IP address or hostname + + + + + + + + + + Encrypt Audit Records Sent With audispd Plugin + + Red Hat Enterprise Linux 9 + + enable_krb5 setting in /etc/audit/audisp-remote.conf is set to 'yes' + + + + + + + + + + Configure audispd's Plugin network_failure_action On Network Failure + + Red Hat Enterprise Linux 9 + + remote_server setting in /etc/audit/audisp-remote.conf is set to a certain IP address or hostname + + + + + + + + + + Configure auditd to use audispd's syslog plugin + + Red Hat Enterprise Linux 9 + + active setting in /etc/audit/plugins.d/syslog.conf is set to 'yes' + + + + + + + + + + Configure auditd Disk Error Action on Disk Error + + Red Hat Enterprise Linux 9 + + disk_error_action setting in /etc/audit/auditd.conf is set to a certain action + + + + + + + + + + Configure auditd Disk Error Action on Disk Error + + Red Hat Enterprise Linux 9 + + disk_error_action setting in /etc/audit/auditd.conf is set to SYSLOG, SINGLE or HALT + + + + + + + + + + + + Configure auditd Disk Full Action when Disk Space Is Full + + Red Hat Enterprise Linux 9 + + disk_full_action setting in /etc/audit/auditd.conf is set to a certain action + + + + + + + + + + Configure auditd Disk Full Action when Disk Space Is Full + + Red Hat Enterprise Linux 9 + + disk_full_action setting in /etc/audit/auditd.conf is set to SYSLOG, SINGLE or HALT + + + + + + + + + + + + Configure auditd mail_acct Action on Low Disk Space + + Red Hat Enterprise Linux 9 + + action_mail_acct setting in /etc/audit/auditd.conf is set to a certain account + + + + + + + + + + Configure auditd admin_space_left Action on Low Disk Space + + Red Hat Enterprise Linux 9 + + admin_space_left_action setting in /etc/audit/auditd.conf is set to a certain action + + + + + + + + + + Configure auditd flush priority + + Red Hat Enterprise Linux 9 + + The setting for flush in /etc/audit/auditd.conf + + + + + + + + + + Configure auditd Max Log File Size + + Red Hat Enterprise Linux 9 + + max_log_file setting in /etc/audit/auditd.conf is set to at least a certain value + + + + + + + + + + Configure auditd max_log_file_action Upon Reaching Maximum Log Size + + Red Hat Enterprise Linux 9 + + max_log_file_action setting in /etc/audit/auditd.conf is set to a certain action + + + + + + + + + + Configure auditd max_log_file_action Upon Reaching Maximum Log Size + + Red Hat Enterprise Linux 9 + + max_log_file_action setting in /etc/audit/auditd.conf is set to a certain action + + + + + + + + + + + Configure auditd Number of Logs Retained + + Red Hat Enterprise Linux 9 + + num_logs setting in /etc/audit/auditd.conf is set to at least a certain value + + + + + + + + + + Configure auditd space_left on Low Disk Space + + Red Hat Enterprise Linux 9 + + space_left setting in /etc/audit/auditd.conf is set to at least a certain value + + + + + + + + + + Configure auditd space_left Action on Low Disk Space + + Red Hat Enterprise Linux 9 + + space_left_action setting in /etc/audit/auditd.conf is set to a certain action + + + + + + + + + + Configure auditd space_left on Low Disk Space + + Red Hat Enterprise Linux 9 + + space_left setting in /etc/audit/auditd.conf is set to at least a certain value + + + + + + + + + + Set hostname as computer node name in audit logs + + Red Hat Enterprise Linux 9 + + Ensure 'name_format' is configured with value 'hostname' in /etc/audit/auditd.conf + + + + + + + + + + Appropriate Action Must be Setup When the Internal Audit Event Queue is Full + + Red Hat Enterprise Linux 9 + + Ensure 'overflow_action' is configured with value '(syslog|single|halt)' in /etc/audit/auditd.conf + + + + + + + + + + Configure audit according to OSPP requirements + + Red Hat Enterprise Linux 9 + + Compare configure audit rules against the recommended pre-configured files. + + + + + + + + + + + + + Disable Recovery Booting + + Red Hat Enterprise Linux 9 + + Recovery mode should be disabled. + + + + + + + + + + Configure kernel to trust the CPU random number generator + + Red Hat Enterprise Linux 9 + + Ensure the kernel is configured to trust the CPU hardware random number generator. + + + + + + + + + + + + + + Set the Boot Loader Admin Username to a Non-Default Value + + Red Hat Enterprise Linux 9 + + The grub2 boot loader superuser should have a username that is hard to guess. + + + + + + + + + + Set Boot Loader Password in grub2 + + Red Hat Enterprise Linux 9 + + The grub2 boot loader should have password protection enabled. + + + + + + + + + + + + + + + + Set the UEFI Boot Loader Admin Username to a Non-Default Value + + Red Hat Enterprise Linux 9 + + The grub2 boot loader superuser should have a username that is hard to guess. + + + + + + + + + + Set the UEFI Boot Loader Password + + Red Hat Enterprise Linux 9 + + The UEFI grub2 boot loader should have password protection enabled. + + + + + + + + + + Ensure all zIPL boot entries are BLS compliant + + Red Hat Enterprise Linux 9 + + Check if /etc/zipl.conf configures any boot entry + + + + + + + + + + Ensure zIPL bootmap is up to date + + Red Hat Enterprise Linux 9 + + Check if /boot/bootmap is up to date + + + + + + + + + + Ensure debug-shell service is not enabled in zIPL + + Red Hat Enterprise Linux 9 + + Ensure systemd.debug-shell option is not configured in the 'options' line in /boot/loader/entries/*.conf. Make sure that newly installed kernels won't have this option, it should not be configured in /etc/kernel/cmdline. + + + + + + + + + + + Ensure cron Is Logging To Rsyslog + + Red Hat Enterprise Linux 9 + + Rsyslog should be configured to capture cron messages. + + + + + + + + + + + Ensure Rsyslog Authenticates Off-Loaded Audit Records + + Red Hat Enterprise Linux 9 + + Rsyslogd must authenticate remote system its sending logs to. + + + + + + + + + + + + + Ensure Rsyslog Encrypts Off-Loaded Audit Records + + Red Hat Enterprise Linux 9 + + Rsyslogd must encrypt the off-loading of logs off of the system. + + + + + + + + + + + + + Ensure Rsyslog Encrypts Off-Loaded Audit Records + + Red Hat Enterprise Linux 9 + + Rsyslogd must encrypt the off-loading of logs off of the system. + + + + + + + + + + + + + Ensure Log Files Are Owned By Appropriate Group + + Red Hat Enterprise Linux 9 + + All syslog log files should be owned by the appropriate group. + + + + + + + + + + Ensure Log Files Are Owned By Appropriate User + + Red Hat Enterprise Linux 9 + + All syslog log files should be owned by the appropriate user. + + + + + + + + + + Ensure System Log Files Have Correct Permissions + + Red Hat Enterprise Linux 9 + + File permissions for all syslog log files should be set correctly. + + + + + + + + + + Ensure remote access methods are monitored in Rsyslog + + Red Hat Enterprise Linux 9 + + Rsyslog should be configured to monitor remote access methods. + + + + + + + + + + + + Ensure Logrotate Runs Periodically + + Red Hat Enterprise Linux 9 + + + The frequency of automatic log files rotation performed by the logrotate utility should be configured to run daily + + + + + + + + + + + + + Ensure rsyslog Does Not Accept Remote Messages Unless Acting As Log Server + + Red Hat Enterprise Linux 9 + + rsyslogd should reject remote messages + + + + + + + + + + Ensure Logs Sent To Remote Host + + Red Hat Enterprise Linux 9 + + Syslog logs should be sent to a remote loghost + + + + + + + + + + + Configure TLS for rsyslog remote logging + + Red Hat Enterprise Linux 9 + + Check that all needed TLS-related options are present + + + + + + + + + + Configure CA certificate for rsyslog remote logging + + Red Hat Enterprise Linux 9 + + Check that the CA certificate path is set + + + + + + + + + + Configure Multiple DNS Servers in /etc/resolv.conf + + Red Hat Enterprise Linux 9 + + Multiple Domain Name System (DNS) Servers should be configured + in /etc/resolv.conf. + + + + + + + + + + + + + + + + + Prevent non-Privileged Users from Modifying Network Interfaces using nmcli + + Red Hat Enterprise Linux 9 + + polkit is properly configured to prevent non-privileged users from changing networking settings + + + + + + + + + + Ensure System is Not Acting as a Network Sniffer + + Red Hat Enterprise Linux 9 + + Disable the network sniffer + + + + + + + + + + Set Default firewalld Zone for Incoming Packets + + Red Hat Enterprise Linux 9 + + Change the default firewalld zone to drop. + + + + + + + + + + Disable IPv6 Networking Support Automatic Loading + + Red Hat Enterprise Linux 9 + + The disable option will allow the IPv6 module to be inserted, but prevent address assignment and activation of the network stack. + + + + + + + + + + Deactivate Wireless Network Interfaces + + Red Hat Enterprise Linux 9 + + All wireless interfaces should be disabled. + + + + + + + + + + Ensure All World-Writable Directories Are Owned by root user + + Red Hat Enterprise Linux 9 + + All world writable directories should be owned by root. + + + + + + + + + + Verify that All World-Writable Directories Have Sticky Bits Set + + Red Hat Enterprise Linux 9 + + The sticky bit should be set for all world-writable directories. + + + + + + + + + + Verify that local System.map file (if exists) is readable only by root + + Red Hat Enterprise Linux 9 + + + Checks that /boot/System.map-* are only readable by root. + + + + + + + + + + Ensure All SGID Executables Are Authorized + + Red Hat Enterprise Linux 9 + + Evaluates to true if all files with SGID set are owned by RPM packages. + + + + + + + + + + Ensure All SUID Executables Are Authorized + + Red Hat Enterprise Linux 9 + + Evaluates to true if all files with SUID set are owned by RPM packages. + + + + + + + + + + Ensure No World-Writable Files Exist + + Red Hat Enterprise Linux 9 + + The world-write permission should be disabled for all files. + + + + + + + + + + Ensure All Files Are Owned by a Group + + Red Hat Enterprise Linux 9 + + All files should be owned by a group + + + + + + + + + + Ensure All Files Are Owned by a User + + Red Hat Enterprise Linux 9 + + All files should be owned by a user + + + + + + + + + + Verify that system commands files are group owned by root or a system account + + Red Hat Enterprise Linux 9 + + + Checks that system commands in /bin /sbin /usr/bin /usr/sbin /usr/local/bin /usr/local/sbin + are owned by root group or a system account. + + + + + + + + + + + Verify that System Executables Have Root Ownership + + Red Hat Enterprise Linux 9 + + + Checks that /bin, /sbin, /usr/bin, /usr/sbin, /usr/local/bin, + /usr/local/sbin, /usr/libexec, and objects therein, are owned by root. + + + + + + + + + + + + Verify that System Executables Have Restrictive Permissions + + Red Hat Enterprise Linux 9 + + + Checks that binary files under /bin, /sbin, /usr/bin, /usr/sbin, + /usr/local/bin, /usr/local/sbin, and /usr/libexec are not group-writable or world-writable. + + + + + + + + + + + Add nodev Option to Non-Root Local Partitions + + Red Hat Enterprise Linux 9 + + The nodev mount option prevents files from being interpreted + as character or block devices. Legitimate character and block devices + should exist in the /dev directory on the root partition or within chroot + jails built for system services. All other locations should not allow + character and block devices. + + + + + + + + + + Bind Mount /var/tmp To /tmp + + Red Hat Enterprise Linux 9 + + The /var/tmp directory should be bind mounted to /tmp in + order to consolidate temporary storage into one location protected by the + same techniques as /tmp. + + + + + + + + + + + + + + Disable storing core dumps + + Red Hat Enterprise Linux 9 + + The kernel 'kernel.core_pattern' parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Disable storing core dumps + + Red Hat Enterprise Linux 9 + + The kernel 'kernel.core_pattern' parameter should be set to an empty string in the system runtime. + + + + + + + + + Disable storing core dumps + + Red Hat Enterprise Linux 9 + + The kernel 'kernel.core_pattern' parameter should be set to an empty string in the system configuration. + + + + + + + + + + + + + + Disable core dump backtraces + + Red Hat Enterprise Linux 9 + + Ensure 'ProcessSizeMax' is configured with value '0 in section 'Coredump' in /etc/systemd/coredump.conf + + + + + + + + + + Disable storing core dump + + Red Hat Enterprise Linux 9 + + Ensure 'Storage' is configured with value 'none in section 'Coredump' in /etc/systemd/coredump.conf + + + + + + + + + + Disable Core Dumps for All Users + + Red Hat Enterprise Linux 9 + + Core dumps for all users should be disabled + + + + + + + + + + + + + + Enable ExecShield via sysctl + + Red Hat Enterprise Linux 9 + + The kernel runtime parameter 'kernel.exec-shield' should not be disabled and set to 1 on 32-bit systems. + + + + + + + + + + + + + Enable NX or XD Support in the BIOS + + Red Hat Enterprise Linux 9 + + The NX (no-execution) bit flag should be set on the system. + + + + + + + + + + + + Ensure SELinux Not Disabled in /etc/default/grub + + Red Hat Enterprise Linux 9 + + + Check if selinux=0 OR enforcing=0 within the GRUB2 configuration files, fail if found. + + + + + + + + + + + + + Ensure No Device Files are Unlabeled by SELinux + + Red Hat Enterprise Linux 9 + + All device files in /dev should be assigned an SELinux security context other than 'device_t' and 'unlabeled_t'. + + + + + + + + + + + Ensure No Daemons are Unconfined by SELinux + + Red Hat Enterprise Linux 9 + + All pids in /proc should be assigned an SELinux security context other than 'unconfined_service_t'. + + + + + + + + + + Configure SELinux Policy + + Red Hat Enterprise Linux 9 + + The SELinux policy should be set appropriately. + + + + + + + + + + Ensure SELinux State is Enforcing + + Red Hat Enterprise Linux 9 + + The SELinux state should be enforcing the local policy. + + + + + + + + + + Prefer to use a 64-bit Operating System when supported + + Red Hat Enterprise Linux 9 + + Check if the system supports a 64-bit Operating System + + + + + + + + + + + Make sure that the dconf databases are up-to-date with regards to respective keyfiles + + Red Hat Enterprise Linux 9 + + Make sure that the dconf databases are up-to-date with regards to respective keyfiles. + + + + + + + + + + + + + + + + + + + + Configure GNOME3 DConf User Profile + + Red Hat Enterprise Linux 9 + + The DConf User profile should have the local DB configured. + + + + + + + + + + + Disable the GNOME3 Login Restart and Shutdown Buttons + + Red Hat Enterprise Linux 9 + + Disable the GNOME3 Login GUI Restart and Shutdown buttons to all users on the login screen. + + + + + + + + + + + + + + + Disable the GNOME3 Login User List + + Red Hat Enterprise Linux 9 + + Disable the GNOME3 GUI listing of all known users on the login screen. + + + + + + + + + + + + + + + Enable the GNOME3 Login Smartcard Authentication + + Red Hat Enterprise Linux 9 + + Enable smartcard authentication in the GNOME3 Login GUI. + + + + + + + + + + + + + + + Set the GNOME3 Login Number of Failures + + Red Hat Enterprise Linux 9 + + Set the GNOME3 number of login failure attempts. + + + + + + + + + + + + + + + Disable GDM Automatic Login + + Red Hat Enterprise Linux 9 + + Disable the GNOME Display Manager (GDM) ability to allow users to + automatically login. + + + + + + + + + + + Disable XDMCP in GDM + + Red Hat Enterprise Linux 9 + + Ensure 'Enable' is configured with value 'false in section 'xdmcp' in /etc/gdm/custom.conf + + + + + + + + + + + + Disable GNOME3 automount + + Red Hat Enterprise Linux 9 + + The system's default desktop environment, GNOME3, will mount + devices and removable media (such as DVDs, CDs and USB flash drives) + whenever they are inserted into the system. Disable automount within GNOME3. + + + + + + + + + + + + + + + Disable GNOME3 automount-open + + Red Hat Enterprise Linux 9 + + The system's default desktop environment, GNOME3, will mount + devices and removable media (such as DVDs, CDs and USB flash drives) + whenever they are inserted into the system. Disable automount-open within GNOME3. + + + + + + + + + + + + + + + Disable GNOME3 autorun + + Red Hat Enterprise Linux 9 + + The system's default desktop environment, GNOME3, will mount + devices and removable media (such as DVDs, CDs and USB flash drives) + whenever they are inserted into the system. Disable autorun within GNOME3. + + + + + + + + + + + + + + + Disable All GNOME3 Thumbnailers + + Red Hat Enterprise Linux 9 + + The system's default desktop environment, GNOME3, uses a + number of different thumbnailer programs to generate thumbnails for any + new or modified content in an opened folder. Disable the execution of + these thumbnail applications within GNOME3. + + + + + + + + + + + + + + + Disable WIFI Network Connection Creation in GNOME3 + + Red Hat Enterprise Linux 9 + + Disable the GNOME3 wireless network creation settings. + + + + + + + + + + + + + + + Disable WIFI Network Notification in GNOME3 + + Red Hat Enterprise Linux 9 + + Disable the GNOME3 wireless network notification. + + + + + + + + + + + + + + + Require Credential Prompting for Remote Access in GNOME3 + + Red Hat Enterprise Linux 9 + + Configure GNOME3 to require credential prompting for remote access. + + + + + + + + + + + + + + + Require Encryption for Remote Access in GNOME3 + + Red Hat Enterprise Linux 9 + + Configure GNOME3 to require encryption for remote access connections. + + + + + + + + + + + + + + + Enable GNOME3 Screensaver Idle Activation + + Red Hat Enterprise Linux 9 + + Idle activation of the screen saver should be enabled. + + + + + + + + + + + + + + + Ensure Users Cannot Change GNOME3 Screensaver Idle Activation + + Red Hat Enterprise Linux 9 + + Idle activation of the screen saver should not be changed by users. + + + + + + + + + + + + + + Set GNOME3 Screensaver Inactivity Timeout + + Red Hat Enterprise Linux 9 + + The allowed period of inactivity before the screensaver is activated. + + + + + + + + + + + + + + + Set GNOME3 Screensaver Lock Delay After Activation Period + + Red Hat Enterprise Linux 9 + + Idle activation of the screen lock should be enabled immediately or + after a delay. + + + + + + + + + + + + + + + Enable GNOME3 Screensaver Lock After Idle Period + + Red Hat Enterprise Linux 9 + + Idle activation of the screen lock should be enabled. + + + + + + + + + + + + + + + Ensure Users Cannot Change GNOME3 Screensaver Lock After Idle Period + + Red Hat Enterprise Linux 9 + + Idle activation of the screen lock should not be changed by users. + + + + + + + + + + + + + + Implement Blank Screensaver + + Red Hat Enterprise Linux 9 + + The GNOME3 screensaver should be blank. + + + + + + + + + + + + + + + Disable Full User Name on Splash Shield + + Red Hat Enterprise Linux 9 + + GNOME3 screen splash shield should not display full name of logged in user. + + + + + + + + + + + + + + + Ensure Users Cannot Change GNOME3 Screensaver Settings + + Red Hat Enterprise Linux 9 + + Ensure that users cannot change GNOME3 screensaver idle and lock settings. + + + + + + + + + + + + + + Ensure Users Cannot Change GNOME3 Session Idle Settings + + Red Hat Enterprise Linux 9 + + Ensure that users cannot change GNOME3 session idle settings. + + + + + + + + + + + + + + Disable Ctrl-Alt-Del Reboot Key Sequence in GNOME3 + + Red Hat Enterprise Linux 9 + + Disable the GNOME3 ctrl-alt-del reboot key sequence in GNOME3. + + + + + + + + + + + + + + + Disable Geolocation in GNOME3 + + Red Hat Enterprise Linux 9 + + Disable GNOME3 Geolocation for the clock and system. + + + + + + + + + + + + + + + + + Disable Power Settings in GNOME3 + + Red Hat Enterprise Linux 9 + + Disable GNOME3 power settings. + + + + + + + + + + + + + + + The Installed Operating System Is FIPS 140-2 Certified + + Red Hat Enterprise Linux 9 + + + The operating system installed on the system is a certified operating system that meets FIPS 140-2 requirements. + + + + + + + + + + + + + + + + + + The Installed Operating System Is Vendor Supported + + Red Hat Enterprise Linux 9 + + + The operating system installed on the system is supported by a vendor that provides security patches. + + + + + + + + + + + + + + + + + Configure BIND to use System Crypto Policy + + Red Hat Enterprise Linux 9 + + BIND should be configured to use the system-wide crypto policy setting. + + + + + + + + + + + Configure System Cryptography Policy + + Red Hat Enterprise Linux 9 + + Ensure crypto policy is correctly configured in /etc/crypto-policies/config, and the policy is current. + + + + + + + + + + + + + Configure GnuTLS library to use DoD-approved TLS Encryption + + Red Hat Enterprise Linux 9 + + Check presence of +VERS-ALL:-VERS-DTLS0.9:-VERS-SSL3.0:-VERS-TLS1.0:-VERS-TLS1.1:-VERS-DTLS1.0 in /etc/crypto-policies/back-ends/gnutls.config + + + + + + + + + + Configure Kerberos to use System Crypto Policy + + Red Hat Enterprise Linux 9 + + Kerberos should be configured to use the system-wide crypto policy setting. + + + + + + + + + + + Configure Libreswan to use System Crypto Policy + + Red Hat Enterprise Linux 9 + + Libreswan should be configured to use the system-wide crypto policy setting. + + + + + + + + + + + Configure OpenSSL library to use System Crypto Policy + + Red Hat Enterprise Linux 9 + + OpenSSL should be configured to use the system-wide crypto policy setting. + + + + + + + + + + Configure OpenSSL library to use TLS Encryption + + Red Hat Enterprise Linux 9 + + Configure OpenSSL library to use TLS Encryption + + + + + + + + + + + + + + Configure SSH to use System Crypto Policy + + Red Hat Enterprise Linux 9 + + SSH should be configured to use the system-wide crypto policy setting. + + + + + + + + + + Harden SSH client Crypto Policy + + Red Hat Enterprise Linux 9 + + Ensure the ssh client ciphers are configured correctly in /etc/ssh/ssh_config.d/02-ospp.conf + + + + + + + + + + + + + + + + Configure SSH Client to Use FIPS 140-2 Validated Ciphers: openssh.config + + Red Hat Enterprise Linux 9 + + Limit the Ciphers to those which are FIPS-approved. + + + + + + + + + + Configure SSH Server to Use FIPS 140-2 Validated Ciphers: opensshserver.config + + Red Hat Enterprise Linux 9 + + Limit the Ciphers to those which are FIPS-approved. + + + + + + + + + + Configure SSH Client to Use FIPS 140-2 Validated MACs: openssh.config + + Red Hat Enterprise Linux 9 + + Limit the Message Authentication Codes (MACs) to those which are FIPS-approved. + + + + + + + + + + Configure SSH Server to Use FIPS 140-2 Validated MACs: opensshserver.config + + Red Hat Enterprise Linux 9 + + Limit the Message Authentication Codes (MACs) to those which are FIPS-approved. + + + + + + + + + + Install Intrusion Detection Software + + Red Hat Enterprise Linux 9 + + Intrusion detection software or SELinux should be installed and enabled. + + + + + + + + + + + Install McAfee Virus Scanning Software + + Red Hat Enterprise Linux 9 + + McAfee Antivirus software should be installed. + + + + + + + + + + Install the McAfee Runtime Libraries and Linux Agent + + Red Hat Enterprise Linux 9 + + Install the McAfee Runtime Libraries (MFErt) and Linux Agent (MFEcma). + + + + + + + + + + Ensure McAfee Endpoint Security for Linux (ENSL) is running + + Red Hat Enterprise Linux 9 + + Ensure that McAfee Endpoint Security for Linux (ENSL) is running. + + + + + + + + + + Install the Asset Configuration Compliance Module (ACCM) + + Red Hat Enterprise Linux 9 + + Install the Asset Configuration Compliance Module (ACCM). + + + + + + + + + Install the Policy Auditor (PA) Module + + Red Hat Enterprise Linux 9 + + Install the Policy Auditor (PA) Module. + + + + + + + + + Enable Dracut FIPS Module + + Red Hat Enterprise Linux 9 + + fips module should be enabled in Dracut configuration + + + + + + + + + + Enable FIPS Mode + + Red Hat Enterprise Linux 9 + + Check if FIPS mode is enabled on the system + + + + + + + + + + + + + + Ensure '/etc/system-fips' exists + + Red Hat Enterprise Linux 9 + + Check /etc/system-fips exists + + + + + + + + + Set kernel parameter 'crypto.fips_enabled' to 1 + + Red Hat Enterprise Linux 9 + + The kernel 'crypto.fips_enabled' parameter should be set to '1' in system runtime. + + + + + + + + + + Build and Test AIDE Database + + Red Hat Enterprise Linux 9 + + The aide database must be initialized. + + + + + + + + + + + + Configure AIDE to Verify the Audit Tools + + Red Hat Enterprise Linux 9 + + The Red Hat Enterprise Linux 9 operating system file integrity tool must be configured to protect the integrity of the audit tools. + + + + + + + + + + + + + + + + + Configure Periodic Execution of AIDE + + Red Hat Enterprise Linux 9 + + By default, AIDE does not install itself for periodic + execution. Periodically running AIDE is necessary to reveal + unexpected changes in installed files. + + + + + + + + + + + + + + + + + Configure Notification of Post-AIDE Scan Details + + Red Hat Enterprise Linux 9 + + AIDE should notify appropriate personnel of the details + of a scan after the scan has been run. + + + + + + + + + + + + + + + Configure AIDE to Use FIPS 140-2 for Validating Hashes + + Red Hat Enterprise Linux 9 + + AIDE should be configured to use the FIPS 140-2 + cryptographic hashes. + + + + + + + + + + + + + Configure AIDE to Verify Access Control Lists (ACLs) + + Red Hat Enterprise Linux 9 + + AIDE should be configured to verify Access Control Lists (ACLs). + + + + + + + + + + + Configure AIDE to Verify Extended Attributes + + Red Hat Enterprise Linux 9 + + AIDE should be configured to verify extended file attributes. + + + + + + + + + + + Verify File Hashes with RPM + + Red Hat Enterprise Linux 9 + + Verify the RPM digests of system binaries using the RPM database. + + + + + + + + + + Verify and Correct Ownership with RPM + + Red Hat Enterprise Linux 9 + + Verify ownership of installed packages + by comparing the installed files with information about the + files taken from the package metadata stored in the RPM + database. + + + + + + + + + + + Verify and Correct File Permissions with RPM + + Red Hat Enterprise Linux 9 + + Verify the permissions of installed packages + by comparing the installed files with information about the + files taken from the package metadata stored in the RPM + database. + + + + + + + + + + Ensure Users Re-Authenticate for Privilege Escalation - sudo !authenticate + + Red Hat Enterprise Linux 9 + + Checks sudo usage without authentication + + + + + + + + + + + Ensure Users Re-Authenticate for Privilege Escalation - sudo NOPASSWD + + Red Hat Enterprise Linux 9 + + Checks sudo usage without password + + + + + + + + + + + Ensure Users Re-Authenticate for Privilege Escalation - sudo + + Red Hat Enterprise Linux 9 + + Checks sudo usage without password + + + + + + + + + + + The operating system must require Re-Authentication when using the sudo command. Ensure sudo timestamp_timeout is appropriate - sudo timestamp_timeout + + Red Hat Enterprise Linux 9 + + 'Ensure sudo timestamp_timeout is appropriate - sudo timestamp_timeout + + + + + + + + + + The operating system must restrict privilege elevation to authorized personnel + + Red Hat Enterprise Linux 9 + + Check that sudoers doesn't allow all users to run commands via sudo + + + + + + + + + + + Only the VDSM User Can Use sudo NOPASSWD + + Red Hat Enterprise Linux 9 + + Checks sudo usage for the vdsm user without a password + + + + + + + + + + + Ensure sudo only includes the default configuration directory + + Red Hat Enterprise Linux 9 + + Check if sudo includes only the default includedir + + + + + + + + + + + + + + + + + + + Explicit arguments in sudo specifications + + Red Hat Enterprise Linux 9 + + Check that sudoers doesn't contain commands without arguments specified + + + + + + + + + + Don't define allowed commands in sudoers by means of exclusion + + Red Hat Enterprise Linux 9 + + Check that sudoers doesn't contain command negations + + + + + + + + + + Don't target root user in the sudoers file + + Red Hat Enterprise Linux 9 + + Check that sudoers doesn't allow users to run commands as root + + + + + + + + + + + Ensure invoking users password for privilege escalation when using sudo + + Red Hat Enterprise Linux 9 + + Ensure invoking user's password for privilege escalation when using sudo + + + + + + + + + + + + + + + Ensure dnf Removes Previous Package Versions + + Red Hat Enterprise Linux 9 + + The clean_requirements_on_remove option should be used to ensure that old + versions of software components are removed after updating. + + + + + + + + + + Configure dnf-automatic to Install Available Updates Automatically + + Red Hat Enterprise Linux 9 + + Ensure 'apply_updates' is configured with value 'yes in section 'commands' in /etc/dnf/automatic.conf + + + + + + + + + + + + + Configure dnf-automatic to Install Only Security Updates + + Red Hat Enterprise Linux 9 + + Ensure 'upgrade_type' is configured with value 'security in section 'commands' in /etc/dnf/automatic.conf + + + + + + + + + + + + + Ensure gpgcheck Enabled In Main dnf Configuration + + Red Hat Enterprise Linux 9 + + The gpgcheck option should be used to ensure that checking + of an RPM package's signature always occurs prior to its + installation. + + + + + + + + + + Ensure gpgcheck Enabled for Local Packages + + Red Hat Enterprise Linux 9 + + The localpkg_gpgcheck option should be used to ensure that checking + of an RPM package's signature always occurs prior to its + installation. + + + + + + + + + + Ensure gpgcheck Enabled for All dnf Package Repositories + + Red Hat Enterprise Linux 9 + + Ensure all yum or dnf repositories utilize signature checking. + + + + + + + + + + Ensure Red Hat GPG Key Installed + + Red Hat Enterprise Linux 9 + + The Red Hat release and auxiliary key packages are required to be installed. + + + + + + + + + + + + + + + + + + + + + + Ensure PAM Enforces Password Requirements - Minimum Digit Characters + + Red Hat Enterprise Linux 9 + + The password dcredit should meet minimum requirements + + + + + + + + + + + + + Ensure PAM Enforces Password Requirements - Prevent the Use of Dictionary Words + + Red Hat Enterprise Linux 9 + + The password dictcheck should meet minimum requirements + + + + + + + + + + + + + Ensure PAM Enforces Password Requirements - Minimum Different Characters + + Red Hat Enterprise Linux 9 + + The password difok should meet minimum requirements + + + + + + + + + + + + + Ensure PAM Enforces Password Requirements - Enforce for Local Accounts Only + + Red Hat Enterprise Linux 9 + + Check presence of local_users_only in /etc/security/pwquality.conf + + + + + + + + + + Ensure PAM Enforces Password Requirements - Enforce for root User + + Red Hat Enterprise Linux 9 + + Check presence of enforce_for_root in /etc/security/pwquality.conf + + + + + + + + + + + Ensure PAM Enforces Password Requirements - Minimum Lowercase Characters + + Red Hat Enterprise Linux 9 + + The password lcredit should meet minimum requirements + + + + + + + + + + + + + Ensure PAM Enforces Password Requirements - Maximum Consecutive Repeating Characters from Same Character Class + + Red Hat Enterprise Linux 9 + + The password maxclassrepeat should meet minimum requirements + + + + + + + + + + + + + Set Password Maximum Consecutive Repeating Characters + + Red Hat Enterprise Linux 9 + + The password maxrepeat should meet minimum requirements + + + + + + + + + + + + + Ensure PAM Enforces Password Requirements - Minimum Different Categories + + Red Hat Enterprise Linux 9 + + The password minclass should meet minimum requirements + + + + + + + + + + + + + Ensure PAM Enforces Password Requirements - Minimum Length + + Red Hat Enterprise Linux 9 + + The password minlen should meet minimum requirements + + + + + + + + + + + + + Ensure PAM Enforces Password Requirements - Minimum Special Characters + + Red Hat Enterprise Linux 9 + + The password ocredit should meet minimum requirements + + + + + + + + + + + + + Ensure PAM Enforces Password Requirements - Minimum Uppercase Characters + + Red Hat Enterprise Linux 9 + + The password ucredit should meet minimum requirements + + + + + + + + + + + + + Configure auditing of unsuccessful file accesses + + Red Hat Enterprise Linux 9 + + Inspect the contents of /etc/audit/rules.d/30-ospp-v42-3-access-failed.rules + + + + + + + + + + Configure auditing of unsuccessful file accesses (AArch64) + + Red Hat Enterprise Linux 9 + + Inspect the contents of /etc/audit/rules.d/30-ospp-v42-3-access-failed.rules + + + + + + + + + + Configure auditing of unsuccessful file accesses (ppc64le) + + Red Hat Enterprise Linux 9 + + Inspect the contents of /etc/audit/rules.d/30-ospp-v42-3-access-failed.rules + + + + + + + + + + Configure auditing of successful file accesses + + Red Hat Enterprise Linux 9 + + Inspect the contents of /etc/audit/rules.d/30-ospp-v42-3-access-success.rules + + + + + + + + + + Configure auditing of successful file accesses (AArch64) + + Red Hat Enterprise Linux 9 + + Inspect the contents of /etc/audit/rules.d/30-ospp-v42-3-access-success.rules + + + + + + + + + + Configure auditing of successful file accesses (ppc64le) + + Red Hat Enterprise Linux 9 + + Inspect the contents of /etc/audit/rules.d/30-ospp-v42-3-access-success.rules + + + + + + + + + + Configure basic parameters of Audit system + + Red Hat Enterprise Linux 9 + + Inspect the contents of /etc/audit/rules.d/10-base-config.rules + + + + + + + + + + Configure auditing of unsuccessful file creations + + Red Hat Enterprise Linux 9 + + Inspect the contents of /etc/audit/rules.d/30-ospp-v42-1-create-failed.rules + + + + + + + + + + Configure auditing of unsuccessful file creations (AArch64) + + Red Hat Enterprise Linux 9 + + Inspect the contents of /etc/audit/rules.d/30-ospp-v42-1-create-failed.rules + + + + + + + + + + Configure auditing of unsuccessful file creations (ppc64le) + + Red Hat Enterprise Linux 9 + + Inspect the contents of /etc/audit/rules.d/30-ospp-v42-1-create-failed.rules + + + + + + + + + + Configure auditing of successful file creations + + Red Hat Enterprise Linux 9 + + Inspect the contents of /etc/audit/rules.d/30-ospp-v42-1-create-success.rules + + + + + + + + + + Configure auditing of successful file creations (AArch64) + + Red Hat Enterprise Linux 9 + + Inspect the contents of /etc/audit/rules.d/30-ospp-v42-1-create-success.rules + + + + + + + + + + Configure auditing of successful file creations (ppc64le) + + Red Hat Enterprise Linux 9 + + Inspect the contents of /etc/audit/rules.d/30-ospp-v42-1-create-success.rules + + + + + + + + + + Configure auditing of unsuccessful file deletions + + Red Hat Enterprise Linux 9 + + Inspect the contents of /etc/audit/rules.d/30-ospp-v42-4-delete-failed.rules + + + + + + + + + + Configure auditing of unsuccessful file deletions (AArch64) + + Red Hat Enterprise Linux 9 + + Inspect the contents of /etc/audit/rules.d/30-ospp-v42-4-delete-failed.rules + + + + + + + + + + Configure auditing of unsuccessful file deletions (ppc64le) + + Red Hat Enterprise Linux 9 + + Inspect the contents of /etc/audit/rules.d/30-ospp-v42-4-delete-failed.rules + + + + + + + + + + Configure auditing of successful file deletions + + Red Hat Enterprise Linux 9 + + Inspect the contents of /etc/audit/rules.d/30-ospp-v42-4-delete-success.rules + + + + + + + + + + Configure auditing of successful file deletions (AArch64) + + Red Hat Enterprise Linux 9 + + Inspect the contents of /etc/audit/rules.d/30-ospp-v42-4-delete-success.rules + + + + + + + + + + Configure auditing of successful file deletions (ppc64le) + + Red Hat Enterprise Linux 9 + + Inspect the contents of /etc/audit/rules.d/30-ospp-v42-4-delete-success.rules + + + + + + + + + + Configure immutable Audit login UIDs + + Red Hat Enterprise Linux 9 + + Inspect the contents of /etc/audit/rules.d/11-loginuid.rules + + + + + + + + + + Configure auditing of unsuccessful file modifications + + Red Hat Enterprise Linux 9 + + Inspect the contents of /etc/audit/rules.d/30-ospp-v42-2-modify-failed.rules + + + + + + + + + + Configure auditing of unsuccessful file modifications (AARch64) + + Red Hat Enterprise Linux 9 + + Inspect the contents of /etc/audit/rules.d/30-ospp-v42-2-modify-failed.rules + + + + + + + + + + Configure auditing of unsuccessful file modifications (ppc64le) + + Red Hat Enterprise Linux 9 + + Inspect the contents of /etc/audit/rules.d/30-ospp-v42-2-modify-failed.rules + + + + + + + + + + Configure auditing of successful file modifications + + Red Hat Enterprise Linux 9 + + Inspect the contents of /etc/audit/rules.d/30-ospp-v42-2-modify-success.rules + + + + + + + + + + Configure auditing of successful file modifications (AArch64) + + Red Hat Enterprise Linux 9 + + Inspect the contents of /etc/audit/rules.d/30-ospp-v42-2-modify-success.rules + + + + + + + + + + Configure auditing of successful file modifications (ppc64le) + + Red Hat Enterprise Linux 9 + + Inspect the contents of /etc/audit/rules.d/30-ospp-v42-2-modify-success.rules + + + + + + + + + + Configure auditing of loading and unloading of kernel modules + + Red Hat Enterprise Linux 9 + + Inspect the contents of /etc/audit/rules.d/43-module-load.rules + + + + + + + + + + Configure auditing of loading and unloading of kernel modules (ppc64le) + + Red Hat Enterprise Linux 9 + + Inspect the contents of /etc/audit/rules.d/43-module-load.rules + + + + + + + + + + Perform general configuration of Audit for OSPP + + Red Hat Enterprise Linux 9 + + Inspect the contents of /etc/audit/rules.d/30-ospp-v42.rules + + + + + + + + + + Perform general configuration of Audit for OSPP (AArch64) + + Red Hat Enterprise Linux 9 + + Inspect the contents of /etc/audit/rules.d/30-ospp-v42.rules + + + + + + + + + + Perform general configuration of Audit for OSPP (ppc64le) + + Red Hat Enterprise Linux 9 + + Inspect the contents of /etc/audit/rules.d/30-ospp-v42.rules + + + + + + + + + + Configure auditing of unsuccessful ownership changes + + Red Hat Enterprise Linux 9 + + Inspect the contents of /etc/audit/rules.d/30-ospp-v42-6-owner-change-failed.rules + + + + + + + + + + Configure auditing of unsuccessful ownership changes (AArch64) + + Red Hat Enterprise Linux 9 + + Inspect the contents of /etc/audit/rules.d/30-ospp-v42-6-owner-change-failed.rules + + + + + + + + + + Configure auditing of unsuccessful ownership changes (ppc64le) + + Red Hat Enterprise Linux 9 + + Inspect the contents of /etc/audit/rules.d/30-ospp-v42-6-owner-change-failed.rules + + + + + + + + + + Configure auditing of successful ownership changes + + Red Hat Enterprise Linux 9 + + Inspect the contents of /etc/audit/rules.d/30-ospp-v42-6-owner-change-success.rules + + + + + + + + + + Configure auditing of successful ownership changes (AArch64) + + Red Hat Enterprise Linux 9 + + Inspect the contents of /etc/audit/rules.d/30-ospp-v42-6-owner-change-success.rules + + + + + + + + + + Configure auditing of successful ownership changes (ppc64le) + + Red Hat Enterprise Linux 9 + + Inspect the contents of /etc/audit/rules.d/30-ospp-v42-6-owner-change-success.rules + + + + + + + + + + Configure auditing of unsuccessful permission changes + + Red Hat Enterprise Linux 9 + + Inspect the contents of /etc/audit/rules.d/30-ospp-v42-5-perm-change-failed.rules + + + + + + + + + + Configure auditing of unsuccessful permission changes (AArch64) + + Red Hat Enterprise Linux 9 + + Inspect the contents of /etc/audit/rules.d/30-ospp-v42-5-perm-change-failed.rules + + + + + + + + + + Configure auditing of unsuccessful permission changes (ppc64le) + + Red Hat Enterprise Linux 9 + + Inspect the contents of /etc/audit/rules.d/30-ospp-v42-5-perm-change-failed.rules + + + + + + + + + + Configure auditing of successful permission changes + + Red Hat Enterprise Linux 9 + + Inspect the contents of /etc/audit/rules.d/30-ospp-v42-5-perm-change-success.rules + + + + + + + + + + Configure auditing of successful permission changes (AArch64) + + Red Hat Enterprise Linux 9 + + Inspect the contents of /etc/audit/rules.d/30-ospp-v42-5-perm-change-success.rules + + + + + + + + + + Configure auditing of successful permission changes (ppc64le) + + Red Hat Enterprise Linux 9 + + Inspect the contents of /etc/audit/rules.d/30-ospp-v42-5-perm-change-success.rules + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - init + + Red Hat Enterprise Linux 9 + + Audit rules about the information on the use of init is enabled. + + + + + + + + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - poweroff + + Red Hat Enterprise Linux 9 + + Audit rules about the information on the use of poweroff is enabled. + + + + + + + + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - reboot + + Red Hat Enterprise Linux 9 + + Audit rules about the information on the use of reboot is enabled. + + + + + + + + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - shutdown + + Red Hat Enterprise Linux 9 + + Audit rules about the information on the use of shutdown is enabled. + + + + + + + + + + + + + + + + + Record Events that Modify the System's Discretionary Access Controls - chmod + + Red Hat Enterprise Linux 9 + + The changing of file permissions and attributes should be audited. + + + + + + + + + + + + + + + + + + + + + + + + + Record Events that Modify the System's Discretionary Access Controls - chown + + Red Hat Enterprise Linux 9 + + The changing of file permissions and attributes should be audited. + + + + + + + + + + + + + + + + + + + + + + + + + Record Events that Modify the System's Discretionary Access Controls - fchmod + + Red Hat Enterprise Linux 9 + + The changing of file permissions and attributes should be audited. + + + + + + + + + + + + + + + + + + + + + + + + + Record Events that Modify the System's Discretionary Access Controls - fchmodat + + Red Hat Enterprise Linux 9 + + The changing of file permissions and attributes should be audited. + + + + + + + + + + + + + + + + + + + + + + + + + Record Events that Modify the System's Discretionary Access Controls - fchown + + Red Hat Enterprise Linux 9 + + The changing of file permissions and attributes should be audited. + + + + + + + + + + + + + + + + + + + + + + + + + Record Events that Modify the System's Discretionary Access Controls - fchownat + + Red Hat Enterprise Linux 9 + + The changing of file permissions and attributes should be audited. + + + + + + + + + + + + + + + + + + + + + + + + + Record Events that Modify the System's Discretionary Access Controls - fremovexattr + + Red Hat Enterprise Linux 9 + + The changing of file permissions and attributes should be audited. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Record Events that Modify the System's Discretionary Access Controls - fsetxattr + + Red Hat Enterprise Linux 9 + + The changing of file permissions and attributes should be audited. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Record Events that Modify the System's Discretionary Access Controls - lchown + + Red Hat Enterprise Linux 9 + + The changing of file permissions and attributes should be audited. + + + + + + + + + + + + + + + + + + + + + + + + + Record Events that Modify the System's Discretionary Access Controls - lremovexattr + + Red Hat Enterprise Linux 9 + + The changing of file permissions and attributes should be audited. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Record Events that Modify the System's Discretionary Access Controls - lsetxattr + + Red Hat Enterprise Linux 9 + + The changing of file permissions and attributes should be audited. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Record Events that Modify the System's Discretionary Access Controls - removexattr + + Red Hat Enterprise Linux 9 + + The changing of file permissions and attributes should be audited. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Record Events that Modify the System's Discretionary Access Controls - setxattr + + Red Hat Enterprise Linux 9 + + The changing of file permissions and attributes should be audited. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Record Events that Modify the System's Discretionary Access Controls - umount2 + + Red Hat Enterprise Linux 9 + + The changing of file permissions and attributes should be audited. + + + + + + + + + + + + + + + + + + + + + + + + + Record Events that Modify User/Group Information via open syscall - /etc/group + + Red Hat Enterprise Linux 9 + + Audit rules about the write events to /etc/group + + + + + + + + + + + + + + + + + + + + + + + + Record Events that Modify User/Group Information via open_by_handle_at syscall - /etc/group + + Red Hat Enterprise Linux 9 + + Audit rules about the write events to /etc/group + + + + + + + + + + + + + + + + + + + + + + + + Record Events that Modify User/Group Information via openat syscall - /etc/group + + Red Hat Enterprise Linux 9 + + Audit rules about the write events to /etc/group + + + + + + + + + + + + + + + + + + + + + + + + Record Events that Modify User/Group Information via open syscall - /etc/gshadow + + Red Hat Enterprise Linux 9 + + Audit rules about the write events to /etc/gshadow + + + + + + + + + + + + + + + + + + + + + + + + Record Events that Modify User/Group Information via open_by_handle_at syscall - /etc/gshadow + + Red Hat Enterprise Linux 9 + + Audit rules about the write events to /etc/gshadow + + + + + + + + + + + + + + + + + + + + + + + + Record Events that Modify User/Group Information via openat syscall - /etc/gshadow + + Red Hat Enterprise Linux 9 + + Audit rules about the write events to /etc/gshadow + + + + + + + + + + + + + + + + + + + + + + + + Record Events that Modify User/Group Information via open syscall - /etc/passwd + + Red Hat Enterprise Linux 9 + + Audit rules about the write events to /etc/passwd + + + + + + + + + + + + + + + + + + + + + + + + Record Events that Modify User/Group Information via open_by_handle_at syscall - /etc/passwd + + Red Hat Enterprise Linux 9 + + Audit rules about the write events to /etc/passwd + + + + + + + + + + + + + + + + + + + + + + + + Record Events that Modify User/Group Information via openat syscall - /etc/passwd + + Red Hat Enterprise Linux 9 + + Audit rules about the write events to /etc/passwd + + + + + + + + + + + + + + + + + + + + + + + + Record Events that Modify User/Group Information via open syscall - /etc/shadow + + Red Hat Enterprise Linux 9 + + Audit rules about the write events to /etc/shadow + + + + + + + + + + + + + + + + + + + + + + + + Record Events that Modify User/Group Information via open_by_handle_at syscall - /etc/shadow + + Red Hat Enterprise Linux 9 + + Audit rules about the write events to /etc/shadow + + + + + + + + + + + + + + + + + + + + + + + + Record Events that Modify User/Group Information via openat syscall - /etc/shadow + + Red Hat Enterprise Linux 9 + + Audit rules about the write events to /etc/shadow + + + + + + + + + + + + + + + + + + + + + + + + Record Any Attempts to Run chacl + + Red Hat Enterprise Linux 9 + + Audit rules about the information on the use of chacl is enabled. + + + + + + + + + + + + + + + + + Record Any Attempts to Run chcon + + Red Hat Enterprise Linux 9 + + Audit rules about the information on the use of chcon is enabled. + + + + + + + + + + + + + + + + + Record Any Attempts to Run restorecon + + Red Hat Enterprise Linux 9 + + Audit rules about the information on the use of restorecon is enabled. + + + + + + + + + + + + + + + + + Record Any Attempts to Run semanage + + Red Hat Enterprise Linux 9 + + Audit rules about the information on the use of semanage is enabled. + + + + + + + + + + + + + + + + + Record Any Attempts to Run setfacl + + Red Hat Enterprise Linux 9 + + Audit rules about the information on the use of setfacl is enabled. + + + + + + + + + + + + + + + + + Record Any Attempts to Run setfiles + + Red Hat Enterprise Linux 9 + + Audit rules about the information on the use of setfiles is enabled. + + + + + + + + + + + + + + + + + Record Any Attempts to Run setsebool + + Red Hat Enterprise Linux 9 + + Audit rules about the information on the use of setsebool is enabled. + + + + + + + + + + + + + + + + + Record Any Attempts to Run seunshare + + Red Hat Enterprise Linux 9 + + Audit rules about the information on the use of seunshare is enabled. + + + + + + + + + + + + + + + + + Ensure auditd Collects File Deletion Events by User - rename + + Red Hat Enterprise Linux 9 + + The deletion of files should be audited. + + + + + + + + + + + + + + + + + + + + + + + + + Ensure auditd Collects File Deletion Events by User - renameat + + Red Hat Enterprise Linux 9 + + The deletion of files should be audited. + + + + + + + + + + + + + + + + + + + + + + + + + Ensure auditd Collects File Deletion Events by User - rmdir + + Red Hat Enterprise Linux 9 + + The deletion of files should be audited. + + + + + + + + + + + + + + + + + + + + + + + + + Ensure auditd Collects File Deletion Events by User - unlink + + Red Hat Enterprise Linux 9 + + The deletion of files should be audited. + + + + + + + + + + + + + + + + + + + + + + + + + Ensure auditd Collects File Deletion Events by User - unlinkat + + Red Hat Enterprise Linux 9 + + The deletion of files should be audited. + + + + + + + + + + + + + + + + + + + + + + + + + Record Attempts to Alter Logon and Logout Events - faillock + + Red Hat Enterprise Linux 9 + + Audit rules should be configured to log successful and unsuccessful login and logout events. + + + + + + + + + + + + + + + + + Record Attempts to Alter Logon and Logout Events - lastlog + + Red Hat Enterprise Linux 9 + + Audit rules should be configured to log successful and unsuccessful login and logout events. + + + + + + + + + + + + + + + + + Record Attempts to Alter Logon and Logout Events - tallylog + + Red Hat Enterprise Linux 9 + + Audit rules should be configured to log successful and unsuccessful login and logout events. + + + + + + + + + + + + + + + + + Ensure auditd Collects Information on Exporting to Media (successful) + + Red Hat Enterprise Linux 9 + + The changing of file permissions and attributes should be audited. + + + + + + + + + + + + + + + + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - at + + Red Hat Enterprise Linux 9 + + Audit rules about the information on the use of at is enabled. + + + + + + + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - chage + + Red Hat Enterprise Linux 9 + + Audit rules about the information on the use of chage is enabled. + + + + + + + + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - chsh + + Red Hat Enterprise Linux 9 + + Audit rules about the information on the use of chsh is enabled. + + + + + + + + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - crontab + + Red Hat Enterprise Linux 9 + + Audit rules about the information on the use of crontab is enabled. + + + + + + + + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - gpasswd + + Red Hat Enterprise Linux 9 + + Audit rules about the information on the use of gpasswd is enabled. + + + + + + + + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - kmod + + Red Hat Enterprise Linux 9 + + Audit rules about the information on the use of kmod is enabled. + + + + + + + + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - mount + + Red Hat Enterprise Linux 9 + + Audit rules about the information on the use of mount is enabled. + + + + + + + + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - newgidmap + + Red Hat Enterprise Linux 9 + + Audit rules about the information on the use of newgidmap is enabled. + + + + + + + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - newgrp + + Red Hat Enterprise Linux 9 + + Audit rules about the information on the use of newgrp is enabled. + + + + + + + + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - newuidmap + + Red Hat Enterprise Linux 9 + + Audit rules about the information on the use of newuidmap is enabled. + + + + + + + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - pam_timestamp_check + + Red Hat Enterprise Linux 9 + + Audit rules about the information on the use of pam_timestamp_check is enabled. + + + + + + + + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - passwd + + Red Hat Enterprise Linux 9 + + Audit rules about the information on the use of passwd is enabled. + + + + + + + + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - postdrop + + Red Hat Enterprise Linux 9 + + Audit rules about the information on the use of postdrop is enabled. + + + + + + + + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - postqueue + + Red Hat Enterprise Linux 9 + + Audit rules about the information on the use of postqueue is enabled. + + + + + + + + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - pt_chown + + Red Hat Enterprise Linux 9 + + Audit rules about the information on the use of pt_chown is enabled. + + + + + + + + + + + + + + + + + Record Any Attempts to Run ssh-agent + + Red Hat Enterprise Linux 9 + + Audit rules about the information on the use of ssh_agent is enabled. + + + + + + + + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - ssh-keysign + + Red Hat Enterprise Linux 9 + + Audit rules about the information on the use of ssh_keysign is enabled. + + + + + + + + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - su + + Red Hat Enterprise Linux 9 + + Audit rules about the information on the use of su is enabled. + + + + + + + + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - sudo + + Red Hat Enterprise Linux 9 + + Audit rules about the information on the use of sudo is enabled. + + + + + + + + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - sudoedit + + Red Hat Enterprise Linux 9 + + Audit rules about the information on the use of sudoedit is enabled. + + + + + + + + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - umount + + Red Hat Enterprise Linux 9 + + Audit rules about the information on the use of umount is enabled. + + + + + + + + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - unix_chkpwd + + Red Hat Enterprise Linux 9 + + Audit rules about the information on the use of unix_chkpwd is enabled. + + + + + + + + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - unix_update + + Red Hat Enterprise Linux 9 + + Audit rules about the information on the use of unix_update is enabled. + + + + + + + + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - userhelper + + Red Hat Enterprise Linux 9 + + Audit rules about the information on the use of userhelper is enabled. + + + + + + + + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - usermod + + Red Hat Enterprise Linux 9 + + Audit rules about the information on the use of usermod is enabled. + + + + + + + + + + + + + + + + + Ensure auditd Collects Information on the Use of Privileged Commands - usernetctl + + Red Hat Enterprise Linux 9 + + Audit rules about the information on the use of usernetctl is enabled. + + + + + + + + + + + + + + + + Record Unsuccessful Permission Changes to Files - chmod + + Red Hat Enterprise Linux 9 + + Audit rules about the unauthorized access attempts to files (unsuccessful) are enabled. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Record Unsuccessful Ownership Changes to Files - chown + + Red Hat Enterprise Linux 9 + + Audit rules about the unauthorized access attempts to files (unsuccessful) are enabled. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Record Unsuccessful Access Attempts to Files - creat + + Red Hat Enterprise Linux 9 + + Audit rules about the unauthorized access attempts to files (unsuccessful) are enabled. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Record Unsuccessful Permission Changes to Files - fchmod + + Red Hat Enterprise Linux 9 + + Audit rules about the unauthorized access attempts to files (unsuccessful) are enabled. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Record Unsuccessful Permission Changes to Files - fchmodat + + Red Hat Enterprise Linux 9 + + Audit rules about the unauthorized access attempts to files (unsuccessful) are enabled. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Record Unsuccessful Ownership Changes to Files - fchown + + Red Hat Enterprise Linux 9 + + Audit rules about the unauthorized access attempts to files (unsuccessful) are enabled. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Record Unsuccessful Ownership Changes to Files - fchownat + + Red Hat Enterprise Linux 9 + + Audit rules about the unauthorized access attempts to files (unsuccessful) are enabled. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Record Unsuccessful Permission Changes to Files - fremovexattr + + Red Hat Enterprise Linux 9 + + Audit rules about the unauthorized access attempts to files (unsuccessful) are enabled. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Record Unsuccessful Permission Changes to Files - fsetxattr + + Red Hat Enterprise Linux 9 + + Audit rules about the unauthorized access attempts to files (unsuccessful) are enabled. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Record Unsuccessful Access Attempts to Files - ftruncate + + Red Hat Enterprise Linux 9 + + Audit rules about the unauthorized access attempts to files (unsuccessful) are enabled. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Record Unsuccessful Ownership Changes to Files - lchown + + Red Hat Enterprise Linux 9 + + Audit rules about the unauthorized access attempts to files (unsuccessful) are enabled. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Record Unsuccessful Permission Changes to Files - lremovexattr + + Red Hat Enterprise Linux 9 + + Audit rules about the unauthorized access attempts to files (unsuccessful) are enabled. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Record Unsuccessful Permission Changes to Files - lsetxattr + + Red Hat Enterprise Linux 9 + + Audit rules about the unauthorized access attempts to files (unsuccessful) are enabled. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Record Unsuccessful Access Attempts to Files - open + + Red Hat Enterprise Linux 9 + + Audit rules about the unauthorized access attempts to files (unsuccessful) are enabled. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Record Unsuccessful Access Attempts to Files - open_by_handle_at + + Red Hat Enterprise Linux 9 + + Audit rules about the unauthorized access attempts to files (unsuccessful) are enabled. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Record Unsuccessful Creation Attempts to Files - open_by_handle_at O_CREAT + + Red Hat Enterprise Linux 9 + + Audit rules about the information on the unsuccessful use of open_by_handle_at O_CREAT is enabled. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Record Unsuccessful Modification Attempts to Files - open_by_handle_at O_TRUNC_WRITE + + Red Hat Enterprise Linux 9 + + Audit rules about the information on the unsuccessful use of open_by_handle_at O_TRUNC is enabled. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Ensure auditd Unauthorized Access Attempts To open_by_handle_at Are Ordered Correctly + + Red Hat Enterprise Linux 9 + + Audit rules about the information on the unsuccessful use of open_by_handle_at is configured in the proper rule order. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Record Unsuccessful Creation Attempts to Files - open O_CREAT + + Red Hat Enterprise Linux 9 + + Audit rules about the information on the unsuccessful use of open O_CREAT is enabled. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Record Unsuccessful Modification Attempts to Files - open O_TRUNC_WRITE + + Red Hat Enterprise Linux 9 + + Audit rules about the information on the unsuccessful use of open O_TRUNC is enabled. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Ensure auditd Rules For Unauthorized Attempts To open Are Ordered Correctly + + Red Hat Enterprise Linux 9 + + Audit rules about the information on the unsuccessful use of open is configured in the proper rule order. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Record Unsuccessful Access Attempts to Files - openat + + Red Hat Enterprise Linux 9 + + Audit rules about the unauthorized access attempts to files (unsuccessful) are enabled. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Record Unsuccessful Creation Attempts to Files - openat O_CREAT + + Red Hat Enterprise Linux 9 + + Audit rules about the information on the unsuccessful use of openat O_CREAT is enabled. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Record Unsuccessful Modification Attempts to Files - openat O_TRUNC_WRITE + + Red Hat Enterprise Linux 9 + + Audit rules about the information on the unsuccessful use of openat O_TRUNC is enabled. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Ensure auditd Rules For Unauthorized Attempts To openat Are Ordered Correctly + + Red Hat Enterprise Linux 9 + + Audit rules about the information on the unsuccessful use of openat is configured in the proper rule order. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Record Unsuccessful Permission Changes to Files - removexattr + + Red Hat Enterprise Linux 9 + + Audit rules about the unauthorized access attempts to files (unsuccessful) are enabled. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Record Unsuccessful Delete Attempts to Files - rename + + Red Hat Enterprise Linux 9 + + Audit rules about the unauthorized access attempts to files (unsuccessful) are enabled. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Record Unsuccessful Delete Attempts to Files - renameat + + Red Hat Enterprise Linux 9 + + Audit rules about the unauthorized access attempts to files (unsuccessful) are enabled. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Record Unsuccessful Permission Changes to Files - setxattr + + Red Hat Enterprise Linux 9 + + Audit rules about the unauthorized access attempts to files (unsuccessful) are enabled. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Record Unsuccessful Access Attempts to Files - truncate + + Red Hat Enterprise Linux 9 + + Audit rules about the unauthorized access attempts to files (unsuccessful) are enabled. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Record Unsuccessful Delete Attempts to Files - unlink + + Red Hat Enterprise Linux 9 + + Audit rules about the unauthorized access attempts to files (unsuccessful) are enabled. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Record Unsuccessful Delete Attempts to Files - unlinkat + + Red Hat Enterprise Linux 9 + + Audit rules about the unauthorized access attempts to files (unsuccessful) are enabled. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Record Events that Modify User/Group Information - /etc/group + + Red Hat Enterprise Linux 9 + + Audit user/group modification. + + + + + + + + + + + + + + + + + Record Events that Modify User/Group Information - /etc/gshadow + + Red Hat Enterprise Linux 9 + + Audit user/group modification. + + + + + + + + + + + + + + + + + Record Events that Modify User/Group Information - /etc/security/opasswd + + Red Hat Enterprise Linux 9 + + Audit user/group modification. + + + + + + + + + + + + + + + + + Record Events that Modify User/Group Information - /etc/passwd + + Red Hat Enterprise Linux 9 + + Audit user/group modification. + + + + + + + + + + + + + + + + + Record Events that Modify User/Group Information - /etc/shadow + + Red Hat Enterprise Linux 9 + + Audit user/group modification. + + + + + + + + + + + + + + + + + Set number of records to cause an explicit flush to audit logs + + Red Hat Enterprise Linux 9 + + Ensure 'freq' is configured with value '50' in /etc/audit/auditd.conf + + + + + + + + + + Include Local Events in Audit Logs + + Red Hat Enterprise Linux 9 + + Ensure 'local_events' is configured with value 'yes' in /etc/audit/auditd.conf + + + + + + + + + + Resolve information before writing to audit logs + + Red Hat Enterprise Linux 9 + + Ensure 'log_format' is configured with value 'ENRICHED' in /etc/audit/auditd.conf + + + + + + + + + + Write Audit Logs to the Disk + + Red Hat Enterprise Linux 9 + + Ensure 'write_logs' is configured with value 'yes' in /etc/audit/auditd.conf + + + + + + + + + + + Ensure SELinux Not Disabled in the kernel arguments + + Red Hat Enterprise Linux 9 + + Ensure selinux=0 argument is not present in the 'options' line of /boot/loader/entries/ostree-2-*.conf (or ostree-1-*.conf if there is no ostree-2-*.conf as ostree has only two enries at the most, with *-2-*.conf entry always being the most recent). Also, ensure that kernel is currently running with this argument by checking /proc/cmdline. + + + + + + + + + + + + + + + + + + + + Disable User Administration in GNOME3 + + Red Hat Enterprise Linux 9 + + Ensure 'user-administration-disabled' is configured with value 'true in section 'org/gnome/desktop/lockdown' in /etc/dconf/db/local.d/ + + + + + + + + + + + Enable the GNOME3 Screen Locking On Smartcard Removal + + Red Hat Enterprise Linux 9 + + Ensure 'removal-action' is configured with value ''lock-screen' in section 'org/gnome/settings-daemon/peripherals/smartcard' in /etc/dconf/db/local.d/ + + + + + + + + + + + Verify that Shared Library Directories Have Root Group Ownership + + Red Hat Enterprise Linux 9 + + This test makes sure that /lib/, /lib64/, /usr/lib/, /usr/lib64/ is group owned by 0. + + + + + + + + + + + + + Verify that System Executable Have Root Ownership + + Red Hat Enterprise Linux 9 + + This test makes sure that /bin/, /sbin/, /usr/bin/, /usr/sbin/, /usr/local/bin/, /usr/local/sbin/ is owned by 0. + + + + + + + + + + + + + + Verify that Shared Library Directories Have Root Ownership + + Red Hat Enterprise Linux 9 + + This test makes sure that /lib/, /lib64/, /usr/lib/, /usr/lib64/ is owned by 0. + + + + + + + + + + + + + Verify that System Executable Directories Have Restrictive Permissions + + Red Hat Enterprise Linux 9 + + This test makes sure that /bin/, /sbin/, /usr/bin/, /usr/sbin/, /usr/local/bin/, /usr/local/sbin/ has mode 0755. + If the target file or directory has an extended ACL, then it will fail the mode check. + + + + + + + + + + + + + + + Verify that Shared Library Directories Have Restrictive Permissions + + Red Hat Enterprise Linux 9 + + This test makes sure that /lib/, /lib64/, /usr/lib/, /usr/lib64/ has mode 7755. + If the target file or directory has an extended ACL, then it will fail the mode check. + + + + + + + + + + + + + + Disable Host-Based Authentication + + Red Hat Enterprise Linux 9 + + Ensure 'HostbasedAuthentication' is configured with value 'no' in /etc/ssh/sshd_config or in /etc/ssh/sshd_config.d + + + + + + + + + + + + + + + + + + + + + Ensure that /etc/at.deny does not exist + + Red Hat Enterprise Linux 9 + + This test makes sure that/etc/at.deny does not exist. + + + + + + + + + + Audit Tools Must Be Group-owned by Root + + Red Hat Enterprise Linux 9 + + This test makes sure that /sbin/auditctl, /sbin/aureport, /sbin/ausearch, /sbin/autrace, /sbin/auditd, /sbin/rsyslogd, /sbin/augenrules is group owned by 0. + + + + + + + + + + + + + + + + Audit Tools Must Be Owned by Root + + Red Hat Enterprise Linux 9 + + This test makes sure that /sbin/auditctl, /sbin/aureport, /sbin/ausearch, /sbin/autrace, /sbin/auditd, /sbin/rsyslogd, /sbin/augenrules is owned by 0. + + + + + + + + + + + + + + + + Audit Tools Must Have a Mode of 0755 or Less Permissive + + Red Hat Enterprise Linux 9 + + This test makes sure that /sbin/auditctl, /sbin/aureport, /sbin/ausearch, /sbin/autrace, /sbin/auditd, /sbin/rsyslogd, /sbin/augenrules has mode 0755. + If the target file or directory has an extended ACL, then it will fail the mode check. + + + + + + + + + + + + + + + + + Ensure that /etc/cron.deny does not exist + + Red Hat Enterprise Linux 9 + + This test makes sure that/etc/cron.deny does not exist. + + + + + + + + + + Verify Group Who Owns /etc/at.allow file + + Red Hat Enterprise Linux 9 + + This test makes sure that /etc/at.allow is group owned by 0. + + + + + + + + + + Verify Group Who Owns Backup group File + + Red Hat Enterprise Linux 9 + + This test makes sure that /etc/group- is group owned by 0. + + + + + + + + + + Verify Group Who Owns Backup gshadow File + + Red Hat Enterprise Linux 9 + + This test makes sure that /etc/gshadow- is group owned by 0. + + + + + + + + + + Verify Group Who Owns Backup passwd File + + Red Hat Enterprise Linux 9 + + This test makes sure that /etc/passwd- is group owned by 0. + + + + + + + + + + Verify User Who Owns Backup shadow File + + Red Hat Enterprise Linux 9 + + This test makes sure that /etc/shadow- is group owned by 0. + + + + + + + + + + Verify Group Who Owns /etc/cron.allow file + + Red Hat Enterprise Linux 9 + + This test makes sure that /etc/cron.allow is group owned by 0. + + + + + + + + + + Verify Group Who Owns cron.d + + Red Hat Enterprise Linux 9 + + This test makes sure that /etc/cron.d/ is group owned by 0. + + + + + + + + + + Verify Group Who Owns cron.daily + + Red Hat Enterprise Linux 9 + + This test makes sure that /etc/cron.daily/ is group owned by 0. + + + + + + + + + + Verify Group Who Owns cron.hourly + + Red Hat Enterprise Linux 9 + + This test makes sure that /etc/cron.hourly/ is group owned by 0. + + + + + + + + + + Verify Group Who Owns cron.monthly + + Red Hat Enterprise Linux 9 + + This test makes sure that /etc/cron.monthly/ is group owned by 0. + + + + + + + + + + Verify Group Who Owns cron.weekly + + Red Hat Enterprise Linux 9 + + This test makes sure that /etc/cron.weekly/ is group owned by 0. + + + + + + + + + + Verify Group Who Owns Crontab + + Red Hat Enterprise Linux 9 + + This test makes sure that /etc/crontab is group owned by 0. + + + + + + + + + + Verify Group Who Owns group File + + Red Hat Enterprise Linux 9 + + This test makes sure that /etc/group is group owned by 0. + + + + + + + + + + Verify Group Who Owns gshadow File + + Red Hat Enterprise Linux 9 + + This test makes sure that /etc/gshadow is group owned by 0. + + + + + + + + + + Verify Group Who Owns passwd File + + Red Hat Enterprise Linux 9 + + This test makes sure that /etc/passwd is group owned by 0. + + + + + + + + + + Verify Group Who Owns shadow File + + Red Hat Enterprise Linux 9 + + This test makes sure that /etc/shadow is group owned by 0. + + + + + + + + + + Verify /boot/grub2/grub.cfg Group Ownership + + Red Hat Enterprise Linux 9 + + This test makes sure that /boot/grub2/grub.cfg is group owned by 0. + + + + + + + + + + Verify Group Who Owns SSH Server config file + + Red Hat Enterprise Linux 9 + + This test makes sure that /etc/ssh/sshd_config is group owned by 0. + + + + + + + + + + Verify Group Who Owns /var/log Directory + + Red Hat Enterprise Linux 9 + + This test makes sure that /var/log/ is group owned by 0. + + + + + + + + + + Verify Group Who Owns /var/log/messages File + + Red Hat Enterprise Linux 9 + + This test makes sure that /var/log/messages is group owned by 0. + + + + + + + + + + Verify Group Who Owns /var/log/syslog File + + Red Hat Enterprise Linux 9 + + This test makes sure that /var/log/syslog is group owned by 4. + + + + + + + + + Audit Configuration Files Must Be Owned By Group root + + Red Hat Enterprise Linux 9 + + This test makes sure that /etc/audit/, /etc/audit/rules.d/ is group owned by 0. + + + + + + + + + + Verify User Who Owns Backup group File + + Red Hat Enterprise Linux 9 + + This test makes sure that /etc/group- is owned by 0. + + + + + + + + + + Verify User Who Owns Backup gshadow File + + Red Hat Enterprise Linux 9 + + This test makes sure that /etc/gshadow- is owned by 0. + + + + + + + + + + Verify User Who Owns Backup passwd File + + Red Hat Enterprise Linux 9 + + This test makes sure that /etc/passwd- is owned by 0. + + + + + + + + + + Verify Group Who Owns Backup shadow File + + Red Hat Enterprise Linux 9 + + This test makes sure that /etc/shadow- is owned by 0. + + + + + + + + + + Verify User Who Owns /etc/cron.allow file + + Red Hat Enterprise Linux 9 + + This test makes sure that /etc/cron.allow is owned by 0. + + + + + + + + + + Verify Owner on cron.d + + Red Hat Enterprise Linux 9 + + This test makes sure that /etc/cron.d/ is owned by 0. + + + + + + + + + + Verify Owner on cron.daily + + Red Hat Enterprise Linux 9 + + This test makes sure that /etc/cron.daily/ is owned by 0. + + + + + + + + + + Verify Owner on cron.hourly + + Red Hat Enterprise Linux 9 + + This test makes sure that /etc/cron.hourly/ is owned by 0. + + + + + + + + + + Verify Owner on cron.monthly + + Red Hat Enterprise Linux 9 + + This test makes sure that /etc/cron.monthly/ is owned by 0. + + + + + + + + + + Verify Owner on cron.weekly + + Red Hat Enterprise Linux 9 + + This test makes sure that /etc/cron.weekly/ is owned by 0. + + + + + + + + + + Verify Owner on crontab + + Red Hat Enterprise Linux 9 + + This test makes sure that /etc/crontab is owned by 0. + + + + + + + + + + Verify User Who Owns group File + + Red Hat Enterprise Linux 9 + + This test makes sure that /etc/group is owned by 0. + + + + + + + + + + Verify User Who Owns gshadow File + + Red Hat Enterprise Linux 9 + + This test makes sure that /etc/gshadow is owned by 0. + + + + + + + + + + Verify User Who Owns passwd File + + Red Hat Enterprise Linux 9 + + This test makes sure that /etc/passwd is owned by 0. + + + + + + + + + + Verify User Who Owns shadow File + + Red Hat Enterprise Linux 9 + + This test makes sure that /etc/shadow is owned by 0. + + + + + + + + + + Verify /boot/grub2/grub.cfg User Ownership + + Red Hat Enterprise Linux 9 + + This test makes sure that /boot/grub2/grub.cfg is owned by 0. + + + + + + + + + + Verify Owner on SSH Server config file + + Red Hat Enterprise Linux 9 + + This test makes sure that /etc/ssh/sshd_config is owned by 0. + + + + + + + + + + Verify User Who Owns /var/log Directory + + Red Hat Enterprise Linux 9 + + This test makes sure that /var/log/ is owned by 0. + + + + + + + + + + Verify User Who Owns /var/log/messages File + + Red Hat Enterprise Linux 9 + + This test makes sure that /var/log/messages is owned by 0. + + + + + + + + + + Verify User Who Owns /var/log/syslog File + + Red Hat Enterprise Linux 9 + + This test makes sure that /var/log/syslog is owned by 104. + + + + + + + + + Audit Configuration Files Must Be Owned By Root + + Red Hat Enterprise Linux 9 + + This test makes sure that /etc/audit/, /etc/audit/rules.d/ is owned by 0. + + + + + + + + + + Verify that Shared Library Files Have Root Ownership + + Red Hat Enterprise Linux 9 + + This test makes sure that /lib/, /lib64/, /usr/lib/, /usr/lib64/ is owned by 0. + + + + + + + + + + + + + Verify Permissions on /etc/at.allow file + + Red Hat Enterprise Linux 9 + + This test makes sure that /etc/at.allow has mode 0600. + If the target file or directory has an extended ACL, then it will fail the mode check. + + + + + + + + + + + Verify Permissions on Backup group File + + Red Hat Enterprise Linux 9 + + This test makes sure that /etc/group- has mode 0644. + If the target file or directory has an extended ACL, then it will fail the mode check. + + + + + + + + + + + Verify Permissions on Backup gshadow File + + Red Hat Enterprise Linux 9 + + This test makes sure that /etc/gshadow- has mode 0000. + If the target file or directory has an extended ACL, then it will fail the mode check. + + + + + + + + + + + Verify Permissions on Backup passwd File + + Red Hat Enterprise Linux 9 + + This test makes sure that /etc/passwd- has mode 0644. + If the target file or directory has an extended ACL, then it will fail the mode check. + + + + + + + + + + + Verify Permissions on Backup shadow File + + Red Hat Enterprise Linux 9 + + This test makes sure that /etc/shadow- has mode 0000. + If the target file or directory has an extended ACL, then it will fail the mode check. + + + + + + + + + + + Verify Permissions on /etc/cron.allow file + + Red Hat Enterprise Linux 9 + + This test makes sure that /etc/cron.allow has mode 0600. + If the target file or directory has an extended ACL, then it will fail the mode check. + + + + + + + + + + + Verify Permissions on cron.d + + Red Hat Enterprise Linux 9 + + This test makes sure that /etc/cron.d/ has mode 0700. + If the target file or directory has an extended ACL, then it will fail the mode check. + + + + + + + + + + + Verify Permissions on cron.daily + + Red Hat Enterprise Linux 9 + + This test makes sure that /etc/cron.daily/ has mode 0700. + If the target file or directory has an extended ACL, then it will fail the mode check. + + + + + + + + + + + Verify Permissions on cron.hourly + + Red Hat Enterprise Linux 9 + + This test makes sure that /etc/cron.hourly/ has mode 0700. + If the target file or directory has an extended ACL, then it will fail the mode check. + + + + + + + + + + + Verify Permissions on cron.monthly + + Red Hat Enterprise Linux 9 + + This test makes sure that /etc/cron.monthly/ has mode 0700. + If the target file or directory has an extended ACL, then it will fail the mode check. + + + + + + + + + + + Verify Permissions on cron.weekly + + Red Hat Enterprise Linux 9 + + This test makes sure that /etc/cron.weekly/ has mode 0700. + If the target file or directory has an extended ACL, then it will fail the mode check. + + + + + + + + + + + Verify Permissions on crontab + + Red Hat Enterprise Linux 9 + + This test makes sure that /etc/crontab has mode 0600. + If the target file or directory has an extended ACL, then it will fail the mode check. + + + + + + + + + + + Verify the UEFI Boot Loader grub.cfg Permissions + + Red Hat Enterprise Linux 9 + + This test makes sure that /boot/grub2/grub.cfg has mode 0700. + If the target file or directory has an extended ACL, then it will fail the mode check. + + + + + + + + + + + Verify Permissions on /etc/audit/auditd.conf + + Red Hat Enterprise Linux 9 + + This test makes sure that /etc/audit/auditd.conf has mode 0640. + If the target file or directory has an extended ACL, then it will fail the mode check. + + + + + + + + + + + Verify Permissions on /etc/audit/rules.d/*.rules + + Red Hat Enterprise Linux 9 + + This test makes sure that /etc/audit/rules.d/ has mode 0640. + If the target file or directory has an extended ACL, then it will fail the mode check. + + + + + + + + + + + Verify Permissions on group File + + Red Hat Enterprise Linux 9 + + This test makes sure that /etc/group has mode 0644. + If the target file or directory has an extended ACL, then it will fail the mode check. + + + + + + + + + + + Verify Permissions on gshadow File + + Red Hat Enterprise Linux 9 + + This test makes sure that /etc/gshadow has mode 0000. + If the target file or directory has an extended ACL, then it will fail the mode check. + + + + + + + + + + + Verify permissions on System Login Banner + + Red Hat Enterprise Linux 9 + + This test makes sure that /etc/issue has mode 0644. + If the target file or directory has an extended ACL, then it will fail the mode check. + + + + + + + + + + + Verify permissions on Message of the Day Banner + + Red Hat Enterprise Linux 9 + + This test makes sure that /etc/motd has mode 0644. + If the target file or directory has an extended ACL, then it will fail the mode check. + + + + + + + + + + + Verify Permissions on passwd File + + Red Hat Enterprise Linux 9 + + This test makes sure that /etc/passwd has mode 0644. + If the target file or directory has an extended ACL, then it will fail the mode check. + + + + + + + + + + + Verify Permissions on shadow File + + Red Hat Enterprise Linux 9 + + This test makes sure that /etc/shadow has mode 0000. + If the target file or directory has an extended ACL, then it will fail the mode check. + + + + + + + + + + + Verify /boot/grub2/grub.cfg Permissions + + Red Hat Enterprise Linux 9 + + This test makes sure that /boot/grub2/grub.cfg has mode 0600. + If the target file or directory has an extended ACL, then it will fail the mode check. + + + + + + + + + + + Verify that Shared Library Files Have Restrictive Permissions + + Red Hat Enterprise Linux 9 + + This test makes sure that /lib/, /lib64/, /usr/lib/, /usr/lib64/ has mode 7755. + If the target file or directory has an extended ACL, then it will fail the mode check. + + + + + + + + + + + + + + Verify Permissions on SSH Server config file + + Red Hat Enterprise Linux 9 + + This test makes sure that /etc/ssh/sshd_config has mode 0600. + If the target file or directory has an extended ACL, then it will fail the mode check. + + + + + + + + + + + Verify Permissions on SSH Server Public *.pub Key Files + + Red Hat Enterprise Linux 9 + + This test makes sure that /etc/ssh/ has mode 0644. + If the target file or directory has an extended ACL, then it will fail the mode check. + + + + + + + + + + + Verify Permissions on /var/log Directory + + Red Hat Enterprise Linux 9 + + This test makes sure that /var/log/ has mode 0755. + If the target file or directory has an extended ACL, then it will fail the mode check. + + + + + + + + + + + Verify Permissions on /var/log/messages File + + Red Hat Enterprise Linux 9 + + This test makes sure that /var/log/messages has mode 0640. + If the target file or directory has an extended ACL, then it will fail the mode check. + + + + + + + + + + + Verify Permissions on /var/log/syslog File + + Red Hat Enterprise Linux 9 + + This test makes sure that /var/log/syslog has mode 0640. + If the target file or directory has an extended ACL, then it will fail the mode check. + + + + + + + + + + Configure Firewalld to Use the Nftables Backend + + Red Hat Enterprise Linux 9 + + Ensure 'FirewallBackend' is configured with value 'nftables' in /etc/firewalld/firewalld.conf + + + + + + + + + + Enable Auditing for Processes Which Start Prior to the Audit Daemon + + Red Hat Enterprise Linux 9 + + Ensure audit=1 is configured in the kernel line in /etc/default/grub. + + + + + + + + + + + + + + + + + Extend Audit Backlog Limit for the Audit Daemon + + Red Hat Enterprise Linux 9 + + Ensure audit_backlog_limit=8192 is configured in the kernel line in /etc/default/grub. + + + + + + + + + + + + + + + + + IOMMU configuration directive + + Red Hat Enterprise Linux 9 + + Ensure iommu=force is configured in the kernel line in /etc/default/grub. + + + + + + + + + + + + + + + + + Configure kernel to zero out memory before allocation + + Red Hat Enterprise Linux 9 + + Ensure init_on_alloc=1 is configured in the kernel line in /etc/default/grub. + + + + + + + + + + + + + + + + + Ensure IPv6 is disabled through kernel boot parameter + + Red Hat Enterprise Linux 9 + + Ensure ipv6.disable=1 is configured in the kernel line in /etc/default/grub. + + + + + + + + + + + + + + + + Configure L1 Terminal Fault mitigations + + Red Hat Enterprise Linux 9 + + Ensure l1tf is configured in the kernel line in /etc/default/grub. + + + + + + + + + + + + + + + + + Force kernel panic on uncorrected MCEs + + Red Hat Enterprise Linux 9 + + Ensure mce=0 is configured in the kernel line in /etc/default/grub. + + + + + + + + + + + + + + + + + Configure Microarchitectural Data Sampling mitigation + + Red Hat Enterprise Linux 9 + + Ensure mds is configured in the kernel line in /etc/default/grub. + + + + + + + + + + + + + + + + + Ensure SMAP is not disabled during boot + + Red Hat Enterprise Linux 9 + + Ensure nosmap is not set in the kernel line in /etc/default/grub. + + + + + + + + + + + + + + + + + Ensure SMEP is not disabled during boot + + Red Hat Enterprise Linux 9 + + Ensure nosmep is not set in the kernel line in /etc/default/grub. + + + + + + + + + + + + + + + + + Enable randomization of the page allocator + + Red Hat Enterprise Linux 9 + + Ensure page_alloc.shuffle=1 is configured in the kernel line in /etc/default/grub. + + + + + + + + + + + + + + + + + Enable page allocator poisoning + + Red Hat Enterprise Linux 9 + + Ensure page_poison=1 is configured in the kernel line in /etc/default/grub. + + + + + + + + + + + + + + + + + Enable Kernel Page-Table Isolation (KPTI) + + Red Hat Enterprise Linux 9 + + Ensure pti=on is configured in the kernel line in /etc/default/grub. + + + + + + + + + + + + + + + + + Configure the confidence in TPM for entropy + + Red Hat Enterprise Linux 9 + + Ensure rng_core.default_quality is configured in the kernel line in /etc/default/grub. + + + + + + + + + + + + + + + + + Disable merging of slabs with similar size + + Red Hat Enterprise Linux 9 + + Ensure slab_nomerge=yes is configured in the kernel line in /etc/default/grub. + + + + + + + + + + + + + + + + + Enable SLUB/SLAB allocator poisoning + + Red Hat Enterprise Linux 9 + + Ensure slub_debug is configured in the kernel line in /etc/default/grub. + + + + + + + + + + + + + + + + + Configure Speculative Store Bypass Mitigation + + Red Hat Enterprise Linux 9 + + Ensure spec_store_bypass_disable is configured in the kernel line in /etc/default/grub. + + + + + + + + + + + + + + + + + Enforce Spectre v2 mitigation + + Red Hat Enterprise Linux 9 + + Ensure spectre_v2=on is configured in the kernel line in /etc/default/grub. + + + + + + + + + + + + + + + + + Ensure debug-shell service is not enabled during boot + + Red Hat Enterprise Linux 9 + + Ensure systemd.debug-shell is not set in the kernel line in /etc/default/grub. + + + + + + + + + + + + + + + + + Disable vsyscalls + + Red Hat Enterprise Linux 9 + + Ensure vsyscall=none is configured in the kernel line in /etc/default/grub. + + + + + + + + + + + + + + + + + Install Smart Card Packages For Multifactor Authentication + + Red Hat Enterprise Linux 9 + + The RPM package openssl-pkcs11 should be installed. + + + + + + + + + + Ensure journald is configured to compress large log files + + Red Hat Enterprise Linux 9 + + Ensure 'Compress' is configured with value 'yes' in /etc/systemd/journald.conf + + + + + + + + + + Ensure journald is configured to send logs to rsyslog + + Red Hat Enterprise Linux 9 + + Ensure 'ForwardToSyslog' is configured with value 'yes' in /etc/systemd/journald.conf + + + + + + + + + + Ensure journald is configured to write log files to persistent disk + + Red Hat Enterprise Linux 9 + + Ensure 'Storage' is configured with value 'persistent' in /etc/systemd/journald.conf + + + + + + + + + + Do not allow ACPI methods to be inserted/replaced at run time + + Red Hat Enterprise Linux 9 + + The kernel CONFIG_ACPI_CUSTOM_METHOD should have value n + + + + + + + + + + + + + + Emulate Privileged Access Never (PAN) + + Red Hat Enterprise Linux 9 + + The kernel CONFIG_ARM64_SW_TTBR0_PAN should have value y + + + + + + + + + + + + + Disable kernel support for MISC binaries + + Red Hat Enterprise Linux 9 + + The kernel CONFIG_BINFMT_MISC should have value n + + + + + + + + + + + + + + Enable support for BUG() + + Red Hat Enterprise Linux 9 + + The kernel CONFIG_BUG should have value y + + + + + + + + + + + + + Trigger a kernel BUG when data corruption is detected + + Red Hat Enterprise Linux 9 + + The kernel CONFIG_BUG_ON_DATA_CORRUPTION should have value y + + + + + + + + + + + + + Disable compatibility with brk() + + Red Hat Enterprise Linux 9 + + The kernel CONFIG_COMPAT_BRK should have value n + + + + + + + + + + + + + + Disable the 32-bit vDSO + + Red Hat Enterprise Linux 9 + + The kernel CONFIG_COMPAT_VDSO should have value n + + + + + + + + + + + + + + Enable checks on credential management + + Red Hat Enterprise Linux 9 + + The kernel CONFIG_DEBUG_CREDENTIALS should have value y + + + + + + + + + + + + + Disable kernel debugfs + + Red Hat Enterprise Linux 9 + + The kernel CONFIG_DEBUG_FS should have value n + + + + + + + + + + + + + + Enable checks on linked list manipulation + + Red Hat Enterprise Linux 9 + + The kernel CONFIG_DEBUG_LIST should have value y + + + + + + + + + + + + + Enable checks on notifier call chains + + Red Hat Enterprise Linux 9 + + The kernel CONFIG_DEBUG_NOTIFIERS should have value y + + + + + + + + + + + + + Enable checks on scatter-gather (SG) table operations + + Red Hat Enterprise Linux 9 + + The kernel CONFIG_DEBUG_SG should have value y + + + + + + + + + + + + + Warn on W+X mappings found at boot + + Red Hat Enterprise Linux 9 + + The kernel CONFIG_DEBUG_WX should have value y + + + + + + + + + + + + + Configure low address space to protect from user allocation + + Red Hat Enterprise Linux 9 + + The kernel CONFIG_DEFAULT_MMAP_MIN_ADDR should have value 65536 + + + + + + + + + + + + + Disable /dev/kmem virtual device support + + Red Hat Enterprise Linux 9 + + The kernel CONFIG_DEVKMEM should have value n + + + + + + + + + + + + + + Harden common str/mem functions against buffer overflows + + Red Hat Enterprise Linux 9 + + The kernel CONFIG_FORTIFY_SOURCE should have value y + + + + + + + + + + + + + Generate some entropy during boot and runtime + + Red Hat Enterprise Linux 9 + + The kernel CONFIG_GCC_PLUGIN_LATENT_ENTROPY should have value y + + + + + + + + + + + + + Randomize layout of sensitive kernel structures + + Red Hat Enterprise Linux 9 + + The kernel CONFIG_GCC_PLUGIN_RANDSTRUCT should have value y + + + + + + + + + + + + + Poison kernel stack before returning from syscalls + + Red Hat Enterprise Linux 9 + + The kernel CONFIG_GCC_PLUGIN_STACKLEAK should have value y + + + + + + + + + + + + + Force initialization of variables containing userspace addresses + + Red Hat Enterprise Linux 9 + + The kernel CONFIG_GCC_PLUGIN_STRUCTLEAK should have value y + + + + + + + + + + + + + zero-init everything passed by reference + + Red Hat Enterprise Linux 9 + + The kernel CONFIG_GCC_PLUGIN_STRUCTLEAK_BYREF_ALL should have value y + + + + + + + + + + + + + Harden memory copies between kernel and userspace + + Red Hat Enterprise Linux 9 + + The kernel CONFIG_HARDENED_USERCOPY should have value y + + + + + + + + + + + + + Do not allow usercopy whitelist violations to fallback to object size + + Red Hat Enterprise Linux 9 + + The kernel CONFIG_HARDENED_USERCOPY_FALLBACK should have value n + + + + + + + + + + + + + + Disable hibernation + + Red Hat Enterprise Linux 9 + + The kernel CONFIG_HIBERNATION should have value n + + + + + + + + + + + + + + Disable IA32 emulation + + Red Hat Enterprise Linux 9 + + The kernel CONFIG_IA32_EMULATION should have value n + + + + + + + + + + + + + + Disable the IPv6 protocol + + Red Hat Enterprise Linux 9 + + The kernel CONFIG_IPV6 should have value n + + + + + + + + + + + + + + Disable kexec system call + + Red Hat Enterprise Linux 9 + + The kernel CONFIG_KEXEC should have value n + + + + + + + + + + + + + + Disable legacy (BSD) PTY support + + Red Hat Enterprise Linux 9 + + The kernel CONFIG_LEGACY_PTYS should have value n + + + + + + + + + + + + + + Disable vsyscall emulation + + Red Hat Enterprise Linux 9 + + The kernel CONFIG_LEGACY_VSYSCALL_EMULATE should have value n + + + + + + + + + + + + + + Disable vsyscall mapping + + Red Hat Enterprise Linux 9 + + The kernel CONFIG_LEGACY_VSYSCALL_NONE should have value y + + + + + + + + + + + + + Disable vsyscall emulate execution only + + Red Hat Enterprise Linux 9 + + The kernel CONFIG_LEGACY_VSYSCALL_XONLY should have value n + + + + + + + + + + + + + + Disable the LDT (local descriptor table) + + Red Hat Enterprise Linux 9 + + The kernel CONFIG_MODIFY_LDT_SYSCALL should have value n + + + + + + + + + + + + + + Enable module signature verification + + Red Hat Enterprise Linux 9 + + The kernel CONFIG_MODULE_SIG should have value y + + + + + + + + + + + + + Enable automatic signing of all modules + + Red Hat Enterprise Linux 9 + + The kernel CONFIG_MODULE_SIG_ALL should have value y + + + + + + + + + + + + + Require modules to be validly signed + + Red Hat Enterprise Linux 9 + + The kernel CONFIG_MODULE_SIG_FORCE should have value y + + + + + + + + + + + + + Specify the hash to use when signing modules + + Red Hat Enterprise Linux 9 + + The kernel CONFIG_MODULE_SIG_HASH should have value according to var_kernel_config_module_sig_hash + + + + + + + + + + + + + Specify module signing key to use + + Red Hat Enterprise Linux 9 + + The kernel CONFIG_MODULE_SIG_KEY should have value according to var_kernel_config_module_sig_key + + + + + + + + + + + + + Sign kernel modules with SHA-512 + + Red Hat Enterprise Linux 9 + + The kernel CONFIG_MODULE_SIG_SHA512 should have value y + + + + + + + + + + + + + Enable poison of pages after freeing + + Red Hat Enterprise Linux 9 + + The kernel CONFIG_PAGE_POISONING should have value y + + + + + + + + + + + + + Enable poison without sanity check + + Red Hat Enterprise Linux 9 + + The kernel CONFIG_PAGE_POISONING_NO_SANITY should have value y + + + + + + + + + + + + + Use zero for poisoning instead of debugging value + + Red Hat Enterprise Linux 9 + + The kernel CONFIG_PAGE_POISONING_ZERO should have value y + + + + + + + + + + + + + Remove the kernel mapping in user mode + + Red Hat Enterprise Linux 9 + + The kernel CONFIG_PAGE_TABLE_ISOLATION should have value y + + + + + + + + + + + + + Kernel panic oops + + Red Hat Enterprise Linux 9 + + The kernel CONFIG_PANIC_ON_OOPS should have value y + + + + + + + + + + + + + Kernel panic timeout + + Red Hat Enterprise Linux 9 + + The kernel CONFIG_PANIC_TIMEOUT should have value according to var_kernel_config_panic_timeout + + + + + + + + + + + + + Disable support for /proc/kkcore + + Red Hat Enterprise Linux 9 + + The kernel CONFIG_PROC_KCORE should have value n + + + + + + + + + + + + + + Randomize the address of the kernel image (KASLR) + + Red Hat Enterprise Linux 9 + + The kernel CONFIG_RANDOMIZE_BASE should have value y + + + + + + + + + + + + + Randomize the kernel memory sections + + Red Hat Enterprise Linux 9 + + The kernel CONFIG_RANDOMIZE_MEMORY should have value y + + + + + + + + + + + + + Perform full reference count validation + + Red Hat Enterprise Linux 9 + + The kernel CONFIG_REFCOUNT_FULL should have value y + + + + + + + + + + + + + Avoid speculative indirect branches in kernel + + Red Hat Enterprise Linux 9 + + The kernel CONFIG_RETPOLINE should have value y + + + + + + + + + + + + + Detect stack corruption on calls to schedule() + + Red Hat Enterprise Linux 9 + + The kernel CONFIG_SCHED_STACK_END_CHECK should have value y + + + + + + + + + + + + + Enable seccomp to safely compute untrusted bytecode + + Red Hat Enterprise Linux 9 + + The kernel CONFIG_SECCOMP should have value y + + + + + + + + + + + + + Enable use of Berkeley Packet Filter with seccomp + + Red Hat Enterprise Linux 9 + + The kernel CONFIG_SECCOMP_FILTER should have value y + + + + + + + + + + + + + Enable different security models + + Red Hat Enterprise Linux 9 + + The kernel CONFIG_SECURITY should have value y + + + + + + + + + + + + + Restrict unprivileged access to the kernel syslog + + Red Hat Enterprise Linux 9 + + The kernel CONFIG_SECURITY_DMESG_RESTRICT should have value n + + + + + + + + + + + + + + Disable mutable hooks + + Red Hat Enterprise Linux 9 + + The kernel CONFIG_SECURITY_WRITABLE_HOOKS should have value y + + + + + + + + + + + + + Enable Yama support + + Red Hat Enterprise Linux 9 + + The kernel CONFIG_SECURITY_YAMA should have value y + + + + + + + + + + + + + Harden slab freelist metadata + + Red Hat Enterprise Linux 9 + + The kernel CONFIG_SLAB_FREELIST_HARDENED should have value y + + + + + + + + + + + + + Randomize slab freelist + + Red Hat Enterprise Linux 9 + + The kernel CONFIG_SLAB_FRELIST_RANDOM should have value y + + + + + + + + + + + + + Disallow merge of slab caches + + Red Hat Enterprise Linux 9 + + The kernel CONFIG_SLAB_MERGE_DEFAULT should have value n + + + + + + + + + + + + + + Enable SLUB debugging support + + Red Hat Enterprise Linux 9 + + The kernel CONFIG_SLUB_DEBUG should have value y + + + + + + + + + + + + + Stack Protector buffer overlow detection + + Red Hat Enterprise Linux 9 + + The kernel CONFIG_STACKPROTECTOR should have value y + + + + + + + + + + + + + Strong Stack Protector + + Red Hat Enterprise Linux 9 + + The kernel CONFIG_STACKPROTECTOR_STRONG should have value y + + + + + + + + + + + + + Make the kernel text and rodata read-only + + Red Hat Enterprise Linux 9 + + The kernel CONFIG_STRICT_KERNEL_RWX should have value y + + + + + + + + + + + + + Make the module text and rodata read-only + + Red Hat Enterprise Linux 9 + + The kernel CONFIG_STRICT_MODULE_RWX should have value y + + + + + + + + + + + + + Enable TCP/IP syncookie support + + Red Hat Enterprise Linux 9 + + The kernel CONFIG_SYN_COOKIES should have value y + + + + + + + + + + + + + Unmap kernel when running in userspace (aka KAISER) + + Red Hat Enterprise Linux 9 + + The kernel CONFIG_UNMAP_KERNEL_AT_EL0 should have value y + + + + + + + + + + + + + User a virtually-mapped stack + + Red Hat Enterprise Linux 9 + + The kernel CONFIG_VMAP_STACK should have value y + + + + + + + + + + + + + Disable x86 vsyscall emulation + + Red Hat Enterprise Linux 9 + + The kernel CONFIG_X86_VSYSCALL_EMULATION should have value n + + + + + + + + + + + + + + Disable ATM Support + + Red Hat Enterprise Linux 9 + + The kernel module atm should be disabled. + + + + + + + + + + + + + Disable Bluetooth Kernel Module + + Red Hat Enterprise Linux 9 + + The kernel module bluetooth should be disabled. + + + + + + + + + + + + + Disable CAN Support + + Red Hat Enterprise Linux 9 + + The kernel module can should be disabled. + + + + + + + + + + + + + Disable Kernel cfg80211 Module + + Red Hat Enterprise Linux 9 + + The kernel module cfg80211 should be disabled. + + + + + + + + + + + + Disable Mounting of cramfs + + Red Hat Enterprise Linux 9 + + The kernel module cramfs should be disabled. + + + + + + + + + + + + + Disable DCCP Support + + Red Hat Enterprise Linux 9 + + The kernel module dccp should be disabled. + + + + + + + + + + + + + Disable IEEE 1394 (FireWire) Support + + Red Hat Enterprise Linux 9 + + The kernel module firewire-core should be disabled. + + + + + + + + + + + + + Disable Mounting of freevxfs + + Red Hat Enterprise Linux 9 + + The kernel module freevxfs should be disabled. + + + + + + + + + + + + Disable Mounting of hfs + + Red Hat Enterprise Linux 9 + + The kernel module hfs should be disabled. + + + + + + + + + + + + Disable Mounting of hfsplus + + Red Hat Enterprise Linux 9 + + The kernel module hfsplus should be disabled. + + + + + + + + + + + + Disable Kernel iwlmvm Module + + Red Hat Enterprise Linux 9 + + The kernel module iwlmvm should be disabled. + + + + + + + + + + + + Disable Kernel iwlwifi Module + + Red Hat Enterprise Linux 9 + + The kernel module iwlwifi should be disabled. + + + + + + + + + + + + Disable Mounting of jffs2 + + Red Hat Enterprise Linux 9 + + The kernel module jffs2 should be disabled. + + + + + + + + + + + + Disable Kernel mac80211 Module + + Red Hat Enterprise Linux 9 + + The kernel module mac80211 should be disabled. + + + + + + + + + + + + Disable RDS Support + + Red Hat Enterprise Linux 9 + + The kernel module rds should be disabled. + + + + + + + + + + + + + Disable SCTP Support + + Red Hat Enterprise Linux 9 + + The kernel module sctp should be disabled. + + + + + + + + + + + + + Disable Mounting of squashfs + + Red Hat Enterprise Linux 9 + + The kernel module squashfs should be disabled. + + + + + + + + + + + + + Disable TIPC Support + + Red Hat Enterprise Linux 9 + + The kernel module tipc should be disabled. + + + + + + + + + + + + + Disable Mounting of udf + + Red Hat Enterprise Linux 9 + + The kernel module udf should be disabled. + + + + + + + + + + + + + Disable Modprobe Loading of USB Storage Driver + + Red Hat Enterprise Linux 9 + + The kernel module usb-storage should be disabled. + + + + + + + + + + + + + Disable the uvcvideo module + + Red Hat Enterprise Linux 9 + + The kernel module uvcvideo should be disabled. + + + + + + + + + + + + + Disable Mounting of vFAT filesystems + + Red Hat Enterprise Linux 9 + + The kernel module vfat should be disabled. + + + + + + + + + + + + Add nosuid Option to /boot/efi + + Red Hat Enterprise Linux 9 + + /boot/efi should be mounted with mount option nosuid. + + + + + + + + + + + Add noauto Option to /boot + + Red Hat Enterprise Linux 9 + + /boot should be mounted with mount option noauto. + + + + + + + + + Add nodev Option to /boot + + Red Hat Enterprise Linux 9 + + /boot should be mounted with mount option nodev. + + + + + + + + + + Add noexec Option to /boot + + Red Hat Enterprise Linux 9 + + /boot should be mounted with mount option noexec. + + + + + + + + + + Add nosuid Option to /boot + + Red Hat Enterprise Linux 9 + + /boot should be mounted with mount option nosuid. + + + + + + + + + + Add nodev Option to /dev/shm + + Red Hat Enterprise Linux 9 + + /dev/shm should be mounted with mount option nodev. + + + + + + + + + + + Add noexec Option to /dev/shm + + Red Hat Enterprise Linux 9 + + /dev/shm should be mounted with mount option noexec. + + + + + + + + + + + Add nosuid Option to /dev/shm + + Red Hat Enterprise Linux 9 + + /dev/shm should be mounted with mount option nosuid. + + + + + + + + + + + Add nodev Option to /home + + Red Hat Enterprise Linux 9 + + /home should be mounted with mount option nodev. + + + + + + + + + + Add noexec Option to /home + + Red Hat Enterprise Linux 9 + + /home should be mounted with mount option noexec. + + + + + + + + + + Add nosuid Option to /home + + Red Hat Enterprise Linux 9 + + /home should be mounted with mount option nosuid. + + + + + + + + + + Mount Remote Filesystems with Kerberos Security + + Red Hat Enterprise Linux 9 + + The sec_krb5_krb5i_krb5p option should be enabled for all NFS mounts in /etc/fstab. + + + + + + + + + + + Mount Remote Filesystems with nodev + + Red Hat Enterprise Linux 9 + + The nodev option should be enabled for all NFS mounts in /etc/fstab. + + + + + + + + + + + Add nodev Option to Removable Media Partitions + + Red Hat Enterprise Linux 9 + + The nodev option should be enabled for all removable devices mounts in /etc/fstab. + + + + + + + + + + + + + + + + + + Mount Remote Filesystems with noexec + + Red Hat Enterprise Linux 9 + + The noexec option should be enabled for all NFS mounts in /etc/fstab. + + + + + + + + + + + Add noexec Option to Removable Media Partitions + + Red Hat Enterprise Linux 9 + + The noexec option should be enabled for all removable devices mounts in /etc/fstab. + + + + + + + + + + + + + + + + + + Mount Remote Filesystems with nosuid + + Red Hat Enterprise Linux 9 + + The nosuid option should be enabled for all NFS mounts in /etc/fstab. + + + + + + + + + + + Add nosuid Option to Removable Media Partitions + + Red Hat Enterprise Linux 9 + + The nosuid option should be enabled for all removable devices mounts in /etc/fstab. + + + + + + + + + + + + + + + + + + Add nosuid Option to /opt + + Red Hat Enterprise Linux 9 + + /opt should be mounted with mount option nosuid. + + + + + + + + + + Add hidepid Option to /proc + + Red Hat Enterprise Linux 9 + + /proc should be mounted with mount option hidepid. + + + + + + + + + + + Add nosuid Option to /srv + + Red Hat Enterprise Linux 9 + + /srv should be mounted with mount option nosuid. + + + + + + + + + + Add nodev Option to /tmp + + Red Hat Enterprise Linux 9 + + /tmp should be mounted with mount option nodev. + + + + + + + + + + Add noexec Option to /tmp + + Red Hat Enterprise Linux 9 + + /tmp should be mounted with mount option noexec. + + + + + + + + + + Add nosuid Option to /tmp + + Red Hat Enterprise Linux 9 + + /tmp should be mounted with mount option nosuid. + + + + + + + + + + Add nodev Option to /var/log/audit + + Red Hat Enterprise Linux 9 + + /var/log/audit should be mounted with mount option nodev. + + + + + + + + + + Add noexec Option to /var/log/audit + + Red Hat Enterprise Linux 9 + + /var/log/audit should be mounted with mount option noexec. + + + + + + + + + + Add nosuid Option to /var/log/audit + + Red Hat Enterprise Linux 9 + + /var/log/audit should be mounted with mount option nosuid. + + + + + + + + + + Add nodev Option to /var/log + + Red Hat Enterprise Linux 9 + + /var/log should be mounted with mount option nodev. + + + + + + + + + + Add noexec Option to /var/log + + Red Hat Enterprise Linux 9 + + /var/log should be mounted with mount option noexec. + + + + + + + + + + Add nosuid Option to /var/log + + Red Hat Enterprise Linux 9 + + /var/log should be mounted with mount option nosuid. + + + + + + + + + + Add nodev Option to /var + + Red Hat Enterprise Linux 9 + + /var should be mounted with mount option nodev. + + + + + + + + + + Add noexec Option to /var + + Red Hat Enterprise Linux 9 + + /var should be mounted with mount option noexec. + + + + + + + + + + Add nosuid Option to /var + + Red Hat Enterprise Linux 9 + + /var should be mounted with mount option nosuid. + + + + + + + + + + Add nodev Option to /var/tmp + + Red Hat Enterprise Linux 9 + + /var/tmp should be mounted with mount option nodev. + + + + + + + + + + Add noexec Option to /var/tmp + + Red Hat Enterprise Linux 9 + + /var/tmp should be mounted with mount option noexec. + + + + + + + + + + Add nosuid Option to /var/tmp + + Red Hat Enterprise Linux 9 + + /var/tmp should be mounted with mount option nosuid. + + + + + + + + + + package_GConf2_installed + + Red Hat Enterprise Linux 9 + + The RPM package GConf2 should be installed. + + + + + + + + + Install the Host Intrusion Prevention System (HIPS) Module + + Red Hat Enterprise Linux 9 + + The RPM package MFEhiplsm should be installed. + + + + + + + + + + Install AIDE + + Red Hat Enterprise Linux 9 + + The RPM package aide should be installed. + + + + + + + + + + Install audispd-plugins Package + + Red Hat Enterprise Linux 9 + + The RPM package audispd-plugins should be installed. + + + + + + + + + + Ensure the default plugins for the audit dispatcher are Installed + + Red Hat Enterprise Linux 9 + + The RPM package audit-audispd-plugins should be installed. + + + + + + + + + + Ensure the audit Subsystem is Installed + + Red Hat Enterprise Linux 9 + + The RPM package audit should be installed. + + + + + + + + + + package_avahi_installed + + Red Hat Enterprise Linux 9 + + The RPM package avahi should be installed. + + + + + + + + + Uninstall bind Package + + Red Hat Enterprise Linux 9 + + The RPM package bind should be removed. + + + + + + + + + The Chrony package is installed + + Red Hat Enterprise Linux 9 + + The RPM package chrony should be installed. + + + + + + + + + + Install the cron service + + Red Hat Enterprise Linux 9 + + The RPM package cron should be installed. + + + + + + + + + Install crypto-policies package + + Red Hat Enterprise Linux 9 + + The RPM package crypto-policies should be installed. + + + + + + + + + + package_dconf_installed + + Red Hat Enterprise Linux 9 + + The RPM package dconf should be installed. + + + + + + + + + Uninstall DHCP Server Package + + Red Hat Enterprise Linux 9 + + The RPM package dhcp-server should be removed. + + + + + + + + + + Install dnf-automatic Package + + Red Hat Enterprise Linux 9 + + The RPM package dnf-automatic should be installed. + + + + + + + + + + Uninstall dovecot Package + + Red Hat Enterprise Linux 9 + + The RPM package dovecot should be removed. + + + + + + + + + + package_esc_installed + + Red Hat Enterprise Linux 9 + + The RPM package esc should be installed. + + + + + + + + + Install fapolicyd Package + + Red Hat Enterprise Linux 9 + + The RPM package fapolicyd should be installed. + + + + + + + + + + Install firewalld Package + + Red Hat Enterprise Linux 9 + + The RPM package firewalld should be installed. + + + + + + + + + + Remove the FreeRadius Server Package + + Red Hat Enterprise Linux 9 + + The RPM package freeradius should be removed. + + + + + + + + + package_gdm_installed + + Red Hat Enterprise Linux 9 + + The RPM package gdm should be installed. + + + + + + + + + Remove the GDM Package Group + + Red Hat Enterprise Linux 9 + + The RPM package gdm should be removed. + + + + + + + + + + Uninstall geolite2-city Package + + Red Hat Enterprise Linux 9 + + The RPM package geolite2-city should be removed. + + + + + + + + + Uninstall geolite2-country Package + + Red Hat Enterprise Linux 9 + + The RPM package geolite2-country should be removed. + + + + + + + + + Ensure gnutls-utils is installed + + Red Hat Enterprise Linux 9 + + The RPM package gnutls-utils should be installed. + + + + + + + + + + Uninstall gssproxy Package + + Red Hat Enterprise Linux 9 + + The RPM package gssproxy should be removed. + + + + + + + + + + Uninstall httpd Package + + Red Hat Enterprise Linux 9 + + The RPM package httpd should be removed. + + + + + + + + + + Uninstall the inet-based telnet server + + Red Hat Enterprise Linux 9 + + The RPM package inetutils-telnetd should be removed. + + + + + + + + + Uninstall iprutils Package + + Red Hat Enterprise Linux 9 + + The RPM package iprutils should be removed. + + + + + + + + + + Remove the Kerberos Server Package + + Red Hat Enterprise Linux 9 + + The RPM package krb5-server should be removed. + + + + + + + + + Uninstall krb5-workstation Package + + Red Hat Enterprise Linux 9 + + The RPM package krb5-workstation should be removed. + + + + + + + + + + Install libreswan Package + + Red Hat Enterprise Linux 9 + + The RPM package libreswan should be installed. + + + + + + + + + + Install libselinux Package + + Red Hat Enterprise Linux 9 + + The RPM package libselinux should be installed. + + + + + + + + + + Install McAfee Endpoint Security for Linux (ENSL) + + Red Hat Enterprise Linux 9 + + The RPM package McAfeeTP should be installed. + + + + + + + + + + Uninstall mcstrans Package + + Red Hat Enterprise Linux 9 + + The RPM package mcstrans should be removed. + + + + + + + + + + Uninstall net-snmp Package + + Red Hat Enterprise Linux 9 + + The RPM package net-snmp should be removed. + + + + + + + + + + Uninstall nfs-utils Package + + Red Hat Enterprise Linux 9 + + The RPM package nfs-utils should be removed. + + + + + + + + + + Uninstall the nis package + + Red Hat Enterprise Linux 9 + + The RPM package nis should be removed. + + + + + + + + + Ensure nss-tools is installed + + Red Hat Enterprise Linux 9 + + The RPM package nss-tools should be installed. + + + + + + + + + + Install the ntp service + + Red Hat Enterprise Linux 9 + + The RPM package ntp should be installed. + + + + + + + + + Uninstall the ntpdate package + + Red Hat Enterprise Linux 9 + + The RPM package ntpdate should be removed. + + + + + + + + + Ensure LDAP client is not installed + + Red Hat Enterprise Linux 9 + + The RPM package openldap-clients should be removed. + + + + + + + + + + Uninstall openldap-servers Package + + Red Hat Enterprise Linux 9 + + The RPM package openldap-servers should be removed. + + + + + + + + + Install the opensc Package For Multifactor Authentication + + Red Hat Enterprise Linux 9 + + The RPM package opensc should be installed. + + + + + + + + + + Install openscap-scanner Package + + Red Hat Enterprise Linux 9 + + The RPM package openscap-scanner should be installed. + + + + + + + + + + Install OpenSSH client software + + Red Hat Enterprise Linux 9 + + The RPM package openssh-clients should be installed. + + + + + + + + + + Install the OpenSSH Server Package + + Red Hat Enterprise Linux 9 + + The RPM package openssh-server should be installed. + + + + + + + + + + Remove the OpenSSH Server Package + + Red Hat Enterprise Linux 9 + + The RPM package openssh-server should be removed. + + + + + + + + + package_pam_ldap_removed + + Red Hat Enterprise Linux 9 + + The RPM package pam_ldap should be removed. + + + + + + + + + Install pam_pwquality Package + + Red Hat Enterprise Linux 9 + + The RPM package libpwquality should be installed. + + + + + + + + + Install the pcsc-lite package + + Red Hat Enterprise Linux 9 + + The RPM package pcsc-lite should be installed. + + + + + + + + + + Install policycoreutils-python-utils package + + Red Hat Enterprise Linux 9 + + The RPM package policycoreutils-python-utils should be installed. + + + + + + + + + + Install policycoreutils Package + + Red Hat Enterprise Linux 9 + + The RPM package policycoreutils should be installed. + + + + + + + + + + The Postfix package is installed + + Red Hat Enterprise Linux 9 + + The RPM package postfix should be installed. + + + + + + + + + + package_prelink_removed + + Red Hat Enterprise Linux 9 + + The RPM package prelink should be removed. + + + + + + + + + Uninstall quagga Package + + Red Hat Enterprise Linux 9 + + The RPM package quagga should be removed. + + + + + + + + + + Install rear Package + + Red Hat Enterprise Linux 9 + + The RPM package rear should be installed. + + + + + + + + + + Install rng-tools Package + + Red Hat Enterprise Linux 9 + + The RPM package rng-tools should be installed. + + + + + + + + + + Uninstall rsh-server Package + + Red Hat Enterprise Linux 9 + + The RPM package rsh-server should be removed. + + + + + + + + + + Uninstall rsh Package + + Red Hat Enterprise Linux 9 + + The RPM package rsh should be removed. + + + + + + + + + + Ensure rsyslog-gnutls is installed + + Red Hat Enterprise Linux 9 + + The RPM package rsyslog-gnutls should be installed. + + + + + + + + + + Ensure rsyslog is Installed + + Red Hat Enterprise Linux 9 + + The RPM package rsyslog should be installed. + + + + + + + + + + Install the Samba Common Package + + Red Hat Enterprise Linux 9 + + The RPM package samba-common should be installed. + + + + + + + + + package_samba-common_removed + + Red Hat Enterprise Linux 9 + + The RPM package samba-common should be removed. + + + + + + + + + Uninstall Samba Package + + Red Hat Enterprise Linux 9 + + The RPM package samba should be removed. + + + + + + + + + + Install scap-security-guide Package + + Red Hat Enterprise Linux 9 + + The RPM package scap-security-guide should be installed. + + + + + + + + + + Uninstall Sendmail Package + + Red Hat Enterprise Linux 9 + + The RPM package sendmail should be removed. + + + + + + + + + + Uninstall setroubleshoot-plugins Package + + Red Hat Enterprise Linux 9 + + The RPM package setroubleshoot-plugins should be removed. + + + + + + + + + + Uninstall setroubleshoot-server Package + + Red Hat Enterprise Linux 9 + + The RPM package setroubleshoot-server should be removed. + + + + + + + + + + Uninstall setroubleshoot Package + + Red Hat Enterprise Linux 9 + + The RPM package setroubleshoot should be removed. + + + + + + + + + + Uninstall squid Package + + Red Hat Enterprise Linux 9 + + The RPM package squid should be removed. + + + + + + + + + + Install subscription-manager Package + + Red Hat Enterprise Linux 9 + + The RPM package subscription-manager should be installed. + + + + + + + + + + Install sudo Package + + Red Hat Enterprise Linux 9 + + The RPM package sudo should be installed. + + + + + + + + + + Ensure syslog-ng is Installed + + Red Hat Enterprise Linux 9 + + The RPM package syslog-ng should be installed. + + + + + + + + + Uninstall talk-server Package + + Red Hat Enterprise Linux 9 + + The RPM package talk-server should be removed. + + + + + + + + + + Uninstall talk Package + + Red Hat Enterprise Linux 9 + + The RPM package talk should be removed. + + + + + + + + + + Uninstall telnet-server Package + + Red Hat Enterprise Linux 9 + + The RPM package telnet-server should be removed. + + + + + + + + + + Remove telnet Clients + + Red Hat Enterprise Linux 9 + + The RPM package telnet should be removed. + + + + + + + + + + Uninstall the ssl compliant telnet server + + Red Hat Enterprise Linux 9 + + The RPM package telnetd-ssl should be removed. + + + + + + + + + Uninstall the telnet server + + Red Hat Enterprise Linux 9 + + The RPM package telnetd should be removed. + + + + + + + + + Uninstall tftp-server Package + + Red Hat Enterprise Linux 9 + + The RPM package tftp-server should be removed. + + + + + + + + + + Remove tftp Daemon + + Red Hat Enterprise Linux 9 + + The RPM package tftp should be removed. + + + + + + + + + + Install the tmux Package + + Red Hat Enterprise Linux 9 + + The RPM package tmux should be installed. + + + + + + + + + + Uninstall tuned Package + + Red Hat Enterprise Linux 9 + + The RPM package tuned should be removed. + + + + + + + + + + Install usbguard Package + + Red Hat Enterprise Linux 9 + + The RPM package usbguard should be installed. + + + + + + + + + + Uninstall vsftpd Package + + Red Hat Enterprise Linux 9 + + The RPM package vsftpd should be removed. + + + + + + + + + + Uninstall xinetd Package + + Red Hat Enterprise Linux 9 + + The RPM package xinetd should be removed. + + + + + + + + + + Remove the X Windows Package Group + + Red Hat Enterprise Linux 9 + + The RPM package xorg-x11-server-common should be removed. + + + + + + + + + + Remove NIS Client + + Red Hat Enterprise Linux 9 + + The RPM package ypbind should be removed. + + + + + + + + + + Uninstall ypserv Package + + Red Hat Enterprise Linux 9 + + The RPM package ypserv should be removed. + + + + + + + + + + Ensure /home Located On Separate Partition + + Red Hat Enterprise Linux 9 + + If stored locally, create a separate partition for + /home. If /home will be mounted from another + system such as an NFS server, then creating a separate partition is not + necessary at this time, and the mountpoint can instead be configured + later. + + + + + + + + + + Ensure /srv Located On Separate Partition + + Red Hat Enterprise Linux 9 + + If stored locally, create a separate partition for + /srv. If /srv will be mounted from another + system such as an NFS server, then creating a separate partition is not + necessary at this time, and the mountpoint can instead be configured + later. + + + + + + + + + + Ensure /tmp Located On Separate Partition + + Red Hat Enterprise Linux 9 + + If stored locally, create a separate partition for + /tmp. If /tmp will be mounted from another + system such as an NFS server, then creating a separate partition is not + necessary at this time, and the mountpoint can instead be configured + later. + + + + + + + + + + Ensure /var Located On Separate Partition + + Red Hat Enterprise Linux 9 + + If stored locally, create a separate partition for + /var. If /var will be mounted from another + system such as an NFS server, then creating a separate partition is not + necessary at this time, and the mountpoint can instead be configured + later. + + + + + + + + + + Ensure /var/log Located On Separate Partition + + Red Hat Enterprise Linux 9 + + If stored locally, create a separate partition for + /var/log. If /var/log will be mounted from another + system such as an NFS server, then creating a separate partition is not + necessary at this time, and the mountpoint can instead be configured + later. + + + + + + + + + + Ensure /var/log/audit Located On Separate Partition + + Red Hat Enterprise Linux 9 + + If stored locally, create a separate partition for + /var/log/audit. If /var/log/audit will be mounted from another + system such as an NFS server, then creating a separate partition is not + necessary at this time, and the mountpoint can instead be configured + later. + + + + + + + + + + Ensure /var/tmp Located On Separate Partition + + Red Hat Enterprise Linux 9 + + If stored locally, create a separate partition for + /var/tmp. If /var/tmp will be mounted from another + system such as an NFS server, then creating a separate partition is not + necessary at this time, and the mountpoint can instead be configured + later. + + + + + + + + + + Verify the system-wide library files in directories +"/lib", "/lib64", "/usr/lib/" and "/usr/lib64" are group-owned by root. + + Red Hat Enterprise Linux 9 + + This test makes sure that /lib/, /lib64/, /usr/lib/, /usr/lib64/ is group owned by 0. + + + + + + + + + + + + + Enable the antivirus_can_scan_system SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'antivirus_can_scan_system' boolean should be set in the system configuration. + + + + + + + + + Disable the antivirus_use_jit SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'antivirus_use_jit' boolean should be set in the system configuration. + + + + + + + + + Enable the auditadm_exec_content SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'auditadm_exec_content' boolean should be set in the system configuration. + + + + + + + + + + Disable the authlogin_nsswitch_use_ldap SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'authlogin_nsswitch_use_ldap' boolean should be set in the system configuration. + + + + + + + + + Disable the authlogin_radius SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'authlogin_radius' boolean should be set in the system configuration. + + + + + + + + + Disable the authlogin_yubikey SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'authlogin_yubikey' boolean should be set in the system configuration. + + + + + + + + + Disable the awstats_purge_apache_log_files SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'awstats_purge_apache_log_files' boolean should be set in the system configuration. + + + + + + + + + Disable the boinc_execmem SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'boinc_execmem' boolean should be set in the system configuration. + + + + + + + + + Disable the cdrecord_read_content SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'cdrecord_read_content' boolean should be set in the system configuration. + + + + + + + + + Disable the cluster_can_network_connect SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'cluster_can_network_connect' boolean should be set in the system configuration. + + + + + + + + + Disable the cluster_manage_all_files SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'cluster_manage_all_files' boolean should be set in the system configuration. + + + + + + + + + Disable the cluster_use_execmem SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'cluster_use_execmem' boolean should be set in the system configuration. + + + + + + + + + Disable the cobbler_anon_write SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'cobbler_anon_write' boolean should be set in the system configuration. + + + + + + + + + Disable the cobbler_can_network_connect SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'cobbler_can_network_connect' boolean should be set in the system configuration. + + + + + + + + + Disable the cobbler_use_cifs SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'cobbler_use_cifs' boolean should be set in the system configuration. + + + + + + + + + Disable the cobbler_use_nfs SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'cobbler_use_nfs' boolean should be set in the system configuration. + + + + + + + + + Disable the collectd_tcp_network_connect SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'collectd_tcp_network_connect' boolean should be set in the system configuration. + + + + + + + + + Disable the condor_tcp_network_connect SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'condor_tcp_network_connect' boolean should be set in the system configuration. + + + + + + + + + Disable the conman_can_network SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'conman_can_network' boolean should be set in the system configuration. + + + + + + + + + Disable the container_connect_any SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'container_connect_any' boolean should be set in the system configuration. + + + + + + + + + Disable the cron_can_relabel SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'cron_can_relabel' boolean should be set in the system configuration. + + + + + + + + + Disable the cron_system_cronjob_use_shares SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'cron_system_cronjob_use_shares' boolean should be set in the system configuration. + + + + + + + + + Enable the cron_userdomain_transition SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'cron_userdomain_transition' boolean should be set in the system configuration. + + + + + + + + + Disable the cups_execmem SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'cups_execmem' boolean should be set in the system configuration. + + + + + + + + + Disable the cvs_read_shadow SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'cvs_read_shadow' boolean should be set in the system configuration. + + + + + + + + + Disable the daemons_dump_core SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'daemons_dump_core' boolean should be set in the system configuration. + + + + + + + + + Disable the daemons_enable_cluster_mode SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'daemons_enable_cluster_mode' boolean should be set in the system configuration. + + + + + + + + + Disable the daemons_use_tcp_wrapper SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'daemons_use_tcp_wrapper' boolean should be set in the system configuration. + + + + + + + + + Disable the daemons_use_tty SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'daemons_use_tty' boolean should be set in the system configuration. + + + + + + + + + Enable the dbadm_exec_content SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'dbadm_exec_content' boolean should be set in the system configuration. + + + + + + + + + Disable the dbadm_manage_user_files SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'dbadm_manage_user_files' boolean should be set in the system configuration. + + + + + + + + + Disable the dbadm_read_user_files SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'dbadm_read_user_files' boolean should be set in the system configuration. + + + + + + + + + Configure the deny_execmem SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'deny_execmem' boolean should be set in the system configuration. + + + + + + + + + + Disable the deny_ptrace SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'deny_ptrace' boolean should be set in the system configuration. + + + + + + + + + Disable the dhcpc_exec_iptables SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'dhcpc_exec_iptables' boolean should be set in the system configuration. + + + + + + + + + Disable the dhcpd_use_ldap SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'dhcpd_use_ldap' boolean should be set in the system configuration. + + + + + + + + + Enable the domain_fd_use SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'domain_fd_use' boolean should be set in the system configuration. + + + + + + + + + Disable the domain_kernel_load_modules SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'domain_kernel_load_modules' boolean should be set in the system configuration. + + + + + + + + + Disable the entropyd_use_audio SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'entropyd_use_audio' boolean should be set in the system configuration. + + + + + + + + + Disable the exim_can_connect_db SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'exim_can_connect_db' boolean should be set in the system configuration. + + + + + + + + + Disable the exim_manage_user_files SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'exim_manage_user_files' boolean should be set in the system configuration. + + + + + + + + + Disable the exim_read_user_files SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'exim_read_user_files' boolean should be set in the system configuration. + + + + + + + + + Disable the fcron_crond SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'fcron_crond' boolean should be set in the system configuration. + + + + + + + + + Disable the fenced_can_network_connect SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'fenced_can_network_connect' boolean should be set in the system configuration. + + + + + + + + + Disable the fenced_can_ssh SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'fenced_can_ssh' boolean should be set in the system configuration. + + + + + + + + + Enable the fips_mode SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'fips_mode' boolean should be set in the system configuration. + + + + + + + + + Disable the ftpd_anon_write SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'ftpd_anon_write' boolean should be set in the system configuration. + + + + + + + + + Disable the ftpd_connect_all_unreserved SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'ftpd_connect_all_unreserved' boolean should be set in the system configuration. + + + + + + + + + Disable the ftpd_connect_db SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'ftpd_connect_db' boolean should be set in the system configuration. + + + + + + + + + Disable the ftpd_full_access SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'ftpd_full_access' boolean should be set in the system configuration. + + + + + + + + + Disable the ftpd_use_cifs SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'ftpd_use_cifs' boolean should be set in the system configuration. + + + + + + + + + Disable the ftpd_use_fusefs SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'ftpd_use_fusefs' boolean should be set in the system configuration. + + + + + + + + + Disable the ftpd_use_nfs SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'ftpd_use_nfs' boolean should be set in the system configuration. + + + + + + + + + Disable the ftpd_use_passive_mode SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'ftpd_use_passive_mode' boolean should be set in the system configuration. + + + + + + + + + Disable the git_cgi_enable_homedirs SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'git_cgi_enable_homedirs' boolean should be set in the system configuration. + + + + + + + + + Disable the git_cgi_use_cifs SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'git_cgi_use_cifs' boolean should be set in the system configuration. + + + + + + + + + Disable the git_cgi_use_nfs SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'git_cgi_use_nfs' boolean should be set in the system configuration. + + + + + + + + + Disable the git_session_bind_all_unreserved_ports SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'git_session_bind_all_unreserved_ports' boolean should be set in the system configuration. + + + + + + + + + Disable the git_session_users SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'git_session_users' boolean should be set in the system configuration. + + + + + + + + + Disable the git_system_enable_homedirs SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'git_system_enable_homedirs' boolean should be set in the system configuration. + + + + + + + + + Disable the git_system_use_cifs SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'git_system_use_cifs' boolean should be set in the system configuration. + + + + + + + + + Disable the git_system_use_nfs SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'git_system_use_nfs' boolean should be set in the system configuration. + + + + + + + + + Disable the gitosis_can_sendmail SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'gitosis_can_sendmail' boolean should be set in the system configuration. + + + + + + + + + Disable the glance_api_can_network SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'glance_api_can_network' boolean should be set in the system configuration. + + + + + + + + + Disable the glance_use_execmem SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'glance_use_execmem' boolean should be set in the system configuration. + + + + + + + + + Disable the glance_use_fusefs SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'glance_use_fusefs' boolean should be set in the system configuration. + + + + + + + + + Disable the global_ssp SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'global_ssp' boolean should be set in the system configuration. + + + + + + + + + Disable the gluster_anon_write SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'gluster_anon_write' boolean should be set in the system configuration. + + + + + + + + + Disable the gluster_export_all_ro SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'gluster_export_all_ro' boolean should be set in the system configuration. + + + + + + + + + Configure the gluster_export_all_rw SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'gluster_export_all_rw' boolean should be set in the system configuration. + + + + + + + + + Disable the gpg_web_anon_write SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'gpg_web_anon_write' boolean should be set in the system configuration. + + + + + + + + + Enable the gssd_read_tmp SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'gssd_read_tmp' boolean should be set in the system configuration. + + + + + + + + + Disable the guest_exec_content SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'guest_exec_content' boolean should be set in the system configuration. + + + + + + + + + Disable the haproxy_connect_any SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'haproxy_connect_any' boolean should be set in the system configuration. + + + + + + + + + Disable the httpd_anon_write SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'httpd_anon_write' boolean should be set in the system configuration. + + + + + + + + + Configure the httpd_builtin_scripting SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'httpd_builtin_scripting' boolean should be set in the system configuration. + + + + + + + + + Disable the httpd_can_check_spam SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'httpd_can_check_spam' boolean should be set in the system configuration. + + + + + + + + + Disable the httpd_can_connect_ftp SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'httpd_can_connect_ftp' boolean should be set in the system configuration. + + + + + + + + + Disable the httpd_can_connect_ldap SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'httpd_can_connect_ldap' boolean should be set in the system configuration. + + + + + + + + + Disable the httpd_can_connect_mythtv SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'httpd_can_connect_mythtv' boolean should be set in the system configuration. + + + + + + + + + Disable the httpd_can_connect_zabbix SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'httpd_can_connect_zabbix' boolean should be set in the system configuration. + + + + + + + + + Disable the httpd_can_network_connect SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'httpd_can_network_connect' boolean should be set in the system configuration. + + + + + + + + + Disable the httpd_can_network_connect_cobbler SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'httpd_can_network_connect_cobbler' boolean should be set in the system configuration. + + + + + + + + + Disable the httpd_can_network_connect_db SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'httpd_can_network_connect_db' boolean should be set in the system configuration. + + + + + + + + + Disable the httpd_can_network_memcache SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'httpd_can_network_memcache' boolean should be set in the system configuration. + + + + + + + + + Disable the httpd_can_network_relay SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'httpd_can_network_relay' boolean should be set in the system configuration. + + + + + + + + + Disable the httpd_can_sendmail SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'httpd_can_sendmail' boolean should be set in the system configuration. + + + + + + + + + Disable the httpd_dbus_avahi SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'httpd_dbus_avahi' boolean should be set in the system configuration. + + + + + + + + + Disable the httpd_dbus_sssd SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'httpd_dbus_sssd' boolean should be set in the system configuration. + + + + + + + + + Disable the httpd_dontaudit_search_dirs SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'httpd_dontaudit_search_dirs' boolean should be set in the system configuration. + + + + + + + + + Configure the httpd_enable_cgi SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'httpd_enable_cgi' boolean should be set in the system configuration. + + + + + + + + + Disable the httpd_enable_ftp_server SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'httpd_enable_ftp_server' boolean should be set in the system configuration. + + + + + + + + + Disable the httpd_enable_homedirs SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'httpd_enable_homedirs' boolean should be set in the system configuration. + + + + + + + + + Disable the httpd_execmem SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'httpd_execmem' boolean should be set in the system configuration. + + + + + + + + + Enable the httpd_graceful_shutdown SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'httpd_graceful_shutdown' boolean should be set in the system configuration. + + + + + + + + + Disable the httpd_manage_ipa SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'httpd_manage_ipa' boolean should be set in the system configuration. + + + + + + + + + Disable the httpd_mod_auth_ntlm_winbind SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'httpd_mod_auth_ntlm_winbind' boolean should be set in the system configuration. + + + + + + + + + Disable the httpd_mod_auth_pam SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'httpd_mod_auth_pam' boolean should be set in the system configuration. + + + + + + + + + Disable the httpd_read_user_content SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'httpd_read_user_content' boolean should be set in the system configuration. + + + + + + + + + Disable the httpd_run_ipa SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'httpd_run_ipa' boolean should be set in the system configuration. + + + + + + + + + Disable the httpd_run_preupgrade SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'httpd_run_preupgrade' boolean should be set in the system configuration. + + + + + + + + + Disable the httpd_run_stickshift SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'httpd_run_stickshift' boolean should be set in the system configuration. + + + + + + + + + Disable the httpd_serve_cobbler_files SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'httpd_serve_cobbler_files' boolean should be set in the system configuration. + + + + + + + + + Disable the httpd_setrlimit SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'httpd_setrlimit' boolean should be set in the system configuration. + + + + + + + + + Disable the httpd_ssi_exec SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'httpd_ssi_exec' boolean should be set in the system configuration. + + + + + + + + + Disable the httpd_sys_script_anon_write SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'httpd_sys_script_anon_write' boolean should be set in the system configuration. + + + + + + + + + Disable the httpd_tmp_exec SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'httpd_tmp_exec' boolean should be set in the system configuration. + + + + + + + + + Disable the httpd_tty_comm SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'httpd_tty_comm' boolean should be set in the system configuration. + + + + + + + + + Disable the httpd_unified SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'httpd_unified' boolean should be set in the system configuration. + + + + + + + + + Disable the httpd_use_cifs SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'httpd_use_cifs' boolean should be set in the system configuration. + + + + + + + + + Disable the httpd_use_fusefs SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'httpd_use_fusefs' boolean should be set in the system configuration. + + + + + + + + + Disable the httpd_use_gpg SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'httpd_use_gpg' boolean should be set in the system configuration. + + + + + + + + + Disable the httpd_use_nfs SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'httpd_use_nfs' boolean should be set in the system configuration. + + + + + + + + + Disable the httpd_use_openstack SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'httpd_use_openstack' boolean should be set in the system configuration. + + + + + + + + + Disable the httpd_use_sasl SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'httpd_use_sasl' boolean should be set in the system configuration. + + + + + + + + + Disable the httpd_verify_dns SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'httpd_verify_dns' boolean should be set in the system configuration. + + + + + + + + + Disable the icecast_use_any_tcp_ports SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'icecast_use_any_tcp_ports' boolean should be set in the system configuration. + + + + + + + + + Disable the irc_use_any_tcp_ports SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'irc_use_any_tcp_ports' boolean should be set in the system configuration. + + + + + + + + + Disable the irssi_use_full_network SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'irssi_use_full_network' boolean should be set in the system configuration. + + + + + + + + + Disable the kdumpgui_run_bootloader SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'kdumpgui_run_bootloader' boolean should be set in the system configuration. + + + + + + + + + Enable the kerberos_enabled SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'kerberos_enabled' boolean should be set in the system configuration. + + + + + + + + + Disable the ksmtuned_use_cifs SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'ksmtuned_use_cifs' boolean should be set in the system configuration. + + + + + + + + + Disable the ksmtuned_use_nfs SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'ksmtuned_use_nfs' boolean should be set in the system configuration. + + + + + + + + + Enable the logadm_exec_content SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'logadm_exec_content' boolean should be set in the system configuration. + + + + + + + + + Disable the logging_syslogd_can_sendmail SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'logging_syslogd_can_sendmail' boolean should be set in the system configuration. + + + + + + + + + Disable the logging_syslogd_run_nagios_plugins SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'logging_syslogd_run_nagios_plugins' boolean should be set in the system configuration. + + + + + + + + + Enable the logging_syslogd_use_tty SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'logging_syslogd_use_tty' boolean should be set in the system configuration. + + + + + + + + + Enable the login_console_enabled SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'login_console_enabled' boolean should be set in the system configuration. + + + + + + + + + Disable the logrotate_use_nfs SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'logrotate_use_nfs' boolean should be set in the system configuration. + + + + + + + + + Disable the logwatch_can_network_connect_mail SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'logwatch_can_network_connect_mail' boolean should be set in the system configuration. + + + + + + + + + Disable the lsmd_plugin_connect_any SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'lsmd_plugin_connect_any' boolean should be set in the system configuration. + + + + + + + + + Disable the mailman_use_fusefs SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'mailman_use_fusefs' boolean should be set in the system configuration. + + + + + + + + + Disable the mcelog_client SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'mcelog_client' boolean should be set in the system configuration. + + + + + + + + + Enable the mcelog_exec_scripts SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'mcelog_exec_scripts' boolean should be set in the system configuration. + + + + + + + + + Disable the mcelog_foreground SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'mcelog_foreground' boolean should be set in the system configuration. + + + + + + + + + Disable the mcelog_server SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'mcelog_server' boolean should be set in the system configuration. + + + + + + + + + Disable the minidlna_read_generic_user_content SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'minidlna_read_generic_user_content' boolean should be set in the system configuration. + + + + + + + + + Disable the mmap_low_allowed SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'mmap_low_allowed' boolean should be set in the system configuration. + + + + + + + + + Disable the mock_enable_homedirs SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'mock_enable_homedirs' boolean should be set in the system configuration. + + + + + + + + + Enable the mount_anyfile SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'mount_anyfile' boolean should be set in the system configuration. + + + + + + + + + Disable the mozilla_plugin_bind_unreserved_ports SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'mozilla_plugin_bind_unreserved_ports' boolean should be set in the system configuration. + + + + + + + + + Disable the mozilla_plugin_can_network_connect SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'mozilla_plugin_can_network_connect' boolean should be set in the system configuration. + + + + + + + + + Disable the mozilla_plugin_use_bluejeans SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'mozilla_plugin_use_bluejeans' boolean should be set in the system configuration. + + + + + + + + + Disable the mozilla_plugin_use_gps SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'mozilla_plugin_use_gps' boolean should be set in the system configuration. + + + + + + + + + Disable the mozilla_plugin_use_spice SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'mozilla_plugin_use_spice' boolean should be set in the system configuration. + + + + + + + + + Disable the mozilla_read_content SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'mozilla_read_content' boolean should be set in the system configuration. + + + + + + + + + Disable the mpd_enable_homedirs SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'mpd_enable_homedirs' boolean should be set in the system configuration. + + + + + + + + + Disable the mpd_use_cifs SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'mpd_use_cifs' boolean should be set in the system configuration. + + + + + + + + + Disable the mpd_use_nfs SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'mpd_use_nfs' boolean should be set in the system configuration. + + + + + + + + + Disable the mplayer_execstack SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'mplayer_execstack' boolean should be set in the system configuration. + + + + + + + + + Disable the mysql_connect_any SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'mysql_connect_any' boolean should be set in the system configuration. + + + + + + + + + Disable the nagios_run_pnp4nagios SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'nagios_run_pnp4nagios' boolean should be set in the system configuration. + + + + + + + + + Disable the nagios_run_sudo SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'nagios_run_sudo' boolean should be set in the system configuration. + + + + + + + + + Disable the named_tcp_bind_http_port SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'named_tcp_bind_http_port' boolean should be set in the system configuration. + + + + + + + + + Disable the named_write_master_zones SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'named_write_master_zones' boolean should be set in the system configuration. + + + + + + + + + Disable the neutron_can_network SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'neutron_can_network' boolean should be set in the system configuration. + + + + + + + + + Enable the nfs_export_all_ro SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'nfs_export_all_ro' boolean should be set in the system configuration. + + + + + + + + + Enable the nfs_export_all_rw SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'nfs_export_all_rw' boolean should be set in the system configuration. + + + + + + + + + Disable the nfsd_anon_write SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'nfsd_anon_write' boolean should be set in the system configuration. + + + + + + + + + Disable the nis_enabled SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'nis_enabled' boolean should be set in the system configuration. + + + + + + + + + Enable the nscd_use_shm SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'nscd_use_shm' boolean should be set in the system configuration. + + + + + + + + + Disable the openshift_use_nfs SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'openshift_use_nfs' boolean should be set in the system configuration. + + + + + + + + + Disable the openvpn_can_network_connect SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'openvpn_can_network_connect' boolean should be set in the system configuration. + + + + + + + + + Disable the openvpn_enable_homedirs SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'openvpn_enable_homedirs' boolean should be set in the system configuration. + + + + + + + + + Disable the openvpn_run_unconfined SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'openvpn_run_unconfined' boolean should be set in the system configuration. + + + + + + + + + Disable the pcp_bind_all_unreserved_ports SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'pcp_bind_all_unreserved_ports' boolean should be set in the system configuration. + + + + + + + + + Disable the pcp_read_generic_logs SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'pcp_read_generic_logs' boolean should be set in the system configuration. + + + + + + + + + Disable the piranha_lvs_can_network_connect SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'piranha_lvs_can_network_connect' boolean should be set in the system configuration. + + + + + + + + + Disable the polipo_connect_all_unreserved SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'polipo_connect_all_unreserved' boolean should be set in the system configuration. + + + + + + + + + Disable the polipo_session_bind_all_unreserved_ports SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'polipo_session_bind_all_unreserved_ports' boolean should be set in the system configuration. + + + + + + + + + Disable the polipo_session_users SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'polipo_session_users' boolean should be set in the system configuration. + + + + + + + + + Disable the polipo_use_cifs SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'polipo_use_cifs' boolean should be set in the system configuration. + + + + + + + + + Disable the polipo_use_nfs SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'polipo_use_nfs' boolean should be set in the system configuration. + + + + + + + + + Configure the polyinstantiation_enabled SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'polyinstantiation_enabled' boolean should be set in the system configuration. + + + + + + + + + + Enable the postfix_local_write_mail_spool SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'postfix_local_write_mail_spool' boolean should be set in the system configuration. + + + + + + + + + Disable the postgresql_can_rsync SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'postgresql_can_rsync' boolean should be set in the system configuration. + + + + + + + + + Disable the postgresql_selinux_transmit_client_label SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'postgresql_selinux_transmit_client_label' boolean should be set in the system configuration. + + + + + + + + + Enable the postgresql_selinux_unconfined_dbadm SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'postgresql_selinux_unconfined_dbadm' boolean should be set in the system configuration. + + + + + + + + + Enable the postgresql_selinux_users_ddl SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'postgresql_selinux_users_ddl' boolean should be set in the system configuration. + + + + + + + + + Disable the pppd_can_insmod SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'pppd_can_insmod' boolean should be set in the system configuration. + + + + + + + + + Disable the pppd_for_user SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'pppd_for_user' boolean should be set in the system configuration. + + + + + + + + + Disable the privoxy_connect_any SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'privoxy_connect_any' boolean should be set in the system configuration. + + + + + + + + + Disable the prosody_bind_http_port SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'prosody_bind_http_port' boolean should be set in the system configuration. + + + + + + + + + Disable the puppetagent_manage_all_files SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'puppetagent_manage_all_files' boolean should be set in the system configuration. + + + + + + + + + Disable the puppetmaster_use_db SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'puppetmaster_use_db' boolean should be set in the system configuration. + + + + + + + + + Disable the racoon_read_shadow SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'racoon_read_shadow' boolean should be set in the system configuration. + + + + + + + + + Disable the rsync_anon_write SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'rsync_anon_write' boolean should be set in the system configuration. + + + + + + + + + Disable the rsync_client SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'rsync_client' boolean should be set in the system configuration. + + + + + + + + + Disable the rsync_export_all_ro SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'rsync_export_all_ro' boolean should be set in the system configuration. + + + + + + + + + Disable the rsync_full_access SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'rsync_full_access' boolean should be set in the system configuration. + + + + + + + + + Disable the samba_create_home_dirs SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'samba_create_home_dirs' boolean should be set in the system configuration. + + + + + + + + + Disable the samba_domain_controller SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'samba_domain_controller' boolean should be set in the system configuration. + + + + + + + + + Disable the samba_enable_home_dirs SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'samba_enable_home_dirs' boolean should be set in the system configuration. + + + + + + + + + Disable the samba_export_all_ro SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'samba_export_all_ro' boolean should be set in the system configuration. + + + + + + + + + Disable the samba_export_all_rw SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'samba_export_all_rw' boolean should be set in the system configuration. + + + + + + + + + Disable the samba_load_libgfapi SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'samba_load_libgfapi' boolean should be set in the system configuration. + + + + + + + + + Disable the samba_portmapper SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'samba_portmapper' boolean should be set in the system configuration. + + + + + + + + + Disable the samba_run_unconfined SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'samba_run_unconfined' boolean should be set in the system configuration. + + + + + + + + + Disable the samba_share_fusefs SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'samba_share_fusefs' boolean should be set in the system configuration. + + + + + + + + + Disable the samba_share_nfs SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'samba_share_nfs' boolean should be set in the system configuration. + + + + + + + + + Disable the sanlock_use_fusefs SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'sanlock_use_fusefs' boolean should be set in the system configuration. + + + + + + + + + Disable the sanlock_use_nfs SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'sanlock_use_nfs' boolean should be set in the system configuration. + + + + + + + + + Disable the sanlock_use_samba SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'sanlock_use_samba' boolean should be set in the system configuration. + + + + + + + + + Disable the saslauthd_read_shadow SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'saslauthd_read_shadow' boolean should be set in the system configuration. + + + + + + + + + Enable the secadm_exec_content SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'secadm_exec_content' boolean should be set in the system configuration. + + + + + + + + + Disable the secure_mode SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'secure_mode' boolean should be set in the system configuration. + + + + + + + + + Configure the secure_mode_insmod SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'secure_mode_insmod' boolean should be set in the system configuration. + + + + + + + + + + Disable the secure_mode_policyload SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'secure_mode_policyload' boolean should be set in the system configuration. + + + + + + + + + Configure the selinuxuser_direct_dri_enabled SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'selinuxuser_direct_dri_enabled' boolean should be set in the system configuration. + + + + + + + + + Disable the selinuxuser_execheap SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'selinuxuser_execheap' boolean should be set in the system configuration. + + + + + + + + + + Enable the selinuxuser_execmod SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'selinuxuser_execmod' boolean should be set in the system configuration. + + + + + + + + + + disable the selinuxuser_execstack SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'selinuxuser_execstack' boolean should be set in the system configuration. + + + + + + + + + + Disable the selinuxuser_mysql_connect_enabled SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'selinuxuser_mysql_connect_enabled' boolean should be set in the system configuration. + + + + + + + + + Enable the selinuxuser_ping SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'selinuxuser_ping' boolean should be set in the system configuration. + + + + + + + + + Disable the selinuxuser_postgresql_connect_enabled SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'selinuxuser_postgresql_connect_enabled' boolean should be set in the system configuration. + + + + + + + + + Disable the selinuxuser_rw_noexattrfile SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'selinuxuser_rw_noexattrfile' boolean should be set in the system configuration. + + + + + + + + + Disable the selinuxuser_share_music SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'selinuxuser_share_music' boolean should be set in the system configuration. + + + + + + + + + Disable the selinuxuser_tcp_server SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'selinuxuser_tcp_server' boolean should be set in the system configuration. + + + + + + + + + Disable the selinuxuser_udp_server SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'selinuxuser_udp_server' boolean should be set in the system configuration. + + + + + + + + + Disable the selinuxuser_use_ssh_chroot SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'selinuxuser_use_ssh_chroot' boolean should be set in the system configuration. + + + + + + + + + Disable the sge_domain_can_network_connect SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'sge_domain_can_network_connect' boolean should be set in the system configuration. + + + + + + + + + Disable the sge_use_nfs SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'sge_use_nfs' boolean should be set in the system configuration. + + + + + + + + + Disable the smartmon_3ware SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'smartmon_3ware' boolean should be set in the system configuration. + + + + + + + + + Disable the smbd_anon_write SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'smbd_anon_write' boolean should be set in the system configuration. + + + + + + + + + Disable the spamassassin_can_network SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'spamassassin_can_network' boolean should be set in the system configuration. + + + + + + + + + Enable the spamd_enable_home_dirs SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'spamd_enable_home_dirs' boolean should be set in the system configuration. + + + + + + + + + Disable the squid_connect_any SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'squid_connect_any' boolean should be set in the system configuration. + + + + + + + + + Disable the squid_use_tproxy SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'squid_use_tproxy' boolean should be set in the system configuration. + + + + + + + + + Disable the ssh_chroot_rw_homedirs SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'ssh_chroot_rw_homedirs' boolean should be set in the system configuration. + + + + + + + + + Disable the ssh_keysign SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'ssh_keysign' boolean should be set in the system configuration. + + + + + + + + + Disable the ssh_sysadm_login SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'ssh_sysadm_login' boolean should be set in the system configuration. + + + + + + + + + + Enable the staff_exec_content SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'staff_exec_content' boolean should be set in the system configuration. + + + + + + + + + Disable the staff_use_svirt SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'staff_use_svirt' boolean should be set in the system configuration. + + + + + + + + + Disable the swift_can_network SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'swift_can_network' boolean should be set in the system configuration. + + + + + + + + + Enable the sysadm_exec_content SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'sysadm_exec_content' boolean should be set in the system configuration. + + + + + + + + + Disable the telepathy_connect_all_ports SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'telepathy_connect_all_ports' boolean should be set in the system configuration. + + + + + + + + + Disable the telepathy_tcp_connect_generic_network_ports SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'telepathy_tcp_connect_generic_network_ports' boolean should be set in the system configuration. + + + + + + + + + Disable the tftp_anon_write SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'tftp_anon_write' boolean should be set in the system configuration. + + + + + + + + + Disable the tftp_home_dir SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'tftp_home_dir' boolean should be set in the system configuration. + + + + + + + + + Disable the tmpreaper_use_nfs SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'tmpreaper_use_nfs' boolean should be set in the system configuration. + + + + + + + + + Disable the tmpreaper_use_samba SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'tmpreaper_use_samba' boolean should be set in the system configuration. + + + + + + + + + Disable the tor_bind_all_unreserved_ports SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'tor_bind_all_unreserved_ports' boolean should be set in the system configuration. + + + + + + + + + Disable the tor_can_network_relay SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'tor_can_network_relay' boolean should be set in the system configuration. + + + + + + + + + Enable the unconfined_chrome_sandbox_transition SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'unconfined_chrome_sandbox_transition' boolean should be set in the system configuration. + + + + + + + + + Enable the unconfined_login SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'unconfined_login' boolean should be set in the system configuration. + + + + + + + + + Enable the unconfined_mozilla_plugin_transition SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'unconfined_mozilla_plugin_transition' boolean should be set in the system configuration. + + + + + + + + + Disable the unprivuser_use_svirt SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'unprivuser_use_svirt' boolean should be set in the system configuration. + + + + + + + + + Disable the use_ecryptfs_home_dirs SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'use_ecryptfs_home_dirs' boolean should be set in the system configuration. + + + + + + + + + Disable the use_fusefs_home_dirs SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'use_fusefs_home_dirs' boolean should be set in the system configuration. + + + + + + + + + Disable the use_lpd_server SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'use_lpd_server' boolean should be set in the system configuration. + + + + + + + + + Disable the use_nfs_home_dirs SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'use_nfs_home_dirs' boolean should be set in the system configuration. + + + + + + + + + Disable the use_samba_home_dirs SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'use_samba_home_dirs' boolean should be set in the system configuration. + + + + + + + + + Enable the user_exec_content SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'user_exec_content' boolean should be set in the system configuration. + + + + + + + + + Disable the varnishd_connect_any SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'varnishd_connect_any' boolean should be set in the system configuration. + + + + + + + + + Disable the virt_read_qemu_ga_data SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'virt_read_qemu_ga_data' boolean should be set in the system configuration. + + + + + + + + + Disable the virt_rw_qemu_ga_data SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'virt_rw_qemu_ga_data' boolean should be set in the system configuration. + + + + + + + + + Disable the virt_sandbox_use_all_caps SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'virt_sandbox_use_all_caps' boolean should be set in the system configuration. + + + + + + + + + Enable the virt_sandbox_use_audit SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'virt_sandbox_use_audit' boolean should be set in the system configuration. + + + + + + + + + Disable the virt_sandbox_use_mknod SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'virt_sandbox_use_mknod' boolean should be set in the system configuration. + + + + + + + + + Disable the virt_sandbox_use_netlink SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'virt_sandbox_use_netlink' boolean should be set in the system configuration. + + + + + + + + + Disable the virt_sandbox_use_sys_admin SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'virt_sandbox_use_sys_admin' boolean should be set in the system configuration. + + + + + + + + + Disable the virt_transition_userdomain SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'virt_transition_userdomain' boolean should be set in the system configuration. + + + + + + + + + Disable the virt_use_comm SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'virt_use_comm' boolean should be set in the system configuration. + + + + + + + + + Disable the virt_use_execmem SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'virt_use_execmem' boolean should be set in the system configuration. + + + + + + + + + Disable the virt_use_fusefs SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'virt_use_fusefs' boolean should be set in the system configuration. + + + + + + + + + Disable the virt_use_nfs SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'virt_use_nfs' boolean should be set in the system configuration. + + + + + + + + + Disable the virt_use_rawip SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'virt_use_rawip' boolean should be set in the system configuration. + + + + + + + + + Disable the virt_use_samba SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'virt_use_samba' boolean should be set in the system configuration. + + + + + + + + + Disable the virt_use_sanlock SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'virt_use_sanlock' boolean should be set in the system configuration. + + + + + + + + + Disable the virt_use_usb SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'virt_use_usb' boolean should be set in the system configuration. + + + + + + + + + Disable the virt_use_xserver SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'virt_use_xserver' boolean should be set in the system configuration. + + + + + + + + + Disable the webadm_manage_user_files SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'webadm_manage_user_files' boolean should be set in the system configuration. + + + + + + + + + Disable the webadm_read_user_files SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'webadm_read_user_files' boolean should be set in the system configuration. + + + + + + + + + Disable the wine_mmap_zero_ignore SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'wine_mmap_zero_ignore' boolean should be set in the system configuration. + + + + + + + + + Disable the xdm_bind_vnc_tcp_port SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'xdm_bind_vnc_tcp_port' boolean should be set in the system configuration. + + + + + + + + + Disable the xdm_exec_bootloader SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'xdm_exec_bootloader' boolean should be set in the system configuration. + + + + + + + + + Disable the xdm_sysadm_login SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'xdm_sysadm_login' boolean should be set in the system configuration. + + + + + + + + + Disable the xdm_write_home SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'xdm_write_home' boolean should be set in the system configuration. + + + + + + + + + Disable the xen_use_nfs SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'xen_use_nfs' boolean should be set in the system configuration. + + + + + + + + + Enable the xend_run_blktap SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'xend_run_blktap' boolean should be set in the system configuration. + + + + + + + + + Enable the xend_run_qemu SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'xend_run_qemu' boolean should be set in the system configuration. + + + + + + + + + Disable the xguest_connect_network SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'xguest_connect_network' boolean should be set in the system configuration. + + + + + + + + + Disable the xguest_exec_content SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'xguest_exec_content' boolean should be set in the system configuration. + + + + + + + + + Disable the xguest_mount_media SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'xguest_mount_media' boolean should be set in the system configuration. + + + + + + + + + Disable the xguest_use_bluetooth SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'xguest_use_bluetooth' boolean should be set in the system configuration. + + + + + + + + + Disable the xserver_clients_write_xshm SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'xserver_clients_write_xshm' boolean should be set in the system configuration. + + + + + + + + + Disable the xserver_execmem SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'xserver_execmem' boolean should be set in the system configuration. + + + + + + + + + Disable the xserver_object_manager SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'xserver_object_manager' boolean should be set in the system configuration. + + + + + + + + + Disable the zabbix_can_network SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'zabbix_can_network' boolean should be set in the system configuration. + + + + + + + + + Disable the zarafa_setrlimit SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'zarafa_setrlimit' boolean should be set in the system configuration. + + + + + + + + + Disable the zebra_write_config SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'zebra_write_config' boolean should be set in the system configuration. + + + + + + + + + Disable the zoneminder_anon_write SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'zoneminder_anon_write' boolean should be set in the system configuration. + + + + + + + + + Disable the zoneminder_run_sudo SELinux Boolean + + Red Hat Enterprise Linux 9 + + The SELinux 'zoneminder_run_sudo' boolean should be set in the system configuration. + + + + + + + + + Disable At Service (atd) + + Red Hat Enterprise Linux 9 + + The atd service should be disabled if possible. + + + + + + + + + + + + + + Enable auditd Service + + Red Hat Enterprise Linux 9 + + The auditd service should be enabled if possible. + + + + + + + + + + + + + + + + + Disable the Automounter + + Red Hat Enterprise Linux 9 + + The autofs service should be disabled if possible. + + + + + + + + + + + + + + Disable Avahi Server Software + + Red Hat Enterprise Linux 9 + + The avahi-daemon service should be disabled if possible. + + + + + + + + + + + + + + The Chronyd service is enabled + + Red Hat Enterprise Linux 9 + + The chronyd service should be enabled if possible. + + + + + + + + + + + + + + + + + Disable Cockpit Management Server + + Red Hat Enterprise Linux 9 + + The cockpit service should be disabled if possible. + + + + + + + + + + + + + Enable cron Service + + Red Hat Enterprise Linux 9 + + The cron service should be enabled if possible. + + + + + + + + + + + + + + + + Enable cron Service + + Red Hat Enterprise Linux 9 + + The crond service should be enabled if possible. + + + + + + + + + + + + + + + + + Disable the CUPS Service + + Red Hat Enterprise Linux 9 + + The cups service should be disabled if possible. + + + + + + + + + + + + + + Disable debug-shell SystemD Service + + Red Hat Enterprise Linux 9 + + The debug-shell service should be disabled if possible. + + + + + + + + + + + + + + Disable DHCP Service + + Red Hat Enterprise Linux 9 + + The dhcpd service should be disabled if possible. + + + + + + + + + + + + + + Disable Dovecot Service + + Red Hat Enterprise Linux 9 + + The dovecot service should be disabled if possible. + + + + + + + + + + + + + + Enable the File Access Policy Service + + Red Hat Enterprise Linux 9 + + The fapolicyd service should be enabled if possible. + + + + + + + + + + + + + + + + + Verify firewalld Enabled + + Red Hat Enterprise Linux 9 + + The firewalld service should be enabled if possible. + + + + + + + + + + + + + + + + + Disable httpd Service + + Red Hat Enterprise Linux 9 + + The httpd service should be disabled if possible. + + + + + + + + + + + + + + Verify ip6tables Enabled if Using IPv6 + + Red Hat Enterprise Linux 9 + + The ip6tables service should be enabled if possible. + + + + + + + + + + + + + + + + + Verify iptables Enabled + + Red Hat Enterprise Linux 9 + + The iptables service should be enabled if possible. + + + + + + + + + + + + + + + + + Disable KDump Kernel Crash Analyzer (kdump) + + Red Hat Enterprise Linux 9 + + The kdump service should be disabled if possible. + + + + + + + + + + + + + + Disable named Service + + Red Hat Enterprise Linux 9 + + The named service should be disabled if possible. + + + + + + + + + + + + + + Disable Network File Systems (netfs) + + Red Hat Enterprise Linux 9 + + The netfs service should be disabled if possible. + + + + + + + + + + + + + Disable Network File System (nfs) + + Red Hat Enterprise Linux 9 + + The nfs-server service should be disabled if possible. + + + + + + + + + + + + + + Enable the NTP Daemon + + Red Hat Enterprise Linux 9 + + The ntp service should be enabled if possible. + + + + + + + + + + + + + + + + Enable the NTP Daemon + + Red Hat Enterprise Linux 9 + + The ntpd service should be enabled if possible. + + + + + + + + + + + + + + + + + Disable ntpdate Service (ntpdate) + + Red Hat Enterprise Linux 9 + + The ntpdate service should be disabled if possible. + + + + + + + + + + + + + + Disable Odd Job Daemon (oddjobd) + + Red Hat Enterprise Linux 9 + + The oddjobd service should be disabled if possible. + + + + + + + + + + + + + + Enable the pcscd Service + + Red Hat Enterprise Linux 9 + + The pcscd service should be enabled if possible. + + + + + + + + + + + + + + + + + Enable Postfix Service + + Red Hat Enterprise Linux 9 + + The postfix service should be enabled if possible. + + + + + + + + + + + + + + + + Disable Apache Qpid (qpidd) + + Red Hat Enterprise Linux 9 + + The qpidd service should be disabled if possible. + + + + + + + + + + + + + + Disable Network Router Discovery Daemon (rdisc) + + Red Hat Enterprise Linux 9 + + The rdisc service should be disabled if possible. + + + + + + + + + + + + + + Disable Red Hat Network Service (rhnsd) + + Red Hat Enterprise Linux 9 + + The rhnsd service should be disabled if possible. + + + + + + + + + + + + + + Disable rlogin Service + + Red Hat Enterprise Linux 9 + + The rlogin service should be disabled if possible. + + + + + + + + + + + + + + Enable the Hardware RNG Entropy Gatherer Service + + Red Hat Enterprise Linux 9 + + The rngd service should be enabled if possible. + + + + + + + + + + + + + + + + + Disable rpcbind Service + + Red Hat Enterprise Linux 9 + + The rpcbind service should be disabled if possible. + + + + + + + + + + + + + + Ensure rsyncd service is diabled + + Red Hat Enterprise Linux 9 + + The rsyncd service should be disabled if possible. + + + + + + + + + + + + + + Enable rsyslog Service + + Red Hat Enterprise Linux 9 + + The rsyslog service should be enabled if possible. + + + + + + + + + + + + + + + + + Disable LDAP Server (slapd) + + Red Hat Enterprise Linux 9 + + The slapd service should be disabled if possible. + + + + + + + + + + + + + + Disable Samba + + Red Hat Enterprise Linux 9 + + The smb service should be disabled if possible. + + + + + + + + + + + + + + Disable snmpd Service + + Red Hat Enterprise Linux 9 + + The snmpd service should be disabled if possible. + + + + + + + + + + + + + + Disable Squid + + Red Hat Enterprise Linux 9 + + The squid service should be disabled if possible. + + + + + + + + + + + + + + Disable SSH Server If Possible (Unusual) + + Red Hat Enterprise Linux 9 + + The sshd service should be disabled if possible. + + + + + + + + + + + + + Enable the OpenSSH Service + + Red Hat Enterprise Linux 9 + + The sshd service should be enabled if possible. + + + + + + + + + + + + + + + + + service_syslog_disabled + + Red Hat Enterprise Linux 9 + + The syslog service should be disabled if possible. + + + + + + + + + + + + + Enable syslog-ng Service + + Red Hat Enterprise Linux 9 + + The syslog-ng service should be enabled if possible. + + + + + + + + + + + + + + + + Disable acquiring, saving, and processing core dumps + + Red Hat Enterprise Linux 9 + + The systemd-coredump service should be disabled if possible. + + + + + + + + + + + + + + Enable systemd-journald Service + + Red Hat Enterprise Linux 9 + + The systemd-journald service should be enabled if possible. + + + + + + + + + + + + + + + + + Disable telnet Service + + Red Hat Enterprise Linux 9 + + The telnet service should be disabled if possible. + + + + + + + + + + + + + + Verify ufw Enabled + + Red Hat Enterprise Linux 9 + + The ufw service should be enabled if possible. + + + + + + + + + + + + + + + + Enable the USBGuard Service + + Red Hat Enterprise Linux 9 + + The usbguard service should be enabled if possible. + + + + + + + + + + + + + + + + + Disable vsftpd Service + + Red Hat Enterprise Linux 9 + + The vsftpd service should be disabled if possible. + + + + + + + + + + + + + + Disable xinetd Service + + Red Hat Enterprise Linux 9 + + The xinetd service should be disabled if possible. + + + + + + + + + + + + + + Disable ypserv Service + + Red Hat Enterprise Linux 9 + + The ypserv service should be disabled if possible. + + + + + + + + + + + + + + Disable SSH Access via Empty Passwords + + Red Hat Enterprise Linux 9 + + Ensure 'PermitEmptyPasswords' is configured with value 'no' in /etc/ssh/sshd_config or in /etc/ssh/sshd_config.d + + + + + + + + + + + + + + + + + + + + + Disable GSSAPI Authentication + + Red Hat Enterprise Linux 9 + + Ensure 'GSSAPIAuthentication' is configured with value 'no' in /etc/ssh/sshd_config or in /etc/ssh/sshd_config.d + + + + + + + + + + + + + + + + + + + + + Disable Kerberos Authentication + + Red Hat Enterprise Linux 9 + + Ensure 'KerberosAuthentication' is configured with value 'no' in /etc/ssh/sshd_config or in /etc/ssh/sshd_config.d + + + + + + + + + + + + + + + + + + + + + Disable PubkeyAuthentication Authentication + + Red Hat Enterprise Linux 9 + + Ensure 'PubkeyAuthentication' is configured with value 'no' in /etc/ssh/sshd_config or in /etc/ssh/sshd_config.d + + + + + + + + + + + + + + + + + + + + Disable SSH Support for .rhosts Files + + Red Hat Enterprise Linux 9 + + Ensure 'IgnoreRhosts' is configured with value 'yes' in /etc/ssh/sshd_config or in /etc/ssh/sshd_config.d + + + + + + + + + + + + + + + + + + + + + Disable SSH Root Login + + Red Hat Enterprise Linux 9 + + Ensure 'PermitRootLogin' is configured with value 'no' in /etc/ssh/sshd_config or in /etc/ssh/sshd_config.d + + + + + + + + + + + + + + + + + + + + + Disable SSH root Login with a Password (Insecure) + + Red Hat Enterprise Linux 9 + + Ensure 'PermitRootLogin' is configured with value 'prohibit-password' in /etc/ssh/sshd_config or in /etc/ssh/sshd_config.d + + + + + + + + + + + + + + + + + + + + Disable SSH TCP Forwarding + + Red Hat Enterprise Linux 9 + + Ensure 'AllowTcpForwarding' is configured with value 'no' in /etc/ssh/sshd_config or in /etc/ssh/sshd_config.d + + + + + + + + + + + + + + + + + + + + + Disable SSH Support for User Known Hosts + + Red Hat Enterprise Linux 9 + + Ensure 'IgnoreUserKnownHosts' is configured with value 'yes' in /etc/ssh/sshd_config or in /etc/ssh/sshd_config.d + + + + + + + + + + + + + + + + + + + + + Disable X11 Forwarding + + Red Hat Enterprise Linux 9 + + Ensure 'X11Forwarding' is configured with value 'no' in /etc/ssh/sshd_config or in /etc/ssh/sshd_config.d + + + + + + + + + + + + + + + + + + + + + Do Not Allow SSH Environment Options + + Red Hat Enterprise Linux 9 + + Ensure 'PermitUserEnvironment' is configured with value 'no' in /etc/ssh/sshd_config or in /etc/ssh/sshd_config.d + + + + + + + + + + + + + + + + + + + + + Enable GSSAPI Authentication + + Red Hat Enterprise Linux 9 + + Ensure 'GSSAPIAuthentication' is configured with value 'yes' in /etc/ssh/sshd_config or in /etc/ssh/sshd_config.d + + + + + + + + + + + + + + + + + + + + Enable PAM + + Red Hat Enterprise Linux 9 + + Ensure 'UsePAM' is configured with value 'yes' in /etc/ssh/sshd_config or in /etc/ssh/sshd_config.d + + + + + + + + + + + + + + + + + + + + + Enable Public Key Authentication + + Red Hat Enterprise Linux 9 + + Ensure 'PubkeyAuthentication' is configured with value 'yes' in /etc/ssh/sshd_config or in /etc/ssh/sshd_config.d + + + + + + + + + + + + + + + + + + + + + Enable Use of Strict Mode Checking + + Red Hat Enterprise Linux 9 + + Ensure 'StrictModes' is configured with value 'yes' in /etc/ssh/sshd_config or in /etc/ssh/sshd_config.d + + + + + + + + + + + + + + + + + + + + + Enable SSH Warning Banner + + Red Hat Enterprise Linux 9 + + Ensure 'Banner' is configured with value '/etc/issue' in /etc/ssh/sshd_config or in /etc/ssh/sshd_config.d + + + + + + + + + + + + + + + + + + + + + Enable SSH Warning Banner + + Red Hat Enterprise Linux 9 + + Ensure 'Banner' is configured with value '/etc/issue.net' in /etc/ssh/sshd_config or in /etc/ssh/sshd_config.d + + + + + + + + + + + + + + + + + + + + Enable Encrypted X11 Forwarding + + Red Hat Enterprise Linux 9 + + Ensure 'X11Forwarding' is configured with value 'yes' in /etc/ssh/sshd_config or in /etc/ssh/sshd_config.d + + + + + + + + + + + + + + + + + + + + + sshd_includes_config_files + + Red Hat Enterprise Linux 9 + + Check presence of Include /etc/ssh/sshd_config.d/*.conf in /etc/ssh/sshd_config + + + + + + + + + Enable SSH Print Last Log + + Red Hat Enterprise Linux 9 + + Ensure 'PrintLastLog' is configured with value 'yes' in /etc/ssh/sshd_config or in /etc/ssh/sshd_config.d + + + + + + + + + + + + + + + + + + + + + Set SSH Client Alive Count Max to zero + + Red Hat Enterprise Linux 9 + + Ensure 'ClientAliveCountMax' is configured with value '0' in /etc/ssh/sshd_config or in /etc/ssh/sshd_config.d + + + + + + + + + + + + + + + + + + + + + Set LogLevel to INFO + + Red Hat Enterprise Linux 9 + + Ensure 'LogLevel' is configured with value 'INFO' in /etc/ssh/sshd_config or in /etc/ssh/sshd_config.d + + + + + + + + + + + + + + + + + + + + + Set SSH Daemon LogLevel to VERBOSE + + Red Hat Enterprise Linux 9 + + Ensure 'LogLevel' is configured with value 'VERBOSE' in /etc/ssh/sshd_config or in /etc/ssh/sshd_config.d + + + + + + + + + + + + + + + + + + + + + SSH server uses strong entropy to seed + + Red Hat Enterprise Linux 9 + + Ensure 'SSH_USE_STRONG_RNG' is configured with value '32' in /etc/sysconfig/sshd + + + + + + + + + + Prevent remote hosts from connecting to the proxy display + + Red Hat Enterprise Linux 9 + + Ensure 'X11UseLocalhost' is configured with value 'yes' in /etc/ssh/sshd_config or in /etc/ssh/sshd_config.d + + + + + + + + + + + + + + + + + + + + + Enable Certmap in SSSD + + Red Hat Enterprise Linux 9 + + Check presence of \[certmap\/.+\/.+\] in /etc/sssd/sssd.conf + + + + + + + + + + Ensure Privileged Escalated Commands Cannot Execute Other Commands - sudo NOEXEC + + Red Hat Enterprise Linux 9 + + Checks sudoers Defaults {{ OPTION }} configuration + + + + + + + + + + Ensure Only Users Logged In To Real tty Can Execute Sudo - sudo requiretty + + Red Hat Enterprise Linux 9 + + Checks sudoers Defaults {{ OPTION }} configuration + + + + + + + + + + Ensure Only Users Logged In To Real tty Can Execute Sudo - sudo use_pty + + Red Hat Enterprise Linux 9 + + Checks sudoers Defaults {{ OPTION }} configuration + + + + + + + + + + Ensure Sudo Logfile Exists - sudo logfile + + Red Hat Enterprise Linux 9 + + Checks sudoers Defaults {{ OPTION }} configuration + + + + + + + + + + Enable Kernel Parameter to Enforce DAC on FIFOs + + Red Hat Enterprise Linux 9 + + The 'fs.protected_fifos' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Enable Kernel Parameter to Enforce DAC on FIFOs + + Red Hat Enterprise Linux 9 + + The kernel 'fs.protected_fifos' parameter should be set to 2 in the system runtime. + + + + + + + + + Enable Kernel Parameter to Enforce DAC on FIFOs + + Red Hat Enterprise Linux 9 + + The kernel 'fs.protected_fifos' parameter should be set to 2 in the system configuration. + + + + + + + + + + + + + + Enable Kernel Parameter to Enforce DAC on Hardlinks + + Red Hat Enterprise Linux 9 + + The 'fs.protected_hardlinks' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Enable Kernel Parameter to Enforce DAC on Hardlinks + + Red Hat Enterprise Linux 9 + + The kernel 'fs.protected_hardlinks' parameter should be set to 1 in the system runtime. + + + + + + + + + Enable Kernel Parameter to Enforce DAC on Hardlinks + + Red Hat Enterprise Linux 9 + + The kernel 'fs.protected_hardlinks' parameter should be set to 1 in the system configuration. + + + + + + + + + + + + + + Enable Kernel Parameter to Enforce DAC on Regular files + + Red Hat Enterprise Linux 9 + + The 'fs.protected_regular' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Enable Kernel Parameter to Enforce DAC on Regular files + + Red Hat Enterprise Linux 9 + + The kernel 'fs.protected_regular' parameter should be set to 2 in the system runtime. + + + + + + + + + Enable Kernel Parameter to Enforce DAC on Regular files + + Red Hat Enterprise Linux 9 + + The kernel 'fs.protected_regular' parameter should be set to 2 in the system configuration. + + + + + + + + + + + + + + Enable Kernel Parameter to Enforce DAC on Symlinks + + Red Hat Enterprise Linux 9 + + The 'fs.protected_symlinks' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Enable Kernel Parameter to Enforce DAC on Symlinks + + Red Hat Enterprise Linux 9 + + The kernel 'fs.protected_symlinks' parameter should be set to 1 in the system runtime. + + + + + + + + + Enable Kernel Parameter to Enforce DAC on Symlinks + + Red Hat Enterprise Linux 9 + + The kernel 'fs.protected_symlinks' parameter should be set to 1 in the system configuration. + + + + + + + + + + + + + + Disable Core Dumps for SUID programs + + Red Hat Enterprise Linux 9 + + The 'fs.suid_dumpable' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Disable Core Dumps for SUID programs + + Red Hat Enterprise Linux 9 + + The kernel 'fs.suid_dumpable' parameter should be set to 0 in the system runtime. + + + + + + + + + Disable Core Dumps for SUID programs + + Red Hat Enterprise Linux 9 + + The kernel 'fs.suid_dumpable' parameter should be set to 0 in the system configuration. + + + + + + + + + + + + + + Disable storing core dumps + + Red Hat Enterprise Linux 9 + + The 'kernel.core_pattern' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Disable storing core dumps + + Red Hat Enterprise Linux 9 + + The kernel 'kernel.core_pattern' parameter should be set to |/bin/false in the system runtime. + + + + + + + + + Disable storing core dumps + + Red Hat Enterprise Linux 9 + + The kernel 'kernel.core_pattern' parameter should be set to |/bin/false in the system configuration. + + + + + + + + + + + + + + Configure file name of core dumps + + Red Hat Enterprise Linux 9 + + The 'kernel.core_uses_pid' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Configure file name of core dumps + + Red Hat Enterprise Linux 9 + + The kernel 'kernel.core_uses_pid' parameter should be set to 0 in the system runtime. + + + + + + + + + Configure file name of core dumps + + Red Hat Enterprise Linux 9 + + The kernel 'kernel.core_uses_pid' parameter should be set to 0 in the system configuration. + + + + + + + + + + + + + + Restrict Access to Kernel Message Buffer + + Red Hat Enterprise Linux 9 + + The 'kernel.dmesg_restrict' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Restrict Access to Kernel Message Buffer + + Red Hat Enterprise Linux 9 + + The kernel 'kernel.dmesg_restrict' parameter should be set to 1 in the system runtime. + + + + + + + + + Restrict Access to Kernel Message Buffer + + Red Hat Enterprise Linux 9 + + The kernel 'kernel.dmesg_restrict' parameter should be set to 1 in the system configuration. + + + + + + + + + + + + + + Disable Kernel Image Loading + + Red Hat Enterprise Linux 9 + + The 'kernel.kexec_load_disabled' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Disable Kernel Image Loading + + Red Hat Enterprise Linux 9 + + The kernel 'kernel.kexec_load_disabled' parameter should be set to 1 in the system runtime. + + + + + + + + + Disable Kernel Image Loading + + Red Hat Enterprise Linux 9 + + The kernel 'kernel.kexec_load_disabled' parameter should be set to 1 in the system configuration. + + + + + + + + + + + + + + Restrict Exposed Kernel Pointer Addresses Access + + Red Hat Enterprise Linux 9 + + The 'kernel.kptr_restrict' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Restrict Exposed Kernel Pointer Addresses Access + + Red Hat Enterprise Linux 9 + + The kernel 'kernel.kptr_restrict' parameter should be set to 1 or 2 in the system runtime. + + + + + + + + + Restrict Exposed Kernel Pointer Addresses Access + + Red Hat Enterprise Linux 9 + + The kernel 'kernel.kptr_restrict' parameter should be set to 1 or 2 in the system configuration. + + + + + + + + + + + + + + Disable loading and unloading of kernel modules + + Red Hat Enterprise Linux 9 + + The 'kernel.modules_disabled' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Disable loading and unloading of kernel modules + + Red Hat Enterprise Linux 9 + + The kernel 'kernel.modules_disabled' parameter should be set to 1 in the system runtime. + + + + + + + + + Disable loading and unloading of kernel modules + + Red Hat Enterprise Linux 9 + + The kernel 'kernel.modules_disabled' parameter should be set to 1 in the system configuration. + + + + + + + + + + + + + + Kernel panic on oops + + Red Hat Enterprise Linux 9 + + The 'kernel.panic_on_oops' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Kernel panic on oops + + Red Hat Enterprise Linux 9 + + The kernel 'kernel.panic_on_oops' parameter should be set to 1 in the system runtime. + + + + + + + + + Kernel panic on oops + + Red Hat Enterprise Linux 9 + + The kernel 'kernel.panic_on_oops' parameter should be set to 1 in the system configuration. + + + + + + + + + + + + + + Limit CPU consumption of the Perf system + + Red Hat Enterprise Linux 9 + + The 'kernel.perf_cpu_time_max_percent' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Limit CPU consumption of the Perf system + + Red Hat Enterprise Linux 9 + + The kernel 'kernel.perf_cpu_time_max_percent' parameter should be set to 1 in the system runtime. + + + + + + + + + Limit CPU consumption of the Perf system + + Red Hat Enterprise Linux 9 + + The kernel 'kernel.perf_cpu_time_max_percent' parameter should be set to 1 in the system configuration. + + + + + + + + + + + + + + Limit sampling frequency of the Perf system + + Red Hat Enterprise Linux 9 + + The 'kernel.perf_event_max_sample_rate' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Limit sampling frequency of the Perf system + + Red Hat Enterprise Linux 9 + + The kernel 'kernel.perf_event_max_sample_rate' parameter should be set to 1 in the system runtime. + + + + + + + + + Limit sampling frequency of the Perf system + + Red Hat Enterprise Linux 9 + + The kernel 'kernel.perf_event_max_sample_rate' parameter should be set to 1 in the system configuration. + + + + + + + + + + + + + + Disallow kernel profiling by unprivileged users + + Red Hat Enterprise Linux 9 + + The 'kernel.perf_event_paranoid' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Disallow kernel profiling by unprivileged users + + Red Hat Enterprise Linux 9 + + The kernel 'kernel.perf_event_paranoid' parameter should be set to 2 in the system runtime. + + + + + + + + + Disallow kernel profiling by unprivileged users + + Red Hat Enterprise Linux 9 + + The kernel 'kernel.perf_event_paranoid' parameter should be set to 2 in the system configuration. + + + + + + + + + + + + + + Configure maximum number of process identifiers + + Red Hat Enterprise Linux 9 + + The 'kernel.pid_max' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Configure maximum number of process identifiers + + Red Hat Enterprise Linux 9 + + The kernel 'kernel.pid_max' parameter should be set to 65536 in the system runtime. + + + + + + + + + Configure maximum number of process identifiers + + Red Hat Enterprise Linux 9 + + The kernel 'kernel.pid_max' parameter should be set to 65536 in the system configuration. + + + + + + + + + + + + + + Enable Randomized Layout of Virtual Address Space + + Red Hat Enterprise Linux 9 + + The 'kernel.randomize_va_space' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Enable Randomized Layout of Virtual Address Space + + Red Hat Enterprise Linux 9 + + The kernel 'kernel.randomize_va_space' parameter should be set to 2 in the system runtime. + + + + + + + + + Enable Randomized Layout of Virtual Address Space + + Red Hat Enterprise Linux 9 + + The kernel 'kernel.randomize_va_space' parameter should be set to 2 in the system configuration. + + + + + + + + + + + + + + Disallow magic SysRq key + + Red Hat Enterprise Linux 9 + + The 'kernel.sysrq' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Disallow magic SysRq key + + Red Hat Enterprise Linux 9 + + The kernel 'kernel.sysrq' parameter should be set to 0 in the system runtime. + + + + + + + + + Disallow magic SysRq key + + Red Hat Enterprise Linux 9 + + The kernel 'kernel.sysrq' parameter should be set to 0 in the system configuration. + + + + + + + + + + + + + + Disable Access to Network bpf() Syscall From Unprivileged Processes + + Red Hat Enterprise Linux 9 + + The 'kernel.unprivileged_bpf_disabled' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Disable Access to Network bpf() Syscall From Unprivileged Processes + + Red Hat Enterprise Linux 9 + + The kernel 'kernel.unprivileged_bpf_disabled' parameter should be set to 1 in the system runtime. + + + + + + + + + Disable Access to Network bpf() Syscall From Unprivileged Processes + + Red Hat Enterprise Linux 9 + + The kernel 'kernel.unprivileged_bpf_disabled' parameter should be set to 1 in the system configuration. + + + + + + + + + + + + + + Disable Access to Network bpf() Syscall From Unprivileged Processes + + Red Hat Enterprise Linux 9 + + The 'kernel.unprivileged_bpf_disabled' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Disable Access to Network bpf() Syscall From Unprivileged Processes + + Red Hat Enterprise Linux 9 + + The kernel 'kernel.unprivileged_bpf_disabled' parameter should be set to 1 or 2 in the system runtime. + + + + + + + + + Disable Access to Network bpf() Syscall From Unprivileged Processes + + Red Hat Enterprise Linux 9 + + The kernel 'kernel.unprivileged_bpf_disabled' parameter should be set to 1 or 2 in the system configuration. + + + + + + + + + + + + + + + + + Restrict usage of ptrace to descendant processes + + Red Hat Enterprise Linux 9 + + The 'kernel.yama.ptrace_scope' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Restrict usage of ptrace to descendant processes + + Red Hat Enterprise Linux 9 + + The kernel 'kernel.yama.ptrace_scope' parameter should be set to 1 in the system runtime. + + + + + + + + + Restrict usage of ptrace to descendant processes + + Red Hat Enterprise Linux 9 + + The kernel 'kernel.yama.ptrace_scope' parameter should be set to 1 in the system configuration. + + + + + + + + + + + + + + Harden the operation of the BPF just-in-time compiler + + Red Hat Enterprise Linux 9 + + The 'net.core.bpf_jit_harden' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Harden the operation of the BPF just-in-time compiler + + Red Hat Enterprise Linux 9 + + The kernel 'net.core.bpf_jit_harden' parameter should be set to 2 in the system runtime. + + + + + + + + + Harden the operation of the BPF just-in-time compiler + + Red Hat Enterprise Linux 9 + + The kernel 'net.core.bpf_jit_harden' parameter should be set to 2 in the system configuration. + + + + + + + + + + + + + + Disable Accepting Packets Routed Between Local Interfaces + + Red Hat Enterprise Linux 9 + + The 'net.ipv4.conf.all.accept_local' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Disable Accepting Packets Routed Between Local Interfaces + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv4.conf.all.accept_local' parameter should be set to 0 in the system runtime. + + + + + + + + + Disable Accepting Packets Routed Between Local Interfaces + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv4.conf.all.accept_local' parameter should be set to 0 in the system configuration. + + + + + + + + + + + + + + Disable Accepting ICMP Redirects for All IPv4 Interfaces + + Red Hat Enterprise Linux 9 + + The 'net.ipv4.conf.all.accept_redirects' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Disable Accepting ICMP Redirects for All IPv4 Interfaces + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv4.conf.all.accept_redirects' parameter should be set to the appropriate value in the system runtime. + + + + + + + + + Disable Accepting ICMP Redirects for All IPv4 Interfaces + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv4.conf.all.accept_redirects' parameter should be set to the appropriate value in the system configuration. + + + + + + + + + + + + + + Disable Kernel Parameter for Accepting Source-Routed Packets on all IPv4 Interfaces + + Red Hat Enterprise Linux 9 + + The 'net.ipv4.conf.all.accept_source_route' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Disable Kernel Parameter for Accepting Source-Routed Packets on all IPv4 Interfaces + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv4.conf.all.accept_source_route' parameter should be set to the appropriate value in the system runtime. + + + + + + + + + Disable Kernel Parameter for Accepting Source-Routed Packets on all IPv4 Interfaces + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv4.conf.all.accept_source_route' parameter should be set to the appropriate value in the system configuration. + + + + + + + + + + + + + + Configure ARP filtering for All IPv4 Interfaces + + Red Hat Enterprise Linux 9 + + The 'net.ipv4.conf.all.arp_filter' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Configure ARP filtering for All IPv4 Interfaces + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv4.conf.all.arp_filter' parameter should be set to the appropriate value in the system runtime. + + + + + + + + + Configure ARP filtering for All IPv4 Interfaces + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv4.conf.all.arp_filter' parameter should be set to the appropriate value in the system configuration. + + + + + + + + + + + + + + Configure Response Mode of ARP Requests for All IPv4 Interfaces + + Red Hat Enterprise Linux 9 + + The 'net.ipv4.conf.all.arp_ignore' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Configure Response Mode of ARP Requests for All IPv4 Interfaces + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv4.conf.all.arp_ignore' parameter should be set to the appropriate value in the system runtime. + + + + + + + + + Configure Response Mode of ARP Requests for All IPv4 Interfaces + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv4.conf.all.arp_ignore' parameter should be set to the appropriate value in the system configuration. + + + + + + + + + + + + + + Drop Gratuitious ARP frames on All IPv4 Interfaces + + Red Hat Enterprise Linux 9 + + The 'net.ipv4.conf.all.drop_gratuitous_arp' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Drop Gratuitious ARP frames on All IPv4 Interfaces + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv4.conf.all.drop_gratuitous_arp' parameter should be set to 1 in the system runtime. + + + + + + + + + Drop Gratuitious ARP frames on All IPv4 Interfaces + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv4.conf.all.drop_gratuitous_arp' parameter should be set to 1 in the system configuration. + + + + + + + + + + + + + + Enable Kernel Parameter to Log Martian Packets on all IPv4 Interfaces + + Red Hat Enterprise Linux 9 + + The 'net.ipv4.conf.all.log_martians' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Enable Kernel Parameter to Log Martian Packets on all IPv4 Interfaces + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv4.conf.all.log_martians' parameter should be set to the appropriate value in the system runtime. + + + + + + + + + Enable Kernel Parameter to Log Martian Packets on all IPv4 Interfaces + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv4.conf.all.log_martians' parameter should be set to the appropriate value in the system configuration. + + + + + + + + + + + + + + Prevent Routing External Traffic to Local Loopback on All IPv4 Interfaces + + Red Hat Enterprise Linux 9 + + The 'net.ipv4.conf.all.route_localnet' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Prevent Routing External Traffic to Local Loopback on All IPv4 Interfaces + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv4.conf.all.route_localnet' parameter should be set to 0 in the system runtime. + + + + + + + + + Prevent Routing External Traffic to Local Loopback on All IPv4 Interfaces + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv4.conf.all.route_localnet' parameter should be set to 0 in the system configuration. + + + + + + + + + + + + + + Enable Kernel Parameter to Use Reverse Path Filtering on all IPv4 Interfaces + + Red Hat Enterprise Linux 9 + + The 'net.ipv4.conf.all.rp_filter' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Enable Kernel Parameter to Use Reverse Path Filtering on all IPv4 Interfaces + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv4.conf.all.rp_filter' parameter should be set to 1 or 2 in the system runtime. + + + + + + + + + Enable Kernel Parameter to Use Reverse Path Filtering on all IPv4 Interfaces + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv4.conf.all.rp_filter' parameter should be set to 1 or 2 in the system configuration. + + + + + + + + + + + + + + Disable Kernel Parameter for Accepting Secure ICMP Redirects on all IPv4 Interfaces + + Red Hat Enterprise Linux 9 + + The 'net.ipv4.conf.all.secure_redirects' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Disable Kernel Parameter for Accepting Secure ICMP Redirects on all IPv4 Interfaces + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv4.conf.all.secure_redirects' parameter should be set to the appropriate value in the system runtime. + + + + + + + + + Disable Kernel Parameter for Accepting Secure ICMP Redirects on all IPv4 Interfaces + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv4.conf.all.secure_redirects' parameter should be set to the appropriate value in the system configuration. + + + + + + + + + + + + + + Disable Kernel Parameter for Sending ICMP Redirects on all IPv4 Interfaces + + Red Hat Enterprise Linux 9 + + The 'net.ipv4.conf.all.send_redirects' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Disable Kernel Parameter for Sending ICMP Redirects on all IPv4 Interfaces + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv4.conf.all.send_redirects' parameter should be set to 0 in the system runtime. + + + + + + + + + Disable Kernel Parameter for Sending ICMP Redirects on all IPv4 Interfaces + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv4.conf.all.send_redirects' parameter should be set to 0 in the system configuration. + + + + + + + + + + + + + + Configure Sending and Accepting Shared Media Redirects for All IPv4 Interfaces + + Red Hat Enterprise Linux 9 + + The 'net.ipv4.conf.all.shared_media' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Configure Sending and Accepting Shared Media Redirects for All IPv4 Interfaces + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv4.conf.all.shared_media' parameter should be set to the appropriate value in the system runtime. + + + + + + + + + Configure Sending and Accepting Shared Media Redirects for All IPv4 Interfaces + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv4.conf.all.shared_media' parameter should be set to the appropriate value in the system configuration. + + + + + + + + + + + + + + Disable Kernel Parameter for Accepting ICMP Redirects by Default on IPv4 Interfaces + + Red Hat Enterprise Linux 9 + + The 'net.ipv4.conf.default.accept_redirects' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Disable Kernel Parameter for Accepting ICMP Redirects by Default on IPv4 Interfaces + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv4.conf.default.accept_redirects' parameter should be set to the appropriate value in the system runtime. + + + + + + + + + Disable Kernel Parameter for Accepting ICMP Redirects by Default on IPv4 Interfaces + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv4.conf.default.accept_redirects' parameter should be set to the appropriate value in the system configuration. + + + + + + + + + + + + + + Disable Kernel Parameter for Accepting Source-Routed Packets on IPv4 Interfaces by Default + + Red Hat Enterprise Linux 9 + + The 'net.ipv4.conf.default.accept_source_route' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Disable Kernel Parameter for Accepting Source-Routed Packets on IPv4 Interfaces by Default + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv4.conf.default.accept_source_route' parameter should be set to the appropriate value in the system runtime. + + + + + + + + + Disable Kernel Parameter for Accepting Source-Routed Packets on IPv4 Interfaces by Default + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv4.conf.default.accept_source_route' parameter should be set to the appropriate value in the system configuration. + + + + + + + + + + + + + + Enable Kernel Paremeter to Log Martian Packets on all IPv4 Interfaces by Default + + Red Hat Enterprise Linux 9 + + The 'net.ipv4.conf.default.log_martians' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Enable Kernel Paremeter to Log Martian Packets on all IPv4 Interfaces by Default + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv4.conf.default.log_martians' parameter should be set to the appropriate value in the system runtime. + + + + + + + + + Enable Kernel Paremeter to Log Martian Packets on all IPv4 Interfaces by Default + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv4.conf.default.log_martians' parameter should be set to the appropriate value in the system configuration. + + + + + + + + + + + + + + Enable Kernel Parameter to Use Reverse Path Filtering on all IPv4 Interfaces by Default + + Red Hat Enterprise Linux 9 + + The 'net.ipv4.conf.default.rp_filter' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Enable Kernel Parameter to Use Reverse Path Filtering on all IPv4 Interfaces by Default + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv4.conf.default.rp_filter' parameter should be set to the appropriate value in the system runtime. + + + + + + + + + Enable Kernel Parameter to Use Reverse Path Filtering on all IPv4 Interfaces by Default + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv4.conf.default.rp_filter' parameter should be set to the appropriate value in the system configuration. + + + + + + + + + + + + + + Configure Kernel Parameter for Accepting Secure Redirects By Default + + Red Hat Enterprise Linux 9 + + The 'net.ipv4.conf.default.secure_redirects' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Configure Kernel Parameter for Accepting Secure Redirects By Default + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv4.conf.default.secure_redirects' parameter should be set to the appropriate value in the system runtime. + + + + + + + + + Configure Kernel Parameter for Accepting Secure Redirects By Default + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv4.conf.default.secure_redirects' parameter should be set to the appropriate value in the system configuration. + + + + + + + + + + + + + + Disable Kernel Parameter for Sending ICMP Redirects on all IPv4 Interfaces by Default + + Red Hat Enterprise Linux 9 + + The 'net.ipv4.conf.default.send_redirects' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Disable Kernel Parameter for Sending ICMP Redirects on all IPv4 Interfaces by Default + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv4.conf.default.send_redirects' parameter should be set to 0 in the system runtime. + + + + + + + + + Disable Kernel Parameter for Sending ICMP Redirects on all IPv4 Interfaces by Default + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv4.conf.default.send_redirects' parameter should be set to 0 in the system configuration. + + + + + + + + + + + + + + Configure Sending and Accepting Shared Media Redirects by Default + + Red Hat Enterprise Linux 9 + + The 'net.ipv4.conf.default.shared_media' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Configure Sending and Accepting Shared Media Redirects by Default + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv4.conf.default.shared_media' parameter should be set to the appropriate value in the system runtime. + + + + + + + + + Configure Sending and Accepting Shared Media Redirects by Default + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv4.conf.default.shared_media' parameter should be set to the appropriate value in the system configuration. + + + + + + + + + + + + + + Enable Kernel Parameter to Ignore ICMP Broadcast Echo Requests on IPv4 Interfaces + + Red Hat Enterprise Linux 9 + + The 'net.ipv4.icmp_echo_ignore_broadcasts' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Enable Kernel Parameter to Ignore ICMP Broadcast Echo Requests on IPv4 Interfaces + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv4.icmp_echo_ignore_broadcasts' parameter should be set to the appropriate value in the system runtime. + + + + + + + + + Enable Kernel Parameter to Ignore ICMP Broadcast Echo Requests on IPv4 Interfaces + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv4.icmp_echo_ignore_broadcasts' parameter should be set to the appropriate value in the system configuration. + + + + + + + + + + + + + + Enable Kernel Parameter to Ignore Bogus ICMP Error Responses on IPv4 Interfaces + + Red Hat Enterprise Linux 9 + + The 'net.ipv4.icmp_ignore_bogus_error_responses' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Enable Kernel Parameter to Ignore Bogus ICMP Error Responses on IPv4 Interfaces + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv4.icmp_ignore_bogus_error_responses' parameter should be set to the appropriate value in the system runtime. + + + + + + + + + Enable Kernel Parameter to Ignore Bogus ICMP Error Responses on IPv4 Interfaces + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv4.icmp_ignore_bogus_error_responses' parameter should be set to the appropriate value in the system configuration. + + + + + + + + + + + + + + Disable Kernel Parameter for IP Forwarding on IPv4 Interfaces + + Red Hat Enterprise Linux 9 + + The 'net.ipv4.ip_forward' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Disable Kernel Parameter for IP Forwarding on IPv4 Interfaces + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv4.ip_forward' parameter should be set to 0 in the system runtime. + + + + + + + + + Disable Kernel Parameter for IP Forwarding on IPv4 Interfaces + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv4.ip_forward' parameter should be set to 0 in the system configuration. + + + + + + + + + + + + + + Set Kernel Parameter to Increase Local Port Range + + Red Hat Enterprise Linux 9 + + The 'net.ipv4.ip_local_port_range' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Set Kernel Parameter to Increase Local Port Range + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv4.ip_local_port_range' parameter should be set to 32768 65535 in the system runtime. + + + + + + + + + Set Kernel Parameter to Increase Local Port Range + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv4.ip_local_port_range' parameter should be set to 32768 65535 in the system configuration. + + + + + + + + + + + + + + Configure Kernel to Rate Limit Sending of Duplicate TCP Acknowledgments + + Red Hat Enterprise Linux 9 + + The 'net.ipv4.tcp_invalid_ratelimit' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Configure Kernel to Rate Limit Sending of Duplicate TCP Acknowledgments + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv4.tcp_invalid_ratelimit' parameter should be set to the appropriate value in the system runtime. + + + + + + + + + Configure Kernel to Rate Limit Sending of Duplicate TCP Acknowledgments + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv4.tcp_invalid_ratelimit' parameter should be set to the appropriate value in the system configuration. + + + + + + + + + + + + + + Enable Kernel Parameter to Use TCP RFC 1337 on IPv4 Interfaces + + Red Hat Enterprise Linux 9 + + The 'net.ipv4.tcp_rfc1337' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Enable Kernel Parameter to Use TCP RFC 1337 on IPv4 Interfaces + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv4.tcp_rfc1337' parameter should be set to the appropriate value in the system runtime. + + + + + + + + + Enable Kernel Parameter to Use TCP RFC 1337 on IPv4 Interfaces + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv4.tcp_rfc1337' parameter should be set to the appropriate value in the system configuration. + + + + + + + + + + + + + + Enable Kernel Parameter to Use TCP Syncookies on Network Interfaces + + Red Hat Enterprise Linux 9 + + The 'net.ipv4.tcp_syncookies' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Enable Kernel Parameter to Use TCP Syncookies on Network Interfaces + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv4.tcp_syncookies' parameter should be set to the appropriate value in the system runtime. + + + + + + + + + Enable Kernel Parameter to Use TCP Syncookies on Network Interfaces + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv4.tcp_syncookies' parameter should be set to the appropriate value in the system configuration. + + + + + + + + + + + + + + Configure Accepting Router Advertisements on All IPv6 Interfaces + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv6.conf.all.accept_ra' parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + + + + Configure Accepting Router Advertisements on All IPv6 Interfaces + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv6.conf.all.accept_ra' parameter should be set to the appropriate value in the system runtime. + + + + + + + + + Configure Accepting Router Advertisements on All IPv6 Interfaces + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv6.conf.all.accept_ra' parameter should be set to the appropriate value in the system configuration. + + + + + + + + + + + + + + Configure Accepting Default Router in Router Advertisements on All IPv6 Interfaces + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv6.conf.all.accept_ra_defrtr' parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + + + + Configure Accepting Default Router in Router Advertisements on All IPv6 Interfaces + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv6.conf.all.accept_ra_defrtr' parameter should be set to the appropriate value in the system runtime. + + + + + + + + + Configure Accepting Default Router in Router Advertisements on All IPv6 Interfaces + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv6.conf.all.accept_ra_defrtr' parameter should be set to the appropriate value in the system configuration. + + + + + + + + + + + + + + Configure Accepting Prefix Information in Router Advertisements on All IPv6 Interfaces + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv6.conf.all.accept_ra_pinfo' parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + + + + Configure Accepting Prefix Information in Router Advertisements on All IPv6 Interfaces + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv6.conf.all.accept_ra_pinfo' parameter should be set to the appropriate value in the system runtime. + + + + + + + + + Configure Accepting Prefix Information in Router Advertisements on All IPv6 Interfaces + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv6.conf.all.accept_ra_pinfo' parameter should be set to the appropriate value in the system configuration. + + + + + + + + + + + + + + Configure Accepting Router Preference in Router Advertisements on All IPv6 Interfaces + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv6.conf.all.accept_ra_rtr_pref' parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + + + + Configure Accepting Router Preference in Router Advertisements on All IPv6 Interfaces + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv6.conf.all.accept_ra_rtr_pref' parameter should be set to the appropriate value in the system runtime. + + + + + + + + + Configure Accepting Router Preference in Router Advertisements on All IPv6 Interfaces + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv6.conf.all.accept_ra_rtr_pref' parameter should be set to the appropriate value in the system configuration. + + + + + + + + + + + + + + Disable Accepting ICMP Redirects for All IPv6 Interfaces + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv6.conf.all.accept_redirects' parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + + + + Disable Accepting ICMP Redirects for All IPv6 Interfaces + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv6.conf.all.accept_redirects' parameter should be set to the appropriate value in the system runtime. + + + + + + + + + Disable Accepting ICMP Redirects for All IPv6 Interfaces + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv6.conf.all.accept_redirects' parameter should be set to the appropriate value in the system configuration. + + + + + + + + + + + + + + Disable Kernel Parameter for Accepting Source-Routed Packets on all IPv6 Interfaces + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv6.conf.all.accept_source_route' parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + + + + Disable Kernel Parameter for Accepting Source-Routed Packets on all IPv6 Interfaces + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv6.conf.all.accept_source_route' parameter should be set to the appropriate value in the system runtime. + + + + + + + + + Disable Kernel Parameter for Accepting Source-Routed Packets on all IPv6 Interfaces + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv6.conf.all.accept_source_route' parameter should be set to the appropriate value in the system configuration. + + + + + + + + + + + + + + Configure Auto Configuration on All IPv6 Interfaces + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv6.conf.all.autoconf' parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + + + + Configure Auto Configuration on All IPv6 Interfaces + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv6.conf.all.autoconf' parameter should be set to the appropriate value in the system runtime. + + + + + + + + + Configure Auto Configuration on All IPv6 Interfaces + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv6.conf.all.autoconf' parameter should be set to the appropriate value in the system configuration. + + + + + + + + + + + + + + Disable IPv6 Addressing on All IPv6 Interfaces + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv6.conf.all.disable_ipv6' parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + + + Disable IPv6 Addressing on All IPv6 Interfaces + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv6.conf.all.disable_ipv6' parameter should be set to 1 in the system runtime. + + + + + + + + + Disable IPv6 Addressing on All IPv6 Interfaces + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv6.conf.all.disable_ipv6' parameter should be set to 1 in the system configuration. + + + + + + + + + + + + + + Disable Kernel Parameter for IPv6 Forwarding + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv6.conf.all.forwarding' parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + + + + Disable Kernel Parameter for IPv6 Forwarding + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv6.conf.all.forwarding' parameter should be set to the appropriate value in the system runtime. + + + + + + + + + Disable Kernel Parameter for IPv6 Forwarding + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv6.conf.all.forwarding' parameter should be set to the appropriate value in the system configuration. + + + + + + + + + + + + + + Configure Maximum Number of Autoconfigured Addresses on All IPv6 Interfaces + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv6.conf.all.max_addresses' parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + + + + Configure Maximum Number of Autoconfigured Addresses on All IPv6 Interfaces + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv6.conf.all.max_addresses' parameter should be set to the appropriate value in the system runtime. + + + + + + + + + Configure Maximum Number of Autoconfigured Addresses on All IPv6 Interfaces + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv6.conf.all.max_addresses' parameter should be set to the appropriate value in the system configuration. + + + + + + + + + + + + + + Configure Denying Router Solicitations on All IPv6 Interfaces + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv6.conf.all.router_solicitations' parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + + + + Configure Denying Router Solicitations on All IPv6 Interfaces + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv6.conf.all.router_solicitations' parameter should be set to the appropriate value in the system runtime. + + + + + + + + + Configure Denying Router Solicitations on All IPv6 Interfaces + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv6.conf.all.router_solicitations' parameter should be set to the appropriate value in the system configuration. + + + + + + + + + + + + + + Disable Accepting Router Advertisements on all IPv6 Interfaces by Default + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv6.conf.default.accept_ra' parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + + + + Disable Accepting Router Advertisements on all IPv6 Interfaces by Default + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv6.conf.default.accept_ra' parameter should be set to the appropriate value in the system runtime. + + + + + + + + + Disable Accepting Router Advertisements on all IPv6 Interfaces by Default + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv6.conf.default.accept_ra' parameter should be set to the appropriate value in the system configuration. + + + + + + + + + + + + + + Configure Accepting Default Router in Router Advertisements on All IPv6 Interfaces By Default + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv6.conf.default.accept_ra_defrtr' parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + + + + Configure Accepting Default Router in Router Advertisements on All IPv6 Interfaces By Default + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv6.conf.default.accept_ra_defrtr' parameter should be set to the appropriate value in the system runtime. + + + + + + + + + Configure Accepting Default Router in Router Advertisements on All IPv6 Interfaces By Default + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv6.conf.default.accept_ra_defrtr' parameter should be set to the appropriate value in the system configuration. + + + + + + + + + + + + + + Configure Accepting Prefix Information in Router Advertisements on All IPv6 Interfaces By Default + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv6.conf.default.accept_ra_pinfo' parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + + + + Configure Accepting Prefix Information in Router Advertisements on All IPv6 Interfaces By Default + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv6.conf.default.accept_ra_pinfo' parameter should be set to the appropriate value in the system runtime. + + + + + + + + + Configure Accepting Prefix Information in Router Advertisements on All IPv6 Interfaces By Default + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv6.conf.default.accept_ra_pinfo' parameter should be set to the appropriate value in the system configuration. + + + + + + + + + + + + + + Configure Accepting Router Preference in Router Advertisements on All IPv6 Interfaces By Default + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv6.conf.default.accept_ra_rtr_pref' parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + + + + Configure Accepting Router Preference in Router Advertisements on All IPv6 Interfaces By Default + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv6.conf.default.accept_ra_rtr_pref' parameter should be set to the appropriate value in the system runtime. + + + + + + + + + Configure Accepting Router Preference in Router Advertisements on All IPv6 Interfaces By Default + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv6.conf.default.accept_ra_rtr_pref' parameter should be set to the appropriate value in the system configuration. + + + + + + + + + + + + + + Disable Kernel Parameter for Accepting ICMP Redirects by Default on IPv6 Interfaces + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv6.conf.default.accept_redirects' parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + + + + Disable Kernel Parameter for Accepting ICMP Redirects by Default on IPv6 Interfaces + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv6.conf.default.accept_redirects' parameter should be set to the appropriate value in the system runtime. + + + + + + + + + Disable Kernel Parameter for Accepting ICMP Redirects by Default on IPv6 Interfaces + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv6.conf.default.accept_redirects' parameter should be set to the appropriate value in the system configuration. + + + + + + + + + + + + + + Disable Kernel Parameter for Accepting Source-Routed Packets on IPv6 Interfaces by Default + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv6.conf.default.accept_source_route' parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + + + + Disable Kernel Parameter for Accepting Source-Routed Packets on IPv6 Interfaces by Default + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv6.conf.default.accept_source_route' parameter should be set to the appropriate value in the system runtime. + + + + + + + + + Disable Kernel Parameter for Accepting Source-Routed Packets on IPv6 Interfaces by Default + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv6.conf.default.accept_source_route' parameter should be set to the appropriate value in the system configuration. + + + + + + + + + + + + + + Configure Auto Configuration on All IPv6 Interfaces By Default + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv6.conf.default.autoconf' parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + + + + Configure Auto Configuration on All IPv6 Interfaces By Default + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv6.conf.default.autoconf' parameter should be set to the appropriate value in the system runtime. + + + + + + + + + Configure Auto Configuration on All IPv6 Interfaces By Default + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv6.conf.default.autoconf' parameter should be set to the appropriate value in the system configuration. + + + + + + + + + + + + + + Disable IPv6 Addressing on IPv6 Interfaces by Default + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv6.conf.default.disable_ipv6' parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + + + Disable IPv6 Addressing on IPv6 Interfaces by Default + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv6.conf.default.disable_ipv6' parameter should be set to 1 in the system runtime. + + + + + + + + + Disable IPv6 Addressing on IPv6 Interfaces by Default + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv6.conf.default.disable_ipv6' parameter should be set to 1 in the system configuration. + + + + + + + + + + + + + + Configure Maximum Number of Autoconfigured Addresses on All IPv6 Interfaces By Default + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv6.conf.default.max_addresses' parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + + + + Configure Maximum Number of Autoconfigured Addresses on All IPv6 Interfaces By Default + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv6.conf.default.max_addresses' parameter should be set to the appropriate value in the system runtime. + + + + + + + + + Configure Maximum Number of Autoconfigured Addresses on All IPv6 Interfaces By Default + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv6.conf.default.max_addresses' parameter should be set to the appropriate value in the system configuration. + + + + + + + + + + + + + + Configure Denying Router Solicitations on All IPv6 Interfaces By Default + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv6.conf.default.router_solicitations' parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + + + + Configure Denying Router Solicitations on All IPv6 Interfaces By Default + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv6.conf.default.router_solicitations' parameter should be set to the appropriate value in the system runtime. + + + + + + + + + Configure Denying Router Solicitations on All IPv6 Interfaces By Default + + Red Hat Enterprise Linux 9 + + The kernel 'net.ipv6.conf.default.router_solicitations' parameter should be set to the appropriate value in the system configuration. + + + + + + + + + + + + + + Disable the use of user namespaces + + Red Hat Enterprise Linux 9 + + The 'user.max_user_namespaces' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Disable the use of user namespaces + + Red Hat Enterprise Linux 9 + + The kernel 'user.max_user_namespaces' parameter should be set to 0 in the system runtime. + + + + + + + + + Disable the use of user namespaces + + Red Hat Enterprise Linux 9 + + The kernel 'user.max_user_namespaces' parameter should be set to 0 in the system configuration. + + + + + + + + + + + + + + Prevent applications from mapping low portion of virtual memory + + Red Hat Enterprise Linux 9 + + The 'vm.mmap_min_addr' kernel parameter should be set to the appropriate value in both system configuration and system runtime. + + + + + + + + + + + Prevent applications from mapping low portion of virtual memory + + Red Hat Enterprise Linux 9 + + The kernel 'vm.mmap_min_addr' parameter should be set to 65536 in the system runtime. + + + + + + + + + Prevent applications from mapping low portion of virtual memory + + Red Hat Enterprise Linux 9 + + The kernel 'vm.mmap_min_addr' parameter should be set to 65536 in the system configuration. + + + + + + + + + + + + + + Enable dnf-automatic Timer + + Red Hat Enterprise Linux 9 + + The dnf-automatic timer should be enabled if possible. + + + + + + + + + + + + + + Enable Auditing to Start Prior to the Audit Daemon in zIPL + + Red Hat Enterprise Linux 9 + + Ensure audit=1 option is configured in the 'options' line in /boot/loader/entries/*.conf. Make sure that newly installed kernels will retain this option, it should be configured in /etc/kernel/cmdline as well. + + + + + + + + + + + Extend Audit Backlog Limit for the Audit Daemon in zIPL + + Red Hat Enterprise Linux 9 + + Ensure audit_backlog_limit=8192 option is configured in the 'options' line in /boot/loader/entries/*.conf. Make sure that newly installed kernels will retain this option, it should be configured in /etc/kernel/cmdline as well. + + + + + + + + + + + Configure kernel to zero out memory before allocation in zIPL + + Red Hat Enterprise Linux 9 + + Ensure init_on_alloc=1 option is configured in the 'options' line in /boot/loader/entries/*.conf. Make sure that newly installed kernels will retain this option, it should be configured in /etc/kernel/cmdline as well. + + + + + + + + + + + Enable randomization of the page allocator in zIPL + + Red Hat Enterprise Linux 9 + + Ensure page_alloc.shuffle=1 option is configured in the 'options' line in /boot/loader/entries/*.conf. Make sure that newly installed kernels will retain this option, it should be configured in /etc/kernel/cmdline as well. + + + + + + + + + + + Enable page allocator poisoning in zIPL + + Red Hat Enterprise Linux 9 + + Ensure page_poison=1 option is configured in the 'options' line in /boot/loader/entries/*.conf. Make sure that newly installed kernels will retain this option, it should be configured in /etc/kernel/cmdline as well. + + + + + + + + + + + Enable SLUB/SLAB allocator poisoning in zIPL + + Red Hat Enterprise Linux 9 + + Ensure slub_debug=P option is configured in the 'options' line in /boot/loader/entries/*.conf. Make sure that newly installed kernels will retain this option, it should be configured in /etc/kernel/cmdline as well. + + + + + + + + + + + Disable vsyscalls in zIPL + + Red Hat Enterprise Linux 9 + + Ensure vsyscall=none option is configured in the 'options' line in /boot/loader/entries/*.conf. Make sure that newly installed kernels will retain this option, it should be configured in /etc/kernel/cmdline as well. + + + + + + + + + + + Check pam_faillock Existence in system-auth + + Red Hat Enterprise Linux 9 + + Check that pam_faillock.so exists in system-auth + + + + + + + + + Check pam_pwquality Existence in system-auth + + Red Hat Enterprise Linux 9 + + Check that pam_pwquality.so exists in system-auth + + + + + + + + + Record Any Attempts to Run semanage + + Red Hat Enterprise Linux 9 + + Test if auditctl is in use for audit rules. + + + + + + + + + Record Any Attempts to Run semanage + + Red Hat Enterprise Linux 9 + + Test if augenrules is enabled for audit rules. + + + + + + + + + Record Events that Modify the System's Network Environment + + Red Hat Enterprise Linux 9 + + The network environment should not be modified by anything other than + administrator action. Any change to network parameters should be audited. + + + + + + + + + + + + + + + + + + + + + + + + Record Events that Modify the System's Network Environment + + Red Hat Enterprise Linux 9 + + The network environment should not be modified by anything other than + administrator action. Any change to network parameters should be audited. + + + + + + + + + + + + + + + + + + + + + + + + 'log_file' Not Set In /etc/audit/auditd.conf + + Red Hat Enterprise Linux 9 + + Verify 'log_file' is not set in /etc/audit/auditd.conf. + + + + + + + + + 'log_group' Not Set To 'root' In /etc/audit/auditd.conf + + Red Hat Enterprise Linux 9 + + Verify 'log_group' is not set to 'root' in + /etc/audit/auditd.conf. + + + + + + + + + + Verify GRUB_DISABLE_RECOVERY Set to true + + Red Hat Enterprise Linux 9 + + GRUB_DISABLE_RECOVERY set to 'true' in + /etc/default/grub + + + + + + + + + Specify Multiple Remote chronyd NTP Servers for Time Data + + Red Hat Enterprise Linux 9 + + Multiple chronyd NTP Servers for time synchronization should be specified. + + + + + + + + + GRUB_CMDLINE_LINUX_DEFAULT existance check + + Red Hat Enterprise Linux 9 + + Check if GRUB_CMDLINE_LINUX_DEFAULT exists in /etc/default/grub. + + + + + + + + + Use $kernelopts in /boot/loader/entries/*.conf + + Red Hat Enterprise Linux 9 + + Ensure that grubenv-defined kernel options are referenced in individual boot loader entries + + + + + + + + + Install McAfee Host-Based Intrusion Detection Software (HBSS) + + Red Hat Enterprise Linux 9 + + McAfee Host-Based Intrusion Detection Software (HBSS) software + should be installed. + + + + + + + + + + + + Alibaba Cloud Linux 2 + + Red Hat Enterprise Linux 9 + + + The operating system installed on the system is Alibaba Cloud Linux 2 + + + + + + + + + + Alibaba Cloud Linux 3 + + Red Hat Enterprise Linux 9 + + + The operating system installed on the system is Alibaba Cloud Linux 3 + + + + + + + + + + CentOS 7 + + Red Hat Enterprise Linux 9 + + + The operating system installed on the system is + CentOS 7 + + + + + + + + + + CentOS 8 + + Red Hat Enterprise Linux 9 + + + The operating system installed on the system is + CentOS 8 + + + + + + + + + + + CentOS Stream 9 + + Red Hat Enterprise Linux 9 + + + The operating system installed on the system is + CentOS Stream 9 + + + + + + + + + + + Debian + + Red Hat Enterprise Linux 9 + + The operating system installed is a Debian System + + + + + + + + + + Debian Linux 10 + + Red Hat Enterprise Linux 9 + + + The operating system installed on the system is Debian 10 + + + + + + + + + + Debian Linux 11 + + Red Hat Enterprise Linux 9 + + + The operating system installed on the system is Debian 11 + + + + + + + + + + Debian 9 + + Red Hat Enterprise Linux 9 + + + The operating system installed on the system is Debian 9 + + + + + + + + + + Installed operating system is Fedora + + Red Hat Enterprise Linux 9 + + + + + + The operating system installed on the system is Fedora + + + + + + + + + + + Oracle Linux 7 + + Red Hat Enterprise Linux 9 + + + The operating system installed on the system is + Oracle Linux 7 + + + + + + + + + + + + Oracle Linux 8 + + Red Hat Enterprise Linux 9 + + + The operating system installed on the system is + Oracle Linux 8 + + + + + + + + + + + + Oracle Linux 9 + + Red Hat Enterprise Linux 9 + + + The operating system installed on the system is + Oracle Linux 9 + + + + + + + + + + + + openSUSE + + Red Hat Enterprise Linux 9 + + The operating system installed on the system is openSUSE. + + + + + + + + + + openSUSE Leap 15 + + Red Hat Enterprise Linux 9 + + + The operating system installed on the system is openSUSE Leap 15. + + + + + + + + + + openSUSE Leap 42 + + Red Hat Enterprise Linux 9 + + + + + The operating system installed on the system is openSUSE Leap 42. + + + + + + + + + + Installed operating system is part of the Unix family + + Red Hat Enterprise Linux 9 + + The operating system installed on the system is part of the Unix OS family + + + + + + + + + Red Hat Enterprise Linux CoreOS + + Red Hat Enterprise Linux 9 + + + The operating system installed on the system is + Red Hat Enterprise Linux CoreOS release 4 + + + + + + + + + + + + Red Hat Enterprise Linux 7 + + Red Hat Enterprise Linux 9 + + + The operating system installed on the system is + Red Hat Enterprise Linux 7 + + + + + + + + + + + + + + + + + + + Red Hat Enterprise Linux 8 + + Red Hat Enterprise Linux 9 + + + The operating system installed on the system is + Red Hat Enterprise Linux 8 + + + + + + + + + + + + + + + + Red Hat Enterprise Linux 8.0 + + Red Hat Enterprise Linux 9 + + + The operating system installed on the system is Red Hat Enterprise Linux 8.0 + + + + + + + + + Red Hat Enterprise Linux 8.1 + + Red Hat Enterprise Linux 9 + + + The operating system installed on the system is Red Hat Enterprise Linux 8.1 + + + + + + + + + Red Hat Enterprise Linux 8.2 + + Red Hat Enterprise Linux 9 + + + The operating system installed on the system is Red Hat Enterprise Linux 8.2 + + + + + + + + + Red Hat Enterprise Linux 8.3 + + Red Hat Enterprise Linux 9 + + + The operating system installed on the system is Red Hat Enterprise Linux 8.3 + + + + + + + + + Red Hat Enterprise Linux 8.4 + + Red Hat Enterprise Linux 9 + + + The operating system installed on the system is Red Hat Enterprise Linux 8.4 + + + + + + + + + Red Hat Enterprise Linux 8.5 + + Red Hat Enterprise Linux 9 + + + The operating system installed on the system is Red Hat Enterprise Linux 8.5 + + + + + + + + + Red Hat Enterprise Linux 8.6 + + Red Hat Enterprise Linux 9 + + + The operating system installed on the system is Red Hat Enterprise Linux 8.6 + + + + + + + + + Red Hat Enterprise Linux 8.7 + + Red Hat Enterprise Linux 9 + + + The operating system installed on the system is Red Hat Enterprise Linux 8.7 + + + + + + + + + Red Hat Enterprise Linux 8.8 + + Red Hat Enterprise Linux 9 + + + The operating system installed on the system is Red Hat Enterprise Linux 8.8 + + + + + + + + + Red Hat Enterprise Linux 8.9 + + Red Hat Enterprise Linux 9 + + + The operating system installed on the system is Red Hat Enterprise Linux 8.9 + + + + + + + + + Red Hat Enterprise Linux 8.10 + + Red Hat Enterprise Linux 9 + + + The operating system installed on the system is Red Hat Enterprise Linux 8.10 + + + + + + + + + Red Hat Enterprise Linux 9 + + Red Hat Enterprise Linux 9 + + + The operating system installed on the system is + Red Hat Enterprise Linux 9 + + + + + + + + + + + + + + + + Red Hat Virtualization 4 + + Red Hat Enterprise Linux 9 + + + The operating system installed on the system is + Red Hat Virtualization Host 4.4+ or Red Hat Enterprise Host. + + + + + + + + + + Scientific Linux 7 + + Red Hat Enterprise Linux 9 + + + The operating system installed on the system is + Scientific Linux 7 + + + + + + + + + + SUSE Linux Enterprise 12 + + Red Hat Enterprise Linux 9 + + + + The operating system installed on the system is + SUSE Linux Enterprise 12. + + + + + + + + + + + + + + SUSE Linux Enterprise 15 + + Red Hat Enterprise Linux 9 + + + + The operating system installed on the system is + SUSE Linux Enterprise 15. + + + + + + + + + + + + + + Ubuntu + + Red Hat Enterprise Linux 9 + + The operating system installed is an Ubuntu System + + + + + + + + + + + Ubuntu 1604 + + Red Hat Enterprise Linux 9 + + + The operating system installed on the system is Ubuntu 1604 + + + + + + + + + + Ubuntu 1804 + + Red Hat Enterprise Linux 9 + + + The operating system installed on the system is Ubuntu 1804 + + + + + + + + + + Ubuntu 2004 + + Red Hat Enterprise Linux 9 + + + The operating system installed on the system is Ubuntu 2004 + + + + + + + + + + Ubuntu 2204 + + Red Hat Enterprise Linux 9 + + + The operating system installed on the system is Ubuntu 2204 + + + + + + + + + + UnionTech OS Server 20 + + Red Hat Enterprise Linux 9 + + + The operating system installed on the system is UnionTech OS Server 20 + + + + + + + + + + Red Hat Virtualization 4 + + Red Hat Enterprise Linux 9 + + + The application installed installed on the system is + Red Hat Virtualization 4. + + + + + + + + + + Package audit is installed + + Red Hat Enterprise Linux 9 + + Checks if package audit is installed. + + + + + + + + + + Package chrony is installed + + Red Hat Enterprise Linux 9 + + Checks if package chrony is installed. + + + + + + + + + + Package gdm is installed + + Red Hat Enterprise Linux 9 + + Checks if package gdm is installed. + + + + + + + + + + Package grub2 is installed + + Red Hat Enterprise Linux 9 + + Checks if package grub2-common is installed. + + + + + + + + + + + + + + Package libuser is installed + + Red Hat Enterprise Linux 9 + + Checks if package libuser is installed. + + + + + + + + + + Package providing /etc/login.defs is installed + + Red Hat Enterprise Linux 9 + + Checks if package providing /etc/login.defs and is installed. + + + + + + + + + + Package net-snmp is installed + + Red Hat Enterprise Linux 9 + + Checks if package net-snmp is installed. + + + + + + + + + + Check if the system doesn't act as an oVirt host or manager + + Red Hat Enterprise Linux 9 + + Check if the system has neither ovirt-host nor ovirt-engine installed. + + + + + + + + + Package nss-pam-ldapd is installed + + Red Hat Enterprise Linux 9 + + Checks if package nss-pam-ldapd is installed. + + + + + + + + + + Package ntp is installed + + Red Hat Enterprise Linux 9 + + Checks if package ntp is installed. + + + + + + + + + + Check if the system acts as an oVirt host or manager + + Red Hat Enterprise Linux 9 + + Check if the system has ovirt-host or ovirt-engine installed + + + + + + + + + + + Package pam is installed + + Red Hat Enterprise Linux 9 + + Checks if package pam is installed. + + + + + + + + + + Package polkit is installed + + Red Hat Enterprise Linux 9 + + Checks if package polkit is installed. + + + + + + + + + + Package postfix is installed + + Red Hat Enterprise Linux 9 + + Checks if package postfix is installed. + + + + + + + + + + Package sssd-common is installed + + Red Hat Enterprise Linux 9 + + Checks if package sssd-common is installed. + + + + + + + + + + Package sudo is installed + + Red Hat Enterprise Linux 9 + + Checks if package sudo is installed. + + + + + + + + + + Package systemd is installed + + Red Hat Enterprise Linux 9 + + Checks if package systemd is installed. + + + + + + + + + + Package tftp-server is installed + + Red Hat Enterprise Linux 9 + + Checks if package tftp-server is installed. + + + + + + + + + + Package tmux is installed + + Red Hat Enterprise Linux 9 + + Checks if package tmux is installed. + + + + + + + + + + Package usbguard is installed + + Red Hat Enterprise Linux 9 + + Checks if package usbguard is installed. + + + + + + + + + + WiFi interface is present + + Red Hat Enterprise Linux 9 + + Checks if any wifi interface is present. + + + + + + + + + + Package yum is installed + + Red Hat Enterprise Linux 9 + + Checks if package yum is installed. + + + + + + + + + + System uses zIPL + + Red Hat Enterprise Linux 9 + + Checks if system uses zIPL bootloader. + + + + + + + + + + Check if the scan target is a container + + Red Hat Enterprise Linux 9 + + Check for presence of files characterizing container filesystems. + + + + + + + + + + + Check if the scan target is a machine + + Red Hat Enterprise Linux 9 + + Check for absence of files characterizing container filesystems. + + + + + + + + + + Partition /tmp exists + + Red Hat Enterprise Linux 9 + + + + + + + + + + + Partition /var/tmp exists + + Red Hat Enterprise Linux 9 + + + + + + + + + + + Kerberos server is older than 1.17-18 + + Red Hat Enterprise Linux 9 + + + Check if version of Kerberos server is lesser than 1.17-18 + + + + + + + + + + Kerberos workstation is older than 1.17-18 + + Red Hat Enterprise Linux 9 + + + Check if version of Kerberos workstation is lesser than 1.17-18 + + + + + + + + + + No CD/DVD drive is configured to automount in /etc/fstab + + Red Hat Enterprise Linux 9 + + Check the /etc/fstab and check if a CD/DVD drive + is not configured for automount. + + + + + + + + + Test that the architecture is aarch64 + + Red Hat Enterprise Linux 9 + + Check that architecture of kernel in /proc/sys/kernel/osrelease is aarch64 + + + + + + + + + Test for different architecture than aarch64 + + Red Hat Enterprise Linux 9 + + Check that architecture of kernel in /proc/sys/kernel/osrelease is not aarch64 + + + + + + + + + Test for different architecture than s390x + + Red Hat Enterprise Linux 9 + + Check that architecture of kernel in /proc/sys/kernel/osrelease is not s390x + + + + + + + + + Test that the architecture is ppc64le + + Red Hat Enterprise Linux 9 + + Check that architecture of kernel in /proc/sys/kernel/osrelease is ppc64le + + + + + + + + + Test that the architecture is s390x + + Red Hat Enterprise Linux 9 + + Check that architecture of kernel in /proc/sys/kernel/osrelease is s390x + + + + + + + + + Device Files for Removable Media Partitions Does Not Exist on the System + + Red Hat Enterprise Linux 9 + + Verify if device file representing removable partitions + exist on the system + + + + + + + + + SSHD is not required to be installed or requirement not set + + Red Hat Enterprise Linux 9 + + If SSHD is not required, we check it is not installed. If SSH requirement is unset, we are good. + + + + + + + + + + SSHD is required to be installed or requirement not set + + Red Hat Enterprise Linux 9 + + If SSHD is required, we check it is installed. If SSH requirement is unset, we are good. + + + + + + + + + + It doesn't matter if sshd is installed or not + + Red Hat Enterprise Linux 9 + + Test if value sshd_required is 0. + + + + + + + + + OpenSSH Server is 7.4 or newer + + Red Hat Enterprise Linux 9 + + Check if version of OpenSSH Server is equal or higher than 7.4 + + + + + + + + + SSSD is configured to use LDAP + + Red Hat Enterprise Linux 9 + + Identification provider is not set to ad within /etc/sssd/sssd.conf + + + + + + + + + + Kernel Runtime Parameter IPv6 Check + + Red Hat Enterprise Linux 9 + + Disables IPv6 for all network interfaces. + + + + + + + + + + + + Non-UEFI system boot mode check + + Red Hat Enterprise Linux 9 + + Check if System boot mode is non-UEFI. + + + + + + + + + + UEFI system boot mode check + + Red Hat Enterprise Linux 9 + + Check if system boot mode is UEFI. + + + + + + + + + + Test for 64-bit Architecture + + Red Hat Enterprise Linux 9 + + Generic test for 64-bit architectures to be used by other tests + + + + + + + + + + + + Test for aarch_64 Architecture + + Red Hat Enterprise Linux 9 + + Generic test for aarch_64 architecture to be used by other tests + + + + + + + + + Test for PPC and PPCLE Architecture + + Red Hat Enterprise Linux 9 + + Generic test for PPC PPC64LE architecture to be used by other tests + + + + + + + + + + Test for s390_64 Architecture + + Red Hat Enterprise Linux 9 + + Generic test for s390_64 architecture to be used by other tests + + + + + + + + + Test for x86 Architecture + + Red Hat Enterprise Linux 9 + + Generic test for x86 architecture to be used by other tests + + + + + + + + + Test for x86_64 Architecture + + Red Hat Enterprise Linux 9 + + Generic test for x86_64 architecture to be used by other tests + + + + + + + + + + + Red Hat Enterprise Linux 9 + + Check /etc/tmux.conf is readable by others + + + + + + + + + Check that file storing USBGuard rules exists and is not empty + + Red Hat Enterprise Linux 9 + + Check that file storing USBGuard rules at /etc/usbguard/rules.conf exists and is not empty + + + + + + + + + Value of 'var_accounts_user_umask' variable represented as octal number + + Red Hat Enterprise Linux 9 + + Value of 'var_accounts_user_umask' variable represented as octal number + + + + + + + + + Value of 'var_removable_partition' variable is set to '/dev/cdrom' + + Red Hat Enterprise Linux 9 + + Verify if value of 'var_removable_partition' variable is set + to '/dev/cdrom' + + + + + + + + + Value of 'var_umask_for_daemons' variable represented as octal number + + Red Hat Enterprise Linux 9 + + Value of 'var_umask_for_daemons' variable represented as octal number + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + /etc/fapolicyd/fapolicyd.rules + (^|\n)\s*deny\s*perm=any\s*all\s*:\s*all\s*$ + 1 + + + /etc/fapolicyd/fapolicyd.conf + ^\s*permissive\s*=\s*(\d+) + 1 + + + + /etc/httpd/conf.d/ + ^.*$ + + + + /etc/httpd/conf + ^.*$ + + + + /etc/httpd/conf.modules.d/ + ^.*$ + + + ^/etc/.+\.keytab$ + + + /etc/aliases + ^(?:[rR][oO][oO][tT]|"[rR][oO][oO][tT]")\s*:\s*(.+)$ + 1 + + + /etc/aliases + ^(?i)postmaster\s*:\s*(.+)$ + 1 + + + /etc/postfix/main.cf + ^[\s]*inet_interfaces[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/postfix/main.cf + ^[ \t]*smtpd_client_restrictions = (.+?)[ \t]*(?:$|#) + 1 + + + ^/etc/postfix/main.cf + + + /etc/exports + ^\/.*\((\S+)\)$ + 0 + + + /etc/exports + ^\/.*$ + 0 + + + /etc/chrony.conf + ^\s*port[\s]+(\S+) + 1 + + + /etc/chrony.conf + ^\s*cmdport[\s]+(\S+) + 1 + + + /etc/ntp.conf + ^server[\s]+[\S]+.*maxpoll[\s]+(\d+) + 1 + + + ^/etc/chrony\.(conf|d/.+\.conf)$ + ^(?:server|pool|peer)[\s]+[\S]+.*maxpoll[\s]+(\d+) + 1 + + + /etc/ntp.conf + ^server[\s]+[\S]+[\s]+(.*) + 1 + + + ^/etc/chrony\.(conf|d/.+\.conf)$ + ^(?:server|pool|peer)[\s]+[\S]+[\s]+(.*) + 1 + + + ^/etc/chrony\.(conf|d/.+\.conf)$ + ^(?:server|pool).* + 1 + + + /etc/ntp.conf + ^server.* + 1 + + + + /etc/sysconfig/chronyd + ^\s*OPTIONS=.*[\s'"]-u(?!\s*chrony\b).* + 0 + + + ^/etc/chrony\.(conf|d/.+\.conf)$ + ^[\s]*server.*$ + 1 + + + ^/etc/chrony\.(conf|d/.+\.conf)$ + ^[\s]+pool.*$ + 1 + + + ^/etc/chrony\.(conf|d/.+\.conf)$ + ^[\s]*(?:server|pool)[\s]+.+$ + 1 + + + /etc/ntp.conf + ^([\s]*server[\s]+.+$){2,}$ + 1 + + + /etc/ntp.conf + ^[\s]*server[\s]+.+$ + 1 + + + + / + shosts.equiv + + + /root + ^\.rhosts$ + + + + /home + ^\.rhosts$ + + + /etc + ^hosts\.equiv$ + + + + / + .shosts + + + /etc/xinetd.d/tftp + ^[\s]*server_args[\s]+=[\s]+.*?-s[\s]+([/\.\w]+).*$ + 1 + + + /etc/fstab + ^[\s]*[\S]+[\s]+[\S]+[\s]+cifs[\s]+([\S]+) + 1 + + + /etc/mtab + ^[\s]*[\S]+[\s]+[\S]+[\s]+cifs[\s]+([\S]+) + 1 + + + /etc/snmp/snmpd.conf + ^[\s]*(com2se|rocommunity|rwcommunity) + 1 + + + /etc/ssh + .*_key$ + oval:ssg-exclude_symlinks__sshd_private_key:ste:1 + oval:ssg-filter_ssh_key_owner_root:ste:1 + oval:ssg-filter_ssh_key_owner_ssh_keys:ste:1 + + + /etc/group + ^ssh_keys:\w+:(\w+):.* + 1 + + + /etc/ssh/ssh_config + ^[\s]*RekeyLimit.*$ + 1 + + + ^/etc/ssh/ssh_config\.d/.*\.conf$ + + 1 + + + /etc/firewalld/services + ^.*\.xml$ + /service/service[@name='ssh'] + + + /etc/firewalld/services + ^.*\.xml$ + <port.*port="(\d+)" + 1 + + + /etc/firewalld/zones + ^.*\.xml$ + /zone/service[@name='ssh'] + + + /etc/firewalld/zones + ^.*\.xml$ + <port.*port="(\d+)" + 1 + + + /etc/firewalld/zones + + /zone/service[@name='ssh'] + + + /etc/NetworkManager/system-connections + .*\.nmconnection + ^zone=(.*)$ + 1 + + + /etc/ssh/sshd_config + ^[\s]*(?i)Protocol[\s]+2[\s]*(?:|(?:#.*))?$ + 1 + + + /etc/ssh/sshd_config + ^[ \t]*(?i)Compression(?-i)[ \t]+(.+?)[ \t]*(?:$|#) + 1 + + + /etc/ssh/sshd_config + ^[\s]*(?i)RhostsRSAAuthentication(?-i)[\s]+no[\s]*(?:#.*)?$ + 1 + + + /etc/ssh/sshd_config + + 1 + + + /etc/ssh/sshd_config.d + .*\.conf$ + + 1 + + + /etc/ssh/sshd_config + ^[\s]*(?i)ClientAliveInterval[\s]+(\d+)[\s]*(?:#.*)?$ + 1 + + + /etc/ssh/sshd_config.d + .*\.conf$ + ^[\s]*(?i)ClientAliveInterval[\s]+(\d+)[\s]*(?:#.*)?$ + 1 + + + /etc/ssh/sshd_config + ^[\s]*(?i)ClientAliveCountMax[\s]+([\d]+)[\s]*(?:#.*)?$ + 1 + + + /etc/ssh/sshd_config.d + .*\.conf$ + ^[\s]*(?i)ClientAliveCountMax[\s]+([\d]+)[\s]*(?:#.*)?$ + 1 + + + /etc/ssh/sshd_config + ^[\s]*(?i)LoginGraceTime[\s]+(\d+)[\s]*(?:#.*)?$ + 1 + + + /etc/ssh/sshd_config + ^[\s]*(?i)MaxAuthTries[\s]+(\d+)[\s]*(?:#.*)?$ + 1 + + + /etc/ssh/sshd_config + ^[\s]*(?i)MaxSessions[\s]+(\d+)[\s]*(?:#.*)?$ + 1 + + + /etc/ssh/sshd_config + (?i)^\s*MaxStartups\s+(\d+):\d+:\d+\s*$ + 1 + + + /etc/ssh/sshd_config + (?i)^\s*MaxStartups\s+\d+:(\d+):\d+\s*$ + 1 + + + /etc/ssh/sshd_config + (?i)^\s*MaxStartups\s+\d+:\d+:(\d+)\s*$ + 1 + + + /etc/ssh/sshd_config + ^[ \t]*(?i)match(?-i)\s+\S+ + 1 + + + /etc/ssh/sshd_config + ^[ \t]*(?i)UsePrivilegeSeparation(?-i)[ \t]+(.+?)[ \t]*(?:$|#) + 1 + + + ^/etc/sssd/(sssd|conf\.d/.*)\.conf$ + ^[\s]*\[sssd](?:[^\n\[]*\n+)+?[\s]*certificate_verification\s*=\s*ocsp_dgst\s*=\s*(\w+)$ + 1 + + + /etc/sssd/sssd.conf + ^[\s]*\[pam](?:[^\n\[]*\n+)+?[\s]*pam_cert_auth[\s]*=[\s]*(?i)true\s*$ + 1 + + + /etc/pam.d/smartcard-auth + ^\s*auth.*?pam_sss\.so(.*) + 1 + + + /etc/pam.d/system-auth + ^\s*auth.*?pam_sss\.so(.*) + 1 + + + /etc/sssd/sssd.conf + ^[\s]*\[pam](?:[^\n\[]*\n+)+?[\s]*offline_credentials_expiration[\s]*=[\s]*1\s*(?:#.*)?$ + 1 + + + /etc/usbguard/usbguard-daemon.conf + ^[ \t]*AuditBackend=(.+?)[ \t]*(?:$|#) + 1 + + + ^/etc/usbguard/usbguard-daemon.conf + + + xorg-x11-server-Xorg + + + xorg-x11-server-utils + + + xorg-x11-server-Xwayland + + + /etc/systemd/system/default.target + + + /etc/pam.d/fingerprint-auth + + + /etc/pam.d/password-auth + + + /etc/pam.d/postlogin + + + /etc/pam.d/smartcard-auth + + + /etc/pam.d/system-auth + + + + ^/etc/issue(\.d/.*)?$ + ^(.*)$ + 1 + + + + /etc/motd + ^(.*)$ + 1 + + + /etc/dconf/db/distro.d/ + ^.*$ + ^\[org/gnome/login-screen\]([^\n]*\n+)+?banner-message-enable=true$ + 1 + + + /etc/dconf/db/distro.d/locks/ + ^.*$ + ^/org/gnome/login-screen/banner-message-enable$ + 1 + + + /etc/dconf/db/distro.d/locks/ + ^.*$ + ^/org/gnome/login-screen/banner-message-text$ + 1 + + + /etc/dconf/db/distro.d/ + ^.*$ + ^banner-message-text=[\s]*'*(.*?)'$ + 1 + + + /etc/pam.d/sudo + ^.*pam_succeed_if.*$ + 1 + + + /etc/pam.d/postlogin + ^\s*session\s+required\s+pam_lastlog\.so(?:\s+[\w=]+)*\s+showfailed(\s|$) + 1 + + + /etc/pam.d/postlogin + ^\s*session\s+.*\s+pam_lastlog\.so(?:\s+[\w=]+)*\s+silent(\s|$) + 1 + + + /etc/pam.d/password-auth|/etc/pam.d/system-auth|/etc/security/faillock.conf + ^\s*(?:auth.*pam_faillock\.so.*)?dir\s*=\s*(\S+) + 1 + + + + + + + oval:ssg-var_account_password_selinux_faillock_dir_collector:var:1 + + + /etc/pam.d/system-auth + + 1 + + + /etc/pam.d/password-auth + + 1 + + + /etc/security/faillock.conf + ^\s*audit + 1 + + + /etc/pam.d/password-auth + + 1 + + + /etc/pam.d/system-auth + + 1 + + + /etc/pam.d/system-auth + ^\s*password\s+(?:(?:sufficient)|(?:required))\s+pam_unix\.so.*remember=([0-9]*).*$ + 1 + + + /etc/pam.d/system-auth + ^\s*password\s+(?:(?:requisite)|(?:required))\s+pam_pwhistory\.so.*remember=([0-9]*).*$ + 1 + + + ^/etc/pam.d/system-auth$ + + 1 + + + ^/etc/pam.d/password-auth$ + + 1 + + + ^/etc/pam.d/system-auth$ + + 1 + + + ^/etc/pam.d/system-auth$ + + 1 + + + ^/etc/pam.d/password-auth$ + + 1 + + + ^/etc/pam.d/password-auth$ + + 1 + + + ^/etc/pam.d/system-auth$ + + 1 + + + ^/etc/pam.d/password-auth$ + + 1 + + + ^/etc/security/faillock.conf$ + + 1 + + + ^/etc/pam.d/system-auth$ + + 1 + + + ^/etc/pam.d/password-auth$ + + 1 + + + ^/etc/pam.d/system-auth$ + + 1 + + + ^/etc/pam.d/system-auth$ + + 1 + + + ^/etc/pam.d/password-auth$ + + 1 + + + ^/etc/pam.d/password-auth$ + + 1 + + + ^/etc/pam.d/system-auth$ + + 1 + + + ^/etc/pam.d/password-auth$ + + 1 + + + ^/etc/security/faillock.conf$ + + 1 + + + /etc/pam.d/system-auth + + 1 + oval:ssg-state_pam_faillock_dir_parameter_not_default_value:ste:1 + + + /etc/pam.d/password-auth + + 1 + oval:ssg-state_pam_faillock_dir_parameter_not_default_value:ste:1 + + + /etc/pam.d/system-auth + + 1 + + + oval:ssg-var_faillock_dir_set_both_preauth_authfail_system_auth:var:1 + + + oval:ssg-var_faillock_dir_set_both_preauth_authfail_password_auth:var:1 + + + /etc/security/faillock.conf + + 1 + + + ^/etc/pam.d/system-auth$ + + 1 + + + ^/etc/pam.d/password-auth$ + + 1 + + + ^/etc/pam.d/system-auth$ + + 1 + + + ^/etc/pam.d/system-auth$ + + 1 + + + ^/etc/pam.d/password-auth$ + + 1 + + + ^/etc/pam.d/password-auth$ + + 1 + + + ^/etc/security/faillock.conf$ + + 1 + + + ^/etc/pam.d/system-auth$ + + 1 + + + ^/etc/pam.d/password-auth$ + + 1 + + + ^/etc/pam.d/system-auth$ + + 1 + + + ^/etc/pam.d/system-auth$ + + 1 + + + ^/etc/pam.d/password-auth$ + + 1 + + + ^/etc/pam.d/password-auth$ + + 1 + + + ^/etc/pam.d/system-auth$ + + 1 + + + ^/etc/pam.d/password-auth$ + + 1 + + + ^/etc/security/faillock.conf$ + + 1 + + + ^/etc/pam.d/system-auth$ + + 1 + + + ^/etc/pam.d/password-auth$ + + 1 + + + ^/etc/pam.d/system-auth$ + + 1 + + + ^/etc/pam.d/system-auth$ + + 1 + + + ^/etc/pam.d/password-auth$ + + 1 + + + ^/etc/pam.d/password-auth$ + + 1 + + + ^/etc/pam.d/system-auth$ + + 1 + + + ^/etc/pam.d/password-auth$ + + 1 + + + ^/etc/security/faillock.conf$ + + 1 + + + /etc/pam.d/password-auth + ^password[\s]*requisite[\s]*pam_pwquality\.so + 1 + + + /etc/pam.d/system-auth + ^password[\s]*requisite[\s]*pam_pwquality\.so + 1 + + + /etc/pam.d/password-auth + ^\s*password\s+(?:(?:required)|(?:requisite))\s+pam_pwquality\.so.*retry=([0-9]*).*$ + 1 + + + /etc/pam.d/system-auth + ^\s*password\s+(?:(?:required)|(?:requisite))\s+pam_pwquality\.so.*retry=([0-9]*).*$ + 1 + + + /etc/security/pwquality.conf + ^[\s]*retry[\s]*=[\s]*(\d+)(?:[\s]|$) + 1 + + + /etc/libuser.conf + ^[\s]*crypt_style[\s]+=[\s]+(?i)sha512[\s]*$ + 1 + + + + /etc/login.defs + .*\n[^#]*(ENCRYPT_METHOD\s+\w+)\s*\n + 1 + + + oval:ssg-variable_last_encrypt_method_instance_value:var:1 + + + /etc/pam.d/password-auth + ^[\s]*password[\s]+(?:(?:required)|(?:sufficient))[\s]+pam_unix\.so[\s]+.*sha512.*$ + 1 + + + /etc/pam.d/system-auth + ^[\s]*password[\s]+(?:(?:required)|(?:sufficient))[\s]+pam_unix\.so[\s]+.*sha512.*$ + 1 + + + /etc/login.defs + ^\s*SHA_CRYPT_MIN_ROUNDS\s* + 1 + + + /etc/login.defs + ^\s*SHA_CRYPT_MIN_ROUNDS\s+(\d+)\s*$ + 1 + + + /etc/login.defs + ^\s*SHA_CRYPT_MAX_ROUNDS\s* + 1 + + + /etc/login.defs + ^\s*SHA_CRYPT_MAX_ROUNDS\s+(\d+)\s*$ + 1 + + + ^/etc/systemd/system.conf(\.d/.*\.conf)?$ + ^[\s]*CtrlAltDelBurstAction[\s]*=[\s]*none$ + 1 + + + /etc/systemd/system/ctrl-alt-del.target + + + /etc/default/grub + ^\s*GRUB_CMDLINE_LINUX="(?:.*\s)?systemd\.confirm_spawn(?:=(?:1|yes|true|on))?(?:\s.*)?"$ + 1 + + + /etc/default/grub + ^\s*GRUB_CMDLINE_LINUX_DEFAULT=".*systemd\.confirm_spawn=(?:1|yes|true|on).*$ + 1 + + + /usr/lib/systemd/system/emergency.service + ^ExecStart=\-/usr/lib/systemd/systemd-sulogin-shell[\s]+emergency + 1 + + + /usr/lib/systemd/system/emergency.target + ^Requires=.*emergency\.service + 1 + + + + /etc/systemd/system + ^emergency.service$ + + + + /etc/systemd/system + ^emergency.target$ + + + /usr/lib/systemd/system/rescue.service + ^ExecStart=\-.*/usr/lib/systemd/systemd-sulogin-shell[ ]+rescue + 1 + + + /usr/lib/systemd/system/runlevel1.target + ^Requires=.*rescue\.service + 1 + + + + /etc/systemd/system + ^rescue.service$ + + + + /etc/systemd/system + ^runlevel1.target$ + + + + ^/etc/bashrc$|^/etc/profile\.d/.*$ + if \[ "\$PS1" \]; then\n\s+parent=\$\(ps -o ppid= -p \$\$\)\n\s+name=\$\(ps -o comm= -p \$parent\)\n\s+case "\$name" in sshd\|login\) exec tmux ;; esac\nfi + 1 + + + /etc/tmux.conf + ^\s*set\s+-g\s+lock-after-time\s+(\d+)\s*(?:#.*)?$ + 1 + + + /etc/tmux.conf + ^\s*set\s+-g\s+lock-command\s+vlock\s*(?:#.*)?$ + 1 + + + /etc/shells + tmux\s*$ + 1 + + + ^/etc/opensc.*\.conf$ + ^[\s]+card_drivers[\s]+=[\s]+(\S+);$ + 1 + + + ^/etc/opensc.*\.conf$ + ^[\s]+force_card_driver[\s]+=[\s]+(\S+);$ + 1 + + + .* + + + oval:ssg-variable_count_of_all_uids:var:1 + + + /etc/passwd + ^([a-zA-Z0-9_.-]+?): + 1 + oval:ssg-state_default_os_user:ste:1 + + + /etc/group + ^.+:.+:(\d+):.*$ + 1 + + + oval:ssg-variable_count_of_all_group_ids:var:1 + + + /etc/default/useradd + ^\s*INACTIVE\s*=\s*(\d+)\s*$ + 1 + + + /etc/passwd + ^([^:]+):.*$ + 1 + + + oval:ssg-variable_count_of_all_usernames_from_etc_passwd:var:1 + + + /etc/login.defs + ^(?:.*\n)*\s*[^#]*(PASS_MAX_DAYS\s+\d+)\s*\n + 1 + + + oval:ssg-variable_last_pass_max_days_instance_value:var:1 + + + + /etc/login.defs + .*\n[^#]*(PASS_MIN_DAYS\s+\d+)\s*\n + 1 + + + oval:ssg-variable_last_pass_min_days_instance_value:var:1 + + + + /etc/login.defs + .*\n[^#]*(PASS_MIN_LEN\s+\d+)\s*\n + 1 + + + oval:ssg-variable_last_pass_min_len_instance_value:var:1 + + + .* + + + .* + + + + /etc/login.defs + .*\n[^#]*(PASS_WARN_AGE\s+\d+)\s*\n + 1 + + + oval:ssg-variable_last_pass_warn_age_instance_value:var:1 + + + .* + + + .* + oval:ssg-state_accounts_password_all_shadowed_has_no_password:ste:1 + oval:ssg-state_accounts_password_all_shadowed_sha512:ste:1 + + + ^/etc/pam.d/password-auth$ + ^\s*password\s+(?:(?:sufficient)|(?:required))\s+pam_unix\.so.*rounds=([0-9]*).*$ + 1 + + + oval:ssg-var_password_pam_unix_rounds:var:1 + + + ^/etc/pam.d/system-auth$ + ^\s*password\s+(?:(?:sufficient)|(?:required))\s+pam_unix\.so.*rounds=([0-9]*).*$ + 1 + + + oval:ssg-var_password_pam_unix_rounds:var:1 + + + /etc/group + ^[^:]+:[^:]+:([0-9]+): + 1 + + + /etc/passwd + ^[^:]+:[^:]+:[0-9]+:([0-9]+): + 1 + + + ^/etc/pam.d/(system|password)-auth$ + ^[^#]*\bnullok\b.*$ + 1 + + + /etc/shadow + ^[^:]+::.*$ + 1 + + + /etc/group + ^\+.*$ + 1 + + + /etc/passwd + ^\+.*$ + 1 + + + /etc/shadow + ^\+.*$ + 1 + + + + /home + ^\.netrc$ + + + /etc/passwd + ^(?!root:)[^:]*:[^:]*:0 + 1 + + + /etc/passwd + ^root:.+:\d+:(\d+).+ + 1 + + + /etc/securetty + ^.*$ + 1 + + + /etc/securetty + ^$ + 1 + + + + /etc/login.defs + .*(?:^|\n)\s*(UID_MIN[\s]+[\d]+)\s*(?:$|\n) + 1 + + + + /etc/login.defs + .*(?:^|\n)\s*(SYS_UID_MIN[\s]+[\d]+)\s*(?:$|\n) + 1 + + + + /etc/login.defs + .*(?:^|\n)\s*(SYS_UID_MAX[\s]+[\d]+)\s*(?:$|\n) + 1 + + + /etc/passwd + ^(?!root).*:x:([\d]+):[\d]+:[^:]*:[^:]*:(?!\/usr\/sbin\/nologin|\/sbin\/nologin|\/bin\/sync|\/sbin\/shutdown|\/sbin\/halt|\/bin\/false|\/usr\/bin\/false).*$ + 1 + + + /etc/securetty + ^ttyS[0-9]+$ + 1 + + + /etc/securetty + ^vc/[0-9]+$ + 1 + + + /etc/pam.d/su + ^[\s]*auth[\s]+required[\s]+pam_wheel\.so[\s]+use_uid$ + 1 + + + /etc/login.defs + ^[\s]*(?i)CREATE_HOME(?-i)[\s]+yes[\s]*(?:#.*)?$ + 1 + + + /etc/login.defs + ^[\s]*(?i)FAIL_DELAY(?-i)[\s]+([^#\s]*) + 1 + + + /etc/security/limits.conf + ^[\s]*\*[\s]+(?:(?:hard)|(?:-))[\s]+maxlogins[\s]+(\d+)\s*$ + 1 + + + /etc/security/limits.d + ^.*\.conf$ + ^[\s]*\*[\s]+(?:(?:hard)|(?:-))[\s]+maxlogins[\s]+(\d+)\s*$ + 1 + + + /etc/security/limits.d + ^.*\.conf$ + ^[\s]*\*[\s]+(?:(?:hard)|(?:-))[\s]+maxlogins + 1 + + + /tmp/tmp-inst + + + + /etc/security/namespace.conf + ^\s*/tmp\s+/tmp/tmp-inst/\s+level\s+root,adm$ + 1 + + + /var/tmp/tmp-inst + + + + /etc/security/namespace.conf + ^\s*/var/tmp\s+/var/tmp/tmp-inst/\s+level\s+root,adm$ + 1 + + + /etc/profile + ^[\s]*declare[\s]+-xr[\s]+TMOUT=([\w$]+).*$ + 1 + + + /etc/profile.d + ^.*\.sh$ + ^[\s]*declare[\s]+-xr[\s]+TMOUT=([\w$]+).*$ + 1 + + + nobody + oval:ssg-state_accounts_user_dot_no_world_writable_programs_interactive_uids:ste:1 + + + + / + ^.*$ + oval:ssg-state_world_writable_programs:ste:1 + + + + + + + 1 + + + nobody + oval:ssg-state_accounts_user_interactive_home_directory_defined_uids:ste:1 + + + nobody + oval:ssg-state_accounts_user_interactive_home_directory_exists_uids:ste:1 + + + + + + + oval:ssg-var_accounts_user_interactive_home_directory_exists_dirs_count_fs:var:1 + + + oval:ssg-var_accounts_user_interactive_home_directory_exists_dirs_count:var:1 + + + .* + oval:ssg-state_file_groupownership_home_directories_interactive_uids:ste:1 + oval:ssg-state_file_permissions_groupownership_user_list:ste:1 + + + + + + + .* + oval:ssg-state_file_permissions_home_directories_interactive_uids:ste:1 + oval:ssg-state_file_permissions_home_files_permissions_user_list:ste:1 + + + + + + + nobody + oval:ssg-state_file_permissions_home_dirs_interactive_uids:ste:1 + + + + + + + + PATH + + + + + oval:ssg-state_accounts_root_path_dirs_wrong_perms:ste:1 + oval:ssg-state_accounts_root_path_dirs_symlink:ste:1 + + + + PATH + + + /etc/bashrc + ^[\s]*umask[\s]+([^#\s]*) + 1 + + + oval:ssg-var_etc_bashrc_umask_as_number:var:1 + + + /etc/csh.cshrc + ^[\s]*(?i)UMASK(?-i)[\s]+([^#\s]*) + 1 + + + oval:ssg-var_etc_csh_cshrc_umask_as_number:var:1 + + + /etc/login.defs + ^[\s]*UMASK[\s]+([^#\s]*) + 1 + + + oval:ssg-var_etc_login_defs_umask_as_number:var:1 + + + /etc/profile + ^[\s]*umask[\s]+([^#\s]*) + 1 + + + oval:ssg-var_etc_profile_umask_as_number:var:1 + + + nobody + oval:ssg-state_accounts_umask_interactive_users_interactive_uids:ste:1 + + + + ^\..* + ^[\s]*umask\s* + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+task,never[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+task,never[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^\-e\s+2\s*$ + 1 + + + /etc/audit/audit.rules + ^\-e\s+2\s*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^\-w[\s]+/etc/selinux/[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ + 1 + + + /etc/audit/audit.rules + ^\-w[\s]+/etc/selinux/[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^\-w[\s]+/etc/issue[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^\-w[\s]+/etc/issue\.net[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^\-w[\s]+/etc/hosts[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^\-w[\s]+/etc/sysconfig/network[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ + 1 + + + /etc/audit/audit.rules + ^\-w[\s]+/etc/issue[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ + 1 + + + /etc/audit/audit.rules + ^\-w[\s]+/etc/issue\.net[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ + 1 + + + /etc/audit/audit.rules + ^\-w[\s]+/etc/hosts[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ + 1 + + + /etc/audit/audit.rules + ^\-w[\s]+/etc/sysconfig/network[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^\-w\s+/var/run/utmp\s+\-p\s+wa\b.*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^\-w\s+/var/log/btmp\s+\-p\s+wa\b.*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^\-w\s+/var/log/wtmp\s+\-p\s+wa\b.*$ + 1 + + + /etc/audit/audit.rules + ^\-w\s+/var/run/utmp\s+\-p\s+wa\b.*$ + 1 + + + /etc/audit/audit.rules + ^\-w\s+/var/log/btmp\s+\-p\s+wa\b.*$ + 1 + + + /etc/audit/audit.rules + ^\-w\s+/var/log/wtmp\s+\-p\s+wa\b.*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^\-w[\s]+/etc/sudoers[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ + 1 + + + /etc/audit/audit.rules + ^\-w[\s]+/etc/sudoers[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^\-w[\s]+/etc/sudoers\.d/[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ + 1 + + + /etc/audit/audit.rules + ^\-w[\s]+/etc/sudoers\.d/[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+arch=b32[\s]+-S[\s]+execve[\s]+-C[\s]+uid!=euid[\s]+-F[\s]+euid=0[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+arch=b64[\s]+-S[\s]+execve[\s]+-C[\s]+uid!=euid[\s]+-F[\s]+euid=0[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+arch=b32[\s]+-S[\s]+execve[\s]+-C[\s]+uid!=euid[\s]+-F[\s]+euid=0[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+arch=b64[\s]+-S[\s]+execve[\s]+-C[\s]+uid!=euid[\s]+-F[\s]+euid=0[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+arch=b32[\s]+-S[\s]+execve[\s]+-C[\s]+gid!=egid[\s]+-F[\s]+egid=0[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+arch=b64[\s]+-S[\s]+execve[\s]+-C[\s]+gid!=egid[\s]+-F[\s]+egid=0[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+arch=b32[\s]+-S[\s]+execve[\s]+-C[\s]+gid!=egid[\s]+-F[\s]+egid=0[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+arch=b64[\s]+-S[\s]+execve[\s]+-C[\s]+gid!=egid[\s]+-F[\s]+egid=0[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^\-w[\s]+/etc/sudoers[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^\-w[\s]+/etc/sudoers\.d/[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ + 1 + + + /etc/audit/audit.rules + ^\-w[\s]+/etc/sudoers[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ + 1 + + + /etc/audit/audit.rules + ^\-w[\s]+/etc/sudoers\.d/[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^\-f\s+2\s*$ + 1 + + + /etc/audit/audit.rules + ^\-f\s+2\s*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^\-w[\s]+/etc/group[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^\-w[\s]+/etc/passwd[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^\-w[\s]+/etc/gshadow[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^\-w[\s]+/etc/shadow[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^\-w[\s]+/etc/security/opasswd[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ + 1 + + + /etc/audit/audit.rules + ^\-w[\s]+/etc/group[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ + 1 + + + /etc/audit/audit.rules + ^\-w[\s]+/etc/passwd[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ + 1 + + + /etc/audit/audit.rules + ^\-w[\s]+/etc/gshadow[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ + 1 + + + /etc/audit/audit.rules + ^\-w[\s]+/etc/shadow[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ + 1 + + + /etc/audit/audit.rules + ^\-w[\s]+/etc/security/opasswd[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + /etc/audit/audit.rules + + 1 + + + + /var/log/audit + + oval:ssg-state_group_owner_not_root_var_log_audit_directories:ste:1 + + + + /var/log/audit + + oval:ssg-state_group_owner_not_root_var_log_audit_directories-non_root:ste:1 + + + + + + oval:ssg-state_group_owner_not_root_var_log_audit_directories:ste:1 + + + + + + + + oval:ssg-state_owner_not_root_var_log_audit_directories:ste:1 + + + /var/log/audit + + oval:ssg-state_owner_not_root_var_log_audit_directories:ste:1 + + + + + + oval:ssg-state_not_mode_0700:ste:1 + + + + /var/log/audit + + oval:ssg-state_not_mode_0700:ste:1 + + + + /var/log/audit + + oval:ssg-state_not_mode_0750:ste:1 + + + + + + oval:ssg-state_not_mode_0750:ste:1 + + + + oval:ssg-state_group_owner_not_root_var_log_audit:ste:1 + + + /var/log/audit/audit.log + oval:ssg-state_group_owner_not_root_var_log_audit:ste:1 + + + + /var/log/audit + + oval:ssg-state_owner_not_root_root_var_log_audit:ste:1 + + + + /var/log/audit + ^.*$ + oval:ssg-state_owner_not_root_root_var_log_audit:ste:1 + + + + /var/log/audit + + oval:ssg-state_owner_not_root_var_log_audit-non_root:ste:1 + + + + /var/log/audit + ^.*$ + oval:ssg-state_owner_not_root_var_log_audit-non_root:ste:1 + + + + oval:ssg-state_owner_not_root_var_log_audit:ste:1 + + + + /var/log/audit + ^.*$ + oval:ssg-state_owner_not_root_var_log_audit:ste:1 + + + + oval:ssg-state_not_mode_0600:ste:1 + + + /var/log/audit/audit.log + oval:ssg-state_not_mode_0600:ste:1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+umount[\s]+|([\s]+|[,])umount([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+umount[\s]+|([\s]+|[,])umount([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+delete_module[\s]+|([\s]+|[,])delete_module([\s]+|[,]))).*(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295))\s+(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+delete_module[\s]+|([\s]+|[,])delete_module([\s]+|[,]))).*(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295))\s+(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+delete_module[\s]+|([\s]+|[,])delete_module([\s]+|[,]))).*(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295))\s+(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+delete_module[\s]+|([\s]+|[,])delete_module([\s]+|[,]))).*(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295))\s+(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+finit_module[\s]+|([\s]+|[,])finit_module([\s]+|[,]))).*(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295))\s+(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+finit_module[\s]+|([\s]+|[,])finit_module([\s]+|[,]))).*(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295))\s+(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+finit_module[\s]+|([\s]+|[,])finit_module([\s]+|[,]))).*(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295))\s+(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+finit_module[\s]+|([\s]+|[,])finit_module([\s]+|[,]))).*(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295))\s+(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+init_module[\s]+|([\s]+|[,])init_module([\s]+|[,]))).*(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295))\s+(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+init_module[\s]+|([\s]+|[,])init_module([\s]+|[,]))).*(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295))\s+(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+init_module[\s]+|([\s]+|[,])init_module([\s]+|[,]))).*(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295))\s+(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+init_module[\s]+|([\s]+|[,])init_module([\s]+|[,]))).*(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295))\s+(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + + / + [a-z]+ + oval:ssg-state_setuid_or_setgid_set:ste:1 + oval:ssg-state_dev_proc_sys_dirs:ste:1 + + + oval:ssg-variable_count_of_suid_sgid_binaries_on_system:var:1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a always,exit (?:-F path=([\S]+) )+-F auid>=1000 -F auid!=(?:4294967295|unset)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + oval:ssg-state_proper_audit_rule_but_for_unprivileged_command:ste:1 + + + /etc/audit/audit.rules + ^[\s]*-a always,exit (?:-F path=([\S]+) )+-F auid>=1000 -F auid!=(?:4294967295|unset)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + oval:ssg-state_proper_audit_rule_but_for_unprivileged_command:ste:1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+arch=b32.*(-S[\s]+adjtimex[\s]+|([\s]+|[,])adjtimex([\s]+|[,])).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+arch=b64.*(-S[\s]+adjtimex[\s]+|([\s]+|[,])adjtimex([\s]+|[,])).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+arch=b32.*(-S[\s]+adjtimex[\s]+|([\s]+|[,])adjtimex([\s]+|[,])).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+arch=b64.*(-S[\s]+adjtimex[\s]+|([\s]+|[,])adjtimex([\s]+|[,])).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+arch=b32[\s]+(-S[\s]+clock_settime[\s]+|([\s]+|[,])clock_settime([\s]+|[,]))-F[\s]+a0=(?:0x)?0[\s]+(?:-F[\s]+key=|-k[\s]+)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+arch=b64[\s]+(-S[\s]+clock_settime[\s]+|([\s]+|[,])clock_settime([\s]+|[,]))-F[\s]+a0=(?:0x)?0[\s]+(?:-F[\s]+key=|-k[\s]+)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+arch=b32[\s]+(-S[\s]+clock_settime[\s]+|([\s]+|[,])clock_settime([\s]+|[,]))-F[\s]+a0=(?:0x)?0[\s]+(?:-F[\s]+key=|-k[\s]+)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+arch=b64[\s]+(-S[\s]+clock_settime[\s]+|([\s]+|[,])clock_settime([\s]+|[,]))-F[\s]+a0=(?:0x)?0[\s]+(?:-F[\s]+key=|-k[\s]+)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+arch=b32.*(-S[\s]+settimeofday[\s]+|([\s]+|[,])settimeofday([\s]+|[,])).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+arch=b64.*(-S[\s]+settimeofday[\s]+|([\s]+|[,])settimeofday([\s]+|[,])).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+arch=b32.*(-S[\s]+settimeofday[\s]+|([\s]+|[,])settimeofday([\s]+|[,])).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+arch=b64.*(-S[\s]+settimeofday[\s]+|([\s]+|[,])settimeofday([\s]+|[,])).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+arch=b32.*(-S[\s]+stime[\s]+|([\s]+|[,])stime([\s]+|[,])).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+arch=b32.*(-S[\s]+stime[\s]+|([\s]+|[,])stime([\s]+|[,])).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-w[\s]+\/etc\/localtime[\s]+-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-w[\s]+\/etc\/localtime[\s]+-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ + 1 + + + /etc/audit/audisp-remote.conf + ^[ ]*remote_server[ ]+=[ ]+(\S+)[ ]*$ + 1 + + + /etc/audit/audisp-remote.conf + ^[ ]*disk_full_action[ ]+=[ ]+(\S+)[ ]*$ + 1 + + + /etc/audit/audisp-remote.conf + ^[ ]*enable_krb5[ ]+=[ ]+yes[ ]*$ + 1 + + + /etc/audit/audisp-remote.conf + ^[ ]*network_failure_action[ ]+=[ ]+(\S+)[ ]*$ + 1 + + + /etc/audit/plugins.d/syslog.conf + ^[ ]*active[ ]+=[ ]+yes[ ]*$ + 1 + + + /etc/audit/auditd.conf + ^[ ]*disk_error_action[ ]+=[ ]+(\S+)[ ]*$ + 1 + + + /etc/audit/auditd.conf + ^[ ]*disk_error_action[ ]+=[ ]+(\S+)[ ]*$ + 1 + + + /etc/audit/auditd.conf + ^[ ]*disk_full_action[ ]+=[ ]+(\S+)[ ]*$ + 1 + + + /etc/audit/auditd.conf + ^[ ]*disk_full_action[ ]+=[ ]+(\S+)[ ]*$ + 1 + + + /etc/audit/auditd.conf + ^[ ]*action_mail_acct[ ]+=[ ]+(\S+)[ ]*$ + 1 + + + /etc/audit/auditd.conf + ^[ ]*admin_space_left_action[ ]+=[ ]+(\S+)[ ]*$ + 1 + + + /etc/audit/auditd.conf + ^[ ]*flush[ ]+=[ ]+(\S+)[ ]*$ + 1 + + + /etc/audit/auditd.conf + ^[ ]*max_log_file[ ]+=[ ]+(\d+)[ ]*$ + 1 + + + /etc/audit/auditd.conf + ^[ ]*max_log_file_action[ ]+=[ ]+(\S+)[ ]*$ + 1 + + + /etc/audit/auditd.conf + ^[ ]*max_log_file_action[ ]+=[ ]+(\S+)[ ]*$ + 1 + + + /etc/audit/auditd.conf + ^[ ]*num_logs[ ]+=[ ]+(\d+)[ ]*$ + 1 + + + /etc/audit/auditd.conf + ^[\s]*space_left[\s]+=[\s]+(\d+)[\s]*$ + 1 + + + /etc/audit/auditd.conf + ^[ ]*space_left_action[ ]+=[ ]+(\S+)[ ]*$ + 1 + + + /etc/audit/auditd.conf + ^[\s]*space_left[\s]+=[\s]+(\d+)%[\s]*$ + 1 + + + /etc/audit/auditd.conf + ^[ \t]*(?i)name_format(?-i)[ \t]*=[ \t]*(.+?)[ \t]*(?:$|#) + 1 + + + /etc/audit/auditd.conf + ^[ \t]*(?i)overflow_action(?-i)[ \t]*=[ \t]*(.+?)[ \t]*(?:$|#) + 1 + + + /etc/audit/rules.d/10-base-config.rules + (?:.*\n)* + 1 + + + ^/usr/share/doc/audit(?:-\d.\d.\d)?/rules/10-base-config.rules + (?:.*\n)* + 1 + + + /etc/audit/rules.d/11-loginuid.rules + (?:.*\n)* + 1 + + + ^/usr/share/doc/audit(?:-\d.\d.\d)?/rules/11-loginuid.rules + (?:.*\n)* + 1 + + + /etc/audit/rules.d/30-ospp-v42.rules + (?:.*\n)* + 1 + + + ^/usr/share/doc/audit(?:-\d.\d.\d)?/rules/30-ospp-v42.rules + (?:.*\n)* + 1 + + + /etc/audit/rules.d/43-module-load.rules + (?:.*\n)* + 1 + + + ^/usr/share/doc/audit(?:-\d.\d.\d)?/rules/43-module-load.rules + (?:.*\n)* + 1 + + + + + ^CONFIG_RANDOM_TRUST_CPU=(y|Y)$ + 1 + + + /boot/loader/entries/ + ^.*\.conf$ + ^options (.*)$ + 1 + + + .* + + + /boot/grub2/grub.cfg + ^[\s]*set[\s]+superusers="(?i)\b(?!(?:root|admin|administrator)\b)(\w+)"$ + 1 + + + /boot/grub2/grub.cfg + ^[\s]*set[\s]+superusers=("?)[a-zA-Z_]+\1$ + 1 + + + /boot/grub2/user.cfg + ^[\s]*GRUB2_PASSWORD=grub\.pbkdf2\.sha512.*$ + 1 + + + /boot/grub2/grub.cfg + ^[\s]*password_pbkdf2[\s]+.*[\s]+grub\.pbkdf2\.sha512.*$ + 1 + + + .* + + + /boot/grub2/grub.cfg + ^[\s]*set[\s]+superusers="(?i)\b(?!(?:root|admin|administrator)\b)(\w+)"$ + 1 + + + /boot/grub2/user.cfg + ^[\s]*GRUB2_PASSWORD=grub\.pbkdf2\.sha512.*$ + 1 + + + /etc/zipl.conf + ^\s*image\s*=.*$ + 1 + + + /boot/bootmap + + + /etc/zipl.conf + + + ^/boot/loader/entries/.*\.conf$ + + + ^/boot/loader/entries/.*\.conf + ^options (.*)$ + 1 + + + ^/etc/kernel/cmdline + ^(.*)$ + 1 + + + /etc/rsyslog.conf + ^[\s]*cron\.\*[\s]+/var/log/cron\s*(?:#.*)?$ + 1 + + + /etc/rsyslog.d + ^.*$ + ^[\s]*cron\.\*[\s]+/var/log/cron\s*(?:#.*)?$ + 1 + + + /etc/rsyslog.conf + ^\$ActionSendStreamDriverAuthMode x509/name$ + 1 + + + /etc/rsyslog.d + ^.*conf$ + ^\$ActionSendStreamDriverAuthMode x509/name$ + 1 + + + /etc/rsyslog.conf + ^\$ActionSendStreamDriverMode 1$ + 1 + + + /etc/rsyslog.d + ^.*conf$ + ^\$ActionSendStreamDriverMode 1$ + 1 + + + /etc/rsyslog.conf + ^\$DefaultNetstreamDriver gtls$ + 1 + + + /etc/rsyslog.d + ^.*conf$ + ^\$DefaultNetstreamDriver gtls$ + 1 + + + /etc/rsyslog.conf + ^(?:include\([\n\s]*file="([^\s;]+)".*|\$IncludeConfig[\s]+([^\s;]+))$ + 1 + + + oval:ssg-var_rfg_include_config_regex:var:1 + + + oval:ssg-var_rfg_syslog_config:var:1 + + + + oval:ssg-object_var_rfg_include_config_regex:obj:1 + oval:ssg-object_var_rfg_syslog_config:obj:1 + + + + + ^[^(\s|#|\$)]+[\s]+.*[\s]+-?(/+[^:;\s]+);*\.*$ + 1 + oval:ssg-state_groupownership_ignore_include_paths:ste:1 + + + + + + /etc/rsyslog.conf + ^(?:include\([\n\s]*file="([^\s;]+)".*|\$IncludeConfig[\s]+([^\s;]+))$ + 1 + + + oval:ssg-var_rfo_include_config_regex:var:1 + + + oval:ssg-var_rfo_syslog_config:var:1 + + + + oval:ssg-object_var_rfo_include_config_regex:obj:1 + oval:ssg-object_var_rfo_syslog_config:obj:1 + + + + + ^[^(#|\$)]+[\s]+.*[\s]+-?(/+[^:;\s]+);*\.*$ + 1 + oval:ssg-state_owner_ignore_include_paths:ste:1 + + + + + + /etc/rsyslog.conf + ^(?:include\([\n\s]*file="([^\s;]+)".*|\$IncludeConfig[\s]+([^\s;]+))$ + 1 + oval:ssg-state_permissions_ignore_hidden_paths:ste:1 + + + oval:ssg-var_rfp_include_config_regex:var:1 + + + oval:ssg-var_rfp_syslog_config:var:1 + + + + oval:ssg-object_var_rfp_include_config_regex:obj:1 + oval:ssg-object_var_rfp_syslog_config:obj:1 + + + + + ^[^(\s|#|\$)]+[\s]+.*[\s]+-?(/+[^:;\s]+);*\.*$ + 1 + oval:ssg-state_permissions_ignore_include_paths:ste:1 + + + + + + ^/etc/rsyslog\.(conf|d/.+\.conf)$ + ^.*auth\.\*.*$ + 1 + + + ^/etc/rsyslog\.(conf|d/.+\.conf)$ + ^.*authpriv\.\*.*$ + 1 + + + ^/etc/rsyslog\.(conf|d/.+\.conf)$ + ^.*daemon\.\*.*$ + 1 + + + /etc/logrotate.conf + ^\s*daily[\s#]*$ + 1 + + + /etc/logrotate.conf + ^\s*(weekly|monthly|yearly)[\s#]*$ + 1 + + + /etc/cron.daily/logrotate + ^[\s]*/usr/sbin/logrotate[\s\S]*/etc/logrotate.conf$ + 1 + + + /etc/rsyslog.conf + ^[\s]*\$((?:Input(?:TCP|RELP)|UDP)ServerRun|ModLoad[\s]+(imtcp|imudp|imrelp)) + 1 + + + /etc/rsyslog.conf + ^\*\.\*[\s]+(?:@|\:omrelp\:) + 1 + + + /etc/rsyslog.d + ^.+\.conf$ + ^\*\.\*[\s]+(?:@|\:omrelp\:) + 1 + + + + ^/etc/rsyslog\.(conf|d/.+\.conf)$ + ^\s*action\((?i)type(?-i)="omfwd"(.+?)\) + 0 + + + ^/etc/rsyslog\.(conf|d/.+\.conf)$ + ^\s*global\(DefaultNetstreamDriverCAFile="(.+?)"\)\s*\n + 0 + + + /etc/resolv.conf + ^[\s]*nameserver[\s]+([0-9\.]+)$ + 1 + + + /etc/nsswitch.conf + ^\s*hosts\s*:\s*.*dns.*$ + 1 + + + /etc/resolv.conf + + + ^/etc/polkit-1/localauthority/20-org.d/.*$ + ^\[.*\]\n\s*Identity=default\n\s*Action=org\.freedesktop\.NetworkManager\.\*\n\s*ResultAny=no\n\s*ResultInactive=no\n\s*(ResultActive=auth_admin)\n*\s*$ + 1 + + + ^.*$ + oval:ssg-state_promisc:ste:1 + + + /etc/firewalld/firewalld.conf + ^DefaultZone=drop$ + 1 + + + /etc/modprobe.d + ^.*\.conf$ + ^\s*options\s+ipv6\s+.*disable=1.*$ + 1 + + + ^wl.*$ + + + + / + + oval:ssg-state_uid_is_not_root_and_world_writable:ste:1 + + + + / + + oval:ssg-state_world_writable_and_not_sticky:ste:1 + + + /boot + ^System\.map.*$ + + + + / + ^.*$ + oval:ssg-state_file_permissions_unauthorized_sgid_sgid_set:ste:1 + oval:ssg-state_file_permissions_unauthorized_sgid_filepaths:ste:1 + + + + .* + .* + .* + .* + .* + + + + + / + ^.*$ + oval:ssg-state_file_permissions_unauthorized_sgid_sgid_set:ste:1 + + + + / + ^.*$ + oval:ssg-state_file_permissions_unauthorized_suid_suid_set:ste:1 + oval:ssg-state_file_permissions_unauthorized_suid_filepaths:ste:1 + + + + .* + .* + .* + .* + .* + + + + + / + ^.*$ + oval:ssg-state_file_permissions_unauthorized_suid_suid_set:ste:1 + + + + / + ^.*$ + oval:ssg-state_file_permissions_unauthorized_world_write:ste:1 + oval:ssg-state_file_permissions_unauthorized_world_write_exclude_special_selinux_files:ste:1 + oval:ssg-state_file_permissions_unauthorized_world_write_exclude_proc:ste:1 + oval:ssg-state_file_permissions_unauthorized_world_write_exclude_sys:ste:1 + + + + / + .* + oval:ssg-state_file_permissions_ungroupowned:ste:1 + + + /etc/group + ^[^:]+:[^:]*:([\d]+):[^:]*$ + 1 + + + .* + + + + / + .* + oval:ssg-file_permissions_unowned_userid_list_match:ste:1 + + + ^\/s?bin|^\/usr\/s?bin|^\/usr\/local\/s?bin + ^.*$ + oval:ssg-state_groupowner_system_commands_dirs_not_root_or_system_account:ste:1 + + + ^\/(|s)bin|^\/usr\/(|local\/)(|s)bin|^\/usr\/libexec + + oval:ssg-state_owner_binaries_not_root:ste:1 + + + ^\/(|s)bin|^\/usr\/(|local\/)(|s)bin|^\/usr\/libexec + ^.*$ + oval:ssg-state_owner_binaries_not_root:ste:1 + + + ^\/(|s)bin|^\/usr\/(|local\/)(|s)bin|^\/usr\/libexec + ^.*$ + oval:ssg-state_perms_binary_files_nogroupwrite_noworldwrite:ste:1 + oval:ssg-state_perms_binary_files_symlink:ste:1 + + + ^/\w.*$ + oval:ssg-state_local_nodev:ste:1 + + + /etc/fstab + ^[\s]*/tmp[\s]+/var/tmp[\s]+.*bind.*$ + 1 + + + ^/var/tmp$ + + + /etc/mtab + ^[\s]*/tmp[\s]+/var/tmp[\s]+.*bind.*$ + 1 + + + ^/tmp$ + + + kernel.core_pattern + + + oval:ssg-local_var_sysctl_kernel_core_pattern_empty_string_counter:var:1 + + + + oval:ssg-object_sysctl_kernel_core_pattern_empty_string_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_kernel_core_pattern_empty_string_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_kernel_core_pattern_empty_string:obj:1 + oval:ssg-var_obj_blank_sysctl_kernel_core_pattern_empty_string:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_kernel_core_pattern_empty_string:var:1 + + + oval:ssg-local_var_symlinks_sysctl_kernel_core_pattern_empty_string:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_kernel_core_pattern_empty_string:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_kernel_core_pattern_empty_string:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_kernel_core_pattern_empty_string:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_kernel_core_pattern_empty_string:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_kernel_core_pattern_empty_string:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_kernel_core_pattern_empty_string:obj:1 + + + + /etc/sysctl.conf + ^[[:blank:]]*kernel.core_pattern[[:blank:]]*=[[:blank:]]*(.*)$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[[:blank:]]*kernel.core_pattern[[:blank:]]*=[[:blank:]]*(.*)$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[[:blank:]]*kernel.core_pattern[[:blank:]]*=[[:blank:]]*(.*)$ + 1 + + + /etc/systemd/coredump.conf + ^\s*\[Coredump\].*(?:\n\s*[^[\s].*)*\n^[ \t]*(?i)ProcessSizeMax(?-i)[ \t]*=[ \t]*(.+?)[ \t]*(?:$|#) + 1 + + + /etc/systemd/coredump.conf + ^\s*\[Coredump\].*(?:\n\s*[^[\s].*)*\n^[ \t]*(?i)Storage(?-i)[ \t]*=[ \t]*(.+?)[ \t]*(?:$|#) + 1 + + + /etc/security/limits.conf + ^[\s]*\*[\s]+(?:hard|-)[\s]+core[\s]+([\d]+) + 1 + + + /etc/security/limits.d + ^.*\.conf$ + ^[\s]*\*[\s]+(?:hard|-)[\s]+core[\s]+([\d]+) + 1 + + + /etc/security/limits.d + ^.*\.conf$ + ^[\s]*\*[\s]+(?:hard|-)[\s]+core + 1 + + + /etc/sysctl.conf + ^[\s]*kernel.exec-shield[\s]*=[\s]*1[\s]*$ + 1 + + + kernel.exec-shield + + + /boot/grub2/grub.cfg + [\s]*noexec[\s]*=[\s]*off + 1 + + + /proc/cpuinfo + ^flags[\s]+:.*[\s]+nx[\s]+.*$ + 1 + + + /var/log/messages + ^.+protection: disabled.+ + 1 + + + /proc/cmdline + .+noexec[0-9]*=off.+ + 1 + + + /etc/default/grub + ^[\s]*GRUB_CMDLINE_LINUX.*(selinux|enforcing)=0.*$ + 1 + + + /etc/grub2.cfg + ^.*(selinux|enforcing)=0.*$ + 1 + + + /etc/grub.d + ^.*$ + ^.*(selinux|enforcing)=0.*$ + 1 + + + + /dev + ^.*$ + oval:ssg-state_block_or_char_device_file:ste:1 + + + + oval:ssg-state_selinux_dev_device_t:ste:1 + + + + oval:ssg-state_selinux_dev_unlabeled_t:ste:1 + + + + /proc + ^.*$ + oval:ssg-state_selinux_confinement_of_daemons:ste:1 + + + /etc/selinux/config + ^SELINUXTYPE=([\w]*)[\s]*$ + 1 + + + /etc/selinux/config + ^SELINUX=(.*)$ + 1 + + + /proc/cpuinfo + ^flags\s+:\s+(.*)$ + 1 + + + /proc/sys/kernel/osrelease + ^.*\.(.*)$ + 1 + + + /etc/dconf/db/distro + + + ^/etc/dconf/db/distro.d/.* + + + oval:ssg-var_dconf_distro_db_modified_time:var:1 + + + /etc/dconf/db/local + + + ^/etc/dconf/db/local.d/.* + + + oval:ssg-var_dconf_local_db_modified_time:var:1 + + + /etc/dconf/profile/user + ^user-db:user\nsystem-db:local$ + 1 + + + /etc/dconf/db/distro.d/ + ^.*$ + ^\[org/gnome/login-screen\]([^\n]*\n+)+?disable-restart-buttons=true$ + 1 + + + /etc/dconf/db/distro.d/locks/ + ^.*$ + ^/org/gnome/login-screen/disable-restart-buttons$ + 1 + + + /etc/dconf/db/distro.d/ + ^.*$ + ^\[org/gnome/login-screen\]([^\n]*\n+)+?disable-user-list=true$ + 1 + + + /etc/dconf/db/distro.d/locks/ + ^.*$ + ^/org/gnome/login-screen/disable-user-list$ + 1 + + + /etc/dconf/db/distro.d/ + ^.*$ + ^\[org/gnome/login-screen\]([^\n]*\n+)+?enable-smartcard-authentication=true$ + 1 + + + /etc/dconf/db/distro.d/locks/ + ^.*$ + ^/org/gnome/login-screen/enable-smartcard-authentication$ + 1 + + + /etc/dconf/db/distro.d/ + ^.*$ + ^\[org/gnome/login-screen\]([^\n]*\n+)+?allowed-failures=3$ + 1 + + + /etc/dconf/db/distro.d/locks/ + ^.*$ + ^/org/gnome/login-screen/allowed-failures$ + 1 + + + /etc/gdm/custom.conf + ^\[daemon]([^\n]*\n+)+?AutomaticLoginEnable=[Ff]alse$ + 1 + + + /etc/gdm/custom.conf + ^\s*\[xdmcp\].*(?:\n\s*[^[\s].*)*\n^\s*Enable[ \t]*=[ \t]*(.+?)[ \t]*(?:$|#) + 1 + + + ^/etc/gdm/custom.conf + + + /etc/dconf/db/local.d/ + ^.*$ + ^\[org/gnome/desktop/media-handling\]([^\n]*\n+)+?automount=false$ + 1 + + + /etc/dconf/db/local.d/locks/ + ^.*$ + ^/org/gnome/desktop/media-handling/automount$ + 1 + + + /etc/dconf/db/local.d/ + ^.*$ + ^\[org/gnome/desktop/media-handling\]([^\n]*\n+)+?automount-open=false$ + 1 + + + /etc/dconf/db/local.d/locks/ + ^.*$ + ^/org/gnome/desktop/media-handling/automount-open$ + 1 + + + /etc/dconf/db/local.d/ + ^.*$ + ^\[org/gnome/desktop/media-handling\]([^\n]*\n+)+?autorun-never=true$ + 1 + + + /etc/dconf/db/local.d/locks/ + ^.*$ + ^/org/gnome/desktop/media-handling/autorun-never$ + 1 + + + /etc/dconf/db/local.d/ + ^.*$ + ^\[org/gnome/desktop/thumbnailers\]([^\n]*\n+)+?disable-all=true$ + 1 + + + /etc/dconf/db/local.d/locks/ + ^.*$ + ^/org/gnome/desktop/thumbnailers/disable-all$ + 1 + + + /etc/dconf/db/local.d/ + ^.*$ + ^\[org/gnome/nm-applet\]([^\n]*\n+)+?disable-wifi-create=true$ + 1 + + + /etc/dconf/db/local.d/locks/ + ^.*$ + ^/org/gnome/nm-applet/disable-wifi-create$ + 1 + + + /etc/dconf/db/local.d/ + ^.*$ + ^\[org/gnome/nm-applet\]([^\n]*\n+)+?suppress-wireless-networks-available=true$ + 1 + + + /etc/dconf/db/local.d/locks/ + ^.*$ + ^/org/gnome/nm-applet/suppress-wireless-networks-available$ + 1 + + + /etc/dconf/db/local.d/ + ^.*$ + ^\[org/gnome/Vino\]([^\n]*\n+)+?authentication-methods=\['vnc'\]$ + 1 + + + /etc/dconf/db/local.d/locks/ + ^.*$ + ^/org/gnome/Vino/authentication-methods$ + 1 + + + /etc/dconf/db/local.d/ + ^.*$ + ^\[org/gnome/Vino\]([^\n]*\n+)+?require-encryption=true$ + 1 + + + /etc/dconf/db/local.d/locks/ + ^.*$ + ^/org/gnome/Vino/require-encryption$ + 1 + + + /etc/dconf/db/local.d/ + ^.*$ + ^\[org/gnome/desktop/screensaver\]([^\n]*\n+)+?idle-activation-enabled=true$ + 1 + + + /etc/dconf/db/local.d/locks/ + ^.*$ + ^/org/gnome/desktop/screensaver/idle-activation-enabled$ + 1 + + + /etc/dconf/db/local.d/locks/ + ^.*$ + ^/org/gnome/desktop/screensaver/idle-activation-enabled$ + 1 + + + /etc/dconf/db/local.d/ + ^.*$ + ^\[org/gnome/desktop/session\]([^\n]*\n+)+?idle-delay=uint32[\s][0-9]*$ + 1 + + + /etc/dconf/db/local.d/ + ^.*$ + ^idle-delay[\s=]*uint32[\s]([^=\s]*) + 1 + + + /etc/dconf/db/local.d/ + ^.*$ + ^\[org/gnome/desktop/screensaver\]([^\n]*\n+)+?lock-delay=uint32[\s][0-9]*$ + 1 + + + /etc/dconf/db/local.d/ + ^.*$ + ^lock-delay[\s=]*uint32[\s]([^=\s]*) + 1 + + + /etc/dconf/db/local.d/ + ^.*$ + ^\[org/gnome/desktop/screensaver\]([^\n]*\n+)+?lock-enabled=true$ + 1 + + + /etc/dconf/db/local.d/locks/ + ^.*$ + ^/org/gnome/desktop/screensaver/lock-enabled$ + 1 + + + /etc/dconf/db/local.d/locks/ + ^.*$ + ^/org/gnome/desktop/screensaver/lock-enabled$ + 1 + + + /etc/dconf/db/local.d/ + ^.*$ + ^\[org/gnome/desktop/screensaver\]([^\n]*\n+)+?picture-uri=string \'\'$ + 1 + + + /etc/dconf/db/local.d/locks/ + ^.*$ + ^/org/gnome/desktop/screensaver/picture-uri$ + 1 + + + /etc/dconf/db/local.d/ + ^.*$ + ^\[org/gnome/desktop/screensaver\]([^\n]*\n+)+?show-full-name-in-top-bar=false$ + 1 + + + /etc/dconf/db/local.d/locks/ + ^.*$ + ^/org/gnome/desktop/screensaver/show-full-name-in-top-bar$ + 1 + + + /etc/dconf/db/local.d/locks/ + ^.*$ + ^/org/gnome/desktop/screensaver/lock-delay$ + 1 + + + /etc/dconf/db/local.d/locks/ + ^.*$ + ^/org/gnome/desktop/session/idle-delay$ + 1 + + + /etc/dconf/db/local.d/ + ^.*$ + ^\[org/gnome/settings-daemon/plugins/media-keys\]([^\n]*\n+)+?logout[\s]*=[\s]*''$ + 1 + + + /etc/dconf/db/local.d/locks/ + ^.*$ + ^/org/gnome/settings-daemon/plugins/media-keys/logout$ + 1 + + + /etc/dconf/db/local.d/ + ^.*$ + ^\[org/gnome/system/location\]([^\n]*\n+)+?enabled=false$ + 1 + + + /etc/dconf/db/local.d/locks/ + ^.*$ + ^/org/gnome/system/location/enabled$ + 1 + + + /etc/dconf/db/local.d/ + ^.*$ + ^\[org/gnome/clocks\]([^\n]*\n+)+?geolocation=false$ + 1 + + + /etc/dconf/db/local.d/locks/ + ^.*$ + ^/org/gnome/clocks/geolocation$ + 1 + + + /etc/dconf/db/local.d/ + ^.*$ + ^\[org/gnome/settings-daemon/plugins/power\]([^\n]*\n+)+?active=false$ + 1 + + + /etc/dconf/db/local.d/locks/ + ^.*$ + ^/org/gnome/settings-daemon/plugins/power/active$ + 1 + + + /etc/named.conf + ^\s*include\s+"/etc/crypto-policies/back-ends/bind.config"\s*;\s*$ + 1 + + + /etc/crypto-policies/state/current + + + /etc/crypto-policies/config + + + oval:ssg-variable_crypto_policies_config_file_timestamp:var:1 + + + /etc/crypto-policies/config + ^(?!#)(\S+)$ + 1 + + + /etc/crypto-policies/state/current + ^(?!#)(\S+)$ + 1 + + + /etc/crypto-policies/back-ends/nss.config + + + /etc/crypto-policies/back-ends/gnutls.config + \+VERS-ALL:-VERS-DTLS0\.9:-VERS-SSL3\.0:-VERS-TLS1\.0:-VERS-TLS1\.1:-VERS-DTLS1\.0 + 1 + + + oval:ssg-var_symlink_kerberos_crypto_policy_configuration:var:1 + + + /etc/krb5.conf.d/crypto-policies + + + /etc/crypto-policies/back-ends/krb5.config + + + /etc/ipsec.conf + ^\s*include\s+/etc/crypto-policies/back-ends/libreswan.config\s*(?:#.*)?$ + 1 + + + /etc/pki/tls/openssl.cnf + ^\s*\[\s*crypto_policy\s*\]\s*\n*\s*\.include\s*(?:=\s*)?/etc/crypto-policies/back-ends/opensslcnf.config\s*$ + 1 + + + /etc/crypto-policies/back-ends/opensslcnf.config + ^\s*(?:TLS\.)?(?i)MinProtocol\s*=\s*TLSv(\S*) + 1 + + + /etc/crypto-policies/back-ends/opensslcnf.config + ^\s*(?:DTLS\.)?(?i)MinProtocol\s*=\s*DTLSv(\S*) + 1 + + + crypto-policies + + + /etc/sysconfig/sshd + ^\s*(?i)CRYPTO_POLICY\s*=.*$ + 1 + + + /etc/ssh/ssh_config.d/02-ospp.conf + ^[ \t]*Match[\s]+(.+?)[ \t]*(?:$|#) + 1 + + + /etc/ssh/ssh_config.d/02-ospp.conf + ^Match final all(?:.* +)*?\s*RekeyLimit[\s]+(.+?)[ \t]*(?:$|#) + 1 + + + /etc/ssh/ssh_config.d/02-ospp.conf + ^Match final all(?:.* +)*?\s*GSSAPIAuthentication[\s]+(.+?)[ \t]*(?:$|#) + 1 + + + /etc/ssh/ssh_config.d/02-ospp.conf + ^Match final all(?:.* +)*?\s*Ciphers[\s]+(.+?)[ \t]*(?:$|#) + 1 + + + /etc/ssh/ssh_config.d/02-ospp.conf + ^Match final all(?:.* +)*?\s*PubkeyAcceptedKeyTypes[\s]+(.+?)[ \t]*(?:$|#) + 1 + + + /etc/ssh/ssh_config.d/02-ospp.conf + ^Match final all(?:.* +)*?\s*MACs[\s]+(.+?)[ \t]*(?:$|#) + 1 + + + /etc/ssh/ssh_config.d/02-ospp.conf + ^Match final all(?:.* +)*?\s*KexAlgorithms[\s]+(.+?)[ \t]*(?:$|#) + 1 + + + /etc/crypto-policies/back-ends/openssh.config + ^Ciphers.*$ + 1 + + + /etc/crypto-policies/back-ends/opensshserver.config + ^(?!#).*(-oCiphers=[^\s']+).*$ + 1 + + + /etc/crypto-policies/back-ends/openssh.config + ^MACs.*$ + 1 + + + /etc/crypto-policies/back-ends/opensshserver.config + ^(?!#).*(-oMACs=\S+).+$ + 1 + + + /etc/selinux/config + ^[\s]*SELINUX[\s]*=[\s]*enforcing[\s]*$ + 1 + + + McAfeeVSEForLinux + + + MFErt + + + MFEcma + + + ^mfetpd.*$ + 0 + + + /opt/McAfee/accm/bin + accm + + + /opt/McAfee/auditengine/bin + auditmanager + + + /etc/dracut.conf.d/40-fips.conf + ^\s*add_dracutmodules\+="\s*(\w*)\s*"\s*(?:#.*)?$ + 1 + + + oval:ssg-var_system_crypto_policy:var:1 + + + /etc/system-fips + + + crypto.fips_enabled + + + /etc/aide.conf + ^@@define[\s]DBDIR[\s]+(/.*)$ + 1 + + + /etc/aide.conf + ^database_out=file:@@{DBDIR}/([a-z.]+)$ + 1 + + + /etc/aide.conf + ^database=file:@@{DBDIR}/([a-z.]+)$ + 1 + + + + + + + + + /etc/aide.conf + ^\/usr\/sbin\/auditctl\s+([^\n]+)$ + 1 + + + /etc/aide.conf + ^/usr/sbin/auditd\s+([^\n]+)$ + 1 + + + /etc/aide.conf + ^/usr/sbin/ausearch\s+([^\n]+)$ + 1 + + + /etc/aide.conf + ^/usr/sbin/aureport\s+([^\n]+)$ + 1 + + + /etc/aide.conf + ^/usr/sbin/autrace\s+([^\n]+)$ + 1 + + + /etc/aide.conf + ^/usr/sbin/audispd\s+([^\n]+)$ + 1 + + + /etc/aide.conf + ^/usr/sbin/rsyslogd\s+([^\n]+)$ + 1 + + + /etc/aide.conf + ^/usr/sbin/augenrules\s+([^\n]+)$ + 1 + + + /etc/crontab + ^(([0-9]*[\s]*[0-9]*[\s]*\*[\s]*\*[\s]*(\*|([0-7]|mon|tue|wed|thu|fri|sat|sun)|[0-7]-[0-7]))|@(hourly|daily|weekly))[\s]*root[\s]*\/usr\/sbin\/aide[\s]*\-\-check.*$ + 1 + + + /etc/cron.d + ^.*$ + ^(([0-9]*[\s]*[0-9]*[\s]*\*[\s]*\*[\s]*(\*|([0-7]|mon|tue|wed|thu|fri|sat|sun)|[0-7]-[0-7]))|@(hourly|daily|weekly))[\s]*root[\s]*\/usr\/sbin\/aide[\s]*\-\-check.*$ + 1 + + + /var/spool/cron/root + ^(([0-9]*[\s]*[0-9]*[\s]*\*[\s]*\*[\s]*(\*|([0-7]|mon|tue|wed|thu|fri|sat|sun)|[0-7]-[0-7]))|@(hourly|daily|weekly))[\s]*(root)?[\s]*\/usr\/sbin\/aide[\s]*\-\-check.*$ + 1 + + + ^/etc/cron.(daily|weekly)$ + ^.*$ + ^\s*\/usr\/sbin\/aide[\s]*\-\-check.*$ + 1 + + + /etc/crontab + ^.*/usr/sbin/aide[\s]*\-\-check.*\|.*/bin/mail[\s]*-s[\s]*".*"[\s]*.+@.+$ + 1 + + + /var/spool/cron/root + ^.*/usr/sbin/aide[\s]*\-\-check.*\|.*/bin/mail[\s]*-s[\s]*".*"[\s]*.+@.+$ + 1 + + + ^/etc/cron.(d|daily|weekly|monthly)$ + ^.*$ + ^.*/usr/sbin/aide[\s]*\-\-check.*\|.*/bin/mail[\s]*-s[\s]*".*"[\s]*.+@.+$ + 1 + + + /etc/aide.conf + ^[A-Z][a-zA-Z_]*[\s]*=[\s]*.*(sha1|rmd160|sha256|whirlpool|tiger|haval|gost|crc32).*$ + 0 + + + /etc/aide.conf + ^[A-Z][A-Za-z_]*[\s]*=[\s]*([a-zA-Z0-9\+]*)$ + 1 + + + /etc/aide.conf + ^(?!ALLXTRAHASHES)[A-Z][a-zA-Z_]*[\s]*=[\s]*([a-zA-Z0-9\+]*)$ + 1 + + + /etc/aide.conf + ^(?!ALLXTRAHASHES)[A-Z][a-zA-Z_]*[\s]*=[\s]*([a-zA-Z0-9\+]*)$ + 1 + + + + .* + .* + .* + .* + .* + ^/(bin|sbin|lib|lib64|usr)/.+$ + oval:ssg-state_files_fail_md5_hash:ste:1 + + + + .* + .* + .* + .* + .* + .* + oval:ssg-state_files_fail_user_ownership:ste:1 + + + + .* + .* + .* + .* + .* + .* + oval:ssg-state_files_fail_group_ownership:ste:1 + + + + .* + .* + .* + .* + .* + .* + oval:ssg-state_files_fail_mode:ste:1 + + + /etc/sudoers + ^(?!#).*[\s]+\!authenticate.*$ + 1 + + + /etc/sudoers.d + ^.*$ + ^(?!#).*[\s]+\!authenticate.*$ + 1 + + + /etc/sudoers + ^(?!#).*[\s]+NOPASSWD[\s]*\:.*$ + 1 + + + /etc/sudoers.d + ^.*$ + ^(?!#).*[\s]+NOPASSWD[\s]*\:.*$ + 1 + + + ^/etc/sudoers(\.d/.*)?$ + ^[\s]*Defaults[\s]+timestamp_timeout=([-]?[\d]+)$ + 1 + + + ^/etc/sudoers(\.d/.*)?$ + ^\s*ALL\s+ALL\=\(ALL\)\s+ALL\s*$ + 1 + + + ^/etc/sudoers(\.d/.*)?$ + ^\s*ALL\s+ALL\=\(ALL\:ALL\)\s+ALL\s* + 1 + + + /etc/sudoers + ^(?!(#|vdsm.*)).*[\s]+NOPASSWD[\s]*\:.*$ + 1 + + + /etc/sudoers.d + ^.*$ + ^(?!(#|vdsm.*)).*[\s]+NOPASSWD[\s]*\:.*$ + 1 + + + /etc/sudoers + ^#includedir[\s]+(.*)$ + 1 + + + /etc/sudoers + ^[#@]include[\s]+.*$ + 1 + + + /etc/sudoers + ^@includedir[\s]+.*$ + 1 + + + /etc/sudoers + ^[#@]includedir[\s]+.*$ + 1 + + + /etc/sudoers.d/ + .* + ^[#@]include(?:dir)?[\s]+.*$ + 1 + + + ^/etc/sudoers(\.d/.*)?$ + ^(?:\s*[^#=]+)=(?:\s*(?:\([^\)]+\))?\s*(?!\s*\()[^,\s]+(?:[ \t]+[^,\s]+)+[ \t]*,)*(\s*(?:\([^\)]+\))?\s*(?!\s*\()[^,\s]+[ \t]*(?:,|$)) + 1 + + + ^/etc/sudoers(\.d/.*)?$ + ^(?:\s*[^#=]+)=(?:\s*(?:\([^\)]+\))?\s*(?!\s*\()[^,!\n][^,\n]+,)*\s*(?:\([^\)]+\))?\s*(?!\s*\()(!\S+).* + 1 + + + ^/etc/sudoers(\.d/.*)?$ + ^\s*((?!root\b)[\w]+)\s*(\w+)\s*=\s*(.*,)?\s*\([\w\s]*\b(root|ALL)\b[\w\s]*\) + 1 + + + ^/etc/sudoers(\.d/.*)?$ + ^\s*((?!root\b)[\w]+)\s*(\w+)\s*=\s*(.*,)?\s*[^\(\s] + 1 + + + ^/etc/sudoers(\.d/.*)?$ + ^Defaults !targetpw$\r?\n + 1 + + + ^/etc/sudoers(\.d/.*)?$ + ^Defaults !rootpw$\r?\n + 1 + + + ^/etc/sudoers(\.d/.*)?$ + ^Defaults !runaspw$\r?\n + 1 + + + ^/etc/sudoers(\.d/.*)?$ + ^Defaults targetpw$\r?\n + 1 + + + ^/etc/sudoers(\.d/.*)?$ + ^Defaults rootpw$\r?\n + 1 + + + ^/etc/sudoers(\.d/.*)?$ + ^Defaults runaspw$\r?\n + 1 + + + /etc/dnf/dnf.conf + ^\s*clean_requirements_on_remove\s*=\s*(1|True|yes)\s*$ + 1 + + + /etc/dnf/automatic.conf + ^\s*\[commands\].*(?:\n\s*[^[\s].*)*\n^\s*apply_updates[ \t]*=[ \t]*(.+?)[ \t]*(?:$|#) + 1 + + + ^/etc/dnf/automatic.conf + + + /etc/dnf/automatic.conf + ^\s*\[commands\].*(?:\n\s*[^[\s].*)*\n^\s*upgrade_type[ \t]*=[ \t]*(.+?)[ \t]*(?:$|#) + 1 + + + ^/etc/dnf/automatic.conf + + + /etc/dnf/dnf.conf + ^\s*gpgcheck\s*=\s*1\s*$ + 1 + + + /etc/dnf/dnf.conf + ^\s*localpkg_gpgcheck\s*=\s*(1|True|yes)\s*$ + 1 + + + /etc/yum.repos.d + .* + ^\s*gpgcheck\s*=\s*0\s*$ + 1 + + + gpg-pubkey + + + /etc/security/pwquality.conf + ^\s*dcredit[\s]*=[\s]*(-?\d+)(?:[\s]|$) + 1 + + + /etc/security/pwquality.conf + ^\s*dictcheck[\s]*=[\s]*(\d+)(?:[\s]|$) + 1 + + + /etc/security/pwquality.conf + ^\s*difok[\s]*=[\s]*(\d+)(?:[\s]|$) + 1 + + + /etc/security/pwquality.conf + ^[\s]*local_users_only[\s]*$ + 1 + + + /etc/security/pwquality.conf + ^[\s]*enforce_for_root[\s]*$ + 1 + + + /etc/security/pwquality.conf + ^\s*lcredit[\s]*=[\s]*(-?\d+)(?:[\s]|$) + 1 + + + /etc/security/pwquality.conf + ^\s*maxclassrepeat[\s]*=[\s]*(\d+)(?:[\s]|$) + 1 + + + /etc/security/pwquality.conf + ^\s*maxrepeat[\s]*=[\s]*(\d+)(?:[\s]|$) + 1 + + + /etc/security/pwquality.conf + ^\s*minclass[\s]*=[\s]*(\d+)(?:[\s]|$) + 1 + + + /etc/security/pwquality.conf + ^\s*minlen[\s]*=[\s]*(\d+)(?:[\s]|$) + 1 + + + /etc/security/pwquality.conf + ^\s*ocredit[\s]*=[\s]*(-?\d+)(?:[\s]|$) + 1 + + + /etc/security/pwquality.conf + ^\s*ucredit[\s]*=[\s]*(-?\d+)(?:[\s]|$) + 1 + + + + /etc/audit/rules.d/30-ospp-v42-3-access-failed.rules + ^.*$ + 1 + + + + /etc/audit/rules.d/30-ospp-v42-3-access-failed.rules + ^.*$ + 1 + + + + /etc/audit/rules.d/30-ospp-v42-3-access-failed.rules + ^.*$ + 1 + + + + /etc/audit/rules.d/30-ospp-v42-3-access-success.rules + ^.*$ + 1 + + + + /etc/audit/rules.d/30-ospp-v42-3-access-success.rules + ^.*$ + 1 + + + + /etc/audit/rules.d/30-ospp-v42-3-access-success.rules + ^.*$ + 1 + + + + /etc/audit/rules.d/10-base-config.rules + ^.*$ + 1 + + + + /etc/audit/rules.d/30-ospp-v42-1-create-failed.rules + ^.*$ + 1 + + + + /etc/audit/rules.d/30-ospp-v42-1-create-failed.rules + ^.*$ + 1 + + + + /etc/audit/rules.d/30-ospp-v42-1-create-failed.rules + ^.*$ + 1 + + + + /etc/audit/rules.d/30-ospp-v42-1-create-success.rules + ^.*$ + 1 + + + + /etc/audit/rules.d/30-ospp-v42-1-create-success.rules + ^.*$ + 1 + + + + /etc/audit/rules.d/30-ospp-v42-1-create-success.rules + ^.*$ + 1 + + + + /etc/audit/rules.d/30-ospp-v42-4-delete-failed.rules + ^.*$ + 1 + + + + /etc/audit/rules.d/30-ospp-v42-4-delete-failed.rules + ^.*$ + 1 + + + + /etc/audit/rules.d/30-ospp-v42-4-delete-failed.rules + ^.*$ + 1 + + + + /etc/audit/rules.d/30-ospp-v42-4-delete-success.rules + ^.*$ + 1 + + + + /etc/audit/rules.d/30-ospp-v42-4-delete-success.rules + ^.*$ + 1 + + + + /etc/audit/rules.d/30-ospp-v42-4-delete-success.rules + ^.*$ + 1 + + + + /etc/audit/rules.d/11-loginuid.rules + ^.*$ + 1 + + + + /etc/audit/rules.d/30-ospp-v42-2-modify-failed.rules + ^.*$ + 1 + + + + /etc/audit/rules.d/30-ospp-v42-2-modify-failed.rules + ^.*$ + 1 + + + + /etc/audit/rules.d/30-ospp-v42-2-modify-failed.rules + ^.*$ + 1 + + + + /etc/audit/rules.d/30-ospp-v42-2-modify-success.rules + ^.*$ + 1 + + + + /etc/audit/rules.d/30-ospp-v42-2-modify-success.rules + ^.*$ + 1 + + + + /etc/audit/rules.d/30-ospp-v42-2-modify-success.rules + ^.*$ + 1 + + + + /etc/audit/rules.d/43-module-load.rules + ^.*$ + 1 + + + + /etc/audit/rules.d/43-module-load.rules + ^.*$ + 1 + + + + /etc/audit/rules.d/30-ospp-v42.rules + ^.*$ + 1 + + + + /etc/audit/rules.d/30-ospp-v42.rules + ^.*$ + 1 + + + + /etc/audit/rules.d/30-ospp-v42.rules + ^.*$ + 1 + + + + /etc/audit/rules.d/30-ospp-v42-6-owner-change-failed.rules + ^.*$ + 1 + + + + /etc/audit/rules.d/30-ospp-v42-6-owner-change-failed.rules + ^.*$ + 1 + + + + /etc/audit/rules.d/30-ospp-v42-6-owner-change-failed.rules + ^.*$ + 1 + + + + /etc/audit/rules.d/30-ospp-v42-6-owner-change-success.rules + ^.*$ + 1 + + + + /etc/audit/rules.d/30-ospp-v42-6-owner-change-success.rules + ^.*$ + 1 + + + + /etc/audit/rules.d/30-ospp-v42-6-owner-change-success.rules + ^.*$ + 1 + + + + /etc/audit/rules.d/30-ospp-v42-5-perm-change-failed.rules + ^.*$ + 1 + + + + /etc/audit/rules.d/30-ospp-v42-5-perm-change-failed.rules + ^.*$ + 1 + + + + /etc/audit/rules.d/30-ospp-v42-5-perm-change-failed.rules + ^.*$ + 1 + + + + /etc/audit/rules.d/30-ospp-v42-5-perm-change-success.rules + ^.*$ + 1 + + + + /etc/audit/rules.d/30-ospp-v42-5-perm-change-success.rules + ^.*$ + 1 + + + + /etc/audit/rules.d/30-ospp-v42-5-perm-change-success.rules + ^.*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/sbin\/init(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/sbin\/init(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/sbin\/poweroff(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/sbin\/poweroff(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/sbin\/reboot(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/sbin\/reboot(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/sbin\/shutdown(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/sbin\/shutdown(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+chmod[\s]+|([\s]+|[,])chmod([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+chmod[\s]+|([\s]+|[,])chmod([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+chmod[\s]+|([\s]+|[,])chmod([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+chmod[\s]+|([\s]+|[,])chmod([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+chown[\s]+|([\s]+|[,])chown([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+chown[\s]+|([\s]+|[,])chown([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+chown[\s]+|([\s]+|[,])chown([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+chown[\s]+|([\s]+|[,])chown([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+fchmod[\s]+|([\s]+|[,])fchmod([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+fchmod[\s]+|([\s]+|[,])fchmod([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+fchmod[\s]+|([\s]+|[,])fchmod([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+fchmod[\s]+|([\s]+|[,])fchmod([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+fchmodat[\s]+|([\s]+|[,])fchmodat([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+fchmodat[\s]+|([\s]+|[,])fchmodat([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+fchmodat[\s]+|([\s]+|[,])fchmodat([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+fchmodat[\s]+|([\s]+|[,])fchmodat([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+fchown[\s]+|([\s]+|[,])fchown([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+fchown[\s]+|([\s]+|[,])fchown([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+fchown[\s]+|([\s]+|[,])fchown([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+fchown[\s]+|([\s]+|[,])fchown([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+fchownat[\s]+|([\s]+|[,])fchownat([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+fchownat[\s]+|([\s]+|[,])fchownat([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+fchownat[\s]+|([\s]+|[,])fchownat([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+fchownat[\s]+|([\s]+|[,])fchownat([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+fremovexattr[\s]+|([\s]+|[,])fremovexattr([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+fremovexattr[\s]+|([\s]+|[,])fremovexattr([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+fremovexattr[\s]+|([\s]+|[,])fremovexattr([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+fremovexattr[\s]+|([\s]+|[,])fremovexattr([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+fremovexattr[\s]+|([\s]+|[,])fremovexattr([\s]+|[,])))(?:.*-F\s+auid=0[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+fremovexattr[\s]+|([\s]+|[,])fremovexattr([\s]+|[,])))(?:.*-F\s+auid=0[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+fremovexattr[\s]+|([\s]+|[,])fremovexattr([\s]+|[,])))(?:.*-F\s+auid=0[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+fremovexattr[\s]+|([\s]+|[,])fremovexattr([\s]+|[,])))(?:.*-F\s+auid=0[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+fsetxattr[\s]+|([\s]+|[,])fsetxattr([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+fsetxattr[\s]+|([\s]+|[,])fsetxattr([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+fsetxattr[\s]+|([\s]+|[,])fsetxattr([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+fsetxattr[\s]+|([\s]+|[,])fsetxattr([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+fsetxattr[\s]+|([\s]+|[,])fsetxattr([\s]+|[,])))(?:.*-F\s+auid=0[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+fsetxattr[\s]+|([\s]+|[,])fsetxattr([\s]+|[,])))(?:.*-F\s+auid=0[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+fsetxattr[\s]+|([\s]+|[,])fsetxattr([\s]+|[,])))(?:.*-F\s+auid=0[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+fsetxattr[\s]+|([\s]+|[,])fsetxattr([\s]+|[,])))(?:.*-F\s+auid=0[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+lchown[\s]+|([\s]+|[,])lchown([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+lchown[\s]+|([\s]+|[,])lchown([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+lchown[\s]+|([\s]+|[,])lchown([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+lchown[\s]+|([\s]+|[,])lchown([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+lremovexattr[\s]+|([\s]+|[,])lremovexattr([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+lremovexattr[\s]+|([\s]+|[,])lremovexattr([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+lremovexattr[\s]+|([\s]+|[,])lremovexattr([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+lremovexattr[\s]+|([\s]+|[,])lremovexattr([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+lremovexattr[\s]+|([\s]+|[,])lremovexattr([\s]+|[,])))(?:.*-F\s+auid=0[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+lremovexattr[\s]+|([\s]+|[,])lremovexattr([\s]+|[,])))(?:.*-F\s+auid=0[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+lremovexattr[\s]+|([\s]+|[,])lremovexattr([\s]+|[,])))(?:.*-F\s+auid=0[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+lremovexattr[\s]+|([\s]+|[,])lremovexattr([\s]+|[,])))(?:.*-F\s+auid=0[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+lsetxattr[\s]+|([\s]+|[,])lsetxattr([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+lsetxattr[\s]+|([\s]+|[,])lsetxattr([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+lsetxattr[\s]+|([\s]+|[,])lsetxattr([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+lsetxattr[\s]+|([\s]+|[,])lsetxattr([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+lsetxattr[\s]+|([\s]+|[,])lsetxattr([\s]+|[,])))(?:.*-F\s+auid=0[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+lsetxattr[\s]+|([\s]+|[,])lsetxattr([\s]+|[,])))(?:.*-F\s+auid=0[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+lsetxattr[\s]+|([\s]+|[,])lsetxattr([\s]+|[,])))(?:.*-F\s+auid=0[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+lsetxattr[\s]+|([\s]+|[,])lsetxattr([\s]+|[,])))(?:.*-F\s+auid=0[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+removexattr[\s]+|([\s]+|[,])removexattr([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+removexattr[\s]+|([\s]+|[,])removexattr([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+removexattr[\s]+|([\s]+|[,])removexattr([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+removexattr[\s]+|([\s]+|[,])removexattr([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+removexattr[\s]+|([\s]+|[,])removexattr([\s]+|[,])))(?:.*-F\s+auid=0[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+removexattr[\s]+|([\s]+|[,])removexattr([\s]+|[,])))(?:.*-F\s+auid=0[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+removexattr[\s]+|([\s]+|[,])removexattr([\s]+|[,])))(?:.*-F\s+auid=0[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+removexattr[\s]+|([\s]+|[,])removexattr([\s]+|[,])))(?:.*-F\s+auid=0[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+setxattr[\s]+|([\s]+|[,])setxattr([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+setxattr[\s]+|([\s]+|[,])setxattr([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+setxattr[\s]+|([\s]+|[,])setxattr([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+setxattr[\s]+|([\s]+|[,])setxattr([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+setxattr[\s]+|([\s]+|[,])setxattr([\s]+|[,])))(?:.*-F\s+auid=0[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+setxattr[\s]+|([\s]+|[,])setxattr([\s]+|[,])))(?:.*-F\s+auid=0[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+setxattr[\s]+|([\s]+|[,])setxattr([\s]+|[,])))(?:.*-F\s+auid=0[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+setxattr[\s]+|([\s]+|[,])setxattr([\s]+|[,])))(?:.*-F\s+auid=0[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+umount2[\s]+|([\s]+|[,])umount2([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+umount2[\s]+|([\s]+|[,])umount2([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+umount2[\s]+|([\s]+|[,])umount2([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+umount2[\s]+|([\s]+|[,])umount2([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/chacl(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/chacl(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/chcon(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/chcon(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/sbin\/restorecon(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/sbin\/restorecon(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/sbin\/semanage(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/sbin\/semanage(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/setfacl(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/setfacl(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/sbin\/setfiles(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/sbin\/setfiles(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/sbin\/setsebool(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/sbin\/setsebool(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/sbin\/seunshare(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/sbin\/seunshare(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+rename[\s]+|([\s]+|[,])rename([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+rename[\s]+|([\s]+|[,])rename([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+rename[\s]+|([\s]+|[,])rename([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+rename[\s]+|([\s]+|[,])rename([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+renameat[\s]+|([\s]+|[,])renameat([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+renameat[\s]+|([\s]+|[,])renameat([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+renameat[\s]+|([\s]+|[,])renameat([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+renameat[\s]+|([\s]+|[,])renameat([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+rmdir[\s]+|([\s]+|[,])rmdir([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+rmdir[\s]+|([\s]+|[,])rmdir([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+rmdir[\s]+|([\s]+|[,])rmdir([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+rmdir[\s]+|([\s]+|[,])rmdir([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+unlink[\s]+|([\s]+|[,])unlink([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+unlink[\s]+|([\s]+|[,])unlink([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+unlink[\s]+|([\s]+|[,])unlink([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+unlink[\s]+|([\s]+|[,])unlink([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+unlinkat[\s]+|([\s]+|[,])unlinkat([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+unlinkat[\s]+|([\s]+|[,])unlinkat([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+unlinkat[\s]+|([\s]+|[,])unlinkat([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+unlinkat[\s]+|([\s]+|[,])unlinkat([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^\-w[\s]+\/var\/log\/faillock[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ + 1 + + + /etc/audit/audit.rules + ^\-w[\s]+\/var\/log\/faillock[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^\-w[\s]+\/var\/log\/lastlog[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ + 1 + + + /etc/audit/audit.rules + ^\-w[\s]+\/var\/log\/lastlog[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^\-w[\s]+\/var\/log\/tallylog[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ + 1 + + + /etc/audit/audit.rules + ^\-w[\s]+\/var\/log\/tallylog[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+mount[\s]+|([\s]+|[,])mount([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+mount[\s]+|([\s]+|[,])mount([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+mount[\s]+|([\s]+|[,])mount([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+mount[\s]+|([\s]+|[,])mount([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/at(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/at(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/chage(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/chage(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/chsh(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/chsh(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/crontab(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/crontab(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/gpasswd(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/gpasswd(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/kmod(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/kmod(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/mount(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/mount(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/newgidmap(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/newgidmap(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/newgrp(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/newgrp(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/newuidmap(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/newuidmap(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/sbin\/pam_timestamp_check(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/sbin\/pam_timestamp_check(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/passwd(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/passwd(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/sbin\/postdrop(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/sbin\/postdrop(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/sbin\/postqueue(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/sbin\/postqueue(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/libexec\/pt_chown(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/libexec\/pt_chown(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/ssh-agent(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/ssh-agent(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/libexec\/openssh\/ssh-keysign(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/libexec\/openssh\/ssh-keysign(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/su(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/su(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/sudo(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/sudo(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/sudoedit(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/sudoedit(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/umount(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/umount(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/sbin\/unix_chkpwd(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/sbin\/unix_chkpwd(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/sbin\/unix_update(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/sbin\/unix_update(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/sbin\/userhelper(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/sbin\/userhelper(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/sbin\/usermod(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/sbin\/usermod(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/sbin\/usernetctl(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/sbin\/usernetctl(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + /etc/audit/audit.rules + + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^\-w[\s]+\/etc\/group[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ + 1 + + + /etc/audit/audit.rules + ^\-w[\s]+\/etc\/group[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^\-w[\s]+\/etc\/gshadow[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ + 1 + + + /etc/audit/audit.rules + ^\-w[\s]+\/etc\/gshadow[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^\-w[\s]+\/etc\/security\/opasswd[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ + 1 + + + /etc/audit/audit.rules + ^\-w[\s]+\/etc\/security\/opasswd[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^\-w[\s]+\/etc\/passwd[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ + 1 + + + /etc/audit/audit.rules + ^\-w[\s]+\/etc\/passwd[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^\-w[\s]+\/etc\/shadow[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ + 1 + + + /etc/audit/audit.rules + ^\-w[\s]+\/etc\/shadow[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ + 1 + + + /etc/audit/auditd.conf + ^[ \t]*(?i)freq(?-i)[ \t]*=[ \t]*(.+?)[ \t]*(?:$|#) + 1 + + + /etc/audit/auditd.conf + ^[ \t]*(?i)local_events(?-i)[ \t]*=[ \t]*(.+?)[ \t]*(?:$|#) + 1 + + + /etc/audit/auditd.conf + ^[ \t]*(?i)log_format(?-i)[ \t]*=[ \t]*(.+?)[ \t]*(?:$|#) + 1 + + + /etc/audit/auditd.conf + ^[ \t]*(?i)write_logs(?-i)[ \t]*=[ \t]*(.+?)[ \t]*(?:$|#) + 1 + + + /etc/audit/auditd.conf + ^[ \t]*(?i)write_logs(?-i)[ \t]*=[ \t]* + 1 + + + ^/boot/loader/entries/ostree-2-.*.conf + + + ^/boot/loader/entries/ostree-1-.*.conf + ^options (.*)$ + 1 + + + ^/boot/loader/entries/ostree-2-.*.conf + ^options (.*)$ + 1 + + + ^/proc/cmdline + ^BOOT_IMAGE(.*)$ + 1 + + + /etc/dconf/db/local.d/ + ^.*$ + ^\s*\[org/gnome/desktop/lockdown\].*(?:\n\s*[^[\s].*)*\n^\s*user-administration-disabled[ \t]*=[ \t]*(.+?)[ \t]*(?:$|#) + 1 + + + /etc/dconf/db/local.d/locks + ^.*$ + ^/org/gnome/desktop/lockdown/user-administration-disabled$ + 1 + + + /etc/dconf/db/local.d/ + ^.*$ + ^\s*\[org/gnome/settings-daemon/peripherals/smartcard\].*(?:\n\s*[^[\s].*)*\n^\s*removal-action[ \t]*=[ \t]*(.+?)[ \t]*(?:$|#) + 1 + + + /etc/dconf/db/local.d/locks + ^.*$ + ^/org/gnome/settings-daemon/peripherals/smartcard/removal-action$ + 1 + + + + /lib + + oval:ssg-symlink_file_groupownerdir_group_ownership_library_dirs_uid_0:ste:1 + oval:ssg-state_file_groupownerdir_group_ownership_library_dirs_gid_0_0:ste:1 + + + + /lib64 + + oval:ssg-symlink_file_groupownerdir_group_ownership_library_dirs_uid_0:ste:1 + oval:ssg-state_file_groupownerdir_group_ownership_library_dirs_gid_0_1:ste:1 + + + + /usr/lib + + oval:ssg-symlink_file_groupownerdir_group_ownership_library_dirs_uid_0:ste:1 + oval:ssg-state_file_groupownerdir_group_ownership_library_dirs_gid_0_2:ste:1 + + + + /usr/lib64 + + oval:ssg-symlink_file_groupownerdir_group_ownership_library_dirs_uid_0:ste:1 + oval:ssg-state_file_groupownerdir_group_ownership_library_dirs_gid_0_3:ste:1 + + + + /bin + + oval:ssg-symlink_file_ownerdir_ownership_binary_dirs_uid_0:ste:1 + oval:ssg-state_file_ownerdir_ownership_binary_dirs_uid_0_0:ste:1 + + + + /sbin + + oval:ssg-symlink_file_ownerdir_ownership_binary_dirs_uid_0:ste:1 + oval:ssg-state_file_ownerdir_ownership_binary_dirs_uid_0_1:ste:1 + + + + /usr/bin + + oval:ssg-symlink_file_ownerdir_ownership_binary_dirs_uid_0:ste:1 + oval:ssg-state_file_ownerdir_ownership_binary_dirs_uid_0_2:ste:1 + + + + /usr/sbin + + oval:ssg-symlink_file_ownerdir_ownership_binary_dirs_uid_0:ste:1 + oval:ssg-state_file_ownerdir_ownership_binary_dirs_uid_0_3:ste:1 + + + + /usr/local/bin + + oval:ssg-symlink_file_ownerdir_ownership_binary_dirs_uid_0:ste:1 + oval:ssg-state_file_ownerdir_ownership_binary_dirs_uid_0_4:ste:1 + + + + /usr/local/sbin + + oval:ssg-symlink_file_ownerdir_ownership_binary_dirs_uid_0:ste:1 + oval:ssg-state_file_ownerdir_ownership_binary_dirs_uid_0_5:ste:1 + + + + /lib + + oval:ssg-symlink_file_ownerdir_ownership_library_dirs_uid_0:ste:1 + oval:ssg-state_file_ownerdir_ownership_library_dirs_uid_0_0:ste:1 + + + + /lib64 + + oval:ssg-symlink_file_ownerdir_ownership_library_dirs_uid_0:ste:1 + oval:ssg-state_file_ownerdir_ownership_library_dirs_uid_0_1:ste:1 + + + + /usr/lib + + oval:ssg-symlink_file_ownerdir_ownership_library_dirs_uid_0:ste:1 + oval:ssg-state_file_ownerdir_ownership_library_dirs_uid_0_2:ste:1 + + + + /usr/lib64 + + oval:ssg-symlink_file_ownerdir_ownership_library_dirs_uid_0:ste:1 + oval:ssg-state_file_ownerdir_ownership_library_dirs_uid_0_3:ste:1 + + + + /bin + + oval:ssg-exclude_symlinks_dir_permissions_binary_dirs:ste:1 + oval:ssg-state_file_permissionsdir_permissions_binary_dirs_0_mode_0755or_stricter_:ste:1 + + + + /sbin + + oval:ssg-exclude_symlinks_dir_permissions_binary_dirs:ste:1 + oval:ssg-state_file_permissionsdir_permissions_binary_dirs_1_mode_0755or_stricter_:ste:1 + + + + /usr/bin + + oval:ssg-exclude_symlinks_dir_permissions_binary_dirs:ste:1 + oval:ssg-state_file_permissionsdir_permissions_binary_dirs_2_mode_0755or_stricter_:ste:1 + + + + /usr/sbin + + oval:ssg-exclude_symlinks_dir_permissions_binary_dirs:ste:1 + oval:ssg-state_file_permissionsdir_permissions_binary_dirs_3_mode_0755or_stricter_:ste:1 + + + + /usr/local/bin + + oval:ssg-exclude_symlinks_dir_permissions_binary_dirs:ste:1 + oval:ssg-state_file_permissionsdir_permissions_binary_dirs_4_mode_0755or_stricter_:ste:1 + + + + /usr/local/sbin + + oval:ssg-exclude_symlinks_dir_permissions_binary_dirs:ste:1 + oval:ssg-state_file_permissionsdir_permissions_binary_dirs_5_mode_0755or_stricter_:ste:1 + + + + /lib + + oval:ssg-exclude_symlinks_dir_permissions_library_dirs:ste:1 + oval:ssg-state_file_permissionsdir_permissions_library_dirs_0_mode_7755or_stricter_:ste:1 + + + + /lib64 + + oval:ssg-exclude_symlinks_dir_permissions_library_dirs:ste:1 + oval:ssg-state_file_permissionsdir_permissions_library_dirs_1_mode_7755or_stricter_:ste:1 + + + + /usr/lib + + oval:ssg-exclude_symlinks_dir_permissions_library_dirs:ste:1 + oval:ssg-state_file_permissionsdir_permissions_library_dirs_2_mode_7755or_stricter_:ste:1 + + + + /usr/lib64 + + oval:ssg-exclude_symlinks_dir_permissions_library_dirs:ste:1 + oval:ssg-state_file_permissionsdir_permissions_library_dirs_3_mode_7755or_stricter_:ste:1 + + + /etc/ssh/sshd_config + ^[ \t]*(?i)HostbasedAuthentication(?-i)[ \t]+(.+?)[ \t]*(?:$|#) + 1 + + + /etc/ssh/sshd_config.d + .*\.conf$ + ^[ \t]*(?i)HostbasedAuthentication(?-i)[ \t]+(.+?)[ \t]*(?:$|#) + 1 + + + /etc/at.deny + + + /sbin/auditctl + oval:ssg-symlink_file_groupownerfile_audit_tools_group_ownership_uid_0:ste:1 + oval:ssg-state_file_groupownerfile_audit_tools_group_ownership_gid_0_0:ste:1 + + + /sbin/aureport + oval:ssg-symlink_file_groupownerfile_audit_tools_group_ownership_uid_0:ste:1 + oval:ssg-state_file_groupownerfile_audit_tools_group_ownership_gid_0_1:ste:1 + + + /sbin/ausearch + oval:ssg-symlink_file_groupownerfile_audit_tools_group_ownership_uid_0:ste:1 + oval:ssg-state_file_groupownerfile_audit_tools_group_ownership_gid_0_2:ste:1 + + + /sbin/autrace + oval:ssg-symlink_file_groupownerfile_audit_tools_group_ownership_uid_0:ste:1 + oval:ssg-state_file_groupownerfile_audit_tools_group_ownership_gid_0_3:ste:1 + + + /sbin/auditd + oval:ssg-symlink_file_groupownerfile_audit_tools_group_ownership_uid_0:ste:1 + oval:ssg-state_file_groupownerfile_audit_tools_group_ownership_gid_0_4:ste:1 + + + /sbin/rsyslogd + oval:ssg-symlink_file_groupownerfile_audit_tools_group_ownership_uid_0:ste:1 + oval:ssg-state_file_groupownerfile_audit_tools_group_ownership_gid_0_5:ste:1 + + + /sbin/augenrules + oval:ssg-symlink_file_groupownerfile_audit_tools_group_ownership_uid_0:ste:1 + oval:ssg-state_file_groupownerfile_audit_tools_group_ownership_gid_0_6:ste:1 + + + /sbin/auditctl + oval:ssg-symlink_file_ownerfile_audit_tools_ownership_uid_0:ste:1 + oval:ssg-state_file_ownerfile_audit_tools_ownership_uid_0_0:ste:1 + + + /sbin/aureport + oval:ssg-symlink_file_ownerfile_audit_tools_ownership_uid_0:ste:1 + oval:ssg-state_file_ownerfile_audit_tools_ownership_uid_0_1:ste:1 + + + /sbin/ausearch + oval:ssg-symlink_file_ownerfile_audit_tools_ownership_uid_0:ste:1 + oval:ssg-state_file_ownerfile_audit_tools_ownership_uid_0_2:ste:1 + + + /sbin/autrace + oval:ssg-symlink_file_ownerfile_audit_tools_ownership_uid_0:ste:1 + oval:ssg-state_file_ownerfile_audit_tools_ownership_uid_0_3:ste:1 + + + /sbin/auditd + oval:ssg-symlink_file_ownerfile_audit_tools_ownership_uid_0:ste:1 + oval:ssg-state_file_ownerfile_audit_tools_ownership_uid_0_4:ste:1 + + + /sbin/rsyslogd + oval:ssg-symlink_file_ownerfile_audit_tools_ownership_uid_0:ste:1 + oval:ssg-state_file_ownerfile_audit_tools_ownership_uid_0_5:ste:1 + + + /sbin/augenrules + oval:ssg-symlink_file_ownerfile_audit_tools_ownership_uid_0:ste:1 + oval:ssg-state_file_ownerfile_audit_tools_ownership_uid_0_6:ste:1 + + + /sbin/auditctl + oval:ssg-exclude_symlinks_file_audit_tools_permissions:ste:1 + oval:ssg-state_file_permissionsfile_audit_tools_permissions_0_mode_0755or_stricter_:ste:1 + + + /sbin/aureport + oval:ssg-exclude_symlinks_file_audit_tools_permissions:ste:1 + oval:ssg-state_file_permissionsfile_audit_tools_permissions_1_mode_0755or_stricter_:ste:1 + + + /sbin/ausearch + oval:ssg-exclude_symlinks_file_audit_tools_permissions:ste:1 + oval:ssg-state_file_permissionsfile_audit_tools_permissions_2_mode_0755or_stricter_:ste:1 + + + /sbin/autrace + oval:ssg-exclude_symlinks_file_audit_tools_permissions:ste:1 + oval:ssg-state_file_permissionsfile_audit_tools_permissions_3_mode_0755or_stricter_:ste:1 + + + /sbin/auditd + oval:ssg-exclude_symlinks_file_audit_tools_permissions:ste:1 + oval:ssg-state_file_permissionsfile_audit_tools_permissions_4_mode_0755or_stricter_:ste:1 + + + /sbin/rsyslogd + oval:ssg-exclude_symlinks_file_audit_tools_permissions:ste:1 + oval:ssg-state_file_permissionsfile_audit_tools_permissions_5_mode_0755or_stricter_:ste:1 + + + /sbin/augenrules + oval:ssg-exclude_symlinks_file_audit_tools_permissions:ste:1 + oval:ssg-state_file_permissionsfile_audit_tools_permissions_6_mode_0755or_stricter_:ste:1 + + + /etc/cron.deny + + + /etc/at.allow + oval:ssg-symlink_file_groupowner_at_allow_uid_0:ste:1 + oval:ssg-state_file_groupowner_at_allow_gid_0_0:ste:1 + + + /etc/group- + oval:ssg-symlink_file_groupowner_backup_etc_group_uid_0:ste:1 + oval:ssg-state_file_groupowner_backup_etc_group_gid_0_0:ste:1 + + + /etc/gshadow- + oval:ssg-symlink_file_groupowner_backup_etc_gshadow_uid_0:ste:1 + oval:ssg-state_file_groupowner_backup_etc_gshadow_gid_0_0:ste:1 + + + /etc/passwd- + oval:ssg-symlink_file_groupowner_backup_etc_passwd_uid_0:ste:1 + oval:ssg-state_file_groupowner_backup_etc_passwd_gid_0_0:ste:1 + + + /etc/shadow- + oval:ssg-symlink_file_groupowner_backup_etc_shadow_uid_0:ste:1 + oval:ssg-state_file_groupowner_backup_etc_shadow_gid_0_0:ste:1 + + + /etc/cron.allow + oval:ssg-symlink_file_groupowner_cron_allow_uid_0:ste:1 + oval:ssg-state_file_groupowner_cron_allow_gid_0_0:ste:1 + + + /etc/cron.d + + oval:ssg-symlink_file_groupowner_cron_d_uid_0:ste:1 + oval:ssg-state_file_groupowner_cron_d_gid_0_0:ste:1 + + + /etc/cron.daily + + oval:ssg-symlink_file_groupowner_cron_daily_uid_0:ste:1 + oval:ssg-state_file_groupowner_cron_daily_gid_0_0:ste:1 + + + /etc/cron.hourly + + oval:ssg-symlink_file_groupowner_cron_hourly_uid_0:ste:1 + oval:ssg-state_file_groupowner_cron_hourly_gid_0_0:ste:1 + + + /etc/cron.monthly + + oval:ssg-symlink_file_groupowner_cron_monthly_uid_0:ste:1 + oval:ssg-state_file_groupowner_cron_monthly_gid_0_0:ste:1 + + + /etc/cron.weekly + + oval:ssg-symlink_file_groupowner_cron_weekly_uid_0:ste:1 + oval:ssg-state_file_groupowner_cron_weekly_gid_0_0:ste:1 + + + /etc/crontab + oval:ssg-symlink_file_groupowner_crontab_uid_0:ste:1 + oval:ssg-state_file_groupowner_crontab_gid_0_0:ste:1 + + + /etc/group + oval:ssg-symlink_file_groupowner_etc_group_uid_0:ste:1 + oval:ssg-state_file_groupowner_etc_group_gid_0_0:ste:1 + + + /etc/gshadow + oval:ssg-symlink_file_groupowner_etc_gshadow_uid_0:ste:1 + oval:ssg-state_file_groupowner_etc_gshadow_gid_0_0:ste:1 + + + /etc/passwd + oval:ssg-symlink_file_groupowner_etc_passwd_uid_0:ste:1 + oval:ssg-state_file_groupowner_etc_passwd_gid_0_0:ste:1 + + + /etc/shadow + oval:ssg-symlink_file_groupowner_etc_shadow_uid_0:ste:1 + oval:ssg-state_file_groupowner_etc_shadow_gid_0_0:ste:1 + + + /boot/grub2/grub.cfg + oval:ssg-symlink_file_groupowner_grub2_cfg_uid_0:ste:1 + oval:ssg-state_file_groupowner_grub2_cfg_gid_0_0:ste:1 + + + /etc/ssh/sshd_config + oval:ssg-symlink_file_groupowner_sshd_config_uid_0:ste:1 + oval:ssg-state_file_groupowner_sshd_config_gid_0_0:ste:1 + + + /var/log + + oval:ssg-symlink_file_groupowner_var_log_uid_0:ste:1 + oval:ssg-state_file_groupowner_var_log_gid_0_0:ste:1 + + + /var/log/messages + oval:ssg-symlink_file_groupowner_var_log_messages_uid_0:ste:1 + oval:ssg-state_file_groupowner_var_log_messages_gid_0_0:ste:1 + + + /var/log/syslog + oval:ssg-symlink_file_groupowner_var_log_syslog_uid_4:ste:1 + oval:ssg-state_file_groupowner_var_log_syslog_gid_4_0:ste:1 + + + /etc/audit + ^audit(\.rules|d\.conf)$ + oval:ssg-symlink_file_groupownership_audit_configuration_uid_0:ste:1 + oval:ssg-state_file_groupownership_audit_configuration_gid_0_0:ste:1 + + + /etc/audit/rules.d + ^.*\.rules$ + oval:ssg-symlink_file_groupownership_audit_configuration_uid_0:ste:1 + oval:ssg-state_file_groupownership_audit_configuration_gid_0_1:ste:1 + + + /etc/group- + oval:ssg-symlink_file_owner_backup_etc_group_uid_0:ste:1 + oval:ssg-state_file_owner_backup_etc_group_uid_0_0:ste:1 + + + /etc/gshadow- + oval:ssg-symlink_file_owner_backup_etc_gshadow_uid_0:ste:1 + oval:ssg-state_file_owner_backup_etc_gshadow_uid_0_0:ste:1 + + + /etc/passwd- + oval:ssg-symlink_file_owner_backup_etc_passwd_uid_0:ste:1 + oval:ssg-state_file_owner_backup_etc_passwd_uid_0_0:ste:1 + + + /etc/shadow- + oval:ssg-symlink_file_owner_backup_etc_shadow_uid_0:ste:1 + oval:ssg-state_file_owner_backup_etc_shadow_uid_0_0:ste:1 + + + /etc/cron.allow + oval:ssg-symlink_file_owner_cron_allow_uid_0:ste:1 + oval:ssg-state_file_owner_cron_allow_uid_0_0:ste:1 + + + /etc/cron.d + + oval:ssg-symlink_file_owner_cron_d_uid_0:ste:1 + oval:ssg-state_file_owner_cron_d_uid_0_0:ste:1 + + + /etc/cron.daily + + oval:ssg-symlink_file_owner_cron_daily_uid_0:ste:1 + oval:ssg-state_file_owner_cron_daily_uid_0_0:ste:1 + + + /etc/cron.hourly + + oval:ssg-symlink_file_owner_cron_hourly_uid_0:ste:1 + oval:ssg-state_file_owner_cron_hourly_uid_0_0:ste:1 + + + /etc/cron.monthly + + oval:ssg-symlink_file_owner_cron_monthly_uid_0:ste:1 + oval:ssg-state_file_owner_cron_monthly_uid_0_0:ste:1 + + + /etc/cron.weekly + + oval:ssg-symlink_file_owner_cron_weekly_uid_0:ste:1 + oval:ssg-state_file_owner_cron_weekly_uid_0_0:ste:1 + + + /etc/crontab + oval:ssg-symlink_file_owner_crontab_uid_0:ste:1 + oval:ssg-state_file_owner_crontab_uid_0_0:ste:1 + + + /etc/group + oval:ssg-symlink_file_owner_etc_group_uid_0:ste:1 + oval:ssg-state_file_owner_etc_group_uid_0_0:ste:1 + + + /etc/gshadow + oval:ssg-symlink_file_owner_etc_gshadow_uid_0:ste:1 + oval:ssg-state_file_owner_etc_gshadow_uid_0_0:ste:1 + + + /etc/passwd + oval:ssg-symlink_file_owner_etc_passwd_uid_0:ste:1 + oval:ssg-state_file_owner_etc_passwd_uid_0_0:ste:1 + + + /etc/shadow + oval:ssg-symlink_file_owner_etc_shadow_uid_0:ste:1 + oval:ssg-state_file_owner_etc_shadow_uid_0_0:ste:1 + + + /boot/grub2/grub.cfg + oval:ssg-symlink_file_owner_grub2_cfg_uid_0:ste:1 + oval:ssg-state_file_owner_grub2_cfg_uid_0_0:ste:1 + + + /etc/ssh/sshd_config + oval:ssg-symlink_file_owner_sshd_config_uid_0:ste:1 + oval:ssg-state_file_owner_sshd_config_uid_0_0:ste:1 + + + /var/log + + oval:ssg-symlink_file_owner_var_log_uid_0:ste:1 + oval:ssg-state_file_owner_var_log_uid_0_0:ste:1 + + + /var/log/messages + oval:ssg-symlink_file_owner_var_log_messages_uid_0:ste:1 + oval:ssg-state_file_owner_var_log_messages_uid_0_0:ste:1 + + + /var/log/syslog + oval:ssg-symlink_file_owner_var_log_syslog_uid_104:ste:1 + oval:ssg-state_file_owner_var_log_syslog_uid_104_0:ste:1 + + + /etc/audit + ^audit(\.rules|d\.conf)$ + oval:ssg-symlink_file_ownership_audit_configuration_uid_0:ste:1 + oval:ssg-state_file_ownership_audit_configuration_uid_0_0:ste:1 + + + /etc/audit/rules.d + ^.*\.rules$ + oval:ssg-symlink_file_ownership_audit_configuration_uid_0:ste:1 + oval:ssg-state_file_ownership_audit_configuration_uid_0_1:ste:1 + + + + /lib + ^.*$ + oval:ssg-symlink_file_ownership_library_dirs_uid_0:ste:1 + oval:ssg-state_file_ownership_library_dirs_uid_0_0:ste:1 + + + + /lib64 + ^.*$ + oval:ssg-symlink_file_ownership_library_dirs_uid_0:ste:1 + oval:ssg-state_file_ownership_library_dirs_uid_0_1:ste:1 + + + + /usr/lib + ^.*$ + oval:ssg-symlink_file_ownership_library_dirs_uid_0:ste:1 + oval:ssg-state_file_ownership_library_dirs_uid_0_2:ste:1 + + + + /usr/lib64 + ^.*$ + oval:ssg-symlink_file_ownership_library_dirs_uid_0:ste:1 + oval:ssg-state_file_ownership_library_dirs_uid_0_3:ste:1 + + + /etc/at.allow + oval:ssg-exclude_symlinks__at_allow:ste:1 + oval:ssg-state_file_permissions_at_allow_0_mode_0600or_stricter_:ste:1 + + + /etc/group- + oval:ssg-exclude_symlinks__backup_etc_group:ste:1 + oval:ssg-state_file_permissions_backup_etc_group_0_mode_0644or_stricter_:ste:1 + + + /etc/gshadow- + oval:ssg-exclude_symlinks__backup_etc_gshadow:ste:1 + oval:ssg-state_file_permissions_backup_etc_gshadow_0_mode_0000or_stricter_:ste:1 + + + /etc/passwd- + oval:ssg-exclude_symlinks__backup_etc_passwd:ste:1 + oval:ssg-state_file_permissions_backup_etc_passwd_0_mode_0644or_stricter_:ste:1 + + + /etc/shadow- + oval:ssg-exclude_symlinks__backup_etc_shadow:ste:1 + oval:ssg-state_file_permissions_backup_etc_shadow_0_mode_0000or_stricter_:ste:1 + + + /etc/cron.allow + oval:ssg-exclude_symlinks__cron_allow:ste:1 + oval:ssg-state_file_permissions_cron_allow_0_mode_0600or_stricter_:ste:1 + + + /etc/cron.d + + oval:ssg-exclude_symlinks__cron_d:ste:1 + oval:ssg-state_file_permissions_cron_d_0_mode_0700or_stricter_:ste:1 + + + /etc/cron.daily + + oval:ssg-exclude_symlinks__cron_daily:ste:1 + oval:ssg-state_file_permissions_cron_daily_0_mode_0700or_stricter_:ste:1 + + + /etc/cron.hourly + + oval:ssg-exclude_symlinks__cron_hourly:ste:1 + oval:ssg-state_file_permissions_cron_hourly_0_mode_0700or_stricter_:ste:1 + + + /etc/cron.monthly + + oval:ssg-exclude_symlinks__cron_monthly:ste:1 + oval:ssg-state_file_permissions_cron_monthly_0_mode_0700or_stricter_:ste:1 + + + /etc/cron.weekly + + oval:ssg-exclude_symlinks__cron_weekly:ste:1 + oval:ssg-state_file_permissions_cron_weekly_0_mode_0700or_stricter_:ste:1 + + + /etc/crontab + oval:ssg-exclude_symlinks__crontab:ste:1 + oval:ssg-state_file_permissions_crontab_0_mode_0600or_stricter_:ste:1 + + + /boot/grub2/grub.cfg + oval:ssg-exclude_symlinks__efi_grub2_cfg:ste:1 + oval:ssg-state_file_permissions_efi_grub2_cfg_0_mode_0700or_stricter_:ste:1 + + + /etc/audit/auditd.conf + oval:ssg-exclude_symlinks__etc_audit_auditd:ste:1 + oval:ssg-state_file_permissions_etc_audit_auditd_0_mode_0640or_stricter_:ste:1 + + + /etc/audit/rules.d + ^.*rules$ + oval:ssg-exclude_symlinks__etc_audit_rulesd:ste:1 + oval:ssg-state_file_permissions_etc_audit_rulesd_0_mode_0640or_stricter_:ste:1 + + + /etc/group + oval:ssg-exclude_symlinks__etc_group:ste:1 + oval:ssg-state_file_permissions_etc_group_0_mode_0644or_stricter_:ste:1 + + + /etc/gshadow + oval:ssg-exclude_symlinks__etc_gshadow:ste:1 + oval:ssg-state_file_permissions_etc_gshadow_0_mode_0000or_stricter_:ste:1 + + + /etc/issue + oval:ssg-exclude_symlinks__etc_issue:ste:1 + oval:ssg-state_file_permissions_etc_issue_0_mode_0644or_stricter_:ste:1 + + + /etc/motd + oval:ssg-exclude_symlinks__etc_motd:ste:1 + oval:ssg-state_file_permissions_etc_motd_0_mode_0644or_stricter_:ste:1 + + + /etc/passwd + oval:ssg-exclude_symlinks__etc_passwd:ste:1 + oval:ssg-state_file_permissions_etc_passwd_0_mode_0644or_stricter_:ste:1 + + + /etc/shadow + oval:ssg-exclude_symlinks__etc_shadow:ste:1 + oval:ssg-state_file_permissions_etc_shadow_0_mode_0000or_stricter_:ste:1 + + + /boot/grub2/grub.cfg + oval:ssg-exclude_symlinks__grub2_cfg:ste:1 + oval:ssg-state_file_permissions_grub2_cfg_0_mode_0600or_stricter_:ste:1 + + + + /lib + ^.*$ + oval:ssg-exclude_symlinks__library_dirs:ste:1 + oval:ssg-state_file_permissions_library_dirs_0_mode_7755or_stricter_:ste:1 + + + + /lib64 + ^.*$ + oval:ssg-exclude_symlinks__library_dirs:ste:1 + oval:ssg-state_file_permissions_library_dirs_1_mode_7755or_stricter_:ste:1 + + + + /usr/lib + ^.*$ + oval:ssg-exclude_symlinks__library_dirs:ste:1 + oval:ssg-state_file_permissions_library_dirs_2_mode_7755or_stricter_:ste:1 + + + + /usr/lib64 + ^.*$ + oval:ssg-exclude_symlinks__library_dirs:ste:1 + oval:ssg-state_file_permissions_library_dirs_3_mode_7755or_stricter_:ste:1 + + + /etc/ssh/sshd_config + oval:ssg-exclude_symlinks__sshd_config:ste:1 + oval:ssg-state_file_permissions_sshd_config_0_mode_0600or_stricter_:ste:1 + + + /etc/ssh + ^.*\.pub$ + oval:ssg-exclude_symlinks__sshd_pub_key:ste:1 + oval:ssg-state_file_permissions_sshd_pub_key_0_mode_0644or_stricter_:ste:1 + + + /var/log + + oval:ssg-exclude_symlinks__var_log:ste:1 + oval:ssg-state_file_permissions_var_log_0_mode_0755or_stricter_:ste:1 + + + /var/log/messages + oval:ssg-exclude_symlinks__var_log_messages:ste:1 + oval:ssg-state_file_permissions_var_log_messages_0_mode_0640or_stricter_:ste:1 + + + /var/log/syslog + oval:ssg-exclude_symlinks__var_log_syslog:ste:1 + oval:ssg-state_file_permissions_var_log_syslog_0_mode_0640or_stricter_:ste:1 + + + /etc/firewalld/firewalld.conf + ^[ \t]*FirewallBackend=(.+?)[ \t]*(?:$|#) + 1 + + + /etc/default/grub + ^\s*GRUB_CMDLINE_LINUX="(.*)"$ + 1 + + + /etc/default/grub + ^\s*GRUB_CMDLINE_LINUX_DEFAULT="(.*)"$ + 1 + + + /boot/loader/entries/ + ^.*\.conf$ + ^options (.*)$ + 1 + oval:ssg-state_grub2_rescue_entry_for_grub2_audit_argument:ste:1 + + + /etc/default/grub + ^\s*GRUB_CMDLINE_LINUX="(.*)"$ + 1 + + + /etc/default/grub + ^\s*GRUB_CMDLINE_LINUX_DEFAULT="(.*)"$ + 1 + + + /boot/loader/entries/ + ^.*\.conf$ + ^options (.*)$ + 1 + oval:ssg-state_grub2_rescue_entry_for_grub2_audit_backlog_limit_argument:ste:1 + + + /etc/default/grub + ^\s*GRUB_CMDLINE_LINUX="(.*)"$ + 1 + + + /etc/default/grub + ^\s*GRUB_CMDLINE_LINUX_DEFAULT="(.*)"$ + 1 + + + /boot/loader/entries/ + ^.*\.conf$ + ^options (.*)$ + 1 + oval:ssg-state_grub2_rescue_entry_for_grub2_enable_iommu_force:ste:1 + + + /etc/default/grub + ^\s*GRUB_CMDLINE_LINUX="(.*)"$ + 1 + + + /etc/default/grub + ^\s*GRUB_CMDLINE_LINUX_DEFAULT="(.*)"$ + 1 + + + /boot/loader/entries/ + ^.*\.conf$ + ^options (.*)$ + 1 + oval:ssg-state_grub2_rescue_entry_for_grub2_init_on_alloc_argument:ste:1 + + + /etc/default/grub + ^\s*GRUB_CMDLINE_LINUX="(.*)"$ + 1 + + + /etc/default/grub + ^\s*GRUB_CMDLINE_LINUX_DEFAULT="(.*)"$ + 1 + + + /boot/loader/entries/ + ^.*\.conf$ + ^options (.*)$ + 1 + oval:ssg-state_grub2_rescue_entry_for_grub2_ipv6_disable_argument:ste:1 + + + /etc/default/grub + ^\s*GRUB_CMDLINE_LINUX="(.*)"$ + 1 + + + /etc/default/grub + ^\s*GRUB_CMDLINE_LINUX_DEFAULT="(.*)"$ + 1 + + + /boot/loader/entries/ + ^.*\.conf$ + ^options (.*)$ + 1 + oval:ssg-state_grub2_rescue_entry_for_grub2_l1tf_argument:ste:1 + + + /etc/default/grub + ^\s*GRUB_CMDLINE_LINUX="(.*)"$ + 1 + + + /etc/default/grub + ^\s*GRUB_CMDLINE_LINUX_DEFAULT="(.*)"$ + 1 + + + /boot/loader/entries/ + ^.*\.conf$ + ^options (.*)$ + 1 + oval:ssg-state_grub2_rescue_entry_for_grub2_mce_argument:ste:1 + + + /etc/default/grub + ^\s*GRUB_CMDLINE_LINUX="(.*)"$ + 1 + + + /etc/default/grub + ^\s*GRUB_CMDLINE_LINUX_DEFAULT="(.*)"$ + 1 + + + /boot/loader/entries/ + ^.*\.conf$ + ^options (.*)$ + 1 + oval:ssg-state_grub2_rescue_entry_for_grub2_mds_argument:ste:1 + + + /etc/default/grub + ^\s*GRUB_CMDLINE_LINUX="(?!.*\bnosmap\b.*).*"$ + 1 + + + /etc/default/grub + ^\s*GRUB_CMDLINE_LINUX_DEFAULT="(?!.*\bnosmap\b).*"$ + 1 + + + /boot/loader/entries/ + ^.*\.conf$ + ^options (.*)$ + 1 + oval:ssg-state_grub2_rescue_entry_for_grub2_nosmap_argument_absent:ste:1 + + + /etc/default/grub + ^\s*GRUB_CMDLINE_LINUX="(?!.*\bnosmep\b.*).*"$ + 1 + + + /etc/default/grub + ^\s*GRUB_CMDLINE_LINUX_DEFAULT="(?!.*\bnosmep\b).*"$ + 1 + + + /boot/loader/entries/ + ^.*\.conf$ + ^options (.*)$ + 1 + oval:ssg-state_grub2_rescue_entry_for_grub2_nosmep_argument_absent:ste:1 + + + /etc/default/grub + ^\s*GRUB_CMDLINE_LINUX="(.*)"$ + 1 + + + /etc/default/grub + ^\s*GRUB_CMDLINE_LINUX_DEFAULT="(.*)"$ + 1 + + + /boot/loader/entries/ + ^.*\.conf$ + ^options (.*)$ + 1 + oval:ssg-state_grub2_rescue_entry_for_grub2_page_alloc_shuffle_argument:ste:1 + + + /etc/default/grub + ^\s*GRUB_CMDLINE_LINUX="(.*)"$ + 1 + + + /etc/default/grub + ^\s*GRUB_CMDLINE_LINUX_DEFAULT="(.*)"$ + 1 + + + /boot/loader/entries/ + ^.*\.conf$ + ^options (.*)$ + 1 + oval:ssg-state_grub2_rescue_entry_for_grub2_page_poison_argument:ste:1 + + + /etc/default/grub + ^\s*GRUB_CMDLINE_LINUX="(.*)"$ + 1 + + + /etc/default/grub + ^\s*GRUB_CMDLINE_LINUX_DEFAULT="(.*)"$ + 1 + + + /boot/loader/entries/ + ^.*\.conf$ + ^options (.*)$ + 1 + oval:ssg-state_grub2_rescue_entry_for_grub2_pti_argument:ste:1 + + + /etc/default/grub + ^\s*GRUB_CMDLINE_LINUX="(.*)"$ + 1 + + + /etc/default/grub + ^\s*GRUB_CMDLINE_LINUX_DEFAULT="(.*)"$ + 1 + + + /boot/loader/entries/ + ^.*\.conf$ + ^options (.*)$ + 1 + oval:ssg-state_grub2_rescue_entry_for_grub2_rng_core_default_quality_argument:ste:1 + + + /etc/default/grub + ^\s*GRUB_CMDLINE_LINUX="(.*)"$ + 1 + + + /etc/default/grub + ^\s*GRUB_CMDLINE_LINUX_DEFAULT="(.*)"$ + 1 + + + /boot/loader/entries/ + ^.*\.conf$ + ^options (.*)$ + 1 + oval:ssg-state_grub2_rescue_entry_for_grub2_slab_nomerge_argument:ste:1 + + + /etc/default/grub + ^\s*GRUB_CMDLINE_LINUX="(.*)"$ + 1 + + + /etc/default/grub + ^\s*GRUB_CMDLINE_LINUX_DEFAULT="(.*)"$ + 1 + + + /boot/loader/entries/ + ^.*\.conf$ + ^options (.*)$ + 1 + oval:ssg-state_grub2_rescue_entry_for_grub2_slub_debug_argument:ste:1 + + + /etc/default/grub + ^\s*GRUB_CMDLINE_LINUX="(.*)"$ + 1 + + + /etc/default/grub + ^\s*GRUB_CMDLINE_LINUX_DEFAULT="(.*)"$ + 1 + + + /boot/loader/entries/ + ^.*\.conf$ + ^options (.*)$ + 1 + oval:ssg-state_grub2_rescue_entry_for_grub2_spec_store_bypass_disable_argument:ste:1 + + + /etc/default/grub + ^\s*GRUB_CMDLINE_LINUX="(.*)"$ + 1 + + + /etc/default/grub + ^\s*GRUB_CMDLINE_LINUX_DEFAULT="(.*)"$ + 1 + + + /boot/loader/entries/ + ^.*\.conf$ + ^options (.*)$ + 1 + oval:ssg-state_grub2_rescue_entry_for_grub2_spectre_v2_argument:ste:1 + + + /etc/default/grub + ^\s*GRUB_CMDLINE_LINUX="(?!.*\bsystemd.debug-shell\b.*).*"$ + 1 + + + /etc/default/grub + ^\s*GRUB_CMDLINE_LINUX_DEFAULT="(?!.*\bsystemd.debug-shell\b).*"$ + 1 + + + /boot/loader/entries/ + ^.*\.conf$ + ^options (.*)$ + 1 + oval:ssg-state_grub2_rescue_entry_for_grub2_systemd_debug-shell_argument_absent:ste:1 + + + /etc/default/grub + ^\s*GRUB_CMDLINE_LINUX="(.*)"$ + 1 + + + /etc/default/grub + ^\s*GRUB_CMDLINE_LINUX_DEFAULT="(.*)"$ + 1 + + + /boot/loader/entries/ + ^.*\.conf$ + ^options (.*)$ + 1 + oval:ssg-state_grub2_rescue_entry_for_grub2_vsyscall_argument:ste:1 + + + openssl-pkcs11 + + + /etc/systemd/journald.conf + ^[ \t]*Compress=(.+?)[ \t]*(?:$|#) + 1 + + + /etc/systemd/journald.conf + ^[ \t]*ForwardToSyslog=(.+?)[ \t]*(?:$|#) + 1 + + + /etc/systemd/journald.conf + ^[ \t]*Storage=(.+?)[ \t]*(?:$|#) + 1 + + + ^/boot/config-.*$ + ^CONFIG_ACPI_CUSTOM_METHOD="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_acpi_custom_method_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_ARM64_SW_TTBR0_PAN="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_arm64_sw_ttbr0_pan_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_BINFMT_MISC="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_binfmt_misc_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_BUG="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_bug_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_BUG_ON_DATA_CORRUPTION="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_bug_on_data_corruption_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_COMPAT_BRK="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_compat_brk_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_COMPAT_VDSO="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_compat_vdso_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_DEBUG_CREDENTIALS="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_debug_credentials_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_DEBUG_FS="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_debug_fs_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_DEBUG_LIST="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_debug_list_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_DEBUG_NOTIFIERS="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_debug_notifiers_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_DEBUG_SG="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_debug_sg_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_DEBUG_WX="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_debug_wx_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_DEFAULT_MMAP_MIN_ADDR="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_default_mmap_min_addr_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_DEVKMEM="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_devkmem_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_FORTIFY_SOURCE="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_fortify_source_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_GCC_PLUGIN_LATENT_ENTROPY="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_gcc_plugin_latent_entropy_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_GCC_PLUGIN_RANDSTRUCT="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_gcc_plugin_randstruct_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_GCC_PLUGIN_STACKLEAK="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_gcc_plugin_stackleak_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_GCC_PLUGIN_STRUCTLEAK="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_gcc_plugin_structleak_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_GCC_PLUGIN_STRUCTLEAK_BYREF_ALL="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_gcc_plugin_structleak_byref_all_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_HARDENED_USERCOPY="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_hardened_usercopy_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_HARDENED_USERCOPY_FALLBACK="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_hardened_usercopy_fallback_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_HIBERNATION="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_hibernation_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_IA32_EMULATION="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_ia32_emulation_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_IPV6="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_ipv6_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_KEXEC="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_kexec_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_LEGACY_PTYS="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_legacy_ptys_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_LEGACY_VSYSCALL_EMULATE="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_legacy_vsyscall_emulate_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_LEGACY_VSYSCALL_NONE="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_legacy_vsyscall_none_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_LEGACY_VSYSCALL_XONLY="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_legacy_vsyscall_xonly_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_MODIFY_LDT_SYSCALL="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_modify_ldt_syscall_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_MODULE_SIG="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_module_sig_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_MODULE_SIG_ALL="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_module_sig_all_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_MODULE_SIG_FORCE="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_module_sig_force_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_MODULE_SIG_HASH="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_module_sig_hash_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_MODULE_SIG_KEY="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_module_sig_key_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_MODULE_SIG_SHA512="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_module_sig_sha512_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_PAGE_POISONING="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_page_poisoning_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_PAGE_POISONING_NO_SANITY="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_page_poisoning_no_sanity_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_PAGE_POISONING_ZERO="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_page_poisoning_zero_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_PAGE_TABLE_ISOLATION="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_page_table_isolation_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_PANIC_ON_OOPS="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_panic_on_oops_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_PANIC_TIMEOUT="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_panic_timeout_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_PROC_KCORE="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_proc_kcore_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_RANDOMIZE_BASE="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_randomize_base_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_RANDOMIZE_MEMORY="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_randomize_memory_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_REFCOUNT_FULL="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_refcount_full_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_RETPOLINE="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_retpoline_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_SCHED_STACK_END_CHECK="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_sched_stack_end_check_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_SECCOMP="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_seccomp_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_SECCOMP_FILTER="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_seccomp_filter_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_SECURITY="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_security_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_SECURITY_DMESG_RESTRICT="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_security_dmesg_restrict_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_SECURITY_WRITABLE_HOOKS="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_security_writable_hooks_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_SECURITY_YAMA="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_security_yama_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_SLAB_FREELIST_HARDENED="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_slab_freelist_hardened_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_SLAB_FRELIST_RANDOM="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_slab_frelist_random_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_SLAB_MERGE_DEFAULT="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_slab_merge_default_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_SLUB_DEBUG="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_slub_debug_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_STACKPROTECTOR="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_stackprotector_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_STACKPROTECTOR_STRONG="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_stackprotector_strong_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_STRICT_KERNEL_RWX="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_strict_kernel_rwx_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_STRICT_MODULE_RWX="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_strict_module_rwx_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_SYN_COOKIES="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_syn_cookies_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_UNMAP_KERNEL_AT_EL0="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_unmap_kernel_at_el0_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_VMAP_STACK="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_vmap_stack_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + ^/boot/config-.*$ + ^CONFIG_X86_VSYSCALL_EMULATION="?(.*?)"?$ + 1 + + + oval:ssg-local_var_config_x86_vsyscall_emulation_count_kernels_installed:var:1 + + + /boot + ^config-.*$ + + + + ^.*\.conf$ + ^\s*install\s+atm\s+(/bin/false|/bin/true)$ + 1 + + + + ^.*\.conf$ + ^blacklist\s+atm$ + 1 + + + + ^.*\.conf$ + ^\s*install\s+bluetooth\s+(/bin/false|/bin/true)$ + 1 + + + + ^.*\.conf$ + ^blacklist\s+bluetooth$ + 1 + + + + ^.*\.conf$ + ^\s*install\s+can\s+(/bin/false|/bin/true)$ + 1 + + + + ^.*\.conf$ + ^blacklist\s+can$ + 1 + + + + ^.*\.conf$ + ^\s*install\s+cfg80211\s+(/bin/false|/bin/true)$ + 1 + + + + ^.*\.conf$ + ^blacklist\s+cfg80211$ + 1 + + + + ^.*\.conf$ + ^\s*install\s+cramfs\s+(/bin/false|/bin/true)$ + 1 + + + + ^.*\.conf$ + ^blacklist\s+cramfs$ + 1 + + + + ^.*\.conf$ + ^\s*install\s+dccp\s+(/bin/false|/bin/true)$ + 1 + + + + ^.*\.conf$ + ^blacklist\s+dccp$ + 1 + + + + ^.*\.conf$ + ^\s*install\s+firewire-core\s+(/bin/false|/bin/true)$ + 1 + + + + ^.*\.conf$ + ^blacklist\s+firewire-core$ + 1 + + + + ^.*\.conf$ + ^\s*install\s+freevxfs\s+(/bin/false|/bin/true)$ + 1 + + + + ^.*\.conf$ + ^blacklist\s+freevxfs$ + 1 + + + + ^.*\.conf$ + ^\s*install\s+hfs\s+(/bin/false|/bin/true)$ + 1 + + + + ^.*\.conf$ + ^blacklist\s+hfs$ + 1 + + + + ^.*\.conf$ + ^\s*install\s+hfsplus\s+(/bin/false|/bin/true)$ + 1 + + + + ^.*\.conf$ + ^blacklist\s+hfsplus$ + 1 + + + + ^.*\.conf$ + ^\s*install\s+iwlmvm\s+(/bin/false|/bin/true)$ + 1 + + + + ^.*\.conf$ + ^blacklist\s+iwlmvm$ + 1 + + + + ^.*\.conf$ + ^\s*install\s+iwlwifi\s+(/bin/false|/bin/true)$ + 1 + + + + ^.*\.conf$ + ^blacklist\s+iwlwifi$ + 1 + + + + ^.*\.conf$ + ^\s*install\s+jffs2\s+(/bin/false|/bin/true)$ + 1 + + + + ^.*\.conf$ + ^blacklist\s+jffs2$ + 1 + + + + ^.*\.conf$ + ^\s*install\s+mac80211\s+(/bin/false|/bin/true)$ + 1 + + + + ^.*\.conf$ + ^blacklist\s+mac80211$ + 1 + + + + ^.*\.conf$ + ^\s*install\s+rds\s+(/bin/false|/bin/true)$ + 1 + + + + ^.*\.conf$ + ^blacklist\s+rds$ + 1 + + + + ^.*\.conf$ + ^\s*install\s+sctp\s+(/bin/false|/bin/true)$ + 1 + + + + ^.*\.conf$ + ^blacklist\s+sctp$ + 1 + + + + ^.*\.conf$ + ^\s*install\s+squashfs\s+(/bin/false|/bin/true)$ + 1 + + + + ^.*\.conf$ + ^blacklist\s+squashfs$ + 1 + + + + ^.*\.conf$ + ^\s*install\s+tipc\s+(/bin/false|/bin/true)$ + 1 + + + + ^.*\.conf$ + ^blacklist\s+tipc$ + 1 + + + + ^.*\.conf$ + ^\s*install\s+udf\s+(/bin/false|/bin/true)$ + 1 + + + + ^.*\.conf$ + ^blacklist\s+udf$ + 1 + + + + ^.*\.conf$ + ^\s*install\s+usb-storage\s+(/bin/false|/bin/true)$ + 1 + + + + ^.*\.conf$ + ^blacklist\s+usb-storage$ + 1 + + + + ^.*\.conf$ + ^\s*install\s+uvcvideo\s+(/bin/false|/bin/true)$ + 1 + + + + ^.*\.conf$ + ^blacklist\s+uvcvideo$ + 1 + + + + ^.*\.conf$ + ^\s*install\s+vfat\s+(/bin/false|/bin/true)$ + 1 + + + + ^.*\.conf$ + ^blacklist\s+vfat$ + 1 + + + /boot/efi + + + /boot + + + /boot + + + /boot + + + /boot + + + /dev/shm + + + /dev/shm + + + /dev/shm + + + /home + + + /home + + + /home + + + /etc/fstab + ^\s*\[?[\.\w:-]+\]?[:=][/\w-]+\s+[/\w\\-]+\s+nfs[4]?\s+(.*)$ + 0 + + + /etc/fstab + ^\s*\[?[\.\w:-]+\]?[:=][/\w-]+\s+[/\w\\-]+\s+nfs[4]?\s+(.*)$ + 0 + + + /etc/fstab + ^\s*\[?[\.\w:-]+\]?[:=][/\w-]+\s+[/\w\\-]+\s+nfs[4]?\s+(.*)$ + 0 + + + /etc/fstab + ^\s*\[?[\.\w:-]+\]?[:=][/\w-]+\s+[/\w\\-]+\s+nfs[4]?\s+(.*)$ + 0 + + + /etc/fstab + + 1 + + + /etc/fstab + + 1 + + + /etc/fstab + ^\s*\[?[\.\w:-]+\]?[:=][/\w-]+\s+[/\w\\-]+\s+nfs[4]?\s+(.*)$ + 0 + + + /etc/fstab + ^\s*\[?[\.\w:-]+\]?[:=][/\w-]+\s+[/\w\\-]+\s+nfs[4]?\s+(.*)$ + 0 + + + /etc/fstab + + 1 + + + /etc/fstab + + 1 + + + /etc/fstab + ^\s*\[?[\.\w:-]+\]?[:=][/\w-]+\s+[/\w\\-]+\s+nfs[4]?\s+(.*)$ + 0 + + + /etc/fstab + ^\s*\[?[\.\w:-]+\]?[:=][/\w-]+\s+[/\w\\-]+\s+nfs[4]?\s+(.*)$ + 0 + + + /etc/fstab + + 1 + + + /etc/fstab + + 1 + + + /opt + + + /proc + + + /srv + + + /tmp + + + /tmp + + + /tmp + + + /var/log/audit + + + /var/log/audit + + + /var/log/audit + + + /var/log + + + /var/log + + + /var/log + + + /var + + + /var + + + /var + + + /var/tmp + + + /var/tmp + + + /var/tmp + + + GConf2 + + + MFEhiplsm + + + aide + + + audispd-plugins + + + audit-audispd-plugins + + + audit + + + avahi + + + bind + + + chrony + + + cron + + + crypto-policies + + + dconf + + + dhcp-server + + + dnf-automatic + + + dovecot + + + esc + + + fapolicyd + + + firewalld + + + freeradius + + + gdm + + + gdm + + + geolite2-city + + + geolite2-country + + + gnutls-utils + + + gssproxy + + + httpd + + + inetutils-telnetd + + + iprutils + + + krb5-server + + + krb5-workstation + + + libreswan + + + libselinux + + + McAfeeTP + + + mcstrans + + + net-snmp + + + nfs-utils + + + nis + + + nss-tools + + + ntp + + + ntpdate + + + openldap-clients + + + openldap-servers + + + opensc + + + openscap-scanner + + + openssh-clients + + + openssh-server + + + openssh-server + + + pam_ldap + + + libpwquality + + + pcsc-lite + + + policycoreutils-python-utils + + + policycoreutils + + + postfix + + + prelink + + + quagga + + + rear + + + rng-tools + + + rsh-server + + + rsh + + + rsyslog-gnutls + + + rsyslog + + + samba-common + + + samba-common + + + samba + + + scap-security-guide + + + sendmail + + + setroubleshoot-plugins + + + setroubleshoot-server + + + setroubleshoot + + + squid + + + subscription-manager + + + sudo + + + syslog-ng + + + talk-server + + + talk + + + telnet-server + + + telnet + + + telnetd-ssl + + + telnetd + + + tftp-server + + + tftp + + + tmux + + + tuned + + + usbguard + + + vsftpd + + + xinetd + + + xorg-x11-server-common + + + ypbind + + + ypserv + + + /home + + + /srv + + + /tmp + + + /var + + + /var/log + + + /var/log/audit + + + /var/tmp + + + + /lib + + oval:ssg-symlink_file_groupownerroot_permissions_syslibrary_files_uid_0:ste:1 + oval:ssg-state_file_groupownerroot_permissions_syslibrary_files_gid_0_0:ste:1 + + + + /lib64 + + oval:ssg-symlink_file_groupownerroot_permissions_syslibrary_files_uid_0:ste:1 + oval:ssg-state_file_groupownerroot_permissions_syslibrary_files_gid_0_1:ste:1 + + + + /usr/lib + + oval:ssg-symlink_file_groupownerroot_permissions_syslibrary_files_uid_0:ste:1 + oval:ssg-state_file_groupownerroot_permissions_syslibrary_files_gid_0_2:ste:1 + + + + /usr/lib64 + + oval:ssg-symlink_file_groupownerroot_permissions_syslibrary_files_uid_0:ste:1 + oval:ssg-state_file_groupownerroot_permissions_syslibrary_files_gid_0_3:ste:1 + + + antivirus_can_scan_system + + + antivirus_use_jit + + + auditadm_exec_content + + + authlogin_nsswitch_use_ldap + + + authlogin_radius + + + authlogin_yubikey + + + awstats_purge_apache_log_files + + + boinc_execmem + + + cdrecord_read_content + + + cluster_can_network_connect + + + cluster_manage_all_files + + + cluster_use_execmem + + + cobbler_anon_write + + + cobbler_can_network_connect + + + cobbler_use_cifs + + + cobbler_use_nfs + + + collectd_tcp_network_connect + + + condor_tcp_network_connect + + + conman_can_network + + + container_connect_any + + + cron_can_relabel + + + cron_system_cronjob_use_shares + + + cron_userdomain_transition + + + cups_execmem + + + cvs_read_shadow + + + daemons_dump_core + + + daemons_enable_cluster_mode + + + daemons_use_tcp_wrapper + + + daemons_use_tty + + + dbadm_exec_content + + + dbadm_manage_user_files + + + dbadm_read_user_files + + + deny_execmem + + + deny_ptrace + + + dhcpc_exec_iptables + + + dhcpd_use_ldap + + + domain_fd_use + + + domain_kernel_load_modules + + + entropyd_use_audio + + + exim_can_connect_db + + + exim_manage_user_files + + + exim_read_user_files + + + fcron_crond + + + fenced_can_network_connect + + + fenced_can_ssh + + + fips_mode + + + ftpd_anon_write + + + ftpd_connect_all_unreserved + + + ftpd_connect_db + + + ftpd_full_access + + + ftpd_use_cifs + + + ftpd_use_fusefs + + + ftpd_use_nfs + + + ftpd_use_passive_mode + + + git_cgi_enable_homedirs + + + git_cgi_use_cifs + + + git_cgi_use_nfs + + + git_session_bind_all_unreserved_ports + + + git_session_users + + + git_system_enable_homedirs + + + git_system_use_cifs + + + git_system_use_nfs + + + gitosis_can_sendmail + + + glance_api_can_network + + + glance_use_execmem + + + glance_use_fusefs + + + global_ssp + + + gluster_anon_write + + + gluster_export_all_ro + + + gluster_export_all_rw + + + gpg_web_anon_write + + + gssd_read_tmp + + + guest_exec_content + + + haproxy_connect_any + + + httpd_anon_write + + + httpd_builtin_scripting + + + httpd_can_check_spam + + + httpd_can_connect_ftp + + + httpd_can_connect_ldap + + + httpd_can_connect_mythtv + + + httpd_can_connect_zabbix + + + httpd_can_network_connect + + + httpd_can_network_connect_cobbler + + + httpd_can_network_connect_db + + + httpd_can_network_memcache + + + httpd_can_network_relay + + + httpd_can_sendmail + + + httpd_dbus_avahi + + + httpd_dbus_sssd + + + httpd_dontaudit_search_dirs + + + httpd_enable_cgi + + + httpd_enable_ftp_server + + + httpd_enable_homedirs + + + httpd_execmem + + + httpd_graceful_shutdown + + + httpd_manage_ipa + + + httpd_mod_auth_ntlm_winbind + + + httpd_mod_auth_pam + + + httpd_read_user_content + + + httpd_run_ipa + + + httpd_run_preupgrade + + + httpd_run_stickshift + + + httpd_serve_cobbler_files + + + httpd_setrlimit + + + httpd_ssi_exec + + + httpd_sys_script_anon_write + + + httpd_tmp_exec + + + httpd_tty_comm + + + httpd_unified + + + httpd_use_cifs + + + httpd_use_fusefs + + + httpd_use_gpg + + + httpd_use_nfs + + + httpd_use_openstack + + + httpd_use_sasl + + + httpd_verify_dns + + + icecast_use_any_tcp_ports + + + irc_use_any_tcp_ports + + + irssi_use_full_network + + + kdumpgui_run_bootloader + + + kerberos_enabled + + + ksmtuned_use_cifs + + + ksmtuned_use_nfs + + + logadm_exec_content + + + logging_syslogd_can_sendmail + + + logging_syslogd_run_nagios_plugins + + + logging_syslogd_use_tty + + + login_console_enabled + + + logrotate_use_nfs + + + logwatch_can_network_connect_mail + + + lsmd_plugin_connect_any + + + mailman_use_fusefs + + + mcelog_client + + + mcelog_exec_scripts + + + mcelog_foreground + + + mcelog_server + + + minidlna_read_generic_user_content + + + mmap_low_allowed + + + mock_enable_homedirs + + + mount_anyfile + + + mozilla_plugin_bind_unreserved_ports + + + mozilla_plugin_can_network_connect + + + mozilla_plugin_use_bluejeans + + + mozilla_plugin_use_gps + + + mozilla_plugin_use_spice + + + mozilla_read_content + + + mpd_enable_homedirs + + + mpd_use_cifs + + + mpd_use_nfs + + + mplayer_execstack + + + mysql_connect_any + + + nagios_run_pnp4nagios + + + nagios_run_sudo + + + named_tcp_bind_http_port + + + named_write_master_zones + + + neutron_can_network + + + nfs_export_all_ro + + + nfs_export_all_rw + + + nfsd_anon_write + + + nis_enabled + + + nscd_use_shm + + + openshift_use_nfs + + + openvpn_can_network_connect + + + openvpn_enable_homedirs + + + openvpn_run_unconfined + + + pcp_bind_all_unreserved_ports + + + pcp_read_generic_logs + + + piranha_lvs_can_network_connect + + + polipo_connect_all_unreserved + + + polipo_session_bind_all_unreserved_ports + + + polipo_session_users + + + polipo_use_cifs + + + polipo_use_nfs + + + polyinstantiation_enabled + + + postfix_local_write_mail_spool + + + postgresql_can_rsync + + + postgresql_selinux_transmit_client_label + + + postgresql_selinux_unconfined_dbadm + + + postgresql_selinux_users_ddl + + + pppd_can_insmod + + + pppd_for_user + + + privoxy_connect_any + + + prosody_bind_http_port + + + puppetagent_manage_all_files + + + puppetmaster_use_db + + + racoon_read_shadow + + + rsync_anon_write + + + rsync_client + + + rsync_export_all_ro + + + rsync_full_access + + + samba_create_home_dirs + + + samba_domain_controller + + + samba_enable_home_dirs + + + samba_export_all_ro + + + samba_export_all_rw + + + samba_load_libgfapi + + + samba_portmapper + + + samba_run_unconfined + + + samba_share_fusefs + + + samba_share_nfs + + + sanlock_use_fusefs + + + sanlock_use_nfs + + + sanlock_use_samba + + + saslauthd_read_shadow + + + secadm_exec_content + + + secure_mode + + + secure_mode_insmod + + + secure_mode_policyload + + + selinuxuser_direct_dri_enabled + + + selinuxuser_execheap + + + selinuxuser_execmod + + + selinuxuser_execstack + + + selinuxuser_mysql_connect_enabled + + + selinuxuser_ping + + + selinuxuser_postgresql_connect_enabled + + + selinuxuser_rw_noexattrfile + + + selinuxuser_share_music + + + selinuxuser_tcp_server + + + selinuxuser_udp_server + + + selinuxuser_use_ssh_chroot + + + sge_domain_can_network_connect + + + sge_use_nfs + + + smartmon_3ware + + + smbd_anon_write + + + spamassassin_can_network + + + spamd_enable_home_dirs + + + squid_connect_any + + + squid_use_tproxy + + + ssh_chroot_rw_homedirs + + + ssh_keysign + + + ssh_sysadm_login + + + staff_exec_content + + + staff_use_svirt + + + swift_can_network + + + sysadm_exec_content + + + telepathy_connect_all_ports + + + telepathy_tcp_connect_generic_network_ports + + + tftp_anon_write + + + tftp_home_dir + + + tmpreaper_use_nfs + + + tmpreaper_use_samba + + + tor_bind_all_unreserved_ports + + + tor_can_network_relay + + + unconfined_chrome_sandbox_transition + + + unconfined_login + + + unconfined_mozilla_plugin_transition + + + unprivuser_use_svirt + + + use_ecryptfs_home_dirs + + + use_fusefs_home_dirs + + + use_lpd_server + + + use_nfs_home_dirs + + + use_samba_home_dirs + + + user_exec_content + + + varnishd_connect_any + + + virt_read_qemu_ga_data + + + virt_rw_qemu_ga_data + + + virt_sandbox_use_all_caps + + + virt_sandbox_use_audit + + + virt_sandbox_use_mknod + + + virt_sandbox_use_netlink + + + virt_sandbox_use_sys_admin + + + virt_transition_userdomain + + + virt_use_comm + + + virt_use_execmem + + + virt_use_fusefs + + + virt_use_nfs + + + virt_use_rawip + + + virt_use_samba + + + virt_use_sanlock + + + virt_use_usb + + + virt_use_xserver + + + webadm_manage_user_files + + + webadm_read_user_files + + + wine_mmap_zero_ignore + + + xdm_bind_vnc_tcp_port + + + xdm_exec_bootloader + + + xdm_sysadm_login + + + xdm_write_home + + + xen_use_nfs + + + xend_run_blktap + + + xend_run_qemu + + + xguest_connect_network + + + xguest_exec_content + + + xguest_mount_media + + + xguest_use_bluetooth + + + xserver_clients_write_xshm + + + xserver_execmem + + + xserver_object_manager + + + zabbix_can_network + + + zarafa_setrlimit + + + zebra_write_config + + + zoneminder_anon_write + + + zoneminder_run_sudo + + + ^atd\.(service|socket)$ + ActiveState + + + ^atd\.(service|socket)$ + LoadState + + + at + + + multi-user.target + + + multi-user.target + + + ^auditd\.(socket|service)$ + ActiveState + + + audit + + + ^autofs\.(service|socket)$ + ActiveState + + + ^autofs\.(service|socket)$ + LoadState + + + autofs + + + ^avahi-daemon\.(service|socket)$ + ActiveState + + + ^avahi-daemon\.(service|socket)$ + LoadState + + + avahi + + + multi-user.target + + + multi-user.target + + + ^chronyd\.(socket|service)$ + ActiveState + + + chrony + + + ^cockpit\.(service|socket)$ + ActiveState + + + ^cockpit\.(service|socket)$ + LoadState + + + cockpit + + + multi-user.target + + + multi-user.target + + + ^cron\.(socket|service)$ + ActiveState + + + cron + + + multi-user.target + + + multi-user.target + + + ^crond\.(socket|service)$ + ActiveState + + + cronie + + + ^cups\.(service|socket)$ + ActiveState + + + ^cups\.(service|socket)$ + LoadState + + + cups + + + ^debug-shell\.(service|socket)$ + ActiveState + + + ^debug-shell\.(service|socket)$ + LoadState + + + systemd + + + ^dhcpd\.(service|socket)$ + ActiveState + + + ^dhcpd\.(service|socket)$ + LoadState + + + dhcp-server + + + ^dovecot\.(service|socket)$ + ActiveState + + + ^dovecot\.(service|socket)$ + LoadState + + + dovecot + + + multi-user.target + + + multi-user.target + + + ^fapolicyd\.(socket|service)$ + ActiveState + + + fapolicyd + + + multi-user.target + + + multi-user.target + + + ^firewalld\.(socket|service)$ + ActiveState + + + firewalld + + + ^httpd\.(service|socket)$ + ActiveState + + + ^httpd\.(service|socket)$ + LoadState + + + httpd + + + multi-user.target + + + multi-user.target + + + ^ip6tables\.(socket|service)$ + ActiveState + + + iptables-ipv6 + + + multi-user.target + + + multi-user.target + + + ^iptables\.(socket|service)$ + ActiveState + + + iptables + + + ^kdump\.(service|socket)$ + ActiveState + + + ^kdump\.(service|socket)$ + LoadState + + + kexec-tools + + + ^named\.(service|socket)$ + ActiveState + + + ^named\.(service|socket)$ + LoadState + + + bind + + + ^netfs\.(service|socket)$ + ActiveState + + + ^netfs\.(service|socket)$ + LoadState + + + netfs + + + ^nfs-server\.(service|socket)$ + ActiveState + + + ^nfs-server\.(service|socket)$ + LoadState + + + nfs-utils + + + multi-user.target + + + multi-user.target + + + ^ntp\.(socket|service)$ + ActiveState + + + ntp + + + multi-user.target + + + multi-user.target + + + ^ntpd\.(socket|service)$ + ActiveState + + + ntp + + + ^ntpdate\.(service|socket)$ + ActiveState + + + ^ntpdate\.(service|socket)$ + LoadState + + + ntpdate + + + ^oddjobd\.(service|socket)$ + ActiveState + + + ^oddjobd\.(service|socket)$ + LoadState + + + oddjob + + + multi-user.target + + + multi-user.target + + + ^pcscd\.(socket|service)$ + ActiveState + + + pcsc-lite + + + multi-user.target + + + multi-user.target + + + ^postfix\.(socket|service)$ + ActiveState + + + postfix + + + ^qpidd\.(service|socket)$ + ActiveState + + + ^qpidd\.(service|socket)$ + LoadState + + + qpid-cpp-server + + + ^rdisc\.(service|socket)$ + ActiveState + + + ^rdisc\.(service|socket)$ + LoadState + + + iputils + + + ^rhnsd\.(service|socket)$ + ActiveState + + + ^rhnsd\.(service|socket)$ + LoadState + + + rhnsd + + + ^rlogin\.(service|socket)$ + ActiveState + + + ^rlogin\.(service|socket)$ + LoadState + + + rsh-server + + + multi-user.target + + + multi-user.target + + + ^rngd\.(socket|service)$ + ActiveState + + + rng-tools + + + ^rpcbind\.(service|socket)$ + ActiveState + + + ^rpcbind\.(service|socket)$ + LoadState + + + nfs-utils + + + ^rsyncd\.(service|socket)$ + ActiveState + + + ^rsyncd\.(service|socket)$ + LoadState + + + rsync-daemon + + + multi-user.target + + + multi-user.target + + + ^rsyslog\.(socket|service)$ + ActiveState + + + rsyslog + + + ^slapd\.(service|socket)$ + ActiveState + + + ^slapd\.(service|socket)$ + LoadState + + + openldap-servers + + + ^smb\.(service|socket)$ + ActiveState + + + ^smb\.(service|socket)$ + LoadState + + + samba + + + ^snmpd\.(service|socket)$ + ActiveState + + + ^snmpd\.(service|socket)$ + LoadState + + + net-snmp + + + ^squid\.(service|socket)$ + ActiveState + + + ^squid\.(service|socket)$ + LoadState + + + squid + + + ^sshd\.(service|socket)$ + ActiveState + + + ^sshd\.(service|socket)$ + LoadState + + + openssh-server + + + multi-user.target + + + multi-user.target + + + ^sshd\.(socket|service)$ + ActiveState + + + openssh-server + + + ^syslog\.(service|socket)$ + ActiveState + + + ^syslog\.(service|socket)$ + LoadState + + + rsyslog + + + multi-user.target + + + multi-user.target + + + ^syslog-ng\.(socket|service)$ + ActiveState + + + syslog-ng + + + ^systemd-coredump\.(service|socket)$ + ActiveState + + + ^systemd-coredump\.(service|socket)$ + LoadState + + + systemd + + + multi-user.target + + + multi-user.target + + + ^systemd-journald\.(socket|service)$ + ActiveState + + + systemd + + + ^telnet\.(service|socket)$ + ActiveState + + + ^telnet\.(service|socket)$ + LoadState + + + telnet + + + multi-user.target + + + multi-user.target + + + ^ufw\.(socket|service)$ + ActiveState + + + ufw + + + multi-user.target + + + multi-user.target + + + ^usbguard\.(socket|service)$ + ActiveState + + + usbguard + + + ^vsftpd\.(service|socket)$ + ActiveState + + + ^vsftpd\.(service|socket)$ + LoadState + + + vsftpd + + + ^xinetd\.(service|socket)$ + ActiveState + + + ^xinetd\.(service|socket)$ + LoadState + + + xinetd + + + ^ypserv\.(service|socket)$ + ActiveState + + + ^ypserv\.(service|socket)$ + LoadState + + + ypserv + + + /etc/ssh/sshd_config + ^[ \t]*(?i)PermitEmptyPasswords(?-i)[ \t]+(.+?)[ \t]*(?:$|#) + 1 + + + /etc/ssh/sshd_config.d + .*\.conf$ + ^[ \t]*(?i)PermitEmptyPasswords(?-i)[ \t]+(.+?)[ \t]*(?:$|#) + 1 + + + /etc/ssh/sshd_config + ^[ \t]*(?i)GSSAPIAuthentication(?-i)[ \t]+(.+?)[ \t]*(?:$|#) + 1 + + + /etc/ssh/sshd_config.d + .*\.conf$ + ^[ \t]*(?i)GSSAPIAuthentication(?-i)[ \t]+(.+?)[ \t]*(?:$|#) + 1 + + + /etc/ssh/sshd_config + ^[ \t]*(?i)KerberosAuthentication(?-i)[ \t]+(.+?)[ \t]*(?:$|#) + 1 + + + /etc/ssh/sshd_config.d + .*\.conf$ + ^[ \t]*(?i)KerberosAuthentication(?-i)[ \t]+(.+?)[ \t]*(?:$|#) + 1 + + + /etc/ssh/sshd_config + ^[ \t]*(?i)PubkeyAuthentication(?-i)[ \t]+(.+?)[ \t]*(?:$|#) + 1 + + + /etc/ssh/sshd_config.d + .*\.conf$ + ^[ \t]*(?i)PubkeyAuthentication(?-i)[ \t]+(.+?)[ \t]*(?:$|#) + 1 + + + /etc/ssh/sshd_config + ^[ \t]*(?i)IgnoreRhosts(?-i)[ \t]+(.+?)[ \t]*(?:$|#) + 1 + + + /etc/ssh/sshd_config.d + .*\.conf$ + ^[ \t]*(?i)IgnoreRhosts(?-i)[ \t]+(.+?)[ \t]*(?:$|#) + 1 + + + /etc/ssh/sshd_config + ^[ \t]*(?i)PermitRootLogin(?-i)[ \t]+(.+?)[ \t]*(?:$|#) + 1 + + + /etc/ssh/sshd_config.d + .*\.conf$ + ^[ \t]*(?i)PermitRootLogin(?-i)[ \t]+(.+?)[ \t]*(?:$|#) + 1 + + + /etc/ssh/sshd_config + ^[ \t]*(?i)PermitRootLogin(?-i)[ \t]+(.+?)[ \t]*(?:$|#) + 1 + + + /etc/ssh/sshd_config.d + .*\.conf$ + ^[ \t]*(?i)PermitRootLogin(?-i)[ \t]+(.+?)[ \t]*(?:$|#) + 1 + + + /etc/ssh/sshd_config + ^[ \t]*(?i)AllowTcpForwarding(?-i)[ \t]+(.+?)[ \t]*(?:$|#) + 1 + + + /etc/ssh/sshd_config.d + .*\.conf$ + ^[ \t]*(?i)AllowTcpForwarding(?-i)[ \t]+(.+?)[ \t]*(?:$|#) + 1 + + + /etc/ssh/sshd_config + ^[ \t]*(?i)IgnoreUserKnownHosts(?-i)[ \t]+(.+?)[ \t]*(?:$|#) + 1 + + + /etc/ssh/sshd_config.d + .*\.conf$ + ^[ \t]*(?i)IgnoreUserKnownHosts(?-i)[ \t]+(.+?)[ \t]*(?:$|#) + 1 + + + /etc/ssh/sshd_config + ^[ \t]*(?i)X11Forwarding(?-i)[ \t]+(.+?)[ \t]*(?:$|#) + 1 + + + /etc/ssh/sshd_config.d + .*\.conf$ + ^[ \t]*(?i)X11Forwarding(?-i)[ \t]+(.+?)[ \t]*(?:$|#) + 1 + + + /etc/ssh/sshd_config + ^[ \t]*(?i)PermitUserEnvironment(?-i)[ \t]+(.+?)[ \t]*(?:$|#) + 1 + + + /etc/ssh/sshd_config.d + .*\.conf$ + ^[ \t]*(?i)PermitUserEnvironment(?-i)[ \t]+(.+?)[ \t]*(?:$|#) + 1 + + + /etc/ssh/sshd_config + ^[ \t]*(?i)GSSAPIAuthentication(?-i)[ \t]+(.+?)[ \t]*(?:$|#) + 1 + + + /etc/ssh/sshd_config.d + .*\.conf$ + ^[ \t]*(?i)GSSAPIAuthentication(?-i)[ \t]+(.+?)[ \t]*(?:$|#) + 1 + + + /etc/ssh/sshd_config + ^[ \t]*(?i)UsePAM(?-i)[ \t]+(.+?)[ \t]*(?:$|#) + 1 + + + /etc/ssh/sshd_config.d + .*\.conf$ + ^[ \t]*(?i)UsePAM(?-i)[ \t]+(.+?)[ \t]*(?:$|#) + 1 + + + /etc/ssh/sshd_config + ^[ \t]*(?i)PubkeyAuthentication(?-i)[ \t]+(.+?)[ \t]*(?:$|#) + 1 + + + /etc/ssh/sshd_config.d + .*\.conf$ + ^[ \t]*(?i)PubkeyAuthentication(?-i)[ \t]+(.+?)[ \t]*(?:$|#) + 1 + + + /etc/ssh/sshd_config + ^[ \t]*(?i)StrictModes(?-i)[ \t]+(.+?)[ \t]*(?:$|#) + 1 + + + /etc/ssh/sshd_config.d + .*\.conf$ + ^[ \t]*(?i)StrictModes(?-i)[ \t]+(.+?)[ \t]*(?:$|#) + 1 + + + /etc/ssh/sshd_config + ^[ \t]*(?i)Banner(?-i)[ \t]+(.+?)[ \t]*(?:$|#) + 1 + + + /etc/ssh/sshd_config.d + .*\.conf$ + ^[ \t]*(?i)Banner(?-i)[ \t]+(.+?)[ \t]*(?:$|#) + 1 + + + /etc/ssh/sshd_config + ^[ \t]*(?i)Banner(?-i)[ \t]+(.+?)[ \t]*(?:$|#) + 1 + + + /etc/ssh/sshd_config.d + .*\.conf$ + ^[ \t]*(?i)Banner(?-i)[ \t]+(.+?)[ \t]*(?:$|#) + 1 + + + /etc/ssh/sshd_config + ^[ \t]*(?i)X11Forwarding(?-i)[ \t]+(.+?)[ \t]*(?:$|#) + 1 + + + /etc/ssh/sshd_config.d + .*\.conf$ + ^[ \t]*(?i)X11Forwarding(?-i)[ \t]+(.+?)[ \t]*(?:$|#) + 1 + + + /etc/ssh/sshd_config + ^[\s]*Include /etc/ssh/sshd_config\.d/\*\.conf[\s]*$ + 1 + + + /etc/ssh/sshd_config + ^[ \t]*(?i)PrintLastLog(?-i)[ \t]+(.+?)[ \t]*(?:$|#) + 1 + + + /etc/ssh/sshd_config.d + .*\.conf$ + ^[ \t]*(?i)PrintLastLog(?-i)[ \t]+(.+?)[ \t]*(?:$|#) + 1 + + + /etc/ssh/sshd_config + ^[ \t]*(?i)ClientAliveCountMax(?-i)[ \t]+(.+?)[ \t]*(?:$|#) + 1 + + + /etc/ssh/sshd_config.d + .*\.conf$ + ^[ \t]*(?i)ClientAliveCountMax(?-i)[ \t]+(.+?)[ \t]*(?:$|#) + 1 + + + /etc/ssh/sshd_config + ^[ \t]*(?i)LogLevel(?-i)[ \t]+(.+?)[ \t]*(?:$|#) + 1 + + + /etc/ssh/sshd_config.d + .*\.conf$ + ^[ \t]*(?i)LogLevel(?-i)[ \t]+(.+?)[ \t]*(?:$|#) + 1 + + + /etc/ssh/sshd_config + ^[ \t]*(?i)LogLevel(?-i)[ \t]+(.+?)[ \t]*(?:$|#) + 1 + + + /etc/ssh/sshd_config.d + .*\.conf$ + ^[ \t]*(?i)LogLevel(?-i)[ \t]+(.+?)[ \t]*(?:$|#) + 1 + + + /etc/sysconfig/sshd + ^[ \t]*SSH_USE_STRONG_RNG=(.+?)[ \t]*(?:$|#) + 1 + + + /etc/ssh/sshd_config + ^[ \t]*(?i)X11UseLocalhost(?-i)[ \t]+(.+?)[ \t]*(?:$|#) + 1 + + + /etc/ssh/sshd_config.d + .*\.conf$ + ^[ \t]*(?i)X11UseLocalhost(?-i)[ \t]+(.+?)[ \t]*(?:$|#) + 1 + + + /etc/sssd/sssd.conf + ^[\s]*\[certmap\/.+\/.+\][\s]*$ + 1 + + + ^/etc/sudoers(|\.d/.*)$ + ^[\s]*Defaults[\s]*\bnoexec.*$ + 1 + + + ^/etc/sudoers(|\.d/.*)$ + ^[\s]*Defaults[\s]*\brequiretty.*$ + 1 + + + ^/etc/sudoers(|\.d/.*)$ + ^[\s]*Defaults[\s]*\buse_pty.*$ + 1 + + + ^/etc/sudoers(|\.d/.*)$ + ^[\s]*Defaults[\s]*\blogfile=("(?:\\"|\\\\|[^"\\\n])*"\B|[^"](?:(?:\\,|\\"|\\ |\\\\|[^", \\\n])*)\b).*$ + 1 + + + fs.protected_fifos + + + oval:ssg-local_var_sysctl_fs_protected_fifos_counter:var:1 + + + + oval:ssg-object_sysctl_fs_protected_fifos_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_fs_protected_fifos_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_fs_protected_fifos:obj:1 + oval:ssg-var_obj_blank_sysctl_fs_protected_fifos:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_fs_protected_fifos:var:1 + + + oval:ssg-local_var_symlinks_sysctl_fs_protected_fifos:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_fs_protected_fifos:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_fs_protected_fifos:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_fs_protected_fifos:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_fs_protected_fifos:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_fs_protected_fifos:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_fs_protected_fifos:obj:1 + + + + /etc/sysctl.conf + ^[\s]*fs.protected_fifos[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*fs.protected_fifos[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*fs.protected_fifos[\s]*=[\s]*(.*)[\s]*$ + 1 + + + fs.protected_hardlinks + + + oval:ssg-local_var_sysctl_fs_protected_hardlinks_counter:var:1 + + + + oval:ssg-object_sysctl_fs_protected_hardlinks_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_fs_protected_hardlinks_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_fs_protected_hardlinks:obj:1 + oval:ssg-var_obj_blank_sysctl_fs_protected_hardlinks:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_fs_protected_hardlinks:var:1 + + + oval:ssg-local_var_symlinks_sysctl_fs_protected_hardlinks:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_fs_protected_hardlinks:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_fs_protected_hardlinks:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_fs_protected_hardlinks:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_fs_protected_hardlinks:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_fs_protected_hardlinks:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_fs_protected_hardlinks:obj:1 + + + + /etc/sysctl.conf + ^[\s]*fs.protected_hardlinks[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*fs.protected_hardlinks[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*fs.protected_hardlinks[\s]*=[\s]*(.*)[\s]*$ + 1 + + + fs.protected_regular + + + oval:ssg-local_var_sysctl_fs_protected_regular_counter:var:1 + + + + oval:ssg-object_sysctl_fs_protected_regular_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_fs_protected_regular_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_fs_protected_regular:obj:1 + oval:ssg-var_obj_blank_sysctl_fs_protected_regular:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_fs_protected_regular:var:1 + + + oval:ssg-local_var_symlinks_sysctl_fs_protected_regular:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_fs_protected_regular:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_fs_protected_regular:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_fs_protected_regular:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_fs_protected_regular:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_fs_protected_regular:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_fs_protected_regular:obj:1 + + + + /etc/sysctl.conf + ^[\s]*fs.protected_regular[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*fs.protected_regular[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*fs.protected_regular[\s]*=[\s]*(.*)[\s]*$ + 1 + + + fs.protected_symlinks + + + oval:ssg-local_var_sysctl_fs_protected_symlinks_counter:var:1 + + + + oval:ssg-object_sysctl_fs_protected_symlinks_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_fs_protected_symlinks_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_fs_protected_symlinks:obj:1 + oval:ssg-var_obj_blank_sysctl_fs_protected_symlinks:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_fs_protected_symlinks:var:1 + + + oval:ssg-local_var_symlinks_sysctl_fs_protected_symlinks:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_fs_protected_symlinks:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_fs_protected_symlinks:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_fs_protected_symlinks:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_fs_protected_symlinks:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_fs_protected_symlinks:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_fs_protected_symlinks:obj:1 + + + + /etc/sysctl.conf + ^[\s]*fs.protected_symlinks[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*fs.protected_symlinks[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*fs.protected_symlinks[\s]*=[\s]*(.*)[\s]*$ + 1 + + + fs.suid_dumpable + + + oval:ssg-local_var_sysctl_fs_suid_dumpable_counter:var:1 + + + + oval:ssg-object_sysctl_fs_suid_dumpable_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_fs_suid_dumpable_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_fs_suid_dumpable:obj:1 + oval:ssg-var_obj_blank_sysctl_fs_suid_dumpable:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_fs_suid_dumpable:var:1 + + + oval:ssg-local_var_symlinks_sysctl_fs_suid_dumpable:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_fs_suid_dumpable:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_fs_suid_dumpable:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_fs_suid_dumpable:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_fs_suid_dumpable:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_fs_suid_dumpable:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_fs_suid_dumpable:obj:1 + + + + /etc/sysctl.conf + ^[\s]*fs.suid_dumpable[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*fs.suid_dumpable[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*fs.suid_dumpable[\s]*=[\s]*(.*)[\s]*$ + 1 + + + kernel.core_pattern + + + oval:ssg-local_var_sysctl_kernel_core_pattern_counter:var:1 + + + + oval:ssg-object_sysctl_kernel_core_pattern_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_kernel_core_pattern_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_kernel_core_pattern:obj:1 + oval:ssg-var_obj_blank_sysctl_kernel_core_pattern:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_kernel_core_pattern:var:1 + + + oval:ssg-local_var_symlinks_sysctl_kernel_core_pattern:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_kernel_core_pattern:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_kernel_core_pattern:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_kernel_core_pattern:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_kernel_core_pattern:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_kernel_core_pattern:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_kernel_core_pattern:obj:1 + + + + /etc/sysctl.conf + ^[\s]*kernel.core_pattern[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*kernel.core_pattern[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*kernel.core_pattern[\s]*=[\s]*(.*)[\s]*$ + 1 + + + kernel.core_uses_pid + + + oval:ssg-local_var_sysctl_kernel_core_uses_pid_counter:var:1 + + + + oval:ssg-object_sysctl_kernel_core_uses_pid_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_kernel_core_uses_pid_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_kernel_core_uses_pid:obj:1 + oval:ssg-var_obj_blank_sysctl_kernel_core_uses_pid:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_kernel_core_uses_pid:var:1 + + + oval:ssg-local_var_symlinks_sysctl_kernel_core_uses_pid:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_kernel_core_uses_pid:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_kernel_core_uses_pid:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_kernel_core_uses_pid:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_kernel_core_uses_pid:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_kernel_core_uses_pid:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_kernel_core_uses_pid:obj:1 + + + + /etc/sysctl.conf + ^[\s]*kernel.core_uses_pid[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*kernel.core_uses_pid[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*kernel.core_uses_pid[\s]*=[\s]*(.*)[\s]*$ + 1 + + + kernel.dmesg_restrict + + + oval:ssg-local_var_sysctl_kernel_dmesg_restrict_counter:var:1 + + + + oval:ssg-object_sysctl_kernel_dmesg_restrict_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_kernel_dmesg_restrict_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_kernel_dmesg_restrict:obj:1 + oval:ssg-var_obj_blank_sysctl_kernel_dmesg_restrict:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_kernel_dmesg_restrict:var:1 + + + oval:ssg-local_var_symlinks_sysctl_kernel_dmesg_restrict:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_kernel_dmesg_restrict:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_kernel_dmesg_restrict:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_kernel_dmesg_restrict:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_kernel_dmesg_restrict:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_kernel_dmesg_restrict:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_kernel_dmesg_restrict:obj:1 + + + + /etc/sysctl.conf + ^[\s]*kernel.dmesg_restrict[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*kernel.dmesg_restrict[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*kernel.dmesg_restrict[\s]*=[\s]*(.*)[\s]*$ + 1 + + + kernel.kexec_load_disabled + + + oval:ssg-local_var_sysctl_kernel_kexec_load_disabled_counter:var:1 + + + + oval:ssg-object_sysctl_kernel_kexec_load_disabled_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_kernel_kexec_load_disabled_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_kernel_kexec_load_disabled:obj:1 + oval:ssg-var_obj_blank_sysctl_kernel_kexec_load_disabled:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_kernel_kexec_load_disabled:var:1 + + + oval:ssg-local_var_symlinks_sysctl_kernel_kexec_load_disabled:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_kernel_kexec_load_disabled:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_kernel_kexec_load_disabled:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_kernel_kexec_load_disabled:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_kernel_kexec_load_disabled:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_kernel_kexec_load_disabled:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_kernel_kexec_load_disabled:obj:1 + + + + /etc/sysctl.conf + ^[\s]*kernel.kexec_load_disabled[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*kernel.kexec_load_disabled[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*kernel.kexec_load_disabled[\s]*=[\s]*(.*)[\s]*$ + 1 + + + kernel.kptr_restrict + + + oval:ssg-local_var_sysctl_kernel_kptr_restrict_counter:var:1 + + + + oval:ssg-object_sysctl_kernel_kptr_restrict_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_kernel_kptr_restrict_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_kernel_kptr_restrict:obj:1 + oval:ssg-var_obj_blank_sysctl_kernel_kptr_restrict:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_kernel_kptr_restrict:var:1 + + + oval:ssg-local_var_symlinks_sysctl_kernel_kptr_restrict:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_kernel_kptr_restrict:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_kernel_kptr_restrict:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_kernel_kptr_restrict:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_kernel_kptr_restrict:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_kernel_kptr_restrict:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_kernel_kptr_restrict:obj:1 + + + + /etc/sysctl.conf + ^[\s]*kernel.kptr_restrict[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*kernel.kptr_restrict[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*kernel.kptr_restrict[\s]*=[\s]*(.*)[\s]*$ + 1 + + + kernel.modules_disabled + + + oval:ssg-local_var_sysctl_kernel_modules_disabled_counter:var:1 + + + + oval:ssg-object_sysctl_kernel_modules_disabled_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_kernel_modules_disabled_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_kernel_modules_disabled:obj:1 + oval:ssg-var_obj_blank_sysctl_kernel_modules_disabled:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_kernel_modules_disabled:var:1 + + + oval:ssg-local_var_symlinks_sysctl_kernel_modules_disabled:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_kernel_modules_disabled:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_kernel_modules_disabled:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_kernel_modules_disabled:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_kernel_modules_disabled:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_kernel_modules_disabled:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_kernel_modules_disabled:obj:1 + + + + /etc/sysctl.conf + ^[\s]*kernel.modules_disabled[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*kernel.modules_disabled[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*kernel.modules_disabled[\s]*=[\s]*(.*)[\s]*$ + 1 + + + kernel.panic_on_oops + + + oval:ssg-local_var_sysctl_kernel_panic_on_oops_counter:var:1 + + + + oval:ssg-object_sysctl_kernel_panic_on_oops_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_kernel_panic_on_oops_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_kernel_panic_on_oops:obj:1 + oval:ssg-var_obj_blank_sysctl_kernel_panic_on_oops:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_kernel_panic_on_oops:var:1 + + + oval:ssg-local_var_symlinks_sysctl_kernel_panic_on_oops:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_kernel_panic_on_oops:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_kernel_panic_on_oops:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_kernel_panic_on_oops:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_kernel_panic_on_oops:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_kernel_panic_on_oops:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_kernel_panic_on_oops:obj:1 + + + + /etc/sysctl.conf + ^[\s]*kernel.panic_on_oops[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*kernel.panic_on_oops[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*kernel.panic_on_oops[\s]*=[\s]*(.*)[\s]*$ + 1 + + + kernel.perf_cpu_time_max_percent + + + oval:ssg-local_var_sysctl_kernel_perf_cpu_time_max_percent_counter:var:1 + + + + oval:ssg-object_sysctl_kernel_perf_cpu_time_max_percent_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_kernel_perf_cpu_time_max_percent_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_kernel_perf_cpu_time_max_percent:obj:1 + oval:ssg-var_obj_blank_sysctl_kernel_perf_cpu_time_max_percent:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_kernel_perf_cpu_time_max_percent:var:1 + + + oval:ssg-local_var_symlinks_sysctl_kernel_perf_cpu_time_max_percent:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_kernel_perf_cpu_time_max_percent:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_kernel_perf_cpu_time_max_percent:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_kernel_perf_cpu_time_max_percent:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_kernel_perf_cpu_time_max_percent:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_kernel_perf_cpu_time_max_percent:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_kernel_perf_cpu_time_max_percent:obj:1 + + + + /etc/sysctl.conf + ^[\s]*kernel.perf_cpu_time_max_percent[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*kernel.perf_cpu_time_max_percent[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*kernel.perf_cpu_time_max_percent[\s]*=[\s]*(.*)[\s]*$ + 1 + + + kernel.perf_event_max_sample_rate + + + oval:ssg-local_var_sysctl_kernel_perf_event_max_sample_rate_counter:var:1 + + + + oval:ssg-object_sysctl_kernel_perf_event_max_sample_rate_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_kernel_perf_event_max_sample_rate_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_kernel_perf_event_max_sample_rate:obj:1 + oval:ssg-var_obj_blank_sysctl_kernel_perf_event_max_sample_rate:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_kernel_perf_event_max_sample_rate:var:1 + + + oval:ssg-local_var_symlinks_sysctl_kernel_perf_event_max_sample_rate:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_kernel_perf_event_max_sample_rate:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_kernel_perf_event_max_sample_rate:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_kernel_perf_event_max_sample_rate:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_kernel_perf_event_max_sample_rate:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_kernel_perf_event_max_sample_rate:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_kernel_perf_event_max_sample_rate:obj:1 + + + + /etc/sysctl.conf + ^[\s]*kernel.perf_event_max_sample_rate[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*kernel.perf_event_max_sample_rate[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*kernel.perf_event_max_sample_rate[\s]*=[\s]*(.*)[\s]*$ + 1 + + + kernel.perf_event_paranoid + + + oval:ssg-local_var_sysctl_kernel_perf_event_paranoid_counter:var:1 + + + + oval:ssg-object_sysctl_kernel_perf_event_paranoid_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_kernel_perf_event_paranoid_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_kernel_perf_event_paranoid:obj:1 + oval:ssg-var_obj_blank_sysctl_kernel_perf_event_paranoid:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_kernel_perf_event_paranoid:var:1 + + + oval:ssg-local_var_symlinks_sysctl_kernel_perf_event_paranoid:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_kernel_perf_event_paranoid:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_kernel_perf_event_paranoid:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_kernel_perf_event_paranoid:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_kernel_perf_event_paranoid:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_kernel_perf_event_paranoid:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_kernel_perf_event_paranoid:obj:1 + + + + /etc/sysctl.conf + ^[\s]*kernel.perf_event_paranoid[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*kernel.perf_event_paranoid[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*kernel.perf_event_paranoid[\s]*=[\s]*(.*)[\s]*$ + 1 + + + kernel.pid_max + + + oval:ssg-local_var_sysctl_kernel_pid_max_counter:var:1 + + + + oval:ssg-object_sysctl_kernel_pid_max_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_kernel_pid_max_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_kernel_pid_max:obj:1 + oval:ssg-var_obj_blank_sysctl_kernel_pid_max:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_kernel_pid_max:var:1 + + + oval:ssg-local_var_symlinks_sysctl_kernel_pid_max:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_kernel_pid_max:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_kernel_pid_max:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_kernel_pid_max:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_kernel_pid_max:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_kernel_pid_max:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_kernel_pid_max:obj:1 + + + + /etc/sysctl.conf + ^[\s]*kernel.pid_max[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*kernel.pid_max[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*kernel.pid_max[\s]*=[\s]*(.*)[\s]*$ + 1 + + + kernel.randomize_va_space + + + oval:ssg-local_var_sysctl_kernel_randomize_va_space_counter:var:1 + + + + oval:ssg-object_sysctl_kernel_randomize_va_space_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_kernel_randomize_va_space_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_kernel_randomize_va_space:obj:1 + oval:ssg-var_obj_blank_sysctl_kernel_randomize_va_space:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_kernel_randomize_va_space:var:1 + + + oval:ssg-local_var_symlinks_sysctl_kernel_randomize_va_space:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_kernel_randomize_va_space:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_kernel_randomize_va_space:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_kernel_randomize_va_space:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_kernel_randomize_va_space:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_kernel_randomize_va_space:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_kernel_randomize_va_space:obj:1 + + + + /etc/sysctl.conf + ^[\s]*kernel.randomize_va_space[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*kernel.randomize_va_space[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*kernel.randomize_va_space[\s]*=[\s]*(.*)[\s]*$ + 1 + + + kernel.sysrq + + + oval:ssg-local_var_sysctl_kernel_sysrq_counter:var:1 + + + + oval:ssg-object_sysctl_kernel_sysrq_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_kernel_sysrq_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_kernel_sysrq:obj:1 + oval:ssg-var_obj_blank_sysctl_kernel_sysrq:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_kernel_sysrq:var:1 + + + oval:ssg-local_var_symlinks_sysctl_kernel_sysrq:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_kernel_sysrq:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_kernel_sysrq:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_kernel_sysrq:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_kernel_sysrq:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_kernel_sysrq:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_kernel_sysrq:obj:1 + + + + /etc/sysctl.conf + ^[\s]*kernel.sysrq[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*kernel.sysrq[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*kernel.sysrq[\s]*=[\s]*(.*)[\s]*$ + 1 + + + kernel.unprivileged_bpf_disabled + + + oval:ssg-local_var_sysctl_kernel_unprivileged_bpf_disabled_counter:var:1 + + + + oval:ssg-object_sysctl_kernel_unprivileged_bpf_disabled_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_kernel_unprivileged_bpf_disabled_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_kernel_unprivileged_bpf_disabled:obj:1 + oval:ssg-var_obj_blank_sysctl_kernel_unprivileged_bpf_disabled:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_kernel_unprivileged_bpf_disabled:var:1 + + + oval:ssg-local_var_symlinks_sysctl_kernel_unprivileged_bpf_disabled:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_kernel_unprivileged_bpf_disabled:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_kernel_unprivileged_bpf_disabled:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_kernel_unprivileged_bpf_disabled:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_kernel_unprivileged_bpf_disabled:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_kernel_unprivileged_bpf_disabled:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_kernel_unprivileged_bpf_disabled:obj:1 + + + + /etc/sysctl.conf + ^[\s]*kernel.unprivileged_bpf_disabled[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*kernel.unprivileged_bpf_disabled[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*kernel.unprivileged_bpf_disabled[\s]*=[\s]*(.*)[\s]*$ + 1 + + + kernel.unprivileged_bpf_disabled + + + oval:ssg-local_var_sysctl_kernel_unprivileged_bpf_disabled_accept_default_counter:var:1 + + + + oval:ssg-object_sysctl_kernel_unprivileged_bpf_disabled_accept_default_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_kernel_unprivileged_bpf_disabled_accept_default_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_kernel_unprivileged_bpf_disabled_accept_default:obj:1 + oval:ssg-var_obj_blank_sysctl_kernel_unprivileged_bpf_disabled_accept_default:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_kernel_unprivileged_bpf_disabled_accept_default:var:1 + + + oval:ssg-local_var_symlinks_sysctl_kernel_unprivileged_bpf_disabled_accept_default:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_kernel_unprivileged_bpf_disabled_accept_default:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_kernel_unprivileged_bpf_disabled_accept_default:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_kernel_unprivileged_bpf_disabled_accept_default:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_kernel_unprivileged_bpf_disabled_accept_default:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_kernel_unprivileged_bpf_disabled_accept_default:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_kernel_unprivileged_bpf_disabled_accept_default:obj:1 + + + + /etc/sysctl.conf + ^[\s]*kernel.unprivileged_bpf_disabled[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*kernel.unprivileged_bpf_disabled[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*kernel.unprivileged_bpf_disabled[\s]*=[\s]*(.*)[\s]*$ + 1 + + + kernel.yama.ptrace_scope + + + oval:ssg-local_var_sysctl_kernel_yama_ptrace_scope_counter:var:1 + + + + oval:ssg-object_sysctl_kernel_yama_ptrace_scope_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_kernel_yama_ptrace_scope_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_kernel_yama_ptrace_scope:obj:1 + oval:ssg-var_obj_blank_sysctl_kernel_yama_ptrace_scope:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_kernel_yama_ptrace_scope:var:1 + + + oval:ssg-local_var_symlinks_sysctl_kernel_yama_ptrace_scope:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_kernel_yama_ptrace_scope:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_kernel_yama_ptrace_scope:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_kernel_yama_ptrace_scope:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_kernel_yama_ptrace_scope:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_kernel_yama_ptrace_scope:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_kernel_yama_ptrace_scope:obj:1 + + + + /etc/sysctl.conf + ^[\s]*kernel.yama.ptrace_scope[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*kernel.yama.ptrace_scope[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*kernel.yama.ptrace_scope[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.core.bpf_jit_harden + + + oval:ssg-local_var_sysctl_net_core_bpf_jit_harden_counter:var:1 + + + + oval:ssg-object_sysctl_net_core_bpf_jit_harden_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_core_bpf_jit_harden_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_core_bpf_jit_harden:obj:1 + oval:ssg-var_obj_blank_sysctl_net_core_bpf_jit_harden:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_core_bpf_jit_harden:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_core_bpf_jit_harden:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_core_bpf_jit_harden:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_core_bpf_jit_harden:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_core_bpf_jit_harden:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_core_bpf_jit_harden:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_core_bpf_jit_harden:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_core_bpf_jit_harden:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.core.bpf_jit_harden[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.core.bpf_jit_harden[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.core.bpf_jit_harden[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.ipv4.conf.all.accept_local + + + oval:ssg-local_var_sysctl_net_ipv4_conf_all_accept_local_counter:var:1 + + + + oval:ssg-object_sysctl_net_ipv4_conf_all_accept_local_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_ipv4_conf_all_accept_local_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_ipv4_conf_all_accept_local:obj:1 + oval:ssg-var_obj_blank_sysctl_net_ipv4_conf_all_accept_local:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_ipv4_conf_all_accept_local:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_ipv4_conf_all_accept_local:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_ipv4_conf_all_accept_local:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_ipv4_conf_all_accept_local:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_ipv4_conf_all_accept_local:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_ipv4_conf_all_accept_local:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_ipv4_conf_all_accept_local:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_ipv4_conf_all_accept_local:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.ipv4.conf.all.accept_local[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.conf.all.accept_local[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.conf.all.accept_local[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.ipv4.conf.all.accept_redirects + + + oval:ssg-local_var_sysctl_net_ipv4_conf_all_accept_redirects_counter:var:1 + + + + oval:ssg-object_sysctl_net_ipv4_conf_all_accept_redirects_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_ipv4_conf_all_accept_redirects_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_ipv4_conf_all_accept_redirects:obj:1 + oval:ssg-var_obj_blank_sysctl_net_ipv4_conf_all_accept_redirects:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_ipv4_conf_all_accept_redirects:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_ipv4_conf_all_accept_redirects:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_ipv4_conf_all_accept_redirects:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_ipv4_conf_all_accept_redirects:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_ipv4_conf_all_accept_redirects:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_ipv4_conf_all_accept_redirects:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_ipv4_conf_all_accept_redirects:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_ipv4_conf_all_accept_redirects:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.ipv4.conf.all.accept_redirects[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.conf.all.accept_redirects[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.conf.all.accept_redirects[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.ipv4.conf.all.accept_source_route + + + oval:ssg-local_var_sysctl_net_ipv4_conf_all_accept_source_route_counter:var:1 + + + + oval:ssg-object_sysctl_net_ipv4_conf_all_accept_source_route_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_ipv4_conf_all_accept_source_route_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_ipv4_conf_all_accept_source_route:obj:1 + oval:ssg-var_obj_blank_sysctl_net_ipv4_conf_all_accept_source_route:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_ipv4_conf_all_accept_source_route:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_ipv4_conf_all_accept_source_route:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_ipv4_conf_all_accept_source_route:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_ipv4_conf_all_accept_source_route:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_ipv4_conf_all_accept_source_route:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_ipv4_conf_all_accept_source_route:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_ipv4_conf_all_accept_source_route:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_ipv4_conf_all_accept_source_route:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.ipv4.conf.all.accept_source_route[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.conf.all.accept_source_route[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.conf.all.accept_source_route[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.ipv4.conf.all.arp_filter + + + oval:ssg-local_var_sysctl_net_ipv4_conf_all_arp_filter_counter:var:1 + + + + oval:ssg-object_sysctl_net_ipv4_conf_all_arp_filter_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_ipv4_conf_all_arp_filter_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_ipv4_conf_all_arp_filter:obj:1 + oval:ssg-var_obj_blank_sysctl_net_ipv4_conf_all_arp_filter:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_ipv4_conf_all_arp_filter:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_ipv4_conf_all_arp_filter:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_ipv4_conf_all_arp_filter:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_ipv4_conf_all_arp_filter:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_ipv4_conf_all_arp_filter:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_ipv4_conf_all_arp_filter:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_ipv4_conf_all_arp_filter:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_ipv4_conf_all_arp_filter:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.ipv4.conf.all.arp_filter[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.conf.all.arp_filter[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.conf.all.arp_filter[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.ipv4.conf.all.arp_ignore + + + oval:ssg-local_var_sysctl_net_ipv4_conf_all_arp_ignore_counter:var:1 + + + + oval:ssg-object_sysctl_net_ipv4_conf_all_arp_ignore_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_ipv4_conf_all_arp_ignore_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_ipv4_conf_all_arp_ignore:obj:1 + oval:ssg-var_obj_blank_sysctl_net_ipv4_conf_all_arp_ignore:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_ipv4_conf_all_arp_ignore:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_ipv4_conf_all_arp_ignore:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_ipv4_conf_all_arp_ignore:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_ipv4_conf_all_arp_ignore:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_ipv4_conf_all_arp_ignore:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_ipv4_conf_all_arp_ignore:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_ipv4_conf_all_arp_ignore:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_ipv4_conf_all_arp_ignore:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.ipv4.conf.all.arp_ignore[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.conf.all.arp_ignore[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.conf.all.arp_ignore[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.ipv4.conf.all.drop_gratuitous_arp + + + oval:ssg-local_var_sysctl_net_ipv4_conf_all_drop_gratuitous_arp_counter:var:1 + + + + oval:ssg-object_sysctl_net_ipv4_conf_all_drop_gratuitous_arp_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_ipv4_conf_all_drop_gratuitous_arp_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_ipv4_conf_all_drop_gratuitous_arp:obj:1 + oval:ssg-var_obj_blank_sysctl_net_ipv4_conf_all_drop_gratuitous_arp:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_ipv4_conf_all_drop_gratuitous_arp:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_ipv4_conf_all_drop_gratuitous_arp:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_ipv4_conf_all_drop_gratuitous_arp:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_ipv4_conf_all_drop_gratuitous_arp:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_ipv4_conf_all_drop_gratuitous_arp:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_ipv4_conf_all_drop_gratuitous_arp:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_ipv4_conf_all_drop_gratuitous_arp:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_ipv4_conf_all_drop_gratuitous_arp:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.ipv4.conf.all.drop_gratuitous_arp[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.conf.all.drop_gratuitous_arp[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.conf.all.drop_gratuitous_arp[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.ipv4.conf.all.log_martians + + + oval:ssg-local_var_sysctl_net_ipv4_conf_all_log_martians_counter:var:1 + + + + oval:ssg-object_sysctl_net_ipv4_conf_all_log_martians_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_ipv4_conf_all_log_martians_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_ipv4_conf_all_log_martians:obj:1 + oval:ssg-var_obj_blank_sysctl_net_ipv4_conf_all_log_martians:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_ipv4_conf_all_log_martians:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_ipv4_conf_all_log_martians:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_ipv4_conf_all_log_martians:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_ipv4_conf_all_log_martians:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_ipv4_conf_all_log_martians:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_ipv4_conf_all_log_martians:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_ipv4_conf_all_log_martians:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_ipv4_conf_all_log_martians:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.ipv4.conf.all.log_martians[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.conf.all.log_martians[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.conf.all.log_martians[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.ipv4.conf.all.route_localnet + + + oval:ssg-local_var_sysctl_net_ipv4_conf_all_route_localnet_counter:var:1 + + + + oval:ssg-object_sysctl_net_ipv4_conf_all_route_localnet_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_ipv4_conf_all_route_localnet_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_ipv4_conf_all_route_localnet:obj:1 + oval:ssg-var_obj_blank_sysctl_net_ipv4_conf_all_route_localnet:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_ipv4_conf_all_route_localnet:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_ipv4_conf_all_route_localnet:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_ipv4_conf_all_route_localnet:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_ipv4_conf_all_route_localnet:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_ipv4_conf_all_route_localnet:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_ipv4_conf_all_route_localnet:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_ipv4_conf_all_route_localnet:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_ipv4_conf_all_route_localnet:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.ipv4.conf.all.route_localnet[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.conf.all.route_localnet[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.conf.all.route_localnet[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.ipv4.conf.all.rp_filter + + + oval:ssg-local_var_sysctl_net_ipv4_conf_all_rp_filter_counter:var:1 + + + + oval:ssg-object_sysctl_net_ipv4_conf_all_rp_filter_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_ipv4_conf_all_rp_filter_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_ipv4_conf_all_rp_filter:obj:1 + oval:ssg-var_obj_blank_sysctl_net_ipv4_conf_all_rp_filter:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_ipv4_conf_all_rp_filter:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_ipv4_conf_all_rp_filter:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_ipv4_conf_all_rp_filter:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_ipv4_conf_all_rp_filter:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_ipv4_conf_all_rp_filter:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_ipv4_conf_all_rp_filter:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_ipv4_conf_all_rp_filter:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_ipv4_conf_all_rp_filter:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.ipv4.conf.all.rp_filter[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.conf.all.rp_filter[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.conf.all.rp_filter[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.ipv4.conf.all.secure_redirects + + + oval:ssg-local_var_sysctl_net_ipv4_conf_all_secure_redirects_counter:var:1 + + + + oval:ssg-object_sysctl_net_ipv4_conf_all_secure_redirects_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_ipv4_conf_all_secure_redirects_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_ipv4_conf_all_secure_redirects:obj:1 + oval:ssg-var_obj_blank_sysctl_net_ipv4_conf_all_secure_redirects:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_ipv4_conf_all_secure_redirects:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_ipv4_conf_all_secure_redirects:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_ipv4_conf_all_secure_redirects:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_ipv4_conf_all_secure_redirects:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_ipv4_conf_all_secure_redirects:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_ipv4_conf_all_secure_redirects:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_ipv4_conf_all_secure_redirects:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_ipv4_conf_all_secure_redirects:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.ipv4.conf.all.secure_redirects[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.conf.all.secure_redirects[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.conf.all.secure_redirects[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.ipv4.conf.all.send_redirects + + + oval:ssg-local_var_sysctl_net_ipv4_conf_all_send_redirects_counter:var:1 + + + + oval:ssg-object_sysctl_net_ipv4_conf_all_send_redirects_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_ipv4_conf_all_send_redirects_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_ipv4_conf_all_send_redirects:obj:1 + oval:ssg-var_obj_blank_sysctl_net_ipv4_conf_all_send_redirects:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_ipv4_conf_all_send_redirects:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_ipv4_conf_all_send_redirects:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_ipv4_conf_all_send_redirects:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_ipv4_conf_all_send_redirects:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_ipv4_conf_all_send_redirects:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_ipv4_conf_all_send_redirects:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_ipv4_conf_all_send_redirects:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_ipv4_conf_all_send_redirects:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.ipv4.conf.all.send_redirects[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.conf.all.send_redirects[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.conf.all.send_redirects[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.ipv4.conf.all.shared_media + + + oval:ssg-local_var_sysctl_net_ipv4_conf_all_shared_media_counter:var:1 + + + + oval:ssg-object_sysctl_net_ipv4_conf_all_shared_media_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_ipv4_conf_all_shared_media_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_ipv4_conf_all_shared_media:obj:1 + oval:ssg-var_obj_blank_sysctl_net_ipv4_conf_all_shared_media:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_ipv4_conf_all_shared_media:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_ipv4_conf_all_shared_media:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_ipv4_conf_all_shared_media:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_ipv4_conf_all_shared_media:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_ipv4_conf_all_shared_media:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_ipv4_conf_all_shared_media:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_ipv4_conf_all_shared_media:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_ipv4_conf_all_shared_media:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.ipv4.conf.all.shared_media[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.conf.all.shared_media[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.conf.all.shared_media[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.ipv4.conf.default.accept_redirects + + + oval:ssg-local_var_sysctl_net_ipv4_conf_default_accept_redirects_counter:var:1 + + + + oval:ssg-object_sysctl_net_ipv4_conf_default_accept_redirects_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_ipv4_conf_default_accept_redirects_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_ipv4_conf_default_accept_redirects:obj:1 + oval:ssg-var_obj_blank_sysctl_net_ipv4_conf_default_accept_redirects:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_ipv4_conf_default_accept_redirects:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_ipv4_conf_default_accept_redirects:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_ipv4_conf_default_accept_redirects:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_ipv4_conf_default_accept_redirects:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_ipv4_conf_default_accept_redirects:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_ipv4_conf_default_accept_redirects:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_ipv4_conf_default_accept_redirects:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_ipv4_conf_default_accept_redirects:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.ipv4.conf.default.accept_redirects[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.conf.default.accept_redirects[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.conf.default.accept_redirects[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.ipv4.conf.default.accept_source_route + + + oval:ssg-local_var_sysctl_net_ipv4_conf_default_accept_source_route_counter:var:1 + + + + oval:ssg-object_sysctl_net_ipv4_conf_default_accept_source_route_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_ipv4_conf_default_accept_source_route_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_ipv4_conf_default_accept_source_route:obj:1 + oval:ssg-var_obj_blank_sysctl_net_ipv4_conf_default_accept_source_route:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_ipv4_conf_default_accept_source_route:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_ipv4_conf_default_accept_source_route:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_ipv4_conf_default_accept_source_route:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_ipv4_conf_default_accept_source_route:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_ipv4_conf_default_accept_source_route:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_ipv4_conf_default_accept_source_route:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_ipv4_conf_default_accept_source_route:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_ipv4_conf_default_accept_source_route:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.ipv4.conf.default.accept_source_route[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.conf.default.accept_source_route[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.conf.default.accept_source_route[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.ipv4.conf.default.log_martians + + + oval:ssg-local_var_sysctl_net_ipv4_conf_default_log_martians_counter:var:1 + + + + oval:ssg-object_sysctl_net_ipv4_conf_default_log_martians_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_ipv4_conf_default_log_martians_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_ipv4_conf_default_log_martians:obj:1 + oval:ssg-var_obj_blank_sysctl_net_ipv4_conf_default_log_martians:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_ipv4_conf_default_log_martians:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_ipv4_conf_default_log_martians:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_ipv4_conf_default_log_martians:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_ipv4_conf_default_log_martians:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_ipv4_conf_default_log_martians:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_ipv4_conf_default_log_martians:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_ipv4_conf_default_log_martians:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_ipv4_conf_default_log_martians:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.ipv4.conf.default.log_martians[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.conf.default.log_martians[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.conf.default.log_martians[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.ipv4.conf.default.rp_filter + + + oval:ssg-local_var_sysctl_net_ipv4_conf_default_rp_filter_counter:var:1 + + + + oval:ssg-object_sysctl_net_ipv4_conf_default_rp_filter_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_ipv4_conf_default_rp_filter_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_ipv4_conf_default_rp_filter:obj:1 + oval:ssg-var_obj_blank_sysctl_net_ipv4_conf_default_rp_filter:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_ipv4_conf_default_rp_filter:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_ipv4_conf_default_rp_filter:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_ipv4_conf_default_rp_filter:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_ipv4_conf_default_rp_filter:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_ipv4_conf_default_rp_filter:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_ipv4_conf_default_rp_filter:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_ipv4_conf_default_rp_filter:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_ipv4_conf_default_rp_filter:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.ipv4.conf.default.rp_filter[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.conf.default.rp_filter[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.conf.default.rp_filter[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.ipv4.conf.default.secure_redirects + + + oval:ssg-local_var_sysctl_net_ipv4_conf_default_secure_redirects_counter:var:1 + + + + oval:ssg-object_sysctl_net_ipv4_conf_default_secure_redirects_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_ipv4_conf_default_secure_redirects_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_ipv4_conf_default_secure_redirects:obj:1 + oval:ssg-var_obj_blank_sysctl_net_ipv4_conf_default_secure_redirects:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_ipv4_conf_default_secure_redirects:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_ipv4_conf_default_secure_redirects:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_ipv4_conf_default_secure_redirects:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_ipv4_conf_default_secure_redirects:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_ipv4_conf_default_secure_redirects:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_ipv4_conf_default_secure_redirects:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_ipv4_conf_default_secure_redirects:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_ipv4_conf_default_secure_redirects:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.ipv4.conf.default.secure_redirects[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.conf.default.secure_redirects[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.conf.default.secure_redirects[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.ipv4.conf.default.send_redirects + + + oval:ssg-local_var_sysctl_net_ipv4_conf_default_send_redirects_counter:var:1 + + + + oval:ssg-object_sysctl_net_ipv4_conf_default_send_redirects_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_ipv4_conf_default_send_redirects_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_ipv4_conf_default_send_redirects:obj:1 + oval:ssg-var_obj_blank_sysctl_net_ipv4_conf_default_send_redirects:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_ipv4_conf_default_send_redirects:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_ipv4_conf_default_send_redirects:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_ipv4_conf_default_send_redirects:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_ipv4_conf_default_send_redirects:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_ipv4_conf_default_send_redirects:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_ipv4_conf_default_send_redirects:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_ipv4_conf_default_send_redirects:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_ipv4_conf_default_send_redirects:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.ipv4.conf.default.send_redirects[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.conf.default.send_redirects[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.conf.default.send_redirects[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.ipv4.conf.default.shared_media + + + oval:ssg-local_var_sysctl_net_ipv4_conf_default_shared_media_counter:var:1 + + + + oval:ssg-object_sysctl_net_ipv4_conf_default_shared_media_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_ipv4_conf_default_shared_media_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_ipv4_conf_default_shared_media:obj:1 + oval:ssg-var_obj_blank_sysctl_net_ipv4_conf_default_shared_media:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_ipv4_conf_default_shared_media:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_ipv4_conf_default_shared_media:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_ipv4_conf_default_shared_media:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_ipv4_conf_default_shared_media:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_ipv4_conf_default_shared_media:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_ipv4_conf_default_shared_media:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_ipv4_conf_default_shared_media:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_ipv4_conf_default_shared_media:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.ipv4.conf.default.shared_media[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.conf.default.shared_media[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.conf.default.shared_media[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.ipv4.icmp_echo_ignore_broadcasts + + + oval:ssg-local_var_sysctl_net_ipv4_icmp_echo_ignore_broadcasts_counter:var:1 + + + + oval:ssg-object_sysctl_net_ipv4_icmp_echo_ignore_broadcasts_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_ipv4_icmp_echo_ignore_broadcasts_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_ipv4_icmp_echo_ignore_broadcasts:obj:1 + oval:ssg-var_obj_blank_sysctl_net_ipv4_icmp_echo_ignore_broadcasts:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_ipv4_icmp_echo_ignore_broadcasts:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_ipv4_icmp_echo_ignore_broadcasts:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_ipv4_icmp_echo_ignore_broadcasts:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_ipv4_icmp_echo_ignore_broadcasts:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_ipv4_icmp_echo_ignore_broadcasts:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_ipv4_icmp_echo_ignore_broadcasts:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_ipv4_icmp_echo_ignore_broadcasts:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_ipv4_icmp_echo_ignore_broadcasts:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.ipv4.icmp_echo_ignore_broadcasts[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.icmp_echo_ignore_broadcasts[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.icmp_echo_ignore_broadcasts[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.ipv4.icmp_ignore_bogus_error_responses + + + oval:ssg-local_var_sysctl_net_ipv4_icmp_ignore_bogus_error_responses_counter:var:1 + + + + oval:ssg-object_sysctl_net_ipv4_icmp_ignore_bogus_error_responses_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_ipv4_icmp_ignore_bogus_error_responses_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_ipv4_icmp_ignore_bogus_error_responses:obj:1 + oval:ssg-var_obj_blank_sysctl_net_ipv4_icmp_ignore_bogus_error_responses:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_ipv4_icmp_ignore_bogus_error_responses:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_ipv4_icmp_ignore_bogus_error_responses:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_ipv4_icmp_ignore_bogus_error_responses:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_ipv4_icmp_ignore_bogus_error_responses:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_ipv4_icmp_ignore_bogus_error_responses:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_ipv4_icmp_ignore_bogus_error_responses:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_ipv4_icmp_ignore_bogus_error_responses:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_ipv4_icmp_ignore_bogus_error_responses:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.ipv4.icmp_ignore_bogus_error_responses[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.icmp_ignore_bogus_error_responses[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.icmp_ignore_bogus_error_responses[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.ipv4.ip_forward + + + oval:ssg-local_var_sysctl_net_ipv4_ip_forward_counter:var:1 + + + + oval:ssg-object_sysctl_net_ipv4_ip_forward_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_ipv4_ip_forward_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_ipv4_ip_forward:obj:1 + oval:ssg-var_obj_blank_sysctl_net_ipv4_ip_forward:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_ipv4_ip_forward:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_ipv4_ip_forward:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_ipv4_ip_forward:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_ipv4_ip_forward:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_ipv4_ip_forward:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_ipv4_ip_forward:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_ipv4_ip_forward:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_ipv4_ip_forward:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.ipv4.ip_forward[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.ip_forward[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.ip_forward[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.ipv4.ip_local_port_range + + + oval:ssg-local_var_sysctl_net_ipv4_ip_local_port_range_counter:var:1 + + + + oval:ssg-object_sysctl_net_ipv4_ip_local_port_range_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_ipv4_ip_local_port_range_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_ipv4_ip_local_port_range:obj:1 + oval:ssg-var_obj_blank_sysctl_net_ipv4_ip_local_port_range:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_ipv4_ip_local_port_range:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_ipv4_ip_local_port_range:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_ipv4_ip_local_port_range:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_ipv4_ip_local_port_range:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_ipv4_ip_local_port_range:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_ipv4_ip_local_port_range:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_ipv4_ip_local_port_range:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_ipv4_ip_local_port_range:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.ipv4.ip_local_port_range[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.ip_local_port_range[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.ip_local_port_range[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.ipv4.tcp_invalid_ratelimit + + + oval:ssg-local_var_sysctl_net_ipv4_tcp_invalid_ratelimit_counter:var:1 + + + + oval:ssg-object_sysctl_net_ipv4_tcp_invalid_ratelimit_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_ipv4_tcp_invalid_ratelimit_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_ipv4_tcp_invalid_ratelimit:obj:1 + oval:ssg-var_obj_blank_sysctl_net_ipv4_tcp_invalid_ratelimit:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_ipv4_tcp_invalid_ratelimit:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_ipv4_tcp_invalid_ratelimit:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_ipv4_tcp_invalid_ratelimit:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_ipv4_tcp_invalid_ratelimit:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_ipv4_tcp_invalid_ratelimit:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_ipv4_tcp_invalid_ratelimit:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_ipv4_tcp_invalid_ratelimit:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_ipv4_tcp_invalid_ratelimit:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.ipv4.tcp_invalid_ratelimit[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.tcp_invalid_ratelimit[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.tcp_invalid_ratelimit[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.ipv4.tcp_rfc1337 + + + oval:ssg-local_var_sysctl_net_ipv4_tcp_rfc1337_counter:var:1 + + + + oval:ssg-object_sysctl_net_ipv4_tcp_rfc1337_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_ipv4_tcp_rfc1337_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_ipv4_tcp_rfc1337:obj:1 + oval:ssg-var_obj_blank_sysctl_net_ipv4_tcp_rfc1337:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_ipv4_tcp_rfc1337:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_ipv4_tcp_rfc1337:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_ipv4_tcp_rfc1337:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_ipv4_tcp_rfc1337:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_ipv4_tcp_rfc1337:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_ipv4_tcp_rfc1337:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_ipv4_tcp_rfc1337:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_ipv4_tcp_rfc1337:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.ipv4.tcp_rfc1337[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.tcp_rfc1337[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.tcp_rfc1337[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.ipv4.tcp_syncookies + + + oval:ssg-local_var_sysctl_net_ipv4_tcp_syncookies_counter:var:1 + + + + oval:ssg-object_sysctl_net_ipv4_tcp_syncookies_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_ipv4_tcp_syncookies_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_ipv4_tcp_syncookies:obj:1 + oval:ssg-var_obj_blank_sysctl_net_ipv4_tcp_syncookies:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_ipv4_tcp_syncookies:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_ipv4_tcp_syncookies:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_ipv4_tcp_syncookies:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_ipv4_tcp_syncookies:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_ipv4_tcp_syncookies:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_ipv4_tcp_syncookies:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_ipv4_tcp_syncookies:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_ipv4_tcp_syncookies:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.ipv4.tcp_syncookies[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.tcp_syncookies[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv4.tcp_syncookies[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.ipv6.conf.all.accept_ra + + + oval:ssg-local_var_sysctl_net_ipv6_conf_all_accept_ra_counter:var:1 + + + + oval:ssg-object_sysctl_net_ipv6_conf_all_accept_ra_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_ipv6_conf_all_accept_ra_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_ipv6_conf_all_accept_ra:obj:1 + oval:ssg-var_obj_blank_sysctl_net_ipv6_conf_all_accept_ra:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_ipv6_conf_all_accept_ra:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_ipv6_conf_all_accept_ra:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_ipv6_conf_all_accept_ra:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_ipv6_conf_all_accept_ra:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_ipv6_conf_all_accept_ra:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_ipv6_conf_all_accept_ra:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_ipv6_conf_all_accept_ra:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_ipv6_conf_all_accept_ra:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.ipv6.conf.all.accept_ra[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv6.conf.all.accept_ra[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv6.conf.all.accept_ra[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.ipv6.conf.all.accept_ra_defrtr + + + oval:ssg-local_var_sysctl_net_ipv6_conf_all_accept_ra_defrtr_counter:var:1 + + + + oval:ssg-object_sysctl_net_ipv6_conf_all_accept_ra_defrtr_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_ipv6_conf_all_accept_ra_defrtr_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_ipv6_conf_all_accept_ra_defrtr:obj:1 + oval:ssg-var_obj_blank_sysctl_net_ipv6_conf_all_accept_ra_defrtr:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_ipv6_conf_all_accept_ra_defrtr:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_ipv6_conf_all_accept_ra_defrtr:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_ipv6_conf_all_accept_ra_defrtr:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_ipv6_conf_all_accept_ra_defrtr:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_ipv6_conf_all_accept_ra_defrtr:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_ipv6_conf_all_accept_ra_defrtr:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_ipv6_conf_all_accept_ra_defrtr:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_ipv6_conf_all_accept_ra_defrtr:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.ipv6.conf.all.accept_ra_defrtr[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv6.conf.all.accept_ra_defrtr[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv6.conf.all.accept_ra_defrtr[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.ipv6.conf.all.accept_ra_pinfo + + + oval:ssg-local_var_sysctl_net_ipv6_conf_all_accept_ra_pinfo_counter:var:1 + + + + oval:ssg-object_sysctl_net_ipv6_conf_all_accept_ra_pinfo_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_ipv6_conf_all_accept_ra_pinfo_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_ipv6_conf_all_accept_ra_pinfo:obj:1 + oval:ssg-var_obj_blank_sysctl_net_ipv6_conf_all_accept_ra_pinfo:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_ipv6_conf_all_accept_ra_pinfo:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_ipv6_conf_all_accept_ra_pinfo:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_ipv6_conf_all_accept_ra_pinfo:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_ipv6_conf_all_accept_ra_pinfo:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_ipv6_conf_all_accept_ra_pinfo:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_ipv6_conf_all_accept_ra_pinfo:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_ipv6_conf_all_accept_ra_pinfo:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_ipv6_conf_all_accept_ra_pinfo:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.ipv6.conf.all.accept_ra_pinfo[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv6.conf.all.accept_ra_pinfo[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv6.conf.all.accept_ra_pinfo[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.ipv6.conf.all.accept_ra_rtr_pref + + + oval:ssg-local_var_sysctl_net_ipv6_conf_all_accept_ra_rtr_pref_counter:var:1 + + + + oval:ssg-object_sysctl_net_ipv6_conf_all_accept_ra_rtr_pref_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_ipv6_conf_all_accept_ra_rtr_pref_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_ipv6_conf_all_accept_ra_rtr_pref:obj:1 + oval:ssg-var_obj_blank_sysctl_net_ipv6_conf_all_accept_ra_rtr_pref:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_ipv6_conf_all_accept_ra_rtr_pref:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_ipv6_conf_all_accept_ra_rtr_pref:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_ipv6_conf_all_accept_ra_rtr_pref:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_ipv6_conf_all_accept_ra_rtr_pref:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_ipv6_conf_all_accept_ra_rtr_pref:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_ipv6_conf_all_accept_ra_rtr_pref:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_ipv6_conf_all_accept_ra_rtr_pref:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_ipv6_conf_all_accept_ra_rtr_pref:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.ipv6.conf.all.accept_ra_rtr_pref[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv6.conf.all.accept_ra_rtr_pref[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv6.conf.all.accept_ra_rtr_pref[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.ipv6.conf.all.accept_redirects + + + oval:ssg-local_var_sysctl_net_ipv6_conf_all_accept_redirects_counter:var:1 + + + + oval:ssg-object_sysctl_net_ipv6_conf_all_accept_redirects_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_ipv6_conf_all_accept_redirects_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_ipv6_conf_all_accept_redirects:obj:1 + oval:ssg-var_obj_blank_sysctl_net_ipv6_conf_all_accept_redirects:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_ipv6_conf_all_accept_redirects:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_ipv6_conf_all_accept_redirects:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_ipv6_conf_all_accept_redirects:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_ipv6_conf_all_accept_redirects:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_ipv6_conf_all_accept_redirects:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_ipv6_conf_all_accept_redirects:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_ipv6_conf_all_accept_redirects:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_ipv6_conf_all_accept_redirects:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.ipv6.conf.all.accept_redirects[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv6.conf.all.accept_redirects[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv6.conf.all.accept_redirects[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.ipv6.conf.all.accept_source_route + + + oval:ssg-local_var_sysctl_net_ipv6_conf_all_accept_source_route_counter:var:1 + + + + oval:ssg-object_sysctl_net_ipv6_conf_all_accept_source_route_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_ipv6_conf_all_accept_source_route_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_ipv6_conf_all_accept_source_route:obj:1 + oval:ssg-var_obj_blank_sysctl_net_ipv6_conf_all_accept_source_route:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_ipv6_conf_all_accept_source_route:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_ipv6_conf_all_accept_source_route:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_ipv6_conf_all_accept_source_route:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_ipv6_conf_all_accept_source_route:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_ipv6_conf_all_accept_source_route:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_ipv6_conf_all_accept_source_route:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_ipv6_conf_all_accept_source_route:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_ipv6_conf_all_accept_source_route:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.ipv6.conf.all.accept_source_route[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv6.conf.all.accept_source_route[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv6.conf.all.accept_source_route[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.ipv6.conf.all.autoconf + + + oval:ssg-local_var_sysctl_net_ipv6_conf_all_autoconf_counter:var:1 + + + + oval:ssg-object_sysctl_net_ipv6_conf_all_autoconf_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_ipv6_conf_all_autoconf_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_ipv6_conf_all_autoconf:obj:1 + oval:ssg-var_obj_blank_sysctl_net_ipv6_conf_all_autoconf:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_ipv6_conf_all_autoconf:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_ipv6_conf_all_autoconf:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_ipv6_conf_all_autoconf:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_ipv6_conf_all_autoconf:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_ipv6_conf_all_autoconf:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_ipv6_conf_all_autoconf:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_ipv6_conf_all_autoconf:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_ipv6_conf_all_autoconf:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.ipv6.conf.all.autoconf[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv6.conf.all.autoconf[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv6.conf.all.autoconf[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.ipv6.conf.all.disable_ipv6 + + + oval:ssg-local_var_sysctl_net_ipv6_conf_all_disable_ipv6_counter:var:1 + + + + oval:ssg-object_sysctl_net_ipv6_conf_all_disable_ipv6_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_ipv6_conf_all_disable_ipv6_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_ipv6_conf_all_disable_ipv6:obj:1 + oval:ssg-var_obj_blank_sysctl_net_ipv6_conf_all_disable_ipv6:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_ipv6_conf_all_disable_ipv6:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_ipv6_conf_all_disable_ipv6:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_ipv6_conf_all_disable_ipv6:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_ipv6_conf_all_disable_ipv6:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_ipv6_conf_all_disable_ipv6:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_ipv6_conf_all_disable_ipv6:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_ipv6_conf_all_disable_ipv6:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_ipv6_conf_all_disable_ipv6:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.ipv6.conf.all.disable_ipv6[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv6.conf.all.disable_ipv6[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv6.conf.all.disable_ipv6[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.ipv6.conf.all.forwarding + + + oval:ssg-local_var_sysctl_net_ipv6_conf_all_forwarding_counter:var:1 + + + + oval:ssg-object_sysctl_net_ipv6_conf_all_forwarding_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_ipv6_conf_all_forwarding_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_ipv6_conf_all_forwarding:obj:1 + oval:ssg-var_obj_blank_sysctl_net_ipv6_conf_all_forwarding:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_ipv6_conf_all_forwarding:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_ipv6_conf_all_forwarding:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_ipv6_conf_all_forwarding:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_ipv6_conf_all_forwarding:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_ipv6_conf_all_forwarding:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_ipv6_conf_all_forwarding:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_ipv6_conf_all_forwarding:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_ipv6_conf_all_forwarding:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.ipv6.conf.all.forwarding[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv6.conf.all.forwarding[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv6.conf.all.forwarding[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.ipv6.conf.all.max_addresses + + + oval:ssg-local_var_sysctl_net_ipv6_conf_all_max_addresses_counter:var:1 + + + + oval:ssg-object_sysctl_net_ipv6_conf_all_max_addresses_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_ipv6_conf_all_max_addresses_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_ipv6_conf_all_max_addresses:obj:1 + oval:ssg-var_obj_blank_sysctl_net_ipv6_conf_all_max_addresses:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_ipv6_conf_all_max_addresses:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_ipv6_conf_all_max_addresses:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_ipv6_conf_all_max_addresses:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_ipv6_conf_all_max_addresses:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_ipv6_conf_all_max_addresses:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_ipv6_conf_all_max_addresses:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_ipv6_conf_all_max_addresses:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_ipv6_conf_all_max_addresses:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.ipv6.conf.all.max_addresses[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv6.conf.all.max_addresses[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv6.conf.all.max_addresses[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.ipv6.conf.all.router_solicitations + + + oval:ssg-local_var_sysctl_net_ipv6_conf_all_router_solicitations_counter:var:1 + + + + oval:ssg-object_sysctl_net_ipv6_conf_all_router_solicitations_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_ipv6_conf_all_router_solicitations_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_ipv6_conf_all_router_solicitations:obj:1 + oval:ssg-var_obj_blank_sysctl_net_ipv6_conf_all_router_solicitations:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_ipv6_conf_all_router_solicitations:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_ipv6_conf_all_router_solicitations:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_ipv6_conf_all_router_solicitations:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_ipv6_conf_all_router_solicitations:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_ipv6_conf_all_router_solicitations:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_ipv6_conf_all_router_solicitations:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_ipv6_conf_all_router_solicitations:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_ipv6_conf_all_router_solicitations:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.ipv6.conf.all.router_solicitations[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv6.conf.all.router_solicitations[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv6.conf.all.router_solicitations[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.ipv6.conf.default.accept_ra + + + oval:ssg-local_var_sysctl_net_ipv6_conf_default_accept_ra_counter:var:1 + + + + oval:ssg-object_sysctl_net_ipv6_conf_default_accept_ra_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_ipv6_conf_default_accept_ra_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_ipv6_conf_default_accept_ra:obj:1 + oval:ssg-var_obj_blank_sysctl_net_ipv6_conf_default_accept_ra:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_ipv6_conf_default_accept_ra:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_ipv6_conf_default_accept_ra:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_ipv6_conf_default_accept_ra:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_ipv6_conf_default_accept_ra:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_ipv6_conf_default_accept_ra:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_ipv6_conf_default_accept_ra:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_ipv6_conf_default_accept_ra:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_ipv6_conf_default_accept_ra:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.ipv6.conf.default.accept_ra[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv6.conf.default.accept_ra[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv6.conf.default.accept_ra[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.ipv6.conf.default.accept_ra_defrtr + + + oval:ssg-local_var_sysctl_net_ipv6_conf_default_accept_ra_defrtr_counter:var:1 + + + + oval:ssg-object_sysctl_net_ipv6_conf_default_accept_ra_defrtr_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_ipv6_conf_default_accept_ra_defrtr_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_ipv6_conf_default_accept_ra_defrtr:obj:1 + oval:ssg-var_obj_blank_sysctl_net_ipv6_conf_default_accept_ra_defrtr:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_ipv6_conf_default_accept_ra_defrtr:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_ipv6_conf_default_accept_ra_defrtr:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_ipv6_conf_default_accept_ra_defrtr:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_ipv6_conf_default_accept_ra_defrtr:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_ipv6_conf_default_accept_ra_defrtr:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_ipv6_conf_default_accept_ra_defrtr:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_ipv6_conf_default_accept_ra_defrtr:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_ipv6_conf_default_accept_ra_defrtr:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.ipv6.conf.default.accept_ra_defrtr[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv6.conf.default.accept_ra_defrtr[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv6.conf.default.accept_ra_defrtr[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.ipv6.conf.default.accept_ra_pinfo + + + oval:ssg-local_var_sysctl_net_ipv6_conf_default_accept_ra_pinfo_counter:var:1 + + + + oval:ssg-object_sysctl_net_ipv6_conf_default_accept_ra_pinfo_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_ipv6_conf_default_accept_ra_pinfo_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_ipv6_conf_default_accept_ra_pinfo:obj:1 + oval:ssg-var_obj_blank_sysctl_net_ipv6_conf_default_accept_ra_pinfo:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_ipv6_conf_default_accept_ra_pinfo:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_ipv6_conf_default_accept_ra_pinfo:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_ipv6_conf_default_accept_ra_pinfo:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_ipv6_conf_default_accept_ra_pinfo:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_ipv6_conf_default_accept_ra_pinfo:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_ipv6_conf_default_accept_ra_pinfo:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_ipv6_conf_default_accept_ra_pinfo:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_ipv6_conf_default_accept_ra_pinfo:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.ipv6.conf.default.accept_ra_pinfo[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv6.conf.default.accept_ra_pinfo[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv6.conf.default.accept_ra_pinfo[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.ipv6.conf.default.accept_ra_rtr_pref + + + oval:ssg-local_var_sysctl_net_ipv6_conf_default_accept_ra_rtr_pref_counter:var:1 + + + + oval:ssg-object_sysctl_net_ipv6_conf_default_accept_ra_rtr_pref_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_ipv6_conf_default_accept_ra_rtr_pref_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_ipv6_conf_default_accept_ra_rtr_pref:obj:1 + oval:ssg-var_obj_blank_sysctl_net_ipv6_conf_default_accept_ra_rtr_pref:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_ipv6_conf_default_accept_ra_rtr_pref:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_ipv6_conf_default_accept_ra_rtr_pref:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_ipv6_conf_default_accept_ra_rtr_pref:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_ipv6_conf_default_accept_ra_rtr_pref:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_ipv6_conf_default_accept_ra_rtr_pref:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_ipv6_conf_default_accept_ra_rtr_pref:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_ipv6_conf_default_accept_ra_rtr_pref:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_ipv6_conf_default_accept_ra_rtr_pref:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.ipv6.conf.default.accept_ra_rtr_pref[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv6.conf.default.accept_ra_rtr_pref[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv6.conf.default.accept_ra_rtr_pref[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.ipv6.conf.default.accept_redirects + + + oval:ssg-local_var_sysctl_net_ipv6_conf_default_accept_redirects_counter:var:1 + + + + oval:ssg-object_sysctl_net_ipv6_conf_default_accept_redirects_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_ipv6_conf_default_accept_redirects_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_ipv6_conf_default_accept_redirects:obj:1 + oval:ssg-var_obj_blank_sysctl_net_ipv6_conf_default_accept_redirects:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_ipv6_conf_default_accept_redirects:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_ipv6_conf_default_accept_redirects:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_ipv6_conf_default_accept_redirects:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_ipv6_conf_default_accept_redirects:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_ipv6_conf_default_accept_redirects:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_ipv6_conf_default_accept_redirects:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_ipv6_conf_default_accept_redirects:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_ipv6_conf_default_accept_redirects:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.ipv6.conf.default.accept_redirects[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv6.conf.default.accept_redirects[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv6.conf.default.accept_redirects[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.ipv6.conf.default.accept_source_route + + + oval:ssg-local_var_sysctl_net_ipv6_conf_default_accept_source_route_counter:var:1 + + + + oval:ssg-object_sysctl_net_ipv6_conf_default_accept_source_route_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_ipv6_conf_default_accept_source_route_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_ipv6_conf_default_accept_source_route:obj:1 + oval:ssg-var_obj_blank_sysctl_net_ipv6_conf_default_accept_source_route:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_ipv6_conf_default_accept_source_route:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_ipv6_conf_default_accept_source_route:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_ipv6_conf_default_accept_source_route:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_ipv6_conf_default_accept_source_route:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_ipv6_conf_default_accept_source_route:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_ipv6_conf_default_accept_source_route:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_ipv6_conf_default_accept_source_route:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_ipv6_conf_default_accept_source_route:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.ipv6.conf.default.accept_source_route[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv6.conf.default.accept_source_route[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv6.conf.default.accept_source_route[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.ipv6.conf.default.autoconf + + + oval:ssg-local_var_sysctl_net_ipv6_conf_default_autoconf_counter:var:1 + + + + oval:ssg-object_sysctl_net_ipv6_conf_default_autoconf_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_ipv6_conf_default_autoconf_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_ipv6_conf_default_autoconf:obj:1 + oval:ssg-var_obj_blank_sysctl_net_ipv6_conf_default_autoconf:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_ipv6_conf_default_autoconf:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_ipv6_conf_default_autoconf:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_ipv6_conf_default_autoconf:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_ipv6_conf_default_autoconf:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_ipv6_conf_default_autoconf:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_ipv6_conf_default_autoconf:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_ipv6_conf_default_autoconf:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_ipv6_conf_default_autoconf:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.ipv6.conf.default.autoconf[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv6.conf.default.autoconf[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv6.conf.default.autoconf[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.ipv6.conf.default.disable_ipv6 + + + oval:ssg-local_var_sysctl_net_ipv6_conf_default_disable_ipv6_counter:var:1 + + + + oval:ssg-object_sysctl_net_ipv6_conf_default_disable_ipv6_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_ipv6_conf_default_disable_ipv6_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_ipv6_conf_default_disable_ipv6:obj:1 + oval:ssg-var_obj_blank_sysctl_net_ipv6_conf_default_disable_ipv6:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_ipv6_conf_default_disable_ipv6:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_ipv6_conf_default_disable_ipv6:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_ipv6_conf_default_disable_ipv6:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_ipv6_conf_default_disable_ipv6:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_ipv6_conf_default_disable_ipv6:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_ipv6_conf_default_disable_ipv6:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_ipv6_conf_default_disable_ipv6:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_ipv6_conf_default_disable_ipv6:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.ipv6.conf.default.disable_ipv6[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv6.conf.default.disable_ipv6[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv6.conf.default.disable_ipv6[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.ipv6.conf.default.max_addresses + + + oval:ssg-local_var_sysctl_net_ipv6_conf_default_max_addresses_counter:var:1 + + + + oval:ssg-object_sysctl_net_ipv6_conf_default_max_addresses_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_ipv6_conf_default_max_addresses_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_ipv6_conf_default_max_addresses:obj:1 + oval:ssg-var_obj_blank_sysctl_net_ipv6_conf_default_max_addresses:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_ipv6_conf_default_max_addresses:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_ipv6_conf_default_max_addresses:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_ipv6_conf_default_max_addresses:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_ipv6_conf_default_max_addresses:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_ipv6_conf_default_max_addresses:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_ipv6_conf_default_max_addresses:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_ipv6_conf_default_max_addresses:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_ipv6_conf_default_max_addresses:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.ipv6.conf.default.max_addresses[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv6.conf.default.max_addresses[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv6.conf.default.max_addresses[\s]*=[\s]*(.*)[\s]*$ + 1 + + + net.ipv6.conf.default.router_solicitations + + + oval:ssg-local_var_sysctl_net_ipv6_conf_default_router_solicitations_counter:var:1 + + + + oval:ssg-object_sysctl_net_ipv6_conf_default_router_solicitations_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_net_ipv6_conf_default_router_solicitations_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_net_ipv6_conf_default_router_solicitations:obj:1 + oval:ssg-var_obj_blank_sysctl_net_ipv6_conf_default_router_solicitations:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_net_ipv6_conf_default_router_solicitations:var:1 + + + oval:ssg-local_var_symlinks_sysctl_net_ipv6_conf_default_router_solicitations:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_net_ipv6_conf_default_router_solicitations:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_net_ipv6_conf_default_router_solicitations:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_net_ipv6_conf_default_router_solicitations:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_net_ipv6_conf_default_router_solicitations:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_net_ipv6_conf_default_router_solicitations:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_net_ipv6_conf_default_router_solicitations:obj:1 + + + + /etc/sysctl.conf + ^[\s]*net.ipv6.conf.default.router_solicitations[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv6.conf.default.router_solicitations[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*net.ipv6.conf.default.router_solicitations[\s]*=[\s]*(.*)[\s]*$ + 1 + + + user.max_user_namespaces + + + oval:ssg-local_var_sysctl_user_max_user_namespaces_counter:var:1 + + + + oval:ssg-object_sysctl_user_max_user_namespaces_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_user_max_user_namespaces_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_user_max_user_namespaces:obj:1 + oval:ssg-var_obj_blank_sysctl_user_max_user_namespaces:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_user_max_user_namespaces:var:1 + + + oval:ssg-local_var_symlinks_sysctl_user_max_user_namespaces:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_user_max_user_namespaces:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_user_max_user_namespaces:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_user_max_user_namespaces:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_user_max_user_namespaces:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_user_max_user_namespaces:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_user_max_user_namespaces:obj:1 + + + + /etc/sysctl.conf + ^[\s]*user.max_user_namespaces[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*user.max_user_namespaces[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*user.max_user_namespaces[\s]*=[\s]*(.*)[\s]*$ + 1 + + + vm.mmap_min_addr + + + oval:ssg-local_var_sysctl_vm_mmap_min_addr_counter:var:1 + + + + oval:ssg-object_sysctl_vm_mmap_min_addr_static_set_sysctls_unfiltered:obj:1 + oval:ssg-state_sysctl_vm_mmap_min_addr_filepath_is_symlink:ste:1 + + + + + oval:ssg-var_obj_symlink_sysctl_vm_mmap_min_addr:obj:1 + oval:ssg-var_obj_blank_sysctl_vm_mmap_min_addr:obj:1 + + + + oval:ssg-local_var_blank_path_sysctl_vm_mmap_min_addr:var:1 + + + oval:ssg-local_var_symlinks_sysctl_vm_mmap_min_addr:var:1 + + + + oval:ssg-state_symlink_points_outside_usual_dirs_sysctl_vm_mmap_min_addr:ste:1 + + + + oval:ssg-object_static_etc_sysctls_sysctl_vm_mmap_min_addr:obj:1 + oval:ssg-object_static_run_usr_sysctls_sysctl_vm_mmap_min_addr:obj:1 + + + + + oval:ssg-object_static_sysctl_sysctl_vm_mmap_min_addr:obj:1 + oval:ssg-object_static_etc_sysctld_sysctl_vm_mmap_min_addr:obj:1 + + + + + oval:ssg-object_static_run_sysctld_sysctl_vm_mmap_min_addr:obj:1 + + + + /etc/sysctl.conf + ^[\s]*vm.mmap_min_addr[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /etc/sysctl.d + ^.*\.conf$ + ^[\s]*vm.mmap_min_addr[\s]*=[\s]*(.*)[\s]*$ + 1 + + + /run/sysctl.d + ^.*\.conf$ + ^[\s]*vm.mmap_min_addr[\s]*=[\s]*(.*)[\s]*$ + 1 + + + multi-user.target + + + dnf-automatic\.timer + ActiveState + + + ^/boot/loader/entries/.*.conf + ^options (.*)$ + 1 + + + ^/etc/kernel/cmdline + ^(.*)$ + 1 + + + ^/boot/loader/entries/.*.conf + ^options (.*)$ + 1 + + + ^/etc/kernel/cmdline + ^(.*)$ + 1 + + + ^/boot/loader/entries/.*.conf + ^options (.*)$ + 1 + + + ^/etc/kernel/cmdline + ^(.*)$ + 1 + + + ^/boot/loader/entries/.*.conf + ^options (.*)$ + 1 + + + ^/etc/kernel/cmdline + ^(.*)$ + 1 + + + ^/boot/loader/entries/.*.conf + ^options (.*)$ + 1 + + + ^/etc/kernel/cmdline + ^(.*)$ + 1 + + + ^/boot/loader/entries/.*.conf + ^options (.*)$ + 1 + + + ^/etc/kernel/cmdline + ^(.*)$ + 1 + + + ^/boot/loader/entries/.*.conf + ^options (.*)$ + 1 + + + ^/etc/kernel/cmdline + ^(.*)$ + 1 + + + /etc/pam.d/system-auth + ^\s*password\s+(?:(?:required)|(?:requisite))\s+pam_faillock\.so.*$ + 1 + + + + ^\s*password\s+(?:(?:required)|(?:requisite))\s+pam_pwquality\.so.*$ + 1 + + + /usr/lib/systemd/system/auditd.service + ^ExecStartPost=\-\/sbin\/auditctl.*$ + 1 + + + /usr/lib/systemd/system/auditd.service + ^(ExecStartPost=\-\/sbin\/augenrules.*$|Requires=augenrules.service) + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+setdomainname[\s]+|([\s]+|[,])setdomainname([\s]+|[,]))).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+setdomainname[\s]+|([\s]+|[,])setdomainname([\s]+|[,]))).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+setdomainname[\s]+|([\s]+|[,])setdomainname([\s]+|[,]))).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+setdomainname[\s]+|([\s]+|[,])setdomainname([\s]+|[,]))).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+sethostname[\s]+|([\s]+|[,])sethostname([\s]+|[,]))).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + ^/etc/audit/rules\.d/.*\.rules$ + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+sethostname[\s]+|([\s]+|[,])sethostname([\s]+|[,]))).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+sethostname[\s]+|([\s]+|[,])sethostname([\s]+|[,]))).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/audit.rules + ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+sethostname[\s]+|([\s]+|[,])sethostname([\s]+|[,]))).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + 1 + + + /etc/audit/auditd.conf + ^(log_file\s*=\s*.*)$ + 1 + + + /etc/audit/auditd.conf + ^[ ]*log_group[ ]+=[ ]+root[ ]*$ + 1 + + + /etc/audit/auditd.conf + ^[ ]*log_group[ ]+=.*$ + 1 + + + /etc/default/grub + ^\s*GRUB_DISABLE_RECOVERY=(.*)$ + 1 + + + ^/etc/chrony\.(conf|d/.+\.conf)$ + ^([\s]*server[\s]+.+$){2,}$ + 1 + + + /etc/default/grub + ^\s*GRUB_CMDLINE_LINUX_DEFAULT=.*$ + 1 + + + /boot/loader/entries/ + ^.*\.conf$ + ^options(?:\s+.*)?\s+\$kernelopts\b.*$ + 1 + + + alinux-release + + + alinux-release + + + centos-release + + + /etc/os-release + ^ID="(\w+)"$ + 1 + + + /etc/os-release + ^VERSION_ID="(\d)"$ + 1 + + + /etc/os-release + ^ID="(\w+)"$ + 1 + + + /etc/os-release + ^VERSION_ID="(\d)"$ + 1 + + + /etc/debian_version + + + /etc/debian_version + ^10.[0-9]+$ + 1 + + + /etc/debian_version + ^11.[0-9]+$ + 1 + + + /etc/debian_version + ^9.[0-9]+$ + 1 + + + fedora-release.* + + + /etc/system-release-cpe + ^cpe:\/o:fedoraproject:fedora:[\d]+$ + 1 + + + oraclelinux-release + + + oraclelinux-release + + + oraclelinux-release + + + openSUSE-release + + + openSUSE-release + + + openSUSE-release + + + + /etc/os-release + ^ID="(\w+)"$ + 1 + + + /etc/os-release + ^VERSION_ID="(\d)\.\d+"$ + 1 + + + + redhat-release-client + + + redhat-release-workstation + + + redhat-release-server + + + redhat-release-computenode + + + /etc/redhat-release + ^Red Hat Enterprise Linux release (\d)\.\d+$ + 1 + + + + redhat-release + + + redhat-release + + + redhat-release + + + redhat-release + + + redhat-release + + + redhat-release + + + redhat-release + + + redhat-release + + + redhat-release + + + redhat-release + + + redhat-release + + + redhat-release + + + /etc/redhat-release + ^Red Hat Enterprise Linux release (\d)\.\d+$ + 1 + + + + redhat-release + + + /etc/redhat-release + ^Red Hat Enterprise Linux release (\d)\.\d+$ + 1 + + + redhat-release-virtualization-host + + + sl-release + + + + sled-release + + + sles-release + + + SLES_SAP-release + + + + sled-release + + + sles-release + + + SLES_SAP-release + + + /etc/lsb-release + + + /etc/lsb-release + ^DISTRIB_ID=Ubuntu$ + 1 + + + /etc/lsb-release + ^DISTRIB_CODENAME=xenial$ + 1 + + + /etc/lsb-release + ^DISTRIB_CODENAME=bionic$ + 1 + + + /etc/lsb-release + ^DISTRIB_CODENAME=focal$ + 1 + + + /etc/lsb-release + ^DISTRIB_CODENAME=jammy$ + 1 + + + uos-release + + + rhvm-appliance + + + audit + + + chrony + + + gdm + + + grub2-common + + + /sys/firmware/opal + + + libuser + + + shadow-utils + + + net-snmp + + + nss-pam-ldapd + + + ntp + + + ovirt-host + + + ovirt-engine + + + pam + + + polkit + + + postfix + + + sssd-common + + + sudo + + + systemd + + + tftp-server + + + tmux + + + usbguard + + + /proc/net/wireless + + + yum + + + s390utils-base + + + /.dockerenv + + + /run/.containerenv + + + /tmp + + + /var/tmp + + + krb5-server + + + krb5-workstation + + + /etc/fstab + + 1 + + + /proc/sys/kernel/osrelease + ^.*\.(.*)$ + 1 + + + /proc/sys/kernel/osrelease + ^.*\.(.*)$ + 1 + + + /proc/sys/kernel/osrelease + ^.*\.(.*)$ + 1 + + + + + + oval:ssg-sshd_required:var:1 + + + oval:ssg-sshd_required:var:1 + + + oval:ssg-sshd_required:var:1 + + + openssh-server + + + /etc/sssd/sssd.conf + ^[\s]*\[domain\/[^]]*]([^\n\[\]]*\n+)+?[\s]*id_provider[ \t]*=[ \t]*((?i)ad)[ \t]*$ + 1 + + + /sys/firmware/efi + + + + + + + + + + /etc/tmux.conf + + + ^/etc/usbguard/(rules|rules\.d/.*)\.conf$ + ^.*\S+.*$ + 1 + + + oval:ssg-var_accounts_user_umask_umask_as_number:var:1 + + + oval:ssg-var_removable_partition:var:1 + + + oval:ssg-var_umask_for_daemons_umask_as_number:var:1 + + + + + 0 + + + false + false + false + false + false + false + false + false + false + + + false + false + false + false + false + false + false + false + false + + + false + false + false + false + false + false + false + false + false + + + + + + (?i)root + + + + + + ^permit_mynetworks,reject$ + + + ^.*,sec=krb5\:krb5i\:krb5p.*$ + + + 0 + + + 0 + + + + + + maxpoll \d+ + + + + + + 2 + sec=(krb5i|ntlmv2i) + + + symbolic link + + + /etc/ssh + .*_key$ + 0 + 0 + false + false + false + false + false + false + false + false + false + false + + + /etc/ssh + .*_key$ + + 0 + false + false + false + false + false + false + false + false + false + + + + + + + + + + + + 0 + + + + + + + + + 0 + + + + + + 0 + + + + + + 0 + + + 10 + + + 30 + + + 100 + + + + + + + + + ^.*(try_cert_auth|require_cert_auth).*$ + + + ^.*allow_missing_name.*$ + + + ^LinuxAudit$ + + + /etc/systemd/system/default.target + ^(/usr)?/lib/systemd/system/multi-user.target$ + + + /etc/pam.d/fingerprint-auth + /etc/authselect/fingerprint-auth + + + /etc/pam.d/password-auth + /etc/authselect/password-auth + + + /etc/pam.d/postlogin + /etc/authselect/postlogin + + + /etc/pam.d/smartcard-auth + /etc/authselect/smartcard-auth + + + /etc/pam.d/system-auth + /etc/authselect/system-auth + + + + + + + + + + + + faillog_t + + + + + + + + + + + + + + + 0 + + + /var/run/faillock + + + 2 + + + 2 + + + + + + + + + + + + 0 + + + + + + 5000 + + + /etc/systemd/system/ctrl-alt-del.target + /dev/null + + + 0 + + + 900 + + + + + + + + + + + + ^root$ + + + + + + + + + + + + -1 + + + + + + + + + + + + + + + .* + + + + .* + + + + + + + + + + + + + ^[x*]$ + + + ^(!|!!|!\*|\*|!locked)$ + + + SHA-512 + + + .* + + + + + + + + + + + + + + + + + + 0 + + + 0 + + + + + + + + + + + + + + + + + + + + + directory + false + false + false + false + false + false + false + false + false + + + directory + false + false + false + false + false + false + false + false + false + + + + + + 1000 + + + regular + true + + + 1000 + + + ^\/[^\/\n]*\/[^\/\n]{1,}.*$ + + + 1000 + + + + + + 1000 + + + ^nobody$ + + + + + + 1000 + + + ^nobody$ + + + directory + false + false + false + false + false + false + false + + + 1000 + + + directory + false + false + false + false + false + false + false + + + true + true + + + symbolic link + + + ^[:\.] + + + :: + + + \.\. + + + [:\.]$ + + + ^[^/] + + + [^\\]:[^/] + + + + + + + + + + + + + + + 1000 + + + 0 + + + 0 + + + 0 + + + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + + + 0 + + + 0 + 0 + + + 0 + 0 + + + 0 + + + true + true + true + true + true + true + true + true + true + true + + + true + true + + + ^\/(dev|proc|sys)\/.*$ + + + + + + + + + + + + + + + + + + + + + + + + + + + SYSLOG + + + SINGLE + + + HALT + + + + + + SYSLOG + + + SINGLE + + + HALT + + + + + + + + + + + + + + + + + + rotate + + + single + + + + + + + + + + + + + + + ^(?i)hostname(?-i)$ + + + ^(?i)(syslog|single|halt)(?-i)$ + + + + + + + + + + + + + + + ^(?:.*\s)?random\.trust_cpu=on(?:\s.*)?$ + + + ^(?:.*\s)?random\.trust_cpu=off(?:\s.*)?$ + + + + + + + + + + + + + + + \bsystemd.debug-shell\b + + + \bsystemd.debug-shell\b + + + (?:file="[^\s;]+"|\$IncludeConfig[\s]+[^\s;]+|\/dev\/.*) + + + regular + 0 + + + (?:file="[^\s;]+"|\$IncludeConfig[\s]+[^\s;]+|\/dev\/.*) + + + regular + 0 + + + ^.*\/\..*$ + + + (?:file="[^\s;]+"|\$IncludeConfig[\s]+[^\s;]+|\/dev\/.*) + + + regular + false + false + false + false + false + false + false + + + (?=[\S\s]*\s(?i)protocol(?-i)="tcp")(?=[\S\s]*\s(?i)Target(?-i)="[^"]+?")(?=[\S\s]*\s(?i)port(?-i)="6514")(?=[\S\s]*\s(?i)StreamDriver(?-i)="gtls")(?=[\S\s]*\s(?i)StreamDriverMode(?-i)="1")(?=[\S\s]*\s(?i)StreamDriverAuthMode(?-i)="x509/name")(?=[\S\s]*\s(?i)StreamDriver\.CheckExtendedKeyPurpose(?-i)="on") + + + 0 + + + ResultActive=auth_admin + + + PROMISC + + + UP + + + 0 + true + + + false + true + + + 0 + + + false + false + false + false + false + false + false + false + false + false + + + true + + + + + + true + + + + + + regular + true + + + ^/selinux/(?:(?:member)|(?:user)|(?:relabel)|(?:create)|(?:access)|(?:context))$ + + + ^/proc/.*$ + + + ^/sys/.*$ + + + + + + + + + 1000 + + + 0 + + + true + true + + + symbolic link + + + ^/dev/.*$ + nodev + + + + + + + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + + + + ^(?i)0(?-i)$ + + + ^(?i)none(?-i)$ + + + 0 + + + 0 + + + 1 + + + ^(block|character) special$ + + + device_t + + + unlabeled_t + + + unconfined_service_t + + + + + + + + + \blm\b + + + ^(x86_64|aarch64|ppc64le|s390x)$ + + + + + + + + + ^false$ + + + + + + + + + + + + + + + + + + + + + /etc/crypto-policies/back-ends/krb5.config + + + 1.2 + + + + + + + + + 0:20210617-1 + + + ^final all$ + + + ^512M 1h$ + + + ^no$ + + + ^aes256-ctr,aes256-cbc,aes128-ctr,aes128-cbc$ + + + ^ssh-rsa,ecdsa-sha2-nistp384,ecdsa-sha2-nistp256$ + + + ^hmac-sha2-512,hmac-sha2-256$ + + + ^ecdh-sha2-nistp521,ecdh-sha2-nistp384,ecdh-sha2-nistp256,diffie-hellman-group14-sha1$ + + + + + + + + + + + + + + + fips + + + ^FIPS(:OSPP)?$ + + + 1 + + + p\+i\+n\+u\+g\+s\+b\+acl(|\+selinux)\+xattrs\+sha512 + + + ^.*sha512.*$ + + + ^.*acl.*$ + + + ^.*xattrs.*$ + + + fail + false + false + + + fail + + + fail + + + fail + + + 0 + + + /etc/sudoers.d + + + ^yes$ + + + ^security$ + + + 4ae0493b + fd431d51 + + + 6229229e + 5a6340b3 + + + 5ccc5b19 + 8483c65d + + + + + + + + + + + + + + + + + + 0 + + + + + + + + + + + + + + + + + + ## Unsuccessful file access (any other opens) This has to go last. +-a always,exit -F arch=b32 -S open,openat,openat2,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-access +-a always,exit -F arch=b64 -S open,openat,openat2,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-access +-a always,exit -F arch=b32 -S open,openat,openat2,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-access +-a always,exit -F arch=b64 -S open,openat,openat2,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-access + + + + ## Unsuccessful file access (any other opens) This has to go last. +-a always,exit -F arch=b32 -S open,openat,openat2,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-access +-a always,exit -F arch=b64 -S openat,openat2,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-access +-a always,exit -F arch=b32 -S open,openat,openat2,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-access +-a always,exit -F arch=b64 -S openat,openat2,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-access + + + + ## Unsuccessful file access (any other opens) This has to go last. +-a always,exit -F arch=b64 -S open,openat,openat2,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-access +-a always,exit -F arch=b64 -S open,openat,openat2,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-access + + + + ## Successful file access (any other opens) This has to go last. +## These next two are likely to result in a whole lot of events +-a always,exit -F arch=b32 -S open,openat,openat2,open_by_handle_at -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-access +-a always,exit -F arch=b64 -S open,openat,openat2,open_by_handle_at -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-access + + + + ## Successful file access (any other opens) This has to go last. +## These next two are likely to result in a whole lot of events +-a always,exit -F arch=b32 -S open,openat,openat2,open_by_handle_at -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-access +-a always,exit -F arch=b64 -S openat,openat2,open_by_handle_at -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-access + + + + ## Successful file access (any other opens) This has to go last. +## These next two are likely to result in a whole lot of events +-a always,exit -F arch=b64 -S open,openat,openat2,open_by_handle_at -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-access + + + + ## First rule - delete all +-D + +## Increase the buffers to survive stress events. +## Make this bigger for busy systems +-b 8192 + +## This determine how long to wait in burst of events +--backlog_wait_time 60000 + +## Set failure mode to syslog +-f 1 + + + + + ## Unsuccessful file creation (open with O_CREAT) +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b32 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b64 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b32 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b64 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create + + + + ## Unsuccessful file creation (open with O_CREAT) +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b32 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b32 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create + + + + ## Unsuccessful file creation (open with O_CREAT) +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b64 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b64 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create + + + + ## Successful file creation (open with O_CREAT) +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create +-a always,exit -F arch=b32 -S open -F a1&0100 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create +-a always,exit -F arch=b64 -S open -F a1&0100 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create +-a always,exit -F arch=b32 -S creat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create +-a always,exit -F arch=b64 -S creat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create + + + + ## Successful file creation (open with O_CREAT) +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create +-a always,exit -F arch=b32 -S open -F a1&0100 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create +-a always,exit -F arch=b32 -S creat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create + + + + ## Successful file creation (open with O_CREAT) +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create +-a always,exit -F arch=b64 -S open -F a1&0100 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create +-a always,exit -F arch=b64 -S creat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create + + + + ## Unsuccessful file delete +-a always,exit -F arch=b32 -S unlink,unlinkat,rename,renameat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete +-a always,exit -F arch=b64 -S unlink,unlinkat,rename,renameat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete +-a always,exit -F arch=b32 -S unlink,unlinkat,rename,renameat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete +-a always,exit -F arch=b64 -S unlink,unlinkat,rename,renameat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete + + + + ## Unsuccessful file delete +-a always,exit -F arch=b32 -S unlink,unlinkat,rename,renameat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete +-a always,exit -F arch=b64 -S unlinkat,renameat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete +-a always,exit -F arch=b32 -S unlink,unlinkat,rename,renameat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete +-a always,exit -F arch=b64 -S unlinkat,renameat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete + + + + ## Unsuccessful file delete +-a always,exit -F arch=b64 -S unlink,unlinkat,rename,renameat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete +-a always,exit -F arch=b64 -S unlink,unlinkat,rename,renameat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete + + + + ## Successful file delete +-a always,exit -F arch=b32 -S unlink,unlinkat,rename,renameat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-delete +-a always,exit -F arch=b64 -S unlink,unlinkat,rename,renameat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-delete + + + + ## Successful file delete +-a always,exit -F arch=b32 -S unlink,unlinkat,rename,renameat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-delete +-a always,exit -F arch=b64 -S unlinkat,renameat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-delete + + + + ## Successful file delete +-a always,exit -F arch=b64 -S unlink,unlinkat,rename,renameat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-delete + + + + ## Make the loginuid immutable. This prevents tampering with the auid. +--loginuid-immutable + + + + + ## Unsuccessful file modifications (open for write or truncate) +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b32 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b32 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification + + + + ## Unsuccessful file modifications (open for write or truncate) +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b32 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b32 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification + + + + ## Unsuccessful file modifications (open for write or truncate) +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification + + + + ## Successful file modifications (open for write or truncate) +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification +-a always,exit -F arch=b32 -S open -F a1&01003 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification +-a always,exit -F arch=b64 -S open -F a1&01003 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification +-a always,exit -F arch=b32 -S truncate,ftruncate -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification +-a always,exit -F arch=b64 -S truncate,ftruncate -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification + + + + ## Successful file modifications (open for write or truncate) +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification +-a always,exit -F arch=b32 -S open -F a1&01003 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification +-a always,exit -F arch=b32 -S truncate,ftruncate -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification +-a always,exit -F arch=b64 -S truncate,ftruncate -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification + + + + ## Successful file modifications (open for write or truncate) +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification +-a always,exit -F arch=b64 -S open -F a1&01003 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification +-a always,exit -F arch=b64 -S truncate,ftruncate -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification + + + + ## These rules watch for kernel module insertion. By monitoring +## the syscall, we do not need any watches on programs. +-a always,exit -F arch=b32 -S init_module,finit_module -F key=module-load +-a always,exit -F arch=b64 -S init_module,finit_module -F key=module-load +-a always,exit -F arch=b32 -S delete_module -F key=module-unload +-a always,exit -F arch=b64 -S delete_module -F key=module-unload + + + + ## These rules watch for kernel module insertion. By monitoring +## the syscall, we do not need any watches on programs. +-a always,exit -F arch=b64 -S init_module,finit_module -F key=module-load +-a always,exit -F arch=b64 -S delete_module -F key=module-unload + + + + ## The purpose of these rules is to meet the requirements for Operating +## System Protection Profile (OSPP)v4.2. These rules depends on having +## the following rule files copied to /etc/audit/rules.d: +## +## 10-base-config.rules, 11-loginuid.rules, +## 30-ospp-v42-1-create-failed.rules, 30-ospp-v42-1-create-success.rules, +## 30-ospp-v42-2-modify-failed.rules, 30-ospp-v42-2-modify-success.rules, +## 30-ospp-v42-3-access-failed.rules, 30-ospp-v42-3-access-success.rules, +## 30-ospp-v42-4-delete-failed.rules, 30-ospp-v42-4-delete-success.rules, +## 30-ospp-v42-5-perm-change-failed.rules, +## 30-ospp-v42-5-perm-change-success.rules, +## 30-ospp-v42-6-owner-change-failed.rules, +## 30-ospp-v42-6-owner-change-success.rules +## +## original copies may be found in /usr/share/audit/sample-rules/ + + +## User add delete modify. This is covered by pam. However, someone could +## open a file and directly create or modify a user, so we'll watch passwd and +## shadow for writes +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b32 -S open -F a1&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b64 -S open -F a1&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b32 -S open -F a1&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b64 -S open -F a1&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify + +## User enable and disable. This is entirely handled by pam. + +## Group add delete modify. This is covered by pam. However, someone could +## open a file and directly create or modify a user, so we'll watch group and +## gshadow for writes +-a always,exit -F path=/etc/passwd -F perm=wa -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F path=/etc/shadow -F perm=wa -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F path=/etc/group -F perm=wa -F auid>=1000 -F auid!=unset -F key=group-modify +-a always,exit -F path=/etc/gshadow -F perm=wa -F auid>=1000 -F auid!=unset -F key=group-modify + + +## Use of special rights for config changes. This would be use of setuid +## programs that relate to user accts. This is not all setuid apps because +## requirements are only for ones that affect system configuration. +-a always,exit -F path=/usr/sbin/unix_chkpwd -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/sbin/usernetctl -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/sbin/userhelper -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/sbin/seunshare -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/mount -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/newgrp -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/newuidmap -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/gpasswd -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/newgidmap -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/umount -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/passwd -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/crontab -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/at -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes + +## Privilege escalation via su or sudo. This is entirely handled by pam. + +## Watch for configuration changes to privilege escalation. +-a always,exit -F path=/etc/sudoers -F perm=wa -F key=special-config-changes +-a always,exit -F dir=/etc/sudoers.d/ -F perm=wa -F key=special-config-changes + +## Audit log access +-a always,exit -F dir=/var/log/audit/ -F perm=r -F auid>=1000 -F auid!=unset -F key=access-audit-trail +## Attempts to Alter Process and Session Initiation Information +-a always,exit -F path=/var/run/utmp -F perm=wa -F auid>=1000 -F auid!=unset -F key=session +-a always,exit -F path=/var/log/btmp -F perm=wa -F auid>=1000 -F auid!=unset -F key=session +-a always,exit -F path=/var/log/wtmp -F perm=wa -F auid>=1000 -F auid!=unset -F key=session + +## Attempts to modify MAC controls +-a always,exit -F dir=/etc/selinux/ -F perm=wa -F auid>=1000 -F auid!=unset -F key=MAC-policy + +## Software updates. This is entirely handled by rpm. + +## System start and shutdown. This is entirely handled by systemd + +## Kernel Module loading. This is handled in 43-module-load.rules + +## Application invocation. The requirements list an user requirement +## FPT_SRP_EXT.1 Software Restriction Policies. This event is intended to +## state results from that policy. This would be handled entirely by +## that daemon. + + + + + ## The purpose of these rules is to meet the requirements for Operating +## System Protection Profile (OSPP)v4.2. These rules depends on having +## the following rule files copied to /etc/audit/rules.d: +## +## 10-base-config.rules, 11-loginuid.rules, +## 30-ospp-v42-1-create-failed.rules, 30-ospp-v42-1-create-success.rules, +## 30-ospp-v42-2-modify-failed.rules, 30-ospp-v42-2-modify-success.rules, +## 30-ospp-v42-3-access-failed.rules, 30-ospp-v42-3-access-success.rules, +## 30-ospp-v42-4-delete-failed.rules, 30-ospp-v42-4-delete-success.rules, +## 30-ospp-v42-5-perm-change-failed.rules, +## 30-ospp-v42-5-perm-change-success.rules, +## 30-ospp-v42-6-owner-change-failed.rules, +## 30-ospp-v42-6-owner-change-success.rules +## +## original copies may be found in /usr/share/audit/sample-rules/ + + +## User add delete modify. This is covered by pam. However, someone could +## open a file and directly create or modify a user, so we'll watch passwd and +## shadow for writes +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b32 -S open -F a1&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b32 -S open -F a1&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify + +## User enable and disable. This is entirely handled by pam. + +## Group add delete modify. This is covered by pam. However, someone could +## open a file and directly create or modify a user, so we'll watch group and +## gshadow for writes +-a always,exit -F path=/etc/passwd -F perm=wa -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F path=/etc/shadow -F perm=wa -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F path=/etc/group -F perm=wa -F auid>=1000 -F auid!=unset -F key=group-modify +-a always,exit -F path=/etc/gshadow -F perm=wa -F auid>=1000 -F auid!=unset -F key=group-modify + + +## Use of special rights for config changes. This would be use of setuid +## programs that relate to user accts. This is not all setuid apps because +## requirements are only for ones that affect system configuration. +-a always,exit -F path=/usr/sbin/unix_chkpwd -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/sbin/usernetctl -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/sbin/userhelper -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/sbin/seunshare -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/mount -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/newgrp -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/newuidmap -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/gpasswd -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/newgidmap -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/umount -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/passwd -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/crontab -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/at -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes + +## Privilege escalation via su or sudo. This is entirely handled by pam. + +## Watch for configuration changes to privilege escalation. +-a always,exit -F path=/etc/sudoers -F perm=wa -F key=special-config-changes +-a always,exit -F dir=/etc/sudoers.d/ -F perm=wa -F key=special-config-changes + +## Audit log access +-a always,exit -F dir=/var/log/audit/ -F perm=r -F auid>=1000 -F auid!=unset -F key=access-audit-trail +## Attempts to Alter Process and Session Initiation Information +-a always,exit -F path=/var/run/utmp -F perm=wa -F auid>=1000 -F auid!=unset -F key=session +-a always,exit -F path=/var/log/btmp -F perm=wa -F auid>=1000 -F auid!=unset -F key=session +-a always,exit -F path=/var/log/wtmp -F perm=wa -F auid>=1000 -F auid!=unset -F key=session + +## Attempts to modify MAC controls +-a always,exit -F dir=/etc/selinux/ -F perm=wa -F auid>=1000 -F auid!=unset -F key=MAC-policy + +## Software updates. This is entirely handled by rpm. + +## System start and shutdown. This is entirely handled by systemd + +## Kernel Module loading. This is handled in 43-module-load.rules + +## Application invocation. The requirements list an user requirement +## FPT_SRP_EXT.1 Software Restriction Policies. This event is intended to +## state results from that policy. This would be handled entirely by +## that daemon. + + + + + ## The purpose of these rules is to meet the requirements for Operating +## System Protection Profile (OSPP)v4.2. These rules depends on having +## the following rule files copied to /etc/audit/rules.d: +## +## 10-base-config.rules, 11-loginuid.rules, +## 30-ospp-v42-1-create-failed.rules, 30-ospp-v42-1-create-success.rules, +## 30-ospp-v42-2-modify-failed.rules, 30-ospp-v42-2-modify-success.rules, +## 30-ospp-v42-3-access-failed.rules, 30-ospp-v42-3-access-success.rules, +## 30-ospp-v42-4-delete-failed.rules, 30-ospp-v42-4-delete-success.rules, +## 30-ospp-v42-5-perm-change-failed.rules, +## 30-ospp-v42-5-perm-change-success.rules, +## 30-ospp-v42-6-owner-change-failed.rules, +## 30-ospp-v42-6-owner-change-success.rules +## +## original copies may be found in /usr/share/audit/sample-rules/ + + +## User add delete modify. This is covered by pam. However, someone could +## open a file and directly create or modify a user, so we'll watch passwd and +## shadow for writes +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b64 -S open -F a1&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b64 -S open -F a1&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify + +## User enable and disable. This is entirely handled by pam. + +## Group add delete modify. This is covered by pam. However, someone could +## open a file and directly create or modify a user, so we'll watch group and +## gshadow for writes +-a always,exit -F path=/etc/passwd -F perm=wa -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F path=/etc/shadow -F perm=wa -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F path=/etc/group -F perm=wa -F auid>=1000 -F auid!=unset -F key=group-modify +-a always,exit -F path=/etc/gshadow -F perm=wa -F auid>=1000 -F auid!=unset -F key=group-modify + + +## Use of special rights for config changes. This would be use of setuid +## programs that relate to user accts. This is not all setuid apps because +## requirements are only for ones that affect system configuration. +-a always,exit -F path=/usr/sbin/unix_chkpwd -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/sbin/usernetctl -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/sbin/userhelper -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/sbin/seunshare -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/mount -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/newgrp -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/newuidmap -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/gpasswd -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/newgidmap -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/umount -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/passwd -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/crontab -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/at -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes + +## Privilege escalation via su or sudo. This is entirely handled by pam. + +## Watch for configuration changes to privilege escalation. +-a always,exit -F path=/etc/sudoers -F perm=wa -F key=special-config-changes +-a always,exit -F dir=/etc/sudoers.d/ -F perm=wa -F key=special-config-changes + +## Audit log access +-a always,exit -F dir=/var/log/audit/ -F perm=r -F auid>=1000 -F auid!=unset -F key=access-audit-trail +## Attempts to Alter Process and Session Initiation Information +-a always,exit -F path=/var/run/utmp -F perm=wa -F auid>=1000 -F auid!=unset -F key=session +-a always,exit -F path=/var/log/btmp -F perm=wa -F auid>=1000 -F auid!=unset -F key=session +-a always,exit -F path=/var/log/wtmp -F perm=wa -F auid>=1000 -F auid!=unset -F key=session + +## Attempts to modify MAC controls +-a always,exit -F dir=/etc/selinux/ -F perm=wa -F auid>=1000 -F auid!=unset -F key=MAC-policy + +## Software updates. This is entirely handled by rpm. + +## System start and shutdown. This is entirely handled by systemd + +## Kernel Module loading. This is handled in 43-module-load.rules + +## Application invocation. The requirements list an user requirement +## FPT_SRP_EXT.1 Software Restriction Policies. This event is intended to +## state results from that policy. This would be handled entirely by +## that daemon. + + + + + ## Unsuccessful ownership change +-a always,exit -F arch=b32 -S lchown,fchown,chown,fchownat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-owner-change +-a always,exit -F arch=b64 -S lchown,fchown,chown,fchownat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-owner-change +-a always,exit -F arch=b32 -S lchown,fchown,chown,fchownat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-owner-change +-a always,exit -F arch=b64 -S lchown,fchown,chown,fchownat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-owner-change + + + + ## Unsuccessful ownership change +-a always,exit -F arch=b32 -S lchown,fchown,chown,fchownat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-owner-change +-a always,exit -F arch=b64 -S fchown,fchownat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-owner-change +-a always,exit -F arch=b32 -S lchown,fchown,chown,fchownat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-owner-change +-a always,exit -F arch=b64 -S fchown,fchownat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-owner-change + + + + ## Unsuccessful ownership change +-a always,exit -F arch=b64 -S lchown,fchown,chown,fchownat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-owner-change +-a always,exit -F arch=b64 -S lchown,fchown,chown,fchownat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-owner-change + + + + ## Successful ownership change +-a always,exit -F arch=b32 -S lchown,fchown,chown,fchownat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-owner-change +-a always,exit -F arch=b64 -S lchown,fchown,chown,fchownat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-owner-change + + + + ## Successful ownership change +-a always,exit -F arch=b32 -S lchown,fchown,chown,fchownat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-owner-change +-a always,exit -F arch=b64 -S fchown,fchownat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-owner-change + + + + ## Successful ownership change +-a always,exit -F arch=b64 -S lchown,fchown,chown,fchownat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-owner-change + + + + ## Unsuccessful permission change +-a always,exit -F arch=b32 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-perm-change +-a always,exit -F arch=b64 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-perm-change +-a always,exit -F arch=b32 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-perm-change +-a always,exit -F arch=b64 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-perm-change + + + + ## Unsuccessful permission change +-a always,exit -F arch=b32 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-perm-change +-a always,exit -F arch=b64 -S fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-perm-change +-a always,exit -F arch=b32 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-perm-change +-a always,exit -F arch=b64 -S fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-perm-change + + + + ## Unsuccessful permission change +-a always,exit -F arch=b64 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-perm-change +-a always,exit -F arch=b64 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-perm-change + + + + ## Successful permission change +-a always,exit -F arch=b32 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change +-a always,exit -F arch=b64 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + + + + ## Successful permission change +-a always,exit -F arch=b32 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change +-a always,exit -F arch=b64 -S fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + + + + ## Successful permission change +-a always,exit -F arch=b64 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + + + + ^(?i)50(?-i)$ + + + ^(?i)yes(?-i)$ + + + ^(?i)ENRICHED(?-i)$ + + + ^(?i)yes(?-i)$ + + + ^(?:.*\s)?selinux=0(?:\s.*)?$ + + + ^(?:.*\s)?selinux=0(?:\s.*)?$ + + + ^(?:.*\s)?selinux=0(?:\s.*)?$ + + + ^true$ + + + ^'lock-screen'$ + + + 0 + + + 0 + + + 0 + + + 0 + + + symbolic link + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + symbolic link + + + 0 + + + 0 + + + 0 + + + 0 + + + symbolic link + + + false + false + false + false + false + + + false + false + false + false + false + + + false + false + false + false + false + + + false + false + false + false + false + + + false + false + false + false + false + + + false + false + false + false + false + + + symbolic link + + + false + false + + + false + false + + + false + false + + + false + false + + + symbolic link + + + ^no$ + + + ^no$ + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + symbolic link + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + symbolic link + + + false + false + false + false + false + + + false + false + false + false + false + + + false + false + false + false + false + + + false + false + false + false + false + + + false + false + false + false + false + + + false + false + false + false + false + + + false + false + false + false + false + + + symbolic link + + + 0 + + + symbolic link + + + 0 + + + symbolic link + + + 0 + + + symbolic link + + + 0 + + + symbolic link + + + 0 + + + symbolic link + + + 0 + + + symbolic link + + + 0 + + + symbolic link + + + 0 + + + symbolic link + + + 0 + + + symbolic link + + + 0 + + + symbolic link + + + 0 + + + symbolic link + + + 0 + + + symbolic link + + + 0 + + + symbolic link + + + 0 + + + symbolic link + + + 0 + + + symbolic link + + + 0 + + + symbolic link + + + 0 + + + symbolic link + + + 0 + + + symbolic link + + + 0 + + + symbolic link + + + 0 + + + symbolic link + + + 4 + + + symbolic link + + + 0 + + + 0 + + + symbolic link + + + 0 + + + symbolic link + + + 0 + + + symbolic link + + + 0 + + + symbolic link + + + 0 + + + symbolic link + + + 0 + + + symbolic link + + + 0 + + + symbolic link + + + 0 + + + symbolic link + + + 0 + + + symbolic link + + + 0 + + + symbolic link + + + 0 + + + symbolic link + + + 0 + + + symbolic link + + + 0 + + + symbolic link + + + 0 + + + symbolic link + + + 0 + + + symbolic link + + + 0 + + + symbolic link + + + 0 + + + symbolic link + + + 0 + + + symbolic link + + + 0 + + + symbolic link + + + 0 + + + symbolic link + + + 104 + + + symbolic link + + + 0 + + + 0 + + + symbolic link + + + 0 + + + 0 + + + 0 + + + 0 + + + symbolic link + + + false + false + false + false + false + false + false + false + false + false + + + symbolic link + + + false + false + false + false + false + false + false + false + + + symbolic link + + + false + false + false + false + false + false + false + false + false + false + false + false + + + symbolic link + + + false + false + false + false + false + false + false + false + + + symbolic link + + + false + false + false + false + false + false + false + false + false + false + false + false + + + symbolic link + + + false + false + false + false + false + false + false + false + false + false + + + symbolic link + + + false + false + false + false + false + false + false + false + false + + + symbolic link + + + false + false + false + false + false + false + false + false + false + + + symbolic link + + + false + false + false + false + false + false + false + false + false + + + symbolic link + + + false + false + false + false + false + false + false + false + false + + + symbolic link + + + false + false + false + false + false + false + false + false + false + + + symbolic link + + + false + false + false + false + false + false + false + false + false + false + + + symbolic link + + + false + false + false + false + false + false + false + false + false + + + symbolic link + + + false + false + false + false + false + false + false + false + false + + + symbolic link + + + false + false + false + false + false + false + false + false + false + + + symbolic link + + + false + false + false + false + false + false + false + false + + + symbolic link + + + false + false + false + false + false + false + false + false + false + false + false + false + + + symbolic link + + + false + false + false + false + false + false + false + false + + + symbolic link + + + false + false + false + false + false + false + false + false + + + symbolic link + + + false + false + false + false + false + false + false + false + + + symbolic link + + + false + false + false + false + false + false + false + false + false + false + false + false + + + symbolic link + + + false + false + false + false + false + false + false + false + false + false + + + symbolic link + + + false + false + + + false + false + + + false + false + + + false + false + + + symbolic link + + + false + false + false + false + false + false + false + false + false + false + + + symbolic link + + + false + false + false + false + false + false + false + false + + + symbolic link + + + false + false + false + false + false + + + symbolic link + + + false + false + false + false + false + false + false + false + false + + + symbolic link + + + false + false + false + false + false + false + false + false + false + + + symbolic link + + + ^((?:"|')?)nftables\1$ + + + .*rescue\.conf$ + + + ^(?:.*\s)?audit=1(?:\s.*)?$ + + + .*rescue\.conf$ + + + ^(?:.*\s)?audit_backlog_limit=8192(?:\s.*)?$ + + + .*rescue\.conf$ + + + ^(?:.*\s)?iommu=force(?:\s.*)?$ + + + .*rescue\.conf$ + + + ^(?:.*\s)?init_on_alloc=1(?:\s.*)?$ + + + .*rescue\.conf$ + + + ^(?:.*\s)?ipv6\.disable=1(?:\s.*)?$ + + + .*rescue\.conf$ + + + + + + .*rescue\.conf$ + + + ^(?:.*\s)?mce=0(?:\s.*)?$ + + + .*rescue\.conf$ + + + + + + .*rescue\.conf$ + + + .*rescue\.conf$ + + + .*rescue\.conf$ + + + ^(?:.*\s)?page_alloc\.shuffle=1(?:\s.*)?$ + + + .*rescue\.conf$ + + + ^(?:.*\s)?page_poison=1(?:\s.*)?$ + + + .*rescue\.conf$ + + + ^(?:.*\s)?pti=on(?:\s.*)?$ + + + .*rescue\.conf$ + + + + + + .*rescue\.conf$ + + + ^(?:.*\s)?slab_nomerge=yes(?:\s.*)?$ + + + .*rescue\.conf$ + + + + + + .*rescue\.conf$ + + + + + + .*rescue\.conf$ + + + ^(?:.*\s)?spectre_v2=on(?:\s.*)?$ + + + .*rescue\.conf$ + + + .*rescue\.conf$ + + + ^(?:.*\s)?vsyscall=none(?:\s.*)?$ + + + ^((?:"|')?)yes\1$ + + + ^((?:"|')?)yes\1$ + + + ^((?:"|')?)persistent\1$ + + + n + + + + + + y + + + + + + n + + + + + + y + + + + + + y + + + + + + n + + + + + + n + + + + + + y + + + + + + n + + + + + + y + + + + + + y + + + + + + y + + + + + + y + + + + + + 65536 + + + + + + n + + + + + + y + + + + + + y + + + + + + y + + + + + + y + + + + + + y + + + + + + y + + + + + + y + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + y + + + + + + n + + + + + + n + + + + + + y + + + + + + y + + + + + + y + + + + + + + + + + + + + + + + + + y + + + + + + y + + + + + + y + + + + + + y + + + + + + y + + + + + + y + + + + + + + + + + + + n + + + + + + y + + + + + + y + + + + + + y + + + + + + y + + + + + + y + + + + + + y + + + + + + y + + + + + + y + + + + + + n + + + + + + y + + + + + + y + + + + + + y + + + + + + y + + + + + + n + + + + + + y + + + + + + y + + + + + + y + + + + + + y + + + + + + y + + + + + + y + + + + + + y + + + + + + y + + + + + + n + + + + + + nosuid + + + noauto + + + nodev + + + noexec + + + nosuid + + + nodev + + + noexec + + + nosuid + + + nodev + + + noexec + + + nosuid + + + ^.*sec=krb5:krb5i:krb5p.*$ + + + ^.*nodev.*$ + + + ^.*,?nodev,?.*$ + + + ^.*,?nodev,?.* + + + ^.*noexec.*$ + + + ^.*,?noexec,?.*$ + + + ^.*,?noexec,?.* + + + ^.*nosuid.*$ + + + ^.*,?nosuid,?.*$ + + + ^.*,?nosuid,?.* + + + nosuid + + + + + + nosuid + + + nodev + + + noexec + + + nosuid + + + nodev + + + noexec + + + nosuid + + + nodev + + + noexec + + + nosuid + + + nodev + + + noexec + + + nosuid + + + nodev + + + noexec + + + nosuid + + + 0 + + + 0 + + + 0 + + + 0 + + + symbolic link + + + antivirus_can_scan_system + + + + + antivirus_use_jit + + + + + auditadm_exec_content + + + + + authlogin_nsswitch_use_ldap + + + + + authlogin_radius + + + + + authlogin_yubikey + + + + + awstats_purge_apache_log_files + + + + + boinc_execmem + + + + + cdrecord_read_content + + + + + cluster_can_network_connect + + + + + cluster_manage_all_files + + + + + cluster_use_execmem + + + + + cobbler_anon_write + + + + + cobbler_can_network_connect + + + + + cobbler_use_cifs + + + + + cobbler_use_nfs + + + + + collectd_tcp_network_connect + + + + + condor_tcp_network_connect + + + + + conman_can_network + + + + + container_connect_any + + + + + cron_can_relabel + + + + + cron_system_cronjob_use_shares + + + + + cron_userdomain_transition + + + + + cups_execmem + + + + + cvs_read_shadow + + + + + daemons_dump_core + + + + + daemons_enable_cluster_mode + + + + + daemons_use_tcp_wrapper + + + + + daemons_use_tty + + + + + dbadm_exec_content + + + + + dbadm_manage_user_files + + + + + dbadm_read_user_files + + + + + deny_execmem + + + + + deny_ptrace + + + + + dhcpc_exec_iptables + + + + + dhcpd_use_ldap + + + + + domain_fd_use + + + + + domain_kernel_load_modules + + + + + entropyd_use_audio + + + + + exim_can_connect_db + + + + + exim_manage_user_files + + + + + exim_read_user_files + + + + + fcron_crond + + + + + fenced_can_network_connect + + + + + fenced_can_ssh + + + + + fips_mode + + + + + ftpd_anon_write + + + + + ftpd_connect_all_unreserved + + + + + ftpd_connect_db + + + + + ftpd_full_access + + + + + ftpd_use_cifs + + + + + ftpd_use_fusefs + + + + + ftpd_use_nfs + + + + + ftpd_use_passive_mode + + + + + git_cgi_enable_homedirs + + + + + git_cgi_use_cifs + + + + + git_cgi_use_nfs + + + + + git_session_bind_all_unreserved_ports + + + + + git_session_users + + + + + git_system_enable_homedirs + + + + + git_system_use_cifs + + + + + git_system_use_nfs + + + + + gitosis_can_sendmail + + + + + glance_api_can_network + + + + + glance_use_execmem + + + + + glance_use_fusefs + + + + + global_ssp + + + + + gluster_anon_write + + + + + gluster_export_all_ro + + + + + gluster_export_all_rw + + + + + gpg_web_anon_write + + + + + gssd_read_tmp + + + + + guest_exec_content + + + + + haproxy_connect_any + + + + + httpd_anon_write + + + + + httpd_builtin_scripting + + + + + httpd_can_check_spam + + + + + httpd_can_connect_ftp + + + + + httpd_can_connect_ldap + + + + + httpd_can_connect_mythtv + + + + + httpd_can_connect_zabbix + + + + + httpd_can_network_connect + + + + + httpd_can_network_connect_cobbler + + + + + httpd_can_network_connect_db + + + + + httpd_can_network_memcache + + + + + httpd_can_network_relay + + + + + httpd_can_sendmail + + + + + httpd_dbus_avahi + + + + + httpd_dbus_sssd + + + + + httpd_dontaudit_search_dirs + + + + + httpd_enable_cgi + + + + + httpd_enable_ftp_server + + + + + httpd_enable_homedirs + + + + + httpd_execmem + + + + + httpd_graceful_shutdown + + + + + httpd_manage_ipa + + + + + httpd_mod_auth_ntlm_winbind + + + + + httpd_mod_auth_pam + + + + + httpd_read_user_content + + + + + httpd_run_ipa + + + + + httpd_run_preupgrade + + + + + httpd_run_stickshift + + + + + httpd_serve_cobbler_files + + + + + httpd_setrlimit + + + + + httpd_ssi_exec + + + + + httpd_sys_script_anon_write + + + + + httpd_tmp_exec + + + + + httpd_tty_comm + + + + + httpd_unified + + + + + httpd_use_cifs + + + + + httpd_use_fusefs + + + + + httpd_use_gpg + + + + + httpd_use_nfs + + + + + httpd_use_openstack + + + + + httpd_use_sasl + + + + + httpd_verify_dns + + + + + icecast_use_any_tcp_ports + + + + + irc_use_any_tcp_ports + + + + + irssi_use_full_network + + + + + kdumpgui_run_bootloader + + + + + kerberos_enabled + + + + + ksmtuned_use_cifs + + + + + ksmtuned_use_nfs + + + + + logadm_exec_content + + + + + logging_syslogd_can_sendmail + + + + + logging_syslogd_run_nagios_plugins + + + + + logging_syslogd_use_tty + + + + + login_console_enabled + + + + + logrotate_use_nfs + + + + + logwatch_can_network_connect_mail + + + + + lsmd_plugin_connect_any + + + + + mailman_use_fusefs + + + + + mcelog_client + + + + + mcelog_exec_scripts + + + + + mcelog_foreground + + + + + mcelog_server + + + + + minidlna_read_generic_user_content + + + + + mmap_low_allowed + + + + + mock_enable_homedirs + + + + + mount_anyfile + + + + + mozilla_plugin_bind_unreserved_ports + + + + + mozilla_plugin_can_network_connect + + + + + mozilla_plugin_use_bluejeans + + + + + mozilla_plugin_use_gps + + + + + mozilla_plugin_use_spice + + + + + mozilla_read_content + + + + + mpd_enable_homedirs + + + + + mpd_use_cifs + + + + + mpd_use_nfs + + + + + mplayer_execstack + + + + + mysql_connect_any + + + + + nagios_run_pnp4nagios + + + + + nagios_run_sudo + + + + + named_tcp_bind_http_port + + + + + named_write_master_zones + + + + + neutron_can_network + + + + + nfs_export_all_ro + + + + + nfs_export_all_rw + + + + + nfsd_anon_write + + + + + nis_enabled + + + + + nscd_use_shm + + + + + openshift_use_nfs + + + + + openvpn_can_network_connect + + + + + openvpn_enable_homedirs + + + + + openvpn_run_unconfined + + + + + pcp_bind_all_unreserved_ports + + + + + pcp_read_generic_logs + + + + + piranha_lvs_can_network_connect + + + + + polipo_connect_all_unreserved + + + + + polipo_session_bind_all_unreserved_ports + + + + + polipo_session_users + + + + + polipo_use_cifs + + + + + polipo_use_nfs + + + + + polyinstantiation_enabled + + + + + postfix_local_write_mail_spool + + + + + postgresql_can_rsync + + + + + postgresql_selinux_transmit_client_label + + + + + postgresql_selinux_unconfined_dbadm + + + + + postgresql_selinux_users_ddl + + + + + pppd_can_insmod + + + + + pppd_for_user + + + + + privoxy_connect_any + + + + + prosody_bind_http_port + + + + + puppetagent_manage_all_files + + + + + puppetmaster_use_db + + + + + racoon_read_shadow + + + + + rsync_anon_write + + + + + rsync_client + + + + + rsync_export_all_ro + + + + + rsync_full_access + + + + + samba_create_home_dirs + + + + + samba_domain_controller + + + + + samba_enable_home_dirs + + + + + samba_export_all_ro + + + + + samba_export_all_rw + + + + + samba_load_libgfapi + + + + + samba_portmapper + + + + + samba_run_unconfined + + + + + samba_share_fusefs + + + + + samba_share_nfs + + + + + sanlock_use_fusefs + + + + + sanlock_use_nfs + + + + + sanlock_use_samba + + + + + saslauthd_read_shadow + + + + + secadm_exec_content + + + + + secure_mode + + + + + secure_mode_insmod + + + + + secure_mode_policyload + + + + + selinuxuser_direct_dri_enabled + + + + + selinuxuser_execheap + + + + + selinuxuser_execmod + + + + + selinuxuser_execstack + + + + + selinuxuser_mysql_connect_enabled + + + + + selinuxuser_ping + + + + + selinuxuser_postgresql_connect_enabled + + + + + selinuxuser_rw_noexattrfile + + + + + selinuxuser_share_music + + + + + selinuxuser_tcp_server + + + + + selinuxuser_udp_server + + + + + selinuxuser_use_ssh_chroot + + + + + sge_domain_can_network_connect + + + + + sge_use_nfs + + + + + smartmon_3ware + + + + + smbd_anon_write + + + + + spamassassin_can_network + + + + + spamd_enable_home_dirs + + + + + squid_connect_any + + + + + squid_use_tproxy + + + + + ssh_chroot_rw_homedirs + + + + + ssh_keysign + + + + + ssh_sysadm_login + + + + + staff_exec_content + + + + + staff_use_svirt + + + + + swift_can_network + + + + + sysadm_exec_content + + + + + telepathy_connect_all_ports + + + + + telepathy_tcp_connect_generic_network_ports + + + + + tftp_anon_write + + + + + tftp_home_dir + + + + + tmpreaper_use_nfs + + + + + tmpreaper_use_samba + + + + + tor_bind_all_unreserved_ports + + + + + tor_can_network_relay + + + + + unconfined_chrome_sandbox_transition + + + + + unconfined_login + + + + + unconfined_mozilla_plugin_transition + + + + + unprivuser_use_svirt + + + + + use_ecryptfs_home_dirs + + + + + use_fusefs_home_dirs + + + + + use_lpd_server + + + + + use_nfs_home_dirs + + + + + use_samba_home_dirs + + + + + user_exec_content + + + + + varnishd_connect_any + + + + + virt_read_qemu_ga_data + + + + + virt_rw_qemu_ga_data + + + + + virt_sandbox_use_all_caps + + + + + virt_sandbox_use_audit + + + + + virt_sandbox_use_mknod + + + + + virt_sandbox_use_netlink + + + + + virt_sandbox_use_sys_admin + + + + + virt_transition_userdomain + + + + + virt_use_comm + + + + + virt_use_execmem + + + + + virt_use_fusefs + + + + + virt_use_nfs + + + + + virt_use_rawip + + + + + virt_use_samba + + + + + virt_use_sanlock + + + + + virt_use_usb + + + + + virt_use_xserver + + + + + webadm_manage_user_files + + + + + webadm_read_user_files + + + + + wine_mmap_zero_ignore + + + + + xdm_bind_vnc_tcp_port + + + + + xdm_exec_bootloader + + + + + xdm_sysadm_login + + + + + xdm_write_home + + + + + xen_use_nfs + + + + + xend_run_blktap + + + + + xend_run_qemu + + + + + xguest_connect_network + + + + + xguest_exec_content + + + + + xguest_mount_media + + + + + xguest_use_bluetooth + + + + + xserver_clients_write_xshm + + + + + xserver_execmem + + + + + xserver_object_manager + + + + + zabbix_can_network + + + + + zarafa_setrlimit + + + + + zebra_write_config + + + + + zoneminder_anon_write + + + + + zoneminder_run_sudo + + + + + inactive|failed + + + masked + + + auditd.service + + + auditd.socket + + + active + + + inactive|failed + + + masked + + + inactive|failed + + + masked + + + chronyd.service + + + chronyd.socket + + + active + + + inactive|failed + + + masked + + + cron.service + + + cron.socket + + + active + + + crond.service + + + crond.socket + + + active + + + inactive|failed + + + masked + + + inactive|failed + + + masked + + + inactive|failed + + + masked + + + inactive|failed + + + masked + + + fapolicyd.service + + + fapolicyd.socket + + + active + + + firewalld.service + + + firewalld.socket + + + active + + + inactive|failed + + + masked + + + ip6tables.service + + + ip6tables.socket + + + active + + + iptables.service + + + iptables.socket + + + active + + + inactive|failed + + + masked + + + inactive|failed + + + masked + + + inactive|failed + + + masked + + + inactive|failed + + + masked + + + ntp.service + + + ntp.socket + + + active + + + ntpd.service + + + ntpd.socket + + + active + + + inactive|failed + + + masked + + + inactive|failed + + + masked + + + pcscd.service + + + pcscd.socket + + + active + + + postfix.service + + + postfix.socket + + + active + + + inactive|failed + + + masked + + + inactive|failed + + + masked + + + inactive|failed + + + masked + + + inactive|failed + + + masked + + + rngd.service + + + rngd.socket + + + active + + + inactive|failed + + + masked + + + inactive|failed + + + masked + + + rsyslog.service + + + rsyslog.socket + + + active + + + inactive|failed + + + masked + + + inactive|failed + + + masked + + + inactive|failed + + + masked + + + inactive|failed + + + masked + + + inactive|failed + + + masked + + + sshd.service + + + sshd.socket + + + active + + + inactive|failed + + + masked + + + syslog-ng.service + + + syslog-ng.socket + + + active + + + inactive|failed + + + masked + + + systemd-journald.service + + + systemd-journald.socket + + + active + + + inactive|failed + + + masked + + + ufw.service + + + ufw.socket + + + active + + + usbguard.service + + + usbguard.socket + + + active + + + inactive|failed + + + masked + + + inactive|failed + + + masked + + + inactive|failed + + + masked + + + ^no$ + + + ^no$ + + + ^no$ + + + ^no$ + + + ^no$ + + + ^no$ + + + ^no$ + + + ^no$ + + + ^yes$ + + + ^yes$ + + + ^no$ + + + ^no$ + + + ^prohibit-password$ + + + ^prohibit-password$ + + + ^no$ + + + ^no$ + + + ^yes$ + + + ^yes$ + + + ^no$ + + + ^no$ + + + ^no$ + + + ^no$ + + + ^yes$ + + + ^yes$ + + + ^yes$ + + + ^yes$ + + + ^yes$ + + + ^yes$ + + + ^yes$ + + + ^yes$ + + + ^/etc/issue$ + + + ^/etc/issue$ + + + ^/etc/issue.net$ + + + ^/etc/issue.net$ + + + ^yes$ + + + ^yes$ + + + ^yes$ + + + ^yes$ + + + ^0$ + + + ^0$ + + + ^INFO$ + + + ^INFO$ + + + ^VERBOSE$ + + + ^VERBOSE$ + + + ^32$ + + + ^yes$ + + + ^yes$ + + + + + + 2 + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + 2 + + + 1 + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + 1 + + + 2 + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + 2 + + + 1 + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + 1 + + + 0 + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + 0 + + + |/bin/false + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + |/bin/false + + + 0 + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + 0 + + + 1 + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + 1 + + + 1 + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + 1 + + + 1 + + + 2 + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + 1 + + + 2 + + + 1 + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + 1 + + + 1 + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + 1 + + + 1 + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + 1 + + + 1 + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + 1 + + + 2 + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + 2 + + + 65536 + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + 65536 + + + 2 + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + 2 + + + 0 + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + 0 + + + 1 + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + 1 + + + 1 + + + 2 + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + 1 + + + 2 + + + 1 + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + 1 + + + 2 + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + 2 + + + 0 + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + 0 + + + + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + + + + + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + + + + + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + + + + + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + + + + 1 + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + 1 + + + + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + + + + 0 + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + 0 + + + 1 + + + 2 + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + 1 + + + 2 + + + + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + + + + 0 + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + 0 + + + + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + + + + + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + + + + + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + + + + + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + + + + + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + + + + + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + + + + 0 + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + 0 + + + + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + + + + + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + + + + + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + + + + 0 + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + 0 + + + 32768\s*65535 + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + 32768\s*65535 + + + + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + + + + + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + + + + + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + + + + + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + + + + + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + + + + + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + + + + + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + + + + + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + + + + + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + + + + + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + + + + 1 + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + 1 + + + + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + + + + + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + + + + + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + + + + + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + + + + + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + + + + + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + + + + + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + + + + + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + + + + + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + + + + + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + + + + 1 + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + 1 + + + + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + + + + + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + + + + 0 + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + 0 + + + 65536 + + + 1 + + + + + + ^(?!(\/etc\/sysctl\.conf$|(\/etc|\/run|\/usr\/lib)\/sysctl\.d\/)).*$ + + + 65536 + + + dnf-automatic.timer + + + active + + + ^(?:.*\s)?audit=1(?:\s.*)?$ + + + ^(?:.*\s)?audit=1(?:\s.*)?$ + + + ^(?:.*\s)?audit_backlog_limit=8192(?:\s.*)?$ + + + ^(?:.*\s)?audit_backlog_limit=8192(?:\s.*)?$ + + + ^(?:.*\s)?init_on_alloc=1(?:\s.*)?$ + + + ^(?:.*\s)?init_on_alloc=1(?:\s.*)?$ + + + ^(?:.*\s)?page_alloc\.shuffle=1(?:\s.*)?$ + + + ^(?:.*\s)?page_alloc\.shuffle=1(?:\s.*)?$ + + + ^(?:.*\s)?page_poison=1(?:\s.*)?$ + + + ^(?:.*\s)?page_poison=1(?:\s.*)?$ + + + ^(?:.*\s)?slub_debug=P(?:\s.*)?$ + + + ^(?:.*\s)?slub_debug=P(?:\s.*)?$ + + + ^(?:.*\s)?vsyscall=none(?:\s.*)?$ + + + ^(?:.*\s)?vsyscall=none(?:\s.*)?$ + + + ^(true|"true")$ + + + ^2.*$ + + + ^3.*$ + + + ^7.*$ + + + centos + + + 8 + + + centos + + + 9 + + + ^7.*$ + + + ^8.*$ + + + ^9.*$ + + + openSUSE-release + + + ^15.*$ + + + ^42.*$ + + + unix + + + rhcos + + + 4 + + + unix + + + ^7.*$ + + + ^7.*$ + + + ^7.*$ + + + ^7.*$ + + + 7 + + + unix + + + ^8.*$ + + + ^8.0*$ + + + ^8.1*$ + + + ^8.2*$ + + + ^8.3*$ + + + ^8.4*$ + + + ^8.5*$ + + + ^8.6*$ + + + ^8.7*$ + + + ^8.8*$ + + + ^8.9*$ + + + ^8.10*$ + + + 8 + + + unix + + + ^9.*$ + + + 9 + + + 0:4.4 + + + ^7.*$ + + + unix + + + ^12.*$ + + + ^12.*$ + + + ^12.*$ + + + unix + + + ^15.*$ + + + ^15.*$ + + + ^15.*$ + + + ^20.*$ + + + ^4.*$ + + + 0:1.17-18 + + + 0:1.17-18 + + + ^aarch64$ + + + ^ppc64le$ + + + ^s390x$ + + + 1 + + + 2 + + + 0 + + + 0:7.4 + + + aarch64 + + + ppc64 + + + ppc64le + + + s390x + + + i686 + + + x86_64 + + + true + + + /dev/cdrom + + + + + + + + + + + + + ^[\s]*RekeyLimit[\s]+ + + [\s]+ + + [\s]*$ + + + + + + + + + + + + + .xml + + + + + + + ^[\s]*RekeyLimit[\s]+ + + [\s]+ + + [\s]*$ + + + + + + + + + + + + + + + + + ^[\s]*auth[\s]+(?:required|requisite)[\s]+pam_faillock.so[^\n#]preauth[^\n#]*audit + + + + ^\s*password\s+(?: + + )\s+pam_pwhistory\.so.*remember=([0-9]*).*$ + + + + + + + + + + + + ^\s*password\s+(?: + + )\s+pam_pwhistory\.so.*remember=([0-9]*).*$ + + + + + + + + + + ^[\s]*auth\N+pam_unix\.so + + + ^[\s]*auth[\s]+(required|\[(?=.*?\bsuccess=ok\b)(?=.*?\bnew_authtok_reqd=ok\b)(?=.*?\bignore=ignore\b)(?=.*?\bdefault=bad\b).*\])[\s]+pam_faillock\.so[\s\w\d=]+preauth[\s\S]*^[\s]*auth[\s]+(sufficient|\[(?=.*\bsuccess=done\b)(?=.*?\bnew_authtok_reqd=done\b)(?=.*?\bdefault=ignore\b).*\])[\s]+pam_unix\.so[\s\S]*^[\s]*auth[\s]+(required|\[(?=.*?\bsuccess=ok\b)(?=.*?\bnew_authtok_reqd=ok\b)(?=.*?\bignore=ignore\b)(?=.*?\bdefault=bad\b).*\])[\s]+pam_faillock\.so[\s\w\d=]+authfail + + + ^[\s]*account[\s]+(required|\[(?=.*?\bsuccess=ok\b)(?=.*?\bnew_authtok_reqd=ok\b)(?=.*?\bignore=ignore\b)(?=.*?\bdefault=bad\b).*\])[\s]+pam_faillock\.so[\s\S]*^[\s]*account[\s]+(required|\[(?=.*?\bsuccess=ok\b)(?=.*?\bnew_authtok_reqd=ok\b)(?=.*?\bignore=ignore\b)(?=.*?\bdefault=bad\b).*\])[\s]+pam_unix\.so + + + ^[\s]*auth[\s]+.+[\s]+pam_faillock.so[\s]+[^\n]*deny=([0-9]+) + + + ^[\s]*deny[\s]*=[\s]*([0-9]+) + + + + ^[\s]*auth\N+pam_unix\.so + + + ^[\s]*auth[\s]+(required|\[(?=.*?\bsuccess=ok\b)(?=.*?\bnew_authtok_reqd=ok\b)(?=.*?\bignore=ignore\b)(?=.*?\bdefault=bad\b).*\])[\s]+pam_faillock\.so[\s\w\d=]+preauth[\s\S]*^[\s]*auth[\s]+(sufficient|\[(?=.*\bsuccess=done\b)(?=.*?\bnew_authtok_reqd=done\b)(?=.*?\bdefault=ignore\b).*\])[\s]+pam_unix\.so[\s\S]*^[\s]*auth[\s]+(required|\[(?=.*?\bsuccess=ok\b)(?=.*?\bnew_authtok_reqd=ok\b)(?=.*?\bignore=ignore\b)(?=.*?\bdefault=bad\b).*\])[\s]+pam_faillock\.so[\s\w\d=]+authfail + + + ^[\s]*account[\s]+(required|\[(?=.*?\bsuccess=ok\b)(?=.*?\bnew_authtok_reqd=ok\b)(?=.*?\bignore=ignore\b)(?=.*?\bdefault=bad\b).*\])[\s]+pam_faillock\.so[\s\S]*^[\s]*account[\s]+(required|\[(?=.*?\bsuccess=ok\b)(?=.*?\bnew_authtok_reqd=ok\b)(?=.*?\bignore=ignore\b)(?=.*?\bdefault=bad\b).*\])[\s]+pam_unix\.so + + + ^[\s]*auth[\s]+.+[\s]+pam_faillock.so[\s]+[^\n]*even_deny_root + + + ^[\s]*even_deny_root + + + dir\s*=\s*(\S+|"[^"]+) + + + + ^[\s]*auth[\s]+(?:required|requisite) + [\s]+pam_faillock.so[^\n#]* + + + + + + ^[\s]* + + + + + + + + + + + + + + + + + + + + + + + ^[\s]*auth\N+pam_unix\.so + + + ^[\s]*auth[\s]+(required|\[(?=.*?\bsuccess=ok\b)(?=.*?\bnew_authtok_reqd=ok\b)(?=.*?\bignore=ignore\b)(?=.*?\bdefault=bad\b).*\])[\s]+pam_faillock\.so[\s\w\d=]+preauth[\s\S]*^[\s]*auth[\s]+(sufficient|\[(?=.*\bsuccess=done\b)(?=.*?\bnew_authtok_reqd=done\b)(?=.*?\bdefault=ignore\b).*\])[\s]+pam_unix\.so[\s\S]*^[\s]*auth[\s]+(required|\[(?=.*?\bsuccess=ok\b)(?=.*?\bnew_authtok_reqd=ok\b)(?=.*?\bignore=ignore\b)(?=.*?\bdefault=bad\b).*\])[\s]+pam_faillock\.so[\s\w\d=]+authfail + + + ^[\s]*account[\s]+(required|\[(?=.*?\bsuccess=ok\b)(?=.*?\bnew_authtok_reqd=ok\b)(?=.*?\bignore=ignore\b)(?=.*?\bdefault=bad\b).*\])[\s]+pam_faillock\.so[\s\S]*^[\s]*account[\s]+(required|\[(?=.*?\bsuccess=ok\b)(?=.*?\bnew_authtok_reqd=ok\b)(?=.*?\bignore=ignore\b)(?=.*?\bdefault=bad\b).*\])[\s]+pam_unix\.so + + + ^[\s]*local_users_only + + + ^[\s]*auth\N+pam_unix\.so + + + ^[\s]*auth[\s]+(required|\[(?=.*?\bsuccess=ok\b)(?=.*?\bnew_authtok_reqd=ok\b)(?=.*?\bignore=ignore\b)(?=.*?\bdefault=bad\b).*\])[\s]+pam_faillock\.so[\s\w\d=]+preauth[\s\S]*^[\s]*auth[\s]+(sufficient|\[(?=.*\bsuccess=done\b)(?=.*?\bnew_authtok_reqd=done\b)(?=.*?\bdefault=ignore\b).*\])[\s]+pam_unix\.so[\s\S]*^[\s]*auth[\s]+(required|\[(?=.*?\bsuccess=ok\b)(?=.*?\bnew_authtok_reqd=ok\b)(?=.*?\bignore=ignore\b)(?=.*?\bdefault=bad\b).*\])[\s]+pam_faillock\.so[\s\w\d=]+authfail + + + ^[\s]*account[\s]+(required|\[(?=.*?\bsuccess=ok\b)(?=.*?\bnew_authtok_reqd=ok\b)(?=.*?\bignore=ignore\b)(?=.*?\bdefault=bad\b).*\])[\s]+pam_faillock\.so[\s\S]*^[\s]*account[\s]+(required|\[(?=.*?\bsuccess=ok\b)(?=.*?\bnew_authtok_reqd=ok\b)(?=.*?\bignore=ignore\b)(?=.*?\bdefault=bad\b).*\])[\s]+pam_unix\.so + + + ^[\s]*auth[\s]+.+[\s]+pam_faillock.so[\s]+[^\n]*fail_interval=([0-9]+) + + + ^[\s]*fail_interval[\s]*=[\s]*([0-9]+) + + + + ^[\s]*auth\N+pam_unix\.so + + + ^[\s]*auth[\s]+(required|\[(?=.*?\bsuccess=ok\b)(?=.*?\bnew_authtok_reqd=ok\b)(?=.*?\bignore=ignore\b)(?=.*?\bdefault=bad\b).*\])[\s]+pam_faillock\.so[\s\w\d=]+preauth[\s\S]*^[\s]*auth[\s]+(sufficient|\[(?=.*\bsuccess=done\b)(?=.*?\bnew_authtok_reqd=done\b)(?=.*?\bdefault=ignore\b).*\])[\s]+pam_unix\.so[\s\S]*^[\s]*auth[\s]+(required|\[(?=.*?\bsuccess=ok\b)(?=.*?\bnew_authtok_reqd=ok\b)(?=.*?\bignore=ignore\b)(?=.*?\bdefault=bad\b).*\])[\s]+pam_faillock\.so[\s\w\d=]+authfail + + + ^[\s]*account[\s]+(required|\[(?=.*?\bsuccess=ok\b)(?=.*?\bnew_authtok_reqd=ok\b)(?=.*?\bignore=ignore\b)(?=.*?\bdefault=bad\b).*\])[\s]+pam_faillock\.so[\s\S]*^[\s]*account[\s]+(required|\[(?=.*?\bsuccess=ok\b)(?=.*?\bnew_authtok_reqd=ok\b)(?=.*?\bignore=ignore\b)(?=.*?\bdefault=bad\b).*\])[\s]+pam_unix\.so + + + ^[\s]*auth[\s]+.+[\s]+pam_faillock.so[\s]+[^\n]*unlock_time=([0-9]+) + + + ^[\s]*unlock_time[\s]*=[\s]*([0-9]+) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5000 + + + + 5000 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ^[^#]* + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 64 + + + + 8 + + + + + + + + + + + + + + + + + + + + + + + + 64 + + + + 8 + + + + + + + + + + + + + + + + + + + + + + + + 64 + + + + 8 + + + + + + + + + + + + + + + + + + + + + + + + 64 + + + + 8 + + + + + + + + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+dir=/var/log/audit/)[\s]+(?:-F[\s]+perm=r)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + (?i) + + + + + + + (?i) + + + + + + + + + + + + + + + + + + + + + + + + + + + + /boot/config- + + + + + + + + + + + + + + + + + + + + + + + + ^/etc/rsyslog.conf$ + + + + + + + + + + + + + + + + ^/etc/rsyslog.conf$ + + + + + + + + + + + + + + + + ^/etc/rsyslog.conf$ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Ciphers + + + + + + + -oCiphers= + + + + + + MACs + + + + + + + -oMACs= + + + + + + + / + + + + + + + / + + + + + + + + + + + + + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:-S[\s]+(?:[\S]+,)*(open)(?:,[\S]+)*)[\s]+(?:-F[\s]+a1&03)[\s]+(?:-F[\s]+path=/etc/group)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:-S[\s]+(?:[\S]+,)*(open)(?:,[\S]+)*)[\s]+(?:-F[\s]+a1&03)[\s]+(?:-F[\s]+path=/etc/group)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:-S[\s]+(?:[\S]+,)*(open_by_handle_at)(?:,[\S]+)*)[\s]+(?:-F[\s]+a2&03)[\s]+(?:-F[\s]+path=/etc/group)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:-S[\s]+(?:[\S]+,)*(open_by_handle_at)(?:,[\S]+)*)[\s]+(?:-F[\s]+a2&03)[\s]+(?:-F[\s]+path=/etc/group)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:-S[\s]+(?:[\S]+,)*(openat)(?:,[\S]+)*)[\s]+(?:-F[\s]+a2&03)[\s]+(?:-F[\s]+path=/etc/group)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:-S[\s]+(?:[\S]+,)*(openat)(?:,[\S]+)*)[\s]+(?:-F[\s]+a2&03)[\s]+(?:-F[\s]+path=/etc/group)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:-S[\s]+(?:[\S]+,)*(open)(?:,[\S]+)*)[\s]+(?:-F[\s]+a1&03)[\s]+(?:-F[\s]+path=/etc/gshadow)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:-S[\s]+(?:[\S]+,)*(open)(?:,[\S]+)*)[\s]+(?:-F[\s]+a1&03)[\s]+(?:-F[\s]+path=/etc/gshadow)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:-S[\s]+(?:[\S]+,)*(open_by_handle_at)(?:,[\S]+)*)[\s]+(?:-F[\s]+a2&03)[\s]+(?:-F[\s]+path=/etc/gshadow)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:-S[\s]+(?:[\S]+,)*(open_by_handle_at)(?:,[\S]+)*)[\s]+(?:-F[\s]+a2&03)[\s]+(?:-F[\s]+path=/etc/gshadow)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:-S[\s]+(?:[\S]+,)*(openat)(?:,[\S]+)*)[\s]+(?:-F[\s]+a2&03)[\s]+(?:-F[\s]+path=/etc/gshadow)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:-S[\s]+(?:[\S]+,)*(openat)(?:,[\S]+)*)[\s]+(?:-F[\s]+a2&03)[\s]+(?:-F[\s]+path=/etc/gshadow)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:-S[\s]+(?:[\S]+,)*(open)(?:,[\S]+)*)[\s]+(?:-F[\s]+a1&03)[\s]+(?:-F[\s]+path=/etc/passwd)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:-S[\s]+(?:[\S]+,)*(open)(?:,[\S]+)*)[\s]+(?:-F[\s]+a1&03)[\s]+(?:-F[\s]+path=/etc/passwd)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:-S[\s]+(?:[\S]+,)*(open_by_handle_at)(?:,[\S]+)*)[\s]+(?:-F[\s]+a2&03)[\s]+(?:-F[\s]+path=/etc/passwd)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:-S[\s]+(?:[\S]+,)*(open_by_handle_at)(?:,[\S]+)*)[\s]+(?:-F[\s]+a2&03)[\s]+(?:-F[\s]+path=/etc/passwd)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:-S[\s]+(?:[\S]+,)*(openat)(?:,[\S]+)*)[\s]+(?:-F[\s]+a2&03)[\s]+(?:-F[\s]+path=/etc/passwd)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:-S[\s]+(?:[\S]+,)*(openat)(?:,[\S]+)*)[\s]+(?:-F[\s]+a2&03)[\s]+(?:-F[\s]+path=/etc/passwd)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:-S[\s]+(?:[\S]+,)*(open)(?:,[\S]+)*)[\s]+(?:-F[\s]+a1&03)[\s]+(?:-F[\s]+path=/etc/shadow)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:-S[\s]+(?:[\S]+,)*(open)(?:,[\S]+)*)[\s]+(?:-F[\s]+a1&03)[\s]+(?:-F[\s]+path=/etc/shadow)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:-S[\s]+(?:[\S]+,)*(open_by_handle_at)(?:,[\S]+)*)[\s]+(?:-F[\s]+a2&03)[\s]+(?:-F[\s]+path=/etc/shadow)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:-S[\s]+(?:[\S]+,)*(open_by_handle_at)(?:,[\S]+)*)[\s]+(?:-F[\s]+a2&03)[\s]+(?:-F[\s]+path=/etc/shadow)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:-S[\s]+(?:[\S]+,)*(openat)(?:,[\S]+)*)[\s]+(?:-F[\s]+a2&03)[\s]+(?:-F[\s]+path=/etc/shadow)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:-S[\s]+(?:[\S]+,)*(openat)(?:,[\S]+)*)[\s]+(?:-F[\s]+a2&03)[\s]+(?:-F[\s]+path=/etc/shadow)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+chmod[\s]+|([\s]+|[,])chmod([\s]+|[,])))(?:(?!-F[\s]+a\d&).)* + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+chmod[\s]+|([\s]+|[,])chmod([\s]+|[,])))(?:(?!-F[\s]+a\d&).)* + + + [\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+chown[\s]+|([\s]+|[,])chown([\s]+|[,])))(?:(?!-F[\s]+a\d&).)* + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+chown[\s]+|([\s]+|[,])chown([\s]+|[,])))(?:(?!-F[\s]+a\d&).)* + + + [\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+creat[\s]+|([\s]+|[,])creat([\s]+|[,])))(?:(?!-F[\s]+a\d&).)* + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+creat[\s]+|([\s]+|[,])creat([\s]+|[,])))(?:(?!-F[\s]+a\d&).)* + + + [\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+fchmod[\s]+|([\s]+|[,])fchmod([\s]+|[,])))(?:(?!-F[\s]+a\d&).)* + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+fchmod[\s]+|([\s]+|[,])fchmod([\s]+|[,])))(?:(?!-F[\s]+a\d&).)* + + + [\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+fchmodat[\s]+|([\s]+|[,])fchmodat([\s]+|[,])))(?:(?!-F[\s]+a\d&).)* + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+fchmodat[\s]+|([\s]+|[,])fchmodat([\s]+|[,])))(?:(?!-F[\s]+a\d&).)* + + + [\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+fchown[\s]+|([\s]+|[,])fchown([\s]+|[,])))(?:(?!-F[\s]+a\d&).)* + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+fchown[\s]+|([\s]+|[,])fchown([\s]+|[,])))(?:(?!-F[\s]+a\d&).)* + + + [\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+fchownat[\s]+|([\s]+|[,])fchownat([\s]+|[,])))(?:(?!-F[\s]+a\d&).)* + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+fchownat[\s]+|([\s]+|[,])fchownat([\s]+|[,])))(?:(?!-F[\s]+a\d&).)* + + + [\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+fremovexattr[\s]+|([\s]+|[,])fremovexattr([\s]+|[,])))(?:(?!-F[\s]+a\d&).)* + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+fremovexattr[\s]+|([\s]+|[,])fremovexattr([\s]+|[,])))(?:(?!-F[\s]+a\d&).)* + + + [\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+fsetxattr[\s]+|([\s]+|[,])fsetxattr([\s]+|[,])))(?:(?!-F[\s]+a\d&).)* + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+fsetxattr[\s]+|([\s]+|[,])fsetxattr([\s]+|[,])))(?:(?!-F[\s]+a\d&).)* + + + [\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+ftruncate[\s]+|([\s]+|[,])ftruncate([\s]+|[,])))(?:(?!-F[\s]+a\d&).)* + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+ftruncate[\s]+|([\s]+|[,])ftruncate([\s]+|[,])))(?:(?!-F[\s]+a\d&).)* + + + [\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+lchown[\s]+|([\s]+|[,])lchown([\s]+|[,])))(?:(?!-F[\s]+a\d&).)* + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+lchown[\s]+|([\s]+|[,])lchown([\s]+|[,])))(?:(?!-F[\s]+a\d&).)* + + + [\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+lremovexattr[\s]+|([\s]+|[,])lremovexattr([\s]+|[,])))(?:(?!-F[\s]+a\d&).)* + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+lremovexattr[\s]+|([\s]+|[,])lremovexattr([\s]+|[,])))(?:(?!-F[\s]+a\d&).)* + + + [\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+lsetxattr[\s]+|([\s]+|[,])lsetxattr([\s]+|[,])))(?:(?!-F[\s]+a\d&).)* + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+lsetxattr[\s]+|([\s]+|[,])lsetxattr([\s]+|[,])))(?:(?!-F[\s]+a\d&).)* + + + [\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+open[\s]+|([\s]+|[,])open([\s]+|[,])))(?:(?!-F[\s]+a\d&).)* + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+open[\s]+|([\s]+|[,])open([\s]+|[,])))(?:(?!-F[\s]+a\d&).)* + + + [\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+open_by_handle_at[\s]+|([\s]+|[,])open_by_handle_at([\s]+|[,])))(?:(?!-F[\s]+a\d&).)* + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+open_by_handle_at[\s]+|([\s]+|[,])open_by_handle_at([\s]+|[,])))(?:(?!-F[\s]+a\d&).)* + + + [\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:-S[\s]+(?:[\S]+,)*(open_by_handle_at)(?:,[\S]+)*)[\s]+ + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:-S[\s]+(?:[\S]+,)*(open_by_handle_at)(?:,[\S]+)*)[\s]+ + + + [\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + (?:[^.]|\.\s)* + + + + + (?:-F\s+a2&0100)[\s]+(?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+a2&0100)[\s]+(?:-F\s+exit=-EPERM) + + + + + + + (?:-F\s+a2&0100)[\s]+(?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+a2&0100)[\s]+(?:-F\s+exit=-EPERM) + + + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:-S[\s]+(?:[\S]+,)*(open_by_handle_at)(?:,[\S]+)*)[\s]+ + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:-S[\s]+(?:[\S]+,)*(open_by_handle_at)(?:,[\S]+)*)[\s]+ + + + [\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + (?:[^.]|\.\s)* + + + + + (?:-F\s+a2&01003)[\s]+(?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+a2&01003)[\s]+(?:-F\s+exit=-EPERM) + + + + + + + (?:-F\s+a2&01003)[\s]+(?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+a2&01003)[\s]+(?:-F\s+exit=-EPERM) + + + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:-S[\s]+(?:[\S]+,)*(open_by_handle_at)(?:,[\S]+)*)[\s]+ + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:-S[\s]+(?:[\S]+,)*(open_by_handle_at)(?:,[\S]+)*)[\s]+ + + + [\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(?:unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + + + (?:-F\s+a2&0100)[\s]+(?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+a2&01003)[\s]+(?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+a2&0100)[\s]+(?:-F\s+exit=-EPERM) + + + + + + + (?:-F\s+a2&01003)[\s]+(?:-F\s+exit=-EPERM) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + + + (?:-F\s+a2&0100)[\s]+(?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+a2&01003)[\s]+(?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+a2&0100)[\s]+(?:-F\s+exit=-EPERM) + + + + + + + (?:-F\s+a2&01003)[\s]+(?:-F\s+exit=-EPERM) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + + ^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $ + + + + + ^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $ + + + + + ^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $ + + + + + ^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $ + + + + + ^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $ + + + + + ^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $ + + + + + ^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $ + + + + + ^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $ + + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:-S[\s]+(?:[\S]+,)*(open)(?:,[\S]+)*)[\s]+ + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:-S[\s]+(?:[\S]+,)*(open)(?:,[\S]+)*)[\s]+ + + + [\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + (?:[^.]|\.\s)* + + + + + (?:-F\s+a1&0100)[\s]+(?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+a1&0100)[\s]+(?:-F\s+exit=-EPERM) + + + + + + + (?:-F\s+a1&0100)[\s]+(?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+a1&0100)[\s]+(?:-F\s+exit=-EPERM) + + + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:-S[\s]+(?:[\S]+,)*(open)(?:,[\S]+)*)[\s]+ + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:-S[\s]+(?:[\S]+,)*(open)(?:,[\S]+)*)[\s]+ + + + [\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + (?:[^.]|\.\s)* + + + + + (?:-F\s+a1&01003)[\s]+(?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+a1&01003)[\s]+(?:-F\s+exit=-EPERM) + + + + + + + (?:-F\s+a1&01003)[\s]+(?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+a1&01003)[\s]+(?:-F\s+exit=-EPERM) + + + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:-S[\s]+(?:[\S]+,)*(open)(?:,[\S]+)*)[\s]+ + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:-S[\s]+(?:[\S]+,)*(open)(?:,[\S]+)*)[\s]+ + + + [\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(?:unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + + + (?:-F\s+a1&0100)[\s]+(?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+a1&01003)[\s]+(?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+a1&0100)[\s]+(?:-F\s+exit=-EPERM) + + + + + + + (?:-F\s+a1&01003)[\s]+(?:-F\s+exit=-EPERM) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + + + (?:-F\s+a1&0100)[\s]+(?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+a1&01003)[\s]+(?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+a1&0100)[\s]+(?:-F\s+exit=-EPERM) + + + + + + + (?:-F\s+a1&01003)[\s]+(?:-F\s+exit=-EPERM) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + + ^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $ + + + + + ^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $ + + + + + ^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $ + + + + + ^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $ + + + + + ^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $ + + + + + ^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $ + + + + + ^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $ + + + + + ^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $ + + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+openat[\s]+|([\s]+|[,])openat([\s]+|[,])))(?:(?!-F[\s]+a\d&).)* + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+openat[\s]+|([\s]+|[,])openat([\s]+|[,])))(?:(?!-F[\s]+a\d&).)* + + + [\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:-S[\s]+(?:[\S]+,)*(openat)(?:,[\S]+)*)[\s]+ + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:-S[\s]+(?:[\S]+,)*(openat)(?:,[\S]+)*)[\s]+ + + + [\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + (?:[^.]|\.\s)* + + + + + (?:-F\s+a2&0100)[\s]+(?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+a2&0100)[\s]+(?:-F\s+exit=-EPERM) + + + + + + + (?:-F\s+a2&0100)[\s]+(?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+a2&0100)[\s]+(?:-F\s+exit=-EPERM) + + + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:-S[\s]+(?:[\S]+,)*(openat)(?:,[\S]+)*)[\s]+ + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:-S[\s]+(?:[\S]+,)*(openat)(?:,[\S]+)*)[\s]+ + + + [\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + (?:[^.]|\.\s)* + + + + + (?:-F\s+a2&01003)[\s]+(?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+a2&01003)[\s]+(?:-F\s+exit=-EPERM) + + + + + + + (?:-F\s+a2&01003)[\s]+(?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+a2&01003)[\s]+(?:-F\s+exit=-EPERM) + + + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:-S[\s]+(?:[\S]+,)*(openat)(?:,[\S]+)*)[\s]+ + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:-S[\s]+(?:[\S]+,)*(openat)(?:,[\S]+)*)[\s]+ + + + [\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(?:unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + + + (?:-F\s+a2&0100)[\s]+(?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+a2&01003)[\s]+(?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+a2&0100)[\s]+(?:-F\s+exit=-EPERM) + + + + + + + (?:-F\s+a2&01003)[\s]+(?:-F\s+exit=-EPERM) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + + + (?:-F\s+a2&0100)[\s]+(?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+a2&01003)[\s]+(?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+a2&0100)[\s]+(?:-F\s+exit=-EPERM) + + + + + + + (?:-F\s+a2&01003)[\s]+(?:-F\s+exit=-EPERM) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + + ^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $ + + + + + ^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $ + + + + + ^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $ + + + + + ^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $ + + + + + ^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $ + + + + + ^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $ + + + + + ^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $ + + + + + ^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $\n(^(?! + + | + + ).*$\n)*^ + + $ + + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+removexattr[\s]+|([\s]+|[,])removexattr([\s]+|[,])))(?:(?!-F[\s]+a\d&).)* + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+removexattr[\s]+|([\s]+|[,])removexattr([\s]+|[,])))(?:(?!-F[\s]+a\d&).)* + + + [\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+rename[\s]+|([\s]+|[,])rename([\s]+|[,])))(?:(?!-F[\s]+a\d&).)* + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+rename[\s]+|([\s]+|[,])rename([\s]+|[,])))(?:(?!-F[\s]+a\d&).)* + + + [\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+renameat[\s]+|([\s]+|[,])renameat([\s]+|[,])))(?:(?!-F[\s]+a\d&).)* + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+renameat[\s]+|([\s]+|[,])renameat([\s]+|[,])))(?:(?!-F[\s]+a\d&).)* + + + [\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+setxattr[\s]+|([\s]+|[,])setxattr([\s]+|[,])))(?:(?!-F[\s]+a\d&).)* + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+setxattr[\s]+|([\s]+|[,])setxattr([\s]+|[,])))(?:(?!-F[\s]+a\d&).)* + + + [\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+truncate[\s]+|([\s]+|[,])truncate([\s]+|[,])))(?:(?!-F[\s]+a\d&).)* + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+truncate[\s]+|([\s]+|[,])truncate([\s]+|[,])))(?:(?!-F[\s]+a\d&).)* + + + [\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+unlink[\s]+|([\s]+|[,])unlink([\s]+|[,])))(?:(?!-F[\s]+a\d&).)* + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+unlink[\s]+|([\s]+|[,])unlink([\s]+|[,])))(?:(?!-F[\s]+a\d&).)* + + + [\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+unlinkat[\s]+|([\s]+|[,])unlinkat([\s]+|[,])))(?:(?!-F[\s]+a\d&).)* + + + ^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+unlinkat[\s]+|([\s]+|[,])unlinkat([\s]+|[,])))(?:(?!-F[\s]+a\d&).)* + + + [\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$ + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + + + (?:-F\s+exit=-EACCES) + + + + + + + (?:-F\s+exit=-EPERM) + + + + + + ^(?:.*\s)?l1tf= + + (?:\s.*)?$ + + + + + + ^(?:.*\s)?mds= + + (?:\s.*)?$ + + + + + + ^(?:.*\s)?rng_core.default_quality= + + (?:\s.*)?$ + + + + + + ^(?:.*\s)?slub_debug= + + (?:\s.*)?$ + + + + + + ^(?:.*\s)?spec_store_bypass_disable= + + (?:\s.*)?$ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + /etc/modprobe.d + /etc/modules-load.d + /run/modprobe.d + /run/modules-load.d + /usr/lib/modprobe.d + /usr/lib/modules-load.d + + + /etc/modprobe.d + /etc/modules-load.d + /run/modprobe.d + /run/modules-load.d + /usr/lib/modprobe.d + /usr/lib/modules-load.d + + + /etc/modprobe.d + /etc/modules-load.d + /run/modprobe.d + /run/modules-load.d + /usr/lib/modprobe.d + /usr/lib/modules-load.d + + + /etc/modprobe.d + /etc/modules-load.d + /run/modprobe.d + /run/modules-load.d + /usr/lib/modprobe.d + /usr/lib/modules-load.d + + + /etc/modprobe.d + /etc/modules-load.d + /run/modprobe.d + /run/modules-load.d + /usr/lib/modprobe.d + /usr/lib/modules-load.d + + + /etc/modprobe.d + /etc/modules-load.d + /run/modprobe.d + /run/modules-load.d + /usr/lib/modprobe.d + /usr/lib/modules-load.d + + + /etc/modprobe.d + /etc/modules-load.d + /run/modprobe.d + /run/modules-load.d + /usr/lib/modprobe.d + /usr/lib/modules-load.d + + + /etc/modprobe.d + /etc/modules-load.d + /run/modprobe.d + /run/modules-load.d + /usr/lib/modprobe.d + /usr/lib/modules-load.d + + + /etc/modprobe.d + /etc/modules-load.d + /run/modprobe.d + /run/modules-load.d + /usr/lib/modprobe.d + /usr/lib/modules-load.d + + + /etc/modprobe.d + /etc/modules-load.d + /run/modprobe.d + /run/modules-load.d + /usr/lib/modprobe.d + /usr/lib/modules-load.d + + + /etc/modprobe.d + /etc/modules-load.d + /run/modprobe.d + /run/modules-load.d + /usr/lib/modprobe.d + /usr/lib/modules-load.d + + + /etc/modprobe.d + /etc/modules-load.d + /run/modprobe.d + /run/modules-load.d + /usr/lib/modprobe.d + /usr/lib/modules-load.d + + + /etc/modprobe.d + /etc/modules-load.d + /run/modprobe.d + /run/modules-load.d + /usr/lib/modprobe.d + /usr/lib/modules-load.d + + + /etc/modprobe.d + /etc/modules-load.d + /run/modprobe.d + /run/modules-load.d + /usr/lib/modprobe.d + /usr/lib/modules-load.d + + + /etc/modprobe.d + /etc/modules-load.d + /run/modprobe.d + /run/modules-load.d + /usr/lib/modprobe.d + /usr/lib/modules-load.d + + + /etc/modprobe.d + /etc/modules-load.d + /run/modprobe.d + /run/modules-load.d + /usr/lib/modprobe.d + /usr/lib/modules-load.d + + + /etc/modprobe.d + /etc/modules-load.d + /run/modprobe.d + /run/modules-load.d + /usr/lib/modprobe.d + /usr/lib/modules-load.d + + + /etc/modprobe.d + /etc/modules-load.d + /run/modprobe.d + /run/modules-load.d + /usr/lib/modprobe.d + /usr/lib/modules-load.d + + + /etc/modprobe.d + /etc/modules-load.d + /run/modprobe.d + /run/modules-load.d + /usr/lib/modprobe.d + /usr/lib/modules-load.d + + + /etc/modprobe.d + /etc/modules-load.d + /run/modprobe.d + /run/modules-load.d + /usr/lib/modprobe.d + /usr/lib/modules-load.d + + + /etc/modprobe.d + /etc/modules-load.d + /run/modprobe.d + /run/modules-load.d + /usr/lib/modprobe.d + /usr/lib/modules-load.d + + + /etc/modprobe.d + /etc/modules-load.d + /run/modprobe.d + /run/modules-load.d + /usr/lib/modprobe.d + /usr/lib/modules-load.d + + + /dev/cdrom + /dev/dvd + /dev/scd0 + /dev/sr0 + + + + ^[\s]* + + [\s]+[/\w]+[\s]+[\w]+[\s]+([^\s]+)(?:[\s]+[\d]+){2}$ + + + + + ^[\s]* + + [\s]+[/\w]+[\s]+[\w]+[\s]+([^\s]+)(?:[\s]+[\d]+){2}$ + + + + + /dev/cdrom + /dev/dvd + /dev/scd0 + /dev/sr0 + + + + ^[\s]* + + [\s]+[/\w]+[\s]+[\w]+[\s]+([^\s]+)(?:[\s]+[\d]+){2}$ + + + + + ^[\s]* + + [\s]+[/\w]+[\s]+[\w]+[\s]+([^\s]+)(?:[\s]+[\d]+){2}$ + + + + /dev/cdrom + /dev/dvd + /dev/scd0 + /dev/sr0 + + + + ^[\s]* + + [\s]+[/\w]+[\s]+[\w]+[\s]+([^\s]+)(?:[\s]+[\d]+){2}$ + + + + + ^[\s]* + + [\s]+[/\w]+[\s]+[\w]+[\s]+([^\s]+)(?:[\s]+[\d]+){2}$ + + + + + + hidepid= + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + /etc/pam.d/system-auth + + + + + + + + /dev/cdrom + /dev/dvd + /dev/scd0 + /dev/sr0 + + + + + + + + + + + + + + + + + + + + + + 64 + + + + 8 + + + + + + + + + + + + + + + + + + + + + + + + + 64 + + + + 8 + + + + + + + + + + + + build_shorthand.py from SCAP Security Guide + ssg: 0.1.64 + 2.0 + 2022-09-30T15:13:21 + + + + Disable the dhcpd_use_ldap SELinux Boolean + + ocil:ssg-sebool_dhcpd_use_ldap_action:testaction:1 + + + + Enable the OpenSSH Service + + ocil:ssg-service_sshd_enabled_action:testaction:1 + + + + Enable Kernel Parameter to Log Martian Packets on all IPv4 Interfaces + + ocil:ssg-sysctl_net_ipv4_conf_all_log_martians_action:testaction:1 + + + + Verify /boot/grub2/grub.cfg Group Ownership + + ocil:ssg-file_groupowner_grub2_cfg_action:testaction:1 + + + + Ensure the Default Umask is Set Correctly in /etc/profile + + ocil:ssg-accounts_umask_etc_profile_action:testaction:1 + + + + Uninstall iprutils Package + + ocil:ssg-package_iprutils_removed_action:testaction:1 + + + + Uninstall Sendmail Package + + ocil:ssg-package_sendmail_removed_action:testaction:1 + + + + Harden slab freelist metadata + + ocil:ssg-kernel_config_slab_freelist_hardened_action:testaction:1 + + + + Disable the git_system_use_nfs SELinux Boolean + + ocil:ssg-sebool_git_system_use_nfs_action:testaction:1 + + + + Configure the Use of the pam_faillock.so Module in the /etc/pam.d/system-auth File. + + ocil:ssg-account_password_pam_faillock_system_auth_action:testaction:1 + + + + Set Password Hashing Algorithm in /etc/libuser.conf + + ocil:ssg-set_password_hashing_algorithm_libuserconf_action:testaction:1 + + + + Record Successful Creation Attempts to Files - open O_CREAT + + ocil:ssg-audit_rules_successful_file_modification_open_o_creat_action:testaction:1 + + + + Ensure auditd Collects Information on the Use of Privileged Commands - newuidmap + + ocil:ssg-audit_rules_privileged_commands_newuidmap_action:testaction:1 + + + + Remove Rsh Trust Files + + ocil:ssg-no_rsh_trust_files_action:testaction:1 + + + + Configure SSH to use System Crypto Policy + + ocil:ssg-configure_ssh_crypto_policy_action:testaction:1 + + + + Disable the haproxy_connect_any SELinux Boolean + + ocil:ssg-sebool_haproxy_connect_any_action:testaction:1 + + + + Ensure auditd Collects Information on the Use of Privileged Commands - chage + + ocil:ssg-audit_rules_privileged_commands_chage_action:testaction:1 + + + + Disable vsyscall mapping + + ocil:ssg-kernel_config_legacy_vsyscall_none_action:testaction:1 + + + + Record Unsuccessful Permission Changes to Files - fchmod + + ocil:ssg-audit_rules_unsuccessful_file_modification_fchmod_action:testaction:1 + + + + Disable the smbd_anon_write SELinux Boolean + + ocil:ssg-sebool_smbd_anon_write_action:testaction:1 + + + + Disable the httpd_anon_write SELinux Boolean + + ocil:ssg-sebool_httpd_anon_write_action:testaction:1 + + + + Disable the logwatch_can_network_connect_mail SELinux Boolean + + ocil:ssg-sebool_logwatch_can_network_connect_mail_action:testaction:1 + + + + Set Default firewalld Zone for Incoming Packets + + ocil:ssg-set_firewalld_default_zone_action:testaction:1 + + + + Disable the httpd_can_connect_mythtv SELinux Boolean + + ocil:ssg-sebool_httpd_can_connect_mythtv_action:testaction:1 + + + + Configure auditing of successful file modifications (ppc64le) + + ocil:ssg-audit_modify_success_ppc64le_action:testaction:1 + + + + Disable the gpg_web_anon_write SELinux Boolean + + ocil:ssg-sebool_gpg_web_anon_write_action:testaction:1 + + + + Record Attempts to Alter Time Through stime + + ocil:ssg-audit_rules_time_stime_action:testaction:1 + + + + Ensure that /etc/cron.deny does not exist + + ocil:ssg-file_cron_deny_not_exist_action:testaction:1 + + + + Disable the httpd_tty_comm SELinux Boolean + + ocil:ssg-sebool_httpd_tty_comm_action:testaction:1 + + + + Uninstall talk Package + + ocil:ssg-package_talk_removed_action:testaction:1 + + + + Log USBGuard daemon audit events using Linux Audit + + ocil:ssg-configure_usbguard_auditbackend_action:testaction:1 + + + + Prevent user from disabling the screen lock + + ocil:ssg-no_tmux_in_shells_action:testaction:1 + + + + Account Lockouts Must Be Logged + + ocil:ssg-account_passwords_pam_faillock_audit_action:testaction:1 + + + + Require Authentication for Single User Mode + + ocil:ssg-require_singleuser_auth_action:testaction:1 + + + + Set Default iptables Policy for Forwarded Packets + + ocil:ssg-set_iptables_default_rule_forward_action:testaction:1 + + + + Disable the httpd_unified SELinux Boolean + + ocil:ssg-sebool_httpd_unified_action:testaction:1 + + + + Disable Dovecot Service + + ocil:ssg-service_dovecot_disabled_action:testaction:1 + + + + Verify that System Executables Have Restrictive Permissions + + ocil:ssg-file_permissions_binary_dirs_action:testaction:1 + + + + Record Unsuccessful Creation Attempts to Files - open O_CREAT + + ocil:ssg-audit_rules_unsuccessful_file_modification_open_o_creat_action:testaction:1 + + + + Ensure All User Initialization Files Have Mode 0740 Or Less Permissive + + ocil:ssg-file_permission_user_init_files_action:testaction:1 + + + + Disable the use_samba_home_dirs SELinux Boolean + + ocil:ssg-sebool_use_samba_home_dirs_action:testaction:1 + + + + Install sudo Package + + ocil:ssg-package_sudo_installed_action:testaction:1 + + + + Set SSH authentication attempt limit + + ocil:ssg-sshd_set_max_auth_tries_action:testaction:1 + + + + Disable the mozilla_read_content SELinux Boolean + + ocil:ssg-sebool_mozilla_read_content_action:testaction:1 + + + + Disable SSH Access via Empty Passwords + + ocil:ssg-sshd_disable_empty_passwords_action:testaction:1 + + + + Set SSH Client Alive Count Max to zero + + ocil:ssg-sshd_set_keepalive_0_action:testaction:1 + + + + Install rear Package + + ocil:ssg-package_rear_installed_action:testaction:1 + + + + Specify a Remote NTP Server + + ocil:ssg-ntpd_specify_remote_server_action:testaction:1 + + + + Disable the httpd_use_sasl SELinux Boolean + + ocil:ssg-sebool_httpd_use_sasl_action:testaction:1 + + + + Verify the system-wide library files in directories +"/lib", "/lib64", "/usr/lib/" and "/usr/lib64" are group-owned by root. + + ocil:ssg-root_permissions_syslibrary_files_action:testaction:1 + + + + The operating system must require Re-Authentication when using the sudo command. Ensure sudo timestamp_timeout is appropriate - sudo timestamp_timeout + + ocil:ssg-sudo_require_reauthentication_action:testaction:1 + + + + Configure dnf-automatic to Install Only Security Updates + + ocil:ssg-dnf-automatic_security_updates_only_action:testaction:1 + + + + Configure audispd's Plugin network_failure_action On Network Failure + + ocil:ssg-auditd_audispd_network_failure_action_action:testaction:1 + + + + Ensure auditd Collects Information on the Use of Privileged Commands - usernetctl + + ocil:ssg-audit_rules_privileged_commands_usernetctl_action:testaction:1 + + + + Ensure Users Re-Authenticate for Privilege Escalation - sudo !authenticate + + ocil:ssg-sudo_remove_no_authenticate_action:testaction:1 + + + + Install pam_pwquality Package + + ocil:ssg-package_pam_pwquality_installed_action:testaction:1 + + + + Disallow magic SysRq key + + ocil:ssg-sysctl_kernel_sysrq_action:testaction:1 + + + + Disable the httpd_can_connect_ldap SELinux Boolean + + ocil:ssg-sebool_httpd_can_connect_ldap_action:testaction:1 + + + + Disable IEEE 1394 (FireWire) Support + + ocil:ssg-kernel_module_firewire-core_disabled_action:testaction:1 + + + + Disable X Windows Startup By Setting Default Target + + ocil:ssg-xwindows_runlevel_target_action:testaction:1 + + + + Deactivate Wireless Network Interfaces + + ocil:ssg-wireless_disable_interfaces_action:testaction:1 + + + + Disable the postgresql_can_rsync SELinux Boolean + + ocil:ssg-sebool_postgresql_can_rsync_action:testaction:1 + + + + Install the OpenSSH Server Package + + ocil:ssg-package_openssh-server_installed_action:testaction:1 + + + + Disable the httpd_use_cifs SELinux Boolean + + ocil:ssg-sebool_httpd_use_cifs_action:testaction:1 + + + + An SELinux Context must be configured for the pam_faillock.so records directory + + ocil:ssg-account_password_selinux_faillock_dir_action:testaction:1 + + + + Disable merging of slabs with similar size + + ocil:ssg-grub2_slab_nomerge_argument_action:testaction:1 + + + + Record Unsuccessful Delete Attempts to Files - unlinkat + + ocil:ssg-audit_rules_unsuccessful_file_modification_unlinkat_action:testaction:1 + + + + Configure dnf-automatic to Install Available Updates Automatically + + ocil:ssg-dnf-automatic_apply_updates_action:testaction:1 + + + + Disable the IPv6 protocol + + ocil:ssg-kernel_config_ipv6_action:testaction:1 + + + + Record Successful Access Attempts to Files - openat + + ocil:ssg-audit_rules_successful_file_modification_openat_action:testaction:1 + + + + Disable the httpd_use_openstack SELinux Boolean + + ocil:ssg-sebool_httpd_use_openstack_action:testaction:1 + + + + Ensure /var Located On Separate Partition + + ocil:ssg-partition_for_var_action:testaction:1 + + + + Record Unsuccessful Access Attempts to Files - open + + ocil:ssg-audit_rules_unsuccessful_file_modification_open_action:testaction:1 + + + + Disable the puppetagent_manage_all_files SELinux Boolean + + ocil:ssg-sebool_puppetagent_manage_all_files_action:testaction:1 + + + + Generate USBGuard Policy + + ocil:ssg-usbguard_generate_policy_action:testaction:1 + + + + Configure auditd space_left Action on Low Disk Space + + ocil:ssg-auditd_data_retention_space_left_action_action:testaction:1 + + + + Configure auditing of successful file accesses (AArch64) + + ocil:ssg-audit_access_success_aarch64_action:testaction:1 + + + + Disable the xdm_sysadm_login SELinux Boolean + + ocil:ssg-sebool_xdm_sysadm_login_action:testaction:1 + + + + Add nosuid Option to /var/tmp + + ocil:ssg-mount_option_var_tmp_nosuid_action:testaction:1 + + + + Disable the uvcvideo module + + ocil:ssg-kernel_module_uvcvideo_disabled_action:testaction:1 + + + + Disable the mcelog_client SELinux Boolean + + ocil:ssg-sebool_mcelog_client_action:testaction:1 + + + + Enable systemd-journald Service + + ocil:ssg-service_systemd-journald_enabled_action:testaction:1 + + + + Add noexec Option to /var/log/audit + + ocil:ssg-mount_option_var_log_audit_noexec_action:testaction:1 + + + + Set Existing Passwords Minimum Age + + ocil:ssg-accounts_password_set_min_life_existing_action:testaction:1 + + + + Ensure rsyslog Does Not Accept Remote Messages Unless Acting As Log Server + + ocil:ssg-rsyslog_nolisten_action:testaction:1 + + + + Prevent applications from mapping low portion of virtual memory + + ocil:ssg-sysctl_vm_mmap_min_addr_action:testaction:1 + + + + Enable the File Access Policy Service + + ocil:ssg-service_fapolicyd_enabled_action:testaction:1 + + + + SSSD Has a Correct Trust Anchor + + ocil:ssg-sssd_has_trust_anchor_action:testaction:1 + + + + Verify Permissions on gshadow File + + ocil:ssg-file_permissions_etc_gshadow_action:testaction:1 + + + + Configure the httpd_enable_cgi SELinux Boolean + + ocil:ssg-sebool_httpd_enable_cgi_action:testaction:1 + + + + Ensure auditd Collects System Administrator Actions + + ocil:ssg-audit_rules_sysadmin_actions_action:testaction:1 + + + + Verify permissions on System Login Banner + + ocil:ssg-file_permissions_etc_issue_action:testaction:1 + + + + Ensure Privileged Escalated Commands Cannot Execute Other Commands - sudo NOEXEC + + ocil:ssg-sudo_add_noexec_action:testaction:1 + + + + Disable Core Dumps for All Users + + ocil:ssg-disable_users_coredumps_action:testaction:1 + + + + Disable the daemons_use_tty SELinux Boolean + + ocil:ssg-sebool_daemons_use_tty_action:testaction:1 + + + + Disable the cluster_manage_all_files SELinux Boolean + + ocil:ssg-sebool_cluster_manage_all_files_action:testaction:1 + + + + Record Successful Delete Attempts to Files - rename + + ocil:ssg-audit_rules_successful_file_modification_rename_action:testaction:1 + + + + Enable the selinuxuser_execmod SELinux Boolean + + ocil:ssg-sebool_selinuxuser_execmod_action:testaction:1 + + + + Add noexec Option to /var + + ocil:ssg-mount_option_var_noexec_action:testaction:1 + + + + Configure Notification of Post-AIDE Scan Details + + ocil:ssg-aide_scan_notification_action:testaction:1 + + + + Disable the antivirus_use_jit SELinux Boolean + + ocil:ssg-sebool_antivirus_use_jit_action:testaction:1 + + + + Ensure nss-tools is installed + + ocil:ssg-package_nss-tools_installed_action:testaction:1 + + + + Enable the domain_fd_use SELinux Boolean + + ocil:ssg-sebool_domain_fd_use_action:testaction:1 + + + + Disable Full User Name on Splash Shield + + ocil:ssg-dconf_gnome_screensaver_user_info_action:testaction:1 + + + + Disable the cvs_read_shadow SELinux Boolean + + ocil:ssg-sebool_cvs_read_shadow_action:testaction:1 + + + + Configure L1 Terminal Fault mitigations + + ocil:ssg-grub2_l1tf_argument_action:testaction:1 + + + + Verify Permissions on /etc/cron.allow file + + ocil:ssg-file_permissions_cron_allow_action:testaction:1 + + + + Install rng-tools Package + + ocil:ssg-package_rng-tools_installed_action:testaction:1 + + + + Disable the cups_execmem SELinux Boolean + + ocil:ssg-sebool_cups_execmem_action:testaction:1 + + + + Disable the rsync_anon_write SELinux Boolean + + ocil:ssg-sebool_rsync_anon_write_action:testaction:1 + + + + Limit Password Reuse + + ocil:ssg-accounts_password_pam_unix_remember_action:testaction:1 + + + + Disable the pcp_bind_all_unreserved_ports SELinux Boolean + + ocil:ssg-sebool_pcp_bind_all_unreserved_ports_action:testaction:1 + + + + Disable the httpd_run_stickshift SELinux Boolean + + ocil:ssg-sebool_httpd_run_stickshift_action:testaction:1 + + + + Enable auditd Service + + ocil:ssg-service_auditd_enabled_action:testaction:1 + + + + Disable IPv6 Addressing on IPv6 Interfaces by Default + + ocil:ssg-sysctl_net_ipv6_conf_default_disable_ipv6_action:testaction:1 + + + + Ensure Users Re-Authenticate for Privilege Escalation - sudo + + ocil:ssg-sudo_require_authentication_action:testaction:1 + + + + Disable the httpd_use_gpg SELinux Boolean + + ocil:ssg-sebool_httpd_use_gpg_action:testaction:1 + + + + Ensure journald is configured to write log files to persistent disk + + ocil:ssg-journald_storage_action:testaction:1 + + + + Disable the condor_tcp_network_connect SELinux Boolean + + ocil:ssg-sebool_condor_tcp_network_connect_action:testaction:1 + + + + Ensure auditd Collects Information on the Use of Privileged Commands - gpasswd + + ocil:ssg-audit_rules_privileged_commands_gpasswd_action:testaction:1 + + + + Verify User Who Owns group File + + ocil:ssg-file_owner_etc_group_action:testaction:1 + + + + Enable support for BUG() + + ocil:ssg-kernel_config_bug_action:testaction:1 + + + + Enable poison without sanity check + + ocil:ssg-kernel_config_page_poisoning_no_sanity_action:testaction:1 + + + + Disable the httpd_enable_ftp_server SELinux Boolean + + ocil:ssg-sebool_httpd_enable_ftp_server_action:testaction:1 + + + + Disable the ftpd_connect_all_unreserved SELinux Boolean + + ocil:ssg-sebool_ftpd_connect_all_unreserved_action:testaction:1 + + + + Enable the postfix_local_write_mail_spool SELinux Boolean + + ocil:ssg-sebool_postfix_local_write_mail_spool_action:testaction:1 + + + + Disable mutable hooks + + ocil:ssg-kernel_config_security_writable_hooks_action:testaction:1 + + + + Set hostname as computer node name in audit logs + + ocil:ssg-auditd_name_format_action:testaction:1 + + + + Record Events that Modify the System's Discretionary Access Controls - fchown + + ocil:ssg-audit_rules_dac_modification_fchown_action:testaction:1 + + + + Verify Permissions on group File + + ocil:ssg-file_permissions_etc_group_action:testaction:1 + + + + Disable the nfsd_anon_write SELinux Boolean + + ocil:ssg-sebool_nfsd_anon_write_action:testaction:1 + + + + Disable Kernel Parameter for Accepting Secure ICMP Redirects on all IPv4 Interfaces + + ocil:ssg-sysctl_net_ipv4_conf_all_secure_redirects_action:testaction:1 + + + + Disable the virt_use_comm SELinux Boolean + + ocil:ssg-sebool_virt_use_comm_action:testaction:1 + + + + Record Unsuccessful Ownership Changes to Files - lchown + + ocil:ssg-audit_rules_unsuccessful_file_modification_lchown_action:testaction:1 + + + + Disable DCCP Support + + ocil:ssg-kernel_module_dccp_disabled_action:testaction:1 + + + + Force kernel panic on uncorrected MCEs + + ocil:ssg-grub2_mce_argument_action:testaction:1 + + + + Enable Kernel Parameter to Enforce DAC on FIFOs + + ocil:ssg-sysctl_fs_protected_fifos_action:testaction:1 + + + + Uninstall net-snmp Package + + ocil:ssg-package_net-snmp_removed_action:testaction:1 + + + + Ensure PAM Enforces Password Requirements - Authentication Retry Prompts Permitted Per-Session + + ocil:ssg-accounts_password_pam_retry_action:testaction:1 + + + + Disable the selinuxuser_tcp_server SELinux Boolean + + ocil:ssg-sebool_selinuxuser_tcp_server_action:testaction:1 + + + + Make the module text and rodata read-only + + ocil:ssg-kernel_config_strict_module_rwx_action:testaction:1 + + + + Ensure debug-shell service is not enabled during boot + + ocil:ssg-grub2_systemd_debug-shell_argument_absent_action:testaction:1 + + + + Verify and Correct File Permissions with RPM + + ocil:ssg-rpm_verify_permissions_action:testaction:1 + + + + Enable ExecShield via sysctl + + ocil:ssg-sysctl_kernel_exec_shield_action:testaction:1 + + + + Ensure auditd Collects File Deletion Events by User - rename + + ocil:ssg-audit_rules_file_deletion_events_rename_action:testaction:1 + + + + Record Events that Modify the System's Discretionary Access Controls - umount + + ocil:ssg-audit_rules_dac_modification_umount_action:testaction:1 + + + + Record Successful Permission Changes to Files - fremovexattr + + ocil:ssg-audit_rules_successful_file_modification_fremovexattr_action:testaction:1 + + + + Record Events that Modify the System's Discretionary Access Controls - chown + + ocil:ssg-audit_rules_dac_modification_chown_action:testaction:1 + + + + Disable the racoon_read_shadow SELinux Boolean + + ocil:ssg-sebool_racoon_read_shadow_action:testaction:1 + + + + Enable SSH Server firewalld Firewall Exception + + ocil:ssg-firewalld_sshd_port_enabled_action:testaction:1 + + + + Record Events that Modify User/Group Information via openat syscall - /etc/gshadow + + ocil:ssg-audit_rules_etc_gshadow_openat_action:testaction:1 + + + + Verify Owner on cron.d + + ocil:ssg-file_owner_cron_d_action:testaction:1 + + + + Enable Dracut FIPS Module + + ocil:ssg-enable_dracut_fips_module_action:testaction:1 + + + + Restrict Exposed Kernel Pointer Addresses Access + + ocil:ssg-sysctl_kernel_kptr_restrict_action:testaction:1 + + + + Sign kernel modules with SHA-512 + + ocil:ssg-kernel_config_module_sig_sha512_action:testaction:1 + + + + Disable the xguest_use_bluetooth SELinux Boolean + + ocil:ssg-sebool_xguest_use_bluetooth_action:testaction:1 + + + + Disable the polipo_session_bind_all_unreserved_ports SELinux Boolean + + ocil:ssg-sebool_polipo_session_bind_all_unreserved_ports_action:testaction:1 + + + + Configure the secure_mode_insmod SELinux Boolean + + ocil:ssg-sebool_secure_mode_insmod_action:testaction:1 + + + + Install the Host Intrusion Prevention System (HIPS) Module + + ocil:ssg-package_MFEhiplsm_installed_action:testaction:1 + + + + Record Successful Creation Attempts to Files - open O_TRUNC_WRITE + + ocil:ssg-audit_rules_successful_file_modification_open_o_trunc_write_action:testaction:1 + + + + Disable the Automounter + + ocil:ssg-service_autofs_disabled_action:testaction:1 + + + + Disable Geolocation in GNOME3 + + ocil:ssg-dconf_gnome_disable_geolocation_action:testaction:1 + + + + Disable the irc_use_any_tcp_ports SELinux Boolean + + ocil:ssg-sebool_irc_use_any_tcp_ports_action:testaction:1 + + + + Ensure that System Accounts Do Not Run a Shell Upon Login + + ocil:ssg-no_shelllogin_for_systemaccounts_action:testaction:1 + + + + Disable the cluster_use_execmem SELinux Boolean + + ocil:ssg-sebool_cluster_use_execmem_action:testaction:1 + + + + Disable kernel debugfs + + ocil:ssg-kernel_config_debug_fs_action:testaction:1 + + + + Ensure Users Cannot Change GNOME3 Screensaver Lock After Idle Period + + ocil:ssg-dconf_gnome_screensaver_lock_locked_action:testaction:1 + + + + Enable the fips_mode SELinux Boolean + + ocil:ssg-sebool_fips_mode_action:testaction:1 + + + + Disable the httpd_serve_cobbler_files SELinux Boolean + + ocil:ssg-sebool_httpd_serve_cobbler_files_action:testaction:1 + + + + Disable the LDT (local descriptor table) + + ocil:ssg-kernel_config_modify_ldt_syscall_action:testaction:1 + + + + Configure auditing of unsuccessful file accesses (ppc64le) + + ocil:ssg-audit_access_failed_ppc64le_action:testaction:1 + + + + Enable module signature verification + + ocil:ssg-kernel_config_module_sig_action:testaction:1 + + + + Configure auditing of unsuccessful file deletions + + ocil:ssg-audit_delete_failed_action:testaction:1 + + + + Disable the unprivuser_use_svirt SELinux Boolean + + ocil:ssg-sebool_unprivuser_use_svirt_action:testaction:1 + + + + Disable Kernel iwlmvm Module + + ocil:ssg-kernel_module_iwlmvm_disabled_action:testaction:1 + + + + Generate some entropy during boot and runtime + + ocil:ssg-kernel_config_gcc_plugin_latent_entropy_action:testaction:1 + + + + Enable the GNOME3 Screen Locking On Smartcard Removal + + ocil:ssg-dconf_gnome_lock_screen_on_smartcard_removal_action:testaction:1 + + + + Disable the daemons_dump_core SELinux Boolean + + ocil:ssg-sebool_daemons_dump_core_action:testaction:1 + + + + Verify All Account Password Hashes are Shadowed + + ocil:ssg-accounts_password_all_shadowed_action:testaction:1 + + + + Disable Samba + + ocil:ssg-service_smb_disabled_action:testaction:1 + + + + Set the UEFI Boot Loader Password + + ocil:ssg-grub2_uefi_password_action:testaction:1 + + + + Enable Use of Strict Mode Checking + + ocil:ssg-sshd_enable_strictmodes_action:testaction:1 + + + + Ensure PAM Enforces Password Requirements - Minimum Lowercase Characters + + ocil:ssg-accounts_password_pam_lcredit_action:testaction:1 + + + + Disable legacy (BSD) PTY support + + ocil:ssg-kernel_config_legacy_ptys_action:testaction:1 + + + + Disable the use of user namespaces + + ocil:ssg-sysctl_user_max_user_namespaces_action:testaction:1 + + + + Disable the zoneminder_run_sudo SELinux Boolean + + ocil:ssg-sebool_zoneminder_run_sudo_action:testaction:1 + + + + Record Successful Ownership Changes to Files - fchownat + + ocil:ssg-audit_rules_successful_file_modification_fchownat_action:testaction:1 + + + + Ensure auditd Collects File Deletion Events by User - renameat + + ocil:ssg-audit_rules_file_deletion_events_renameat_action:testaction:1 + + + + Configure Polyinstantiation of /tmp Directories + + ocil:ssg-accounts_polyinstantiated_tmp_action:testaction:1 + + + + Verify and Correct Ownership with RPM + + ocil:ssg-rpm_verify_ownership_action:testaction:1 + + + + Disable the httpd_can_sendmail SELinux Boolean + + ocil:ssg-sebool_httpd_can_sendmail_action:testaction:1 + + + + Disable Kernel Parameter for Accepting Source-Routed Packets on all IPv4 Interfaces + + ocil:ssg-sysctl_net_ipv4_conf_all_accept_source_route_action:testaction:1 + + + + Disable the httpd_mod_auth_pam SELinux Boolean + + ocil:ssg-sebool_httpd_mod_auth_pam_action:testaction:1 + + + + Record Unsuccessful Creation Attempts to Files - openat O_CREAT + + ocil:ssg-audit_rules_unsuccessful_file_modification_openat_o_creat_action:testaction:1 + + + + Uninstall tuned Package + + ocil:ssg-package_tuned_removed_action:testaction:1 + + + + Enable randomization of the page allocator in zIPL + + ocil:ssg-zipl_page_alloc_shuffle_argument_action:testaction:1 + + + + Install Intrusion Detection Software + + ocil:ssg-install_hids_action:testaction:1 + + + + Ensure debug-shell service is not enabled in zIPL + + ocil:ssg-zipl_systemd_debug-shell_argument_absent_action:testaction:1 + + + + Add nosuid Option to /dev/shm + + ocil:ssg-mount_option_dev_shm_nosuid_action:testaction:1 + + + + Uninstall bind Package + + ocil:ssg-package_bind_removed_action:testaction:1 + + + + Disable the mpd_use_cifs SELinux Boolean + + ocil:ssg-sebool_mpd_use_cifs_action:testaction:1 + + + + Detect stack corruption on calls to schedule() + + ocil:ssg-kernel_config_sched_stack_end_check_action:testaction:1 + + + + Extend Audit Backlog Limit for the Audit Daemon + + ocil:ssg-grub2_audit_backlog_limit_argument_action:testaction:1 + + + + Disable the logrotate_use_nfs SELinux Boolean + + ocil:ssg-sebool_logrotate_use_nfs_action:testaction:1 + + + + Configure basic parameters of Audit system + + ocil:ssg-audit_basic_configuration_action:testaction:1 + + + + Disable the gluster_anon_write SELinux Boolean + + ocil:ssg-sebool_gluster_anon_write_action:testaction:1 + + + + Disable the authlogin_radius SELinux Boolean + + ocil:ssg-sebool_authlogin_radius_action:testaction:1 + + + + Record Events that Modify User/Group Information via openat syscall - /etc/group + + ocil:ssg-audit_rules_etc_group_openat_action:testaction:1 + + + + Enable the login_console_enabled SELinux Boolean + + ocil:ssg-sebool_login_console_enabled_action:testaction:1 + + + + Ensure Chrony is only configured with the server directive + + ocil:ssg-chronyd_server_directive_action:testaction:1 + + + + Configure auditd admin_space_left Action on Low Disk Space + + ocil:ssg-auditd_data_retention_admin_space_left_action_action:testaction:1 + + + + Enable Randomized Layout of Virtual Address Space + + ocil:ssg-sysctl_kernel_randomize_va_space_action:testaction:1 + + + + Configure auditing of successful file modifications + + ocil:ssg-audit_modify_success_action:testaction:1 + + + + Require Credential Prompting for Remote Access in GNOME3 + + ocil:ssg-dconf_gnome_remote_access_credential_prompt_action:testaction:1 + + + + Explicit arguments in sudo specifications + + ocil:ssg-sudoers_explicit_command_args_action:testaction:1 + + + + Disable TIPC Support + + ocil:ssg-kernel_module_tipc_disabled_action:testaction:1 + + + + Disable the samba_share_fusefs SELinux Boolean + + ocil:ssg-sebool_samba_share_fusefs_action:testaction:1 + + + + User Initialization Files Must Not Run World-Writable Programs + + ocil:ssg-accounts_user_dot_no_world_writable_programs_action:testaction:1 + + + + Ensure auditd Rules For Unauthorized Attempts To openat Are Ordered Correctly + + ocil:ssg-audit_rules_unsuccessful_file_modification_openat_rule_order_action:testaction:1 + + + + Record Events that Modify User/Group Information - /etc/shadow + + ocil:ssg-audit_rules_usergroup_modification_shadow_action:testaction:1 + + + + Ensure Only Users Logged In To Real tty Can Execute Sudo - sudo use_pty + + ocil:ssg-sudo_add_use_pty_action:testaction:1 + + + + Configure audispd's Plugin disk_full_action When Disk Is Full + + ocil:ssg-auditd_audispd_disk_full_action_action:testaction:1 + + + + Verify Owner on SSH Server config file + + ocil:ssg-file_owner_sshd_config_action:testaction:1 + + + + Add nosuid Option to /tmp + + ocil:ssg-mount_option_tmp_nosuid_action:testaction:1 + + + + Uninstall vsftpd Package + + ocil:ssg-package_vsftpd_removed_action:testaction:1 + + + + Enable page allocator poisoning in zIPL + + ocil:ssg-zipl_page_poison_argument_action:testaction:1 + + + + Ensure gpgcheck Enabled for Local Packages + + ocil:ssg-ensure_gpgcheck_local_packages_action:testaction:1 + + + + Ensure rsyncd service is diabled + + ocil:ssg-service_rsyncd_disabled_action:testaction:1 + + + + Add noexec Option to /home + + ocil:ssg-mount_option_home_noexec_action:testaction:1 + + + + Configure auditing of unsuccessful file deletions (ppc64le) + + ocil:ssg-audit_delete_failed_ppc64le_action:testaction:1 + + + + Set Password Minimum Length in login.defs + + ocil:ssg-accounts_password_minlen_login_defs_action:testaction:1 + + + + Verify Permissions on cron.daily + + ocil:ssg-file_permissions_cron_daily_action:testaction:1 + + + + Remove the kernel mapping in user mode + + ocil:ssg-kernel_config_page_table_isolation_action:testaction:1 + + + + Disable vsyscall emulation + + ocil:ssg-kernel_config_legacy_vsyscall_emulate_action:testaction:1 + + + + Enable PAM + + ocil:ssg-sshd_enable_pam_action:testaction:1 + + + + Authorize USB hubs in USBGuard daemon + + ocil:ssg-usbguard_allow_hub_action:testaction:1 + + + + Enable the NTP Daemon + + ocil:ssg-service_ntpd_enabled_action:testaction:1 + + + + Configure auditing of unsuccessful ownership changes + + ocil:ssg-audit_owner_change_failed_action:testaction:1 + + + + Disable the use_lpd_server SELinux Boolean + + ocil:ssg-sebool_use_lpd_server_action:testaction:1 + + + + Configure the root Account for Failed Password Attempts + + ocil:ssg-accounts_passwords_pam_faillock_deny_root_action:testaction:1 + + + + Disable the conman_can_network SELinux Boolean + + ocil:ssg-sebool_conman_can_network_action:testaction:1 + + + + Record Successful Permission Changes to Files - removexattr + + ocil:ssg-audit_rules_successful_file_modification_removexattr_action:testaction:1 + + + + Verify Group Who Owns Backup group File + + ocil:ssg-file_groupowner_backup_etc_group_action:testaction:1 + + + + Record Events that Modify User/Group Information via open_by_handle_at syscall - /etc/passwd + + ocil:ssg-audit_rules_etc_passwd_open_by_handle_at_action:testaction:1 + + + + Disable IPv6 Addressing on All IPv6 Interfaces + + ocil:ssg-sysctl_net_ipv6_conf_all_disable_ipv6_action:testaction:1 + + + + Record Attempts to Alter Time Through clock_settime + + ocil:ssg-audit_rules_time_clock_settime_action:testaction:1 + + + + Configure Accepting Prefix Information in Router Advertisements on All IPv6 Interfaces By Default + + ocil:ssg-sysctl_net_ipv6_conf_default_accept_ra_pinfo_action:testaction:1 + + + + Ensure PAM password complexity module is enabled in system-auth + + ocil:ssg-accounts_password_pam_pwquality_system_auth_action:testaction:1 + + + + Install audispd-plugins Package + + ocil:ssg-package_audispd-plugins_installed_action:testaction:1 + + + + Verify User Who Owns shadow File + + ocil:ssg-file_owner_etc_shadow_action:testaction:1 + + + + Disable the httpd_can_network_memcache SELinux Boolean + + ocil:ssg-sebool_httpd_can_network_memcache_action:testaction:1 + + + + Disable the use_ecryptfs_home_dirs SELinux Boolean + + ocil:ssg-sebool_use_ecryptfs_home_dirs_action:testaction:1 + + + + Verify Owner on cron.daily + + ocil:ssg-file_owner_cron_daily_action:testaction:1 + + + + Configure auditd Disk Error Action on Disk Error + + ocil:ssg-auditd_data_disk_error_action_action:testaction:1 + + + + System Audit Logs Must Be Owned By Root + + ocil:ssg-file_ownership_var_log_audit_action:testaction:1 + + + + Disable Kernel Parameter for Sending ICMP Redirects on all IPv4 Interfaces + + ocil:ssg-sysctl_net_ipv4_conf_all_send_redirects_action:testaction:1 + + + + Harden memory copies between kernel and userspace + + ocil:ssg-kernel_config_hardened_usercopy_action:testaction:1 + + + + Configure auditing of unsuccessful ownership changes (ppc64le) + + ocil:ssg-audit_owner_change_failed_ppc64le_action:testaction:1 + + + + Configure GNOME3 DConf User Profile + + ocil:ssg-enable_dconf_user_profile_action:testaction:1 + + + + Disable Mounting of cramfs + + ocil:ssg-kernel_module_cramfs_disabled_action:testaction:1 + + + + Ensure PAM Enforces Password Requirements - Minimum Different Characters + + ocil:ssg-accounts_password_pam_difok_action:testaction:1 + + + + Disable the virt_use_xserver SELinux Boolean + + ocil:ssg-sebool_virt_use_xserver_action:testaction:1 + + + + Enable Postfix Service + + ocil:ssg-service_postfix_enabled_action:testaction:1 + + + + Enable the postgresql_selinux_unconfined_dbadm SELinux Boolean + + ocil:ssg-sebool_postgresql_selinux_unconfined_dbadm_action:testaction:1 + + + + Enable the gssd_read_tmp SELinux Boolean + + ocil:ssg-sebool_gssd_read_tmp_action:testaction:1 + + + + Enable FIPS Mode + + ocil:ssg-enable_fips_mode_action:testaction:1 + + + + Ensure PAM Enforces Password Requirements - Minimum Different Categories + + ocil:ssg-accounts_password_pam_minclass_action:testaction:1 + + + + Record Events that Modify User/Group Information + + ocil:ssg-audit_rules_usergroup_modification_action:testaction:1 + + + + Disable network management of chrony daemon + + ocil:ssg-chronyd_no_chronyc_network_action:testaction:1 + + + + Ensure All World-Writable Directories Are Owned by root user + + ocil:ssg-dir_perms_world_writable_root_owned_action:testaction:1 + + + + Enable the spamd_enable_home_dirs SELinux Boolean + + ocil:ssg-sebool_spamd_enable_home_dirs_action:testaction:1 + + + + Set Password Maximum Consecutive Repeating Characters + + ocil:ssg-accounts_password_pam_maxrepeat_action:testaction:1 + + + + Avoid speculative indirect branches in kernel + + ocil:ssg-kernel_config_retpoline_action:testaction:1 + + + + Disable Kernel Parameter for Sending ICMP Redirects on all IPv4 Interfaces by Default + + ocil:ssg-sysctl_net_ipv4_conf_default_send_redirects_action:testaction:1 + + + + Disable the exim_can_connect_db SELinux Boolean + + ocil:ssg-sebool_exim_can_connect_db_action:testaction:1 + + + + Make sure that the dconf databases are up-to-date with regards to respective keyfiles + + ocil:ssg-dconf_db_up_to_date_action:testaction:1 + + + + Disable the dbadm_read_user_files SELinux Boolean + + ocil:ssg-sebool_dbadm_read_user_files_action:testaction:1 + + + + Disable the webadm_manage_user_files SELinux Boolean + + ocil:ssg-sebool_webadm_manage_user_files_action:testaction:1 + + + + Disable Access to Network bpf() Syscall From Unprivileged Processes + + ocil:ssg-sysctl_kernel_unprivileged_bpf_disabled_action:testaction:1 + + + + Disable the nagios_run_sudo SELinux Boolean + + ocil:ssg-sebool_nagios_run_sudo_action:testaction:1 + + + + Configure SSSD to Expire Offline Credentials + + ocil:ssg-sssd_offline_cred_expiration_action:testaction:1 + + + + Enable Kernel Paremeter to Log Martian Packets on all IPv4 Interfaces by Default + + ocil:ssg-sysctl_net_ipv4_conf_default_log_martians_action:testaction:1 + + + + Emulate Privileged Access Never (PAN) + + ocil:ssg-kernel_config_arm64_sw_ttbr0_pan_action:testaction:1 + + + + Configure audit according to OSPP requirements + + ocil:ssg-audit_rules_for_ospp_action:testaction:1 + + + + Modify the System Login Banner + + ocil:ssg-banner_etc_issue_action:testaction:1 + + + + Disable the httpd_execmem SELinux Boolean + + ocil:ssg-sebool_httpd_execmem_action:testaction:1 + + + + Disable the polipo_session_users SELinux Boolean + + ocil:ssg-sebool_polipo_session_users_action:testaction:1 + + + + Require Client SMB Packet Signing, if using mount.cifs + + ocil:ssg-mount_option_smb_client_signing_action:testaction:1 + + + + Enable the GNOME3 Login Smartcard Authentication + + ocil:ssg-dconf_gnome_enable_smartcard_auth_action:testaction:1 + + + + Enable use of Berkeley Packet Filter with seccomp + + ocil:ssg-kernel_config_seccomp_filter_action:testaction:1 + + + + Record Unsuccessful Access Attempts to Files - ftruncate + + ocil:ssg-audit_rules_unsuccessful_file_modification_ftruncate_action:testaction:1 + + + + Record Unsuccessful Permission Changes to Files - setxattr + + ocil:ssg-audit_rules_unsuccessful_file_modification_setxattr_action:testaction:1 + + + + Remove the Kerberos Server Package + + ocil:ssg-package_krb5-server_removed_action:testaction:1 + + + + Enable checks on credential management + + ocil:ssg-kernel_config_debug_credentials_action:testaction:1 + + + + Record Events that Modify the System's Discretionary Access Controls - setxattr + + ocil:ssg-audit_rules_dac_modification_setxattr_action:testaction:1 + + + + Disable the sanlock_use_samba SELinux Boolean + + ocil:ssg-sebool_sanlock_use_samba_action:testaction:1 + + + + Ensure auditd Collects Information on the Use of Privileged Commands - at + + ocil:ssg-audit_rules_privileged_commands_at_action:testaction:1 + + + + Disable the mcelog_server SELinux Boolean + + ocil:ssg-sebool_mcelog_server_action:testaction:1 + + + + Ensure rsyslog is Installed + + ocil:ssg-package_rsyslog_installed_action:testaction:1 + + + + Disable the xdm_bind_vnc_tcp_port SELinux Boolean + + ocil:ssg-sebool_xdm_bind_vnc_tcp_port_action:testaction:1 + + + + Disable the entropyd_use_audio SELinux Boolean + + ocil:ssg-sebool_entropyd_use_audio_action:testaction:1 + + + + Disable the minidlna_read_generic_user_content SELinux Boolean + + ocil:ssg-sebool_minidlna_read_generic_user_content_action:testaction:1 + + + + Configure Accepting Router Preference in Router Advertisements on All IPv6 Interfaces By Default + + ocil:ssg-sysctl_net_ipv6_conf_default_accept_ra_rtr_pref_action:testaction:1 + + + + Uninstall geolite2-country Package + + ocil:ssg-package_geolite2-country_removed_action:testaction:1 + + + + Ensure auditd Collects Information on the Use of Privileged Commands - poweroff + + ocil:ssg-audit_privileged_commands_poweroff_action:testaction:1 + + + + Set Default iptables Policy for Incoming Packets + + ocil:ssg-set_iptables_default_rule_action:testaction:1 + + + + Remove User Host-Based Authentication Files + + ocil:ssg-no_user_host_based_files_action:testaction:1 + + + + Disable the git_system_use_cifs SELinux Boolean + + ocil:ssg-sebool_git_system_use_cifs_action:testaction:1 + + + + Install AIDE + + ocil:ssg-package_aide_installed_action:testaction:1 + + + + Disable the httpd_tmp_exec SELinux Boolean + + ocil:ssg-sebool_httpd_tmp_exec_action:testaction:1 + + + + Install usbguard Package + + ocil:ssg-package_usbguard_installed_action:testaction:1 + + + + Disable the ftpd_use_cifs SELinux Boolean + + ocil:ssg-sebool_ftpd_use_cifs_action:testaction:1 + + + + Verify Permissions on Backup group File + + ocil:ssg-file_permissions_backup_etc_group_action:testaction:1 + + + + Install the pcsc-lite package + + ocil:ssg-package_pcsc-lite_installed_action:testaction:1 + + + + Ensure McAfee Endpoint Security for Linux (ENSL) is running + + ocil:ssg-agent_mfetpd_running_action:testaction:1 + + + + Warn on W+X mappings found at boot + + ocil:ssg-kernel_config_debug_wx_action:testaction:1 + + + + Disable the httpd_use_nfs SELinux Boolean + + ocil:ssg-sebool_httpd_use_nfs_action:testaction:1 + + + + Configure Polyinstantiation of /var/tmp Directories + + ocil:ssg-accounts_polyinstantiated_var_tmp_action:testaction:1 + + + + Disable GNOME3 Automounting + + ocil:ssg-dconf_gnome_disable_automount_action:testaction:1 + + + + Build and Test AIDE Database + + ocil:ssg-aide_build_database_action:testaction:1 + + + + Ensure auditd Collects Information on the Use of Privileged Commands - postdrop + + ocil:ssg-audit_rules_privileged_commands_postdrop_action:testaction:1 + + + + Disable the authlogin_yubikey SELinux Boolean + + ocil:ssg-sebool_authlogin_yubikey_action:testaction:1 + + + + Disable Kernel cfg80211 Module + + ocil:ssg-kernel_module_cfg80211_disabled_action:testaction:1 + + + + Enable SLUB/SLAB allocator poisoning in zIPL + + ocil:ssg-zipl_slub_debug_argument_action:testaction:1 + + + + User a virtually-mapped stack + + ocil:ssg-kernel_config_vmap_stack_action:testaction:1 + + + + Disable the httpd_can_check_spam SELinux Boolean + + ocil:ssg-sebool_httpd_can_check_spam_action:testaction:1 + + + + Disable the httpd_can_network_relay SELinux Boolean + + ocil:ssg-sebool_httpd_can_network_relay_action:testaction:1 + + + + Verify Any Configured IPSec Tunnel Connections + + ocil:ssg-libreswan_approved_tunnels_action:testaction:1 + + + + Disable the gluster_export_all_ro SELinux Boolean + + ocil:ssg-sebool_gluster_export_all_ro_action:testaction:1 + + + + Ensure Logrotate Runs Periodically + + ocil:ssg-ensure_logrotate_activated_action:testaction:1 + + + + Disable the dbadm_manage_user_files SELinux Boolean + + ocil:ssg-sebool_dbadm_manage_user_files_action:testaction:1 + + + + Disable the openvpn_run_unconfined SELinux Boolean + + ocil:ssg-sebool_openvpn_run_unconfined_action:testaction:1 + + + + Disable Ctrl-Alt-Del Reboot Activation + + ocil:ssg-disable_ctrlaltdel_reboot_action:testaction:1 + + + + Audit Configuration Files Must Be Owned By Root + + ocil:ssg-file_ownership_audit_configuration_action:testaction:1 + + + + Set Interval For Counting Failed Password Attempts + + ocil:ssg-accounts_passwords_pam_faillock_interval_action:testaction:1 + + + + Record Events that Modify User/Group Information via open_by_handle_at syscall - /etc/group + + ocil:ssg-audit_rules_etc_group_open_by_handle_at_action:testaction:1 + + + + Enable Kernel Parameter to Enforce DAC on Symlinks + + ocil:ssg-sysctl_fs_protected_symlinks_action:testaction:1 + + + + Disable vsyscalls + + ocil:ssg-grub2_vsyscall_argument_action:testaction:1 + + + + Disable the saslauthd_read_shadow SELinux Boolean + + ocil:ssg-sebool_saslauthd_read_shadow_action:testaction:1 + + + + Configure auditing of unsuccessful file modifications + + ocil:ssg-audit_modify_failed_action:testaction:1 + + + + Disable Cockpit Management Server + + ocil:ssg-service_cockpit_disabled_action:testaction:1 + + + + Disable the sge_domain_can_network_connect SELinux Boolean + + ocil:ssg-sebool_sge_domain_can_network_connect_action:testaction:1 + + + + Install libselinux Package + + ocil:ssg-package_libselinux_installed_action:testaction:1 + + + + Verify File Hashes with RPM + + ocil:ssg-rpm_verify_hashes_action:testaction:1 + + + + Configure auditing of unsuccessful file deletions (AArch64) + + ocil:ssg-audit_delete_failed_aarch64_action:testaction:1 + + + + Disable the virt_use_execmem SELinux Boolean + + ocil:ssg-sebool_virt_use_execmem_action:testaction:1 + + + + Configure auditing of unsuccessful file accesses (AArch64) + + ocil:ssg-audit_access_failed_aarch64_action:testaction:1 + + + + Record Unsuccessful Delete Attempts to Files - rename + + ocil:ssg-audit_rules_unsuccessful_file_modification_rename_action:testaction:1 + + + + Record Unsuccessful Access Attempts to Files - open_by_handle_at + + ocil:ssg-audit_rules_unsuccessful_file_modification_open_by_handle_at_action:testaction:1 + + + + Remove the GDM Package Group + + ocil:ssg-package_gdm_removed_action:testaction:1 + + + + Disable support for /proc/kkcore + + ocil:ssg-kernel_config_proc_kcore_action:testaction:1 + + + + Record Unsuccessful Creation Attempts to Files - open_by_handle_at O_CREAT + + ocil:ssg-audit_rules_unsuccessful_file_modification_open_by_handle_at_o_creat_action:testaction:1 + + + + Disable ntpdate Service (ntpdate) + + ocil:ssg-service_ntpdate_disabled_action:testaction:1 + + + + Record Attempts to Alter Logon and Logout Events - tallylog + + ocil:ssg-audit_rules_login_events_tallylog_action:testaction:1 + + + + Configure Maximum Number of Autoconfigured Addresses on All IPv6 Interfaces + + ocil:ssg-sysctl_net_ipv6_conf_all_max_addresses_action:testaction:1 + + + + Restrict Serial Port Root Logins + + ocil:ssg-restrict_serial_port_logins_action:testaction:1 + + + + Record Events that Modify User/Group Information via open syscall - /etc/group + + ocil:ssg-audit_rules_etc_group_open_action:testaction:1 + + + + Disable KDump Kernel Crash Analyzer (kdump) + + ocil:ssg-service_kdump_disabled_action:testaction:1 + + + + Disable telnet Service + + ocil:ssg-service_telnet_disabled_action:testaction:1 + + + + Uninstall rsh Package + + ocil:ssg-package_rsh_removed_action:testaction:1 + + + + Add nodev Option to /home + + ocil:ssg-mount_option_home_nodev_action:testaction:1 + + + + Disable kernel support for MISC binaries + + ocil:ssg-kernel_config_binfmt_misc_action:testaction:1 + + + + Ensure auditd Collects Information on the Use of Privileged Commands - usermod + + ocil:ssg-audit_rules_privileged_commands_usermod_action:testaction:1 + + + + Install OpenSSH client software + + ocil:ssg-package_openssh-clients_installed_action:testaction:1 + + + + Disable the glance_api_can_network SELinux Boolean + + ocil:ssg-sebool_glance_api_can_network_action:testaction:1 + + + + Enable the USBGuard Service + + ocil:ssg-service_usbguard_enabled_action:testaction:1 + + + + Disable Kernel mac80211 Module + + ocil:ssg-kernel_module_mac80211_disabled_action:testaction:1 + + + + Firewalld Must Employ a Deny-all, Allow-by-exception Policy for Allowing Connections to Other Systems + + ocil:ssg-configured_firewalld_default_deny_action:testaction:1 + + + + Disable Host-Based Authentication + + ocil:ssg-disable_host_auth_action:testaction:1 + + + + Disable compatibility with brk() + + ocil:ssg-kernel_config_compat_brk_action:testaction:1 + + + + Disable the lsmd_plugin_connect_any SELinux Boolean + + ocil:ssg-sebool_lsmd_plugin_connect_any_action:testaction:1 + + + + Set SSH Daemon LogLevel to VERBOSE + + ocil:ssg-sshd_set_loglevel_verbose_action:testaction:1 + + + + Ensure gnutls-utils is installed + + ocil:ssg-package_gnutls-utils_installed_action:testaction:1 + + + + Configure auditd max_log_file_action Upon Reaching Maximum Log Size + + ocil:ssg-auditd_data_retention_max_log_file_action_action:testaction:1 + + + + Configure auditing of successful ownership changes + + ocil:ssg-audit_owner_change_success_action:testaction:1 + + + + Ensure journald is configured to send logs to rsyslog + + ocil:ssg-journald_forward_to_syslog_action:testaction:1 + + + + Disable the ftpd_full_access SELinux Boolean + + ocil:ssg-sebool_ftpd_full_access_action:testaction:1 + + + + Ensure Users Re-Authenticate for Privilege Escalation - sudo NOPASSWD + + ocil:ssg-sudo_remove_nopasswd_action:testaction:1 + + + + Set Boot Loader Password in grub2 + + ocil:ssg-grub2_password_action:testaction:1 + + + + Ensure PAM Enforces Password Requirements - Minimum Uppercase Characters + + ocil:ssg-accounts_password_pam_ucredit_action:testaction:1 + + + + Disable the use_fusefs_home_dirs SELinux Boolean + + ocil:ssg-sebool_use_fusefs_home_dirs_action:testaction:1 + + + + Add nosuid Option to /srv + + ocil:ssg-mount_option_srv_nosuid_action:testaction:1 + + + + Configure Backups of User Data + + ocil:ssg-configure_user_data_backups_action:testaction:1 + + + + Record Successful Access Attempts to Files - ftruncate + + ocil:ssg-audit_rules_successful_file_modification_ftruncate_action:testaction:1 + + + + Disable the cobbler_use_nfs SELinux Boolean + + ocil:ssg-sebool_cobbler_use_nfs_action:testaction:1 + + + + Configure kernel to zero out memory before allocation in zIPL + + ocil:ssg-zipl_init_on_alloc_argument_action:testaction:1 + + + + Verify User Who Owns /var/log/messages File + + ocil:ssg-file_owner_var_log_messages_action:testaction:1 + + + + Record Successful Creation Attempts to Files - openat O_CREAT + + ocil:ssg-audit_rules_successful_file_modification_openat_o_creat_action:testaction:1 + + + + Disable the cluster_can_network_connect SELinux Boolean + + ocil:ssg-sebool_cluster_can_network_connect_action:testaction:1 + + + + Enable Smartcards in SSSD + + ocil:ssg-sssd_enable_smartcards_action:testaction:1 + + + + Disable the collectd_tcp_network_connect SELinux Boolean + + ocil:ssg-sebool_collectd_tcp_network_connect_action:testaction:1 + + + + Ensure Rsyslog Encrypts Off-Loaded Audit Records + + ocil:ssg-rsyslog_encrypt_offload_actionsendstreamdrivermode_action:testaction:1 + + + + Audit Tools Must Be Owned by Root + + ocil:ssg-file_audit_tools_ownership_action:testaction:1 + + + + Enable Auditing to Start Prior to the Audit Daemon in zIPL + + ocil:ssg-zipl_audit_argument_action:testaction:1 + + + + Verify that Shared Library Directories Have Root Ownership + + ocil:ssg-dir_ownership_library_dirs_action:testaction:1 + + + + Disable debug-shell SystemD Service + + ocil:ssg-service_debug-shell_disabled_action:testaction:1 + + + + Configure the polyinstantiation_enabled SELinux Boolean + + ocil:ssg-sebool_polyinstantiation_enabled_action:testaction:1 + + + + Ensure auditd Collects Information on the Use of Privileged Commands - unix_update + + ocil:ssg-audit_rules_privileged_commands_unix_update_action:testaction:1 + + + + Restrict Access to Kernel Message Buffer + + ocil:ssg-sysctl_kernel_dmesg_restrict_action:testaction:1 + + + + Configure ARP filtering for All IPv4 Interfaces + + ocil:ssg-sysctl_net_ipv4_conf_all_arp_filter_action:testaction:1 + + + + Only the VDSM User Can Use sudo NOPASSWD + + ocil:ssg-sudo_vdsm_nopasswd_action:testaction:1 + + + + Disable the ssh_chroot_rw_homedirs SELinux Boolean + + ocil:ssg-sebool_ssh_chroot_rw_homedirs_action:testaction:1 + + + + Limit Users' SSH Access + + ocil:ssg-sshd_limit_user_access_action:testaction:1 + + + + Record Unsuccessful Access Attempts to Files - truncate + + ocil:ssg-audit_rules_unsuccessful_file_modification_truncate_action:testaction:1 + + + + Configure tmux to lock session after inactivity + + ocil:ssg-configure_tmux_lock_after_time_action:testaction:1 + + + + Ensure cron Is Logging To Rsyslog + + ocil:ssg-rsyslog_cron_logging_action:testaction:1 + + + + Disable the exim_manage_user_files SELinux Boolean + + ocil:ssg-sebool_exim_manage_user_files_action:testaction:1 + + + + Install the tmux Package + + ocil:ssg-package_tmux_installed_action:testaction:1 + + + + Disable the virt_transition_userdomain SELinux Boolean + + ocil:ssg-sebool_virt_transition_userdomain_action:testaction:1 + + + + Disable the polipo_use_nfs SELinux Boolean + + ocil:ssg-sebool_polipo_use_nfs_action:testaction:1 + + + + Disable the rsync_client SELinux Boolean + + ocil:ssg-sebool_rsync_client_action:testaction:1 + + + + Enable the mount_anyfile SELinux Boolean + + ocil:ssg-sebool_mount_anyfile_action:testaction:1 + + + + Disable the httpd_dbus_avahi SELinux Boolean + + ocil:ssg-sebool_httpd_dbus_avahi_action:testaction:1 + + + + Ensure auditd Collects Information on the Use of Privileged Commands - unix_chkpwd + + ocil:ssg-audit_rules_privileged_commands_unix_chkpwd_action:testaction:1 + + + + Use Kerberos Security on All Exports + + ocil:ssg-use_kerberos_security_all_exports_action:testaction:1 + + + + Disable the daemons_use_tcp_wrapper SELinux Boolean + + ocil:ssg-sebool_daemons_use_tcp_wrapper_action:testaction:1 + + + + Configure GnuTLS library to use DoD-approved TLS Encryption + + ocil:ssg-configure_gnutls_tls_crypto_policy_action:testaction:1 + + + + Ensure /var/tmp Located On Separate Partition + + ocil:ssg-partition_for_var_tmp_action:testaction:1 + + + + Record Unsuccessful Permission Changes to Files - removexattr + + ocil:ssg-audit_rules_unsuccessful_file_modification_removexattr_action:testaction:1 + + + + Verify ip6tables Enabled if Using IPv6 + + ocil:ssg-service_ip6tables_enabled_action:testaction:1 + + + + Disable the virt_use_usb SELinux Boolean + + ocil:ssg-sebool_virt_use_usb_action:testaction:1 + + + + Record Attempts to Alter Logon and Logout Events - faillock + + ocil:ssg-audit_rules_login_events_faillock_action:testaction:1 + + + + Disable the rsync_export_all_ro SELinux Boolean + + ocil:ssg-sebool_rsync_export_all_ro_action:testaction:1 + + + + Enable the logging_syslogd_use_tty SELinux Boolean + + ocil:ssg-sebool_logging_syslogd_use_tty_action:testaction:1 + + + + Enable GNOME3 Screensaver Idle Activation + + ocil:ssg-dconf_gnome_screensaver_idle_activation_enabled_action:testaction:1 + + + + Enable the kerberos_enabled SELinux Boolean + + ocil:ssg-sebool_kerberos_enabled_action:testaction:1 + + + + Disable the mcelog_foreground SELinux Boolean + + ocil:ssg-sebool_mcelog_foreground_action:testaction:1 + + + + Audit Tools Must Be Group-owned by Root + + ocil:ssg-file_audit_tools_group_ownership_action:testaction:1 + + + + Ensure auditd Collects Information on the Use of Privileged Commands - userhelper + + ocil:ssg-audit_rules_privileged_commands_userhelper_action:testaction:1 + + + + Ensure /home Located On Separate Partition + + ocil:ssg-partition_for_home_action:testaction:1 + + + + Certificate status checking in SSSD + + ocil:ssg-sssd_certificate_verification_action:testaction:1 + + + + Set kernel parameter 'crypto.fips_enabled' to 1 + + ocil:ssg-sysctl_crypto_fips_enabled_action:testaction:1 + + + + Set Password Maximum Age + + ocil:ssg-accounts_maximum_age_login_defs_action:testaction:1 + + + + Remove the OpenSSH Server Package + + ocil:ssg-package_openssh-server_removed_action:testaction:1 + + + + Add nosuid Option to /home + + ocil:ssg-mount_option_home_nosuid_action:testaction:1 + + + + Disable the ssh_keysign SELinux Boolean + + ocil:ssg-sebool_ssh_keysign_action:testaction:1 + + + + Disable the mozilla_plugin_can_network_connect SELinux Boolean + + ocil:ssg-sebool_mozilla_plugin_can_network_connect_action:testaction:1 + + + + Enable authselect + + ocil:ssg-enable_authselect_action:testaction:1 + + + + Disable Kernel iwlwifi Module + + ocil:ssg-kernel_module_iwlwifi_disabled_action:testaction:1 + + + + Support session locking with tmux + + ocil:ssg-configure_bashrc_exec_tmux_action:testaction:1 + + + + Configure auditd Number of Logs Retained + + ocil:ssg-auditd_data_retention_num_logs_action:testaction:1 + + + + Configure Libreswan to use System Crypto Policy + + ocil:ssg-configure_libreswan_crypto_policy_action:testaction:1 + + + + Authorize Human Interface Devices in USBGuard daemon + + ocil:ssg-usbguard_allow_hid_action:testaction:1 + + + + Set number of records to cause an explicit flush to audit logs + + ocil:ssg-auditd_freq_action:testaction:1 + + + + Disable the mplayer_execstack SELinux Boolean + + ocil:ssg-sebool_mplayer_execstack_action:testaction:1 + + + + Disable the fcron_crond SELinux Boolean + + ocil:ssg-sebool_fcron_crond_action:testaction:1 + + + + Disable the exim_read_user_files SELinux Boolean + + ocil:ssg-sebool_exim_read_user_files_action:testaction:1 + + + + Set Existing Passwords Maximum Age + + ocil:ssg-accounts_password_set_max_life_existing_action:testaction:1 + + + + Record Unsuccessful Ownership Changes to Files - fchown + + ocil:ssg-audit_rules_unsuccessful_file_modification_fchown_action:testaction:1 + + + + Configure maximum number of process identifiers + + ocil:ssg-sysctl_kernel_pid_max_action:testaction:1 + + + + Verify Group Who Owns SSH Server config file + + ocil:ssg-file_groupowner_sshd_config_action:testaction:1 + + + + Add nosuid Option to /boot + + ocil:ssg-mount_option_boot_nosuid_action:testaction:1 + + + + Lock Accounts After Failed Password Attempts + + ocil:ssg-accounts_passwords_pam_faillock_deny_action:testaction:1 + + + + Uninstall dovecot Package + + ocil:ssg-package_dovecot_removed_action:testaction:1 + + + + Disable Modprobe Loading of USB Storage Driver + + ocil:ssg-kernel_module_usb-storage_disabled_action:testaction:1 + + + + Verify Group Who Owns cron.hourly + + ocil:ssg-file_groupowner_cron_hourly_action:testaction:1 + + + + Disable the virt_sandbox_use_netlink SELinux Boolean + + ocil:ssg-sebool_virt_sandbox_use_netlink_action:testaction:1 + + + + Disable the ftpd_anon_write SELinux Boolean + + ocil:ssg-sebool_ftpd_anon_write_action:testaction:1 + + + + Ensure Only Users Logged In To Real tty Can Execute Sudo - sudo requiretty + + ocil:ssg-sudo_add_requiretty_action:testaction:1 + + + + Verify Permissions on Backup gshadow File + + ocil:ssg-file_permissions_backup_etc_gshadow_action:testaction:1 + + + + Disable chrony daemon from acting as server + + ocil:ssg-chronyd_client_only_action:testaction:1 + + + + Perform full reference count validation + + ocil:ssg-kernel_config_refcount_full_action:testaction:1 + + + + Disable loading and unloading of kernel modules + + ocil:ssg-sysctl_kernel_modules_disabled_action:testaction:1 + + + + Configure System Cryptography Policy + + ocil:ssg-configure_crypto_policy_action:testaction:1 + + + + Modify the System Message of the Day Banner + + ocil:ssg-banner_etc_motd_action:testaction:1 + + + + System Audit Logs Must Have Mode 0640 or Less Permissive + + ocil:ssg-file_permissions_var_log_audit_action:testaction:1 + + + + Disable the mozilla_plugin_use_gps SELinux Boolean + + ocil:ssg-sebool_mozilla_plugin_use_gps_action:testaction:1 + + + + Uninstall nfs-utils Package + + ocil:ssg-package_nfs-utils_removed_action:testaction:1 + + + + Enable the nfs_export_all_ro SELinux Boolean + + ocil:ssg-sebool_nfs_export_all_ro_action:testaction:1 + + + + Ensure zIPL bootmap is up to date + + ocil:ssg-zipl_bootmap_is_up_to_date_action:testaction:1 + + + + Install McAfee Endpoint Security for Linux (ENSL) + + ocil:ssg-package_mcafeetp_installed_action:testaction:1 + + + + Ensure syslog-ng is Installed + + ocil:ssg-package_syslogng_installed_action:testaction:1 + + + + Disable the glance_use_fusefs SELinux Boolean + + ocil:ssg-sebool_glance_use_fusefs_action:testaction:1 + + + + Ensure SNMP Read Write is disabled + + ocil:ssg-snmpd_no_rwusers_action:testaction:1 + + + + Kernel panic oops + + ocil:ssg-kernel_config_panic_on_oops_action:testaction:1 + + + + Implement Blank Screensaver + + ocil:ssg-dconf_gnome_screensaver_mode_blank_action:testaction:1 + + + + Verify Permissions on cron.d + + ocil:ssg-file_permissions_cron_d_action:testaction:1 + + + + A remote time server for Chrony is configured + + ocil:ssg-chronyd_specify_remote_server_action:testaction:1 + + + + Configure auditd Max Log File Size + + ocil:ssg-auditd_data_retention_max_log_file_action:testaction:1 + + + + Configure auditing of unsuccessful file creations (AArch64) + + ocil:ssg-audit_create_failed_aarch64_action:testaction:1 + + + + Configure auditing of successful file creations (ppc64le) + + ocil:ssg-audit_create_success_ppc64le_action:testaction:1 + + + + Ensure Home Directories are Created for New Users + + ocil:ssg-accounts_have_homedir_login_defs_action:testaction:1 + + + + Disable Ctrl-Alt-Del Burst Action + + ocil:ssg-disable_ctrlaltdel_burstaction_action:testaction:1 + + + + Disable the httpd_manage_ipa SELinux Boolean + + ocil:ssg-sebool_httpd_manage_ipa_action:testaction:1 + + + + Verify Owner on cron.hourly + + ocil:ssg-file_owner_cron_hourly_action:testaction:1 + + + + Disable the boinc_execmem SELinux Boolean + + ocil:ssg-sebool_boinc_execmem_action:testaction:1 + + + + Enable Kernel Parameter to Enforce DAC on Regular files + + ocil:ssg-sysctl_fs_protected_regular_action:testaction:1 + + + + Ensure auditd Rules For Unauthorized Attempts To open Are Ordered Correctly + + ocil:ssg-audit_rules_unsuccessful_file_modification_open_rule_order_action:testaction:1 + + + + Record Successful Creation Attempts to Files - open_by_handle_at O_TRUNC_WRITE + + ocil:ssg-audit_rules_successful_file_modification_open_by_handle_at_o_trunc_write_action:testaction:1 + + + + Disable snmpd Service + + ocil:ssg-service_snmpd_disabled_action:testaction:1 + + + + Configure Response Mode of ARP Requests for All IPv4 Interfaces + + ocil:ssg-sysctl_net_ipv4_conf_all_arp_ignore_action:testaction:1 + + + + Uninstall krb5-workstation Package + + ocil:ssg-package_krb5-workstation_removed_action:testaction:1 + + + + Disable SSH Support for User Known Hosts + + ocil:ssg-sshd_disable_user_known_hosts_action:testaction:1 + + + + Enable Yama support + + ocil:ssg-kernel_config_security_yama_action:testaction:1 + + + + Enable automatic signing of all modules + + ocil:ssg-kernel_config_module_sig_all_action:testaction:1 + + + + Verify Permissions on cron.weekly + + ocil:ssg-file_permissions_cron_weekly_action:testaction:1 + + + + Extend Audit Backlog Limit for the Audit Daemon in zIPL + + ocil:ssg-zipl_audit_backlog_limit_argument_action:testaction:1 + + + + Uninstall quagga Package + + ocil:ssg-package_quagga_removed_action:testaction:1 + + + + Enforce pam_faillock for Local Accounts Only + + ocil:ssg-accounts_passwords_pam_faillock_enforce_local_action:testaction:1 + + + + Disable the cobbler_can_network_connect SELinux Boolean + + ocil:ssg-sebool_cobbler_can_network_connect_action:testaction:1 + + + + Verify Permissions on cron.hourly + + ocil:ssg-file_permissions_cron_hourly_action:testaction:1 + + + + Configure Kernel Parameter for Accepting Secure Redirects By Default + + ocil:ssg-sysctl_net_ipv4_conf_default_secure_redirects_action:testaction:1 + + + + Ensure auditd Collects Information on the Use of Privileged Commands - passwd + + ocil:ssg-audit_rules_privileged_commands_passwd_action:testaction:1 + + + + Verify Permissions on cron.monthly + + ocil:ssg-file_permissions_cron_monthly_action:testaction:1 + + + + All Interactive User Home Directories Must Be Group-Owned By The Primary User + + ocil:ssg-file_groupownership_home_directories_action:testaction:1 + + + + Configure auditing of successful file creations (AArch64) + + ocil:ssg-audit_create_success_aarch64_action:testaction:1 + + + + Configure auditing of successful file deletions + + ocil:ssg-audit_delete_success_action:testaction:1 + + + + Disable the logging_syslogd_run_nagios_plugins SELinux Boolean + + ocil:ssg-sebool_logging_syslogd_run_nagios_plugins_action:testaction:1 + + + + Disable the named_tcp_bind_http_port SELinux Boolean + + ocil:ssg-sebool_named_tcp_bind_http_port_action:testaction:1 + + + + Uninstall geolite2-city Package + + ocil:ssg-package_geolite2-city_removed_action:testaction:1 + + + + The Chrony package is installed + + ocil:ssg-package_chrony_installed_action:testaction:1 + + + + Ensure the Default Umask is Set Correctly For Interactive Users + + ocil:ssg-accounts_umask_interactive_users_action:testaction:1 + + + + Disable vsyscalls in zIPL + + ocil:ssg-zipl_vsyscall_argument_action:testaction:1 + + + + Verify Permissions on SSH Server Private *_key Key Files + + ocil:ssg-file_permissions_sshd_private_key_action:testaction:1 + + + + Disable the piranha_lvs_can_network_connect SELinux Boolean + + ocil:ssg-sebool_piranha_lvs_can_network_connect_action:testaction:1 + + + + Configure auditing of successful ownership changes (AArch64) + + ocil:ssg-audit_owner_change_success_aarch64_action:testaction:1 + + + + Verify User Who Owns /etc/cron.allow file + + ocil:ssg-file_owner_cron_allow_action:testaction:1 + + + + Mount Remote Filesystems with Kerberos Security + + ocil:ssg-mount_option_krb_sec_remote_filesystems_action:testaction:1 + + + + Configure OpenSSL library to use TLS Encryption + + ocil:ssg-configure_openssl_tls_crypto_policy_action:testaction:1 + + + + Record Events that Modify User/Group Information via open syscall - /etc/shadow + + ocil:ssg-audit_rules_etc_shadow_open_action:testaction:1 + + + + Enable the unconfined_chrome_sandbox_transition SELinux Boolean + + ocil:ssg-sebool_unconfined_chrome_sandbox_transition_action:testaction:1 + + + + Verify Group Who Owns /var/log/syslog File + + ocil:ssg-file_groupowner_var_log_syslog_action:testaction:1 + + + + Disable the mozilla_plugin_bind_unreserved_ports SELinux Boolean + + ocil:ssg-sebool_mozilla_plugin_bind_unreserved_ports_action:testaction:1 + + + + Configure auditing of successful file modifications (AArch64) + + ocil:ssg-audit_modify_success_aarch64_action:testaction:1 + + + + Disable Kernel Image Loading + + ocil:ssg-sysctl_kernel_kexec_load_disabled_action:testaction:1 + + + + Configure auditing of successful file accesses + + ocil:ssg-audit_access_success_action:testaction:1 + + + + Disable the xserver_clients_write_xshm SELinux Boolean + + ocil:ssg-sebool_xserver_clients_write_xshm_action:testaction:1 + + + + Disable the virt_use_fusefs SELinux Boolean + + ocil:ssg-sebool_virt_use_fusefs_action:testaction:1 + + + + Verify Group Who Owns cron.weekly + + ocil:ssg-file_groupowner_cron_weekly_action:testaction:1 + + + + Configure the httpd_builtin_scripting SELinux Boolean + + ocil:ssg-sebool_httpd_builtin_scripting_action:testaction:1 + + + + Ensure auditd Collects Information on the Use of Privileged Commands - reboot + + ocil:ssg-audit_privileged_commands_reboot_action:testaction:1 + + + + Set Account Expiration Following Inactivity + + ocil:ssg-account_disable_post_pw_expiration_action:testaction:1 + + + + Ensure All Groups on the System Have Unique Group ID + + ocil:ssg-group_unique_id_action:testaction:1 + + + + Disable the httpd_verify_dns SELinux Boolean + + ocil:ssg-sebool_httpd_verify_dns_action:testaction:1 + + + + Record Events that Modify the System's Discretionary Access Controls - fchmod + + ocil:ssg-audit_rules_dac_modification_fchmod_action:testaction:1 + + + + Disable the httpd_can_network_connect_db SELinux Boolean + + ocil:ssg-sebool_httpd_can_network_connect_db_action:testaction:1 + + + + Disable the tmpreaper_use_nfs SELinux Boolean + + ocil:ssg-sebool_tmpreaper_use_nfs_action:testaction:1 + + + + Ensure gpgcheck Enabled In Main dnf Configuration + + ocil:ssg-ensure_gpgcheck_globally_activated_action:testaction:1 + + + + Uninstall openldap-servers Package + + ocil:ssg-package_openldap-servers_removed_action:testaction:1 + + + + Configure SSH Server to Use FIPS 140-2 Validated MACs: opensshserver.config + + ocil:ssg-harden_sshd_macs_opensshserver_conf_crypto_policy_action:testaction:1 + + + + Prevent remote hosts from connecting to the proxy display + + ocil:ssg-sshd_x11_use_localhost_action:testaction:1 + + + + Harden the operation of the BPF just-in-time compiler + + ocil:ssg-sysctl_net_core_bpf_jit_harden_action:testaction:1 + + + + Disable the xserver_object_manager SELinux Boolean + + ocil:ssg-sebool_xserver_object_manager_action:testaction:1 + + + + Ensure PAM Enforces Password Requirements - Maximum Consecutive Repeating Characters from Same Character Class + + ocil:ssg-accounts_password_pam_maxclassrepeat_action:testaction:1 + + + + Enable cron Service + + ocil:ssg-service_cron_enabled_action:testaction:1 + + + + Ensure SELinux Not Disabled in /etc/default/grub + + ocil:ssg-grub2_enable_selinux_action:testaction:1 + + + + Ensure Red Hat GPG Key Installed + + ocil:ssg-ensure_redhat_gpgkey_installed_action:testaction:1 + + + + Ensure there are no legacy + NIS entries in /etc/passwd + + ocil:ssg-no_legacy_plus_entries_etc_passwd_action:testaction:1 + + + + Disable the cobbler_use_cifs SELinux Boolean + + ocil:ssg-sebool_cobbler_use_cifs_action:testaction:1 + + + + Configure Accepting Default Router in Router Advertisements on All IPv6 Interfaces By Default + + ocil:ssg-sysctl_net_ipv6_conf_default_accept_ra_defrtr_action:testaction:1 + + + + Disable the use_nfs_home_dirs SELinux Boolean + + ocil:ssg-sebool_use_nfs_home_dirs_action:testaction:1 + + + + Disable the httpd_dontaudit_search_dirs SELinux Boolean + + ocil:ssg-sebool_httpd_dontaudit_search_dirs_action:testaction:1 + + + + Disable the fenced_can_network_connect SELinux Boolean + + ocil:ssg-sebool_fenced_can_network_connect_action:testaction:1 + + + + Mount Remote Filesystems with nodev + + ocil:ssg-mount_option_nodev_remote_filesystems_action:testaction:1 + + + + Ensure auditd Collects Information on the Use of Privileged Commands - sudoedit + + ocil:ssg-audit_rules_privileged_commands_sudoedit_action:testaction:1 + + + + Disable Kernel Parameter for IP Forwarding on IPv4 Interfaces + + ocil:ssg-sysctl_net_ipv4_ip_forward_action:testaction:1 + + + + Configure the selinuxuser_direct_dri_enabled SELinux Boolean + + ocil:ssg-sebool_selinuxuser_direct_dri_enabled_action:testaction:1 + + + + Disable WIFI Network Notification in GNOME3 + + ocil:ssg-dconf_gnome_disable_wifi_notification_action:testaction:1 + + + + Record Events that Modify the System's Network Environment + + ocil:ssg-audit_rules_networkconfig_modification_action:testaction:1 + + + + Ensure dnf Removes Previous Package Versions + + ocil:ssg-clean_components_post_updating_action:testaction:1 + + + + Disable Kernel Parameter for Accepting Source-Routed Packets on IPv6 Interfaces by Default + + ocil:ssg-sysctl_net_ipv6_conf_default_accept_source_route_action:testaction:1 + + + + Configure auditing of loading and unloading of kernel modules + + ocil:ssg-audit_module_load_action:testaction:1 + + + + Record Access Events to Audit Log Directory + + ocil:ssg-directory_access_var_log_audit_action:testaction:1 + + + + Disable the rsync_full_access SELinux Boolean + + ocil:ssg-sebool_rsync_full_access_action:testaction:1 + + + + Perform general configuration of Audit for OSPP + + ocil:ssg-audit_ospp_general_action:testaction:1 + + + + Configure Accepting Prefix Information in Router Advertisements on All IPv6 Interfaces + + ocil:ssg-sysctl_net_ipv6_conf_all_accept_ra_pinfo_action:testaction:1 + + + + Record Events that Modify the System's Discretionary Access Controls - fsetxattr + + ocil:ssg-audit_rules_dac_modification_fsetxattr_action:testaction:1 + + + + Disable the httpd_run_preupgrade SELinux Boolean + + ocil:ssg-sebool_httpd_run_preupgrade_action:testaction:1 + + + + Verify Group Who Owns group File + + ocil:ssg-file_groupowner_etc_group_action:testaction:1 + + + + Ensure /tmp Located On Separate Partition + + ocil:ssg-partition_for_tmp_action:testaction:1 + + + + Disable the selinuxuser_mysql_connect_enabled SELinux Boolean + + ocil:ssg-sebool_selinuxuser_mysql_connect_enabled_action:testaction:1 + + + + OpenSSH Service Must Use Passcode for Their Private Keys + + ocil:ssg-ssh_private_keys_have_passcode_action:testaction:1 + + + + Randomize the kernel memory sections + + ocil:ssg-kernel_config_randomize_memory_action:testaction:1 + + + + Disable Access to Network bpf() Syscall From Unprivileged Processes + + ocil:ssg-sysctl_kernel_unprivileged_bpf_disabled_accept_default_action:testaction:1 + + + + Ensure auditd Collects Information on the Use of Privileged Commands - shutdown + + ocil:ssg-audit_privileged_commands_shutdown_action:testaction:1 + + + + Make the auditd Configuration Immutable + + ocil:ssg-audit_rules_immutable_action:testaction:1 + + + + Disable graphical user interface + + ocil:ssg-xwindows_remove_packages_action:testaction:1 + + + + Disable Avahi Server Software + + ocil:ssg-service_avahi-daemon_disabled_action:testaction:1 + + + + Configure auditing of unsuccessful permission changes (ppc64le) + + ocil:ssg-audit_perm_change_failed_ppc64le_action:testaction:1 + + + + Configure the gluster_export_all_rw SELinux Boolean + + ocil:ssg-sebool_gluster_export_all_rw_action:testaction:1 + + + + Record Any Attempts to Run setfiles + + ocil:ssg-audit_rules_execution_setfiles_action:testaction:1 + + + + Enable Use of Privilege Separation + + ocil:ssg-sshd_use_priv_separation_action:testaction:1 + + + + Enable the NTP Daemon + + ocil:ssg-service_ntp_enabled_action:testaction:1 + + + + Disable the logging_syslogd_can_sendmail SELinux Boolean + + ocil:ssg-sebool_logging_syslogd_can_sendmail_action:testaction:1 + + + + Configure Sending and Accepting Shared Media Redirects by Default + + ocil:ssg-sysctl_net_ipv4_conf_default_shared_media_action:testaction:1 + + + + System Audit Logs Must Have Mode 0750 or Less Permissive + + ocil:ssg-directory_permissions_var_log_audit_action:testaction:1 + + + + Disable the mozilla_plugin_use_spice SELinux Boolean + + ocil:ssg-sebool_mozilla_plugin_use_spice_action:testaction:1 + + + + Disable GSSAPI Authentication + + ocil:ssg-sshd_disable_gssapi_auth_action:testaction:1 + + + + Ensure SELinux State is Enforcing + + ocil:ssg-selinux_state_action:testaction:1 + + + + Disable the sanlock_use_fusefs SELinux Boolean + + ocil:ssg-sebool_sanlock_use_fusefs_action:testaction:1 + + + + Verify User Who Owns /var/log/syslog File + + ocil:ssg-file_owner_var_log_syslog_action:testaction:1 + + + + Disable the tor_bind_all_unreserved_ports SELinux Boolean + + ocil:ssg-sebool_tor_bind_all_unreserved_ports_action:testaction:1 + + + + Ensure the audit Subsystem is Installed + + ocil:ssg-package_audit_installed_action:testaction:1 + + + + Record Events that Modify User/Group Information - /etc/group + + ocil:ssg-audit_rules_usergroup_modification_group_action:testaction:1 + + + + Ensure the default plugins for the audit dispatcher are Installed + + ocil:ssg-package_audit-audispd-plugins_installed_action:testaction:1 + + + + Ensure gpgcheck Enabled for All dnf Package Repositories + + ocil:ssg-ensure_gpgcheck_never_disabled_action:testaction:1 + + + + Set GNOME3 Screensaver Inactivity Timeout + + ocil:ssg-dconf_gnome_screensaver_idle_delay_action:testaction:1 + + + + All Interactive User Home Directories Must Have mode 0750 Or Less Permissive + + ocil:ssg-file_permissions_home_directories_action:testaction:1 + + + + Record Events that Modify User/Group Information via open_by_handle_at syscall - /etc/gshadow + + ocil:ssg-audit_rules_etc_gshadow_open_by_handle_at_action:testaction:1 + + + + Record Events that Modify User/Group Information via open syscall - /etc/gshadow + + ocil:ssg-audit_rules_etc_gshadow_open_action:testaction:1 + + + + Disable the tor_can_network_relay SELinux Boolean + + ocil:ssg-sebool_tor_can_network_relay_action:testaction:1 + + + + Disable the samba_run_unconfined SELinux Boolean + + ocil:ssg-sebool_samba_run_unconfined_action:testaction:1 + + + + Ensure PAM Displays Last Logon/Access Notification + + ocil:ssg-display_login_attempts_action:testaction:1 + + + + Configure System to Forward All Mail From Postmaster to The Root Account + + ocil:ssg-postfix_client_configure_mail_alias_postmaster_action:testaction:1 + + + + Disable the guest_exec_content SELinux Boolean + + ocil:ssg-sebool_guest_exec_content_action:testaction:1 + + + + Disable the tmpreaper_use_samba SELinux Boolean + + ocil:ssg-sebool_tmpreaper_use_samba_action:testaction:1 + + + + Prevent Unrestricted Mail Relaying + + ocil:ssg-postfix_prevent_unrestricted_relay_action:testaction:1 + + + + Ensure auditd Unauthorized Access Attempts To open_by_handle_at Are Ordered Correctly + + ocil:ssg-audit_rules_unsuccessful_file_modification_open_by_handle_at_rule_order_action:testaction:1 + + + + Configure auditing of unsuccessful permission changes (AArch64) + + ocil:ssg-audit_perm_change_failed_aarch64_action:testaction:1 + + + + Ensure sudo only includes the default configuration directory + + ocil:ssg-sudoers_default_includedir_action:testaction:1 + + + + Configure System to Forward All Mail through a specific host + + ocil:ssg-postfix_client_configure_relayhost_action:testaction:1 + + + + Disable httpd Service + + ocil:ssg-service_httpd_disabled_action:testaction:1 + + + + Install policycoreutils-python-utils package + + ocil:ssg-package_policycoreutils-python-utils_installed_action:testaction:1 + + + + Verify Permissions on SSH Server Public *.pub Key Files + + ocil:ssg-file_permissions_sshd_pub_key_action:testaction:1 + + + + Ensure the Default Bash Umask is Set Correctly + + ocil:ssg-accounts_umask_etc_bashrc_action:testaction:1 + + + + Disable SCTP Support + + ocil:ssg-kernel_module_sctp_disabled_action:testaction:1 + + + + Enable the mcelog_exec_scripts SELinux Boolean + + ocil:ssg-sebool_mcelog_exec_scripts_action:testaction:1 + + + + Prevent Login to Accounts With Empty Password + + ocil:ssg-no_empty_passwords_action:testaction:1 + + + + Record Unsuccessful Permission Changes to Files - fremovexattr + + ocil:ssg-audit_rules_unsuccessful_file_modification_fremovexattr_action:testaction:1 + + + + Configure Firewalld to Use the Nftables Backend + + ocil:ssg-firewalld-backend_action:testaction:1 + + + + Record Successful Permission Changes to Files - setxattr + + ocil:ssg-audit_rules_successful_file_modification_setxattr_action:testaction:1 + + + + Ensure '/etc/system-fips' exists + + ocil:ssg-etc_system_fips_exists_action:testaction:1 + + + + Disable IPv6 Networking Support Automatic Loading + + ocil:ssg-kernel_module_ipv6_option_disabled_action:testaction:1 + + + + Randomize layout of sensitive kernel structures + + ocil:ssg-kernel_config_gcc_plugin_randstruct_action:testaction:1 + + + + Disable the mailman_use_fusefs SELinux Boolean + + ocil:ssg-sebool_mailman_use_fusefs_action:testaction:1 + + + + Disable the selinuxuser_use_ssh_chroot SELinux Boolean + + ocil:ssg-sebool_selinuxuser_use_ssh_chroot_action:testaction:1 + + + + Verify permissions on Message of the Day Banner + + ocil:ssg-file_permissions_etc_motd_action:testaction:1 + + + + disable the selinuxuser_execstack SELinux Boolean + + ocil:ssg-sebool_selinuxuser_execstack_action:testaction:1 + + + + Ensure auditd Collects Information on the Use of Privileged Commands - postqueue + + ocil:ssg-audit_rules_privileged_commands_postqueue_action:testaction:1 + + + + Enable SLUB debugging support + + ocil:ssg-kernel_config_slub_debug_action:testaction:1 + + + + Disable Apache Qpid (qpidd) + + ocil:ssg-service_qpidd_disabled_action:testaction:1 + + + + Disable the xguest_exec_content SELinux Boolean + + ocil:ssg-sebool_xguest_exec_content_action:testaction:1 + + + + Set Password Warning Age + + ocil:ssg-accounts_password_warn_age_login_defs_action:testaction:1 + + + + Configure a Sufficiently Large Partition for Audit Logs + + ocil:ssg-auditd_audispd_configure_sufficiently_large_partition_action:testaction:1 + + + + Ensure All SUID Executables Are Authorized + + ocil:ssg-file_permissions_unauthorized_suid_action:testaction:1 + + + + Require Authentication for Emergency Systemd Target + + ocil:ssg-require_emergency_target_auth_action:testaction:1 + + + + Stack Protector buffer overlow detection + + ocil:ssg-kernel_config_stackprotector_action:testaction:1 + + + + The Postfix package is installed + + ocil:ssg-package_postfix_installed_action:testaction:1 + + + + Verify User Who Owns Backup gshadow File + + ocil:ssg-file_owner_backup_etc_gshadow_action:testaction:1 + + + + Disable the nagios_run_pnp4nagios SELinux Boolean + + ocil:ssg-sebool_nagios_run_pnp4nagios_action:testaction:1 + + + + Ensure auditd Collects Information on Kernel Module Unloading - delete_module + + ocil:ssg-audit_rules_kernel_module_loading_delete_action:testaction:1 + + + + Add hidepid Option to /proc + + ocil:ssg-mount_option_proc_hidepid_action:testaction:1 + + + + Verify that Shared Library Directories Have Restrictive Permissions + + ocil:ssg-dir_permissions_library_dirs_action:testaction:1 + + + + Disable the mock_enable_homedirs SELinux Boolean + + ocil:ssg-sebool_mock_enable_homedirs_action:testaction:1 + + + + Disable XDMCP in GDM + + ocil:ssg-gnome_gdm_disable_xdmcp_action:testaction:1 + + + + Do not allow ACPI methods to be inserted/replaced at run time + + ocil:ssg-kernel_config_acpi_custom_method_action:testaction:1 + + + + Disable the openshift_use_nfs SELinux Boolean + + ocil:ssg-sebool_openshift_use_nfs_action:testaction:1 + + + + Disable the mysql_connect_any SELinux Boolean + + ocil:ssg-sebool_mysql_connect_any_action:testaction:1 + + + + Configure auditd space_left on Low Disk Space + + ocil:ssg-auditd_data_retention_space_left_percentage_action:testaction:1 + + + + Configure auditing of successful file creations + + ocil:ssg-audit_create_success_action:testaction:1 + + + + Ensure PAM Enforces Password Requirements - Minimum Special Characters + + ocil:ssg-accounts_password_pam_ocredit_action:testaction:1 + + + + Configure AIDE to Verify the Audit Tools + + ocil:ssg-aide_check_audit_tools_action:testaction:1 + + + + Verify No netrc Files Exist + + ocil:ssg-no_netrc_files_action:testaction:1 + + + + Verify Group Who Owns cron.daily + + ocil:ssg-file_groupowner_cron_daily_action:testaction:1 + + + + Enable checks on scatter-gather (SG) table operations + + ocil:ssg-kernel_config_debug_sg_action:testaction:1 + + + + Ensure that chronyd is running under chrony user account + + ocil:ssg-chronyd_run_as_chrony_user_action:testaction:1 + + + + Enable dnf-automatic Timer + + ocil:ssg-timer_dnf-automatic_enabled_action:testaction:1 + + + + Set the GNOME3 Login Warning Banner Text + + ocil:ssg-dconf_gnome_login_banner_text_action:testaction:1 + + + + Disable the privoxy_connect_any SELinux Boolean + + ocil:ssg-sebool_privoxy_connect_any_action:testaction:1 + + + + Record Successful Access Attempts to Files - truncate + + ocil:ssg-audit_rules_successful_file_modification_truncate_action:testaction:1 + + + + Record Events that Modify User/Group Information via openat syscall - /etc/shadow + + ocil:ssg-audit_rules_etc_shadow_openat_action:testaction:1 + + + + Disable the pcp_read_generic_logs SELinux Boolean + + ocil:ssg-sebool_pcp_read_generic_logs_action:testaction:1 + + + + Disable the glance_use_execmem SELinux Boolean + + ocil:ssg-sebool_glance_use_execmem_action:testaction:1 + + + + The operating system must restrict privilege elevation to authorized personnel + + ocil:ssg-sudo_restrict_privilege_elevation_to_authorized_action:testaction:1 + + + + Record Successful Permission Changes to Files - chmod + + ocil:ssg-audit_rules_successful_file_modification_chmod_action:testaction:1 + + + + Enable Kernel Parameter to Use Reverse Path Filtering on all IPv4 Interfaces + + ocil:ssg-sysctl_net_ipv4_conf_all_rp_filter_action:testaction:1 + + + + Add nodev Option to /boot + + ocil:ssg-mount_option_boot_nodev_action:testaction:1 + + + + Ensure Users Cannot Change GNOME3 Screensaver Idle Activation + + ocil:ssg-dconf_gnome_screensaver_idle_activation_locked_action:testaction:1 + + + + Ensure auditd Collects Information on the Use of Privileged Commands - sudo + + ocil:ssg-audit_rules_privileged_commands_sudo_action:testaction:1 + + + + Disable the gitosis_can_sendmail SELinux Boolean + + ocil:ssg-sebool_gitosis_can_sendmail_action:testaction:1 + + + + Disable the httpd_can_network_connect SELinux Boolean + + ocil:ssg-sebool_httpd_can_network_connect_action:testaction:1 + + + + Verify Group Who Owns Backup shadow File + + ocil:ssg-file_owner_backup_etc_shadow_action:testaction:1 + + + + Record Any Attempts to Run semanage + + ocil:ssg-audit_rules_execution_semanage_action:testaction:1 + + + + Disable the sanlock_use_nfs SELinux Boolean + + ocil:ssg-sebool_sanlock_use_nfs_action:testaction:1 + + + + Disable RDS Support + + ocil:ssg-kernel_module_rds_disabled_action:testaction:1 + + + + Set PAM''s Password Hashing Algorithm + + ocil:ssg-set_password_hashing_algorithm_systemauth_action:testaction:1 + + + + Specify module signing key to use + + ocil:ssg-kernel_config_module_sig_key_action:testaction:1 + + + + Configure the tmux Lock Command + + ocil:ssg-configure_tmux_lock_command_action:testaction:1 + + + + Ensure auditd Collects System Administrator Actions - /etc/sudoers.d/ + + ocil:ssg-audit_rules_sudoers_d_action:testaction:1 + + + + Ensure tftp Daemon Uses Secure Mode + + ocil:ssg-tftpd_uses_secure_mode_action:testaction:1 + + + + Verify Group Who Owns /etc/at.allow file + + ocil:ssg-file_groupowner_at_allow_action:testaction:1 + + + + Record Events that Modify User/Group Information via open syscall - /etc/passwd + + ocil:ssg-audit_rules_etc_passwd_open_action:testaction:1 + + + + Record Unsuccessful Permission Changes to Files - lremovexattr + + ocil:ssg-audit_rules_unsuccessful_file_modification_lremovexattr_action:testaction:1 + + + + Configure auditd Disk Full Action when Disk Space Is Full + + ocil:ssg-auditd_data_disk_full_action_action:testaction:1 + + + + Ensure auditd Collects Information on the Use of Privileged Commands - init + + ocil:ssg-audit_privileged_commands_init_action:testaction:1 + + + + Verify the UEFI Boot Loader grub.cfg Permissions + + ocil:ssg-file_permissions_efi_grub2_cfg_action:testaction:1 + + + + Add noexec Option to /tmp + + ocil:ssg-mount_option_tmp_noexec_action:testaction:1 + + + + Disable Ctrl-Alt-Del Reboot Key Sequence in GNOME3 + + ocil:ssg-dconf_gnome_disable_ctrlaltdel_reboot_action:testaction:1 + + + + Force frequent session key renegotiation + + ocil:ssg-sshd_rekey_limit_action:testaction:1 + + + + Ensure that User Home Directories are not Group-Writable or World-Readable + + ocil:ssg-file_permissions_home_dirs_action:testaction:1 + + + + Disable the nis_enabled SELinux Boolean + + ocil:ssg-sebool_nis_enabled_action:testaction:1 + + + + Verify iptables Enabled + + ocil:ssg-service_iptables_enabled_action:testaction:1 + + + + Disable the neutron_can_network SELinux Boolean + + ocil:ssg-sebool_neutron_can_network_action:testaction:1 + + + + Install dnf-automatic Package + + ocil:ssg-package_dnf-automatic_installed_action:testaction:1 + + + + Add nodev Option to /tmp + + ocil:ssg-mount_option_tmp_nodev_action:testaction:1 + + + + Disable the httpd_can_connect_zabbix SELinux Boolean + + ocil:ssg-sebool_httpd_can_connect_zabbix_action:testaction:1 + + + + Record Unsuccessful Modification Attempts to Files - open O_TRUNC_WRITE + + ocil:ssg-audit_rules_unsuccessful_file_modification_open_o_trunc_write_action:testaction:1 + + + + Add nodev Option to /var + + ocil:ssg-mount_option_var_nodev_action:testaction:1 + + + + Disable the openvpn_enable_homedirs SELinux Boolean + + ocil:ssg-sebool_openvpn_enable_homedirs_action:testaction:1 + + + + Disable Accepting ICMP Redirects for All IPv4 Interfaces + + ocil:ssg-sysctl_net_ipv4_conf_all_accept_redirects_action:testaction:1 + + + + Disable Network Router Discovery Daemon (rdisc) + + ocil:ssg-service_rdisc_disabled_action:testaction:1 + + + + IOMMU configuration directive + + ocil:ssg-grub2_enable_iommu_force_action:testaction:1 + + + + Configure Accepting Router Preference in Router Advertisements on All IPv6 Interfaces + + ocil:ssg-sysctl_net_ipv6_conf_all_accept_ra_rtr_pref_action:testaction:1 + + + + Ensure PAM Enforces Password Requirements - Enforce for root User + + ocil:ssg-accounts_password_pam_enforce_root_action:testaction:1 + + + + Install the ntp service + + ocil:ssg-package_ntp_installed_action:testaction:1 + + + + Set Interactive Session Timeout + + ocil:ssg-accounts_tmout_action:testaction:1 + + + + Enable the Hardware RNG Entropy Gatherer Service + + ocil:ssg-service_rngd_enabled_action:testaction:1 + + + + Enable the sysadm_exec_content SELinux Boolean + + ocil:ssg-sebool_sysadm_exec_content_action:testaction:1 + + + + Configure System to Forward All Mail For The Root Account + + ocil:ssg-postfix_client_configure_mail_alias_action:testaction:1 + + + + Remove the X Windows Package Group + + ocil:ssg-package_xorg-x11-server-common_removed_action:testaction:1 + + + + Verify that System Executable Have Root Ownership + + ocil:ssg-dir_ownership_binary_dirs_action:testaction:1 + + + + Disable the git_session_users SELinux Boolean + + ocil:ssg-sebool_git_session_users_action:testaction:1 + + + + Perform general configuration of Audit for OSPP (ppc64le) + + ocil:ssg-audit_ospp_general_ppc64le_action:testaction:1 + + + + Disable the httpd_run_ipa SELinux Boolean + + ocil:ssg-sebool_httpd_run_ipa_action:testaction:1 + + + + Set PAM''s Password Hashing Algorithm - password-auth + + ocil:ssg-set_password_hashing_algorithm_passwordauth_action:testaction:1 + + + + Configure auditing of unsuccessful file creations + + ocil:ssg-audit_create_failed_action:testaction:1 + + + + Disable ATM Support + + ocil:ssg-kernel_module_atm_disabled_action:testaction:1 + + + + Ensure SSH MaxStartups is configured + + ocil:ssg-sshd_set_maxstartups_action:testaction:1 + + + + Randomize the address of the kernel image (KASLR) + + ocil:ssg-kernel_config_randomize_base_action:testaction:1 + + + + Record Unsuccessful Permission Changes to Files - lsetxattr + + ocil:ssg-audit_rules_unsuccessful_file_modification_lsetxattr_action:testaction:1 + + + + Disable the httpd_dbus_sssd SELinux Boolean + + ocil:ssg-sebool_httpd_dbus_sssd_action:testaction:1 + + + + Verify that System Executable Directories Have Restrictive Permissions + + ocil:ssg-dir_permissions_binary_dirs_action:testaction:1 + + + + Configure auditing of unsuccessful file accesses + + ocil:ssg-audit_access_failed_action:testaction:1 + + + + Disable SSH Support for Rhosts RSA Authentication + + ocil:ssg-sshd_disable_rhosts_rsa_action:testaction:1 + + + + Trigger a kernel BUG when data corruption is detected + + ocil:ssg-kernel_config_bug_on_data_corruption_action:testaction:1 + + + + Record Unsuccessful Access Attempts to Files - openat + + ocil:ssg-audit_rules_unsuccessful_file_modification_openat_action:testaction:1 + + + + Ensure SMEP is not disabled during boot + + ocil:ssg-grub2_nosmep_argument_absent_action:testaction:1 + + + + Enable Kernel Parameter to Enforce DAC on Hardlinks + + ocil:ssg-sysctl_fs_protected_hardlinks_action:testaction:1 + + + + Record Attempts to Alter Logon and Logout Events - lastlog + + ocil:ssg-audit_rules_login_events_lastlog_action:testaction:1 + + + + Configure auditd max_log_file_action Upon Reaching Maximum Log Size + + ocil:ssg-auditd_data_retention_max_log_file_action_stig_action:testaction:1 + + + + Verify User Who Owns Backup passwd File + + ocil:ssg-file_owner_backup_etc_passwd_action:testaction:1 + + + + Configure kernel to zero out memory before allocation + + ocil:ssg-grub2_init_on_alloc_argument_action:testaction:1 + + + + Enable the unconfined_mozilla_plugin_transition SELinux Boolean + + ocil:ssg-sebool_unconfined_mozilla_plugin_transition_action:testaction:1 + + + + Record Events that Modify the System's Discretionary Access Controls - lsetxattr + + ocil:ssg-audit_rules_dac_modification_lsetxattr_action:testaction:1 + + + + Disable the git_cgi_use_cifs SELinux Boolean + + ocil:ssg-sebool_git_cgi_use_cifs_action:testaction:1 + + + + Configure Microarchitectural Data Sampling mitigation + + ocil:ssg-grub2_mds_argument_action:testaction:1 + + + + Verify /boot/grub2/grub.cfg User Ownership + + ocil:ssg-file_owner_grub2_cfg_action:testaction:1 + + + + Disable the pppd_for_user SELinux Boolean + + ocil:ssg-sebool_pppd_for_user_action:testaction:1 + + + + Install crypto-policies package + + ocil:ssg-package_crypto-policies_installed_action:testaction:1 + + + + Verify Only Root Has UID 0 + + ocil:ssg-accounts_no_uid_except_zero_action:testaction:1 + + + + Specify the hash to use when signing modules + + ocil:ssg-kernel_config_module_sig_hash_action:testaction:1 + + + + Ensure auditd Collects Information on Kernel Module Loading and Unloading - finit_module + + ocil:ssg-audit_rules_kernel_module_loading_finit_action:testaction:1 + + + + Disable the selinuxuser_rw_noexattrfile SELinux Boolean + + ocil:ssg-sebool_selinuxuser_rw_noexattrfile_action:testaction:1 + + + + Ensure No Daemons are Unconfined by SELinux + + ocil:ssg-selinux_confinement_of_daemons_action:testaction:1 + + + + Verify Group Who Owns /var/log Directory + + ocil:ssg-file_groupowner_var_log_action:testaction:1 + + + + Enable TCP/IP syncookie support + + ocil:ssg-kernel_config_syn_cookies_action:testaction:1 + + + + Record Successful Ownership Changes to Files - lchown + + ocil:ssg-audit_rules_successful_file_modification_lchown_action:testaction:1 + + + + Record Events that Modify User/Group Information via open_by_handle_at syscall - /etc/shadow + + ocil:ssg-audit_rules_etc_shadow_open_by_handle_at_action:testaction:1 + + + + Don't define allowed commands in sudoers by means of exclusion + + ocil:ssg-sudoers_no_command_negation_action:testaction:1 + + + + All Interactive Users Must Have A Home Directory Defined + + ocil:ssg-accounts_user_interactive_home_directory_defined_action:testaction:1 + + + + Verify Permissions on /etc/audit/rules.d/*.rules + + ocil:ssg-file_permissions_etc_audit_rulesd_action:testaction:1 + + + + Disable the xen_use_nfs SELinux Boolean + + ocil:ssg-sebool_xen_use_nfs_action:testaction:1 + + + + System Audit Directories Must Be Owned By Root + + ocil:ssg-directory_ownership_var_log_audit_action:testaction:1 + + + + Write Audit Logs to the Disk + + ocil:ssg-auditd_write_logs_action:testaction:1 + + + + Enable Kernel Parameter to Use Reverse Path Filtering on all IPv4 Interfaces by Default + + ocil:ssg-sysctl_net_ipv4_conf_default_rp_filter_action:testaction:1 + + + + Verify All Account Password Hashes are Shadowed with SHA512 + + ocil:ssg-accounts_password_all_shadowed_sha512_action:testaction:1 + + + + Remove telnet Clients + + ocil:ssg-package_telnet_removed_action:testaction:1 + + + + Record Any Attempts to Run setsebool + + ocil:ssg-audit_rules_execution_setsebool_action:testaction:1 + + + + Disable the webadm_read_user_files SELinux Boolean + + ocil:ssg-sebool_webadm_read_user_files_action:testaction:1 + + + + Only Authorized Local User Accounts Exist on Operating System + + ocil:ssg-accounts_authorized_local_users_action:testaction:1 + + + + Ensure auditd Collects Unauthorized Access Attempts to Files (unsuccessful) + + ocil:ssg-audit_rules_unsuccessful_file_modification_action:testaction:1 + + + + Disable the smartmon_3ware SELinux Boolean + + ocil:ssg-sebool_smartmon_3ware_action:testaction:1 + + + + Verify Root Has A Primary GID 0 + + ocil:ssg-accounts_root_gid_zero_action:testaction:1 + + + + Disable the httpd_enable_homedirs SELinux Boolean + + ocil:ssg-sebool_httpd_enable_homedirs_action:testaction:1 + + + + Record Unsuccessful Access Attempts to Files - creat + + ocil:ssg-audit_rules_unsuccessful_file_modification_creat_action:testaction:1 + + + + Disable the domain_kernel_load_modules SELinux Boolean + + ocil:ssg-sebool_domain_kernel_load_modules_action:testaction:1 + + + + Configure Kerberos to use System Crypto Policy + + ocil:ssg-configure_kerberos_crypto_policy_action:testaction:1 + + + + Use Centralized and Automated Authentication + + ocil:ssg-account_use_centralized_automated_auth_action:testaction:1 + + + + Uninstall gssproxy Package + + ocil:ssg-package_gssproxy_removed_action:testaction:1 + + + + Uninstall tftp-server Package + + ocil:ssg-package_tftp-server_removed_action:testaction:1 + + + + Restrict unprivileged access to the kernel syslog + + ocil:ssg-kernel_config_security_dmesg_restrict_action:testaction:1 + + + + Disable the secure_mode_policyload SELinux Boolean + + ocil:ssg-sebool_secure_mode_policyload_action:testaction:1 + + + + Disable Accepting Packets Routed Between Local Interfaces + + ocil:ssg-sysctl_net_ipv4_conf_all_accept_local_action:testaction:1 + + + + Configure auditd to use audispd's syslog plugin + + ocil:ssg-auditd_audispd_syslog_plugin_activated_action:testaction:1 + + + + Disable Core Dumps for SUID programs + + ocil:ssg-sysctl_fs_suid_dumpable_action:testaction:1 + + + + Ensure All Files Are Owned by a User + + ocil:ssg-no_files_unowned_by_user_action:testaction:1 + + + + The Chronyd service is enabled + + ocil:ssg-service_chronyd_enabled_action:testaction:1 + + + + Record Successful Delete Attempts to Files - renameat + + ocil:ssg-audit_rules_successful_file_modification_renameat_action:testaction:1 + + + + Enable syslog-ng Service + + ocil:ssg-service_syslogng_enabled_action:testaction:1 + + + + Ensure auditd Collects Information on the Use of Privileged Commands + + ocil:ssg-audit_rules_privileged_commands_action:testaction:1 + + + + Configure auditing of successful file accesses (ppc64le) + + ocil:ssg-audit_access_success_ppc64le_action:testaction:1 + + + + Verify Permissions on crontab + + ocil:ssg-file_permissions_crontab_action:testaction:1 + + + + Configure auditing of unsuccessful ownership changes (AArch64) + + ocil:ssg-audit_owner_change_failed_aarch64_action:testaction:1 + + + + Configure BIND to use System Crypto Policy + + ocil:ssg-configure_bind_crypto_policy_action:testaction:1 + + + + Record Events that Modify User/Group Information via openat syscall - /etc/passwd + + ocil:ssg-audit_rules_etc_passwd_openat_action:testaction:1 + + + + Limit Password Reuse: password-auth + + ocil:ssg-accounts_password_pam_pwhistory_remember_password_auth_action:testaction:1 + + + + Force initialization of variables containing userspace addresses + + ocil:ssg-kernel_config_gcc_plugin_structleak_action:testaction:1 + + + + Enable the antivirus_can_scan_system SELinux Boolean + + ocil:ssg-sebool_antivirus_can_scan_system_action:testaction:1 + + + + Ensure Rsyslog Encrypts Off-Loaded Audit Records + + ocil:ssg-rsyslog_encrypt_offload_defaultnetstreamdriver_action:testaction:1 + + + + Add nosuid Option to /var + + ocil:ssg-mount_option_var_nosuid_action:testaction:1 + + + + Add nodev Option to /var/log + + ocil:ssg-mount_option_var_log_nodev_action:testaction:1 + + + + Disable the icecast_use_any_tcp_ports SELinux Boolean + + ocil:ssg-sebool_icecast_use_any_tcp_ports_action:testaction:1 + + + + Verify Permissions on /var/log/messages File + + ocil:ssg-file_permissions_var_log_messages_action:testaction:1 + + + + Set Password Hashing Rounds in /etc/login.defs + + ocil:ssg-set_password_hashing_min_rounds_logindefs_action:testaction:1 + + + + Ensure auditd Collects Information on Kernel Module Loading - init_module + + ocil:ssg-audit_rules_kernel_module_loading_init_action:testaction:1 + + + + Do not allow usercopy whitelist violations to fallback to object size + + ocil:ssg-kernel_config_hardened_usercopy_fallback_action:testaction:1 + + + + Remove tftp Daemon + + ocil:ssg-package_tftp_removed_action:testaction:1 + + + + Ensure auditd Collects Information on Exporting to Media (successful) + + ocil:ssg-audit_rules_media_export_action:testaction:1 + + + + Disable core dump backtraces + + ocil:ssg-coredump_disable_backtraces_action:testaction:1 + + + + Remove Host-Based Authentication Files + + ocil:ssg-no_host_based_files_action:testaction:1 + + + + Disable the httpd_ssi_exec SELinux Boolean + + ocil:ssg-sebool_httpd_ssi_exec_action:testaction:1 + + + + Ensure All-Squashing Disabled On All Exports + + ocil:ssg-no_all_squash_exports_action:testaction:1 + + + + Configure auditing of loading and unloading of kernel modules (ppc64le) + + ocil:ssg-audit_module_load_ppc64le_action:testaction:1 + + + + Disable the zoneminder_anon_write SELinux Boolean + + ocil:ssg-sebool_zoneminder_anon_write_action:testaction:1 + + + + Ensure that Users Path Contains Only Local Directories + + ocil:ssg-accounts_user_home_paths_only_action:testaction:1 + + + + Disable the fenced_can_ssh SELinux Boolean + + ocil:ssg-sebool_fenced_can_ssh_action:testaction:1 + + + + Enable the dbadm_exec_content SELinux Boolean + + ocil:ssg-sebool_dbadm_exec_content_action:testaction:1 + + + + Disable the CUPS Service + + ocil:ssg-service_cups_disabled_action:testaction:1 + + + + Ensure auditd Collects Information on the Use of Privileged Commands - crontab + + ocil:ssg-audit_rules_privileged_commands_crontab_action:testaction:1 + + + + Add nodev Option to /var/log/audit + + ocil:ssg-mount_option_var_log_audit_nodev_action:testaction:1 + + + + Record Successful Permission Changes to Files - lremovexattr + + ocil:ssg-audit_rules_successful_file_modification_lremovexattr_action:testaction:1 + + + + Ensure System is Not Acting as a Network Sniffer + + ocil:ssg-network_sniffer_disabled_action:testaction:1 + + + + Ensure /var/log/audit Located On Separate Partition + + ocil:ssg-partition_for_var_log_audit_action:testaction:1 + + + + Record Unsuccessful Ownership Changes to Files - fchownat + + ocil:ssg-audit_rules_unsuccessful_file_modification_fchownat_action:testaction:1 + + + + Record Any Attempts to Run chacl + + ocil:ssg-audit_rules_execution_chacl_action:testaction:1 + + + + Ensure PAM password complexity module is enabled in password-auth + + ocil:ssg-accounts_password_pam_pwquality_password_auth_action:testaction:1 + + + + Disable the mozilla_plugin_use_bluejeans SELinux Boolean + + ocil:ssg-sebool_mozilla_plugin_use_bluejeans_action:testaction:1 + + + + Enable the nscd_use_shm SELinux Boolean + + ocil:ssg-sebool_nscd_use_shm_action:testaction:1 + + + + Disable the wine_mmap_zero_ignore SELinux Boolean + + ocil:ssg-sebool_wine_mmap_zero_ignore_action:testaction:1 + + + + Record Attempts to Alter the localtime File + + ocil:ssg-audit_rules_time_watch_localtime_action:testaction:1 + + + + Verify Permissions on Backup shadow File + + ocil:ssg-file_permissions_backup_etc_shadow_action:testaction:1 + + + + Ensure journald is configured to compress large log files + + ocil:ssg-journald_compress_action:testaction:1 + + + + Configure Speculative Store Bypass Mitigation + + ocil:ssg-grub2_spec_store_bypass_disable_argument_action:testaction:1 + + + + Disallow merge of slab caches + + ocil:ssg-kernel_config_slab_merge_default_action:testaction:1 + + + + Record Unsuccessful Modification Attempts to Files - openat O_TRUNC_WRITE + + ocil:ssg-audit_rules_unsuccessful_file_modification_openat_o_trunc_write_action:testaction:1 + + + + Configure OpenSSL library to use System Crypto Policy + + ocil:ssg-configure_openssl_crypto_policy_action:testaction:1 + + + + Configure auditing of successful permission changes (ppc64le) + + ocil:ssg-audit_perm_change_success_ppc64le_action:testaction:1 + + + + Poison kernel stack before returning from syscalls + + ocil:ssg-kernel_config_gcc_plugin_stackleak_action:testaction:1 + + + + Set the Boot Loader Admin Username to a Non-Default Value + + ocil:ssg-grub2_admin_username_action:testaction:1 + + + + Disable the xdm_exec_bootloader SELinux Boolean + + ocil:ssg-sebool_xdm_exec_bootloader_action:testaction:1 + + + + Disable the virt_use_sanlock SELinux Boolean + + ocil:ssg-sebool_virt_use_sanlock_action:testaction:1 + + + + Configure Auto Configuration on All IPv6 Interfaces By Default + + ocil:ssg-sysctl_net_ipv6_conf_default_autoconf_action:testaction:1 + + + + Disable the awstats_purge_apache_log_files SELinux Boolean + + ocil:ssg-sebool_awstats_purge_apache_log_files_action:testaction:1 + + + + Verify Group Who Owns Backup gshadow File + + ocil:ssg-file_groupowner_backup_etc_gshadow_action:testaction:1 + + + + Install libreswan Package + + ocil:ssg-package_libreswan_installed_action:testaction:1 + + + + Disable the pppd_can_insmod SELinux Boolean + + ocil:ssg-sebool_pppd_can_insmod_action:testaction:1 + + + + Configure Accepting Default Router in Router Advertisements on All IPv6 Interfaces + + ocil:ssg-sysctl_net_ipv6_conf_all_accept_ra_defrtr_action:testaction:1 + + + + Verify that Shared Library Directories Have Root Group Ownership + + ocil:ssg-dir_group_ownership_library_dirs_action:testaction:1 + + + + Disable Recovery Booting + + ocil:ssg-grub2_disable_recovery_action:testaction:1 + + + + Record Unsuccessful Delete Attempts to Files - unlink + + ocil:ssg-audit_rules_unsuccessful_file_modification_unlink_action:testaction:1 + + + + Verify User Who Owns passwd File + + ocil:ssg-file_owner_etc_passwd_action:testaction:1 + + + + Configure Denying Router Solicitations on All IPv6 Interfaces + + ocil:ssg-sysctl_net_ipv6_conf_all_router_solicitations_action:testaction:1 + + + + Record Events that Modify the System's Discretionary Access Controls - umount2 + + ocil:ssg-audit_rules_dac_modification_umount2_action:testaction:1 + + + + Ensure Software Patches Installed + + ocil:ssg-security_patches_up_to_date_action:testaction:1 + + + + Ensure auditd Collects Information on the Use of Privileged Commands - umount + + ocil:ssg-audit_rules_privileged_commands_umount_action:testaction:1 + + + + Disable WIFI Network Connection Creation in GNOME3 + + ocil:ssg-dconf_gnome_disable_wifi_create_action:testaction:1 + + + + Disable the xdm_write_home SELinux Boolean + + ocil:ssg-sebool_xdm_write_home_action:testaction:1 + + + + Configure AIDE to Verify Extended Attributes + + ocil:ssg-aide_verify_ext_attributes_action:testaction:1 + + + + Ensure that /etc/at.deny does not exist + + ocil:ssg-file_at_deny_not_exist_action:testaction:1 + + + + Ensure that Root's Path Does Not Include World or Group-Writable Directories + + ocil:ssg-accounts_root_path_dirs_no_write_action:testaction:1 + + + + Disable Postfix Network Listening + + ocil:ssg-postfix_network_listening_disabled_action:testaction:1 + + + + Record Unsuccessful Delete Attempts to Files - renameat + + ocil:ssg-audit_rules_unsuccessful_file_modification_renameat_action:testaction:1 + + + + Disable the samba_create_home_dirs SELinux Boolean + + ocil:ssg-sebool_samba_create_home_dirs_action:testaction:1 + + + + Disable the openvpn_can_network_connect SELinux Boolean + + ocil:ssg-sebool_openvpn_can_network_connect_action:testaction:1 + + + + Disable acquiring, saving, and processing core dumps + + ocil:ssg-service_systemd-coredump_disabled_action:testaction:1 + + + + Add nosuid Option to /boot/efi + + ocil:ssg-mount_option_boot_efi_nosuid_action:testaction:1 + + + + Configure auditing of successful file deletions (ppc64le) + + ocil:ssg-audit_delete_success_ppc64le_action:testaction:1 + + + + Add nodev Option to /var/tmp + + ocil:ssg-mount_option_var_tmp_nodev_action:testaction:1 + + + + Disable the samba_export_all_ro SELinux Boolean + + ocil:ssg-sebool_samba_export_all_ro_action:testaction:1 + + + + Ensure rsyslog-gnutls is installed + + ocil:ssg-package_rsyslog-gnutls_installed_action:testaction:1 + + + + Enable the cron_userdomain_transition SELinux Boolean + + ocil:ssg-sebool_cron_userdomain_transition_action:testaction:1 + + + + Do Not Allow SSH Environment Options + + ocil:ssg-sshd_do_not_permit_user_env_action:testaction:1 + + + + Disable the samba_export_all_rw SELinux Boolean + + ocil:ssg-sebool_samba_export_all_rw_action:testaction:1 + + + + Disable Kernel Parameter for Accepting Source-Routed Packets on IPv4 Interfaces by Default + + ocil:ssg-sysctl_net_ipv4_conf_default_accept_source_route_action:testaction:1 + + + + Install scap-security-guide Package + + ocil:ssg-package_scap-security-guide_installed_action:testaction:1 + + + + Disable the squid_connect_any SELinux Boolean + + ocil:ssg-sebool_squid_connect_any_action:testaction:1 + + + + Set Password Hashing Algorithm in /etc/login.defs + + ocil:ssg-set_password_hashing_algorithm_logindefs_action:testaction:1 + + + + Ensure Users Cannot Change GNOME3 Screensaver Settings + + ocil:ssg-dconf_gnome_screensaver_user_locks_action:testaction:1 + + + + Verify firewalld Enabled + + ocil:ssg-service_firewalld_enabled_action:testaction:1 + + + + Disable the ftpd_use_passive_mode SELinux Boolean + + ocil:ssg-sebool_ftpd_use_passive_mode_action:testaction:1 + + + + Enable the selinuxuser_ping SELinux Boolean + + ocil:ssg-sebool_selinuxuser_ping_action:testaction:1 + + + + Unmap kernel when running in userspace (aka KAISER) + + ocil:ssg-kernel_config_unmap_kernel_at_el0_action:testaction:1 + + + + Disable xinetd Service + + ocil:ssg-service_xinetd_disabled_action:testaction:1 + + + + Allow Only SSH Protocol 2 + + ocil:ssg-sshd_allow_only_protocol2_action:testaction:1 + + + + Uninstall telnet-server Package + + ocil:ssg-package_telnet-server_removed_action:testaction:1 + + + + Add noexec Option to /var/log + + ocil:ssg-mount_option_var_log_noexec_action:testaction:1 + + + + Configure the deny_execmem SELinux Boolean + + ocil:ssg-sebool_deny_execmem_action:testaction:1 + + + + Disable vsftpd Service + + ocil:ssg-service_vsftpd_disabled_action:testaction:1 + + + + Ensure the Logon Failure Delay is Set Correctly in login.defs + + ocil:ssg-accounts_logon_fail_delay_action:testaction:1 + + + + Verify User Who Owns Backup shadow File + + ocil:ssg-file_groupowner_backup_etc_shadow_action:testaction:1 + + + + Disable Squid + + ocil:ssg-service_squid_disabled_action:testaction:1 + + + + Disable the 32-bit vDSO + + ocil:ssg-kernel_config_compat_vdso_action:testaction:1 + + + + Enable different security models + + ocil:ssg-kernel_config_security_action:testaction:1 + + + + Enable SSH Warning Banner + + ocil:ssg-sshd_enable_warning_banner_net_action:testaction:1 + + + + Encrypt Audit Records Sent With audispd Plugin + + ocil:ssg-auditd_audispd_encrypt_sent_records_action:testaction:1 + + + + Configure Auto Configuration on All IPv6 Interfaces + + ocil:ssg-sysctl_net_ipv6_conf_all_autoconf_action:testaction:1 + + + + Ensure /srv Located On Separate Partition + + ocil:ssg-partition_for_srv_action:testaction:1 + + + + Set Default ip6tables Policy for Incoming Packets + + ocil:ssg-set_ip6tables_default_rule_action:testaction:1 + + + + Verify User Who Owns gshadow File + + ocil:ssg-file_owner_etc_gshadow_action:testaction:1 + + + + The Installed Operating System Is Vendor Supported + + ocil:ssg-installed_OS_is_vendor_supported_action:testaction:1 + + + + Disable x86 vsyscall emulation + + ocil:ssg-kernel_config_x86_vsyscall_emulation_action:testaction:1 + + + + Drop Gratuitious ARP frames on All IPv4 Interfaces + + ocil:ssg-sysctl_net_ipv4_conf_all_drop_gratuitous_arp_action:testaction:1 + + + + Configure Multiple DNS Servers in /etc/resolv.conf + + ocil:ssg-network_configure_name_resolution_action:testaction:1 + + + + Enable SSH Warning Banner + + ocil:ssg-sshd_enable_warning_banner_action:testaction:1 + + + + Disable the kdumpgui_run_bootloader SELinux Boolean + + ocil:ssg-sebool_kdumpgui_run_bootloader_action:testaction:1 + + + + Disable the dhcpc_exec_iptables SELinux Boolean + + ocil:ssg-sebool_dhcpc_exec_iptables_action:testaction:1 + + + + Configure Kernel to Rate Limit Sending of Duplicate TCP Acknowledgments + + ocil:ssg-sysctl_net_ipv4_tcp_invalid_ratelimit_action:testaction:1 + + + + Disable the git_cgi_use_nfs SELinux Boolean + + ocil:ssg-sebool_git_cgi_use_nfs_action:testaction:1 + + + + Enable Kernel Page-Table Isolation (KPTI) + + ocil:ssg-grub2_pti_argument_action:testaction:1 + + + + Disable the GNOME3 Login User List + + ocil:ssg-dconf_gnome_disable_user_list_action:testaction:1 + + + + Restrict Virtual Console Root Logins + + ocil:ssg-securetty_root_login_console_only_action:testaction:1 + + + + Ensure auditd Collects File Deletion Events by User - unlinkat + + ocil:ssg-audit_rules_file_deletion_events_unlinkat_action:testaction:1 + + + + Enable poison of pages after freeing + + ocil:ssg-kernel_config_page_poisoning_action:testaction:1 + + + + Ensure SMAP is not disabled during boot + + ocil:ssg-grub2_nosmap_argument_absent_action:testaction:1 + + + + Disable Kernel Parameter for Accepting Source-Routed Packets on all IPv6 Interfaces + + ocil:ssg-sysctl_net_ipv6_conf_all_accept_source_route_action:testaction:1 + + + + Disable rlogin Service + + ocil:ssg-service_rlogin_disabled_action:testaction:1 + + + + Record Events When Privileged Executables Are Run + + ocil:ssg-audit_rules_suid_privilege_function_action:testaction:1 + + + + Configure Denying Router Solicitations on All IPv6 Interfaces By Default + + ocil:ssg-sysctl_net_ipv6_conf_default_router_solicitations_action:testaction:1 + + + + Enable the httpd_graceful_shutdown SELinux Boolean + + ocil:ssg-sebool_httpd_graceful_shutdown_action:testaction:1 + + + + Configure auditing of unsuccessful permission changes + + ocil:ssg-audit_perm_change_failed_action:testaction:1 + + + + Configure auditing of successful permission changes + + ocil:ssg-audit_perm_change_success_action:testaction:1 + + + + Enable Certmap in SSSD + + ocil:ssg-sssd_enable_certmap_action:testaction:1 + + + + SSH server uses strong entropy to seed + + ocil:ssg-sshd_use_strong_rng_action:testaction:1 + + + + Uninstall ypserv Package + + ocil:ssg-package_ypserv_removed_action:testaction:1 + + + + Don't target root user in the sudoers file + + ocil:ssg-sudoers_no_root_target_action:testaction:1 + + + + Disable User Administration in GNOME3 + + ocil:ssg-dconf_gnome_disable_user_admin_action:testaction:1 + + + + Add noexec Option to /boot + + ocil:ssg-mount_option_boot_noexec_action:testaction:1 + + + + Disable the ksmtuned_use_cifs SELinux Boolean + + ocil:ssg-sebool_ksmtuned_use_cifs_action:testaction:1 + + + + Randomize slab freelist + + ocil:ssg-kernel_config_slab_freelist_random_action:testaction:1 + + + + Ensure there are no legacy + NIS entries in /etc/group + + ocil:ssg-no_legacy_plus_entries_etc_group_action:testaction:1 + + + + Configure SELinux Policy + + ocil:ssg-selinux_policytype_action:testaction:1 + + + + Disable the container_connect_any SELinux Boolean + + ocil:ssg-sebool_container_connect_any_action:testaction:1 + + + + Disable the xguest_mount_media SELinux Boolean + + ocil:ssg-sebool_xguest_mount_media_action:testaction:1 + + + + Disable the httpd_can_network_connect_cobbler SELinux Boolean + + ocil:ssg-sebool_httpd_can_network_connect_cobbler_action:testaction:1 + + + + Verify User Who Owns Backup group File + + ocil:ssg-file_owner_backup_etc_group_action:testaction:1 + + + + Record Events that Modify the System's Discretionary Access Controls - fremovexattr + + ocil:ssg-audit_rules_dac_modification_fremovexattr_action:testaction:1 + + + + Enable Auditing for Processes Which Start Prior to the Audit Daemon + + ocil:ssg-grub2_audit_argument_action:testaction:1 + + + + Configure auditing of successful file deletions (AArch64) + + ocil:ssg-audit_delete_success_aarch64_action:testaction:1 + + + + Disable the daemons_enable_cluster_mode SELinux Boolean + + ocil:ssg-sebool_daemons_enable_cluster_mode_action:testaction:1 + + + + Disable LDAP Server (slapd) + + ocil:ssg-service_slapd_disabled_action:testaction:1 + + + + Install firewalld Package + + ocil:ssg-package_firewalld_installed_action:testaction:1 + + + + Disable the httpd_sys_script_anon_write SELinux Boolean + + ocil:ssg-sebool_httpd_sys_script_anon_write_action:testaction:1 + + + + Record Successful Creation Attempts to Files - open_by_handle_at O_CREAT + + ocil:ssg-audit_rules_successful_file_modification_open_by_handle_at_o_creat_action:testaction:1 + + + + Disable the tftp_home_dir SELinux Boolean + + ocil:ssg-sebool_tftp_home_dir_action:testaction:1 + + + + Disable Odd Job Daemon (oddjobd) + + ocil:ssg-service_oddjobd_disabled_action:testaction:1 + + + + Map System Users To The Appropriate SELinux Role + + ocil:ssg-selinux_user_login_roles_action:testaction:1 + + + + Disable the irssi_use_full_network SELinux Boolean + + ocil:ssg-sebool_irssi_use_full_network_action:testaction:1 + + + + Verify /boot/grub2/grub.cfg Permissions + + ocil:ssg-file_permissions_grub2_cfg_action:testaction:1 + + + + Ensure auditd Collects File Deletion Events by User - rmdir + + ocil:ssg-audit_rules_file_deletion_events_rmdir_action:testaction:1 + + + + Kernel panic on oops + + ocil:ssg-sysctl_kernel_panic_on_oops_action:testaction:1 + + + + Configure immutable Audit login UIDs + + ocil:ssg-audit_immutable_login_uids_action:testaction:1 + + + + Disable the xguest_connect_network SELinux Boolean + + ocil:ssg-sebool_xguest_connect_network_action:testaction:1 + + + + Disable the staff_use_svirt SELinux Boolean + + ocil:ssg-sebool_staff_use_svirt_action:testaction:1 + + + + Configure Accepting Router Advertisements on All IPv6 Interfaces + + ocil:ssg-sysctl_net_ipv6_conf_all_accept_ra_action:testaction:1 + + + + Uninstall httpd Package + + ocil:ssg-package_httpd_removed_action:testaction:1 + + + + Enable Public Key Authentication + + ocil:ssg-sshd_enable_pubkey_auth_action:testaction:1 + + + + Disable the prosody_bind_http_port SELinux Boolean + + ocil:ssg-sebool_prosody_bind_http_port_action:testaction:1 + + + + Verify that Shared Library Files Have Restrictive Permissions + + ocil:ssg-file_permissions_library_dirs_action:testaction:1 + + + + Perform general configuration of Audit for OSPP (AArch64) + + ocil:ssg-audit_ospp_general_aarch64_action:testaction:1 + + + + Install openscap-scanner Package + + ocil:ssg-package_openscap-scanner_installed_action:testaction:1 + + + + Disable DHCP Service + + ocil:ssg-service_dhcpd_disabled_action:testaction:1 + + + + Configure auditing of unsuccessful file modifications (ppc64le) + + ocil:ssg-audit_modify_failed_ppc64le_action:testaction:1 + + + + Disable the httpd_read_user_content SELinux Boolean + + ocil:ssg-sebool_httpd_read_user_content_action:testaction:1 + + + + Configure SNMP Service to Use Only SNMPv3 or Newer + + ocil:ssg-snmpd_use_newer_protocol_action:testaction:1 + + + + Disable the varnishd_connect_any SELinux Boolean + + ocil:ssg-sebool_varnishd_connect_any_action:testaction:1 + + + + Configure the Use of the pam_faillock.so Module in the /etc/pam.d/password-auth File. + + ocil:ssg-account_password_pam_faillock_password_auth_action:testaction:1 + + + + Record Events that Modify User/Group Information - /etc/security/opasswd + + ocil:ssg-audit_rules_usergroup_modification_opasswd_action:testaction:1 + + + + Set Password Minimum Age + + ocil:ssg-accounts_minimum_age_login_defs_action:testaction:1 + + + + Disable the zabbix_can_network SELinux Boolean + + ocil:ssg-sebool_zabbix_can_network_action:testaction:1 + + + + Verify Group Who Owns shadow File + + ocil:ssg-file_groupowner_etc_shadow_action:testaction:1 + + + + Enable Kernel Parameter to Ignore Bogus ICMP Error Responses on IPv4 Interfaces + + ocil:ssg-sysctl_net_ipv4_icmp_ignore_bogus_error_responses_action:testaction:1 + + + + Ensure PAM Enforces Password Requirements - Minimum Length + + ocil:ssg-accounts_password_pam_minlen_action:testaction:1 + + + + Disable the virt_sandbox_use_all_caps SELinux Boolean + + ocil:ssg-sebool_virt_sandbox_use_all_caps_action:testaction:1 + + + + Set LogLevel to INFO + + ocil:ssg-sshd_set_loglevel_info_action:testaction:1 + + + + Enable randomization of the page allocator + + ocil:ssg-grub2_page_alloc_shuffle_argument_action:testaction:1 + + + + Add nosuid Option to /var/log/audit + + ocil:ssg-mount_option_var_log_audit_nosuid_action:testaction:1 + + + + Kernel panic timeout + + ocil:ssg-kernel_config_panic_timeout_action:testaction:1 + + + + Uninstall setroubleshoot-server Package + + ocil:ssg-package_setroubleshoot-server_removed_action:testaction:1 + + + + Add noexec Option to /var/tmp + + ocil:ssg-mount_option_var_tmp_noexec_action:testaction:1 + + + + Install the opensc Package For Multifactor Authentication + + ocil:ssg-package_opensc_installed_action:testaction:1 + + + + Disable /dev/kmem virtual device support + + ocil:ssg-kernel_config_devkmem_action:testaction:1 + + + + Ensure the Default C Shell Umask is Set Correctly + + ocil:ssg-accounts_umask_etc_csh_cshrc_action:testaction:1 + + + + Ensure auditd Collects Information on the Use of Privileged Commands - chsh + + ocil:ssg-audit_rules_privileged_commands_chsh_action:testaction:1 + + + + Enable the xend_run_qemu SELinux Boolean + + ocil:ssg-sebool_xend_run_qemu_action:testaction:1 + + + + Verify that local System.map file (if exists) is readable only by root + + ocil:ssg-file_permissions_systemmap_action:testaction:1 + + + + Configure low address space to protect from user allocation + + ocil:ssg-kernel_config_default_mmap_min_addr_action:testaction:1 + + + + Record Any Attempts to Run chcon + + ocil:ssg-audit_rules_execution_chcon_action:testaction:1 + + + + Lock Accounts Must Persist + + ocil:ssg-accounts_passwords_pam_faillock_dir_action:testaction:1 + + + + Verify Permissions on SSH Server config file + + ocil:ssg-file_permissions_sshd_config_action:testaction:1 + + + + Disable the virt_use_rawip SELinux Boolean + + ocil:ssg-sebool_virt_use_rawip_action:testaction:1 + + + + Ensure All SGID Executables Are Authorized + + ocil:ssg-file_permissions_unauthorized_sgid_action:testaction:1 + + + + Ensure All Accounts on the System Have Unique Names + + ocil:ssg-account_unique_name_action:testaction:1 + + + + Configure the confidence in TPM for entropy + + ocil:ssg-grub2_rng_core_default_quality_argument_action:testaction:1 + + + + All GIDs referenced in /etc/passwd must be defined in /etc/group + + ocil:ssg-gid_passwd_group_same_action:testaction:1 + + + + Ensure System Log Files Have Correct Permissions + + ocil:ssg-rsyslog_files_permissions_action:testaction:1 + + + + Record Events that Modify the System's Discretionary Access Controls - fchmodat + + ocil:ssg-audit_rules_dac_modification_fchmodat_action:testaction:1 + + + + Ensure auditd Collects Information on Kernel Module Loading and Unloading + + ocil:ssg-audit_rules_kernel_module_loading_action:testaction:1 + + + + Ensure remote access methods are monitored in Rsyslog + + ocil:ssg-rsyslog_remote_access_monitoring_action:testaction:1 + + + + Verify Owner on crontab + + ocil:ssg-file_owner_crontab_action:testaction:1 + + + + Configure TLS for rsyslog remote logging + + ocil:ssg-rsyslog_remote_tls_action:testaction:1 + + + + Ensure auditd Collects Information on the Use of Privileged Commands - ssh-keysign + + ocil:ssg-audit_rules_privileged_commands_ssh_keysign_action:testaction:1 + + + + Configure SSH Client to Use FIPS 140-2 Validated Ciphers: openssh.config + + ocil:ssg-harden_sshd_ciphers_openssh_conf_crypto_policy_action:testaction:1 + + + + Ensure Logs Sent To Remote Host + + ocil:ssg-rsyslog_remote_loghost_action:testaction:1 + + + + Record Successful Creation Attempts to Files - openat O_TRUNC_WRITE + + ocil:ssg-audit_rules_successful_file_modification_openat_o_trunc_write_action:testaction:1 + + + + Verify Owner on cron.weekly + + ocil:ssg-file_owner_cron_weekly_action:testaction:1 + + + + Disable the git_session_bind_all_unreserved_ports SELinux Boolean + + ocil:ssg-sebool_git_session_bind_all_unreserved_ports_action:testaction:1 + + + + Disable Red Hat Network Service (rhnsd) + + ocil:ssg-service_rhnsd_disabled_action:testaction:1 + + + + Record Any Attempts to Run restorecon + + ocil:ssg-audit_rules_execution_restorecon_action:testaction:1 + + + + Appropriate Action Must be Setup When the Internal Audit Event Queue is Full + + ocil:ssg-auditd_overflow_action_action:testaction:1 + + + + Disable the samba_portmapper SELinux Boolean + + ocil:ssg-sebool_samba_portmapper_action:testaction:1 + + + + Configure Fapolicy Module to Employ a Deny-all, Permit-by-exception Policy to Allow the Execution of Authorized Software Programs. + + ocil:ssg-fapolicy_default_deny_action:testaction:1 + + + + System Audit Logs Must Be Group Owned By Root + + ocil:ssg-file_group_ownership_var_log_audit_action:testaction:1 + + + + Enable the logadm_exec_content SELinux Boolean + + ocil:ssg-sebool_logadm_exec_content_action:testaction:1 + + + + Ensure Log Files Are Owned By Appropriate Group + + ocil:ssg-rsyslog_files_groupownership_action:testaction:1 + + + + Record Successful Permission Changes to Files - fchmod + + ocil:ssg-audit_rules_successful_file_modification_fchmod_action:testaction:1 + + + + Record attempts to alter time through settimeofday + + ocil:ssg-audit_rules_time_settimeofday_action:testaction:1 + + + + Disable GNOME3 Automount Opening + + ocil:ssg-dconf_gnome_disable_automount_open_action:testaction:1 + + + + Set Kernel Parameter to Increase Local Port Range + + ocil:ssg-sysctl_net_ipv4_ip_local_port_range_action:testaction:1 + + + + Ensure auditd Collects System Administrator Actions - /etc/sudoers + + ocil:ssg-audit_rules_sudoers_action:testaction:1 + + + + Configure Periodic Execution of AIDE + + ocil:ssg-aide_periodic_cron_checking_action:testaction:1 + + + + Set the GNOME3 Login Number of Failures + + ocil:ssg-dconf_gnome_login_retries_action:testaction:1 + + + + Make the kernel text and rodata read-only + + ocil:ssg-kernel_config_strict_kernel_rwx_action:testaction:1 + + + + Disable the virt_use_nfs SELinux Boolean + + ocil:ssg-sebool_virt_use_nfs_action:testaction:1 + + + + Disable the ksmtuned_use_nfs SELinux Boolean + + ocil:ssg-sebool_ksmtuned_use_nfs_action:testaction:1 + + + + Configure auditd Disk Full Action when Disk Space Is Full + + ocil:ssg-auditd_data_disk_full_action_stig_action:testaction:1 + + + + Ensure auditd Collects Information on the Use of Privileged Commands - pt_chown + + ocil:ssg-audit_rules_privileged_commands_pt_chown_action:testaction:1 + + + + Verify Permissions on Backup passwd File + + ocil:ssg-file_permissions_backup_etc_passwd_action:testaction:1 + + + + Disable the named_write_master_zones SELinux Boolean + + ocil:ssg-sebool_named_write_master_zones_action:testaction:1 + + + + Enable Kernel Parameter to Use TCP RFC 1337 on IPv4 Interfaces + + ocil:ssg-sysctl_net_ipv4_tcp_rfc1337_action:testaction:1 + + + + Disable the cron_can_relabel SELinux Boolean + + ocil:ssg-sebool_cron_can_relabel_action:testaction:1 + + + + Ensure Sudo Logfile Exists - sudo logfile + + ocil:ssg-sudo_custom_logfile_action:testaction:1 + + + + Disable the swift_can_network SELinux Boolean + + ocil:ssg-sebool_swift_can_network_action:testaction:1 + + + + Record Events that Modify the System's Discretionary Access Controls - removexattr + + ocil:ssg-audit_rules_dac_modification_removexattr_action:testaction:1 + + + + Disable the secure_mode SELinux Boolean + + ocil:ssg-sebool_secure_mode_action:testaction:1 + + + + Ensure IPv6 is disabled through kernel boot parameter + + ocil:ssg-grub2_ipv6_disable_argument_action:testaction:1 + + + + Require modules to be validly signed + + ocil:ssg-kernel_config_module_sig_force_action:testaction:1 + + + + Assign Expiration Date to Emergency Accounts + + ocil:ssg-account_emergency_expire_date_action:testaction:1 + + + + Record Events that Modify User/Group Information - /etc/gshadow + + ocil:ssg-audit_rules_usergroup_modification_gshadow_action:testaction:1 + + + + Enforce Spectre v2 mitigation + + ocil:ssg-grub2_spectre_v2_argument_action:testaction:1 + + + + Disable the squid_use_tproxy SELinux Boolean + + ocil:ssg-sebool_squid_use_tproxy_action:testaction:1 + + + + Enable rsyslog Service + + ocil:ssg-service_rsyslog_enabled_action:testaction:1 + + + + Disable the ssh_sysadm_login SELinux Boolean + + ocil:ssg-sebool_ssh_sysadm_login_action:testaction:1 + + + + Disable the httpd_can_connect_ftp SELinux Boolean + + ocil:ssg-sebool_httpd_can_connect_ftp_action:testaction:1 + + + + Disable All GNOME3 Thumbnailers + + ocil:ssg-dconf_gnome_disable_thumbnailers_action:testaction:1 + + + + Record Successful Permission Changes to Files - lsetxattr + + ocil:ssg-audit_rules_successful_file_modification_lsetxattr_action:testaction:1 + + + + Disable the ftpd_use_fusefs SELinux Boolean + + ocil:ssg-sebool_ftpd_use_fusefs_action:testaction:1 + + + + Resolve information before writing to audit logs + + ocil:ssg-auditd_log_format_action:testaction:1 + + + + Limit Password Reuse: system-auth + + ocil:ssg-accounts_password_pam_pwhistory_remember_system_auth_action:testaction:1 + + + + Disable the mpd_use_nfs SELinux Boolean + + ocil:ssg-sebool_mpd_use_nfs_action:testaction:1 + + + + Configure auditd space_left on Low Disk Space + + ocil:ssg-auditd_data_retention_space_left_action:testaction:1 + + + + Record Successful Ownership Changes to Files - fchown + + ocil:ssg-audit_rules_successful_file_modification_fchown_action:testaction:1 + + + + Disable named Service + + ocil:ssg-service_named_disabled_action:testaction:1 + + + + Disable IA32 emulation + + ocil:ssg-kernel_config_ia32_emulation_action:testaction:1 + + + + Disable Kernel Parameter for Accepting ICMP Redirects by Default on IPv6 Interfaces + + ocil:ssg-sysctl_net_ipv6_conf_default_accept_redirects_action:testaction:1 + + + + Record Events that Modify the System's Mandatory Access Controls + + ocil:ssg-audit_rules_mac_modification_action:testaction:1 + + + + Disable Compression Or Set Compression to delayed + + ocil:ssg-sshd_disable_compression_action:testaction:1 + + + + Disable the httpd_use_fusefs SELinux Boolean + + ocil:ssg-sebool_httpd_use_fusefs_action:testaction:1 + + + + Verify that system commands files are group owned by root or a system account + + ocil:ssg-file_groupownership_system_commands_dirs_action:testaction:1 + + + + Disable the httpd_setrlimit SELinux Boolean + + ocil:ssg-sebool_httpd_setrlimit_action:testaction:1 + + + + Set Permissions on All Configuration Files Inside /etc/httpd/conf/ + + ocil:ssg-file_permissions_httpd_server_conf_files_action:testaction:1 + + + + Uninstall DHCP Server Package + + ocil:ssg-package_dhcp_removed_action:testaction:1 + + + + Record Unsuccessful Permission Changes to Files - chmod + + ocil:ssg-audit_rules_unsuccessful_file_modification_chmod_action:testaction:1 + + + + Distribute the SSH Server configuration to multiple files in a config directory. + + ocil:ssg-sshd_use_directory_configuration_action:testaction:1 + + + + Set SSH Idle Timeout Interval + + ocil:ssg-sshd_set_idle_timeout_action:testaction:1 + + + + Record Successful Access Attempts to Files - open_by_handle_at + + ocil:ssg-audit_rules_successful_file_modification_open_by_handle_at_action:testaction:1 + + + + Disable the virt_read_qemu_ga_data SELinux Boolean + + ocil:ssg-sebool_virt_read_qemu_ga_data_action:testaction:1 + + + + Strong Stack Protector + + ocil:ssg-kernel_config_stackprotector_strong_action:testaction:1 + + + + zero-init everything passed by reference + + ocil:ssg-kernel_config_gcc_plugin_structleak_byref_all_action:testaction:1 + + + + Remove NIS Client + + ocil:ssg-package_ypbind_removed_action:testaction:1 + + + + Verify ufw Enabled + + ocil:ssg-service_ufw_enabled_action:testaction:1 + + + + Record Any Attempts to Run seunshare + + ocil:ssg-audit_rules_execution_seunshare_action:testaction:1 + + + + System Audit Logs Must Be Owned By Root + + ocil:ssg-file_ownership_var_log_audit_stig_action:testaction:1 + + + + Enable Kernel Parameter to Use TCP Syncookies on Network Interfaces + + ocil:ssg-sysctl_net_ipv4_tcp_syncookies_action:testaction:1 + + + + Ensure auditd Collects Information on the Use of Privileged Commands - mount + + ocil:ssg-audit_rules_privileged_commands_mount_action:testaction:1 + + + + Disable storing core dump + + ocil:ssg-coredump_disable_storage_action:testaction:1 + + + + Disable the virt_sandbox_use_sys_admin SELinux Boolean + + ocil:ssg-sebool_virt_sandbox_use_sys_admin_action:testaction:1 + + + + Record Events that Modify the System's Discretionary Access Controls - lchown + + ocil:ssg-audit_rules_dac_modification_lchown_action:testaction:1 + + + + Disable Network File System (nfs) + + ocil:ssg-service_nfs_disabled_action:testaction:1 + + + + Enable the staff_exec_content SELinux Boolean + + ocil:ssg-sebool_staff_exec_content_action:testaction:1 + + + + Enable the virt_sandbox_use_audit SELinux Boolean + + ocil:ssg-sebool_virt_sandbox_use_audit_action:testaction:1 + + + + Enable SLUB/SLAB allocator poisoning + + ocil:ssg-grub2_slub_debug_argument_action:testaction:1 + + + + Add nosuid Option to /var/log + + ocil:ssg-mount_option_var_log_nosuid_action:testaction:1 + + + + Enable page allocator poisoning + + ocil:ssg-grub2_page_poison_argument_action:testaction:1 + + + + Ensure There Are No Accounts With Blank or Null Passwords + + ocil:ssg-no_empty_passwords_etc_shadow_action:testaction:1 + + + + Use zero for poisoning instead of debugging value + + ocil:ssg-kernel_config_page_poisoning_zero_action:testaction:1 + + + + Enable the user_exec_content SELinux Boolean + + ocil:ssg-sebool_user_exec_content_action:testaction:1 + + + + Verify Permissions on /var/log Directory + + ocil:ssg-file_permissions_var_log_action:testaction:1 + + + + Ensure auditd Collects Information on the Use of Privileged Commands - kmod + + ocil:ssg-audit_rules_privileged_commands_kmod_action:testaction:1 + + + + Disable GDM Automatic Login + + ocil:ssg-gnome_gdm_disable_automatic_login_action:testaction:1 + + + + Record Unsuccessful Permission Changes to Files - fchmodat + + ocil:ssg-audit_rules_unsuccessful_file_modification_fchmodat_action:testaction:1 + + + + Verify Permissions on shadow File + + ocil:ssg-file_permissions_etc_shadow_action:testaction:1 + + + + Prevent non-Privileged Users from Modifying Network Interfaces using nmcli + + ocil:ssg-network_nmcli_permissions_action:testaction:1 + + + + Enable the xend_run_blktap SELinux Boolean + + ocil:ssg-sebool_xend_run_blktap_action:testaction:1 + + + + Configure auditd Disk Error Action on Disk Error + + ocil:ssg-auditd_data_disk_error_action_stig_action:testaction:1 + + + + Account Lockouts Must Persist + + ocil:ssg-account_passwords_pam_faillock_dir_action:testaction:1 + + + + Disable hibernation + + ocil:ssg-kernel_config_hibernation_action:testaction:1 + + + + Configure auditing of successful permission changes (AArch64) + + ocil:ssg-audit_perm_change_success_aarch64_action:testaction:1 + + + + Set SSH MaxSessions limit + + ocil:ssg-sshd_set_max_sessions_action:testaction:1 + + + + Shutdown System When Auditing Failures Occur + + ocil:ssg-audit_rules_system_shutdown_action:testaction:1 + + + + Record Successful Permission Changes to Files - fchmodat + + ocil:ssg-audit_rules_successful_file_modification_fchmodat_action:testaction:1 + + + + Ensure Rsyslog Authenticates Off-Loaded Audit Records + + ocil:ssg-rsyslog_encrypt_offload_actionsendstreamdriverauthmode_action:testaction:1 + + + + Verify that System Executables Have Root Ownership + + ocil:ssg-file_ownership_binary_dirs_action:testaction:1 + + + + All Interactive Users Home Directories Must Exist + + ocil:ssg-accounts_user_interactive_home_directory_exists_action:testaction:1 + + + + Disable the global_ssp SELinux Boolean + + ocil:ssg-sebool_global_ssp_action:testaction:1 + + + + Disable kexec system call + + ocil:ssg-kernel_config_kexec_action:testaction:1 + + + + Ensure the Default Umask is Set Correctly in login.defs + + ocil:ssg-accounts_umask_etc_login_defs_action:testaction:1 + + + + Limit the Number of Concurrent Login Sessions Allowed Per User + + ocil:ssg-accounts_max_concurrent_login_sessions_action:testaction:1 + + + + Disable SSH root Login with a Password (Insecure) + + ocil:ssg-sshd_disable_root_password_login_action:testaction:1 + + + + Set the UEFI Boot Loader Admin Username to a Non-Default Value + + ocil:ssg-grub2_uefi_admin_username_action:testaction:1 + + + + Add nosuid Option to Removable Media Partitions + + ocil:ssg-mount_option_nosuid_removable_partitions_action:testaction:1 + + + + Limit CPU consumption of the Perf system + + ocil:ssg-sysctl_kernel_perf_cpu_time_max_percent_action:testaction:1 + + + + Uninstall talk-server Package + + ocil:ssg-package_talk-server_removed_action:testaction:1 + + + + Audit Configuration Files Must Be Owned By Group root + + ocil:ssg-file_groupownership_audit_configuration_action:testaction:1 + + + + Disable the xserver_execmem SELinux Boolean + + ocil:ssg-sebool_xserver_execmem_action:testaction:1 + + + + Include Local Events in Audit Logs + + ocil:ssg-auditd_local_events_action:testaction:1 + + + + Disable SSH Root Login + + ocil:ssg-sshd_disable_root_login_action:testaction:1 + + + + Add nodev Option to Non-Root Local Partitions + + ocil:ssg-mount_option_nodev_nonroot_local_partitions_action:testaction:1 + + + + Enable GSSAPI Authentication + + ocil:ssg-sshd_enable_gssapi_auth_action:testaction:1 + + + + Record Unsuccessful Modification Attempts to Files - open_by_handle_at O_TRUNC_WRITE + + ocil:ssg-audit_rules_unsuccessful_file_modification_open_by_handle_at_o_trunc_write_action:testaction:1 + + + + Disable the spamassassin_can_network SELinux Boolean + + ocil:ssg-sebool_spamassassin_can_network_action:testaction:1 + + + + Add nodev Option to Removable Media Partitions + + ocil:ssg-mount_option_nodev_removable_partitions_action:testaction:1 + + + + Verify Permissions on /etc/audit/auditd.conf + + ocil:ssg-file_permissions_etc_audit_auditd_action:testaction:1 + + + + Verify Group Who Owns Backup passwd File + + ocil:ssg-file_groupowner_backup_etc_passwd_action:testaction:1 + + + + Configure file name of core dumps + + ocil:ssg-sysctl_kernel_core_uses_pid_action:testaction:1 + + + + Ensure Users Cannot Change GNOME3 Session Idle Settings + + ocil:ssg-dconf_gnome_session_idle_user_locks_action:testaction:1 + + + + Configure Time Service Maxpoll Interval + + ocil:ssg-chronyd_or_ntpd_set_maxpoll_action:testaction:1 + + + + Disable the cdrecord_read_content SELinux Boolean + + ocil:ssg-sebool_cdrecord_read_content_action:testaction:1 + + + + The Installed Operating System Is FIPS 140-2 Certified + + ocil:ssg-installed_OS_is_FIPS_certified_action:testaction:1 + + + + Verify Owner on cron.monthly + + ocil:ssg-file_owner_cron_monthly_action:testaction:1 + + + + Ensure invoking users password for privilege escalation when using sudo + + ocil:ssg-sudoers_validate_passwd_action:testaction:1 + + + + Add noexec Option to Removable Media Partitions + + ocil:ssg-mount_option_noexec_removable_partitions_action:testaction:1 + + + + Record attempts to alter time through adjtimex + + ocil:ssg-audit_rules_time_adjtimex_action:testaction:1 + + + + Disable SSH Support for .rhosts Files + + ocil:ssg-sshd_disable_rhosts_action:testaction:1 + + + + Configure auditing of unsuccessful file modifications (AARch64) + + ocil:ssg-audit_modify_failed_aarch64_action:testaction:1 + + + + Enable GNOME3 Screensaver Lock After Idle Period + + ocil:ssg-dconf_gnome_screensaver_lock_enabled_action:testaction:1 + + + + Enable Encrypted X11 Forwarding + + ocil:ssg-sshd_enable_x11_forwarding_action:testaction:1 + + + + Configure audispd Plugin To Send Logs To Remote Server + + ocil:ssg-auditd_audispd_configure_remote_server_action:testaction:1 + + + + Disable the telepathy_tcp_connect_generic_network_ports SELinux Boolean + + ocil:ssg-sebool_telepathy_tcp_connect_generic_network_ports_action:testaction:1 + + + + Ensure auditd Collects Information on the Use of Privileged Commands - su + + ocil:ssg-audit_rules_privileged_commands_su_action:testaction:1 + + + + Prevent Routing External Traffic to Local Loopback on All IPv4 Interfaces + + ocil:ssg-sysctl_net_ipv4_conf_all_route_localnet_action:testaction:1 + + + + Force opensc To Use Defined Smart Card Driver + + ocil:ssg-force_opensc_card_drivers_action:testaction:1 + + + + Enable the secadm_exec_content SELinux Boolean + + ocil:ssg-sebool_secadm_exec_content_action:testaction:1 + + + + Harden common str/mem functions against buffer overflows + + ocil:ssg-kernel_config_fortify_source_action:testaction:1 + + + + Disable ypserv Service + + ocil:ssg-service_ypserv_disabled_action:testaction:1 + + + + Configure auditing of successful ownership changes (ppc64le) + + ocil:ssg-audit_owner_change_success_ppc64le_action:testaction:1 + + + + Configure CA certificate for rsyslog remote logging + + ocil:ssg-rsyslog_remote_tls_cacert_action:testaction:1 + + + + Disable the virt_sandbox_use_mknod SELinux Boolean + + ocil:ssg-sebool_virt_sandbox_use_mknod_action:testaction:1 + + + + Configure session renegotiation for SSH client + + ocil:ssg-ssh_client_rekey_limit_action:testaction:1 + + + + Verify that All World-Writable Directories Have Sticky Bits Set + + ocil:ssg-dir_perms_world_writable_sticky_bits_action:testaction:1 + + + + Install policycoreutils Package + + ocil:ssg-package_policycoreutils_installed_action:testaction:1 + + + + Verify Permissions on /var/log/syslog File + + ocil:ssg-file_permissions_var_log_syslog_action:testaction:1 + + + + Add noauto Option to /boot + + ocil:ssg-mount_option_boot_noauto_action:testaction:1 + + + + System Audit Directories Must Be Group Owned By Root + + ocil:ssg-directory_group_ownership_var_log_audit_action:testaction:1 + + + + Remove the FreeRadius Server Package + + ocil:ssg-package_freeradius_removed_action:testaction:1 + + + + Disable the virt_rw_qemu_ga_data SELinux Boolean + + ocil:ssg-sebool_virt_rw_qemu_ga_data_action:testaction:1 + + + + Disable the httpd_mod_auth_ntlm_winbind SELinux Boolean + + ocil:ssg-sebool_httpd_mod_auth_ntlm_winbind_action:testaction:1 + + + + Ensure auditd Collects File Deletion Events by User + + ocil:ssg-audit_rules_file_deletion_events_action:testaction:1 + + + + Configure AIDE to Use FIPS 140-2 for Validating Hashes + + ocil:ssg-aide_use_fips_hashes_action:testaction:1 + + + + Assign Expiration Date to Temporary Accounts + + ocil:ssg-account_temp_expire_date_action:testaction:1 + + + + Ensure SSH LoginGraceTime is configured + + ocil:ssg-sshd_set_login_grace_time_action:testaction:1 + + + + Ensure auditd Collects Information on the Use of Privileged Commands - newgidmap + + ocil:ssg-audit_rules_privileged_commands_newgidmap_action:testaction:1 + + + + Enable the nfs_export_all_rw SELinux Boolean + + ocil:ssg-sebool_nfs_export_all_rw_action:testaction:1 + + + + Direct root Logins Not Allowed + + ocil:ssg-no_direct_root_logins_action:testaction:1 + + + + Configure auditd mail_acct Action on Low Disk Space + + ocil:ssg-auditd_data_retention_action_mail_acct_action:testaction:1 + + + + Disable the mmap_low_allowed SELinux Boolean + + ocil:ssg-sebool_mmap_low_allowed_action:testaction:1 + + + + Ensure No World-Writable Files Exist + + ocil:ssg-file_permissions_unauthorized_world_writable_action:testaction:1 + + + + Disable the sge_use_nfs SELinux Boolean + + ocil:ssg-sebool_sge_use_nfs_action:testaction:1 + + + + Install subscription-manager Package + + ocil:ssg-package_subscription-manager_installed_action:testaction:1 + + + + Enable GNOME3 Login Warning Banner + + ocil:ssg-dconf_gnome_banner_enabled_action:testaction:1 + + + + Disable the authlogin_nsswitch_use_ldap SELinux Boolean + + ocil:ssg-sebool_authlogin_nsswitch_use_ldap_action:testaction:1 + + + + Disable SSH TCP Forwarding + + ocil:ssg-sshd_disable_tcp_forwarding_action:testaction:1 + + + + Enable cron Service + + ocil:ssg-service_crond_enabled_action:testaction:1 + + + + Verify that Interactive Boot is Disabled + + ocil:ssg-grub2_disable_interactive_boot_action:testaction:1 + + + + Record Any Attempts to Run ssh-agent + + ocil:ssg-audit_rules_privileged_commands_ssh_agent_action:testaction:1 + + + + Ensure PAM Enforces Password Requirements - Enforce for Local Accounts Only + + ocil:ssg-accounts_password_pam_enforce_local_action:testaction:1 + + + + Record Events that Modify the System's Discretionary Access Controls - lremovexattr + + ocil:ssg-audit_rules_dac_modification_lremovexattr_action:testaction:1 + + + + Enable NX or XD Support in the BIOS + + ocil:ssg-bios_enable_execution_restrictions_action:testaction:1 + + + + Configure auditd flush priority + + ocil:ssg-auditd_data_retention_flush_action:testaction:1 + + + + Verify that Shared Library Files Have Root Ownership + + ocil:ssg-file_ownership_library_dirs_action:testaction:1 + + + + Ensure SELinux Not Disabled in the kernel arguments + + ocil:ssg-coreos_enable_selinux_kernel_argument_action:testaction:1 + + + + Enable the pcscd Service + + ocil:ssg-service_pcscd_enabled_action:testaction:1 + + + + Record Successful Permission Changes to Files - fsetxattr + + ocil:ssg-audit_rules_successful_file_modification_fsetxattr_action:testaction:1 + + + + Disable the ftpd_use_nfs SELinux Boolean + + ocil:ssg-sebool_ftpd_use_nfs_action:testaction:1 + + + + Add noexec Option to /dev/shm + + ocil:ssg-mount_option_dev_shm_noexec_action:testaction:1 + + + + Record Events that Modify User/Group Information - /etc/passwd + + ocil:ssg-audit_rules_usergroup_modification_passwd_action:testaction:1 + + + + Disable the tftp_anon_write SELinux Boolean + + ocil:ssg-sebool_tftp_anon_write_action:testaction:1 + + + + Configure SSH Server to Use FIPS 140-2 Validated Ciphers: opensshserver.config + + ocil:ssg-harden_sshd_ciphers_opensshserver_conf_crypto_policy_action:testaction:1 + + + + Verify Group Who Owns cron.d + + ocil:ssg-file_groupowner_cron_d_action:testaction:1 + + + + Uninstall rsh-server Package + + ocil:ssg-package_rsh-server_removed_action:testaction:1 + + + + Ensure all zIPL boot entries are BLS compliant + + ocil:ssg-zipl_bls_entries_only_action:testaction:1 + + + + Disable the puppetmaster_use_db SELinux Boolean + + ocil:ssg-sebool_puppetmaster_use_db_action:testaction:1 + + + + Disable Bluetooth Kernel Module + + ocil:ssg-kernel_module_bluetooth_disabled_action:testaction:1 + + + + Disable Kerberos by removing host keytab + + ocil:ssg-kerberos_disable_no_keytab_action:testaction:1 + + + + fapolicyd Must be Configured to Limit Access to Users Home Folders + + ocil:ssg-fapolicyd_prevent_home_folder_access_action:testaction:1 + + + + Add nosuid Option to /opt + + ocil:ssg-mount_option_opt_nosuid_action:testaction:1 + + + + Harden SSH client Crypto Policy + + ocil:ssg-harden_ssh_client_crypto_policy_action:testaction:1 + + + + Disable the virt_use_samba SELinux Boolean + + ocil:ssg-sebool_virt_use_samba_action:testaction:1 + + + + Set number of Password Hashing Rounds - password-auth + + ocil:ssg-accounts_password_pam_unix_rounds_password_auth_action:testaction:1 + + + + Configure Sending and Accepting Shared Media Redirects for All IPv4 Interfaces + + ocil:ssg-sysctl_net_ipv4_conf_all_shared_media_action:testaction:1 + + + + Configure Maximum Number of Autoconfigured Addresses on All IPv6 Interfaces By Default + + ocil:ssg-sysctl_net_ipv6_conf_default_max_addresses_action:testaction:1 + + + + Disable Accepting Router Advertisements on all IPv6 Interfaces by Default + + ocil:ssg-sysctl_net_ipv6_conf_default_accept_ra_action:testaction:1 + + + + Disable the ftpd_connect_db SELinux Boolean + + ocil:ssg-sebool_ftpd_connect_db_action:testaction:1 + + + + Enable seccomp to safely compute untrusted bytecode + + ocil:ssg-kernel_config_seccomp_action:testaction:1 + + + + Disable the samba_domain_controller SELinux Boolean + + ocil:ssg-sebool_samba_domain_controller_action:testaction:1 + + + + Configure the Firewalld Ports + + ocil:ssg-configure_firewalld_ports_action:testaction:1 + + + + Disable X11 Forwarding + + ocil:ssg-sshd_disable_x11_forwarding_action:testaction:1 + + + + Set GNOME3 Screensaver Lock Delay After Activation Period + + ocil:ssg-dconf_gnome_screensaver_lock_delay_action:testaction:1 + + + + Install Smart Card Packages For Multifactor Authentication + + ocil:ssg-install_smartcard_packages_action:testaction:1 + + + + Ensure auditd Collects Information on the Use of Privileged Commands - newgrp + + ocil:ssg-audit_rules_privileged_commands_newgrp_action:testaction:1 + + + + Ensure that System Accounts Are Locked + + ocil:ssg-no_password_auth_for_systemaccounts_action:testaction:1 + + + + Disable vsyscall emulate execution only + + ocil:ssg-kernel_config_legacy_vsyscall_xonly_action:testaction:1 + + + + Disable the cron_system_cronjob_use_shares SELinux Boolean + + ocil:ssg-sebool_cron_system_cronjob_use_shares_action:testaction:1 + + + + Record Unsuccessful Permission Changes to Files - fsetxattr + + ocil:ssg-audit_rules_unsuccessful_file_modification_fsetxattr_action:testaction:1 + + + + Verify Group Who Owns passwd File + + ocil:ssg-file_groupowner_etc_passwd_action:testaction:1 + + + + Verify User Who Owns /var/log Directory + + ocil:ssg-file_owner_var_log_action:testaction:1 + + + + Disable the git_cgi_enable_homedirs SELinux Boolean + + ocil:ssg-sebool_git_cgi_enable_homedirs_action:testaction:1 + + + + Disable GNOME3 Automount running + + ocil:ssg-dconf_gnome_disable_autorun_action:testaction:1 + + + + Disable storing core dumps + + ocil:ssg-sysctl_kernel_core_pattern_empty_string_action:testaction:1 + + + + Configure SSH Client to Use FIPS 140-2 Validated MACs: openssh.config + + ocil:ssg-harden_sshd_macs_openssh_conf_crypto_policy_action:testaction:1 + + + + Disable the zebra_write_config SELinux Boolean + + ocil:ssg-sebool_zebra_write_config_action:testaction:1 + + + + Disable the polipo_use_cifs SELinux Boolean + + ocil:ssg-sebool_polipo_use_cifs_action:testaction:1 + + + + Disable the selinuxuser_postgresql_connect_enabled SELinux Boolean + + ocil:ssg-sebool_selinuxuser_postgresql_connect_enabled_action:testaction:1 + + + + Record Successful Access Attempts to Files - creat + + ocil:ssg-audit_rules_successful_file_modification_creat_action:testaction:1 + + + + Uninstall setroubleshoot-plugins Package + + ocil:ssg-package_setroubleshoot-plugins_removed_action:testaction:1 + + + + Disable the samba_enable_home_dirs SELinux Boolean + + ocil:ssg-sebool_samba_enable_home_dirs_action:testaction:1 + + + + Ensure No Device Files are Unlabeled by SELinux + + ocil:ssg-selinux_all_devicefiles_labeled_action:testaction:1 + + + + Record Successful Access Attempts to Files - open + + ocil:ssg-audit_rules_successful_file_modification_open_action:testaction:1 + + + + Verify Group Who Owns gshadow File + + ocil:ssg-file_groupowner_etc_gshadow_action:testaction:1 + + + + Record Events that Modify the System's Discretionary Access Controls - chmod + + ocil:ssg-audit_rules_dac_modification_chmod_action:testaction:1 + + + + Install fapolicyd Package + + ocil:ssg-package_fapolicyd_installed_action:testaction:1 + + + + Disable the zarafa_setrlimit SELinux Boolean + + ocil:ssg-sebool_zarafa_setrlimit_action:testaction:1 + + + + Record Successful Ownership Changes to Files - chown + + ocil:ssg-audit_rules_successful_file_modification_chown_action:testaction:1 + + + + Install the Samba Common Package + + ocil:ssg-package_samba-common_installed_action:testaction:1 + + + + Limit sampling frequency of the Perf system + + ocil:ssg-sysctl_kernel_perf_event_max_sample_rate_action:testaction:1 + + + + Record Successful Delete Attempts to Files - unlink + + ocil:ssg-audit_rules_successful_file_modification_unlink_action:testaction:1 + + + + Disable the telepathy_connect_all_ports SELinux Boolean + + ocil:ssg-sebool_telepathy_connect_all_ports_action:testaction:1 + + + + Disable Power Settings in GNOME3 + + ocil:ssg-dconf_gnome_disable_power_settings_action:testaction:1 + + + + Disable the polipo_connect_all_unreserved SELinux Boolean + + ocil:ssg-sebool_polipo_connect_all_unreserved_action:testaction:1 + + + + Ensure /var/log Located On Separate Partition + + ocil:ssg-partition_for_var_log_action:testaction:1 + + + + Disable the selinuxuser_share_music SELinux Boolean + + ocil:ssg-sebool_selinuxuser_share_music_action:testaction:1 + + + + Require Encryption for Remote Access in GNOME3 + + ocil:ssg-dconf_gnome_remote_access_encryption_action:testaction:1 + + + + Verify Group Who Owns /etc/cron.allow file + + ocil:ssg-file_groupowner_cron_allow_action:testaction:1 + + + + Uninstall squid Package + + ocil:ssg-package_squid_removed_action:testaction:1 + + + + Disable the selinuxuser_execheap SELinux Boolean + + ocil:ssg-sebool_selinuxuser_execheap_action:testaction:1 + + + + Disable the cobbler_anon_write SELinux Boolean + + ocil:ssg-sebool_cobbler_anon_write_action:testaction:1 + + + + Authorize Human Interface Devices and USB hubs in USBGuard daemon + + ocil:ssg-usbguard_allow_hid_and_hub_action:testaction:1 + + + + Disable the deny_ptrace SELinux Boolean + + ocil:ssg-sebool_deny_ptrace_action:testaction:1 + + + + Ensure auditd Collects File Deletion Events by User - unlink + + ocil:ssg-audit_rules_file_deletion_events_unlink_action:testaction:1 + + + + Restrict usage of ptrace to descendant processes + + ocil:ssg-sysctl_kernel_yama_ptrace_scope_action:testaction:1 + + + + Disable the git_system_enable_homedirs SELinux Boolean + + ocil:ssg-sebool_git_system_enable_homedirs_action:testaction:1 + + + + Record Any Attempts to Run setfacl + + ocil:ssg-audit_rules_execution_setfacl_action:testaction:1 + + + + Prefer to use a 64-bit Operating System when supported + + ocil:ssg-prefer_64bit_os_action:testaction:1 + + + + Verify Permissions on passwd File + + ocil:ssg-file_permissions_etc_passwd_action:testaction:1 + + + + Disable At Service (atd) + + ocil:ssg-service_atd_disabled_action:testaction:1 + + + + Configure AIDE to Verify Access Control Lists (ACLs) + + ocil:ssg-aide_verify_acls_action:testaction:1 + + + + Record Events that Modify the System's Discretionary Access Controls - fchownat + + ocil:ssg-audit_rules_dac_modification_fchownat_action:testaction:1 + + + + Ensure PAM Enforces Password Requirements - Minimum Digit Characters + + ocil:ssg-accounts_password_pam_dcredit_action:testaction:1 + + + + Configure kernel to trust the CPU random number generator + + ocil:ssg-grub2_kernel_trust_cpu_rng_action:testaction:1 + + + + Record Successful Delete Attempts to Files - unlinkat + + ocil:ssg-audit_rules_successful_file_modification_unlinkat_action:testaction:1 + + + + Verify Group Who Owns /var/log/messages File + + ocil:ssg-file_groupowner_var_log_messages_action:testaction:1 + + + + Set number of Password Hashing Rounds - system-auth + + ocil:ssg-accounts_password_pam_unix_rounds_system_auth_action:testaction:1 + + + + Ensure SELinux Not Disabled in zIPL + + ocil:ssg-zipl_enable_selinux_action:testaction:1 + + + + Enforce usage of pam_wheel for su authentication + + ocil:ssg-use_pam_wheel_for_su_action:testaction:1 + + + + Mount Remote Filesystems with nosuid + + ocil:ssg-mount_option_nosuid_remote_filesystems_action:testaction:1 + + + + Enable checks on notifier call chains + + ocil:ssg-kernel_config_debug_notifiers_action:testaction:1 + + + + Add nodev Option to /dev/shm + + ocil:ssg-mount_option_dev_shm_nodev_action:testaction:1 + + + + Uninstall xinetd Package + + ocil:ssg-package_xinetd_removed_action:testaction:1 + + + + Disable the postgresql_selinux_transmit_client_label SELinux Boolean + + ocil:ssg-sebool_postgresql_selinux_transmit_client_label_action:testaction:1 + + + + Enable SSH Print Last Log + + ocil:ssg-sshd_print_last_log_action:testaction:1 + + + + Set Permissions on All Configuration Files Inside /etc/httpd/conf.d/ + + ocil:ssg-file_permissions_httpd_server_conf_d_files_action:testaction:1 + + + + Ensure All Accounts on the System Have Unique User IDs + + ocil:ssg-account_unique_id_action:testaction:1 + + + + Disable storing core dumps + + ocil:ssg-sysctl_kernel_core_pattern_action:testaction:1 + + + + Disable the GNOME3 Login Restart and Shutdown Buttons + + ocil:ssg-dconf_gnome_disable_restart_shutdown_action:testaction:1 + + + + Enable the postgresql_selinux_users_ddl SELinux Boolean + + ocil:ssg-sebool_postgresql_selinux_users_ddl_action:testaction:1 + + + + Audit Tools Must Have a Mode of 0755 or Less Permissive + + ocil:ssg-file_audit_tools_permissions_action:testaction:1 + + + + Ensure PAM Enforces Password Requirements - Prevent the Use of Dictionary Words + + ocil:ssg-accounts_password_pam_dictcheck_action:testaction:1 + + + + Disable PubkeyAuthentication Authentication + + ocil:ssg-sshd_disable_pubkey_auth_action:testaction:1 + + + + Disable the samba_load_libgfapi SELinux Boolean + + ocil:ssg-sebool_samba_load_libgfapi_action:testaction:1 + + + + Enable the auditadm_exec_content SELinux Boolean + + ocil:ssg-sebool_auditadm_exec_content_action:testaction:1 + + + + Disable Kerberos Authentication + + ocil:ssg-sshd_disable_kerb_auth_action:testaction:1 + + + + Disable the mpd_enable_homedirs SELinux Boolean + + ocil:ssg-sebool_mpd_enable_homedirs_action:testaction:1 + + + + Configure opensc Smart Card Drivers + + ocil:ssg-configure_opensc_card_drivers_action:testaction:1 + + + + Ensure LDAP client is not installed + + ocil:ssg-package_openldap-clients_removed_action:testaction:1 + + + + Ensure All Files Are Owned by a Group + + ocil:ssg-file_permissions_ungroupowned_action:testaction:1 + + + + Set Permissions on All Configuration Files Inside /etc/httpd/conf.modules.d/ + + ocil:ssg-file_permissions_httpd_server_modules_files_action:testaction:1 + + + + Disable Kernel Parameter for Accepting ICMP Redirects by Default on IPv4 Interfaces + + ocil:ssg-sysctl_net_ipv4_conf_default_accept_redirects_action:testaction:1 + + + + Verify Group Who Owns cron.monthly + + ocil:ssg-file_groupowner_cron_monthly_action:testaction:1 + + + + Mount Remote Filesystems with noexec + + ocil:ssg-mount_option_noexec_remote_filesystems_action:testaction:1 + + + + Disable the samba_share_nfs SELinux Boolean + + ocil:ssg-sebool_samba_share_nfs_action:testaction:1 + + + + Enable the unconfined_login SELinux Boolean + + ocil:ssg-sebool_unconfined_login_action:testaction:1 + + + + Disallow Configuration to Bypass Password Requirements for Privilege Escalation + + ocil:ssg-disallow_bypass_password_sudo_action:testaction:1 + + + + Ensure auditd Collects Information on the Use of Privileged Commands - pam_timestamp_check + + ocil:ssg-audit_rules_privileged_commands_pam_timestamp_check_action:testaction:1 + + + + Disable Kernel Parameter for IPv6 Forwarding + + ocil:ssg-sysctl_net_ipv6_conf_all_forwarding_action:testaction:1 + + + + Disable the selinuxuser_udp_server SELinux Boolean + + ocil:ssg-sebool_selinuxuser_udp_server_action:testaction:1 + + + + Ensure Log Files Are Owned By Appropriate User + + ocil:ssg-rsyslog_files_ownership_action:testaction:1 + + + + Record Unsuccessful Ownership Changes to Files - chown + + ocil:ssg-audit_rules_unsuccessful_file_modification_chown_action:testaction:1 + + + + Set SSH Client Alive Count Max + + ocil:ssg-sshd_set_keepalive_action:testaction:1 + + + + Configure auditing of unsuccessful file creations (ppc64le) + + ocil:ssg-audit_create_failed_ppc64le_action:testaction:1 + + + + Verify Group Who Owns Crontab + + ocil:ssg-file_groupowner_crontab_action:testaction:1 + + + + Uninstall Samba Package + + ocil:ssg-package_samba_removed_action:testaction:1 + + + + Verify Permissions on /etc/at.allow file + + ocil:ssg-file_permissions_at_allow_action:testaction:1 + + + + Enable Kernel Parameter to Ignore ICMP Broadcast Echo Requests on IPv4 Interfaces + + ocil:ssg-sysctl_net_ipv4_icmp_echo_ignore_broadcasts_action:testaction:1 + + + + Ensure there are no legacy + NIS entries in /etc/shadow + + ocil:ssg-no_legacy_plus_entries_etc_shadow_action:testaction:1 + + + + Disable Accepting ICMP Redirects for All IPv6 Interfaces + + ocil:ssg-sysctl_net_ipv6_conf_all_accept_redirects_action:testaction:1 + + + + Encrypt Partitions + + ocil:ssg-encrypt_partitions_action:testaction:1 + + + + Set Lockout Time for Failed Password Attempts + + ocil:ssg-accounts_passwords_pam_faillock_unlock_time_action:testaction:1 + + + + Disable CAN Support + + ocil:ssg-kernel_module_can_disabled_action:testaction:1 + + + + Enable checks on linked list manipulation + + ocil:ssg-kernel_config_debug_list_action:testaction:1 + + + + Disallow kernel profiling by unprivileged users + + ocil:ssg-sysctl_kernel_perf_event_paranoid_action:testaction:1 + + + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + PASS + + + FAIL + + + + + + +Run the following command to determine if the dhcpd_use_ldap SELinux boolean is disabled: +$ getsebool dhcpd_use_ldap +If properly configured, the output should show the following: +dhcpd_use_ldap --> off + Is it the case that dhcpd_use_ldap is not disabled? + + + + + +Run the following command to determine the current status of the +sshd service: +$ sudo systemctl is-active sshd +If the service is running, it should return the following: active + Is it the case that sshd service is disabled? + + + + The runtime status of the net.ipv4.conf.all.log_martians kernel parameter can be queried +by running the following command: +$ sysctl net.ipv4.conf.all.log_martians +1. + + Is it the case that the correct value is not returned? + + + + To check the group ownership of /boot/grub2/grub.cfg, +run the command: +$ ls -lL /boot/grub2/grub.cfg +If properly configured, the output should indicate the following group-owner: +root + Is it the case that /boot/grub2/grub.cfg does not have a group owner of root? + + + + Verify the umask setting is configured correctly in the /etc/profile file +with the following command: +$ grep "umask" /etc/profile +umask + Is it the case that the value for the "umask" parameter is not "<sub idref="var_accounts_user_umask" />", +or the "umask" parameter is missing or is commented out? + + + + Run the following command to determine if the iprutils package is installed: +$ rpm -q iprutils + Is it the case that the package is installed? + + + + Run the following command to determine if the sendmail package is installed: +$ rpm -q sendmail + Is it the case that the package is installed? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_SLAB_FREELIST_HARDENED /boot/config.* + + For each kernel installed, a line with value "y" should be returned. + + Is it the case that the kernel was not built with the required value? + + + + +Run the following command to determine if the git_system_use_nfs SELinux boolean is disabled: +$ getsebool git_system_use_nfs +If properly configured, the output should show the following: +git_system_use_nfs --> off + Is it the case that git_system_use_nfs is not disabled? + + + + Verify the pam_faillock.so module is present in the "/etc/pam.d/system-auth" file: + +$ sudo grep pam_faillock.so /etc/pam.d/system-auth + +auth required pam_faillock.so preauth +auth required pam_faillock.so authfail +account required pam_faillock.so + Is it the case that the pam_faillock.so module is not present in the "/etc/pam.d/system-auth" file with the "preauth" line listed before pam_unix.so? + + + + Verify that the libuser is set to encrypt password with a FIPS 140-3 approved cryptographic hashing algorithm. + + +Check the hashing algorithm that is being used to hash passwords with the following command: + +$ sudo grep -i crypt_style /etc/libuser.conf + +crypt_style = sha512 + Is it the case that crypt_style is not set to sha512? + + + + To determine if the system is configured to audit successful calls +to the open system call, run the following command: +$ sudo grep "open" /etc/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + To verify that auditing of privileged command use is configured, run the +following command: +$ sudo grep newuidmap /etc/audit/audit.rules /etc/audit/rules.d/* +It should return a relevant line in the audit rules. + Is it the case that the command does not return a line, or the line is commented out? + + + + The existence of the file /etc/hosts.equiv or a file named +.rhosts inside a user home directory indicates the presence +of an Rsh trust relationship. + Is it the case that these files exist? + + + + Verify that sshd isn't configured to ignore the system wide cryptographic policy. + +Check that the CRYPTO_POLICY variable is not set or is commented out in the +/etc/sysconfig/sshd. + +Run the following command: + +$ sudo grep CRYPTO_POLICY /etc/sysconfig/sshd + Is it the case that the CRYPTO_POLICY variable is set or is not commented out in the /etc/sysconfig/sshd? + + + + +Run the following command to determine if the haproxy_connect_any SELinux boolean is disabled: +$ getsebool haproxy_connect_any +If properly configured, the output should show the following: +haproxy_connect_any --> off + Is it the case that haproxy_connect_any is not disabled? + + + + Verify that Red Hat Enterprise Linux 9 is configured to audit the execution of the "chage" command with the following command: + +$ sudo auditctl -l | grep chage + +-a always,exit -F path=/usr/bin/chage -F perm=x -F auid>=1000 -F auid!=unset -k privileged-chage + Is it the case that the command does not return a line, or the line is commented out? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_LEGACY_VSYSCALL_NONE /boot/config.* + + For each kernel installed, a line with value "y" should be returned. + + Is it the case that the kernel was not built with the required value? + + + + To determine if the system is configured to audit unsuccessful calls +to the fchmod system call, run the following command: +$ sudo grep "fchmod" /etc/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + +Run the following command to determine if the smbd_anon_write SELinux boolean is disabled: +$ getsebool smbd_anon_write +If properly configured, the output should show the following: +smbd_anon_write --> off + Is it the case that smbd_anon_write is not disabled? + + + + +Run the following command to determine if the httpd_anon_write SELinux boolean is disabled: +$ getsebool httpd_anon_write +If properly configured, the output should show the following: +httpd_anon_write --> off + Is it the case that httpd_anon_write is not disabled? + + + + +Run the following command to determine if the logwatch_can_network_connect_mail SELinux boolean is disabled: +$ getsebool logwatch_can_network_connect_mail +If properly configured, the output should show the following: +logwatch_can_network_connect_mail --> off + Is it the case that logwatch_can_network_connect_mail is not disabled? + + + + Inspect the file /etc/firewalld/firewalld.conf to determine +the default zone for the firewalld. It should be set to DefaultZone=drop: +$ sudo grep DefaultZone /etc/firewalld/firewalld.conf + Is it the case that the default zone is not set to DROP? + + + + +Run the following command to determine if the httpd_can_connect_mythtv SELinux boolean is disabled: +$ getsebool httpd_can_connect_mythtv +If properly configured, the output should show the following: +httpd_can_connect_mythtv --> off + Is it the case that httpd_can_connect_mythtv is not disabled? + + + + To verify that the Audit is correctly configured according to recommended rules, check the content of the file with the following command: +cat /etc/audit/rules.d/30-ospp-v42-2-modify-success.rules +The output has to be exactly as follows: +## Successful file modifications (open for write or truncate) +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification +-a always,exit -F arch=b64 -S open -F a1&01003 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification +-a always,exit -F arch=b64 -S truncate,ftruncate -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification + Is it the case that the file does not exist or the content differs? + + + + +Run the following command to determine if the gpg_web_anon_write SELinux boolean is disabled: +$ getsebool gpg_web_anon_write +If properly configured, the output should show the following: +gpg_web_anon_write --> off + Is it the case that gpg_web_anon_write is not disabled? + + + + If the system is not configured to audit time changes, this is a finding. +If the system is 64-bit only, this is not applicable +ocil: | +To determine if the system is configured to audit calls to the +stime system call, run the following command: +$ sudo grep "stime" /etc/audit/audit.* +If the system is configured to audit this activity, it will return a line. + Is it the case that no line is returned? + + + + The file /etc/cron.deny should not exist. +This can be checked by runnig the following + +stat /etc/cron.deny + +and the output should be + +stat: cannot stat `/etc/cron.deny': No such file or directory + + Is it the case that the file /etc/cron.deny exists? + + + + +Run the following command to determine if the httpd_tty_comm SELinux boolean is disabled: +$ getsebool httpd_tty_comm +If properly configured, the output should show the following: +httpd_tty_comm --> off + Is it the case that httpd_tty_comm is not disabled? + + + + Run the following command to determine if the talk package is installed: +$ rpm -q talk + Is it the case that the package is installed? + + + + To verify that Linux Audit logging is enabled for the USBGuard daemon, +run the following command: +$ sudo grep AuditBackend /etc/usbguard/usbguard-daemon.conf +The output should be +AuditBackend=LinuxAudit + Is it the case that AuditBackend is not set to LinuxAudit? + + + + To verify that tmux is not listed as allowed shell on the system +run the following command: +$ grep 'tmux$' /etc/shells +The output should be empty. + Is it the case that tmux is listed in /etc/shells? + + + + Verify the "/etc/security/faillock.conf" file is configured to log user name information when unsuccessful logon attempts occur: + +$ sudo grep audit /etc/security/faillock.conf + +audit + Is it the case that the "audit" option is not set, is missing or commented out? + + + + To check if authentication is required for single-user mode, run the following command: +$ grep sulogin /usr/lib/systemd/system/rescue.service +The output should be similar to the following, and the line must begin with +ExecStart and /usr/lib/systemd/systemd-sulogin-shell. + ExecStart=-/usr/lib/systemd/systemd-sulogin-shell rescue + + +Then, verify that the rescue service is in the runlevel1.target. +Run the following command: +$ sudo grep "^Requires=.*rescue\.service" /usr/lib/systemd/system/runlevel1.target +The output should be the following: +Requires=sysinit.target rescue.service + +Then, check if there is no custom runlevel1 target configured in systemd configuration. +Run the following command: +$ sudo grep -r "^runlevel1.target$" /etc/systemd/system +There should be no output. + +Then, check if there is no custom rescue service configured in systemd configuration. +Run the following command: +$ sudo grep -r "^rescue.service$" /etc/systemd/system +There should be no output. + Is it the case that the output is different? + + + + Run the following command to ensure the default FORWARD policy is DROP: +grep ":FORWARD" /etc/sysconfig/iptables +The output should be similar to the following: +$ sudo grep ":FORWARD" /etc/sysconfig/iptables +:FORWARD DROP [0:0 + Is it the case that the default policy for the FORWARD chain is not set to DROP? + + + + +Run the following command to determine if the httpd_unified SELinux boolean is disabled: +$ getsebool httpd_unified +If properly configured, the output should show the following: +httpd_unified --> off + Is it the case that httpd_unified is not disabled? + + + + To check that the dovecot service is disabled in system boot configuration, +run the following command: +$ sudo systemctl is-enabled dovecot +Output should indicate the dovecot service has either not been installed, +or has been disabled at all runlevels, as shown in the example below: +$ sudo systemctl is-enabled dovecot disabled + +Run the following command to verify dovecot is not active (i.e. not running) through current runtime configuration: +$ sudo systemctl is-active dovecot + +If the service is not running the command will return the following output: +inactive + +The service will also be masked, to check that the dovecot is masked, run the following command: +$ sudo systemctl show dovecot | grep "LoadState\|UnitFileState" + +If the service is masked the command will return the following outputs: + +LoadState=masked + +UnitFileState=masked + Is it the case that the "dovecot" is loaded and not masked? + + + + Verify the system commands contained in the following directories have mode "755" or less permissive with the following command: + +$ sudo find -L /bin /sbin /usr/bin /usr/sbin /usr/libexec /usr/local/bin /usr/local/sbin -perm /022 -exec ls -l {} \; + Is it the case that any system commands are found to be group-writable or world-writable? + + + + Verify Red Hat Enterprise Linux 9 generates an audit record for unsuccessful attempts to create files using the open system call with O_CREAT flag. + +If the auditd daemon is configured to use the "augenrules" program to read audit rules during daemon startup (the default), run the following command: + +$ sudo grep -r open /etc/audit/rules.d + +If the auditd daemon is configured to use the "auditctl" utility to read audit rules during daemon startup, run the following command: + +$ sudo grep open /etc/audit/audit.rules + +The output should be the following: + +-a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + Is it the case that the command does not return a line, or the line is commented out? + + + + To verify that all user initialization files have a mode of 0740 or +less permissive, run the following command: +$ sudo find /home -type f -name '\.*' \( -perm -0002 -o -perm -0020 \) +There should be no output. + Is it the case that they are not 0740 or more permissive? + + + + +Run the following command to determine if the use_samba_home_dirs SELinux boolean is disabled: +$ getsebool use_samba_home_dirs +If properly configured, the output should show the following: +use_samba_home_dirs --> off + Is it the case that use_samba_home_dirs is not disabled? + + + + Run the following command to determine if the sudo package is installed: $ rpm -q sudo + Is it the case that the package is not installed? + + + + To ensure the MaxAuthTries parameter is set, run the following command: +$ sudo grep MaxAuthTries /etc/ssh/sshd_config +If properly configured, output should be: +MaxAuthTries + Is it the case that it is commented out or not configured properly? + + + + +Run the following command to determine if the mozilla_read_content SELinux boolean is disabled: +$ getsebool mozilla_read_content +If properly configured, the output should show the following: +mozilla_read_content --> off + Is it the case that mozilla_read_content is not disabled? + + + + To determine how the SSH daemon's PermitEmptyPasswords option is set, run the following command: + +$ sudo grep -i PermitEmptyPasswords /etc/ssh/sshd_config.d/00-complianceascode-hardening.conf +$ sudo grep -i PermitEmptyPasswords /etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf + +If a line indicating no is returned, then the required value is set. + + Is it the case that the required value is not set? + + + + To ensure ClientAliveInterval is set correctly, run the following command: + +$ sudo grep ClientAliveCountMax /etc/ssh/sshd_config /etc/ssh/sshd_config.d/*.conf + +If properly configured, the output should be: +ClientAliveCountMax 0 + +In this case, the SSH idle timeout occurs precisely when +the ClientAliveInterval is set. + Is it the case that it is commented out or not configured properly? + + + + Run the following command to determine if the rear package is installed: $ rpm -q rear + Is it the case that the package is not installed? + + + + To verify that a remote NTP service is configured for time synchronization, +open the following file: +/etc/ntp.conf +In the file, there should be a section similar to the following: +server ntpserver + Is it the case that this is not the case? + + + + +Run the following command to determine if the httpd_use_sasl SELinux boolean is disabled: +$ getsebool httpd_use_sasl +If properly configured, the output should show the following: +httpd_use_sasl --> off + Is it the case that httpd_use_sasl is not disabled? + + + + Verify the system-wide shared library files are group-owned by "root" with the following command: + +$ sudo find -L /lib /lib64 /usr/lib /usr/lib64 ! -group root -exec ls -l {} \; + Is it the case that any system wide shared library file is returned and is not group-owned by a required system account? + + + + Verify the operating system requires re-authentication +when using the "sudo" command to elevate privileges, run the following command: +sudo grep -ri '^Defaults.*timestamp_timeout' /etc/sudoers /etc/sudoers.d +The output should be: +/etc/sudoers:Defaults timestamp_timeout=0 or "timestamp_timeout" is set to a positive number. +If conflicting results are returned, this is a finding. + Is it the case that timestamp_timeout is not set with the appropriate value for sudo? + + + + To verify that only security updates will be automatically installed by dnf-automatic, run the following command: +$ sudo grep upgrade_type /etc/dnf/automatic.conf +The output should return the following: +upgrade_type = security + Is it the case that the upgrade_type is not set to security? + + + + Inspect /etc/audit/audisp-remote.conf and locate the following line to +determine if the system is configured to perform a correct action according to the policy: +$ sudo grep -i network_failure_action /etc/audit/audisp-remote.conf +The output should return: +network_failure_action = + Is it the case that the system is not configured to switch to single user mode for corrective action? + + + + To verify that auditing of privileged command use is configured, run the +following command: +$ sudo grep usernetctl /etc/audit/audit.rules /etc/audit/rules.d/* +It should return a relevant line in the audit rules. + Is it the case that the command does not return a line, or the line is commented out? + + + + To determine if !authenticate has not been configured for sudo, run the following command: +$ sudo grep -r \!authenticate /etc/sudoers /etc/sudoers.d/ +The command should return no output. + Is it the case that !authenticate is specified in the sudo config files? + + + + Run the following command to determine if the libpwquality package is installed: +$ rpm -q libpwquality + Is it the case that the package is not installed? + + + + The runtime status of the kernel.sysrq kernel parameter can be queried +by running the following command: +$ sysctl kernel.sysrq +0. + + Is it the case that the correct value is not returned? + + + + +Run the following command to determine if the httpd_can_connect_ldap SELinux boolean is disabled: +$ getsebool httpd_can_connect_ldap +If properly configured, the output should show the following: +httpd_can_connect_ldap --> off + Is it the case that httpd_can_connect_ldap is not disabled? + + + + +If the system is configured to prevent the loading of the firewire-core kernel module, +it will contain lines inside any file in /etc/modprobe.d or the deprecated /etc/modprobe.conf. +These lines instruct the module loading system to run another program (such as /bin/true) upon a module install event. + +Run the following command to search for such lines in all files in /etc/modprobe.d and the deprecated /etc/modprobe.conf: +$ grep -r firewire-core /etc/modprobe.conf /etc/modprobe.d + Is it the case that no line is returned? + + + + Verify the default target is multi-user, run the following command: +$ systemctl get-default +multi-user.target + Is it the case that the system default target is not set to "multi-user.target" and the Information System Security Officer (ISSO) lacks a documented requirement for a graphical user interface? + + + + Verify that there are no wireless interfaces configured on the system +with the following command: + +$ sudo nmcli device +The output should only contain wireless devices in unavailable state, like in the +following example: +wlp0s20f3 wifi unavailable -- + Is it the case that wireless interfaces are not active? + + + + +Run the following command to determine if the postgresql_can_rsync SELinux boolean is disabled: +$ getsebool postgresql_can_rsync +If properly configured, the output should show the following: +postgresql_can_rsync --> off + Is it the case that postgresql_can_rsync is not disabled? + + + + Run the following command to determine if the openssh-server package is installed: $ rpm -q openssh-server + Is it the case that the package is not installed? + + + + +Run the following command to determine if the httpd_use_cifs SELinux boolean is disabled: +$ getsebool httpd_use_cifs +If properly configured, the output should show the following: +httpd_use_cifs --> off + Is it the case that httpd_use_cifs is not disabled? + + + + If the system does not have SELinux enabled and enforcing a targeted policy, or if the +pam_faillock.so module is not configured for use, this requirement is not applicable. + +Verify the location of the non-default tally directory for the pam_faillock.so module with +the following command: + +$ sudo grep -w dir /etc/security/faillock.conf + +dir = /var/log/faillock + +Check the security context type of the non-default tally directory with the following command: + +$ sudo ls -Zd /var/log/faillock + +unconfined_u:object_r:faillog_t:s0 /var/log/faillock + Is it the case that the security context type of the non-default tally directory is not "faillog_t"? + + + + Inspect the form of default GRUB 2 command line for the Linux operating system +in /etc/default/grub. If it includes slab_nomerge=yes, +then the parameter will be configured for newly installed kernels. +First check if the GRUB recovery is enabled: +$ sudo grep 'GRUB_DISABLE_RECOVERY' /etc/default/grub +If this option is set to true, then check that a line is output by the following command: +$ sudo grep 'GRUB_CMDLINE_LINUX_DEFAULT.*slab_nomerge=yes.*' /etc/default/grub +If the recovery is disabled, check the line with +$ sudo grep 'GRUB_CMDLINE_LINUX.*slab_nomerge=yes.*' /etc/default/grub.Moreover, command line parameters for currently installed kernels should be checked as well. +Run the following command: +$ sudo grubby --info=ALL | grep args | grep -v 'slab_nomerge=yes' +The command should not return any output. + Is it the case that merging of slabs with similar size is enabled? + + + + Verify Red Hat Enterprise Linux 9 generates an audit record for unsuccessful attempts to use the unlinkat system call. + +If the auditd daemon is configured to use the "augenrules" program to to read audit rules during daemon startup (the default), run the following command: + +$ sudo grep -r unlinkat /etc/audit/rules.d + +If the auditd daemon is configured to use the "auditctl" utility to read audit rules during daemon startup, run the following command: + +$ sudo grep unlinkat /etc/audit/audit.rules + +The output should be the following: + +-a always,exit -F arch=b32 -S unlinkat -F exit=-EPERM -F auid>=1000 -F auid!=unset -k unsuccessful-delete +-a always,exit -F arch=b64 -S unlinkat -F exit=-EPERM -F auid>=1000 -F auid!=unset -k unsuccessful-delete +-a always,exit -F arch=b32 -S unlinkat -F exit=-EACCES -F auid>=1000 -F auid!=unset -k unsuccessful-delete +-a always,exit -F arch=b64 -S unlinkat -F exit=-EACCES -F auid>=1000 -F auid!=unset -k unsuccessful-delete + Is it the case that the command does not return a line, or the line is commented out? + + + + To verify that packages comprising the available updates will be automatically installed by dnf-automatic, run the following command: +$ sudo grep apply_updates /etc/dnf/automatic.conf +The output should return the following: +apply_updates = yes + Is it the case that apply_updates is not set to yes? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_IPV6 /boot/config.* + + Configs with value 'n' are not explicitly set in the file, so either commented lines or no + lines should be returned. + + Is it the case that the kernel was not built with the required value? + + + + To determine if the system is configured to audit successful calls +to the openat system call, run the following command: +$ sudo grep "openat" /etc/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + +Run the following command to determine if the httpd_use_openstack SELinux boolean is disabled: +$ getsebool httpd_use_openstack +If properly configured, the output should show the following: +httpd_use_openstack --> off + Is it the case that httpd_use_openstack is not disabled? + + + + Verify that a separate file system/partition has been created for /var with the following command: + +$ mountpoint /var + + Is it the case that "/var is not a mountpoint" is returned? + + + + Verify Red Hat Enterprise Linux 9 generates an audit record for unsuccessful attempts to use the open system call. + +If the auditd daemon is configured to use the "augenrules" program to to read audit rules during daemon startup (the default), run the following command: + +$ sudo grep -r open /etc/audit/rules.d + +If the auditd daemon is configured to use the "auditctl" utility to read audit rules during daemon startup, run the following command: + +$ sudo grep open /etc/audit/audit.rules + +The output should be the following: + +-a always,exit -F arch=b32 -S open -F exit=-EPERM -F auid>=1000 -F auid!=unset -k access +-a always,exit -F arch=b64 -S open -F exit=-EPERM -F auid>=1000 -F auid!=unset -k access +-a always,exit -F arch=b32 -S open -F exit=-EACCES -F auid>=1000 -F auid!=unset -k access +-a always,exit -F arch=b64 -S open -F exit=-EACCES -F auid>=1000 -F auid!=unset -k access + Is it the case that the command does not return a line, or the line is commented out? + + + + +Run the following command to determine if the puppetagent_manage_all_files SELinux boolean is disabled: +$ getsebool puppetagent_manage_all_files +If properly configured, the output should show the following: +puppetagent_manage_all_files --> off + Is it the case that puppetagent_manage_all_files is not disabled? + + + + Verify the USBGuard has a policy configured with the following command: + +$ sudo usbguard list-rules + +If the command does not return results or an error is returned, ask the SA to indicate how unauthorized peripherals are being blocked. + Is it the case that there is no evidence that unauthorized peripherals are being blocked before establishing a connection? + + + + Inspect /etc/audit/auditd.conf and locate the following line to +determine if the system is configured to email the administrator when +disk space is starting to run low: +$ sudo grep space_left_action /etc/audit/auditd.conf +space_left_action +Acceptable values are email, suspend, single, and halt. + Is it the case that the system is not configured to send an email to the system administrator when disk space is starting to run low? + + + + To verify that the Audit is correctly configured according to recommended rules, check the content of the file with the following command: +cat /etc/audit/rules.d/30-ospp-v42-3-access-success.rules +The output has to be exactly as follows: +## Successful file access (any other opens) This has to go last. +## These next two are likely to result in a whole lot of events +-a always,exit -F arch=b32 -S open,openat,openat2,open_by_handle_at -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-access +-a always,exit -F arch=b64 -S openat,openat2,open_by_handle_at -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-access + Is it the case that the file does not exist or the content differs? + + + + +Run the following command to determine if the xdm_sysadm_login SELinux boolean is disabled: +$ getsebool xdm_sysadm_login +If properly configured, the output should show the following: +xdm_sysadm_login --> off + Is it the case that xdm_sysadm_login is not disabled? + + + + Verify the nosuid option is configured for the /var/tmp mount point, + run the following command: + $ sudo mount | grep '\s/var/tmp\s' + . . . /var/tmp . . . nosuid . . . + + Is it the case that the "/var/tmp" file system does not have the "nosuid" option set? + + + + If the device or Red Hat Enterprise Linux 9 does not have a camera installed, this requirement is not applicable. + +This requirement is not applicable to mobile devices (smartphones and tablets), where the use of the camera is a local AO decision. + +This requirement is not applicable to dedicated VTC suites located in approved VTC locations that are centrally managed. + +For an external camera, if there is not a method for the operator to manually disconnect the camera at the end of collaborative computing sessions, this is a finding. + +For a built-in camera, the camera must be protected by a camera cover (e.g., laptop camera cover slide) when not in use. If the built-in camera is not protected with a camera cover, or is not physically disabled, this is a finding. + +If the camera is not disconnected, covered, or physically disabled, determine if it is being disabled via software with the following commands: + +Verify the operating system disables the ability to load the uvcvideo kernel module. + +$ sudo grep -r uvcvideo /etc/modprobe.d/* | grep "/bin/true" + +install uvcvideo /bin/true + Is it the case that the command does not return any output, or the line is commented out, and the collaborative computing device has not been authorized for use? + + + + +Run the following command to determine if the mcelog_client SELinux boolean is disabled: +$ getsebool mcelog_client +If properly configured, the output should show the following: +mcelog_client --> off + Is it the case that mcelog_client is not disabled? + + + + + +Run the following command to determine the current status of the +systemd-journald service: +$ sudo systemctl is-active systemd-journald +If the service is running, it should return the following: active + Is it the case that the systemd-journald service is not running? + + + + Verify the noexec option is configured for the /var/log/audit mount point, + run the following command: + $ sudo mount | grep '\s/var/log/audit\s' + . . . /var/log/audit . . . noexec . . . + + Is it the case that the "/var/log/audit" file system does not have the "noexec" option set? + + + + Check whether the minimum time period between password changes for each user account is one day or greater. + +$ sudo awk -F: '$4 < 1 {print $1 " " $4}' /etc/shadow + Is it the case that any results are returned that are not associated with a system account? + + + + Verify that the system is not accepting "rsyslog" messages from other systems unless it is documented as a log aggregation server. +Display the contents of the configuration file: +cat /etc/rsyslog.conf +$ModLoad imtcp +$InputTCPServerRun port +$ModLoad imudp +$UDPServerRun port +$ModLoad imrelp +$InputRELPServerRun port + +If any of the above modules are being loaded in the "/etc/rsyslog.conf" file, ask to see the documentation for the system being used for log aggregation. + Is it the case that rsyslog accepts remote messages and is not documented as a log aggregation system? + + + + The runtime status of the vm.mmap_min_addr kernel parameter can be queried +by running the following command: +$ sysctl vm.mmap_min_addr +65536. + + Is it the case that the correct value is not returned? + + + + + +Run the following command to determine the current status of the +fapolicyd service: +$ sudo systemctl is-active fapolicyd +If the service is running, it should return the following: active + Is it the case that the service is not enabled? + + + + Verify Red Hat Enterprise Linux 9 for PKI-based authentication has valid certificates by constructing a +certification path (which includes status information) to an accepted trust anchor. + +Check that the system has a valid DoD root CA installed with the following command: + +$ sudo openssl x509 -text -in /etc/sssd/pki/sssd_auth_ca_db.pem + +Certificate: +Data: +Version: 3 (0x2) +Serial Number: 1 (0x1) +Signature Algorithm: sha256WithRSAEncryption +Issuer: C = US, O = U.S. Government, OU = DoD, OU = PKI, CN = DoD Root CA 3 +Validity +Not Before: Mar 20 18:46:41 2012 GMT +Not After : Dec 30 18:46:41 2029 GMT +Subject: C = US, O = U.S. Government, OU = DoD, OU = PKI, CN = DoD Root CA 3 +Subject Public Key Info: +Public Key Algorithm: rsaEncryption + Is it the case that root CA file is not a DoD-issued certificate with a valid date and installed in the /etc/sssd/pki/sssd_auth_ca_db.pem location? + + + + To check the permissions of /etc/gshadow, +run the command: +$ ls -l /etc/gshadow +If properly configured, the output should indicate the following permissions: +---------- + Is it the case that /etc/gshadow does not have unix mode ----------? + + + + +Run the following command to determine if the httpd_enable_cgi SELinux boolean is disabled: +$ getsebool httpd_enable_cgi +If properly configured, the output should show the following: +httpd_enable_cgi --> off + Is it the case that httpd_enable_cgi is not disabled? + + + + To verify that auditing is configured for system administrator actions, run the following command: +$ sudo auditctl -l | grep "watch=/etc/sudoers\|watch=/etc/sudoers.d\|-w /etc/sudoers\|-w /etc/sudoers.d" + Is it the case that there is not output? + + + + To check the permissions of /etc/issue, +run the command: +$ ls -l /etc/issue +If properly configured, the output should indicate the following permissions: +-rw-r--r-- + Is it the case that /etc/issue does not have unix mode -rw-r--r--? + + + + To determine if NOEXEC has been configured for sudo, run the following command: +$ sudo grep -ri "^[\s]*Defaults.*\bnoexec\b.*" /etc/sudoers /etc/sudoers.d/ +The command should return a matching output. + Is it the case that noexec is not enabled in sudo? + + + + Verify that core dumps are disabled for all users, run the following command: +$ grep core /etc/security/limits.conf +* hard core 0 + Is it the case that the "core" item is missing, commented out, or the value is anything other than "0" and the need for core dumps is not documented with the Information System Security Officer (ISSO) as an operational requirement for all domains that have the "core"? + + + + +Run the following command to determine if the daemons_use_tty SELinux boolean is disabled: +$ getsebool daemons_use_tty +If properly configured, the output should show the following: +daemons_use_tty --> off + Is it the case that daemons_use_tty is not disabled? + + + + +Run the following command to determine if the cluster_manage_all_files SELinux boolean is disabled: +$ getsebool cluster_manage_all_files +If properly configured, the output should show the following: +cluster_manage_all_files --> off + Is it the case that cluster_manage_all_files is not disabled? + + + + To determine if the system is configured to audit successful calls +to the rename system call, run the following command: +$ sudo grep "rename" /etc/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + +Run the following command to determine if the selinuxuser_execmod SELinux boolean is enabled: +$ getsebool selinuxuser_execmod +If properly configured, the output should show the following: +selinuxuser_execmod --> on + Is it the case that selinuxuser_execmod is not enabled? + + + + Verify the noexec option is configured for the /var mount point, + run the following command: + $ sudo mount | grep '\s/var\s' + . . . /var . . . noexec . . . + + Is it the case that the "/var" file system does not have the "noexec" option set? + + + + To determine that periodic AIDE execution has been scheduled, run the following command: +$ grep aide /etc/crontab +The output should return something similar to the following: +05 4 * * * root /usr/sbin/aide --check | /bin/mail -s "$(hostname) - AIDE Integrity Check" root@localhost +The email address that the notifications are sent to can be changed by overriding +. + Is it the case that AIDE has not been configured or has not been configured to notify personnel of scan details? + + + + +Run the following command to determine if the antivirus_use_jit SELinux boolean is disabled: +$ getsebool antivirus_use_jit +If properly configured, the output should show the following: +antivirus_use_jit --> off + Is it the case that antivirus_use_jit is not disabled? + + + + Run the following command to determine if the nss-tools package is installed: $ rpm -q nss-tools + Is it the case that the package is not installed? + + + + +Run the following command to determine if the domain_fd_use SELinux boolean is enabled: +$ getsebool domain_fd_use +If properly configured, the output should show the following: +domain_fd_use --> on + Is it the case that domain_fd_use is not enabled? + + + + To ensure the splash screen is configured not to show user name, run the following command: +$ gsettings get org.gnome.desktop.screensaver show-full-name-in-top-bar +If properly configured, the output should be false. +To ensure that users cannot enable user name on the lock screen, run the following: +$ grep show-full-name-in-top-bar /etc/dconf/db/local.d/locks/* +If properly configured, the output should be /org/gnome/desktop/screensaver/show-full-name-in-top-bar + Is it the case that it is not set or configured properly? + + + + +Run the following command to determine if the cvs_read_shadow SELinux boolean is disabled: +$ getsebool cvs_read_shadow +If properly configured, the output should show the following: +cvs_read_shadow --> off + Is it the case that cvs_read_shadow is not disabled? + + + + Inspect the form of default GRUB 2 command line for the Linux operating system +in /etc/default/grub. If it includes l1tf=, +then the parameter will be configured for newly installed kernels. +First check if the GRUB recovery is enabled: +$ sudo grep 'GRUB_DISABLE_RECOVERY' /etc/default/grub +If this option is set to true, then check that a line is output by the following command: +$ sudo grep 'GRUB_CMDLINE_LINUX_DEFAULT.*l1tf=.*' /etc/default/grub +If the recovery is disabled, check the line with +$ sudo grep 'GRUB_CMDLINE_LINUX.*l1tf=.*' /etc/default/grub.Moreover, command line parameters for currently installed kernels should be checked as well. +Run the following command: +$ sudo grubby --info=ALL | grep args | grep -v 'l1tf=' +The command should not return any output. + Is it the case that l1tf mitigations are not configured appropriately? + + + + To check the permissions of /etc/cron.allow, +run the command: +$ ls -l /etc/cron.allow +If properly configured, the output should indicate the following permissions: +-rw------- + Is it the case that /etc/cron.allow does not have unix mode -rw-------? + + + + Run the following command to determine if the rng-tools package is installed: $ rpm -q rng-tools + Is it the case that the package is not installed? + + + + +Run the following command to determine if the cups_execmem SELinux boolean is disabled: +$ getsebool cups_execmem +If properly configured, the output should show the following: +cups_execmem --> off + Is it the case that cups_execmem is not disabled? + + + + +Run the following command to determine if the rsync_anon_write SELinux boolean is disabled: +$ getsebool rsync_anon_write +If properly configured, the output should show the following: +rsync_anon_write --> off + Is it the case that rsync_anon_write is not disabled? + + + + To verify the password reuse setting is compliant, run the following command: +$ grep remember /etc/pam.d/system-auth +The output should show the following at the end of the line: +remember= + Is it the case that the value of remember is not equal to or greater than the expected value? + + + + +Run the following command to determine if the pcp_bind_all_unreserved_ports SELinux boolean is disabled: +$ getsebool pcp_bind_all_unreserved_ports +If properly configured, the output should show the following: +pcp_bind_all_unreserved_ports --> off + Is it the case that pcp_bind_all_unreserved_ports is not disabled? + + + + +Run the following command to determine if the httpd_run_stickshift SELinux boolean is disabled: +$ getsebool httpd_run_stickshift +If properly configured, the output should show the following: +httpd_run_stickshift --> off + Is it the case that httpd_run_stickshift is not disabled? + + + + + +Run the following command to determine the current status of the +auditd service: +$ sudo systemctl is-active auditd +If the service is running, it should return the following: active + Is it the case that the auditd service is not running? + + + + If the system uses IPv6, this is not applicable. + +If the system is configured to prevent the usage of the ipv6 on +network interfaces, it will contain a line of the form: +net.ipv6.conf.default.disable_ipv6 = 1 +Such lines may be inside any file in the /etc/sysctl.d directory. +This permits insertion of the IPv6 kernel module (which other parts of the +system expect to be present), but otherwise keeps network interfaces +from using IPv6. Run the following command to search for such lines in all +files in /etc/sysctl.d: +$ grep -r ipv6 /etc/sysctl.d + Is it the case that the ipv6 support is disabled by default on network interfaces? + + + + To determine if NOPASSWD or !authenticate have been configured for +sudo, run the following command: +$ sudo grep -ri "nopasswd\|\!authenticate" /etc/sudoers /etc/sudoers.d/ +The command should return no output. + Is it the case that nopasswd and/or !authenticate is enabled in sudo? + + + + +Run the following command to determine if the httpd_use_gpg SELinux boolean is disabled: +$ getsebool httpd_use_gpg +If properly configured, the output should show the following: +httpd_use_gpg --> off + Is it the case that httpd_use_gpg is not disabled? + + + + Storing logs with persistent storage ensures they are available after a reboot or system crash. +Run the command below to verify that logs are being persistently stored to disk. + +grep "^\sStorage" /etc/systemd/journald.conf + +and it should return + +Storage=persistent + + Is it the case that is commented out or not configured correctly? + + + + +Run the following command to determine if the condor_tcp_network_connect SELinux boolean is disabled: +$ getsebool condor_tcp_network_connect +If properly configured, the output should show the following: +condor_tcp_network_connect --> off + Is it the case that condor_tcp_network_connect is not disabled? + + + + Verify that Red Hat Enterprise Linux 9 is configured to audit the execution of the "gpasswd" command with the following command: + +$ sudo auditctl -l | grep gpasswd + +-a always,exit -F path=/usr/bin/gpasswd -F perm=x -F auid>=1000 -F auid!=unset -k privileged-gpasswd + Is it the case that the command does not return a line, or the line is commented out? + + + + To check the ownership of /etc/group, +run the command: +$ ls -lL /etc/group +If properly configured, the output should indicate the following owner: +root + Is it the case that /etc/group does not have an owner of root? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_BUG /boot/config.* + + For each kernel installed, a line with value "y" should be returned. + + Is it the case that the kernel was not built with the required value? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_PAGE_POISONING_NO_SANITY /boot/config.* + + For each kernel installed, a line with value "y" should be returned. + + Is it the case that the kernel was not built with the required value? + + + + +Run the following command to determine if the httpd_enable_ftp_server SELinux boolean is disabled: +$ getsebool httpd_enable_ftp_server +If properly configured, the output should show the following: +httpd_enable_ftp_server --> off + Is it the case that httpd_enable_ftp_server is not disabled? + + + + +Run the following command to determine if the ftpd_connect_all_unreserved SELinux boolean is disabled: +$ getsebool ftpd_connect_all_unreserved +If properly configured, the output should show the following: +ftpd_connect_all_unreserved --> off + Is it the case that ftpd_connect_all_unreserved is not disabled? + + + + +Run the following command to determine if the postfix_local_write_mail_spool SELinux boolean is enabled: +$ getsebool postfix_local_write_mail_spool +If properly configured, the output should show the following: +postfix_local_write_mail_spool --> on + Is it the case that postfix_local_write_mail_spool is not enabled? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_SECURITY_WRITABLE_HOOKS /boot/config.* + + For each kernel installed, a line with value "y" should be returned. + + Is it the case that the kernel was not built with the required value? + + + + To verify that Audit Daemon is configured to record the hostname +in audit events, run the following command: +$ sudo grep name_format /etc/audit/auditd.conf +The output should return the following: +name_format = hostname + Is it the case that name_format isn't set to hostname? + + + + To determine if the system is configured to audit calls to the +fchown system call, run the following command: +$ sudo grep "fchown" /etc/audit/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + To check the permissions of /etc/group, +run the command: +$ ls -l /etc/group +If properly configured, the output should indicate the following permissions: +-rw-r--r-- + Is it the case that /etc/group does not have unix mode -rw-r--r--? + + + + +Run the following command to determine if the nfsd_anon_write SELinux boolean is disabled: +$ getsebool nfsd_anon_write +If properly configured, the output should show the following: +nfsd_anon_write --> off + Is it the case that nfsd_anon_write is not disabled? + + + + The runtime status of the net.ipv4.conf.all.secure_redirects kernel parameter can be queried +by running the following command: +$ sysctl net.ipv4.conf.all.secure_redirects +0. + + Is it the case that the correct value is not returned? + + + + +Run the following command to determine if the virt_use_comm SELinux boolean is disabled: +$ getsebool virt_use_comm +If properly configured, the output should show the following: +virt_use_comm --> off + Is it the case that virt_use_comm is not disabled? + + + + To determine if the system is configured to audit unsuccessful calls +to the lchown system call, run the following command: +$ sudo grep "lchown" /etc/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + +If the system is configured to prevent the loading of the dccp kernel module, +it will contain lines inside any file in /etc/modprobe.d or the deprecated /etc/modprobe.conf. +These lines instruct the module loading system to run another program (such as /bin/true) upon a module install event. + +Run the following command to search for such lines in all files in /etc/modprobe.d and the deprecated /etc/modprobe.conf: +$ grep -r dccp /etc/modprobe.conf /etc/modprobe.d + Is it the case that no line is returned? + + + + Inspect the form of default GRUB 2 command line for the Linux operating system +in /etc/default/grub. If it includes mce=0, +then the parameter will be configured for newly installed kernels. +First check if the GRUB recovery is enabled: +$ sudo grep 'GRUB_DISABLE_RECOVERY' /etc/default/grub +If this option is set to true, then check that a line is output by the following command: +$ sudo grep 'GRUB_CMDLINE_LINUX_DEFAULT.*mce=0.*' /etc/default/grub +If the recovery is disabled, check the line with +$ sudo grep 'GRUB_CMDLINE_LINUX.*mce=0.*' /etc/default/grub.Moreover, command line parameters for currently installed kernels should be checked as well. +Run the following command: +$ sudo grubby --info=ALL | grep args | grep -v 'mce=0' +The command should not return any output. + Is it the case that MCE tolerance is not set to zero? + + + + The runtime status of the fs.protected_fifos kernel parameter can be queried +by running the following command: +$ sysctl fs.protected_fifos +2. + + Is it the case that the correct value is not returned? + + + + Run the following command to determine if the net-snmp package is installed: +$ rpm -q net-snmp + Is it the case that the package is installed? + + + + Verify Red Hat Enterprise Linux 9 is configured to limit the "pwquality" retry option to . + + +Check for the use of the "pwquality" retry option in the pwquality.conf file with the following command: +$ grep retry /etc/security/pwquality.conf + Is it the case that the value of "retry" is set to "0" or greater than "<sub idref="var_password_pam_retry" />", or is missing? + + + + +Run the following command to determine if the selinuxuser_tcp_server SELinux boolean is disabled: +$ getsebool selinuxuser_tcp_server +If properly configured, the output should show the following: +selinuxuser_tcp_server --> off + Is it the case that selinuxuser_tcp_server is not disabled? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_STRICT_MODULE_RWX /boot/config.* + + For each kernel installed, a line with value "y" should be returned. + + Is it the case that the kernel was not built with the required value? + + + + Ensure that debug-shell service is not enabled with the following command: +grep systemd\.debug-shell=1 /boot/grub2/grubenv /etc/default/grub +If the command returns a line, it means that debug-shell service is being enabled. + Is it the case that the comand returns a line? + + + + The following command will list which files on the system have permissions different from what +is expected by the RPM database: +$ rpm -Va | awk '{ if (substr($0,2,1)=="M") print $NF }' + Is it the case that there is output? + + + + To verify ExecShield is enabled on 64-bit Red Hat Enterprise Linux 9 systems, +run the following command: +$ dmesg | grep '[NX|DX]*protection' +The output should not contain 'disabled by kernel command line option'. +Inspect the form of default GRUB 2 command line for the Linux operating system +in /etc/default/grub. If it includes noexec=off, +then the parameter will be configured for newly installed kernels. +First check if the GRUB recovery is enabled: +$ sudo grep 'GRUB_DISABLE_RECOVERY' /etc/default/grub +If this option is set to true, then check that a line is output by the following command: +$ sudo grep 'GRUB_CMDLINE_LINUX_DEFAULT.*noexec=off.*' /etc/default/grub +If the recovery is disabled, check the line with +$ sudo grep 'GRUB_CMDLINE_LINUX.*noexec=off.*' /etc/default/grub.Moreover, command line parameters for currently installed kernels should be checked as well. +Run the following command: +$ sudo grubby --info=ALL | grep args | grep -v 'noexec=off' +The command should not return any output. + Is it the case that ExecShield is not supported by the hardware, is not enabled, or has been disabled by the kernel configuration.? + + + + To determine if the system is configured to audit calls to the +rename system call, run the following command: +$ sudo grep "rename" /etc/audit/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + Verify that Red Hat Enterprise Linux 9 generates an audit record for all uses of the "umount" and system call. +To determine if the system is configured to audit calls to the +"umount" system call, run the following command: +$ sudo grep "umount" /etc/audit/audit.* +If the system is configured to audit this activity, it will return a line like the following. +-a always,exit -F arch=b32 -S umount -F auid>=1000 -F auid!=unset -k privileged-umount + Is it the case that the command does not return a line, or the line is commented out? + + + + To determine if the system is configured to audit successful calls +to the fremovexattr system call, run the following command: +$ sudo grep "fremovexattr" /etc/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + To determine if the system is configured to audit calls to the +chown system call, run the following command: +$ sudo grep "chown" /etc/audit/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + +Run the following command to determine if the racoon_read_shadow SELinux boolean is disabled: +$ getsebool racoon_read_shadow +If properly configured, the output should show the following: +racoon_read_shadow --> off + Is it the case that racoon_read_shadow is not disabled? + + + + + +To determine if firewalld is configured to allow access to ssh +on port 22/tcp, run the following command(s): + + firewall-cmd --list-ports + + + firewall-cmd --list-services + +If firewalld is configured to allow access through the firewall, something similar to the following will be output: + +If it is a service: +ssh + + +If it is a port: +22/tcp + + Is it the case that sshd service is disabled by firewall? + + + + To determine if the system is configured to audit calls to the +openat system call, run the following command: +$ sudo grep "openat" /etc/audit/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + To check the ownership of /etc/cron.d, +run the command: +$ ls -lL /etc/cron.d +If properly configured, the output should indicate the following owner: +root + Is it the case that /etc/cron.d does not have an owner of root? + + + + To verify that the Dracut FIPS module is enabled, run the following command: +grep "add_dracutmodules" /etc/dracut.conf.d/40-fips.conf +The output should look like this: +add_dracutmodules+=" fips " + Is it the case that the Dracut FIPS module is not enabled? + + + + The runtime status of the kernel.kptr_restrict kernel parameter can be queried +by running the following command: +$ sysctl kernel.kptr_restrict +The output of the command should indicate either: +kernel.kptr_restrict = 1 +or: +kernel.kptr_restrict = 2 +The output of the command should not indicate: +kernel.kptr_restrict = 0 + +The preferable way how to assure the runtime compliance is to have +correct persistent configuration, and rebooting the system. + +The persistent kernel parameter configuration is performed by specifying the appropriate +assignment in any file located in the /etc/sysctl.d directory. +Verify that there is not any existing incorrect configuration by executing the following command: +$ grep -r '^\s*kernel.kptr_restrict\s*=' /etc/sysctl.conf /etc/sysctl.d +The command should not find any assignments other than: +kernel.kptr_restrict = 1 +or: +kernel.kptr_restrict = 2 + +Conflicting assignments are not allowed. + Is it the case that the kernel.kptr_restrict is not set to 1 or 2 or is configured to be 0? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_MODULE_SIG_SHA512 /boot/config.* + + For each kernel installed, a line with value "y" should be returned. + + Is it the case that the kernel was not built with the required value? + + + + +Run the following command to determine if the xguest_use_bluetooth SELinux boolean is disabled: +$ getsebool xguest_use_bluetooth +If properly configured, the output should show the following: +xguest_use_bluetooth --> off + Is it the case that xguest_use_bluetooth is not disabled? + + + + +Run the following command to determine if the polipo_session_bind_all_unreserved_ports SELinux boolean is disabled: +$ getsebool polipo_session_bind_all_unreserved_ports +If properly configured, the output should show the following: +polipo_session_bind_all_unreserved_ports --> off + Is it the case that polipo_session_bind_all_unreserved_ports is not disabled? + + + + +Run the following command to get the current configured value for secure_mode_insmod +SELinux boolean: +$ getsebool secure_mode_insmod +The expected cofiguration is . +"on" means true, and "off" means false + Is it the case that secure_mode_insmod is not set as expected? + + + + To verify that McAfee HIPS is installed, run the following command(s): +$ rpm -q MFEhiplsm + Is it the case that the HBSS HIPS module is not installed? + + + + To determine if the system is configured to audit successful calls +to the open system call, run the following command: +$ sudo grep "open" /etc/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + To check that the autofs service is disabled in system boot configuration, +run the following command: +$ sudo systemctl is-enabled autofs +Output should indicate the autofs service has either not been installed, +or has been disabled at all runlevels, as shown in the example below: +$ sudo systemctl is-enabled autofs disabled + +Run the following command to verify autofs is not active (i.e. not running) through current runtime configuration: +$ sudo systemctl is-active autofs + +If the service is not running the command will return the following output: +inactive + +The service will also be masked, to check that the autofs is masked, run the following command: +$ sudo systemctl show autofs | grep "LoadState\|UnitFileState" + +If the service is masked the command will return the following outputs: + +LoadState=masked + +UnitFileState=masked + Is it the case that the "autofs" is loaded and not masked? + + + + To ensure that system location tracking is not active, run the following command: +$ gsettings get org.gnome.system.location enabled +$ gsettings get org.gnome.clocks geolocation +If properly configured, the output should be false. +To ensure that users cannot enable system location tracking, run the following: +$ grep location /etc/dconf/db/local.d/locks/* +If properly configured, the output should be +/org/gnome/system/location/enabled and /org/gnome/clocks/geolocation. + Is it the case that geolocation is enabled and not disabled? + + + + +Run the following command to determine if the irc_use_any_tcp_ports SELinux boolean is disabled: +$ getsebool irc_use_any_tcp_ports +If properly configured, the output should show the following: +irc_use_any_tcp_ports --> off + Is it the case that irc_use_any_tcp_ports is not disabled? + + + + To obtain a listing of all users, their UIDs, and their shells, run the +command: $ awk -F: '{print $1 ":" $3 ":" $7}' /etc/passwd Identify +the system accounts from this listing. These will primarily be the accounts +with UID numbers less than UID_MIN, other than root. Value of the UID_MIN +directive is set in /etc/login.defs configuration file. In the default +configuration UID_MIN is set to 1000. + Is it the case that any system account (other than root) has a login shell? + + + + +Run the following command to determine if the cluster_use_execmem SELinux boolean is disabled: +$ getsebool cluster_use_execmem +If properly configured, the output should show the following: +cluster_use_execmem --> off + Is it the case that cluster_use_execmem is not disabled? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_DEBUG_FS /boot/config.* + + Configs with value 'n' are not explicitly set in the file, so either commented lines or no + lines should be returned. + + Is it the case that the kernel was not built with the required value? + + + + To ensure that users cannot change how long until the screensaver locks, run the following: +$ grep lock-enabled /etc/dconf/db/local.d/locks/* +If properly configured, the output for lock-enabled should be /org/gnome/desktop/screensaver/lock-enabled + Is it the case that screensaver locking is not locked? + + + + +Run the following command to determine if the fips_mode SELinux boolean is enabled: +$ getsebool fips_mode +If properly configured, the output should show the following: +fips_mode --> on + Is it the case that fips_mode is not enabled? + + + + +Run the following command to determine if the httpd_serve_cobbler_files SELinux boolean is disabled: +$ getsebool httpd_serve_cobbler_files +If properly configured, the output should show the following: +httpd_serve_cobbler_files --> off + Is it the case that httpd_serve_cobbler_files is not disabled? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_MODIFY_LDT_SYSCALL /boot/config.* + + Configs with value 'n' are not explicitly set in the file, so either commented lines or no + lines should be returned. + + Is it the case that the kernel was not built with the required value? + + + + To verify that the Audit is correctly configured according to recommended rules, check the content of the file with the following command: +cat /etc/audit/rules.d/30-ospp-v42-3-access-failed.rules +The output has to be exactly as follows: +## Unsuccessful file access (any other opens) This has to go last. +-a always,exit -F arch=b64 -S open,openat,openat2,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-access +-a always,exit -F arch=b64 -S open,openat,openat2,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-access + Is it the case that the file does not exist or the content differs? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_MODULE_SIG /boot/config.* + + For each kernel installed, a line with value "y" should be returned. + + Is it the case that the kernel was not built with the required value? + + + + To verify that the Audit is correctly configured according to recommended rules, check the content of the file with the following command: +cat /etc/audit/rules.d/30-ospp-v42-4-delete-failed.rules +The output has to be exactly as follows: +## Unsuccessful file delete +-a always,exit -F arch=b32 -S unlink,unlinkat,rename,renameat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete +-a always,exit -F arch=b64 -S unlink,unlinkat,rename,renameat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete +-a always,exit -F arch=b32 -S unlink,unlinkat,rename,renameat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete +-a always,exit -F arch=b64 -S unlink,unlinkat,rename,renameat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete + Is it the case that the file does not exist or the content differs? + + + + +Run the following command to determine if the unprivuser_use_svirt SELinux boolean is disabled: +$ getsebool unprivuser_use_svirt +If properly configured, the output should show the following: +unprivuser_use_svirt --> off + Is it the case that unprivuser_use_svirt is not disabled? + + + + +If the system is configured to prevent the loading of the iwlmvm kernel module, +it will contain lines inside any file in /etc/modprobe.d or the deprecated /etc/modprobe.conf. +These lines instruct the module loading system to run another program (such as /bin/true) upon a module install event. + +Run the following command to search for such lines in all files in /etc/modprobe.d and the deprecated /etc/modprobe.conf: +$ grep -r iwlmvm /etc/modprobe.conf /etc/modprobe.d + Is it the case that no line is returned? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_GCC_PLUGIN_LATENT_ENTROPY /boot/config.* + + For each kernel installed, a line with value "y" should be returned. + + Is it the case that the kernel was not built with the required value? + + + + To ensure screen locking on smartcard removal is enabled, run the following command: +$ grep removal-action /etc/dconf/db/local.d/* +The output should be 'lock-screen'. +To ensure that users cannot disable screen locking on smartcard removal, run the following: +$ grep removal-action /etc/dconf/db/local.d/locks/* +If properly configured, the output should be /org/gnome/settings-daemon/peripherals/smartcard/removal-action + Is it the case that removal-action has not been configured? + + + + +Run the following command to determine if the daemons_dump_core SELinux boolean is disabled: +$ getsebool daemons_dump_core +If properly configured, the output should show the following: +daemons_dump_core --> off + Is it the case that daemons_dump_core is not disabled? + + + + To check that no password hashes are stored in +/etc/passwd, run the following command: +awk '!/\S:x|\*/ {print}' /etc/passwd +If it produces any output, then a password hash is +stored in /etc/passwd. + Is it the case that any stored hashes are found in /etc/passwd? + + + + To check that the smb service is disabled in system boot configuration, +run the following command: +$ sudo systemctl is-enabled smb +Output should indicate the smb service has either not been installed, +or has been disabled at all runlevels, as shown in the example below: +$ sudo systemctl is-enabled smb disabled + +Run the following command to verify smb is not active (i.e. not running) through current runtime configuration: +$ sudo systemctl is-active smb + +If the service is not running the command will return the following output: +inactive + +The service will also be masked, to check that the smb is masked, run the following command: +$ sudo systemctl show smb | grep "LoadState\|UnitFileState" + +If the service is masked the command will return the following outputs: + +LoadState=masked + +UnitFileState=masked + Is it the case that the "smb" is loaded and not masked? + + + + To verify the boot loader superuser password has been set, run the following command: +$ sudo grep "^[\s]*GRUB2_PASSWORD=grub\.pbkdf2\.sha512.*$" /boot/grub2/user.cfg +The output should be similar to: +GRUB2_PASSWORD=grub.pbkdf2.sha512.10000.C4E08AC72FBFF7E837FD267BFAD7AEB3D42DDC +2C99F2A94DD5E2E75C2DC331B719FE55D9411745F82D1B6CFD9E927D61925F9BBDD1CFAA0080E0 +916F7AB46E0D.1302284FCCC52CD73BA3671C6C12C26FF50BA873293B24EE2A96EE3B57963E6D7 +0C83964B473EC8F93B07FE749AA6710269E904A9B08A6BBACB00A2D242AD828 + Is it the case that no password is set? + + + + To determine how the SSH daemon's StrictModes option is set, run the following command: + +$ sudo grep -i StrictModes /etc/ssh/sshd_config.d/00-complianceascode-hardening.conf +$ sudo grep -i StrictModes /etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf + +If a line indicating yes is returned, then the required value is set. + + Is it the case that the required value is not set? + + + + To check how many lowercase characters are required in a password, run the following command: +$ grep lcredit /etc/security/pwquality.conf +The lcredit parameter (as a negative number) will indicate how many special characters are required. + Is it the case that lcredit is not found or not less than or equal to the required value? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_LEGACY_PTYS /boot/config.* + + Configs with value 'n' are not explicitly set in the file, so either commented lines or no + lines should be returned. + + Is it the case that the kernel was not built with the required value? + + + + Verify that Red Hat Enterprise Linux 9 disables the use of user namespaces with the following commands: + +Note: User namespaces are used primarily for Linux containers. If containers are in use, this requirement is not applicable. + +The runtime status of the user.max_user_namespaces kernel parameter can be queried +by running the following command: +$ sysctl user.max_user_namespaces +0. + + Is it the case that the correct value is not returned? + + + + +Run the following command to determine if the zoneminder_run_sudo SELinux boolean is disabled: +$ getsebool zoneminder_run_sudo +If properly configured, the output should show the following: +zoneminder_run_sudo --> off + Is it the case that zoneminder_run_sudo is not disabled? + + + + To determine if the system is configured to audit successful calls +to the fchownat system call, run the following command: +$ sudo grep "fchownat" /etc/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + To determine if the system is configured to audit calls to the +renameat system call, run the following command: +$ sudo grep "renameat" /etc/audit/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + Run the following command to ensure that /tmp is configured as a +polyinstantiated directory: +$ sudo grep /tmp /etc/security/namespace.conf +The output should return the following: +/tmp /tmp/tmp-inst/ level root,adm + Is it the case that is not configured? + + + + The following command will list which files on the system have ownership different from what +is expected by the RPM database: +$ rpm -Va | rpm -Va --nofiledigest | awk '{ if (substr($0,6,1)=="U" || substr($0,7,1)=="G") print $NF }' + Is it the case that there is output? + + + + +Run the following command to determine if the httpd_can_sendmail SELinux boolean is disabled: +$ getsebool httpd_can_sendmail +If properly configured, the output should show the following: +httpd_can_sendmail --> off + Is it the case that httpd_can_sendmail is not disabled? + + + + The runtime status of the net.ipv4.conf.all.accept_source_route kernel parameter can be queried +by running the following command: +$ sysctl net.ipv4.conf.all.accept_source_route +0. + + Is it the case that the correct value is not returned? + + + + +Run the following command to determine if the httpd_mod_auth_pam SELinux boolean is disabled: +$ getsebool httpd_mod_auth_pam +If properly configured, the output should show the following: +httpd_mod_auth_pam --> off + Is it the case that httpd_mod_auth_pam is not disabled? + + + + Verify Red Hat Enterprise Linux 9 generates an audit record for unsuccessful attempts to create files using the openat system call with O_CREAT flag. + +If the auditd daemon is configured to use the "augenrules" program to read audit rules during daemon startup (the default), run the following command: + +$ sudo grep -r openat /etc/audit/rules.d + +If the auditd daemon is configured to use the "auditctl" utility to read audit rules during daemon startup, run the following command: + +$ sudo grep openat /etc/audit/audit.rules + +The output should be the following: + +-a always,exit -F arch=b32 -S openat -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S openat -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S openat -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S openat -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + Is it the case that the command does not return a line, or the line is commented out? + + + + Run the following command to determine if the tuned package is installed: +$ rpm -q tuned + Is it the case that the package is installed? + + + + To check that the randomization of the page allocator in the kernel is +enabled, check all boot entries with following command: +sudo grep -L"^options\s+.*\bpage_alloc\.shuffle=1\b" /boot/loader/entries/*.conf +No line should be returned, each line returned is a boot entry that doesn't enable audit. + Is it the case that randomization of the page allocator is not enabled in the kernel? + + + + Inspect the system to determine if intrusion detection software has been installed. +Verify this intrusion detection software is active. + Is it the case that no host-based intrusion detection tools are installed? + + + + Ensure that debug-shell service is not enabled with the following command: +sudo grep -L "^options\s+.*\bsystemd.debug-shell=1\b" /boot/loader/entries/*.conf +No line should be returned, each line returned is a boot entry that enables the debug-shell. + Is it the case that the comand returns a line? + + + + Verify the nosuid option is configured for the /dev/shm mount point, + run the following command: + $ sudo mount | grep '\s/dev/shm\s' + . . . /dev/shm . . . nosuid . . . + + Is it the case that the "/dev/shm" file system does not have the "nosuid" option set? + + + + Run the following command to determine if the bind package is installed: +$ rpm -q bind + Is it the case that the package is installed? + + + + +Run the following command to determine if the mpd_use_cifs SELinux boolean is disabled: +$ getsebool mpd_use_cifs +If properly configured, the output should show the following: +mpd_use_cifs --> off + Is it the case that mpd_use_cifs is not disabled? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_SCHED_STACK_END_CHECK /boot/config.* + + For each kernel installed, a line with value "y" should be returned. + + Is it the case that the kernel was not built with the required value? + + + + Inspect the form of default GRUB 2 command line for the Linux operating system +in /etc/default/grub. If it includes audit_backlog_limit=8192, +then the parameter will be configured for newly installed kernels. +First check if the GRUB recovery is enabled: +$ sudo grep 'GRUB_DISABLE_RECOVERY' /etc/default/grub +If this option is set to true, then check that a line is output by the following command: +$ sudo grep 'GRUB_CMDLINE_LINUX_DEFAULT.*audit_backlog_limit=8192.*' /etc/default/grub +If the recovery is disabled, check the line with +$ sudo grep 'GRUB_CMDLINE_LINUX.*audit_backlog_limit=8192.*' /etc/default/grub.Moreover, command line parameters for currently installed kernels should be checked as well. +Run the following command: +$ sudo grubby --info=ALL | grep args | grep -v 'audit_backlog_limit=8192' +The command should not return any output. + Is it the case that audit backlog limit is not configured? + + + + +Run the following command to determine if the logrotate_use_nfs SELinux boolean is disabled: +$ getsebool logrotate_use_nfs +If properly configured, the output should show the following: +logrotate_use_nfs --> off + Is it the case that logrotate_use_nfs is not disabled? + + + + To verify that the Audit is correctly configured according to recommended rules, check the content of the file with the following command: +cat /etc/audit/rules.d/10-base-config.rules +The output has to be exactly as follows: +## First rule - delete all +-D + +## Increase the buffers to survive stress events. +## Make this bigger for busy systems +-b 8192 + +## This determine how long to wait in burst of events +--backlog_wait_time 60000 + +## Set failure mode to syslog +-f 1 + Is it the case that the file does not exist or the content differs? + + + + +Run the following command to determine if the gluster_anon_write SELinux boolean is disabled: +$ getsebool gluster_anon_write +If properly configured, the output should show the following: +gluster_anon_write --> off + Is it the case that gluster_anon_write is not disabled? + + + + +Run the following command to determine if the authlogin_radius SELinux boolean is disabled: +$ getsebool authlogin_radius +If properly configured, the output should show the following: +authlogin_radius --> off + Is it the case that authlogin_radius is not disabled? + + + + To determine if the system is configured to audit calls to the +openat system call, run the following command: +$ sudo grep "openat" /etc/audit/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + +Run the following command to determine if the login_console_enabled SELinux boolean is enabled: +$ getsebool login_console_enabled +If properly configured, the output should show the following: +login_console_enabled --> on + Is it the case that login_console_enabled is not enabled? + + + + Run the following command and verify that time sources are only configure with server directive: +# grep -E "^(server|pool)" /etc/chrony.conf +A line with the appropriate server should be returned, any line returned starting with pool is a finding. + Is it the case that an authoritative remote time server is not configured or configured with pool directive? + + + + Inspect /etc/audit/auditd.conf and locate the following line to +determine if the system is configured to either suspend, switch to single user mode, +or halt when disk space has run low: +admin_space_left_action single + Is it the case that the system is not configured to switch to single user mode for corrective action? + + + + The runtime status of the kernel.randomize_va_space kernel parameter can be queried +by running the following command: +$ sysctl kernel.randomize_va_space +2. + + Is it the case that the correct value is not returned? + + + + To verify that the Audit is correctly configured according to recommended rules, check the content of the file with the following command: +cat /etc/audit/rules.d/30-ospp-v42-2-modify-success.rules +The output has to be exactly as follows: +## Successful file modifications (open for write or truncate) +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification +-a always,exit -F arch=b32 -S open -F a1&01003 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification +-a always,exit -F arch=b64 -S open -F a1&01003 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification +-a always,exit -F arch=b32 -S truncate,ftruncate -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification +-a always,exit -F arch=b64 -S truncate,ftruncate -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification + Is it the case that the file does not exist or the content differs? + + + + To ensure that remote access requires credentials, run the following command: +$ gsettings get org.gnome.Vino authentication-methods +If properly configured, the output should be false. +To ensure that users cannot disable credentials for remote access, run the following: +$ grep authentication-methods /etc/dconf/db/local.d/locks/* +If properly configured, the output should be +/org/gnome/Vino/authentication-methods + Is it the case that wireless network notification is enabled and not disabled? + + + + To determine if arguments that commands can be executed with are restricted, run the following command: +$ sudo grep -PR '^(?:\s*[^#=]+)=(?:\s*(?:\([^\)]+\))?\s*(?!\s*\()[^,\s]+(?:[ \t]+[^,\s]+)+[ \t]*,)*(\s*(?:\([^\)]+\))?\s*(?!\s*\()[^,\s]+[ \t]*(?:,|$))' /etc/sudoers /etc/sudoers.d/ +The command should return no output. + Is it the case that /etc/sudoers file contains user specifications that allow execution of commands with any arguments? + + + + +If the system is configured to prevent the loading of the tipc kernel module, +it will contain lines inside any file in /etc/modprobe.d or the deprecated /etc/modprobe.conf. +These lines instruct the module loading system to run another program (such as /bin/true) upon a module install event. + +Run the following command to search for such lines in all files in /etc/modprobe.d and the deprecated /etc/modprobe.conf: +$ grep -r tipc /etc/modprobe.conf /etc/modprobe.d + Is it the case that no line is returned? + + + + +Run the following command to determine if the samba_share_fusefs SELinux boolean is disabled: +$ getsebool samba_share_fusefs +If properly configured, the output should show the following: +samba_share_fusefs --> off + Is it the case that samba_share_fusefs is not disabled? + + + + Verify that local initialization files do not execute world-writable programs, +execute the following command: +$ sudo find /home -perm -002 -type f -name ".[^.]*" -exec ls -ld {} \; +For all files listed, check for their presence in the local +initialization files with the following command: +Note: The example will be for a system that is configured to create +users' home directories in the "/home" directory. + sudo find /home/* -maxdepth 1 -type f -name \.\* -exec grep -H <file> {} \; + Is it the case that user initialization files are executing world-writable programs? + + + + Verify that rules for unsuccessful calls of the openat syscall are in the order shown below. + + If the auditd daemon is configured to use the "augenrules" program to read audit rules during daemon startup (the default), check the order of rules below in a file with suffix ".rules" in the directory "/etc/audit/rules.d". + If the auditd daemon is configured to use the "auditctl" utility to read audit rules during daemon startup, check the order of rules below in "/etc/audit/audit.rules" file. + + -a always,exit -F arch=b32 -S openat -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b32 -S openat -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b32 -S openat -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b32 -S openat -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b32 -S openat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-access + -a always,exit -F arch=b32 -S openat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-access + + If the system is 64 bit then also add the following lines: + + -a always,exit -F arch=b64 -S openat -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b64 -S openat -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b64 -S openat -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b64 -S openat -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b64 -S openat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-access + -a always,exit -F arch=b64 -S openat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-access + Is it the case that the rules are in a different order? + + + + Verify Red Hat Enterprise Linux 9 generates audit records for all account creations, modifications, disabling, and termination events that affect "/etc/passwd with the following command: + +$ sudo auditctl -l | egrep '(/etc/shadow)' + +-w /etc/shadow -p wa -k identity + Is it the case that command does not return a line, or the line is commented out? + + + + To determine if use_pty has been configured for sudo, run the following command: +$ sudo grep -ri "^[\s]*Defaults.*\buse_pty\b.*" /etc/sudoers /etc/sudoers.d/ +The command should return a matching output. + Is it the case that use_pty is not enabled in sudo? + + + + Inspect /etc/audit/audisp-remote.conf and locate the following line to +determine if the system is configured to either send to syslog, switch to single user mode, +or halt when the disk is full: +$ sudo grep -i disk_full_action /etc/audit/audisp-remote.conf +The output should return something similar to: +disk_full_action = single +Acceptable values also include syslog and halt. + Is it the case that the system is not configured to switch to single user mode for corrective action? + + + + To check the ownership of /etc/ssh/sshd_config, +run the command: +$ ls -lL /etc/ssh/sshd_config +If properly configured, the output should indicate the following owner: +root + Is it the case that /etc/ssh/sshd_config does not have an owner of root? + + + + Verify the nosuid option is configured for the /tmp mount point, + run the following command: + $ sudo mount | grep '\s/tmp\s' + . . . /tmp . . . nosuid . . . + + Is it the case that the "/tmp" file system does not have the "nosuid" option set? + + + + Run the following command to determine if the vsftpd package is installed: +$ rpm -q vsftpd + Is it the case that the package is installed? + + + + To check that page poisoning is enabled at boot time, check all boot entries with following command: +sudo grep -L "^options\s+.*\bpage_poison=1\b" /boot/loader/entries/*.conf +No line should be returned, each line returned is a boot entry that doesn't enable page poisoning. + Is it the case that page allocator poisoning is not enabled? + + + + To verify that localpkg_gpgcheck is configured properly, run the following +command: +$ grep localpkg_gpgcheck /etc/dnf/dnf.conf +The output should return something similar to: +localpkg_gpgcheck=1 + Is it the case that gpgcheck is not enabled or configured correctly to verify local packages? + + + + To check that the rsyncd service is disabled in system boot configuration, +run the following command: +$ sudo systemctl is-enabled rsyncd +Output should indicate the rsyncd service has either not been installed, +or has been disabled at all runlevels, as shown in the example below: +$ sudo systemctl is-enabled rsyncd disabled + +Run the following command to verify rsyncd is not active (i.e. not running) through current runtime configuration: +$ sudo systemctl is-active rsyncd + +If the service is not running the command will return the following output: +inactive + +The service will also be masked, to check that the rsyncd is masked, run the following command: +$ sudo systemctl show rsyncd | grep "LoadState\|UnitFileState" + +If the service is masked the command will return the following outputs: + +LoadState=masked + +UnitFileState=masked + Is it the case that the "rsyncd" is loaded and not masked? + + + + Verify the noexec option is configured for the /home mount point, + run the following command: + $ sudo mount | grep '\s/home\s' + . . . /home . . . noexec . . . + + Is it the case that the "/home" file system does not have the "noexec" option set? + + + + To verify that the Audit is correctly configured according to recommended rules, check the content of the file with the following command: +cat /etc/audit/rules.d/30-ospp-v42-4-delete-failed.rules +The output has to be exactly as follows: +## Unsuccessful file delete +-a always,exit -F arch=b64 -S unlink,unlinkat,rename,renameat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete +-a always,exit -F arch=b64 -S unlink,unlinkat,rename,renameat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete + Is it the case that the file does not exist or the content differs? + + + + To check the minimum password length, run the command: +$ grep PASS_MIN_LEN /etc/login.defs +The DoD requirement is 15. + Is it the case that it is not set to the required value? + + + + To check the permissions of /etc/cron.daily, +run the command: +$ ls -l /etc/cron.daily +If properly configured, the output should indicate the following permissions: +-rwx------ + Is it the case that /etc/cron.daily does not have unix mode -rwx------? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_PAGE_TABLE_ISOLATION /boot/config.* + + For each kernel installed, a line with value "y" should be returned. + + Is it the case that the kernel was not built with the required value? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_LEGACY_VSYSCALL_EMULATE /boot/config.* + + Configs with value 'n' are not explicitly set in the file, so either commented lines or no + lines should be returned. + + Is it the case that the kernel was not built with the required value? + + + + To determine how the SSH daemon's UsePAM option is set, run the following command: + +$ sudo grep -i UsePAM /etc/ssh/sshd_config.d/00-complianceascode-hardening.conf + + +If a line indicating yes is returned, then the required value is set. + + Is it the case that the required value is not set? + + + + To verify that USB hubs will be authorized by the USBGuard daemon, +run the following command: +$ sudo grep allow /etc/usbguard/rules.conf +One of the output lines should be +allow with-interface match-all { 09:00:* } + Is it the case that USB devices of class 9 are not authorized? + + + + + +Run the following command to determine the current status of the +ntpd service: +$ sudo systemctl is-active ntpd +If the service is running, it should return the following: active + Is it the case that ? + + + + To verify that the Audit is correctly configured according to recommended rules, check the content of the file with the following command: +cat /etc/audit/rules.d/30-ospp-v42-6-owner-change-failed.rules +The output has to be exactly as follows: +## Unsuccessful ownership change +-a always,exit -F arch=b32 -S lchown,fchown,chown,fchownat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-owner-change +-a always,exit -F arch=b64 -S lchown,fchown,chown,fchownat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-owner-change +-a always,exit -F arch=b32 -S lchown,fchown,chown,fchownat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-owner-change +-a always,exit -F arch=b64 -S lchown,fchown,chown,fchownat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-owner-change + Is it the case that the file does not exist or the content differs? + + + + +Run the following command to determine if the use_lpd_server SELinux boolean is disabled: +$ getsebool use_lpd_server +If properly configured, the output should show the following: +use_lpd_server --> off + Is it the case that use_lpd_server is not disabled? + + + + Verify Red Hat Enterprise Linux 9 is configured to lock the root account after +unsuccessful logon attempts with the command: + + +$ grep even_deny_root /etc/security/faillock.conf +even_deny_root + Is it the case that the "even_deny_root" option is not set, is missing or commented out? + + + + +Run the following command to determine if the conman_can_network SELinux boolean is disabled: +$ getsebool conman_can_network +If properly configured, the output should show the following: +conman_can_network --> off + Is it the case that conman_can_network is not disabled? + + + + To determine if the system is configured to audit successful calls +to the removexattr system call, run the following command: +$ sudo grep "removexattr" /etc/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + To check the group ownership of /etc/group-, +run the command: +$ ls -lL /etc/group- +If properly configured, the output should indicate the following group-owner: +root + Is it the case that /etc/group- does not have a group owner of root? + + + + To determine if the system is configured to audit calls to the +open_by_handle_at system call, run the following command: +$ sudo grep "open_by_handle_at" /etc/audit/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + If the system uses IPv6, this is not applicable. + +If the system is configured to prevent the usage of the ipv6 on +network interfaces, it will contain a line of the form: +net.ipv6.conf.all.disable_ipv6 = 1 +Such lines may be inside any file in the /etc/sysctl.d directory. +This permits insertion of the IPv6 kernel module (which other parts of the +system expect to be present), but otherwise keeps all network interfaces +from using IPv6. Run the following command to search for such lines in all +files in /etc/sysctl.d: +$ grep -r ipv6 /etc/sysctl.d + Is it the case that the ipv6 support is disabled on all network interfaces? + + + + To determine if the system is configured to audit calls to the +clock_settime system call, run the following command: +$ sudo grep "clock_settime" /etc/audit/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + The runtime status of the net.ipv6.conf.default.accept_ra_pinfo kernel parameter can be queried +by running the following command: +$ sysctl net.ipv6.conf.default.accept_ra_pinfo +0. + + Is it the case that the correct value is not returned? + + + + To check if pam_pwquality.so is enabled in system-auth, run the following command: +$ grep pam_pwquality /etc/pam.d/system-auth +The output should be similar to the following: +password requisite pam_pwquality.so + Is it the case that pam_pwquality.so is not enabled in system-auth? + + + + Run the following command to determine if the audispd-plugins package is installed: $ rpm -q audispd-plugins + Is it the case that the package is not installed? + + + + To check the ownership of /etc/shadow, +run the command: +$ ls -lL /etc/shadow +If properly configured, the output should indicate the following owner: +root + Is it the case that /etc/shadow does not have an owner of root? + + + + +Run the following command to determine if the httpd_can_network_memcache SELinux boolean is disabled: +$ getsebool httpd_can_network_memcache +If properly configured, the output should show the following: +httpd_can_network_memcache --> off + Is it the case that httpd_can_network_memcache is not disabled? + + + + +Run the following command to determine if the use_ecryptfs_home_dirs SELinux boolean is disabled: +$ getsebool use_ecryptfs_home_dirs +If properly configured, the output should show the following: +use_ecryptfs_home_dirs --> off + Is it the case that use_ecryptfs_home_dirs is not disabled? + + + + To check the ownership of /etc/cron.daily, +run the command: +$ ls -lL /etc/cron.daily +If properly configured, the output should indicate the following owner: +root + Is it the case that /etc/cron.daily does not have an owner of root? + + + + Verify Red Hat Enterprise Linux 9 takes the appropriate action when an audit processing failure occurs. + +Check that Red Hat Enterprise Linux 9 takes the appropriate action when an audit processing failure occurs with the following command: + +$ sudo grep disk_error_action /etc/audit/auditd.conf + +disk_error_action = + +If the value of the "disk_error_action" option is not "SYSLOG", "SINGLE", or "HALT", or the line is commented out, ask the system administrator to indicate how the system takes appropriate action when an audit process failure occurs. + Is it the case that there is no evidence of appropriate action? + + + + +To properly set the owner of /var/log/audit, run the command: +$ sudo chown root /var/log/audit + +To properly set the owner of /var/log/audit/*, run the command: +$ sudo chown root /var/log/audit/* + Is it the case that ? + + + + The runtime status of the net.ipv4.conf.all.send_redirects kernel parameter can be queried +by running the following command: +$ sysctl net.ipv4.conf.all.send_redirects +0. + + Is it the case that the correct value is not returned? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_HARDENED_USERCOPY /boot/config.* + + For each kernel installed, a line with value "y" should be returned. + + Is it the case that the kernel was not built with the required value? + + + + To verify that the Audit is correctly configured according to recommended rules, check the content of the file with the following command: +cat /etc/audit/rules.d/30-ospp-v42-6-owner-change-failed.rules +The output has to be exactly as follows: +## Unsuccessful ownership change +-a always,exit -F arch=b64 -S lchown,fchown,chown,fchownat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-owner-change +-a always,exit -F arch=b64 -S lchown,fchown,chown,fchownat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-owner-change + Is it the case that the file does not exist or the content differs? + + + + To verify that the DConf User profile is configured correctly, run the following +command: + +$ cat /etc/dconf/profile/user +The output should show the following: +user-db:user +system-db:local +system-db:site +system-db:distro + Is it the case that DConf User profile does not exist or is not configured correctly? + + + + +If the system is configured to prevent the loading of the cramfs kernel module, +it will contain lines inside any file in /etc/modprobe.d or the deprecated /etc/modprobe.conf. +These lines instruct the module loading system to run another program (such as /bin/true) upon a module install event. + +Run the following command to search for such lines in all files in /etc/modprobe.d and the deprecated /etc/modprobe.conf: +$ grep -r cramfs /etc/modprobe.conf /etc/modprobe.d + Is it the case that no line is returned? + + + + To check how many characters must differ during a password change, run the following command: +$ sudo grep difok /etc/security/pwquality.conf +difok = + +The difok parameter will indicate how many characters must differ. + Is it the case that difok is not found or set to less than the required value? + + + + +Run the following command to determine if the virt_use_xserver SELinux boolean is disabled: +$ getsebool virt_use_xserver +If properly configured, the output should show the following: +virt_use_xserver --> off + Is it the case that virt_use_xserver is not disabled? + + + + + +Run the following command to determine the current status of the +postfix service: +$ sudo systemctl is-active postfix +If the service is running, it should return the following: active + Is it the case that the system is not a cross domain solution and the service is not enabled? + + + + +Run the following command to determine if the postgresql_selinux_unconfined_dbadm SELinux boolean is enabled: +$ getsebool postgresql_selinux_unconfined_dbadm +If properly configured, the output should show the following: +postgresql_selinux_unconfined_dbadm --> on + Is it the case that postgresql_selinux_unconfined_dbadm is not enabled? + + + + +Run the following command to determine if the gssd_read_tmp SELinux boolean is enabled: +$ getsebool gssd_read_tmp +If properly configured, the output should show the following: +gssd_read_tmp --> on + Is it the case that gssd_read_tmp is not enabled? + + + + To verify that FIPS mode is enabled properly, run the following command: +fips-mode-setup --check +The output should contain the following: +FIPS mode is enabled. +To verify that the cryptographic policy has been configured correctly, run the +following command: +$ update-crypto-policies --show +The output should return . + Is it the case that FIPS mode is not enabled? + + + + To check how many categories of characters must be used in password during a password change, +run the following command: +$ sudo grep minclass /etc/security/pwquality.conf +The minclass parameter will indicate how many character classes must be used. If +the requirement was for the password to contain characters from different categories, +then this would appear as minclass = . + Is it the case that the value of "minclass" is set to less than "<sub idref="var_password_pam_minclass" />" or is commented out? + + + + To determine if the system is configured to audit account changes, +run the following command: +auditctl -l | egrep '(/etc/passwd|/etc/shadow|/etc/group|/etc/gshadow|/etc/security/opasswd)' +If the system is configured to watch for account changes, lines should be returned for +each file specified (and with perm=wa for each). + Is it the case that the system is not configured to audit account changes? + + + + To verify that cmdport has been set properly, perform the following: +$ grep '\bcmdport\b' /etc/chrony.conf +The output should return +cmdport 0 + Is it the case that cmdport is not set or cmdport is set to a non-zero value? + + + + The following command will discover and print world-writable directories that +are not owned by root. Run it once for each local partition PART: +$ sudo find PART -xdev -type d -perm -0002 -uid +0 -print + Is it the case that there is output? + + + + +Run the following command to determine if the spamd_enable_home_dirs SELinux boolean is enabled: +$ getsebool spamd_enable_home_dirs +If properly configured, the output should show the following: +spamd_enable_home_dirs --> on + Is it the case that spamd_enable_home_dirs is not enabled? + + + + To check the maximum value for consecutive repeating characters, run the following command: +$ sudo grep maxrepeat /etc/security/pwquality.conf + Is it the case that the value of "maxrepeat" is set to more than "<sub idref="var_password_pam_maxrepeat" />" or is commented out? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_RETPOLINE /boot/config.* + + For each kernel installed, a line with value "y" should be returned. + + Is it the case that the kernel was not built with the required value? + + + + The runtime status of the net.ipv4.conf.default.send_redirects kernel parameter can be queried +by running the following command: +$ sysctl net.ipv4.conf.default.send_redirects +0. + + Is it the case that the correct value is not returned? + + + + +Run the following command to determine if the exim_can_connect_db SELinux boolean is disabled: +$ getsebool exim_can_connect_db +If properly configured, the output should show the following: +exim_can_connect_db --> off + Is it the case that exim_can_connect_db is not disabled? + + + + In order to be sure that the databases are up-to-date, run the +dconf update +command as the administrator. + Is it the case that The system-wide dconf databases are up-to-date with regards to respective keyfiles? + + + + +Run the following command to determine if the dbadm_read_user_files SELinux boolean is disabled: +$ getsebool dbadm_read_user_files +If properly configured, the output should show the following: +dbadm_read_user_files --> off + Is it the case that dbadm_read_user_files is not disabled? + + + + +Run the following command to determine if the webadm_manage_user_files SELinux boolean is disabled: +$ getsebool webadm_manage_user_files +If properly configured, the output should show the following: +webadm_manage_user_files --> off + Is it the case that webadm_manage_user_files is not disabled? + + + + The runtime status of the kernel.unprivileged_bpf_disabled kernel parameter can be queried +by running the following command: +$ sysctl kernel.unprivileged_bpf_disabled +1. + + Is it the case that the correct value is not returned? + + + + +Run the following command to determine if the nagios_run_sudo SELinux boolean is disabled: +$ getsebool nagios_run_sudo +If properly configured, the output should show the following: +nagios_run_sudo --> off + Is it the case that nagios_run_sudo is not disabled? + + + + +To verify that SSSD expires offline credentials, run the following command: +$ sudo grep offline_credentials_expiration /etc/sssd/sssd.conf +If configured properly, output should be +offline_credentials_expiration = 1 + Is it the case that it does not exist or is not configured properly? + + + + The runtime status of the net.ipv4.conf.default.log_martians kernel parameter can be queried +by running the following command: +$ sysctl net.ipv4.conf.default.log_martians +1. + + Is it the case that the correct value is not returned? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_ARM64_SW_TTBR0_PAN /boot/config.* + + For each kernel installed, a line with value "y" should be returned. + + Is it the case that the kernel was not built with the required value? + + + + To verify that audit is configured for OSPP v4.2.1, run the following commands: +for file in "10-base-config" "11-loginuid" "30-ospp-v42" "43-module-load";do diff /etc/audit/rules.d/$file.rules /usr/share/doc/audit*/rules/$file.rules; done + +If the system is configured properly, no lines should be returned. + Is it the case that the files are not there or differ? + + + + To check if the system login banner is compliant, +run the following command: +$ cat /etc/issue + Is it the case that it does not display the required banner? + + + + +Run the following command to determine if the httpd_execmem SELinux boolean is disabled: +$ getsebool httpd_execmem +If properly configured, the output should show the following: +httpd_execmem --> off + Is it the case that httpd_execmem is not disabled? + + + + +Run the following command to determine if the polipo_session_users SELinux boolean is disabled: +$ getsebool polipo_session_users +If properly configured, the output should show the following: +polipo_session_users --> off + Is it the case that polipo_session_users is not disabled? + + + + To verify that Samba clients using mount.cifs must use packet signing, run the following command: +$ grep sec /etc/fstab +The output should show either krb5i or ntlmv2i in use. + Is it the case that it does not? + + + + To ensure smart card authentication on the login screen is enabled, run the following command: +$ grep enable-smartcard-authentication /etc/dconf/db/distro.d/* +The output should be true. +To ensure that users cannot disable smart card authentication on the login screen, run the following: +$ grep enable-smartcard-authentication /etc/dconf/db/distro.d/locks/* +If properly configured, the output should be /org/gnome/login-screen/enable-smartcard-authentication + Is it the case that enable-smartcard-authentication has not been configured or is disabled? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_SECCOMP_FILTER /boot/config.* + + For each kernel installed, a line with value "y" should be returned. + + Is it the case that the kernel was not built with the required value? + + + + Verify Red Hat Enterprise Linux 9 generates an audit record for unsuccessful attempts to use the ftruncate system call. + +If the auditd daemon is configured to use the "augenrules" program to to read audit rules during daemon startup (the default), run the following command: + +$ sudo grep -r ftruncate /etc/audit/rules.d + +If the auditd daemon is configured to use the "auditctl" utility to read audit rules during daemon startup, run the following command: + +$ sudo grep ftruncate /etc/audit/audit.rules + +The output should be the following: + +-a always,exit -F arch=b32 -S ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -k access +-a always,exit -F arch=b64 -S ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -k access +-a always,exit -F arch=b32 -S ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -k access +-a always,exit -F arch=b64 -S ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -k access + Is it the case that the command does not return a line, or the line is commented out? + + + + To determine if the system is configured to audit unsuccessful calls +to the setxattr system call, run the following command: +$ sudo grep "setxattr" /etc/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + Run the following command to determine if the krb5-server package is installed: $ rpm -q krb5-server + Is it the case that the package is installed? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_DEBUG_CREDENTIALS /boot/config.* + + For each kernel installed, a line with value "y" should be returned. + + Is it the case that the kernel was not built with the required value? + + + + To determine if the system is configured to audit calls to the +setxattr system call, run the following command: +$ sudo grep "setxattr" /etc/audit/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + +Run the following command to determine if the sanlock_use_samba SELinux boolean is disabled: +$ getsebool sanlock_use_samba +If properly configured, the output should show the following: +sanlock_use_samba --> off + Is it the case that sanlock_use_samba is not disabled? + + + + To verify that auditing of privileged command use is configured, run the +following command: +$ sudo grep '\bat\b' /etc/audit/audit.rules /etc/audit/rules.d/* +It should return a relevant line in the audit rules. + Is it the case that the command does not return a line, or the line is commented out? + + + + +Run the following command to determine if the mcelog_server SELinux boolean is disabled: +$ getsebool mcelog_server +If properly configured, the output should show the following: +mcelog_server --> off + Is it the case that mcelog_server is not disabled? + + + + Run the following command to determine if the rsyslog package is installed: $ rpm -q rsyslog + Is it the case that the package is not installed? + + + + +Run the following command to determine if the xdm_bind_vnc_tcp_port SELinux boolean is disabled: +$ getsebool xdm_bind_vnc_tcp_port +If properly configured, the output should show the following: +xdm_bind_vnc_tcp_port --> off + Is it the case that xdm_bind_vnc_tcp_port is not disabled? + + + + +Run the following command to determine if the entropyd_use_audio SELinux boolean is disabled: +$ getsebool entropyd_use_audio +If properly configured, the output should show the following: +entropyd_use_audio --> off + Is it the case that entropyd_use_audio is not disabled? + + + + +Run the following command to determine if the minidlna_read_generic_user_content SELinux boolean is disabled: +$ getsebool minidlna_read_generic_user_content +If properly configured, the output should show the following: +minidlna_read_generic_user_content --> off + Is it the case that minidlna_read_generic_user_content is not disabled? + + + + The runtime status of the net.ipv6.conf.default.accept_ra_rtr_pref kernel parameter can be queried +by running the following command: +$ sysctl net.ipv6.conf.default.accept_ra_rtr_pref +0. + + Is it the case that the correct value is not returned? + + + + Run the following command to determine if the geolite2-country package is installed: +$ rpm -q geolite2-country + Is it the case that the package is installed? + + + + Verify that Red Hat Enterprise Linux 9 is configured to audit the execution of the "poweroff" command with the following command: + +$ sudo auditctl -l | grep poweroff + +-a always,exit -F path=/usr/sbin/poweroff -F perm=x -F auid>=1000 -F auid!=unset -k privileged-poweroff + Is it the case that the command does not return a line, or the line is commented out? + + + + Inspect the file /etc/sysconfig/iptables to determine +the default policy for the INPUT chain. It should be set to DROP: +$ sudo grep ":INPUT" /etc/sysconfig/iptables + Is it the case that the default policy for the INPUT chain is not set to DROP? + + + + To verify that there are no .shosts files +on the system, run the following command: +$ sudo find / -name '.shosts' + Is it the case that .shosts files exist? + + + + +Run the following command to determine if the git_system_use_cifs SELinux boolean is disabled: +$ getsebool git_system_use_cifs +If properly configured, the output should show the following: +git_system_use_cifs --> off + Is it the case that git_system_use_cifs is not disabled? + + + + Run the following command to determine if the aide package is installed: $ rpm -q aide + Is it the case that the package is not installed? + + + + +Run the following command to determine if the httpd_tmp_exec SELinux boolean is disabled: +$ getsebool httpd_tmp_exec +If properly configured, the output should show the following: +httpd_tmp_exec --> off + Is it the case that httpd_tmp_exec is not disabled? + + + + Run the following command to determine if the usbguard package is installed: $ rpm -q usbguard + Is it the case that the package is not installed? + + + + +Run the following command to determine if the ftpd_use_cifs SELinux boolean is disabled: +$ getsebool ftpd_use_cifs +If properly configured, the output should show the following: +ftpd_use_cifs --> off + Is it the case that ftpd_use_cifs is not disabled? + + + + To check the permissions of /etc/group-, +run the command: +$ ls -l /etc/group- +If properly configured, the output should indicate the following permissions: +-rw-r--r-- + Is it the case that /etc/group- does not have unix mode -rw-r--r--? + + + + Run the following command to determine if the pcsc-lite package is installed: $ rpm -q pcsc-lite + Is it the case that the package is not installed? + + + + To verify that McAfee Endpoint Security for Linux is +running, run the following command: +$ sudo ps -ef | grep -i mfetpd + Is it the case that virus scanning software is not running? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_DEBUG_WX /boot/config.* + + For each kernel installed, a line with value "y" should be returned. + + Is it the case that the kernel was not built with the required value? + + + + +Run the following command to determine if the httpd_use_nfs SELinux boolean is disabled: +$ getsebool httpd_use_nfs +If properly configured, the output should show the following: +httpd_use_nfs --> off + Is it the case that httpd_use_nfs is not disabled? + + + + Run the following command to ensure that /var/tmp is configured as a +polyinstantiated directory: +$ sudo grep /var/tmp /etc/security/namespace.conf +The output should return the following: +/var/tmp /var/tmp/tmp-inst/ level root,adm + Is it the case that is not configured? + + + + These settings can be verified by running the following: +$ gsettings get org.gnome.desktop.media-handling automount +If properly configured, the output for automount should be false. +To ensure that users cannot enable automount in GNOME3, run the following: +$ grep 'automount' /etc/dconf/db/local.d/locks/* +If properly configured, the output for automount should be /org/gnome/desktop/media-handling/automount + Is it the case that GNOME automounting is not disabled? + + + + To find the location of the AIDE database file, run the following command: +$ sudo ls -l DBDIR/database_file_name + Is it the case that there is no database file? + + + + Verify that Red Hat Enterprise Linux 9 is configured to audit the execution of the "postdrop" command with the following command: + +$ sudo auditctl -l | grep postdrop + +-a always,exit -F path=/usr/bin/postdrop -F perm=x -F auid>=1000 -F auid!=unset -k privileged-postdrop + Is it the case that the command does not return a line, or the line is commented out? + + + + +Run the following command to determine if the authlogin_yubikey SELinux boolean is disabled: +$ getsebool authlogin_yubikey +If properly configured, the output should show the following: +authlogin_yubikey --> off + Is it the case that authlogin_yubikey is not disabled? + + + + +If the system is configured to prevent the loading of the cfg80211 kernel module, +it will contain lines inside any file in /etc/modprobe.d or the deprecated /etc/modprobe.conf. +These lines instruct the module loading system to run another program (such as /bin/true) upon a module install event. + +Run the following command to search for such lines in all files in /etc/modprobe.d and the deprecated /etc/modprobe.conf: +$ grep -r cfg80211 /etc/modprobe.conf /etc/modprobe.d + Is it the case that no line is returned? + + + + To check that SLUB/SLAB poisoning is enabled, check all boot entries with following command; +sudo grep -L "^options\s+.*\bslub_debug=P\b" /boot/loader/entries/*.conf +No line should be returned, each line returned is a boot entry that does not enable poisoning. + Is it the case that SLUB/SLAB poisoning is not enabled? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_VMAP_STACK /boot/config.* + + For each kernel installed, a line with value "y" should be returned. + + Is it the case that the kernel was not built with the required value? + + + + +Run the following command to determine if the httpd_can_check_spam SELinux boolean is disabled: +$ getsebool httpd_can_check_spam +If properly configured, the output should show the following: +httpd_can_check_spam --> off + Is it the case that httpd_can_check_spam is not disabled? + + + + +Run the following command to determine if the httpd_can_network_relay SELinux boolean is disabled: +$ getsebool httpd_can_network_relay +If properly configured, the output should show the following: +httpd_can_network_relay --> off + Is it the case that httpd_can_network_relay is not disabled? + + + + Verify that Red Hat Enterprise Linux 9 does not have unauthorized IP tunnels configured. + + +# dnf list installed libreswan +libreswan.x86-64 3.20-5.el7_4 + + +If "libreswan" is installed, check to see if the "IPsec" service is active with the following command: + +# systemctl status ipsec +ipsec.service - Internet Key Exchange (IKE) Protocol Daemon for IPsec +Loaded: loaded (/usr/lib/systemd/system/ipsec.service; disabled) +Active: inactive (dead) + + +If the "IPsec" service is active, check for configured IPsec connections (conn), perform the following: +grep -rni conn /etc/ipsec.conf /etc/ipsec.d/ +Verify any returned results for organizational approval. + Is it the case that the IPSec tunnels are not approved? + + + + +Run the following command to determine if the gluster_export_all_ro SELinux boolean is disabled: +$ getsebool gluster_export_all_ro +If properly configured, the output should show the following: +gluster_export_all_ro --> off + Is it the case that gluster_export_all_ro is not disabled? + + + + To determine the status and frequency of logrotate, run the following command: +$ sudo grep logrotate /var/log/cron* +If logrotate is configured properly, output should include references to +/etc/cron.daily. + Is it the case that logrotate is not configured to run daily? + + + + +Run the following command to determine if the dbadm_manage_user_files SELinux boolean is disabled: +$ getsebool dbadm_manage_user_files +If properly configured, the output should show the following: +dbadm_manage_user_files --> off + Is it the case that dbadm_manage_user_files is not disabled? + + + + +Run the following command to determine if the openvpn_run_unconfined SELinux boolean is disabled: +$ getsebool openvpn_run_unconfined +If properly configured, the output should show the following: +openvpn_run_unconfined --> off + Is it the case that openvpn_run_unconfined is not disabled? + + + + To ensure the system is configured to mask the Ctrl-Alt-Del sequence, Check +that the ctrl-alt-del.target is masked and not active with the following +command: +sudo systemctl status ctrl-alt-del.target +The output should indicate that the target is masked and not active. It +might resemble following output: +ctrl-alt-del.target +Loaded: masked (/dev/null; bad) +Active: inactive (dead) + Is it the case that the system is configured to reboot when Ctrl-Alt-Del is pressed? + + + + +To properly set the owner of /etc/audit/, run the command: +$ sudo chown root /etc/audit/ + +To properly set the owner of /etc/audit/rules.d/, run the command: +$ sudo chown root /etc/audit/rules.d/ + Is it the case that ? + + + + To ensure the failed password attempt policy is configured correctly, run the following command: + +$ grep fail_interval /etc/security/faillock.conf +The output should show fail_interval = <interval-in-seconds> where interval-in-seconds is or greater. + Is it the case that the "fail_interval" option is not set to "<sub idref="var_accounts_passwords_pam_faillock_fail_interval" />" +or less (but not "0"), the line is commented out, or the line is missing? + + + + To determine if the system is configured to audit calls to the +open_by_handle_at system call, run the following command: +$ sudo grep "open_by_handle_at" /etc/audit/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + The runtime status of the fs.protected_symlinks kernel parameter can be queried +by running the following command: +$ sysctl fs.protected_symlinks +1. + + Is it the case that the correct value is not returned? + + + + Inspect the form of default GRUB 2 command line for the Linux operating system +in /etc/default/grub. If it includes vsyscall=none, +then the parameter will be configured for newly installed kernels. +First check if the GRUB recovery is enabled: +$ sudo grep 'GRUB_DISABLE_RECOVERY' /etc/default/grub +If this option is set to true, then check that a line is output by the following command: +$ sudo grep 'GRUB_CMDLINE_LINUX_DEFAULT.*vsyscall=none.*' /etc/default/grub +If the recovery is disabled, check the line with +$ sudo grep 'GRUB_CMDLINE_LINUX.*vsyscall=none.*' /etc/default/grub.Moreover, command line parameters for currently installed kernels should be checked as well. +Run the following command: +$ sudo grubby --info=ALL | grep args | grep -v 'vsyscall=none' +The command should not return any output. + Is it the case that vsyscalls are enabled? + + + + +Run the following command to determine if the saslauthd_read_shadow SELinux boolean is disabled: +$ getsebool saslauthd_read_shadow +If properly configured, the output should show the following: +saslauthd_read_shadow --> off + Is it the case that saslauthd_read_shadow is not disabled? + + + + To verify that the Audit is correctly configured according to recommended rules, check the content of the file with the following command: +cat /etc/audit/rules.d/30-ospp-v42-2-modify-failed.rules +The output has to be exactly as follows: +## Unsuccessful file modifications (open for write or truncate) +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b32 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b32 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification + Is it the case that the file does not exist or the content differs? + + + + To check that the cockpit service is disabled in system boot configuration, +run the following command: +$ sudo systemctl is-enabled cockpit +Output should indicate the cockpit service has either not been installed, +or has been disabled at all runlevels, as shown in the example below: +$ sudo systemctl is-enabled cockpit disabled + +Run the following command to verify cockpit is not active (i.e. not running) through current runtime configuration: +$ sudo systemctl is-active cockpit + +If the service is not running the command will return the following output: +inactive + +The service will also be masked, to check that the cockpit is masked, run the following command: +$ sudo systemctl show cockpit | grep "LoadState\|UnitFileState" + +If the service is masked the command will return the following outputs: + +LoadState=masked + +UnitFileState=masked + Is it the case that the "cockpit" is loaded and not masked? + + + + +Run the following command to determine if the sge_domain_can_network_connect SELinux boolean is disabled: +$ getsebool sge_domain_can_network_connect +If properly configured, the output should show the following: +sge_domain_can_network_connect --> off + Is it the case that sge_domain_can_network_connect is not disabled? + + + + Run the following command to determine if the libselinux package is installed: $ rpm -q libselinux + Is it the case that the package is not installed? + + + + The following command will list which files on the system +have file hashes different from what is expected by the RPM database. +$ rpm -Va --noconfig | awk '$1 ~ /..5/ && $2 != "c"' + Is it the case that there is output? + + + + To verify that the Audit is correctly configured according to recommended rules, check the content of the file with the following command: +cat /etc/audit/rules.d/30-ospp-v42-4-delete-failed.rules +The output has to be exactly as follows: +## Unsuccessful file delete +-a always,exit -F arch=b32 -S unlink,unlinkat,rename,renameat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete +-a always,exit -F arch=b64 -S unlinkat,renameat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete +-a always,exit -F arch=b32 -S unlink,unlinkat,rename,renameat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete +-a always,exit -F arch=b64 -S unlinkat,renameat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete + Is it the case that the file does not exist or the content differs? + + + + +Run the following command to determine if the virt_use_execmem SELinux boolean is disabled: +$ getsebool virt_use_execmem +If properly configured, the output should show the following: +virt_use_execmem --> off + Is it the case that virt_use_execmem is not disabled? + + + + To verify that the Audit is correctly configured according to recommended rules, check the content of the file with the following command: +cat /etc/audit/rules.d/30-ospp-v42-3-access-failed.rules +The output has to be exactly as follows: +## Unsuccessful file access (any other opens) This has to go last. +-a always,exit -F arch=b32 -S open,openat,openat2,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-access +-a always,exit -F arch=b64 -S openat,openat2,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-access +-a always,exit -F arch=b32 -S open,openat,openat2,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-access +-a always,exit -F arch=b64 -S openat,openat2,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-access + Is it the case that the file does not exist or the content differs? + + + + Verify Red Hat Enterprise Linux 9 generates an audit record for unsuccessful attempts to use the rename system call. + +If the auditd daemon is configured to use the "augenrules" program to to read audit rules during daemon startup (the default), run the following command: + +$ sudo grep -r rename /etc/audit/rules.d + +If the auditd daemon is configured to use the "auditctl" utility to read audit rules during daemon startup, run the following command: + +$ sudo grep rename /etc/audit/audit.rules + +The output should be the following: + +-a always,exit -F arch=b32 -S rename -F exit=-EPERM -F auid>=1000 -F auid!=unset -k unsuccessful-delete +-a always,exit -F arch=b64 -S rename -F exit=-EPERM -F auid>=1000 -F auid!=unset -k unsuccessful-delete +-a always,exit -F arch=b32 -S rename -F exit=-EACCES -F auid>=1000 -F auid!=unset -k unsuccessful-delete +-a always,exit -F arch=b64 -S rename -F exit=-EACCES -F auid>=1000 -F auid!=unset -k unsuccessful-delete + Is it the case that the command does not return a line, or the line is commented out? + + + + Verify Red Hat Enterprise Linux 9 generates an audit record for unsuccessful attempts to use the open_by_handle_at system call. + +If the auditd daemon is configured to use the "augenrules" program to to read audit rules during daemon startup (the default), run the following command: + +$ sudo grep -r open_by_handle_at /etc/audit/rules.d + +If the auditd daemon is configured to use the "auditctl" utility to read audit rules during daemon startup, run the following command: + +$ sudo grep open_by_handle_at /etc/audit/audit.rules + +The output should be the following: + +-a always,exit -F arch=b32 -S open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -k access +-a always,exit -F arch=b64 -S open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -k access +-a always,exit -F arch=b32 -S open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -k access +-a always,exit -F arch=b64 -S open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -k access + Is it the case that the command does not return a line, or the line is commented out? + + + + To ensure the gdm package group is removed, run the following command: +$ rpm -qi gdm +The output should be: +package gdm is not installed + Is it the case that gdm has not been removed? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_PROC_KCORE /boot/config.* + + Configs with value 'n' are not explicitly set in the file, so either commented lines or no + lines should be returned. + + Is it the case that the kernel was not built with the required value? + + + + Verify Red Hat Enterprise Linux 9 generates an audit record for unsuccessful attempts to create files using the open_by_handle_at system call with O_CREAT flag. + +If the auditd daemon is configured to use the "augenrules" program to read audit rules during daemon startup (the default), run the following command: + +$ sudo grep -r open_by_handle_at /etc/audit/rules.d + +If the auditd daemon is configured to use the "auditctl" utility to read audit rules during daemon startup, run the following command: + +$ sudo grep open_by_handle_at /etc/audit/audit.rules + +The output should be the following: + +-a always,exit -F arch=b32 -S open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + Is it the case that the command does not return a line, or the line is commented out? + + + + To check that the ntpdate service is disabled in system boot configuration, +run the following command: +$ sudo systemctl is-enabled ntpdate +Output should indicate the ntpdate service has either not been installed, +or has been disabled at all runlevels, as shown in the example below: +$ sudo systemctl is-enabled ntpdate disabled + +Run the following command to verify ntpdate is not active (i.e. not running) through current runtime configuration: +$ sudo systemctl is-active ntpdate + +If the service is not running the command will return the following output: +inactive + +The service will also be masked, to check that the ntpdate is masked, run the following command: +$ sudo systemctl show ntpdate | grep "LoadState\|UnitFileState" + +If the service is masked the command will return the following outputs: + +LoadState=masked + +UnitFileState=masked + Is it the case that the "ntpdate" is loaded and not masked? + + + + Verify Red Hat Enterprise Linux 9 generates audit records for all account creations, modifications, disabling, and termination events that affect "/var/log/tallylog" with the following command: + +$ sudo auditctl -l | grep /var/log/tallylog + +-w /var/log/tallylog -p wa -k logins + Is it the case that the command does not return a line, or the line is commented out? + + + + The runtime status of the net.ipv6.conf.all.max_addresses kernel parameter can be queried +by running the following command: +$ sysctl net.ipv6.conf.all.max_addresses +1. + + Is it the case that the correct value is not returned? + + + + To check for serial port entries which permit root login, +run the following command: +$ sudo grep ^ttyS/[0-9] /etc/securetty +If any output is returned, then root login over serial ports is permitted. + Is it the case that root login over serial ports is permitted? + + + + To determine if the system is configured to audit calls to the +open system call, run the following command: +$ sudo grep "open" /etc/audit/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + To check that the kdump service is disabled in system boot configuration, +run the following command: +$ sudo systemctl is-enabled kdump +Output should indicate the kdump service has either not been installed, +or has been disabled at all runlevels, as shown in the example below: +$ sudo systemctl is-enabled kdump disabled + +Run the following command to verify kdump is not active (i.e. not running) through current runtime configuration: +$ sudo systemctl is-active kdump + +If the service is not running the command will return the following output: +inactive + +The service will also be masked, to check that the kdump is masked, run the following command: +$ sudo systemctl show kdump | grep "LoadState\|UnitFileState" + +If the service is masked the command will return the following outputs: + +LoadState=masked + +UnitFileState=masked + Is it the case that the "kdump" is loaded and not masked? + + + + +To check that the telnet service is disabled in system boot configuration with xinetd, run the following command: +$ chkconfig telnet --list +Output should indicate the telnet service has either not been installed, or has been disabled, as shown in the example below: +$ chkconfig telnet --list + +Note: This output shows SysV services only and does not include native +systemd services. SysV configuration data might be overridden by native +systemd configuration. + +If you want to list systemd services use 'systemctl list-unit-files'. +To see services enabled on particular target use +'systemctl list-dependencies [target]'. + +telnet off + +To check that the telnet socket is disabled in system boot configuration with systemd, run the following command: +$ systemctl is-enabled telnet +Output should indicate the telnet socket has either not been installed, +or has been disabled at all runlevels, as shown in the example below: +$ sudo systemctl is-enabled telnetdisabled + +Run the following command to verify telnet is not active (i.e. not running) through current runtime configuration: +$ sudo systemctl is-active telnet + +If the socket is not running the command will return the following output: +inactive + +The socket will also be masked, to check that the telnet is masked, run the following command: +$ sudo systemctl show telnet | grep "LoadState\|UnitFileState" + +If the socket is masked the command will return the following outputs: + +LoadState=masked + +UnitFileState=masked + Is it the case that service and/or socket are running? + + + + The rsh package can be removed with the following command: $ sudo dnf erase rsh + Is it the case that ? + + + + Verify the nodev option is configured for the /home mount point, + run the following command: + $ sudo mount | grep '\s/home\s' + . . . /home . . . nodev . . . + + Is it the case that the "/home" file system does not have the "nodev" option set? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_BINFMT_MISC /boot/config.* + + Configs with value 'n' are not explicitly set in the file, so either commented lines or no + lines should be returned. + + Is it the case that the kernel was not built with the required value? + + + + Verify that Red Hat Enterprise Linux 9 is configured to audit the execution of the "usermod" command with the following command: + +$ sudo auditctl -l | grep usermod + +-a always,exit -F path=/usr/bin/usermod -F perm=x -F auid>=1000 -F auid!=unset -k privileged-usermod + Is it the case that the command does not return a line, or the line is commented out? + + + + Run the following command to determine if the openssh-clients package is installed: $ rpm -q openssh-clients + Is it the case that the package is not installed? + + + + +Run the following command to determine if the glance_api_can_network SELinux boolean is disabled: +$ getsebool glance_api_can_network +If properly configured, the output should show the following: +glance_api_can_network --> off + Is it the case that glance_api_can_network is not disabled? + + + + + +Run the following command to determine the current status of the +usbguard service: +$ sudo systemctl is-active usbguard +If the service is running, it should return the following: active + Is it the case that the service is not enabled? + + + + +If the system is configured to prevent the loading of the mac80211 kernel module, +it will contain lines inside any file in /etc/modprobe.d or the deprecated /etc/modprobe.conf. +These lines instruct the module loading system to run another program (such as /bin/true) upon a module install event. + +Run the following command to search for such lines in all files in /etc/modprobe.d and the deprecated /etc/modprobe.conf: +$ grep -r mac80211 /etc/modprobe.conf /etc/modprobe.d + Is it the case that no line is returned? + + + + Verify "firewalld" is configured to employ a deny-all, allow-by-exception policy for allowing connections to other systems with the following commands: + +$ sudo firewall-cmd --state + +running + +$ sudo firewall-cmd --get-active-zones + +[custom] +interfaces: ens33 + +$ sudo firewall-cmd --info-zone=[custom] | grep target + +target: DROP + Is it the case that no zones are active on the interfaces or if the target is set to a different option other than "DROP"? + + + + To determine how the SSH daemon's HostbasedAuthentication option is set, run the following command: + +$ sudo grep -i HostbasedAuthentication /etc/ssh/sshd_config.d/00-complianceascode-hardening.conf +$ sudo grep -i HostbasedAuthentication /etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf + +If a line indicating no is returned, then the required value is set. + + Is it the case that the required value is not set? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_COMPAT_BRK /boot/config.* + + Configs with value 'n' are not explicitly set in the file, so either commented lines or no + lines should be returned. + + Is it the case that the kernel was not built with the required value? + + + + +Run the following command to determine if the lsmd_plugin_connect_any SELinux boolean is disabled: +$ getsebool lsmd_plugin_connect_any +If properly configured, the output should show the following: +lsmd_plugin_connect_any --> off + Is it the case that lsmd_plugin_connect_any is not disabled? + + + + To determine how the SSH daemon's LogLevel option is set, run the following command: + +$ sudo grep -i LogLevel /etc/ssh/sshd_config.d/00-complianceascode-hardening.conf + + +If a line indicating VERBOSE is returned, then the required value is set. + + Is it the case that the required value is not set? + + + + Run the following command to determine if the gnutls-utils package is installed: $ rpm -q gnutls-utils + Is it the case that the package is not installed? + + + + Verify that the SA and ISSO (at a minimum) are notified when the audit storage volume is full. + +Check which action Red Hat Enterprise Linux 9 takes when the audit storage volume is full with the following command: + +$ sudo grep max_log_file_action /etc/audit/auditd.conf +max_log_file_action = + Is it the case that the value of the "max_log_file_action" option is set to "ignore", "rotate", or "suspend", or the line is commented out? + + + + To verify that the Audit is correctly configured according to recommended rules, check the content of the file with the following command: +cat /etc/audit/rules.d/30-ospp-v42-6-owner-change-success.rules +The output has to be exactly as follows: +## Successful ownership change +-a always,exit -F arch=b32 -S lchown,fchown,chown,fchownat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-owner-change +-a always,exit -F arch=b64 -S lchown,fchown,chown,fchownat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-owner-change + Is it the case that the file does not exist or the content differs? + + + + Storing logs remotely protects the integrity of the data from local attacks. +Run the following command to verify that journald is forwarding logs to a remote host. + +grep "^\sForwardToSyslog" /etc/systemd/journald.conf + +and it should return + +ForwardToSyslog=yes + + Is it the case that is commented out or not configured correctly? + + + + +Run the following command to determine if the ftpd_full_access SELinux boolean is disabled: +$ getsebool ftpd_full_access +If properly configured, the output should show the following: +ftpd_full_access --> off + Is it the case that ftpd_full_access is not disabled? + + + + To determine if NOPASSWD has been configured for sudo, run the following command: +$ sudo grep -ri nopasswd /etc/sudoers /etc/sudoers.d/ +The command should return no output. + Is it the case that nopasswd is specified in the sudo config files? + + + + First, check whether the password is defined in either /boot/grub2/user.cfg or +/boot/grub2/grub.cfg. +Run the following commands: +$ sudo grep '^[\s]*GRUB2_PASSWORD=grub\.pbkdf2\.sha512.*$' /boot/grub2/user.cfg +$ sudo grep '^[\s]*password_pbkdf2[\s]+.*[\s]+grub\.pbkdf2\.sha512.*$' /boot/grub2/grub.cfg + + +Second, check that a superuser is defined in /boot/grub2/grub.cfg. +$ sudo grep '^[\s]*set[\s]+superusers=("?)[a-zA-Z_]+\1$' /boot/grub2/grub.cfg + Is it the case that it does not produce any output? + + + + To check how many uppercase characters are required in a password, run the following command: +$ grep ucredit /etc/security/pwquality.conf +The ucredit parameter (as a negative number) will indicate how many uppercase characters are required. +This would appear as ucredit = -1. + Is it the case that ucredit is not found or not set to the required value? + + + + +Run the following command to determine if the use_fusefs_home_dirs SELinux boolean is disabled: +$ getsebool use_fusefs_home_dirs +If properly configured, the output should show the following: +use_fusefs_home_dirs --> off + Is it the case that use_fusefs_home_dirs is not disabled? + + + + Verify the nosuid option is configured for the /srv mount point, + run the following command: + $ sudo mount | grep '\s/srv\s' + . . . /srv . . . nosuid . . . + + Is it the case that the "/srv" file system does not have the "nosuid" option set? + + + + Verify that the system backups user data. + Is it the case that it is not? + + + + To determine if the system is configured to audit successful calls +to the ftruncate system call, run the following command: +$ sudo grep "ftruncate" /etc/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + +Run the following command to determine if the cobbler_use_nfs SELinux boolean is disabled: +$ getsebool cobbler_use_nfs +If properly configured, the output should show the following: +cobbler_use_nfs --> off + Is it the case that cobbler_use_nfs is not disabled? + + + + To check that the kernel is configured to zero out memory before allocation +time, check all boot entries with following command: +sudo grep -L"^options\s+.*\binit_on_alloc=1\b" /boot/loader/entries/*.conf +No line should be returned, each line returned is a boot entry that doesn't enable audit. + Is it the case that the kernel is not configured to zero out memory before allocation? + + + + To check the ownership of /var/log/messages, +run the command: +$ ls -lL /var/log/messages +If properly configured, the output should indicate the following owner: +root + Is it the case that /var/log/messages does not have an owner of root? + + + + To determine if the system is configured to audit successful calls +to the openat system call, run the following command: +$ sudo grep "openat" /etc/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + +Run the following command to determine if the cluster_can_network_connect SELinux boolean is disabled: +$ getsebool cluster_can_network_connect +If properly configured, the output should show the following: +cluster_can_network_connect --> off + Is it the case that cluster_can_network_connect is not disabled? + + + + To verify that smart cards are enabled in SSSD, run the following command: +$ sudo grep pam_cert_auth /etc/sssd/sssd.conf +If configured properly, output should be +pam_cert_auth = True + + +To verify that smart cards are enabled in PAM files, run the following command: +$ sudo grep -e "auth.*pam_sss\.so.*\(allow_missing_name\|try_cert_auth\)" /etc/pam.d/smartcard-auth /etc/pam.d/system-auth +If configured properly, output should be + +/etc/pam.d/smartcard-auth:auth sufficient pam_sss.so allow_missing_name +/etc/pam.d/system-auth:auth [success=done authinfo_unavail=ignore ignore=ignore default=die] pam_sss.so try_cert_auth + + Is it the case that smart cards are not enabled in SSSD? + + + + +Run the following command to determine if the collectd_tcp_network_connect SELinux boolean is disabled: +$ getsebool collectd_tcp_network_connect +If properly configured, the output should show the following: +collectd_tcp_network_connect --> off + Is it the case that collectd_tcp_network_connect is not disabled? + + + + Verify the operating system encrypts audit records off-loaded onto a different system +or media from the system being audited with the following commands: + +$ sudo grep -i '$ActionSendStreamDriverMode' /etc/rsyslog.conf /etc/rsyslog.d/*.conf + +The output should be: + +/etc/rsyslog.conf:$ActionSendStreamDriverMode 1 + Is it the case that rsyslogd ActionSendStreamDriverMode is not set to 1? + + + + Verify the audit tools are owned by "root" to prevent any unauthorized access, deletion, or modification. + +Check the owner of each audit tool by running the following command: + +$ sudo stat -c "%U %n" /sbin/auditctl /sbin/aureport /sbin/ausearch /sbin/autrace /sbin/auditd /sbin/rsyslogd /sbin/augenrules + +root /sbin/auditctl +root /sbin/aureport +root /sbin/ausearch +root /sbin/autrace +root /sbin/auditd +root /sbin/rsyslogd +root /sbin/augenrules + Is it the case that any audit tools are not owned by root? + + + + To check that audit is enabled at boot time, check all boot entries with following command: +sudo grep -L "^options\s+.*\baudit=1\b" /boot/loader/entries/*.conf +No line should be returned, each line returned is a boot entry that doesn't enable audit. + Is it the case that auditing is not enabled at boot time? + + + + Verify the system-wide shared library directories are owned by "root" with the following command: + +$ sudo find /lib /lib64 /usr/lib /usr/lib64 ! -user root -type d -exec stat -c "%n %U" '{}' \; + Is it the case that any system-wide shared library directory is not owned by root? + + + + To check that the debug-shell service is disabled in system boot configuration, +run the following command: +$ sudo systemctl is-enabled debug-shell +Output should indicate the debug-shell service has either not been installed, +or has been disabled at all runlevels, as shown in the example below: +$ sudo systemctl is-enabled debug-shell disabled + +Run the following command to verify debug-shell is not active (i.e. not running) through current runtime configuration: +$ sudo systemctl is-active debug-shell + +If the service is not running the command will return the following output: +inactive + +The service will also be masked, to check that the debug-shell is masked, run the following command: +$ sudo systemctl show debug-shell | grep "LoadState\|UnitFileState" + +If the service is masked the command will return the following outputs: + +LoadState=masked + +UnitFileState=masked + Is it the case that the "debug-shell" is loaded and not masked? + + + + +Run the following command to get the current configured value for polyinstantiation_enabled +SELinux boolean: +$ getsebool polyinstantiation_enabled +The expected cofiguration is . +"on" means true, and "off" means false + Is it the case that polyinstantiation_enabled is not set as expected? + + + + Verify that Red Hat Enterprise Linux 9 is configured to audit the execution of the "unix_update" command with the following command: + +$ sudo auditctl -l | grep unix_update + +-a always,exit -F path=/usr/bin/unix_update -F perm=x -F auid>=1000 -F auid!=unset -k privileged-unix_update + Is it the case that the command does not return a line, or the line is commented out? + + + + The runtime status of the kernel.dmesg_restrict kernel parameter can be queried +by running the following command: +$ sysctl kernel.dmesg_restrict +1. + + Is it the case that the correct value is not returned? + + + + The runtime status of the net.ipv4.conf.all.arp_filter kernel parameter can be queried +by running the following command: +$ sysctl net.ipv4.conf.all.arp_filter +. + + Is it the case that the correct value is not returned? + + + + To determine if NOPASSWD has been configured for the vdsm user for sudo, +run the following command: +$ sudo grep -ri nopasswd /etc/sudoers.d/ +The command should return output only for the vdsm user. + Is it the case that nopasswd is set for any users beyond vdsm? + + + + +Run the following command to determine if the ssh_chroot_rw_homedirs SELinux boolean is disabled: +$ getsebool ssh_chroot_rw_homedirs +If properly configured, the output should show the following: +ssh_chroot_rw_homedirs --> off + Is it the case that ssh_chroot_rw_homedirs is not disabled? + + + + To ensure sshd limits the users who can log in, run the following: +$ sudo grep AllowUsers /etc/ssh/sshd_config +If properly configured, the output should be a list of usernames allowed to log in +to this system. + Is it the case that sshd does not limit the users who can log in? + + + + Verify Red Hat Enterprise Linux 9 generates an audit record for unsuccessful attempts to use the truncate system call. + +If the auditd daemon is configured to use the "augenrules" program to to read audit rules during daemon startup (the default), run the following command: + +$ sudo grep -r truncate /etc/audit/rules.d + +If the auditd daemon is configured to use the "auditctl" utility to read audit rules during daemon startup, run the following command: + +$ sudo grep truncate /etc/audit/audit.rules + +The output should be the following: + +-a always,exit -F arch=b32 -S truncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -k access +-a always,exit -F arch=b64 -S truncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -k access +-a always,exit -F arch=b32 -S truncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -k access +-a always,exit -F arch=b64 -S truncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -k access + Is it the case that the command does not return a line, or the line is commented out? + + + + To verify that session locking after period of inactivity is configured in tmux, +run the following command: + +$ sudo grep lock-after-time /etc/tmux.conf + +The output should return the following: + +set -g lock-after-time 900 + +Then, verify that the /etc/tmux.conf file can be read by other users than root: + +$ sudo ls -al /etc/tmux.conf + Is it the case that lock-after-time is set to a value greater than 900 or zero? + + + + Verify that cron is logging to rsyslog, +run the following command: +grep -rni "cron\.\*" /etc/rsyslog.* +cron.* /var/log/cron + Is it the case that cron is not logging to rsyslog? + + + + +Run the following command to determine if the exim_manage_user_files SELinux boolean is disabled: +$ getsebool exim_manage_user_files +If properly configured, the output should show the following: +exim_manage_user_files --> off + Is it the case that exim_manage_user_files is not disabled? + + + + Run the following command to determine if the tmux package is installed: $ rpm -q tmux + Is it the case that the package is not installed? + + + + +Run the following command to determine if the virt_transition_userdomain SELinux boolean is disabled: +$ getsebool virt_transition_userdomain +If properly configured, the output should show the following: +virt_transition_userdomain --> off + Is it the case that virt_transition_userdomain is not disabled? + + + + +Run the following command to determine if the polipo_use_nfs SELinux boolean is disabled: +$ getsebool polipo_use_nfs +If properly configured, the output should show the following: +polipo_use_nfs --> off + Is it the case that polipo_use_nfs is not disabled? + + + + +Run the following command to determine if the rsync_client SELinux boolean is disabled: +$ getsebool rsync_client +If properly configured, the output should show the following: +rsync_client --> off + Is it the case that rsync_client is not disabled? + + + + +Run the following command to determine if the mount_anyfile SELinux boolean is enabled: +$ getsebool mount_anyfile +If properly configured, the output should show the following: +mount_anyfile --> on + Is it the case that mount_anyfile is not enabled? + + + + +Run the following command to determine if the httpd_dbus_avahi SELinux boolean is disabled: +$ getsebool httpd_dbus_avahi +If properly configured, the output should show the following: +httpd_dbus_avahi --> off + Is it the case that httpd_dbus_avahi is not disabled? + + + + Verify that Red Hat Enterprise Linux 9 is configured to audit the execution of the "unix_chkpwd" command with the following command: + +$ sudo auditctl -l | grep unix_chkpwd + +-a always,exit -F path=/usr/bin/unix_chkpwd -F perm=x -F auid>=1000 -F auid!=unset -k privileged-unix_chkpwd + Is it the case that the command does not return a line, or the line is commented out? + + + + To verify the sec option is configured for all NFS mounts, run the following command: +$ grep "sec=" /etc/exports +All configured NFS exports should show the sec=krb5:krb5i:krb5p setting in parentheses. +This is not applicable if NFS is not implemented. + Is it the case that the setting is not configured, has the 'sys' option added, or does not have all Kerberos options added? + + + + +Run the following command to determine if the daemons_use_tcp_wrapper SELinux boolean is disabled: +$ getsebool daemons_use_tcp_wrapper +If properly configured, the output should show the following: +daemons_use_tcp_wrapper --> off + Is it the case that daemons_use_tcp_wrapper is not disabled? + + + + To verify if GnuTLS uses defined DoD-approved TLS Crypto Policy, run: +$ sudo grep +'+VERS-ALL:-VERS-DTLS0.9:-VERS-SSL3.0:-VERS-TLS1.0:-VERS-TLS1.1:-VERS-DTLS1.0' +/etc/crypto-policies/back-ends/gnutls.config and verify that a match exists. + Is it the case that cryptographic policy for gnutls is not configured or is configured incorrectly? + + + + Verify that a separate file system/partition has been created for /var/tmp with the following command: + +$ mountpoint /var/tmp + + Is it the case that "/var/tmp is not a mountpoint" is returned? + + + + To determine if the system is configured to audit unsuccessful calls +to the removexattr system call, run the following command: +$ sudo grep "removexattr" /etc/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + If IPv6 is disabled, this is not applicable. + + + +Run the following command to determine the current status of the +ip6tables service: +$ sudo systemctl is-active ip6tables +If the service is running, it should return the following: active + Is it the case that ? + + + + +Run the following command to determine if the virt_use_usb SELinux boolean is disabled: +$ getsebool virt_use_usb +If properly configured, the output should show the following: +virt_use_usb --> off + Is it the case that virt_use_usb is not disabled? + + + + Verify Red Hat Enterprise Linux 9 generates audit records for all account creations, modifications, disabling, and termination events that affect "/etc/security/opasswd" with the following command: + +$ sudo auditctl -l | grep /var/run/faillock + +-w /var/run/faillock -p wa -k logins + Is it the case that the command does not return a line, or the line is commented out? + + + + +Run the following command to determine if the rsync_export_all_ro SELinux boolean is disabled: +$ getsebool rsync_export_all_ro +If properly configured, the output should show the following: +rsync_export_all_ro --> off + Is it the case that rsync_export_all_ro is not disabled? + + + + +Run the following command to determine if the logging_syslogd_use_tty SELinux boolean is enabled: +$ getsebool logging_syslogd_use_tty +If properly configured, the output should show the following: +logging_syslogd_use_tty --> on + Is it the case that logging_syslogd_use_tty is not enabled? + + + + To check the screensaver rhisam use status, run the following command: +$ gsettings get org.gnome.desktop.screensaver idle-activation-enabled +If properly configured, the output should be true. +To ensure that users cannot disable the screensaver idle inactivity setting, run the following: +$ grep idle-activation-enabled /etc/dconf/db/local.d/locks/* +If properly configured, the output should be /org/gnome/desktop/screensaver/idle-activation-enabled + Is it the case that idle-activation-enabled is not enabled or configured? + + + + +Run the following command to determine if the kerberos_enabled SELinux boolean is enabled: +$ getsebool kerberos_enabled +If properly configured, the output should show the following: +kerberos_enabled --> on + Is it the case that kerberos_enabled is not enabled? + + + + +Run the following command to determine if the mcelog_foreground SELinux boolean is disabled: +$ getsebool mcelog_foreground +If properly configured, the output should show the following: +mcelog_foreground --> off + Is it the case that mcelog_foreground is not disabled? + + + + Verify the audit tools are group-owned by "root" to prevent any unauthorized access, deletion, or modification. + +Check the group-owner of each audit tool by running the following command: + +$ sudo stat -c "%G %n" /sbin/auditctl /sbin/aureport /sbin/ausearch /sbin/autrace /sbin/auditd /sbin/rsyslogd /sbin/augenrules + +root /sbin/auditctl +root /sbin/aureport +root /sbin/ausearch +root /sbin/autrace +root /sbin/auditd +root /sbin/rsyslogd +root /sbin/augenrules + Is it the case that any audit tools are not group-owned by root? + + + + Verify that Red Hat Enterprise Linux 9 is configured to audit the execution of the "userhelper" command with the following command: + +$ sudo auditctl -l | grep userhelper + +-a always,exit -F path=/usr/bin/userhelper -F perm=x -F auid>=1000 -F auid!=unset -k privileged-userhelper + Is it the case that the command does not return a line, or the line is commented out? + + + + Verify that a separate file system/partition has been created for /home with the following command: + +$ mountpoint /home + + Is it the case that "/home is not a mountpoint" is returned? + + + + Check to see if Online Certificate Status Protocol (OCSP) +is enabled and using the proper digest value on the system with the following command: +$ sudo grep certificate_verification /etc/sssd/sssd.conf /etc/sssd/conf.d/*.conf | grep -v "^#" +If configured properly, output should look like + + certificate_verification = ocsp_dgst= + + Is it the case that certificate_verification in sssd is not configured? + + + + To verify that kernel parameter 'crypto.fips_enabled' is set properly, run the following command: +sysctl crypto.fips_enabled +The output should contain the following: +crypto.fips_enabled = 1 + Is it the case that crypto.fips_enabled is not 1? + + + + To check the maximum password age, run the command: +$ grep PASS_MAX_DAYS /etc/login.defs +The profile requirement is . + Is it the case that PASS_MAX_DAYS is not set equal to or greater than the required value? + + + + Run the following command to determine if the openssh-server package is installed: $ rpm -q openssh-server + Is it the case that the package is installed? + + + + Verify the nosuid option is configured for the /home mount point, + run the following command: + $ sudo mount | grep '\s/home\s' + . . . /home . . . nosuid . . . + + Is it the case that the "/home" file system does not have the "nosuid" option set? + + + + +Run the following command to determine if the ssh_keysign SELinux boolean is disabled: +$ getsebool ssh_keysign +If properly configured, the output should show the following: +ssh_keysign --> off + Is it the case that ssh_keysign is not disabled? + + + + +Run the following command to determine if the mozilla_plugin_can_network_connect SELinux boolean is disabled: +$ getsebool mozilla_plugin_can_network_connect +If properly configured, the output should show the following: +mozilla_plugin_can_network_connect --> off + Is it the case that mozilla_plugin_can_network_connect is not disabled? + + + + Verify that authselect is enabled by running +authselect current +If authselect is enabled on the system, the output should show the ID of the profile which is currently in use. + Is it the case that authselect is not used to manage user authentication setup on the system? + + + + +If the system is configured to prevent the loading of the iwlwifi kernel module, +it will contain lines inside any file in /etc/modprobe.d or the deprecated /etc/modprobe.conf. +These lines instruct the module loading system to run another program (such as /bin/true) upon a module install event. + +Run the following command to search for such lines in all files in /etc/modprobe.d and the deprecated /etc/modprobe.conf: +$ grep -r iwlwifi /etc/modprobe.conf /etc/modprobe.d + Is it the case that no line is returned? + + + + To verify that tmux is configured to execute, +run the following command: +$ grep -A1 -B3 "case ..name. in sshd|login) exec tmux ;; esac" /etc/bashrc /etc/profile.d/* +The output should return the following: +if [ "$PS1" ]; then + parent=$(ps -o ppid= -p $$) + name=$(ps -o comm= -p $parent) + case "$name" in sshd|login) exec tmux ;; esac +fi + Is it the case that exec tmux is not present at the end of bashrc? + + + + Inspect /etc/audit/auditd.conf and locate the following line to +determine how many logs the system is configured to retain after rotation: +$ sudo grep num_logs /etc/audit/auditd.conf +num_logs = 5 + Is it the case that the system log file retention has not been properly configured? + + + + Verify that the IPSec service uses the system crypto policy. + +If the ipsec service is not installed is not applicable. + +Check to see if the "IPsec" service is active with the following command: + +$ systemctl status ipsec + +ipsec.service - Internet Key Exchange (IKE) Protocol Daemon for IPsec +Loaded: loaded (/usr/lib/systemd/system/ipsec.service; disabled) +Active: inactive (dead) + +If the "IPsec" service is active, check to see if it is using the system crypto policy with the following command: + +$ sudo grep include /etc/ipsec.conf /etc/ipsec.d/*.conf + +/etc/ipsec.conf:include /etc/crypto-policies/back-ends/libreswan.config + Is it the case that the "IPsec" service is active and the ipsec configuration file does not contain does not contain <tt>include /etc/crypto-policies/back-ends/libreswan.config</tt>? + + + + To verify that USB Human Interface Devices will be authorized by the USBGuard daemon, +run the following command: +$ sudo grep allow /etc/usbguard/rules.conf +The output lines should include +allow with-interface match-all { 03:*:* } + Is it the case that USB devices of class 3 are not authorized? + + + + To verify that Audit Daemon is configured to flush to disk after +every records, run the following command: +$ sudo grep freq /etc/audit/auditd.conf +The output should return the following: +freq = + Is it the case that freq isn't set to <sub idref="var_auditd_freq" />? + + + + +Run the following command to determine if the mplayer_execstack SELinux boolean is disabled: +$ getsebool mplayer_execstack +If properly configured, the output should show the following: +mplayer_execstack --> off + Is it the case that mplayer_execstack is not disabled? + + + + +Run the following command to determine if the fcron_crond SELinux boolean is disabled: +$ getsebool fcron_crond +If properly configured, the output should show the following: +fcron_crond --> off + Is it the case that fcron_crond is not disabled? + + + + +Run the following command to determine if the exim_read_user_files SELinux boolean is disabled: +$ getsebool exim_read_user_files +If properly configured, the output should show the following: +exim_read_user_files --> off + Is it the case that exim_read_user_files is not disabled? + + + + Check whether the maximum time period for existing passwords is restricted to days with the following commands: + +$ sudo awk -F: '$5 > 60 {print $1 " " $5}' /etc/shadow + +$ sudo awk -F: '$5 <= 0 {print $1 " " $5}' /etc/shadow + Is it the case that any results are returned that are not associated with a system account? + + + + To determine if the system is configured to audit unsuccessful calls +to the fchown system call, run the following command: +$ sudo grep "fchown" /etc/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + The runtime status of the kernel.pid_max kernel parameter can be queried +by running the following command: +$ sysctl kernel.pid_max +65536. + + Is it the case that the correct value is not returned? + + + + To check the group ownership of /etc/ssh/sshd_config, +run the command: +$ ls -lL /etc/ssh/sshd_config +If properly configured, the output should indicate the following group-owner: +root + Is it the case that /etc/ssh/sshd_config does not have a group owner of root? + + + + Verify the nosuid option is configured for the /boot mount point, + run the following command: + $ sudo mount | grep '\s/boot\s' + . . . /boot . . . nosuid . . . + + Is it the case that the "/boot" file system does not have the "nosuid" option set? + + + + Verify Red Hat Enterprise Linux 9 is configured to lock an account after +unsuccessful logon attempts with the command: + + +$ grep 'deny =' /etc/security/faillock.conf +deny = . + Is it the case that the "deny" option is not set to "<sub idref="var_accounts_passwords_pam_faillock_deny" />" +or less (but not "0"), is missing or commented out? + + + + Run the following command to determine if the dovecot package is installed: +$ rpm -q dovecot + Is it the case that the package is installed? + + + + +If the system is configured to prevent the loading of the usb-storage kernel module, +it will contain lines inside any file in /etc/modprobe.d or the deprecated /etc/modprobe.conf. +These lines instruct the module loading system to run another program (such as /bin/true) upon a module install event. + +Run the following command to search for such lines in all files in /etc/modprobe.d and the deprecated /etc/modprobe.conf: +$ grep -r usb-storage /etc/modprobe.conf /etc/modprobe.d + Is it the case that no line is returned? + + + + To check the group ownership of /etc/cron.hourly, +run the command: +$ ls -lL /etc/cron.hourly +If properly configured, the output should indicate the following group-owner: +root + Is it the case that /etc/cron.hourly does not have a group owner of root? + + + + +Run the following command to determine if the virt_sandbox_use_netlink SELinux boolean is disabled: +$ getsebool virt_sandbox_use_netlink +If properly configured, the output should show the following: +virt_sandbox_use_netlink --> off + Is it the case that virt_sandbox_use_netlink is not disabled? + + + + +Run the following command to determine if the ftpd_anon_write SELinux boolean is disabled: +$ getsebool ftpd_anon_write +If properly configured, the output should show the following: +ftpd_anon_write --> off + Is it the case that ftpd_anon_write is not disabled? + + + + To determine if requiretty has been configured for sudo, run the following command: +$ sudo grep -ri "^[\s]*Defaults.*\brequiretty\b.*" /etc/sudoers /etc/sudoers.d/ +The command should return a matching output. + Is it the case that requiretty is not enabled in sudo? + + + + To check the permissions of /etc/gshadow-, +run the command: +$ ls -l /etc/gshadow- +If properly configured, the output should indicate the following permissions: +---------- + Is it the case that /etc/gshadow- does not have unix mode ----------? + + + + To verify that port has been set properly, perform the following: +$ grep '\bport\b' /etc/chrony.conf +The output should return +port 0 + Is it the case that port is not set or port is set to a non-zero value? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_REFCOUNT_FULL /boot/config.* + + For each kernel installed, a line with value "y" should be returned. + + Is it the case that the kernel was not built with the required value? + + + + The runtime status of the kernel.modules_disabled kernel parameter can be queried +by running the following command: +$ sysctl kernel.modules_disabled +1. + + Is it the case that the correct value is not returned? + + + + To verify that cryptography policy has been configured correctly, run the +following command: +$ update-crypto-policies --show +The output should return . +Run the command to check if the policy is correctly applied: +$ update-crypto-policies --is-applied +The output should be The configured policy is applied. +Moreover, check if settings for selected crypto policy are as expected. +List all libraries for which it holds that their crypto policies do not have symbolic link in /etc/crypto-policies/back-ends. +$ ls -l /etc/crypto-policies/back-ends/ | grep '^[^l]' | tail -n +2 | awk -F' ' '{print $NF}' | awk -F'.' '{print $1}' | sort +Subsequently, check if matching libraries have drop in files in the /etc/crypto-policies/local.d directory. +$ ls /etc/crypto-policies/local.d/ | awk -F'-' '{print $1}' | uniq | sort +Outputs of two previous commands should match. + Is it the case that cryptographic policy is not configured or is configured incorrectly? + + + + To check if the system login banner is compliant, +run the following command: +$ cat /etc/motd + Is it the case that it does not display the required banner? + + + + Run the following command to check the mode of the system audit logs: +$ sudo grep -iw log_file /etc/audit/auditd.conf +log_file=/var/log/audit/audit.log +$ sudo stat -c "%n %a" /var/log/audit/* +$ sudo ls -l /var/log/audit +Audit logs must be mode 0640 or less permissive. + Is it the case that any permissions are more permissive? + + + + +Run the following command to determine if the mozilla_plugin_use_gps SELinux boolean is disabled: +$ getsebool mozilla_plugin_use_gps +If properly configured, the output should show the following: +mozilla_plugin_use_gps --> off + Is it the case that mozilla_plugin_use_gps is not disabled? + + + + Run the following command to determine if the nfs-utils package is installed: +$ rpm -q nfs-utils + Is it the case that the package is installed? + + + + +Run the following command to determine if the nfs_export_all_ro SELinux boolean is enabled: +$ getsebool nfs_export_all_ro +If properly configured, the output should show the following: +nfs_export_all_ro --> on + Is it the case that nfs_export_all_ro is not enabled? + + + + Make sure that /boot/bootmap is newer than /boot/loader/entries/*.conf +and /etc/zipl.conf: +find /boot/loader/entries/*.conf /etc/zipl.conf -newer /boot/bootmap +No line should be returned, if a line is returned /boot/bootmap is outdated and needs to be regenerated. + Is it the case that the bootmap is outdated? + + + + Run the following command to determine if the McAfeeTP package is installed: $ rpm -q McAfeeTP + Is it the case that the package is not installed? + + + + Run the following command to determine if the syslog-ng-core package is installed: $ rpm -q syslog-ng-core + Is it the case that the package is not installed? + + + + +Run the following command to determine if the glance_use_fusefs SELinux boolean is disabled: +$ getsebool glance_use_fusefs +If properly configured, the output should show the following: +glance_use_fusefs --> off + Is it the case that glance_use_fusefs is not disabled? + + + + To ensure there are no read-write users, run the following command: +$ sudo grep -v "^#" /etc/snmp/snmpd.conf| grep 'rwuser' +There should be no output. + Is it the case that there are users who can write to SNMP values? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_PANIC_ON_OOPS /boot/config.* + + For each kernel installed, a line with value "y" should be returned. + + Is it the case that the kernel was not built with the required value? + + + + To ensure the screensaver is configured to be blank, run the following command: +$ gsettings get org.gnome.desktop.screensaver picture-uri +If properly configured, the output should be ''. + +To ensure that users cannot set the screensaver background, run the following: +$ grep picture-uri /etc/dconf/db/local.d/locks/* +If properly configured, the output should be /org/gnome/desktop/screensaver/picture-uri + Is it the case that it is not set or configured properly? + + + + To check the permissions of /etc/cron.d, +run the command: +$ ls -l /etc/cron.d +If properly configured, the output should indicate the following permissions: +-rwx------ + Is it the case that /etc/cron.d does not have unix mode -rwx------? + + + + Run the following command and verify remote server is configured properly: +# grep -E "^(server|pool)" /etc/chrony.conf + Is it the case that a remote time server is not configured? + + + + Inspect /etc/audit/auditd.conf and locate the following line to +determine how much data the system will retain in each audit log file: +$ sudo grep max_log_file /etc/audit/auditd.conf +max_log_file = 6 + Is it the case that the system audit data threshold has not been properly configured? + + + + To verify that the Audit is correctly configured according to recommended rules, check the content of the file with the following command: +cat /etc/audit/rules.d/30-ospp-v42-1-create-failed.rules +The output has to be exactly as follows: +## Unsuccessful file creation (open with O_CREAT) +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b32 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b32 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create + Is it the case that the file does not exist or the content differs? + + + + To verify that the Audit is correctly configured according to recommended rules, check the content of the file with the following command: +cat /etc/audit/rules.d/30-ospp-v42-1-create-success.rules +The output has to be exactly as follows: +## Successful file creation (open with O_CREAT) +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create +-a always,exit -F arch=b64 -S open -F a1&0100 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create +-a always,exit -F arch=b64 -S creat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create + Is it the case that the file does not exist or the content differs? + + + + Verify all local interactive users on Red Hat Enterprise Linux 9 are assigned a home +directory upon creation with the following command: +$ grep -i create_home /etc/login.defs +CREATE_HOME yes + Is it the case that the value for "CREATE_HOME" parameter is not set to "yes", the line is missing, or the line is commented out? + + + + To ensure the system is configured to ignore the Ctrl-Alt-Del setting, +enter the following command: +$ sudo grep -i ctrlaltdelburstaction /etc/systemd/system.conf +The output should return: +CtrlAltDelBurstAction=none + Is it the case that the system is configured to reboot when Ctrl-Alt-Del is pressed more than 7 times in 2 seconds.? + + + + +Run the following command to determine if the httpd_manage_ipa SELinux boolean is disabled: +$ getsebool httpd_manage_ipa +If properly configured, the output should show the following: +httpd_manage_ipa --> off + Is it the case that httpd_manage_ipa is not disabled? + + + + To check the ownership of /etc/cron.hourly, +run the command: +$ ls -lL /etc/cron.hourly +If properly configured, the output should indicate the following owner: +root + Is it the case that /etc/cron.hourly does not have an owner of root? + + + + +Run the following command to determine if the boinc_execmem SELinux boolean is disabled: +$ getsebool boinc_execmem +If properly configured, the output should show the following: +boinc_execmem --> off + Is it the case that boinc_execmem is not disabled? + + + + The runtime status of the fs.protected_regular kernel parameter can be queried +by running the following command: +$ sysctl fs.protected_regular +2. + + Is it the case that the correct value is not returned? + + + + Verify that rules for unsuccessful calls of the open syscall are in the order shown below. + + If the auditd daemon is configured to use the "augenrules" program to read audit rules during daemon startup (the default), check the order of rules below in a file with suffix ".rules" in the directory "/etc/audit/rules.d". + If the auditd daemon is configured to use the "auditctl" utility to read audit rules during daemon startup, check the order of rules below in "/etc/audit/audit.rules" file. + + -a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b32 -S open -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-access + -a always,exit -F arch=b32 -S open -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-access + + If the system is 64 bit then also add the following lines: + + -a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b64 -S open -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-access + -a always,exit -F arch=b64 -S open -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-access + Is it the case that the rules are in a different order? + + + + To determine if the system is configured to audit successful calls +to the open_by_handle_at system call, run the following command: +$ sudo grep "open_by_handle_at" /etc/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + To check that the snmpd service is disabled in system boot configuration, +run the following command: +$ sudo systemctl is-enabled snmpd +Output should indicate the snmpd service has either not been installed, +or has been disabled at all runlevels, as shown in the example below: +$ sudo systemctl is-enabled snmpd disabled + +Run the following command to verify snmpd is not active (i.e. not running) through current runtime configuration: +$ sudo systemctl is-active snmpd + +If the service is not running the command will return the following output: +inactive + +The service will also be masked, to check that the snmpd is masked, run the following command: +$ sudo systemctl show snmpd | grep "LoadState\|UnitFileState" + +If the service is masked the command will return the following outputs: + +LoadState=masked + +UnitFileState=masked + Is it the case that the "snmpd" is loaded and not masked? + + + + The runtime status of the net.ipv4.conf.all.arp_ignore kernel parameter can be queried +by running the following command: +$ sysctl net.ipv4.conf.all.arp_ignore +. + + Is it the case that the correct value is not returned? + + + + Run the following command to determine if the krb5-workstation package is installed: +$ rpm -q krb5-workstation + Is it the case that the package is installed? + + + + To determine how the SSH daemon's IgnoreUserKnownHosts option is set, run the following command: + +$ sudo grep -i IgnoreUserKnownHosts /etc/ssh/sshd_config.d/00-complianceascode-hardening.conf + + +If a line indicating yes is returned, then the required value is set. + + Is it the case that the required value is not set? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_SECURITY_YAMA /boot/config.* + + For each kernel installed, a line with value "y" should be returned. + + Is it the case that the kernel was not built with the required value? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_MODULE_SIG_ALL /boot/config.* + + For each kernel installed, a line with value "y" should be returned. + + Is it the case that the kernel was not built with the required value? + + + + To check the permissions of /etc/cron.weekly, +run the command: +$ ls -l /etc/cron.weekly +If properly configured, the output should indicate the following permissions: +-rwx------ + Is it the case that /etc/cron.weekly does not have unix mode -rwx------? + + + + To check that all boot entries extend the backlog limit; +Check that all boot entries extend the log events queue: +sudo grep -L "^options\s+.*\baudit_backlog_limit=8192\b" /boot/loader/entries/*.conf +No line should be returned, each line returned is a boot entry that does not extend the log events queue. + Is it the case that audit backlog limit is not configured? + + + + Run the following command to determine if the quagga package is installed: +$ rpm -q quagga + Is it the case that the package is installed? + + + + To check if only local user are impacted by pam_faillock, run the following command: +$ grep local_users_only /etc/security/faillock.conf +The output should return local_users_only not commented. + Is it the case that local_users_only is not uncommented or configured correctly? + + + + +Run the following command to determine if the cobbler_can_network_connect SELinux boolean is disabled: +$ getsebool cobbler_can_network_connect +If properly configured, the output should show the following: +cobbler_can_network_connect --> off + Is it the case that cobbler_can_network_connect is not disabled? + + + + To check the permissions of /etc/cron.hourly, +run the command: +$ ls -l /etc/cron.hourly +If properly configured, the output should indicate the following permissions: +-rwx------ + Is it the case that /etc/cron.hourly does not have unix mode -rwx------? + + + + The runtime status of the net.ipv4.conf.default.secure_redirects kernel parameter can be queried +by running the following command: +$ sysctl net.ipv4.conf.default.secure_redirects +0. + + Is it the case that the correct value is not returned? + + + + Verify that Red Hat Enterprise Linux 9 is configured to audit the execution of the "passwd" command with the following command: + +$ sudo auditctl -l | grep passwd + +-a always,exit -F path=/usr/bin/passwd -F perm=x -F auid>=1000 -F auid!=unset -k privileged-passwd + Is it the case that the command does not return a line, or the line is commented out? + + + + To check the permissions of /etc/cron.monthly, +run the command: +$ ls -l /etc/cron.monthly +If properly configured, the output should indicate the following permissions: +-rwx------ + Is it the case that /etc/cron.monthly does not have unix mode -rwx------? + + + + To verify the assigned home directory of all interactive users is group- +owned by that users primary GID, run the following command: +# ls -ld $(awk -F: '($3>=1000)&&($7 !~ /nologin/){print $6}' /etc/passwd) + Is it the case that the group ownership is incorrect? + + + + To verify that the Audit is correctly configured according to recommended rules, check the content of the file with the following command: +cat /etc/audit/rules.d/30-ospp-v42-1-create-success.rules +The output has to be exactly as follows: +## Successful file creation (open with O_CREAT) +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create +-a always,exit -F arch=b32 -S open -F a1&0100 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create +-a always,exit -F arch=b32 -S creat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create + Is it the case that the file does not exist or the content differs? + + + + To verify that the Audit is correctly configured according to recommended rules, check the content of the file with the following command: +cat /etc/audit/rules.d/30-ospp-v42-4-delete-success.rules +The output has to be exactly as follows: +## Successful file delete +-a always,exit -F arch=b32 -S unlink,unlinkat,rename,renameat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-delete +-a always,exit -F arch=b64 -S unlink,unlinkat,rename,renameat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-delete + Is it the case that the file does not exist or the content differs? + + + + +Run the following command to determine if the logging_syslogd_run_nagios_plugins SELinux boolean is disabled: +$ getsebool logging_syslogd_run_nagios_plugins +If properly configured, the output should show the following: +logging_syslogd_run_nagios_plugins --> off + Is it the case that logging_syslogd_run_nagios_plugins is not disabled? + + + + +Run the following command to determine if the named_tcp_bind_http_port SELinux boolean is disabled: +$ getsebool named_tcp_bind_http_port +If properly configured, the output should show the following: +named_tcp_bind_http_port --> off + Is it the case that named_tcp_bind_http_port is not disabled? + + + + Run the following command to determine if the geolite2-city package is installed: +$ rpm -q geolite2-city + Is it the case that the package is installed? + + + + Run the following command to determine if the chrony package is installed: $ rpm -q chrony + Is it the case that the package is not installed? + + + + Verify the UMASK setting is not configured for interactive users, +run the following command: +$ sudo grep -ri "UMASK" /home + Is it the case that the above command returns no output, or if the umask is configured incorrectly? + + + + To check that virtual syscalls are disabled at boot time, check all boot entries with following command: +sudo grep -L "^options\s+.*\bvsyscall=none\b" /boot/loader/entries/*.conf +No line should be returned, each line returned is a boot entry that doesn't disable virtual syscalls. + Is it the case that vsyscalls are enabled? + + + + To check the permissions of /etc/ssh/*_key, +run the command: +$ ls -l /etc/ssh/*_key +If properly configured, the output should indicate the following permissions: +-rw------- + Is it the case that /etc/ssh/*_key does not have unix mode -rw-------? + + + + +Run the following command to determine if the piranha_lvs_can_network_connect SELinux boolean is disabled: +$ getsebool piranha_lvs_can_network_connect +If properly configured, the output should show the following: +piranha_lvs_can_network_connect --> off + Is it the case that piranha_lvs_can_network_connect is not disabled? + + + + To verify that the Audit is correctly configured according to recommended rules, check the content of the file with the following command: +cat /etc/audit/rules.d/30-ospp-v42-6-owner-change-success.rules +The output has to be exactly as follows: +## Successful ownership change +-a always,exit -F arch=b32 -S lchown,fchown,chown,fchownat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-owner-change +-a always,exit -F arch=b64 -S fchown,fchownat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-owner-change + Is it the case that the file does not exist or the content differs? + + + + To check the ownership of /etc/cron.allow, +run the command: +$ ls -lL /etc/cron.allow +If properly configured, the output should indicate the following owner: +root + Is it the case that /etc/cron.allow does not have an owner of root? + + + + To verify the sec option is configured for all NFS mounts, run the following command: +$ mount | grep "sec=" +All NFS mounts should show the sec=krb5:krb5i:krb5p setting in parentheses. +This is not applicable if NFS is not implemented. + Is it the case that the setting is not configured, has the 'sys' option added, or does not have all Kerberos options added? + + + + To verify if the OpenSSL uses defined TLS Crypto Policy, run: +$ grep -P '^(TLS\.)?MinProtocol' /etc/crypto-policies/back-ends/opensslcnf.config +and verify that the value is +TLSv1.2 + Is it the case that cryptographic policy for openssl is not configured or is configured incorrectly? + + + + To determine if the system is configured to audit calls to the +open system call, run the following command: +$ sudo grep "open" /etc/audit/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + +Run the following command to determine if the unconfined_chrome_sandbox_transition SELinux boolean is enabled: +$ getsebool unconfined_chrome_sandbox_transition +If properly configured, the output should show the following: +unconfined_chrome_sandbox_transition --> on + Is it the case that unconfined_chrome_sandbox_transition is not enabled? + + + + To check the group ownership of /var/log/syslog, +run the command: +$ ls -lL /var/log/syslog +If properly configured, the output should indicate the following group-owner: +adm + Is it the case that /var/log/syslog does not have a group owner of adm? + + + + +Run the following command to determine if the mozilla_plugin_bind_unreserved_ports SELinux boolean is disabled: +$ getsebool mozilla_plugin_bind_unreserved_ports +If properly configured, the output should show the following: +mozilla_plugin_bind_unreserved_ports --> off + Is it the case that mozilla_plugin_bind_unreserved_ports is not disabled? + + + + To verify that the Audit is correctly configured according to recommended rules, check the content of the file with the following command: +cat /etc/audit/rules.d/30-ospp-v42-2-modify-success.rules +The output has to be exactly as follows: +## Successful file modifications (open for write or truncate) +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification +-a always,exit -F arch=b32 -S open -F a1&01003 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification +-a always,exit -F arch=b32 -S truncate,ftruncate -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification +-a always,exit -F arch=b64 -S truncate,ftruncate -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification + Is it the case that the file does not exist or the content differs? + + + + The runtime status of the kernel.kexec_load_disabled kernel parameter can be queried +by running the following command: +$ sysctl kernel.kexec_load_disabled +1. + + Is it the case that the correct value is not returned? + + + + To verify that the Audit is correctly configured according to recommended rules, check the content of the file with the following command: +cat /etc/audit/rules.d/30-ospp-v42-3-access-success.rules +The output has to be exactly as follows: +## Successful file access (any other opens) This has to go last. +## These next two are likely to result in a whole lot of events +-a always,exit -F arch=b32 -S open,openat,openat2,open_by_handle_at -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-access +-a always,exit -F arch=b64 -S open,openat,openat2,open_by_handle_at -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-access + Is it the case that the file does not exist or the content differs? + + + + +Run the following command to determine if the xserver_clients_write_xshm SELinux boolean is disabled: +$ getsebool xserver_clients_write_xshm +If properly configured, the output should show the following: +xserver_clients_write_xshm --> off + Is it the case that xserver_clients_write_xshm is not disabled? + + + + +Run the following command to determine if the virt_use_fusefs SELinux boolean is disabled: +$ getsebool virt_use_fusefs +If properly configured, the output should show the following: +virt_use_fusefs --> off + Is it the case that virt_use_fusefs is not disabled? + + + + To check the group ownership of /etc/cron.weekly, +run the command: +$ ls -lL /etc/cron.weekly +If properly configured, the output should indicate the following group-owner: +root + Is it the case that /etc/cron.weekly does not have a group owner of root? + + + + +Run the following command to determine if the httpd_builtin_scripting SELinux boolean is disabled: +$ getsebool httpd_builtin_scripting +If properly configured, the output should show the following: +httpd_builtin_scripting --> off + Is it the case that httpd_builtin_scripting is not disabled? + + + + Verify that Red Hat Enterprise Linux 9 is configured to audit the execution of the "reboot" command with the following command: + +$ sudo auditctl -l | grep reboot + +-a always,exit -F path=/usr/sbin/reboot -F perm=x -F auid>=1000 -F auid!=unset -k privileged-reboot + Is it the case that the command does not return a line, or the line is commented out? + + + + To verify the INACTIVE setting, run the following command: +$ grep "INACTIVE" /etc/default/useradd +The output should indicate the INACTIVE configuration option is set +to an appropriate integer as shown in the example below: +$ grep "INACTIVE" /etc/default/useradd +INACTIVE= + Is it the case that the value of INACTIVE is greater than the expected value or is -1? + + + + Run the following command to check for duplicate group names: +Check that the operating system contains no duplicate group names for interactive users by running the following command: + + cut -d : -f 3 /etc/group | uniq -d + +If output is produced, this is a finding. +Configure the operating system to contain no duplicate names for groups. +Edit the file "/etc/group" and provide each group that has a duplicate group id with a unique group id. + Is it the case that the system has duplicate group ids? + + + + +Run the following command to determine if the httpd_verify_dns SELinux boolean is disabled: +$ getsebool httpd_verify_dns +If properly configured, the output should show the following: +httpd_verify_dns --> off + Is it the case that httpd_verify_dns is not disabled? + + + + To determine if the system is configured to audit calls to the +fchmod system call, run the following command: +$ sudo grep "fchmod" /etc/audit/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + +Run the following command to determine if the httpd_can_network_connect_db SELinux boolean is disabled: +$ getsebool httpd_can_network_connect_db +If properly configured, the output should show the following: +httpd_can_network_connect_db --> off + Is it the case that httpd_can_network_connect_db is not disabled? + + + + +Run the following command to determine if the tmpreaper_use_nfs SELinux boolean is disabled: +$ getsebool tmpreaper_use_nfs +If properly configured, the output should show the following: +tmpreaper_use_nfs --> off + Is it the case that tmpreaper_use_nfs is not disabled? + + + + To determine whether dnf is configured to use gpgcheck, +inspect /etc/dnf/dnf.conf and ensure the following appears in the +[main] section: +gpgcheck=1 +A value of 1 indicates that gpgcheck is enabled. Absence of a +gpgcheck line or a setting of 0 indicates that it is +disabled. + Is it the case that GPG checking is not enabled? + + + + +To verify the openldap-servers package is not installed, run the +following command: +$ rpm -q openldap-servers +The output should show the following: +package openldap-servers is not installed + Is it the case that it does not? + + + + To verify if the OpenSSH server uses defined MACs in the Crypto Policy, run: +$ grep -Po '(-oMACs=\S+)' /etc/crypto-policies/back-ends/opensshserver.config +and verify that the line matches: +-oMACS=hmac-sha2-512,hmac-sha2-256 + Is it the case that Crypto Policy for OpenSSH Server is not configured correctly? + + + + To determine how the SSH daemon's X11UseLocalhost option is set, run the following command: + +$ sudo grep -i X11UseLocalhost /etc/ssh/sshd_config.d/00-complianceascode-hardening.conf +$ sudo grep -i X11UseLocalhost /etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf + +If a line indicating yes is returned, then the required value is set. + Is it the case that the display proxy is listening on wildcard address? + + + + The runtime status of the net.core.bpf_jit_harden kernel parameter can be queried +by running the following command: +$ sysctl net.core.bpf_jit_harden +2. + + Is it the case that the correct value is not returned? + + + + +Run the following command to determine if the xserver_object_manager SELinux boolean is disabled: +$ getsebool xserver_object_manager +If properly configured, the output should show the following: +xserver_object_manager --> off + Is it the case that xserver_object_manager is not disabled? + + + + To check the value for maximum consecutive repeating characters, run the following command: +$ sudo grep maxclassrepeat /etc/security/pwquality.conf + Is it the case that the value of "maxclassrepeat" is set to "0", more than "<sub idref="var_password_pam_maxclassrepeat" />" or is commented out? + + + + + +Run the following command to determine the current status of the +cron service: +$ sudo systemctl is-active cron +If the service is running, it should return the following: active + Is it the case that ? + + + + Inspect /etc/default/grub for any instances of selinux=0 +in the kernel boot arguments. Presence of selinux=0 indicates +that SELinux is disabled at boot time. + Is it the case that SELinux is disabled at boot time? + + + + To ensure that the GPG key is installed, run: +$ rpm -q --queryformat "%{SUMMARY}\n" gpg-pubkey +The command should return the string below: +gpg(Red Hat, Inc. (release key 2) <security@redhat.com> + Is it the case that the Red Hat GPG Key is not installed? + + + + To check for legacy lines in /etc/passwd, run the following command: + grep '^\+' /etc/passwd +The command should not return any output. + Is it the case that the file contains legacy lines? + + + + +Run the following command to determine if the cobbler_use_cifs SELinux boolean is disabled: +$ getsebool cobbler_use_cifs +If properly configured, the output should show the following: +cobbler_use_cifs --> off + Is it the case that cobbler_use_cifs is not disabled? + + + + The runtime status of the net.ipv6.conf.default.accept_ra_defrtr kernel parameter can be queried +by running the following command: +$ sysctl net.ipv6.conf.default.accept_ra_defrtr +0. + + Is it the case that the correct value is not returned? + + + + +Run the following command to determine if the use_nfs_home_dirs SELinux boolean is disabled: +$ getsebool use_nfs_home_dirs +If properly configured, the output should show the following: +use_nfs_home_dirs --> off + Is it the case that use_nfs_home_dirs is not disabled? + + + + +Run the following command to determine if the httpd_dontaudit_search_dirs SELinux boolean is disabled: +$ getsebool httpd_dontaudit_search_dirs +If properly configured, the output should show the following: +httpd_dontaudit_search_dirs --> off + Is it the case that httpd_dontaudit_search_dirs is not disabled? + + + + +Run the following command to determine if the fenced_can_network_connect SELinux boolean is disabled: +$ getsebool fenced_can_network_connect +If properly configured, the output should show the following: +fenced_can_network_connect --> off + Is it the case that fenced_can_network_connect is not disabled? + + + + To verify the nodev option is configured for all NFS mounts, run +the following command: +$ mount | grep nfs +All NFS mounts should show the nodev setting in parentheses. This +is not applicable if NFS is not implemented. + Is it the case that the setting does not show? + + + + Verify that Red Hat Enterprise Linux 9 is configured to audit the execution of the "sudoedit" command with the following command: + +$ sudo auditctl -l | grep sudoedit + +-a always,exit -F path=/usr/bin/sudoedit -F perm=x -F auid>=1000 -F auid!=unset -k privileged-sudoedit + Is it the case that the command does not return a line, or the line is commented out? + + + + The runtime status of the net.ipv4.ip_forward kernel parameter can be queried +by running the following command: +$ sysctl net.ipv4.ip_forward +0. +The ability to forward packets is only appropriate for routers. + Is it the case that the correct value is not returned? + + + + +Run the following command to determine if the selinuxuser_direct_dri_enabled SELinux boolean is disabled: +$ getsebool selinuxuser_direct_dri_enabled +If properly configured, the output should show the following: +selinuxuser_direct_dri_enabled --> off + Is it the case that selinuxuser_direct_dri_enabled is not disabled? + + + + To ensure that wireless network notification is disabled, run the following command: +$ gsettings get org.gnome.nm-applet suppress-wireless-networks-available +If properly configured, the output should be true. +To ensure that users cannot enable wireless notification, run the following: +$ grep wireless-networks-available /etc/dconf/db/local.d/locks/* +If properly configured, the output should be +/org/gnome/nm-applet/suppress-wireless-networks-available + Is it the case that wireless network notification is enabled and not disabled? + + + + To determine if the system is configured to audit changes to its network configuration, +run the following command: +auditctl -l | egrep '(/etc/issue|/etc/issue.net|/etc/hosts|/etc/sysconfig/network)' +If the system is configured to watch for network configuration changes, a line should be returned for +each file specified (and perm=wa should be indicated for each). + Is it the case that the system is not configured to audit changes of the network configuration? + + + + Verify Red Hat Enterprise Linux 9 removes all software components after updated versions have been installed. + + +$ grep clean_requirements_on_remove /etc/dnf/dnf.conf +clean_requirements_on_remove=1 + Is it the case that '"clean_requirements_on_remove" is not set to "1"'? + + + + The runtime status of the net.ipv6.conf.default.accept_source_route kernel parameter can be queried +by running the following command: +$ sysctl net.ipv6.conf.default.accept_source_route +0. + + Is it the case that the correct value is not returned? + + + + To verify that the Audit is correctly configured according to recommended rules, check the content of the file with the following command: +cat /etc/audit/rules.d/43-module-load.rules +The output has to be exactly as follows: +## These rules watch for kernel module insertion. By monitoring +## the syscall, we do not need any watches on programs. +-a always,exit -F arch=b32 -S init_module,finit_module -F key=module-load +-a always,exit -F arch=b64 -S init_module,finit_module -F key=module-load +-a always,exit -F arch=b32 -S delete_module -F key=module-unload +-a always,exit -F arch=b64 -S delete_module -F key=module-unload + Is it the case that the file does not exist or the content differs? + + + + To determine if the system is configured to audit accesses to +/var/log/audit directory, run the following command: +$ sudo grep "dir=/var/log/audit" /etc/audit/audit.rules +If the system is configured to audit this activity, it will return a line. + Is it the case that no line is returned? + + + + +Run the following command to determine if the rsync_full_access SELinux boolean is disabled: +$ getsebool rsync_full_access +If properly configured, the output should show the following: +rsync_full_access --> off + Is it the case that rsync_full_access is not disabled? + + + + To verify that the Audit is correctly configured according to recommended rules, check the content of the file with the following command: +cat /etc/audit/rules.d/30-ospp-v42.rules +The output has to be exactly as follows: +## The purpose of these rules is to meet the requirements for Operating +## System Protection Profile (OSPP)v4.2. These rules depends on having +## the following rule files copied to /etc/audit/rules.d: +## +## 10-base-config.rules, 11-loginuid.rules, +## 30-ospp-v42-1-create-failed.rules, 30-ospp-v42-1-create-success.rules, +## 30-ospp-v42-2-modify-failed.rules, 30-ospp-v42-2-modify-success.rules, +## 30-ospp-v42-3-access-failed.rules, 30-ospp-v42-3-access-success.rules, +## 30-ospp-v42-4-delete-failed.rules, 30-ospp-v42-4-delete-success.rules, +## 30-ospp-v42-5-perm-change-failed.rules, +## 30-ospp-v42-5-perm-change-success.rules, +## 30-ospp-v42-6-owner-change-failed.rules, +## 30-ospp-v42-6-owner-change-success.rules +## +## original copies may be found in /usr/share/audit/sample-rules/ + + +## User add delete modify. This is covered by pam. However, someone could +## open a file and directly create or modify a user, so we'll watch passwd and +## shadow for writes +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b32 -S open -F a1&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b64 -S open -F a1&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b32 -S open -F a1&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b64 -S open -F a1&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify + +## User enable and disable. This is entirely handled by pam. + +## Group add delete modify. This is covered by pam. However, someone could +## open a file and directly create or modify a user, so we'll watch group and +## gshadow for writes +-a always,exit -F path=/etc/passwd -F perm=wa -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F path=/etc/shadow -F perm=wa -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F path=/etc/group -F perm=wa -F auid>=1000 -F auid!=unset -F key=group-modify +-a always,exit -F path=/etc/gshadow -F perm=wa -F auid>=1000 -F auid!=unset -F key=group-modify + + +## Use of special rights for config changes. This would be use of setuid +## programs that relate to user accts. This is not all setuid apps because +## requirements are only for ones that affect system configuration. +-a always,exit -F path=/usr/sbin/unix_chkpwd -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/sbin/usernetctl -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/sbin/userhelper -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/sbin/seunshare -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/mount -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/newgrp -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/newuidmap -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/gpasswd -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/newgidmap -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/umount -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/passwd -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/crontab -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/at -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes + +## Privilege escalation via su or sudo. This is entirely handled by pam. + +## Watch for configuration changes to privilege escalation. +-a always,exit -F path=/etc/sudoers -F perm=wa -F key=special-config-changes +-a always,exit -F dir=/etc/sudoers.d/ -F perm=wa -F key=special-config-changes + +## Audit log access +-a always,exit -F dir=/var/log/audit/ -F perm=r -F auid>=1000 -F auid!=unset -F key=access-audit-trail +## Attempts to Alter Process and Session Initiation Information +-a always,exit -F path=/var/run/utmp -F perm=wa -F auid>=1000 -F auid!=unset -F key=session +-a always,exit -F path=/var/log/btmp -F perm=wa -F auid>=1000 -F auid!=unset -F key=session +-a always,exit -F path=/var/log/wtmp -F perm=wa -F auid>=1000 -F auid!=unset -F key=session + +## Attempts to modify MAC controls +-a always,exit -F dir=/etc/selinux/ -F perm=wa -F auid>=1000 -F auid!=unset -F key=MAC-policy + +## Software updates. This is entirely handled by rpm. + +## System start and shutdown. This is entirely handled by systemd + +## Kernel Module loading. This is handled in 43-module-load.rules + +## Application invocation. The requirements list an user requirement +## FPT_SRP_EXT.1 Software Restriction Policies. This event is intended to +## state results from that policy. This would be handled entirely by +## that daemon. + Is it the case that the file does not exist or the content differs? + + + + The runtime status of the net.ipv6.conf.all.accept_ra_pinfo kernel parameter can be queried +by running the following command: +$ sysctl net.ipv6.conf.all.accept_ra_pinfo +0. + + Is it the case that the correct value is not returned? + + + + To determine if the system is configured to audit calls to the +fsetxattr system call, run the following command: +$ sudo grep "fsetxattr" /etc/audit/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + +Run the following command to determine if the httpd_run_preupgrade SELinux boolean is disabled: +$ getsebool httpd_run_preupgrade +If properly configured, the output should show the following: +httpd_run_preupgrade --> off + Is it the case that httpd_run_preupgrade is not disabled? + + + + To check the group ownership of /etc/group, +run the command: +$ ls -lL /etc/group +If properly configured, the output should indicate the following group-owner: +root + Is it the case that /etc/group does not have a group owner of root? + + + + Verify that a separate file system/partition has been created for /tmp with the following command: + +$ mountpoint /tmp + + Is it the case that "/tmp is not a mountpoint" is returned? + + + + +Run the following command to determine if the selinuxuser_mysql_connect_enabled SELinux boolean is disabled: +$ getsebool selinuxuser_mysql_connect_enabled +If properly configured, the output should show the following: +selinuxuser_mysql_connect_enabled --> off + Is it the case that selinuxuser_mysql_connect_enabled is not disabled? + + + + For each private key stored on the system, use the following command: + +$ sudo ssh-keygen -y -f /path/to/file + Is it the case that Any contents were displayed without asking a passphrase? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_RANDOMIZE_MEMORY /boot/config.* + + For each kernel installed, a line with value "y" should be returned. + + Is it the case that the kernel was not built with the required value? + + + + The runtime status of the kernel.unprivileged_bpf_disabled +kernel parameter can be queried by running the following command: +$ sysctl kernel.unprivileged_bpf_disabled +The output of the command should indicate either: +kernel.unprivileged_bpf_disabled = 1 +or: +kernel.unprivileged_bpf_disabled = 2 +The output of the command should not indicate: +kernel.unprivileged_bpf_disabled = 0 + +The preferable way how to assure the runtime compliance is to have +correct persistent configuration, and rebooting the system. + +The persistent kernel parameter configuration is performed by specifying the appropriate +assignment in any file located in the /etc/sysctl.d directory. +Verify that there is not any existing incorrect configuration by executing the following command: +$ grep -r '^\s*\s*=' /etc/sysctl.conf /etc/sysctl.d +The command should not find any assignments other than: +kernel.unprivileged_bpf_disabled = 1 +or: +kernel.unprivileged_bpf_disabled = 2 + +Duplicate assignments are not allowed. Empty output is allowed, because the system default is 2. + Is it the case that the kernel.unprivileged_bpf_disabled is not set to 1 or 2 or is configured to be 0? + + + + Verify that Red Hat Enterprise Linux 9 is configured to audit the execution of the "shutdown" command with the following command: + +$ sudo auditctl -l | grep shutdown + +-a always,exit -F path=/usr/sbin/shutdown -F perm=x -F auid>=1000 -F auid!=unset -k privileged-shutdown + Is it the case that the command does not return a line, or the line is commented out? + + + + Verify the audit system prevents unauthorized changes with the following command: + +$ sudo grep "^\s*[^#]" /etc/audit/audit.rules | tail -1 +-e 2 + + Is it the case that the audit system is not set to be immutable by adding the "-e 2" option to the end of "/etc/audit/audit.rules"? + + + + To ensure the X Windows package group is removed, run the following command: + +$ rpm -qi xorg-x11-server-Xorg xorg-x11-server-common xorg-x11-server-utils xorg-x11-server-Xwayland + +For each package mentioned above you should receive following line: +package <package> is not installed + Is it the case that xorg related packages are not removed and run level is not correctly configured? + + + + To check that the avahi-daemon service is disabled in system boot configuration, +run the following command: +$ sudo systemctl is-enabled avahi-daemon +Output should indicate the avahi-daemon service has either not been installed, +or has been disabled at all runlevels, as shown in the example below: +$ sudo systemctl is-enabled avahi-daemon disabled + +Run the following command to verify avahi-daemon is not active (i.e. not running) through current runtime configuration: +$ sudo systemctl is-active avahi-daemon + +If the service is not running the command will return the following output: +inactive + +The service will also be masked, to check that the avahi-daemon is masked, run the following command: +$ sudo systemctl show avahi-daemon | grep "LoadState\|UnitFileState" + +If the service is masked the command will return the following outputs: + +LoadState=masked + +UnitFileState=masked + Is it the case that the "avahi-daemon" is loaded and not masked? + + + + To verify that the Audit is correctly configured according to recommended rules, check the content of the file with the following command: +cat /etc/audit/rules.d/30-ospp-v42-5-perm-change-failed.rules +The output has to be exactly as follows: +## Unsuccessful permission change +-a always,exit -F arch=b64 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-perm-change +-a always,exit -F arch=b64 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-perm-change + Is it the case that the file does not exist or the content differs? + + + + +Run the following command to determine if the gluster_export_all_rw SELinux boolean is disabled: +$ getsebool gluster_export_all_rw +If properly configured, the output should show the following: +gluster_export_all_rw --> off + Is it the case that gluster_export_all_rw is not disabled? + + + + Verify that Red Hat Enterprise Linux 9 is configured to audit the execution of the "setfiles" command with the following command: + +$ sudo auditctl -l | grep setfiles + +-a always,exit -F path=/usr/sbin/setfiles -F perm=x -F auid>=1000 -F auid!=unset -k privileged-unix-update + Is it the case that the command does not return a line, or the line is commented out? + + + + To check if UsePrivilegeSeparation is enabled or set correctly, run the +following command: +$ sudo grep UsePrivilegeSeparation /etc/ssh/sshd_config +If configured properly, output should be . + Is it the case that it is commented out or is not enabled? + + + + + +Run the following command to determine the current status of the +ntp service: +$ sudo systemctl is-active ntp +If the service is running, it should return the following: active + Is it the case that ? + + + + +Run the following command to determine if the logging_syslogd_can_sendmail SELinux boolean is disabled: +$ getsebool logging_syslogd_can_sendmail +If properly configured, the output should show the following: +logging_syslogd_can_sendmail --> off + Is it the case that logging_syslogd_can_sendmail is not disabled? + + + + The runtime status of the net.ipv4.conf.default.shared_media kernel parameter can be queried +by running the following command: +$ sysctl net.ipv4.conf.default.shared_media +0. + + Is it the case that the correct value is not returned? + + + + Verify the audit log directories have a correct mode or less permissive mode. + +Find the location of the audit logs: + +$ sudo grep "^log_file" /etc/audit/auditd.conf + + +Find the group that owns audit logs: + +$ sudo grep "^log_group" /etc/audit/auditd.conf + + +Run the following command to check the mode of the system audit logs: + +$ sudo stat -c "%a %n" [audit_log_directory] + +Replace "[audit_log_directory]" to the correct audit log directory path, by default this location is "/var/log/audit". + + +If the log_group is "root" or is not set, the correct permissions are 0700, otherwise they are 0750. + Is it the case that audit logs have a more permissive mode? + + + + +Run the following command to determine if the mozilla_plugin_use_spice SELinux boolean is disabled: +$ getsebool mozilla_plugin_use_spice +If properly configured, the output should show the following: +mozilla_plugin_use_spice --> off + Is it the case that mozilla_plugin_use_spice is not disabled? + + + + To determine how the SSH daemon's GSSAPIAuthentication option is set, run the following command: + +$ sudo grep -i GSSAPIAuthentication /etc/ssh/sshd_config.d/00-complianceascode-hardening.conf +$ sudo grep -i GSSAPIAuthentication /etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf + +If a line indicating no is returned, then the required value is set. + + Is it the case that the required value is not set? + + + + Ensure that Red Hat Enterprise Linux 9 verifies correct operation of security functions. + +Check if "SELinux" is active and in "" mode with the following command: + +$ sudo getenforce + + Is it the case that SELINUX is not set to enforcing? + + + + +Run the following command to determine if the sanlock_use_fusefs SELinux boolean is disabled: +$ getsebool sanlock_use_fusefs +If properly configured, the output should show the following: +sanlock_use_fusefs --> off + Is it the case that sanlock_use_fusefs is not disabled? + + + + To check the ownership of /var/log/syslog, +run the command: +$ ls -lL /var/log/syslog +If properly configured, the output should indicate the following owner: +syslog + Is it the case that /var/log/syslog does not have an owner of syslog? + + + + +Run the following command to determine if the tor_bind_all_unreserved_ports SELinux boolean is disabled: +$ getsebool tor_bind_all_unreserved_ports +If properly configured, the output should show the following: +tor_bind_all_unreserved_ports --> off + Is it the case that tor_bind_all_unreserved_ports is not disabled? + + + + Run the following command to determine if the audit package is installed: $ rpm -q audit + Is it the case that the audit package is not installed? + + + + Verify Red Hat Enterprise Linux 9 generates audit records for all account creations, modifications, disabling, and termination events that affect "/etc/group" with the following command: + +$ sudo auditctl -l | egrep '(/etc/group)' + +-w /etc/group -p wa -k identity + Is it the case that the command does not return a line, or the line is commented out? + + + + + Is it the case that the package is not installed? + + + + To determine whether dnf has been configured to disable +gpgcheck for any repos, inspect all files in +/etc/yum.repos.d and ensure the following does not appear in any +sections: +gpgcheck=0 +A value of 0 indicates that gpgcheck has been disabled for that repo. + Is it the case that GPG checking is disabled? + + + + To check the current idle time-out value, run the following command: +$ gsettings get org.gnome.desktop.session idle-delay +If properly configured, the output should be 'uint32 '. +To ensure that users cannot change the screensaver inactivity timeout setting, run the following: +$ grep idle-delay /etc/dconf/db/local.d/locks/* +If properly configured, the output should be /org/gnome/desktop/session/idle-delay + Is it the case that idle-delay is set to 0 or a value greater than <sub idref="inactivity_timeout_value" />? + + + + To verify the assigned home directory of all interactive user home directories +have a mode of 0750 or less permissive, run the following command: +$ sudo ls -l /home +Inspect the output for any directories with incorrect permissions. + Is it the case that they are more permissive? + + + + To determine if the system is configured to audit calls to the +open_by_handle_at system call, run the following command: +$ sudo grep "open_by_handle_at" /etc/audit/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + To determine if the system is configured to audit calls to the +open system call, run the following command: +$ sudo grep "open" /etc/audit/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + +Run the following command to determine if the tor_can_network_relay SELinux boolean is disabled: +$ getsebool tor_can_network_relay +If properly configured, the output should show the following: +tor_can_network_relay --> off + Is it the case that tor_can_network_relay is not disabled? + + + + +Run the following command to determine if the samba_run_unconfined SELinux boolean is disabled: +$ getsebool samba_run_unconfined +If properly configured, the output should show the following: +samba_run_unconfined --> off + Is it the case that samba_run_unconfined is not disabled? + + + + Verify users are provided with feedback on when account accesses last occurred with the following command: + +$ sudo grep pam_lastlog /etc/pam.d/postlogin + +session required pam_lastlog.so showfailed + Is it the case that "pam_lastlog" is missing from "/etc/pam.d/postlogin" file, or the silent option is present? + + + + Find the list of alias maps used by the Postfix mail server: +$ sudo postconf alias_maps +Query the Postfix alias maps for an alias for the postmaster user: +$ sudo postmap -q postmaster hash:/etc/aliases +The output should return root. + Is it the case that the alias is not set or is not root? + + + + +Run the following command to determine if the guest_exec_content SELinux boolean is disabled: +$ getsebool guest_exec_content +If properly configured, the output should show the following: +guest_exec_content --> off + Is it the case that guest_exec_content is not disabled? + + + + +Run the following command to determine if the tmpreaper_use_samba SELinux boolean is disabled: +$ getsebool tmpreaper_use_samba +If properly configured, the output should show the following: +tmpreaper_use_samba --> off + Is it the case that tmpreaper_use_samba is not disabled? + + + + Verify that Red Hat Enterprise Linux 9 is configured to prevent unrestricted mail relaying, +run the following command: +$ sudo postconf -n smtpd_client_restrictions + Is it the case that the "smtpd_client_restrictions" parameter contains any entries other than "permit_mynetworks" and "reject"? + + + + Verify that rules for unsuccessful calls of the open_by_handle_at syscall are in the order shown below. + + If the auditd daemon is configured to use the "augenrules" program to read audit rules during daemon startup (the default), check the order of rules below in a file with suffix ".rules" in the directory "/etc/audit/rules.d". + If the auditd daemon is configured to use the "auditctl" utility to read audit rules during daemon startup, check the order of rules below in "/etc/audit/audit.rules" file. + + -a always,exit -F arch=b32 -S open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b32 -S open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b32 -S open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b32 -S open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b32 -S open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-access + -a always,exit -F arch=b32 -S open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-access + + If the system is 64 bit then also add the following lines: + + -a always,exit -F arch=b64 -S open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b64 -S open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + -a always,exit -F arch=b64 -S open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b64 -S open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-modification + -a always,exit -F arch=b64 -S open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-access + -a always,exit -F arch=b64 -S open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-access + Is it the case that the rules are in a different order? + + + + To verify that the Audit is correctly configured according to recommended rules, check the content of the file with the following command: +cat /etc/audit/rules.d/30-ospp-v42-5-perm-change-failed.rules +The output has to be exactly as follows: +## Unsuccessful permission change +-a always,exit -F arch=b32 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-perm-change +-a always,exit -F arch=b64 -S fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-perm-change +-a always,exit -F arch=b32 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-perm-change +-a always,exit -F arch=b64 -S fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-perm-change + Is it the case that the file does not exist or the content differs? + + + + To determine whether sudo command includes configuration files from the appropriate directory, +run the following command: +$ sudo grep -rP '^[#@]include(dir)?' /etc/sudoers /etc/sudoers.d +If only the line /etc/sudoers:#includedir /etc/sudoers.d is returned, then the drop-in include configuration is set correctly. +Any other line returned is a finding. + Is it the case that the /etc/sudoers doesn't include /etc/sudores.d or includes other directories?? + + + + Run the following command to ensure postfix routes mail to this system: +$ grep relayhost /etc/postfix/main.cf +If properly configured, the output should show only . + Is it the case that it is not? + + + + To check that the httpd service is disabled in system boot configuration, +run the following command: +$ sudo systemctl is-enabled httpd +Output should indicate the httpd service has either not been installed, +or has been disabled at all runlevels, as shown in the example below: +$ sudo systemctl is-enabled httpd disabled + +Run the following command to verify httpd is not active (i.e. not running) through current runtime configuration: +$ sudo systemctl is-active httpd + +If the service is not running the command will return the following output: +inactive + +The service will also be masked, to check that the httpd is masked, run the following command: +$ sudo systemctl show httpd | grep "LoadState\|UnitFileState" + +If the service is masked the command will return the following outputs: + +LoadState=masked + +UnitFileState=masked + Is it the case that the "httpd" is loaded and not masked? + + + + Run the following command to determine if the policycoreutils-python-utils package is installed: $ rpm -q policycoreutils-python-utils + Is it the case that the package is not installed? + + + + To check the permissions of /etc/ssh/*.pub, +run the command: +$ ls -l /etc/ssh/*.pub +If properly configured, the output should indicate the following permissions: +-rw-r--r-- + Is it the case that /etc/ssh/*.pub does not have unix mode -rw-r--r--? + + + + Verify the umask setting is configured correctly in the /etc/bashrc file by +running the following command: +$ sudo grep "umask" /etc/bashrc +All output must show the value of umask set as shown below: +umask + Is it the case that the above command returns no output, or the umask is configured incorrectly? + + + + +If the system is configured to prevent the loading of the sctp kernel module, +it will contain lines inside any file in /etc/modprobe.d or the deprecated /etc/modprobe.conf. +These lines instruct the module loading system to run another program (such as /bin/true) upon a module install event. + +Run the following command to search for such lines in all files in /etc/modprobe.d and the deprecated /etc/modprobe.conf: +$ grep -r sctp /etc/modprobe.conf /etc/modprobe.d + Is it the case that no line is returned? + + + + +Run the following command to determine if the mcelog_exec_scripts SELinux boolean is enabled: +$ getsebool mcelog_exec_scripts +If properly configured, the output should show the following: +mcelog_exec_scripts --> on + Is it the case that mcelog_exec_scripts is not enabled? + + + + To verify that null passwords cannot be used, run the following command: + +$ grep nullok /etc/pam.d/system-auth /etc/pam.d/password-auth + +If this produces any output, it may be possible to log into accounts +with empty passwords. Remove any instances of the nullok option to +prevent logins with empty passwords. + Is it the case that NULL passwords can be used? + + + + To determine if the system is configured to audit unsuccessful calls +to the fremovexattr system call, run the following command: +$ sudo grep "fremovexattr" /etc/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + Verify "nftables" is configured to allow rate limits on any connection to the system with the following command: + +Verify "firewalld" has "nftables" set as the default backend: + +$ sudo grep -i firewallbackend /etc/firewalld/firewalld.conf + +# FirewallBackend +FirewallBackend=nftables + Is it the case that the "nftables" is not set as the "firewallbackend"? + + + + To determine if the system is configured to audit successful calls +to the setxattr system call, run the following command: +$ sudo grep "setxattr" /etc/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + To verify /etc/system-fips exists, run the following command: +ls -l /etc/system-fips +The output should be similar to the following: +-rw-r--r--. 1 root root 36 Nov 26 11:31 /etc/system-fips + Is it the case that /etc/system-fips does not exist? + + + + If the system uses IPv6, this is not applicable. + +If the system is configured to disable the +ipv6 kernel module, it will contain a line +of the form: +options ipv6 disable=1 +Such lines may be inside any file in /etc/modprobe.d or the +deprecated/etc/modprobe.conf. This permits insertion of the IPv6 +kernel module (which other parts of the system expect to be present), but +otherwise keeps it inactive. Run the following command to search for such +lines in all files in /etc/modprobe.d and the deprecated +/etc/modprobe.conf: +$ grep -r ipv6 /etc/modprobe.conf /etc/modprobe.d + Is it the case that the ipv6 kernel module is not disabled? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_GCC_PLUGIN_RANDSTRUCT /boot/config.* + + For each kernel installed, a line with value "y" should be returned. + + Is it the case that the kernel was built with the required value? + + + + +Run the following command to determine if the mailman_use_fusefs SELinux boolean is disabled: +$ getsebool mailman_use_fusefs +If properly configured, the output should show the following: +mailman_use_fusefs --> off + Is it the case that mailman_use_fusefs is not disabled? + + + + +Run the following command to determine if the selinuxuser_use_ssh_chroot SELinux boolean is disabled: +$ getsebool selinuxuser_use_ssh_chroot +If properly configured, the output should show the following: +selinuxuser_use_ssh_chroot --> off + Is it the case that selinuxuser_use_ssh_chroot is not disabled? + + + + To check the permissions of /etc/motd, +run the command: +$ ls -l /etc/motd +If properly configured, the output should indicate the following permissions: +-rw-r--r-- + Is it the case that /etc/motd does not have unix mode -rw-r--r--? + + + + +Run the following command to determine if the selinuxuser_execstack SELinux boolean is disabled: +$ getsebool selinuxuser_execstack +If properly configured, the output should show the following: +selinuxuser_execstack --> off + Is it the case that selinuxuser_execstack is not disabled? + + + + Verify that Red Hat Enterprise Linux 9 is configured to audit the execution of the "postqueue" command with the following command: + +$ sudo auditctl -l | grep postqueue + +-a always,exit -F path=/usr/bin/postqueue -F perm=x -F auid>=1000 -F auid!=unset -k privileged-postqueue + Is it the case that the command does not return a line, or the line is commented out? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_SLUB_DEBUG /boot/config.* + + For each kernel installed, a line with value "y" should be returned. + + Is it the case that the kernel was not built with the required value? + + + + To check that the qpidd service is disabled in system boot configuration, +run the following command: +$ sudo systemctl is-enabled qpidd +Output should indicate the qpidd service has either not been installed, +or has been disabled at all runlevels, as shown in the example below: +$ sudo systemctl is-enabled qpidd disabled + +Run the following command to verify qpidd is not active (i.e. not running) through current runtime configuration: +$ sudo systemctl is-active qpidd + +If the service is not running the command will return the following output: +inactive + +The service will also be masked, to check that the qpidd is masked, run the following command: +$ sudo systemctl show qpidd | grep "LoadState\|UnitFileState" + +If the service is masked the command will return the following outputs: + +LoadState=masked + +UnitFileState=masked + Is it the case that the "qpidd" is loaded and not masked? + + + + +Run the following command to determine if the xguest_exec_content SELinux boolean is disabled: +$ getsebool xguest_exec_content +If properly configured, the output should show the following: +xguest_exec_content --> off + Is it the case that xguest_exec_content is not disabled? + + + + To check the password warning age, run the command: +$ grep PASS_WARN_AGE /etc/login.defs +The DoD requirement is 7. + Is it the case that it is not set to the required value? + + + + To verify whether audispd plugin off-loads audit records onto a different +system or media from the system being audited, run the following command: + +$ sudo grep -i remote_server /etc/audit/audisp-remote.conf + +The output should return something similar to where REMOTE_SYSTEM +is an IP address or hostname: +remote_server = REMOTE_SYSTEM + +Determine which partition the audit records are being written to with the +following command: + +$ sudo grep log_file /etc/audit/auditd.conf +log_file = /var/log/audit/audit.log + +Check the size of the partition that audit records are written to with the +following command and verify whether it is sufficiently large: + +$ sudo df -h /var/log/audit/ +/dev/sda2 24G 10.4G 13.6G 43% /var/log/audit + Is it the case that audispd is not sending logs to a remote system and the local partition has inadequate space? + + + + To find SUID files, run the following command: +$ sudo find / -xdev -type f -perm -4000 + Is it the case that only authorized files appear in the output of the find command? + + + + To check if authentication is required for emergency mode, run the following command: +$ grep sulogin /usr/lib/systemd/system/emergency.service +The output should be similar to the following, and the line must begin with +ExecStart and /usr/lib/systemd/systemd-sulogin-shell. + ExecStart=-/usr/lib/systemd/systemd-sulogin-shell emergency + +Then, check if the emergency target requires the emergency service: +Run the following command: +$ sudo grep Requires /usr/lib/systemd/system/emergency.target +The output should be the following: +Requires=emergency.service + +Then, check if there is no custom emergency target configured in systemd configuration. +Run the following command: +$ sudo grep -r emergency.target /etc/systemd/system/ +The output should be empty. + +Then, check if there is no custom emergency service configured in systemd configuration. +Run the following command: +$ sudo grep -r emergency.service /etc/systemd/system/ +The output should be empty. + Is it the case that the output is different? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_STACKPROTECTOR /boot/config.* + + For each kernel installed, a line with value "y" should be returned. + + Is it the case that the kernel was not built with the required value? + + + + Run the following command to determine if the postfix package is installed: $ rpm -q postfix + Is it the case that the package is not installed? + + + + To check the ownership of /etc/gshadow-, +run the command: +$ ls -lL /etc/gshadow- +If properly configured, the output should indicate the following owner: +root + Is it the case that /etc/gshadow- does not have an owner of root? + + + + +Run the following command to determine if the nagios_run_pnp4nagios SELinux boolean is disabled: +$ getsebool nagios_run_pnp4nagios +If properly configured, the output should show the following: +nagios_run_pnp4nagios --> off + Is it the case that nagios_run_pnp4nagios is not disabled? + + + + To determine if the system is configured to audit calls to the +delete_module system call, run the following command: +$ sudo grep "delete_module" /etc/audit/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + Verify the hidepid=value option is configured for the /proc mount point, + run the following command: + $ sudo mount | grep '\s/proc\s' + . . . /proc . . . hidepid=value . . . + + Is it the case that the "/proc" file system does not have the "hidepid=value" option set? + + + + Shared libraries are stored in the following directories: +/lib +/lib64 +/usr/lib +/usr/lib64 + +To find shared libraries that are group-writable or world-writable, +run the following command for each directory DIR which contains shared libraries: +$ sudo find -L DIR -perm /022 -type d + Is it the case that any of these files are group-writable or world-writable? + + + + +Run the following command to determine if the mock_enable_homedirs SELinux boolean is disabled: +$ getsebool mock_enable_homedirs +If properly configured, the output should show the following: +mock_enable_homedirs --> off + Is it the case that mock_enable_homedirs is not disabled? + + + + To ensure that XDMCP is disabled in /etc/gdm/custom.conf, run the following command: +grep -Pzo "\[xdmcp\]\nEnable=false" /etc/gdm/custom.conf +The output should return the following: + +[xdmcp] +Enable=false + + Is it the case that the Enable is not set to false or is missing in the xdmcp section of the /etc/gdm/custom.conf gdm configuration file? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_ACPI_CUSTOM_METHOD /boot/config.* + + Configs with value 'n' are not explicitly set in the file, so either commented lines or no + lines should be returned. + + Is it the case that the kernel was not built with the required value? + + + + +Run the following command to determine if the openshift_use_nfs SELinux boolean is disabled: +$ getsebool openshift_use_nfs +If properly configured, the output should show the following: +openshift_use_nfs --> off + Is it the case that openshift_use_nfs is not disabled? + + + + +Run the following command to determine if the mysql_connect_any SELinux boolean is disabled: +$ getsebool mysql_connect_any +If properly configured, the output should show the following: +mysql_connect_any --> off + Is it the case that mysql_connect_any is not disabled? + + + + Inspect /etc/audit/auditd.conf and locate the following line to +determine if the system is configured correctly: +space_left PERCENTAGE% + Is it the case that the system is not configured with a specific percentage to notify administrators of an issue? + + + + To verify that the Audit is correctly configured according to recommended rules, check the content of the file with the following command: +cat /etc/audit/rules.d/30-ospp-v42-1-create-success.rules +The output has to be exactly as follows: +## Successful file creation (open with O_CREAT) +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create +-a always,exit -F arch=b32 -S open -F a1&0100 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create +-a always,exit -F arch=b64 -S open -F a1&0100 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create +-a always,exit -F arch=b32 -S creat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create +-a always,exit -F arch=b64 -S creat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create + Is it the case that the file does not exist or the content differs? + + + + To check how many special characters are required in a password, run the following command: +$ grep ocredit /etc/security/pwquality.conf +The ocredit parameter (as a negative number) will indicate how many special +characters are required. + Is it the case that value of "ocredit" is a positive number or is commented out? + + + + Check that AIDE is properly configured to protect the integrity of the +audit tools by running the following command: + +# sudo cat /etc/aide.conf | grep /usr/sbin/au + +/usr/sbin/auditctl p+i+n+u+g+s+b+acl+xattrs+sha512 + +/usr/sbin/auditd p+i+n+u+g+s+b+acl+xattrs+sha512 + +/usr/sbin/ausearch p+i+n+u+g+s+b+acl+xattrs+sha512 + +/usr/sbin/aureport p+i+n+u+g+s+b+acl+xattrs+sha512 + +/usr/sbin/autrace p+i+n+u+g+s+b+acl+xattrs+sha512 + + + +/usr/sbin/autrace p+i+n+u+g+s+b+acl+xattrs+sha512 + +/usr/sbin/augenrules p+i+n+u+g+s+b+acl+xattrs+sha512 + + + +If AIDE is configured properly to protect the integrity of the audit tools, +all lines listed above will be returned from the command. + +If one or more lines are missing, this is a finding. + Is it the case that integrity checks of the audit tools are missing or incomplete? + + + + To check the system for the existence of any .netrc files, +run the following command: +$ sudo find /home -xdev -name .netrc + Is it the case that any .netrc files exist? + + + + To check the group ownership of /etc/cron.daily, +run the command: +$ ls -lL /etc/cron.daily +If properly configured, the output should indicate the following group-owner: +root + Is it the case that /etc/cron.daily does not have a group owner of root? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_DEBUG_SG /boot/config.* + + For each kernel installed, a line with value "y" should be returned. + + Is it the case that the kernel was not built with the required value? + + + + # grep "^OPTIONS.*-u" /etc/sysconfig/chronyd | grep -v -e '-u\s*chrony\b' +returns no output + Is it the case that chronyd is not running under chrony user account? + + + + Run the following command to determine the current status of the dnf-automatic timer: $ sudo systemctl is-active dnf-automatic.timer If the timer is running, it should return the following: active + Is it the case that the dnf-automatic.timer is not enabled? + + + + +To ensure the login warning banner text is properly set, run the following: +$ grep banner-message-text /etc/dconf/db/distro.d/* +If properly configured, the proper banner text will appear. +To ensure the login warning banner text is locked and cannot be changed by a user, run the following: +$ grep banner-message-text /etc/dconf/db/distro.d/locks/* +If properly configured, the output should be /org/gnome/login-screen/banner-message-text. + Is it the case that it does not? + + + + +Run the following command to determine if the privoxy_connect_any SELinux boolean is disabled: +$ getsebool privoxy_connect_any +If properly configured, the output should show the following: +privoxy_connect_any --> off + Is it the case that privoxy_connect_any is not disabled? + + + + To determine if the system is configured to audit successful calls +to the truncate system call, run the following command: +$ sudo grep "truncate" /etc/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + To determine if the system is configured to audit calls to the +openat system call, run the following command: +$ sudo grep "openat" /etc/audit/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + +Run the following command to determine if the pcp_read_generic_logs SELinux boolean is disabled: +$ getsebool pcp_read_generic_logs +If properly configured, the output should show the following: +pcp_read_generic_logs --> off + Is it the case that pcp_read_generic_logs is not disabled? + + + + +Run the following command to determine if the glance_use_execmem SELinux boolean is disabled: +$ getsebool glance_use_execmem +If properly configured, the output should show the following: +glance_use_execmem --> off + Is it the case that glance_use_execmem is not disabled? + + + + Determine if "sudoers" file restricts sudo access run the following commands: +$ sudo grep -PR '^\s*ALL\s+ALL\=\(ALL\)\s+ALL\s*$' /etc/sudoers /etc/sudoers.d/* +$ sudo grep -PR '^\s*ALL\s+ALL\=\(ALL\:ALL\)\s+ALL\s*$' /etc/sudoers /etc/sudoers.d/* + Is it the case that either of the commands returned a line? + + + + To determine if the system is configured to audit successful calls +to the chmod system call, run the following command: +$ sudo grep "chmod" /etc/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + The runtime status of the net.ipv4.conf.all.rp_filter parameter can be queried +by running the following command: +$ sysctl net.ipv4.conf.all.rp_filter +The output of the command should indicate either: +net.ipv4.conf.all.rp_filter = 1 +or: +net.ipv4.conf.all.rp_filter = 2 +The output of the command should not indicate: +net.ipv4.conf.all.rp_filter = 0 + +The preferable way how to assure the runtime compliance is to have +correct persistent configuration, and rebooting the system. + +The persistent sysctl parameter configuration is performed by specifying the appropriate +assignment in any file located in the /etc/sysctl.d directory. +Verify that there is not any existing incorrect configuration by executing the following command: +$ grep -r '^\s*net.ipv4.conf.all.rp_filter\s*=' /etc/sysctl.conf /etc/sysctl.d +The command should not find any assignments other than: +net.ipv4.conf.all.rp_filter = 1 +or: +net.ipv4.conf.all.rp_filter = 2 + +Conflicting assignments are not allowed. + Is it the case that the net.ipv4.conf.all.rp_filter is not set to 1 or 2 or is configured to be 0? + + + + Verify the nodev option is configured for the /boot mount point, + run the following command: + $ sudo mount | grep '\s/boot\s' + . . . /boot . . . nodev . . . + + Is it the case that the "/boot" file system does not have the "nodev" option set? + + + + To ensure that users cannot disable the screensaver idle inactivity setting, run the following: +$ grep idle-activation-enabled /etc/dconf/db/local.d/locks/* +If properly configured, the output should be /org/gnome/desktop/screensaver/idle-activation-enabled + Is it the case that idle-activation-enabled is not locked? + + + + Verify that Red Hat Enterprise Linux 9 is configured to audit the execution of the "sudo" command with the following command: + +$ sudo auditctl -l | grep sudo + +-a always,exit -F path=/usr/bin/sudo -F perm=x -F auid>=1000 -F auid!=unset -k privileged-sudo + Is it the case that the command does not return a line, or the line is commented out? + + + + +Run the following command to determine if the gitosis_can_sendmail SELinux boolean is disabled: +$ getsebool gitosis_can_sendmail +If properly configured, the output should show the following: +gitosis_can_sendmail --> off + Is it the case that gitosis_can_sendmail is not disabled? + + + + +Run the following command to determine if the httpd_can_network_connect SELinux boolean is disabled: +$ getsebool httpd_can_network_connect +If properly configured, the output should show the following: +httpd_can_network_connect --> off + Is it the case that httpd_can_network_connect is not disabled? + + + + To check the ownership of /etc/shadow-, +run the command: +$ ls -lL /etc/shadow- +If properly configured, the output should indicate the following owner: +root + Is it the case that /etc/shadow- does not have an owner of root? + + + + Verify that Red Hat Enterprise Linux 9 is configured to audit the execution of the "semanage" command with the following command: + +$ sudo auditctl -l | grep semanage + +-a always,exit -F path=/usr/sbin/semanage -F perm=x -F auid>=1000 -F auid!=unset -k privileged-unix-update + Is it the case that the command does not return a line, or the line is commented out? + + + + +Run the following command to determine if the sanlock_use_nfs SELinux boolean is disabled: +$ getsebool sanlock_use_nfs +If properly configured, the output should show the following: +sanlock_use_nfs --> off + Is it the case that sanlock_use_nfs is not disabled? + + + + +If the system is configured to prevent the loading of the rds kernel module, +it will contain lines inside any file in /etc/modprobe.d or the deprecated /etc/modprobe.conf. +These lines instruct the module loading system to run another program (such as /bin/true) upon a module install event. + +Run the following command to search for such lines in all files in /etc/modprobe.d and the deprecated /etc/modprobe.conf: +$ grep -r rds /etc/modprobe.conf /etc/modprobe.d + Is it the case that no line is returned? + + + + Inspect the password section of /etc/pam.d/system-auth +and ensure that the pam_unix.so module is configured to use the argument +sha512: + +$ sudo grep "^password.*pam_unix\.so.*sha512" /etc/pam.d/system-auth + +password sufficient pam_unix.so sha512 + Is it the case that "sha512" is missing, or is commented out? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_MODULE_SIG_KEY /boot/config.* + + For each kernel installed, a line with value "" should be returned. + + Is it the case that the kernel was not built with the required value? + + + + To verify that vlock is configured as a locking mechanism in tmux, run the following command: + +$ sudo grep lock-command /etc/tmux.conf + +The output should return the following: + +set -g lock-command vlock + +Then, verify that the /etc/tmux.conf file can be read by other users than root: + +$ sudo ls -al /etc/tmux.conf + Is it the case that lock-command is not set? + + + + Verify RHEL 9 generates audit records for all account creations, modifications, disabling, and termination events that affect "/etc/sudoers.d/" with the following command: + +$ sudo auditctl -l | grep/etc/sudoers.d + +-w /etc/sudoers.d/ -p wa -k identity + Is it the case that the command does not return a line, or the line is commented out? + + + + If TFTP is not installed, this is not applicable. To determine if TFTP is +installed, run the following command: +$ rpm -qa | grep tftp + +Verify tftp is configured by with the -s option by +running the following command: +grep "server_args" /etc/xinetd.d/tftp +server_args = -s + Is it the case that "server_args" line does not have a "-s" option, and a subdirectory is not assigned? + + + + To check the group ownership of /etc/at.allow, +run the command: +$ ls -lL /etc/at.allow +If properly configured, the output should indicate the following group-owner: +root + Is it the case that /etc/at.allow does not have a group owner of root? + + + + To determine if the system is configured to audit calls to the +open system call, run the following command: +$ sudo grep "open" /etc/audit/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + To determine if the system is configured to audit unsuccessful calls +to the lremovexattr system call, run the following command: +$ sudo grep "lremovexattr" /etc/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + Verify Red Hat Enterprise Linux 9 takes the appropriate action when the audit storage volume is full. + +Check that Red Hat Enterprise Linux 9 takes the appropriate action when the audit storage volume is full with the following command: + +$ sudo grep disk_full_action /etc/audit/auditd.conf + +disk_full_action = + +If the value of the "disk_full_action" option is not "SYSLOG", "SINGLE", or "HALT", or the line is commented out, ask the system administrator to indicate how the system takes appropriate action when an audit storage volume is full. + Is it the case that there is no evidence of appropriate action? + + + + Verify that Red Hat Enterprise Linux 9 is configured to audit the execution of the "init" command with the following command: + +$ sudo auditctl -l | grep init + +-a always,exit -F path=/usr/sbin/init -F perm=x -F auid>=1000 -F auid!=unset -k privileged-init + Is it the case that the command does not return a line, or the line is commented out? + + + + To check the permissions of /boot/grub2/grub.cfg, run the command: +$ sudo ls -lL /boot/grub2/grub.cfg +If properly configured, the output should indicate the following +permissions: -rwx------ + Is it the case that it does not? + + + + Verify the noexec option is configured for the /tmp mount point, + run the following command: + $ sudo mount | grep '\s/tmp\s' + . . . /tmp . . . noexec . . . + + Is it the case that the "/tmp" file system does not have the "noexec" option set? + + + + To ensure the system is configured to ignore the Ctrl-Alt-Del sequence, +run the following command: +$ gsettings get org.gnome.settings-daemon.plugins.media-keys logout +$ grep logout /etc/dconf/db/local.d/locks/* +If properly configured, the output should be +/org/gnome/settings-daemon/plugins/media-keys/logout + Is it the case that GNOME3 is configured to reboot when Ctrl-Alt-Del is pressed? + + + + To check if RekeyLimit is set correctly, run the +following command: + +$ sudo grep RekeyLimit /etc/ssh/sshd_config /etc/ssh/sshd_config.d/* + +If configured properly, output should be +RekeyLimit + Is it the case that it is commented out or is not set? + + + + To ensure the user home directory is not group-writable or world-readable, run the following: +# ls -ld /home/USER + Is it the case that the user home directory is group-writable or world-readable? + + + + +Run the following command to determine if the nis_enabled SELinux boolean is disabled: +$ getsebool nis_enabled +If properly configured, the output should show the following: +nis_enabled --> off + Is it the case that nis_enabled is not disabled? + + + + + +Run the following command to determine the current status of the +iptables service: +$ sudo systemctl is-active iptables +If the service is running, it should return the following: active + Is it the case that ? + + + + +Run the following command to determine if the neutron_can_network SELinux boolean is disabled: +$ getsebool neutron_can_network +If properly configured, the output should show the following: +neutron_can_network --> off + Is it the case that neutron_can_network is not disabled? + + + + Run the following command to determine if the dnf-automatic package is installed: $ rpm -q dnf-automatic + Is it the case that the package is not installed? + + + + Verify the nodev option is configured for the /tmp mount point, + run the following command: + $ sudo mount | grep '\s/tmp\s' + . . . /tmp . . . nodev . . . + + Is it the case that the "/tmp" file system does not have the "nodev" option set? + + + + +Run the following command to determine if the httpd_can_connect_zabbix SELinux boolean is disabled: +$ getsebool httpd_can_connect_zabbix +If properly configured, the output should show the following: +httpd_can_connect_zabbix --> off + Is it the case that httpd_can_connect_zabbix is not disabled? + + + + Verify Red Hat Enterprise Linux 9 generates an audit record for unsuccessful attempts to modify files using the open system call with O_TRUNC_WRITE flag. + +If the auditd daemon is configured to use the "augenrules" program to read audit rules during daemon startup (the default), run the following command: + +$ sudo grep -r open /etc/audit/rules.d + +If the auditd daemon is configured to use the "auditctl" utility to read audit rules during daemon startup, run the following command: + +$ sudo grep open /etc/audit/audit.rules + +The output should be the following: + +-a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + Is it the case that the command does not return a line, or the line is commented out? + + + + Verify the nodev option is configured for the /var mount point, + run the following command: + $ sudo mount | grep '\s/var\s' + . . . /var . . . nodev . . . + + Is it the case that the "/var" file system does not have the "nodev" option set? + + + + +Run the following command to determine if the openvpn_enable_homedirs SELinux boolean is disabled: +$ getsebool openvpn_enable_homedirs +If properly configured, the output should show the following: +openvpn_enable_homedirs --> off + Is it the case that openvpn_enable_homedirs is not disabled? + + + + The runtime status of the net.ipv4.conf.all.accept_redirects kernel parameter can be queried +by running the following command: +$ sysctl net.ipv4.conf.all.accept_redirects +0. + + Is it the case that the correct value is not returned? + + + + To check that the rdisc service is disabled in system boot configuration, +run the following command: +$ sudo systemctl is-enabled rdisc +Output should indicate the rdisc service has either not been installed, +or has been disabled at all runlevels, as shown in the example below: +$ sudo systemctl is-enabled rdisc disabled + +Run the following command to verify rdisc is not active (i.e. not running) through current runtime configuration: +$ sudo systemctl is-active rdisc + +If the service is not running the command will return the following output: +inactive + +The service will also be masked, to check that the rdisc is masked, run the following command: +$ sudo systemctl show rdisc | grep "LoadState\|UnitFileState" + +If the service is masked the command will return the following outputs: + +LoadState=masked + +UnitFileState=masked + Is it the case that the "rdisc" is loaded and not masked? + + + + Inspect the form of default GRUB 2 command line for the Linux operating system +in /etc/default/grub. If it includes iommu=force, +then the parameter will be configured for newly installed kernels. +First check if the GRUB recovery is enabled: +$ sudo grep 'GRUB_DISABLE_RECOVERY' /etc/default/grub +If this option is set to true, then check that a line is output by the following command: +$ sudo grep 'GRUB_CMDLINE_LINUX_DEFAULT.*iommu=force.*' /etc/default/grub +If the recovery is disabled, check the line with +$ sudo grep 'GRUB_CMDLINE_LINUX.*iommu=force.*' /etc/default/grub.Moreover, command line parameters for currently installed kernels should be checked as well. +Run the following command: +$ sudo grubby --info=ALL | grep args | grep -v 'iommu=force' +The command should not return any output. + Is it the case that I/OMMU is not activated? + + + + The runtime status of the net.ipv6.conf.all.accept_ra_rtr_pref kernel parameter can be queried +by running the following command: +$ sysctl net.ipv6.conf.all.accept_ra_rtr_pref +0. + + Is it the case that the correct value is not returned? + + + + To verify if root user is required to use complex passwords, run the following command: +$ grep enforce_for_root /etc/security/pwquality.conf +The output should return enforce_for_root uncommented. + Is it the case that enforce_for_root is commented or not present? + + + + Run the following command to determine if the ntp package is installed: $ rpm -q ntp + Is it the case that the package is not installed? + + + + Run the following command to ensure the TMOUT value is configured for all users +on the system: + +$ sudo grep TMOUT /etc/profile /etc/profile.d/*.sh + +The output should return the following: +TMOUT= + Is it the case that TMOUT is not set or its value is greater than expected setting? + + + + + +Run the following command to determine the current status of the +rngd service: +$ sudo systemctl is-active rngd +If the service is running, it should return the following: active + Is it the case that the "rngd" service is disabled, masked, or not started.? + + + + +Run the following command to determine if the sysadm_exec_content SELinux boolean is enabled: +$ getsebool sysadm_exec_content +If properly configured, the output should show the following: +sysadm_exec_content --> on + Is it the case that sysadm_exec_content is not enabled? + + + + Find the list of alias maps used by the Postfix mail server: +$ sudo postconf alias_maps +Query the Postfix alias maps for an alias for the root user: +$ sudo postmap -q root hash:/etc/aliases +The output should return an alias. + Is it the case that the alias is not set? + + + + To ensure the X Windows package group is removed, run the following command: +$ rpm -qi xorg-x11-server-common +The output should be: +package xorg-x11-server-common is not installed + Is it the case that the X Windows package group or xorg-x11-server-common has not be removed? + + + + System executables are stored in the following directories by default: +/bin +/sbin +/usr/bin +/usr/local/bin +/usr/local/sbin +/usr/sbin +For each of these directories, run the following command to find files +not owned by root: +$ sudo find -L DIR/ ! -user root -type d -exec chown root {} \; + Is it the case that any system executables directories are found to not be owned by root? + + + + +Run the following command to determine if the git_session_users SELinux boolean is disabled: +$ getsebool git_session_users +If properly configured, the output should show the following: +git_session_users --> off + Is it the case that git_session_users is not disabled? + + + + To verify that the Audit is correctly configured according to recommended rules, check the content of the file with the following command: +cat /etc/audit/rules.d/30-ospp-v42.rules +The output has to be exactly as follows: +## The purpose of these rules is to meet the requirements for Operating +## System Protection Profile (OSPP)v4.2. These rules depends on having +## the following rule files copied to /etc/audit/rules.d: +## +## 10-base-config.rules, 11-loginuid.rules, +## 30-ospp-v42-1-create-failed.rules, 30-ospp-v42-1-create-success.rules, +## 30-ospp-v42-2-modify-failed.rules, 30-ospp-v42-2-modify-success.rules, +## 30-ospp-v42-3-access-failed.rules, 30-ospp-v42-3-access-success.rules, +## 30-ospp-v42-4-delete-failed.rules, 30-ospp-v42-4-delete-success.rules, +## 30-ospp-v42-5-perm-change-failed.rules, +## 30-ospp-v42-5-perm-change-success.rules, +## 30-ospp-v42-6-owner-change-failed.rules, +## 30-ospp-v42-6-owner-change-success.rules +## +## original copies may be found in /usr/share/audit/sample-rules/ + + +## User add delete modify. This is covered by pam. However, someone could +## open a file and directly create or modify a user, so we'll watch passwd and +## shadow for writes +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b64 -S open -F a1&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b64 -S open -F a1&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify + +## User enable and disable. This is entirely handled by pam. + +## Group add delete modify. This is covered by pam. However, someone could +## open a file and directly create or modify a user, so we'll watch group and +## gshadow for writes +-a always,exit -F path=/etc/passwd -F perm=wa -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F path=/etc/shadow -F perm=wa -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F path=/etc/group -F perm=wa -F auid>=1000 -F auid!=unset -F key=group-modify +-a always,exit -F path=/etc/gshadow -F perm=wa -F auid>=1000 -F auid!=unset -F key=group-modify + + +## Use of special rights for config changes. This would be use of setuid +## programs that relate to user accts. This is not all setuid apps because +## requirements are only for ones that affect system configuration. +-a always,exit -F path=/usr/sbin/unix_chkpwd -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/sbin/usernetctl -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/sbin/userhelper -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/sbin/seunshare -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/mount -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/newgrp -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/newuidmap -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/gpasswd -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/newgidmap -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/umount -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/passwd -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/crontab -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/at -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes + +## Privilege escalation via su or sudo. This is entirely handled by pam. + +## Watch for configuration changes to privilege escalation. +-a always,exit -F path=/etc/sudoers -F perm=wa -F key=special-config-changes +-a always,exit -F dir=/etc/sudoers.d/ -F perm=wa -F key=special-config-changes + +## Audit log access +-a always,exit -F dir=/var/log/audit/ -F perm=r -F auid>=1000 -F auid!=unset -F key=access-audit-trail +## Attempts to Alter Process and Session Initiation Information +-a always,exit -F path=/var/run/utmp -F perm=wa -F auid>=1000 -F auid!=unset -F key=session +-a always,exit -F path=/var/log/btmp -F perm=wa -F auid>=1000 -F auid!=unset -F key=session +-a always,exit -F path=/var/log/wtmp -F perm=wa -F auid>=1000 -F auid!=unset -F key=session + +## Attempts to modify MAC controls +-a always,exit -F dir=/etc/selinux/ -F perm=wa -F auid>=1000 -F auid!=unset -F key=MAC-policy + +## Software updates. This is entirely handled by rpm. + +## System start and shutdown. This is entirely handled by systemd + +## Kernel Module loading. This is handled in 43-module-load.rules + +## Application invocation. The requirements list an user requirement +## FPT_SRP_EXT.1 Software Restriction Policies. This event is intended to +## state results from that policy. This would be handled entirely by +## that daemon. + Is it the case that the file does not exist or the content differs? + + + + +Run the following command to determine if the httpd_run_ipa SELinux boolean is disabled: +$ getsebool httpd_run_ipa +If properly configured, the output should show the following: +httpd_run_ipa --> off + Is it the case that httpd_run_ipa is not disabled? + + + + Inspect the password section of /etc/pam.d/password-auth +and ensure that the pam_unix.so module includes the argument +sha512: +$ grep sha512 /etc/pam.d/password-auth + Is it the case that it does not? + + + + To verify that the Audit is correctly configured according to recommended rules, check the content of the file with the following command: +cat /etc/audit/rules.d/30-ospp-v42-1-create-failed.rules +The output has to be exactly as follows: +## Unsuccessful file creation (open with O_CREAT) +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b32 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b64 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b32 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b64 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create + Is it the case that the file does not exist or the content differs? + + + + +If the system is configured to prevent the loading of the atm kernel module, +it will contain lines inside any file in /etc/modprobe.d or the deprecated /etc/modprobe.conf. +These lines instruct the module loading system to run another program (such as /bin/true) upon a module install event. + +Run the following command to search for such lines in all files in /etc/modprobe.d and the deprecated /etc/modprobe.conf: +$ grep -r atm /etc/modprobe.conf /etc/modprobe.d + Is it the case that no line is returned? + + + + To check if MaxStartups is configured, run the following command: +$ sudo grep MaxStartups /etc/ssh/sshd_config +If configured, this command should output the configuration. + Is it the case that maxstartups is not configured? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_RANDOMIZE_BASE /boot/config.* + + For each kernel installed, a line with value "y" should be returned. + + Is it the case that the kernel was not built with the required value? + + + + To determine if the system is configured to audit unsuccessful calls +to the lsetxattr system call, run the following command: +$ sudo grep "lsetxattr" /etc/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + +Run the following command to determine if the httpd_dbus_sssd SELinux boolean is disabled: +$ getsebool httpd_dbus_sssd +If properly configured, the output should show the following: +httpd_dbus_sssd --> off + Is it the case that httpd_dbus_sssd is not disabled? + + + + System executables are stored in the following directories by default: +/bin +/sbin +/usr/bin +/usr/sbin +/usr/local/bin +/usr/local/sbin +To find system executables directories that are group-writable or +world-writable, run the following command for each directory DIR +which contains system executables: +$ sudo find -L DIR -perm /022 -type d + Is it the case that any of these files are group-writable or world-writable? + + + + To verify that the Audit is correctly configured according to recommended rules, check the content of the file with the following command: +cat /etc/audit/rules.d/30-ospp-v42-3-access-failed.rules +The output has to be exactly as follows: +## Unsuccessful file access (any other opens) This has to go last. +-a always,exit -F arch=b32 -S open,openat,openat2,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-access +-a always,exit -F arch=b64 -S open,openat,openat2,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-access +-a always,exit -F arch=b32 -S open,openat,openat2,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-access +-a always,exit -F arch=b64 -S open,openat,openat2,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-access + Is it the case that the file does not exist or the content differs? + + + + To check which SSH protocol version is allowed, check version of +openssh-server with following command: +$ rpm -qi openssh-server | grep Version +Versions equal to or higher than 7.4 have deprecated the RhostsRSAAuthentication option. +If version is lower than 7.4, run the following command to check configuration: +To determine how the SSH daemon's RhostsRSAAuthentication option is set, run the following command: + +$ sudo grep -i RhostsRSAAuthentication /etc/ssh/sshd_config.d/00-complianceascode-hardening.conf + + +If a line indicating no is returned, then the required value is set. + + Is it the case that the required value is not set? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_BUG_ON_DATA_CORRUPTION /boot/config.* + + For each kernel installed, a line with value "y" should be returned. + + Is it the case that the kernel was not built with the required value? + + + + Verify Red Hat Enterprise Linux 9 generates an audit record for unsuccessful attempts to use the openat system call. + +If the auditd daemon is configured to use the "augenrules" program to to read audit rules during daemon startup (the default), run the following command: + +$ sudo grep -r openat /etc/audit/rules.d + +If the auditd daemon is configured to use the "auditctl" utility to read audit rules during daemon startup, run the following command: + +$ sudo grep openat /etc/audit/audit.rules + +The output should be the following: + +-a always,exit -F arch=b32 -S openat -F exit=-EPERM -F auid>=1000 -F auid!=unset -k access +-a always,exit -F arch=b64 -S openat -F exit=-EPERM -F auid>=1000 -F auid!=unset -k access +-a always,exit -F arch=b32 -S openat -F exit=-EACCES -F auid>=1000 -F auid!=unset -k access +-a always,exit -F arch=b64 -S openat -F exit=-EACCES -F auid>=1000 -F auid!=unset -k access + Is it the case that the command does not return a line, or the line is commented out? + + + + Make sure that the kernel is not disabling SMEP with the following +commands. +grep -q nosmep /boot/config-`uname -r` +If the command returns a line, it means that SMEP is being disabled. + Is it the case that the kernel is configured to disable SMEP? + + + + The runtime status of the fs.protected_hardlinks kernel parameter can be queried +by running the following command: +$ sysctl fs.protected_hardlinks +1. + + Is it the case that the correct value is not returned? + + + + Verify Red Hat Enterprise Linux 9 generates audit records for all account creations, modifications, disabling, and termination events that affect "/var/log/lastlog" with the following command: + +$ sudo auditctl -l | grep /var/log/lastlog + +-w /var/log/lastlog -p wa -k logins + Is it the case that the command does not return a line, or the line is commented out? + + + + Verify Red Hat Enterprise Linux 9 takes the appropriate action when the audit files have reached maximum size. + +Check that Red Hat Enterprise Linux 9 takes the appropriate action when the audit files have reached maximum size with the following command: + +$ sudo grep max_log_file_action /etc/audit/auditd.conf + +max_log_file_action = + Is it the case that the value of the "disk_full_action" option is not "ROTATE", "SINGLE", or the line is commented out, ask the system administrator to indicate how the system takes appropriate action when an audit storage volume is full. If there is no evidence of appropriate action? + + + + To check the ownership of /etc/passwd-, +run the command: +$ ls -lL /etc/passwd- +If properly configured, the output should indicate the following owner: +root + Is it the case that /etc/passwd- does not have an owner of root? + + + + Inspect the form of default GRUB 2 command line for the Linux operating system +in /etc/default/grub. If it includes init_on_alloc=1, +then the parameter will be configured for newly installed kernels. +First check if the GRUB recovery is enabled: +$ sudo grep 'GRUB_DISABLE_RECOVERY' /etc/default/grub +If this option is set to true, then check that a line is output by the following command: +$ sudo grep 'GRUB_CMDLINE_LINUX_DEFAULT.*init_on_alloc=1.*' /etc/default/grub +If the recovery is disabled, check the line with +$ sudo grep 'GRUB_CMDLINE_LINUX.*init_on_alloc=1.*' /etc/default/grub.Moreover, command line parameters for currently installed kernels should be checked as well. +Run the following command: +$ sudo grubby --info=ALL | grep args | grep -v 'init_on_alloc=1' +The command should not return any output. + Is it the case that the kernel is not configured to zero out memory before allocation? + + + + +Run the following command to determine if the unconfined_mozilla_plugin_transition SELinux boolean is enabled: +$ getsebool unconfined_mozilla_plugin_transition +If properly configured, the output should show the following: +unconfined_mozilla_plugin_transition --> on + Is it the case that unconfined_mozilla_plugin_transition is not enabled? + + + + To determine if the system is configured to audit calls to the +lsetxattr system call, run the following command: +$ sudo grep "lsetxattr" /etc/audit/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + +Run the following command to determine if the git_cgi_use_cifs SELinux boolean is disabled: +$ getsebool git_cgi_use_cifs +If properly configured, the output should show the following: +git_cgi_use_cifs --> off + Is it the case that git_cgi_use_cifs is not disabled? + + + + Inspect the form of default GRUB 2 command line for the Linux operating system +in /etc/default/grub. If it includes mds=, +then the parameter will be configured for newly installed kernels. +First check if the GRUB recovery is enabled: +$ sudo grep 'GRUB_DISABLE_RECOVERY' /etc/default/grub +If this option is set to true, then check that a line is output by the following command: +$ sudo grep 'GRUB_CMDLINE_LINUX_DEFAULT.*mds=.*' /etc/default/grub +If the recovery is disabled, check the line with +$ sudo grep 'GRUB_CMDLINE_LINUX.*mds=.*' /etc/default/grub.Moreover, command line parameters for currently installed kernels should be checked as well. +Run the following command: +$ sudo grubby --info=ALL | grep args | grep -v 'mds=' +The command should not return any output. + Is it the case that MDS mitigations are not configured appropriately? + + + + To check the ownership of /boot/grub2/grub.cfg, +run the command: +$ ls -lL /boot/grub2/grub.cfg +If properly configured, the output should indicate the following owner: +root + Is it the case that /boot/grub2/grub.cfg does not have an owner of root? + + + + +Run the following command to determine if the pppd_for_user SELinux boolean is disabled: +$ getsebool pppd_for_user +If properly configured, the output should show the following: +pppd_for_user --> off + Is it the case that pppd_for_user is not disabled? + + + + Run the following command to determine if the crypto-policies package is installed: $ rpm -q crypto-policies + Is it the case that the package is not installed? + + + + Verify that only the "root" account has a UID "0" assignment with the +following command: +$ awk -F: '$3 == 0 {print $1}' /etc/passwd +root + Is it the case that any accounts other than "root" have a UID of "0"? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_MODULE_SIG_HASH /boot/config.* + + For each kernel installed, a line with value "" should be returned. + + Is it the case that the kernel was not built with the required value? + + + + To determine if the system is configured to audit calls to the +finit_module system call, run the following command: +$ sudo grep "finit_module" /etc/audit/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + +Run the following command to determine if the selinuxuser_rw_noexattrfile SELinux boolean is disabled: +$ getsebool selinuxuser_rw_noexattrfile +If properly configured, the output should show the following: +selinuxuser_rw_noexattrfile --> off + Is it the case that selinuxuser_rw_noexattrfile is not disabled? + + + + Ensure there are no unconfined daemons running on the system, +the following command should produce no output: +$ sudo ps -eZ | grep "unconfined_service_t" + Is it the case that There are unconfined daemons running on the system? + + + + To check the group ownership of /var/log, +run the command: +$ ls -lL /var/log +If properly configured, the output should indicate the following group-owner: +root + Is it the case that /var/log does not have a group owner of root? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_SYN_COOKIES /boot/config.* + + For each kernel installed, a line with value "y" should be returned. + + Is it the case that the kernel was not built with the required value? + + + + To determine if the system is configured to audit successful calls +to the lchown system call, run the following command: +$ sudo grep "lchown" /etc/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + To determine if the system is configured to audit calls to the +open_by_handle_at system call, run the following command: +$ sudo grep "open_by_handle_at" /etc/audit/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + To determine if negation is used to define commands users are allowed to execute using sudo, run the following command: +$ sudo grep -PR '^(?:\s*[^#=]+)=(?:\s*(?:\([^\)]+\))?\s*(?!\s*\()[^,!\n][^,\n]+,)*\s*(?:\([^\)]+\))?\s*(?!\s*\()(!\S+).*' /etc/sudoers /etc/sudoers.d/ +The command should return no output. + Is it the case that /etc/sudoers file contains rules that define the set of allowed commands using negation? + + + + To verify interactive users on the system have a home directory assigned, +run the following command: +$ sudo awk -F":" '{print $1 ":" $6}' /etc/passwd +Inspect the output and verify that all interactive users have a home directory +defined. + Is it the case that users home directory is not defined? + + + + To check the permissions of /etc/audit/rules.d/*.rules, +run the command: +$ ls -l /etc/audit/rules.d/*.rules +If properly configured, the output should indicate the following permissions: +-rw-r----- + Is it the case that /etc/audit/rules.d/*.rules does not have unix mode -rw-r-----? + + + + +Run the following command to determine if the xen_use_nfs SELinux boolean is disabled: +$ getsebool xen_use_nfs +If properly configured, the output should show the following: +xen_use_nfs --> off + Is it the case that xen_use_nfs is not disabled? + + + + Determine where the audit logs are stored with the following command: + +$ sudo grep -iw log_file /etc/audit/auditd.conf + +log_file = /var/log/audit/audit.log + +Determine the owner of the audit log directory by using the output of the above command +(default: "/var/log/audit/"). Run the following command with the correct audit log directory +path: + +$ sudo ls -ld /var/log/audit + +drwx------ 2 root root 23 Jun 11 11:56 /var/log/audit + +The audit log directory must be owned by "root" + Is it the case that the directory is not owned by root? + + + + To verify that Audit Daemon is configured to write logs to the disk, run the +following command: +$ sudo grep write_logs /etc/audit/auditd.conf +The output should return the following: +write_logs = yes + Is it the case that write_logs isn't set to yes? + + + + The runtime status of the net.ipv4.conf.default.rp_filter kernel parameter can be queried +by running the following command: +$ sysctl net.ipv4.conf.default.rp_filter +1. + + Is it the case that the correct value is not returned? + + + + Verify that the interactive user account passwords are using a strong +password hash with the following command: + +$ sudo cut -d: -f2 /etc/shadow + +$6$kcOnRq/5$NUEYPuyL.wghQwWssXRcLRFiiru7f5JPV6GaJhNC2aK5F3PZpE/BCCtwrxRc/AInKMNX3CdMw11m9STiql12f/ + +Password hashes ! or * indicate inactive accounts not +available for logon and are not evaluated. + Is it the case that any interactive user password hash does not begin with "$6"? + + + + The telnet package can be removed with the following command: $ sudo dnf erase telnet + Is it the case that ? + + + + Verify that Red Hat Enterprise Linux 9 is configured to audit the execution of the "setsebool" command with the following command: + +$ sudo auditctl -l | grep setsebool + +-a always,exit -F path=/usr/sbin/setsebool -F perm=x -F auid>=1000 -F auid!=unset -k privileged + Is it the case that the command does not return a line, or the line is commented out? + + + + +Run the following command to determine if the webadm_read_user_files SELinux boolean is disabled: +$ getsebool webadm_read_user_files +If properly configured, the output should show the following: +webadm_read_user_files --> off + Is it the case that webadm_read_user_files is not disabled? + + + + To verify that there are no unauthorized local user accounts, run the following command: +$ less /etc/passwd +Inspect the results, and if unauthorized local user accounts exist, remove them by running +the following command: +$ sudo userdel unauthorized_user + Is it the case that there are unauthorized local user accounts on the system? + + + + To verify that the audit system collects unauthorized file accesses, run the following commands: +$ sudo grep EACCES /etc/audit/audit.rules +$ sudo grep EPERM /etc/audit/audit.rules + Is it the case that 32-bit and 64-bit system calls to creat, open, openat, open_by_handle_at, truncate, and ftruncate are not audited during EACCES and EPERM? + + + + +Run the following command to determine if the smartmon_3ware SELinux boolean is disabled: +$ getsebool smartmon_3ware +If properly configured, the output should show the following: +smartmon_3ware --> off + Is it the case that smartmon_3ware is not disabled? + + + + To verify that root's primary group is zero run the following command: + + grep '^root:' /etc/passwd | cut -d : -f 4 + +The command should return: + +0 + + Is it the case that root has a primary gid not equal to zero? + + + + +Run the following command to determine if the httpd_enable_homedirs SELinux boolean is disabled: +$ getsebool httpd_enable_homedirs +If properly configured, the output should show the following: +httpd_enable_homedirs --> off + Is it the case that httpd_enable_homedirs is not disabled? + + + + Verify Red Hat Enterprise Linux 9 generates an audit record for unsuccessful attempts to use the creat system call. + +If the auditd daemon is configured to use the "augenrules" program to to read audit rules during daemon startup (the default), run the following command: + +$ sudo grep -r creat /etc/audit/rules.d + +If the auditd daemon is configured to use the "auditctl" utility to read audit rules during daemon startup, run the following command: + +$ sudo grep creat /etc/audit/audit.rules + +The output should be the following: + +-a always,exit -F arch=b32 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -k access +-a always,exit -F arch=b64 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -k access +-a always,exit -F arch=b32 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -k access +-a always,exit -F arch=b64 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -k access + Is it the case that the command does not return a line, or the line is commented out? + + + + +Run the following command to determine if the domain_kernel_load_modules SELinux boolean is disabled: +$ getsebool domain_kernel_load_modules +If properly configured, the output should show the following: +domain_kernel_load_modules --> off + Is it the case that domain_kernel_load_modules is not disabled? + + + + Check that the symlink exists and target the correct Kerberos crypto policy, with the following command: +file /etc/krb5.conf.d/crypto-policies +If command ouput shows the following line, Kerberos is configured to use the system-wide crypto policy. +/etc/krb5.conf.d/crypto-policies: symbolic link to /etc/crypto-policies/back-ends/krb5.config + Is it the case that the symlink does not exist or points to a different target? + + + + Verify that the system is integrated with a centralized authentication mechanism +such as as Active Directory, Kerberos, Directory Server, etc. that has +automated account mechanisms in place. + Is it the case that the system is not using a centralized authentication mechanism, or it is not automated? + + + + Run the following command to determine if the gssproxy package is installed: +$ rpm -q gssproxy + Is it the case that the package is installed? + + + + Run the following command to determine if the tftp-server package is installed: +$ rpm -q tftp-server + Is it the case that the package is installed? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_SECURITY_DMESG_RESTRICT /boot/config.* + + Configs with value 'n' are not explicitly set in the file, so either commented lines or no + lines should be returned. + + Is it the case that the kernel was not built with the required value? + + + + +Run the following command to determine if the secure_mode_policyload SELinux boolean is disabled: +$ getsebool secure_mode_policyload +If properly configured, the output should show the following: +secure_mode_policyload --> off + Is it the case that secure_mode_policyload is not disabled? + + + + The runtime status of the net.ipv4.conf.all.accept_local kernel parameter can be queried +by running the following command: +$ sysctl net.ipv4.conf.all.accept_local +0. + + Is it the case that the correct value is not returned? + + + + To verify the audispd's syslog plugin is active, run the following command: +$ sudo grep active /etc/audit/plugins.d/syslog.conf +If the plugin is active, the output will show yes. + Is it the case that it is not activated? + + + + The runtime status of the fs.suid_dumpable kernel parameter can be queried +by running the following command: +$ sysctl fs.suid_dumpable +0. + + Is it the case that the correct value is not returned? + + + + The following command will discover and print any +files on local partitions which do not belong to a valid user. +$ df --local -P | awk {'if (NR!=1) print $6'} | sudo xargs -I '{}' find '{}' -xdev -nouser + +Either remove all files and directories from the system that do not have a +valid user, or assign a valid user to all unowned files and directories on +the system with the chown command: +$ sudo chown user file + Is it the case that files exist that are not owned by a valid user? + + + + + +Run the following command to determine the current status of the +chronyd service: +$ sudo systemctl is-active chronyd +If the service is running, it should return the following: active + Is it the case that the chronyd process is not running? + + + + To determine if the system is configured to audit successful calls +to the renameat system call, run the following command: +$ sudo grep "renameat" /etc/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + + +Run the following command to determine the current status of the +syslog-ng service: +$ sudo systemctl is-active syslog-ng +If the service is running, it should return the following: active + Is it the case that the "syslog-ng" service is disabled, masked, or not started.? + + + + To verify that auditing of privileged command use is configured, run the +following command for each local partition PART to find relevant +setuid / setgid programs: +$ sudo find PART -xdev -type f -perm -4000 -o -type f -perm -2000 2>/dev/null +Run the following command to verify entries in the audit rules for all programs +found with the previous command: +$ sudo grep path /etc/audit/audit.rules +All relevant setuid / setgid programs have a line +in the audit rules. + Is it the case that any setuid or setgid programs doesn't have a line in the audit rules? + + + + To verify that the Audit is correctly configured according to recommended rules, check the content of the file with the following command: +cat /etc/audit/rules.d/30-ospp-v42-3-access-success.rules +The output has to be exactly as follows: +## Successful file access (any other opens) This has to go last. +## These next two are likely to result in a whole lot of events +-a always,exit -F arch=b64 -S open,openat,openat2,open_by_handle_at -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-access + Is it the case that the file does not exist or the content differs? + + + + To check the permissions of /etc/crontab, +run the command: +$ ls -l /etc/crontab +If properly configured, the output should indicate the following permissions: +-rw------- + Is it the case that /etc/crontab does not have unix mode -rw-------? + + + + To verify that the Audit is correctly configured according to recommended rules, check the content of the file with the following command: +cat /etc/audit/rules.d/30-ospp-v42-6-owner-change-failed.rules +The output has to be exactly as follows: +## Unsuccessful ownership change +-a always,exit -F arch=b32 -S lchown,fchown,chown,fchownat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-owner-change +-a always,exit -F arch=b64 -S fchown,fchownat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-owner-change +-a always,exit -F arch=b32 -S lchown,fchown,chown,fchownat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-owner-change +-a always,exit -F arch=b64 -S fchown,fchownat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-owner-change + Is it the case that the file does not exist or the content differs? + + + + To verify that BIND uses the system crypto policy, check out that the BIND config file +/etc/named.conf contains the include "/etc/crypto-policies/back-ends/bind.config"; +directive: +$ sudo grep 'include "/etc/crypto-policies/back-ends/bind.config";' /etc/named.conf +Verify that the directive is at the bottom of the options section of the config file. + Is it the case that BIND is installed and the BIND config file doesn't contain the +<pre>include "/etc/crypto-policies/back-ends/bind.config";</pre> directive? + + + + To determine if the system is configured to audit calls to the +openat system call, run the following command: +$ sudo grep "openat" /etc/audit/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + Verify Red Hat Enterprise Linux 9 is configured in the password-auth file to prohibit password reuse +for a minimum of generations with the following command: + +$ grep -i remember /etc/pam.d/password-auth +password pam_pwhistory.so use_authtok remember= retry=3 + Is it the case that the line containing "pam_pwhistory.so" does not have the "remember" module argument set, +is commented out, or the value of the "remember" module argument is set to less than +"<sub idref="var_password_pam_remember" />"? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_GCC_PLUGIN_STRUCTLEAK /boot/config.* + + For each kernel installed, a line with value "y" should be returned. + + Is it the case that the kernel was not built with the required value? + + + + +Run the following command to determine if the antivirus_can_scan_system SELinux boolean is enabled: +$ getsebool antivirus_can_scan_system +If properly configured, the output should show the following: +antivirus_can_scan_system --> on + Is it the case that antivirus_can_scan_system is not enabled? + + + + Verify the operating system encrypts audit records off-loaded onto a different system +or media from the system being audited with the following commands: + +$ sudo grep -i '$DefaultNetstreamDriver' /etc/rsyslog.conf /etc/rsyslog.d/*.conf + +The output should be: + +/etc/rsyslog.conf:$DefaultNetstreamDriver gtls + Is it the case that rsyslogd DefaultNetstreamDriver not set to gtls? + + + + Verify the nosuid option is configured for the /var mount point, + run the following command: + $ sudo mount | grep '\s/var\s' + . . . /var . . . nosuid . . . + + Is it the case that the "/var" file system does not have the "nosuid" option set? + + + + Verify the nodev option is configured for the /var/log mount point, + run the following command: + $ sudo mount | grep '\s/var/log\s' + . . . /var/log . . . nodev . . . + + Is it the case that the "/var/log" file system does not have the "nodev" option set? + + + + +Run the following command to determine if the icecast_use_any_tcp_ports SELinux boolean is disabled: +$ getsebool icecast_use_any_tcp_ports +If properly configured, the output should show the following: +icecast_use_any_tcp_ports --> off + Is it the case that icecast_use_any_tcp_ports is not disabled? + + + + To check the permissions of /var/log/messages, +run the command: +$ ls -l /var/log/messages +If properly configured, the output should indicate the following permissions: +-rw-r----- + Is it the case that /var/log/messages does not have unix mode -rw-r-----? + + + + Inspect /etc/login.defs and ensure that if eihter +SHA_CRYPT_MIN_ROUNDS or SHA_CRYPT_MAX_ROUNDS +are set, they must have the minimum value of 5000. + Is it the case that it does not? + + + + To determine if the system is configured to audit calls to the +init_module system call, run the following command: +$ sudo grep "init_module" /etc/audit/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_HARDENED_USERCOPY_FALLBACK /boot/config.* + + Configs with value 'n' are not explicitly set in the file, so either commented lines or no + lines should be returned. + + Is it the case that the kernel was not built with the required value? + + + + The tftp package can be removed with the following command: $ sudo dnf erase tftp + Is it the case that ? + + + + To determine if the system is configured to audit calls to the +mount system call, run the following command: +$ sudo grep "mount" /etc/audit/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + Verify that logging core dump backtraces is disabled, run the +following command: +$ grep ProcessSizeMax /etc/systemd/coredump.conf + Is it the case that ProcessSizeMax is not set to zero? + + + + Verify that there are no shosts.equiv files +on the system, run the following command: +$ find / -name shosts.equiv + Is it the case that shosts.equiv files exist? + + + + +Run the following command to determine if the httpd_ssi_exec SELinux boolean is disabled: +$ getsebool httpd_ssi_exec +If properly configured, the output should show the following: +httpd_ssi_exec --> off + Is it the case that httpd_ssi_exec is not disabled? + + + + To verify all squashing has been disabled, run the following command: +$ grep all_squash /etc/exports + Is it the case that there is output? + + + + To verify that the Audit is correctly configured according to recommended rules, check the content of the file with the following command: +cat /etc/audit/rules.d/43-module-load.rules +The output has to be exactly as follows: +## These rules watch for kernel module insertion. By monitoring +## the syscall, we do not need any watches on programs. +-a always,exit -F arch=b64 -S init_module,finit_module -F key=module-load +-a always,exit -F arch=b64 -S delete_module -F key=module-unload + Is it the case that the file does not exist or the content differs? + + + + +Run the following command to determine if the zoneminder_anon_write SELinux boolean is disabled: +$ getsebool zoneminder_anon_write +If properly configured, the output should show the following: +zoneminder_anon_write --> off + Is it the case that zoneminder_anon_write is not disabled? + + + + To verify that all interactive user initialization files executable search +path statements do not contain statements that will reference a working +directory other than the users home directory, run the following command: +$ sudo grep -r PATH /home/ +Inspect the output for any PATH is references directories outside the home directory. + Is it the case that paths contain more than local home directories? + + + + +Run the following command to determine if the fenced_can_ssh SELinux boolean is disabled: +$ getsebool fenced_can_ssh +If properly configured, the output should show the following: +fenced_can_ssh --> off + Is it the case that fenced_can_ssh is not disabled? + + + + +Run the following command to determine if the dbadm_exec_content SELinux boolean is enabled: +$ getsebool dbadm_exec_content +If properly configured, the output should show the following: +dbadm_exec_content --> on + Is it the case that dbadm_exec_content is not enabled? + + + + To check that the cups service is disabled in system boot configuration, +run the following command: +$ sudo systemctl is-enabled cups +Output should indicate the cups service has either not been installed, +or has been disabled at all runlevels, as shown in the example below: +$ sudo systemctl is-enabled cups disabled + +Run the following command to verify cups is not active (i.e. not running) through current runtime configuration: +$ sudo systemctl is-active cups + +If the service is not running the command will return the following output: +inactive + +The service will also be masked, to check that the cups is masked, run the following command: +$ sudo systemctl show cups | grep "LoadState\|UnitFileState" + +If the service is masked the command will return the following outputs: + +LoadState=masked + +UnitFileState=masked + Is it the case that the "cups" is loaded and not masked? + + + + Verify that Red Hat Enterprise Linux 9 is configured to audit the execution of the "crontab" command with the following command: + +$ sudo auditctl -l | grep crontab + +-a always,exit -F path=/usr/bin/crontab -F perm=x -F auid>=1000 -F auid!=unset -k privileged-crontab + Is it the case that the command does not return a line, or the line is commented out? + + + + Verify the nodev option is configured for the /var/log/audit mount point, + run the following command: + $ sudo mount | grep '\s/var/log/audit\s' + . . . /var/log/audit . . . nodev . . . + + Is it the case that the "/var/log/audit" file system does not have the "nodev" option set? + + + + To determine if the system is configured to audit successful calls +to the lremovexattr system call, run the following command: +$ sudo grep "lremovexattr" /etc/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + Verify that Promiscuous mode of an interface is disabled, run the following command: +$ ip link | grep PROMISC + Is it the case that any network device is in promiscuous mode? + + + + Verify that a separate file system/partition has been created for /var/log/audit with the following command: + +$ mountpoint /var/log/audit + + Is it the case that "/var/log/audit is not a mountpoint" is returned? + + + + To determine if the system is configured to audit unsuccessful calls +to the fchownat system call, run the following command: +$ sudo grep "fchownat" /etc/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + Verify that Red Hat Enterprise Linux 9 is configured to audit the execution of the "chacl" command with the following command: + +$ sudo auditctl -l | grep chacl + +-a always,exit -F path=/usr/bin/chacl -F perm=x -F auid>=1000 -F auid!=unset -k perm_mod + Is it the case that the command does not return a line, or the line is commented out? + + + + To check if pam_pwquality.so is enabled in password-auth, run the following command: +$ grep pam_pwquality /etc/pam.d/password-auth +The output should be similar to the following: +password requisite pam_pwquality.so + Is it the case that pam_pwquality.so is not enabled in password-auth? + + + + +Run the following command to determine if the mozilla_plugin_use_bluejeans SELinux boolean is disabled: +$ getsebool mozilla_plugin_use_bluejeans +If properly configured, the output should show the following: +mozilla_plugin_use_bluejeans --> off + Is it the case that mozilla_plugin_use_bluejeans is not disabled? + + + + +Run the following command to determine if the nscd_use_shm SELinux boolean is enabled: +$ getsebool nscd_use_shm +If properly configured, the output should show the following: +nscd_use_shm --> on + Is it the case that nscd_use_shm is not enabled? + + + + +Run the following command to determine if the wine_mmap_zero_ignore SELinux boolean is disabled: +$ getsebool wine_mmap_zero_ignore +If properly configured, the output should show the following: +wine_mmap_zero_ignore --> off + Is it the case that wine_mmap_zero_ignore is not disabled? + + + + To determine if the system is configured to audit attempts to +alter time via the /etc/localtime file, run the following +command: +$ sudo auditctl -l | grep "watch=/etc/localtime" +If the system is configured to audit this activity, it will return a line. + Is it the case that the system is not configured to audit time changes? + + + + To check the permissions of /etc/shadow-, +run the command: +$ ls -l /etc/shadow- +If properly configured, the output should indicate the following permissions: +---------- + Is it the case that /etc/shadow- does not have unix mode ----------? + + + + Storing logs with compression can help avoid filling the system disk. +Run the following command to verify that journald is compressing logs. + +grep "^\sCompress" /etc/systemd/journald.conf + +and it should return + +Compress=yes + + Is it the case that is commented out or not configured correctly? + + + + Inspect the form of default GRUB 2 command line for the Linux operating system +in /etc/default/grub. If it includes spec_store_bypass_disable=, +then the parameter will be configured for newly installed kernels. +First check if the GRUB recovery is enabled: +$ sudo grep 'GRUB_DISABLE_RECOVERY' /etc/default/grub +If this option is set to true, then check that a line is output by the following command: +$ sudo grep 'GRUB_CMDLINE_LINUX_DEFAULT.*spec_store_bypass_disable=.*' /etc/default/grub +If the recovery is disabled, check the line with +$ sudo grep 'GRUB_CMDLINE_LINUX.*spec_store_bypass_disable=.*' /etc/default/grub.Moreover, command line parameters for currently installed kernels should be checked as well. +Run the following command: +$ sudo grubby --info=ALL | grep args | grep -v 'spec_store_bypass_disable=' +The command should not return any output. + Is it the case that SSB is not configured appropriately? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_SLAB_MERGE_DEFAULT /boot/config.* + + Configs with value 'n' are not explicitly set in the file, so either commented lines or no + lines should be returned. + + Is it the case that the kernel was not built with the required value? + + + + Verify Red Hat Enterprise Linux 9 generates an audit record for unsuccessful attempts to modify files using the openat system call with O_TRUNC_WRITE flag. + +If the auditd daemon is configured to use the "augenrules" program to read audit rules during daemon startup (the default), run the following command: + +$ sudo grep -r openat /etc/audit/rules.d + +If the auditd daemon is configured to use the "auditctl" utility to read audit rules during daemon startup, run the following command: + +$ sudo grep openat /etc/audit/audit.rules + +The output should be the following: + +-a always,exit -F arch=b32 -S openat -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S openat -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S openat -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S openat -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + Is it the case that the command does not return a line, or the line is commented out? + + + + To verify that OpenSSL uses the system crypto policy, check out that the OpenSSL config file +/etc/pki/tls/openssl.cnf contains the [ crypto_policy ] section with the +.include = /etc/crypto-policies/back-ends/opensslcnf.config directive: + +$ sudo grep '\.include\s* /etc/crypto-policies/back-ends/opensslcnf.config$' /etc/pki/tls/openssl.cnf. + Is it the case that the OpenSSL config file doesn't contain the whole section, +or the section doesn't contain the <pre>.include = /etc/crypto-policies/back-ends/opensslcnf.config</pre> directive? + + + + To verify that the Audit is correctly configured according to recommended rules, check the content of the file with the following command: +cat /etc/audit/rules.d/30-ospp-v42-5-perm-change-success.rules +The output has to be exactly as follows: +## Successful permission change +-a always,exit -F arch=b64 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + Is it the case that the file does not exist or the content differs? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_GCC_PLUGIN_STACKLEAK /boot/config.* + + For each kernel installed, a line with value "y" should be returned. + + Is it the case that the kernel was not built with the required value? + + + + To verify the boot loader superuser account has been set, run the following +command: +sudo grep -A1 "superusers" /boot/grub2/grub.cfg +The output should show the following: +set superusers="superusers-account" +export superusers +where superusers-account is the actual account name different from common names like root, +admin, or administrator and different from any other existing user name. + Is it the case that superuser account is not set or is set to root, admin, administrator or any other existing user name? + + + + +Run the following command to determine if the xdm_exec_bootloader SELinux boolean is disabled: +$ getsebool xdm_exec_bootloader +If properly configured, the output should show the following: +xdm_exec_bootloader --> off + Is it the case that xdm_exec_bootloader is not disabled? + + + + +Run the following command to determine if the virt_use_sanlock SELinux boolean is disabled: +$ getsebool virt_use_sanlock +If properly configured, the output should show the following: +virt_use_sanlock --> off + Is it the case that virt_use_sanlock is not disabled? + + + + The runtime status of the net.ipv6.conf.default.autoconf kernel parameter can be queried +by running the following command: +$ sysctl net.ipv6.conf.default.autoconf +0. + + Is it the case that the correct value is not returned? + + + + +Run the following command to determine if the awstats_purge_apache_log_files SELinux boolean is disabled: +$ getsebool awstats_purge_apache_log_files +If properly configured, the output should show the following: +awstats_purge_apache_log_files --> off + Is it the case that awstats_purge_apache_log_files is not disabled? + + + + To check the group ownership of /etc/gshadow-, +run the command: +$ ls -lL /etc/gshadow- +If properly configured, the output should indicate the following group-owner: +root + Is it the case that /etc/gshadow- does not have a group owner of root? + + + + Run the following command to determine if the libreswan package is installed: $ rpm -q libreswan + Is it the case that the package is not installed? + + + + +Run the following command to determine if the pppd_can_insmod SELinux boolean is disabled: +$ getsebool pppd_can_insmod +If properly configured, the output should show the following: +pppd_can_insmod --> off + Is it the case that pppd_can_insmod is not disabled? + + + + The runtime status of the net.ipv6.conf.all.accept_ra_defrtr kernel parameter can be queried +by running the following command: +$ sysctl net.ipv6.conf.all.accept_ra_defrtr +0. + + Is it the case that the correct value is not returned? + + + + Verify the system-wide shared library directories are group-owned by "root" with the following command: + +$ sudo find /lib /lib64 /usr/lib /usr/lib64 ! -group root -type d -exec stat -c "%n %G" '{}' \; + +If any system-wide shared library directory is returned and is not group-owned by a required system account, this is a finding. + Is it the case that any system-wide shared library directory is returned and is not group-owned by a required system account? + + + + Verify that GRUB_DISABLE_RECOVERY is set to true in /etc/default/grub to disable recovery boot. +Run the following command: + +$ sudo grep GRUB_DISABLE_RECOVERY /etc/default/grub + Is it the case that GRUB_DISABLE_RECOVERY is not set to true or is missing? + + + + Verify Red Hat Enterprise Linux 9 generates an audit record for unsuccessful attempts to use the unlink system call. + +If the auditd daemon is configured to use the "augenrules" program to to read audit rules during daemon startup (the default), run the following command: + +$ sudo grep -r unlink /etc/audit/rules.d + +If the auditd daemon is configured to use the "auditctl" utility to read audit rules during daemon startup, run the following command: + +$ sudo grep unlink /etc/audit/audit.rules + +The output should be the following: + +-a always,exit -F arch=b32 -S unlink -F exit=-EPERM -F auid>=1000 -F auid!=unset -k unsuccessful-delete +-a always,exit -F arch=b64 -S unlink -F exit=-EPERM -F auid>=1000 -F auid!=unset -k unsuccessful-delete +-a always,exit -F arch=b32 -S unlink -F exit=-EACCES -F auid>=1000 -F auid!=unset -k unsuccessful-delete +-a always,exit -F arch=b64 -S unlink -F exit=-EACCES -F auid>=1000 -F auid!=unset -k unsuccessful-delete + Is it the case that the command does not return a line, or the line is commented out? + + + + To check the ownership of /etc/passwd, +run the command: +$ ls -lL /etc/passwd +If properly configured, the output should indicate the following owner: +root + Is it the case that /etc/passwd does not have an owner of root? + + + + The runtime status of the net.ipv6.conf.all.router_solicitations kernel parameter can be queried +by running the following command: +$ sysctl net.ipv6.conf.all.router_solicitations +0. + + Is it the case that the correct value is not returned? + + + + To determine if the system is configured to audit calls to the +umount2 system call, run the following command: +$ sudo grep "umount2" /etc/audit/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + Verify Red Hat Enterprise Linux 9 security patches and updates are installed and up to date. +Updates are required to be applied with a frequency determined by organizational policy. + + +Obtain the list of available package security updates from Red Hat. The URL for updates is https://access.redhat.com/errata-search/. +It is important to note that updates provided by Red Hat may not be present on the system if the underlying packages are not installed. + + +Check that the available package security updates have been installed on the system with the following command: + +$ sudo yum history list | more + +Loaded plugins: langpacks, product-id, subscription-manager +ID | Command line | Date and time | Action(s) | Altered +------------------------------------------------------------------------------- +70 | install aide | 2020-03-05 10:58 | Install | 1 +69 | update -y | 2020-03-04 14:34 | Update | 18 EE +68 | install vlc | 2020-02-21 17:12 | Install | 21 +67 | update -y | 2020-02-21 17:04 | Update | 7 EE + + +Typical update frequency may be overridden by Information Assurance Vulnerability Alert (IAVA) notifications from CYBERCOM. + Is it the case that Red Hat Enterprise Linux 9 is in non-compliance with the organizational patching policy? + + + + Verify that Red Hat Enterprise Linux 9 is configured to audit the execution of the "umount" command with the following command: + +$ sudo auditctl -l | grep umount + +-a always,exit -F path=/usr/bin/umount -F perm=x -F auid>=1000 -F auid!=unset -k privileged-umount + Is it the case that the command does not return a line, or the line is commented out? + + + + To ensure that WIFI connections caanot be created, run the following command: +$ gsettings get org.gnome.nm-applet disable-wifi-create +If properly configured, the output should be true. +To ensure that users cannot enable WIFI connection creation, run the following: +$ grep wifi-create /etc/dconf/db/local.d/locks/* +If properly configured, the output should be +/org/gnome/nm-applet/disable-wifi-create + Is it the case that WIFI connections can be created through GNOME? + + + + +Run the following command to determine if the xdm_write_home SELinux boolean is disabled: +$ getsebool xdm_write_home +If properly configured, the output should show the following: +xdm_write_home --> off + Is it the case that xdm_write_home is not disabled? + + + + To determine that AIDE is verifying extended file attributes, run the following command: +$ grep xattrs /etc/aide.conf +Verify that the xattrs option is added to the correct ruleset. + Is it the case that the xattrs option is missing or not added to the correct ruleset? + + + + The file /etc/at.deny should not exist. +This can be checked by running the following + +stat /etc/at.deny + +and the output should be + +stat: cannot stat `/etc/at.deny': No such file or directory + + Is it the case that the file /etc/at.deny exists? + + + + To ensure write permissions are disabled for group and other + for each element in root's path, run the following command: +# ls -ld DIR + Is it the case that group or other write permissions exist? + + + + Run the following command to ensure postfix accepts mail messages from only the local system: +$ grep inet_interfaces /etc/postfix/main.cf +If properly configured, the output should show only . + Is it the case that it does not? + + + + Verify Red Hat Enterprise Linux 9 generates an audit record for unsuccessful attempts to use the renameat system call. + +If the auditd daemon is configured to use the "augenrules" program to to read audit rules during daemon startup (the default), run the following command: + +$ sudo grep -r renameat /etc/audit/rules.d + +If the auditd daemon is configured to use the "auditctl" utility to read audit rules during daemon startup, run the following command: + +$ sudo grep renameat /etc/audit/audit.rules + +The output should be the following: + +-a always,exit -F arch=b32 -S renameat -F exit=-EPERM -F auid>=1000 -F auid!=unset -k unsuccessful-delete +-a always,exit -F arch=b64 -S renameat -F exit=-EPERM -F auid>=1000 -F auid!=unset -k unsuccessful-delete +-a always,exit -F arch=b32 -S renameat -F exit=-EACCES -F auid>=1000 -F auid!=unset -k unsuccessful-delete +-a always,exit -F arch=b64 -S renameat -F exit=-EACCES -F auid>=1000 -F auid!=unset -k unsuccessful-delete + Is it the case that the command does not return a line, or the line is commented out? + + + + +Run the following command to determine if the samba_create_home_dirs SELinux boolean is disabled: +$ getsebool samba_create_home_dirs +If properly configured, the output should show the following: +samba_create_home_dirs --> off + Is it the case that samba_create_home_dirs is not disabled? + + + + +Run the following command to determine if the openvpn_can_network_connect SELinux boolean is disabled: +$ getsebool openvpn_can_network_connect +If properly configured, the output should show the following: +openvpn_can_network_connect --> off + Is it the case that openvpn_can_network_connect is not disabled? + + + + To verify that acquiring, saving, and processing core dumps is disabled, run the +following command: +$ systemctl status systemd-coredump.socket +The output should be similar to: +● systemd-coredump.socket + Loaded: masked (Reason: Unit systemd-coredump.socket is masked.) + Active: inactive (dead) ... + + Is it the case that unit systemd-coredump.socket is not masked or running? + + + + Verify the nosuid option is configured for the /boot/efi mount point, + run the following command: + $ sudo mount | grep '\s/boot/efi\s' + . . . /boot/efi . . . nosuid . . . + + Is it the case that the "/boot/efi" file system does not have the "nosuid" option set? + + + + To verify that the Audit is correctly configured according to recommended rules, check the content of the file with the following command: +cat /etc/audit/rules.d/30-ospp-v42-4-delete-success.rules +The output has to be exactly as follows: +## Successful file delete +-a always,exit -F arch=b64 -S unlink,unlinkat,rename,renameat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-delete + Is it the case that the file does not exist or the content differs? + + + + Verify the nodev option is configured for the /var/tmp mount point, + run the following command: + $ sudo mount | grep '\s/var/tmp\s' + . . . /var/tmp . . . nodev . . . + + Is it the case that the "/var/tmp" file system does not have the "nodev" option set? + + + + +Run the following command to determine if the samba_export_all_ro SELinux boolean is disabled: +$ getsebool samba_export_all_ro +If properly configured, the output should show the following: +samba_export_all_ro --> off + Is it the case that samba_export_all_ro is not disabled? + + + + Run the following command to determine if the rsyslog-gnutls package is installed: $ rpm -q rsyslog-gnutls + Is it the case that the package is not installed? + + + + +Run the following command to determine if the cron_userdomain_transition SELinux boolean is enabled: +$ getsebool cron_userdomain_transition +If properly configured, the output should show the following: +cron_userdomain_transition --> on + Is it the case that cron_userdomain_transition is not enabled? + + + + To determine how the SSH daemon's PermitUserEnvironment option is set, run the following command: + +$ sudo grep -i PermitUserEnvironment /etc/ssh/sshd_config.d/00-complianceascode-hardening.conf +$ sudo grep -i PermitUserEnvironment /etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf + +If a line indicating no is returned, then the required value is set. + + Is it the case that the required value is not set? + + + + +Run the following command to determine if the samba_export_all_rw SELinux boolean is disabled: +$ getsebool samba_export_all_rw +If properly configured, the output should show the following: +samba_export_all_rw --> off + Is it the case that samba_export_all_rw is not disabled? + + + + The runtime status of the net.ipv4.conf.default.accept_source_route kernel parameter can be queried +by running the following command: +$ sysctl net.ipv4.conf.default.accept_source_route +0. + + Is it the case that the correct value is not returned? + + + + Run the following command to determine if the scap-security-guide package is installed: $ rpm -q scap-security-guide + Is it the case that the package is not installed? + + + + +Run the following command to determine if the squid_connect_any SELinux boolean is disabled: +$ getsebool squid_connect_any +If properly configured, the output should show the following: +squid_connect_any --> off + Is it the case that squid_connect_any is not disabled? + + + + Verify that the shadow password suite configuration is set to encrypt password with a FIPS 140-3 approved cryptographic hashing algorithm. + + +Check the hashing algorithm that is being used to hash passwords with the following command: + +$ sudo grep -i ENCRYPT_METHOD /etc/login.defs + +ENCRYPT_METHOD SHA512 + Is it the case that ENCRYPT_METHOD is not set to SHA512? + + + + To ensure that users cannot change session idle and lock settings, run the following: +$ grep 'lock-delay' /etc/dconf/db/local.d/locks/* +If properly configured, the output should return: +/org/gnome/desktop/screensaver/lock-delay + Is it the case that GNOME3 session settings are not locked or configured properly? + + + + + +Run the following command to determine the current status of the +firewalld service: +$ sudo systemctl is-active firewalld +If the service is running, it should return the following: active + Is it the case that the "firewalld" service is disabled, masked, or not started.? + + + + +Run the following command to determine if the ftpd_use_passive_mode SELinux boolean is disabled: +$ getsebool ftpd_use_passive_mode +If properly configured, the output should show the following: +ftpd_use_passive_mode --> off + Is it the case that ftpd_use_passive_mode is not disabled? + + + + +Run the following command to determine if the selinuxuser_ping SELinux boolean is enabled: +$ getsebool selinuxuser_ping +If properly configured, the output should show the following: +selinuxuser_ping --> on + Is it the case that selinuxuser_ping is not enabled? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_UNMAP_KERNEL_AT_EL0 /boot/config.* + + For each kernel installed, a line with value "y" should be returned. + + Is it the case that the kernel was not built with the required value? + + + + If network services are using the xinetd service, this is not applicable. + +To check that the xinetd service is disabled in system boot configuration, +run the following command: +$ sudo systemctl is-enabled xinetd +Output should indicate the xinetd service has either not been installed, +or has been disabled at all runlevels, as shown in the example below: +$ sudo systemctl is-enabled xinetd disabled + +Run the following command to verify xinetd is not active (i.e. not running) through current runtime configuration: +$ sudo systemctl is-active xinetd + +If the service is not running the command will return the following output: +inactive + +The service will also be masked, to check that the xinetd is masked, run the following command: +$ sudo systemctl show xinetd | grep "LoadState\|UnitFileState" + +If the service is masked the command will return the following outputs: + +LoadState=masked + +UnitFileState=masked + Is it the case that the "xinetd" is loaded and not masked? + + + + To check which SSH protocol version is allowed, check version of openssh-server with following command: + +$ rpm -qi openssh-server | grep Version + +Versions equal to or higher than 7.4 only allow Protocol 2. +If version is lower than 7.4, run the following command to check configuration: +$ sudo grep Protocol /etc/ssh/sshd_config +If configured properly, output should be Protocol 2 + Is it the case that it is commented out or is not set correctly to Protocol 2? + + + + Run the following command to determine if the telnet-server package is installed: +$ rpm -q telnet-server + Is it the case that the package is installed? + + + + Verify the noexec option is configured for the /var/log mount point, + run the following command: + $ sudo mount | grep '\s/var/log\s' + . . . /var/log . . . noexec . . . + + Is it the case that the "/var/log" file system does not have the "noexec" option set? + + + + +Run the following command to get the current configured value for deny_execmem +SELinux boolean: +$ getsebool deny_execmem +The expected cofiguration is . +"on" means true, and "off" means false + Is it the case that deny_execmem is not set as expected? + + + + To check that the vsftpd service is disabled in system boot configuration, +run the following command: +$ sudo systemctl is-enabled vsftpd +Output should indicate the vsftpd service has either not been installed, +or has been disabled at all runlevels, as shown in the example below: +$ sudo systemctl is-enabled vsftpd disabled + +Run the following command to verify vsftpd is not active (i.e. not running) through current runtime configuration: +$ sudo systemctl is-active vsftpd + +If the service is not running the command will return the following output: +inactive + +The service will also be masked, to check that the vsftpd is masked, run the following command: +$ sudo systemctl show vsftpd | grep "LoadState\|UnitFileState" + +If the service is masked the command will return the following outputs: + +LoadState=masked + +UnitFileState=masked + Is it the case that the "vsftpd" is loaded and not masked? + + + + Verify the FAIL_DELAY setting is configured correctly in the /etc/login.defs file by +running the following command: +$ sudo grep -i "FAIL_DELAY" /etc/login.defs +All output must show the value of FAIL_DELAY set as shown in the below: +$ sudo grep -i "FAIL_DELAY" /etc/login.defs +FAIL_DELAY + Is it the case that the above command returns no output, or FAIL_DELAY is configured less than the expected value? + + + + To check the group ownership of /etc/shadow-, +run the command: +$ ls -lL /etc/shadow- +If properly configured, the output should indicate the following group-owner: +root + Is it the case that /etc/shadow- does not have a group owner of root? + + + + To check that the squid service is disabled in system boot configuration, +run the following command: +$ sudo systemctl is-enabled squid +Output should indicate the squid service has either not been installed, +or has been disabled at all runlevels, as shown in the example below: +$ sudo systemctl is-enabled squid disabled + +Run the following command to verify squid is not active (i.e. not running) through current runtime configuration: +$ sudo systemctl is-active squid + +If the service is not running the command will return the following output: +inactive + +The service will also be masked, to check that the squid is masked, run the following command: +$ sudo systemctl show squid | grep "LoadState\|UnitFileState" + +If the service is masked the command will return the following outputs: + +LoadState=masked + +UnitFileState=masked + Is it the case that the "squid" is loaded and not masked? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_COMPAT_VDSO /boot/config.* + + Configs with value 'n' are not explicitly set in the file, so either commented lines or no + lines should be returned. + + Is it the case that the kernel was not built with the required value? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_SECURITY /boot/config.* + + For each kernel installed, a line with value "y" should be returned. + + Is it the case that the kernel was not built with the required value? + + + + To determine how the SSH daemon's Banner option is set, run the following command: + +$ sudo grep -i Banner /etc/ssh/sshd_config.d/00-complianceascode-hardening.conf + + +If a line indicating /etc/issue.net is returned, then the required value is set. + + Is it the case that the required value is not set? + + + + To verify the audispd plugin encrypts audit records off-loaded onto a different +system or media from the system being audited, run the following command: + +$ sudo grep -i enable_krb5 /etc/audit/audisp-remote.conf +The output should return the following: +enable_krb5 = yes + Is it the case that audispd is not encrypting audit records when sent over the network? + + + + The runtime status of the net.ipv6.conf.all.autoconf kernel parameter can be queried +by running the following command: +$ sysctl net.ipv6.conf.all.autoconf +0. + + Is it the case that the correct value is not returned? + + + + Verify that a separate file system/partition has been created for /srv with the following command: + +$ mountpoint /srv + + Is it the case that "/srv is not a mountpoint" is returned? + + + + If IPv6 is disabled, this is not applicable. + +Inspect the file /etc/sysconfig/ip6tables to determine +the default policy for the INPUT chain. It should be set to DROP: +$ sudo grep ":INPUT" /etc/sysconfig/ip6tables + Is it the case that the default policy for the INPUT chain is not set to DROP? + + + + To check the ownership of /etc/gshadow, +run the command: +$ ls -lL /etc/gshadow +If properly configured, the output should indicate the following owner: +root + Is it the case that /etc/gshadow does not have an owner of root? + + + + To verify that the installed operating system is supported, run +the following command: + +$ grep -i "red hat" /etc/redhat-release + +Red Hat Enterprise Linux 9 + Is it the case that the installed operating system is not supported? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_X86_VSYSCALL_EMULATION /boot/config.* + + Configs with value 'n' are not explicitly set in the file, so either commented lines or no + lines should be returned. + + Is it the case that the kernel was not built with the required value? + + + + The runtime status of the net.ipv4.conf.all.drop_gratuitous_arp kernel parameter can be queried +by running the following command: +$ sysctl net.ipv4.conf.all.drop_gratuitous_arp +1. + + Is it the case that the correct value is not returned? + + + + Verify that DNS servers have been configured properly, perform the following: +$ sudo grep nameserver /etc/resolv.conf + Is it the case that less than two lines are returned that are not commented out? + + + + To determine how the SSH daemon's Banner option is set, run the following command: + +$ sudo grep -i Banner /etc/ssh/sshd_config.d/00-complianceascode-hardening.conf + + +If a line indicating /etc/issue is returned, then the required value is set. + + Is it the case that the required value is not set? + + + + +Run the following command to determine if the kdumpgui_run_bootloader SELinux boolean is disabled: +$ getsebool kdumpgui_run_bootloader +If properly configured, the output should show the following: +kdumpgui_run_bootloader --> off + Is it the case that kdumpgui_run_bootloader is not disabled? + + + + +Run the following command to determine if the dhcpc_exec_iptables SELinux boolean is disabled: +$ getsebool dhcpc_exec_iptables +If properly configured, the output should show the following: +dhcpc_exec_iptables --> off + Is it the case that dhcpc_exec_iptables is not disabled? + + + + To verify that the operating system protects against or limits the effects of DoS +attacks by ensuring implementation of rate-limiting measures +on impacted network interfaces, run the following command: +# grep 'net.ipv4.tcp_invalid_ratelimit' /etc/sysctl.conf /etc/sysctl.d/* +The command should output the following line: +/etc/sysctl.conf:net.ipv4.tcp_invalid_ratelimit = +The file where the line has been found can differ, but it must be either /etc/sysctl.conf +or a file located under the /etc/sysctl.d/ directory. + Is it the case that rate limiting of duplicate TCP acknowledgments is not configured? + + + + +Run the following command to determine if the git_cgi_use_nfs SELinux boolean is disabled: +$ getsebool git_cgi_use_nfs +If properly configured, the output should show the following: +git_cgi_use_nfs --> off + Is it the case that git_cgi_use_nfs is not disabled? + + + + Inspect the form of default GRUB 2 command line for the Linux operating system +in /etc/default/grub. If it includes pti=on, +then the parameter will be configured for newly installed kernels. +First check if the GRUB recovery is enabled: +$ sudo grep 'GRUB_DISABLE_RECOVERY' /etc/default/grub +If this option is set to true, then check that a line is output by the following command: +$ sudo grep 'GRUB_CMDLINE_LINUX_DEFAULT.*pti=on.*' /etc/default/grub +If the recovery is disabled, check the line with +$ sudo grep 'GRUB_CMDLINE_LINUX.*pti=on.*' /etc/default/grub.Moreover, command line parameters for currently installed kernels should be checked as well. +Run the following command: +$ sudo grubby --info=ALL | grep args | grep -v 'pti=on' +The command should not return any output. + Is it the case that Kernel page-table isolation is not enabled? + + + + To ensure the user list is disabled, run the following command: +$ grep disable-user-list /etc/dconf/db/distro.d/* +The output should be true. +To ensure that users cannot enable displaying the user list, run the following: +$ grep disable-user-list /etc/dconf/db/distro.d/locks/* +If properly configured, the output should be /org/gnome/login-screen/disable-user-list + Is it the case that disable-user-list has not been configured or is not disabled? + + + + To check for virtual console entries which permit root login, run the +following command: +$ sudo grep ^vc/[0-9] /etc/securetty +If any output is returned, then root logins over virtual console devices is permitted. + Is it the case that root login over virtual console devices is permitted? + + + + To determine if the system is configured to audit calls to the +unlinkat system call, run the following command: +$ sudo grep "unlinkat" /etc/audit/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_PAGE_POISONING /boot/config.* + + For each kernel installed, a line with value "y" should be returned. + + Is it the case that the kernel was not built with the required value? + + + + Make sure that the kernel is not disabling SMAP with the following +commands. +grep -q nosmap /boot/config-`uname -r` +If the command returns a line, it means that SMAP is being disabled. + Is it the case that the kernel is configured to disable SMAP? + + + + The runtime status of the net.ipv6.conf.all.accept_source_route kernel parameter can be queried +by running the following command: +$ sysctl net.ipv6.conf.all.accept_source_route +0. + + Is it the case that the correct value is not returned? + + + + +To check that the rlogin service is disabled in system boot configuration with xinetd, run the following command: +$ chkconfig rlogin --list +Output should indicate the rlogin service has either not been installed, or has been disabled, as shown in the example below: +$ chkconfig rlogin --list + +Note: This output shows SysV services only and does not include native +systemd services. SysV configuration data might be overridden by native +systemd configuration. + +If you want to list systemd services use 'systemctl list-unit-files'. +To see services enabled on particular target use +'systemctl list-dependencies [target]'. + +rlogin off + +To check that the rlogin socket is disabled in system boot configuration with systemd, run the following command: +$ systemctl is-enabled rlogin +Output should indicate the rlogin socket has either not been installed, +or has been disabled at all runlevels, as shown in the example below: +$ sudo systemctl is-enabled rlogindisabled + +Run the following command to verify rlogin is not active (i.e. not running) through current runtime configuration: +$ sudo systemctl is-active rlogin + +If the socket is not running the command will return the following output: +inactive + +The socket will also be masked, to check that the rlogin is masked, run the following command: +$ sudo systemctl show rlogin | grep "LoadState\|UnitFileState" + +If the socket is masked the command will return the following outputs: + +LoadState=masked + +UnitFileState=masked + Is it the case that service and/or socket are running? + + + + Verify Red Hat Enterprise Linux 9 audits the execution of privileged functions. + +Check if Red Hat Enterprise Linux 9 is configured to audit the execution of the "execve" system call using the following command: + +$ sudo grep execve /etc/audit/audit.rules + +The output should be the following: + + +-a always,exit -F arch=b32 -S execve -C uid!=euid -F euid=0 -k setuid +-a always,exit -F arch=b64 -S execve -C uid!=euid -F euid=0 -k setuid +-a always,exit -F arch=b32 -S execve -C gid!=egid -F egid=0 -k setgid +-a always,exit -F arch=b64 -S execve -C gid!=egid -F egid=0 -k setgid + Is it the case that the command does not return all lines, or the lines are commented out? + + + + The runtime status of the net.ipv6.conf.default.router_solicitations kernel parameter can be queried +by running the following command: +$ sysctl net.ipv6.conf.default.router_solicitations +0. + + Is it the case that the correct value is not returned? + + + + +Run the following command to determine if the httpd_graceful_shutdown SELinux boolean is enabled: +$ getsebool httpd_graceful_shutdown +If properly configured, the output should show the following: +httpd_graceful_shutdown --> on + Is it the case that httpd_graceful_shutdown is not enabled? + + + + To verify that the Audit is correctly configured according to recommended rules, check the content of the file with the following command: +cat /etc/audit/rules.d/30-ospp-v42-5-perm-change-failed.rules +The output has to be exactly as follows: +## Unsuccessful permission change +-a always,exit -F arch=b32 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-perm-change +-a always,exit -F arch=b64 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-perm-change +-a always,exit -F arch=b32 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-perm-change +-a always,exit -F arch=b64 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-perm-change + Is it the case that the file does not exist or the content differs? + + + + To verify that the Audit is correctly configured according to recommended rules, check the content of the file with the following command: +cat /etc/audit/rules.d/30-ospp-v42-5-perm-change-success.rules +The output has to be exactly as follows: +## Successful permission change +-a always,exit -F arch=b32 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change +-a always,exit -F arch=b64 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + Is it the case that the file does not exist or the content differs? + + + + To verify Certmap is enabled in SSSD, run the following command: +$ sudo cat /etc/sssd/sssd.conf +If configured properly, output should contain section like the following + +[certmap/testing.test/rule_name] +matchrule =<SAN>.*EDIPI@mil +maprule = (userCertificate;binary={cert!bin}) +domains = testing.test + + Is it the case that Certmap is not configured in SSSD? + + + + To determine whether the SSH service is configured to use strong entropy seed, +run $ sudo grep SSH_USE_STRONG_RNG /etc/sysconfig/sshd +If a line indicating that SSH_USE_STRONG_RNG is set to 32 is returned, +then the option is set correctly. + Is it the case that the SSH_USE_STRONG_RNG is not set to 32 in /etc/sysconfig/sshd? + + + + Run the following command to determine if the ypserv package is installed: +$ rpm -q ypserv + Is it the case that the package is installed? + + + + To determine if the users are allowed to run commands as root, run the following commands: +$ sudo grep -PR '^\s*((?!root\b)[\w]+)\s*(\w+)\s*=\s*(.*,)?\s*[^\(\s]' /etc/sudoers /etc/sudoers.d/ +and +$ sudo grep -PR '^\s*((?!root\b)[\w]+)\s*(\w+)\s*=\s*(.*,)?\s*\([\w\s]*\b(root|ALL)\b[\w\s]*\)' /etc/sudoers /etc/sudoers.d/ +Both commands should return no output. + Is it the case that /etc/sudoers file contains rules that allow non-root users to run commands as root? + + + + To ensure the GUI does not allow user administratrion capabilities to all users, +run the following command: +$ gsettings get org.gnome.desktop.lockdown user-administration-disabled +If properly configured, the output should be true. +To ensure that users cannot enable user administration, run the following: +$ grep user-administration /etc/dconf/db/local.d/locks/* +If properly configured, the output should be +/org/gnome/desktop/lockdown/user-administration-disabled + Is it the case that user administration is not configured or disabled? + + + + Verify the noexec option is configured for the /boot mount point, + run the following command: + $ sudo mount | grep '\s/boot\s' + . . . /boot . . . noexec . . . + + Is it the case that the "/boot" file system does not have the "noexec" option set? + + + + +Run the following command to determine if the ksmtuned_use_cifs SELinux boolean is disabled: +$ getsebool ksmtuned_use_cifs +If properly configured, the output should show the following: +ksmtuned_use_cifs --> off + Is it the case that ksmtuned_use_cifs is not disabled? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_SLAB_FRELIST_RANDOM /boot/config.* + + For each kernel installed, a line with value "y" should be returned. + + Is it the case that the kernel was not built with the required value? + + + + To check for legacy lines in /etc/group, run the following command: + grep '^\+' /etc/group +The command should not return any output. + Is it the case that the file contains legacy lines? + + + + Verify the SELINUX on Red Hat Enterprise Linux 9 is using the policy with the following command: + +$ sestatus | grep policy + +Loaded policy name: + Is it the case that the loaded policy name is not "<sub idref="var_selinux_policy_name" />"? + + + + +Run the following command to determine if the container_connect_any SELinux boolean is disabled: +$ getsebool container_connect_any +If properly configured, the output should show the following: +container_connect_any --> off + Is it the case that container_connect_any is not disabled? + + + + +Run the following command to determine if the xguest_mount_media SELinux boolean is disabled: +$ getsebool xguest_mount_media +If properly configured, the output should show the following: +xguest_mount_media --> off + Is it the case that xguest_mount_media is not disabled? + + + + +Run the following command to determine if the httpd_can_network_connect_cobbler SELinux boolean is disabled: +$ getsebool httpd_can_network_connect_cobbler +If properly configured, the output should show the following: +httpd_can_network_connect_cobbler --> off + Is it the case that httpd_can_network_connect_cobbler is not disabled? + + + + To check the ownership of /etc/group-, +run the command: +$ ls -lL /etc/group- +If properly configured, the output should indicate the following owner: +root + Is it the case that /etc/group- does not have an owner of root? + + + + To determine if the system is configured to audit calls to the +fremovexattr system call, run the following command: +$ sudo grep "fremovexattr" /etc/audit/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + Inspect the form of default GRUB 2 command line for the Linux operating system +in /etc/default/grub. If it includes audit=1, +then the parameter will be configured for newly installed kernels. +First check if the GRUB recovery is enabled: +$ sudo grep 'GRUB_DISABLE_RECOVERY' /etc/default/grub +If this option is set to true, then check that a line is output by the following command: +$ sudo grep 'GRUB_CMDLINE_LINUX_DEFAULT.*audit=1.*' /etc/default/grub +If the recovery is disabled, check the line with +$ sudo grep 'GRUB_CMDLINE_LINUX.*audit=1.*' /etc/default/grub.Moreover, command line parameters for currently installed kernels should be checked as well. +Run the following command: +$ sudo grubby --info=ALL | grep args | grep -v 'audit=1' +The command should not return any output. + Is it the case that auditing is not enabled at boot time? + + + + To verify that the Audit is correctly configured according to recommended rules, check the content of the file with the following command: +cat /etc/audit/rules.d/30-ospp-v42-4-delete-success.rules +The output has to be exactly as follows: +## Successful file delete +-a always,exit -F arch=b32 -S unlink,unlinkat,rename,renameat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-delete +-a always,exit -F arch=b64 -S unlinkat,renameat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-delete + Is it the case that the file does not exist or the content differs? + + + + +Run the following command to determine if the daemons_enable_cluster_mode SELinux boolean is disabled: +$ getsebool daemons_enable_cluster_mode +If properly configured, the output should show the following: +daemons_enable_cluster_mode --> off + Is it the case that daemons_enable_cluster_mode is not disabled? + + + + To check that the slapd service is disabled in system boot configuration, +run the following command: +$ sudo systemctl is-enabled slapd +Output should indicate the slapd service has either not been installed, +or has been disabled at all runlevels, as shown in the example below: +$ sudo systemctl is-enabled slapd disabled + +Run the following command to verify slapd is not active (i.e. not running) through current runtime configuration: +$ sudo systemctl is-active slapd + +If the service is not running the command will return the following output: +inactive + +The service will also be masked, to check that the slapd is masked, run the following command: +$ sudo systemctl show slapd | grep "LoadState\|UnitFileState" + +If the service is masked the command will return the following outputs: + +LoadState=masked + +UnitFileState=masked + Is it the case that the "slapd" is loaded and not masked? + + + + Run the following command to determine if the firewalld package is installed: $ rpm -q firewalld + Is it the case that the package is not installed? + + + + +Run the following command to determine if the httpd_sys_script_anon_write SELinux boolean is disabled: +$ getsebool httpd_sys_script_anon_write +If properly configured, the output should show the following: +httpd_sys_script_anon_write --> off + Is it the case that httpd_sys_script_anon_write is not disabled? + + + + To determine if the system is configured to audit successful calls +to the open_by_handle_at system call, run the following command: +$ sudo grep "open_by_handle_at" /etc/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + +Run the following command to determine if the tftp_home_dir SELinux boolean is disabled: +$ getsebool tftp_home_dir +If properly configured, the output should show the following: +tftp_home_dir --> off + Is it the case that tftp_home_dir is not disabled? + + + + To check that the oddjobd service is disabled in system boot configuration, +run the following command: +$ sudo systemctl is-enabled oddjobd +Output should indicate the oddjobd service has either not been installed, +or has been disabled at all runlevels, as shown in the example below: +$ sudo systemctl is-enabled oddjobd disabled + +Run the following command to verify oddjobd is not active (i.e. not running) through current runtime configuration: +$ sudo systemctl is-active oddjobd + +If the service is not running the command will return the following output: +inactive + +The service will also be masked, to check that the oddjobd is masked, run the following command: +$ sudo systemctl show oddjobd | grep "LoadState\|UnitFileState" + +If the service is masked the command will return the following outputs: + +LoadState=masked + +UnitFileState=masked + Is it the case that the "oddjobd" is loaded and not masked? + + + + To verify the operating system prevents non-privileged users from executing +privileged functions to include disabling, circumventing, or altering +implemented security safeguards/countermeasures, run the following +command: +$ sudo semanage login -l +All administrators must be mapped to the sysadm_u or staff_u +users with the appropriate domains (sysadm_t and staff_t). + +All authorized non-administrative +users must be mapped to the user_u role or the appropriate domain +(user_t). + Is it the case that non-admin users are not confined correctly? + + + + +Run the following command to determine if the irssi_use_full_network SELinux boolean is disabled: +$ getsebool irssi_use_full_network +If properly configured, the output should show the following: +irssi_use_full_network --> off + Is it the case that irssi_use_full_network is not disabled? + + + + To check the permissions of /boot/grub2/grub.cfg, run the command: +$ sudo ls -lL /boot/grub2/grub.cfg +If properly configured, the output should indicate the following +permissions: -rw------- + Is it the case that it does not? + + + + To determine if the system is configured to audit calls to the +rmdir system call, run the following command: +$ sudo grep "rmdir" /etc/audit/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + The runtime status of the kernel.panic_on_oops kernel parameter can be queried +by running the following command: +$ sysctl kernel.panic_on_oops +1. + + Is it the case that the correct value is not returned? + + + + To verify that the Audit is correctly configured according to recommended rules, check the content of the file with the following command: +$ sudo cat /etc/audit/rules.d/11-loginuid.rules +The output has to be exactly as follows: +## Make the loginuid immutable. This prevents tampering with the auid. +--loginuid-immutable + Is it the case that the file does not exist or the content differs? + + + + +Run the following command to determine if the xguest_connect_network SELinux boolean is disabled: +$ getsebool xguest_connect_network +If properly configured, the output should show the following: +xguest_connect_network --> off + Is it the case that xguest_connect_network is not disabled? + + + + +Run the following command to determine if the staff_use_svirt SELinux boolean is disabled: +$ getsebool staff_use_svirt +If properly configured, the output should show the following: +staff_use_svirt --> off + Is it the case that staff_use_svirt is not disabled? + + + + The runtime status of the net.ipv6.conf.all.accept_ra kernel parameter can be queried +by running the following command: +$ sysctl net.ipv6.conf.all.accept_ra +0. + + Is it the case that the correct value is not returned? + + + + Run the following command to determine if the httpd package is installed: +$ rpm -q httpd + Is it the case that the package is installed? + + + + To determine how the SSH daemon's PubkeyAuthentication option is set, run the following command: + +$ sudo grep -i PubkeyAuthentication /etc/ssh/sshd_config.d/00-complianceascode-hardening.conf + + +If a line indicating yes is returned, then the required value is set. + + Is it the case that the required value is not set? + + + + +Run the following command to determine if the prosody_bind_http_port SELinux boolean is disabled: +$ getsebool prosody_bind_http_port +If properly configured, the output should show the following: +prosody_bind_http_port --> off + Is it the case that prosody_bind_http_port is not disabled? + + + + Verify the system-wide shared library files contained in the following directories have mode "755" or less permissive with the following command: + +$ sudo find -L /lib /lib64 /usr/lib /usr/lib64 -perm /022 -type f -exec ls -l {} \; + Is it the case that any system-wide shared library file is found to be group-writable or world-writable? + + + + To verify that the Audit is correctly configured according to recommended rules, check the content of the file with the following command: +cat /etc/audit/rules.d/30-ospp-v42.rules +The output has to be exactly as follows: +## The purpose of these rules is to meet the requirements for Operating +## System Protection Profile (OSPP)v4.2. These rules depends on having +## the following rule files copied to /etc/audit/rules.d: +## +## 10-base-config.rules, 11-loginuid.rules, +## 30-ospp-v42-1-create-failed.rules, 30-ospp-v42-1-create-success.rules, +## 30-ospp-v42-2-modify-failed.rules, 30-ospp-v42-2-modify-success.rules, +## 30-ospp-v42-3-access-failed.rules, 30-ospp-v42-3-access-success.rules, +## 30-ospp-v42-4-delete-failed.rules, 30-ospp-v42-4-delete-success.rules, +## 30-ospp-v42-5-perm-change-failed.rules, +## 30-ospp-v42-5-perm-change-success.rules, +## 30-ospp-v42-6-owner-change-failed.rules, +## 30-ospp-v42-6-owner-change-success.rules +## +## original copies may be found in /usr/share/audit/sample-rules/ + + +## User add delete modify. This is covered by pam. However, someone could +## open a file and directly create or modify a user, so we'll watch passwd and +## shadow for writes +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b32 -S open -F a1&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b32 -S open -F a1&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify + +## User enable and disable. This is entirely handled by pam. + +## Group add delete modify. This is covered by pam. However, someone could +## open a file and directly create or modify a user, so we'll watch group and +## gshadow for writes +-a always,exit -F path=/etc/passwd -F perm=wa -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F path=/etc/shadow -F perm=wa -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F path=/etc/group -F perm=wa -F auid>=1000 -F auid!=unset -F key=group-modify +-a always,exit -F path=/etc/gshadow -F perm=wa -F auid>=1000 -F auid!=unset -F key=group-modify + + +## Use of special rights for config changes. This would be use of setuid +## programs that relate to user accts. This is not all setuid apps because +## requirements are only for ones that affect system configuration. +-a always,exit -F path=/usr/sbin/unix_chkpwd -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/sbin/usernetctl -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/sbin/userhelper -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/sbin/seunshare -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/mount -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/newgrp -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/newuidmap -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/gpasswd -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/newgidmap -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/umount -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/passwd -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/crontab -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/at -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes + +## Privilege escalation via su or sudo. This is entirely handled by pam. + +## Watch for configuration changes to privilege escalation. +-a always,exit -F path=/etc/sudoers -F perm=wa -F key=special-config-changes +-a always,exit -F dir=/etc/sudoers.d/ -F perm=wa -F key=special-config-changes + +## Audit log access +-a always,exit -F dir=/var/log/audit/ -F perm=r -F auid>=1000 -F auid!=unset -F key=access-audit-trail +## Attempts to Alter Process and Session Initiation Information +-a always,exit -F path=/var/run/utmp -F perm=wa -F auid>=1000 -F auid!=unset -F key=session +-a always,exit -F path=/var/log/btmp -F perm=wa -F auid>=1000 -F auid!=unset -F key=session +-a always,exit -F path=/var/log/wtmp -F perm=wa -F auid>=1000 -F auid!=unset -F key=session + +## Attempts to modify MAC controls +-a always,exit -F dir=/etc/selinux/ -F perm=wa -F auid>=1000 -F auid!=unset -F key=MAC-policy + +## Software updates. This is entirely handled by rpm. + +## System start and shutdown. This is entirely handled by systemd + +## Kernel Module loading. This is handled in 43-module-load.rules + +## Application invocation. The requirements list an user requirement +## FPT_SRP_EXT.1 Software Restriction Policies. This event is intended to +## state results from that policy. This would be handled entirely by +## that daemon. + Is it the case that the file does not exist or the content differs? + + + + Run the following command to determine if the openscap-scanner package is installed: $ rpm -q openscap-scanner + Is it the case that the package is not installed? + + + + To check that the dhcpd service is disabled in system boot configuration, +run the following command: +$ sudo systemctl is-enabled dhcpd +Output should indicate the dhcpd service has either not been installed, +or has been disabled at all runlevels, as shown in the example below: +$ sudo systemctl is-enabled dhcpd disabled + +Run the following command to verify dhcpd is not active (i.e. not running) through current runtime configuration: +$ sudo systemctl is-active dhcpd + +If the service is not running the command will return the following output: +inactive + +The service will also be masked, to check that the dhcpd is masked, run the following command: +$ sudo systemctl show dhcpd | grep "LoadState\|UnitFileState" + +If the service is masked the command will return the following outputs: + +LoadState=masked + +UnitFileState=masked + Is it the case that the "dhcpd" is loaded and not masked? + + + + To verify that the Audit is correctly configured according to recommended rules, check the content of the file with the following command: +cat /etc/audit/rules.d/30-ospp-v42-2-modify-failed.rules +The output has to be exactly as follows: +## Unsuccessful file modifications (open for write or truncate) +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification + Is it the case that the file does not exist or the content differs? + + + + +Run the following command to determine if the httpd_read_user_content SELinux boolean is disabled: +$ getsebool httpd_read_user_content +If properly configured, the output should show the following: +httpd_read_user_content --> off + Is it the case that httpd_read_user_content is not disabled? + + + + To ensure only SNMPv3 or newer is used, run the following command: +$ sudo grep 'rocommunity\|rwcommunity\|com2sec' /etc/snmp/snmpd.conf | grep -v "^#" +There should be no output. + Is it the case that there is output? + + + + +Run the following command to determine if the varnishd_connect_any SELinux boolean is disabled: +$ getsebool varnishd_connect_any +If properly configured, the output should show the following: +varnishd_connect_any --> off + Is it the case that varnishd_connect_any is not disabled? + + + + Verify the pam_faillock.so module is present in the "/etc/pam.d/password-auth" file: + +$ sudo grep pam_faillock.so /etc/pam.d/password-auth + +auth required pam_faillock.so preauth +auth required pam_faillock.so authfail +account required pam_faillock.so + Is it the case that the pam_faillock.so module is not present in the "/etc/pam.d/password-auth" file with the "preauth" line listed before pam_unix.so? + + + + Verify Red Hat Enterprise Linux 9 generates audit records for all account creations, modifications, disabling, and termination events that affect "/etc/security/opasswd" with the following command: + +$ sudo auditctl -l | egrep '(/etc/security/opasswd)' + +-w /etc/security/opasswd -p wa -k identity + Is it the case that the command does not return a line, or the line is commented out? + + + + To check the minimum password age, run the command: +$ grep PASS_MIN_DAYS /etc/login.defs + Is it the case that it is not equal to or greater than the required value? + + + + +Run the following command to determine if the zabbix_can_network SELinux boolean is disabled: +$ getsebool zabbix_can_network +If properly configured, the output should show the following: +zabbix_can_network --> off + Is it the case that zabbix_can_network is not disabled? + + + + To check the group ownership of /etc/shadow, +run the command: +$ ls -lL /etc/shadow +If properly configured, the output should indicate the following group-owner: +root + Is it the case that /etc/shadow does not have a group owner of root? + + + + The runtime status of the net.ipv4.icmp_ignore_bogus_error_responses kernel parameter can be queried +by running the following command: +$ sysctl net.ipv4.icmp_ignore_bogus_error_responses +1. + + Is it the case that the correct value is not returned? + + + + To check how many characters are required in a password, run the following command: +$ grep minlen /etc/security/pwquality.conf +Your output should contain minlen = + Is it the case that minlen is not found, or not equal to or greater than the required value? + + + + +Run the following command to determine if the virt_sandbox_use_all_caps SELinux boolean is disabled: +$ getsebool virt_sandbox_use_all_caps +If properly configured, the output should show the following: +virt_sandbox_use_all_caps --> off + Is it the case that virt_sandbox_use_all_caps is not disabled? + + + + To determine how the SSH daemon's LogLevel option is set, run the following command: + +$ sudo grep -i LogLevel /etc/ssh/sshd_config.d/00-complianceascode-hardening.conf +$ sudo grep -i LogLevel /etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf + +If a line indicating INFO is returned, then the required value is set. + + Is it the case that the required value is not set? + + + + Inspect the form of default GRUB 2 command line for the Linux operating system +in /etc/default/grub. If it includes page_alloc.shuffle=1, +then the parameter will be configured for newly installed kernels. +First check if the GRUB recovery is enabled: +$ sudo grep 'GRUB_DISABLE_RECOVERY' /etc/default/grub +If this option is set to true, then check that a line is output by the following command: +$ sudo grep 'GRUB_CMDLINE_LINUX_DEFAULT.*page_alloc.shuffle=1.*' /etc/default/grub +If the recovery is disabled, check the line with +$ sudo grep 'GRUB_CMDLINE_LINUX.*page_alloc.shuffle=1.*' /etc/default/grub.Moreover, command line parameters for currently installed kernels should be checked as well. +Run the following command: +$ sudo grubby --info=ALL | grep args | grep -v 'page_alloc.shuffle=1' +The command should not return any output. + Is it the case that randomization of the page allocator is not enabled in the kernel? + + + + Verify the nosuid option is configured for the /var/log/audit mount point, + run the following command: + $ sudo mount | grep '\s/var/log/audit\s' + . . . /var/log/audit . . . nosuid . . . + + Is it the case that the "/var/log/audit" file system does not have the "nosuid" option set? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_PANIC_TIMEOUT /boot/config.* + + For each kernel installed, a line with value "" should be returned. + + Is it the case that the kernel was not built with the required value? + + + + Run the following command to determine if the setroubleshoot-server package is installed: +$ rpm -q setroubleshoot-server + Is it the case that the package is installed? + + + + Verify the noexec option is configured for the /var/tmp mount point, + run the following command: + $ sudo mount | grep '\s/var/tmp\s' + . . . /var/tmp . . . noexec . . . + + Is it the case that the "/var/tmp" file system does not have the "noexec" option set? + + + + Run the following command to determine if the opensc package is installed: $ rpm -q opensc + Is it the case that the package is not installed? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_DEVKMEM /boot/config.* + + Configs with value 'n' are not explicitly set in the file, so either commented lines or no + lines should be returned. + + Is it the case that the kernel was not built with the required value? + + + + Verify the umask setting is configured correctly in the /etc/csh.cshrc file by +running the following command: +$ sudo grep "umask" /etc/csh.cshrc +All output must show the value of umask set as shown in the below: +umask + Is it the case that the above command returns no output, or the umask is configured incorrectly? + + + + Verify that Red Hat Enterprise Linux 9 is configured to audit the execution of the "chsh" command with the following command: + +$ sudo auditctl -l | grep chsh + +-a always,exit -F path=/usr/bin/chsh -F perm=x -F auid>=1000 -F auid!=unset -k privileged-chsh + Is it the case that the command does not return a line, or the line is commented out? + + + + +Run the following command to determine if the xend_run_qemu SELinux boolean is enabled: +$ getsebool xend_run_qemu +If properly configured, the output should show the following: +xend_run_qemu --> on + Is it the case that xend_run_qemu is not enabled? + + + + To check the permissions of /boot/Sysem.map-*, +run the command: +$ ls -l /boot/Sysem.map-* +If properly configured, the output should indicate the following permissions: +-rw------- + Is it the case that ? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_DEFAULT_MMAP_MIN_ADDR /boot/config.* + + For each kernel installed, a line with value "65536" should be returned. + + Is it the case that the kernel was not built with the required value? + + + + Verify that Red Hat Enterprise Linux 9 is configured to audit the execution of the "chcon" command with the following command: + +$ sudo auditctl -l | grep chcon + +-a always,exit -F path=/usr/bin/chcon -F perm=x -F auid>=1000 -F auid!=unset -k perm_mod + Is it the case that the command does not return a line, or the line is commented out? + + + + To ensure the tally directory is configured correctly, run the following command: +$ grep 'dir =' /etc/security/faillock.conf +The output should show that dir is set to something different to "/var/run/faillock" + Is it the case that dir is not set or is set to /var/run/faillock? + + + + To check the permissions of /etc/ssh/sshd_config, +run the command: +$ ls -l /etc/ssh/sshd_config +If properly configured, the output should indicate the following permissions: +-rw------- + Is it the case that /etc/ssh/sshd_config does not have unix mode -rw-------? + + + + +Run the following command to determine if the virt_use_rawip SELinux boolean is disabled: +$ getsebool virt_use_rawip +If properly configured, the output should show the following: +virt_use_rawip --> off + Is it the case that virt_use_rawip is not disabled? + + + + To find SGID files, run the following command: +$ sudo find / -xdev -type f -perm -2000 + Is it the case that there is output? + + + + To verify all accounts have unique names, run the following command: +$ sudo getent passwd | awk -F: '{ print $1}' | uniq -d +No output should be returned. + Is it the case that a line is returned? + + + + Inspect the form of default GRUB 2 command line for the Linux operating system +in /etc/default/grub. If it includes rng_core.default_quality=, +then the parameter will be configured for newly installed kernels. +First check if the GRUB recovery is enabled: +$ sudo grep 'GRUB_DISABLE_RECOVERY' /etc/default/grub +If this option is set to true, then check that a line is output by the following command: +$ sudo grep 'GRUB_CMDLINE_LINUX_DEFAULT.*rng_core.default_quality=.*' /etc/default/grub +If the recovery is disabled, check the line with +$ sudo grep 'GRUB_CMDLINE_LINUX.*rng_core.default_quality=.*' /etc/default/grub.Moreover, command line parameters for currently installed kernels should be checked as well. +Run the following command: +$ sudo grubby --info=ALL | grep args | grep -v 'rng_core.default_quality=' +The command should not return any output. + Is it the case that trust on hardware random number generator is not configured appropriately? + + + + To ensure all GIDs referenced in /etc/passwd are defined in /etc/group, +run the following command: +$ sudo pwck -qr +There should be no output. + Is it the case that GIDs referenced in /etc/passwd are returned as not defined in /etc/group? + + + + The file permissions for all log files written by rsyslog should +be set to 600, or more restrictive. These log files are determined by the +second part of each Rule line in /etc/rsyslog.conf and typically +all appear in /var/log. To see the permissions of a given log +file, run the following command: +$ ls -l LOGFILE +The permissions should be 600, or more restrictive. + Is it the case that the permissions are not correct? + + + + To determine if the system is configured to audit calls to the +fchmodat system call, run the following command: +$ sudo grep "fchmodat" /etc/audit/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + To determine if the system is configured to audit calls to the +init_module system call, run the following command: +$ sudo grep "init_module" /etc/audit/audit.* +If the system is configured to audit this activity, it will return a line. +To determine if the system is configured to audit calls to the +delete_module system call, run the following command: +$ sudo grep "delete_module" /etc/audit/audit.* +If the system is configured to audit this activity, it will return a line. + Is it the case that no line is returned? + + + + To verify that remote access methods are logging to rsyslog, +run the following command: +grep -rE '(auth.\*|authpriv.\*|daemon.\*)' /etc/rsyslog.* +The output should contain auth.*, authpriv.*, and daemon.* +pointing to a log file. + Is it the case that remote access methods are not logging to rsyslog? + + + + To check the ownership of /etc/crontab, +run the command: +$ ls -lL /etc/crontab +If properly configured, the output should indicate the following owner: +root + Is it the case that /etc/crontab does not have an owner of root? + + + + To verify that rsyslog's Forwarding Output Module is configured +to use TLS for logging to remote server, run the following command: +$ grep omfwd /etc/rsyslog.conf /etc/rsyslog.d/*.conf +The output should include record similar to +action(type="omfwd" protocol="tcp" Target="<remote system>" port="6514" + StreamDriver="gtls" StreamDriverMode="1" StreamDriverAuthMode="x509/name" streamdriver.CheckExtendedKeyPurpose="on") + +where the <remote system> present in the configuration line above must be a valid IP address or a host name of the remote logging server. + Is it the case that omfwd is not configured with gtls and AuthMode? + + + + Verify that Red Hat Enterprise Linux 9 is configured to audit the execution of the "ssh-keysign" command with the following command: + +$ sudo auditctl -l | grep ssh-keysign + +-a always,exit -F path=/usr/libexec/openssh/ssh-keysign -F perm=x -F auid>=1000 -F auid!=unset -k privileged-ssh-keysign + Is it the case that the command does not return a line, or the line is commented out? + + + + To verify if the OpenSSH client uses defined Cipher suite in the Crypto Policy, run: +$ grep -i ciphers /etc/crypto-policies/back-ends/openssh.config +and verify that the line matches: +Ciphers + Is it the case that Crypto Policy for OpenSSH client is not configured correctly? + + + + To ensure logs are sent to a remote host, examine the file +/etc/rsyslog.conf. +If using UDP, a line similar to the following should be present: + *.* @ +If using TCP, a line similar to the following should be present: + *.* @@ +If using RELP, a line similar to the following should be present: + *.* :omrelp: + Is it the case that no evidence that the audit logs are being off-loaded to another system or media? + + + + To determine if the system is configured to audit successful calls +to the openat system call, run the following command: +$ sudo grep "openat" /etc/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + To check the ownership of /etc/cron.weekly, +run the command: +$ ls -lL /etc/cron.weekly +If properly configured, the output should indicate the following owner: +root + Is it the case that /etc/cron.weekly does not have an owner of root? + + + + +Run the following command to determine if the git_session_bind_all_unreserved_ports SELinux boolean is disabled: +$ getsebool git_session_bind_all_unreserved_ports +If properly configured, the output should show the following: +git_session_bind_all_unreserved_ports --> off + Is it the case that git_session_bind_all_unreserved_ports is not disabled? + + + + To check that the rhnsd service is disabled in system boot configuration, +run the following command: +$ sudo systemctl is-enabled rhnsd +Output should indicate the rhnsd service has either not been installed, +or has been disabled at all runlevels, as shown in the example below: +$ sudo systemctl is-enabled rhnsd disabled + +Run the following command to verify rhnsd is not active (i.e. not running) through current runtime configuration: +$ sudo systemctl is-active rhnsd + +If the service is not running the command will return the following output: +inactive + +The service will also be masked, to check that the rhnsd is masked, run the following command: +$ sudo systemctl show rhnsd | grep "LoadState\|UnitFileState" + +If the service is masked the command will return the following outputs: + +LoadState=masked + +UnitFileState=masked + Is it the case that the "rhnsd" is loaded and not masked? + + + + Verify that Red Hat Enterprise Linux 9 is configured to audit the execution of the "restorecon" command with the following command: + +$ sudo auditctl -l | grep restorecon + +-a always,exit -F path=/usr/sbin/restorecon -F perm=x -F auid>=1000 -F auid!=unset -k privileged-restorecon + Is it the case that the command does not return a line, or the line is commented out? + + + + Verify the audit system is configured to take an appropriate action when the internal event queue is full: +$ sudo grep -i overflow_action /etc/audit/auditd.conf + +The output should contain overflow_action = syslog + +If the value of the "overflow_action" option is not set to syslog, +single, halt or the line is commented out, ask the System Administrator +to indicate how the audit logs are off-loaded to a different system or media. + Is it the case that auditd overflow action is not set correctly? + + + + +Run the following command to determine if the samba_portmapper SELinux boolean is disabled: +$ getsebool samba_portmapper +If properly configured, the output should show the following: +samba_portmapper --> off + Is it the case that samba_portmapper is not disabled? + + + + Verify the Red Hat Enterprise Linux 9 "fapolicyd" employs a deny-all, permit-by-exception policy. + +Check that "fapolicyd" is in enforcement mode with the following command: + +$ sudo grep permissive /etc/fapolicyd/fapolicyd.conf + +permissive = 0 + +Check that fapolicyd employs a deny-all policy on system mounts with the following command: + +$ sudo tail /etc/fapolicyd/fapolicyd.rules + +allow exe=/usr/bin/python3.7 : ftype=text/x-python +deny_audit perm=any pattern=ld_so : all +deny perm=any all : all + Is it the case that fapolicyd is not running in enforcement mode with a deny-all, permit-by-exception policy? + + + + Check group owners of the system audit logs. + +First, determine where the audit log file is located. + +$ sudo grep -iw ^log_file /etc/audit/auditd.conf +log_file = /var/log/audit/audit.log + +The log_file option specifies the audit log file path. +If the log_file option isn't defined, check all files within /var/log/audit directory. + + +Then, determine the audit log group by running the following command: +$ sudo grep -P '^[ ]*log_group[ ]+=.*$' /etc/audit/auditd.conf + + +Then, check that the audit log file is owned by the correct group. +Run the following command to display the owner of the audit log file: + +$ sudo stat -c "%n %G" log_file + + +The audit log file must be owned by the log_group or by root if the log_group is not specified. + Is it the case that audit log files are owned by incorrect group? + + + + +Run the following command to determine if the logadm_exec_content SELinux boolean is enabled: +$ getsebool logadm_exec_content +If properly configured, the output should show the following: +logadm_exec_content --> on + Is it the case that logadm_exec_content is not enabled? + + + + The group-owner of all log files written by rsyslog should be . +These log files are determined by the second part of each Rule line in +/etc/rsyslog.conf and typically all appear in /var/log. +To see the group-owner of a given log file, run the following command: +$ ls -l LOGFILE + Is it the case that the group-owner is not correct? + + + + To determine if the system is configured to audit successful calls +to the fchmod system call, run the following command: +$ sudo grep "fchmod" /etc/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + To determine if the system is configured to audit calls to the +settimeofday system call, run the following command: +$ sudo grep "settimeofday" /etc/audit/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + These settings can be verified by running the following: +$ gsettings get org.gnome.desktop.media-handling automount-open +If properly configured, the output for automount-openshould be false. +To ensure that users cannot enable automount opening in GNOME3, run the following: +$ grep 'automount-open' /etc/dconf/db/local.d/locks/* +If properly configured, the output for automount-open should be /org/gnome/desktop/media-handling/automount-open + Is it the case that GNOME automounting is not disabled? + + + + The runtime status of the net.ipv4.ip_local_port_range kernel parameter can be queried +by running the following command: +$ sysctl net.ipv4.ip_local_port_range +32768 65535. + + Is it the case that the correct value is not returned? + + + + Verify Red Hat Enterprise Linux 9 generates audit records for all account creations, modifications, disabling, and termination events that affect "/etc/sudoers" with the following command: + +$ sudo auditctl -l | grep /etc/sudoers + +-w /etc/sudoers -p wa -k identity + Is it the case that the command does not return a line, or the line is commented out? + + + + Verify the operating system routinely checks the baseline configuration for unauthorized changes. + +To determine that periodic AIDE execution has been scheduled, run the following command: +$ grep aide /etc/crontab +The output should return something similar to the following: +05 4 * * * root /usr/sbin/aide --check + +NOTE: The usage of special cron times, such as @daily or @weekly, is acceptable. + Is it the case that AIDE is not configured to scan periodically? + + + + To ensure the login screen resets after a specified number of failures, +run the following command: +$ grep allowed-failures /etc/dconf/db/distro.d/* +The output should be 3 or less. +To ensure that users cannot change or configure the resets after a specified +number of failures on the login screen, run the following: +$ grep allowed-failures /etc/dconf/db/distro.d/locks/* +If properly configured, the output should be /org/gnome/login-screen/allowed-failures + Is it the case that allowed-failures is not equal to or less than the expected value? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_STRICT_KERNEL_WRX /boot/config.* + + For each kernel installed, a line with value "y" should be returned. + + Is it the case that the kernel was not built with the required value? + + + + +Run the following command to determine if the virt_use_nfs SELinux boolean is disabled: +$ getsebool virt_use_nfs +If properly configured, the output should show the following: +virt_use_nfs --> off + Is it the case that virt_use_nfs is not disabled? + + + + +Run the following command to determine if the ksmtuned_use_nfs SELinux boolean is disabled: +$ getsebool ksmtuned_use_nfs +If properly configured, the output should show the following: +ksmtuned_use_nfs --> off + Is it the case that ksmtuned_use_nfs is not disabled? + + + + Verify Red Hat Enterprise Linux 9 takes the appropriate action when the audit storage volume is full. + +Check that Red Hat Enterprise Linux 9 takes the appropriate action when the audit storage volume is full with the following command: + +$ sudo grep disk_full_action /etc/audit/auditd.conf + +disk_full_action = HALT + +If the value of the "disk_full_action" option is not "SYSLOG", "SINGLE", or "HALT", or the line is commented out, ask the system administrator to indicate how the system takes appropriate action when an audit storage volume is full. + Is it the case that there is no evidence of appropriate action? + + + + Verify that Red Hat Enterprise Linux 9 is configured to audit the execution of the "pt_chown" command with the following command: + +$ sudo auditctl -l | grep pt_chown + +-a always,exit -F path=/usr/libexec/pt_chown -F perm=x -F auid>=1000 -F auid!=unset -k privileged-pt_chown + Is it the case that the command does not return a line, or the line is commented out? + + + + To check the permissions of /etc/passwd-, +run the command: +$ ls -l /etc/passwd- +If properly configured, the output should indicate the following permissions: +-rw-r--r-- + Is it the case that /etc/passwd- does not have unix mode -rw-r--r--? + + + + +Run the following command to determine if the named_write_master_zones SELinux boolean is disabled: +$ getsebool named_write_master_zones +If properly configured, the output should show the following: +named_write_master_zones --> off + Is it the case that named_write_master_zones is not disabled? + + + + The runtime status of the net.ipv4.tcp_rfc1337 kernel parameter can be queried +by running the following command: +$ sysctl net.ipv4.tcp_rfc1337 +1. + + Is it the case that the correct value is not returned? + + + + +Run the following command to determine if the cron_can_relabel SELinux boolean is disabled: +$ getsebool cron_can_relabel +If properly configured, the output should show the following: +cron_can_relabel --> off + Is it the case that cron_can_relabel is not disabled? + + + + To determine if logfile has been configured for sudo, run the following command: +$ sudo grep -ri "^[\s]*Defaults\s*\blogfile\b.*" /etc/sudoers /etc/sudoers.d/ +The command should return a matching output. + Is it the case that logfile is not enabled in sudo? + + + + +Run the following command to determine if the swift_can_network SELinux boolean is disabled: +$ getsebool swift_can_network +If properly configured, the output should show the following: +swift_can_network --> off + Is it the case that swift_can_network is not disabled? + + + + To determine if the system is configured to audit calls to the +removexattr system call, run the following command: +$ sudo grep "removexattr" /etc/audit/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + +Run the following command to determine if the secure_mode SELinux boolean is disabled: +$ getsebool secure_mode +If properly configured, the output should show the following: +secure_mode --> off + Is it the case that secure_mode is not disabled? + + + + Inspect the form of default GRUB 2 command line for the Linux operating system +in /etc/default/grub. If it includes ipv6.disable=1, +then the parameter will be configured for newly installed kernels. +First check if the GRUB recovery is enabled: +$ sudo grep 'GRUB_DISABLE_RECOVERY' /etc/default/grub +If this option is set to true, then check that a line is output by the following command: +$ sudo grep 'GRUB_CMDLINE_LINUX_DEFAULT.*ipv6.disable=1.*' /etc/default/grub +If the recovery is disabled, check the line with +$ sudo grep 'GRUB_CMDLINE_LINUX.*ipv6.disable=1.*' /etc/default/grub.Moreover, command line parameters for currently installed kernels should be checked as well. +Run the following command: +$ sudo grubby --info=ALL | grep args | grep -v 'ipv6.disable=1' +The command should not return any output. + Is it the case that IPv6 is not disabled? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_MODULE_SIG_FORCE /boot/config.* + + For each kernel installed, a line with value "y" should be returned. + + Is it the case that the kernel was not built with the required value? + + + + Verify emergency accounts have been provisioned with an expiration date of 72 hours. + +For every emergency account, run the following command to obtain its account aging and expiration information: + +$ sudo chage -l emergency_account_name + +Verify each of these accounts has an expiration date set within 72 hours or as documented. + Is it the case that any emergency accounts have no expiration date set or do not expire within 72 hours? + + + + Verify Red Hat Enterprise Linux 9 generates audit records for all account creations, modifications, disabling, and termination events that affect "/etc/gshadow" with the following command: + +$ sudo auditctl -l | egrep '(/etc/gshadow)' + +-w /etc/gshadow -p wa -k identity + +If the command does not return a line, or the line is commented out, this is a finding. + Is it the case that the system is not configured to audit account changes? + + + + Inspect the form of default GRUB 2 command line for the Linux operating system +in /etc/default/grub. If it includes spectre_v2=on, +then the parameter will be configured for newly installed kernels. +First check if the GRUB recovery is enabled: +$ sudo grep 'GRUB_DISABLE_RECOVERY' /etc/default/grub +If this option is set to true, then check that a line is output by the following command: +$ sudo grep 'GRUB_CMDLINE_LINUX_DEFAULT.*spectre_v2=on.*' /etc/default/grub +If the recovery is disabled, check the line with +$ sudo grep 'GRUB_CMDLINE_LINUX.*spectre_v2=on.*' /etc/default/grub.Moreover, command line parameters for currently installed kernels should be checked as well. +Run the following command: +$ sudo grubby --info=ALL | grep args | grep -v 'spectre_v2=on' +The command should not return any output. + Is it the case that spectre_v2 mitigation is not enforced? + + + + +Run the following command to determine if the squid_use_tproxy SELinux boolean is disabled: +$ getsebool squid_use_tproxy +If properly configured, the output should show the following: +squid_use_tproxy --> off + Is it the case that squid_use_tproxy is not disabled? + + + + + +Run the following command to determine the current status of the +rsyslog service: +$ sudo systemctl is-active rsyslog +If the service is running, it should return the following: active + Is it the case that the "rsyslog" service is disabled, masked, or not started.? + + + + +Run the following command to determine if the ssh_sysadm_login SELinux boolean is disabled: +$ getsebool ssh_sysadm_login +If properly configured, the output should show the following: +ssh_sysadm_login --> off + Is it the case that ssh_sysadm_login is not disabled? + + + + +Run the following command to determine if the httpd_can_connect_ftp SELinux boolean is disabled: +$ getsebool httpd_can_connect_ftp +If properly configured, the output should show the following: +httpd_can_connect_ftp --> off + Is it the case that httpd_can_connect_ftp is not disabled? + + + + These settings can be verified by running the following: +$ gsettings get org.gnome.desktop.thumbnailers disable-all +If properly configured, the output should be true. +To ensure that users cannot how long until the screensaver locks, run the following: +$ grep disable-all /etc/dconf/db/local.d/locks/* +If properly configured, the output should be /org/gnome/desktop/thumbnailers/disable-all + Is it the case that GNOME thumbnailers are not disabled? + + + + To determine if the system is configured to audit successful calls +to the lsetxattr system call, run the following command: +$ sudo grep "lsetxattr" /etc/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + +Run the following command to determine if the ftpd_use_fusefs SELinux boolean is disabled: +$ getsebool ftpd_use_fusefs +If properly configured, the output should show the following: +ftpd_use_fusefs --> off + Is it the case that ftpd_use_fusefs is not disabled? + + + + To verify that Audit Daemon is configured to resolve all uid, gid, syscall, +architecture, and socket address information before writing the event to disk, +run the following command: +$ sudo grep log_format /etc/audit/auditd.conf +The output should return the following: +log_format = ENRICHED + Is it the case that log_format isn't set to ENRICHED? + + + + Verify Red Hat Enterprise Linux 9 is configured in the system-auth file to prohibit password reuse +for a minimum of generations with the following command: + +$ grep -i remember /etc/pam.d/system-auth +password pam_pwhistory.so use_authtok remember= retry=3 + Is it the case that the line containing "pam_pwhistory.so" does not have the "remember" module argument set, +is commented out, or the value of the "remember" module argument is set to less than +"<sub idref="var_password_pam_remember" />"? + + + + +Run the following command to determine if the mpd_use_nfs SELinux boolean is disabled: +$ getsebool mpd_use_nfs +If properly configured, the output should show the following: +mpd_use_nfs --> off + Is it the case that mpd_use_nfs is not disabled? + + + + Inspect /etc/audit/auditd.conf and locate the following line to +determine if the system is configured correctly: +space_left SIZE_in_MB + Is it the case that the system is not configured a specfic size in MB to notify administrators of an issue? + + + + To determine if the system is configured to audit successful calls +to the fchown system call, run the following command: +$ sudo grep "fchown" /etc/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + To check that the named service is disabled in system boot configuration, +run the following command: +$ sudo systemctl is-enabled named +Output should indicate the named service has either not been installed, +or has been disabled at all runlevels, as shown in the example below: +$ sudo systemctl is-enabled named disabled + +Run the following command to verify named is not active (i.e. not running) through current runtime configuration: +$ sudo systemctl is-active named + +If the service is not running the command will return the following output: +inactive + +The service will also be masked, to check that the named is masked, run the following command: +$ sudo systemctl show named | grep "LoadState\|UnitFileState" + +If the service is masked the command will return the following outputs: + +LoadState=masked + +UnitFileState=masked + Is it the case that the "named" is loaded and not masked? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_IA32_EMULATION /boot/config.* + + Configs with value 'n' are not explicitly set in the file, so either commented lines or no + lines should be returned. + + Is it the case that the kernel was not built with the required value? + + + + The runtime status of the net.ipv6.conf.default.accept_redirects kernel parameter can be queried +by running the following command: +$ sysctl net.ipv6.conf.default.accept_redirects +0. + + Is it the case that the correct value is not returned? + + + + To determine if the system is configured to audit changes to its SELinux +configuration files, run the following command: +$ sudo auditctl -l | grep "dir=/etc/selinux" +If the system is configured to watch for changes to its SELinux +configuration, a line should be returned (including +perm=wa indicating permissions that are watched). + Is it the case that the system is not configured to audit attempts to change the MAC policy? + + + + To check if compression is enabled or set correctly, run the +following command: +$ sudo grep Compression /etc/ssh/sshd_config +If configured properly, output should be no or delayed. + Is it the case that it is commented out, or is not set to no or delayed? + + + + +Run the following command to determine if the httpd_use_fusefs SELinux boolean is disabled: +$ getsebool httpd_use_fusefs +If properly configured, the output should show the following: +httpd_use_fusefs --> off + Is it the case that httpd_use_fusefs is not disabled? + + + + Verify the system commands contained in the following directories are group-owned by "root", or a required system account, with the following command: + +$ sudo find -L /bin /sbin /usr/bin /usr/sbin /usr/local/bin /usr/local/sbin ! -group root -exec ls -l {} \; + Is it the case that any system commands are returned and is not group-owned by a required system account? + + + + +Run the following command to determine if the httpd_setrlimit SELinux boolean is disabled: +$ getsebool httpd_setrlimit +If properly configured, the output should show the following: +httpd_setrlimit --> off + Is it the case that httpd_setrlimit is not disabled? + + + + To check the permissions of /etc/http/conf/*, +run the command: +$ ls -l /etc/http/conf/* +If properly configured, the output should indicate the following permissions: +-rw-r----- + Is it the case that /etc/http/conf/* does not have unix mode -rw-r-----? + + + + Run the following command to determine if the dhcp-server package is installed: +$ rpm -q dhcp-server + Is it the case that the package is installed? + + + + To determine if the system is configured to audit unsuccessful calls +to the chmod system call, run the following command: +$ sudo grep "chmod" /etc/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + To determine whether the SSH server includes configuration files from the right directory, run the following command: +$ sudo grep -i '^Include' /etc/ssh/sshd_config +If a line Include /etc/ssh/sshd_config.d/*.conf is returned, then the configuration file inclusion is set correctly. + Is it the case that you don't include other configuration files from the main configuration file? + + + + Run the following command to see what the timeout interval is: +$ sudo grep ClientAliveInterval /etc/ssh/sshd_config +If properly configured, the output should be: +ClientAliveInterval + Is it the case that it is commented out or not configured properly? + + + + To determine if the system is configured to audit successful calls +to the open_by_handle_at system call, run the following command: +$ sudo grep "open_by_handle_at" /etc/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + +Run the following command to determine if the virt_read_qemu_ga_data SELinux boolean is disabled: +$ getsebool virt_read_qemu_ga_data +If properly configured, the output should show the following: +virt_read_qemu_ga_data --> off + Is it the case that virt_read_qemu_ga_data is not disabled? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_STACKPROTECTOR_STRONG /boot/config.* + + For each kernel installed, a line with value "y" should be returned. + + Is it the case that the kernel was not built with the required value? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_GCC_PLUGIN_STRUCTLEAK_BYREF_ALL /boot/config.* + + For each kernel installed, a line with value "y" should be returned. + + Is it the case that the kernel was not built with the required value? + + + + The ypbind package can be removed with the following command: $ sudo dnf erase ypbind + Is it the case that ? + + + + + +Run the following command to determine the current status of the +ufw service: +$ sudo systemctl is-active ufw +If the service is running, it should return the following: active + Is it the case that the service is not enabled? + + + + To verify that execution of the command is being audited, run the following command: +$ sudo grep "path=/usr/sbin/seunshare" /etc/audit/audit.rules /etc/audit/rules.d/* +The output should return something similar to: +-a always,exit -F path=/usr/sbin/seunshare -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged + Is it the case that ? + + + + Verify the audit logs are owned by "root". First, determine where the audit logs are stored with the following command: +$ sudo grep -iw log_file /etc/audit/auditd.conf +log_file = /var/log/audit/audit.log +Using the location of the audit log file, determine if the audit log is owned by "root" using the following command: +$ sudo stat -c "%n %U" /var/log/audit/audit.log +Audit logs must be owned by user root. +If the log_file isn't defined in /etc/audit/auditd.conf, check all files in /var/log/audit/ directory instead. + Is it the case that the audit log is not owned by root? + + + + The runtime status of the net.ipv4.tcp_syncookies kernel parameter can be queried +by running the following command: +$ sysctl net.ipv4.tcp_syncookies +1. + + Is it the case that the correct value is not returned? + + + + Verify that Red Hat Enterprise Linux 9 is configured to audit the execution of the "mount" command with the following command: + +$ sudo auditctl -l | grep mount + +-a always,exit -F path=/usr/bin/mount -F perm=x -F auid>=1000 -F auid!=unset -k privileged-mount + Is it the case that the command does not return a line, or the line is commented out? + + + + Verify Red Hat Enterprise Linux 9 disables storing core dumps for all users by issuing the following command: + +$ grep -i storage /etc/systemd/coredump.conf + +Storage=none + Is it the case that Storage is not set to none or is commented out and the need for core dumps is not documented with the Information System Security Officer (ISSO) as an operational requirement for all domains that have the "core" item assigned? + + + + +Run the following command to determine if the virt_sandbox_use_sys_admin SELinux boolean is disabled: +$ getsebool virt_sandbox_use_sys_admin +If properly configured, the output should show the following: +virt_sandbox_use_sys_admin --> off + Is it the case that virt_sandbox_use_sys_admin is not disabled? + + + + To determine if the system is configured to audit calls to the +lchown system call, run the following command: +$ sudo grep "lchown" /etc/audit/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + To check that the nfs-server service is disabled in system boot configuration, +run the following command: +$ sudo systemctl is-enabled nfs-server +Output should indicate the nfs-server service has either not been installed, +or has been disabled at all runlevels, as shown in the example below: +$ sudo systemctl is-enabled nfs-server disabled + +Run the following command to verify nfs-server is not active (i.e. not running) through current runtime configuration: +$ sudo systemctl is-active nfs-server + +If the service is not running the command will return the following output: +inactive + +The service will also be masked, to check that the nfs-server is masked, run the following command: +$ sudo systemctl show nfs-server | grep "LoadState\|UnitFileState" + +If the service is masked the command will return the following outputs: + +LoadState=masked + +UnitFileState=masked + Is it the case that the "nfs-server" is loaded and not masked? + + + + +Run the following command to determine if the staff_exec_content SELinux boolean is enabled: +$ getsebool staff_exec_content +If properly configured, the output should show the following: +staff_exec_content --> on + Is it the case that staff_exec_content is not enabled? + + + + +Run the following command to determine if the virt_sandbox_use_audit SELinux boolean is enabled: +$ getsebool virt_sandbox_use_audit +If properly configured, the output should show the following: +virt_sandbox_use_audit --> on + Is it the case that virt_sandbox_use_audit is not enabled? + + + + Inspect the form of default GRUB 2 command line for the Linux operating system +in /etc/default/grub. If it includes slub_debug=, +then the parameter will be configured for newly installed kernels. +First check if the GRUB recovery is enabled: +$ sudo grep 'GRUB_DISABLE_RECOVERY' /etc/default/grub +If this option is set to true, then check that a line is output by the following command: +$ sudo grep 'GRUB_CMDLINE_LINUX_DEFAULT.*slub_debug=.*' /etc/default/grub +If the recovery is disabled, check the line with +$ sudo grep 'GRUB_CMDLINE_LINUX.*slub_debug=.*' /etc/default/grub.Moreover, command line parameters for currently installed kernels should be checked as well. +Run the following command: +$ sudo grubby --info=ALL | grep args | grep -v 'slub_debug=' +The command should not return any output. + Is it the case that SLUB/SLAB poisoning is not enabled? + + + + Verify the nosuid option is configured for the /var/log mount point, + run the following command: + $ sudo mount | grep '\s/var/log\s' + . . . /var/log . . . nosuid . . . + + Is it the case that the "/var/log" file system does not have the "nosuid" option set? + + + + Inspect the form of default GRUB 2 command line for the Linux operating system +in /etc/default/grub. If it includes page_poison=1, +then the parameter will be configured for newly installed kernels. +First check if the GRUB recovery is enabled: +$ sudo grep 'GRUB_DISABLE_RECOVERY' /etc/default/grub +If this option is set to true, then check that a line is output by the following command: +$ sudo grep 'GRUB_CMDLINE_LINUX_DEFAULT.*page_poison=1.*' /etc/default/grub +If the recovery is disabled, check the line with +$ sudo grep 'GRUB_CMDLINE_LINUX.*page_poison=1.*' /etc/default/grub.Moreover, command line parameters for currently installed kernels should be checked as well. +Run the following command: +$ sudo grubby --info=ALL | grep args | grep -v 'page_poison=1' +The command should not return any output. + Is it the case that page allocator poisoning is not enabled? + + + + To verify that null passwords cannot be used, run the following command: +$ sudo awk -F: '!$2 {print $1}' /etc/shadow +If this produces any output, it may be possible to log into accounts +with empty passwords. + Is it the case that Blank or NULL passwords can be used? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_PAGE_POISONING_ZERO /boot/config.* + + For each kernel installed, a line with value "y" should be returned. + + Is it the case that the kernel was not built with the required value? + + + + +Run the following command to determine if the user_exec_content SELinux boolean is enabled: +$ getsebool user_exec_content +If properly configured, the output should show the following: +user_exec_content --> on + Is it the case that user_exec_content is not enabled? + + + + To check the permissions of /var/log, +run the command: +$ ls -l /var/log +If properly configured, the output should indicate the following permissions: +drwxr-xr-x + Is it the case that /var/log does not have unix mode drwxr-xr-x? + + + + Verify that Red Hat Enterprise Linux 9 is configured to audit the execution of the "kmod" command with the following command: + +$ sudo auditctl -l | grep kmod + +-a always,exit -F path=/usr/bin/kmod -F perm=x -F auid>=1000 -F auid!=unset -k privileged-kmod + Is it the case that the command does not return a line, or the line is commented out? + + + + To verify that automatic logins are disabled, run the following command: +$ grep -Pzoi "^\[daemon]\\nautomaticlogin.*" /etc/gdm/custom.conf +The output should show the following: +[daemon] +AutomaticLoginEnable=false + Is it the case that GDM allows users to automatically login? + + + + To determine if the system is configured to audit unsuccessful calls +to the fchmodat system call, run the following command: +$ sudo grep "fchmodat" /etc/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + To check the permissions of /etc/shadow, +run the command: +$ ls -l /etc/shadow +If properly configured, the output should indicate the following permissions: +---------- + Is it the case that /etc/shadow does not have unix mode ----------? + + + + Using a non-privileged account, verify that users cannot modify or change +network settings with the nmcli command with the following command: +$ nmcli general permissions +The output should contain the following: +PERMISSION VALUE +org.freedesktop.NetworkManager.enable-disable-network auth +org.freedesktop.NetworkManager.enable-disable-wifi auth +org.freedesktop.NetworkManager.enable-disable-wwan auth +org.freedesktop.NetworkManager.enable-disable-wimax auth +org.freedesktop.NetworkManager.sleep-wake auth +org.freedesktop.NetworkManager.network-control auth +org.freedesktop.NetworkManager.wifi.share.protected auth +org.freedesktop.NetworkManager.wifi.share.open auth +org.freedesktop.NetworkManager.settings.modify.system auth +org.freedesktop.NetworkManager.settings.modify.own auth +org.freedesktop.NetworkManager.settings.modify.hostname auth +org.freedesktop.NetworkManager.settings.modify.global-dns auth +org.freedesktop.NetworkManager.reload auth +org.freedesktop.NetworkManager.checkpoint-rollback auth +org.freedesktop.NetworkManager.enable-disable-statistics auth +org.freedesktop.NetworkManager.enable-disable-connectivity-check auth +org.freedesktop.NetworkManager.wifi.scan auth + + Is it the case that non-privileged users can modify or change network settings? + + + + +Run the following command to determine if the xend_run_blktap SELinux boolean is enabled: +$ getsebool xend_run_blktap +If properly configured, the output should show the following: +xend_run_blktap --> on + Is it the case that xend_run_blktap is not enabled? + + + + Verify Red Hat Enterprise Linux 9 takes the appropriate action when an audit processing failure occurs. + +Check that Red Hat Enterprise Linux 9 takes the appropriate action when an audit processing failure occurs with the following command: + +$ sudo grep disk_error_action /etc/audit/auditd.conf + +disk_error_action = HALT + +If the value of the "disk_error_action" option is not "SYSLOG", "SINGLE", or "HALT", or the line is commented out, ask the system administrator to indicate how the system takes appropriate action when an audit process failure occurs. + Is it the case that there is no evidence of appropriate action? + + + + Verify the "/etc/security/faillock.conf" file is configured use a non-default faillock directory to ensure contents persist after reboot: + +$ sudo grep 'dir =' /etc/security/faillock.conf + +dir = /var/log/faillock + Is it the case that the "dir" option is not set to a non-default documented tally log directory, is missing or commented out? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_HIBERNATION /boot/config.* + + Configs with value 'n' are not explicitly set in the file, so either commented lines or no + lines should be returned. + + Is it the case that the kernel was not built with the required value? + + + + To verify that the Audit is correctly configured according to recommended rules, check the content of the file with the following command: +cat /etc/audit/rules.d/30-ospp-v42-5-perm-change-success.rules +The output has to be exactly as follows: +## Successful permission change +-a always,exit -F arch=b32 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change +-a always,exit -F arch=b64 -S fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change + Is it the case that the file does not exist or the content differs? + + + + Run the following command to see what the max sessions number is: +$ sudo grep MaxSessions /etc/ssh/sshd_config +If properly configured, the output should be: +MaxSessions + Is it the case that MaxSessions is not configured or not configured correctly? + + + + To verify that the system will shutdown when auditd fails, +run the following command: +$ sudo grep "\-f 2" /etc/audit/audit.rules +The output should contain: +-f 2 + Is it the case that the system is not configured to shutdown on auditd failures? + + + + To determine if the system is configured to audit successful calls +to the fchmodat system call, run the following command: +$ sudo grep "fchmodat" /etc/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + Verify the operating system authenticates the remote logging server for off-loading audit logs with the following command: + +$ sudo grep -i '$ActionSendStreamDriverAuthMode' /etc/rsyslog.conf /etc/rsyslog.d/*.conf +The output should be +$/etc/rsyslog.conf:$ActionSendStreamDriverAuthMode x509/name + Is it the case that $ActionSendStreamDriverAuthMode in /etc/rsyslog.conf is not set to x509/name? + + + + Verify the system commands contained in the following directories are owned by "root" with the following command: + +$ sudo find -L /bin /sbin /usr/bin /usr/sbin /usr/libexec /usr/local/bin /usr/local/sbin ! -user root -exec ls -l {} \; + Is it the case that any system commands are found to not be owned by root? + + + + To verify the assigned home directory of all interactive users on the system +exist, run the following command: +$ sudo pwck -r + Is it the case that users home directory does not exist? + + + + +Run the following command to determine if the global_ssp SELinux boolean is disabled: +$ getsebool global_ssp +If properly configured, the output should show the following: +global_ssp --> off + Is it the case that global_ssp is not disabled? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_KEXEC /boot/config.* + + Configs with value 'n' are not explicitly set in the file, so either commented lines or no + lines should be returned. + + Is it the case that the kernel was not built with the required value? + + + + Verify the UMASK setting is configured correctly in the /etc/login.defs file by +running the following command: +$ sudo grep "UMASK" /etc/login.defs +All output must show the value of UMASK set as shown in the below: +UMASK + Is it the case that the above command returns no output, or the umask is configured incorrectly? + + + + Verify Red Hat Enterprise Linux 9 limits the number of concurrent sessions to +"" for all +accounts and/or account types with the following command: +$ grep -r -s maxlogins /etc/security/limits.conf /etc/security/limits.d/*.conf +/etc/security/limits.conf:* hard maxlogins 10 +This can be set as a global domain (with the * wildcard) but may be set differently for multiple domains. + Is it the case that the "maxlogins" item is missing, commented out, or the value is set greater +than "<sub idref="var_accounts_max_concurrent_login_sessions" />" and +is not documented with the Information System Security Officer (ISSO) as an +operational requirement for all domains that have the "maxlogins" item +assigned'? + + + + To determine how the SSH daemon's PermitRootLogin option is set, run the following command: + +$ sudo grep -i PermitRootLogin /etc/ssh/sshd_config.d/00-complianceascode-hardening.conf + + +If a line indicating prohibit-password is returned, then the required value is set. + Is it the case that it is commented out or not configured properly? + + + + To verify the boot loader superuser account has been set, run the following +command: +sudo grep -A1 "superusers" /boot/grub2/grub.cfg +The output should show the following: +set superusers="superusers-account" +export superusers +where superusers-account is the actual account name different from common names like root, +admin, or administrator and different from any other existing user name. + Is it the case that superuser account is not set or is set to an existing name or to a common name? + + + + Verify file systems that are used for removable media are mounted with the "nosuid" option with the following command: + +$ sudo more /etc/fstab + +UUID=2bc871e4-e2a3-4f29-9ece-3be60c835222 /mnt/usbflash vfat noauto,owner,ro,nosuid,nodev,noexec 0 0 + Is it the case that file system found in "/etc/fstab" refers to removable media and it does not have the "nosuid" option set? + + + + The runtime status of the kernel.perf_cpu_time_max_percent kernel parameter can be queried +by running the following command: +$ sysctl kernel.perf_cpu_time_max_percent +1. + + Is it the case that the correct value is not returned? + + + + Run the following command to determine if the talk-server package is installed: +$ rpm -q talk-server + Is it the case that the package is installed? + + + + +To properly set the group owner of /etc/audit/, run the command: +$ sudo chgrp root /etc/audit/ + +To properly set the group owner of /etc/audit/rules.d/, run the command: +$ sudo chgrp root /etc/audit/rules.d/ + Is it the case that ? + + + + +Run the following command to determine if the xserver_execmem SELinux boolean is disabled: +$ getsebool xserver_execmem +If properly configured, the output should show the following: +xserver_execmem --> off + Is it the case that xserver_execmem is not disabled? + + + + To verify that Audit Daemon is configured to include local events, run the +following command: +$ sudo grep local_events /etc/audit/auditd.conf +The output should return the following: +local_events = yes + Is it the case that local_events isn't set to yes? + + + + To determine how the SSH daemon's PermitRootLogin option is set, run the following command: + +$ sudo grep -i PermitRootLogin /etc/ssh/sshd_config.d/00-complianceascode-hardening.conf + + +If a line indicating no is returned, then the required value is set. + + Is it the case that the required value is not set? + + + + To verify the nodev option is configured for non-root local partitions, run the following command: +$ sudo mount | grep '^/dev\S* on /\S' | grep --invert-match 'nodev' +The output shows local non-root partitions mounted without the nodev option, and there should be no output at all. + + Is it the case that some mounts appear among output lines? + + + + To determine how the SSH daemon's GSSAPIAuthentication option is set, run the following command: + +$ sudo grep -i GSSAPIAuthentication /etc/ssh/sshd_config.d/00-complianceascode-hardening.conf + + +If a line indicating yes is returned, then the required value is set. + + Is it the case that the required value is not set? + + + + Verify Red Hat Enterprise Linux 9 generates an audit record for unsuccessful attempts to modify files using the open_by_handle_at system call with O_TRUNC_WRITE flag. + +If the auditd daemon is configured to use the "augenrules" program to read audit rules during daemon startup (the default), run the following command: + +$ sudo grep -r open_by_handle_at /etc/audit/rules.d + +If the auditd daemon is configured to use the "auditctl" utility to read audit rules during daemon startup, run the following command: + +$ sudo grep open_by_handle_at /etc/audit/audit.rules + +The output should be the following: + +-a always,exit -F arch=b32 -S open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b32 -S open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccesful-create +-a always,exit -F arch=b64 -S open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccesful-create + Is it the case that the command does not return a line, or the line is commented out? + + + + +Run the following command to determine if the spamassassin_can_network SELinux boolean is disabled: +$ getsebool spamassassin_can_network +If properly configured, the output should show the following: +spamassassin_can_network --> off + Is it the case that spamassassin_can_network is not disabled? + + + + Verify file systems that are used for removable media are mounted with the "nodev" option with the following command: + +$ sudo more /etc/fstab + +UUID=2bc871e4-e2a3-4f29-9ece-3be60c835222 /mnt/usbflash vfat noauto,owner,ro,nosuid,nodev,noexec 0 0 + Is it the case that a file system found in "/etc/fstab" refers to removable media and it does not have the "nodev" option set? + + + + To check the permissions of /etc/audit/auditd.conf, +run the command: +$ ls -l /etc/audit/auditd.conf +If properly configured, the output should indicate the following permissions: +-rw-r----- + Is it the case that /etc/audit/auditd.conf does not have unix mode -rw-r-----? + + + + To check the group ownership of /etc/passwd-, +run the command: +$ ls -lL /etc/passwd- +If properly configured, the output should indicate the following group-owner: +root + Is it the case that /etc/passwd- does not have a group owner of root? + + + + The runtime status of the kernel.core_uses_pid kernel parameter can be queried +by running the following command: +$ sysctl kernel.core_uses_pid +0. + Is it the case that the returned line does not have a value of 0? + + + + To ensure that users cannot change session idle and lock settings, run the following: +$ grep 'idle-delay' /etc/dconf/db/local.d/locks/* +If properly configured, the output should return: +/org/gnome/desktop/session/idle-delay + Is it the case that idle-delay is not locked? + + + + Verify Red Hat Enterprise Linux 9 is securely comparing internal information system clocks at a regular interval with an NTP server with the following command: +$ sudo grep maxpoll /etc/ntp.conf /etc/chrony.conf +server [ntp.server.name] iburst maxpoll . + Is it the case that "maxpoll" has not been set to the value of "<sub idref="var_time_service_set_maxpoll" />", is commented out, or is missing? + + + + +Run the following command to determine if the cdrecord_read_content SELinux boolean is disabled: +$ getsebool cdrecord_read_content +If properly configured, the output should show the following: +cdrecord_read_content --> off + Is it the case that cdrecord_read_content is not disabled? + + + + To verify that the installed operating system is supported or certified, run +the following command: + +The output should contain something similar to: +Red Hat Enterprise Linux 9 + Is it the case that the installed operating system is not FIPS 140-2 certified? + + + + To check the ownership of /etc/cron.monthly, +run the command: +$ ls -lL /etc/cron.monthly +If properly configured, the output should indicate the following owner: +root + Is it the case that /etc/cron.monthly does not have an owner of root? + + + + Run the following command to Verify that the sudoers security policy is configured to use the invoking user's password for privilege escalation: + sudo cvtsudoers -f sudoers /etc/sudoers | grep -E '^Defaults !?(rootpw|targetpw|runaspw)' +or if cvtsudoers not supported: + sudo find /etc/sudoers /etc/sudoers.d \( \! -name '*~' -a \! -name '*.*' \) -exec grep -E --with-filename '^[[:blank:]]*Defaults[[:blank:]](.*[[:blank:]])?!?\b(rootpw|targetpw|runaspw)' -- {} \; +If no results are returned, this is a finding. +If conflicting results are returned, this is a finding. +If "Defaults !targetpw" is not defined, this is a finding. +If "Defaults !rootpw" is not defined, this is a finding. +If "Defaults !runaspw" is not defined, this is a finding. + Is it the case that invoke user passwd when using sudo? + + + + To verify that binaries cannot be directly executed from removable media, run the following command: +$ grep -v noexec /etc/fstab +The resulting output will show partitions which do not have the noexec flag. Verify all partitions +in the output are not removable media. + Is it the case that removable media partitions are present? + + + + To determine if the system is configured to audit calls to the +adjtimex system call, run the following command: +$ sudo grep "adjtimex" /etc/audit/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + To determine how the SSH daemon's IgnoreRhosts option is set, run the following command: + +$ sudo grep -i IgnoreRhosts /etc/ssh/sshd_config.d/00-complianceascode-hardening.conf +$ sudo grep -i IgnoreRhosts /etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf + +If a line indicating yes is returned, then the required value is set. + + Is it the case that the required value is not set? + + + + To verify that the Audit is correctly configured according to recommended rules, check the content of the file with the following command: +cat /etc/audit/rules.d/30-ospp-v42-2-modify-failed.rules +The output has to be exactly as follows: +## Unsuccessful file modifications (open for write or truncate) +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b32 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b32 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification + Is it the case that the file does not exist or the content differs? + + + + To check the status of the idle screen lock activation, run the following command: + +$ gsettings get org.gnome.desktop.screensaver lock-enabled +If properly configured, the output should be true. +To ensure that users cannot change how long until the screensaver locks, run the following: +$ grep lock-enabled /etc/dconf/db/local.d/locks/* +If properly configured, the output for lock-enabled should be /org/gnome/desktop/screensaver/lock-enabled + Is it the case that screensaver locking is not enabled and/or has not been set or configured correctly? + + + + To determine how the SSH daemon's X11Forwarding option is set, run the following command: + +$ sudo grep -i X11Forwarding /etc/ssh/sshd_config.d/00-complianceascode-hardening.conf + + +If a line indicating yes is returned, then the required value is set. + + Is it the case that the required value is not set? + + + + To verify the audispd plugin off-loads audit records onto a different system or +media from the system being audited, run the following command: +$ sudo grep -i remote_server /etc/audit/audisp-remote.conf +The output should return something similar to +remote_server = + Is it the case that audispd is not sending logs to a remote system? + + + + +Run the following command to determine if the telepathy_tcp_connect_generic_network_ports SELinux boolean is disabled: +$ getsebool telepathy_tcp_connect_generic_network_ports +If properly configured, the output should show the following: +telepathy_tcp_connect_generic_network_ports --> off + Is it the case that telepathy_tcp_connect_generic_network_ports is not disabled? + + + + Verify that Red Hat Enterprise Linux 9 is configured to audit the execution of the "su" command with the following command: + +$ sudo auditctl -l | grep su + +-a always,exit -F path=/usr/bin/su -F perm=x -F auid>=1000 -F auid!=unset -k privileged-su + Is it the case that the command does not return a line, or the line is commented out? + + + + The runtime status of the net.ipv4.conf.all.route_localnet kernel parameter can be queried +by running the following command: +$ sysctl net.ipv4.conf.all.route_localnet +0. + + Is it the case that the correct value is not returned? + + + + To verify that is configured +as the smart card driver, run the following command: +$ grep force_card_driver /etc/opensc.conf +The output should return something similar to: +force_card_drivers = ; + Is it the case that the smart card driver is not configured correctly? + + + + +Run the following command to determine if the secadm_exec_content SELinux boolean is enabled: +$ getsebool secadm_exec_content +If properly configured, the output should show the following: +secadm_exec_content --> on + Is it the case that secadm_exec_content is not enabled? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_FORTIFY_SOURCE /boot/config.* + + For each kernel installed, a line with value "y" should be returned. + + Is it the case that the kernel was not built with the required value? + + + + To check that the ypserv service is disabled in system boot configuration, +run the following command: +$ sudo systemctl is-enabled ypserv +Output should indicate the ypserv service has either not been installed, +or has been disabled at all runlevels, as shown in the example below: +$ sudo systemctl is-enabled ypserv disabled + +Run the following command to verify ypserv is not active (i.e. not running) through current runtime configuration: +$ sudo systemctl is-active ypserv + +If the service is not running the command will return the following output: +inactive + +The service will also be masked, to check that the ypserv is masked, run the following command: +$ sudo systemctl show ypserv | grep "LoadState\|UnitFileState" + +If the service is masked the command will return the following outputs: + +LoadState=masked + +UnitFileState=masked + Is it the case that the "ypserv" is loaded and not masked? + + + + To verify that the Audit is correctly configured according to recommended rules, check the content of the file with the following command: +cat /etc/audit/rules.d/30-ospp-v42-6-owner-change-success.rules +The output has to be exactly as follows: +## Successful ownership change +-a always,exit -F arch=b64 -S lchown,fchown,chown,fchownat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-owner-change + Is it the case that the file does not exist or the content differs? + + + + To verify that rsyslog's Forwarding Output Module has CA certificate +configured for its TLS connections to remote server, run the following command: +$ grep DefaultNetstreamDriverCAFile /etc/rsyslog.conf /etc/rsyslog.d/*.conf +The output should include record similar to +global(DefaultNetstreamDriverCAFile="/etc/pki/tls/cert.pem") +where the path to the CA file (/etc/pki/tls/cert.pem in case above) must point to the correct CA certificate. + Is it the case that CA certificate for rsyslog remote logging via TLS is not set? + + + + +Run the following command to determine if the virt_sandbox_use_mknod SELinux boolean is disabled: +$ getsebool virt_sandbox_use_mknod +If properly configured, the output should show the following: +virt_sandbox_use_mknod --> off + Is it the case that virt_sandbox_use_mknod is not disabled? + + + + To check if RekeyLimit is set correctly, run the following command: +$ sudo grep RekeyLimit /etc/ssh/ssh_config.d/*.conf +If configured properly, output should be +/etc/ssh/ssh_config.d/02-rekey-limit.conf: +RekeyLimit +Check also the main configuration file with the following command: +$ sudo grep RekeyLimit /etc/ssh/ssh_config +The command should not return any output. + Is it the case that it is commented out or is not set? + + + + To find world-writable directories that lack the sticky bit, run the following command: +$ sudo find / -type d \( -perm -0002 -a ! -perm -1000 \) -print 2>/dev/null + Is it the case that any world-writable directories are missing the sticky bit? + + + + Run the following command to determine if the policycoreutils package is installed: $ rpm -q policycoreutils + Is it the case that the policycoreutils package is not installed? + + + + To check the permissions of /var/log/syslog, +run the command: +$ ls -l /var/log/syslog +If properly configured, the output should indicate the following permissions: +-rw-r----- + Is it the case that /var/log/syslog does not have unix mode -rw-r-----? + + + + Verify the noauto option is configured for the /boot mount point, + run the following command: + $ sudo mount | grep '\s/boot\s' + . . . /boot . . . noauto . . . + + Is it the case that the "/boot" file system does not have the "noauto" option set? + + + + +Determine the audit log group by running the following command: + +$ sudo grep -P '^[ ]*log_group[ ]+=.*$' /etc/audit/auditd.conf + +Then, check that all directories within the /var/log/audit directory are owned by the group specified as log_group or by root if the log_group is not specified. +Run the following command: + +$ sudo find /var/log/audit -type d -printf "%p %g\n" + +All listed directories must be owned by the log_group or by root if the log_group is not specified. + Is it the case that there is a directory owned by different group? + + + + Run the following command to determine if the freeradius package is installed: $ rpm -q freeradius + Is it the case that the package is installed? + + + + +Run the following command to determine if the virt_rw_qemu_ga_data SELinux boolean is disabled: +$ getsebool virt_rw_qemu_ga_data +If properly configured, the output should show the following: +virt_rw_qemu_ga_data --> off + Is it the case that virt_rw_qemu_ga_data is not disabled? + + + + +Run the following command to determine if the httpd_mod_auth_ntlm_winbind SELinux boolean is disabled: +$ getsebool httpd_mod_auth_ntlm_winbind +If properly configured, the output should show the following: +httpd_mod_auth_ntlm_winbind --> off + Is it the case that httpd_mod_auth_ntlm_winbind is not disabled? + + + + To determine if the system is configured to audit calls to the +rmdir system call, run the following command: +$ sudo grep "rmdir" /etc/audit/audit.* +If the system is configured to audit this activity, it will return a line. +To determine if the system is configured to audit calls to the +unlink system call, run the following command: +$ sudo grep "unlink" /etc/audit/audit.* +If the system is configured to audit this activity, it will return a line. +To determine if the system is configured to audit calls to the +unlinkat system call, run the following command: +$ sudo grep "unlinkat" /etc/audit/audit.* +If the system is configured to audit this activity, it will return a line. +To determine if the system is configured to audit calls to the +rename system call, run the following command: +$ sudo grep "rename" /etc/audit/audit.* +If the system is configured to audit this activity, it will return a line. +To determine if the system is configured to audit calls to the +renameat system call, run the following command: +$ sudo grep "renameat" /etc/audit/audit.* +If the system is configured to audit this activity, it will return a line. + Is it the case that no line is returned? + + + + To determine that AIDE is configured for FIPS 140-2 file hashing, run the following command: +$ grep sha512 /etc/aide.conf +Verify that the sha512 option is added to the correct ruleset. + Is it the case that the sha512 option is missing or not added to the correct ruleset? + + + + Verify that temporary accounts have been provisioned with an expiration date +of 72 hours. For every temporary account, run the following command to +obtain its account aging and expiration information: +$ sudo chage -l temporary_account_name +Verify each of these accounts has an expiration date set within 72 hours or +as documented. + Is it the case that any temporary accounts have no expiration date set or do not expire within 72 hours? + + + + To ensure LoginGraceTime is set correctly, run the following command: +$ sudo grep LoginGraceTime /etc/ssh/sshd_config +If properly configured, the output should be: +LoginGraceTime +If the option is set to a number greater than 0, then the unauthenticated session will be disconnected +after the configured number seconds. + Is it the case that it is commented out or not configured properly? + + + + To verify that auditing of privileged command use is configured, run the +following command: +$ sudo grep newgidmap /etc/audit/audit.rules /etc/audit/rules.d/* +It should return a relevant line in the audit rules. + Is it the case that the command does not return a line, or the line is commented out? + + + + +Run the following command to determine if the nfs_export_all_rw SELinux boolean is enabled: +$ getsebool nfs_export_all_rw +If properly configured, the output should show the following: +nfs_export_all_rw --> on + Is it the case that nfs_export_all_rw is not enabled? + + + + To ensure root may not directly login to the system over physical consoles, +run the following command: +cat /etc/securetty +If any output is returned, this is a finding. + Is it the case that the /etc/securetty file is not empty? + + + + Verify that the Red Hat Enterprise Linux 9 "auditd" service is configured to notify the SA and ISSO in the event of an audit processing failure. +Inspect /etc/audit/auditd.conf and locate the following line to +determine if the system is configured to send email to an +account when it needs to notify an administrator: +action_mail_acct = + Is it the case that auditd is not configured to send emails per identified actions? + + + + +Run the following command to determine if the mmap_low_allowed SELinux boolean is disabled: +$ getsebool mmap_low_allowed +If properly configured, the output should show the following: +mmap_low_allowed --> off + Is it the case that mmap_low_allowed is not disabled? + + + + To find world-writable files, run the following command: +$ sudo find / -xdev -type f -perm -002 + Is it the case that there is output? + + + + +Run the following command to determine if the sge_use_nfs SELinux boolean is disabled: +$ getsebool sge_use_nfs +If properly configured, the output should show the following: +sge_use_nfs --> off + Is it the case that sge_use_nfs is not disabled? + + + + Run the following command to determine if the subscription-manager package is installed: $ rpm -q subscription-manager + Is it the case that the package is not installed? + + + + To ensure a login warning banner is enabled, run the following: +$ grep banner-message-enable /etc/dconf/db/distro.d/* +If properly configured, the output should be true. +To ensure a login warning banner is locked and cannot be changed by a user, run the following: +$ grep banner-message-enable /etc/dconf/db/distro.d/locks/* +If properly configured, the output should be /org/gnome/login-screen/banner-message-enable. + Is it the case that it is not? + + + + +Run the following command to determine if the authlogin_nsswitch_use_ldap SELinux boolean is disabled: +$ getsebool authlogin_nsswitch_use_ldap +If properly configured, the output should show the following: +authlogin_nsswitch_use_ldap --> off + Is it the case that authlogin_nsswitch_use_ldap is not disabled? + + + + To determine how the SSH daemon's AllowTcpForwarding option is set, run the following command: + +$ sudo grep -i AllowTcpForwarding /etc/ssh/sshd_config.d/00-complianceascode-hardening.conf + + +If a line indicating no is returned, then the required value is set. + Is it the case that The AllowTcpForwarding option exists and is disabled? + + + + + +Run the following command to determine the current status of the +crond service: +$ sudo systemctl is-active crond +If the service is running, it should return the following: active + Is it the case that ? + + + + Inspect /etc/default/grub for any instances of +systemd.confirm_spawn=(1|yes|true|on) in the kernel boot arguments. +Presence of a systemd.confirm_spawn=(1|yes|true|on) indicates +that interactive boot is enabled at boot time and verify that +GRUB_DISABLE_RECOVERY=true to disable recovery boot. + Is it the case that Interactive boot is enabled at boot time? + + + + Verify that Red Hat Enterprise Linux 9 is configured to audit the execution of the "ssh-agent" command with the following command: + +$ sudo auditctl -l | grep ssh-agent + +-a always,exit -F path=/usr/bin/ssh-agent -F perm=x -F auid>=1000 -F auid!=unset -k privileged-ssh-agent + Is it the case that the command does not return a line, or the line is commented out? + + + + To verify if password complexities are only enforce on local users, run the following command: +$ grep local_users_only /etc/security/pwquality.conf +The output should return local_users_only uncommented. + Is it the case that local_users_only is not uncommented or configured correctly? + + + + To determine if the system is configured to audit calls to the +lremovexattr system call, run the following command: +$ sudo grep "lremovexattr" /etc/audit/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + Verify the NX (no-execution) bit flag is set on the system. + +Check that the no-execution bit flag is set with the following commands: + +$ sudo dmesg | grep NX + +[ 0.000000] NX (Execute Disable) protection: active + +If "dmesg" does not show "NX (Execute Disable) protection" active, check the cpuinfo settings with the following command: + +$ sudo grep flags /proc/cpuinfo +flags : fpu vme de pse tsc ms nx rdtscp lm constant_ts + +The output should contain the "nx" flag. + +Then, verify that there are no log messsages stating that NX is disabled in the system log. Run the following command: +$ sudo grep -P "^.+protection: disabled.+" /var/log/dmesg +The output should be empty. + +Then, check that NX is not disabled in the kernel command line. +$ sudo grep -P ".+noexec[0-9]*=off.+" /proc/cmdline +The output should be empty. + Is it the case that NX is disabled? + + + + Inspect /etc/audit/auditd.conf and locate the following line to +determine if the system is configured to synchronize audit event data +with the log files on the disk: +$ sudo grep flush /etc/audit/auditd.conf +flush = DATA +Acceptable values are DATA, and SYNC. The setting is +case-insensitive. + Is it the case that auditd is not configured to synchronously write audit event data to disk? + + + + Verify the system-wide shared library files are owned by "root" with the following command: + +$ sudo find -L /lib /lib64 /usr/lib /usr/lib64 ! -user root -exec ls -l {} \; + Is it the case that any system wide shared library file is not owned by root? + + + + Inspect /proc/cmdline for any instances of selinux=0 +in the kernel boot arguments. Presence of selinux=0 indicates +that SELinux is disabled at boot time. + +If it would be disabled anywhere, make sure to enable it via a +MachineConfig object. + Is it the case that SELinux is disabled at boot time? + + + + + +Run the following command to determine the current status of the +pcscd service: +$ sudo systemctl is-active pcscd +If the service is running, it should return the following: active + Is it the case that the pcscd service is not enabled? + + + + To determine if the system is configured to audit successful calls +to the fsetxattr system call, run the following command: +$ sudo grep "fsetxattr" /etc/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + +Run the following command to determine if the ftpd_use_nfs SELinux boolean is disabled: +$ getsebool ftpd_use_nfs +If properly configured, the output should show the following: +ftpd_use_nfs --> off + Is it the case that ftpd_use_nfs is not disabled? + + + + Verify the noexec option is configured for the /dev/shm mount point, + run the following command: + $ sudo mount | grep '\s/dev/shm\s' + . . . /dev/shm . . . noexec . . . + + Is it the case that the "/dev/shm" file system does not have the "noexec" option set? + + + + Verify Red Hat Enterprise Linux 9 generates audit records for all account creations, modifications, disabling, and termination events that affect "/etc/passwd" with the following command: + +$ sudo auditctl -l | egrep '(/etc/passwd)' + +-w /etc/passwd -p wa -k identity + Is it the case that the command does not return a line, or the line is commented out? + + + + +Run the following command to determine if the tftp_anon_write SELinux boolean is disabled: +$ getsebool tftp_anon_write +If properly configured, the output should show the following: +tftp_anon_write --> off + Is it the case that tftp_anon_write is not disabled? + + + + To verify if the OpenSSH server uses defined ciphers in the Crypto Policy, run: +$ grep -Po '(-oCiphers=\S+)' /etc/crypto-policies/back-ends/opensshserver.config +and verify that the line matches: +-oCiphers= + Is it the case that Crypto Policy for OpenSSH Server is not configured correctly? + + + + To check the group ownership of /etc/cron.d, +run the command: +$ ls -lL /etc/cron.d +If properly configured, the output should indicate the following group-owner: +root + Is it the case that /etc/cron.d does not have a group owner of root? + + + + Run the following command to determine if the rsh-server package is installed: +$ rpm -q rsh-server + Is it the case that the package is installed? + + + + Check that no boot image file is specified in /etc/zipl.conf: +grep -R "^image\s*=" /etc/zipl.conf +No line should be returned, if a line is returned non BLS compliant boot entries are configured for zIPL. + Is it the case that a non BLS boot entry is configured? + + + + +Run the following command to determine if the puppetmaster_use_db SELinux boolean is disabled: +$ getsebool puppetmaster_use_db +If properly configured, the output should show the following: +puppetmaster_use_db --> off + Is it the case that puppetmaster_use_db is not disabled? + + + + +If the system is configured to prevent the loading of the bluetooth kernel module, +it will contain lines inside any file in /etc/modprobe.d or the deprecated /etc/modprobe.conf. +These lines instruct the module loading system to run another program (such as /bin/true) upon a module install event. + +Run the following command to search for such lines in all files in /etc/modprobe.d and the deprecated /etc/modprobe.conf: +$ grep -r bluetooth /etc/modprobe.conf /etc/modprobe.d + Is it the case that no line is returned? + + + + Run the following command to see if there are some keytabs +that would potentially allow the use of Kerberos by system daemons. +$ ls -la /etc/*.keytab +The expected result is +ls: cannot access '/etc/*.keytab': No such file or directory + Is it the case that a keytab file is present on the system? + + + + Verify that fapolicyd on Red Hat Enterprise Linux 9 prevents ability of non-privileged users to grant other users direct access to the contents of their home directories/folders. +Run the following command: + +grep -r "deny_audit perm=chmod path=/home" /etc/fapolicyd/rules.d + Is it the case that fapolicyd allows non-privileged users to grant other users direct access to the contents of their home directories/folders? + + + + Verify the nosuid option is configured for the /opt mount point, + run the following command: + $ sudo mount | grep '\s/opt\s' + . . . /opt . . . nosuid . . . + + Is it the case that the "/opt" file system does not have the "nosuid" option set? + + + + To verify if the OpenSSH Client uses defined Crypto Policy, run: +$ cat /etc/ssh/ssh_config.d/02-ospp.conf +and verify that the line matches +Match final all +RekeyLimit 512M 1h +GSSAPIAuthentication no +Ciphers aes256-ctr,aes256-cbc,aes128-ctr,aes128-cbc +PubkeyAcceptedKeyTypes ssh-rsa,ecdsa-sha2-nistp384,ecdsa-sha2-nistp256 +MACs hmac-sha2-512,hmac-sha2-256 +KexAlgorithms ecdh-sha2-nistp521,ecdh-sha2-nistp384,ecdh-sha2-nistp256,diffie-hellman-group14-sha1 + Is it the case that Crypto Policy for OpenSSH Client is not configured according to CC requirements? + + + + +Run the following command to determine if the virt_use_samba SELinux boolean is disabled: +$ getsebool virt_use_samba +If properly configured, the output should show the following: +virt_use_samba --> off + Is it the case that virt_use_samba is not disabled? + + + + To verify the number of rounds for the password hashing algorithm is configured, run the following command: +$ sudo grep rounds /etc/pam.d/password-auth +The output should show the following match: +password sufficient pam_unix.so sha512 rounds= + Is it the case that rounds is not set to <sub idref="var_password_pam_unix_rounds" /> or is commented out? + + + + The runtime status of the net.ipv4.conf.all.shared_media kernel parameter can be queried +by running the following command: +$ sysctl net.ipv4.conf.all.shared_media +0. + + Is it the case that the correct value is not returned? + + + + The runtime status of the net.ipv6.conf.default.max_addresses kernel parameter can be queried +by running the following command: +$ sysctl net.ipv6.conf.default.max_addresses +1. + + Is it the case that the correct value is not returned? + + + + The runtime status of the net.ipv6.conf.default.accept_ra kernel parameter can be queried +by running the following command: +$ sysctl net.ipv6.conf.default.accept_ra +0. + + Is it the case that the correct value is not returned? + + + + +Run the following command to determine if the ftpd_connect_db SELinux boolean is disabled: +$ getsebool ftpd_connect_db +If properly configured, the output should show the following: +ftpd_connect_db --> off + Is it the case that ftpd_connect_db is not disabled? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_SECCOMP /boot/config.* + + For each kernel installed, a line with value "y" should be returned. + + Is it the case that the kernel was not built with the required value? + + + + +Run the following command to determine if the samba_domain_controller SELinux boolean is disabled: +$ getsebool samba_domain_controller +If properly configured, the output should show the following: +samba_domain_controller --> off + Is it the case that samba_domain_controller is not disabled? + + + + Inspect the list of enabled firewall ports and verify they are configured correctly by running +the following command: +$ sudo firewall-cmd --list-all + Is it the case that the firewalld rules are not configured? + + + + To determine how the SSH daemon's X11Forwarding option is set, run the following command: + +$ sudo grep -i X11Forwarding /etc/ssh/sshd_config.d/00-complianceascode-hardening.conf +$ sudo grep -i X11Forwarding /etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf + +If a line indicating no is returned, then the required value is set. + + Is it the case that the required value is not set? + + + + To check that the screen locks immediately when activated, run the following command: +$ gsettings get org.gnome.desktop.screensaver lock-delay +If properly configured, the output should be 'uint32 '. + Is it the case that the screensaver lock delay is missing, or is set to a value greater than <sub idref="var_screensaver_lock_delay" />? + + + + Check that Red Hat Enterprise Linux 9 has the packages for smart card support installed. + +Run the following command to determine if the openssl-pkcs11 package is installed: +$ rpm -q openssl-pkcs11 + Is it the case that smartcard software is not installed? + + + + Verify that Red Hat Enterprise Linux 9 is configured to audit the execution of the "newgrp" command with the following command: + +$ sudo auditctl -l | grep newgrp + +-a always,exit -F path=/usr/bin/newgrp -F perm=x -F auid>=1000 -F auid!=unset -k privileged-newgrp + Is it the case that the command does not return a line, or the line is commented out? + + + + To obtain a listing of all users and the contents of their shadow password +field, run the command: +$ sudo awk -F: '$1 !~ /^root$/ && $2 !~ /^[!*]/ {print $1 ":" $2}' /etc/shadow +Identify the system accounts from this listing. These will primarily be the accounts +with UID numbers less than UID_MIN, other than root. Value of the UID_MIN +directive is set in /etc/login.defs configuration file. In the default +configuration, UID_MIN is set to 500. + Is it the case that it is not? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_LEGACY_VSYSCALL_XONLY /boot/config.* + + Configs with value 'n' are not explicitly set in the file, so either commented lines or no + lines should be returned. + + Is it the case that the kernel was not built with the required value? + + + + +Run the following command to determine if the cron_system_cronjob_use_shares SELinux boolean is disabled: +$ getsebool cron_system_cronjob_use_shares +If properly configured, the output should show the following: +cron_system_cronjob_use_shares --> off + Is it the case that cron_system_cronjob_use_shares is not disabled? + + + + To determine if the system is configured to audit unsuccessful calls +to the fsetxattr system call, run the following command: +$ sudo grep "fsetxattr" /etc/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + To check the group ownership of /etc/passwd, +run the command: +$ ls -lL /etc/passwd +If properly configured, the output should indicate the following group-owner: +root + Is it the case that /etc/passwd does not have a group owner of root? + + + + To check the ownership of /var/log, +run the command: +$ ls -lL /var/log +If properly configured, the output should indicate the following owner: +root + Is it the case that /var/log does not have an owner of root? + + + + +Run the following command to determine if the git_cgi_enable_homedirs SELinux boolean is disabled: +$ getsebool git_cgi_enable_homedirs +If properly configured, the output should show the following: +git_cgi_enable_homedirs --> off + Is it the case that git_cgi_enable_homedirs is not disabled? + + + + These settings can be verified by running the following: +$ gsettings get org.gnome.desktop.media-handling autorun-never +If properly configured, the output for autorun-nevershould be true. +To ensure that users cannot enable autorun in GNOME3, run the following: +$ grep 'autorun-never' /etc/dconf/db/local.d/locks/* +If properly configured, the output for autorun-never should be /org/gnome/desktop/media-handling/autorun-never + Is it the case that GNOME autorun is not disabled? + + + + The runtime status of the kernel.core_pattern kernel parameter can be queried +by running the following command: +$ sysctl kernel.core_pattern | cat -A +kernel.core_pattern = $ + + Is it the case that the returned line does not have an empty string? + + + + To verify if the OpenSSH client uses defined MACs in the Crypto Policy, run: +$ grep -i macs /etc/crypto-policies/back-ends/openssh.config +and verify that the line matches: +MACs hmac-sha2-512,hmac-sha2-256 + Is it the case that Crypto Policy for OpenSSH client is not configured correctly? + + + + +Run the following command to determine if the zebra_write_config SELinux boolean is disabled: +$ getsebool zebra_write_config +If properly configured, the output should show the following: +zebra_write_config --> off + Is it the case that zebra_write_config is not disabled? + + + + +Run the following command to determine if the polipo_use_cifs SELinux boolean is disabled: +$ getsebool polipo_use_cifs +If properly configured, the output should show the following: +polipo_use_cifs --> off + Is it the case that polipo_use_cifs is not disabled? + + + + +Run the following command to determine if the selinuxuser_postgresql_connect_enabled SELinux boolean is disabled: +$ getsebool selinuxuser_postgresql_connect_enabled +If properly configured, the output should show the following: +selinuxuser_postgresql_connect_enabled --> off + Is it the case that selinuxuser_postgresql_connect_enabled is not disabled? + + + + To determine if the system is configured to audit successful calls +to the creat system call, run the following command: +$ sudo grep "creat" /etc/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + Run the following command to determine if the setroubleshoot-plugins package is installed: +$ rpm -q setroubleshoot-plugins + Is it the case that the package is installed? + + + + +Run the following command to determine if the samba_enable_home_dirs SELinux boolean is disabled: +$ getsebool samba_enable_home_dirs +If properly configured, the output should show the following: +samba_enable_home_dirs --> off + Is it the case that samba_enable_home_dirs is not disabled? + + + + To check for incorrectly labeled device files, run following commands: +$ sudo find /dev -context *:device_t:* \( -type c -o -type b \) -printf "%p %Z\n" +$ sudo find /dev -context *:unlabeled_t:* \( -type c -o -type b \) -printf "%p %Z\n" +It should produce no output in a well-configured system. + Is it the case that there is output? + + + + To determine if the system is configured to audit successful calls +to the open system call, run the following command: +$ sudo grep "open" /etc/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + To check the group ownership of /etc/gshadow, +run the command: +$ ls -lL /etc/gshadow +If properly configured, the output should indicate the following group-owner: +root + Is it the case that /etc/gshadow does not have a group owner of root? + + + + To determine if the system is configured to audit calls to the +chmod system call, run the following command: +$ sudo grep "chmod" /etc/audit/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + Run the following command to determine if the fapolicyd package is installed: $ rpm -q fapolicyd + Is it the case that the fapolicyd package is not installed? + + + + +Run the following command to determine if the zarafa_setrlimit SELinux boolean is disabled: +$ getsebool zarafa_setrlimit +If properly configured, the output should show the following: +zarafa_setrlimit --> off + Is it the case that zarafa_setrlimit is not disabled? + + + + To determine if the system is configured to audit successful calls +to the chown system call, run the following command: +$ sudo grep "chown" /etc/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + Run the following command to determine if the samba-common package is installed: $ rpm -q samba-common + Is it the case that the package is not installed? + + + + The runtime status of the kernel.perf_event_max_sample_rate kernel parameter can be queried +by running the following command: +$ sysctl kernel.perf_event_max_sample_rate +1. + + Is it the case that the correct value is not returned? + + + + To determine if the system is configured to audit successful calls +to the unlink system call, run the following command: +$ sudo grep "unlink" /etc/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + +Run the following command to determine if the telepathy_connect_all_ports SELinux boolean is disabled: +$ getsebool telepathy_connect_all_ports +If properly configured, the output should show the following: +telepathy_connect_all_ports --> off + Is it the case that telepathy_connect_all_ports is not disabled? + + + + To ensure that the GUI power settings are not active, run the following command: +$ gsettings get org.gnome.settings-daemon.plugins.power active +If properly configured, the output should be false. +To ensure that users cannot enable the power settings, run the following: +$ grep power /etc/dconf/db/local.d/locks/* +If properly configured, the output should be +/org/gnome/settings-daemon/plugins/power/active + Is it the case that power settings are enabled and are not disabled? + + + + +Run the following command to determine if the polipo_connect_all_unreserved SELinux boolean is disabled: +$ getsebool polipo_connect_all_unreserved +If properly configured, the output should show the following: +polipo_connect_all_unreserved --> off + Is it the case that polipo_connect_all_unreserved is not disabled? + + + + Verify that a separate file system/partition has been created for /var/log with the following command: + +$ mountpoint /var/log + + Is it the case that "/var/log is not a mountpoint" is returned? + + + + +Run the following command to determine if the selinuxuser_share_music SELinux boolean is disabled: +$ getsebool selinuxuser_share_music +If properly configured, the output should show the following: +selinuxuser_share_music --> off + Is it the case that selinuxuser_share_music is not disabled? + + + + To ensure that remote access connections are encrypted, run the following command: +$ gsettings get org.gnome.Vino require-encrpytion +If properly configured, the output should be true. +To ensure that users cannot disable encrypted remote connections, run the following: +$ grep require-encryption /etc/dconf/db/local.d/locks/* +If properly configured, the output should be +/org/gnome/Vino/require-encryption + Is it the case that remote access connections are not encrypted? + + + + To check the group ownership of /etc/cron.allow, +run the command: +$ ls -lL /etc/cron.allow +If properly configured, the output should indicate the following group-owner: +root + Is it the case that /etc/cron.allow does not have a group owner of root? + + + + Run the following command to determine if the squid package is installed: +$ rpm -q squid + Is it the case that the package is installed? + + + + +Run the following command to determine if the selinuxuser_execheap SELinux boolean is disabled: +$ getsebool selinuxuser_execheap +If properly configured, the output should show the following: +selinuxuser_execheap --> off + Is it the case that selinuxuser_execheap is not disabled? + + + + +Run the following command to determine if the cobbler_anon_write SELinux boolean is disabled: +$ getsebool cobbler_anon_write +If properly configured, the output should show the following: +cobbler_anon_write --> off + Is it the case that cobbler_anon_write is not disabled? + + + + To verify that USB Human Interface Devices and hubs will be authorized by the USBGuard daemon, +run the following command: +$ sudo grep allow /etc/usbguard/rules.conf +The output lines should include +allow with-interface match-all { 03:*:* 09:00:* } + Is it the case that USB devices of class 3 and 9:00 are not authorized? + + + + +Run the following command to determine if the deny_ptrace SELinux boolean is disabled: +$ getsebool deny_ptrace +If properly configured, the output should show the following: +deny_ptrace --> off + Is it the case that deny_ptrace is not disabled? + + + + To determine if the system is configured to audit calls to the +unlink system call, run the following command: +$ sudo grep "unlink" /etc/audit/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + The runtime status of the kernel.yama.ptrace_scope kernel parameter can be queried +by running the following command: +$ sysctl kernel.yama.ptrace_scope +1. + + Is it the case that the correct value is not returned? + + + + +Run the following command to determine if the git_system_enable_homedirs SELinux boolean is disabled: +$ getsebool git_system_enable_homedirs +If properly configured, the output should show the following: +git_system_enable_homedirs --> off + Is it the case that git_system_enable_homedirs is not disabled? + + + + Verify that Red Hat Enterprise Linux 9 is configured to audit the execution of the "setfacl" command with the following command: + +$ sudo auditctl -l | grep setfacl + +-a always,exit -F path=/usr/bin/setfacl -F perm=x -F auid>=1000 -F auid!=unset -k perm_mod + Is it the case that the command does not return a line, or the line is commented out? + + + + To check if the installed Operating System is 64-bit, run the following command: +$ uname -m +The output should be one of the following: x86_64, aarch64, ppc64le or s390x. +If the output is i686 or i386 the operating system is 32-bit. +Check if the installed CPU supports 64-bit operating systems by running the following command: +$ lscpu | grep "CPU op-mode" +If the output contains 64bit, the CPU supports 64-bit operating systems. + Is it the case that the installed operating sytem is 32-bit but the CPU supports operation in 64-bit? + + + + To check the permissions of /etc/passwd, +run the command: +$ ls -l /etc/passwd +If properly configured, the output should indicate the following permissions: +-rw-r--r-- + Is it the case that /etc/passwd does not have unix mode -rw-r--r--? + + + + To check that the atd service is disabled in system boot configuration, +run the following command: +$ sudo systemctl is-enabled atd +Output should indicate the atd service has either not been installed, +or has been disabled at all runlevels, as shown in the example below: +$ sudo systemctl is-enabled atd disabled + +Run the following command to verify atd is not active (i.e. not running) through current runtime configuration: +$ sudo systemctl is-active atd + +If the service is not running the command will return the following output: +inactive + +The service will also be masked, to check that the atd is masked, run the following command: +$ sudo systemctl show atd | grep "LoadState\|UnitFileState" + +If the service is masked the command will return the following outputs: + +LoadState=masked + +UnitFileState=masked + Is it the case that the "atd" is loaded and not masked? + + + + To determine that AIDE is verifying ACLs, run the following command: +$ grep acl /etc/aide.conf +Verify that the acl option is added to the correct ruleset. + Is it the case that the acl option is missing or not added to the correct ruleset? + + + + To determine if the system is configured to audit calls to the +fchownat system call, run the following command: +$ sudo grep "fchownat" /etc/audit/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + To check how many digits are required in a password, run the following command: +$ grep dcredit /etc/security/pwquality.conf +The dcredit parameter (as a negative number) will indicate how many digits are required. + Is it the case that dcredit is not found or not equal to or less than the required value? + + + + Make sure that the kernel is configured to trust the CPU RNG by following +commands. To check if the option was correctly configured at kernel compile +time, run the following command: +grep -q CONFIG_RANDOM_TRUST_CPU=y /boot/config-`uname -r` +If the command outputs: +CONFIG_RANDOM_TRUST_CPU=y, +it means that the option is compiled into the kernel. Make sure that the +option is not overridden through a boot parameter: +sudo grep 'kernelopts.*random\.trust_cpu=off.*' /boot/grub2/grubenv +The command should not return any output. If the option is not compiled into +the kernel, check that the option is configured through boot parameter. +Inspect the form of default GRUB 2 command line for the Linux operating system +in /etc/default/grub. If it includes random.trust_cpu=on, +then the parameter will be configured for newly installed kernels. +First check if the GRUB recovery is enabled: +$ sudo grep 'GRUB_DISABLE_RECOVERY' /etc/default/grub +If this option is set to true, then check that a line is output by the following command: +$ sudo grep 'GRUB_CMDLINE_LINUX_DEFAULT.*random.trust_cpu=on.*' /etc/default/grub +If the recovery is disabled, check the line with +$ sudo grep 'GRUB_CMDLINE_LINUX.*random.trust_cpu=on.*' /etc/default/grub.Moreover, command line parameters for currently installed kernels should be checked as well. +Run the following command: +$ sudo grubby --info=ALL | grep args | grep -v 'random.trust_cpu=on' +The command should not return any output. + Is it the case that the kernel is not configured to trust the CPU RNG? + + + + To determine if the system is configured to audit successful calls +to the unlinkat system call, run the following command: +$ sudo grep "unlinkat" /etc/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + To check the group ownership of /var/log/messages, +run the command: +$ ls -lL /var/log/messages +If properly configured, the output should indicate the following group-owner: +root + Is it the case that /var/log/messages does not have a group owner of root? + + + + To verify the number of rounds for the password hashing algorithm is configured, run the following command: +$ sudo grep rounds /etc/pam.d/system-auth +The output should show the following match: +password sufficient pam_unix.so sha512 rounds= + Is it the case that rounds is not set to <sub idref="var_password_pam_unix_rounds" /> or is commented out? + + + + To check that SELinux is not disabled at boot time; +Check that no boot entry disables selinux: +sudo grep -L "^options\s+.*\bselinux=0\b" /boot/loader/entries/*.conf +No line should be returned, each line returned is a boot entry that disables SELinux. + Is it the case that SELinux is disabled at boot time? + + + + Run the following command to check if the line is present: +grep pam_wheel /etc/pam.d/su +The output should contain the following line: +auth required pam_wheel.so use_uid + Is it the case that the line is not in the file or it is commented? + + + + To verify the nosuid option is configured for all NFS mounts, run +the following command: +$ mount | grep nfs +All NFS mounts should show the nosuid setting in parentheses. This +is not applicable if NFS is not implemented. + Is it the case that the setting does not show? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_DEBUG_NOTIFIERS /boot/config.* + + For each kernel installed, a line with value "y" should be returned. + + Is it the case that the kernel was not built with the required value? + + + + Verify the nodev option is configured for the /dev/shm mount point, + run the following command: + $ sudo mount | grep '\s/dev/shm\s' + . . . /dev/shm . . . nodev . . . + + Is it the case that the "/dev/shm" file system does not have the "nodev" option set? + + + + Run the following command to determine if the xinetd package is installed: +$ rpm -q xinetd + Is it the case that the package is installed? + + + + +Run the following command to determine if the postgresql_selinux_transmit_client_label SELinux boolean is disabled: +$ getsebool postgresql_selinux_transmit_client_label +If properly configured, the output should show the following: +postgresql_selinux_transmit_client_label --> off + Is it the case that postgresql_selinux_transmit_client_label is not disabled? + + + + To determine how the SSH daemon's PrintLastLog option is set, run the following command: + +$ sudo grep -i PrintLastLog /etc/ssh/sshd_config.d/00-complianceascode-hardening.conf +$ sudo grep -i PrintLastLog /etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf + +If a line indicating yes is returned, then the required value is set. + + Is it the case that the required value is not set? + + + + To check the permissions of /etc/http/conf.d/*, +run the command: +$ ls -l /etc/http/conf.d/* +If properly configured, the output should indicate the following permissions: +-rw-r----- + Is it the case that /etc/http/conf.d/* does not have unix mode -rw-r-----? + + + + Run the following command to check for duplicate account names: +Check that the operating system contains no duplicate UIDs for interactive users by running the following command: +# awk -F ":" 'list[$3]++{print $1, $3}' /etc/passwd +If output is produced, this is a finding. +Configure the operating system to contain no duplicate UIDs for interactive users. +Edit the file "/etc/passwd" and provide each interactive user account that has a duplicate UID with a unique UID. + Is it the case that a line is returned? + + + + The runtime status of the kernel.core_pattern kernel parameter can be queried +by running the following command: +$ sysctl kernel.core_pattern +|/bin/false. + + Is it the case that the returned line does not have a value of "|/bin/false", or a line is not +returned and the need for core dumps is not documented with the Information +System Security Officer (ISSO) as an operational requirement? + + + + To ensure disable and restart on the login screen are disabled, run the following command: +$ grep disable-restart-buttons /etc/dconf/db/distro.d/* +The output should be true. +To ensure that users cannot enable disable and restart on the login screen, run the following: +$ grep disable-restart-buttons /etc/dconf/db/distro.d/locks/* +If properly configured, the output should be /org/gnome/login-screen/disable-restart-buttons + Is it the case that disable-restart-buttons has not been configured or is not disabled? + + + + +Run the following command to determine if the postgresql_selinux_users_ddl SELinux boolean is enabled: +$ getsebool postgresql_selinux_users_ddl +If properly configured, the output should show the following: +postgresql_selinux_users_ddl --> on + Is it the case that postgresql_selinux_users_ddl is not enabled? + + + + Verify the audit tools are protected from unauthorized access, deletion, or modification by checking the permissive mode. + +Check the octal permission of each audit tool by running the following command: + +$ sudo stat -c "%U %n" /sbin/auditctl /sbin/aureport /sbin/ausearch /sbin/autrace /sbin/auditd /sbin/rsyslogd /sbin/augenrules + Is it the case that any of these files have more permissive permissions than 0755? + + + + To check if dictionary words are disallowed run the following command: +$ sudo grep dictcheck /etc/security/pwquality.conf /etc/pwquality.conf.d/*.conf +The dictcheck parameter should be equal to 1. The value should look like +dictcheck=1 + Is it the case that dictcheck is not found or not equal to the required value? + + + + To determine how the SSH daemon's PubkeyAuthentication option is set, run the following command: + +$ sudo grep -i PubkeyAuthentication /etc/ssh/sshd_config.d/00-complianceascode-hardening.conf + + +If a line indicating no is returned, then the required value is set. + + Is it the case that the required value is not set? + + + + +Run the following command to determine if the samba_load_libgfapi SELinux boolean is disabled: +$ getsebool samba_load_libgfapi +If properly configured, the output should show the following: +samba_load_libgfapi --> off + Is it the case that samba_load_libgfapi is not disabled? + + + + +Run the following command to determine if the auditadm_exec_content SELinux boolean is enabled: +$ getsebool auditadm_exec_content +If properly configured, the output should show the following: +auditadm_exec_content --> on + Is it the case that auditadm_exec_content is not enabled? + + + + To determine how the SSH daemon's KerberosAuthentication option is set, run the following command: + +$ sudo grep -i KerberosAuthentication /etc/ssh/sshd_config.d/00-complianceascode-hardening.conf +$ sudo grep -i KerberosAuthentication /etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf + +If a line indicating no is returned, then the required value is set. + + Is it the case that the required value is not set? + + + + +Run the following command to determine if the mpd_enable_homedirs SELinux boolean is disabled: +$ getsebool mpd_enable_homedirs +If properly configured, the output should show the following: +mpd_enable_homedirs --> off + Is it the case that mpd_enable_homedirs is not disabled? + + + + To verify that is configured +as the smart card driver, run the following command: +$ grep card_drivers /etc/opensc.conf +The output should return something similar to: +card_drivers = ; + Is it the case that the smart card driver is not configured correctly? + + + + Run the following command to determine if the openldap-clients package is installed: +$ rpm -q openldap-clients + Is it the case that the package is installed? + + + + The following command will discover and print any +files on local partitions which do not belong to a valid group. +$ df --local -P | awk '{if (NR!=1) print $6}' | sudo xargs -I '{}' find '{}' -xdev -nogroup + +Either remove all files and directories from the system that do not have a valid group, +or assign a valid group with the chgrp command: +$ sudo chgrp group file + Is it the case that there is output? + + + + To check the permissions of /etc/http/conf.modules.d/*, +run the command: +$ ls -l /etc/http/conf.modules.d/* +If properly configured, the output should indicate the following permissions: +-rw-r----- + Is it the case that /etc/http/conf.modules.d/* does not have unix mode -rw-r-----? + + + + The runtime status of the net.ipv4.conf.default.accept_redirects kernel parameter can be queried +by running the following command: +$ sysctl net.ipv4.conf.default.accept_redirects +0. + + Is it the case that the correct value is not returned? + + + + To check the group ownership of /etc/cron.monthly, +run the command: +$ ls -lL /etc/cron.monthly +If properly configured, the output should indicate the following group-owner: +root + Is it the case that /etc/cron.monthly does not have a group owner of root? + + + + To verify the noexec option is configured for all NFS mounts, run the following command: +$ mount | grep nfs +All NFS mounts should show the noexec setting in parentheses. This is not applicable if NFS is +not implemented. + Is it the case that the setting does not show? + + + + +Run the following command to determine if the samba_share_nfs SELinux boolean is disabled: +$ getsebool samba_share_nfs +If properly configured, the output should show the following: +samba_share_nfs --> off + Is it the case that samba_share_nfs is not disabled? + + + + +Run the following command to determine if the unconfined_login SELinux boolean is enabled: +$ getsebool unconfined_login +If properly configured, the output should show the following: +unconfined_login --> on + Is it the case that unconfined_login is not enabled? + + + + Verify the operating system is not configured to bypass password requirements for privilege +escalation. Check the configuration of the "/etc/pam.d/sudo" file with the following command: +$ sudo grep pam_succeed_if /etc/pam.d/sudo + Is it the case that system is configured to bypass password requirements for privilege escalation? + + + + Verify that Red Hat Enterprise Linux 9 is configured to audit the execution of the "pam_timestamp_check" command with the following command: + +$ sudo auditctl -l | grep pam_timestamp_check + +-a always,exit -F path=/usr/sbin/pam_timestamp_check -F perm=x -F auid>=1000 -F auid!=unset -k privileged-pam_timestamp_check + Is it the case that the command does not return a line, or the line is commented out? + + + + The runtime status of the net.ipv6.conf.all.forwarding kernel parameter can be queried +by running the following command: +$ sysctl net.ipv6.conf.all.forwarding +0. +The ability to forward packets is only appropriate for routers. + Is it the case that IP forwarding value is "1" and the system is not router? + + + + +Run the following command to determine if the selinuxuser_udp_server SELinux boolean is disabled: +$ getsebool selinuxuser_udp_server +If properly configured, the output should show the following: +selinuxuser_udp_server --> off + Is it the case that selinuxuser_udp_server is not disabled? + + + + The owner of all log files written by rsyslog should be . +These log files are determined by the second part of each Rule line in +/etc/rsyslog.conf and typically all appear in /var/log. +To see the owner of a given log file, run the following command: +$ ls -l LOGFILE + Is it the case that the owner is not correct? + + + + To determine if the system is configured to audit unsuccessful calls +to the chown system call, run the following command: +$ sudo grep "chown" /etc/audit.* +If the system is configured to audit this activity, it will return a line. + + Is it the case that no line is returned? + + + + To ensure ClientAliveInterval is set correctly, run the following command: +$ sudo grep ClientAliveCountMax /etc/ssh/sshd_config +If properly configured, the output should be: +ClientAliveCountMax +For SSH earlier than v8.2, a ClientAliveCountMax value of 0 causes an idle timeout precisely when +the ClientAliveInterval is set. Starting with v8.2, a value of 0 disables the timeout +functionality completely. +If the option is set to a number greater than 0, then the idle session will be disconnected after +ClientAliveInterval * ClientAliveCountMax seconds. + Is it the case that it is commented out or not configured properly? + + + + To verify that the Audit is correctly configured according to recommended rules, check the content of the file with the following command: +cat /etc/audit/rules.d/30-ospp-v42-1-create-failed.rules +The output has to be exactly as follows: +## Unsuccessful file creation (open with O_CREAT) +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b64 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b64 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create + Is it the case that the file does not exist or the content differs? + + + + To check the group ownership of /etc/crontab, +run the command: +$ ls -lL /etc/crontab +If properly configured, the output should indicate the following group-owner: +root + Is it the case that /etc/crontab does not have a group owner of root? + + + + Run the following command to determine if the samba package is installed: +$ rpm -q samba + Is it the case that the package is installed? + + + + To check the permissions of /etc/at.allow, +run the command: +$ ls -l /etc/at.allow +If properly configured, the output should indicate the following permissions: +-rw------- + Is it the case that /etc/at.allow does not have unix mode -rw-------? + + + + The runtime status of the net.ipv4.icmp_echo_ignore_broadcasts kernel parameter can be queried +by running the following command: +$ sysctl net.ipv4.icmp_echo_ignore_broadcasts +1. + + Is it the case that the correct value is not returned? + + + + To check for legacy lines in /etc/shadow, run the following command: + grep '^\+' /etc/shadow +The command should not return any output. + Is it the case that the file contains legacy lines? + + + + The runtime status of the net.ipv6.conf.all.accept_redirects kernel parameter can be queried +by running the following command: +$ sysctl net.ipv6.conf.all.accept_redirects +0. + + Is it the case that the correct value is not returned? + + + + Check the system partitions to determine if they are encrypted with the following command: +blkid + +Output will be similar to: +/dev/sda1: UUID=" ab12c3de-4f56-789a-8f33-3850cc8ce3a2 +" TYPE="crypto_LUKS" +/dev/sda2: UUID=" bc98d7ef-6g54-321h-1d24-9870de2ge1a2 +" TYPE="crypto_LUKS" + +The boot partition and pseudo-file systems, such as /proc, /sys, and tmpfs, +are not required to use disk encryption and are not a finding. + Is it the case that partitions do not have a type of crypto_LUKS? + + + + Verify Red Hat Enterprise Linux 9 is configured to lock an account until released by an administrator +after unsuccessful logon +attempts with the command: + + +$ grep 'unlock_time =' /etc/security/faillock.conf +unlock_time = + Is it the case that the "unlock_time" option is not set to "<sub idref="var_accounts_passwords_pam_faillock_unlock_time" />", +the line is missing, or commented out? + + + + +If the system is configured to prevent the loading of the can kernel module, +it will contain lines inside any file in /etc/modprobe.d or the deprecated /etc/modprobe.conf. +These lines instruct the module loading system to run another program (such as /bin/true) upon a module install event. + +Run the following command to search for such lines in all files in /etc/modprobe.d and the deprecated /etc/modprobe.conf: +$ grep -r can /etc/modprobe.conf /etc/modprobe.d + Is it the case that no line is returned? + + + + To determine the config value the kernel was built with, run the following command: + $ grep CONFIG_DEBUG_LIST /boot/config.* + + For each kernel installed, a line with value "y" should be returned. + + Is it the case that the kernel was not built with the required value? + + + + The runtime status of the kernel.perf_event_paranoid kernel parameter can be queried +by running the following command: +$ sysctl kernel.perf_event_paranoid +2. + + Is it the case that the correct value is not returned? + + + + + + + + + combine_ovals.py from SCAP Security Guide + ssg: [0, 1, 64], python: 3.10.4 + 5.11 + 2022-09-30T15:10:20 + + + + + Alibaba Cloud Linux 2 + + Red Hat Enterprise Linux 9 + + + The operating system installed on the system is Alibaba Cloud Linux 2 + + + + + + + + + Alibaba Cloud Linux 3 + + Red Hat Enterprise Linux 9 + + + The operating system installed on the system is Alibaba Cloud Linux 3 + + + + + + + + + CentOS 7 + + Red Hat Enterprise Linux 9 + + + The operating system installed on the system is + CentOS 7 + + + + + + + + + CentOS 8 + + Red Hat Enterprise Linux 9 + + + The operating system installed on the system is + CentOS 8 + + + + + + + + + + CentOS Stream 9 + + Red Hat Enterprise Linux 9 + + + The operating system installed on the system is + CentOS Stream 9 + + + + + + + + + + Debian + + Red Hat Enterprise Linux 9 + + The operating system installed is a Debian System + + + + + + + + + Debian Linux 10 + + Red Hat Enterprise Linux 9 + + + The operating system installed on the system is Debian 10 + + + + + + + + + Debian Linux 11 + + Red Hat Enterprise Linux 9 + + + The operating system installed on the system is Debian 11 + + + + + + + + + Debian 9 + + Red Hat Enterprise Linux 9 + + + The operating system installed on the system is Debian 9 + + + + + + + + + Installed operating system is Fedora + + Red Hat Enterprise Linux 9 + + + + + + The operating system installed on the system is Fedora + + + + + + + + + + Oracle Linux 7 + + Red Hat Enterprise Linux 9 + + + The operating system installed on the system is + Oracle Linux 7 + + + + + + + + + + + Oracle Linux 8 + + Red Hat Enterprise Linux 9 + + + The operating system installed on the system is + Oracle Linux 8 + + + + + + + + + + + Oracle Linux 9 + + Red Hat Enterprise Linux 9 + + + The operating system installed on the system is + Oracle Linux 9 + + + + + + + + + + + openSUSE + + Red Hat Enterprise Linux 9 + + The operating system installed on the system is openSUSE. + + + + + + + + + openSUSE Leap 15 + + Red Hat Enterprise Linux 9 + + + The operating system installed on the system is openSUSE Leap 15. + + + + + + + + + openSUSE Leap 42 + + Red Hat Enterprise Linux 9 + + + + + The operating system installed on the system is openSUSE Leap 42. + + + + + + + + + Installed operating system is part of the Unix family + + Red Hat Enterprise Linux 9 + + The operating system installed on the system is part of the Unix OS family + + + + + + + + Red Hat Enterprise Linux CoreOS + + Red Hat Enterprise Linux 9 + + + The operating system installed on the system is + Red Hat Enterprise Linux CoreOS release 4 + + + + + + + + + + + Red Hat Enterprise Linux 7 + + Red Hat Enterprise Linux 9 + + + The operating system installed on the system is + Red Hat Enterprise Linux 7 + + + + + + + + + + + + + + + + + + Red Hat Enterprise Linux 8 + + Red Hat Enterprise Linux 9 + + + The operating system installed on the system is + Red Hat Enterprise Linux 8 + + + + + + + + + + + + + + + Red Hat Enterprise Linux 8.0 + + Red Hat Enterprise Linux 9 + + + The operating system installed on the system is Red Hat Enterprise Linux 8.0 + + + + + + + + Red Hat Enterprise Linux 8.1 + + Red Hat Enterprise Linux 9 + + + The operating system installed on the system is Red Hat Enterprise Linux 8.1 + + + + + + + + Red Hat Enterprise Linux 8.2 + + Red Hat Enterprise Linux 9 + + + The operating system installed on the system is Red Hat Enterprise Linux 8.2 + + + + + + + + Red Hat Enterprise Linux 8.3 + + Red Hat Enterprise Linux 9 + + + The operating system installed on the system is Red Hat Enterprise Linux 8.3 + + + + + + + + Red Hat Enterprise Linux 8.4 + + Red Hat Enterprise Linux 9 + + + The operating system installed on the system is Red Hat Enterprise Linux 8.4 + + + + + + + + Red Hat Enterprise Linux 8.5 + + Red Hat Enterprise Linux 9 + + + The operating system installed on the system is Red Hat Enterprise Linux 8.5 + + + + + + + + Red Hat Enterprise Linux 8.6 + + Red Hat Enterprise Linux 9 + + + The operating system installed on the system is Red Hat Enterprise Linux 8.6 + + + + + + + + Red Hat Enterprise Linux 8.7 + + Red Hat Enterprise Linux 9 + + + The operating system installed on the system is Red Hat Enterprise Linux 8.7 + + + + + + + + Red Hat Enterprise Linux 8.8 + + Red Hat Enterprise Linux 9 + + + The operating system installed on the system is Red Hat Enterprise Linux 8.8 + + + + + + + + Red Hat Enterprise Linux 8.9 + + Red Hat Enterprise Linux 9 + + + The operating system installed on the system is Red Hat Enterprise Linux 8.9 + + + + + + + + Red Hat Enterprise Linux 8.10 + + Red Hat Enterprise Linux 9 + + + The operating system installed on the system is Red Hat Enterprise Linux 8.10 + + + + + + + + Red Hat Enterprise Linux 9 + + Red Hat Enterprise Linux 9 + + + The operating system installed on the system is + Red Hat Enterprise Linux 9 + + + + + + + + + + + + + + + Red Hat Virtualization 4 + + Red Hat Enterprise Linux 9 + + + The operating system installed on the system is + Red Hat Virtualization Host 4.4+ or Red Hat Enterprise Host. + + + + + + + + + Scientific Linux 7 + + Red Hat Enterprise Linux 9 + + + The operating system installed on the system is + Scientific Linux 7 + + + + + + + + + SUSE Linux Enterprise 12 + + Red Hat Enterprise Linux 9 + + + + The operating system installed on the system is + SUSE Linux Enterprise 12. + + + + + + + + + + + + + SUSE Linux Enterprise 15 + + Red Hat Enterprise Linux 9 + + + + The operating system installed on the system is + SUSE Linux Enterprise 15. + + + + + + + + + + + + + Ubuntu + + Red Hat Enterprise Linux 9 + + The operating system installed is an Ubuntu System + + + + + + + + + + Ubuntu 1604 + + Red Hat Enterprise Linux 9 + + + The operating system installed on the system is Ubuntu 1604 + + + + + + + + + Ubuntu 1804 + + Red Hat Enterprise Linux 9 + + + The operating system installed on the system is Ubuntu 1804 + + + + + + + + + Ubuntu 2004 + + Red Hat Enterprise Linux 9 + + + The operating system installed on the system is Ubuntu 2004 + + + + + + + + + Ubuntu 2204 + + Red Hat Enterprise Linux 9 + + + The operating system installed on the system is Ubuntu 2204 + + + + + + + + + UnionTech OS Server 20 + + Red Hat Enterprise Linux 9 + + + The operating system installed on the system is UnionTech OS Server 20 + + + + + + + + + Red Hat Virtualization 4 + + Red Hat Enterprise Linux 9 + + + The application installed installed on the system is + Red Hat Virtualization 4. + + + + + + + + + Package audit is installed + + Red Hat Enterprise Linux 9 + + Checks if package audit is installed. + + + + + + + + + Package chrony is installed + + Red Hat Enterprise Linux 9 + + Checks if package chrony is installed. + + + + + + + + + Package gdm is installed + + Red Hat Enterprise Linux 9 + + Checks if package gdm is installed. + + + + + + + + + Package grub2 is installed + + Red Hat Enterprise Linux 9 + + Checks if package grub2-common is installed. + + + + + + + + + + + + + Package libuser is installed + + Red Hat Enterprise Linux 9 + + Checks if package libuser is installed. + + + + + + + + + Package providing /etc/login.defs is installed + + Red Hat Enterprise Linux 9 + + Checks if package providing /etc/login.defs and is installed. + + + + + + + + + Package net-snmp is installed + + Red Hat Enterprise Linux 9 + + Checks if package net-snmp is installed. + + + + + + + + + Check if the system doesn't act as an oVirt host or manager + + Red Hat Enterprise Linux 9 + + Check if the system has neither ovirt-host nor ovirt-engine installed. + + + + + + + + Package nss-pam-ldapd is installed + + Red Hat Enterprise Linux 9 + + Checks if package nss-pam-ldapd is installed. + + + + + + + + + Package ntp is installed + + Red Hat Enterprise Linux 9 + + Checks if package ntp is installed. + + + + + + + + + Check if the system acts as an oVirt host or manager + + Red Hat Enterprise Linux 9 + + Check if the system has ovirt-host or ovirt-engine installed + + + + + + + + + + Package pam is installed + + Red Hat Enterprise Linux 9 + + Checks if package pam is installed. + + + + + + + + + Package polkit is installed + + Red Hat Enterprise Linux 9 + + Checks if package polkit is installed. + + + + + + + + + Package postfix is installed + + Red Hat Enterprise Linux 9 + + Checks if package postfix is installed. + + + + + + + + + Package sssd-common is installed + + Red Hat Enterprise Linux 9 + + Checks if package sssd-common is installed. + + + + + + + + + Package sudo is installed + + Red Hat Enterprise Linux 9 + + Checks if package sudo is installed. + + + + + + + + + Package systemd is installed + + Red Hat Enterprise Linux 9 + + Checks if package systemd is installed. + + + + + + + + + Package tftp-server is installed + + Red Hat Enterprise Linux 9 + + Checks if package tftp-server is installed. + + + + + + + + + Package tmux is installed + + Red Hat Enterprise Linux 9 + + Checks if package tmux is installed. + + + + + + + + + Package usbguard is installed + + Red Hat Enterprise Linux 9 + + Checks if package usbguard is installed. + + + + + + + + + WiFi interface is present + + Red Hat Enterprise Linux 9 + + Checks if any wifi interface is present. + + + + + + + + + Package yum is installed + + Red Hat Enterprise Linux 9 + + Checks if package yum is installed. + + + + + + + + + System uses zIPL + + Red Hat Enterprise Linux 9 + + Checks if system uses zIPL bootloader. + + + + + + + + + Check if the scan target is a container + + Red Hat Enterprise Linux 9 + + Check for presence of files characterizing container filesystems. + + + + + + + + + + Check if the scan target is a machine + + Red Hat Enterprise Linux 9 + + Check for absence of files characterizing container filesystems. + + + + + + + + + Partition /tmp exists + + Red Hat Enterprise Linux 9 + + + + + + + + + + Partition /var/tmp exists + + Red Hat Enterprise Linux 9 + + + + + + + + + + Kerberos server is older than 1.17-18 + + Red Hat Enterprise Linux 9 + + + Check if version of Kerberos server is lesser than 1.17-18 + + + + + + + + + Kerberos workstation is older than 1.17-18 + + Red Hat Enterprise Linux 9 + + + Check if version of Kerberos workstation is lesser than 1.17-18 + + + + + + + + + Test that the architecture is aarch64 + + Red Hat Enterprise Linux 9 + + Check that architecture of kernel in /proc/sys/kernel/osrelease is aarch64 + + + + + + + + Test for different architecture than aarch64 + + Red Hat Enterprise Linux 9 + + Check that architecture of kernel in /proc/sys/kernel/osrelease is not aarch64 + + + + + + + + Test for different architecture than s390x + + Red Hat Enterprise Linux 9 + + Check that architecture of kernel in /proc/sys/kernel/osrelease is not s390x + + + + + + + + Test that the architecture is ppc64le + + Red Hat Enterprise Linux 9 + + Check that architecture of kernel in /proc/sys/kernel/osrelease is ppc64le + + + + + + + + Test that the architecture is s390x + + Red Hat Enterprise Linux 9 + + Check that architecture of kernel in /proc/sys/kernel/osrelease is s390x + + + + + + + + SSSD is configured to use LDAP + + Red Hat Enterprise Linux 9 + + Identification provider is not set to ad within /etc/sssd/sssd.conf + + + + + + + + + Non-UEFI system boot mode check + + Red Hat Enterprise Linux 9 + + Check if System boot mode is non-UEFI. + + + + + + + + + UEFI system boot mode check + + Red Hat Enterprise Linux 9 + + Check if system boot mode is UEFI. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + alinux-release + + + alinux-release + + + centos-release + + + /etc/os-release + ^ID="(\w+)"$ + 1 + + + /etc/os-release + ^VERSION_ID="(\d)"$ + 1 + + + /etc/os-release + ^ID="(\w+)"$ + 1 + + + /etc/os-release + ^VERSION_ID="(\d)"$ + 1 + + + /etc/debian_version + + + /etc/debian_version + ^10.[0-9]+$ + 1 + + + /etc/debian_version + ^11.[0-9]+$ + 1 + + + /etc/debian_version + ^9.[0-9]+$ + 1 + + + fedora-release.* + + + /etc/system-release-cpe + ^cpe:\/o:fedoraproject:fedora:[\d]+$ + 1 + + + oraclelinux-release + + + oraclelinux-release + + + oraclelinux-release + + + openSUSE-release + + + openSUSE-release + + + openSUSE-release + + + + /etc/os-release + ^ID="(\w+)"$ + 1 + + + /etc/os-release + ^VERSION_ID="(\d)\.\d+"$ + 1 + + + + redhat-release-client + + + redhat-release-workstation + + + redhat-release-server + + + redhat-release-computenode + + + /etc/redhat-release + ^Red Hat Enterprise Linux release (\d)\.\d+$ + 1 + + + + redhat-release + + + redhat-release + + + redhat-release + + + redhat-release + + + redhat-release + + + redhat-release + + + redhat-release + + + redhat-release + + + redhat-release + + + redhat-release + + + redhat-release + + + redhat-release + + + /etc/redhat-release + ^Red Hat Enterprise Linux release (\d)\.\d+$ + 1 + + + + redhat-release + + + /etc/redhat-release + ^Red Hat Enterprise Linux release (\d)\.\d+$ + 1 + + + redhat-release-virtualization-host + + + sl-release + + + + sled-release + + + sles-release + + + SLES_SAP-release + + + + sled-release + + + sles-release + + + SLES_SAP-release + + + /etc/lsb-release + + + /etc/lsb-release + ^DISTRIB_ID=Ubuntu$ + 1 + + + /etc/lsb-release + ^DISTRIB_CODENAME=xenial$ + 1 + + + /etc/lsb-release + ^DISTRIB_CODENAME=bionic$ + 1 + + + /etc/lsb-release + ^DISTRIB_CODENAME=focal$ + 1 + + + /etc/lsb-release + ^DISTRIB_CODENAME=jammy$ + 1 + + + uos-release + + + rhvm-appliance + + + audit + + + chrony + + + gdm + + + grub2-common + + + /sys/firmware/opal + + + libuser + + + shadow-utils + + + net-snmp + + + nss-pam-ldapd + + + ntp + + + ovirt-host + + + ovirt-engine + + + pam + + + polkit + + + postfix + + + sssd-common + + + sudo + + + systemd + + + tftp-server + + + tmux + + + usbguard + + + /proc/net/wireless + + + yum + + + s390utils-base + + + /.dockerenv + + + /run/.containerenv + + + /tmp + + + /var/tmp + + + krb5-server + + + krb5-workstation + + + /proc/sys/kernel/osrelease + ^.*\.(.*)$ + 1 + + + /proc/sys/kernel/osrelease + ^.*\.(.*)$ + 1 + + + /proc/sys/kernel/osrelease + ^.*\.(.*)$ + 1 + + + /etc/sssd/sssd.conf + ^[\s]*\[domain\/[^]]*]([^\n\[\]]*\n+)+?[\s]*id_provider[ \t]*=[ \t]*((?i)ad)[ \t]*$ + 1 + + + /sys/firmware/efi + + + + + + + ^2.*$ + + + ^3.*$ + + + ^7.*$ + + + centos + + + 8 + + + centos + + + 9 + + + ^7.*$ + + + ^8.*$ + + + ^9.*$ + + + openSUSE-release + + + ^15.*$ + + + ^42.*$ + + + unix + + + rhcos + + + 4 + + + unix + + + ^7.*$ + + + ^7.*$ + + + ^7.*$ + + + ^7.*$ + + + 7 + + + unix + + + ^8.*$ + + + ^8.0*$ + + + ^8.1*$ + + + ^8.2*$ + + + ^8.3*$ + + + ^8.4*$ + + + ^8.5*$ + + + ^8.6*$ + + + ^8.7*$ + + + ^8.8*$ + + + ^8.9*$ + + + ^8.10*$ + + + 8 + + + unix + + + ^9.*$ + + + 9 + + + 0:4.4 + + + ^7.*$ + + + unix + + + ^12.*$ + + + ^12.*$ + + + ^12.*$ + + + unix + + + ^15.*$ + + + ^15.*$ + + + ^15.*$ + + + ^20.*$ + + + ^4.*$ + + + 0:1.17-18 + + + 0:1.17-18 + + + ^aarch64$ + + + ^ppc64le$ + + + ^s390x$ + + + ppc64le + + + + + \ No newline at end of file diff --git a/roles/satellite_config/meta/main.yml b/roles/satellite_config/meta/main.yml new file mode 100644 index 0000000..98395ab --- /dev/null +++ b/roles/satellite_config/meta/main.yml @@ -0,0 +1,16 @@ +--- +galaxy_info: + author: Showroom Project Team + description: This roles configures Red Hat Satellite. + company: Red Hat, Inc. + license: GPL-3.0-only + min_ansible_version: "2.14" + platforms: + - name: EL + versions: + - "8" + - "9" + galaxy_tags: [] +dependencies: [] +collections: + - redhat.satellite diff --git a/roles/satellite_config/tasks/main.yml b/roles/satellite_config/tasks/main.yml new file mode 100644 index 0000000..a12b531 --- /dev/null +++ b/roles/satellite_config/tasks/main.yml @@ -0,0 +1,145 @@ +--- +- name: "Ensure hammer configuration" + ansible.builtin.include_tasks: "satellite_configure_hammer.yml" + +- name: "Ensure manifest uploaded to Satellite" + ansible.builtin.import_role: + name: redhat.satellite.manifest + when: satellite_manifest_uuid is defined + +- name: "Ensure defined repositories enabled" + ansible.builtin.import_role: + name: redhat.satellite.repositories + when: satellite_products is defined + +- name: "Ensure content credentials are configured" + ansible.builtin.import_role: + name: redhat.satellite.content_credentials + when: "satellite_content_credentials is defined" + +- name: "Ensure the state of the specified custom products" + ansible.builtin.include_tasks: "satellite_ensure_custom_product.yml" + loop: "{{ satellite_config_custom_products }}" + loop_control: + loop_var: custom_product + when: "satellite_config_custom_products is defined" + +- name: "Sync repositories" + ansible.builtin.include_tasks: "satellite_sync_repositories.yml" + when: satellite_config_sync | bool() + +- name: "Ensure sync plans are configured" + ansible.builtin.import_role: + name: redhat.satellite.sync_plans + when: satellite_sync_plans is defined + +- name: "Ensure lifecycle environments are configured" + ansible.builtin.import_role: + name: redhat.satellite.lifecycle_environments + when: satellite_lifecycle_environments is defined + +- name: "Ensure content views and composite content views are configured" + ansible.builtin.import_role: + name: redhat.satellite.content_views + when: satellite_content_views is defined + +- name: "Ensure content views and composite content views are published" + ansible.builtin.import_role: + name: redhat.satellite.content_view_publish + when: satellite_content_views is defined + +- name: "Ensure all locations are presented" + ansible.builtin.import_role: + name: redhat.satellite.locations + when: satellite_locations is defined + +- name: "Ensure the state of the specified partition tables" + ansible.builtin.include_tasks: "satellite_partition_tables.yml" + when: "satellite_config_partition_tables is defined" + +- name: "Ensure the state of the provisioning templates" + ansible.builtin.import_role: + name: redhat.satellite.provisioning_templates + when: "satellite_provisioning_templates is defined" + +- name: "Ensure operatingsystems are configured" + ansible.builtin.include_tasks: "satellite_operatingsystems.yml" + when: "satellite_config_operatingsystems is defined" + +- name: "Ensure activation keys are configured" + ansible.builtin.import_role: + name: redhat.satellite.activation_keys + when: "satellite_activation_keys is defined" + +- name: "Ensure domains are configured" + ansible.builtin.import_role: + name: redhat.satellite.domains + when: "satellite_domains is defined" + +- name: "Ensure realms are configured" + ansible.builtin.include_tasks: "satellite_realms.yml" + when: "satellite_config_realms is defined" + +- name: "Ensure subnets are configured" + ansible.builtin.import_role: + name: redhat.satellite.subnets + when: "satellite_subnets is defined" + +- name: "Ensure compute resources are configured" + ansible.builtin.import_role: + name: redhat.satellite.compute_resources + when: "satellite_compute_resources is defined" + +- name: "Ensure compute profiles are configured" + ansible.builtin.import_role: + name: redhat.satellite.compute_profiles + when: "satellite_compute_profiles is defined" + +- name: "Ensure satellite ansible roles are configured" + ansible.builtin.include_tasks: "satellite_ansible_roles.yml" + when: "satellite_config_ansible_roles is defined" + +- name: "Ensure host groups are configured" + ansible.builtin.import_role: + name: redhat.satellite.hostgroups + when: "satellite_hostgroups is defined" + +- name: "Ensure satellite scap content are configured" + ansible.builtin.include_tasks: "satellite_scap_contents.yml" + when: "satellite_scap_contents is defined" + loop: "{{ satellite_scap_contents }}" + loop_control: + loop_var: scap_content + +- name: "Ensure satellite scap tailoring files are configured" + ansible.builtin.include_tasks: "satellite_scap_tailoring_files.yml" + when: "satellite_scap_tailoring_files is defined" + loop: "{{ satellite_scap_tailoring_files }}" + loop_control: + loop_var: scap_tailoring_file + +- name: "Ensure satellite scap policies are configured" + ansible.builtin.include_tasks: "satellite_scap_policies.yml" + when: "satellite_scap_policies is defined" + +- name: "Ensure discovery rules are configured" + ansible.builtin.include_tasks: "satellite_discovery_rules.yml" + when: "satellite_config_discovery_rules is defined" + loop: "{{ satellite_config_discovery_rules }}" + loop_control: + loop_var: discovery_rule + +- name: "Ensure global parameters are configured" + ansible.builtin.include_tasks: "satellite_global_parameters.yml" + when: "satellite_config_global_parameters is defined" + loop: "{{ satellite_config_global_parameters }}" + loop_control: + loop_var: global_parameter + +- name: "Ensure user and groups are configured" + ansible.builtin.include_tasks: "satellite_manage_users.yml" + +- name: "Ensure settings are configured" + ansible.builtin.import_role: + name: redhat.satellite.settings + when: "satellite_settings is defined" diff --git a/roles/satellite_config/tasks/satellite_ansible_roles.yml b/roles/satellite_config/tasks/satellite_ansible_roles.yml new file mode 100644 index 0000000..4f28a06 --- /dev/null +++ b/roles/satellite_config/tasks/satellite_ansible_roles.yml @@ -0,0 +1,28 @@ +--- +- name: "Clone the repositories locally" # noqa: latest[git] + become: true + ansible.builtin.git: + repo: "{{ git_repo.repository }}" + dest: "{{ git_repo.dest }}" + clone: "{{ git_repo.clone | default(omit) }}" + force: "{{ git_repo.force | default(omit) }}" + when: satellite_config_git_repos is defined + loop: "{{ satellite_config_git_repos }}" + loop_control: + loop_var: git_repo + +- name: "Call the Satellite ansible import API" + ansible.builtin.uri: + url: "{{ satellite_server_url }}/ansible/api/ansible_roles/sync" + method: "PUT" + user: "{{ satellite_username }}" + password: "{{ satellite_password }}" + force_basic_auth: true + body: + proxy_id: "1-{{ inventory_hostname }}" + role_names: "{{ satellite_config_ansible_roles | to_json() }}" + body_format: "json" + status_code: [200, 201] + timeout: 300 + register: __result + failed_when: __result.failed diff --git a/roles/satellite_config/tasks/satellite_configure_hammer.yml b/roles/satellite_config/tasks/satellite_configure_hammer.yml new file mode 100644 index 0000000..2db40fd --- /dev/null +++ b/roles/satellite_config/tasks/satellite_configure_hammer.yml @@ -0,0 +1,39 @@ +--- +- name: Block for hammer configuration + become: true + block: + - name: "Create a directory for the hammer config for the root user" + ansible.builtin.file: + dest: "/root/.hammer/cli.modules.d/" + owner: root + group: root + mode: "0600" + state: "directory" + + - name: "Create the configuration file" + ansible.builtin.template: + src: "foreman.yml.j2" + owner: root + group: root + mode: "0600" + dest: "/root/.hammer/cli.modules.d/foreman.yml" + + - name: "Check hammer" + ansible.builtin.command: "hammer ping" + register: __output + changed_when: __output.changed + failed_when: __output.failed + + - name: "Success check of hammer" + ansible.builtin.fail: + when: "'Status: ok' not in __output.stdout" + + - name: "Check health" + ansible.builtin.command: "satellite-maintain health check --label server-ping" + register: __output_health + changed_when: __output_health.changed + failed_when: __output_health.failed + + - name: "Success check of satellite health check" + ansible.builtin.fail: + when: "'[OK]' not in __output_health.stdout" diff --git a/roles/satellite_config/tasks/satellite_discovery_rules.yml b/roles/satellite_config/tasks/satellite_discovery_rules.yml new file mode 100644 index 0000000..14b3182 --- /dev/null +++ b/roles/satellite_config/tasks/satellite_discovery_rules.yml @@ -0,0 +1,16 @@ +--- +- name: "Ensure discovery rules" + redhat.satellite.discovery_rule: + username: "{{ satellite_username }}" + password: "{{ satellite_password }}" + server_url: "{{ satellite_server_url }}" + organizations: "{{ discovery_rule.organizations | default(omit) }}" + locations: "{{ discovery_rule.locations | default(omit) }}" + name: "{{ discovery_rule.name }}" + search: "{{ discovery_rule.search | default(omit) }}" + hostgroup: "{{ discovery_rule.hostgroup | default(omit) }}" + hostname: "{{ discovery_rule.hostname_pattern | default(omit) }}" + max_count: "{{ discovery_rule.max_count | default(omit) }}" + priority: "{{ discovery_rule.priority | default(omit) }}" + enabled: "{{ discovery_rule.enabled | default(omit) }}" + state: "{{ discovery_rule.state | default(omit) }}" diff --git a/roles/satellite_config/tasks/satellite_ensure_custom_product.yml b/roles/satellite_config/tasks/satellite_ensure_custom_product.yml new file mode 100644 index 0000000..efc5676 --- /dev/null +++ b/roles/satellite_config/tasks/satellite_ensure_custom_product.yml @@ -0,0 +1,17 @@ +--- +- name: "Ensure custom product - {{ custom_product.name }}" + redhat.satellite.product: + username: "{{ satellite_username }}" + password: "{{ satellite_password }}" + server_url: "{{ satellite_server_url }}" + organization: "{{ satellite_organization }}" + name: "{{ custom_product.name }}" + state: "{{ custom_product.state | default(omit) }}" + + +- name: "Ensure the state the repositories required for the custom product." + ansible.builtin.include_tasks: "satellite_ensure_custom_product_repo.yml" + loop: "{{ custom_product.repositories }}" + loop_control: + loop_var: custom_product_repository + when: "custom_product.repositories is defined" diff --git a/roles/satellite_config/tasks/satellite_ensure_custom_product_repo.yml b/roles/satellite_config/tasks/satellite_ensure_custom_product_repo.yml new file mode 100644 index 0000000..2085107 --- /dev/null +++ b/roles/satellite_config/tasks/satellite_ensure_custom_product_repo.yml @@ -0,0 +1,16 @@ +--- +- name: "Ensure the state of repository - {{ custom_product_repository.name }}" + redhat.satellite.repository: + username: "{{ satellite_username }}" + password: "{{ satellite_password }}" + server_url: "{{ satellite_server_url }}" + organization: "{{ satellite_organization }}" + name: "{{ custom_product_repository.name }}" + description: "{{ custom_product_repository.description | default(omit) }}" + content_type: "{{ custom_product_repository.content_type }}" + product: "{{ custom_product.name }}" + url: "{{ custom_product_repository.url }}" + mirror_on_sync: "{{ custom_product_repository.mirror_on_sync | default(omit) }}" + download_policy: "{{ custom_product_repository.download_policy | default(omit) }}" + gpg_key: "{{ custom_product_repository.gpg_key | default(omit) }}" + state: "{{ custom_product_repository.state | default(omit) }}" diff --git a/roles/satellite_config/tasks/satellite_get_ccv_cvs.yml b/roles/satellite_config/tasks/satellite_get_ccv_cvs.yml new file mode 100644 index 0000000..7a59830 --- /dev/null +++ b/roles/satellite_config/tasks/satellite_get_ccv_cvs.yml @@ -0,0 +1,32 @@ +--- +- name: "Fetch CCV info" + redhat.satellite.content_view_info: + username: "{{ satellite_username }}" + password: "{{ satellite_password }}" + server_url: "{{ satellite_server_url }}" + organization: "{{ satellite_organization }}" + name: "{{ satellite_config_ccv_name }}" + register: __result + +- name: "Ensure CV is published" + redhat.satellite.content_view_version: + username: "{{ satellite_username }}" + password: "{{ satellite_password }}" + server_url: "{{ satellite_server_url }}" + organization: "{{ satellite_organization }}" + content_view: "{{ component.content_view.name }}" + description: "{{ now(utc=true, fmt='%Y-%m-%d %H:%M:%S') }}" + loop: "{{ __result.content_view.components }}" + loop_control: + loop_var: component + +- name: "Ensure CCV is promoted to lifecycle" + redhat.satellite.content_view_version: + username: "{{ satellite_username }}" + password: "{{ satellite_password }}" + server_url: "{{ satellite_server_url }}" + organization: "{{ satellite_organization }}" + content_view: "{{ satellite_config_ccv_name }}" + description: "{{ now(utc=true, fmt='%Y-%m-%d %H:%M:%S') }}" + lifecycle_environments: + - "Dev" diff --git a/roles/satellite_config/tasks/satellite_global_parameters.yml b/roles/satellite_config/tasks/satellite_global_parameters.yml new file mode 100644 index 0000000..a6e59ab --- /dev/null +++ b/roles/satellite_config/tasks/satellite_global_parameters.yml @@ -0,0 +1,11 @@ +--- +- name: "Ensure the state of global parameter - {{ global_parameter.name }}" + redhat.satellite.global_parameter: + username: "{{ satellite_username }}" + password: "{{ satellite_password }}" + server_url: "{{ satellite_server_url }}" + name: "{{ global_parameter.name }}" + value: "{{ global_parameter.value }}" + parameter_type: "{{ global_parameter.parameter_type }}" + hidden_value: "{{ global_parameter.hidden_value | default(omit) }}" + state: "{{ global_parameter.state | default(omit) }}" diff --git a/roles/satellite_config/tasks/satellite_manage_users.yml b/roles/satellite_config/tasks/satellite_manage_users.yml new file mode 100644 index 0000000..7188e4b --- /dev/null +++ b/roles/satellite_config/tasks/satellite_manage_users.yml @@ -0,0 +1,30 @@ +--- +- name: Create a user group + redhat.satellite.usergroup: + username: "{{ satellite_username }}" + password: "{{ satellite_password }}" + server_url: "{{ satellite_server_url }}" + name: "{{ usergroups.name }}" + roles: "{{ usergroups.roles | default(omit) }}" + state: "{{ usergroups.state | default(omit) }}" + admin: "{{ usergroups.admin | default(omit) }}" + users: "{{ usergroups.users | default(omit) }}" + usergroups: "{{ usergroups.usergroups | default(omit) }}" + loop: "{{ satellite_config_usergroups }}" + loop_control: + loop_var: usergroups + when: satellite_config_usergroups is defined + +- name: Map external user groups + redhat.satellite.external_usergroup: + username: "{{ satellite_username }}" + password: "{{ satellite_password }}" + server_url: "{{ satellite_server_url }}" + name: "{{ external_usergroup.name }}" + auth_source: "{{ external_usergroup.auth_source | default(omit) }}" + state: "{{ external_usergroup.state | default(omit) }}" + usergroup: "{{ external_usergroup.usergroup | default(omit) }}" + loop: "{{ satellite_config_external_usergroups }}" + loop_control: + loop_var: external_usergroup + when: satellite_config_external_usergroups is defined diff --git a/roles/satellite_config/tasks/satellite_operatingsystems.yml b/roles/satellite_config/tasks/satellite_operatingsystems.yml new file mode 100644 index 0000000..bad72ca --- /dev/null +++ b/roles/satellite_config/tasks/satellite_operatingsystems.yml @@ -0,0 +1,32 @@ +--- +- name: Block for operating system creation + block: + - name: Create OS + redhat.satellite.operatingsystem: + username: "{{ satellite_username }}" + password: "{{ satellite_password }}" + server_url: "{{ satellite_server_url }}" + name: "{{ operating_system.name }}" + description: "{{ operating_system.description | default(omit) }}" + major: "{{ operating_system.major }}" + minor: "{{ operating_system.minor | default(omit) }}" + family: "{{ operating_system.family }}" + password_hash: "{{ operating_system.password_hash }}" + architectures: "{{ operating_system.architectures | default(omit) }}" + ptables: "{{ operating_system.ptables | default(omit) }}" + provisioning_templates: "{{ operating_system.provisioning_templates | default(omit) }}" + loop: "{{ satellite_config_operatingsystems }}" + loop_control: + loop_var: operating_system + + - name: Add default provisioning_template type provision to the OS Object + redhat.satellite.os_default_template: + username: "{{ satellite_username }}" + password: "{{ satellite_password }}" + server_url: "{{ satellite_server_url }}" + operatingsystem: "{{ operating_system_templates.description }}" + template_kind: "provision" + provisioning_template: "{{ operating_system_templates.os_default_templates.provision }}" + loop: "{{ satellite_config_operatingsystems }}" + loop_control: + loop_var: operating_system_templates diff --git a/roles/satellite_config/tasks/satellite_partition_tables.yml b/roles/satellite_config/tasks/satellite_partition_tables.yml new file mode 100644 index 0000000..9fee54a --- /dev/null +++ b/roles/satellite_config/tasks/satellite_partition_tables.yml @@ -0,0 +1,16 @@ +--- +- name: "Create a Partition Template from a file and modify with parameter(s)" + redhat.satellite.partition_table: + username: "{{ satellite_username }}" + password: "{{ satellite_password }}" + server_url: "{{ satellite_server_url }}" + name: "{{ partition_table.name }}" + state: "{{ partition_table.state | default(omit) }}" + locations: "{{ partition_table.locations | default(omit) }}" + organizations: "{{ partition_table.organization | default(satellite_organization) }}" + os_family: "{{ partition_table.os_family | default('Redhat') }}" + locked: "{{ partition_table.is_template_locked | default(omit) }}" + layout: "{{ partition_table.layout }}" + loop: "{{ satellite_config_partition_tables }}" + loop_control: + loop_var: partition_table diff --git a/roles/satellite_config/tasks/satellite_promote_ccv.yml b/roles/satellite_config/tasks/satellite_promote_ccv.yml new file mode 100644 index 0000000..a08a57a --- /dev/null +++ b/roles/satellite_config/tasks/satellite_promote_ccv.yml @@ -0,0 +1,12 @@ +--- +- name: "Ensure CCV is promoted to lifecycle" + redhat.satellite.content_view_version: + username: "{{ satellite_username }}" + password: "{{ satellite_password }}" + server_url: "{{ satellite_server_url }}" + organization: "{{ satellite_organization }}" + content_view: "{{ satellite_config_ccv_name }}" + description: "{{ now(utc=true, fmt='%Y-%m-%d %H:%M:%S') }}" + current_lifecycle_environment: "Dev" + lifecycle_environments: + - "{{ lifecycle_environment }}" diff --git a/roles/satellite_config/tasks/satellite_publish_cvs.yml b/roles/satellite_config/tasks/satellite_publish_cvs.yml new file mode 100644 index 0000000..1e810d6 --- /dev/null +++ b/roles/satellite_config/tasks/satellite_publish_cvs.yml @@ -0,0 +1,6 @@ +--- +- name: Include satellite_get_ccv_cvs.yml + ansible.builtin.include_tasks: satellite_get_ccv_cvs.yml + loop: "{{ satellite_config_ccvs }}" + loop_control: + loop_var: satellite_config_ccv_name diff --git a/roles/satellite_config/tasks/satellite_realms.yml b/roles/satellite_config/tasks/satellite_realms.yml new file mode 100644 index 0000000..8fd9468 --- /dev/null +++ b/roles/satellite_config/tasks/satellite_realms.yml @@ -0,0 +1,15 @@ +--- +- name: "Create realm" + redhat.satellite.realm: + username: "{{ satellite_username }}" + password: "{{ satellite_password }}" + server_url: "{{ satellite_server_url }}" + organizations: "{{ satellite_organization | default(omit) }}" + locations: "{{ realm.locations | default(omit) }}" + name: "{{ realm.name }}" + realm_proxy: "{{ realm.realm_proxy }}" + realm_type: "{{ realm.realm_type }}" + state: "{{ realm.state }}" + loop: "{{ satellite_config_realms }}" + loop_control: + loop_var: realm diff --git a/roles/satellite_config/tasks/satellite_scap_contents.yml b/roles/satellite_config/tasks/satellite_scap_contents.yml new file mode 100644 index 0000000..1dae740 --- /dev/null +++ b/roles/satellite_config/tasks/satellite_scap_contents.yml @@ -0,0 +1,22 @@ +--- +- name: "Copy the scap file to target" + become: true + ansible.builtin.copy: + src: "{{ scap_content.scap_file }}" + dest: "{{ scap_content.scap_file }}" + owner: root + group: root + mode: "0644" + when: "scap_content.scap_file is defined" + +- name: "Ensure the scap content is loaded" + redhat.satellite.scap_content: + username: "{{ satellite_username }}" + password: "{{ satellite_password }}" + server_url: "{{ satellite_server_url }}" + title: "{{ scap_content.title }}" + scap_file: "{{ scap_content.scap_file | default(omit) }}" + locations: "{{ scap_content.locations | default(omit) }}" + organizations: "{{ scap_content.organizations | default(omit) }}" + updated_title: "{{ scap_content.updated_title | default(omit) }}" + state: "{{ scap_content.state | default(omit) }}" diff --git a/roles/satellite_config/tasks/satellite_scap_policies.yml b/roles/satellite_config/tasks/satellite_scap_policies.yml new file mode 100644 index 0000000..9e5724e --- /dev/null +++ b/roles/satellite_config/tasks/satellite_scap_policies.yml @@ -0,0 +1,14 @@ +--- +# - name: "Ensure the scap policy is mapped" +# redhat.satellite.scap_policy: +# username: "{{ satellite_username }}" +# password: "{{ satellite_password }}" +# server_url: "{{ satellite_server_url }}" +# name: "{{ scap_policies.name }}" +# scap_file: "{{ scap_policies.scap_file | defautl(omit) }}" +# locations: "{{ scap_policies.locations | default(omit) }}" +# organizations: "{{ scap_policies.organizations | default(omit) }}" +# updated_name: "{{ scap_policies.updated_name | default(omit) }}" +# loop: "{{ satellite_scap_policies }}" +# loop_control: +# loop_var: scap_policies diff --git a/roles/satellite_config/tasks/satellite_scap_tailoring_files.yml b/roles/satellite_config/tasks/satellite_scap_tailoring_files.yml new file mode 100644 index 0000000..a8636e7 --- /dev/null +++ b/roles/satellite_config/tasks/satellite_scap_tailoring_files.yml @@ -0,0 +1,21 @@ +--- +- name: "Copy the scap tailoring file to target" + become: true + ansible.builtin.copy: + src: "{{ scap_tailoring_file.scap_file }}" + dest: "{{ scap_tailoring_file.scap_file }}" + owner: root + group: root + mode: "0644" + +- name: "Ensure the scap tailoring file is loaded" + redhat.satellite.scap_tailoring_file: + username: "{{ satellite_username }}" + password: "{{ satellite_password }}" + server_url: "{{ satellite_server_url }}" + name: "{{ scap_tailoring_file.name }}" + scap_file: "{{ scap_tailoring_file.scap_file | default(omit) }}" + locations: "{{ scap_tailoring_file.locations | default(omit) }}" + organizations: "{{ scap_tailoring_file.organizations | default(omit) }}" + updated_name: "{{ scap_tailoring_file.updated_name | default(omit) }}" + state: "{{ scap_tailoring_file.state | default(omit) }}" diff --git a/roles/satellite_config/tasks/satellite_sync_repositories.yml b/roles/satellite_config/tasks/satellite_sync_repositories.yml new file mode 100644 index 0000000..8eadb8c --- /dev/null +++ b/roles/satellite_config/tasks/satellite_sync_repositories.yml @@ -0,0 +1,60 @@ +--- +# synchronization of repositories +- name: "Sync repositories" + block: + - name: "Set or increment retry count" + ansible.builtin.set_fact: + sync_retry_count: "{{ 0 if sync_retry_count is undefined else sync_retry_count | int + 1 }}" + + - name: "Get the unique list of Red Hat products" + ansible.builtin.set_fact: + __rh_products_to_sync: "{{ satellite_products | map(attribute='name') | list | unique }}" + + - name: "List the products to be synchronized" + ansible.builtin.debug: + var: __rh_products_to_sync + + - name: "Sync the Red Hat products with the CDN" + redhat.satellite.repository_sync: + username: "{{ satellite_username }}" + password: "{{ satellite_password }}" + server_url: "{{ satellite_server_url }}" + organization: "{{ satellite_organization }}" + product: "{{ rh_product }}" + loop: "{{ __rh_products_to_sync }}" + loop_control: + loop_var: rh_product + async: "{{ async_timeout }}" + poll: "{{ async_delay }}" + register: async_update + + - name: "Get the unique list of custom products" + ansible.builtin.set_fact: + __custom_products_to_sync: "{{ satellite_config_custom_products | map(attribute='name') | list | unique }}" + + - name: "Sync the custom products" + redhat.satellite.repository_sync: + username: "{{ satellite_username }}" + password: "{{ satellite_password }}" + server_url: "{{ satellite_server_url }}" + organization: "{{ satellite_organization }}" + product: "{{ custom_products }}" + loop: "{{ __custom_products_to_sync }}" + loop_control: + loop_var: custom_products + async: "{{ async_timeout }}" + poll: "{{ async_delay }}" + register: async_update + + rescue: + - name: "Retry count exceeded, failing" + ansible.builtin.fail: + msg: "Maximum retry count exceeded, have tried {{ sync_retry_count }} times" + when: sync_retry_count|int == 4 + + - name: "Retrying" + ansible.builtin.debug: + msg: "Retrying, retry count {{ sync_retry_count }} of 4..." + + - name: "Retry" + ansible.builtin.include_tasks: satellite_sync_repositories.yml diff --git a/roles/satellite_config/templates/foreman.yml.j2 b/roles/satellite_config/templates/foreman.yml.j2 new file mode 100644 index 0000000..7dcde6d --- /dev/null +++ b/roles/satellite_config/templates/foreman.yml.j2 @@ -0,0 +1,14 @@ +--- +# Current templating with installer causes space trailing the password +# using our own templating to resolve the matter + +:foreman: + # Credentials. You'll be asked for the interactively if you leave them blank here + :username: 'admin' + :password: {{ satellite_password | trim | quote }} + + # Check API documentation cache status on each request + :refresh_cache: false + + # API request timeout in seconds, set -1 for infinity + :request_timeout: 120 \ No newline at end of file diff --git a/roles/satellite_host_deploy/README.md b/roles/satellite_host_deploy/README.md new file mode 100644 index 0000000..a882917 --- /dev/null +++ b/roles/satellite_host_deploy/README.md @@ -0,0 +1,52 @@ +satellite_host_deploy +========= + +This role deploy/deletes host (VM) on defined host group in location, organization, with using specified image in the compute resource. + +Requirements +------------ + +Satellite host group must be configured and specified. Location and organization information must be defined. Also image and the compute resource of the image must be defined. + +Role Variables +-------------- + +```yaml +# default variables +satellite_host_deploy: true +satellite_host_deploy_hostgroup: "hg_example" +satellite_host_deploy_compute_attributes: + vm_size: "Standard_B1ms" + tags: "sequencestart=1,sequencestop=5" +satellite_host_deploy_organization: "{{ satellite_organization }}" +satellite_host_deploy_location: "{{ satellite_location }}" +satellite_host_deploy_compute_resource: "example_compute_resource" +satellite_host_deploy_image: "example_image" +satellite_host_compute_profile: "example_compute_profile" +# Optional variables +satellite_host_deploy_state: [ present|absent ] # default value is present +``` + +Dependencies +------------ + +A list of other roles hosted on Galaxy should go here, plus any details in regards to parameters that may need to be set for other roles, or variables that are used from other roles. + +Example Playbook +---------------- + +```yaml +- hosts: host.example.com | example_group + roles: + - satellite_host_deploy +``` + +License +------- + +GPL-3.0-only + +Author Information +------------------ + +Showroom Project Team \ No newline at end of file diff --git a/roles/satellite_host_deploy/defaults/main.yml b/roles/satellite_host_deploy/defaults/main.yml new file mode 100644 index 0000000..e00b074 --- /dev/null +++ b/roles/satellite_host_deploy/defaults/main.yml @@ -0,0 +1,8 @@ +--- +# defaults file for satellite_host_deploy +# satellite_host_deploy: [ true|false ] +# satellite_host_deploy_organization: "" +# satellite_host_deploy_location: "" +# satellite_host_deploy_hostgroup: "" +# satellite_host_deploy_compute_resource: "" +# satellite_host_deploy_image: "" diff --git a/roles/satellite_host_deploy/meta/main.yml b/roles/satellite_host_deploy/meta/main.yml new file mode 100644 index 0000000..748e9df --- /dev/null +++ b/roles/satellite_host_deploy/meta/main.yml @@ -0,0 +1,16 @@ +--- +galaxy_info: + author: Showroom Project Team + description: This role deploy/deletes host (VM) on defined host group in location, organization, with using specified image in the compute resource. + company: Red Hat, Inc. + license: GPL-3.0-only + min_ansible_version: "2.14" + platforms: + - name: EL + versions: + - "8" + - "9" + galaxy_tags: [] +dependencies: [] +collections: + - redhat.satellite diff --git a/roles/satellite_host_deploy/tasks/main.yml b/roles/satellite_host_deploy/tasks/main.yml new file mode 100644 index 0000000..78d19aa --- /dev/null +++ b/roles/satellite_host_deploy/tasks/main.yml @@ -0,0 +1,26 @@ +--- +# tasks file for satellite_host_deploy +- name: "Create a Satellite Host" + redhat.satellite.host: + username: "{{ hostvars[groups['satellite'][0]]['satellite_username'] }}" + password: "{{ hostvars[groups['satellite'][0]]['satellite_password'] }}" + server_url: "{{ hostvars[groups['satellite'][0]]['satellite_server_url'] }}" + name: "{{ inventory_hostname }}" + organization: "{{ satellite_host_deploy_organization }}" + location: "{{ satellite_host_deploy_location }}" + hostgroup: "{{ satellite_host_deploy_hostgroup }}" + compute_attributes: "{{ satellite_host_deploy_compute_attributes }}" + compute_resource: "{{ satellite_host_deploy_compute_resource }}" + image: "{{ satellite_host_deploy_image }}" + state: "{{ satellite_host_deploy_state | default(omit) }}" + compute_profile: "{{ satellite_host_compute_profile }}" + when: ( satellite_host_deploy_state is defined and satellite_host_deploy_state == "present" ) or (satellite_host_deploy_state is not defined) + +- name: "Delete a Satellite Host" + redhat.satellite.host: + username: "{{ hostvars[groups['satellite'][0]]['satellite_username'] }}" + password: "{{ hostvars[groups['satellite'][0]]['satellite_password'] }}" + server_url: "{{ hostvars[groups['satellite'][0]]['satellite_server_url'] }}" + name: "{{ inventory_hostname }}" + state: "{{ satellite_host_deploy_state }}" + when: satellite_host_deploy_state is defined and satellite_host_deploy_state == "absent" diff --git a/roles/satellite_prepare/README.md b/roles/satellite_prepare/README.md new file mode 100644 index 0000000..c0faf64 --- /dev/null +++ b/roles/satellite_prepare/README.md @@ -0,0 +1,52 @@ +satellite_prepare +========= + +This roles prepares host to Red Hat Satellite installation. + +Requirements +------------ + +- A VM must be deployed to run the role. + +Role Variables +-------------- + +```yaml +# default variables +satellite_prepare_ipa_default_bind_policy: "grant {{ ipaclient_realm }} krb5-self * A; grant {{ ipaclient_realm }} krb5-self * AAAA; grant {{ ipaclient_realm }} krb5-self * SSHFP;" +satellite_prepare_foreman_proxy_realm_principal: "" +satellite_prepare_foreman_proxy_bind_update_policy: "{{ satellite_prepare_ipa_default_bind_policy }} grant {{ satellite_prepare_foreman_proxy_realm_principal }}\@{{ ipaclient_realm }} wildcard * ANY;" +satellite_prepare_keytab_path: "" +satellite_prepare_skip_prepare_realm: false + +# configuration variables +satellite_prepare_ipa_server_ca_path: "{{ ipa_server_ca_path }}" +satellite_prepare_ipa_client_trust_path: "{{ ipa_client_trust_path }}" +satellite_prepare_ipaclient_domain: "{{ ipaclient_domain }}" + +``` + +Dependencies +------------ + +- RH IdM is required to be deployed before when RH Satellite will be used with Kerberos integration preferred with RH IdM. + +Example Playbook +---------------- + +```yaml +- hosts: satellite + roles: + - role: satellite_prepare + become: true +``` + +License +------- + +GPL-3.0-only + +Author Information +------------------ + +Showroom Project Team diff --git a/roles/satellite_prepare/defaults/main.yml b/roles/satellite_prepare/defaults/main.yml new file mode 100644 index 0000000..3bc8873 --- /dev/null +++ b/roles/satellite_prepare/defaults/main.yml @@ -0,0 +1,16 @@ +--- +satellite_prepare_ipa_default_bind_policy: >- + grant {{ ipaclient_realm }} krb5-self * A; grant {{ ipaclient_realm }} krb5-self * AAAA; + grant {{ ipaclient_realm }} krb5-self * SSHFP; +satellite_prepare_foreman_proxy_realm_principal: "" +satellite_prepare_foreman_proxy_bind_update_policy: >- + {{ satellite_prepare_ipa_default_bind_policy }} grant + {{ satellite_prepare_foreman_proxy_realm_principal }}\@{{ ipaclient_realm }} + wildcard * ANY; + +satellite_prepare_keytab_path: "" +satellite_prepare_skip_prepare_realm: false + +satellite_prepare_ipaclient_domain: "{{ ipaclient_domain }}" +satellite_prepare_ipa_server_ca_path: "{{ ipa_server_ca_path }}" +satellite_prepare_ipa_client_trust_path: "{{ ipa_client_trust_path }}" diff --git a/roles/satellite_prepare/meta/main.yml b/roles/satellite_prepare/meta/main.yml new file mode 100644 index 0000000..c9cd76b --- /dev/null +++ b/roles/satellite_prepare/meta/main.yml @@ -0,0 +1,18 @@ +--- +galaxy_info: + author: Showroom Project Team + description: This roles prepares host to Red Hat Satellite installation. + company: Red Hat, Inc. + license: GPL-3.0-only + min_ansible_version: "2.14" + platforms: + - name: EL + versions: + - "8" + - "9" + galaxy_tags: [] +dependencies: [] +collections: + - community.crypto + - community.general + - redhat.rhel_idm diff --git a/roles/satellite_prepare/tasks/check_certs.yml b/roles/satellite_prepare/tasks/check_certs.yml new file mode 100644 index 0000000..7814a7d --- /dev/null +++ b/roles/satellite_prepare/tasks/check_certs.yml @@ -0,0 +1,8 @@ +--- +- name: "Checking the certificates for Satellite" + ansible.builtin.command: >- + katello-certs-check -c {{ satellite_prepare_ssl_crt_path }} -k {{ satellite_prepare_ssl_key_path }} -b + {{ satellite_prepare_ipa_server_ca_path }} + register: __check_return + changed_when: __check_return.changed + failed_when: __check_return.failed diff --git a/roles/satellite_prepare/tasks/ensure_cdn_registration.yml b/roles/satellite_prepare/tasks/ensure_cdn_registration.yml new file mode 100644 index 0000000..5f641b6 --- /dev/null +++ b/roles/satellite_prepare/tasks/ensure_cdn_registration.yml @@ -0,0 +1,6 @@ +--- +- name: "Ensure Satellite system registered to CDN" + community.general.redhat_subscription: + activationkey: "{{ rh_activation_key }}" + org_id: "{{ rh_organization_number }}" + state: present diff --git a/roles/satellite_prepare/tasks/ensure_firewalld_config.yml b/roles/satellite_prepare/tasks/ensure_firewalld_config.yml new file mode 100644 index 0000000..3bc801e --- /dev/null +++ b/roles/satellite_prepare/tasks/ensure_firewalld_config.yml @@ -0,0 +1,18 @@ +--- +- name: "Ensure that firewalld is installed" + ansible.builtin.dnf: + name: firewalld + state: present + +- name: "Ensure that the Firewalld service is enabled and running" + ansible.builtin.systemd: + name: firewalld + state: started + enabled: true + masked: false + +- name: "Configure Firewalld services and ports for Satellite" # noqa var-naming[no-role-prefix] + ansible.builtin.include_role: + name: "redhat.rhel_system_roles.firewall" + vars: + firewall: "{{ satellite_firewall }}" diff --git a/roles/satellite_prepare/tasks/ensure_repositories.yml b/roles/satellite_prepare/tasks/ensure_repositories.yml new file mode 100644 index 0000000..d42a714 --- /dev/null +++ b/roles/satellite_prepare/tasks/ensure_repositories.yml @@ -0,0 +1,6 @@ +--- +- name: "Ensure the proper repositories are enabled" + community.general.rhsm_repository: + name: "{{ satellite_repository_ids }}" + state: enabled + purge: true diff --git a/roles/satellite_prepare/tasks/ensure_sat_binaries.yml b/roles/satellite_prepare/tasks/ensure_sat_binaries.yml new file mode 100644 index 0000000..a94484f --- /dev/null +++ b/roles/satellite_prepare/tasks/ensure_sat_binaries.yml @@ -0,0 +1,68 @@ +--- +- name: "Update the system" # noqa: package-latest + ansible.builtin.dnf: + name: '*' + state: latest + +- name: Check if we need to reboot + ansible.builtin.command: dnf needs-restarting -r + ignore_errors: true + register: __reboot_hint + changed_when: false + failed_when: __reboot_hint.rc not in [0, 1] + +- name: Reboot the machine if required + ansible.builtin.reboot: + reboot_timeout: 1200 + when: __reboot_hint.rc | int == 1 + +- name: "Check if Ruby module is enabled or installed" + ansible.builtin.command: "dnf module list ruby" + register: __ruby_module_status + failed_when: __ruby_module_status.failed + changed_when: __ruby_module_status.changed + +- name: "Reset the Ruby module" + ansible.builtin.command: "dnf -y module reset ruby" + when: "'[e]' or '[i]' not in __ruby_module_status.stdout" + register: __ruby_return + failed_when: __ruby_return.failed + changed_when: __ruby_return.changed + +- name: "Check if PostgreSQL module is enabled or installed" + ansible.builtin.command: "dnf module list postgresql" + register: __postgresql_module_status + failed_when: __postgresql_module_status.failed + changed_when: __postgresql_module_status.changed + +- name: "Reset the PostgreSQL module" + ansible.builtin.command: "dnf -y module reset postgresql" + when: "'[e]' or '[i]' not in __postgresql_module_status.stdout" + register: __postgresql_return + failed_when: __postgresql_return.failed + changed_when: __postgresql_return.changed + +- name: "Check if Satellite module is enabled or installed" + ansible.builtin.command: "dnf module list satellite:el8" + register: __satellite_module_status + failed_when: __satellite_module_status.failed + changed_when: __satellite_module_status.changed + +- name: "Reset the Satellite module" + ansible.builtin.command: "dnf -y module reset satellite:el8" + when: "'[e]' or '[i]' not in __satellite_module_status.stdout" + register: __satellite_return + failed_when: __satellite_return.failed + changed_when: __satellite_return.changed + +- name: "Enable the Satellite module" + ansible.builtin.command: "dnf -y module enable satellite:el8" + when: "'[e]' or '[i]' not in __satellite_module_status.stdout" + register: __satellite_module_return + failed_when: __satellite_module_return.failed + changed_when: __satellite_module_return.changed + +- name: "Install the Satellite RPM packages" + ansible.builtin.dnf: + name: satellite + state: present diff --git a/roles/satellite_prepare/tasks/foreman_prepare_realm.yml b/roles/satellite_prepare/tasks/foreman_prepare_realm.yml new file mode 100644 index 0000000..896b19b --- /dev/null +++ b/roles/satellite_prepare/tasks/foreman_prepare_realm.yml @@ -0,0 +1,76 @@ +--- +- name: "Clean up old keytabs" + ansible.builtin.file: + path: "{{ satellite_prepare_keytab_path }}" + state: absent + +- name: "Setup satellite realm user" + ansible.builtin.shell: >- + set -o pipefail && echo '{{ ipaadmin_password }}' | kinit {{ ipaadmin_principal }}; + echo '{{ ipaadmin_password }}' | /usr/sbin/foreman-prepare-realm admin + {{ satellite_prepare_foreman_proxy_realm_principal }} + args: + chdir: "/etc/foreman-proxy/" + register: __result + changed_when: __result.changed + failed_when: __result.failed + +- name: "Wait for keytab generation and retrieval" + ansible.builtin.wait_for: + path: "{{ satellite_prepare_keytab_path }}" + +- name: "Ensure foreman-proxy owns the /etc/foreman-proxy/freeipa.keytab" + ansible.builtin.file: + path: "{{ satellite_prepare_keytab_path }}" + owner: foreman-proxy + group: foreman-proxy + mode: "0644" + state: file + +- name: "Test the new keytab" + ansible.builtin.command: "kinit -kt {{ satellite_prepare_keytab_path }} {{ satellite_prepare_foreman_proxy_realm_principal }}" + register: __result + changed_when: __result.changed + failed_when: __result.failed + +- name: "Copy the ipa ca-certificates to the trust directory" + ansible.builtin.copy: + src: "{{ satellite_prepare_ipa_server_ca_path }}" + dest: "{{ satellite_prepare_ipa_client_trust_path }}" + remote_src: true + owner: root + group: root + mode: "0644" + +- name: "Enable the trust" + ansible.builtin.command: "update-ca-trust enable" + register: __result + changed_when: __result.changed + failed_when: __result.failed + +- name: "Update the trust" + ansible.builtin.command: "update-ca-trust" + register: __result + changed_when: __result.changed + failed_when: __result.failed + +- name: "Grant Satellite to update reverse dns zones" + redhat.rhel_idm.ipadnszone: + ipaadmin_principal: "{{ ipaadmin_principal | default(omit) }}" + ipaadmin_password: "{{ ipaadmin_password }}" + name_from_ip: "{{ subnets_var.subnet }}" + update_policy: >- + grant {{ ipaclient_realm }} krb5-subdomain + {{ subnets_var.subnet.split('.')[2] }}.{{ subnets_var.subnet.split('.')[1] }}.{{ subnets_var.subnet.split('.')[0] }}.in-addr.arpa. + PTR; grant {{ satellite_prepare_foreman_proxy_realm_principal }}\\@{{ ipaclient_realm }} wildcard * ANY; + loop: "{{ satellite_config_reverse_dns_networks }}" + loop_control: + loop_var: subnets_var + when: satellite_config_reverse_dns_networks | length() + +- name: "Set up IdM forward lookup zone to allow foreman proxy to update DNS" + redhat.rhel_idm.ipadnszone: + ipaadmin_principal: "{{ ipaadmin_principal }}" + ipaadmin_password: "{{ ipaadmin_password }}" + name: "{{ satellite_prepare_ipaclient_domain }}" + update_policy: "{{ satellite_prepare_foreman_proxy_bind_update_policy }}" diff --git a/roles/satellite_prepare/tasks/get_existing_keytab.yml b/roles/satellite_prepare/tasks/get_existing_keytab.yml new file mode 100644 index 0000000..eb85315 --- /dev/null +++ b/roles/satellite_prepare/tasks/get_existing_keytab.yml @@ -0,0 +1,28 @@ +--- +- name: "Grant access to the service keytab" + redhat.rhel_idm.ipaservice: + ipaadmin_password: "{{ ipaadmin_password }}" + name: "{{ __target_service }}" + allow_retrieve_keytab_user: + - "{{ ipaadmin_principal }}" + allow_retrieve_keytab_host: + - "{{ inventory_hostname }}" + action: member + +- name: "Grant access to the host keytab" + redhat.rhel_idm.ipahost: + ipaadmin_password: "{{ ipaadmin_password }}" + name: "{{ inventory_hostname }}" + state: present + allow_retrieve_keytab_user: + - "{{ ipaadmin_principal }}" + managedby_host: "{{ inventory_hostname }}" + action: member + +- name: "Ensure foreman-proxy owns the /etc/foreman-proxy/freeipa.keytab" + ansible.builtin.file: + path: "{{ satellite_prepare_keytab_path }}" + owner: foreman-proxy + group: foreman-proxy + mode: "0644" + state: file diff --git a/roles/satellite_prepare/tasks/main.yml b/roles/satellite_prepare/tasks/main.yml new file mode 100644 index 0000000..c2cd7a0 --- /dev/null +++ b/roles/satellite_prepare/tasks/main.yml @@ -0,0 +1,26 @@ +--- +- name: "Ensure facts are gathered" + ansible.builtin.setup: + +- name: "Ensure CDN registration" + ansible.builtin.import_tasks: ensure_cdn_registration.yml + +- name: "Ensure satellite repositories are enabled" + ansible.builtin.import_tasks: ensure_repositories.yml + +- name: "Ensure satellite firewall ports are configured" + ansible.builtin.import_tasks: ensure_firewalld_config.yml + +- name: "Ensure satellite binaries are installed" + ansible.builtin.import_tasks: ensure_sat_binaries.yml + +- name: "Ensure satellite certificates are validated" + ansible.builtin.import_tasks: check_certs.yml + +- name: "Ensure identity management realm is prepared for use with Satellite" + ansible.builtin.import_tasks: foreman_prepare_realm.yml + when: not satellite_prepare_skip_prepare_realm + +- name: "Get existing keytab for foreman-proxy use with Realm" + ansible.builtin.import_tasks: get_existing_keytab.yml + when: satellite_prepare_skip_prepare_realm diff --git a/roles/satellite_register/README.md b/roles/satellite_register/README.md new file mode 100644 index 0000000..360a116 --- /dev/null +++ b/roles/satellite_register/README.md @@ -0,0 +1,45 @@ +satellite_register +========= + +This roles registers the given host to the Red Hat Satellite. + +Requirements +------------ + +- A Red Hat Satellite server should have been provisioned. +- Basic configuration of Red Hat Satellite server should have been done. + +Role Variables +-------------- + +```yaml +# configuration variables +satellite_register_username: "{{ satellite_username }}" +satellite_register_password: "{{ satellite_password }}" +satellite_register_server_url: "https://{{ groups.satellite | first }}" +``` + +Dependencies +------------ + +- N/A + +Example Playbook +---------------- + +```yaml +- hosts: bastion + roles: + - role: satellite_register + become: true +``` + +License +------- + +GPL-3.0-only + +Author Information +------------------ + +Showroom Project Team diff --git a/roles/satellite_register/defaults/main.yml b/roles/satellite_register/defaults/main.yml new file mode 100644 index 0000000..ed97d53 --- /dev/null +++ b/roles/satellite_register/defaults/main.yml @@ -0,0 +1 @@ +--- diff --git a/roles/satellite_register/meta/main.yml b/roles/satellite_register/meta/main.yml new file mode 100644 index 0000000..99a041e --- /dev/null +++ b/roles/satellite_register/meta/main.yml @@ -0,0 +1,16 @@ +--- +galaxy_info: + author: Showroom Project Team + description: This roles registers the given host to the Red Hat Satellite. + company: Red Hat, Inc. + license: GPL-3.0-only + min_ansible_version: "2.14" + platforms: + - name: EL + versions: + - "8" + - "9" + galaxy_tags: [] +dependencies: [] +collections: + - redhat.satellite diff --git a/roles/satellite_register/tasks/main.yml b/roles/satellite_register/tasks/main.yml new file mode 100644 index 0000000..d52b666 --- /dev/null +++ b/roles/satellite_register/tasks/main.yml @@ -0,0 +1,24 @@ +--- +- name: "Unregister from Red Hat CDN" + redhat.rhel_system_roles.redhat_subscription: + state: absent + +- name: "Run subscription-manager clean for cleanup stuff incl. SSL certs" + ansible.builtin.command: subscription-manager clean + register: __result + changed_when: __result.changed + failed_when: __result.failed + +- name: "Generate registration command" + redhat.satellite.registration_command: + username: "{{ hostvars[groups.satellite | first]['satellite_username'] }}" + password: "{{ hostvars[groups.satellite | first]['satellite_password'] }}" + server_url: "https://{{ groups.satellite | first }}" + hostgroup: "{{ satellite_hostgroup }}" + register: registration_command + +- name: "Perform registration" # noqa command-instead-of-shell + ansible.builtin.shell: "{{ registration_command.registration_command }}" + register: __result + changed_when: __result.changed + failed_when: __result.failed diff --git a/roles/vault_string/README.md b/roles/vault_string/README.md new file mode 100644 index 0000000..36e533a --- /dev/null +++ b/roles/vault_string/README.md @@ -0,0 +1,46 @@ +vault_string +========= + +This roles encrypts a file in a given path using ansible-vault + +Requirements +------------ + +- Configuration variables should be provided with a variable file. + +Role Variables +-------------- + +```yaml +# configuration variables +vault_string_vault_password: "{{ vault_password }}" + +# execution variables +vault_string_filepath: +``` + +Dependencies +------------ + +- N/A + +Example Playbook +---------------- + +```yaml +- hosts: rootCA + roles: + - role: vault_string + vars: + vault_string_filepath: "{{ __rootca_cert_path }}" +``` + +License +------- + +GPL-3.0-only + +Author Information +------------------ + +Showroom Project Team diff --git a/roles/vault_string/defaults/main.yml b/roles/vault_string/defaults/main.yml new file mode 100644 index 0000000..d6fe8f4 --- /dev/null +++ b/roles/vault_string/defaults/main.yml @@ -0,0 +1,2 @@ +--- +vault_string_vault_password: "{{ vault_password }}" diff --git a/roles/vault_string/meta/main.yml b/roles/vault_string/meta/main.yml new file mode 100644 index 0000000..66a90a3 --- /dev/null +++ b/roles/vault_string/meta/main.yml @@ -0,0 +1,16 @@ +--- +galaxy_info: + author: Showroom Project Team + description: This roles encrypts a file in a given path using ansible-vault. + company: Red Hat, Inc. + license: GPL-3.0-only + min_ansible_version: "2.14" + platforms: + - name: EL + versions: + - "8" + - "9" + galaxy_tags: [] +dependencies: [] +collections: + - community.builtin diff --git a/roles/vault_string/tasks/main.yml b/roles/vault_string/tasks/main.yml new file mode 100644 index 0000000..a6b27d5 --- /dev/null +++ b/roles/vault_string/tasks/main.yml @@ -0,0 +1,28 @@ +--- +- name: Slurp hosts file + ansible.builtin.slurp: + src: "{{ vault_string_filepath }}" + register: slurpfile + +- name: Set file content to fact + ansible.builtin.set_fact: + file_content: "{{ slurpfile['content'] | b64decode }}" + +- name: Set file name to fact + ansible.builtin.set_fact: + vault_string_variable_name: "{{ vault_string_filepath | basename | split('.') | first }}" + when: vault_string_variable_name is not defined + +- name: Encrypt and write it to a file + ansible.builtin.copy: + content: "{{ vault_string_variable_name }}: !vault |\n{{ file_content | vault(passphrase) | indent(width=2, first=True) }}" + dest: "{{ vault_string_filepath | dirname }}/vault_{{ vault_string_variable_name }}.yml" + mode: "0644" + vars: + passphrase: "{{ vault_password }}" + become: true + register: __return + +- name: "Set encrypted file path" + ansible.builtin.set_fact: + __path_file_encrypted: "{{ __return.dest }}"